Skip to content

fix(ci): enforce PR-owned manifest registration continuity - #1070

Merged
seathatflowsinourveins merged 3 commits into
mainfrom
codex/ns2604-pr-owned-registry-ci-20261013
Oct 13, 2026
Merged

seathatflowsinourveins merged 3 commits into
mainfrom
codex/ns2604-pr-owned-registry-ci-20261013

Conversation

@seathatflowsinourveins

@seathatflowsinourveins seathatflowsinourveins commented Oct 13, 2026 •

Copy link
Copy Markdown
Collaborator

Scope

Fail CI when a PR loses a required manifests/evidence.json row or its row disagrees with the committed file. The validator now compares committed Git trees against the registration policy in docs/lanes.md: changed paths registered at base, new evidence, and paths registered by the PR must keep matching rows while the files still exist.

The validation workflow supplies the event base and paginated prior force-push heads. The validator follows registry-bearing ancestors as well, so a second push cannot erase the earlier registration intent. Removed files may remove their rows; never-registered non-evidence source files retain their existing policy.

  • Base commit: 107bcca66133db379e9bf31560ba617519c59bec (fresh origin/main).
  • Lane: lane:foundation.
  • Owned paths: .github/workflows/validate.yml, scripts/validate.py, new tests/test_registry_rows.py, the workflow-hardening permission assertion, least-privilege record and dated monitoring delta/index; the last round adds one fast-forward correction commit and updates only its own registration rows.

SOTA sources

Evidence-class table

Claim Evidence class Command / receipt
Main's validator overlooks missing registered PR rows synthetic Same ten new Git-fixture tests against unmodified main validator: four fail-first failures, including missing changed row and lost new registration after rebase
The fixed validator refuses missing rows, stale hashes/bytes and unavailable history; repaired registry and main pass synthetic Last-round registry-green.log: 25 tests, including empty/trailing-null/empty-timeline controls, command-scoped fake private authentication and reachable current-manifest commit:path diagnostics
The actual #1045 incident is detected local_integration Check exact old head 6c8348d29939b41bd3a5d806a5fd4827e4fd2427 with prior head 70ff2753bea0858b153ab9239f79ddc0841d85dd: exit 1, precisely the three new agents and decision record missing
#1045's repaired head passes local_integration Same checker at 7906b883b68748c2771789b4121ca8e801834a78: exit 0
Current main passes its validator and committed registration check local_integration main-validator-before.log, main-registration-check.log at base 107bcca6
Native GitHub history inputs are recoverable native_proven native-timeline-fetch.log, unchanged workflow script against public #1045: all four prior heads fetched, exit 0
Workflow history acquisition fails closed on incomplete requests while warning/counting inaccessible empty node lines synthetic Fake gh emits the vendor's actual empty-line null form; trailing nulls remain counted, an empty timeline is zero nodes, partial GraphQL output/fetch failures refuse and valid refs retain command-scoped authentication
Existing validation/workflow checks pass local_integration 86 validation tests; 118 hardening tests (one existing skip); 29 policy tests; actionlint/offline zizmor; final publication validator

Local commands run

python3 -m unittest tests.test_registry_rows.RegistryRowsTests -v
against main validator: exit 1, ten cases / four expected fail-first failures

python3 -m unittest tests.test_registry_rows -v
exit 0, 25 cases / 5.754 seconds (last round)

python3 -m unittest tests.test_validate -v
exit 0, 86 tests / 14.223 seconds (idle-class, final round)

python3 -m unittest tests.test_workflow_hardening -v
exit 0, 118 tests / 25.474 seconds, one existing skip (idle-class, installed GNU tee, final round)

python3 -m unittest tests.test_workflow_policy -v
exit 0, 29 tests / 3.934 seconds (idle-class, final round)

python3 scripts/validate.py
exit 0, 71 components / 11880 hashed files / 4 profiles / 240 receipts

python3 scripts/evidence_manifest.py --check
exit 0

actionlint -shellcheck <checksum-pinned Ubuntu ShellCheck 0.11.0> .github/workflows/validate.yml
old workflow exit 1 / SC2016; corrected workflow exit 0

zizmor --offline --no-config --no-ignores .github/workflows/validate.yml
exit 0

git diff --check 107bcca6..HEAD
exit 0

gitleaks git . --config .gitleaks.toml --log-opts=107bcca66133db379e9bf31560ba617519c59bec..HEAD --redact=100 --no-banner
exit 0, complete three-commit PR range after the single last-round child commit

Task scratch and test outputs use an explicit /var/tmp TMPDIR. Complete execution captures and exact cited source blobs are retained in a hashed review packet for the co-op's two-family reads.

Decision record

This PR records correction #207's CI guard. Registration intent is inferred from native Git registry history and GitHub's persistent force-push history; no private reviewer packet path enters the repository. Unavailable Git objects and failed API requests refuse. Malformed historical manifests emit a warning naming the commit/path and are skipped; the current manifest stays strict. An accidental manifest self-row may be removed because the manifest is excluded from its own registration policy. Prior refs stay scoped to the validation step via GITHUB_OUTPUT.

The wider check covers registry-only edits, a second push after loss, force-pushed history, still-present versus deleted files, valid repairs through register_file, and a worktree restored to old bytes while the committed head contains changed bytes. No registry writer or blanket source-registration policy is introduced. SHA/byte comparison reads committed blobs. Workflow permissions are exactly contents: read and pull-requests: read. Last-round Git fetches receive the existing read-only token through command-scoped environment configuration; the fake private-fetch test uses actual Git's configuration reader and verifies no config-file change, no token argv and no residual header afterward. No actual private repository or credential is used in that fixture.

Authenticated-fetch trigger and scope: on a private-repository synchronize run, checkout's persist-credentials: false leaves subsequent Git history fetches without authentication; GH_TOKEN authenticates the timeline request through gh, but does not authenticate Git. The transient header addresses that private-repository boundary, reproduced with a fake-token fixture rather than an observed private hosted failure. It applies only to each prior-head Git fetch and uses the job's existing ephemeral, read-only token through command-scoped GIT_CONFIG_* configuration. The encoded header is masked; no repository configuration is written, no token is passed in Git argv, and no additional permission or secret is introduced. The public-repository hosted proof below establishes history recovery; it does not establish private-repository authentication.

Round 1 corrects the missing read scope and actionlint's SC2016 on the literal GraphQL query. Escaped dollar signs in a double-quoted query preserve GraphQL variables without suppression. Earlier local actionlint lacked ShellCheck; the package-pinned native ShellCheck control now reproduces the hosted error and passes the fixed script. Historical first-head logs remain separate from final results.

The monitoring delta/index append records the native-derived 349 reads versus the prior 348, one extra committed-blob read in the validator, with permission_change: false. Prior baselines/deltas remain immutable. The recorded source is the original public head; final-source native derivation confirms the same counters/shapes after line movement. Existing monitoring assertions remain unchanged.

Fail-before controls cover each blocker, including a rule-removed negative control that fails both direct hash/byte assertions while ordinary publication validation passes. The single CC-authorized leased replacement of the registration-only tail creates #1070's first actual force-push event, before dd660c28 and after fbcc2564. Native measurement corrected the CC's initial event count: the previous head had zero filtered force-push nodes. The script counts projected nodes, never unfiltered totalCount.

Hosted proof at exact fbcc2564fead3b4413ca958a05e73afbf45f6b44: run 38389505326, shard-0 job 115269503336, steps 9 (pinned actionlint), 10 (prior-head timeline/fetch), 11 (publication and committed registration checks) and 12 (host receipts) all SUCCESS at the 11:22:59Z native job read. Its one actual timeline node names the orphaned dd660c28a3ed2f26df45afd3fc945dbeeb114567; the fail-closed step successfully fetches that full object ID. The timeline API uses the job's scoped token; public Git transport retains persist-credentials: false. Full matrix/aggregate checks remain in progress. The in-progress job log endpoint is not yet available, so retained job metadata plus the exact timeline authenticate this step result.

Publication correction: an initial full upstream REST schema capture triggered the private packet's gitleaks scan on unrelated key-shaped examples. Publication stopped, and the full capture was removed from the deliverable. The packet now contains only the cited schema-field projection and passes the complete uncapped scan. No scanner suppression or allowlist was added; no finding value was displayed.

Correction #209: the earlier literal null recovery claim was wrong at the pinned gh version. Native gh emits an empty line, and shell command substitution would strip trailing empty node lines. Last-round controls fail before the fix and pass after preserving output with a success-only sentinel, counting every actual node line and warning on empty lines. The private-history authentication and pinned GraphQL/gh citations are included in the same final correction. InvalidPublication is caught before generic ValueError, preserving the current-manifest commit:path diagnostic; its direct regression fails before that exception-order fix. No pass/fail refusal is weakened.

The LAST round is one normal child commit, 0764bb6f72e0315dc8cc98b3712d74c581194fbf, after the reviewed fbcc2564 head. Final author-head OpenHands gate module: 127 tests / 108.229 seconds / exit 0. Full author-head validation includes both registration base 107bcca6 and before head fbcc2564, and passes 71 components / 11880 hashes / 4 profiles / 240 receipts. Only the three edited source/test/workflow registrations are refreshed; the frozen monitoring delta/index and all unrelated manifest fields stay unchanged. The final push is fast-forward, with no force or rebase. Current-head hosted checks and the three delta reads follow publication; the earlier hosted proof above remains explicitly at fbcc2564.

Follow-ups

  • Astra fix1 F1 — At the next read delivery, attach the complete original and round-1 source/capture packets and their manifests, historical Route each Claude agent definition to its table's model and effort #1045 inputs and the completed hosted log; retain local, hosted-token and fixture evidence distinctions.
  • Astra fix1 F2 — When maintaining the correction record, cite ShellCheck v0.11.0's SC2016 vendor reference, explain historical-manifest recovery against the pinned registration policy, and cite github/docs@be38ec5d schema.docs.graphql:19788 for nullable beforeCommit; preserve the native filtered-node measurement.
  • Astra fix1 F3 — When the convergence decision next changes, replace its stale active final-stack workflow line range with stable step names/current locations, preserving commit-pinned historical references.
  • Astra fix1 F4 — During an authorized historical-record cleanup, replace inherited hostname labels with public role labels while preserving evidence meaning and updating registrations.
  • Astra fix1 F5 — On the next local/manual continuity-entry-point or merge-parent normalization change, document the explicit-base requirement, cover merge-parent/rename/main-only transitions and external scratch placement, and keep archive validation distinct from history verification.
  • Astra fix1 F6 — On the next registration-test change, add a direct committed-tree new-evidence assertion that fails with the continuity rule removed; preserve the ordinary publication check.
  • Retain the completed full shard log when GitHub exposes it; the exact-head history, actionlint and registration step successes are already recorded through native job metadata, while the remaining matrix is pending.
  • Deliver every cited original and round-1 capture/source with both manifests in subsequent review packets; preserve local authenticated versus hosted-token evidence classes.
  • Update the neighboring convergence decision's active final-stack workflow locator to a stable step name when that record next changes; preserve historical commit-pinned references.
  • Replace inherited hostname labels only through an authorized historical cleanup, preserving evidence meaning and registrations.
  • Document explicit-base local/manual continuity checks and make the workflow fixture honor external scratch placement; bare archive validation does not establish Git-history continuity.
  • Assess merge-parent base normalization, rename destinations, independently advanced main and main-only registration rows in a separate scope; these are the CC's neighboring P3 boundaries.
  • CC N3 — On the next registration baseline/error-policy change, assess lenient parsing of a broken main and the historical CalledProcessError catch, preserving the distinction between invalid content and an unavailable manifest.
  • CC N4 — On the next registration-test maintenance, replace the vacuous nested mock/assertion control with a direct mutation-sensitive case; retain the separate direct hash/byte rule-removal evidence.
  • CC N5 — Preserve the frozen monitoring delta's source-pinned line locator; any future observation belongs in a new append, never a rewrite of the historical delta.

Host evidence

Not applicable; no evidence/hosts/ edits.

Checklist

  • Existing action pins remain unchanged.
  • Top-level permissions stay {}; the changed job grants exactly contents: read and pull-requests: read for its read-only inputs.
  • No credentials read, printed or committed; no new secret or paid service.
  • Peer-owned files and worktrees preserved.

@seathatflowsinourveins seathatflowsinourveins added the lane:foundation Foundation lane: Claude/Codex setup, hosts, memory, RAG, research, workers label Oct 13, 2026
@seathatflowsinourveins
seathatflowsinourveins force-pushed the codex/ns2604-pr-owned-registry-ci-20261013 branch from dd660c2 to fbcc256 Compare October 13, 2026 11:20
@seathatflowsinourveins
seathatflowsinourveins marked this pull request as ready for review October 13, 2026 15:22
@seathatflowsinourveins
seathatflowsinourveins merged commit df3a477 into main Oct 13, 2026
35 checks passed
@seathatflowsinourveins
seathatflowsinourveins deleted the codex/ns2604-pr-owned-registry-ci-20261013 branch October 13, 2026 15:24
seathatflowsinourveins added a commit that referenced this pull request Oct 13, 2026
…'s count, 353, and the freshness workflow provisions the bun its suite needs

Two rulings of the command center on the landing rebase onto 77cc7ef, in one commit.

- docs/decisions/2026-10-13-claude-agent-drift-check.md: the dotdot delta was re-derived over main after #1070 (344 of the baseline snapshot, scripts/adoption_status.py 4,
  tools/credentials/set_credential.py 4, scripts/validate.py 1), so it accounts for 353 reads; the sentence that describes it said 352, the count on the previous base. A record must match the delta
  it describes.
- .github/workflows/catalog-freshness.yml (the local review's P1 on the rebased head): the freshness job runs the whole suite (python3 -m unittest --durations 50) under GitHub Actions, where the
  tests that need the real bun fail rather than skip (tests/test_adoption_status.py unavailable()), and only validate.yml provisioned the pinned bun, so the scheduled and the manual run failed at
  RealBunTests and the vendor-definition tests and its propose job never started. The job now has the same step, with the same version and checksum, before its suite.
- tests/test_adoption_status.py: test_every_job_that_runs_the_suite_provisions_the_pinned_bun_before_it reads every workflow, finds each job whose unittest step reaches the module (a bare
  unittest, a named tests.test_adoption_status, the shard runner) and holds the step, the version and the checksum equal to validate.yml's; it fails at the freshness job before the change
  (receipts/fail-before-freshness-bun.txt). test_the_reader_tells_the_suite_runners_from_the_named_modules holds the reader's own cases.
- Evidence: receipts/freshness-bun-local-check.txt runs the step's own script and the module (215 tests OK with the exported variable, FAILED without it); 13 of 13 mutants killed
  (receipts/mutants-freshness-bun-13.txt); the record has a section for the review. The workflow was not dispatched: its propose job force-creates an evidence branch and opens a pull request.
- Nothing else changes.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
seathatflowsinourveins added a commit that referenced this pull request Oct 13, 2026
…epository's (CPA-V-10) (#1062)

* Detect drift between the installed Claude agent definitions and the repository's: adoption_status --client-wiring counts them and the wiring rule requires zero

CPA-V-10 of the Claude-family practice audit. The client serves the definitions in ~/.claude/agents to every project that does not define the name itself; tools/adoption/install_claude_profile.py copies the 11 of
adoption/agents/claude there verbatim, and nothing compared them afterwards (the audit found five of 11 host copies stale; scripts/adoption_status.py had no reference to the Claude agent paths). A stale copy is
old text; a wider one is a different kind of risk, since an omitted tools field means every tool (anthropics/claude-code@3deb87368737 plugins/plugin-dev/skills/agent-development/SKILL.md L152-L154).

- scripts/adoption_status.py: claude.stack_agents_drift, a count that is 0 when every *.md of adoption/agents/claude is installed byte for byte under <Claude home>/agents and nothing else is there but the four
  vendor-installed search definitions (codebase-memory, codebase-memory-auditor, codebase-memory-scout, semble-search: listed, not counted). A copy that is absent, a link, a folder or another kind of file counts
  (a link's target is not what the client was shown); a *.md not shipped here and not listed counts; backups, other extensions and anything below the top of the folder do not. null (the comparison cannot be made:
  the source folder has no definition or cannot be read, the agents path is a link or not a folder, an installed copy cannot be read) makes complete false like every other null. A non-zero count makes complete
  false: the fail-first of the audit item, a fixture with one stale user agent that reported complete: true.
- docs/token-efficiency-stack.md: the coverage check and its rule; docs/decisions/2026-10-13-claude-agent-drift-check.md: the decision, the evidence, what is not covered; evidence/artifacts/agent-drift-check-20261013:
  the receipts (the run on the first host: 0 and complete true; the fail-before; the mutants).

Tests: 12 new ClientWiringTests and the wired, unwired and wrong-type fixtures updated (153 tests in the module). 16 of them fail on the script at c63c83a (constants aside), among them the stale-agent
fixture. 20 of 20 mutants killed. The practice page note (reference/workers.md L13, "four host copies differ") is stale; it is corrected with the next re-pin of the practice skill, which pins the page.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>

* Review fix of the Claude agent drift check: the agents folder is read through descriptors and at any depth, a vendor file that declares a shipped name is drift, and the catalog citation is repaired

Astra's first read of e70d2de (CHANGES_REQUESTED: one P1, one P2, three follow-ups) and the command center's Claude read of the same head (one P1, one P2, four P3), folded into one commit.

- P1 (Astra): the check inspected a path with lstat and opened it again, so a shipped file, or the whole agents folder, replaced by a link in between was read through (outside bytes equal to the source: drift 0 and
  complete true). The Claude home is opened, then agents with O_DIRECTORY|O_NOFOLLOW, then every entry with O_NOFOLLOW relative to the descriptor of its folder, and what is described is fstat of the descriptor that was
  opened. There is no fallback to a path check: a platform without O_NOFOLLOW, O_DIRECTORY or descriptor-relative open, stat and listdir reads nothing (null). The shared reader read_regular_bytes and its Codex caller
  stack_roles_matching read the same way, as the read asked. python/cpython@cbc944f4bc59 Modules/_io/fileio.c L404, Doc/library/os.rst L1272, L1364-L1365, L3507, L3551.
- P1 (Claude read): the client loads the folder recursively (https://code.claude.com/docs/en/sub-agents.md L176-L182; the installed 2.1.296 loader enters every folder, follows links and lists hidden files), so a
  nested definition read as no drift. Every *.md at any depth is counted, a link, pipe or socket named *.md included; a link that leads to a folder, an entry that vanishes while it is read, a tree deeper than 8 folders or
  with more than 5,000 entries are null.
- P3 (Claude read): a vendor-named file whose front matter declares the name of a shipped definition is counted (one name, one definition wins); a vendor name below the top, or as a link, is not exempt.
- P2 (Astra): the inserted lines moved the source cited for codex:host:RTK.md in the catalog, and two citation assertions failed; the citation is scripts/adoption_status.py:532-553 (valid whether lines are counted by
  splitlines or by LF) and its registry binding is refreshed.
- P2 and P3 (Claude read, Astra): the record cites its sources (documentation page, loader, CHANGELOG L8578 for CLAUDE_CONFIG_DIR and L7088 narrowed), its two wrong claims about links and nested files are corrected, it has a
  registry row, and its host command is as the receipt records it.

Tests (170): 16 tests fail on the sources of e70d2de (FAILED failures=24, errors=3, counting subtests), among them the four replacement tests, which try a replacement after every file-system call of the read (the old
check answered 0); 49 of 49 mutants killed. The installer's link handling, the non-default Claude home and the 18 hostname lines are follow-ups in the pull request body.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>

* Round 2 of the Claude agent drift check: a vendor file's name is read as the client reads it or the count is unknown, the descriptor opens are accounted in the push gate's advisory inventory, and the record's counts are held to the receipts

Astra's second read of e22358e (CHANGES_REQUESTED: two P2, five P3) and the command center's Claude read of the same head (one blocker, three P3), folded into one commit.

- P2 (Astra): the descriptor opens added three reads to the OpenHands push gate's advisory inventory (9 to 12 for scripts/adoption_status.py), and tests.test_runtime_worker_openhands_push_gate failed two tests. A new
  dated monitoring delta (unclassified-gate-reads-20261013-pr1062-delta.json, derived with the native GitTree and derive_ci_protected, index entry appended, earlier snapshots and deltas unchanged, both registered) accounts
  for them: 347 reads against the 344 of the baseline it names; the three are the opens in open_below, stack_roles_matching and stack_agents_drift.
- P2 (Astra): the vendor-name rule read the plain `name:` line only, so a vendor-named file that declared a shipped name through a quoted key, a block scalar, an escape sequence or a byte order mark gave 0 and complete
  wiring. agent_identity applies the client's block extraction and reads the plain spellings; every other spelling is unknown (the count null, complete false), and so is a shipped definition whose name it cannot read. Measured
  against the installed 2.1.296 client's own parser (its functions copied unchanged by byte interval and run on Bun 1.4.2; the client embeds 1.4.3): 0 misses over 8,582 generated texts and 26 real definitions, and over
  five further seeds of 34,556 texts. The first review's claim for the vendor rule is narrowed to what is measured.
- Blocker (command center): the record quoted 46 of 46 killed for a receipt of 49. Each line that names a mutation receipt now carries that receipt's own count, and tests/test_agent_drift_record.py holds it.
- P3 (command center): a pipe, a socket, a device and a link to nothing are not counted (the client's walker keeps regular files only); a *.md with no name stays counted, now said in the record and the docstring; a link is
  told by the ELOOP of the open and not by a later look; `name:` with its value on the next line is read.
- P3 (Astra): the locator in tools/adoption/host-baseline.md follows launcher_resolution.

Tests: 178 in the module; 9 of them fail on the sources of e22358e (failures=42, errors=62, counting subtests), the record test fails on its record, and the push-gate module's 127 tests pass here and fail two at e22358e;
92 of 92 mutants killed. The installer's link handling and non-default Claude home, the hostname lines of the touched files and the upstream captures are follow-ups in the pull request body.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>

* Design round of the Claude agent drift check: a vendor file's name is asked of the client's own YAML parser or the count is unknown

The command center's Claude read of e7260fd (one P1, reproduced) and Astra's second delta (CHANGES_REQUESTED: one P2, the same case, and four P3) found that the hand-written name reader, rewritten in each of two
rounds, still returned none for a name followed by a comment line at the left margin, so a vendor-named file declaring a shipped name gave drift 0 and complete wiring. The adjudication ruled a design round: parse
the front matter with the client's own implementation, failing closed.

- agent_front_matter extracts the block as the client's loader does (a byte order mark dropped, /^---\s*\n([\s\S]*?)---\s*\n?/); agent_names hands every block of the check to Bun.YAML.parse through bun at
  CLIENT_BUN_VERSION (1.4.3, the version the installed 2.1.296 client embeds) in one call, in an empty scratch directory with an empty configuration, that directory as home and a three-variable environment, the
  blocks as JSON on stdin. A name, none or a throw; a throw, no bun, another version, a bun that fails or does not answer in 30 s is unknown (the count null, complete false), never 0. agent_identity and its regexes are removed.
- Measured against the installed client itself (each text written as a project agent, the client started in headless mode and killed after its init record): 16,428 generated texts, 0 misses with bun 1.4.3 and with
  1.4.2; the reader of e7260fd missed 501, all of them comment lines between a key and its value and nine CRLF texts. The 2,987-text subset and the client's answers are committed
  (tests/fixtures/agent_frontmatter/client-oracle.json) and replayed by the tests.
- The tests that need the real bun skip on a developer's machine and fail under GitHub Actions (the pattern of tests/test_token_full_save_evidence.py); validate.yml installs bun 1.4.3 from the release at its checksum.
- P3: a test holds every test name that the record and the README cite; the README states the real definitions once; the oracle receipt names the client's whole parse path by function and byte interval and the record
  states Iwt's other refusals; the superseded oracle receipt is marked; the advisory delta of the design round states the edited read line; the hostname count is 57 occurrences on 55 lines.

Tests: 203 in tests.test_adoption_status; on the sources of e7260fd the same file gives failures=1, errors=58 in the 196 tests that run there (counting subtests; 24 tests and the setup of the real-bun class raise
AttributeError for the API the parser call adds; the 7 tests of that class cannot start); 38 of 38 mutants killed. Host step, not applied here: bun 1.4.3 (mise install [email protected], or AGENT_FRONTMATTER_BUN) on the first host, where mise has 1.4.2 and the count is null until then. The installer's link handling and non-default
Claude home, the upstream captures and the hostname lines of the touched files are follow-ups in the pull request body.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>

* Record of the Claude agent drift check: the departure from the ruling's wording on a non-string name, and that mise install keeps the active version

The command center's Claude delta of fb968af (PASS_WITH_P3) accepted two points on condition that the record say them: a successful parse that is not a mapping, or whose name is not a string, is none and not unknown,
which departs on purpose from the adjudication's wording (item 2 asked for unknown) because the client's own loader serves a definition only under a string name (measured: the client served nothing in all 1,829 oracle texts
the pipeline answered none for, and no text where it said none and the client served a name); and `mise install [email protected]` does not change the active version, so the check needs AGENT_FRONTMATTER_BUN (the shim in a scratch
home resolves 1.4.2, measured by the command center). Record text only: no code, test or receipt changes.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>

* Claude agent drift check: the front matter extraction keeps the client's delimiter precheck, so an unterminated block of whitespace is read in linear time

The local codex review of the rebased head 2723f8f said "patch is incorrect" (one P2) and the command center ruled one bounded, scope-locked round. A vendor-named file of "---" followed by many whitespace bytes and no
closing line made the front matter pattern backtrack quadratically over the whitespace (64 KiB of newlines took 18.9 s, 32 Ki pairs of a space and a newline 10.7 s, a file may be 1 MiB), and the extraction runs before
bun, so no timeout of bun bounds it. The design round had removed the client's check that "---" occurs after the first three characters as one that changes no result; it changes no result and it bounds the work.

- scripts/adoption_status.py agent_front_matter: return none when "---" does not occur after the first three characters, the check of the client's own oi (Claude Code 2.1.296, bytes 211908840-211910460:
  e.indexOf("---",3)<0?null:e.match(RE)). With the check the pattern is tried only on a text that has a closing line, where it succeeds at the first candidate; 1 MiB of newlines now takes 2 ms.
- tests: two extraction tests (five fillers of 64 KiB under a second; a closing line far below a long run still found) and one client_wiring test (the whole call under ten seconds, no bun asked). On the sources of
  2723f8f the extraction tests fail at 19 s, 6 s and 9 s and the wiring test at 19 s (receipts/fail-before-precheck.txt); 4 of 4 mutants of the check are killed (receipts/mutants-precheck.txt).
- The record has a section for this review, the evidence row and the receipts README rows. Nothing else changes.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>

* Claude agent drift check: the bun that is selected is an absolute path before either launch, so a relative AGENT_FRONTMATTER_BUN or a relative PATH entry names the same file in the scratch directory

The local codex re-review of b3da64d said "patch is incorrect" (one P2) and the command center adjudicated it as fixed in this landing, as one bounded commit. find_bun kept a relative AGENT_FRONTMATTER_BUN, or a bun
found through a relative PATH entry (./bin), relative, and both launches of agent_names run with cwd=scratch, where subprocess looks for a relative executable relative to cwd (python/cpython@cbc944f4bc59,
Doc/library/subprocess.rst L572-L576, Lib/subprocess.py L1912-L1919). The bun that parses from its absolute path gave Unknown from a relative path to the same file (reproduced with bun 1.4.3), and the check then
said null and complete false.

- scripts/adoption_status.py find_bun: the selection is made absolute before either launch. A value of the variable with a directory is made absolute against the caller's directory; a value with none is a name,
  looked up on PATH as a shell does, never as a file of the caller's directory; with no variable the first bun on PATH is found and made absolute; a caller whose own directory is gone cannot make a relative path
  absolute, which is None and so Unknown. Which bun is selected does not change (the mise shim remains the adjudicated follow-up).
- tests: three stand-in tests (six framings of a relative selection run from the caller's directory; a name is a PATH lookup and never a file of the caller's directory; a vanished directory is Unknown) and one with
  the real bun, run from the bun's own directory. All four fail on the sources of b3da64d (receipts/fail-before-absolute-bun.txt); 9 of 9 mutants of the change are killed (receipts/mutants-absolute-bun.txt).
- The record has a section for this review, the evidence row and the receipts README rows. Nothing else changes.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>

* Claude agent drift check: a relative bun is made absolute without normalizing it, so a ".." after a link keeps the meaning the file system gives it

The second local codex re-review of 9720257 said "patch is incorrect" (one P2) and the command center adjudicated it as fixed in this landing, as one bounded commit. find_bun made a relative selection absolute
with os.path.abspath, which removes each ".." lexically, before the file system resolves the link in front of it: with current a link to actual/subdir, current/../bun is the file actual/bun and became the bun beside
current, so healthy wiring gave Unknown (reproduced with bun 1.4.3).

- scripts/adoption_status.py find_bun: the caller's directory is put in front of a relative path and nothing is normalized, so the components stay as written and the kernel resolves them at the launch as it resolves
  the relative path from the caller's directory (python/cpython@cbc944f4bc59, Doc/library/subprocess.rst L572-L576). An absolute value is passed as it is. A link is not resolved either: os.path.realpath would replace the
  mise shim, a link to mise, by mise, and an executable can be known by its name.
- tests: a stand-in test (a relative value, a relative PATH entry and an absolute value through the link; a decoy of another version beside the link) and one with the real bun fail on the sources of 9720257 and pass
  here (receipts/fail-before-dotdot.txt); a test that a link to the bun keeps its name holds at both heads; 5 of 5 mutants are killed (receipts/mutants-dotdot.txt). The stand-in bun found its behavior file by
  os.path.abspath(sys.argv[0]), the same mistake; it now uses the real path of the file that ran.
- The record has a section for this review, the evidence row and the receipts README rows. Nothing else changes.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>

* Claude agent drift check: the working directory read of a relative bun is accounted in the push gate's advisory inventory

The second re-review commit (31d377e) put the caller's directory in front of a relative bun path with os.getcwd(), which the OpenHands push gate's derivation counts as one unclassified read of
scripts/adoption_status.py (os.path.abspath made the same read inside the library and was not counted). tests.test_runtime_worker_openhands_push_gate derives its reads from the committed tree; it was run on
the working tree before that commit, so the continuous integration found the read first (validate-shard-2: test_advisory_baseline_and_explicit_categories_stay_bounded_on_this_repository and
test_baseline_refresh_regression_accepts_a_committed_outer_observation).

- blueprints/runtime-workers/openhands/evidence: the delta unclassified-gate-reads-20261013-pr1062-dotdot-delta.json (352 reads; the one new read, classified as a user status observation with the scope
  read-only client-wiring status; no worker execution authority) and its index entry, derived with the native GitTree and derive_ci_protected at the committed head.
- the record and the receipts README name the read and the delta. Nothing else changes.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>

* Claude agent drift check: the decision record gives the rebased delta's count, 353, and the freshness workflow provisions the bun its suite needs

Two rulings of the command center on the landing rebase onto 77cc7ef, in one commit.

- docs/decisions/2026-10-13-claude-agent-drift-check.md: the dotdot delta was re-derived over main after #1070 (344 of the baseline snapshot, scripts/adoption_status.py 4,
  tools/credentials/set_credential.py 4, scripts/validate.py 1), so it accounts for 353 reads; the sentence that describes it said 352, the count on the previous base. A record must match the delta
  it describes.
- .github/workflows/catalog-freshness.yml (the local review's P1 on the rebased head): the freshness job runs the whole suite (python3 -m unittest --durations 50) under GitHub Actions, where the
  tests that need the real bun fail rather than skip (tests/test_adoption_status.py unavailable()), and only validate.yml provisioned the pinned bun, so the scheduled and the manual run failed at
  RealBunTests and the vendor-definition tests and its propose job never started. The job now has the same step, with the same version and checksum, before its suite.
- tests/test_adoption_status.py: test_every_job_that_runs_the_suite_provisions_the_pinned_bun_before_it reads every workflow, finds each job whose unittest step reaches the module (a bare
  unittest, a named tests.test_adoption_status, the shard runner) and holds the step, the version and the checksum equal to validate.yml's; it fails at the freshness job before the change
  (receipts/fail-before-freshness-bun.txt). test_the_reader_tells_the_suite_runners_from_the_named_modules holds the reader's own cases.
- Evidence: receipts/freshness-bun-local-check.txt runs the step's own script and the module (215 tests OK with the exported variable, FAILED without it); 13 of 13 mutants killed
  (receipts/mutants-freshness-bun-13.txt); the record has a section for the review. The workflow was not dispatched: its propose job force-creates an evidence branch and opens a pull request.
- Nothing else changes.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>

---------

Co-authored-by: Claude Sonnet 5.5 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lane:foundation Foundation lane: Claude/Codex setup, hosts, memory, RAG, research, workers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant