feat(admin): add user-scoped Gastown admin panel#1052
Conversation
jrf0110
left a comment
There was a problem hiding this comment.
Self-Review
Performed a thorough review of the full diff. Found several issues across code quality, security, and error handling categories.
High priority:
ascasts throughout (violates project coding rules) — ~15 occurrences across gastown-router.ts, BeadsTab, ConfigTab, AgentInspectorDashboard, BeadInspectorDashboard- Unvalidated
pr_urlrendered as href in ReviewQueueTab (potential XSS) - Missing mutation error handlers for destructive admin actions (force reset/close/fail/restart)
- Silent error swallowing in
gastownTrpcGetmasks service outages
Medium priority:
- Query stubs silently return
[]while mutation stubs throwMETHOD_NOT_SUPPORTED(inconsistent) - Missing error states in ContainerTab for health/events/config queries
- Unsafe metadata access in BeadInspectorDashboard (missing
Array.isArrayguard)
Low priority:
- Duplicated
STATUS_COLORSacross BeadsTab and ReviewQueueTab - Magic numbers (token expiry, truncation limit) should be named constants
- Agent events returned as
z.unknown()forces excessiveascasting downstream
src/app/admin/gastown/towns/[townId]/agents/[agentId]/AgentInspectorDashboard.tsx
Outdated
Show resolved
Hide resolved
src/app/admin/gastown/towns/[townId]/beads/[beadId]/BeadInspectorDashboard.tsx
Outdated
Show resolved
Hide resolved
src/app/admin/gastown/towns/[townId]/beads/[beadId]/BeadInspectorDashboard.tsx
Outdated
Show resolved
Hide resolved
Code Review SummaryStatus: 2 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)WARNING
SUGGESTION
Other Observations (not in diff)Issues found in unchanged code that cannot receive inline comments: N/A Files Reviewed (29 files)
Reviewed by gpt-5.4-20260305 · 2,725,304 tokens |
2038404 to
848f8fb
Compare
2eb9fe3 to
9f5614d
Compare
src/app/admin/gastown/towns/[townId]/beads/[beadId]/BeadInspectorDashboard.tsx
Show resolved
Hide resolved
cb7d2f6 to
69a5d64
Compare
| if (metaRows.length > 0) return beadId; | ||
|
|
||
| return null; | ||
| if (rows.length === 0) return null; |
There was a problem hiding this comment.
WARNING: Convoy self-lookups now drop landing MR context
processConvoyLandings() submits the final landing review with bead_id = convoyId. Before this refactor getConvoyForBead() handled that case by returning beadId, but the new early return null treats convoy beads as standalone work. Landing review creation/review and any other callers that pass a convoy bead directly now lose convoy-specific routing and metadata.
…, beads, and agents Add a comprehensive admin panel for diagnosing and intervening in Gastown user issues. Includes town inspector with tabs for beads, agents, review queue, container, config, and events. Adds bead and agent inspector deep-dive views, an audit log for admin interventions, and tRPC endpoints exposing TownDO state to the admin dashboard. Closes #897
- Replace `as GastownApiResponse` casts with Zod schema parsing - Add console.error logging for non-404 failures in gastownTrpcGet - Fix XSS risk: validate pr_url scheme before rendering as href - Remove `as` casts in BeadsTab, ConfigTab, AgentInspector, BeadInspector using runtime guards and safe extraction helpers - Add onError toast handlers to all destructive mutations across AgentsTab, BeadsTab, ContainerTab, ReviewQueueTab, BeadInspector, AgentInspector - Add missing error state for health query in ContainerTab - Use Array.isArray guard for metadata.depends_on in BeadInspector
…x config clearing - Restore updateConvoyProgress export and explicit call in completeReviewWithResult, since closeBead short-circuits when completeReview already set status to 'closed' via direct SQL - Fix config field clearing: use null instead of undefined so the value is included in JSON serialization; update schema to accept nullable model fields - Add comment noting dependency graph reads from metadata as temporary fallback until bead_dependencies endpoint is available
…ting Add admin-only tRPC procedures on the Gastown worker that bypass per-user ownership checks: - adminListBeads: town-wide bead listing (no rigId required) - adminListAgents: town-wide agent listing - adminForceRestartContainer: destroys the container (auto-restarts on next request) - adminForceResetAgent: unhooks bead and resets agent to idle - adminForceCloseBead: force-closes a bead - adminForceFailBead: force-fails a bead Wire the admin gastown-router.ts to proxy these via gastownTrpcMutate (new POST helper). forceRetryReview and forceRefreshCredentials remain stubs as they require more complex worker-side logic. Update UI empty states to remove stale bead-0 references.
- Remove unused 'userId' prop from TownRow component - Remove unnecessary type assertion on bead.status (already typed by Zod enum) - Remove unused GastownApiResponse type alias - Format EventsTab, AuditLogDashboard, and audit page with Prettier
…lth/events - Add 'lightweight' flag to StartAgentRequest so triage agents dispatched as 'polecat' skip repo clone and use a git-init-only workspace (restores pre-refactor behavior) - Add adminGetAlarmStatus and adminGetTownEvents worker tRPC routes that bypass verifyTownOwnership, fixing empty health/events for towns not owned by the admin user - Pass beadId through to adminGetTownEvents so the bead inspector gets filtered events instead of only town-wide latest - Disable Force Retry button in ReviewQueueTab (stub not yet implemented on the worker)
… flag Restore agent-runner.ts and container types.ts to match main, removing the unrelated createLightweightWorkspace inlining and triage role deletion from the fork's feature branch. The only additions vs main are: - lightweight field on StartAgentRequest (types.ts) - request.lightweight check in runAgent (agent-runner.ts) This keeps the triage role's existing lightweight workspace path intact while also supporting the new lightweight flag for polecat agents dispatched with lightweight: true.
- Wrap AlertDialogCancel with DialogClose so clicking Cancel actually closes the confirmation modal - Add adminGetBead worker tRPC route and getBead admin procedure for fetching a single bead by ID (not limited by pagination) - BeadInspectorDashboard now uses getBead for primary data, listBeads only for the dependency graph - Update stale comment about server-side beadId filtering
69a5d64 to
3638aa9
Compare
| const BeadRecord = z.object({ | ||
| bead_id: z.string(), | ||
| type: z.enum(['issue', 'message', 'escalation', 'merge_request', 'convoy', 'molecule', 'agent']), | ||
| status: z.enum(['open', 'in_progress', 'closed', 'failed']), |
There was a problem hiding this comment.
WARNING: Admin bead parsing rejects a valid Gastown status
The worker bead schema includes in_review, and the normal polecat/refinery flow moves source beads into that state. Because this admin-side Zod enum omits it, gastownTrpcGet() will safe-parse the whole payload as null whenever listBeads() or getBead() encounters an in-review bead, so active towns can render as an empty bead list and direct bead lookups can disappear.
|
|
||
| const filtered = events.filter(e => { | ||
| if (beadFilter && !e.bead_id.toLowerCase().includes(beadFilter.toLowerCase())) return false; | ||
| if (agentFilter && e.agent_id && !e.agent_id.toLowerCase().includes(agentFilter.toLowerCase())) |
There was a problem hiding this comment.
SUGGESTION: Agent filtering still keeps agent-less events
When agentFilter is set and e.agent_id is null, this condition is skipped and the predicate falls through to return true, so system/town-level events still appear in a filtered-by-agent view. Treating a missing agent_id as a non-match here will make the filter results accurate.
Summary
Adds a user-scoped Gastown admin panel for diagnosing and intervening in user issues across towns, beads, and agents. The architecture follows a proxy pattern: the Next.js admin dashboard calls tRPC endpoints that authenticate to the Gastown Cloudflare Worker via short-lived admin JWTs + CF Access service tokens.
Key additions:
UserAdminGastown.tsx), showing all towns with traffic-light health indicators (green/yellow/red based on dead agents, stalled work, failed beads) and rigs with integration status/admin/gastown/towns/[townId]) with 6 tabs: Beads, Agents, Review Queue, Container, Config, Events — each filterable/sortable with admin intervention buttons (force-close, force-fail, force-reset, force-restart)gastown-router.ts) with working read endpoints for towns, rigs, beads, agents, config, convoys, and health — plus stubbed write endpoints awaiting bead-0 admin-bypass routes on the Gastown workerpolecatrole and real repo workspaces instead of the former lightweight/fake workspace path, eliminating thecreateLightweightWorkspaceabstractionCloses #897
Verification
pnpm typecheck— not yet run (new files depend on Gastown worker types that may not resolve in the monorepo typecheck without the worker running)Visual Changes
Reviewer Notes
gastown-router.tsare stubs returning empty arrays or throwingMETHOD_NOT_SUPPORTED. These are scaffolded ahead of bead-0 admin-bypass endpoints on the Gastown worker. The frontend is wired up to handle empty/error states gracefully.alert-dialog.tsxshadcn component was added as a new UI primitive for confirmation dialogs on destructive admin actions.