Skip to content

Make the macOS signal-injection tests target the script PID (not $PPID) - #146

Merged
seathatflowsinourveins merged 3 commits into
mainfrom
claude/signal-test-determinism-20260923
Sep 23, 2026
Merged

seathatflowsinourveins merged 3 commits into
mainfrom
claude/signal-test-determinism-20260923

Conversation

@seathatflowsinourveins

Copy link
Copy Markdown
Owner

agent-lab-e7 reported that #94's signal-injection tests fail on a heavily loaded WSL host (load average about 20). The failures were 3 in isolated runs and 9 in the full suite. The same tests pass on hosted CI.

Fix

  • Signal target. The fault-injection shims signalled $PPID. When a shim runs inside $(...), $PPID is the command-substitution subshell, whose traps are reset, so the signal never reached the script under test. Both product scripts now export ADOPTION_SCRIPT_PID, and the shims signal that PID. This is a real bug that doesn't depend on load; the pre-round-3i bootout_and_wait bug had the same shape.
  • Trap preemption. Bash services a pending trap as soon as its current foreground child (the shim) exits, so the shim signals the script and exits after a short fixed pause. A polling "handshake" is circular here, because the shim is the target's own foreground child; an earlier draft of that approach was rejected.
  • New tests. A load-independent SignalShimMechanismProofTests class adds two tests:
    • $PPID inside $(...) never reaches the script, while ADOPTION_SCRIPT_PID always does;
    • the trap preempts the script's next line once the shim exits.

Evidence (this WSL host)

Reproduction runs:

Load method Result on unmodified main
Busy loops on every core (2×nproc) 0 failures in 81 runs
All cores plus fork churn 0 failures in 45 runs
Pinned to 2 cores 0 failures in 90 runs

e7's heavier-load failure did not reproduce on this host at this time.

Before and after the fix:

Load method Before After
2-core confined 90/90 pass 60/60 pass
Single core, 6 burners 5 of 6 tests at 15/15; the convergence-matrix test at 14/15 same: the matrix test at 14/15
  • Residual: that 1-in-15 flake in test_convergence_matrix_every_step_x_failure_term_int_then_fault_free_retry, seen only under single-core starvation, is unchanged by this PR. It is left open, not claimed fixed. Likely cause: a timeout among that test's many subprocess calls.
  • Other checks:
    • 182 launchd and bootstrap tests pass, plus 208 tests after merging main.
    • bash 5.2 and a real bash 3.2 are clean, and shellcheck is clean.
    • The validators pass, and guarded gitleaks was run on each commit.

🤖 Generated with Claude Code

…al deadlock in the first draft of the timing fix

e7's report: signal-injection tests fail on unmodified main under this
WSL host's own load (~20 avg): 3 failures in isolated module runs, 9 in
the full suite, symptom "return code 0, as if never interrupted". Two
suspected causes: $PPID resolving to a command-substitution subshell
instead of the script, and a fixed `sleep 0.3` racing the trap it is
meant to reach.

What this round did, honestly, in the order it actually happened:

1. Reproduction. nproc*2 full-core busy loops (0/81 runs), then the same
   plus fork-churn (0/45), then full-core confined to 2 CPUs with 5
   burners (0/90): none reproduced e7's failure. Per the coordinator's
   revised, shared-host-safe method (taskset-confined contention instead
   of loading every core), single-CPU confinement with 6 burners on core
   0 reproduced ONE failure in 15 runs of the launchd convergence-matrix
   test on the OLD code -- the only reproduction this round achieved.
   e7's own heavier-load failure was not recreated.

2. First fix draft: ADOPTION_SCRIPT_PID (exported by each script next to
   its own `set -Eeuo pipefail`) replaces $PPID as the signal target in
   every fault-injection shim, and a poll loop (`while kill -0 "$target"`)
   replaces the fixed sleep, meant to block until the target has actually
   exited. This draft was WRONG: every shim here is the target script's
   own SYNCHRONOUS foreground child (exactly how real npm/mv/ln/launchctl
   are invoked), so the target's `wait()` cannot return -- it cannot
   exit -- while still blocked waiting for this shim. Polling the
   target's own pid from inside its own foreground child is circular, not
   a handshake, and does not fail loud either: its 30s bound always
   fired, silently producing the nonzero exit these tests already
   expected. Measured directly: every affected test took ~30-63s per run
   under this draft, passing for the wrong reason.

3. Second, corrected draft (this commit). Two things are actually true,
   confirmed by direct, repeated (bash 5.2, this host, 10/10 each)
   testing, not assumed:
   - The PID-targeting bug is real and load-independent: a shim invoked
     from inside $(...) -- the exact shape of the pre-round-3i
     bootout_and_wait bug this project already fixed once -- signals a
     subshell that has no trap of its own, every single time, regardless
     of load. ADOPTION_SCRIPT_PID (bash's `$$`, inherited unchanged into
     any subshell) fixes this unconditionally.
   - The "fixed sleep races the trap" theory does not hold for any of
     the current call sites: bash defers a trapped signal's handler
     until whatever is CURRENTLY in the foreground -- here, the shim
     itself -- finishes, then services it immediately afterward, before
     the script's next line, regardless of how long that takes (0s and
     0.5s post-kill sleeps both tested 10/10). Ordering is already
     guaranteed by bash's synchronous foreground-child execution model
     once the signal reaches the right PID.
   _signal_and_wait_for_exit now sends the signal to ADOPTION_SCRIPT_PID
   and sleeps a short, fixed 0.5s (defensive margin only, not a race)
   instead of polling. Confirmed fast and correct: the three affected
   bootstrap-macos tests run in 8.8s combined (was 60s+ each under the
   deadlocked draft); the three affected launchd tests in 15s.

4. New deterministic (load-independent) proof:
   SignalShimMechanismProofTests in tests/test_adoption_launchd.py, two
   tests, no busy loops or taskset involved:
   - test_ppid_inside_a_command_substitution_subshell_is_not_the_script:
     $PPID from inside $(...) never reaches the script's trap;
     ADOPTION_SCRIPT_PID always does.
   - test_the_trap_runs_before_the_scripts_next_line_once_the_shim_exits:
     the trap always preempts the script's own next line once the shim
     (its synchronous foreground child) exits, with or without a
     post-kill sleep.

5. Residual: the convergence-matrix test's 1/15 failure under 6-burners-
   on-1-core is UNCHANGED by this fix -- it recurs at the same ~1/15 rate
   on the fixed code, under the same extreme single-core confinement
   only (never reproduced under 2-core/5-burner confinement, nor in any
   full-core load attempt). Same test, same rate, before and after:
   this points away from the signal-targeting/handshake mechanism this
   round addresses and toward something else entirely under EXTREME
   single-core starvation (most plausibly an unrelated timeout
   elsewhere in that test's own dozen subprocess invocations). Time-
   boxed per the coordinator's instruction rather than chased further;
   left open, not claimed fixed.

Acceptance re-run on this commit: full module suite 182/182 (2 new
mechanism-proof tests), bash -n and shellcheck clean on bash 5.2 and
real bash 3.2, ScriptBehaviorUnderRealBash32Tests 3/3 under
BASH32_BINARY, confined-load (taskset -c 0,1, 5 burners) 10/10 on all
six affected tests, single-core confined (taskset -c 0, 6 burners)
15/15 on five of six and 14/15 on the convergence-matrix residual
above, scripts/validate.py and evidence_manifest.py --check passed,
manifests/evidence.json re-registered for all four changed files.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…terminism-20260923

# Conflicts:
#	manifests/evidence.json
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-23T20:40:56.076649Z be8eb18 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

P2: SignalShimMechanismProofTests' two existing tests each build their
OWN shim script by hand, so neither would notice a regression in
_signal_and_wait_for_exit itself -- the class would still pass even if
the real fix were reverted. Adds
test_the_actual_helper_through_a_subshell_uses_the_fixed_target_not_ppid,
which runs the ACTUAL helper's generated text (not a hand-written
stand-in) as the shim, through the same non-optimizable command-
substitution subshell as the existing $PPID test, against a script
that exports ADOPTION_SCRIPT_PID exactly like the real product scripts.
Manually confirmed before committing: temporarily changing
_signal_and_wait_for_exit's own target_var default back to "PPID" makes
this exact test fail (marker never created, rc=143 from errexit on the
subshell's own uncaught SIGTERM, not from the script's trap); the
default was restored to "ADOPTION_SCRIPT_PID" immediately after and
re-verified passing before this commit.

P3: the comment at the mv-migration signal shim (test_adoption_bootstrap_
macos.py, "test_signal_between_the_one_time_migration_and_the_flip...")
still described the FIRST, reverted draft of the helper ("blocks until
the parent has actually exited"). Rewritten to describe what the code
actually does: signal ADOPTION_SCRIPT_PID, then pause briefly (0.5s,
defensive margin only) -- it cannot block on the parent's exit, since
this shim is the parent's own synchronous foreground child.

Full module suite: 186 tests, OK (skipped=3). shellcheck -S style clean
on both product scripts (unchanged this round). ScriptBehaviorUnderRealBash32Tests
3/3 under BASH32_BINARY (real bash 3.2.0). scripts/validate.py and
evidence_manifest.py --check passed; manifests/evidence.json
re-registered for both changed test files.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@seathatflowsinourveins
seathatflowsinourveins merged commit a24da5a into main Sep 23, 2026
22 checks passed
@seathatflowsinourveins
seathatflowsinourveins deleted the claude/signal-test-determinism-20260923 branch September 23, 2026 20:49
seathatflowsinourveins added a commit that referenced this pull request Oct 6, 2026
### Scope

- **What this PR changes:** it removes the 2026-10-04 hcom safety posture from main. The user decided at 2026-10-06T03:03:10Z (11:03 PM EDT on October 5) to relax the hcom deny list; the safety rules give way to the seamless, LLM-native workflow. The PR removes:
  - the 52 Claude deny entries;
  - `config/hcom-deny.rules`;
  - the adapter's running-Codex interlock and its empty-deny refusal;
  - the acceptance checks that required `forbidden` decisions.

  Peer text stays data and never counts as the user's approval. Upstream `hcom.rules` becomes the only Codex hcom policy.
- **Base commit:** `ecfa112764c664d35377dd66b8cfcb67e5a94d60`
- **Lane:** `lane:foundation`
- **Owned paths touched:**
  - `adoption/new-wsl/client-config-map.json`
  - under `evidence/artifacts/new-wsl-install-plan-20261002/`: `config/hcom-client-config.py`, `config/hcom-deny.rules` (deleted), `install-plan.json`, `install.sh`, `accept.sh`, `check_plan.py`, `README.md` and `SOURCES.md`
  - `tests/test_new_wsl_client_config.py` and `tests/test_codex_hook_trust.py`
  - `docs/decisions/2026-10-06-hcom-relaxation.md` (new)
  - `docs/decisions/2026-10-04-round2-plan-g1-messaging.md` (an inline pointer only; no line moves)
  - `manifests/evidence.json`, in the last commit only

| Verdict P1-1 item | Main ecfa112 | Change |
| --- | --- | --- |
| `claude_settings.permissions.deny` | map :912-963 (52 entries) | The whole `claude_settings` object is removed. An empty object would still go through the settings merge. |
| Prohibition sentence in `hcom_config.launch.hints` | map :977 | Removed. The data, approval and configuration sentences stay. |
| `codex_rule_file` and `config/hcom-deny.rules` | map :983 | Removed. Upstream `hcom.rules` is the only Codex hcom policy. |
| Prohibition sentences in `peer_instructions` | map :984 | Removed, including the `hcom claude` prohibition (see below). The data and approval sentences stay. |
| Quiet interlock and empty-deny refusal | adapter :123-128 and :61 | Removed. The adapter also no longer merges settings (:98-106), installs rules (:108-114) or runs the forbidden check (:145-150). |
| Contract | `check_plan.py` :179-196 | Replaced by the relaxed contract (below). |
| post_install `--check` | install-plan row | Dropped. post_install is the upstream-derived smoke only. |
| after_sign_in | install-plan row | Reduced, not deleted, so stage counts and the after-sign-in owner lists stay valid. Revised at 7eb9bea: it takes the Codex home the way hcom does (`CODEX_HOME`, else the parent of `HCOM_DIR`). It exits 78 while retired Claude hcom deny entries remain and until `hcom codex` writes `hcom.rules`. Then it passes every `*.rules` file there to `codex execpolicy check`; `hcom send` and `hcom term inject` must both be `allow`, so a leftover `hcom-deny.rules` exits 78. |
| Decision record | supersedes `2026-10-04-round2-plan-g1-messaging.md:65-72` | `docs/decisions/2026-10-06-hcom-relaxation.md` |

The verdict cites the same pieces at #723's head, at other lines (map :894-951, :962, :968 and :969; checker :180-197). The table uses main's lines.

The relaxed `check_plan.py` contract keeps the four `hcom_config` value checks and the smoke tokens. It asserts three things:
- no Codex rule file and no Claude hcom settings are mapped;
- both peer texts keep the data and approval sentences;
- after_sign_in derives the Codex home from `HCOM_DIR`, evaluates every rules file there and checks the retired Claude denies.

### The `hcom claude` rule: hook-gap finding at hcom's current release

- **Current release.** `gh api repos/aannoo/hcom/releases/latest` returns **v0.7.27, still the latest** (published 2026-10-01T02:11:55Z, target `2c5f343b2f9ec4bf2acf49c0431860e7c2ae578b`, the existing pin).
- **The gap still holds.** Hooks load only in `hcom <tool>` sessions ([release notes, #146](https://github.com/aannoo/hcom/releases/tag/v0.7.27)). The Claude hook handler exits 0 silently without `HCOM_PROCESS_ID` ([claude.rs:135-140](https://github.com/aannoo/hcom/blob/2c5f343b2f9ec4bf2acf49c0431860e7c2ae578b/src/hooks/claude.rs#L135)). A plain `claude` has no hooks ([start.rs:412-417](https://github.com/aannoo/hcom/blob/2c5f343b2f9ec4bf2acf49c0431860e7c2ae578b/src/commands/start.rs#L412)). Only `hcom claude` gets automatic delivery; anything else uses manual `hcom listen` ([README.md:230-246](https://github.com/aannoo/hcom/blob/2c5f343b2f9ec4bf2acf49c0431860e7c2ae578b/README.md#L230)).
- **Upstream main.** Main is 20 unreleased commits ahead, and its claude.rs diff leaves the guard untouched.
- **The sentence is dropped** (the command center's read at 247d71e, finding P2, option a).
  - The sentence met the task's "stays only if" test, which is a necessary condition, not a sufficient one.
  - The gap is the *cost* of not launching through `hcom claude`, not a reason against it.
  - The peer-instruction block now keeps only the neutral route facts: a plain Claude session uses `hcom start` and `hcom listen`, which do not wake it when idle, and `hcom claude` is upstream's automatic-delivery route for Claude Code.
- **Why not option b.** Option (b) would keep the rule on the wave-5 transport-scoping basis and add Claude lanes to the command center's orchestration decision. That changes the command center's own decision, so it is not this lane's to make.
- **No recipes.** This PR adds no launch, kill, term or transcript recipe.

### Fixes after the command center's read at 247d71e (ACK_AFTER)

| Finding | Fix |
| --- | --- |
| P2: the kept `hcom claude` sentence | Option (a): `peer_instructions` drops "Never launch Claude lanes through hcom claude" and "Claude lanes stay plain Claude sessions" and keeps the route facts. The record's Result and overturn sections are rewritten. |
| P3: no supersession pointer for the 2026-10-05 combined-rules acceptance | Inline pointer at the start of the old record's :196. No line moves; :65, :109 and :196 stay where the new record cites them. |
| P3: rc 3 at `--apply` when the user's own `~/.hcom/config.toml` differs (pre-existing) | Recorded in the decision's host section (see Host evidence below). No code change: `run_slot` scores any non-zero rc, 78 included, as failed. |
| P3: the adapter's `--check` has no caller | The docstring marks it a manual operator check that no plan stage runs. The record's post_install row says the same. |
| P3: the `exec_rules_reviewed.json` entry stays | No change. The read accepts the deviation. |

The history was rebuilt so that `manifests/evidence.json` changes only in the last commit (`docs/lanes.md:96-128`):
- `f0d9b2f5`: the relaxation;
- `88eb3981`: the read's fixes;
- `10160d89`: the registry.

It was pushed with `--force-with-lease`, replacing 247d71e.

### Deliberate non-changes

- **`tools/adoption/exec_rules_reviewed.json` keeps its `hcom-deny.rules` entry.** This is a deliberate non-change to the verdict's item (e). The entry reviews one exact file for the RTK Codex hook-trust tool and enforces nothing. Removing it would rewrite about twenty trust tests and `rtk-rewrite-heads-check.json`. It would also make `codex_hook_trust.py` refuse on any host that did install the file. The plan-wide test that every plan rule file is reviewed or allow-only stays; only its docstring changed, and it is vacuous while the plan ships none.
- **The verdict's item (f) quiet-interlock and quiet-apply wording exists only in #723.** On main, `git grep -i -E 'quiet (interlock|apply|point)'` returns nothing.
- **Configuration values are unchanged.** These are `title_mode=off`, `relay.enabled=false`, `auto_trust_workspace=false` and `auto_approve=true`. The verdict reserves `auto_trust_workspace` and `title_mode` to the user.

### SOTA sources

- **aannoo/hcom v0.7.27** at `2c5f343b2f9ec4bf2acf49c0431860e7c2ae578b` (https://github.com/aannoo/hcom/releases/tag/v0.7.27), still the latest release, read with `gh api repos/aannoo/hcom/releases/latest` on 2026-10-06:
  - **Upstream `hcom.rules`, the only Codex hcom policy:**
    - [src/hooks/codex.rs:1538-1563](https://github.com/aannoo/hcom/blob/2c5f343b2f9ec4bf2acf49c0431860e7c2ae578b/src/hooks/codex.rs#L1538) (`build_codex_rules`);
    - [:1566-1579](https://github.com/aannoo/hcom/blob/2c5f343b2f9ec4bf2acf49c0431860e7c2ae578b/src/hooks/codex.rs#L1566), which writes `<codex home>/rules/hcom.rules`;
    - [:337-350](https://github.com/aannoo/hcom/blob/2c5f343b2f9ec4bf2acf49c0431860e7c2ae578b/src/hooks/codex.rs#L337), the call made when `auto_approve=true`;
    - [:72-75](https://github.com/aannoo/hcom/blob/2c5f343b2f9ec4bf2acf49c0431860e7c2ae578b/src/hooks/codex.rs#L72), the Codex home;
    - [src/hooks/common.rs:46-72](https://github.com/aannoo/hcom/blob/2c5f343b2f9ec4bf2acf49c0431860e7c2ae578b/src/hooks/common.rs#L46), the command list.
  - **Hook-gap check:**
    - the release notes (#146);
    - [src/hooks/claude.rs:135-140](https://github.com/aannoo/hcom/blob/2c5f343b2f9ec4bf2acf49c0431860e7c2ae578b/src/hooks/claude.rs#L135);
    - [src/launcher.rs:920](https://github.com/aannoo/hcom/blob/2c5f343b2f9ec4bf2acf49c0431860e7c2ae578b/src/launcher.rs#L920);
    - [src/commands/start.rs:412-417](https://github.com/aannoo/hcom/blob/2c5f343b2f9ec4bf2acf49c0431860e7c2ae578b/src/commands/start.rs#L412);
    - [README.md:230-246](https://github.com/aannoo/hcom/blob/2c5f343b2f9ec4bf2acf49c0431860e7c2ae578b/README.md#L230).
  - **Mapped configuration fields:** [src/config.rs:126-152](https://github.com/aannoo/hcom/blob/2c5f343b2f9ec4bf2acf49c0431860e7c2ae578b/src/config.rs#L126).
- **Codex native policy checker:** https://developers.openai.com/codex/rules, and the installed codex-cli 0.160.0 `codex execpolicy check --help` (repeatable `--rules`, JSON `decision`).

### Evidence-class table

| Claim | Evidence class | Command / receipt |
| --- | --- | --- |
| The plan rows, `install.sh`, `accept.sh`, the map and the relaxed contract agree | `local_integration` | `check_plan.py` exit 0 |
| The adapter applies while a Codex PID is reported, leaves Claude settings unchanged, writes no Codex rules directory, runs no subprocess and keeps the data and approval sentences | `synthetic` | `test_hcom_apply_never_grants_inbound_authorization_or_requires_codex` |
| `copy_config` still refreshes plan-owned helpers and keeps operator files | `synthetic` | `test_plan_owned_helpers_refresh_without_overwriting_operator_configuration` |
| The contract fails if after_sign_in falls back to a fixed `$HOME/.codex` | `synthetic` (throwaway negative control, not committed) | `check_plan.py --plan-dir <mutated copy>`: exit 1, "after sign-in must evaluate every rules file in the Codex home that hcom derives from HCOM_DIR, and report retired Claude hcom denies" |
| Native-checker probes and the after_sign_in program on frozen fixtures | `synthetic` (real codex-cli 0.160.1 and jq 1.8.1 with scratch homes) | Retained receipt `evidence/artifacts/hcom-relaxation-20261006/probe-receipt.json`, run at 13:11:32Z: `allow`/`allow`/no decision over `hcom.rules` alone, `forbidden` for term with the retired file, and 8 after_sign_in runs with the expected exits 78,0,78,0,78,0,78,0 |
| The after_sign_in shell logic (`HCOM_DIR` derivation, every rules file, retired Claude entries) | `synthetic` (stub codex) | `test_hcom_after_sign_in_reads_hcoms_codex_home_and_every_rules_file` |
| hcom v0.7.27 is the latest release, and the plain-Claude hook gap holds there | `source_review` | `gh api` releases, tags and compare; pinned source read |
| No host changed | (scope) | no `install.sh`, `accept.sh` or `--only agent-messaging` run; NativeStack2604 never had the posture (the command center's read-only check) |

### Review threads at 3b6ea9d, fixed at 7eb9bea

The fix commit is `2c8890c9`, and the registry and regenerated outputs are the last commit, `7eb9beaa`.

| Thread | Fix |
| --- | --- |
| P1, decision record :131: retain the probe's actual result | `evidence/artifacts/hcom-relaxation-20261006/` keeps the frozen synthetic `hcom.rules` (sha256 `2a667dcb…`) and `probe-receipt.json` (every argv, sanitized environment, UTC times, exit codes, stdout and stderr). The record's Evidence section cites it. |
| P2, accept.sh:330: resolve under the Codex home hcom derives from HCOM_DIR | The after_sign_in derives `CODEX_HOME`, else the parent of `HCOM_DIR` (hcom `src/hooks/codex.rs:72-75`, `src/paths.rs:26-53`). The check_plan contract requires it. |
| P2, accept.sh:335: check the effective policy | Every `*.rules` file in that home goes to the native checker, as Codex loads them (openai/codex rust-v0.160.0 `exec_policy.rs:662-700`). `hcom send` and `hcom term inject` must both be `allow`. A leftover `hcom-deny.rules`, or any of the 52 retired Claude hcom deny entries, reports needs_user (78). |
| P2, README.md:476: the generated handbook still stated the retired posture | The handbook's source record (layer consensus, wave 5, agent-messaging) and the WSL recipe's exec-rules sentence were updated. `assemble_manifest.py` and `scripts/build_new_wsl_handbook.py --write` then regenerated the outputs, and the handbook receipt follows them. |

The installed Codex is now 0.160.1, which adds two commits to rust-v0.160.0, neither touching exec policy.

### Local commands run

Run at head `7eb9beaaf9680acebe0d8d1c8b0331cb50e4ff2a` with `nice -n 19` and `PYTHONDONTWRITEBYTECODE=1`:

```
$ python3 scripts/validate.py
exit 0: {"components": 69, "hashed_files": 10322, "profiles": 4, "receipts": 212, "status": "passed"}
$ python3 evidence/artifacts/new-wsl-install-plan-20261002/check_plan.py
exit 0: OK: 84 rows ... 185 commands and 113 acceptance entries agree with the scripts (2 additional checks included)
$ python3 tools/adoption/new_wsl_client_config.py --check
exit 0: check passed (two existing MCP_AUTO_OPEN_ENABLED warnings)
$ python3 scripts/build_new_wsl_handbook.py --check
exit 0
$ (cd evidence/artifacts/new-wsl-definitive-defaults-20261001 && python3 assemble_manifest.py --check && python3 render_tables.py --check ../../../docs/decisions/2026-10-01-new-wsl-definitive-defaults.md)
exit 0, exit 0
$ bash -n install.sh && bash -n accept.sh; git diff --check 3b6ea9d..HEAD
exit 0, exit 0
$ python3 -m unittest <the 6 touched modules>; python3 -m unittest <the 4 other pinning modules>
exit 0: 567 tests OK (skipped=3); exit 0: 122 tests OK (skipped=1)
```

The earlier runs at 10160d8 and 247d71e follow.

```
$ python3 scripts/validate.py
exit 0: {"components": 69, "hashed_files": 10271, "profiles": 4, "receipts": 212, "status": "passed"}
$ python3 evidence/artifacts/new-wsl-install-plan-20261002/check_plan.py
exit 0: OK: 84 rows ... 185 commands and 113 acceptance entries agree with the scripts (2 additional checks included). Base ecfa112 gives the same counts, so no stage was dropped.
$ python3 tools/adoption/new_wsl_client_config.py --check
exit 0: check passed, with two MCP_AUTO_OPEN_ENABLED warnings. Base ecfa112, run in a `git archive` extraction, gives the same exit 0 and the same two warnings.
$ python3 scripts/build_new_wsl_handbook.py --check
exit 0: status passed (no regeneration needed)
$ python3 evidence/artifacts/new-wsl-install-plan-20261002/config/hcom-client-config.py --repo-root . --check-map
exit 0
$ bash -n evidence/artifacts/new-wsl-install-plan-20261002/install.sh && bash -n evidence/artifacts/new-wsl-install-plan-20261002/accept.sh
exit 0
$ git diff --check origin/main...HEAD
exit 0
$ python3 -m unittest tests.test_codex_hook_trust tests.test_new_wsl_client_config tests.test_new_wsl_definitive_defaults tests.test_new_wsl_handbook tests.test_new_wsl_profile tests.test_wsl_new_distro_recipe tests.test_gpt6_family_tiering_20260926 tests.test_gpu_inference tests.test_local_inference_latest_20260926 tests.test_workflow_policy
exit 0: Ran 670 tests in 176.855s, OK (skipped=4)
$ python3 scripts/component_matrix.py --write && python3 scripts/new_host_grand_list.py --write
exit 0, exit 0: no output change
```

The module list covers every test module that names hcom, agent-messaging, hcom-deny, the plan directory, the map, `check_plan`, `exec_rules_reviewed` or the round-2 record. It is the `git grep -l` over `tests/`.

### Decision record

`docs/decisions/2026-10-06-hcom-relaxation.md` records:
- the user's decision, described rather than quoted;
- the sources;
- six alternatives;
- the overturn conditions, both for the posture and for reinstating an `hcom claude` launch rule;
- the host boundaries and a completeness critic.

It supersedes `docs/decisions/2026-10-04-round2-plan-g1-messaging.md:65-72`, plus that record's 2026-10-05 combined-rules acceptance.

### Host evidence

Not applicable: no `evidence/hosts/` change. NativeStack2604 never had the posture applied, so nothing needs undoing. A host that did apply it keeps its hcom deny entries and `~/.codex/rules/hcom-deny.rules` until that host's owner removes them; none is known.

**Expected after landing on NativeStack2604.** This behavior is unchanged from base.
- The user's own `~/.hcom/config.toml` already exists there.
- Unless it matches the mapped template's digest, `install.sh --only agent-messaging` writes the two instruction blocks, keeps the file, prints `needs_user` and prints `agent-messaging | install | 3`. `run_slot` scores that rc as failed.
- The four mapped hcom values are not written there.

### Integration

- Hot files are in the last commit only (`docs/lanes.md:96-128`). That commit removes the retired file's registry entry and re-registers the ten changed registered files.
- #723 rebases onto this PR after it lands, in the fixwave-defects lane.

### Checklist

- [x] New/changed GitHub Actions are pinned to a full commit SHA with a
      version comment (no floating tags). No workflow changes.
- [x] New/changed workflows declare top-level `permissions: {}` and grant each job
      only what it needs (`contents: read`, or an explicitly justified addition). No workflow changes.
- [x] No secrets are printed, logged or committed; no new required secret was
      added without a documented owner.
- [x] No new paid hosting, subscription or billing surface was introduced.
- [x] Peer-owned untracked files and worktrees were preserved (not deleted,
      moved or overwritten).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
seathatflowsinourveins added a commit that referenced this pull request Oct 10, 2026
The command center's correction #146: no personal copy of claude-native-practice
is installed on this host; sessions load the checkout's project skill. The
follow-up is now stated neutrally: the skill rows for the retired route are
updated in a cc-native-practice pull request, then re-pinned.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
seathatflowsinourveins added a commit that referenced this pull request Oct 10, 2026
The command center's correction #146: no personal copy of claude-native-practice
is installed on this host; sessions load the checkout's project skill. The
follow-up is now stated neutrally: the skill rows for the retired route are
updated in a cc-native-practice pull request, then re-pinned.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
seathatflowsinourveins added a commit that referenced this pull request Oct 10, 2026
The command center's correction #146: no personal copy of claude-native-practice
is installed on this host; sessions load the checkout's project skill. The
follow-up is now stated neutrally: the skill rows for the retired route are
updated in a cc-native-practice pull request, then re-pinned.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
seathatflowsinourveins added a commit that referenced this pull request Oct 10, 2026
The command center's correction #146: no personal copy of claude-native-practice
is installed on this host; sessions load the checkout's project skill. The
follow-up is now stated neutrally: the skill rows for the retired route are
updated in a cc-native-practice pull request, then re-pinned.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
seathatflowsinourveins added a commit that referenced this pull request Oct 11, 2026
The command center's correction #146: no personal copy of claude-native-practice
is installed on this host; sessions load the checkout's project skill. The
follow-up is now stated neutrally: the skill rows for the retired route are
updated in a cc-native-practice pull request, then re-pinned.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
seathatflowsinourveins added a commit that referenced this pull request Oct 11, 2026
The command center's correction #146: no personal copy of claude-native-practice
is installed on this host; sessions load the checkout's project skill. The
follow-up is now stated neutrally: the skill rows for the retired route are
updated in a cc-native-practice pull request, then re-pinned.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
seathatflowsinourveins added a commit that referenced this pull request Oct 11, 2026
* Retire Claude review inside GitHub; AI reviews run locally

The owner decided on 2026-10-10 (about 17:15Z, relayed by the command center)
that Claude review no longer runs inside GitHub: AI reviews run on the local
host and GitHub enforces the gates. The API refused every request that carried
claude-code-action's attribution (CLAUDE_CODE_ENTRYPOINT=claude-code-github-action,
base-action/src/parse-sdk-options.ts:284 at 2dca132f) on all five Console keys,
while the same keys work from the CLI and the Agent SDK (the command center's
correction #141, measured).

Removed: claude-pr-review.yml, claude-pr-toolkit-review.yml, harness-audit.yml
and their three test modules, their zizmor-coverage entries and their
local-pages inventory paths. test_claude_federation_diagnostics keeps its
retained-snapshot and decision-claim tests. A new
InGitHubClaudeReviewRetiredTests keeps the three files absent and no workflow
running the Claude action or naming an Anthropic key.

New record docs/decisions/2026-10-10-retire-in-github-claude-review.md (cause,
what remains, revisit triggers, inverse); supersession notes in the four
claude-actions/federation records and the decision-record index;
docs/github-automation.md's current practice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Pin the repository grant count at 90 after the retirement

The three removed workflows were in the local-pages repository grants. At the
landing base, main f38116f (93 grants), the test's own derivation
(grants.inventory_paths over git ls-files) gives 90 for this branch, equal to
the policy's repository grants, and the test pins 90. At the earlier bases
eb31d23 and 9d4c00c the same derivation gave 90 and 89.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Take up the command center's notes on the retirement

- .github/actions-permissions.json drops anthropics/claude-code-action@* and
  oven-sh/setup-bun@*: no workflow uses them; a new test keeps every allowed
  pattern in use, and the record's inverse puts them back.
- docs/github-automation.md: the allow-list text, no "harness description",
  no "still awaits hosted acceptance" clause.
- The retirement test catches the claude-review environment in its plain,
  name: and quoted forms, and reads *.yaml as well.
- Record nits: #953 marked open, the check is named sota-sources, and the
  federation-diagnostics note says the three workflows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Cite #968's real merge commit in the inverse

The record's inverse named a SHA that does not exist; #968's merge commit is
9d4c00c (git rev-parse 9d4c00c). The
federation note now says the history of those workflows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Read each job's environment in the retirement guard

The GPT read at 48e4bd3 found three valid YAML forms of the claude-review
environment that the regex guard let through: a flow mapping, a quoted name
key, and a comment after the key. The guard now reads each job's environment
as a scalar or a mapping's name (job_environments), cross-checked against
PyYAML when it is installed; all three forms are regressions, each caught here
and missed at e46f68e, and actionlint accepts each fixture.

The record names the claude-native-practice skill rows as a dated follow-up
for cc-native-practice (the command center's ruling at 20:19Z).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Word the skill-row follow-up without the pinned-copy premise

The command center's correction #146: no personal copy of claude-native-practice
is installed on this host; sessions load the checkout's project skill. The
follow-up is now stated neutrally: the skill rows for the retired route are
updated in a cc-native-practice pull request, then re-pinned.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* State what the environment reading covers, and what PyYAML covers

The docstring claimed comments in general; the text reading handles a comment
after the key, not a comment line before name: at the job's indentation or a
multi-line flow mapping. The PyYAML comparison covers those, and CI runs it
(validate run 38084944806, passed, not skipped). The command center's micro at
08a76ab.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Cite the environment forms' sources, and test the inverse pin against git

The GPT read at 13d1eb7 asked, under the correction rule, for:
- the upstream sources of the environment reading: GitHub's workflow syntax
  for jobs.<job_id>.environment (a name, or an object with name and url) and
  YAML 1.2.2's block and flow mappings, quoted keys and comments, cited with
  page digests in the record and next to the regression cases;
- a regression for the corrected inverse pin: RetirementInversePinTests reads
  the pin from the record and checks it is #968's merge commit holding the
  three workflows. It fails on the former SHA (no such commit) and is skipped,
  with the reason, only outside a worktree or in a shallow clone (CI clones
  with fetch-depth 0).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <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