feat(gastown): add mayor edit tools and manual editing UI#1076
Conversation
…oy editing Adds 7 new mayor edit tools (gt_bead_update, gt_bead_reassign, gt_agent_reset, gt_convoy_close, gt_bead_delete, gt_escalation_acknowledge, gt_bead_fail) with corresponding PATCH endpoints. Adds dashboard UI edit controls (status dropdowns, editable fields, unhook/reset buttons). Updates mayor system prompt with Surgical Editing section documenting the new capabilities. Closes #996
… API Expand the PATCH /beads/:id endpoint to accept type, rig_id, and parent_bead_id in addition to existing fields. Add corresponding updateBeadFields support. Dashboard UI gains: labels input (comma- separated), metadata textarea (JSON), type/rig/parent dropdowns, in_review status option, Load button to populate the form from an existing bead, and an Edit button in the beads table for one-click editing. Also adds priority column to the beads table and failed badge style.
Code Review SummaryStatus: 2 Issues Found | Recommendation: Address before merge Overview
Fix these issues in Kilo Cloud Issue Details (click to expand)WARNING
Other Observations (not in diff)None. Files Reviewed (12 files)
Reviewed by gpt-5.4-20260305 · 2,413,594 tokens |
Add updateBead tRPC mutation to the gastown worker router accepting all editable bead fields (title, body, status, priority, type, labels, metadata, rig_id, parent_bead_id). The BeadPanel drawer now has a pencil icon toggle that switches to edit mode: title becomes an input, status/ type/priority become selects, and labels/body/metadata/rig_id/parent fields appear as additional inputs. Save computes a diff against the original bead and only sends changed fields. Regenerated router.d.ts type declarations.
The Save button was at the bottom of the edit fields section, making it hard to find when only changing status/type/priority. Moved it inline with the status/type/priority selects so it's always visible at the top of the edit form. Removed the duplicate Save/Cancel bar from the bottom.
…ting
- Remove bead type from editable fields to prevent inconsistent state
(type changes require satellite metadata tables that aren't created)
- Add rig ownership check in handleMayorBeadUpdate matching the pattern
in handleMayorBeadDelete — rejects updates to beads in other rigs
- Fix reassign atomicity: hook new agent before unhooking old one so
failures don't leave beads unassigned
- Fix reassign unhook safety: verify old agent is still hooked to this
specific bead before unhooking (stale assignee_agent_bead_id)
- Fix reassign response: return updated bead instead of { reassigned }
to match client contract
- Fix ESLint: void floating promises, destructure useMutation result
- Fix Prettier formatting in 5 files
- Regenerate router.d.ts type declarations
…Bead - Reassign: validate target agent belongs to the specified rig and verify bead belongs to the rig before allowing reassignment - Agent reset: verify agent belongs to the specified rig before allowing reset (prevents cross-rig reset via any valid rig ID) - tRPC updateBead: verify bead belongs to the specified rig before allowing mutation (mirrors the HTTP handler fix)
- Fix pre-existing type errors: use sql.exec() directly for dynamic SET clause queries where query() can't statically verify param count - Clear closed_at when reopening a previously-closed bead (status transition away from 'closed' now nulls out the stale timestamp) - Add in_review to gt_bead_update tool status enum and client type - Remove Type dropdown from debug dashboard (API no longer accepts it)
- Dashboard unhook: use mayorApi() with mayor route instead of api() with the rig route, so the bearer token is sent correctly - Dashboard bead table: use b.bead_id (the actual PK) instead of b.id (undefined), and b.assignee_agent_bead_id for the assignee column - BeadPanel metadata: reject save with inline error when metadata JSON is invalid instead of silently dropping the field. Error clears on edit and displays with red border on the textarea.
| labels: z.array(z.string()).optional(), | ||
| metadata: z.record(z.string(), z.unknown()).optional(), | ||
| rig_id: z.string().min(1).nullable().optional(), | ||
| parent_bead_id: z.string().min(1).nullable().optional(), |
There was a problem hiding this comment.
WARNING: parent_bead_id can create an undeletable cycle
This accepts any non-empty string, including the bead's own ID. deleteBead() recursively walks child beads by parent_bead_id with no cycle detection, so a self-reference or two-bead loop will recurse forever and make cleanup fail.
| const rId = el('editAgentRigId').value.trim(); | ||
| const aId = el('editAgentId').value.trim(); | ||
| if (!tId || !rId || !aId) { toast('Fill in Town ID, Rig ID, and Agent ID', true); return; } | ||
| const r = await mayorApi('DELETE', '/api/mayor/' + tId + '/tools/rigs/' + rId + '/agents/' + aId + '/hook'); |
There was a problem hiding this comment.
WARNING: This mayor endpoint is not registered
gastown.worker.ts adds a mayor reset route, but there is no matching DELETE /api/mayor/:townId/tools/rigs/:rigId/agents/:agentId/hook route or handler. Clicking the new Unhook button will 404 every time.
Summary
gt_bead_update(status, title, body, priority),gt_bead_reassign,gt_agent_reset,gt_convoy_close,gt_bead_delete,gt_escalation_acknowledge, andgt_bead_failgastown.worker.tsfor bead fields, agent status, and convoy metadata with user auth (not agent-scoped)updateBead,resetAgent, andupdateConvoymethods on TownDOCloses #996
Verification
origin/mainVisual Changes
Adds an edit button to bead drawer:
Reviewer Notes
gt_bead_deleteis a hard delete — consider whether soft delete (archive) would be safer for production