Skip to content

[coordination] Ticket 1 — subordinate_status: give the Lead a global picture #205

Description

@serge-ivo

Problem

coder-lead supervises N coder-repo instances through agent_supervision (#183) — the Overseer moved up a level, which is the design. But the Lead is not yet as capable as the thing it replaces.

The hardcoded Overseer (routes/coding.ts ~1085) opens by building a global picture — for every repo: is a session live, what did it last print, what are its instructions — then answers from it in one model call, or delegates. That answering half is what makes it an overseer.

list_subordinates returns {instanceId, name, status}, and status is agent_instances.status — the subscription lifecycle (active|paused|canceled), permanently "active" whether a subordinate is idle, mid-run, or on fire. So the Lead must delegate a run (durable workflow + full LLM loop) to learn anything.

This is a wrong field on a read path, not a missing abstraction.

Decision: compose, do not unify

Rejected: widening agent_loop_runs with a kind discriminator so pipeline/apply/browse/coding runs share one table.

  • "Idle" and "stuck" are not rows. Idle is the absence of a row; stuck is a derivation over timestamps + runner liveness. A resolver is needed either way — the composition is irreducible, the table merge is not. That inverts the build order.
  • Four lifecycle records, not three. A human-driven coding session writes only coding_sessions — and that is the Lead's main case. A kind over the three named tables misses the row it most needs.
  • A kind enum is a second closed enum, in D1 where it is harder to change, while [coordination][deferred] Retire the workflow closed enum → declarative behavior #160 is retiring the first.
  • Measured cost. The one partial unification already done (delegateToPilot writing a loop row) shipped two stranded-row bugs, both memorialised in coding-session.ts:122 and :158. Unifying adds ~5 open and ~20 terminal sites, and a stranded row is permanent data — unlike a read bug, which a redeploy fixes.
  • It fails the docs' own test (coordination-primitives.md: "Build the abstraction when the fix you'd make anyway can be made through it"). The fix you'd make anyway is a status read, and it cannot be made through the unified table — it would be blocked behind five write-path conversions before producing one correct answer.

The staged gate for revisiting: a second consumer needs cross-instance run pagination that an in-memory merge cannot serve, and the reader is measured as the bottleneck. A read-only SQL VIEW union is the correct intermediate, and probably the terminal state.

The rule this must not break

Connectors read generic platform tables or a declared per-instance setting — never a domain store. Precedent: lib/connectors/repo-local.ts:35-51 stays generic by reading config.settings["repo_path"] rather than importing coding-store. lib/delegation.ts:8-27: "Generic so the board card doesn't have to be re-shaped per target kind."

The domain writes into a generic record; the supervisor reads platform records. A first attempt made lib/connectors/supervision.ts import coding-store/coding-timeline — reverted in 3f14bd3, because that is exactly the coupling migration 0063 says was removed.

The enabler

capabilities.boardColumns is already a declared, per-agent status vocabulary ({id,title,statuses[]}), resolved by boardConfigForInstance (lib/board.ts:33). A supervisor normalises any subordinate's free-text status through the subordinate's own declaration — no new declaration surface, and a third-party agent with columns "Triage / Cooking / Shipped" works on day one.

Two vocabularies, handled differently — this distinction is the design:

  • instance_runtime_tasks.status is free text → bucketed through the subordinate's declared columns; output carries both the raw status and that agent's own columnTitle.
  • agent_loop_runs.status is a closed platform enum → passed through verbatim, never bucketed.

Scope

  1. columnForStatus in lib/agent-capabilities.ts. The rule exists twice already, both outside the API and both also matching column.id === status: store/console/src/tabs/BoardTab.tsx:636 and workers/mcp/src/instance-tools/shared.ts:87. Point the MCP copy at the new canonical one.
  2. lib/instance-work.ts (new) — recentWorkForInstances / recentRunsForInstances. One D1 statement each, a UNION ALL of one indexed branch per subordinate (a flat IN (…) ORDER BY … LIMIT n lets one busy subordinate crowd out eleven). Must filter hidden = 0 (0019) or cleared cards resurface. Guard the empty-id case — an empty UNION ALL is a syntax error and a Lead with no links is reachable.
  3. lib/subordinate-observation.ts (new) — pure, no D1. Bucketing, staleness, and the 16 000-char cap (the ceiling the Overseer itself uses).
  4. lib/agent-loop-store.tslast_progress_at, written by recordIteration via a defaulted arg so AgentLoopWorkflow starts reporting progress with no change. check_delegation improves for free.
  5. lib/connectors/supervision.ts — add subordinate_status (read); rename list_subordinates' misleading statussubscription; intersect any instanceId argument with the graph.
  6. agent-think.ts — add to READ_ONLY_TOOLS, or the cross-round dedup refuses the Lead's second observation in a turn.
  7. Migration 0067 — the column, plus json_set on the coder-lead template's declared tools. No per-instance backfill: agentCapabilities resolves from agents.config at read time, so every subscribed Lead picks it up.

Read-only feature: it writes nothing, so no row can be stranded by it.

Acceptance

Ask the Lead "what is each of your agents doing right now?" — it answers from onesubordinate_status call with zerodelegate_goal calls.

Depth is deliberately outcomes + progress (objective, running/needs-you/done, step 4/10, quiet-for-N-min, outcome text), not raw terminal. The honest equivalent of the Overseer's terminal tail is deferred to #207.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions