research-wizard: schedule step (once/nightly/manual) + paired coding loop
Completes the research/loop fold. The wizard now closes with a
"How should this research run?" step; picking any mode materializes a
kind='research' loop bound to the topic, and an optional checkbox
adds a paired kind='exec' loop that consumes each new artifact.
Frontend:
- ResearchWizard grows from 5 to 6 steps. Step 6 is the schedule
picker:
· Just once — initial_burst=1, no other triggers
· Nightly — initial_burst=1 + cron "0 3 * * *"
· Manual — webhook_enabled=true
Below the radios, an optional card offers "Also create a coding
loop that consumes each new artifact" — creates a paired
kind='exec' loop with on_artifact_update=true + initial_burst=1
bound to the same topic.
- createTopic API type extended with `schedule` + `create_paired_coding_loop`.
- Both fields ride the existing POST /api/research call; back-compat
is preserved when the wizard omits them.
Backend:
- CreateTopicRequest gains TopicSchedule + create_paired_coding_loop.
- After topic + agent attach, create_topic calls
materialize_topic_loops which:
1. Creates a research-kind loop titled "Research · <topic>" bound
to the topic. Triggers vary by schedule mode; next_fire_at
computed from cron for nightly. Falls back silently if
loop-create errors so the topic still lands.
2. Flips kind to 'research' via loops::set_kind (NewLoop doesn't
take kind directly — default is 'exec' for backward compat).
3. Optionally creates a coding loop titled "Coding · <topic>"
with on_artifact_update=true + initial_burst=1.
- cm_db::repo::loops::set_kind — trivial UPDATE helper used by the
materialize path.
D-answer callouts:
- D1 (fold): every runnable thing is now a loop. "Just once" is a
research loop with initial_burst=1 and no other triggers.
- D2 (inherit): both paired loops carry the same source_research_topic_id
— repo binding lives on the topic, not duplicated.
- D3 (coordinator resolves): the research iteration prompt (from the
earlier commit) instructs the team to preserve stable INT ids and
mark deprecations; coding loops' consumed lists stay valid across
versions.
Follow-ups queued:
- Kind pill on LoopsList cards (research=purple, exec=cyan) so users
can tell them apart at a glance.
- Extract start_topic's task-build so kind='research' iterations
reuse the same coordinator prompt shape as one-shot runs (they
currently use a simpler refresh-oriented prompt; that's fine for
MVP but a rich shared build would give better parity).
- Research topic sidebar shows "linked to N loops" badge.
This commit is contained in:
@@ -322,6 +322,19 @@ pub async fn set_zeroclaw_container(
|
||||
/// downstream mini-timeline can show WHEN the plan was adjusted and
|
||||
/// WHY. Idempotent: appending a duplicate text/run_id combo is allowed
|
||||
/// (rare — indicates the parser matched twice on the same line).
|
||||
/// Set a loop's kind. Used by materialize_topic_loops right after
|
||||
/// create() — the create path doesn't take a kind parameter (default
|
||||
/// 'exec' matches every legacy loop), so research-kind loops flip the
|
||||
/// column in a follow-up UPDATE.
|
||||
pub async fn set_kind(pool: &PgPool, loop_id: Uuid, kind: &str) -> Result<(), DbError> {
|
||||
sqlx::query("UPDATE loops SET kind = $2, updated_at = now() WHERE id = $1")
|
||||
.bind(loop_id)
|
||||
.bind(kind)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set the countdown for the triggers.initial_burst quota. On
|
||||
/// create_loop we set this to `burst - 1` after firing the first
|
||||
/// iteration inline; on each subsequent completion we decrement and,
|
||||
|
||||
Reference in New Issue
Block a user