Skip to content

T-NP7H-task-work-sub-agent-verdict-contract-escape-recurrence

Status: closed/superseded · Impact: high · Complexity: medium

LLM sub-agents are unreliable emitters of structured terminal output after substantive work: dispatched /sdlc:task-work runs escape the one-line verdict contract despite the strengthened “CRITICAL VERDICT CONTRACT” prompt preamble shipped in [T-ICA5-task-work-sub-agent-verdict-contract-clarity](/planning/tasks/task-work-sub-agent-verdict-contract-clarity/). The parent-side defenses that contain the damage have shipped (see Today); what remains is structural: the parse cascade exists only as pseudocode inside orchestrate’s spec, so every other dispatching parent must re-derive it, and the verdict path still depends on prompt interpretation instead of a deterministic command. This task extracts the parser into shared substrate and reshapes dispatch so the verdict comes from code, not prompt compliance.

LocationRole today
apps/sdlc/skills/orchestrate/SKILL.mdStep 4 dispatch prompt with verdict-contract preamble, plus the full parse cascade as inline pseudocode: strict first-line match (TASK_WORK_VERDICT_RE) → lenient last-match scan (LENIENT-RECOVERY) → escape-marker recovery (ESCAPE_RECOVERY_MAPESCAPE-RECOVERY) → ANOMALY; per-slug re-dispatch back-off keeps a deterministically-escaping sub-agent from consuming a cap slot every tick
apps/sdlc/skills/task-ensure-ready/SKILL.mdEmits slug-namespaced markers (ENSURE-READY-OK:, ENSURE-READY-NEEDS-DEFINITION:, …) so intermediate markers cannot match the final-verdict allowlist
apps/sdlc/skills/task-define/SKILL.mdSame namespacing: TASK-DEFINE-DEFINED: / TASK-DEFINE-NO-CHANGES: / TASK-DEFINE-ALREADY-READY:
apps/sdlc/skills/task-work/SKILL.mdFinal verdicts are namespaced (TASK-WORK-DONE / TASK-WORK-BLOCKED / TASK-WORK-NEEDS-DEFINITION / ERROR); Steps 5b–10 are what an escaping sub-agent fails to continue through
apps/sdlc/lib/services/orchestrator/ops/log-tick.tsDigest writer renders the ANOMALY / LENIENT-RECOVERY / ESCAPE-RECOVERY event lines

Two escape shapes are observed in the field; both are contained by the shipped parser, but only inside orchestrate:

ShapeSub-agent behaviorShipped defense
Stop-at-intermediate-markerHalts at a sub-skill marker and returns it as the final answer (3 escapes in 12 ticks in one 2026-05-24 orchestrate session, then-bare READY:)Marker namespacing removed the collision; ESCAPE-RECOVERY maps each known marker back to a task-work outcome, no same-tick re-dispatch
Verdict-after-proseEmits the correct namespaced verdict, but after a prose preamble (a 2026-05-30 case returned ~30 lines of dependency reasoning, verdict on the last line); a first-line-only guard reads the prose and files an ANOMALY, losing the diagnosticLenient last-match scan recovers the buried verdict and files LENIENT-RECOVERY, preserving prompt-tuning pressure without dropping signal

Both shapes share one root cause: sub-agents don’t reliably comply with terse-output instructions after long tool chains. That makes this a class problem, not a task-work problem — every parent-style skill that dispatches sub-agents (orchestrate, pr-respond, task-close-out, future skills) hits the same risk, so the contract-and-parser pattern wants a project-level shape.

Two deliverables, parser first:

  1. Shared verdict parser — extract the parse cascade (strict first-line → lenient last-match → escape-marker recovery → anomaly) from orchestrate’s spec pseudocode into a deterministic helper in apps/sdlc/lib/util/, surfaced to skills as an sdlc CLI verb per the op-substrate rule ([D-H7FS-op-substrate-surface](/planning/decisions/op-substrate-surface/)). Every dispatching skill calls the verb; none re-derives the algorithm from prose.
  2. Driver-shaped dispatch — the structural fix: the dispatched sub-agent’s terminal action becomes “run this one deterministic command and return its stdout”, so the verdict line is produced by code rather than by prompt-interpretation. Most invasive of the options considered, and the most reliable — it makes future dispatching skills correct-by-construction.
  1. Implement the shared parser in apps/sdlc/lib/util/subagent_verdict.ts (return text + strict allowlist + escape-marker map in; verdict or typed recovery/anomaly event out), with unit tests per parse class.
  2. Expose it as an sdlc CLI verb and repoint orchestrate SKILL.md Step 4 at the verb, deleting the inline pseudocode. The spec keeps the contract description; the verb owns the algorithm.
  3. Adopt the verb wherever other dispatching parents (pr-respond, task-close-out) parse sub-agent returns.
  4. Design and ship the driver-shaped dispatch for task-work: decide how much of the flow tail (verdict assembly and emission) a deterministic CLI verb can own vs. what stays LLM-executed, then reshape the dispatch prompt to “run the verb, return its stdout”.
  5. Add an eval fixture under apps/sdlc/skills/orchestrate/tests/: synthesized sub-agent returns for each parse class, asserting the right digest events land via the log-tick op.
LocationKindChange
apps/sdlc/lib/util/subagent_verdict.tsnewshared verdict parser: strict first-line → lenient last-match → escape-marker recovery → anomaly
apps/sdlc/cli/modifyregister the parse verb
apps/sdlc/skills/orchestrate/SKILL.mdmodifyreplace the inline parse pseudocode with the verb invocation
apps/sdlc/skills/pr-respond/SKILL.mdmodifyparse sub-agent returns via the shared verb
apps/sdlc/skills/task-close-out/SKILL.mdmodifyparse sub-agent returns via the shared verb
apps/sdlc/skills/task-work/SKILL.mdmodifydriver-shaped verdict emission: terminal verdict produced by a deterministic verb’s stdout
apps/sdlc/skills/orchestrate/tests/modifyeval fixture covering each parse class end-to-end
  • AC-1: Unit tests on the shared parser cover each parse class by name: (a) clean first-line verdict, (b) verdict-after-prose recovered by last-match with a lenient-recovery event, (c) one case per ESCAPE_RECOVERY_MAP entry asserting its mapped outcome, (d) a no-match return producing anomaly.
  • AC-2: apps/sdlc/skills/orchestrate/SKILL.md Step 4 invokes the CLI verb and no longer carries the inline parser pseudocode block.
  • AC-3: The task-work dispatch prompt instructs the sub-agent to run a deterministic command and return its stdout as the verdict line; the verdict is no longer assembled by the sub-agent from prose instructions.
  • AC-4: All quality_checks in sdlc.yaml pass.
  • Same-tick auto-resume of escaped dispatches — cross-tick re-pick with per-slug back-off already recovers stamped-but-stopped tasks, and same-tick re-dispatch risks tight loops with a deterministically-escaping sub-agent. Add it as a layered defense only if escapes persist after the parser and driver ship.
  • Sub-agent prompt re-engineering — the verdict-contract preamble is already verbose and proven insufficient; more text is not the tool.
  • Harness-level enforcement of the verdict contract (rejecting sub-agent returns at the platform layer) — outside the plugin’s scope.
  • none. Builds on [T-ICA5-task-work-sub-agent-verdict-contract-clarity](/planning/tasks/task-work-sub-agent-verdict-contract-clarity/) (closed/done), which shipped the prompt preamble and strict-regex validation this task hardens structurally.
  • Surfaced during a 2026-05-24 rust-ontogen /sdlc:orchestrate session: three verdict-contract escapes in 12 ticks, all stop-at-intermediate-marker, despite every dispatch carrying the strengthened preamble. A 2026-05-30 session added the verdict-after-prose shape. Companion to [T-NUML-task-work-preflight-permissions-probe-extension-for-skill-internal-scripts](/planning/tasks/task-work-preflight-permissions-probe-extension-for-skill-internal-scripts/), which surfaced in the same session.

← Back to Tasks