feat: sync auto-next obligation gate hardening
This commit is contained in:
195
docs/plans/2026-04-24-auto-next-obligation-gate.md
Normal file
195
docs/plans/2026-04-24-auto-next-obligation-gate.md
Normal file
@@ -0,0 +1,195 @@
|
||||
# Auto-Next Obligation Gate Implementation Plan
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
**Goal:** Enforce that an approved plan may not stop at a task boundary when the current task is complete and the next task is already known, unless the closure is explicitly `waiting_user`, `blocked`, `pending_verification`, or a separately-declared high-risk stop point; otherwise the flow must auto-dispatch the next task and any stop is a continuity failure.
|
||||
|
||||
**Architecture:** Extend the current approved-plan continuity gate from a passive “missing dispatch receipt” detector into an obligation gate that evaluates task-boundary stops as first-class failures. Keep the design minimal: preserve the current receipt-based truth model, add explicit stop-intent / high-risk-stop metadata, fail first in tests, then wire the hook path so dry-run planner intent is no longer enough when the next task is deterministically known. Design the slices so the same evaluator can later be extracted into the continuity plugin MVP without changing the behavior contract.
|
||||
|
||||
**Tech Stack:** Node.js ESM scripts, TypeScript hook integration, JSON input/output envelopes, file-backed dispatch receipts, script-level tests, continuity plugin MVP compatibility layer
|
||||
|
||||
---
|
||||
|
||||
## Context Baseline
|
||||
|
||||
The current repo already has a partial continuity hard gate:
|
||||
|
||||
- `scripts/approved_plan_continuity_gate.mjs`
|
||||
- fails only when `taskState=complete` + next action known + no valid `dispatchReceipt` + closure not in legal terminal states
|
||||
- `scripts/approved_plan_dispatch_binding.mjs`
|
||||
- writes receipt files once a dispatch is actually bound
|
||||
- `hooks/force-recall/handler.ts`
|
||||
- builds continuity input from wrapper/planner state and injects the continuity block
|
||||
- `scripts/test_approved_plan_continuity_gate.mjs`
|
||||
- already covers missing receipt, fake receipt, valid receipt, and legal terminal states
|
||||
- `docs/plans/2026-04-24-continuity-plugin-mvp.md`
|
||||
- already assumes this continuity behavior will later be extracted into a plugin
|
||||
|
||||
The remaining gap is narrower and more specific:
|
||||
|
||||
1. The current gate says “don’t close if a known next action exists but no real receipt exists.”
|
||||
2. But it does not yet model the stronger obligation: **if the next task in the same approved plan is already known and not blocked by an allowed stop condition, the system must auto-next dispatch instead of pausing at the boundary.**
|
||||
3. This means the failure is not only “missing receipt,” but also **“stopped at a task boundary when auto-next was obligatory.”**
|
||||
4. We need a minimal extension that preserves existing receipt truth, avoids speculative dispatch, and remains compatible with continuity-plugin extraction.
|
||||
|
||||
## Target Behavior Contract
|
||||
|
||||
When all of the following are true:
|
||||
|
||||
- current workflow is inside the same approved plan
|
||||
- current task is complete
|
||||
- the next task is known / derivable as a concrete next task
|
||||
- closure state is not `waiting_user`, `blocked`, or `pending_verification`
|
||||
- no explicit high-risk stop point is active
|
||||
|
||||
Then:
|
||||
|
||||
- the system must not stop at the task boundary
|
||||
- the execution layer must auto-dispatch the next task
|
||||
- a real dispatch receipt must exist for the next task handoff
|
||||
- otherwise the reply/hook path must produce a continuity failure
|
||||
|
||||
When any of the following are true, auto-next is not obligatory:
|
||||
|
||||
- closure state is `waiting_user`
|
||||
- closure state is `blocked`
|
||||
- closure state is `pending_verification`
|
||||
- an explicit high-risk stop point is active
|
||||
- no next task is known
|
||||
- current task is not complete
|
||||
- plan scope is absent or ambiguous
|
||||
|
||||
## Required New Concepts
|
||||
|
||||
To keep the design minimal, introduce only the following new concepts:
|
||||
|
||||
- `nextTaskKnown`: boolean or derivable fact that the next task in the same approved plan is known
|
||||
- `sameApprovedPlan`: boolean proving the next task belongs to the same approved plan, not merely a generic next action
|
||||
- `taskBoundaryStop`: boolean indicating the system is trying to end the current reply at a completed-task boundary instead of dispatching onward
|
||||
- `highRiskStop`: boolean indicating an allowed explicit stop point outside the normal legal closure states
|
||||
- `autoNextObligatory`: derived evaluator result when auto-next must happen now
|
||||
- `reason=missing_auto_next_dispatch` (or equivalent canonical reason) for the new failure mode
|
||||
|
||||
Do not widen this into a generalized workflow engine or arbitrary planner ontology in this slice.
|
||||
|
||||
## Current Gap
|
||||
|
||||
- current continuity gate checks a known next action, but it does not specifically require that the next task is the next task in the same approved plan
|
||||
- current hook can surface planner-derived action from dry-run planning, but planner intent is not a real dispatch and does not prove continuity actually happened
|
||||
- current dispatch binding writes receipts once dispatch is actually bound, but the gate does not yet express "must auto-dispatch now" as its own obligation at the task boundary
|
||||
- current legal terminal states are hard-coded and do not include explicit `highRiskStop` metadata
|
||||
|
||||
## Non-goals
|
||||
|
||||
- generalized multi-plan scheduling
|
||||
- speculative dispatch when the next task is ambiguous
|
||||
- removing current receipt validation
|
||||
- implementing continuity-plugin extraction in this slice
|
||||
|
||||
## Canonical Task-Boundary Stop Scenario
|
||||
|
||||
This is the scenario the implementation must lock down:
|
||||
|
||||
1. Approved plan has ordered tasks, e.g. Task 8 -> Task 9.
|
||||
2. Task 8 just completed.
|
||||
3. Task 9 is already known from the same approved plan.
|
||||
4. The agent emits a normal closeout / handoff / “next I can continue with Task 9” style response.
|
||||
5. No real auto-dispatch receipt exists for Task 9.
|
||||
6. Closure is not `waiting_user`, `blocked`, `pending_verification`.
|
||||
7. No high-risk stop point is active.
|
||||
|
||||
Expected outcome:
|
||||
|
||||
- continuity gate fails
|
||||
- hook output explicitly forbids stopping at this task boundary
|
||||
- system must route to auto-next dispatch path or continuity failure path
|
||||
- dry-run planner intent alone does not satisfy the obligation
|
||||
|
||||
---
|
||||
|
||||
## Verification Record
|
||||
|
||||
### Commands run
|
||||
|
||||
```bash
|
||||
node --check hooks/force-recall/handler.ts
|
||||
node --check scripts/approved_plan_continuity_gate.mjs
|
||||
node --check scripts/approved_plan_dispatch_binding.mjs
|
||||
node scripts/test_approved_plan_continuity_gate.mjs
|
||||
node scripts/test_force_recall_long_task_preflight.mjs
|
||||
```
|
||||
|
||||
### Result summary
|
||||
|
||||
- `node --check hooks/force-recall/handler.ts` ✅
|
||||
- `node --check scripts/approved_plan_continuity_gate.mjs` ✅
|
||||
- `node --check scripts/approved_plan_dispatch_binding.mjs` ✅
|
||||
- `node scripts/test_approved_plan_continuity_gate.mjs` ✅ `17/17 passed`
|
||||
- `node scripts/test_force_recall_long_task_preflight.mjs` ✅
|
||||
|
||||
### What was hardened in this slice
|
||||
|
||||
- continuity evaluator now rejects receipts that do not match the required `planId`, `currentTask`, and expected next dispatch action
|
||||
- minimal receipt linkage field `nextTaskId` was added so the evaluator can distinguish the required next-task dispatch from a stale or unrelated receipt
|
||||
- continuity tests now fail when the receipt links to the wrong next task
|
||||
- continuity tests now fail when a receipt only contains checkpoint/session-style metadata instead of real dispatch linkage
|
||||
- hook preflight verification still confirms that dry-run planner intent alone does not satisfy continuity, and that the failure reason remains `missing_auto_next_dispatch`
|
||||
|
||||
### Deliberately deferred
|
||||
|
||||
- stronger upstream source-of-truth for `sameApprovedPlan`
|
||||
- broader non-`force-recall` entry-point enforcement
|
||||
- continuity plugin extraction work
|
||||
|
||||
---
|
||||
|
||||
## Minimal Enforcement Design Summary
|
||||
|
||||
The enforcement should stay intentionally small:
|
||||
|
||||
1. **Keep receipt truth model**
|
||||
- a real dispatch receipt remains the pass proof
|
||||
- planner intent alone is not proof
|
||||
|
||||
2. **Add one stronger evaluator branch**
|
||||
- when the next task in the same approved plan is known and the current reply is stopping at a completed-task boundary, auto-next becomes obligatory
|
||||
- missing receipt in this branch is a dedicated continuity failure
|
||||
|
||||
3. **Allow only narrow exemptions**
|
||||
- `waiting_user`
|
||||
- `blocked`
|
||||
- `pending_verification`
|
||||
- `highRiskStop=true`
|
||||
|
||||
4. **Keep hook integration thin**
|
||||
- hook computes structured booleans
|
||||
- evaluator makes the decision
|
||||
- hook renders the reason-specific block
|
||||
|
||||
5. **Preserve plugin extraction path**
|
||||
- no hook-only business logic
|
||||
- no receipt-store / evaluator coupling
|
||||
- no prompt-only policy with no machine-checkable input
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [x] A completed task in the same approved plan cannot stop at a boundary when the next task is known unless an allowed exemption applies.
|
||||
- [x] The continuity evaluator emits a dedicated failure for missing required auto-next dispatch.
|
||||
- [x] A real dispatch receipt is still required; dry-run planner output alone cannot pass.
|
||||
- [x] Legal closure states `waiting_user`, `blocked`, `pending_verification` still pass unchanged.
|
||||
- [x] Explicit `highRiskStop` bypass is supported and test-covered.
|
||||
- [x] Hook output clearly explains the auto-next obligation failure.
|
||||
- [x] Script-level continuity tests pass.
|
||||
- [x] Hook smoke tests pass.
|
||||
- [ ] The plan documents how this behavior migrates cleanly into the continuity plugin MVP.
|
||||
|
||||
## Risks / Open Questions
|
||||
|
||||
1. The current hook may not yet expose a strong enough source of truth for `sameApprovedPlan`; if so, one narrow upstream metadata field may be needed.
|
||||
2. `highRiskStop` may not currently exist in structured input, so the first implementation may need a conservative default of `false` until an upstream gate can set it explicitly.
|
||||
3. Receipt schema may still need one future compatibility pass if downstream writers have not yet been upgraded to emit `nextTaskId` everywhere continuity depends on same-plan auto-next proof.
|
||||
4. This slice deliberately does not solve non-hook entry points or general workflow orchestration.
|
||||
|
||||
## Status
|
||||
|
||||
pending verification / reviewer checked
|
||||
Reference in New Issue
Block a user