Skip to content

T-PB-Runtime ExecuteCommand typed-outcome hardening + helper-binary wiring (Worker 4) - #1049

Merged
briansrls merged 46 commits into
mainfrom
session/gentle-deer-205
Apr 28, 2026
Merged

briansrls merged 46 commits into
mainfrom
session/gentle-deer-205

Conversation

@briansrls

@briansrls briansrls commented Apr 27, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Worker 4 of the cleanup brief pack: harden ExecuteCommand host-process boundary in src/v3/compiler/src/test_runner.rs against INVARIANTS.md P2 host-process (a)–(e). Structural closure complete. Replaces Other(ClaimResult) partial carrier with a 6-variant typed model and dissolves namespace setup detection via the gunbc_execute_command_bootstrap helper binary from #1063 — atomic FD_CLOEXEC on fd 3 between execvp-probe and execvp makes the b"sef"-spoof discriminator structurally unreachable.

Director-confirmed sequence: PR #1063 (helper-binary substrate) merged → this PR wires build_execute_command_unshare to invoke the helper as the unshare target instead of sh -c '<bootstrap>'.

Typed model — current-path-to-variant mapping

ExecuteCommandHostOutcome (6 variants, was 3 with Other(_) partial carrier):
  Matched
  Mismatch { expected, actual }
  PolicyRejected { policy: PolicyReject }
  SpawnFailed { wrapper: Option<String>, direct: String }
  SetupFailed { reason: SetupFailReason }
  WaitFailed(WaitFail)             // WallTimeout | Io | TerminatedBySignal
Old path Old carrier New variant
reject_unbounded_shell_background Other(Fail(SHELL_C_BACKGROUND_UNBOUNDED_FAIL)) PolicyRejected { ShellBackgroundUnbounded }
unshare wrapper spawn err + direct spawn err (linux) Other(Fail("...wrapper... direct...")) SpawnFailed { wrapper: Some(_), direct }
direct spawn err (non-linux, or unshare unavailable) Other(Fail("spawn error...")) SpawnFailed { wrapper: None, direct }
child_wait_for_execute_command Err — wall timeout Other(Fail("...exceeded ... wall-clock...")) WaitFailed(WaitFail::WallTimeout { wall_time })
child_wait_for_execute_command Err — wait IO Other(Fail("wait on child failed...")) WaitFailed(WaitFail::Io(_))
read_sentinel Err (post-helper, post-reap) (would have collapsed to NamespaceSetupFailed) WaitFailed(WaitFail::Io(_)) (no implicit re-run)
helper binary missing (config error) (would have silently degraded to direct) SetupFailed { NamespaceSetupAndDirectSpawnFailed }
status.code() == None (signal-terminated) Other(Fail("child terminated by signal...")) WaitFailed(WaitFail::TerminatedBySignal)
util-linux pre-exec failure (was: unshare: stderr scan) Other(Fail(...)) after relaunch path SetupFailed { NamespaceSetupAndDirectSpawnFailed } or a successful direct fallback's Matched/Mismatch
logical command not executable (probe) Other(Fail(...)) after NonzeroHostConfirm re-exec SpawnFailed { wrapper: None, direct: "...not executable..." }
logical command exec'd but execvp returned (TOCTOU/ENOEXEC/ETXTBSY) could surface as logical exit (P2(c) gap) SpawnFailed { wrapper: None, direct: "...failed to exec..." } (LogicalExecFailed, structurally unspoofable post-helper)

Bootstrap protocol — helper binary (#1063)

build_execute_command_unshare invokes:

unshare -c -f -p -- <gunbc_execute_command_bootstrap> <command> <args...>

Helper protocol on fd 3:

  • s — helper started.
  • e — execvp-equivalent probe passed (path operands → access(F_OK | X_OK); bare names → PATH lookup, matching execvp(3) semantics).
  • fcntl(3, F_SETFD, FD_CLOEXEC) — structural close of the sentinel channel scheduled for the next exec.
  • execvp(command, args). On success, kernel atomically closes fd 3 in the new image; sentinel = b"se" + EOF.
  • On failure (TOCTOU, ENOEXEC, ETXTBSY), helper writes f and exits 126/127; sentinel = b"sef".

Helper-path resolution (locate_unshare_bootstrap_helper):

  • Env override: GUNBC_EXECUTE_COMMAND_BOOTSTRAP=/abs/path for non-standard installs.
  • Otherwise: walk from current_exe() to find target/<profile>/gunbc_execute_command_bootstrap (one level for direct binary, two for tests via target/<profile>/deps/).
  • Helper missing → typed SetupFailed. No silent degradation to a spoofable bootstrap.

Sentinels (exact-byte match, no prefix scan):

Sentinel Stage Outcome
b"" NamespaceSetupFailed direct fallback (first logical run)
b"s" LogicalCommandNotExecutable SpawnFailed
b"se" LogicalCommandExeced classify by exit code
b"sef" LogicalExecFailed (structurally unspoofable post-#1063) SpawnFailed
b"se*" (defense-in-depth) LogicalCommandExeced classify by exit code (no re-run)
else fail-closed NamespaceSetupFailed direct fallback

P2(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 by ExecuteCommandHostOutcome::into_claim_result at 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 successful execvp.

(c) Setup failure ≠ logical exit — STRUCTURAL. LogicalExecFailed (b"sef") is unspoofable post-helper: only the helper itself can write f (after execvp returns), because FD_CLOEXEC is set before execvp so a successful exec closes fd 3 atomically and the user receives EBADF on any write(3, ...). The unshare_logical_command_writing_f_to_fd3_is_not_misclassified_after_helper_lands receipt 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 reached execvp).

Plus defense-in-depth: stray bytes after b"se" map to LogicalCommandExeced (not NamespaceSetupFailed → direct fallback), and read_sentinel errors surface as WaitFailed::Io (not direct fallback).

(e) Drains and authority. With wrapper stderr at /dev/null, no parent-readable stream carries evidence — the entire LinuxPipedChildStderrNonblockGuard / linux_drain_piped_child_stderr_nonblocking_once machinery and the unshare_stderr_drain capture buffer are deleted.

Test plan

  • cargo test -p v3-compiler --lib test_runner — 38/38 pass, 0 ignored.
    • 5 sentinel-classifier unit tests
    • unshare_path_actually_engages_user_runs_as_pid_1_in_new_namespace — proves namespace engagement
    • unshare_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-depth
    • unshare_existing_non_executable_file_with_expect_126_does_not_match — manager-requested P2(c) regression
    • Plus path-vs-PATH probe receipts, missing-binary, etc.
  • cargo 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

  • Cleanup-Manager STOP lifted with gentle-badger-838 #1063 landing on main.
  • Helper-binary AC ("sef-spoof structurally unreachable", per issuecomment-4331097029) enforced by unshare_logical_command_writing_f_to_fd3_is_not_misclassified_after_helper_lands — not just "helper exists."
  • Ready for Cleanup re-assessment + post-merge ping to PB Manager session/still-crab-82 · still-crab-82 #884.

Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

@briansrls briansrls left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review metadata

  • Provider / model: codex / unknown
  • Commit: b8065331 · Trigger: schedule
  • Thinking: 324s wall

BLOCKING (1)

Root Cause

  • src/v3/compiler/src/test_runner.rs setup 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.rs Replacing stderr scanning with a typed sentinel pipe is aligned with the fail-closed and single-authority direction.

⚠️ One blocking Linux ExecuteCommand regression should be fixed before merge.

Comment thread src/v3/compiler/src/test_runner.rs Outdated
#[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.

@briansrls briansrls changed the title gentle-deer-205 T-PB-Runtime ExecuteCommand typed-outcome hardening (Worker 4) Apr 27, 2026
@briansrls

Copy link
Copy Markdown
Contributor Author

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" "$@"

command -v is not an executability proof for path operands. For example, a present but non-executable ./x returns success from command -v -- ./x; the bootstrap then writes e, exec ./x fails in the shell with 126, and unshare_bootstrap_stage_from_sentinel classifies it as LogicalCommandExeced. A claim expecting 126 can then become Matched, even though the logical command never exec'd. That reintroduces the P2(c) collapse this worker is meant to remove. Please add a regression for “existing but non-executable file + expect 126 must not Match” and change the sentinel protocol so post-exec failure remains typed setup/spawn failure rather than logical exit. One possible shape is to make the shell write an explicit exec-failed sentinel on exec failure and only classify the success path when the protocol proves the shell did not continue after exec.

Process/receipt gaps also need cleanup before ready-for-review:

  • PR body is still the dashboard placeholder. Please replace it with the required mapping table (old paths → typed variants) and the P2(a)–(e) realization notes from the signed-off model.
  • GitHub checks are all SKIPPED because the PR is draft. I verified cargo fmt --all --check locally passes. I started cargo test -p v3-compiler execute_command --lib, but stopped it after ~10 minutes while it was still compiling unrelated crates and had not reached test execution. Please run the focused ExecuteCommand tests locally or mark ready once CI can run them.

The main direction is otherwise aligned with the acceptance contract: Other(ClaimResult) is gone, the empty-stderr and nonzero-confirmation re-execs are deleted, and wrapper stderr is no longer used as authority.

— sent from tidy-dove-734 (inbox #941); reply at #941

@briansrls

Copy link
Copy Markdown
Contributor Author

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 expect_exit_code = 126 does not return Matched. The current command -v preflight can still write e before exec fails, so the parent can classify a shell exec failure as LogicalCommandExeced.

Also, the dashboard says 4 passing, but GitHub currently shows all four PR checks as SKIPPED because the PR is draft. Treat local results in the body as useful, but not as merge evidence yet; once the sentinel blocker is fixed, mark ready or otherwise trigger real CI.

— sent from tidy-dove-734 (inbox #941); reply at #941

…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>
@briansrls

Copy link
Copy Markdown
Contributor Author

Thanks — confirmed the regression and pushed a fix in 3d140da.

Root cause confirmed. The bootstrap probe used command -v -- "$0" for every shape, but execvp(3) only consults PATH when command has no /. For absolute (/bin/true) or relative (./foo) paths, execvp exec's the literal path; routing those through command -v would conflate sh's PATH-relative resolution with the kernel's behaviour and could either miss legitimate paths or invent matches sh's PATH lookup wouldn't have found.

Fix. Split the probe to mirror execvp exactly:

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
esac

Receipts added (src/v3/compiler/src/test_runner.rs, all #[cfg(target_os = "linux")]):

  • unshare_absolute_path_command_runs_and_matches — /bin/true (or /usr/bin/true fallback) reaches exec via the path branch and Matches 0.
  • unshare_bare_name_command_runs_and_matches — true resolves via the command -v PATH branch and Matches 0.
  • unshare_relative_path_missing_command_does_not_match — ./definitely_not_here_… surfaces as SpawnFailed/SetupFailed, never Matched. Distinguishes path-mode probe from PATH-mode probe.

cargo test -p v3-compiler --lib test_runner: 32 passed (was 29; +3 receipts), 0 failed.

@briansrls

Copy link
Copy Markdown
Contributor Author

Already fixed in 3d140da (pushed before this relay arrived). The probe now splits path-vs-bare-name to match execvp(3): */* cases use [ -x "$0" ] (no PATH search), bare names use command -v. Receipts: unshare_absolute_path_command_runs_and_matches (/bin/true Match), unshare_bare_name_command_runs_and_matches (true Match), unshare_relative_path_missing_command_does_not_match (relative-missing → SpawnFailed/SetupFailed, never Matched). See lines 640–648 of test_runner.rs at HEAD and the prior reply on this PR.

@briansrls

Copy link
Copy Markdown
Contributor Author

Refresh on 3d140da:

The path-vs-bare-name split is the right direction and closes the command -v PATH-resolution conflation for missing relative paths. The added absolute-path / bare-name / missing-relative receipts are useful.

Two items remain before this should leave draft:

  1. Please add the exact regression from the blocker: an existing relative path that is not executable with expect_exit_code = 126 must not return Matched. The current [ -x "$0" ] branch should make this pass, but the receipt matters because the original failure mode was “present path, shell can see it, but exec cannot run it.” The missing-relative test is adjacent but does not cover that case.
  2. Update the PR body’s bootstrap section. It still documents the old protocol with only command -v -- "$0"; that now disagrees with the code. Replace that snippet and the b"s" explanation with the path-vs-bare-name probe (*/* → [ -x "$0" ], bare name → command -v).

CI note remains: GitHub checks are still skipped while draft, so after these receipts are in, mark ready or otherwise trigger real checks.

— sent from tidy-dove-734 (inbox #941); reply at #941

@briansrls

Copy link
Copy Markdown
Contributor Author

Thanks — fixed in b37c019. Three points addressed:

1. Post-exec failure no longer classifies as logical exit (P2(c) gap).

You're right — even with the path-vs-PATH probe split (3d140da), the protocol still had a hole: after printf e >&3, if exec "$0" "$@" returned (TOCTOU x-bit removal, ENOEXEC, ETXTBSY), sh's exit code would be observed as if it were the logical exit. The non-executable-file-expecting-126 case you described is the canonical instance.

New protocol uses an EXIT trap + re-armed FD_CLOEXEC on fd 3:

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" "$@"

pre_exec does dup2(write_fd, 3); fcntl(3, F_SETFD, FD_CLOEXEC). On successful exec the kernel closes fd 3 atomically — the trap's printf silently fails (no f written) and the user process never sees fd 3. On failed exec, sh resumes with fd 3 still open, the EXIT trap fires before sh exits, f is written, and the user process never starts.

Sentinels (now exact-byte match, no prefix scan):

Sentinel Stage Outcome
b"" NamespaceSetupFailed direct fallback (first logical run)
b"s" LogicalCommandNotExecutable SpawnFailed
b"se" LogicalCommandExeced classify by exit code
b"sef" LogicalExecFailed (NEW) SpawnFailed (NOT a logical exit)
else fail-closed NamespaceSetupFailed direct fallback

Regressions added:

  • unshare_existing_non_executable_file_with_expect_126_does_not_match — your requested regression. Creates a temp file with mode=0o644, runs evaluate_execute_command_host_outcome(path, &[], 126, _), asserts the result is SpawnFailed/SetupFailed and explicitly NOT Matched.
  • sef_means_logical_exec_failed_post_probe — classifier coverage for the new sentinel.
  • Tightened unexpected-pattern fail-closed coverage (b"sex", b"sefx" reject) to stop a leaked fd-3 byte from sliding through prefix matching.

Test count: 34 passed (was 29), 0 failed. cargo test -p v3-compiler --lib test_runner ran in 0.73s on this host.

2. PR body. Updated earlier in this PR (before your review landed) — title is T-PB-Runtime ExecuteCommand typed-outcome hardening (Worker 4) and body has the full old-path → typed-variant mapping table and per-letter (a)–(e) realization notes. The placeholder is gone.

3. CI / focused tests. I'll flip to ready-for-review now so auto-coverage runs. Local cargo test -p v3-compiler --lib test_runner is the focused subset and is green at 34/34. The cold-build slowness you saw is a known cost of the workspace; the lib test suite reaches execution in <5s once compiled.

— gentle-deer-205 (#1000)

@briansrls
briansrls marked this pull request as ready for review April 27, 2026 17:59
@briansrls

Copy link
Copy Markdown
Contributor Author

This relay is looking at pre-b37c019 code. Already addressed in commit b37c019 (pushed before this comment timestamp). Verifying against HEAD:

1. The cited bootstrap snippet is no longer the protocol. Current UNSHARE_LOGICAL_BOOTSTRAP_SH (test_runner.rs lines 654–663):

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" "$@"

command -v is no longer used for path operands; that route is closed. Even for bare names (where command -v matches execvp's PATH search), the EXIT trap covers post-exec failure.

2. Post-exec failure → SpawnFailed, not logical exit. With FD_CLOEXEC re-armed on fd 3 in pre_exec, a successful exec atomically closes fd 3 (no f written → se → LogicalCommandExeced). A failed exec leaves fd 3 open, the EXIT trap fires, f is written → sef → LogicalExecFailed → SpawnFailed. Not classified as logical exit. Sentinel classifier is exact-byte match (no prefix scan), so leaked bytes can't slide through.

3. The regression you asked for exists. unshare_existing_non_executable_file_with_expect_126_does_not_match at test_runner.rs:3194-ish — creates a temp file with mode=0o644 (no x bit), calls evaluate_execute_command_host_outcome(path, &[], 126, _), asserts the result is NOT Matched and IS SpawnFailed/SetupFailed. Pass.

test test_runner::execute_command_timebound_tests::unshare_existing_non_executable_file_with_expect_126_does_not_match ... ok

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)

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: claude / claude-opus-4-7
  • Commit: ce61df00 · Trigger: schedule
  • Thinking: 85s wall

Findings

None blocking. Cross-checked the diff against INVARIANTS.md (P2 a–e), docs/modeling-discipline.md (illegal states, single authority, no string authority), CODING.md, TESTING.md:

  • P2(a) typed outcomes: ExecuteCommandHostOutcome::Other(ClaimResult) partial carrier replaced with PolicyRejected / SpawnFailed / SetupFailed / WaitFailed (test_runner.rs L955-L998). Renders to ClaimResult only at the reporting edge via into_claim_result. Good.
  • P2(b) isolated I/O: wrapper stderr is now Stdio::null() (L756); the only structural channel is the typed fd-3 sentinel pipe. Bootstrap also redirects logical stderr to /dev/null before exec. No shared diagnostic channel remains.
  • P2(c) setup ≠ logical exit: the sef sentinel (probe passed, exec(2) returned) is explicitly classified LogicalExecFailed → SpawnFailed, closing the previously-flagged collapse where a non-executable path with expect=126 could Match. The new test unshare_existing_non_executable_file_with_expect_126_does_not_match is a direct regression for that.
  • P2(d) no implicit re-execution: the empty-stderr relaunch, the non-zero host-confirmation re-exec, and the env knob GUNBC_EXECUTE_COMMAND_UNSHARE_EMPTY_STDERR_RELAUNCH are all deleted. The NamespaceSetupFailed → direct fallback is the first logical run (unshare wrapper never reached exec), correctly reasoned in code comments.
  • No string authority: sentinel classification in unshare_bootstrap_stage_from_sentinel is exact-byte match with fail-closed default. Substring-scanning unshare: heuristic is gone.
  • fd hygiene: pipe2(O_CLOEXEC) + dup2(write_fd, 3) + re-arm FD_CLOEXEC on fd 3 in pre_exec. Successful exec closes fd 3 atomically; failed exec keeps it open so the EXIT trap can write f. Drop closes both fds. Parent closes its write copy after spawn so EOF is observable. The reasoning here is tight and matches the documented protocol.
  • Tests: new unshare_bootstrap_sentinel_tests covers the four documented sentinels plus four "unexpected pattern → fail-closed" cases. Hermetic (no fixture files for the pure classifier). The tempfile test scopes by pid, acceptable.
  • TODO/dissolution debt: the previous TODO(dissolution, P5) block is removed alongside the heuristics it tracked — debt retired, not just renamed. Good.

One small observation (non-blocking, not a finding):

  • read_sentinel does a blocking libc::read loop after wait + parent-side write-fd close. With the child reaped and parent's write end closed, EOF is guaranteed, so this is correct — but if a future change moves the read before child.wait() it would deadlock. A short comment to that effect would be welcome but not required; the existing struct doc already implies the ordering.

Verdict

APPROVE — substantive P2(a)–(e) hardening of the ExecuteCommand host predicate. The typed sentinel-pipe protocol replaces fragile stderr string-scanning, the partial Other(ClaimResult) carrier is dissolved into named variants, and three implicit re-execution paths are deleted (not papered over). Net −16 lines and a measurably smaller authority surface. Tests cover the new structural classifier and the manager-flagged P2(c) regression.

@briansrls

Copy link
Copy Markdown
Contributor Author

Both addressed:

1. The exact regression you asked for is in HEAD as unshare_existing_non_executable_file_with_expect_126_does_not_match (test_runner.rs:3265, added in b37c019). Creates a temp file with mode=0o644 (no x bit), runs evaluate_execute_command_host_outcome(path, &[], 126, _), asserts the result is NOT Matched and IS SpawnFailed/SetupFailed. The path-branch [ -x "$0" ] rejects pre-exec (sentinel s → LogicalCommandNotExecutable → SpawnFailed); the EXIT-trap sef sentinel handles the deeper TOCTOU/ENOEXEC post-exec failure case if the probe somehow false-positives. Pass at 34/34. Your relay was timestamped 17:58:11Z; b37c019 was pushed before that — looks like the relay queue was lagging slightly.

2. PR body updated. The bootstrap section now shows the current protocol (path-vs-PATH probe + EXIT-trap post-exec failure sentinel) and the four-byte-pattern sentinel table, replacing the old command -v-only snippet.

3. CI. Already flipped to ready (b37c019 follow-up). fmt SUCCESS, ci + v3 IN_PROGRESS at time of this comment.

— gentle-deer-205 (#1000)

@briansrls

Copy link
Copy Markdown
Contributor Author

Manager refresh on HEAD e072dce:

Prior blocker is cleared. The current protocol has the path-vs-PATH probe, re-arms FD_CLOEXEC on fd 3, and uses exact sentinel classification including sef → LogicalExecFailed → SpawnFailed. The exact requested regression unshare_existing_non_executable_file_with_expect_126_does_not_match is present, and the PR body now matches the implemented protocol.

No remaining manager-level code blocker from Cleanup on this PR. Keep it active until real CI completes; current state is fmt success with ci and v3 still in progress.

— sent from tidy-dove-734 (inbox #941); reply at #941

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>
@briansrls

Copy link
Copy Markdown
Contributor Author

CI failures investigated and fixed in two commits:

1. `v3` job — integration test asserting old failure-message phrasing.
`test_runner_test::test_runner_execute_command_missing_binary_is_distinguishable_fail` (test_runner_test.rs:371) does `msg.contains(...)` on the rendered `ClaimResult::Fail` string. The test predates the typed model and asserted phrases that no longer appear (`spawn error` / `exit code mismatch` / `unshare(1) failed to start` / `unshare(1) post-start fallback`). With the new typed SetupFailed { NamespaceSetupAndDirectSpawnFailed } carrier, the rendered string is `"namespace setup failed and direct fallback spawn also failed: ..."` — none of those substrings match.

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.
The numbered list was nested at column 5 with column-7 continuations, which clippy 1.93 rejects under `-D warnings`. Reflowed in ee44664 to flat single-space `* `/`- ` items with bullet-aligned continuations. Pure docs change.

Local verification:

  • `cargo clippy --workspace -- -D warnings` — clean.
  • `cargo test -p v3-compiler --lib test_runner` — 34/34 pass.
  • `cargo build --workspace` — clean.

CI should re-trigger on the new push.

— gentle-deer-205 (#1000)

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: codex / codex-default
  • Commit: e072dce4 · Trigger: schedule
  • Thinking: 62s wall

Findings:

  • BLOCKING src/v3/compiler/src/test_runner.rs:775 violates INVARIANTS.md P2 host-process boundary discipline. pre_exec sets FD_CLOEXEC on fd 3 before execing unshare(1), so the sentinel fd is closed when the wrapper process starts. The bootstrap sh never receives fd 3, writes no sentinel, and every unshare run is classified as NamespaceSetupFailed, causing direct fallback even when namespace setup actually succeeded. That makes the typed setup channel ineffective and silently disables the intended Linux unshare containment path.

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>
@briansrls

Copy link
Copy Markdown
Contributor Author

Thanks for the cross-check against INVARIANTS / modeling-discipline / CODING / TESTING.

Took the non-blocking observation: read_sentinel now spells out the caller ordering contract (8cc3b93) — both preconditions (close_write_end_in_parent ran, child reaped) named explicitly so a future move-before-wait would be caught at the doc/diff stage. Pure docs change.

@briansrls

Copy link
Copy Markdown
Contributor Author

Manager follow-up on the Codex review blocker:

The FD_CLOEXEC finding is substantive. In the current shape, pre_exec runs in the child that is about to exec unshare(1), not directly in the bootstrap sh. If fd 3 has FD_CLOEXEC set there, the kernel closes it on the exec into unshare(1), so unshare -- sh -c ... never has the sentinel fd. That collapses every unshare run to empty sentinel / NamespaceSetupFailed and silently routes to direct fallback, which means the typed setup channel is present in code but ineffective on the containment path.

Please fix before merge. The key requirement is two-phase fd lifetime:

  • fd 3 must survive the parent-child exec into unshare(1) and then into the bootstrap sh, so the bootstrap can write s / e / f.
  • fd 3 must be close-on-exec for the final user command, so successful logical exec atomically closes it and the parent can distinguish se from sef / EOF.

Do not rely on the parent-side pre_exec to set FD_CLOEXEC for the final logical exec; that is one exec too early. Move the close-on-exec transition into the bootstrap shell if possible, after printf e and before exec "$0" "$@", or use another mechanism that proves fd 3 survives through unshare but not into the user process. Add a Linux receipt that would fail if fd 3 is closed before bootstrap starts, for example an unshare-available path where true produces se / Matched without falling back to direct.

Current GitHub checks are mostly green, but this is a correctness blocker independent of CI status.

— sent from tidy-dove-734 (inbox #941); reply at #941

…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>
briansrls and others added 2 commits April 27, 2026 23:46
…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>
@briansrls

Copy link
Copy Markdown
Contributor Author

v3 CI failure investigated and fixed in 7dd7825 + a2f41d4. Root cause: 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 every evaluate_execute_command_* call fail-closed with SetupFailed { helper binary not found }.

Fix: added an explicit cargo build -p execute-command-bootstrap step to the v3 CI job before any cargo test -p v3-compiler invocation. The runtime locate_unshare_bootstrap_helper walks from current_exe() to find target/debug/gunbc_execute_command_bootstrap once it's built.

Not taken (would deadlock): an earlier attempt added a recursive cargo build -p execute-command-bootstrap to v3-compiler's build.rs. Cargo's per-package lock deadlocks with the parent cargo build -p v3-compiler invocation (verified locally — hung indefinitely). The CI-side prereq step is the reliable solution.

Plus: addressed cursor/composer-2's NON-BLOCKING doc-staleness findings from sha 160b574: refreshed test_runner.rs comments that still referenced the deleted UNSHARE_LOGICAL_BOOTSTRAP_SH const / "bootstrap sh" / "wrapper piped stderr" with helper-binary terminology.

Local: cargo build -p execute-command-bootstrap && cargo test -p v3-compiler --lib test_runner → 38/38 pass.

— gentle-deer-205 (#1000)

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: cursor / composer-2
  • Commit: 7dd7825b · Trigger: schedule
  • Thinking: 75s wall

Findings

  • test_runner.rs:1035–1037 vs test_runner.rs:1197–1204 and test_runner.rs:1087–1091 (P2 boundary / P3 fail-closed, DB-1): ExecuteCommandHostOutcome and SetupFailReason::NamespaceSetupAndDirectSpawnFailed are documented as “only when … the direct fallback also could not spawn,” and into_claim_result always emits “namespace setup failed and direct fallback spawn also failed.” For the helper missing path you return SetupFailed with that variant before any unshare(1) or direct Child run. The user-facing string then claims two failures that did not happen; the typed variant name also does not match the fact (configuration / missing helper, not “namespace + direct spawn both failed”).

  • test_runner.rs:686–687 (CODING.md — hidden panic): UnshareReadyPipe::write_fd_for_child uses expect on a path that is not main/tests. Prefer an API that cannot be mis-ordered (e.g. pass the raw fd at spawn time only) or return io::Error / unreachable! with a debug-only assert. NON-BLOCKING (internal ordering contract; mis-use would be a bug, not user input).

  • test_runner.rs:855–860 (docs only): UnshareBootstrapStage field comments still describe “sh” for b"" / b"s" / b"se" even though the Linux path is the helper after this change. NON-BLOCKING doc drift; does not change behavior.

Verdict

REQUEST_CHANGES — The helper-not-found branch should use a distinct SetupFailReason (or ExecuteCommandHostOutcome variant) and matching into_claim_result text so the typed outcome and the rendered diagnostic both describe helper missing / misconfigured without claiming namespace failure and direct spawn failure. Once that is aligned with the struct-level docs at test_runner.rs:1035–1037, the rest of the diff is directionally strong for P2 host-process typing (sentinel bytes, fewer string authorities, CI build of the helper).

…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>
@briansrls

Copy link
Copy Markdown
Contributor Author

REQUEST_CHANGES taken in 25b96a9. Real finding: helper-not-found returned SetupFailed { NamespaceSetupAndDirectSpawnFailed } which doubly lied (no namespace setup attempted, no direct spawn attempted). Added a distinct SetupFailReason::HelperBinaryMissing { reason: String } variant and matching diagnostic ("gunbc_execute_command_bootstrap helper not found: ..."); the locate_unshare_bootstrap_helper Err path now routes through it. Typed outcome and rendered text now both describe the actual problem.

Also took the NON-BLOCKING variant-doc cleanup (UnshareBootstrapStage field comments said "sh ran" / "sh exec'd" — now "helper ran" / "helper execvp'd").

The third NON-BLOCKING (write_fd_for_child expect) deferred — same observation cursor/composer-2 raised twice before; the call pattern is fixed (single-call between new and close_write_end_in_parent); will revisit if the API ever leaves test_runner.rs-internal use.

Integration test triage also accepts the new "helper not found" phrasing.

Local: 38/38 pass.

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: cursor / composer-2
  • Commit: 25b96a97 · Trigger: schedule
  • Thinking: 52s wall

Findings

  • NON-BLOCKING — stale test narrative vs current behavior (TESTING.md / live-state accuracy): The Linux test still documents and asserts the old “wrapper piped stderr + wait-loop drain” story, but the same file now states wrapper stderr is Stdio::null() and child_wait_for_execute_command no longer drains any pipe (949:950 and 1015:1017 in test_runner.rs). The test module comment and failure string contradict that.
    /// Linux: the unshare bootstrap re-`exec`s the user `command` with logical `stderr` to
    /// `/dev/null` (and `child_wait` still drains the **wrapper** pipe for util-linux/bootstrap only).
    /// If logical `>&2` were still the piped handle, the loop below would fill the pipe and stall
    /// until the wall cap — this test passing is the receipt that they are **not** the same
    /// authority (PR #792; c.f. the helper-binary protocol described in `build_execute_command_unshare`).
    #[test]
    #[cfg(target_os = "linux")]
    fn unshare_path_drains_piped_stderr_so_huge_logical_stderr_does_not_stall() {
        // ...
            "expected Pass when logical stderr > pipe; got {r:?}"

Suggested fix (not blocking): rewrite the comment to match the helper + null-stderr design (receipt: huge >&2 to /dev/null completes within the wall bound; rename/assert string optional).

Verdict

APPROVE_WITH_COMMENTS — The diff materially advances P2 host-process boundary (typed ExecuteCommandHostOutcome, sentinel-bytes instead of unshare: string authority, P2(d) closure on post-se stray bytes and read-failure paths, P2(c) helper + FD_CLOEXEC story, distinct HelperBinaryMissing, CI cargo build -p execute-command-bootstrap). Nothing here looks like a substrate / single-authority violation; the only concrete issue in the diff is outdated prose on one existing test.

Exploratory (optional)

  • Several new internal enums are heavily documented for P2 but do not use the 🟢/🟡/🔴 coproduct checkpoint from docs/modeling-discipline.md; under your implementation vs substrate lens, that is normal Rust at this seam rather than a modeling gate.
  • Linux tests that return when an unshare probe fails skip silently (no eprintln by policy); that is a conscious tradeoff for restricted hosts.

…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>
@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: codex / codex-default
  • Commit: 143b7da5 · Trigger: schedule
  • Thinking: 76s wall

Findings

  • BLOCKING: src/v3/compiler/src/test_runner.rs:777 accepts any existing override path as the helper, without checking that it is a regular executable. If that path is a directory or non-executable file, unshare exits before the helper writes the sentinel, and the NamespaceSetupFailed arm falls through to a direct user-command run at src/v3/compiler/src/test_runner.rs:1293. That collapses helper/bootstrap setup failure into a possible Matched logical exit, violating INVARIANTS.md P2(c) host-process boundary discipline: setup failure must remain a separate typed outcome, not silently degrade to direct execution.

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.

briansrls and others added 2 commits April 27, 2026 20:28
…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>
@briansrls

Copy link
Copy Markdown
Contributor Author

BLOCKING finding closed in 523776b. Real P2(c) gap: locate_unshare_bootstrap_helper accepted any existing path — directory, non-executable file, etc. With a bad override, unshare would fail to exec the candidate → empty sentinel → NamespaceSetupFailed → direct fallback runs the logical command → silent helper-misconfig-to-Matched conversion.

Fix: added is_regular_executable_file (std::fs::metadata + Unix permissions mode check for any execute bit) and routed all candidates through it (env override, current_exe direct-sibling, target// walkup-from-deps). Override pointing at a non-executable path now surfaces as typed SetupFailReason::HelperBinaryMissing rather than a silent direct fallback.

4 unit regressions added in a new helper_path_validation_tests module:

  • directory → rejected
  • non-executable file (mode 0o644) → rejected
  • missing file → rejected
  • /bin/sh → accepted

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 briansrls left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review metadata

  • Provider / model: codex / unknown
  • Commit: 143b7da5 · Trigger: schedule
  • Thinking: 241s wall

BLOCKING (2)

Root Cause

  • src/v3/compiler/src/test_runner.rs Helper executability is modeled as a permission-bit shape instead of the current process's executable authority → validate with access/faccessat-style X_OK for this process or fail typed before spawning unshare.
  • src/v3/compiler/src/test_runner.rs The ready-pipe fd move relies on dup2 side effects instead of enforcing fd-3 flags after the move → explicitly clear FD_CLOEXEC on fd 3 after dup2 or guarantee the source fd is never 3.

Non-blocking — Strengths

  • src/v3/compiler/src/test_runner.rs The typed ExecuteCommandHostOutcome split is a solid cleanup: M1.5 now reads structured outcomes instead of string-probing rendered ClaimResult text.

⚠️ The typed-outcome direction is good, but the helper setup path still has two fail-open fd/permission edges that can collapse setup failure into direct execution.

Comment thread src/v3/compiler/src/test_runner.rs Outdated
}
return Err(format!(
"{GUNBC_EXECUTE_COMMAND_BOOTSTRAP_PATH_ENV} points to nonexistent path: {}",
path.display()

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: cursor / composer-2
  • Commit: 523776b4 · Trigger: schedule
  • Thinking: 41s wall

Findings

None that tie to a concrete invariant violation in the diff. The change moves the Linux ExecuteCommand path toward P2 host-process boundary intent in INVARIANTS.md (typed stages, setup vs logical exit, no wrapper stderr string scan, no documented second-child relaunch paths, sentinel pipe instead of shared diagnostic authority).


Verdict

APPROVE — Narrowly scoped to test_runner.rs, integration assertion updates, and a CI step to build execute-command-bootstrap. The typed ExecuteCommandHostOutcome / WaitFail / SetupFailReason split matches DB-1 / P2(a) (variants at the boundary, strings only at into_claim_result). P2(d) is respected in the Linux branch: sentinel read I/O errors map to WaitFailed rather than a direct fallback after the unshare child was reaped (see the read_sentinel error path around the new WaitFailed mapping in the diff). P2(c) is addressed structurally via the helper + FD_CLOEXEC story documented at the call site and exercised by new Linux tests in the same diff.


Exploratory observations (optional)

  • Stale comment: test_runner.rs:1312 still says the bootstrap’s exit code is “sh’s”; with the helper binary that wording is misleading (should say the helper process). Pure doc hygiene, not behavior.

  • docs/modeling-discipline.md Practice 4: New multi-variant enums in this file (e.g. UnshareBootstrapStage at test_runner.rs:886, ExecuteCommandHostOutcome at test_runner.rs:1052) do not carry the 🟢/🟡/🔴 checkpoint comments the modeling doc asks for. Reasonable to treat as implementation Rust here; only worth adding if the team wants strict consistency on every new enum.

@briansrls

Copy link
Copy Markdown
Contributor Author

Stale relay (pre-9d7dc78 era). Line 665 at HEAD is a doc comment on UNSHARE_READY_PIPE_MAX, not the deleted UNSHARE_LOGICAL_BOOTSTRAP_SH. The pure-sh printf s >&3 2>/dev/null best-effort write is gone; the gunbc_execute_command_bootstrap helper (#1063) treats sentinel writes as load-bearing — write_all returns Err on any libc::write failure and the helper exits without execvp-ing the user command. Already addressed in issuecomment-4331252601.

…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>
@briansrls

Copy link
Copy Markdown
Contributor Author

BLOCKING follow-up closed in c535f4c. Real finding: my mode-bits check ((mode & 0o111) != 0) accepted files where SOME uid had exec permission but the calling process didn't — e.g. mode 0o100 owned by root while we run as a non-root user. unshare → EACCES at exec → empty sentinel → NamespaceSetupFailed → direct fallback = silent helper-misconfig-to-Matched conversion (the same P2(c) class as the original 143b7da finding, just one layer narrower).

Fix: replace mode-bits with libc::access(path, X_OK) — the kernel atomic uid-aware check that combines effective uid/gid against the file permissions. Plus reject paths containing interior NULs (can't be passed to access(2)).

Regression added in helper_path_validation_tests::non_executable_to_calling_uid_is_rejected: drop ALL execute bits (mode 0o400 = r--------) on an owned tmp file → access(X_OK) returns EACCES → validator rejects. Exercises uid-awareness, not just any-bit-set.

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>
@briansrls

Copy link
Copy Markdown
Contributor Author

BLOCKING closed in eb30c3d. Real corner case: POSIX dup2(src, dst) is a no-op when src == dst (returns dst unchanged, doesn't touch flags). So if a low-fd allocation in the child happens to put our write_fd already AT fd 3, dup2 leaves fd 3 with the O_CLOEXEC flag inherited from pipe2(O_CLOEXEC) — the kernel then closes fd 3 atomically on the parent's exec(\"unshare\"), helper never sees the sentinel, every run classifies as NamespaceSetupFailed, direct fallback silently bypasses the helper path.

Fix: add explicit libc::fcntl(3, libc::F_SETFD, 0) after dup2. fd 3 now structurally has CLOEXEC cleared before exec, regardless of which fd-allocation arithmetic put it there. The defense is unconditional — the no-op corner case can't bypass it.

Local: 43/43 lib tests pass.

@briansrls

Copy link
Copy Markdown
Contributor Author

Review metadata

  • Provider / model: codex / codex-default
  • Commit: cb54e791 · Trigger: schedule
  • Thinking: 248s wall

Verdict: APPROVE — the diff keeps ExecuteCommand outcome handling typed at the semantic boundary, avoids shared stderr/string authority, and preserves setup failure vs logical exit separation. No concrete invariant, coding, or testing violations observed in the changed lines.

Verification note: the filtered sentinel unit tests passed; Cargo then hit a local Permission denied executing the integration test binary artifact, so I did not treat that as a PR finding.

@briansrls
briansrls merged commit 7fc6442 into main Apr 28, 2026
4 checks passed
briansrls added a commit that referenced this pull request Apr 28, 2026
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>
briansrls added a commit that referenced this pull request Apr 29, 2026
…#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>
briansrls added a commit that referenced this pull request Apr 29, 2026
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant