Skip to content

feat(gastown): wire convoys end-to-end#867

Merged
jrf0110 merged 3 commits intomainfrom
540-wire-convoys-e2e
Mar 6, 2026
Merged

feat(gastown): wire convoys end-to-end#867
jrf0110 merged 3 commits intomainfrom
540-wire-convoys-e2e

Conversation

@jrf0110
Copy link
Copy Markdown
Contributor

@jrf0110 jrf0110 commented Mar 5, 2026

Summary

Video Walkthrough

image image

Implements #540 — full convoy support from Mayor tools through the DO layer, HTTP/tRPC API, and dashboard UI. This is a large cross-cutting change touching 24 files across the worker, container, and frontend.

Mayor tools (container plugin)

  • gt_sling_batch — creates N beads + 1 convoy as a tracked group. Uses structured Zod array args instead of JSON-in-string to eliminate first-call parsing failures.
  • gt_list_convoys / gt_convoy_status — progress checking tools for the Mayor.
  • MayorGastownClient gains slingBatch, listConvoys, getConvoyStatus methods.
  • Mayor system prompt rewritten to prioritize gt_sling_batch over gt_sling for multi-task work.

TownDO (Durable Object)

  • slingConvoy — atomic batch creation of convoy bead + N issue beads + bead_dependencies links + polecat assignment + fire-and-forget dispatch.
  • listConvoys / getConvoyStatus — query active convoys with per-bead breakdown including rig_id and assignee_agent_name.
  • closeConvoy — force-close a convoy and all its tracked beads, unhooking any assigned agents.
  • Convoy progress tracking moved into beadOps.updateBeadStatus so all bead-close paths (review-queue, agentCompleted, schedulePendingWork) update convoy counters and auto-land.

Parallel dispatch fixes (container)

  • Per-rig git mutex in git-manager.ts — serializes cloneRepo/createWorktree/removeWorktree for the same rig to prevent index.lock collisions while allowing different rigs to proceed concurrently.
  • Global SDK server mutex in process-manager.ts — serializes ensureSDKServer to prevent process.chdir/process.env races when concurrent agents call createKilo().

Bead lifecycle fixes

  • hookBead no longer sets bead to in_progress — beads stay open until dispatchAgent confirms the container process started.
  • closeOrphanedReviewBeads — new alarm-driven cleanup for MR beads with pr_url whose assigned agent is idle/dead/missing (30-min timeout).
  • Alarm re-armed in initializeDatabase() so the alarm loop survives restarts/deploys.

Dashboard UI

  • ConvoyTimeline component rewritten to consume tRPC listConvoys data (was broken — grouped by parent_bead_id instead of bead_dependencies).
  • Town Overview: convoy section below activity graph with collapsible header and close-convoy button.
  • Rig Detail: convoy section filtered to beads in the current rig, also collapsible with close button.
  • z-index isolation on bead chips to prevent overlap with sticky navbar.

Dead code cleanup

  • Removed unused town-convoys.table.ts, town-convoy-beads.table.ts, orphaned convoy_id field from CreateBeadBody.

Verification

  • pnpm typecheck — all packages pass
  • vitest run mayor-tools.test.ts tools.test.ts — 21 tests pass (mayor tool tests + rig tool tests)
  • vitest run client.test.ts — 3 new MayorGastownClient tests pass (9 pre-existing failures on main confirmed unrelated)
  • Manual testing: convoy creation via gt_sling_batch, progress updates, close convoy, collapsible UI

Visual Changes

Convoy progress timeline added to Town Overview (below activity graph) and Rig Detail pages. Each convoy shows a progress bar, bead chips with status colors, assignee names, and a close button with confirmation. The section header is clickable to collapse/expand.

Reviewer Notes

  • The dist-types regeneration (build:types) has pre-existing errors from Hono type inference issues — these are not introduced by this PR.
  • The client.test.ts has 9 pre-existing failures on main due to missing townId in the test env fixture — confirmed by checking out main and running the same tests.
  • The per-rig git mutex uses promise chaining (no external deps). The lock map self-cleans when idle.
  • updateConvoyProgress in beads.ts is called on every bead close/fail — it does a bead_dependencies lookup which is indexed, so the overhead is minimal for non-convoy beads (returns immediately when no rows found).

Closes #540

Comment thread cloudflare-gastown/src/dos/town/review-queue.ts Outdated
Comment thread cloudflare-gastown/src/handlers/mayor-tools.handler.ts Outdated
Comment thread cloudflare-gastown/src/dos/Town.do.ts
Comment thread cloudflare-gastown/src/dos/town/beads.ts Outdated
Comment thread cloudflare-gastown/src/trpc/router.ts Outdated
Comment thread cloudflare-gastown/src/trpc/router.ts Outdated
Comment thread cloudflare-gastown/src/dos/Town.do.ts Outdated
@kilo-code-bot
Copy link
Copy Markdown
Contributor

kilo-code-bot Bot commented Mar 5, 2026

Code Review Summary

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 2
SUGGESTION 0

Fix these issues in Kilo Cloud

Issue Details (click to expand)

WARNING

File Line Issue
cloudflare-gastown/src/dos/Town.do.ts 1039 closeConvoy() can mark a convoy landed while child review beads are still open/in_progress
Other Observations (not in diff)

Issues found in unchanged code that cannot receive inline comments:

File Line Issue
src/components/gastown/drawer-panels/BeadPanel.tsx 234 Related-bead navigation still pushes the current rig id, so cross-rig convoy blocker/dependent links open the wrong rig
Files Reviewed (32 files)
  • cloudflare-gastown/container/plugin/client.test.ts
  • cloudflare-gastown/container/plugin/client.ts
  • cloudflare-gastown/container/plugin/mayor-tools.test.ts
  • cloudflare-gastown/container/plugin/mayor-tools.ts
  • cloudflare-gastown/container/plugin/types.ts
  • cloudflare-gastown/container/src/git-manager.ts
  • cloudflare-gastown/container/src/process-manager.ts
  • cloudflare-gastown/src/db/tables/convoy-metadata.table.ts
  • cloudflare-gastown/src/db/tables/town-convoy-beads.table.ts
  • cloudflare-gastown/src/db/tables/town-convoys.table.ts
  • cloudflare-gastown/src/dos/Town.do.ts
  • cloudflare-gastown/src/dos/town/agents.ts
  • cloudflare-gastown/src/dos/town/beads.ts
  • cloudflare-gastown/src/dos/town/container-dispatch.ts
  • cloudflare-gastown/src/dos/town/review-queue.ts
  • cloudflare-gastown/src/gastown.worker.ts
  • cloudflare-gastown/src/handlers/mayor-tools.handler.ts
  • cloudflare-gastown/src/handlers/rig-beads.handler.ts
  • cloudflare-gastown/src/prompts/mayor-system.prompt.ts
  • cloudflare-gastown/src/prompts/refinery-system.prompt.ts
  • cloudflare-gastown/src/trpc/router.ts
  • cloudflare-gastown/src/trpc/schemas.ts
  • cloudflare-gastown/src/types.ts
  • cloudflare-gastown/test/integration/convoy-dag.test.ts
  • cloudflare-gastown/test/unit/convoy-branches.test.ts
  • eslint.config.mjs
  • src/app/(app)/gastown/[townId]/TownOverviewPageClient.tsx
  • src/app/(app)/gastown/[townId]/rigs/[rigId]/RigDetailPageClient.tsx
  • src/components/gastown/ConvoyTimeline.tsx
  • src/components/gastown/drawer-panels/BeadPanel.tsx
  • src/lib/gastown/types/router.d.ts
  • src/lib/gastown/types/schemas.d.ts

Comment thread cloudflare-gastown/src/dos/town/review-queue.ts
Comment thread cloudflare-gastown/src/dos/Town.do.ts Outdated
Comment thread cloudflare-gastown/container/src/process-manager.ts Outdated
Comment thread cloudflare-gastown/src/dos/Town.do.ts Outdated
Comment thread src/app/(app)/gastown/[townId]/TownOverviewPageClient.tsx
Comment thread src/app/(app)/gastown/[townId]/rigs/[rigId]/RigDetailPageClient.tsx
Comment thread cloudflare-gastown/src/dos/Town.do.ts Outdated
Comment thread cloudflare-gastown/src/dos/town/beads.ts Outdated
Comment thread cloudflare-gastown/src/dos/Town.do.ts
Comment thread cloudflare-gastown/src/dos/Town.do.ts Outdated
Comment thread cloudflare-gastown/src/dos/town/review-queue.ts
Comment thread cloudflare-gastown/src/handlers/mayor-tools.handler.ts
@jrf0110 jrf0110 force-pushed the 540-wire-convoys-e2e branch from c37cc96 to c06babc Compare March 6, 2026 16:07
Comment thread src/components/gastown/drawer-panels/BeadPanel.tsx Outdated
Comment thread cloudflare-gastown/src/dos/Town.do.ts Outdated
Comment thread cloudflare-gastown/src/dos/town/review-queue.ts Outdated
Comment thread cloudflare-gastown/src/dos/town/review-queue.ts
Comment thread cloudflare-gastown/src/dos/town/container-dispatch.ts
Comment thread cloudflare-gastown/src/dos/town/review-queue.ts
Comment thread cloudflare-gastown/src/dos/town/beads.ts Outdated
@jrf0110 jrf0110 force-pushed the 540-wire-convoys-e2e branch from b7b53ca to 4e8ddf4 Compare March 6, 2026 16:54
Comment thread cloudflare-gastown/src/dos/town/beads.ts
Comment thread cloudflare-gastown/src/dos/town/review-queue.ts Outdated
Comment thread cloudflare-gastown/src/dos/Town.do.ts
jrf0110 added 2 commits March 6, 2026 11:30
…odes, and DAG visualization

Wire convoys end-to-end with feature branches, DAG dependency tracking,
two merge modes, and full UI visualization.

## Convoy Feature Branches
- Convoys create long-lived feature branches (convoy/<slug>/<id>/head)
- Agent branches are siblings under the same prefix (convoy/<slug>/<id>/gt/<agent>/<bead>)
- Sub-beads branch from and merge into the feature branch
- Final landing MR merges feature branch to main when all beads complete
- Always clone from rig default branch (feature branch may not exist on remote yet)

## DAG-Aware Scheduling
- Beads with 'blocks' dependencies only dispatch when all blockers close
- dispatchUnblockedBeads fires when a bead closes, cascading through DAG
- Bead status transitions to in_progress before container start (not after)
- Cycle detection (Kahn's algorithm) rejects circular depends_on graphs
- Guard prevents reverting terminal statuses (closed/failed) to in_progress

## Convoy Merge Modes
- review-then-land (default): refinery reviews each bead and merges into
  feature branch via direct merge; final PR/merge to main at convoy end
- review-and-merge: each bead goes through full review + merge/PR cycle
  independently using the town's configured merge strategy
- Mayor's gt_sling_batch tool accepts merge_mode parameter
- Refinery system prompt includes convoy context for each mode

## Mayor Prompt — depends_on Emphasis
- Dedicated section: 'depends_on — THE MOST IMPORTANT PART OF CONVOY PLANNING'
- Default assumption shifted to 'most beads need depends_on'
- Common patterns: foundation first, API before UI, code before tests
- Bad/good examples with explicit dependency chains

## DAG Visualization
- ConvoyTimeline renders beads in topological wave order (Kahn's algorithm)
- Arrow indicators between dependency waves
- Feature branch name shown in convoy header
- BeadPanel drawer shows convoy membership, blockers, and dependents
- Convoy section constrained to max-h-[40vh] with internal scroll
- min-w-0 chain prevents horizontal page overflow from bead chips

## Data Model
- convoy_metadata: feature_branch, merge_mode columns (with migrations)
- getConvoyDependencyEdges, getConvoyForBead, getConvoyFeatureBranch,
  getConvoyMergeMode query helpers
- tRPC schemas include dependency_edges, feature_branch, merge_mode

## Tests
- 20 integration tests: feature branches, DAG edges, scheduling,
  progress tracking, merge modes, force close, edge cases
- 15 unit tests: branch naming, git ref conflict prevention, cycle detection

Refs: #204, #540
1. review-and-merge convoys now target the rig's default branch (not
   the convoy feature branch), so each bead lands independently
2. Standalone reviews use rig's default_branch instead of hardcoded 'main'
3. review-and-merge convoys auto-close immediately instead of queuing
   a redundant feature-branch landing step
4. Convoy progress only counts beads whose MR reviews are fully resolved,
   preventing premature ready_to_land while reviews are in flight
5. Cycle validation moved before any DB writes so invalid payloads
   don't leave partial convoy state behind
6. updateConvoyProgress guards against non-convoy tracks edges (MR beads
   tracking their source) that have no convoy_metadata row
7. BeadPanel fallback objects now carry rig_id from convoy data so
   cross-rig drawer navigation works correctly

Refs: #204, #540
@jrf0110 jrf0110 force-pushed the 540-wire-convoys-e2e branch from 7ba8671 to 0ae89a5 Compare March 6, 2026 17:30
…back

- Remove non-null assertions in cycle detection (use ?? fallback instead)
- Remove unused BeadDependencyRecord import
- Remove stale eslint-disable directive in schemas.ts
- closeOrphanedReviewBeads: only close 'open' MR beads (not 'in_progress'
  ones that are actively being polled by pollPendingPRs)
- Add fallback slug 'convoy' when title sanitizes to empty string,
  preventing invalid git refs with empty path segments

Refs: #204, #540
@jrf0110 jrf0110 force-pushed the 540-wire-convoys-e2e branch from 5d15385 to 51c687d Compare March 6, 2026 17:41
Comment on lines +413 to +415
* Only affects beads with a pr_url (excluded by recoverStuckReviews) that
* are stale (>30 min) and whose agent is idle/dead/missing.
*/
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not review feedback, just me trying to keep up. If your setup to require a human to merge the PR, does this mean the pr has to be merged within 30 minutes too ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Desired behavior is that an open PR keeps the review bead open until closed/merged. If that's not happening, I will fix in a follow-up!

INNER JOIN ${beads} ON ${bead_dependencies.depends_on_bead_id} = ${beads.bead_id}
WHERE ${bead_dependencies.bead_id} = ?
AND ${bead_dependencies.dependency_type} = 'blocks'
AND ${beads.status} NOT IN ('closed', 'failed')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same deal as above, not review feedback.

So a blocker is unresolved if status is neither closed or failed. So failed is not a blocker? Robot says yes and that this is fine:

  The important distinction is:

  - closed: prerequisite satisfied
  - failed: prerequisite unsatisfied, but terminal

  For convoy progress, it can be reasonable to count failed as terminal.

this.sql,
/* sql */ `
UPDATE ${convoy_metadata}
SET ${convoy_metadata.columns.closed_beads} = ${convoy_metadata.columns.total_beads},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Force-closing bypasses outstanding review beads

closeConvoy() only iterates the convoy's tracked source beads, but merge-request beads are attached one level lower (MR -> source bead). This manual closed_beads = total_beads update can therefore mark the convoy as landed while some review beads are still open/in_progress, and the alarm will keep processing those stale review entries afterward. Either close/fail the child review beads here too, or derive the final convoy state from the same "no pending MR children" rule used in updateConvoyProgress().

@jrf0110 jrf0110 merged commit 1f99f8f into main Mar 6, 2026
13 checks passed
@jrf0110 jrf0110 deleted the 540-wire-convoys-e2e branch March 6, 2026 18:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Wire convoys end-to-end: batch sling, Mayor tools, and dead code cleanup

2 participants