feat(sf): dispatch pause-for-escalation rule (PDD)
Closes the basic escalation loop. With this commit, end-to-end: - Task agent writes escalation_pending=1 + escalation_artifact_path to the tasks DB row (DB schema from62dacb627). - State derivation detects the pause and emits phase='escalating-task' with /sf escalate hint in nextAction (ea8819906). - Auto-dispatch sees phase='escalating-task' FIRST in the rule order and returns 'stop' with the nextAction message — no other rule runs. PDD spec: Purpose: never let the loop continue past a pending escalation. Consumer: auto-mode dispatcher (DISPATCH_RULES first entry). Contract: 1. state.phase !== 'escalating-task' → return null (fall through). 2. state.phase === 'escalating-task' → return action='stop' with the state's nextAction (the /sf escalate hint state.ts produced). 3. Rule sits at index 0 of DISPATCH_RULES so phase-agnostic rules below (rewrite-docs, UAT, reassess) cannot bypass it. Failure boundary: pure phase check, no fs/db access — nothing to fail. Evidence: typecheck clean. State derivation already smoke-tested inea8819906— once that returns phase='escalating-task', this rule emits the stop. End-to-end happy path is just two function calls. Non-goals: - Tools to write escalation_pending (the producer side — task agents need a tool for this; later fire) - /sf escalate user command (later fire) - Resolution flow (escalation.ts has the schema; resolveEscalation helper from gsd-2 is not yet ported — later fire) Invariants: - Safety: phase !== 'escalating-task' → 1 condition check, return null. Zero overhead in the common case. - Liveness: when paused, dispatch returns immediately — never runs another rule that could mutate slice state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ea8819906d
commit
a558ff6c64
1 changed files with 19 additions and 0 deletions
|
|
@ -522,6 +522,25 @@ When done, say: "Validation attention remediated; ready for revalidation."`;
|
|||
// ─── Rules ────────────────────────────────────────────────────────────────
|
||||
|
||||
export const DISPATCH_RULES: DispatchRule[] = [
|
||||
{
|
||||
// ADR-011 Phase 2 (gsd-2 ADR): mid-execution escalation pause.
|
||||
// Must evaluate FIRST — phase-agnostic rules below (rewrite-docs gate,
|
||||
// UAT checks, reassess) cannot bypass a pending user decision.
|
||||
// state.ts emits phase='escalating-task' only when there's an actionable
|
||||
// escalation; this rule turns that into a clean 'stop' with the message
|
||||
// state.nextAction already populated.
|
||||
name: "escalating-task → pause-for-escalation",
|
||||
match: async ({ state, mid }) => {
|
||||
if (state.phase !== "escalating-task") return null;
|
||||
return {
|
||||
action: "stop",
|
||||
reason:
|
||||
state.nextAction ||
|
||||
`${mid}: task escalation awaits user resolution. Run /sf escalate list to see pending items.`,
|
||||
level: "info",
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "rewrite-docs (override gate)",
|
||||
match: async ({ mid, midTitle, state, basePath, session: _session }) => {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue