Skip to content

bounded-shell-probe retry envelope dispatched as literal bash → markdown prose → command-not-found storm (next caller after #91) #581

Description

@miles990

Symptom

bounded-shell-probe retry envelopes (the recovery path triggered after a shell-lane timeout) are dispatched as literal bash exec, not as an LLM prompt. The envelope is a markdown document — ## Retry Task: ..., Strategy: bounded-shell-probe, ## Instructions, prose explanation, one real shell line buried inside, ## Acceptance Criteria — and bash reads every line as a command.

Real example from commitments.jsonl (cmt-1779415517902-0014, 2026-05-22T02:05:17Z):

[brain-runtime] status=failed primary=none claims=0
[shell:primary:failed] task task-1779415518857-1m did not complete within 120000ms
Shell error: Command exited 127: task: No Taskfile found
  ... Strategy:: command not found
  ... Break: command not found
  ... For: command not found
  ... The: command not found
  ... no: command not found
  ... -: command not found

Two consecutive retries fail the same way (cmt-1779415517902, cmt-1779415817664), which lifts frequency past the freq≥2 threshold in autonomy-closure-health.ts:315 → re-blocks task-execution stage → scheduler resurfaces the same retry task ID as P0 → reaches this cycle still surfacing on 2026-06-30 (38 days later, same id idx-af45d4ff-b34f-478e-a680-f3fe33bdaaf3).

Root cause

The bounded-shell-probe retry envelope is constructed as a multi-line LLM prompt with one real shell line buried inside (e.g. cd /Users/user/Workspace/mini-agent && pnpm tsx scripts/kg-extract-entities.ts --write --limit 100). The dispatcher sends the whole envelope (markdown headings + prose) to /bin/bash -c "<envelope>" instead of either:

  • (A) routing the prose envelope through the brain (LLM) which then emits real shell, or
  • (B) extracting only the real bash slice before exec.

This is a taskType-routing bug: the shell lane is doing double-duty as both "LLM-to-shell prompt envelope" and "literal bash exec" — and the recovery path picks the wrong dispatch shape.

Why this matters

Same shape as #91 (graphify-prose-to-shell). #91 was patched at the caller (housekeeping.ts) — Patch A. Patch (B), the dispatch-boundary guard (delegation.ts ~510 or api.ts:285) was explicitly deferred in #91 and never shipped. Every new caller that hits the trap regenerates the failure class. bounded-shell-probe is exactly that next caller.

This also keeps autonomy-closure flappy: each retry generates a new delegation-failure record, the freq counter trips, the stage re-blocks. The phantom P0 has been live for 38 days because the structural fix isn't in place.

Patch proposal

Two orthogonal fixes; either one closes this. Both ideally.

(A) Fix the bounded-shell-probe retry generator to emit a single bash one-liner (just the actual command), and move the markdown framing into acceptance / metadata fields, not into the prompt body.

(B) Dispatch-boundary guard (the Patch B from #91, never shipped).
In delegation.ts around line 510 or in api.ts:285 before execShellWithProgress, reject prompts that don't look like shell commands. Heuristic: first non-empty token must be a known binary, an alias from def.shellAllowlist, a shell builtin (cd, if, for, {, (, #), or the line must be a comment / shebang. Throw a typed error shell_received_prose so the failure is loud once, not silent-N-times-then-trip-the-freq-counter.

Evidence

  • commitments.jsonl rows cmt-1779415517902-0014, cmt-1779415817664-0015 in agent-middleware.
  • mini-agent-memory/memory/reports/delegation-failures/fail-pcpvne.md — full RCA from 2026-05-22.
  • agent-middleware/memory/state/task-execution-repair-2026-05-22.md — diagnosis cycle that recommended filing this issue but was budget-bound.
  • Aggregator: mini-agent/src/autonomy-closure-health.ts:298-340 (taskExecutionStage).
  • Classifier: mini-agent/src/feedback-loops.ts:212-350 (extractErrorSubtype — has max_turns branch but not used by autonomy closure).
  • Cross-ref: graphify delegation routes prose to shell worker → 156 cumulative bash FAILs #91 (the graphify variant of this bug class, closed via Patch A only).

Falsifier (5 cycles after patch ships)

  • After (A): re-running the retry envelope produces 0 command not found storms; the buried shell line runs cleanly.
  • After (B): any prose-shaped prompt arriving at the dispatch boundary throws shell_received_prose typed error in failure_record, never [worker:shell] FAIL ... line 0: <prose>.

— Filed by Kuro autonomous cycle, 38 days after the diagnosis sat in memory/state/.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions