Hooks call DispatchCommands directly; delete Pipe, Script, HookSource (supersedes ADR-0172 item 4) #767
Labels
No labels
P0
P1
P2
P3
bug
category/code-change
category/cross-repo
category/eval-run
category/investigation
category/no-test-target
category/owner-ruling
category/prose-artifact
category/recurring-chore
category/structure
category/umbrella
crc-refactor
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
rule-proposal
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#767
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?
Problem
Each hook today calls
DispatchCommandsthrough an in-process Thor CLI invocation, not directly.hooks/subagent_start.rbcallsHook::SubagentStart.call, which callsPipe.call("subagent-start"), which callsRunnerCLI.start(["subagent-start"], invocation:)in-process.Pipebuilds aStringIOstdout/stderr pair, rescues theSystemExitthat Thor raises on exit 0, and re-wraps the CLI's JSON output into the hook envelope. The Thor subcommands read onlyinvocation.stdinandcwd; they never read environment variables. This indirection exists to satisfy ADR-0172 item 4, "each hook is a pipe to the runner subcommand," but the CLI layer adds no behavior the hook does not already have to unwrap.Reproduce
n/a: design and refactor ticket, not a reproducible bug.
Approach
VERDICT: CREATE (user-approved 2026-09-27)
Hooks call
DispatchCommandsdirectly. Agreed shape, user-written 2026-09-27:Entry scripts pass
io: $stdin. TheSubagentStophook follows the same shape, callingDispatchCommands.subagent_stop_from_stdin(event.cwd, StringIO.new(raw))or a new event-taking method. ThePostToolUsehook'snextcall moves toDispatchCommands.next_instruction_from_event. Accepted behavior change: malformed JSON and command errors now raise (exit 1, non-blocking for Claude Code) instead of exiting 0 silently or exiting 3.Required work:
bin/suite-check(or the project's equivalent) passes against the pre-change hooks.SubagentStart,SubagentStop, thePostToolUse/nexthandler) to callDispatchCommandsdirectly per the shape above.command/pipe.rb,hook/script.rb,model/hook_source.rband their tests (tests/command/pipe_*.rb,tests/support/hook_stdin_contract.rb).hooks.jsonmatchers route exactly one event per script; if so, drop theevent.name ==check in each hook class.Rejected: keep
Pipeas a thin pass-through — rejected because it still wrapsSystemExitand re-serializes JSON for no behavioral gain once hooks callDispatchCommandsdirectly.Acceptance criteria
lib/os_sdlc/hook/forRunnerCLI, when the search runs after this change, then it returns no matches.Owner sketch
Non-goals
Dissolving
RunnerCLIitself and its bin scripts — tracked separately, and depends on this ticket.Origin
Investigated after #766 (double subagent-stop path).
Resolution
Done: Hooks call DispatchCommands directly. lib/os_sdlc/hook/subagent_start.rb, subagent_stop.rb, post_tool_use.rb parse the event from io, call subagent_start / subagent_stop_from_event / next_instruction_from_event, and print the envelope. Deleted command/pipe.rb, hook/script.rb, model/hook_source.rb, tests/support/hook_stdin_contract.rb, tests/command/pipe_*_test.rb, tests/hook/. lint_changed.rb calls new.call without Script. Event-name checks dropped: hooks.json routes one event type per script; post_tool_use keeps its tool_name == Agent filter. Errors raise and exit 1 as accepted.
Evidence: Commit
5e5457ceon branch crc-pass-os-sdlc. tests/hooks/hook_envelope_bytes_test.rb pins byte-identical success envelopes for all three hooks, green before and after. Suite: 1327 runs, 2912 assertions, 0 failures, 0 errors, 10 skips. rubocop: 72 pre-existing offenses, none in touched files. grep -rn RunnerCLI lib/os_sdlc/hook/ returns nothing. ADR-0231 supersedes ADR-0172 item 4 and ADR-0217, amends ADR-0207 and ADR-0194 item 6. plugins/os-sdlc/CLAUDE.md hook-contract section and docs/implementation-status/os-sdlc.md History row updated.Follow-ups: none; #768 (RunnerCLI dissolution) is the next ticket and was already open.