Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
b38fc7f
fix(hook): read the prompt payload without /dev/stdin when a native W…
screenleon Nov 10, 2026
29fa898
fix(gate): name a published result when the sentinel key is gone
screenleon Nov 10, 2026
28cad51
feat(gate): pmctl gate result prints a gate's published result
screenleon Nov 10, 2026
cc0b783
feat(gate): gate result lists section-heading verdicts beside the fro…
screenleon Nov 10, 2026
e1610da
feat(gate): report an exhausted reviewer quota with its reset hint in…
screenleon Nov 10, 2026
e9aaef9
feat(gate): gate.reviewer_executor config pins the reviewer executor
screenleon Nov 10, 2026
c6a629d
feat(gate): pmctl gate mutations --preview lists the mutation checkli…
screenleon Nov 10, 2026
bff145b
feat(ops): wsl-run.sh sends script files and pmctl args to WSL withou…
screenleon Nov 10, 2026
ebf506f
test: skip link-only PATH cases where native Git Bash cannot link
screenleon Nov 10, 2026
218d973
fix(gate): follow the adapter trace for quota evidence, quote recover…
screenleon Nov 10, 2026
0ded0a1
fix(gate): show verify's three axes, name --cd in the status hint, te…
screenleon Nov 11, 2026
73948d3
fix(gate): quote --cd in the gate result no-result hint
screenleon Nov 11, 2026
1a9f63d
feat(gate): gate result accepts a result file; test the real verifier…
screenleon Nov 11, 2026
b9f9359
test: skip capability-bound cases and fix spelling-bound assertions o…
screenleon Nov 11, 2026
2d1449b
docs: WSL driving, native gate evidence, native test status, targeted…
screenleon Nov 11, 2026
be51b5d
chore: register new commands, variables and scripts; satisfy shellche…
screenleon Nov 11, 2026
228a211
test: the WSL round-trip cases run only on native Windows
screenleon Nov 11, 2026
e048e7d
docs: native-Windows open test failures are Windows-only (pass in WSL2)
screenleon Nov 11, 2026
609cba5
test: wsl-run script case asserts the script's exit status; gate resu…
screenleon Nov 11, 2026
7dfb1ec
fix: gate mutations lists non-ASCII test names; test: gate result fal…
screenleon Nov 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ Versions follow [Semantic Versioning](https://semver.org/).

### Changed

- **Windows usage documented, and the native test suite made honest (Windows report items 1 to 6, 10).** `docs/platform-support.md` gains the standard way to drive WSL2 from a Windows session (`ops/diagnostics/wsl-run.sh`: script files and pmctl arguments reach WSL without re-quoting, with `--pmctl-command` and the command's own exit status), where to edit, which permission rules to allow, what a native gate run needs (measured: about 17 minutes, five gates on one branch), and the native test status in four groups. `docs/review-model.md` states why a targeted pass is judged against the whole current subject. Test fixes: cases that need symlinks, POSIX mode bits, special file names or a PATH of links now skip with a reason instead of failing; two tests no longer use `grep -Fi`, which aborts in the GNU grep 3.0 of some Git for Windows installs; path assertions accept the `C:/` spelling the product records; the relocated-host fixture copies `jq-lf.sh` as a real checkout has it.
- **`pmctl gate mutations --preview` gives the mutation checklist before a gate (issue 9 of the Windows report).** qa-tester asks, per test, whether it would fail if the code were wrong in a plausible way, and a weak test used to surface only inside a gate round. The command lists the test files the change touches (`--base` defaults to `HEAD`, so uncommitted and untracked files count) and the mutation classes qa-tester uses: off-by-one or inverted output, first/last element, dropped exit status, removed guard, flipped operator, skipped or reordered call, empty input treated as valid. It is a checklist, not a prediction: it runs no mutants and asks for no test per boundary.
- **`gate.reviewer_executor` in `~/.pm-dispatch/config` pins the PR-gate reviewer (issue 13 of the Windows report).** `gate.reviewer_executor = codex` (or `claude`) makes `pr-gate` use that executor when `--executor` is absent or `auto`, and refuse a different `--executor` with "gate.reviewer_executor pins the reviewer to codex, so --executor claude is refused". A project that fixes "claude implements, codex reviews" can now say so once, and a used-up reviewer quota can no longer move the review to the implementing model unnoticed. `auto` or no key keeps the old behaviour.
- **A reviewer whose executor quota is used up fails the gate at once and says when to rerun (issue 8 of the Windows report).** Codex answers a used-up plan with "You've hit your usage limit ... try again at 9:15 PM". The gate filed that under the retryable "transport failure" and spent a retry round re-hitting it, with no hint when the quota returns. `gate_reviewer_quota_exhausted_signature` (next to the sandbox-unavailable one) now recognises the executor's usage-limit line. The adapter writes that line to its per-run `.stderr` and event stream, not to the reviewer's dispatch log, so the scan follows the `trace:` path the adapter's banner prints in that log to those two files; and it runs only for a reviewer that failed or wrote no result, because a successful review of quota-handling code may quote the same words. The parallel path records it as unrecoverable and reports "executor quota exhausted: <the executor's line, with its reset time> (rerun the gate unchanged after the reset it names; the reviewer executor is not switched automatically)". The sequential path still reports a dispatch failure as before.
- **`pmctl gate wait` and `gate status` name a published result when the sentinel key is gone.** When the sentinel key was lost (session cleanup, a new login) `gate wait` said only "indeterminate: sentinel key absent", and `gate status` showed "starting" and "result: not written yet" for a gate whose phase events recorded `gate end` and `publish end result=<path>`. `gate wait` still exits 3 and never treats the result as verified; it now adds that a published result exists, that no sentinel authenticates it, and the `pmctl gate verify <path> --cd <dir>` command to check it. `gate status` reports the end per the phase events and the result path with the same caveat. New `pmctl gate result <gate-id|result-file>` prints the published result's frontmatter verdicts, its Gate Conclusion and Must-Fix sections and the file path, and says whether `gate verify` accepts it, so nobody has to find `.gate-results/gate-*.md` by hand. It shows verify's three answers (`artifact_valid`, `subject_current`, `policy_applicable`) on separate lines, so a valid result for a tree that has since changed is not read as plainly accepted. It also lists each reviewer's verdict as the result's section headings state it, because the frontmatter words qa, security and risk verdicts differently (`pass`, `needs-tests`) and an `advise` would otherwise read as a `pass`.
- **The prompt-context hook no longer fails on every prompt when a native Windows host pipes the payload in.** Claude Code and PowerShell start Git Bash with an anonymous Windows pipe as stdin; there `/dev/stdin` cannot be opened, so `payload=$(</dev/stdin)` ended `inject-context.sh` with exit 1 and `line 29: /dev/stdin: No such file or directory` (a `||` cannot catch it). The hook now probes `/dev/stdin` with a builtin redirection and falls back to the `read` builtin only where it cannot be opened, keeping the one-fork fast path elsewhere. A regression case pipes the payload from PowerShell and fails on the old hook with exactly that message. Known separate Windows failure, unchanged: `ctx-inject-hook/sqlite-missing-skips-pmctl` (exit 127, its link-only PATH has no usable bash).
- **Building the memory-plane index no longer starts a `wc`, `tr` and `date` process per file (CC-687, issue #738).** `pmctl context query --source memory`, `/mem-search`, `/mem-recall` and the gate's memory context read `<memory_dir>/.pm-dispatch/context.db`, which is built and refreshed by `_ctx_index_tree`. Reading it takes under 0.1 s, but building it for 1000 cards took 301 s on native Windows, and about 150 ms per file of that were three processes: `wc -c | tr -d ' '` for the size and `date` for the time. The size now comes from the one batched `stat` pass (`_ctx_batch_stats`) and the clock is read once per run, so `files.indexed_at` and `index_meta.built_at` are the time the run started. A cold build is 36 s to 24 s at 100 cards and 100 s to 42 s at 300 cards (one run each, noisy). Against a clean export of `main`, the `files`, `file_chunks`, symbol count and `index_meta` rows of a 40-file fixture (empty file, no trailing newline, a quote and Chinese characters in file names) are identical. A new case counts `wc`/`tr`/`date` starts and fails when the old calls come back. A symlinked file keeps the size of its target, as `wc -c <` always gave (the gate review found that `stat` without `-L` returns the length of the link text; a second new case covers it and runs on Linux and WSL only). Not done and recorded in BACKLOG CC-687: the line-by-line chunker (about 120 ms per file), the loops of a refresh with nothing changed (4 to 5 s at 1000 files), and Linux and WSL timings.
- **The memory injection hook no longer starts processes per card (CC-686, issue #738).** `guard-inject-memory.sh` runs on every prompt and used to start three `awk` and one `tr` for each ordinary card in `MEMORY.md`, so on native Windows (a process costs tens of milliseconds) its time grew by about 0.23 s per card and passed the host's 30 s limit at roughly 130 cards; the output is discarded when the limit is hit. One `awk` (`memory_index_snapshot` in `runtime/lib/memory.sh`) now reads the index and opens each linked card itself, the payload and the resolution are each read by one `jq`, the episode log is read once instead of twice, and the age bucket no longer needs a command substitution per ranked card. **What it prints is unchanged**: compared with the previous hook on the same fixtures, the output and the usage table are identical for pinned cards (also quoted, after a preamble line), duplicate keys, fields after the closing `---`, a missing card, link-less lines, ASCII case folding, multi-line prompts, and fourteen kinds of last episode record. Measured on native Windows with synthetic cards, one run each: 10 cards 4.6 s to about 2.1 s, 50 cards 12.4 to 13.2 s to about 2.2 s, 100 cards 25.1 s to about 2.9 s, 200 cards 48.2 s to about 3.5 to 3.7 s; 1000 cards about 7 s before and about 8 s after the check that a linked card is a regular file was put back (the old hook was not run at that size). The fixed cost of about 2 s is not touched (the memory directory resolution, 0.8 s of it, computes the project key twice, and the usage store is still read and written synchronously), and the time still grows with the number of cards, only about fifty times more slowly; whether to keep a metadata index is left open in CC-686. New suite `tests/shell/test-memory-hook.sh`.
- **The working-tree fingerprint hashes its files in one call instead of one process per file (CC-685, issue #736).** A gate computes the fingerprint of the reviewed tree three times that its phase events show (the subject snapshot, and before and after the pre-flight tests) and, by the call sites, twice more after the reviewers (the pre-flight freshness check and the final assurance snapshot; confirmed by the phase events of the final gate), and each one started a `sha256sum` per tracked file. On native Windows, where a process costs tens of milliseconds, that was 118 to 127 seconds for the 564 files of this repository (78 to 85 seconds inside a gate), about 85% of it the per-file digests. `_gate_subject_tree_fingerprint` now takes the digests of the regular files with one `xargs -0 sha256sum -z --`, keeps them for that call only, and quotes paths without a subshell: 105 seconds became 5 on the same tree, 6 seconds became 1 under WSL. **The fingerprint is bit-for-bit the same** (the same value on native Windows and on Linux for the same tree), so a result that was recorded before this change is still judged current. A file the batch cannot carry (a name with a bracket, backslash or newline, exactly `@`, `*` or `-`), a failed batch, or a `sha256sum` without `-z` falls back to the old one-at-a-time hashing, which still reports a read error with status 2. `PM_GATE_SUBJECT_BATCH_DIGESTS=0` turns the batch off. The first version did not engage inside a real detached gate: xargs refused to run ("environment is too large for exec") because a detached gate's environment is tens of kilobytes, and the fingerprint silently fell back to one process per file, so the gate stayed as slow as before while a standalone call took 5 seconds. xargs now runs under a minimal environment (`env -i PATH`), and each fingerprint records `digests batched=N per-file=M` as a phase event, so `pmctl gate status` shows a batch that fell back. Measured with gates that stop after their pre-flight, same machine, one run each: the subject snapshot went from 72 to 88 seconds to 8, and the two pre-flight fingerprints from 72 to 85 seconds to 5 to 7. On Linux the saving is a few seconds per fingerprint. A whole gate with reviewers, measured once on the same machine in the same mode: 15 min 50 s against 32 min 11 s for the earlier gate of this change. The batch accounts for about 5 minutes of that (the snapshot, the four other fingerprints, and the stretch after the reviewers that had no event); the rest is run-to-run variance in model time and test time, not measured.
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@ router and [`cli/commands.tsv`](cli/commands.tsv).
- `gate status` — Show where a detached gate is: state, phase, last progress, log and result. [experimental; JSON: false; mutating: false]
- `gate cancel` — Stop the verified gate producer process tree, then cancel only its recorded child runs; partial termination stays indeterminate. [experimental; JSON: false; mutating: true]
- `gate reconcile` — Reconcile a gate operation from trusted child terminal claims. [experimental; JSON: false; mutating: true]
- `gate result` — Print a gate's published result: verdicts, conclusion and the result file path, with whether gate verify accepts it. [experimental; JSON: false; mutating: false]
- `gate mutations` — Before a gate: list the test files this change touches and the mutation classes qa-tester asks of each test. [experimental; JSON: false; mutating: false]
- `gate stats` — Aggregate operational cost of past PR-gate runs from on-disk artifacts. [experimental; JSON: true; mutating: false]
- `context index` — Build the repository context index. [experimental; JSON: false; mutating: true]
- `context update` — Update the repository context index. [experimental; JSON: false; mutating: true]
Expand Down
2 changes: 2 additions & 0 deletions cli/commands.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ gate wait Wait for a detached gate run. pmctl gate wait <gate-id> [options] expe
gate status Show where a detached gate is: state, phase, last progress, log and result. pmctl gate status <gate-id> [--cd <dir>] experimental false false --cd pmctl gate status gate-123 --cd .
gate cancel Stop the verified gate producer process tree, then cancel only its recorded child runs; partial termination stays indeterminate. pmctl gate cancel <operation-id> --cd <dir> [--grace <seconds>] experimental false true --cd, --grace pmctl gate cancel op-20260724T000000Z-abcdef --cd .
gate reconcile Reconcile a gate operation from trusted child terminal claims. pmctl gate reconcile <operation-id> --cd <dir> experimental false true --cd pmctl gate reconcile op-20260724T000000Z-abcdef --cd .
gate result Print a gate's published result: verdicts, conclusion and the result file path, with whether gate verify accepts it. pmctl gate result <gate-id|result-file> [--cd <dir>] experimental false false --cd pmctl gate result gate-123 --cd .
gate mutations Before a gate: list the test files this change touches and the mutation classes qa-tester asks of each test. pmctl gate mutations --preview [--cd <dir>] [--base <ref>] experimental false false --preview, --cd, --base pmctl gate mutations --preview --cd .
gate stats Aggregate operational cost of past PR-gate runs from on-disk artifacts. pmctl gate stats [--since <date>] [--json] [--cd <dir>] experimental true false --since, --json, --cd pmctl gate stats --since 2026-08-01 --json
context index Build the repository context index. pmctl context index [<repo-root>] [--source <repo|memory>] [--budget-ms <ms>] experimental false true --source, --budget-ms pmctl context index . --source repo
context update Update the repository context index. pmctl context update [<repo-root>] [<path>] experimental false true none pmctl context update . README.md
Expand Down
12 changes: 12 additions & 0 deletions cli/pmctl
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,18 @@ case "$cmd/$sub" in
fi
pmctl_gate_wait "$REPO_ROOT" "$@"
;;
gate/result)
if ! declare -F pmctl_gate_result >/dev/null; then
pmctl_die "gate result unavailable"
fi
pmctl_gate_result "$REPO_ROOT" "$@"
;;
gate/mutations)
if ! declare -F pmctl_gate_mutations >/dev/null; then
pmctl_die "gate mutations unavailable"
fi
pmctl_gate_mutations "$REPO_ROOT" "$@"
;;
gate/status)
if ! declare -F pmctl_gate_status >/dev/null; then
pmctl_die "gate status unavailable"
Expand Down
5 changes: 5 additions & 0 deletions docs/architecture/script-variable-consumers.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ CODEX_GATE_STUB_* CODEX_GATE_STUB_PROTOCOL_MUTATION_ON_RETRY tests/shell/test-pr
CODEX_GATE_STUB_* CODEX_GATE_STUB_QA_ABORT_AFTER_CHECKPOINT tests/shell/test-pr-gate.sh test
CODEX_GATE_STUB_* CODEX_GATE_STUB_QA_RUN_COMMAND tests/shell/test-pr-gate.sh test
CODEX_GATE_STUB_* CODEX_GATE_STUB_QA_TAMPER_EVIDENCE tests/shell/test-pr-gate.sh test
CODEX_GATE_STUB_* CODEX_GATE_STUB_QUOTE_QUOTA tests/shell/test-pr-gate.sh test
CODEX_GATE_STUB_* CODEX_GATE_STUB_RESULT_VERSION tests/shell/test-pr-gate.sh test
CODEX_GATE_STUB_* CODEX_GATE_STUB_SYNTHESIS_EXTRA_FINAL tests/shell/test-pr-gate.sh test
CODEX_GATE_STUB_* CODEX_GATE_STUB_SYNTHESIS_FINAL tests/shell/test-pr-gate.sh test
Expand Down Expand Up @@ -152,6 +153,7 @@ MSYS2_ARG_CONV_EXCL MSYS2_ARG_CONV_EXCL hosts/claude/bin/install-guards.sh produ
MSYS2_ARG_CONV_EXCL MSYS2_ARG_CONV_EXCL hosts/claude/bin/uninstall-guards.sh production
MSYS2_ARG_CONV_EXCL MSYS2_ARG_CONV_EXCL hosts/codex/bin/install.sh production
MSYS2_ARG_CONV_EXCL MSYS2_ARG_CONV_EXCL hosts/codex/lib/hook-paths.sh production
MSYS2_ARG_CONV_EXCL MSYS2_ARG_CONV_EXCL ops/diagnostics/wsl-run.sh production
MSYS2_ARG_CONV_EXCL MSYS2_ARG_CONV_EXCL runtime/lib/gate-assurance.sh production
MSYS2_ARG_CONV_EXCL MSYS2_ARG_CONV_EXCL runtime/lib/gate-policy.sh production
MSYS2_ARG_CONV_EXCL MSYS2_ARG_CONV_EXCL runtime/lib/gate-protocol.sh production
Expand Down Expand Up @@ -231,6 +233,7 @@ PATH PATH ops/diagnostics/gate-subprocess-census.sh production
PATH PATH ops/diagnostics/run-ci-lints.sh production
PATH PATH ops/diagnostics/run-tests-in-wsl.sh production
PATH PATH ops/diagnostics/windows-acceptance.sh production
PATH PATH ops/diagnostics/wsl-run.sh production
PATH PATH ops/release/clean-install-smoke.sh production
PATH PATH ops/release/release-verify.sh production
PATH PATH ops/release/upgrade-smoke-v0.10-v0.11.sh production
Expand Down Expand Up @@ -273,6 +276,8 @@ PM_CFG_* PM_CFG_DEFAULT_MODEL adapters/grok/dispatch.sh production
PM_CFG_* PM_CFG_DEFAULT_MODEL adapters/opencode/dispatch.sh production
PM_CFG_* PM_CFG_DEFAULT_MODEL runtime/lib/pmctl-config.sh production
PM_CFG_* PM_CFG_DEFAULT_MODEL runtime/lib/pmctl-dispatch.sh production
PM_CFG_* PM_CFG_GATE_REVIEWER_EXECUTOR runtime/bin/pr-gate.sh production
PM_CFG_* PM_CFG_GATE_REVIEWER_EXECUTOR runtime/lib/pmctl-config.sh production
PM_CFG_* PM_CFG_LEGACY_MEMORY_DIR runtime/lib/pmctl-config.sh production
PM_CFG_* PM_CFG_LEGACY_MEMORY_DIR runtime/lib/pmctl-memory-config.sh production
PM_CFG_* PM_CFG_LEGACY_MEMORY_DIR_INVALID runtime/lib/pmctl-config.sh production
Expand Down
Loading
Loading