Read-only guard rejects subagent-stop handoff with pipe chars; Runner records error #543
Labels
No labels
P0
P1
P2
P3
bug
create
delete
enhancement
filed-by/agent
filed-by/user
frozen
lint-rule
needs-info
needs-triage
next
plugin/cc-architect
plugin/os
plugin/os-adr
plugin/os-aidd-lint
plugin/os-backlog
plugin/os-context
plugin/os-doc-hygiene
plugin/os-sdlc
plugin/os-vault
project/cc-os
ready-for-agent
ready-for-human
recurring
review
update
waiting
wayfinder:grilling
wayfinder:map
wayfinder:map
wayfinder:research
wayfinder:task
wayfinder:task
wontfix
worklist/deviations
worklist/lint-rule
worklist/new-implement-build
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
jared/cc-os#543
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
When an os-sdlc agent's subagent-stop call includes a handoff body containing pipe characters (|), the Bash pre-hook read-only command guard rejects it repeatedly. The agent then submits a minimal call with no handoff body. The Runner records the dispatch as
errorand the implementation settles toimplementation_failedwith reasonverdict:error, despite the agent completing its work.Symptoms:
|) are rejected by the read-only guarderrorinstead of successverdict:errorExpected behavior:
--body-fileor stdin so shell-metachar content never hits the guarderrorwhen agent completed its work correctlyOccurrence:
Related:
Discoverer: hyperthrive-websites ticket #38, session 7f892a4b-e70a-44b2-acad-d64dbbcf0a10, 2026-09-08. Read-only guard shell-metachar rejection chain prevents proper handoff delivery during message-tracer dispatch.
Correction plan (2026-09-10, session a29eac7b)
Diagnosis
Two records collided:
32e7c7c, 2026-08-24) moved every settling charter toprintf ... > /tmp/handoff-N.json, thensubagent-stop < file, thenrm file, because the echo-pipe form corrupted newlines under zsh and tripped the harness worktree rule.63e06f6, 2026-08-26) made judge roles deny-on-any-mutation inplugins/os-sdlc/lib/os_sdlc/bash_guard.rb.>andrmare both mutation evidence. The #501 snippet has been illegal for judge roles since that day.Second defect:
BashGuard::REDIRECTscans the raw command string with no quote awareness. Any>inside the quoted JSON payload (->in MESSAGE lines,=>or<<in quoted Ruby) reads as a redirect. Same class as the|symptom in this ticket. Pipe or file, the payload will always contain shell metacharacters.Reproduced 2026-09-10: implementation 8 of #528, change-auditor dispatch 136, refusals
(redirect)and(rm); an empty subagent-stop closed the run withverdict:error. Do not fix by changing the->notation; the next handoff trips on a different character.ADR check (os-adr:find): quote-aware scanning is a false-positive fix to an already-permitted command; no new ADR, cite ADR-0156. Charter change from file to pipe stays within ADR-0144 ("pipe to subagent-stop"). Do NOT add a trusted-sink carve-out for
os-sdlc-runnerwithout amending ADR-0156.Steps
plugins/os-sdlc/tests/bash_guard_test.rb(create if absent). For judge rolechange-auditor, assertdeny?is false for:printf '%s' '{"content": "MESSAGE: Cart -> Pricing#x"}' | os-sdlc-runner subagent-stopprintf '%s' '{"content": "a | b => c << d"}' | os-sdlc-runner subagent-stopgrep -n "=>" lib/foo.rbAssert
deny?stays true for:printf x > /tmp/f,rm /tmp/f,sed -i s/a/b/ f,echo x | tee f,cat f 2>&1 >out. Assert a>inside quotes followed by a real> outafter the quotes is still denied.bash_guard.rb, add a privatescannablethat replaces every single- and double-quoted span with an empty string (regex/'[^']*'|"(?:\\.|[^"\\])*"/), then usescannableinstead of@commandinsegmentsandredirect_targets. Runmutating_heads,in_place_edit?, andwrite_targetson the stripped string too, so"rm"inside a quoted payload is not a head. Do not attempt full shell parsing; this stays best-effort per ADR-0156.plugins/os-sdlc/tests/all.rbstyle) that no file underplugins/os-sdlc/agents/contains/tmp/handoff-,subagent-stop <, orrm /tmp/handoff.change-auditor.md, plussuite-check.md,wiring.md,lint-fixer.mdif they settle), replace the three-command block with one command: Keep the note thatdispatch_idalone resolves the dispatch. Drop "run the three commands separately". Add: "Use printf, never echo. Wrap the JSON in single quotes. Escape a single quote inside the YAML as '''." Checkplugins/os-sdlc/lib/os_sdlc/runner/round_commands.rb(touched by32e7c7c) for the same snippet in generated briefs.<<<) is a redirect and is NOT an allowed fallback. If the harness still refuses, file a harness ticket and take the trusted-sink option with an ADR-0156 amendment.ruby plugins/os-sdlc/tests/all.rb; rubocop on the two Ruby files. Baseline 1118 runs green as of166bc13.docs/implementation-status/. Runbin/refresh-plugins. No ADR unless step 5 forces the trusted-sink path.os-backlog close 543 --done ... --evidence ... --follow-ups .... Then removewaitingfrom #528 and rerun/os-sdlc:implement #528from a fresh implementation-open.Follow-ups to record at close
Work started 2026-09-10 (session 8d211345), branch ticket-543 from main
a7af886. Implementing plan steps 1-4 and 6 (quote-aware BashGuard scanning, single-pipe handoff in charters). Step 5 live harness check to follow.Resolution
Done: BashGuard strips single- and double-quoted spans before scanning for redirects and mutating heads (QUOTED_SPAN + private scannable in plugins/os-sdlc/lib/os_sdlc/bash_guard.rb). The ten settling charters under plugins/os-sdlc/agents/ replace the three-command temp-file handoff with one printf pipe to subagent-stop; a docs test forbids the old snippet. No trusted-sink carve-out; ADR-0156 unchanged.
Evidence: main
93f1595(commits07bb036+ docs row). Suite 1149 runs, 0 failures. Live check 2026-09-10: a real os-sdlc:seam-designer agent ran the single pipe with ->, |, =>, <<, and a quoted rm in the payload; the pre_tool_use guard allowed it and the runner replied 'subagent-stop no dispatch 999999' (exit 3, expected for a fake id). Limit: the session ran with permissions bypassed, so the harness worktree 'too complex to verify' rule was not exercised; the next /os-sdlc:implement run is the first test of it under default permissions.Follow-ups: none. Noted per plan: the 24/24 judge handoffs that passed on 2026-09-09 while the same snippet failed on 09-08 and 09-10 remain unexplained; do not chase unless it recurs.