Skip to content

Detect drift between the installed Claude agent definitions and the repository's (CPA-V-10) - #1062

Merged
seathatflowsinourveins merged 10 commits into
mainfrom
claude/cc-native-practice-agent-drift-check-20261013
Oct 13, 2026
Merged

seathatflowsinourveins merged 10 commits into
mainfrom
claude/cc-native-practice-agent-drift-check-20261013

Conversation

@seathatflowsinourveins

@seathatflowsinourveins seathatflowsinourveins commented Oct 13, 2026 •

Copy link
Copy Markdown
Collaborator

Scope

  • What this PR changes, in one or two sentences: CPA-V-10 of the Claude-family practice audit (claude-practice-audit-converged-20261012, group CPA-V-10): scripts/adoption_status.py --client-wiring reports claude.stack_agents_drift, the number of Claude agent definitions under the user home's agents/, at any depth, that differ from this checkout's adoption/agents/claude (byte for byte; four vendor-installed search definitions are listed, not counted unless their front matter declares the name of a shipped definition, which Bun.YAML.parse reads, run through a bun of the version the client embeds, and is unknown where that cannot be done), and the wiring rule requires 0. The folders are read through descriptors, never through a link. Before it, a fixture with one stale user agent reported complete: true.
  • Base commit: 77cc7ef42a55ca6b7c18ab24653000b687eb2a0d (main after Fingerprint the reviewed narrative commit ids the merge-commit history scan now reaches #1072, fix(ci): enforce PR-owned manifest registration continuity #1070 and Let the CC's adjudication decide a P2-only "patch is incorrect" local review; port SQUASH_BODY_FILE #1075; rebased for landing in three registry-and-index-only steps from c63c83a82, main after Rule text: converged candidates by multi-angle convergence, models only through a converged harness #1049, through 3f1a921dd, main after Credential inventory: github-review-app key file and a --from-file custody mode #1057 and Route each Claude agent definition to its table's model and effort #1045, and 993cc890b3ff3fc95fa7cdba51edd726565f9ace, main after Keep lane-A journal continuity for session-only config changes #1068)
  • Lane: lane:foundation
  • Owned paths touched: scripts/adoption_status.py, tests/test_adoption_status.py, tests/test_agent_drift_record.py (new, round 2), docs/token-efficiency-stack.md, docs/decisions/2026-10-13-claude-agent-drift-check.md (new), evidence/artifacts/agent-drift-check-20261013/ (new), catalogs/foundation/upstream-surface-dispositions.json (one citation, first review fix), blueprints/runtime-workers/openhands/evidence/ (the advisory index and one dated delta, round 2), tools/adoption/host-baseline.md (one locator, round 2), .github/workflows/validate.yml (the bun step, design round), tests/fixtures/agent_frontmatter/client-oracle.json (new, design round), manifests/evidence.json.
  • Commits: ten, all rebased for landing onto main 77cc7ef42 (the tenth, eecd506f2, is the commit of the review of the rebase below) (the heads named in the reads and rounds below are those on 993cc890b, the base of the second rebase, unless a text says otherwise): the change (d629d10ba), the first review fix (137e2b198), round 2 (8d8c66180), the design round (4222bdd82), the record update (e3bdca462), the delimiter precheck after the local codex review (b3da64d76, below), the absolute bun path after the local codex re-review (9720257e7, below, a fast-forward of b3da64d76), the path made absolute without normalizing it after the second re-review (31d377e3c, below, a fast-forward of 9720257e7) and the advisory delta for the read that commit adds (80f552245, below, found red by the continuous integration). On 3f1a921dd (the first rebase) they were e72d8415a, 69ee3efab, ebc3520ce, 007ded7db, 2723f8fd0 and f8008637; before it e70d2de0a, e22358e46, e7260fd3c, fb968aff6 and 3733443f0; the reads name those heads. Before the rebase they were e70d2de0a, e22358e46, e7260fd3c, fb968aff6 and 3733443f0; the reads below name those heads. On 77cc7ef42 (the rebase below) the first nine are 11a1f318b, 5b180be51, 3a4068c41, 9a3a6ea85, 30e3072e3, 98f9459cd, 08e044771, e8047fb9e and 4e3780b10, and the tenth is eecd506f2.

Why

The client serves the definitions in ~/.claude/agents/ to every project that does not define the name itself, and tools/adoption/install_claude_profile.py copies the 11 of adoption/agents/claude there verbatim. Nothing compared them afterwards: the audit found five of 11 host copies stale at 06:27Z on 2026-10-12 (fixed since: 11 of 11 equal, checked again here), scripts/adoption_status.py had no reference to the Claude agent paths, and the same drift class produced the incident settled in CPA-V-37. A stale copy is old text; a wider one is a different risk, since an omitted tools field means every tool.

The change

Part Change
claude.stack_agents_drift a count, 0 when every *.md of adoption/agents/claude is installed byte for byte at the top of <Claude home>/agents (CLAUDE_CONFIG_DIR when set) and nothing else of that kind is in the folder at any depth but the four listed vendor definitions. Counted: a shipped copy that is absent, a link, a folder or another kind of file, or not equal (a stale copy and a wider one are both bytes that differ); every other *.md at any depth, hidden files and a link to a file named *.md included, whether or not it declares a name (apart from the four vendor names, the check reads no front matter of a file it does not ship, so a nameless one, which the client skips, counts: remove or rename it), because the client loads the folder recursively. Not counted: a pipe, a socket, a device and a link to nothing (the client's walker keeps regular files only), backups, other extensions
VENDOR_HOST_AGENTS codebase-memory, codebase-memory-auditor, codebase-memory-scout, semble-search: installed by the tools they route to, named as the search roles in tools/local-pages/architecture_mapping.json; a listed exception at the top of the folder, as the audit item says, unless the file's front matter declares the name of a shipped definition (the client keys a definition by its name; of two files that declare one, one is chosen by read order). Below the top, or as a link, a vendor name is not exempt
agent_front_matter, agent_names the name a definition declares, asked of the client's own YAML parser: the block is extracted as the client does (a byte order mark dropped, what /^---\s*\n([\s\S]*?)---\s*\n?/ captures), then every block of the check (the 11 shipped definitions and the vendor-named files, at most 15) goes in one call to Bun.YAML.parse through bun at CLIENT_BUN_VERSION (1.4.3, the version the installed client embeds), in an empty scratch directory with an empty configuration, that directory as home and an environment of PATH, HOME and LANG only; the blocks are JSON on stdin. An answer is a name, none or a throw; a throw is unknown (the client's second attempt is not reproduced). No bun, another version, a bun that fails or does not answer in 30 s: unknown, never 0. A vendor file with no front matter needs no bun. The hand-written reader of rounds 1 and 2 is removed
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, a link below leads to a folder (what the client loads through it is not known here), a folder or an installed copy cannot be read or is over the limit, an entry vanishes between the listing and its opening, the tree is deeper than 8 folders or holds more than 5,000 entries, the folder holds a vendor-named file with front matter and the client's parser cannot be asked or does not settle a definition (no bun, a bun that is not 1.4.3, a parse that throws, a bun that does not answer; a vendor file over the limit), or the platform cannot read without following a link. Like every null leaf it makes complete false
Reads 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: nothing is inspected by path and then opened by path. The shared reader read_regular_bytes and its Codex caller stack_roles_matching read the same way
Wiring rule stack_agents_drift must be 0 (unlike stack_roles_matching, which is information): the audit's fail-first is a stale agent that must fail the status. docs/token-efficiency-stack.md carries the rule
Output a count, never a name, a path or text (the tests give every definition a text holding a private marker)

SOTA sources

  • anthropics/claude-code@3deb8736 plugins/plugin-dev/skills/agent-development/SKILL.md L152-L154: the tools field of an agent definition, "If omitted, agent has access to all tools" (why a wider copy is a risk and not only old text); CHANGELOG.md L4147 (release 2.1.198: subagents are managed by editing .claude/agents/ directly), L7088 (release 2.1.43: "Fixed spurious warnings for non-agent markdown files in .claude/agents/ directory", which shows that the client reads markdown files in that folder and skips those that are not agents; it does not say how it walks the folder; the first description of this change read it as a warning about non-agent files) and L8578 (release 1.0.6: "Respect CLAUDE_CONFIG_DIR everywhere"). The command center's read cited L8578 for the merge order: the line is the CLAUDE_CONFIG_DIR entry, and the order is on the documentation page and in the loader (next bullet).
  • Create custom subagents (fetched 2026-10-13): L158-L168 (the scopes and their priority: managed settings, the --agents flag, the project, the user ~/.claude/agents/, plugins) and L176-L182 ("scans .claude/agents/ and ~/.claude/agents/ recursively"; identity comes only from the name front matter field; of two files under one tree that declare a name, "Claude Code loads only one of them, chosen by filesystem read order rather than a documented precedence"). The installed client 2.1.296 was read for the loader: the walker (OVn) enters every folder, follows symbolic links, skips a folder it already visited and keeps files whose names end in .md; the alternative path (gU) is rg --files --hidden --follow --no-ignore --glob *.md, so hidden files are listed; a file without a front matter name is skipped without a message (pHo); the active set (d2) is built built-in, plugin, user, project, flag, managed, a later definition of a name replacing an earlier one.
  • python/cpython@cbc944f4bc59 (3.13): Modules/_io/fileio.c L404 (the ordinary open passes the path to open(2) as given, so a path checked first and opened after is read through a replacement), Doc/library/os.rst L1272 (os.open, dir_fd), L1364-L1365 (O_DIRECTORY, O_NOFOLLOW), L3507 and L3551 (os.supports_dir_fd, os.supports_fd), Lib/shutil.py L262 (copyfile opens the destination for writing, through a link: the installer follow-up).
  • The installed Claude Code 2.1.296 client, the primary source for what a definition's identity is: its front matter parser and loader by function and byte interval (WS, BL, oi/W9t/C/I, Vk, Iwt, pHo, d2; receipts/client-oracle.txt) and its embedded engine, Bun v1.4.3 (eecfd55de), which is not a public commit; the client itself, started on each text of a corpus as a project agent, is the oracle (the loader, the parser and the engine are its own).
  • oven-sh/bun bun-v1.4.3 (published 2026-10-10, commit c6da4a4d3): bun-linux-x64.zip, sha256 3e730528d89775f03f87f05c06787c4df4099d0a37f3f252e7b829cb23816e18 as the release's SHASUMS256.txt lists it, the pin of .github/workflows/validate.yml; Bun.YAML.parse is the engine call the client's BL makes. Bun 1.4.2 (the first host's mise): answered alike on the corpus.
  • blueprints/runtime-workers/openhands/evidence/advisory-gate-deltas.md and resolver/push_gate.py derive_ci_protected at c63c83a82 (append a dated delta; the native GitTree derivation; earlier snapshots unchanged).
  • This repository: tools/adoption/install_claude_profile.py install_agents (the verbatim copy to ~/.claude/agents), scripts/adoption_status.py stack_roles_matching (the Codex precedent: link, folder and absent cases, null for what cannot be read), tools/local-pages/architecture_mapping.json (the four search roles), docs/decisions/2026-10-13-claude-agent-drift-check.md.

Evidence-class table

Claim Evidence class Command / receipt
A fixture with one stale or wider user agent reported complete: true; now stack_agents_drift is 1, complete is false and every other flag is as it was local_integration ClientWiringTests.test_a_stale_or_wider_user_agent_makes_the_wiring_incomplete; 16 of the 153 tests of the module fail on the script at c63c83a82 (two constants added so that they fail on behavior), receipts/fail-before.txt
The count covers changed, same-size, missing, unlisted, link and folder cases; backups and other extensions are not counted; unreadable sources and folders are null; the home follows CLAUDE_CONFIG_DIR local_integration the 12 new ClientWiringTests of the first commit; 20 of 20 mutants killed (receipts/mutants-20.txt)
A definition below the top, a vendor-named file that declares a shipped name and a link to a file named *.md are counted; a pipe, a socket, a device and a link to nothing are not; a link to a folder, a vanished entry, a tree past its bound and an oversize copy are null local_integration the review tests (below); receipts/mutants-review.txt (49 of 49 killed, the first review fix) and receipts/mutants-round2.txt (92 of 92 killed, the whole list on the final tests)
A vendor-named file that declares a shipped name is counted in every spelling the client's parser reads and unknown when that parser throws, never 0 with complete wiring; wherever the installed client served a name the check reads that name or says unknown local_integration; measured against the installed client the vendor tests of the design round; receipts/client-oracle.txt (16,428 texts: 0 misses; the hand-written reader of e7260fd3: 501); receipts/fail-before-design.txt
The workflow installs the bun that the tests need, at the release's version and checksum local_integration .github/workflows/validate.yml; test_the_validation_workflow_installs_the_bun_the_check_requires
The design round's code and tests are caught by their mutants local_integration receipts/mutants-design.txt (38 of 38 killed)
The OpenHands push gate's advisory read inventory accounts for the descriptor opens (347 reads against the 344 of the baseline it names; three added, 24 line moves) local_integration unclassified-gate-reads-20261013-pr1062-delta.json; tests.test_runtime_worker_openhands_push_gate (127 tests: two failures at e22358e46, none now)
The mutation counts that the record and the README quote are the receipts' local_integration tests/test_agent_drift_record.py (fails on the record of e22358e46: it quoted 46 of 46 for a receipt of 49)
A replacement of a shipped file, of the agents folder or of a folder below it, made after every file-system call of the read, is never read through: the answer is the stale copy that was opened or null, never 0; the Codex role count the same local_integration test_a_replacement_of_... and test_the_codex_role_count_is_never_read_through_a_replacement_either (each tries the replacement at every point between two calls); receipts/fail-before-review.txt
The client loads the folder recursively, follows links and lists hidden files; of two files that declare one name, one is chosen by read order source_review, native_proven the documentation page and the installed 2.1.296 loader, as cited above
On the first host the 11 shipped definitions equal the installed ones, the four vendor ones are listed, and the check reports 0 with complete true (before and after each review round) native_proven python3 scripts/adoption_status.py --profile token-efficiency --client-wiring --json (receipts/host-run.json); cmp of the 11 files

Local commands run

$ python3 -m unittest tests.test_adoption_status
  at the first head: Ran 153 tests, OK; on c63c83a82's script with the two constants added: FAILED (failures=15, errors=1); at this head: Ran 170 tests, OK (17.4 s)
$ the same file (170 tests) against the sources of e70d2de0a, the head that was read  ->  FAILED (failures=24, errors=3), in 16 tests; the four replacement tests answer 0 after a replacement (receipts/fail-before-review.txt)
$ python3 -E -s -B <mutation script over scripts/adoption_status.py>  ->  20 of 20 mutants killed; the review fix: 49 of 49 (the first run left 3 survivors: an unreadable vendor file, a text without an opening block, a folder that cannot be listed once opened; each now has a test)
$ python3 -m unittest <the modules that name a changed file, one process each>  ->  first head: 64 modules, 4,157 tests, 63 OK (skipped=42); the other, tests.test_token_e2e_grader, fails the same on main (a test clone hard-links across devices); review fix: 62 modules, 4,020 tests, 61 exit 0 (54 end with the unittest status OK, skipped=27; the others print more after it, so their skips are not counted); the one other, tests.test_token_e2e_grader, fails the same on main (a test clone hard-links across devices); tests.test_freeze_snapshot, which runs the real script and which the command center asked about, 152 tests OK; tests.test_adoption_status 170 OK; tests.test_upstream_surface_watch, which the file-name selector does not pick up, run on its own: 116 tests OK (skipped=6)
$ python3 scripts/validate.py  ->  exit 0 (11,867 files); python3 scripts/evidence_manifest.py --check  ->  exit 0; git merge-tree --write-tree origin/main HEAD  ->  clean against main c63c83a82
$ python3 -m unittest tests.test_upstream_surface_watch  ->  Ran 116 tests, OK (skipped=6) (two failures at e70d2de0a: the citation of codex:host:RTK.md)
$ python3 scripts/adoption_status.py --profile token-efficiency --client-wiring --json  ->  claude.stack_agents_drift 0, complete true
round 2 (the lines below are at e7260fd3):
$ python3 -m unittest tests.test_adoption_status  ->  Ran 178 tests, OK; the same file against the sources of e22358e46  ->  FAILED (failures=42, errors=62, counting subtests), in 9 tests: 7 fail on behavior (10 rows of the vendor-collision test and all 27 spellings of the unknown test, among them the reviewer's four, which gave 0 and complete wiring; the oversize vendor file; a shipped name that cannot be read; the limitation text; the kinds the walker skips; the link told by the open), 2 raise because the name reader does not exist there (receipts/fail-before-round2.txt)
$ python3 -E -s -B <mutation script over scripts/adoption_status.py>  ->  92 of 92 mutants killed (the first run left 2 survivors: a key line with no space after the colon, a comment with no space after a closing quote; each now has a test row)
$ <differential measurement: the client's parser functions copied from the binary, run by Bun 1.4.2 on generated texts>  ->  8,582 texts and 26 real definitions: 0 misses; five more seeds: 0 misses (receipts/identity-oracle.txt)
$ python3 -m unittest tests.test_runtime_worker_openhands_push_gate  ->  Ran 127 tests, OK (62.9 s); two failures at e22358e46
$ python3 -m unittest tests.test_agent_drift_record  ->  Ran 2 tests, OK; on the record of e22358e46 it fails ('49 of 49 killed' not found in the line that says 46 of 46)
$ python3 -m unittest <the modules that name a changed file, one process each>  ->  67 modules, 4,340 tests, 66 exit 0; the one other, tests.test_token_e2e_grader (test_keys_at_the_pinned_revision), fails the same on main (a test clone hard-links across devices)
$ python3 scripts/validate.py  ->  exit 0 (11,872 files hashed); git merge-tree --write-tree origin/main HEAD  ->  clean against main dbe7a8da1 (#1060); the privacy scan of the lines added since e22358e46: 0 account, host, home-path, e-mail, non-ASCII or token hits
$ python3 scripts/adoption_status.py --profile token-efficiency --client-wiring --json  ->  claude.stack_agents_drift 0, complete true (the command's own status line is prerequisites_missing, exit 2, here and at e22358e46)
design round (at fb968aff):
$ AGENT_FRONTMATTER_BUN=<bun 1.4.3> python3 -m unittest tests.test_adoption_status  ->  Ran 203 tests, OK; without that bun: the tests that need it skip (GITHUB_ACTIONS unset) and fail (GITHUB_ACTIONS=true)
$ the same file against the sources of e7260fd3  ->  FAILED (failures=1, errors=58) of 196 tests (receipts/fail-before-design.txt); the reviews' counterexamples through client_wiring: drift 0 and complete true at e7260fd3, 1 and false here, null and false with another bun
$ <mutation script over scripts/adoption_status.py, in a scratch copy, GITHUB_ACTIONS=true>  ->  38 of 38 mutants killed
$ <the client as oracle: 16,428 texts written as project agents, the client started in headless mode and killed after its init record>  ->  the pipeline: MISS 0 (bun 1.4.3 and 1.4.2 alike); the reader of e7260fd3: MISS 501
$ python3 -E -s -B -m unittest <the modules that name a changed file, one process each>  ->  84 modules, 5,008 tests, 81 exit 0; the three others: tests.test_token_e2e_grader (test_keys_at_the_pinned_revision) fails the same on main (a test clone hard-links across devices); tests.test_secret_path_guard (test_host_profile_copy_is_verbatim) compares the installed guard of this host, which includes #1057, with the base tree's copy and fails the same on the unchanged base tree; tests.test_agent_drift_record failed on two test names that its new citation test found wrong in the record (fixed in this commit: 3 tests, OK). tests.test_adoption_status 203 OK, tests.test_runtime_worker_openhands_push_gate 127 OK (62 s), tests.test_upstream_surface_watch 116 OK, tests.test_freeze_snapshot 152 OK
$ the ten modules that read the docs, README, receipts or registry, rerun on the final tree  ->  all exit 0 (test_agent_drift_record 3, test_adoption_status 203, test_adoption_docs_consistency 39 (skipped=1), test_evidence_manifest 5, test_runtime_worker_openhands_push_gate 127, test_validate 86, test_host_baseline_probe 28, test_upstream_surface_watch 116, test_workflow_hardening 117 (skipped=1), test_workflow_security_coverage 10)
$ python3 scripts/validate.py  ->  exit 0 (11,877 files hashed); python3 scripts/evidence_manifest.py --check  ->  passed; the privacy scan of the lines added since c63c83a82: 0 account, host, home-path, e-mail, non-ASCII or token hits
$ git merge-tree --write-tree origin/main HEAD  ->  CONFLICTS in manifests/evidence.json and blueprints/runtime-workers/openhands/evidence/unclassified-gate-read-deltas.json against main 107bcca66 (#1057 landed after the base); e7260fd3 conflicts the same way (mergeStateStatus DIRTY): the landing rebase resolves them and recomputes the two advisory deltas
$ python3 scripts/adoption_status.py --profile token-efficiency --client-wiring --json  ->  with the bun 1.4.3 named in AGENT_FRONTMATTER_BUN: claude.stack_agents_drift 0, complete true; with the first host's default bun (mise 1.4.2): stack_agents_drift null, complete false (receipts/host-run.json)

Decision record

docs/decisions/2026-10-13-claude-agent-drift-check.md: the context, the decision, the evidence, what is not covered and the conditions that overturn it.

Host evidence

Only relevant when this PR adds or changes files under evidence/hosts/. This PR changes none.

Checklist

  • New/changed GitHub Actions are pinned to a full commit SHA with a version comment (no floating tags). None added or changed; the design round adds one run: step to validate.yml that downloads the bun release archive and checks its sha256 before unpacking it.
  • New/changed workflows declare top-level permissions: {} and grant each job only what it needs. None added; validate.yml is edited in one job (the run: step above), and its top-level permissions: {} and the job's grants are unchanged.
  • No secrets are printed, logged or committed; no new required secret was added without a documented owner. The check reads agent files and reports a count; the tests assert that no file text, name or path reaches the report.
  • No new paid hosting, subscription or billing surface was introduced.
  • Peer-owned untracked files and worktrees were preserved (not deleted, moved or overwritten).

Review of e70d2de0a: Astra's first read and the command center's Claude read, one fix commit

Finding Fix Test (fails before the fix)
P1 (Astra): inspection and opening were two steps, so a shipped file, or the whole agents folder, replaced by a link after the check was read through it (outside bytes equal to the source: 0 and complete: true; the reproducer schedules the replacement between the real lstat and the real open) the descriptor-bound reads above, with no fallback to a path check; the shared reader and its Codex caller changed with it, as the read asked test_a_replacement_of_a_shipped_file_at_any_point_is_never_read_through, test_a_replacement_of_the_agents_folder_at_any_point_is_never_read_through, test_a_replacement_of_a_folder_below_the_top_at_any_point_is_never_read_through, test_the_codex_role_count_is_never_read_through_a_replacement_either (file and folder), test_the_folders_are_opened_below_a_descriptor_without_following_a_link, test_a_platform_that_cannot_refuse_links_reads_nothing, test_read_regular_bytes_answers_for_every_kind_of_entry_without_following_a_link
P1 (Claude read): the client loads the folder recursively, so a nested definition reads as "no drift" (agents/old/stack-verifier.md: 0 and complete: true) every *.md at any depth is counted; the test that expected a nested file to be ignored is reversed test_a_definition_below_the_top_is_drift_at_any_depth, test_the_walk_is_bounded_and_a_tree_past_the_bound_is_unknown_not_a_count, test_links_and_other_kinds_below_the_top_are_counted_or_unknown_never_followed
P2 (Astra): the inserted lines moved the source the catalog cites for codex:host:RTK.md, and two citation assertions failed catalogs/foundation/upstream-surface-dispositions.json cites scripts/adoption_status.py:532-553 (codex_instructions and rtk_instructions_inline whole; a range that holds the instruction names whether lines are counted by splitlines() or by LF, which differ by two because of Unicode separators in RUST_WHITESPACE); the registry binding is refreshed tests.test_upstream_surface_watch (116 tests, two failures at e70d2de0a)
P2 (Claude read): no source for how the client loads the folder, and two claims were wrong (links are not followed, a nested file is not loaded) the section "How the client loads the folder" above; the lines on links are corrected (the record; the sources were re-read, see below)
P3 (Claude read): the four vendor names were exempt whatever they contain a vendor-named file at the top whose front matter declares a shipped name is counted; below the top, or a link, it is not exempt test_a_vendor_file_that_declares_the_name_of_a_shipped_definition_is_drift, test_a_vendor_name_below_the_top_or_as_a_link_is_not_the_vendors_own, test_a_vendor_file_that_cannot_be_read_is_not_drift_for_what_it_might_declare
P3 (Claude read): the record had no registry row; its host command omitted --profile token-efficiency; (Astra P3) the changelog paraphrase overstated its line a row is registered; the command is as the receipt records it; the paraphrase is narrowed to the entry's words (spurious warnings were fixed) and the loading rules rest on the loader and the documentation instead scripts/validate.py; the registry check

Two corrections to what the first review relayed, both from the primary sources: the changelog line cited for the merge order (L8578) is the CLAUDE_CONFIG_DIR entry, and the priority is documented on the sub-agents page and
implemented in the loader (above); and the installer is not a clean recovery path (next section).

The behavior changes to know: a replacement made after the check looked at an entry is read as the object that was opened (or refused, if it was a link when opened); a link to a folder anywhere below the agents folder makes the count null; a nested definition now makes complete false.

Review of e22358e46: Astra's second read and the command center's Claude read, one round-2 commit

Superseded in part: the name reader (agent_identity) and the tests that name it were replaced in the design round (the next section); the rows stay as the record of round 2.

Finding Fix Test (fails before the fix)
P2 (Astra): the descriptor opens added three reads to the OpenHands push gate's advisory inventory (9 to 12 for scripts/adoption_status.py); tests.test_runtime_worker_openhands_push_gate failed two tests at e22358e46 a new dated monitoring delta derived with the native GitTree and derive_ci_protected: 347 reads against the 344 of the baseline it names; the three added reads are the opens in open_below, stack_roles_matching and stack_agents_drift; the 24 location changes are line moves (9 in this script, 15 in scripts/skills_status.py and tools/adoption/install_skills.py, which other pull requests moved since the baseline); the index entry is appended, every earlier snapshot and delta is unchanged, both files are registered. source_commit is null: the input is a local commit of this round's content before the delta existed, and the published head holds the same scripts tests.test_runtime_worker_openhands_push_gate (127 tests; test_advisory_baseline_and_explicit_categories_stay_bounded_on_this_repository and test_baseline_refresh_regression_accepts_a_committed_outer_observation fail at e22358e46)
P2 (Astra): the vendor-name rule read the plain name: line only: a vendor-named file declaring a shipped name through a quoted key, a block scalar, an escape or a byte order mark gave 0 and complete: true agent_identity replaces the regex: the client's block extraction and a strict reader of the plain spellings, unknown for every other; the shipped definitions' names are read the same way; the first review's claim is narrowed in the record to what is measured test_a_vendor_file_that_declares_the_name_of_a_shipped_definition_is_drift (every row asserts the count and complete), test_a_vendor_file_whose_name_is_spelt_in_a_way_the_reader_does_not_take_is_unknown_and_incomplete (27 spellings, among them the reviewer's quoted key and escape), test_the_name_reader_gives_what_the_clients_parser_reads_or_says_unknown, test_every_shipped_definition_declares_a_name_the_reader_takes, test_a_shipped_definition_whose_name_cannot_be_read_makes_the_count_unknown, test_a_vendor_file_over_the_clients_size_limit_is_unknown
P2 (command center, the only blocker): the record quoted 46 of 46 for mutants-review.txt, whose receipt and README hold 49 each line that names a mutation receipt carries that receipt's own count; a test holds it tests/test_agent_drift_record.py
P3 (command center): a pipe or socket named *.md, or a *.md with no name, counted although the walker skips it a pipe, a socket, a device and a link to nothing are not counted; a *.md with no name stays counted and the record and the docstring say why test_a_link_to_a_file_is_counted_and_what_the_clients_walker_skips_is_not
P3 (command center): a vendor-named link swapped for a regular file between the open and a later look was classified by the later look a link is told by the open's own refusal (ELOOP, measured on the first host's Linux 6.18; EMLINK is accepted too) test_a_link_is_told_by_the_answer_of_the_open_and_not_by_a_later_look
P3 (command center): name: with the value on the next line read, with the other forms the client reads at the top level, by agent_identity test_the_name_reader_gives_what_the_clients_parser_reads_or_says_unknown
P3 (Astra): tools/adoption/host-baseline.md cites adoption_status.py:1124-1134 for the default launcher root, which moved the guide cites the current lines of launcher_resolution and keeps the old range as the one at its reference commit (the lines were read)
P3 (Astra): the installer; hostname text in the touched files; the upstream captures were not in the packet the follow-ups below -

Review of e7260fd3: the command center's Claude delta, Astra's second delta and the adjudication, one design-round commit

Finding Fix Test (fails before the fix)
P1 (the command center, reproduced) and P2 (Astra, the same case): a comment line at the left margin between name: and its indented value made the hand-written reader return none, so drift 0 and complete: true; Bun.YAML.parse and the client read the name the reader is removed; the name is asked of Bun.YAML.parse through bun at the version the client embeds, in one call, failing closed (no bun, another version, a throw, a bun that does not answer: unknown) RealBunTests.test_the_counterexample_of_the_command_centers_read_and_its_variants_are_read_as_the_client_reads_them, ClientWiringTests.test_a_vendor_file_that_declares_the_name_of_a_shipped_definition_is_drift (68 rows, each the client's), RealBunTests.test_the_pipeline_never_reads_a_name_other_than_the_one_the_client_served (2,987 texts), AgentNamesTests, NeedsBunTests
P3: the record cited a test that a round renamed a test holds every test name cited in the record and the README to the test files tests/test_agent_drift_record.py test_every_test_the_record_and_the_readme_cite_exists
P3: the receipts README double-counted the real definitions; the oracle receipt under-described the client's parse path; the record omitted Iwt's other refusals the README states the corpus once; receipts/client-oracle.txt names the whole path by function and byte interval; the record states Iwt's refusals; identity-oracle.txt is marked superseded the record, the receipts
P3: the push-gate delta classed adoption_status.py:1426 as move-only although this PR edited that line the delta of the design round states the edited line: adoption_status.py:1377 stays source_line_move_only with a scope that says the line itself was edited (it hands the agent definitions' source folder to the client wiring) and the read is unchanged; its derivation text names it tests.test_runtime_worker_openhands_push_gate (127 tests)
P3 (Astra): upstream captures and private drivers; installer; hostname text; stale locators the committed inputs of the oracle are replayed by the tests; the rest are the follow-ups below (hostname text: 57 occurrences on 55 lines in the three touched files, none added) -

The behavior changes to know: on a host whose bun is not 1.4.3 (the first host's mise has 1.4.2) stack_agents_drift is null and the wiring incomplete until bun 1.4.3 is installed by its supported path (mise install [email protected]) or named in AGENT_FRONTMATTER_BUN: a step for the host owner, not applied here; a vendor-named file whose front matter the first parse throws on is unknown; and validate.yml downloads and checksums bun 1.4.3 in every shard.

Landing rebase onto 3f1a921dd (2026-10-13), after Astra's ACK and the command center's PASS_WITH_P3 at fb968aff6

The branch was rebased once, onto main after #1057 (which landed with a delta of its own) and #1045, with the owning tools and no hand edit of a derived file; the pull request had gone DIRTY on manifests/evidence.json and the advisory delta index. The record update was the last commit (the command center's two conditions: the sentence at R:57 that a successful parse with no string name is none and departs from the adjudication's wording on purpose, with the oracle's numbers, and that mise install [email protected] does not change the active version).

  • Per file (net_diff_compare.py, the patch against the old merge base c63c83a82 against the patch against the new one): 25 files before and after, none only on one side; 22 have the same patch; the 3 that differ are the ones that had to: manifests/evidence.json (98 added and 13 removed lines on both sides; the rows were re-registered with host_receipts.register_file for the files each commit changes, on main's file) and the two advisory deltas (re-derived, below).
  • Registry rows (registry_rows_check.py): 24 PR-owned rows at the old head, 0 missing at the new one; 4 have another hash on purpose: the delta index, the two deltas, and catalogs/foundation/upstream-surface-dispositions.json, which Credential inventory: github-review-app key file and a --from-file custody mode #1057 also edited (the PR's patch to it is among the 22 unchanged; the row follows the merged bytes).
  • The two advisory deltas were re-derived with the native GitTree and derive_ci_protected over the tree that now holds Credential inventory: github-review-app key file and a --from-file custody mode #1057's reads: 351 reads against the 344 of the baseline snapshot (the 3 descriptor-bound opens of this change and the 4 of tools/credentials/set_credential.py, whose scope is the one Credential inventory: github-review-app key file and a --from-file custody mode #1057's delta gave them); 33 line moves; source_line_move_only for the edited line :1377 keeps the scope that says the line was edited.
  • Checks on the rebased head 2723f8fd0 (idle-class, read at that head; a rerun at that commit returns the same): python3 scripts/validate.py exit 0 (11,899 files); tests.test_runtime_worker_openhands_push_gate 127 OK; tests.test_adoption_status 203 OK with bun 1.4.3 (AGENT_FRONTMATTER_BUN), tests.test_agent_drift_record, tests.test_evidence_manifest, tests.test_upstream_surface_watch, tests.test_adoption_docs_consistency, tests.test_host_baseline_probe, tests.test_validate, tests.test_workflow_hardening, tests.test_workflow_security_coverage, tests.test_credential_tools and tests.test_credential_status OK; git merge-tree --write-tree origin/main HEAD clean; the privacy scan of the added lines: 0 hits.

Review of 2723f8fd: the local codex review, one P2, one scope-locked commit

The branch was rebased a second time, onto main after #1068 (a registry-only conflict again; 27 of 27 files have the same patch against the previous head, 26 PR-owned registry rows, 0 missing, 0 changed, the two advisory deltas keep their bytes; the table of patch-ids is /var/tmp/ccnp-tests-tmp/perfile-patchids-1062b.txt), and the checks below are on that head.

The local codex review of the rebased head said "patch is incorrect" (one P2) and the lander blocks on a completed review; the command center ruled one bounded round, scope-locked to it. Astra's rebase micro at 2723f8fd is an ACK with no blockers.

Finding Fix Test (fails before the fix)
P2, scripts/adoption_status.py:1084: the extraction matched the front matter pattern without the client's delimiter precheck, so a vendor-named file of --- and then whitespace with no closing line made the pattern backtrack quadratically (reproduced: 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); the extraction runs before bun, so no timeout of bun bounds it agent_front_matter returns 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)). It changes no result and removes the backtracking: the pattern is tried only on a text that has a closing line. 1 MiB of newlines now takes 2 ms ExtractionTests.test_an_unterminated_block_of_whitespace_is_refused_in_linear_time (five fillers of 64 KiB under a second), ExtractionTests.test_a_closing_line_far_below_a_long_run_of_whitespace_is_still_found_in_linear_time (a control), ClientWiringTests.test_a_vendor_file_that_is_one_long_unterminated_block_is_read_in_linear_time (the whole call under ten seconds, no bun asked); the first and the third fail on the sources of 2723f8fd (19 s, 6 s, 9 s and 19 s: receipts/fail-before-precheck.txt); 4 of 4 mutants of the check are killed (receipts/mutants-precheck.txt)

Checks at b3da64d76 (idle-class): python3 scripts/validate.py exit 0 (11,903 files, read at that head: this line first said 11,901, a count read before the last registry rows; Astra's read of that head returned 11,903 twice and a rerun here at that commit returns 11,903); tests.test_adoption_status 206 OK with bun 1.4.3 under GITHUB_ACTIONS=true; tests.test_agent_drift_record, test_evidence_manifest, test_upstream_surface_watch, test_adoption_docs_consistency, test_validate, test_workflow_hardening, test_workflow_security_coverage, test_host_baseline_probe, test_credential_tools, test_credential_status OK and tests.test_runtime_worker_openhands_push_gate 127 OK; the push-gate inventory is unchanged (no read added: the module passes without a new delta); privacy scan of the added lines: 0 hits.

Review of b3da64d76: the local codex re-review (one P2) and Astra's read (the description only), one scope-locked commit

The local codex re-review of b3da64d76 said "patch is incorrect" (one P2) and the lander blocks on a completed review; the command center adjudicated it as fixed in this landing, as one bounded commit. Astra's read of the same head confirms the precheck independently (the 2.1.296 bytes match; 65,536 newlines 17.9 s before and 0.000066 s after, 32,768 space and newline pairs 9.08 s and 0.000046 s; 0 extraction differences on 2,987 oracle texts and on 2,993 upstream-oi inputs; the Bun replay has 0 misses; the search-start 3 to 4 mutant survives and is behaviorally equivalent) and is CHANGES_REQUESTED on one P2 in this description only.

Finding Fix Test (fails before the fix)
P2, scripts/adoption_status.py:1116: 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; subprocess looks for a relative executable relative to cwd (python/cpython@cbc944f4bc59639a444dd971c737788ba2283a91 Doc/library/subprocess.rst L572-L576; Lib/subprocess.py L1912-L1919), so the name was looked up in the scratch directory. Reproduced with bun 1.4.3 (the receipt): the absolute path parses and a relative path to the same file gives Unknown, and the check then reports null and complete false find_bun returns an absolute path before either launch: a value 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 and 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 Unknown. Which bun is selected does not change test_a_relative_bun_is_made_absolute_before_either_launch (six framings), test_a_value_with_no_directory_is_a_name_looked_up_on_path_and_never_in_the_callers_directory, test_a_relative_bun_of_a_caller_whose_directory_is_gone_is_unknown and, with the real bun, test_a_bun_named_by_a_path_relative_to_the_callers_directory_is_the_same_bun (run from the bun's own directory); all four fail on the sources of b3da64d76 (receipts/fail-before-absolute-bun.txt: the reviewer's scenario gives Unknown there and the name here); 9 of 9 mutants of the change are killed (receipts/mutants-absolute-bun.txt)
P2 (Astra), this description at pr-body.md:163: it gave python3 scripts/validate.py exit 0 (11,901 files) for b3da64d76, which returns 11,903 the count of that head is corrected above (11,903, read at that head) and each older count is labelled with the head it was read at (11,899 at 2723f8fd0, below); the count at this head is below (the description; the validator itself passes)

Checks at 9720257e7 (idle-class, read at this head): python3 scripts/validate.py exit 0 (11,905 files); tests.test_adoption_status and tests.test_agent_drift_record 213 OK with bun 1.4.3 under GITHUB_ACTIONS=true; the modules that name a changed file, one process each: 70 modules, 4,659 tests, 69 exit 0; the one other is tests.test_token_e2e_grader (test_keys_at_the_pinned_revision: a test clone hard-links across devices, the same failure as at every earlier head); tests.test_adoption_status 210 OK, tests.test_agent_drift_record 3 OK, tests.test_runtime_worker_openhands_push_gate 127 OK (the inventory is unchanged: no read added and no new delta), tests.test_host_baseline_probe 28 OK; git merge-tree --write-tree origin/main HEAD clean (exit status read directly); the privacy scan of the lines added since b3da64d76: 0 account, host, home-path, e-mail, non-ASCII or token hits (gitleaks on the commit: no leaks).

The mise shim stays outside this fix, as adjudicated: with no variable the selection is still the first bun on PATH (now as an absolute path), so a shim that resolves to another version is Unknown by the version check; the variable naming the installed 1.4.3 binary stays the way (the follow-up below).

Review of 9720257e7: the second local codex re-review (one P2), one scope-locked commit

The second local codex re-review said "patch is incorrect" (one P2) and the command center adjudicated it as fixed in this landing, as one bounded commit, pushed at once.

Finding Fix Test (fails before the fix)
P2, scripts/adoption_status.py:1105: os.path.abspath made the selected bun absolute by removing 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) find_bun puts the caller's directory in front of a relative path and normalizes nothing: 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@cbc944f4bc59639a444dd971c737788ba2283a91 Doc/library/subprocess.rst L572-L576); an absolute value is passed as it is; a link is not resolved (os.path.realpath would replace the mise shim, a link to mise, by mise: an executable can be known by its name) test_a_dot_dot_after_a_link_keeps_the_meaning_the_file_system_gives_it (a relative value, a relative PATH entry, an absolute value through the link; a decoy of another version beside the link) and, with the real bun, test_a_dot_dot_after_a_link_reaches_the_real_bun_the_kernel_finds: both fail on the sources of 9720257e7 (receipts/fail-before-dotdot.txt: Unknown there, the name here); test_a_link_to_the_bun_is_kept_by_its_name_since_an_executable_can_be_known_by_it holds at both heads (the guard against resolving the final component); 5 of 5 mutants killed (receipts/mutants-dotdot.txt)

The stand-in bun of the tests found its behavior file by os.path.abspath(sys.argv[0]), the same lexical mistake; it now uses the real path of the file that ran.

Checks at 31d377e3c (idle-class, read at this head): python3 scripts/validate.py exit 0 (11,907 files); tests.test_adoption_status, tests.test_agent_drift_record, tests.test_evidence_manifest and tests.test_validate: 307 tests OK with bun 1.4.3 under GITHUB_ACTIONS=true; tests.test_runtime_worker_openhands_push_gate 127 OK at 80f552245 (the module derives its reads from the committed tree: run on the working tree before the commit it passed, and the continuous integration of 31d377e3c failed it, validate-shard-2; see the next paragraph), test_upstream_surface_watch 116, test_adoption_docs_consistency 39, test_host_baseline_probe 28, test_workflow_hardening 118, test_credential_tools 52, test_credential_status 52 and test_workflow_security_coverage 10 OK (the 70-module sweep of the previous head is above; this head's sweep is the eight modules that the change can reach); git merge-tree --write-tree origin/main HEAD clean against main ba6afd8d7 (exit status read directly); the privacy scan of the lines added since 9720257e7: 0 account, host, home-path, e-mail, non-ASCII or token hits (gitleaks on the commit: no leaks).

The advisory delta of 31d377e3c (80f552245): a read that the second re-review commit adds

os.getcwd() in find_bun (scripts/adoption_status.py:1107) is a read of the working directory that the OpenHands push gate's derivation counts as one unclassified read (os.path.abspath made the same read inside the library and was not counted). My earlier statement here that this round added no read was wrong: I ran tests.test_runtime_worker_openhands_push_gate on the working tree before committing, and the module derives its reads from the committed tree, so the continuous integration (validate-shard-2: test_advisory_baseline_and_explicit_categories_stay_bounded_on_this_repository and test_baseline_refresh_regression_accepts_a_committed_outer_observation) found it. The read is real and read-only, so it is accounted, not written around: blueprints/runtime-workers/openhands/evidence/unclassified-gate-reads-20261013-pr1062-dotdot-delta.json (352 reads; the new read classified as a user status observation, scope "read-only client-wiring status; no worker execution authority") and its index entry, derived with the native GitTree and derive_ci_protected. At 80f552245: python3 scripts/validate.py exit 0 (11,908 files), tests.test_runtime_worker_openhands_push_gate 127 OK on the committed tree, gitleaks no leaks. Another pull request that appends a delta to the same index (V-07 #1074 does) re-derives its cumulative delta after this one lands, as #1057's landing required of this one.

Landing rebase onto 77cc7ef42 (2026-10-13), after Astra's ACK at 80f552245 and the command center's approval of one leased registry-only rebase

The pull request went DIRTY on manifests/evidence.json and the advisory delta index as main moved (#1072, #1070, which landed a read delta of its own for scripts/validate.py, and #1075). It was rebased once, commit by commit, with the owning tools and no hand edit of a derived file: each commit is cherry-picked onto main; a conflict in the registry or the index takes main's file and the pull request's part is re-derived (host_receipts.register_file for the rows the commit changed; the delta of each of the three commits that carried one is re-derived with the native GitTree and derive_ci_protected over the tree that now holds #1070's read). The command center's stop rule (a change beyond the registry and index files is not a registry-only rebase) held: no other file differs.

  • Per file (git patch-id --verbatim of the -U0 --binary diff against each side's own merge base: old head 80f552245 against 993cc890b, new head 4e3780b10 against 77cc7ef42): 32 files before and after, none only on one side; 28 have the same patch; the 4 that differ are the ones that had to: manifests/evidence.json and the three advisory deltas (re-derived, below). The delta index has the same patch: the same three entries, appended after main's.
  • Per commit: 6 of 9 have the same patch-id as before; the 3 that differ are the commits that carried a delta (8d8c66180 to 3a4068c41, 4222bdd82 to 9a3a6ea85, 80f552245 to 4e3780b10).
  • Registry rows: 31 PR-owned rows at the old head (the rows that differ from the old base's), 0 missing at the new one, 0 not current (each row's sha256 and byte count are those of the file at the new head); 5 rows differ from the old head's row: the three deltas and the index (re-derived) and .github/workflows/validate.yml, which main also edited (the pull request's hunk is among the 28 unchanged patches; the row follows the merged bytes); the other 11,879 rows are main's, unchanged, and no row is added beyond main's and the pull request's. python3 scripts/validate.py --registration-base origin/main --registration-before "<80f552245> <fb968aff6> <2723f8fd0>" (fix(ci): enforce PR-owned manifest registration continuity #1070's gate, with the old head and the two force-pushed predecessors of the pull request's timeline): exit 0.
  • The three advisory deltas were re-derived over main's tree: 352 reads at the round-2 and design-round commits and 353 at the head, against the 344 of the baseline snapshot (scripts/adoption_status.py 3, then 4 with the os.getcwd() read of find_bun; tools/credentials/set_credential.py 4, with the scope Credential inventory: github-review-app key file and a --from-file custody mode #1057's delta gave them; scripts/validate.py 1, with the scope fix(ci): enforce PR-owned manifest registration continuity #1070's delta gave it); 33 line moves; source_line_move_only for the edited line keeps the scope that says the line was edited. source_commit is null as in the earlier entries (each delta is derived from the local commit that precedes it; the published head holds the same scripts).
  • One count in the decision record was one behind: the sentence about the dotdot delta ("352 reads, scripts/adoption_status.py 4 of the delta's added") stated the delta as it was derived on 993cc890b; the rebased delta is 353 (main's scripts/validate.py read). The rebase changed no text, so the command center ruled the correction into the next commit (below).
  • Checks on the rebased head 4e3780b10 (idle-class, read at that head): python3 scripts/validate.py with the registration gate above exit 0 (11,910 files hashed); tests.test_runtime_worker_openhands_push_gate 127 OK on the committed tree; tests.test_adoption_status and tests.test_agent_drift_record 216 OK with bun 1.4.3 (AGENT_FRONTMATTER_BUN; with the host's PATH shim, bun 1.4.2, the two tests that need 1.4.3 fail by design, "so its absence there is a defect"), test_evidence_manifest 5, test_validate 86, test_registry_rows 25 (new on main), test_workflow_hardening 118, test_workflow_security_coverage 10, test_adoption_docs_consistency 39, test_upstream_surface_watch 116, test_host_baseline_probe 28 and test_gitleaks_config 58 OK; git merge-tree --write-tree origin/main HEAD exit status 0 (read directly, right before the push); identities all the no-reply one; gitleaks on the nine commits: no leaks; the privacy scan of the added lines: 0 account, host, home-path, e-mail, non-ASCII or token hits (the same 36 digests as before).

Review of 4e3780b10: the local review of the landing rebase (one P1), folded with the count into one commit

The local review of the rebased head (normalized 71a2fd4f, sha256 prefix) found one P1; the command center ruled it into the one commit that also corrects the count above. The commit is a fast-forward of 4e3780b10 (merges clean against main 24a209016, which landed #1080 meanwhile: git merge-tree --write-tree, exit status read directly).

Finding Fix Test (fails before the fix)
P1, tests/test_adoption_status.py:1932-1933: the bun tests fail under GitHub Actions rather than skip, and catalog-freshness.yml runs the whole suite (python3 -m unittest --durations 50) on a runner without bun 1.4.3; only validate.yml provisioned it, so the scheduled and the manual freshness run failed at RealBunTests and the vendor-definition tests and its propose job never started the same step, with the same version and checksum, in the freshness job before its suite (the local workflow check runs the step's own script: the archive checks, bun --version is 1.4.3, AGENT_FRONTMATTER_BUN is exported, and tests.test_adoption_status under GITHUB_ACTIONS=true is 215 OK with it and FAILED (failures=4, errors=1) without it) test_every_job_that_runs_the_suite_provisions_the_pinned_bun_before_it (fails at job freshness: 0 != 1; it reads every workflow, finds each job whose unittest step reaches the module and holds the step, the version and the checksum equal to validate.yml's) and test_the_reader_tells_the_suite_runners_from_the_named_modules
the count (the command center's ruling): the record's sentence about the dotdot delta "352 reads" becomes "353 reads" and its registry row follows (the record; tests.test_agent_drift_record guards its citations)

The mutants of the step and of the reader: 13 of 13 killed (receipts/mutants-freshness-bun-13.txt). The workflow was not dispatched: its propose job force-creates an evidence branch and opens a pull request; receipts/freshness-bun-local-check.txt is the local workflow check. The decision record has a section for this review.

Checks at eecd506f2 (idle-class, read at that head): python3 scripts/validate.py --registration-base 77cc7ef42 --registration-before "<4e3780b10> <80f552245> <fb968aff6> <2723f8fd0>" exit 0 (11,913 files hashed); the 70 modules that name a changed file, one process each: 4,555 tests, 68 exit 0 and two that fail for reasons outside this change: tests.test_claude_usage_metrics (prometheus_client is not installed on this host) and tests.test_token_e2e_grader (a test clone hard-links across devices); tests.test_runtime_worker_openhands_push_gate 127 OK on the committed tree (the commit changes no read), tests.test_adoption_status 215 OK with bun 1.4.3 under GITHUB_ACTIONS=true, tests.test_agent_drift_record 3, tests.test_workflow_hardening 118, tests.test_registry_rows 25, the four tests.test_catalog_freshness_* OK; gitleaks on the commit: no leaks; the privacy scan of the lines added since 4e3780b10: 0 account, host, home-path, e-mail, non-ASCII or token hits.

Follow-ups

  • From Astra's read of 31d377e3c (CHANGES_REQUESTED on one P2, the inventory count, answered by 80f552245; verdict file fix5-pr1062-31d377e3c/GPT-VERDICT-31d377e3c.md, sha256 4e25aa00...): seven follow-ups of the family of the read of b3da64d76 below, with the locators of that head (launcher_resolution at scripts/adoption_status.py:1487, the default root and launcher at :1496-1497); the PATH-shim P2 stays previously adjudicated; triggers as below.
  • From Astra's read of 80f552245 (ACK, no blockers; verdict file fix6-pr1062-80f552245/GPT-VERDICT-80f552245.md, sha256 cddc80f1...; the micro changes the monitoring record, its index and two explanatory records, no production code): eight follow-ups, each with its trigger, none changing code. (1) P3, bind an immutable source_commit to the new observation: the delta's source_commit is null and its derivation says input commit HEAD; trigger: the next advisory-record append, citing the native implementation at that pin (the published row is preserved; the rebased deltas are null too, derived from a local commit that is not in the published history). (2) P3, current locators (launcher_resolution at :1487, root and launcher at :1496-1497; the historical 1124-1134 stays) and the inherited workflow locator: the landing documentation refresh and that record's next edit. (3) P3, the retained edited-read classification wording: the next record edit. (4) P2, previously adjudicated: the Bun PATH-shim selection, keep AGENT_FRONTMATTER_BUN: the next selection change. (5) P3, qualified test citations outside the record guard: the next qualified citation edit. (6) P3, replay, refusal and recovery boundaries: a later pull request re-derives its cumulative delta on the integration tree, as the rebase above did; installer link replacement and non-default Claude homes stay in the installer pull request. (7) P3, the missing captures and original measurement inputs: the next packet. (8) P3 hygiene, the historical hostname text (58 occurrences, 57 in complete changed files, none added): the registry hygiene change.
  • From Astra's read of b3da64d76 (CHANGES_REQUESTED on this description only; verdict file fix4-pr1062-b3da64d76/GPT-VERDICT-b3da64d76.md, sha256 2f8674ec...; the precheck confirmed independently): seven follow-ups, none changing code in this round except where stated.
    1. P3, the launcher locator tools/adoption/host-baseline.md:221 still gives 1521-1531 as the current range (Astra: 1471-1481 at b3da64d76; this commit adds 12 lines to find_bun, so launcher_resolution is now at scripts/adoption_status.py:1483, the default root at :1492 and the launcher at :1493, 1483-1493); keep the historical 1124-1134. No test binds it. Trigger: the landing documentation refresh.
    2. P3, the record says an edited-line read is "listed apart" where the delta retains source_line_move_only and its scope says the line was edited: describe the retained class and the scope at the next record edit.
    3. P2, previously adjudicated: the Bun PATH-shim selection stays outside this fix. With no variable find_bun still selects the first PATH executable (now an absolute path) and invokes it with a scratch HOME; this commit changes how the selection is expressed, not which bun is selected. Keep the pinned binary configured in AGENT_FRONTMATTER_BUN; the separately scoped selection change resolves the pinned install or requires the override. Trigger: the next change to the selection.
    4. P3, the record guard tests/test_agent_drift_record.py recognizes a bare backticked test name, not a class-qualified one (the citations of the design round and of the precheck round are qualified); extend it and show that a nonexistent qualified method fails it. Trigger: the next record edit that cites one.
    5. P3, recovery and snapshot limits stay visible: YAML failures are unknown, a file with no front matter avoids Bun, descriptor-relative reads refuse replacements, and the check performs no install or recovery; the installer (install_claude_profile.py: default home, equal symlink targets skipped, copy2 over a link) is fixed only in its own authorized pull request.
    6. P3, missing captures and drivers: the dated sub-agents.md page, the oven-sh/[email protected] source, release archive and SHASUMS256.txt (and the mise source behind the active-version observation), openai/[email protected] at scripts/adoption_status.py:1007, the installed-client intervals other than the oi slice, the full 16,428-text run and the private oracle and mutation drivers, the review captures 9bb830a1 and 27fba06d, and the per-file comparison script cited above. Supply them with their recorded hashes when assembling the next packet.
    7. P3 hygiene, the hostname lines already in the changed files (58 occurrences: 1 in patch context, 57 in complete changed files; none added here): the registry work.
  • From Astra's rebase micro at 2723f8fd (ACK, no blockers; verdict file rebase1-pr1062-2723f8fd0/GPT-VERDICT-2723f8fd0.md, sha256 14a0ca53...; it reproduced the advisory derivation: 351 = 344 + 3 + 4 with exact locations; the delta index's stable patch-id differs only because main's Credential inventory: github-review-app key file and a --from-file custody mode #1057 entry now sits before ours, a context change). Eight follow-ups, six of them the ones above; triggers as above:
    1. P3, the launcher locator tools/adoption/host-baseline.md:221 (1521-1531, now 1468-1478) and the record's historical range: the landing documentation refresh.
    2. P3, the record's "listed apart" wording for the edited-line read: the next record edit.
    3. P2, previously adjudicated: Bun selection through the PATH shim in a scratch home; the record's "so the shim is never run" is the intended consequence of naming the binary, not an implemented prohibition on the fallback: the next find_bun change.
    4. P3, the missing UPSTREAM-SRC captures, the dated subagents page and the complete measurement inputs: the next packet.
    5. P3, the guard for class-qualified test names: the next record edit that cites one.
    6. P3, the adjacent installer (CLAUDE_CONFIG_DIR, link replacement; manual recovery for a non-default Claude home) and the snapshot boundary (the check is one observation, not protection against edits after it): the separately authorized installer pull request.
    7. P3 hygiene, the historical hostname text (57 occurrences on 55 lines in the three changed files; this PR adds none): the registry work.
    8. P3, an inherited stale workflow locator: docs/decisions/2026-10-11-code-scanning-repo-level.md:53 cites validate.yml:251 for the scanner action, which is at :274 (already stale at the base): that record's next edit.
  • From the command center's delta read of fb968aff6 (PASS_WITH_P3, research-cc-role-20261008/pr1062-claude-delta-fb968aff.md, sha256 9bb830a1...; no new reproduced P1; G1 measured on the 2.1.296 client: one BOM is served and two are not, as the test says):
    1. P2-b: the PATH and mise route does not work in a scratch home (measured: env -i ... HOME=<scratch> ~/.local/share/mise/shims/bun --version gives 1.4.2, and mise installs into the scratch home), so the shim route resolves the wrong version and the check reports null (safe). Trigger: the next change to find_bun: resolve the pinned install (for example mise where [email protected]) or require AGENT_FRONTMATTER_BUN, and never run the shim in a scratch home. Host action (the command center): set AGENT_FRONTMATTER_BUN for the host's drift-check runs; the record now says that mise install does not change the active version.
    2. P3-c: the cited-test guard (tests/test_agent_drift_record.py) skips class-qualified names. Trigger: the next record edit that cites a class-qualified test.
    3. P3-d: the push-gate delta keeps the edited line :1377 in source_line_move_only, with a scope that says it was edited (the same as Astra's follow-up 3).
    4. Gaps: G2 the rule that picked the 2,987-text oracle subset is not recorded; G3 invalid UTF-8 names are not checked against the client; G4 the embedded build's parity with the release is measured on the corpus only (trigger: any change of the client's parser or embedded bun, the record's overturn condition).
    5. P2-a, accepted: a non-mapping result or a non-string name is none, as the client's loader does (oracle: 1,829 agree-none, 0 misses); the record (R:57) states it as a deliberate departure from the adjudication's wording.
  • From Astra's delta read of fb968aff6 (ACK at 09:32:45Z, no remaining blocker; verdict file fix3-pr1062-fb968aff6/GPT-VERDICT-fb968aff6.md, sha256 27fba06d...): six P3 follow-ups, each with its trigger:
    1. The live host-guide locator regressed when this delta removed parser lines: tools/adoption/host-baseline.md L221 and the decision record (L114) still give the current launcher range as :1521-1531; at this head launcher_resolution is at scripts/adoption_status.py:1468 and the default root at :1477-1478. No test binds the locator. Trigger: the landing documentation refresh: change the current range to 1468-1478 and keep the historical 1124-1134.
    2. The cited upstream captures and the full measurement drivers are absent from the packet (the dated subagents page and the oven-sh/bun release and source captures are unmirrored; the 2,987 committed inputs replay, the 16,428-text collection and the 38-mutant driver cannot be replayed from their labels). Trigger: assembling the next packet: attach the dated page, the pinned source and release captures and the public measurement inputs.
    3. Wording: the decision record (L128) says an edited-line read is "listed apart"; the delta keeps source_line_move_only and its scope says the line was edited. Trigger: the next record edit: say that the classification is retained and its scope distinguishes the edited line.
    4. The installer recovery (CLAUDE_CONFIG_DIR, links) and the stale practice and currency notes stay on their triggers (the installer bullet and the practice-page and test_currency_due.py bullets below); manual recovery for a non-default Claude home still needs explicit guidance.
    5. The historical hostname text in three touched files (57 occurrences on 55 lines; the hostname bullet below) waits for the hygiene change.
    6. Contract boundaries to keep explicit: a successful parse with no string name gives none, where the adjudication's literal item 2 asked for unknown for that shape (the client's mapping and name guard rejects those non-agents); and when no vendor file has front matter the check does not parse the otherwise byte-identical shipped definitions. The tests exercise the no-name policy. Trigger: the next CPA-V-10 contract clarification: reconcile the literal wording with the documented no-agent policy and keep invalid-definition correctness separate from byte drift.
  • The practice page note (.claude/skills/claude-native-practice/reference/workers.md L13, "four host copies differ") is stale: 11 of 11 are equal and this check reports drift. The page is pinned by the skills manifest, so the correction belongs to the next re-pin of the practice skill (the practice-currency change, Bring the Claude native-practice skill from client 2.1.295 to 2.1.296 with a source-cited delta (CPA-V-09) #1053, is the nearest).
  • The audit's alternative, running the profile installer's agent step when an agent change lands, is not done here: it changes the host and stays the command center's act. With this check the drift is reported the first time the wiring is read.
  • Project-scope (.claude/agents) and plugin-provided definitions are not read: the check covers the user scope, where the installer writes.
  • The installer (tools/adoption/install_claude_profile.py) is not a clean recovery path; the command center ruled that it is its own small pull request after this one lands: install_agents writes under ~/.claude/agents and not under CLAUDE_CONFIG_DIR; its equality test (L178-L179) follows a link, so a linked copy with equal bytes is skipped and its drift never clears; and shutil.copy2 over a link writes through it (python/cpython@cbc944f4bc59 Lib/shutil.py L262). That pull request: not dest.is_symlink() before writing, remove before copy, install_agents honouring CLAUDE_CONFIG_DIR, a test that fails before. Recovery by hand until then: remove the stale, linked or unlisted file, then rerun the installer.
  • Hostname text already stands in three files this pull request touches (57 occurrences on 55 lines: 35 in catalogs/foundation/upstream-surface-dispositions.json, 20 on 18 lines of manifests/evidence.json, 2 in tools/adoption/host-baseline.md); none is added here. A hygiene pull request owns them (the registry change is separately authorized).
  • The brief for the next read attaches UPSTREAM-SRC captures of the upstream files it cites and a dated capture of the subagents documentation page (the Claude and CPython files were read at their local mirror commits, which the packet did not carry; the page is unmirrored). The mutation and client-oracle drivers stay in the author's private record; the oracle's inputs and the client's answers are committed (tests/fixtures/agent_frontmatter/client-oracle.json) and replayed by the tests.
  • Comments in tests/test_currency_due.py cite adoption_status.py line ranges (1282-1344 and others) that were already off at c63c83a82; unrelated to this change, they are listed for the next edit of that file.
  • Astra's research question (do nested user-scope definitions load) is answered above from the documentation page and the loader; the practice page's loading notes could cite them at the next re-pin.
  • VENDOR_HOST_AGENTS is a list of four names: a vendor that installs another definition shows as drift until it is listed (the decision record's overturn conditions).

🤖 Generated with Claude Code

@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 claude/cc-native-practice-agent-drift-check-20261013 branch 2 times, most recently from 2723f8f to b3da64d Compare October 13, 2026 12:17
…epository'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]>
… 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]>
… 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]>
… 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]>
…'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]>
…t'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]>
…h 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]>
…malizing 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]>
…n 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]>
@seathatflowsinourveins
seathatflowsinourveins force-pushed the claude/cc-native-practice-agent-drift-check-20261013 branch from 80f5522 to 4e3780b Compare October 13, 2026 17:57
…'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
seathatflowsinourveins merged commit 329985d into main Oct 13, 2026
33 checks passed
@seathatflowsinourveins
seathatflowsinourveins deleted the claude/cc-native-practice-agent-drift-check-20261013 branch October 13, 2026 20:17
seathatflowsinourveins added a commit that referenced this pull request Oct 14, 2026
…opy, refuses a folder there and follows CLAUDE_CONFIG_DIR for the current user's own install (CPA-V-10 follow-up)

The drift check of #1062 counts a link at a shipped agent name and reads the Claude home from CLAUDE_CONFIG_DIR; the installer did neither, so it could not clear what the check reports. Measured on
Claude Code 2.1.296 (zero model spend; the client killed at its init record, receipts/client-agents-probe.json): with CLAUDE_CONFIG_DIR set the client lists the definitions under that folder's agents/
and none under <home>/.claude/agents; an empty value is read as its working directory and a relative one is resolved against it; a link to a file is followed (listed by its front matter name) and a link to
nothing is not loaded.

- tools/adoption/install_claude_profile.py: install_agents writes each copy beside its destination and renames it over (os.replace), so a link at a shipped name (to a file, to nothing, to a folder) is
  replaced by a regular copy and never written through (shutil.copy2 opened the destination through the link, python/cpython@cbc944f4bc59 Lib/shutil.py L262); the equality test no longer follows a link; a
  folder at a shipped name is refused before anything is copied (copy2 copied into it, a nested definition the client then loads); a dry run names the link it would replace.
- Without --home the agents step follows a set, absolute CLAUDE_CONFIG_DIR (made absolute and NFC-normalized as the client reads it); an empty value is read as unset, as the drift check reads it; a relative
  value is refused; with --home the target is that home's own .claude whatever the environment names (the new-WSL tool passes --home and strips the variable). A relative value does not stop a run that does
  not install agents.
- tests/test_install_claude_profile.py: AgentsInstallTests and AgentsConfigDirTests (15 tests; 9 fail or raise on the sources of 329985d, receipts/fail-before.txt); 15 of 15 mutants are killed
  (receipts/mutants-installer.txt; the first run killed 14 and the survivor's test now asserts that a dot-dot is collapsed without the file system).
- Docs and records: docs/decisions/2026-10-13-installer-agent-links-and-config-dir.md; adoption/bootstrap.md (the agents bullet); a pointer from the "Not covered" bullet of the drift record; evidence/artifacts/
  installer-agent-links-20261013/ (value-free captures: definition names and counts).

Not changed: the guard hooks (registered by absolute command), the routing skill and the workflows steps, which are found by folder and need their own measurement of where the client looks (a follow-up).

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