-- Folds research into loops as a "kind" of loop. Two kinds exist: -- -- 'exec' — today's behavior. Each iteration runs the coordinator -- task_template (optionally prepended with a research -- artifact + focus instructions for INT-consumption -- loops). -- 'research' — the fire runs the research pipeline against a bound -- topic config, appending a versioned research_outcomes -- row each time. Used for nightly paper-survey loops -- (D1 answer: every runnable thing is a loop; even a -- one-shot topic gets its own kind='research' loop with -- initial_burst=1). -- -- Default 'exec' + CHECK constraint keeps every existing row valid. -- The paired research + coding pattern (nightly research produces -- outcome, coding loop wakes on on_artifact_update to consume next INT) -- composes two loops of different kinds sharing a source topic id. ALTER TABLE loops ADD COLUMN kind TEXT NOT NULL DEFAULT 'exec' CHECK (kind IN ('exec', 'research')), -- Countdown for the triggers.initial_burst quota. On create_loop -- we enqueue the first iteration inline and set this to burst-1; -- on each completion the topology_worker checks and, when > 0, -- enqueues the next iteration + decrements. Once it hits 0 the -- burst is exhausted (further iterations happen only through -- cron / webhook / on_completion / on_artifact_update). ADD COLUMN initial_burst_remaining INT NOT NULL DEFAULT 0; CREATE INDEX loops_kind_source_idx ON loops (kind, source_research_topic_id) WHERE source_research_topic_id IS NOT NULL; -- Fast lookup for the on_artifact_update hook: given a topic_id, find -- all exec-kind loops bound to it with the trigger enabled. CREATE INDEX loops_on_artifact_update_idx ON loops (source_research_topic_id) WHERE source_research_topic_id IS NOT NULL AND (triggers ->> 'on_artifact_update')::boolean = true AND enabled = true;