suite gate fails BinMissingThorGuardTest under the runner's subprocess environment while the suite passes directly #594
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#594
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?
Context
Implementation 19 on ticket #588 ran the os-backlog target through the poodr-ticket-implementation map. The run ended implementation_failed with bound_exhausted:suite-check/fail after two programmer-repair rounds. The suite gate reported one red,
BinMissingThorGuardTest, that a direct run of the same suite does not report.Diagnosis (2026-09-15, verified by a reproducing loop)
plugins/os-sdlc/lib/os_sdlc/boot.rbrequiresbundler/setup. Bundler writesRUBYOPT=-r.../bundler/setupinto the runner's own ENV. Every child the runner spawns withoutBundler.with_unbundled_envinherits it. The test hides thor withruby --disable-gems; that flag does not block an explicit-rfromRUBYOPT, so the os-sdlc Gemfile's thor lands on the child's load path and the bin succeeds.Load-bearing factor:
RUBYOPTalone.BUNDLE_GEMFILEalone stays green.Reproduce, red:
Simpler equivalent on this machine:
Green: the same command without
RUBYOPT.Blast radius audit: 13 spawn sites in os-sdlc. Three run a project-supplied command and inherit the leak. The rubocop sites are unaffected (rubocop resolves through the rbenv shim and loads Sdlc cops through its own
require:paths; verified green with and without the scrub). git, tea, ast-grep, pbm are indifferent.hook/pipe.rbre-invokes the runner, which self-boots, so it must NOT be scrubbed.Scope
Files that change:
plugins/os-sdlc/lib/os_sdlc/runner/gate_evaluator.rbrun_gate(the named bug)plugins/os-sdlc/lib/os_sdlc/test_runner.rbrun(green/test command, same leak)plugins/os-sdlc/lib/os_sdlc/project_config_command.rbtest_command_clean_exit?(candidate test_command validation, same leak)Out of scope, separate ticket: a map route for a suite red in a file outside the diff (suite-check/fail routes unconditionally to programmer-repair today).
Owner sketch
One object owns the fact "a project-supplied command runs with the environment a terminal would give it". The three call sites delegate to it.
Callers:
Bundleris loaded in every entry point; all bin/ and hooks/ files require boot first (verified).Cases
RUBYOPT=-rbundler/setup, whenGateEvaluatorrunsruby -e 'print ENV.fetch("RUBYOPT", "")', then stdout does not containbundler/setup. Red today.TestRunner#runruns that command, then output does not containbundler/setup. Red today.ProjectConfigCommandvalidates a candidate test_command that exits 0 only whenRUBYOPTis unset, then it is accepted. Red today.RUBYOPTin the runner's ENV, when a gate command runs, then behavior is unchanged from today. Green today, must stay green.BUNDLE_GEMFILEandRUBYOPT(with_unbundled_env restores). Guards the runner's later requires.Verification against the original symptom: run the suite gate from the runner on the os-backlog target;
BinMissingThorGuardTestpasses.Expected
The gate's verdict matches a direct run for every project-supplied command.
Origin
Work starting via /os-sdlc:implement in a worktree. Diagnosis and scope written to the body (three spawn sites, ProjectShell owner).
Implementation 20 (poodr-ticket-implementation, target os-sdlc, worktree ticket-594) ended implementation_failed: bound_exhausted:test-reviewer/fail. Three test-writer rounds (258, 260, 262), three reviewer rejections (259, 261, 264). Diff-test gate was red for the intended reason each round; test-lint green. Reviewing the reviewer handoffs to see whether the feedback was actionable before a rerun.
Implementation 22 on branch ticket-594: 11 dispatches, one refactorer round (require hygiene), suite 1209/0 twice. Second contract-auditor returned error on a test-side defect only: the test-writer had overwritten the entry file plugins/os-sdlc/tests/project_config_command_test.rb, orphaning project_root_and_config_ticket_583_test.rb. Hand-restored both requires, full suite 1209 runs 0 failures, rubocop clean on 8 changed files, BinMissingThorGuardTest passes through the real GateEvaluator with the runner's RUBYOPT set. Commit
bc19155. Also filed #596 (seam-designer public-boundary self-check) from the implementation-20 handoff audit.Resolution
Done: OsSdlc::ProjectShell owns the project-command environment; GateEvaluator, TestRunner and ProjectConfigCommand spawn project-supplied commands through Bundler.with_unbundled_env. Gate verdicts now match a direct terminal run. ADR-0191 amends ADR-0166; ADR-0166 stale affected-path corrected.
Evidence: Merged to main
be6d047(fixbc19155, docs3c7862a). Loop: env RUBYOPT=-r/bundler/setup ruby plugins/os-backlog/tests/cli_test.rb -n /thor_is_unavailable/ was red before; BinMissingThorGuardTest passes through the real GateEvaluator with the runner's RUBYOPT set after. os-sdlc suite 1209 runs 0 failures; rubocop clean on 8 changed files. Blast-radius audit: 13 spawn sites, 3 changed, rubocop/git/tea/ast-grep/pbm/Hook::Pipe verified indifferent.Follow-ups: #596 seam-designer public-boundary self-check (filed, user-approved). Map route for a suite red in a file outside the diff: not filed, user decides. Test-writer replacing an existing entry file's require (orphaned ticket-583 tests, hand-fixed): none, lesson recorded in the os-sdlc status leaf.