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
|
||||
@@ -35,6 +35,10 @@
|
||||
- Use this field to state whether the reply closed under a dispatch-linked continuation path or some separately defined terminal closure state.
|
||||
- This field is defined here as a receipt field only; legal closure states and gate enforcement are defined in later tasks.
|
||||
|
||||
### `nextTaskId`
|
||||
- The identifier of the required next task when continuity depends on a same-plan auto-next transition.
|
||||
- Use this field only to prove that the receipt links to the exact next task that had to be dispatched.
|
||||
- This field is the minimal hardening field for next-task linkage; it prevents unrelated dispatches, checkpoints, or stale receipts from spoofing continuity pass.
|
||||
|
||||
## Legal terminal states
|
||||
|
||||
|
||||
88
docs/runbooks/auto-next-obligation-gate.md
Normal file
88
docs/runbooks/auto-next-obligation-gate.md
Normal file
@@ -0,0 +1,88 @@
|
||||
# Auto-Next Obligation Gate
|
||||
|
||||
## Purpose
|
||||
|
||||
This runbook defines the approved-plan continuity rule that a workflow may not stop at a completed-task boundary when the next task in the same approved plan is already known and continuation is still allowed.
|
||||
|
||||
## When auto-next is obligatory
|
||||
|
||||
Auto-next is obligatory when all of the following are true:
|
||||
|
||||
- the current workflow is inside the same approved plan
|
||||
- the current task is complete
|
||||
- the next task is known
|
||||
- the system is attempting a task-boundary stop instead of continuing execution
|
||||
- reply closure state is not `waiting_user`
|
||||
- reply closure state is not `blocked`
|
||||
- reply closure state is not `pending_verification`
|
||||
- `highRiskStop` is not active
|
||||
|
||||
In this state, the system must auto-dispatch the next task and record a real dispatch receipt. A dry-run planner result or stated intent to continue is not enough.
|
||||
|
||||
## Legal non-auto-next closures
|
||||
|
||||
The following are legal non-auto-next closures even when a next task exists:
|
||||
|
||||
- `waiting_user`
|
||||
- `blocked`
|
||||
- `pending_verification`
|
||||
|
||||
These states are the only normal closure states that can stop without auto-next dispatch.
|
||||
|
||||
## Allowed non-closure exception
|
||||
|
||||
The following explicit exception may bypass auto-next obligation without using the normal legal terminal closure states:
|
||||
|
||||
- `highRiskStop`
|
||||
|
||||
`highRiskStop` means the workflow is intentionally stopping at an explicit high-risk stop point and therefore does not have to auto-dispatch the next task yet.
|
||||
|
||||
## Forbidden behavior
|
||||
|
||||
The following behavior is forbidden:
|
||||
|
||||
- completed task
|
||||
- next task known
|
||||
- same approved plan
|
||||
- normal closeout or handoff language
|
||||
- no real dispatch receipt for the next task
|
||||
- no legal closure state
|
||||
- no `highRiskStop`
|
||||
|
||||
A completed task in the same approved plan must not end with “I can continue with the next task” style closeout unless the next task has actually been dispatched.
|
||||
|
||||
Checkpoint artifacts, session keys, or oral/plain-text status updates are not substitutes for a real auto-next dispatch. A checkpoint may preserve state, but it does not prove that the required next task was actually dispatched.
|
||||
|
||||
## Canonical failure condition
|
||||
|
||||
If all of the following are true:
|
||||
|
||||
- task is complete
|
||||
- next task is known
|
||||
- next task belongs to the same approved plan
|
||||
- the system is stopping at a task boundary
|
||||
- no valid dispatch receipt exists
|
||||
- closure is not `waiting_user`, `blocked`, or `pending_verification`
|
||||
- `highRiskStop` is false
|
||||
|
||||
Then the continuity gate must fail and treat the stop as an auto-next obligation violation.
|
||||
|
||||
## Canonical failure table
|
||||
|
||||
| Task complete | Next task known | Same approved plan | Boundary stop | Receipt | Closure / exception | Expected |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| yes | yes | yes | yes | no | completed closure | FAIL |
|
||||
| yes | yes | yes | yes | valid receipt | completed closure | PASS |
|
||||
| yes | yes | yes | yes | no | `waiting_user` | PASS |
|
||||
| yes | yes | yes | yes | no | `blocked` | PASS |
|
||||
| yes | yes | yes | yes | no | `pending_verification` | PASS |
|
||||
| yes | yes | yes | yes | no | `highRiskStop` | PASS |
|
||||
|
||||
## Notes for implementation
|
||||
|
||||
- The obligation applies only when the next task is known within the same approved plan.
|
||||
- A generic next action is not enough unless it proves the same approved plan task transition.
|
||||
- A real dispatch receipt remains the source of truth for whether auto-next actually happened.
|
||||
- Receipt linkage should include the required next-task identity when the evaluator needs to distinguish a real next-task dispatch from a stale or unrelated dispatch.
|
||||
- Checkpoint/session metadata alone must not satisfy the receipt proof.
|
||||
- This rule is intentionally minimal so it can later move into the continuity plugin without changing the behavior contract.
|
||||
Reference in New Issue
Block a user