The bridge lets a coding loop "consume" an integrations research
artifact one INT-XX item per iteration. Options b (order-sequential
iteration) and C (snapshot in task_template + save the pointer for
future refresh) from the design discussion.
Migration 0042 — three new loops columns:
- source_research_topic_id — nullable pointer to research_topics.
- consumed_int_ids TEXT[] — INT-XX ids the loop has completed.
Advances when topology_worker parses "COMPLETED: INT-<NN>" markers
from the run's final output (wired in a follow-up commit).
- current_int_index INT — monotonic pointer for order-sequential
iteration. Coordinator addresses INT-<current+1> unless prereqs are
unmet, in which case it works on the smallest unblocking INT-XX and
logs the reorder rationale.
Backend:
- cm_db::repo::loops::set_source_research_topic — bind/unbind pointer.
- cm_db::repo::loops::source_research_context — read pointer + state.
- routes::loops::compose_iteration_task — new caller-side helper
that reads the pointer, fetches the topic's latest research_outcome,
and prepends the artifact + focus instruction to task_template.
- run_now + webhook_receive both pass task_template through
compose_iteration_task before enqueue. Standalone loops (no pointer)
behave identically to before.
- CreateLoopRequest accepts `source_research_topic_id`, ownership-
checked via research_topics::get before persist.
Frontend:
- New ResearchArtifactPicker modal — lists published topics, fetches
the artifact on pick, returns (topic_id, markdown) to caller.
- LoopsWizard task_template step gains "Import from research artifact"
button (right-aligned). Click opens the picker. On pick: task
populates with the artifact markdown, pointer saved, textarea
expands to 8 rows, small info strip shows "Loop is bound to topic
<id>. Each iteration will focus on the next unconsumed INT-XX."
- Unlink button reverts to standalone loop mode.
Follow-up (next commit):
- topology_worker completion hook — parse "COMPLETED: INT-<NN>" out
of the run's final output + update consumed_int_ids +
current_int_index atomically. Without this, current_int_index stays
at 0 forever and every iteration works on the same INT.
- Loop card refresh button — re-read source topic's latest outcome
(useful after a reject-with-revision cycle on the source topic).