Skip to content

fix(space): gate banners only show after activation; task transitions to review when gate blocks#1558

Merged
lsm merged 5 commits intodevfrom
space/fix-gate-banners-and-task-state-only-show-when-gate-is
Apr 21, 2026
Merged

fix(space): gate banners only show after activation; task transitions to review when gate blocks#1558
lsm merged 5 commits intodevfrom
space/fix-gate-banners-and-task-state-only-show-when-gate-is

Conversation

@lsm
Copy link
Copy Markdown
Owner

@lsm lsm commented Apr 21, 2026

Fixes two related gate UX bugs:

  1. Premature gate bannerPendingGateBanner was iterating all gates in the workflow definition and calling evaluateGateStatus even when no gate data existed in the DB. For external-approval gates (empty writers), this returned waiting_human before the agent had even written anything. The fix: skip gates with no entry in gateDataMap.

  2. Task stays in_progress instead of review — when an agent writes gate data but a human hasn't approved yet, the task should surface in the "Needs Attention" group. The fix adds an onGatePendingApproval callback to ChannelRouter that fires for external-approval gates when they're still blocked, and SpaceRuntimeService.handleGatePendingApproval transitions the canonical task to review (with pendingCheckpointType: 'gate'). The gate's eventual approval re-activates the node and clears the checkpoint type back to in_progress.

Files changed:

  • packages/web/src/components/space/PendingGateBanner.tsx — skip ungated entries
  • packages/daemon/src/lib/space/runtime/channel-router.tsonGatePendingApproval config + wiring
  • packages/daemon/src/lib/space/runtime/space-runtime-service.tshandleGatePendingApproval method + router wiring
  • packages/daemon/src/lib/space/runtime/task-agent-manager.ts — router wiring
  • packages/daemon/src/lib/space/runtime/space-runtime.ts — handle review+gatein_progress on agent spawn
  • Tests updated and new onGatePendingApproval callback tests added

… to review when gate blocks

- PendingGateBanner: skip gates with no data in gateDataMap — an external-approval
  gate with no DB entry has never been triggered, so the banner must not appear
- ChannelRouter: add onGatePendingApproval callback; fires when gate data is written
  but the gate is still blocked, specifically for external-approval gates (approved
  field with empty writers array)
- SpaceRuntimeService: add handleGatePendingApproval which transitions the canonical
  task from in_progress/open → review (pendingCheckpointType: 'gate') and emits
  space.task.updated; wire onGatePendingApproval into notifyGateDataChanged router
- TaskAgentManager: wire onGatePendingApproval into the node-agent ChannelRouter
- SpaceRuntime: when spawning node agents, treat review+gate tasks like open tasks —
  transition to in_progress and clear pendingCheckpointType so the gate re-activating
  the agent resets the task state correctly
@lsm
Copy link
Copy Markdown
Owner Author

lsm commented Apr 21, 2026

Review: gate banners only show after activation; task transitions to review when gate blocks

Overall: approve with one minor request — the two core fixes (premature banner + task status transition) are correct and well-implemented. Tests are thorough. One small gap in TaskStatusActions should be addressed.

What's done well

  1. Banner fix is minimal and correct — the gateDataMap.has(gate.id) guard in PendingGateBanner.tsx is the right fix. Gates with no DB data are skipped entirely, preventing the undefinedwaiting_human false positive in evaluateGateStatus.

  2. onGatePendingApproval callback design is clean — fires only in onGateDataChanged (the right place, not on every message delivery), only for external-approval gates (empty writers on approved field), and is correctly scoped to the channel-router config. The fire-and-forget with .catch() logging is appropriate.

  3. handleGatePendingApproval is idempotent — if the task is already review from a previous gate evaluation, the method returns early (no in_progress or open task found). No double-transition risk.

  4. space-runtime.ts transition back — the review + pendingCheckpointType === 'gate'in_progress guard with pendingCheckpointType: null cleanup is correct and properly scoped (won't accidentally transition completion-action tasks).

  5. Both wiring points coverednotifyGateDataChanged and task-agent-manager.ts both pass the callback.

  6. Tests are comprehensive — 3 new daemon tests covering fires/not-fires/not-fires-for-non-external; 1 new web test for no-data-skip; existing tests properly updated with real gate data instead of empty fetch results.

Tests: all green

  • Web: 7127 passed across 245 files ✅
  • Daemon channel-router: 62 passed ✅

One minor request

See the inline comment on TaskStatusActions.tsx — when pendingCheckpointType === 'gate', the generic Approve/Cancel buttons are still visible. Clicking them would bypass the gate approval workflow and directly transition the task to done/cancelled. The filter should include 'gate' alongside 'completion_action' and 'task_completion' so the dedicated PendingGateBanner owns the approval UX.

@lsm
Copy link
Copy Markdown
Owner Author

lsm commented Apr 21, 2026

💡 Suggested fix: TaskStatusActions.tsx — hide generic Approve/Cancel for gate-pending tasks

File: packages/web/src/components/space/TaskStatusActions.tsx, line 99

When the task is in review status because a gate is blocking (pendingCheckpointType === 'gate'), the generic Approve (review→done) and Cancel (review→cancelled) buttons are still visible. Clicking "Approve" would directly set the task to done via spaceStore.updateTask(task.id, { status: 'done' }), bypassing the gate approval workflow — the gate stays closed but the task is marked done.

This should follow the same pattern as completion_action and task_completion:

-  pendingCheckpointType === 'completion_action' || pendingCheckpointType === 'task_completion'
+  pendingCheckpointType === 'completion_action' || pendingCheckpointType === 'task_completion' || pendingCheckpointType === 'gate'

This is a one-line change that ensures the dedicated PendingGateBanner owns the gate approval UX instead of leaking the generic status buttons.

…nding

When pendingCheckpointType === 'gate', the PendingGateBanner owns the
approval UX. The generic Approve (review→done) and Cancel buttons must
be hidden to prevent bypassing the gate — clicking Approve would mark the
task done without opening the gate.

Follows the same filter pattern already used for completion_action and
task_completion checkpoints.
@lsm
Copy link
Copy Markdown
Owner Author

lsm commented Apr 21, 2026

Fixed in 789d44b. Added pendingCheckpointType === 'gate' to the filter alongside completion_action and task_completion. Also updated the test that previously asserted gate kept the buttons visible — it now verifies both that null keeps them (unchanged behavior) and that 'gate' hides them (new behavior).

@lsm
Copy link
Copy Markdown
Owner Author

lsm commented Apr 21, 2026

Review round 2: approved ✅

The TaskStatusActions.tsx fix is exactly right — pendingCheckpointType === 'gate' now filters out the generic Approve/Cancel buttons (line 103), with a clear comment explaining why. The test was properly split into two cases:

  • null → buttons visible ✅
  • 'gate' → buttons hidden, non-approval transitions still visible ✅

All 43 tests pass (29 TaskStatusActions + 14 PendingGateBanner). Ready to merge.

@lsm lsm enabled auto-merge (squash) April 21, 2026 03:12
@lsm lsm merged commit 725c58f into dev Apr 21, 2026
44 checks passed
@lsm
Copy link
Copy Markdown
Owner Author

lsm commented Apr 21, 2026

Review round 2: approved ✅

The TaskStatusActions.tsx fix is exactly right — pendingCheckpointType === 'gate' now filters out the generic Approve/Cancel buttons (line 103), with a clear comment explaining why. The test was properly split into two cases:

  • null → buttons visible ✅
  • 'gate' → buttons hidden, non-approval transitions still visible ✅

All 43 tests pass (29 TaskStatusActions + 14 PendingGateBanner). Ready to merge.

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.

1 participant