feat: add manual human acceptance gate#44
Conversation
- Add generic manual workflow node with decision-specific routing - Add decision API, waiting_for_human status, and run-view UI - Add validation, SSE event support, and acceptance coverage Closes #43
Acceptance evidence collected by HermesVerdict: ready for review. I verified the implementation after Claude Code fixed the independent-review blockers. Commands run
Acceptance coverage
Independent review blockers resolvedAn independent review initially blocked acceptance on four issues:
Known limitationRestart-persistent pending human gates are not implemented in this PR. If the server restarts while a gate is parked, existing crash recovery fails the run record rather than silently losing the pending gate. Full rehydration of parked gate promises should be handled as a separate runtime-persistence feature. |
CI evidence updateGitHub checks for this PR are now green:
This is in addition to the local evidence already posted above:
|
Human-gate runtime smoke evidenceYuki was right that CI/typecheck alone is not enough evidence for this feature. I ran a real local runtime smoke probe on this PR branch and verified the manual gate end-to-end. Runtime setup
Smoke workflow createdWorkflow id: Shape: Manual gate config used: {
"brief": "Smoke proof: confirm manual gate pauses and resumes through accepted.",
"reviewerHint": "Hermes smoke probe",
"decisions": [
{ "key": "accepted", "label": "Accept smoke" },
{ "key": "needs_changes", "label": "Needs changes" },
{ "key": "rejected", "label": "Reject smoke" }
]
}Workflow save proof:
Waiting gate proofStarted run:
Then polled:
Observed: {
"status": "waiting_for_human",
"waitingFor": {
"kind": "human",
"nodeId": "manual-1",
"brief": "Smoke proof: confirm manual gate pauses and resumes through accepted.",
"reviewerHint": "Hermes smoke probe",
"decisions": ["accepted", "needs_changes", "rejected"]
},
"pendingHumanDecisions": [
{ "nodeId": "manual-1" }
]
}Also verified:
Human decision / resume proofSubmitted decision: POST /api/runs/manual-gate-smoke-20260524/eb9c5a53-f9ef-453b-9a89-a244d53ed6cc/decisionPayload: {
"nodeId": "manual-1",
"decision": "accepted",
"comment": "Hermes smoke probe accepted the manual gate."
}Response proof: {
"ok": true,
"decision": "accepted",
"decidedBy": "open",
"comment": "Hermes smoke probe accepted the manual gate."
}Final run proof:
Runtime smoke verdict✅ The human acceptance gate works in a real runtime flow:
|
Summary
manual/ human acceptance workflow node withaccepted,needs_changes, andrejecteddecision handles.waiting_for_humanwithout marking them failed, exposes reviewer brief/decision metadata, and resumes through the selected handle.Closes #43
Acceptance evidence
Evidence was collected locally before opening this PR and is also posted as a PR comment.
bun test→ 1286 pass / 1 skip / 0 fail, 3109 expect calls across 119 filesbun run typecheck→ clean (tsc --noEmit)bun run build→ success;/api/runs/[workflowId]/[runId]/decisioncompilednode_waiting_for_human/node_human_decidedto SSE allowlistrejectedwhentimeoutMs > 0waiting_for_humanfrompendingHumanDecisions()for multi-gate runsNotes