II.
Page JSON
Structured · livepage:docs-user-guide-features-breakpoints
Breakpoints: Human-in-the-Loop Approval json
Inspect the normalized record payload exactly as the atlas UI reads it.
{
"id": "page:docs-user-guide-features-breakpoints",
"_kind": "Page",
"_file": "wiki/docs/user-guide/features/breakpoints.md",
"_cluster": "wiki",
"attributes": {
"nodeKind": "Page",
"sourcePath": "docs/user-guide/features/breakpoints.md",
"sourceKind": "repo-docs",
"title": "Breakpoints: Human-in-the-Loop Approval",
"displayName": "Breakpoints: Human-in-the-Loop Approval",
"slug": "docs/user-guide/features/breakpoints",
"articlePath": "wiki/docs/user-guide/features/breakpoints.md",
"article": "\n# Breakpoints: Human-in-the-Loop Approval\n\n**Version:** 3.0\n**Last Updated:** 2026-03-30\n**Category:** Feature Guide\n\n---\n\n## In Plain English\n\n**A breakpoint is a pause button.** When your workflow reaches a breakpoint, it stops and waits for you to say \"OK, continue.\"\n\n**Why does this matter?**\n- The AI writes a plan → pauses → you review it → approve → then it builds\n- The AI makes changes → pauses → you check the changes → approve → then it deploys\n- You stay in control of important decisions\n\n**How it works:** When a breakpoint is reached, Claude asks you directly in the chat using the `AskUserQuestion` tool. You respond, and the workflow continues.\n\n**No setup required!** Breakpoints work out of the box in Claude Code sessions.\n\n---\n\n## Overview\n\nBreakpoints provide human-in-the-loop approval gates within Babysitter workflows. Use `ctx.breakpoint()` to pause automated execution at critical decision points, present context to the user, and make informed approvals before proceeding. The call returns a `BreakpointResult` object containing the reviewer's decision and any feedback provided.\n\n### How Breakpoints Work\n\nWhen running Babysitter within a Claude Code session, breakpoints are handled **directly in the chat**:\n\n1. Process reaches a `ctx.breakpoint()` call\n2. Claude uses the `AskUserQuestion` tool to present the question\n3. You respond in the chat (approve, reject, or provide feedback)\n4. Claude posts your response and the process continues\n5. The call resolves with a `BreakpointResult` containing your decision\n\n**Key benefits:**\n- No external services required\n- Immediate, real-time interaction\n- Context preserved in conversation\n- Simple API - just call `ctx.breakpoint()`\n\n### Why Use Breakpoints\n\n- **Production Safety**: Require human approval before deploying to production environments\n- **Quality Gates**: Review generated plans, specifications, or code before implementation\n- **Compliance**: Create audit trails of human approvals for regulated environments\n- **Risk Mitigation**: Pause execution when automated decisions carry significant risk\n- **Informed Decisions**: Present context files so reviewers have all necessary information\n\n---\n\n## Use Cases and Scenarios\n\n### Scenario 1: Plan Approval Before Implementation\n\nPause after generating an implementation plan to ensure the approach is correct.\n\n```javascript\nexport async function process(inputs, ctx) {\n // Generate implementation plan\n const plan = await ctx.task(generatePlanTask, { feature: inputs.feature });\n\n // Request human approval\n const review = await ctx.breakpoint({\n question: 'Review the implementation plan. Approve to proceed?',\n title: 'Plan Review',\n context: {\n runId: ctx.runId,\n files: [\n { path: 'artifacts/plan.md', format: 'markdown' }\n ]\n }\n });\n\n if (!review.approved) {\n return { success: false, reason: review.feedback };\n }\n\n // Continue only after approval\n const result = await ctx.task(implementTask, { plan });\n return result;\n}\n```\n\n### Scenario 2: Pre-Deployment Approval\n\nRequire sign-off before deploying changes to production.\n\n```javascript\nawait ctx.breakpoint({\n question: 'Deploy to production?',\n title: 'Production Deployment',\n context: {\n runId: ctx.runId,\n files: [\n { path: 'artifacts/final-report.md', format: 'markdown' },\n { path: 'artifacts/coverage-report.html', format: 'html' },\n { path: 'artifacts/quality-score.json', format: 'code', language: 'json' }\n ]\n }\n});\n```\n\n### Scenario 3: Quality Score Review\n\nAllow humans to review quality convergence results and decide whether to continue iteration.\n\n```javascript\nif (qualityScore < targetQuality && iteration < maxIterations) {\n await ctx.breakpoint({\n question: `Iteration ${iteration} complete. Quality: ${qualityScore}/${targetQuality}. Continue to iteration ${iteration + 1}?`,\n title: `Iteration ${iteration} Review`,\n context: {\n runId: ctx.runId,\n files: [\n { path: `artifacts/iteration-${iteration}-report.md`, format: 'markdown' }\n ]\n }\n });\n}\n```\n\n---\n\n## Using Breakpoints\n\n### Basic Usage\n\nAdd breakpoints to your process definition using `ctx.breakpoint()`:\n\n**Simple breakpoint:**\n\n```javascript\nconst result = await ctx.breakpoint({\n question: 'Approve the changes?',\n title: 'Review Required'\n});\n// result.approved — boolean indicating approval\n// result.feedback — optional reviewer feedback\n```\n\n**Breakpoint with rejection handling:**\n\n```javascript\nconst result = await ctx.breakpoint({\n question: 'Approve the implementation plan?',\n title: 'Plan Approval',\n context: {\n runId: ctx.runId,\n files: [\n { path: 'artifacts/plan.md', format: 'markdown' },\n { path: 'code/main.js', format: 'code', language: 'javascript' },\n { path: 'inputs.json', format: 'code', language: 'json' }\n ]\n }\n});\n\nif (!result.approved) {\n // Handle rejection — use result.feedback for reviewer's reasoning\n ctx.log('Plan rejected', { feedback: result.feedback });\n return { success: false, reason: result.feedback };\n}\n```\n\n> **Backward compatibility:** Existing processes that do not capture the return value (`await ctx.breakpoint(...)`) continue to work without changes.\n\n### Interactive Approval Flow\n\nWhen your workflow reaches a breakpoint:\n\n1. Claude presents the question directly in the chat\n2. Context files are displayed for your review\n3. You respond with your decision (approve, reject, or provide feedback)\n4. The workflow continues based on your response\n\n**Example interaction:**\n```\nClaude: The implementation plan is ready. Review the plan below:\n [Plan summary...]\n\n Do you approve this plan to proceed with implementation?\n\nYou: Yes, looks good. Proceed with implementation.\n\nClaude: Plan approved. Proceeding with implementation...\n```\n\n---\n\n## Configuration Options\n\n### BreakpointResult Interface\n\n`ctx.breakpoint()` returns `Promise<BreakpointResult>` with the following fields:\n\n| Field | Type | Always Present | Description |\n|-------|------|----------------|-------------|\n| `approved` | boolean | Yes | Whether the reviewer approved the breakpoint |\n| `response` | string | No | The reviewer's raw response text |\n| `feedback` | string | No | Structured feedback from the reviewer |\n| `option` | string | No | The selected option when multiple choices are presented |\n| `respondedBy` | string | No | Identifier of the person who responded |\n| `allResponses` | array | No | All responses collected (for `collect-all` or `quorum` strategies) |\n| `[key: string]` | unknown | No | Additional custom fields from the resolution |\n\n### Breakpoint Payload Schema\n\n| Field | Type | Required | Description |\n|-------|------|----------|-------------|\n| `question` | string | Yes | The question presented to the reviewer |\n| `title` | string | No | A short title for the breakpoint |\n| `context` | object | No | Additional context for the reviewer |\n| `context.runId` | string | No | The run ID for linking context files |\n| `context.files` | array | No | Array of files to display for review |\n| `expert` | string \\| string[] | No | Domain expert(s) to route to, or `'owner'` for the project owner |\n| `tags` | string[] | No | Categorization tags for the breakpoint |\n| `strategy` | string | No | Resolution strategy: `'single'` (default), `'first-response-wins'`, `'collect-all'`, or `'quorum'` |\n| `previousFeedback` | string | No | Feedback from a prior rejection (used for retry context) |\n| `attempt` | number | No | Current retry attempt number |\n| `breakpointId` | string | No | Canonical identity for cross-run/cross-process matching. Dotted namespace, kebab-case (e.g., `confirm.star-repo`). Auto-derived from title via slugification if not provided. |\n| `autoApproveAfterN` | number | No | Auto-approve after N consecutive approvals for this breakpointId. Default: `-1` (disabled). |\n| `presentAlwaysApprove` | boolean | No | Whether to present an \"Always Approve\" option to the user. Default: `true`. |\n\n### Breakpoint Routing\n\nBreakpoint routing controls who receives the approval request and how responses are collected.\n\n**Expert routing** directs the breakpoint to specific reviewers:\n\n```javascript\nawait ctx.breakpoint({\n question: 'Review the security audit results?',\n title: 'Security Review',\n expert: 'security-team', // Route to a specific expert\n tags: ['security', 'audit'],\n});\n```\n\nRoute to multiple experts:\n\n```javascript\nawait ctx.breakpoint({\n question: 'Approve the architecture changes?',\n title: 'Architecture Review',\n expert: ['tech-lead', 'architect'], // Route to multiple experts\n tags: ['architecture', 'design'],\n});\n```\n\nRoute to the project owner:\n\n```javascript\nawait ctx.breakpoint({\n question: 'Approve the release?',\n title: 'Release Approval',\n expert: 'owner', // Route to the project owner\n});\n```\n\n**Resolution strategies** control how multiple responses are handled:\n\n| Strategy | Description |\n|----------|-------------|\n| `single` | Default. One reviewer responds. |\n| `first-response-wins` | First response from any expert is accepted. |\n| `collect-all` | Waits for all experts to respond. Results available in `result.allResponses`. |\n| `quorum` | Waits for a majority of experts to respond. |\n\n```javascript\nconst result = await ctx.breakpoint({\n question: 'Approve production deployment?',\n title: 'Production Deployment',\n expert: ['tech-lead', 'ops-lead', 'security-lead'],\n strategy: 'quorum',\n});\n// result.approved — true if a quorum approved\n// result.respondedBy — who responded\n// result.allResponses — all collected responses\n```\n\n### Context File Schema\n\n| Field | Type | Required | Description |\n|-------|------|----------|-------------|\n| `path` | string | Yes | Relative path to the file within the run directory |\n| `format` | string | Yes | File format: `markdown`, `html`, `code`, `text` |\n| `language` | string | No | Programming language for syntax highlighting (when format is `code`) |\n\n---\n\n## Code Examples and Best Practices\n\n### Example 1: Conditional Breakpoints\n\nOnly request approval when certain conditions are met.\n\n```javascript\nexport async function process(inputs, ctx) {\n const analysis = await ctx.task(analyzeTask, { code: inputs.code });\n\n // Only request approval for high-risk changes\n if (analysis.riskLevel === 'high') {\n const review = await ctx.breakpoint({\n question: `High-risk changes detected (${analysis.riskFactors.join(', ')}). Approve to proceed?`,\n title: 'High-Risk Change Review',\n context: {\n runId: ctx.runId,\n files: [\n { path: 'artifacts/risk-analysis.md', format: 'markdown' }\n ]\n }\n });\n\n if (!review.approved) {\n return { success: false, reason: 'High-risk changes rejected', feedback: review.feedback };\n }\n }\n\n return await ctx.task(applyChangesTask, { changes: analysis.changes });\n}\n```\n\n### Example 2: Multi-Stage Approval Workflow\n\nImplement multiple approval gates for different phases.\n\n```javascript\nexport async function process(inputs, ctx) {\n // Phase 1: Design\n const design = await ctx.task(designTask, inputs);\n\n await ctx.breakpoint({\n question: 'Approve the design?',\n title: 'Design Review',\n context: { runId: ctx.runId, files: [{ path: 'artifacts/design.md', format: 'markdown' }] }\n });\n\n // Phase 2: Implementation\n const implementation = await ctx.task(implementTask, { design });\n\n await ctx.breakpoint({\n question: 'Approve the implementation?',\n title: 'Implementation Review',\n context: { runId: ctx.runId, files: [{ path: 'artifacts/implementation.md', format: 'markdown' }] }\n });\n\n // Phase 3: Deployment\n await ctx.breakpoint({\n question: 'Approve deployment to production?',\n title: 'Deployment Approval',\n context: { runId: ctx.runId, files: [{ path: 'artifacts/deployment-checklist.md', format: 'markdown' }] }\n });\n\n return await ctx.task(deployTask, { implementation });\n}\n```\n\n### Example 3: Breakpoints with Quality Gates\n\nCombine breakpoints with quality scoring for informed decisions.\n\n```javascript\nconst qualityScore = await ctx.task(agentQualityScoringTask, {\n tests: testsResult,\n implementation: implementationResult,\n coverage: coverageResult\n});\n\nawait ctx.breakpoint({\n question: `Quality score: ${qualityScore.overallScore}/100. ${qualityScore.summary}. Approve for merge?`,\n title: 'Final Quality Review',\n context: {\n runId: ctx.runId,\n files: [\n { path: 'artifacts/quality-report.md', format: 'markdown' },\n { path: 'artifacts/coverage-report.html', format: 'html' }\n ]\n }\n});\n```\n\n### Example 4: Robust Rejection Pattern (Retry on Rejection)\n\nBreakpoints should never fail a process. When a reviewer rejects, retry with their feedback incorporated. Always use a clean question string (no ternary operators in the question text).\n\n```javascript\nexport async function process(inputs, ctx) {\n const plan = await ctx.task(generatePlanTask, { feature: inputs.feature });\n\n let approved = false;\n let attempt = 0;\n let previousFeedback = undefined;\n\n while (!approved) {\n attempt++;\n\n // Refine plan if we have feedback from a prior rejection\n if (previousFeedback) {\n await ctx.task(refinePlanTask, {\n plan,\n feedback: previousFeedback,\n });\n }\n\n const question = previousFeedback\n ? `Plan revised based on your feedback. Please review again.`\n : `Review the implementation plan. Approve to proceed?`;\n\n const review = await ctx.breakpoint({\n question,\n title: 'Plan Review',\n previousFeedback,\n attempt,\n context: {\n runId: ctx.runId,\n files: [{ path: 'artifacts/plan.md', format: 'markdown' }],\n },\n });\n\n if (review.approved) {\n approved = true;\n } else {\n previousFeedback = review.feedback;\n ctx.log(`Plan rejected (attempt ${attempt})`, { feedback: review.feedback });\n }\n }\n\n return await ctx.task(implementTask, { plan });\n}\n```\n\n**Key principles for the robust rejection pattern:**\n- Breakpoints NEVER fail the process -- always loop and retry\n- Pass `previousFeedback` and `attempt` so reviewers have context\n- Use the feedback to refine the work before the next review\n- Keep the question string clean and readable (no ternary in the question itself)\n\n### Best Practices\n\n1. **Write Clear Questions**: Make the question specific and actionable\n2. **Provide Sufficient Context**: Include all files necessary for making an informed decision\n3. **Use Descriptive Titles**: Help reviewers quickly understand what they are approving\n4. **Place Strategically**: Add breakpoints before irreversible actions\n5. **Minimize Unnecessary Approvals**: Too many breakpoints slow down workflows\n6. **Ensure Files Exist**: Write context files before calling the breakpoint\n7. **Use Routing for Team Workflows**: Set `expert` and `strategy` to direct breakpoints to the right people\n8. **Never Fail on Rejection**: Use the robust rejection pattern to retry with feedback instead of failing the process\n\n---\n\n## Auto-Approval Rules\n\nBreakpoint auto-approval lets you configure rules to automatically approve recurring breakpoints based on patterns, reducing repetitive manual approvals while maintaining control over critical decisions.\n\n### How It Works\n\n1. When a breakpoint effect is dispatched, the SDK evaluates rules from `~/.a5c/breakpoint-approvals/rules.json`\n2. A pre-computed `autoApproval` field is written to `task.json` with the recommendation\n3. The harness reads `autoApproval.recommended` and can auto-approve without prompting\n\n### Precedence (highest wins)\n\n| Priority | Source | Description |\n|----------|--------|-------------|\n| 1 | `never-auto-approve` rule | Explicit block — always prompt |\n| 2 | Profile `alwaysBreakOn` tags | Tags configured in user profile that always require manual approval |\n| 3 | `auto-approve` rule | Explicit allow — skip the prompt |\n| 4 | `autoApproveAfterN` threshold | Auto-approve after N consecutive manual approvals |\n| 5 | Prompt (default) | No matching rule — ask the user |\n\n### Managing Rules via CLI\n\n```bash\n# Add an auto-approve rule for all \"confirm.*\" breakpoints\nbabysitter breakpoint:approve-rule \"confirm.*\" --note \"Routine confirmations\"\n\n# Add a never-auto-approve rule for production deployments\nbabysitter breakpoint:approve-rule \"gate.deploy-production\" --action never-auto-approve --note \"Always review prod deploys\"\n\n# Add a rule with attribute predicates\nbabysitter breakpoint:approve-rule \"*.review(tags contains 'design')\" --note \"Auto-approve design reviews\"\n\n# List all rules\nbabysitter breakpoint:list-rules\n\n# Check if a breakpoint should auto-approve\nbabysitter breakpoint:should-auto-approve \"confirm.star-repo\" --json\n\n# View breakpoint approval history\nbabysitter breakpoint:history --limit 20\n\n# Remove a rule\nbabysitter breakpoint:remove-rule <rule-id>\n```\n\n### Pattern Syntax\n\nPatterns match against `breakpointId` values with optional attribute predicates:\n\n| Pattern | Matches |\n|---------|---------|\n| `confirm.*` | Any breakpointId starting with `confirm.` |\n| `*.review` | Any breakpointId ending with `.review` |\n| `gate.deploy-production` | Exact match |\n| `*.review(tags contains 'design')` | Matching IDs where tags include \"design\" |\n| `gate.*(tags contains 'prerequisites' AND expert = 'owner')` | Matching IDs with both tag and expert conditions |\n\n### Using Auto-Approval in Processes\n\n```javascript\n// Breakpoint with explicit ID and auto-approve threshold\nawait ctx.breakpoint({\n question: 'Confirm repository star?',\n title: 'Star Repository',\n breakpointId: 'confirm.star-repo', // Canonical cross-run identity\n autoApproveAfterN: 3, // Auto-approve after 3 consecutive approvals\n presentAlwaysApprove: true, // Show \"Always Approve\" option\n tags: ['routine', 'github'],\n});\n\n// breakpointId is auto-derived from title if not provided:\n// title \"Review Design Document\" → breakpointId \"review-design-document\"\nawait ctx.breakpoint({\n question: 'Review the design?',\n title: 'Review Design Document',\n tags: ['design'],\n});\n```\n\n### Pre-Computed autoApproval in task.json\n\nWhen a breakpoint effect is written to disk, the SDK evaluates rules and writes the result to `task.json`:\n\n```json\n{\n \"kind\": \"breakpoint\",\n \"title\": \"Star Repository\",\n \"metadata\": {\n \"breakpointId\": \"confirm.star-repo\",\n \"tags\": [\"routine\", \"github\"]\n },\n \"autoApproval\": {\n \"recommended\": true,\n \"reason\": \"Matched auto-approve rule: rule-a1b2c3d4\",\n \"matchedRule\": \"rule-a1b2c3d4\",\n \"consecutiveApprovals\": 5\n }\n}\n```\n\nThe harness can read `autoApproval.recommended` directly from task.json without calling the CLI.\n\n---\n\n## Common Pitfalls and Troubleshooting\n\n### Pitfall 1: Session Timeout During Review\n\n**Symptom:** Workflow fails or loses state while waiting for lengthy review.\n\n**Solution:**\n\nBabysitter workflows are fully resumable. If a session times out:\n```\nClaude \"Resume the babysitter run and continue\"\n```\n\nThe breakpoint state is preserved in the journal and will be restored on resume.\n\n### Pitfall 2: Context Files Not Displaying\n\n**Symptom:** Breakpoint appears but context files are missing or empty.\n\n**Causes:**\n- Incorrect file paths in the context configuration\n- Files not yet written when breakpoint triggered\n\n**Solution:**\n\n1. Ensure files are written before calling `ctx.breakpoint()`:\n ```javascript\n await ctx.task(writeArtifactTask, { content: plan, path: 'artifacts/plan.md' });\n await ctx.breakpoint({ /* ... */ });\n ```\n\n2. Verify file paths are relative to the run directory:\n ```javascript\n { path: 'artifacts/plan.md', format: 'markdown' } // Correct\n { path: '/absolute/path/plan.md', format: 'markdown' } // Incorrect\n ```\n\n### Pitfall 3: Breakpoints in Automated Pipelines\n\n**Symptom:** CI/CD job hangs waiting for manual approval.\n\n**Cause:** Automated pipelines cannot interact with breakpoints requiring human input.\n\n**Solution:**\n\n1. Use conditional breakpoints that only trigger in non-CI environments:\n ```javascript\n if (process.env.CI !== 'true') {\n await ctx.breakpoint({ /* ... */ });\n }\n ```\n\n2. Implement auto-approval for CI with appropriate safeguards:\n ```javascript\n if (process.env.CI === 'true' && qualityScore >= targetQuality) {\n ctx.log('Auto-approved in CI environment');\n } else {\n await ctx.breakpoint({ /* ... */ });\n }\n ```\n\n### Pitfall 4: Missed Breakpoint Question\n\n**Symptom:** Workflow appears stuck, but no question was seen.\n\n**Solution:**\n1. Scroll up in your Claude Code conversation to find the question\n2. If the session timed out, resume the run\n\n---\n\n## Related Documentation\n\n- [Process Definitions](./process-definitions.md) - Learn how to create workflows with breakpoints\n- [Run Resumption](./run-resumption.md) - Resume workflows after breakpoint approval\n- [Journal System](./journal-system.md) - Understand how breakpoint events are recorded\n- [Best Practices](./best-practices.md) - Patterns for strategic breakpoint placement and workflow design\n\n---\n\n## Summary\n\nBreakpoints enable human-in-the-loop approval within automated workflows. Use `ctx.breakpoint()` to pause execution at critical decision points, present context to the user, and ensure human oversight before proceeding.\n\n**Key points:**\n- Call `ctx.breakpoint()` with a question and optional context files\n- Returns a `BreakpointResult` with `approved`, `response`, `feedback`, `option`, `respondedBy`, and `allResponses` fields\n- Use `result.approved` to branch on approval/rejection\n- Route breakpoints to specific experts with `expert` and control resolution with `strategy`\n- Use `previousFeedback` and `attempt` for retry context on rejection\n- Breakpoints should never fail a process -- use the robust rejection pattern to retry with feedback\n- Claude presents the question directly in the chat via `AskUserQuestion`\n- You respond to approve, reject, or provide feedback\n- The workflow continues based on your response\n- No external services or setup required - breakpoints work in-session\n- Backward compatible: existing code that ignores the return value still works\n",
"documents": []
},
"outgoingEdges": [],
"incomingEdges": [
{
"from": "page:docs-user-guide-features",
"to": "page:docs-user-guide-features-breakpoints",
"kind": "contains_page"
}
]
}