feat(gastown): add bead dependency editing#1231
Merged
Conversation
…tions Add two new exported functions to beads.ts for managing bead dependency edges after creation. Includes self-reference validation, cycle detection via DFS for 'blocks' dependencies, and protection against removing system-managed 'tracks' edges. Also adds 'dependency_added' and 'dependency_removed' to the BeadEventType enum for audit trail support. Closes #1100 (partial — layer 1/7)
…thods Wire the core dependency functions into TownDO with proper initialization, event logging, and alarm arming for unblocked bead dispatch. Closes #1100 (partial — layer 2/7)
Add bead-dependencies.handler.ts with POST and DELETE handlers for adding and removing bead dependencies. Register routes in gastown.worker.ts under user auth middleware. POST /api/towns/:townId/rigs/:rigId/beads/:beadId/dependencies DELETE /api/towns/:townId/rigs/:rigId/beads/:beadId/dependencies/:dependsOnBeadId Closes #1100 (partial — layer 3/7)
…ependency Wire the mayor tool client to the new bead dependency HTTP endpoints. Closes #1100 (partial — layer 4/7)
…tools and gt_sling depends_on Add two new mayor tools for managing bead dependencies after creation: - gt_bead_add_dependency: add a blocks/parent-child dependency edge - gt_bead_remove_dependency: remove a dependency edge (auto-dispatches if unblocked) Also add optional depends_on parameter to gt_sling for declaring dependencies at sling time. Closes #1100 (partial — layers 5-6/7)
Add gt_bead_add_dependency and gt_bead_remove_dependency to the Surgical Editing section of the mayor's system prompt. Update gt_sling description to mention the new optional depends_on parameter. Closes #1100 (partial — layer 7/7)
- Use literal union type instead of z.infer<typeof DependencyType> to satisfy consistent-type-imports rule - Replace non-null assertion with explicit undefined check in DFS loop - Fix prettier formatting in gastown.worker.ts
…ency handlers - removeBeadDependency now correctly checks if beadId itself is unblocked (using hasUnresolvedBlockers) rather than calling getNewlyUnblockedBeads with dependsOnBeadId, which was designed for the bead-close path - handleAddBeadDependency and handleRemoveBeadDependency now validate that beadId belongs to the rigId in the URL path, matching handleDeleteBead
Contributor
Code Review SummaryStatus: 2 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)WARNING
Fix these issues in Kilo Cloud Other Observations (not in diff)Issues found in unchanged code that cannot receive inline comments: WARNING
Files Reviewed (2 files)
Reviewed by gpt-5.4-20260305 · 673,080 tokens |
- Fix Comment 1: Add dependsOn parameter to slingBead() so dependency rows are inserted atomically before dispatch alarm is armed, preventing a new bead from being dispatched before its blockers are registered. - Fix Comment 2: Restrict AddDependencyBody to EditableDependencyType (blocks, parent-child) so callers cannot create system-managed 'tracks' edges via the public API. - Fix Comment 3: Validate that depends_on_bead_id belongs to the same rig as the primary bead in handleAddBeadDependency, preventing cross-rig dependency injection. - Fix Comment 4: Same cross-rig validation for dependsOnBeadId in handleRemoveBeadDependency.
…heck Mirror the slingConvoy() pattern: only call dispatchAgent() when the new bead has no unresolved blockers. Previously, slingBead() with depends_on would insert the dependency rows atomically but then immediately dispatch the agent unconditionally, allowing blocked beads to start work before their prerequisites close.
Add convoy_id support to gt_sling and gt_bead_update mayor tools so the mayor can sling new beads directly into a convoy and move existing beads in/out of convoys. Introduces addBeadToConvoy/removeBeadFromConvoy helpers that manage the tracks dependency, metadata, and convoy counters atomically.
- Validate convoy_id before creating the bead in slingBead() to prevent orphan rows when the convoy doesn't exist or isn't type=convoy. - addBeadToConvoy now recounts closed_beads (via shared recountConvoyClosedBeads helper) so adding an already-closed bead is reflected correctly, and clears the ready_to_land flag when adding an open bead to a convoy that was already marked complete. - removeBeadFromConvoy uses recountConvoyClosedBeads instead of a naive decrement, matching updateConvoyProgress's MR-aware counting logic.
| `, | ||
| [convoyId] | ||
| ); | ||
| recountConvoyClosedBeads(sql, convoyId); |
Contributor
There was a problem hiding this comment.
WARNING: Removing the last incomplete bead can leave the convoy stuck open
recountConvoyClosedBeads() fixes the counters, but this path never re-runs the landing logic. If a convoy is 1/2 complete and the remaining open bead is removed, the counts become 1/1 here, yet ready_to_land is never set (and review-and-merge convoys never auto-close), so the convoy stays open forever.
pandemicsyn
approved these changes
Mar 18, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements bead dependency management (issue #1100) across the full stack:
beads.ts):addBeadDependency()with self-reference validation and DFS cycle detection for'blocks'edges (ON CONFLICT DO NOTHING for idempotent adds);removeBeadDependency()that protects'tracks'edges from deletion.addBeadDependency()andremoveBeadDependency()with initialization, bead event logging, and alarm arming to dispatch newly unblocked beads.bead-dependencies.handler.ts): POST and DELETE routes under the existing rig auth middleware, withrigIdownership validation matching the pattern inhandleDeleteBead.addBeadDependency()andremoveBeadDependency()onMayorGastownClient.gt_bead_add_dependencyandgt_bead_remove_dependency, plus optionaldepends_onarray ongt_sling.gt_slingdescription.Two bugs fixed during review:
removeBeadDependencyalarm logic: was callinggetNewlyUnblockedBeads(dependsOnBeadId)(wrong — that function is for the bead-close path). Fixed to callhasUnresolvedBlockers(beadId)directly, sincebeadIdis the bead whose blocking situation just changed.rigIdvalidation in both HTTP handlers: addedgetBeadAsync+bead.rig_id !== params.rigIdcheck, consistent withhandleDeleteBead.Closes #1100
Verification
pnpm typecheck— passedpnpm prettier --check— passedpnpm eslinton changed files — passedVisual Changes
N/A
Reviewer Notes
The cycle detection DFS is correct: the adjacency map encodes
bead_id → depends_on_bead_id(A depends on B = edge A→B). Starting fromdependsOnBeadIdand traversing its owndepends_onedges — if we reachbeadId, the proposed new edge would close a cycle. Self-reference is caught separately before the DFS.The
gt_bead_add_dependencytool intentionally exposes only'blocks'and'parent-child'dependency types (not'tracks'), sincetracksedges are system-managed by the convoy machinery.