Skip to content

ci(live): run corpus equivalence as a required CTest row (CR-223-ROW) - #481

Merged
luisleo526 merged 15 commits into
mainfrom
core/cr150-cr223row
Oct 25, 2026
Merged

luisleo526 merged 15 commits into
mainfrom
core/cr150-cr223row

Conversation

@luisleo526

Copy link
Copy Markdown
Collaborator

What this does

tests/native_live_equivalence_e2e.py is a real end-to-end test: it drives the
live runner over the corpus and asserts engine trades equal the recorded ones.
It passed on a spot box at 80759276ea (exit 0,
EQUIVALENCE_SUMMARY probes=8 scenarios=109 failed=0 observer_failed=0, 983 s),
but nothing ran it automatically: no add_test named it, and
scripts/ci_verify.py excluded it by name from runner_e2e_test_names()
(asserted by scripts/test_ci_verify.py).

This registers it as a permanent CTest row and removes the exclusion.

  • Row native_live_equivalence_e2e, label private_fixtures,
    SKIP_RETURN_CODE 77, registered at runner/CMakeLists.txt:472 inside the
    existing live-runner + source-layer block (the same block that owns the
    sibling native_live_genuine_retry_e2e).
  • The row runs tests/native_live_equivalence_ctest.py, which discovers the
    prerequisites from the real driver's own PROBES declaration by AST/literal
    eval (no duplicated probe list), then executes the real script as __main__
    with its full defaults — no probe, scenario or count reduction, no reuse
    flags. A missing prerequisite returns 77 and prints
    SKIP native_live_equivalence_e2e prerequisites absent: <paths>; once
    execution starts, a driver exit 77 is reclassified as a failure, so an
    output regex cannot turn a runtime failure into a skip.
  • scripts/ci_verify.py: the runner_e2e_test_names exclusion is dropped; a
    required row that skips now fails the profile regardless of the numeric
    floor; floors are raised, not lowered (native 963 -> 964,
    LIVE_SANITIZERS_MIN_TESTS 12 -> 13, PRIVATE_FIXTURE_ROW_COUNT 375 -> 376),
    and tests/private_fixture_rows.txt is updated.
  • .github/workflows/native-live.yml installs git-lfs so the corpus feed is
    explicitly provisioned rather than relying on an implicit checkout smudge
    (scripts/ci_verify.py ensure_corpus_submodule() still initializes the
    recorded gitlink).

Where the row actually runs

The row is registered only under
if(PINEFORGE_BUILD_TESTS AND PINEFORGE_BUILD_SOURCE_LAYER) inside runner/,
which the root adds under if(PINEFORGE_BUILD_LIVE_RUNNER)
(CMakeLists.txt:509-510). The release, debug and docs profiles build
with PINEFORGE_BUILD_LIVE_RUNNER=OFF, so those profiles do not configure the
row at all. The profiles that do are native, live-sanitizers, live-tsan
(scripts/ci_verify.py:949-959), all matrix jobs of native-live.yml — so the
row's proof is this PR's own native-live CI: native_live_equivalence_e2e
present and Passed there. A ***Not Run / absent row, or a skip in
live-sanitizers/live-tsan, fails the job per scripts/ci_verify.py:2190.

What is proven, and what is not

Proven on this head (clean tree, lab remote four-step, NTU): preflight, release
(962 rows), debug, docs all exit 0 — the tree builds and every other row passes.

Not proven by that job: it never executes this row. The row's execution proof is
the native-live CI run on this PR; a skip there is visible in the CTest log and,
in a live profile, is a failure.

No push, merge or rebase was done by the lane; this branch is the lane head.

🤖 Generated with Claude Code

luisleo526 and others added 10 commits October 22, 2026 08:32
The corpus equivalence driver becomes a permanent runner CTest row, and the
runner-e2e equivalence exclusion leaves `ci_verify.py`.

Two sentences of `docs/ci.md` that dbd17b3 ("Live runner: normalized feed input
and per-action webhook routing (#320)") introduced are retired here because this
change makes them false, and the message names them so the doc-revert gate can
read the retirement:

- "The standalone corpus equivalence driver is not a runner CTest row; its
  harness unit tests remain in the engine-wide inventory." The driver is now the
  `native_live_equivalence_e2e` row in `runner/CMakeLists.txt`, and the same
  sentence's second clause (the harness unit tests staying in the engine-wide
  inventory) is kept verbatim in the replacement text.
- "It refuses label exclusions, missing runner compile commands, missing
  ASan/UBSan or frame-pointer flags, skipped/disabled tests, and, while
  `runner/transport.cpp` exists, a missing WebSocket row or transport skip even
  without `--require-websocket`." Its "skipped/disabled tests" clause is rewritten
  because a required runner e2e skip now fails outright; every other clause is
  kept in the replacement paragraph.

Restored on purpose: nothing. The rest of the diff only adds text.
Bring main (717c75d) into the PR branch with a merge commit; no conflicts.

Lane: ci-m481 (CI-CX).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Bring current main into the branch with a merge commit; no conflicts. Statuses recorded on the old
tree are void for the gate, so the branch is re-verified on the merged tree.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Main moved by one AGENTS.md-only commit. Merge commit, no conflicts. The earlier full verification
of this branch is carried across that docs-only delta (preflight and docs are re-run on the new tree).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Two textual conflicts, one semantic follow-up:

- .github/workflows/native-live.yml: main added strace to the native build
  dependencies, this PR added git-lfs; the install line keeps both.
- scripts/ci_verify.py EXCLUDED_REGISTERED_MIN: main's native floor is 963,
  this PR adds one native runner row, so the floor stays 964; main's reworded
  D1 comment is kept.
- scripts/test_ci_verify.py: main's test_the_native_profile_is_not_under_this_rule
  (not in the PR's base) ran the native profile at 963 registered and 963 rows;
  with this PR's row the tree registers 964, so the test runs at 964/964.

Doc deletions that arrive from main with this merge, named for
check_doc_reverts (they are main's own edits to docs/ci.md, not changes made
here): six sentences deleted or reworded by 717c75d (promote-baseline refuses
untrusted heads and actors, #482) and by 3a0d150 (the CR-233 split of the PR
wall from full-event verification, #477): "Only after those checks can an
unmerged close pass without a merger lookup and skip `promote`", "The trigger
has no `branches:` filter and the guard does not require a `main` base ref",
"Release and promotion are unchanged:", "The same stage pins the complete shared
`promote-guard` block byte for byte", "Decision tests execute the guard script
extracted from the real workflow with bash and an executable `gh` stub first on
PATH", "Mutation tests alter the real promotion workflow to replace the script
with `exit 0`".

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The tree is identical to the previous head of the PR branch (46fe81f, which
already contained main). This merge only reverses the parent order so that the
first-parent chain of the head reaches main's last pull request commit
(7a7d099, #509) at once. check_doc_reverts finds its base on a checkout
with no branch refs (the lab hosts) from that chain; with the PR branch as the
first parent it fell back to c32c732 (#478) and judged main's own commits
(717c75d, 3a0d150, ...) as if this PR had made their doc deletions.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…y, gate unchanged) [replaces #489] (#505)

* ci(release): the ruleset probe lists every branch ruleset as visible or hidden, even when the gate verdict is a refusal (CR-245 F1 measurement)

The one dispatch of release-ruleset-probe.yml from main (run 40083495967) refused on the first branch ruleset
of the inventory, the organization ruleset `default` (13094758): "returned missing or invalid bypass_actors
(token cannot see this ruleset's bypass_actors)". The verdict reader stops at the first failure, so the run
did not say whether the release App token can see bypass_actors on the repository ruleset that is the real
barrier (21476386), nor on any other. The probe now first prints one line per branch ruleset of the
includes_parents inventory (id, name, source_type, source, enforcement, updated_at, bypass_actors visible
with the actor count or hidden, applies to main, has a pull_request rule), then runs the unchanged verdict:
success prints the existing success lines; a refusal prints the same ::error:: text on stderr and exits 1,
but stdout now carries the listing. A detail read that fails is reported on its own row and the listing
continues. The listing is a measurement: `gate`, `_release_bump` and `_require_release_rulesets` are
untouched, every gate test and the existing source mutants are kept, and the probe's verdict replay over every
existing ruleset fixture still compares verdict and refusal text byte for byte. The workflow
release-ruleset-probe.yml is not changed. The listing reads each branch ruleset detail independently of the
verdict read (two reads per detail in the probe), which docs/ci.md now says.

Red-first rows: the real situation (org ruleset hidden, repository ruleset visible: both listed, the org one
hidden, refusal unchanged), both hidden, one detail read failing, all visible with a valid barrier, the
read-only call list, and a source mutant that drops the listing.

Rewrites named for the doc-reverts check, all introduced by 9c18d41 (#488, "ci(release): read-only ruleset
probe for the release gate (CR-245 F1)"): docs/ci.md "Success prints the barrier id/name and one compact
inventory line with enforcement, source and bypass visibility."; docs/ci.md "Refusal exits 1 with empty stdout
and the reader's `::error::` diagnostic; API/response failures name the endpoint."; docs/ci.md "Two hidden
rulesets need two dispatches: the first refusal names only the first hidden ruleset; make its `bypass_actors`
visible before dispatching again to identify the second." (replaced: all hidden rulesets are listed in one
dispatch); the probe docstring "Read exactly the bump's ruleset evidence, with the same refusal policy."; two
test comments ("The reader report's complete [] inventory fails on main without an endpoint." and "The
runtime assertions already hold there; the prose does not."). The module docstring sentence "Each
endpoint/page is read at most once; failures refuse without retry or sleep." (ad1ebac, #479) is scoped to the
gate's reads.

Lane: ci-fu-cr488m (Codex gpt-6.1-sol).

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

* ci(release): the probe listing bounds its inventory=ERROR row to 240 characters like the detail-error rows (CR-245 F1 measurement, sol P2)

The new stdout listing printed the whole exception text on an `inventory=ERROR` row, while the per-ruleset
`detail=ERROR` rows truncate it to 240 characters; a `gh` transport error with 300 characters of stderr was
echoed in full. The row now applies the same bound ([:240], still !r-escaped). Four rows assert the exact bounded
line (an inventory transport error, a repository transport error, a default-branch rejection with a long repository
argument, an invalid summary with a long field) together with the unchanged stderr, exit code 1 and API call list
of the verdict; each has a mutation control that removes only the bound and must fail. The verdict functions are
untouched and the verdict's own stderr is not bounded. No documentation sentence is changed (none described a length
limit).

Lane: ci-fu-cr488n (Codex gpt-6.1-sol).

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

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
@luisleo526
luisleo526 marked this pull request as ready for review October 25, 2026 09:57
luisleo526 and others added 5 commits October 25, 2026 19:09
…ncestry check and the doc-reverts default base read (ORG-ACTIONPINS-CARD) [replaces #494] (#507)

* ci: fully qualify the two remote-tracking refs the promote-baseline ancestry check and the doc-reverts default base read (ORG-ACTIONPINS-CARD, LB-464 P3)

promote-baseline.yml tested `git merge-base --is-ancestor "$MERGE_SHA" "origin/$BASE_REF"`, and
check_doc_reverts.py default_base() resolved the bare names `origin/main` then `main`. A short name resolves through
git's ref-lookup order, so a tag called `origin/main` (or `main`) would win over the remote-tracking branch or the
local branch. Both now use the fully qualified names (`refs/remotes/origin/$BASE_REF`; `refs/remotes/origin/main`,
then `refs/heads/main`, same order and same fallback to the merged-pull-request walk). The exact-text pin in
scripts/ci_preflight.py is updated to the new command and stays as strict; the old short form is now a finding.

Red-first tests: the pin refuses the short command and accepts the qualified one; a repository where a TAG named
`origin/main` points at an unrelated commit and the real remote-tracking ref points elsewhere, and the same for a tag
named `main` with no remote-tracking ref, each returns the merge base of the real ref and not the tag's. Five controls
keep the order, the environment override, the local fallback, the merged-pull-request fallback and the no-base result.
The first-parent walk is untouched (the lab-host defect is a separate card).

The default_base docstring in scripts/check_doc_reverts.py is re-wrapped around the two renamed refs; the sentences it
carries come from 1e360bd and d093560 and keep their wording, only the ref names change.

Lane: ci-fu-shortrefs (Codex gpt-6.1-sol).

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

* ci: release ruleset probe lists every branch ruleset (measurement only, gate unchanged) [replaces #489] (#505)

* ci(release): the ruleset probe lists every branch ruleset as visible or hidden, even when the gate verdict is a refusal (CR-245 F1 measurement)

The one dispatch of release-ruleset-probe.yml from main (run 40083495967) refused on the first branch ruleset
of the inventory, the organization ruleset `default` (13094758): "returned missing or invalid bypass_actors
(token cannot see this ruleset's bypass_actors)". The verdict reader stops at the first failure, so the run
did not say whether the release App token can see bypass_actors on the repository ruleset that is the real
barrier (21476386), nor on any other. The probe now first prints one line per branch ruleset of the
includes_parents inventory (id, name, source_type, source, enforcement, updated_at, bypass_actors visible
with the actor count or hidden, applies to main, has a pull_request rule), then runs the unchanged verdict:
success prints the existing success lines; a refusal prints the same ::error:: text on stderr and exits 1,
but stdout now carries the listing. A detail read that fails is reported on its own row and the listing
continues. The listing is a measurement: `gate`, `_release_bump` and `_require_release_rulesets` are
untouched, every gate test and the existing source mutants are kept, and the probe's verdict replay over every
existing ruleset fixture still compares verdict and refusal text byte for byte. The workflow
release-ruleset-probe.yml is not changed. The listing reads each branch ruleset detail independently of the
verdict read (two reads per detail in the probe), which docs/ci.md now says.

Red-first rows: the real situation (org ruleset hidden, repository ruleset visible: both listed, the org one
hidden, refusal unchanged), both hidden, one detail read failing, all visible with a valid barrier, the
read-only call list, and a source mutant that drops the listing.

Rewrites named for the doc-reverts check, all introduced by 9c18d41 (#488, "ci(release): read-only ruleset
probe for the release gate (CR-245 F1)"): docs/ci.md "Success prints the barrier id/name and one compact
inventory line with enforcement, source and bypass visibility."; docs/ci.md "Refusal exits 1 with empty stdout
and the reader's `::error::` diagnostic; API/response failures name the endpoint."; docs/ci.md "Two hidden
rulesets need two dispatches: the first refusal names only the first hidden ruleset; make its `bypass_actors`
visible before dispatching again to identify the second." (replaced: all hidden rulesets are listed in one
dispatch); the probe docstring "Read exactly the bump's ruleset evidence, with the same refusal policy."; two
test comments ("The reader report's complete [] inventory fails on main without an endpoint." and "The
runtime assertions already hold there; the prose does not."). The module docstring sentence "Each
endpoint/page is read at most once; failures refuse without retry or sleep." (ad1ebac, #479) is scoped to the
gate's reads.

Lane: ci-fu-cr488m (Codex gpt-6.1-sol).

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

* ci(release): the probe listing bounds its inventory=ERROR row to 240 characters like the detail-error rows (CR-245 F1 measurement, sol P2)

The new stdout listing printed the whole exception text on an `inventory=ERROR` row, while the per-ruleset
`detail=ERROR` rows truncate it to 240 characters; a `gh` transport error with 300 characters of stderr was
echoed in full. The row now applies the same bound ([:240], still !r-escaped). Four rows assert the exact bounded
line (an inventory transport error, a repository transport error, a default-branch rejection with a long repository
argument, an invalid summary with a long field) together with the unchanged stderr, exit code 1 and API call list
of the verdict; each has a mutation control that removes only the bound and must fail. The verdict functions are
untouched and the verdict's own stderr is not bounded. No documentation sentence is changed (none described a length
limit).

Lane: ci-fu-cr488n (Codex gpt-6.1-sol).

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

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: ci-cx <ci-cx@invalid>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…rapped, bytes-decoded and fullwidth respellings (PROBE-FU) [replaces #493] (#506)

* ci(release): the permission-claim text guard also catches backslash-wrapped, bytes-decoded and fullwidth respellings (PROBE-FU, sol P2 1/2/4)

The guard in WorkflowPinTests.assert_ruleset_visibility_documentation fails when the gate source, docs/ci.md or
release.yml state that the App needs "administration: read". A read of the probe found three respellings it accepted:
a Markdown hard break written as a backslash before the newline, a bytes constant (b"administr\x61tion: re\x61d",
with or without .decode()), and fullwidth compatibility letters. The collapsed view now treats backslash-newline like
whitespace, bytes constants are decoded into the scanned strings, and every view is NFKC-normalised before case
folding. Three new tests (12 rows) each read the real artifacts, assert the unmodified baseline passes, mutate a copy
and require the specific permission-claim AssertionError; removing only the matching handling turns its rows red.

Not closed, by design: YAML double-quoted scalars with escapes in release.yml (sol P2 3) and runtime string
construction (sol P3); a static text guard cannot decide them, and the exact-output pins on the refusal text remain the
decisive protection. No existing assertion, control or forbidden pattern is changed. The rewritten `view` tuple is code,
not a documentation or comment sentence.

Lane: ci-fu-pfuguard (Codex gpt-6.1-sol).

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

* ci(release): the permission-claim guard scans the casefold-only views as well as the NFKC views (PROBE-FU round 2, sol R237 P2)

The first PROBE-FU commit normalised every scanned view with NFKC and replaced the casefold-only views. For an input
such as `Needs administration: read` followed by the trade mark sign, the old guard matched (the sign is a non-word
character after `read`), but after NFKC the text is `readtm` and the word boundary no longer matches, so the claim was
accepted: the change made the guard weaker for that input. Each text is now checked in four views (the two original
views casefolded, and both NFKC+casefolded), so normalisation can only add matches. A restoration control appends the
trade mark case to the gate source, docs/ci.md and the workflow text (direct and line-wrapped, six rows), and a removal
mutant drops the casefold-only scan and must break all six rows.

No existing assertion or control changes; no documentation or comment sentence is deleted or rewritten.

Lane: ci-fu-pfuguard2 (Codex gpt-6.1-sol).

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

* ci: release ruleset probe lists every branch ruleset (measurement only, gate unchanged) [replaces #489] (#505)

* ci(release): the ruleset probe lists every branch ruleset as visible or hidden, even when the gate verdict is a refusal (CR-245 F1 measurement)

The one dispatch of release-ruleset-probe.yml from main (run 40083495967) refused on the first branch ruleset
of the inventory, the organization ruleset `default` (13094758): "returned missing or invalid bypass_actors
(token cannot see this ruleset's bypass_actors)". The verdict reader stops at the first failure, so the run
did not say whether the release App token can see bypass_actors on the repository ruleset that is the real
barrier (21476386), nor on any other. The probe now first prints one line per branch ruleset of the
includes_parents inventory (id, name, source_type, source, enforcement, updated_at, bypass_actors visible
with the actor count or hidden, applies to main, has a pull_request rule), then runs the unchanged verdict:
success prints the existing success lines; a refusal prints the same ::error:: text on stderr and exits 1,
but stdout now carries the listing. A detail read that fails is reported on its own row and the listing
continues. The listing is a measurement: `gate`, `_release_bump` and `_require_release_rulesets` are
untouched, every gate test and the existing source mutants are kept, and the probe's verdict replay over every
existing ruleset fixture still compares verdict and refusal text byte for byte. The workflow
release-ruleset-probe.yml is not changed. The listing reads each branch ruleset detail independently of the
verdict read (two reads per detail in the probe), which docs/ci.md now says.

Red-first rows: the real situation (org ruleset hidden, repository ruleset visible: both listed, the org one
hidden, refusal unchanged), both hidden, one detail read failing, all visible with a valid barrier, the
read-only call list, and a source mutant that drops the listing.

Rewrites named for the doc-reverts check, all introduced by 9c18d41 (#488, "ci(release): read-only ruleset
probe for the release gate (CR-245 F1)"): docs/ci.md "Success prints the barrier id/name and one compact
inventory line with enforcement, source and bypass visibility."; docs/ci.md "Refusal exits 1 with empty stdout
and the reader's `::error::` diagnostic; API/response failures name the endpoint."; docs/ci.md "Two hidden
rulesets need two dispatches: the first refusal names only the first hidden ruleset; make its `bypass_actors`
visible before dispatching again to identify the second." (replaced: all hidden rulesets are listed in one
dispatch); the probe docstring "Read exactly the bump's ruleset evidence, with the same refusal policy."; two
test comments ("The reader report's complete [] inventory fails on main without an endpoint." and "The
runtime assertions already hold there; the prose does not."). The module docstring sentence "Each
endpoint/page is read at most once; failures refuse without retry or sleep." (ad1ebac, #479) is scoped to the
gate's reads.

Lane: ci-fu-cr488m (Codex gpt-6.1-sol).

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

* ci(release): the probe listing bounds its inventory=ERROR row to 240 characters like the detail-error rows (CR-245 F1 measurement, sol P2)

The new stdout listing printed the whole exception text on an `inventory=ERROR` row, while the per-ruleset
`detail=ERROR` rows truncate it to 240 characters; a `gh` transport error with 300 characters of stderr was
echoed in full. The row now applies the same bound ([:240], still !r-escaped). Four rows assert the exact bounded
line (an inventory transport error, a repository transport error, a default-branch rejection with a long repository
argument, an invalid summary with a long field) together with the unchanged stderr, exit code 1 and API call list
of the verdict; each has a mutation control that removes only the bound and must fail. The verdict functions are
untouched and the verdict's own stderr is not bounded. No documentation sentence is changed (none described a length
limit).

Lane: ci-fu-cr488n (Codex gpt-6.1-sol).

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

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: ci-cx <ci-cx@invalid>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…e, P2-CM-F4) (#504)

* tests: drop the TIMEOUT 180 property from the six ownership manifest tests (P2-CM-F4)

One set_tests_properties call carried TIMEOUT 180 for test_selected_window_ownership_manifest,
the four test_selected_window_ownership_manifest_controls_shard_{1..4}of4 and
test_selected_window_ownership_d4. The owner's no-time-limit rule forbids a ctest
ceiling: under pool contention shard 1of4 (119 s normally) hit the 180 s ceiling on the
b250ceb merge-commit ci-post (#283). WORKING_DIRECTORY is kept; no test, label or
line count changes.

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

* ci: release ruleset probe lists every branch ruleset (measurement only, gate unchanged) [replaces #489] (#505)

* ci(release): the ruleset probe lists every branch ruleset as visible or hidden, even when the gate verdict is a refusal (CR-245 F1 measurement)

The one dispatch of release-ruleset-probe.yml from main (run 40083495967) refused on the first branch ruleset
of the inventory, the organization ruleset `default` (13094758): "returned missing or invalid bypass_actors
(token cannot see this ruleset's bypass_actors)". The verdict reader stops at the first failure, so the run
did not say whether the release App token can see bypass_actors on the repository ruleset that is the real
barrier (21476386), nor on any other. The probe now first prints one line per branch ruleset of the
includes_parents inventory (id, name, source_type, source, enforcement, updated_at, bypass_actors visible
with the actor count or hidden, applies to main, has a pull_request rule), then runs the unchanged verdict:
success prints the existing success lines; a refusal prints the same ::error:: text on stderr and exits 1,
but stdout now carries the listing. A detail read that fails is reported on its own row and the listing
continues. The listing is a measurement: `gate`, `_release_bump` and `_require_release_rulesets` are
untouched, every gate test and the existing source mutants are kept, and the probe's verdict replay over every
existing ruleset fixture still compares verdict and refusal text byte for byte. The workflow
release-ruleset-probe.yml is not changed. The listing reads each branch ruleset detail independently of the
verdict read (two reads per detail in the probe), which docs/ci.md now says.

Red-first rows: the real situation (org ruleset hidden, repository ruleset visible: both listed, the org one
hidden, refusal unchanged), both hidden, one detail read failing, all visible with a valid barrier, the
read-only call list, and a source mutant that drops the listing.

Rewrites named for the doc-reverts check, all introduced by 9c18d41 (#488, "ci(release): read-only ruleset
probe for the release gate (CR-245 F1)"): docs/ci.md "Success prints the barrier id/name and one compact
inventory line with enforcement, source and bypass visibility."; docs/ci.md "Refusal exits 1 with empty stdout
and the reader's `::error::` diagnostic; API/response failures name the endpoint."; docs/ci.md "Two hidden
rulesets need two dispatches: the first refusal names only the first hidden ruleset; make its `bypass_actors`
visible before dispatching again to identify the second." (replaced: all hidden rulesets are listed in one
dispatch); the probe docstring "Read exactly the bump's ruleset evidence, with the same refusal policy."; two
test comments ("The reader report's complete [] inventory fails on main without an endpoint." and "The
runtime assertions already hold there; the prose does not."). The module docstring sentence "Each
endpoint/page is read at most once; failures refuse without retry or sleep." (ad1ebac, #479) is scoped to the
gate's reads.

Lane: ci-fu-cr488m (Codex gpt-6.1-sol).

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

* ci(release): the probe listing bounds its inventory=ERROR row to 240 characters like the detail-error rows (CR-245 F1 measurement, sol P2)

The new stdout listing printed the whole exception text on an `inventory=ERROR` row, while the per-ruleset
`detail=ERROR` rows truncate it to 240 characters; a `gh` transport error with 300 characters of stderr was
echoed in full. The row now applies the same bound ([:240], still !r-escaped). Four rows assert the exact bounded
line (an inventory transport error, a repository transport error, a default-branch rejection with a long repository
argument, an invalid summary with a long field) together with the unchanged stderr, exit code 1 and API call list
of the verdict; each has a mutation control that removes only the bound and must fail. The verdict functions are
untouched and the verdict's own stderr is not bounded. No documentation sentence is changed (none described a length
limit).

Lane: ci-fu-cr488n (Codex gpt-6.1-sol).

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

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…comment, and lint it in preflight (ORG-ACTIONPINS) [replaces #495] (#508)

* ci: pin every third-party action in the engine workflows to a full commit SHA with a version comment (ORG-ACTIONPINS)

66 `uses:` lines changed in .github/workflows/*.yml and .github/actions/setup-live-curl/action.yml: 65 mutable tags
rewritten to `@<40-hex sha> # v<version>` and one bare SHA that lacked its comment. Each pin is the same major the file
used before; no step, job, permission, environment, secret or token mint changes (a before/after comparison of the
diff with the refs stripped is identical line for line). Resolved on 2026-10-24 22:48 with `git ls-remote --tags` on a
spot box; every tag is lightweight, so the tag's sha is the commit:

  actions/checkout v4                         11d5960a326750d5838078e36cf38b85af677262  v4.4.0
  actions/upload-artifact v4                  ea165f8d65b6e75b540449e92b4886f43607fa02  v4.6.2
  actions/setup-python v5                     a26af69be951a213d495a4c3e4e4022e16d87065  v5.6.0
  actions/cache, cache/save, cache/restore v4 0057852bfaa89a56745cba8c7296529d2fc39830  v4.3.0
  actions/create-github-app-token v1          d72941d797fd3113feb6b93fd0dec494b13a2547  v1.12.0
  cloudflare/wrangler-action v3               9acf94ace14e7dc412b076f2c5c20b8ce93c79cd  v3.15.0

The byte-pinned copies of the workflow text that scripts/ci_preflight.py, scripts/test_ci_preflight.py and
scripts/test_ci_release_gate.py compare against (the embedded probe workflow, the release and cache expectations,
the Wrangler deployment, test mutations and fixtures) are moved to the same pinned text so each still equals its
workflow. Two tests read the real workflows and composite action and require a 40-hex sha and a version comment on
every third-party reference.

No documentation or comment sentence is deleted or rewritten.

Lane: ci-fu-pins (Codex gpt-6.1-sol).

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

* ci(preflight): refuse any third-party `uses:` that is not a 40-hex commit SHA with a same-line version comment; the lint accepts plain block-style YAML only (ORG-ACTIONPINS)

scripts/ci_preflight.py gains `action_pin_findings`: every `uses:` in .github/workflows/*.yml and every composite
.github/actions/**/action.yml (or .yaml) that is not a local `./` path or a `docker://` image must be
`owner/action[/path]@<40 lowercase hex> # v<version>` on the same line; quoted values, step and job-level `uses:`, remote
reusable workflows and a ref-less `uses:` are judged; the finding names file, line and the offending reference. The lint
is a regression guard for our own reviewed workflow files, so it does not parse arbitrary YAML: it accepts plain block-style
YAML and refuses, with a finding naming file, line, construct and source text, what it cannot follow: anchors, aliases, merge
keys, explicit keys, tags, non-empty flow mappings, quoted keys, backslash escapes in quoted values, quoted values that do not
end on their line, tab indentation, document markers, `uses` without a same-line scalar, unrecognised block lines, non-ASCII
whitespace in structural positions, and any non-LF line break (bare CR, NEL, LS, PS, VT, FF) or C0 control character in the
whole file. Allowed (surveyed on the real tree, twelve YAML files): plain block mappings and sequences, plain and quoted
scalar values, empty `{}` and `[]` as a whole plain value, non-empty flow sequences, block-scalar bodies including `- |`.
Quotes inside flow sequences count only at element start; delimiters of a refused flow collection are counted without quote
masking and the refusal region ends at the first outdent. The `${{ ... }}` scan is linear. The loaders read the bytes and
decode strict UTF-8 (a file that is not UTF-8 is a finding), so a bare CR is no longer erased before the refusal runs. The
documented limit for exotic YAML is in docs/ci.md. The lint was read by sol and argon over five rounds (every finding closed
or classified); the loader and the three promotion-guard tests that now also see the lint's quoted-key finding were fixed
from the lab legs.

Tests: 15 first-round and the later red-first tests (one per refused construct, each break character in a block-scalar body and
in a comment, the C0 controls, flow-quote variants, outdent resumption, loader tests on real files in a temp layout, a real-tree
zero-findings test, mutation controls for the refusal branch and the integration call). docs/ci.md describes the rule.

This commit is the squash of the lint rounds of the series; the pins commit before it rewrites the workflow files. Lanes:
ci-fu-pinlint, ci-fu-pins2/3/4/5 (Codex gpt-6.1-sol), ci-fu-pins6 and pins7 (Claude Sonnet 5.5 via claudock).

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

---------

Co-authored-by: ci-cx <ci-cx@invalid>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Brings current main (the CI batch: #504 to #508 and the earlier merges) into
the PR; first parent is main so the lab's doc-reverts base resolves to main's
last pull request commit. No conflicts. The PR's own change set is unchanged
(nine files vs main).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@luisleo526
luisleo526 merged commit dad2530 into main Oct 25, 2026
13 checks passed
luisleo526 added a commit that referenced this pull request Oct 26, 2026
## What

Adds the `## 1.8.0 — 2026-10-27` section (with migration notes) to `CHANGELOG.md`, above `## 1.7.0`. `CHANGELOG.md` is the only file changed (+131/-0). Documentation only: no `src/`, `include/`, `docker/`, `scripts/`, `tests/` or workflow file is touched.

## Why

The engine release body is generated from git-log subjects only and carries no migration notes; the release notes users read are this file (and the GitHub release body edited from it after publish). The notes must be on main before the release dispatch.

## Content

- Compatibility and migration: exact pairing with codegen 1.8.0, regenerate C++ and relink every strategy library, no public header change, the new codegen `color` input type, the changed harness file `docker/run_json.py`.
- Faster `run_json` report handling (#517) with the measured table against 1.6.3 and 1.7.0 and the statement that the gap to 1.6.3 is not closed.
- Scratch placement restated (#518); the unused storage building block (#515; its allocation-gate sites are unreviewed, no bound claimed); a known limit (memory with never-filled pending orders, "a fix is planned for a later release").
- CI and release process: every merged change since 1.7.0 not listed above (#505-#510, #513, #514, #516, #519, #520, #522, #469, #481, #504). Every line is taken from the merged commit body; none claims a behaviour change for a tests-only or CI-only change.

Not in this commit (not on main at base `641349a`): the release gate, tmpwatch and the TvJO fix. Each gets one line from its merged PR body as a follow-up commit on this branch before this PR merges; this PR is meant to merge after those three.

## Checks

- Route: docs-only, `lab verify --via ci-only` (no sweep).
- Read as a committed diff by an independent reviewer: GO. All 18 engine commits `v1.7.0..641349a` have a bullet, none extra.
- Numbers: the #517 timings and RSS are the measurements in the #517 body; the ratios in the text were recomputed (1.77/0.36 = 4.9, 8.26/1.35 = 6.1, 8.94/5.39 = 1.66; 4.61/0.36 = 12.8, 22.31/1.35 = 16.5, 22.99/5.39 = 4.27).
- The heading date `2026-10-27` is the planned tag date and is corrected before the tag if the date moves.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
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