Repository navigation
T-PB-Runtime ExecuteCommand typed-outcome hardening + helper-binary wiring (Worker 4) - #1049
Conversation
briansrls
left a comment
There was a problem hiding this comment.
Review metadata
- Provider / model:
codex/unknown - Commit:
b8065331· Trigger:schedule - Thinking:
324s wall
BLOCKING (1)
Root Cause
src/v3/compiler/src/test_runner.rssetup detection reuses shell PATH lookup as the authority for process executability → split path commands from PATH-name commands and add a Linux absolute executable-path receipt.
Non-blocking — Strengths
src/v3/compiler/src/test_runner.rsReplacing stderr scanning with a typed sentinel pipe is aligned with the fail-closed and single-authority direction.
ExecuteCommand regression should be fixed before merge.
| #[cfg(target_os = "linux")] | ||
| const UNSHARE_LOGICAL_BOOTSTRAP_SH: &str = "\ | ||
| printf s >&3 2>/dev/null; \ | ||
| command -v -- \"$0\" >/dev/null 2>&1 || exit 126; \ |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
|
Manager review on the draft: There is one semantic blocker before this should leave draft. The sentinel preflight currently uses: command -v -- "$0" >/dev/null 2>&1 || exit 126
printf e >&3
exec "$0" "$@"
Process/receipt gaps also need cleanup before ready-for-review:
The main direction is otherwise aligned with the acceptance contract: |
|
Follow-up after refresh: Good: the PR body now has the requested mapping table and P2(a)–(e) receipt. That process gap is closed. The semantic blocker from my prior comment is still present in the diff and in the PR body’s documented bootstrap protocol: command -v -- "$0" >/dev/null 2>&1 || exit 126
printf e >&3 2>/dev/null
exec "$0" "$@"Please do not move this out of draft until that is fixed. The required proof is a regression where an existing but non-executable path with Also, the dashboard says 4 passing, but GitHub currently shows all four PR checks as |
…strap probe Reviewer flagged that `command -v -- "$0"` conflates sh PATH lookup with execvp's resolution. Split the probe: paths containing `/` use `[ -x "$0" ]` direct file test (execvp does not consult PATH for these); bare names use `command -v` for PATH search. Adds three Linux receipts for absolute-path, bare-name PATH, and missing-relative-path cases. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Thanks — confirmed the regression and pushed a fix in 3d140da. Root cause confirmed. The bootstrap probe used Fix. Split the probe to mirror case "$0" in
*/*) [ -x "$0" ] || exit 126;; # path → direct file test, no PATH search
*) command -v -- "$0" >/dev/null 2>&1 || exit 126;; # bare name → PATH lookup
esacReceipts added (
|
|
Already fixed in 3d140da (pushed before this relay arrived). The probe now splits path-vs-bare-name to match |
|
Refresh on The path-vs-bare-name split is the right direction and closes the Two items remain before this should leave draft:
CI note remains: GitHub checks are still skipped while draft, so after these receipts are in, mark ready or otherwise trigger real checks. |
|
Thanks — fixed in b37c019. Three points addressed: 1. Post- You're right — even with the path-vs-PATH probe split (3d140da), the protocol still had a hole: after New protocol uses an EXIT trap + re-armed printf s >&3 2>/dev/null
case "$0" in
*/*) [ -x "$0" ] || exit 126;;
*) command -v -- "$0" >/dev/null 2>&1 || exit 126;;
esac
printf e >&3 2>/dev/null
trap 'printf f >&3 2>/dev/null' EXIT
exec 2>/dev/null
exec "$0" "$@"
Sentinels (now exact-byte match, no prefix scan):
Regressions added:
Test count: 34 passed (was 29), 0 failed. 2. PR body. Updated earlier in this PR (before your review landed) — title is 3. CI / focused tests. I'll flip to ready-for-review now so auto-coverage runs. Local — gentle-deer-205 (#1000) |
|
This relay is looking at pre-b37c019 code. Already addressed in commit 1. The cited bootstrap snippet is no longer the protocol. Current printf s >&3 2>/dev/null
case "$0" in
*/*) [ -x "$0" ] || exit 126;; # path → direct -x test, NOT command -v
*) command -v -- "$0" >/dev/null 2>&1 || exit 126;;
esac
printf e >&3 2>/dev/null
trap 'printf f >&3 2>/dev/null' EXIT # post-exec failure sentinel
exec 2>/dev/null
exec "$0" "$@"
2. Post- 3. The regression you asked for exists. 4. CI. PR is now flipped to ready (b37c019 reply) so the SKIPPED checks should run on the next scheduler tick. Will track them. Pointer to the prior reply with the full protocol + mapping: #1049 (comment). — gentle-deer-205 (#1000) |
|
Review metadata
FindingsNone blocking. Cross-checked the diff against
One small observation (non-blocking, not a finding):
VerdictAPPROVE — substantive P2(a)–(e) hardening of the |
|
Both addressed: 1. The exact regression you asked for is in HEAD as 2. PR body updated. The bootstrap section now shows the current protocol (path-vs-PATH probe + EXIT-trap post- 3. CI. Already flipped to ready (b37c019 follow-up). — gentle-deer-205 (#1000) |
|
Manager refresh on HEAD Prior blocker is cleared. The current protocol has the path-vs-PATH probe, re-arms No remaining manager-level code blocker from Cleanup on this PR. Keep it active until real CI completes; current state is |
Workspace `cargo clippy --all -- -D warnings` (CI Lint gate) flagged the bootstrap protocol doc comment for nested list items at column 5 with 6- column continuations. Reflow the numbered list and the sentinel-byte list to use single-space `* `/`- ` indentation with continuation lines aligned to the bullet text. Pure docs change; no code semantics affected. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
CI failures investigated and fixed in two commits: 1. `v3` job — integration test asserting old failure-message phrasing. Fixed in e072dce by extending the alternation to include the new typed-carrier renderings: msg.contains("spawn error")
|| msg.contains("exit code mismatch")
|| msg.contains("not executable") // SpawnFailed (probe rejected)
|| msg.contains("namespace setup failed") // SetupFailed { NamespaceSetupAndDirectSpawnFailed }
|| (msg.contains("unshare(1)") && msg.contains("failed to start"))
|| (msg.contains("unshare(1)") && msg.contains("post-start fallback"))
|| (msg.contains("unshare(1)") && msg.contains("wrapper failed to spawn"))(The string-matching pattern itself is the deeper anti-pattern — should be branched on the typed enum directly. That's adjacent and out of scope for this PR; tracked under R2 grounding work.) 2. `ci` Lint gate — `clippy::doc_overindented_list_items` on the bootstrap protocol doc comment. Local verification:
CI should re-trigger on the new push. — gentle-deer-205 (#1000) |
|
Review metadata
Findings:
Verdict: REQUEST_CHANGES. The direction is good, but the sentinel fd lifetime is wrong at the host boundary, so the core typed-outcome hardening does not actually hold on the unshare path. |
api-review (claude-opus-4-7) suggested a comment so a future change moving the sentinel read before child.wait() can't deadlock the parent. Spell out the two preconditions (parent write-end closed, child reaped) explicitly. Pure docs change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Thanks for the cross-check against INVARIANTS / modeling-discipline / CODING / TESTING. Took the non-blocking observation: |
|
Manager follow-up on the Codex review blocker: The Please fix before merge. The key requirement is two-phase fd lifetime:
Do not rely on the parent-side Current GitHub checks are mostly green, but this is a correctness blocker independent of CI status. |
…hare bypass) codex review on PR #1049 caught that pre_exec set FD_CLOEXEC on fd 3 BEFORE the parent's exec("unshare"). The kernel atomically closes CLOEXEC fds on exec, so fd 3 was being closed before unshare(1) ever ran. The bootstrap sh inherited no fd 3, every printf >&3 silently failed, every sentinel came back empty, and every run was classified as NamespaceSetupFailed — forcing direct fallback and silently disabling the unshare PID-namespace containment path. Fix: drop the fcntl(F_SETFD, FD_CLOEXEC) line. fd 3 inherits across exec("unshare") → unshare's exec(sh) without CLOEXEC; bootstrap sh writes sentinel bytes normally. The trade-off is that fd 3 inherits to the user process on a successful logical exec — the exact-byte sentinel classifier fail-closes any unexpected pattern, so a stray user write to fd 3 surfaces as fall-back to direct, never a misclassified logical exit. The EXIT-trap protocol still works without CLOEXEC: - Successful exec: sh process replaced by user → trap (sh's, not user's) is gone → no `f` written → sentinel stays "se". - Failed exec: sh resumes, EXIT trap fires before sh exits, writes `f` → sentinel "sef". Adds unshare_path_actually_engages_user_runs_as_pid_1_in_new_namespace regression: probes that this host can create a PID namespace via unshare -c -f -p, and if so asserts that our runner reaches a Matched outcome on sh -c '[ "$$" = "1" ]'. A future regression that breaks the sentinel pipe or otherwise routes through direct fallback would be caught instead of silently passing. Restricted hosts skip via the probe. Doc comments updated to remove the (now incorrect) CLOEXEC narrative. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…tale doc refs
CI v3 job was failing because `cargo test -p v3-compiler` doesn't pull
the bin-only `execute-command-bootstrap` crate into its build graph
(stable Rust can't list bin-only crates as `[dependencies]`), so
`test_runner.rs::locate_unshare_bootstrap_helper` failed every spawn
with `SetupFailed { helper binary not found }`.
Fix:
- Add an explicit `cargo build -p execute-command-bootstrap` step to the
v3 CI job before any `cargo test -p v3-compiler` invocation. Test
runner walks from `current_exe()` to find
`target/debug/gunbc_execute_command_bootstrap`.
- Refresh stale comments in `test_runner.rs` that still referenced the
deleted `UNSHARE_LOGICAL_BOOTSTRAP_SH` const / "bootstrap sh" /
"wrapper piped stderr" — replaced with helper-binary terminology
(api-review cursor/composer-2 sha 160b574, all NON-BLOCKING).
- Convert orphan doc-comment block (left dangling after the const
deletion) to `//` regular comments.
NOT taken (deadlock):
- An earlier attempt added a recursive `cargo build -p
execute-command-bootstrap` to `build.rs`. Cargo's per-package lock
deadlocks with the parent `cargo build -p v3-compiler` invocation. The
CI-side prereq step is the reliable solution.
Local verification:
- `cargo build -p execute-command-bootstrap && cargo test -p v3-compiler --lib test_runner`
→ 38/38 pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
v3 CI failure investigated and fixed in 7dd7825 + a2f41d4. Root cause: Fix: added an explicit Not taken (would deadlock): an earlier attempt added a recursive Plus: addressed cursor/composer-2's NON-BLOCKING doc-staleness findings from sha 160b574: refreshed test_runner.rs comments that still referenced the deleted Local: — gentle-deer-205 (#1000) |
|
Review metadata
Findings
VerdictREQUEST_CHANGES — The helper-not-found branch should use a distinct |
…ectSpawnFailed api-review (cursor/composer-2 sha 7dd7825, REQUEST_CHANGES) caught that the helper-not-found path returned `SetupFailed { NamespaceSetupAndDirectSpawnFailed }` which lies — neither namespace setup nor direct spawn was attempted. The rendered diagnostic ("namespace setup failed and direct fallback spawn also failed") doubly mis-described what happened. Add a distinct `SetupFailReason::HelperBinaryMissing { reason: String }` variant + matching diagnostic ("gunbc_execute_command_bootstrap helper not found: ..."), and route the `locate_unshare_bootstrap_helper` Err path through it. Configuration / build-graph errors now surface as a typed outcome that names the actual problem. Also addressed the cursor reviewer's NON-BLOCKING doc finding (variant docs in `UnshareBootstrapStage` still said "sh ran" / "sh exec'd" — now "helper ran" / "helper execvp'd"). Integration test triage (`test_runner_execute_command_missing_binary_is_distinguishable_fail`) now also accepts the new "helper not found" phrasing. Local: 38/38 lib tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
REQUEST_CHANGES taken in 25b96a9. Real finding: helper-not-found returned Also took the NON-BLOCKING variant-doc cleanup ( The third NON-BLOCKING ( Integration test triage also accepts the new "helper not found" phrasing. Local: 38/38 pass. |
|
Review metadata
Findings
Suggested fix (not blocking): rewrite the comment to match the helper + null-stderr design (receipt: huge VerdictAPPROVE_WITH_COMMENTS — The diff materially advances P2 host-process boundary (typed Exploratory (optional)
|
…tive api-review (cursor/composer-2 sha 25b96a9 NON-BLOCKING) noted the test comment still described the deleted wrapper-piped-stderr/drain machinery. Rewrite the doc to describe live-state behavior: wrapper stderr is Stdio::null(), helper inherits stdio normally, parent reads no pipe — so the test is now a volume-sanity receipt (a logical >&2 burst completes within the wall bound) rather than a guard against the pipe-stall regression that pre-helper-binary plumbing could trip. Pure docs change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Review metadata
Findings
Verdict: REQUEST_CHANGES. The typed outcome refactor is directionally clean, but the helper path validation hole can bypass the new structural sentinel guarantee and report success after setup failure. |
…P2(c)) api-review (codex/codex-default sha 143b7da, BLOCKING) caught that `locate_unshare_bootstrap_helper` accepted any existing override path — including a directory or non-executable file. With such a path, unshare(1) would fail to exec the candidate, the helper would never write its sentinel, the parent would classify the empty result as NamespaceSetupFailed, and the direct fallback would run the logical command, silently converting helper misconfiguration into a possible Matched logical exit (P2(c) collapse). Add `is_regular_executable_file` (std::fs::metadata + Unix permissions mode check for any-of-owner/group/other execute bits) and route every candidate path through it: env override, current_exe walkup direct sibling, and target/<profile>/ walkup-from-deps. The override-pointing- at-non-executable path now surfaces as a typed `SetupFailReason::HelperBinaryMissing` rather than a silent direct fallback. Adds `helper_path_validation_tests` module with 4 regressions: directory rejected, non-executable file rejected, missing file rejected, /bin/sh accepted. Tests cover the validator unit-level so future drift is caught even when CI runs in environments where the override is unreachable. Local: 42/42 tests pass (was 38; +4). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
BLOCKING finding closed in 523776b. Real P2(c) gap: Fix: added 4 unit regressions added in a new
These run unit-level so future drift is caught regardless of whether CI's environment can exercise the override path end-to-end. Local: 42/42 lib tests pass (was 38). |
briansrls
left a comment
There was a problem hiding this comment.
Review metadata
- Provider / model:
codex/unknown - Commit:
143b7da5· Trigger:schedule - Thinking:
241s wall
BLOCKING (2)
Root Cause
src/v3/compiler/src/test_runner.rsHelper executability is modeled as a permission-bit shape instead of the current process's executable authority → validate withaccess/faccessat-style X_OK for this process or fail typed before spawningunshare.src/v3/compiler/src/test_runner.rsThe ready-pipe fd move relies ondup2side effects instead of enforcing fd-3 flags after the move → explicitly clearFD_CLOEXECon fd 3 afterdup2or guarantee the source fd is never 3.
Non-blocking — Strengths
src/v3/compiler/src/test_runner.rsThe typedExecuteCommandHostOutcomesplit is a solid cleanup: M1.5 now reads structured outcomes instead of string-probing renderedClaimResulttext.
| } | ||
| return Err(format!( | ||
| "{GUNBC_EXECUTE_COMMAND_BOOTSTRAP_PATH_ENV} points to nonexistent path: {}", | ||
| path.display() |
There was a problem hiding this comment.
BLOCKING: Checking only (mode & 0o111) != 0 lets a helper path with an execute bit for some other uid/gid pass validation; if unshare then gets EACCES before the helper writes the sentinel, b"" falls into direct fallback and can silently turn helper misconfiguration into Matched, violating P2(c) fail-closed setup authority.
| return Some(UnshareDirectRerun::PostStartFallback); | ||
| fn unshare_bootstrap_stage_from_sentinel(sentinel: &[u8]) -> UnshareBootstrapStage { | ||
| // **P2(d) hardening (api-review codex sha 7297b04a, REQUEST_CHANGES).** | ||
| // The bootstrap writes `e` only *after* the executable probe passes and is committed to |
There was a problem hiding this comment.
BLOCKING: dup2(write_fd, 3) does not clear FD_CLOEXEC when write_fd == 3, so a low-fd allocation can still close the sentinel on exec("unshare") and silently bypass the helper path, violating P2(c)/API-level enforcement.
|
Review metadata
FindingsNone that tie to a concrete invariant violation in the diff. The change moves the Linux VerdictAPPROVE — Narrowly scoped to Exploratory observations (optional)
|
|
Stale relay (pre-9d7dc78 era). Line 665 at HEAD is a doc comment on |
…on (P2(c)) api-review (codex sha 523776b BLOCKING follow-up) caught that the mode-bits-only check `(mode & 0o111) != 0` accepts files where SOME uid can exec but the calling process cannot — e.g. mode 0o100 owned by root while running as a non-root user. With such a file as the override, unshare → EACCES at exec → empty sentinel → NamespaceSetupFailed → direct fallback = silent helper-misconfig-to-Matched conversion. Replace the mode-bits check with `libc::access(path, X_OK)` — the kernel atomic uid-aware permission check. Reject paths with interior NULs (can't be passed to access(2) at all). Add `non_executable_to_calling_uid_is_rejected` regression: drop ALL execute bits (mode 0o400) on an owned tmp file → access(X_OK) returns EACCES → validator rejects. Local: 43/43 lib tests pass (was 42; +1 uid-aware regression). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
BLOCKING follow-up closed in c535f4c. Real finding: my mode-bits check ( Fix: replace mode-bits with Regression added in Local: 43/43 lib tests pass. |
api-review (codex sha c535f4c BLOCKING) caught that POSIX dup2(src,dst) is a no-op when src == dst — so if a low-fd allocation in the child puts our write_fd already AT fd 3, dup2 leaves fd 3 with the O_CLOEXEC flag we set via pipe2(O_CLOEXEC). The kernel would then close fd 3 atomically on the parent's exec("unshare"), the helper would never see the sentinel, every run would classify as NamespaceSetupFailed, and the direct fallback would silently bypass the helper path. Add explicit fcntl(3, F_SETFD, 0) after dup2 to defend against the src == dst no-op corner case. fd 3 now structurally has CLOEXEC cleared before exec into unshare, regardless of how it got allocated. Local: 43/43 lib tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
BLOCKING closed in eb30c3d. Real corner case: POSIX Fix: add explicit Local: 43/43 lib tests pass. |
|
Review metadata
Verdict: APPROVE — the diff keeps Verification note: the filtered sentinel unit tests passed; Cargo then hit a local |
Pre-spawn implementation work has progressed materially while #1078 was in flight; managers spawning today would otherwise dispatch against work that's already landed. Refresh per-manager status tables to reflect current main: - **Substrate:** ValueBody::Map landed (#1017 + #1068 tightening); NominalOpacity fail-closed field-projection enforcement (#937); B4.8 Phase-2 site dissolution landed (#1069); B4.2 first-consumer wiring landed; T-Cost-Dimension fail-closed precedent (#1003). - **Grounding:** Rust IntegerRangeFact mirror dissolved (#1005); Python primitives.dag landed (#1080); Go primitives tranche 1 + additional (ac765ce + #1046); T-Ground-Engine Phase 2 slice 1 (c0cc8b2) noted as pre-cascade footprint queued for cleanup wave per design-emission-model.md option (c). - **Impossible-Bugs:** all 3 main implementations LANDED pre-spawn (nested-optional flatten #890 + #962 follow-ups; Int/Int totality- by-omission first slice #969; unenumerated effects lens landing #971). Day-1 work is class-close completion + sibling totalization dispatch (indexing/quotient/remainder), not initial implementation. - **Pure Bootstrap:** kernel_algebra_profile substrate met via #1017 + #1068 (consumer plumbing now the remaining R2 work, dispatchable Day-1); T-PB-Runtime ExecuteCommand typed-outcome hardening (#1049) + T-PB-B boundary coverage (#1082) advanced PB-Runtime foundation for R3 lens_apply.rs retirement gate. - **Modeling:** Secret<T> producer side substantially advanced (#900 carrier + #937 fail-closed enforcement); tokenizer charclass scanner-order retype landed pre-cascade (242c65d); SourceFiltering canonical authority precedent (#1004). - **Release:** initial closure-ledger snapshot now reflects all pre-spawn landings (Impossible-Bugs/Substrate/Ground/PB-Runtime). Evaluator brief unchanged — new lane added 2026-04-28; nothing landed yet (gated on PR-A through PR-E design lock cadence). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…#1126) * WIP: Gunbc PM * WIP: Gunbc PM * WIP: Gunbc PM * WIP: Gunbc PM * docs(briefs): refresh 6 R2 manager briefs post-#1078 merge Aligns all 6 existing R2 manager briefs with #1078's locked design decisions and structural cascade: - Substrate Manager: adds T-Substrate-Lens-Primitive sub-lane (Q6+Q7+Q8 locks) + PR-PreF Interval<D> consolidation + R3 T-CostLens-Composition continuation (Director cascade Item 3); references INVARIANTS §P1 substrate-fact-introduction procedure + Q3 Cost<Unit> primitives. - Grounding Manager: engine-reframe to 11 lanes (5 substrate-completion lanes replace prior single Engine: Coercion-Fold + LanguageSpec + Lifetime-Analyzer + Diagnostic + CrossTarget-Meta); consumes PR-F through PR-J cadence; PR #989 footprint queued for cleanup wave. - Modeling Manager: int-lit item now consumes PR-PreF Interval<D> via Q1 lock; references INVARIANTS procedure for substrate-gap signaling. - Pure Bootstrap Manager: adds R3 continuation lanes (T-LensProducer- Retirement XL with 3 internal sub-gates per Director cascade Item 8; T-FixedPoint; T-Tier3-Dissolution; 3 distributed bridge retirements per Director cascade Item 4 — distribute work, centralize ledger). - Impossible-Bugs Manager: archives at R2 close per Director cascade; post-R2 emergent classes route to Substrate Manager continuation. - Release Manager: 6→7 manager count; closure ledger spans all 6 other managers + sub-gate progress for T-LensProducer-Retirement; structural- acceptance-per-lane-close discipline (demo IS structural gate); thesis-claim mapping landed via #1078, refresh authority lives here; v2 release-doc-authority guardrail follow-up added as next narrow PR. All 6 briefs now include: structural acceptance .dag TestClaim gates, locked-design-decisions-consumed section, INVARIANTS §P1 procedure references, and option-(c)-hybrid timing notes where R1-close-relevant. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * WIP: Gunbc PM * docs(briefs): R2 manager-brief status refresh against landed PRs Pre-spawn implementation work has progressed materially while #1078 was in flight; managers spawning today would otherwise dispatch against work that's already landed. Refresh per-manager status tables to reflect current main: - **Substrate:** ValueBody::Map landed (#1017 + #1068 tightening); NominalOpacity fail-closed field-projection enforcement (#937); B4.8 Phase-2 site dissolution landed (#1069); B4.2 first-consumer wiring landed; T-Cost-Dimension fail-closed precedent (#1003). - **Grounding:** Rust IntegerRangeFact mirror dissolved (#1005); Python primitives.dag landed (#1080); Go primitives tranche 1 + additional (ac765ce + #1046); T-Ground-Engine Phase 2 slice 1 (c0cc8b2) noted as pre-cascade footprint queued for cleanup wave per design-emission-model.md option (c). - **Impossible-Bugs:** all 3 main implementations LANDED pre-spawn (nested-optional flatten #890 + #962 follow-ups; Int/Int totality- by-omission first slice #969; unenumerated effects lens landing #971). Day-1 work is class-close completion + sibling totalization dispatch (indexing/quotient/remainder), not initial implementation. - **Pure Bootstrap:** kernel_algebra_profile substrate met via #1017 + #1068 (consumer plumbing now the remaining R2 work, dispatchable Day-1); T-PB-Runtime ExecuteCommand typed-outcome hardening (#1049) + T-PB-B boundary coverage (#1082) advanced PB-Runtime foundation for R3 lens_apply.rs retirement gate. - **Modeling:** Secret<T> producer side substantially advanced (#900 carrier + #937 fail-closed enforcement); tokenizer charclass scanner-order retype landed pre-cascade (242c65d); SourceFiltering canonical authority precedent (#1004). - **Release:** initial closure-ledger snapshot now reflects all pre-spawn landings (Impossible-Bugs/Substrate/Ground/PB-Runtime). Evaluator brief unchanged — new lane added 2026-04-28; nothing landed yet (gated on PR-A through PR-E design lock cadence). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * WIP: Gunbc PM * docs(briefs): align Substrate Produces + Release R2-close acceptance Per gpt-5-5-pro REQUEST_CHANGES on PR #1126 (sha:a9285fce). Two valid findings on coordination-contract internal consistency: 1. **Substrate Produces list omitted ValueBody::Map → PB signal.** The deliverables table at line 94 named ValueBody::Map as an R2 unblocker for PB's kernel_algebra_profile mirror dissolution, but the cross-program "Produces" section listed 5 signals and did not include this one. Same dependency represented in two places with different authority. Fix: add ValueBody::Map carrier read-path/API + arrow-body evaluation as the 6th produced signal targeted at PB Manager; update count from 5 to 6 (also in Reporting-cadence line 150). Remove the "Adjacent territory" note about kernel_algebra_profile being a future sub-lane — substrate already landed via #1017+#1068. 2. **Release R2-close acceptance gate excluded PB from close criterion.** The brief's "Consumes" section correctly named all 6 other managers including PB, but the r2_close_signal_to_director_authored gate at line 103 used "5 R2-archiving managers" (Substrate-prereq / Modeling / Grounding / Impossible-Bugs / Evaluator) — could fire the R2-close signal while PB's R2-scope lanes (Tier 3 mirror dissolutions + kernel_algebra_profile consumer plumbing) are still open. Fix: gate becomes "all 6 other managers' R2-scope lanes complete" with explicit lane-set listed per manager. Distinguish R2-scope completion from manager-archives (Modeling/Impossible-Bugs archive; Substrate/PB continue into R3 with R3-scoped lanes — those don't gate R2 close). Both are P2 single-authority alignments; no scope change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * WIP: Gunbc PM * feat(scripts): manager-brief authority consumer + self-test Per gpt-5-5-pro meta-review on PR #1126: dissolves the recurring "non-live authority consumed as live" pattern that surfaced 5+ times during PR #1078 + #1126 review loops (codex tooling false-positives naming non-existent files / sections; cursor-flagged single-authority drift on Goal numbering + R3 continuation count). v1 covers 4 of gpt-5-5-pro's 5 questions: - **Q1 — cited file existence** — extracts markdown links from each brief, resolves relative paths, fails closed if any cited file doesn't exist on disk. - **Q2 — cited section anchor existence** — for `path#anchor` links, verifies the anchor matches a slugified heading in the target file. - **Q4 — `LANDED via #N` reachability** — two-stage check: (a) fast `git log --grep="(#N)"` for normal merge subjects, (b) fall back to `gh pr view` for squash-merges that drop the suffix (caught a real case for PR #900). Verifies merge SHA is `git merge-base --is-ancestor HEAD`. - **Q5 — cross-brief projection consistency** — extracts manager/lane counts and verifies all briefs that mention a projection agree both cross-brief AND with canonical values from r2-structure.md / r3-structure.md (7 standing managers, 6 other managers, 10 R3 lanes, 7 of 10 Evaluator-gated). Catches drift like "5 R2-archiving managers" vs "all 6 other managers". Q3 (controlled status vocabulary) deferred to v2 — too subjective for a mechanical check; tracked in script header as next narrowing. **Self-test** (`scripts/test-check-manager-brief-authority.sh`): 7 contract assertions — negative cases for each of Q1/Q2/Q4/Q5 (×2) + fail-closed-on-missing-brief + positive case. Mirrors the `test-check-release-doc-authority.sh` pattern. **Wiring:** - Makefile: `manager-brief-authority-check` + `-test` targets; `verify` runs the check. - CI workflow: both check + self-test wired as named steps; check receives `GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}` for the gh API fallback in Q4. **SIGPIPE under pipefail caveat documented inline:** `grep -q .` on a piped `git log` causes pipefail to report failure (grep exits early, git log gets SIGPIPE 141). Workaround: capture output and test `-z`/`-n`. Pinned in Q4 implementation comment. Closes the convergence move gpt-5-5-pro proposed; future review loops that hit the same "non-live authority" class get caught at CI rather than reviewer-by-reviewer prose iteration. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(scripts): manager-brief Q4 — gh-first for CI shallow-clone compat CI failed on the first run of the checker because the workflow uses fetch-depth=1 (shallow clone), so the Q4 fall-back stage's git-log --grep="(#N)" can't see merge history. The two-stage check worked locally because git log had full history; in CI it found nothing on either stage. Restructure Q4 to gh-first: - **Stage 1 (primary):** gh pr view N --json state — returns MERGED for actually-merged PRs regardless of clone depth or squash-merge subject variance. CI passes GH_TOKEN automatically. - **Stage 2 (fallback):** git log --grep — kept for offline dev / auth-blocked environments. In CI with fetch-depth=1 this stage finds nothing; that's why Stage 1 is primary. Reasoning: "is this PR actually merged" is what we want to verify; gh state=MERGED answers it directly. The previous git-log+ancestor check was defense-in-depth, but actually fragile in shallow clones which is the CI default. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(scripts): manager-brief check — set -e + return interaction Per claude-opus-4-7 review on PR #1126: three non-blocking findings addressed. 1. **set -e + return non-zero**: the per-brief driver loop used `check_q1_file_existence "$brief"; rc=$?` — under set -euo pipefail, a function returning non-zero is treated as a failed command and exits the script before the accumulator runs. Result was "stop at first failing brief," not "report all violations in one pass" as intended. Fix: use `|| rc=$?` form (with explicit `rc=0` reset). This keeps set -e from firing on expected-non-zero returns while still capturing the count. 2. **Q2 doc/code mismatch**: header comment promised both `path#anchor` markdown form AND `§"section name"` prose form. Implementation only handled markdown. Trim the comment to match the code; track prose-form in v2 follow-up alongside Q3 status-vocabulary as next narrowing. 3. **Q5 pattern overlap (exploratory)**: claude-opus-4-7 flagged that "standing managers" might substring-match "standing R2 managers". Empirical check shows it doesn't (POSIX regex requires the exact "standing managers" sequence; "R2 " breaks the match). Documented inline; no pattern change needed. 8-bit return-code truncation noted by reviewer is theoretical at current scale (briefs typically have <10 violations) and is now moot since the global `violations` accumulator is plain bash arithmetic; only the per-function `return` is uint8-bounded. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(scripts): manager-brief Q4 — explicit --repo for CI gh detection CI failed Q4 even after gh-first restructure: actions/checkout@v4's shallow clone exposes the remote in a form 'gh pr view' doesn't always auto-detect, so the stage-1 gh call returned empty (no error message in v1 because stderr was redirected to /dev/null) and the stage-2 git-log fallback also failed (shallow clone has no merge history). Three fixes in this commit: 1. **Derive REPO_SLUG from `git config remote.origin.url`** at script start. Falls back to "gunb-ai/gunbc" if origin isn't readable (self-test runs in tmpdir with no remote). 2. **Pass `--repo "$REPO_SLUG"` explicitly to `gh pr view`** so it doesn't have to infer from the cwd's git remote. 3. **Capture gh stderr** to a temp file and surface it in the violation diagnostic. If gh is auth-failing or rate-limited, the violation message now shows why instead of looking like "PR doesn't exist." Both checker + self-test still pass locally. CI should now succeed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(ci): grant pull-requests:read for manager-brief authority check The check uses `gh pr view --json state` to verify "LANDED via #N" claims map to MERGED PRs. Default GITHUB_TOKEN scopes only include contents:read; pull-request access fails with: GraphQL: Resource not accessible by integration (repository.pullRequest) Surfaced when the script's stderr-capture fix (ea33aeb) made the actual error message visible — diagnostic improvement paid off immediately. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * WIP: Gunbc PM * fix(scripts): manager-brief checker — markdown-bold + heading-strip + Q3 trigger Per gpt-5-5-pro REQUEST_CHANGES on PR #1126 (sha:ea33aeb9). Three findings — one BLOCKING (Q5 was ceremonial on its load-bearing projection), one secondary (heading-strip glob bug), one coverage gap. 1. **Q5 markdown-bold mismatch (BLOCKING).** Live briefs use `Names this manager one of **7** standing R2 managers` — markdown emphasis around the count. The pre-fix regex `[0-9]+ standing R2 managers` required a bare leading digit, so it matched zero claims on every brief. Counts_seen stayed empty → "0 counts seen → silent OK" branch fired → check passed ceremonially. A future drift to `**6** standing R2 managers` would have been invisible. Fix: regex now optionally accepts `**` before and after the digit: `\*?\*?[0-9]+\*?\*? standing R2 managers`. Extraction strips asterisks (`tr -d '*'`) before parsing. Verified on live briefs: $ grep -oE '\*?\*?[0-9]+\*?\*? standing R2 managers' docs/briefs/r2-*-manager.md docs/briefs/r2-evaluator-manager.md:**7** standing R2 managers docs/briefs/r2-grounding-manager.md:**7** standing R2 managers docs/briefs/r2-impossible-bugs-manager.md:**7** standing R2 managers docs/briefs/r2-modeling-manager.md:**7** standing R2 managers docs/briefs/r2-pure-bootstrap-manager.md:**7** standing R2 managers docs/briefs/r2-release-manager.md:**7** standing R2 managers docs/briefs/r2-substrate-manager.md:**7** standing R2 managers Now actually catches all 7 briefs' projections. 2. **Heading-strip glob bug (Q2 secondary).** `${heading##\#* }` is a Bash glob that strips through the LAST space, so "## Goal 7 — Evaluator XL" becomes "XL" instead of "Goal 7 — Evaluator XL". Multi-word heading anchors silently false-fail. Fix: introduced `strip_heading_marker()` helper using sed regex `^#{1,6}[[:space:]]+` for accurate prefix-only stripping. 3. **Self-test fixture format mismatch (coverage gap).** Self-test used bare-digit form ("7 standing R2 managers"); live briefs use markdown-bold ("**7** standing R2 managers"). Fixture proved Q5 for a format the live docs don't use, masking finding 1. Fix: updated all clean + drift fixtures in self-test to use markdown-bold form. Verifies Q5 catches the actual format. 4. **Q3 dissolution trigger (per debt-tracking discipline).** Previous "v2; the next narrowing opportunity" was a future bucket without a checkable trigger. Replaced with concrete trigger: "first reviewer-flagged status-string drift class that Q1/Q2/Q4/Q5 don't catch." Until that surfaces, status vocabulary is captured indirectly via Q5 count-projection consistency. Local checker + self-test still pass after fixes; Q5 now actually fires on live brief content rather than silently passing ceremonial. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(scripts+briefs): manager-brief Q2 prose-form check + align 7 citations Per gpt-5-5-pro BLOCKING on PR #1126 (sha:91b5274fc): Q2 explicitly excluded prose §"section name" citations, but live briefs use those for load-bearing INVARIANTS / r2-structure / design authority claims. The exclusion left 12 real authority drifts uncheckable. **Implements Q2-prose** (in addition to Q2-markdown-anchor): For each `§"quoted section"` or `§AnchorToken` in a brief: 1. Compute the prefix BEFORE this citation (running prefix; the bug in v0 was using before-first-§ for every iteration, so subsequent citations on the same line resolved against the first link's target instead of their own). 2. Find the most recent markdown link `[text](path)` in the prefix — that's the cited file. Fall back to bare `<NAME>.md` token via `resolve_authority_file()` (tries `$ROOT/`, `$ROOT/docs/`, `$ROOT/docs/thesis/`, `$ROOT/docs/briefs/`). 3. `grep -F` for the section text in the cited file. Permissive substring match (vs Q2-markdown's slug match) — accepts paraphrased section names while still catching the load-bearing "section deleted" failure mode. **Caught 12 real drifts on first run** — all now fixed: - r2-evaluator-manager.md: §"Goal 7 — Evaluator XL" + §"Evaluator Manager (added 2026-04-28 as Goal 7)" → §"Evaluator Manager (added 2026-04-28 amendment)" (matches r2-structure.md:159 actual heading) - r2-grounding-manager.md: §"Tier 1 — Structural correctness — Grounding completeness" → §"Tier 1 — Structural correctness" (matches THESIS.md:168 actual prose) - r2-impossible-bugs-manager.md (×2): §"R2 manager continuation" → §"Manager structure" (matches r3-structure.md:103 actual heading) - r2-impossible-bugs-manager.md: §Q1-Q3 + §Q6 → §Q1, §Q2, §Q3 + §Q6 (range citation didn't match anything literal; expand to discrete) - r2-release-manager.md (×2): §"R2 manager continuation" → §"Manager structure" - r2-release-manager.md (×2): §v2-guardrail-requirement-3 → §"v2 guardrail requirements" (matches r2-structure.md:490 body) - r2-substrate-manager.md: §"R3 lane structure" → §"Lane structure" (matches r3-structure.md:86 actual heading) Local checker + self-test still pass after fixes. Reinforces gpt-5-5-pro's earlier meta-observation: a checker that names a discipline but doesn't enforce it on the live format is documented cheating. Q2-prose closes that gap; the briefs' authority citations now have to match section text that actually exists. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(scripts): manager-brief — mktemp gh-stderr + Q1 false-pos trigger Per claude-opus-4-7 APPROVE-with-exploratory-observations on PR #1126 (sha:91b5274f). Two non-blocking cleanups landed. 1. **gh-stderr capture: $$ → mktemp.** Previous form used `/tmp/gh-stderr-$$` which is fine in CI but a crashed run on a shared dev box could leak the file. `mktemp` gives a unique path + paired cleanup in scope. 2. **Q1 false-positive trigger documented.** Q1 currently treats every `](path)` as a filesystem reference. Markdown reference- style link definitions and code-block examples containing `](foo)` would false-positive. No briefs use either form today; added DISSOLUTION TRIGGER comment naming the condition that would force context-aware extraction (skip fenced code blocks + reference definitions). The third observation (squash-merge for the WIP: Gunbc PM commits) is a merge-time decision; PR-level chore. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * WIP: Gunbc PM * fix(scripts): manager-brief Q4 case-insensitive + title-case test Per gpt-5-5-pro APPROVE_WITH_COMMENTS on PR #1126 (sha:91b5274f → 6dafaec): Q4 silently missed title-case "Landed via #N" claims. **Finding 1 (Q4 case sensitivity):** live briefs use three case forms of "landed via #N": - UPPERCASE — emphasized status-table claims (most common) - lowercase — inline prose ("landed via #900", "landed via #937", ...) - title-case — sentence-leading headings (r2-release-manager.md:113 "Landed via #1078:") The pre-fix regex `(LANDED|landed) via` missed the title-case form, silently passing any future unique `Landed via #N` claim. Fix: `grep -oEi 'landed via #[0-9]+'` (case-insensitive flag). **Finding 2 (Q4 self-test gap):** Q4 negative fixture used UPPERCASE "LANDED via #88888888"; positive fixture had no landed-PR claim at all. Title-case wasn't covered. Fixes: - Added `test_negative_q4_unreachable_pr_titlecase` using "Landed via #88888887" — verifies case-insensitive Q4 catches title-case. - Updated `write_clean_briefs` clean fixture to include "Substrate landed via #999" (lowercase, matching real brief format). Q4 positive path is now non-vacuous: tmp git repo seeds "(#999)" merge subject so this resolves cleanly. **Finding 3 (Q2 prose deferral note)**: STALE — Q2-prose was implemented in 97affdb (2 commits before this review). The reviewer cited line numbers from before the implementation; current code at `scripts/check-manager-brief-authority.sh:121` says "Two forms covered" not "v2 candidate". No action needed. Self-test now: 8 contract assertions (6 negative + 1 positive + 1 fail-closed-on-missing-brief). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(briefs): strip trailing whitespace at r2-evaluator-manager.md:46 Per codex review on PR #1126 (sha:3ba4f2c1): `git diff --check origin/main...HEAD` flagged trailing whitespace inside the PR-A-through-PR-E dependency-graph ASCII art. Removed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…1156) * WIP: Gunbc PM * WIP: Gunbc PM * WIP: Gunbc PM * WIP: Gunbc PM * docs(briefs): refresh 6 R2 manager briefs post-#1078 merge Aligns all 6 existing R2 manager briefs with #1078's locked design decisions and structural cascade: - Substrate Manager: adds T-Substrate-Lens-Primitive sub-lane (Q6+Q7+Q8 locks) + PR-PreF Interval<D> consolidation + R3 T-CostLens-Composition continuation (Director cascade Item 3); references INVARIANTS §P1 substrate-fact-introduction procedure + Q3 Cost<Unit> primitives. - Grounding Manager: engine-reframe to 11 lanes (5 substrate-completion lanes replace prior single Engine: Coercion-Fold + LanguageSpec + Lifetime-Analyzer + Diagnostic + CrossTarget-Meta); consumes PR-F through PR-J cadence; PR #989 footprint queued for cleanup wave. - Modeling Manager: int-lit item now consumes PR-PreF Interval<D> via Q1 lock; references INVARIANTS procedure for substrate-gap signaling. - Pure Bootstrap Manager: adds R3 continuation lanes (T-LensProducer- Retirement XL with 3 internal sub-gates per Director cascade Item 8; T-FixedPoint; T-Tier3-Dissolution; 3 distributed bridge retirements per Director cascade Item 4 — distribute work, centralize ledger). - Impossible-Bugs Manager: archives at R2 close per Director cascade; post-R2 emergent classes route to Substrate Manager continuation. - Release Manager: 6→7 manager count; closure ledger spans all 6 other managers + sub-gate progress for T-LensProducer-Retirement; structural- acceptance-per-lane-close discipline (demo IS structural gate); thesis-claim mapping landed via #1078, refresh authority lives here; v2 release-doc-authority guardrail follow-up added as next narrow PR. All 6 briefs now include: structural acceptance .dag TestClaim gates, locked-design-decisions-consumed section, INVARIANTS §P1 procedure references, and option-(c)-hybrid timing notes where R1-close-relevant. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * WIP: Gunbc PM * docs(briefs): R2 manager-brief status refresh against landed PRs Pre-spawn implementation work has progressed materially while #1078 was in flight; managers spawning today would otherwise dispatch against work that's already landed. Refresh per-manager status tables to reflect current main: - **Substrate:** ValueBody::Map landed (#1017 + #1068 tightening); NominalOpacity fail-closed field-projection enforcement (#937); B4.8 Phase-2 site dissolution landed (#1069); B4.2 first-consumer wiring landed; T-Cost-Dimension fail-closed precedent (#1003). - **Grounding:** Rust IntegerRangeFact mirror dissolved (#1005); Python primitives.dag landed (#1080); Go primitives tranche 1 + additional (ac765ce + #1046); T-Ground-Engine Phase 2 slice 1 (c0cc8b2) noted as pre-cascade footprint queued for cleanup wave per design-emission-model.md option (c). - **Impossible-Bugs:** all 3 main implementations LANDED pre-spawn (nested-optional flatten #890 + #962 follow-ups; Int/Int totality- by-omission first slice #969; unenumerated effects lens landing #971). Day-1 work is class-close completion + sibling totalization dispatch (indexing/quotient/remainder), not initial implementation. - **Pure Bootstrap:** kernel_algebra_profile substrate met via #1017 + #1068 (consumer plumbing now the remaining R2 work, dispatchable Day-1); T-PB-Runtime ExecuteCommand typed-outcome hardening (#1049) + T-PB-B boundary coverage (#1082) advanced PB-Runtime foundation for R3 lens_apply.rs retirement gate. - **Modeling:** Secret<T> producer side substantially advanced (#900 carrier + #937 fail-closed enforcement); tokenizer charclass scanner-order retype landed pre-cascade (242c65d); SourceFiltering canonical authority precedent (#1004). - **Release:** initial closure-ledger snapshot now reflects all pre-spawn landings (Impossible-Bugs/Substrate/Ground/PB-Runtime). Evaluator brief unchanged — new lane added 2026-04-28; nothing landed yet (gated on PR-A through PR-E design lock cadence). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * WIP: Gunbc PM * docs(briefs): align Substrate Produces + Release R2-close acceptance Per gpt-5-5-pro REQUEST_CHANGES on PR #1126 (sha:a9285fce). Two valid findings on coordination-contract internal consistency: 1. **Substrate Produces list omitted ValueBody::Map → PB signal.** The deliverables table at line 94 named ValueBody::Map as an R2 unblocker for PB's kernel_algebra_profile mirror dissolution, but the cross-program "Produces" section listed 5 signals and did not include this one. Same dependency represented in two places with different authority. Fix: add ValueBody::Map carrier read-path/API + arrow-body evaluation as the 6th produced signal targeted at PB Manager; update count from 5 to 6 (also in Reporting-cadence line 150). Remove the "Adjacent territory" note about kernel_algebra_profile being a future sub-lane — substrate already landed via #1017+#1068. 2. **Release R2-close acceptance gate excluded PB from close criterion.** The brief's "Consumes" section correctly named all 6 other managers including PB, but the r2_close_signal_to_director_authored gate at line 103 used "5 R2-archiving managers" (Substrate-prereq / Modeling / Grounding / Impossible-Bugs / Evaluator) — could fire the R2-close signal while PB's R2-scope lanes (Tier 3 mirror dissolutions + kernel_algebra_profile consumer plumbing) are still open. Fix: gate becomes "all 6 other managers' R2-scope lanes complete" with explicit lane-set listed per manager. Distinguish R2-scope completion from manager-archives (Modeling/Impossible-Bugs archive; Substrate/PB continue into R3 with R3-scoped lanes — those don't gate R2 close). Both are P2 single-authority alignments; no scope change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * WIP: Gunbc PM * feat(scripts): manager-brief authority consumer + self-test Per gpt-5-5-pro meta-review on PR #1126: dissolves the recurring "non-live authority consumed as live" pattern that surfaced 5+ times during PR #1078 + #1126 review loops (codex tooling false-positives naming non-existent files / sections; cursor-flagged single-authority drift on Goal numbering + R3 continuation count). v1 covers 4 of gpt-5-5-pro's 5 questions: - **Q1 — cited file existence** — extracts markdown links from each brief, resolves relative paths, fails closed if any cited file doesn't exist on disk. - **Q2 — cited section anchor existence** — for `path#anchor` links, verifies the anchor matches a slugified heading in the target file. - **Q4 — `LANDED via #N` reachability** — two-stage check: (a) fast `git log --grep="(#N)"` for normal merge subjects, (b) fall back to `gh pr view` for squash-merges that drop the suffix (caught a real case for PR #900). Verifies merge SHA is `git merge-base --is-ancestor HEAD`. - **Q5 — cross-brief projection consistency** — extracts manager/lane counts and verifies all briefs that mention a projection agree both cross-brief AND with canonical values from r2-structure.md / r3-structure.md (7 standing managers, 6 other managers, 10 R3 lanes, 7 of 10 Evaluator-gated). Catches drift like "5 R2-archiving managers" vs "all 6 other managers". Q3 (controlled status vocabulary) deferred to v2 — too subjective for a mechanical check; tracked in script header as next narrowing. **Self-test** (`scripts/test-check-manager-brief-authority.sh`): 7 contract assertions — negative cases for each of Q1/Q2/Q4/Q5 (×2) + fail-closed-on-missing-brief + positive case. Mirrors the `test-check-release-doc-authority.sh` pattern. **Wiring:** - Makefile: `manager-brief-authority-check` + `-test` targets; `verify` runs the check. - CI workflow: both check + self-test wired as named steps; check receives `GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}` for the gh API fallback in Q4. **SIGPIPE under pipefail caveat documented inline:** `grep -q .` on a piped `git log` causes pipefail to report failure (grep exits early, git log gets SIGPIPE 141). Workaround: capture output and test `-z`/`-n`. Pinned in Q4 implementation comment. Closes the convergence move gpt-5-5-pro proposed; future review loops that hit the same "non-live authority" class get caught at CI rather than reviewer-by-reviewer prose iteration. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(scripts): manager-brief Q4 — gh-first for CI shallow-clone compat CI failed on the first run of the checker because the workflow uses fetch-depth=1 (shallow clone), so the Q4 fall-back stage's git-log --grep="(#N)" can't see merge history. The two-stage check worked locally because git log had full history; in CI it found nothing on either stage. Restructure Q4 to gh-first: - **Stage 1 (primary):** gh pr view N --json state — returns MERGED for actually-merged PRs regardless of clone depth or squash-merge subject variance. CI passes GH_TOKEN automatically. - **Stage 2 (fallback):** git log --grep — kept for offline dev / auth-blocked environments. In CI with fetch-depth=1 this stage finds nothing; that's why Stage 1 is primary. Reasoning: "is this PR actually merged" is what we want to verify; gh state=MERGED answers it directly. The previous git-log+ancestor check was defense-in-depth, but actually fragile in shallow clones which is the CI default. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(scripts): manager-brief check — set -e + return interaction Per claude-opus-4-7 review on PR #1126: three non-blocking findings addressed. 1. **set -e + return non-zero**: the per-brief driver loop used `check_q1_file_existence "$brief"; rc=$?` — under set -euo pipefail, a function returning non-zero is treated as a failed command and exits the script before the accumulator runs. Result was "stop at first failing brief," not "report all violations in one pass" as intended. Fix: use `|| rc=$?` form (with explicit `rc=0` reset). This keeps set -e from firing on expected-non-zero returns while still capturing the count. 2. **Q2 doc/code mismatch**: header comment promised both `path#anchor` markdown form AND `§"section name"` prose form. Implementation only handled markdown. Trim the comment to match the code; track prose-form in v2 follow-up alongside Q3 status-vocabulary as next narrowing. 3. **Q5 pattern overlap (exploratory)**: claude-opus-4-7 flagged that "standing managers" might substring-match "standing R2 managers". Empirical check shows it doesn't (POSIX regex requires the exact "standing managers" sequence; "R2 " breaks the match). Documented inline; no pattern change needed. 8-bit return-code truncation noted by reviewer is theoretical at current scale (briefs typically have <10 violations) and is now moot since the global `violations` accumulator is plain bash arithmetic; only the per-function `return` is uint8-bounded. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(scripts): manager-brief Q4 — explicit --repo for CI gh detection CI failed Q4 even after gh-first restructure: actions/checkout@v4's shallow clone exposes the remote in a form 'gh pr view' doesn't always auto-detect, so the stage-1 gh call returned empty (no error message in v1 because stderr was redirected to /dev/null) and the stage-2 git-log fallback also failed (shallow clone has no merge history). Three fixes in this commit: 1. **Derive REPO_SLUG from `git config remote.origin.url`** at script start. Falls back to "gunb-ai/gunbc" if origin isn't readable (self-test runs in tmpdir with no remote). 2. **Pass `--repo "$REPO_SLUG"` explicitly to `gh pr view`** so it doesn't have to infer from the cwd's git remote. 3. **Capture gh stderr** to a temp file and surface it in the violation diagnostic. If gh is auth-failing or rate-limited, the violation message now shows why instead of looking like "PR doesn't exist." Both checker + self-test still pass locally. CI should now succeed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(ci): grant pull-requests:read for manager-brief authority check The check uses `gh pr view --json state` to verify "LANDED via #N" claims map to MERGED PRs. Default GITHUB_TOKEN scopes only include contents:read; pull-request access fails with: GraphQL: Resource not accessible by integration (repository.pullRequest) Surfaced when the script's stderr-capture fix (ea33aeb) made the actual error message visible — diagnostic improvement paid off immediately. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * WIP: Gunbc PM * fix(scripts): manager-brief checker — markdown-bold + heading-strip + Q3 trigger Per gpt-5-5-pro REQUEST_CHANGES on PR #1126 (sha:ea33aeb9). Three findings — one BLOCKING (Q5 was ceremonial on its load-bearing projection), one secondary (heading-strip glob bug), one coverage gap. 1. **Q5 markdown-bold mismatch (BLOCKING).** Live briefs use `Names this manager one of **7** standing R2 managers` — markdown emphasis around the count. The pre-fix regex `[0-9]+ standing R2 managers` required a bare leading digit, so it matched zero claims on every brief. Counts_seen stayed empty → "0 counts seen → silent OK" branch fired → check passed ceremonially. A future drift to `**6** standing R2 managers` would have been invisible. Fix: regex now optionally accepts `**` before and after the digit: `\*?\*?[0-9]+\*?\*? standing R2 managers`. Extraction strips asterisks (`tr -d '*'`) before parsing. Verified on live briefs: $ grep -oE '\*?\*?[0-9]+\*?\*? standing R2 managers' docs/briefs/r2-*-manager.md docs/briefs/r2-evaluator-manager.md:**7** standing R2 managers docs/briefs/r2-grounding-manager.md:**7** standing R2 managers docs/briefs/r2-impossible-bugs-manager.md:**7** standing R2 managers docs/briefs/r2-modeling-manager.md:**7** standing R2 managers docs/briefs/r2-pure-bootstrap-manager.md:**7** standing R2 managers docs/briefs/r2-release-manager.md:**7** standing R2 managers docs/briefs/r2-substrate-manager.md:**7** standing R2 managers Now actually catches all 7 briefs' projections. 2. **Heading-strip glob bug (Q2 secondary).** `${heading##\#* }` is a Bash glob that strips through the LAST space, so "## Goal 7 — Evaluator XL" becomes "XL" instead of "Goal 7 — Evaluator XL". Multi-word heading anchors silently false-fail. Fix: introduced `strip_heading_marker()` helper using sed regex `^#{1,6}[[:space:]]+` for accurate prefix-only stripping. 3. **Self-test fixture format mismatch (coverage gap).** Self-test used bare-digit form ("7 standing R2 managers"); live briefs use markdown-bold ("**7** standing R2 managers"). Fixture proved Q5 for a format the live docs don't use, masking finding 1. Fix: updated all clean + drift fixtures in self-test to use markdown-bold form. Verifies Q5 catches the actual format. 4. **Q3 dissolution trigger (per debt-tracking discipline).** Previous "v2; the next narrowing opportunity" was a future bucket without a checkable trigger. Replaced with concrete trigger: "first reviewer-flagged status-string drift class that Q1/Q2/Q4/Q5 don't catch." Until that surfaces, status vocabulary is captured indirectly via Q5 count-projection consistency. Local checker + self-test still pass after fixes; Q5 now actually fires on live brief content rather than silently passing ceremonial. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(scripts+briefs): manager-brief Q2 prose-form check + align 7 citations Per gpt-5-5-pro BLOCKING on PR #1126 (sha:91b5274fc): Q2 explicitly excluded prose §"section name" citations, but live briefs use those for load-bearing INVARIANTS / r2-structure / design authority claims. The exclusion left 12 real authority drifts uncheckable. **Implements Q2-prose** (in addition to Q2-markdown-anchor): For each `§"quoted section"` or `§AnchorToken` in a brief: 1. Compute the prefix BEFORE this citation (running prefix; the bug in v0 was using before-first-§ for every iteration, so subsequent citations on the same line resolved against the first link's target instead of their own). 2. Find the most recent markdown link `[text](path)` in the prefix — that's the cited file. Fall back to bare `<NAME>.md` token via `resolve_authority_file()` (tries `$ROOT/`, `$ROOT/docs/`, `$ROOT/docs/thesis/`, `$ROOT/docs/briefs/`). 3. `grep -F` for the section text in the cited file. Permissive substring match (vs Q2-markdown's slug match) — accepts paraphrased section names while still catching the load-bearing "section deleted" failure mode. **Caught 12 real drifts on first run** — all now fixed: - r2-evaluator-manager.md: §"Goal 7 — Evaluator XL" + §"Evaluator Manager (added 2026-04-28 as Goal 7)" → §"Evaluator Manager (added 2026-04-28 amendment)" (matches r2-structure.md:159 actual heading) - r2-grounding-manager.md: §"Tier 1 — Structural correctness — Grounding completeness" → §"Tier 1 — Structural correctness" (matches THESIS.md:168 actual prose) - r2-impossible-bugs-manager.md (×2): §"R2 manager continuation" → §"Manager structure" (matches r3-structure.md:103 actual heading) - r2-impossible-bugs-manager.md: §Q1-Q3 + §Q6 → §Q1, §Q2, §Q3 + §Q6 (range citation didn't match anything literal; expand to discrete) - r2-release-manager.md (×2): §"R2 manager continuation" → §"Manager structure" - r2-release-manager.md (×2): §v2-guardrail-requirement-3 → §"v2 guardrail requirements" (matches r2-structure.md:490 body) - r2-substrate-manager.md: §"R3 lane structure" → §"Lane structure" (matches r3-structure.md:86 actual heading) Local checker + self-test still pass after fixes. Reinforces gpt-5-5-pro's earlier meta-observation: a checker that names a discipline but doesn't enforce it on the live format is documented cheating. Q2-prose closes that gap; the briefs' authority citations now have to match section text that actually exists. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(scripts): manager-brief — mktemp gh-stderr + Q1 false-pos trigger Per claude-opus-4-7 APPROVE-with-exploratory-observations on PR #1126 (sha:91b5274f). Two non-blocking cleanups landed. 1. **gh-stderr capture: $$ → mktemp.** Previous form used `/tmp/gh-stderr-$$` which is fine in CI but a crashed run on a shared dev box could leak the file. `mktemp` gives a unique path + paired cleanup in scope. 2. **Q1 false-positive trigger documented.** Q1 currently treats every `](path)` as a filesystem reference. Markdown reference- style link definitions and code-block examples containing `](foo)` would false-positive. No briefs use either form today; added DISSOLUTION TRIGGER comment naming the condition that would force context-aware extraction (skip fenced code blocks + reference definitions). The third observation (squash-merge for the WIP: Gunbc PM commits) is a merge-time decision; PR-level chore. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * WIP: Gunbc PM * fix(scripts): manager-brief Q4 case-insensitive + title-case test Per gpt-5-5-pro APPROVE_WITH_COMMENTS on PR #1126 (sha:91b5274f → 6dafaec): Q4 silently missed title-case "Landed via #N" claims. **Finding 1 (Q4 case sensitivity):** live briefs use three case forms of "landed via #N": - UPPERCASE — emphasized status-table claims (most common) - lowercase — inline prose ("landed via #900", "landed via #937", ...) - title-case — sentence-leading headings (r2-release-manager.md:113 "Landed via #1078:") The pre-fix regex `(LANDED|landed) via` missed the title-case form, silently passing any future unique `Landed via #N` claim. Fix: `grep -oEi 'landed via #[0-9]+'` (case-insensitive flag). **Finding 2 (Q4 self-test gap):** Q4 negative fixture used UPPERCASE "LANDED via #88888888"; positive fixture had no landed-PR claim at all. Title-case wasn't covered. Fixes: - Added `test_negative_q4_unreachable_pr_titlecase` using "Landed via #88888887" — verifies case-insensitive Q4 catches title-case. - Updated `write_clean_briefs` clean fixture to include "Substrate landed via #999" (lowercase, matching real brief format). Q4 positive path is now non-vacuous: tmp git repo seeds "(#999)" merge subject so this resolves cleanly. **Finding 3 (Q2 prose deferral note)**: STALE — Q2-prose was implemented in 97affdb (2 commits before this review). The reviewer cited line numbers from before the implementation; current code at `scripts/check-manager-brief-authority.sh:121` says "Two forms covered" not "v2 candidate". No action needed. Self-test now: 8 contract assertions (6 negative + 1 positive + 1 fail-closed-on-missing-brief). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(briefs): strip trailing whitespace at r2-evaluator-manager.md:46 Per codex review on PR #1126 (sha:3ba4f2c1): `git diff --check origin/main...HEAD` flagged trailing whitespace inside the PR-A-through-PR-E dependency-graph ASCII art. Removed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(scripts): manager-brief Q2-prose digit-leading + negative test Per gpt-5-5-pro REQUEST_CHANGES on PR #1126 (sha:b9f7a1c1): Q2-prose extractor required §-followed-by-letter, silently skipping the digit-leading citation forms used in the same diff. Live brief usage caught: §4 — r2-evaluator/grounding/impossible-bugs/modeling/pure-bootstrap §6a — r2-modeling-manager.md (cite of design-substrate-carrier-port-program §6a) §0.7 — r2-pure-bootstrap-manager.md (cite of debt-paydown-synthesis §0.7) §5 — r2-release-manager.md All previously skipped → "Q2 (prose §) resolved" was vacuously true on those lines. Fix: regex `§[A-Za-z][A-Za-z0-9._-]*[A-Za-z0-9]|§[A-Za-z]` → `§[A-Za-z0-9][A-Za-z0-9._-]*[A-Za-z0-9]|§[A-Za-z0-9]` (extends [A-Za-z]-leading to [A-Za-z0-9]-leading; quoted form unchanged). Documented limitation: short digit-only tokens like §4 resolve permissively because grep -F "4" matches anywhere; multi-character tokens like §6a are discriminating. Self-test gap (also flagged): added `test_negative_q2_missing_prose_numeric_section` using §99zzz (digit-leading, multi-char so substring match doesn't trivially pass). Verifies regex extraction triggers Q2-prose violation on digit-leading citation drift. Self-test now: 9 contract assertions (7 negative + 1 positive + 1 fail-closed-on-missing-brief). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(briefs): consume Tier 1 design locks 1+2+3 from #1129 Director landed Items 1+2+3 design locks together via #1129 (`e1afabe47`): - Item 1 (Q1 asymmetric bound algebra) — `docs/design-emission-model.md` §"Q1 — `BoundDeclaration` substrate type" - Item 2 (reflection completeness) — NEW `docs/design-reflection-completeness.md` - Item 3 (Q6.5 two-layer diagnostic-kind) — `docs/design-lens-framework.md` §"Q6.5 — Two-layer authority for diagnostic kinds" Per agreed PM role on inbox #828: as each design-lock doc lands, PM consumes the lock into worker brief updates (statuses move from PENDING/gated → LIVE; cited authority anchors verified by the manager-brief authority checker). Mostly mechanical. Brief updates: - **Substrate** (3 sites): T-Substrate-Lens-Primitive flips from "gated on PR-K" to "Q6/Q6.5/Q7/Q8 LANDED via #1129; ready to dispatch"; "Diagnostic-kind extensibility (Q6 lock)" replaced with the locked Q6.5 two-layer authority cite (Layer 1 closed sum Substrate-owned; Layer 2 lens-instance via inhabitance; additive widening of `Diagnostic.kind` named). - **Evaluator** (5 sites): "Lens application gated on PR-C" → cites the landed reflection-completeness doc; PR-C row in cadence table flips to LANDED; Q6 disposition becomes Q6+Q6.5 with explicit cite to design-lens-framework.md §Q6.5; "Reflection completeness lives in PR-C" → "lives in design-reflection-completeness.md (LANDED via #1129)"; PR-C worker brief in pending list crossed out as superseded. - **Modeling** (1 site): status header now cites Q1 lock landing with explicit anchor; int-lit item already references Interval<D> via PR-PreF. - **Grounding** (2 sites): T-Ground-Diagnostic lane and Substrate- Manager-cross-program-dependency cite Q6.5 — clarifies lane is Layer-1 consumer (not Layer-2 author), no cross-manager handoff. - **Pure Bootstrap** (1 site): Q6 disposition becomes Q6+Q6.5 + reflection-completeness cite added (load-bearing for R3-T- LensProducer-Retirement per design-reflection-completeness.md §"Cascade and gates" §7.3). - **Impossible-Bugs** (1 site): Q6 cite becomes Q6+Q6.5; classes consume Layer 1, not author Layer 2. Verified: `bash scripts/check-manager-brief-authority.sh` passes all 7 briefs (Q1/Q2-md/Q2-prose/Q4/Q5); 9 contract assertions in self-test still pass. Note: one brief edit required restructuring (modeling-manager.md:3) because the original cite put §"section" inside the markdown link's display text, while the heuristic finds the rightmost `](path)` BEFORE the §. Moved cite outside the link to align: `[file.md](path) §"section"`. Same pattern as other landed cites; the checker enforces it structurally. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(scripts): manager-brief — concrete dissolution trigger for short-digit § limitation Per codex APPROVE_WITH_COMMENTS on PR #1156 (sha:00540f36): the short digit-only § resolve-permissively limitation was documented and bounded but lacked a concrete dissolution trigger. Updated to match Q3 dissolution-trigger discipline: trigger fires on first reviewer-flagged stale `§N` (single-digit) citation that survives the substring check because the digit appears elsewhere in the target file. At that point the check tightens to require structural context — match `§N` only if the target has a heading `## N`, `### N`, etc. or numbered-list item at column 0. Until that surfaces, multi-character disambiguation is the load-bearing discriminator (and live briefs predominantly use multi-char forms — §P1, §Q6, §Q6.5, §"Lane structure" — so single-digit `§4` citations are uncommon). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(design): consume Q6.5 lock in worked examples + r2-structure Q6 row Per Director (zesty-bear-812) endorsement on inbox #828: fold the design-doc Q6.5-consumption edits originally drafted in PR #1137 (jolly-ram-908) into the canonical consumption PR. Single-sourced consumption story; #1137 ends up as a clean no-op redirect. 8 lens-framework worked-example reframes + 1 r2-structure Q6 row update. All consume the Q6.5 two-layer authority disposition landed via #1129: **design-lens-framework.md (8 sites):** - §"Lens<TenantFlow>" `validate(dag, set)`: "new CompilerDiagnosticKind variant" → "lens-local diagnostic-kind declaration" - §"Lens<IFC>" `validate(dag, label)`: same reframe for IFCDowngradeViolation - §"D5 Failure modes": "appropriate CompilerDiagnosticKind variant (lens instances may extend CompilerDiagnosticKind...)" → "appropriate lens-local diagnostic-kind declaration" - §Q6 alternative (d): "pushes structural failure data into Diagnostic.kind (which is CompilerDiagnosticKind sum type — already extends per-instance per feedback_state_space_vs_behavioral_invariants)" → "pushes structural failure data into lens-local Diagnostic.kind declarations" - §Q6 anti-bridge claim renaming `no_string_parsing_in_witness_consumers` description: "Diagnostic.kind extensions" → "lens-local Diagnostic.kind declarations" - §Q6 Recommendation (d): "encode into Diagnostic.kind sum-type variants. Lens instances ... extend CompilerDiagnosticKind with their own variants" → "encode into lens-local Diagnostic.kind declarations. Lens instances ... declare their own kinds beside the lens instance" - §Q6 DECISION line: "(c)/(d) hybrid — Witness<C> stays as-is; rich structural validation failures encode into Diagnostic.kind extensions via the lens-framework's structural inhabitance" → same with "lens-local Diagnostic.kind declarations"; date stamp augmented with "refined 2026-04-29" - §Q6 Director's framing #1: "CapabilityViolation as a CompilerDiagnosticKind variant is uniform" → "CapabilityViolation as a lens-local diagnostic-kind declaration is uniform" **r2-structure.md (1 site):** §"Q1-Q8 disposition" Q6 row updated to match design-lens-framework's locked language: "encode into Diagnostic.kind extensions via lens-framework's structural inhabitance" → "encode into lens-local Diagnostic.kind declarations via lens-framework structural inhabitance, not into the closed compiler-core CompilerDiagnosticKind sum". These edits are *editorial* — the Q6.5 lock at design-lens-framework.md §"Q6.5 — Two-layer authority for diagnostic kinds" remains the canonical authority; this just aligns the worked examples + r2- structure summary row with that canonical phrasing so future readers don't see the older "extends CompilerDiagnosticKind" framing in worked examples and assume it survived. Verified: manager-brief authority check passes (7 briefs / 0 violations); 9 contract assertions in self-test pass; release-doc authority check passes. Per inbox #828 + #1130 coordination: jolly-ram-908 confirmed PR #1137 will close as redundant once #1156 lands (the brief edits were already absorbed by my prior consumption pass; these design-doc edits are the residual that's now folded in). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * WIP: Gunbc PM --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Worker 4 of the cleanup brief pack: harden
ExecuteCommandhost-process boundary insrc/v3/compiler/src/test_runner.rsagainst INVARIANTS.md P2 host-process (a)–(e). Structural closure complete. ReplacesOther(ClaimResult)partial carrier with a 6-variant typed model and dissolves namespace setup detection via thegunbc_execute_command_bootstraphelper binary from #1063 — atomicFD_CLOEXECon fd 3 betweenexecvp-probe andexecvpmakes theb"sef"-spoof discriminator structurally unreachable.Director-confirmed sequence: PR #1063 (helper-binary substrate) merged → this PR wires
build_execute_command_unshareto invoke the helper as the unshare target instead ofsh -c '<bootstrap>'.Typed model — current-path-to-variant mapping
reject_unbounded_shell_backgroundOther(Fail(SHELL_C_BACKGROUND_UNBOUNDED_FAIL))PolicyRejected { ShellBackgroundUnbounded }Other(Fail("...wrapper... direct..."))SpawnFailed { wrapper: Some(_), direct }Other(Fail("spawn error..."))SpawnFailed { wrapper: None, direct }child_wait_for_execute_commandErr — wall timeoutOther(Fail("...exceeded ... wall-clock..."))WaitFailed(WaitFail::WallTimeout { wall_time })child_wait_for_execute_commandErr — wait IOOther(Fail("wait on child failed..."))WaitFailed(WaitFail::Io(_))read_sentinelErr (post-helper, post-reap)WaitFailed(WaitFail::Io(_))(no implicit re-run)SetupFailed { NamespaceSetupAndDirectSpawnFailed }status.code() == None(signal-terminated)Other(Fail("child terminated by signal..."))WaitFailed(WaitFail::TerminatedBySignal)execfailure (was:unshare:stderr scan)Other(Fail(...))after relaunch pathSetupFailed { NamespaceSetupAndDirectSpawnFailed }or a successful direct fallback'sMatched/MismatchOther(Fail(...))afterNonzeroHostConfirmre-execSpawnFailed { wrapper: None, direct: "...not executable..." }execvpreturned (TOCTOU/ENOEXEC/ETXTBSY)SpawnFailed { wrapper: None, direct: "...failed to exec..." }(LogicalExecFailed, structurally unspoofable post-helper)Bootstrap protocol — helper binary (#1063)
build_execute_command_unshareinvokes:Helper protocol on fd 3:
s— helper started.e—execvp-equivalent probe passed (path operands →access(F_OK | X_OK); bare names →PATHlookup, matchingexecvp(3)semantics).fcntl(3, F_SETFD, FD_CLOEXEC)— structural close of the sentinel channel scheduled for the nextexec.execvp(command, args). On success, kernel atomically closes fd 3 in the new image; sentinel =b"se"+ EOF.ENOEXEC,ETXTBSY), helper writesfand exits 126/127; sentinel =b"sef".Helper-path resolution (
locate_unshare_bootstrap_helper):GUNBC_EXECUTE_COMMAND_BOOTSTRAP=/abs/pathfor non-standard installs.current_exe()to findtarget/<profile>/gunbc_execute_command_bootstrap(one level for direct binary, two for tests viatarget/<profile>/deps/).SetupFailed. No silent degradation to a spoofable bootstrap.Sentinels (exact-byte match, no prefix scan):
b""NamespaceSetupFailedb"s"LogicalCommandNotExecutableSpawnFailedb"se"LogicalCommandExecedb"sef"LogicalExecFailed(structurally unspoofable post-#1063)SpawnFailedb"se*"(defense-in-depth)LogicalCommandExecedNamespaceSetupFailedP2(a)–(e) realization
(a) Typed results, not string authority.
Other(ClaimResult)partial carrier deleted. Every reachable failure has a named variant;Fail(String)is rendered only byExecuteCommandHostOutcome::into_claim_resultat the reporting edge.(b) Isolated logical-child I/O. Wrapper stderr is
/dev/null. The only structural channel between helper and parent is the typed sentinel pipe. With CLOEXEC, fd 3 is closed atomically in the user's address space on successfulexecvp.(c) Setup failure ≠ logical exit — STRUCTURAL.
LogicalExecFailed(b"sef") is unspoofable post-helper: only the helper itself can writef(afterexecvpreturns), becauseFD_CLOEXECis set beforeexecvpso a successful exec closes fd 3 atomically and the user receivesEBADFon anywrite(3, ...). Theunshare_logical_command_writing_f_to_fd3_is_not_misclassified_after_helper_landsreceipt directly verifies this.(d) No implicit re-execution. Three pre-existing implicit re-exec paths are gone:
ExitMismatchEmptyWrapperStderr(env-gated empty-stderr relaunch) — deleted.NonzeroHostConfirm(always-on for nonzero exit match on unshare path) — deleted.PostStartFallback— re-classified as the first logical run (the unshare wrapper never reachedexecvp).Plus defense-in-depth: stray bytes after
b"se"map toLogicalCommandExeced(notNamespaceSetupFailed→ direct fallback), andread_sentinelerrors surface asWaitFailed::Io(not direct fallback).(e) Drains and authority. With wrapper stderr at
/dev/null, no parent-readable stream carries evidence — the entireLinuxPipedChildStderrNonblockGuard/linux_drain_piped_child_stderr_nonblocking_oncemachinery and theunshare_stderr_draincapture buffer are deleted.Test plan
cargo test -p v3-compiler --lib test_runner— 38/38 pass, 0 ignored.unshare_path_actually_engages_user_runs_as_pid_1_in_new_namespace— proves namespace engagementunshare_logical_command_writing_f_to_fd3_is_not_misclassified_after_helper_lands— structurally enforces the helper-binary AC (sef-spoof unreachable)unshare_post_exec_fd3_write_does_not_trigger_implicit_rerun— defense-in-depthunshare_existing_non_executable_file_with_expect_126_does_not_match— manager-requested P2(c) regressioncargo build -p v3-compiler— clean.cargo clippy -p v3-compiler --all-targets -- -D warnings— clean.cargo build -p execute-command-bootstrap— required prereq for tests; CI builds workspace.Status
unshare_logical_command_writing_f_to_fd3_is_not_misclassified_after_helper_lands— not just "helper exists."Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com