Skip to content

Daily skill record: a Claude and a Codex unit with measured limits, run from the live clone (CPA-C-002) - #1043

Draft
seathatflowsinourveins wants to merge 12 commits into
mainfrom
claude/cc-native-practice-skill-record-halves-20261012
Draft

seathatflowsinourveins wants to merge 12 commits into
mainfrom
claude/cc-native-practice-skill-record-halves-20261012

Conversation

@seathatflowsinourveins

@seathatflowsinourveins seathatflowsinourveins commented Oct 12, 2026 •

Copy link
Copy Markdown
Collaborator

Scope

  • What this PR changes, in one or two sentences: CPA-C-002 of the Claude-family practice audit (claude-practice-audit-claude-20261012, G3-01): the daily per-skill invoke record of Measure daily native skill use and test the security-audit trigger cue #970 becomes two user units, one per client, each with its own record and measured limits, run from the live clone; the /skill-doctor capture gets a 300 s limit and the daily run a --claude-timeout. Installing the units is the command center's act after this lands.
  • Base commit: 3f1a921dd23223e8429ecf2615e3c7242bd2cfc0 (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; rebased for landing from 703d588c5660d698a87b622efe0e3bd8c22f7e29, main after Add isolated Pi and OpenCode trial profiles and native invocation receipts #1033; the first head was validated on d8f7e2051, main after Apply verified event scopes to CI secret scans #949).
  • Lane: lane:foundation
  • Owned paths touched: tools/skill-usage/ (daily_skill_usage.py, skill_usage.py, README.md), adoption/templates/systemd/ (the unit templates), docs/decisions/ (one new record, two links to moved lines), evidence/artifacts/skill-invoke-rate-timer-20261012/ (receipts, review round 2), tests/test_skill_usage_daily.py, tests/test_skill_usage.py, manifests/evidence.json.
  • Commits: twelve, eleven of them rebased for landing onto main 8c11dac09 on 2026-10-14 (below): the change (47097b669), review round 2 (3915120bd), review round 3 (23c9376f0), review round 4 (ef26a7137), review round 5 (4b36ae41d), the scope-locked round after the local codex review and Astra's read (2f6824743), round 7 (c205f3a74), round 8 (64a35ac75), round 9 (879f0c5fb), round 10 (0b5b3e1b9), round 11 (d2f96f5be) and the scope-locked round 12, the one commit for the fix10 verdict file's blocker 1 (e217e1ef0, below). Before the 2026-10-14 rebase the eleven were 615198b67, cba514600, 0aa343171, 3efec45ed, 0ebbcdb98, c56121e4a, 0383243e4, c23b9ce48, cb42f789c, 8e93415fe and 8ec8ebc37, and before the 2026-10-13 rebase the first six were 6c4bbd0db, 68d4f4343, 9caafa601, 322fe463d, da4895b51 and e181c3ff2; the reads and the rounds below name the heads they read.

What was wrong, each measured on the first host (2026-10-12)

  1. The capture timed out. daily_skill_usage.py ran skill_usage.py --run-skill-doctor with no timeout argument, so the 30 s default applied; the last manual host report (2026-10-11T16:13:49Z) refused its Claude half with TimeoutExpired. A capture is about one core of work: 17.2 to 20.1 s on a quiet host (13 runs), 57.7 and 61.9 s at three runnable threads per CPU.
  2. The clone test refused the clone. The run exited 2 unless git branch --show-current printed main; the unit renders for ~/code/native-agent-stack-live, a detached worktree of origin/main kept by the review worker's tick (docs/decisions/2026-10-09-claude-review-worker.md), whose branch name is empty.
  3. One unit held both clients behind one exit status. Run once as a transient user unit (09:13:47Z to 09:37:44Z) it took 23 min 57 s and exited 1, because the Codex lanes scan alone takes 25 min 47 s, past the 1,200 s the run allows each command.

The change

Part Change
Capture limit skill_usage.py DEFAULT_CLAUDE_TIMEOUT = 300 (five times the capture at the seven-day load peak); daily_skill_usage.py --claude-timeout SECONDS (1 to 3600) hands a given value to the capture only
Clone test on_reviewed_main: git merge-base --is-ancestor HEAD origin/main; a feature branch, a commit of the clone's own and a clone with no origin/main are refused with a fixed message, in the collection and in --render-units (a unit that would fail every day is never rendered)
Two halves --half claude runs the capture and child-usage.mjs --lanes-sweep; --half codex runs the host report's Codex scan and skill_usage.py --lanes. A half's measured holds only its own keys (Claude: host, claude; Codex: codex) and the exit status stands for them
Records skill-invoke-rate-claude-latest.json and skill-invoke-rate-codex-latest.json; after its own record a half refreshes the combined skill-invoke-rate-latest.json that the Fleet page reads (--combined-out, --other-record): the older of the two stamps, each half's measured keys, a missing, unreadable or foreign record is an absent half. A failed refresh never changes the half's exit status. The Fleet reader is unchanged and a test feeds the merge through it
Limits per command the capture 360 s (its own limit plus a minute), the Claude sweep 600 s, the host report's Codex scan 600 s, the Codex lanes scan 3,120 s (twice its measured wall: the command center's rule). TimeoutStartSec of each unit exceeds the sum of its half's limits by a minute (17 min and 63 min); a test pins both the sum and a margin of at most two minutes
Schedule skill-invoke-rate-claude.timer 09:30 UTC and skill-invoke-rate-codex.timer 10:00 UTC (OnCalendar with the literal UTC), Persistent=true, RandomizedDelaySec=5m, Nice=10: inside the quietest span of the host's load by hour of day (08:00 to 13:00 UTC; the series is sparse, so the hour is provisional)
Units skill-invoke-rate-{claude,codex}.{service,timer} replace the never-installed skill-invoke-rate.{service,timer}; since review round 2 each service is confined to its record directory (below)

Measurements (transient user units, systemd-run --user --wait, one command at a time, 1 minute load 12 to 22 on 64 CPUs)

Half Command Wall CPU Peak memory
Claude /skill-doctor capture 19.9 s 30.4 s 1.5 GB
Claude child-usage.mjs --lanes-sweep (7 days) 2 min 17.9 s 2 min 39.7 s 1.8 GB
Codex host report's Codex scan (3,234 rollout files) 1 min 10.5 s 1 min 5.2 s 2.8 GB
Codex skill_usage.py --lanes 25 min 47.1 s 29 min 18.1 s 1.8 GB
both, end to end the units rendered from a detached worktree of origin/main carrying this change Claude 2 min 43.9 s, Codex 28 min 3.6 s 3 min 16.0 s, 31 min 33.8 s 2.1 GB, 3.4 GB

Both halves exited 0; the combined record was measured true for host, codex and claude, carried both halves' stamps and was mode 0600.

SOTA sources

  • anthropics/claude-code@3deb8736, CHANGELOG.md L2439: /skill-doctor "show which loaded skills go unused and what they cost in context"; the installed client is 2.1.296, whose -p /skill-doctor capture is the measured command (0 turns, 0 USD every run).
  • systemd/systemd@e846bbad04d87e58ba9c3e30d8a1087141691715 (the mirror's development head: its meson.version reads 263~devel and the mirror has no v259.5 ref; each behaviour cited was read again in the installed systemd 259.5 manuals): man/systemd.service.xml L682-L689 (TimeoutStartSec=: a Type=oneshot service has no start timeout unless one is set), man/systemd.time.xml L254-L256 (a calendar event may name the timezone UTC), man/systemd.timer.xml L190 (OnCalendar=), L265 (RandomizedDelaySec=), L384 (Persistent=), man/systemd.exec.xml L1356 (Nice=), L1497 (ProtectSystem=: strict makes the whole hierarchy read-only), L1526 (ProtectHome=), L1809 (ReadWritePaths=), L1919 (PrivateTmp=); man/system-or-user-ns.xml L11-L14 (in a user instance PrivateUsers= is implicit and needs unprivileged user namespaces).
  • util-linux/util-linux@5305e6c70b27 sys-utils/flock.1.adoc L78 (a lock is dropped when the file is closed) and L84 (flock(2) locks belong to the open file description); python/cpython@cbc944f4bc59 Doc/library/fcntl.rst L176-L183 (fcntl.flock, an OSError on failure): the refresh lock.
  • git/git@67ad42147a7acc2af6074753ebd03d904476118f, Documentation/git-merge-base.adoc L55: --is-ancestor exits 0 when the first commit is an ancestor of the second, 1 when not, and another status for an error.
  • systemd/systemd-stable@387a14a7b67b (v255.4, the runner's systemd; Ubuntu's 255.4-1ubuntu8.17 is a build of it), read for round 3: src/core/namespace.c L2488-L2497 (unshare(CLONE_NEWNS) refused with a privilege or support error returns -ENOANO), src/core/exec-invoke.c L3212-L3237 (the caller proceeds: "assuming containerized execution and ignoring"), L2849-L2888 (insist_on_sandboxing: only a different root, temporary file systems, mount images, a dynamic user, extension images or a bind mount that is not onto itself make it mandatory), L4595-L4609 (a user namespace that cannot be set up fails the unit with EXIT_USER only when PrivateUsers= was set), src/shared/exit-status.h L30, L47, L56 (200; USER 217; NAMESPACE 226). The development head e846bbad has the same logic (exec-invoke.c L4083-L4105 and L6060-L6088, namespace.c L3098-L3107) and man/systemd.exec.xml L1476 says "many of these sandboxing features are gracefully turned off on systems where the underlying security mechanism is not available"; the installed 259.5 manual has the sentence and the status table. actions/runner-images@2c421200c9e6: image 20261004.327.1, kernel 6.17.0-1022-azure, systemd 255.4-1ubuntu8.17. python/cpython@cbc944f4bc59 Doc/library/json.rst L385-L387: json.loads gives an array as a list and an object as a dict.
  • systemd/systemd-stable@387a14a7b67b, read again for round 4 (what the directives leave behind): man/systemd.exec.xml L1381-L1382 (ProtectSystem=strict: "the entire file system hierarchy is mounted read-only") and L1630-L1632 (a path a unit may only read: "writing will be refused even if the usual file access controls would permit this"), src/core/namespace.c make_read_only L1708-L1754 (the flag is set at L1720-L1721: "we only turn on the MS_RDONLY flag here, we never turn it off", L1744); the development head e846bbad has the same two manual passages (man/systemd.exec.xml L1503-L1504 and L1823-L1825). The kernel's answer to a write on a read-only file system is EROFS, and EACCES is the usual file access controls: the installed open(2) manual (man-pages 6.19, 2026-02-08), the EROFS and EACCES entries of ERRORS.
  • systemd/systemd-stable@387a14a7b67b, read again for round 5 (the manager enforces; route B): src/core/exec-invoke.c L2849-L2888 (insist_on_sandboxing(): TemporaryFileSystem= is a member, ProtectSystem=, ProtectHome=, ReadWritePaths= and PrivateTmp= are not), L3212-L3237 (the caller continues when setup_namespace answers -ENOANO, unless it insists), L4595-L4609 (PrivateUsers=true that cannot be set up: EXIT_USER, 217) and L4655-L4664 (apply_mount_namespace failing: EXIT_NAMESPACE, 226); src/core/namespace.c L1284-L1293 (mount_tmpfs creates the mount point and ignores that result, then mounts), L1708-L1754 and L1736-L1741 (make_read_only and the recursion over the submounts that exist), L1992-L2007 (the deny list of the unit's own mount entries, which the recursion skips) and L2488-L2497; man/systemd.exec.xml L1359-L1364 ("gracefully turned off"), L1366-L1370 (user services work "in conjunction with PrivateUsers=true"), L1381-L1384 (/dev, /proc and /sys are exempt from ProtectSystem=strict), L4316-L4317 and L4356-L4358 (the exit codes). All of it is read from the source; what the manager does on a host is measured in receipts/marker-probe-*.json and receipts/design-probe-*.json.
  • The hosted runner's image (ubuntu-24.04 image 20261004.327.1: Ubuntu 24.04.5, systemd 255.4-1ubuntu8.17, Linux 6.17.0-1022-azure, from the image's readme at actions/runner-images@2c421200c9e6) is where the refusal path was exercised: runs 38362998961 and 38365391814 of a scratch branch that is deleted and not in main.
  • This repository: docs/decisions/2026-10-09-claude-review-worker.md (the live clone is a detached worktree of origin/main), docs/decisions/2026-10-10-daily-skill-invoke-rate.md (Measure daily native skill use and test the security-audit trigger cue #970), tools/local-pages/fleet_data.py _skill_usage (the reader of the combined record).

Evidence-class table

Claim Evidence class Command / receipt
A /skill-doctor capture takes 17.2 to 20.1 s on a quiet host and 57.7 to 61.9 s at three runnable threads per CPU; the old 30 s limit could not hold native_proven the table of docs/decisions/2026-10-12-skill-invoke-rate-timer.md; 13 + 2 captures; the failed manual report of 2026-10-11T16:13:49Z at a 1 minute load of 175
Each command of the daily run takes the wall and CPU of the table above; the single unit exceeded its Codex limit native_proven transient user units, 2026-10-12; value-free receipts in evidence/artifacts/skill-invoke-rate-timer-20261012/receipts/ (the README lists what has none)
The two halves run end to end from a detached worktree of origin/main and write a combined record that is measured for all three keys native_proven the units rendered by --render-units, run as transient units with the record paths redirected to a scratch directory, before (28 min 3.6 s for Codex) and after the sandbox (3 min 7.5 s and 33 min 58.1 s, exit 0, receipts/sandboxed-halves-end-to-end.txt)
Through a manager that applies the directives, each service cannot write outside its record directory: a probe started with them is refused writes to the clone, a sibling directory and the home (EROFS), writes its records and writes no bytecode; without them it writes everywhere and creates __pycache__ in the clone; the /skill-doctor capture measures under them native_proven NativeManagerTests under the user manager of the first host; receipts/sandbox-probe.json; the analyzer reads the directives as protection (ServiceSandboxTests, receipts/analyze-security.txt)
A manager can accept the directives and apply none, and then the unit runs unconfined and says nothing (round 3): on a GitHub-hosted runner the probe wrote to the clone, a sibling directory and the home native_proven, source_review the failed shard of the pull request at 68d4f4343 (run 38325995828, job 115064673607; receipts/ci-runner-unconfined-20261013.txt); the systemd source and manual cited above
Under systemd the unit runs in the mount namespace its directives describe or does not start: the marker TemporaryFileSystem=/srv:ro makes the manager insist, so a manager that cannot make the namespace stops the unit with 226/NAMESPACE (the shipped directives without the marker ran unconfined on the hosted runner, systemd 255.4); on the first host (systemd 259.5) the units run in a namespace of their own with every protected write EROFS, and a marker on a missing mount point fails closed on both classes. A run started any other way is not confined, and the program no longer checks native_proven, source_review receipts/marker-probe-first-host.json, receipts/marker-probe-hosted-runner.json (run 38365391814), receipts/design-probe-first-host.json, receipts/design-probe-hosted-runner.json; NativeManagerTests (accepted: enforced or 226 with nothing printed; the reference is a control unit started by the same manager); ClassifierTests, StubManagerTests, RetainedReceiptTests, ObserveTests; the v255.4 source by line range (sources above)
A command line the program cannot use ends in one fixed line and exit 2 that names no value, so replaying a unit rendered by an earlier version (the removed --refuse-writable <home> --refuse-writable <clone>) prints neither the account nor a path local_integration test_replaying_the_retired_probe_option_refuses_without_the_account_or_a_path (eight framings; the replay printed the account twice on da4895b5), test_the_program_has_no_confinement_probe_option
A native test that cannot run fails under GitHub Actions instead of skipping, so a green run there asserts something local_integration UnavailableTests (the pattern of tests/test_token_full_save_evidence.py, which reads GITHUB_ACTIONS on every call); the mutant that skips under Actions is killed
Rendering refuses a clone, an interpreter or node at or below /srv, by its path or the file it resolves to, with a fixed message that names no path; /serve, /srvx and a srv below another directory pass local_integration ServiceSandboxTests (test_rendering_refuses_a_path_that_the_marker_hides, the command line's exit 2)
A native report of the wrong shape is unmeasured, never an error or a traceback naming the account; a lanes report of the right kind whose containers have the wrong shape (groups: [], main: []) is unmeasured too, publishes no lane and says why on stderr, where in round 3 it published measured.claude: true with no lane; the stricter check does not turn the real daily record into not measured: both validators accept the real lanes reports captured on the first host on 2026-10-12 (a 39 MB Codex report and a 198 MB Claude sweep output) local_integration, native_proven NativeReportShapeTests (30 malformed shapes; 9 container shapes and the valid empty control; the command line from a copy of the tool under the home directory, with --combined-out: the own and the combined record say not measured, no traceback, no account name or home path on either stream); claude_lanes_usable and codex_lanes_usable run on the two real reports (outside the suite: the reports are not committed)
The clone test accepts a clone on main and a detached worktree of origin/main, and refuses a feature branch, a clone's own commit and a clone with no origin/main local_integration ReviewedMainCloneTests (a bare repository and a clone, real git)
A half's measured keys, the merge of two halves, the Fleet reader's view of it, the per-command limits, the unit budgets and the schedule local_integration HalvesTests, CaptureTimeoutTests, DailyProjectionTests (tests/test_skill_usage_daily.py, 28 tests)
The tests fail before the change local_integration round 1: 28 tests give FAILED (errors=23) on d8f7e2051 and pass; round 2: the 41 tests give FAILED (failures=25, errors=9) on the sources of 6c4bbd0db and pass here; the 34 mutants of the split are killed again on the new tests and the 26 of round 2 are killed; round 3: the 59 tests give FAILED (failures=11, errors=39) on the sources of 68d4f4343, in 20 tests, and the 35 mutants of round 3 are killed (receipts/fail-before-round3.txt, receipts/mutants-*.txt); round 4: the 65 tests give FAILED (failures=28, errors=8) on the sources of 9caafa601, in 9 tests, among them the stand-in manager's two-root regression (AssertionError: 0 != 3: the process got past the check), and the 24 mutants of round 4 are killed (receipts/fail-before-round4.txt, receipts/mutants-round4-24.txt); round 5: the 83 tests give FAILED (failures=6, errors=19) on the sources of 322fe463d, in 13 tests (the units without the marker, the program without the layout refusal and with the probe option, the native and stand-in runs that need them), and the 45 mutants of round 5 are killed (receipts/fail-before-round5.txt, receipts/mutants-round5-45.txt); the classifier lives in the test module, so its evidence is the mutants; the scope-locked round: the four cases of the two reads fail on da4895b5 (0 of the Codex lanes on the Fleet page; 'fails_closed' != 'fail' for the marker comparison on a manager that refuses PrivateUsers=true with 217/USER; the account's home path in stderr when the retired option is replayed; a contradictory framing of the payload's output classified as enforced), the lane-order and replay tests fail on its sources, and the 19 mutants of the round are killed (receipts/fail-before-round6.txt, receipts/mutants-round6-19.txt)
The report carries a last-used value only from the closed set the client prints; the clients print never, today, 1 day, N days and -N days native_proven (the three installed clients, one host; the set); local_integration (the tests) receipts/native-last-used-spellings.txt (42 printed rows kept), LastUsedBoundaryTests, receipts/fail-before-round11.txt (5 of 6 fail on 8e93415fe), receipts/mutants-round11-37.txt (37 of 37 killed)

Local commands run

$ python3 -m unittest tests.test_skill_usage_daily
  on d8f7e2051 with the first head's test file: Ran 28 tests, FAILED (errors=23); at the round-2 head: Ran 41 tests, OK (the 41 give FAILED (failures=25, errors=9) on the sources of 6c4bbd0db); at the round-3 head: Ran 59 tests, OK; at this head: Ran 65 tests, OK in 4.658 s
$ the same file against the sources of 68d4f4343  ->  Ran 59 tests, FAILED (failures=11, errors=39), in 20 tests
$ the round-4 file against the sources of 9caafa601  ->  Ran 65 tests, FAILED (failures=28, errors=8), in 9 tests (the stand-in manager's two-root regression: AssertionError: 0 != 3)
$ python3 -E -s -B <mutation scripts over tools/skill-usage/daily_skill_usage.py, skill_usage.py and the unit templates>  ->  34 of 34 mutants of the split killed on the round-2 tests (the first run of round 2 left one survivor, a combined record offered as a half, now covered) and 26 of 26 of round 2; round 3: 35 of 35 (the first run left 2: a container read through the host's codex state that the claude half never reaches, and a mistyped anchor); round 4: 24 of 24
$ python3 -m unittest <the modules that name a changed file, one process each>  ->  round 1: 7 modules, 351 tests, OK (skipped=10); round 2: 62 modules, 4,083 tests, 61 OK (skipped=34); not OK: test_token_e2e_grader; round 3: 54 modules, 3,751 tests, 52 OK (skipped=20); the two others: tests.test_token_e2e_grader (the same failure on the sources of `68d4f4343`: a test clone hard-links across devices) and tests.test_workflow_hardening (one failure in the sweep, which ran three modules at a time; the module passes alone and in three parallel runs); round 4: 54 modules, 3,757 tests, 53 exit 0 (46 end with the unittest status OK, skipped=20 among them; seven print other output after the verdict, so their status line is not the last line and their skips are not counted); the one other: tests.test_token_e2e_grader, the same failure on the sources of `9caafa601` (a test clone hard-links across devices; reproduced alone on those sources). The sweep ran on the tree before a line-range correction of a citation in a comment, a docstring and the record (`L1708-L1751` to `L1708-L1754`); the unit module was run again at the commit: Ran 65 tests, OK
$ python3 scripts/validate.py  ->  exit 0 (11,877 files); python3 scripts/evidence_manifest.py --check  ->  exit 0; git merge-tree --write-tree origin/main HEAD  ->  clean against main `c63c83a82`
$ claude_lanes_usable(<the Claude sweep output of 2026-10-12>) and codex_lanes_usable(<the Codex lanes report of 2026-10-12>), the real reports of the first host  ->  True and True
$ python3 -m unittest tests.test_token_e2e_grader.F27c_KeysOnRealContent.test_keys_at_the_pinned_revision on the sources of 9caafa601  ->  FAILED (failures=1): git clone failed: Invalid cross-device link (the sweep's one failure, not caused by this change)
$ systemd-analyze --user verify <the four rendered units>  ->  exit 0 (inside the test, no unit installed)
round 5 (route B; the lines below are at da4895b5):
$ python3 -m unittest tests.test_skill_usage_daily  ->  Ran 83 tests, OK (3.1 s); the same file against the sources of 322fe463d  ->  FAILED (failures=6, errors=19), in 13 tests (receipts/fail-before-round5.txt; corrected in the scope-locked round: the first count, errors=20 in 14 tests, included a test that errored in the harness for lack of node on its PATH)
$ python3 -E -s -B <mutation script over tools/skill-usage/daily_skill_usage.py, the unit templates and the test module's classifier, GITHUB_ACTIONS=true>  ->  45 of 45 killed (the first run left 3 survivors: a control that lacks a target, a start without a namespace, an unfinished run that printed a start; each now has a test)
$ <the marker probe through the user manager of each host class>  ->  first host (systemd 259.5): enforced x3, missing mount point 226; hosted runner (systemd 255.4): shipped directives unconfined (fail), both units and the missing mount point 226 (receipts/marker-probe-*.json)
$ <the two rendered units as transient user units from a scratch live clone, with the marker>  ->  Claude half 151 s and Codex half 1,482 s, both exit 0 (status=0/SUCCESS), records mode 0600 and measured for all three keys, the clone untouched and 0 __pycache__ directories before the halves and after them (receipts/sandboxed-halves-end-to-end-round5.txt; the first run counted 1 directory after the halves and its driver rendered the units without -B, so the driver was changed to render without writing bytecode and to count before the halves, and run again: 0 before, 0 after)
$ python3 -m unittest <the modules that name a changed file, one process each>  ->  66 modules, 4,406 tests, 64 exit 0; the two others fail for reasons outside this change: tests.test_token_e2e_grader (test_keys_at_the_pinned_revision: a test clone hard-links across devices) and 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; this change does not touch the hook); tests.test_skill_usage_daily 83 OK, tests.test_runtime_worker_openhands_push_gate 127 OK (62 s, no advisory delta needed), tests.test_upstream_surface_watch 116 OK, tests.test_freeze_snapshot 152 OK
$ python3 scripts/validate.py  ->  exit 0 (11,884 files hashed); python3 scripts/evidence_manifest.py --check  ->  passed; git merge-tree --write-tree origin/main HEAD  ->  clean against main d6056831b; the privacy scan of the lines added since 322fe463d: 0 account, host, home-path, e-mail, non-ASCII or token hits
the scope-locked round (the lines below are at e181c3ff):
$ python3 -m unittest tests.test_skill_usage_daily  ->  Ran 87 tests, OK; the cases of the two reads on da4895b5 (r6_repro.py)  ->  finding 1: the Fleet page shows 0 of the Codex lanes (200 of 567 lanes, the Claude half holding 566); finding 2: AssertionError: 'fails_closed' != 'fail'; finding 3: exit 2 with the account name twice and the home path twice in stderr; finding 4: ('enforced', ''); the same cases on this head pass (receipts/fail-before-round6.txt)
$ python3 -E -s -B <mutation script over tools/skill-usage/daily_skill_usage.py and the test module's marker comparison, stand-in managers and reader of the payload's output, GITHUB_ACTIONS=true, idle-class>  ->  19 of 19 killed (one stand-in condition that no test could tell apart was simplified)
$ python3 -m unittest <the modules that name a changed file, one process each, idle-class>  ->  66 modules, 4,410 tests, 64 exit 0; the two others fail for reasons outside this change: tests.test_token_e2e_grader (test_keys_at_the_pinned_revision: a test clone hard-links across devices) and 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; this change does not touch the hook); tests.test_skill_usage_daily 87 OK, tests.test_runtime_worker_openhands_push_gate 127 OK, tests.test_upstream_surface_watch 116 OK, tests.test_freeze_snapshot 152 OK
$ python3 scripts/validate.py  ->  exit 0 (11,886 files hashed); python3 scripts/evidence_manifest.py --check  ->  passed; git merge-tree --write-tree origin/main HEAD  ->  clean against main 3f1a921dd; the privacy scan of the lines added since da4895b5: 0 account, host, home-path, e-mail, non-ASCII or token hits
round 7 (the lines below are at the round-7 head):
$ python3 -m unittest tests.test_skill_usage_daily  ->  Ran 91 tests, OK; the cases of the local re-review and of Astra's read on c56121e4 (r7_fail_before.py): the analyzer test in a caller environment without XDG_RUNTIME_DIR fails with the manager's message on c56121e4's module and passes here; the citation test fails 10 subtests on c56121e4's helper and record; the two report-decoding tests fail (3 subtests; 2 errors) on c56121e4's program; the reviewer's command gives rc 1 and a traceback there and rc 2 and the fixed line here (receipts/fail-before-round7.txt)
$ python3 -E -s -B <mutation script over tools/skill-usage/daily_skill_usage.py, the test module and the record, GITHUB_ACTIONS=true, idle-class>  ->  12 of 12 killed
$ python3 -m unittest <the modules that name a changed file, one process each, idle-class>  ->  55 modules, 3,853 tests, 54 exit 0; the one other fails for a reason outside this change: 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_skill_usage_daily 91 OK (under GITHUB_ACTIONS=true), tests.test_runtime_worker_openhands_push_gate 127 OK (no advisory delta needed), tests.test_adoption_docs_consistency 39 OK, tests.test_validate 86 OK, tests.test_evidence_manifest 5 OK
$ python3 scripts/validate.py  ->  exit 0 (11,916 files hashed); python3 scripts/evidence_manifest.py --check  ->  passed; git merge-tree --write-tree origin/main HEAD  ->  clean against main 993cc890b (exit status read directly); the privacy scan of the lines added since c56121e4: 0 account, host, home-path, e-mail, non-ASCII or token hits (gitleaks on the commit: no leaks)
round 11 (the lines below are at the round-11 head):
$ GITHUB_ACTIONS=true python3 -m unittest tests.test_skill_usage.LastUsedBoundaryTests  ->  Ran 6 tests, OK; on 8e93415fe with the module copied in: 5 of 6 fail or raise (receipts/fail-before-round11.txt)
$ python3 -E -s -B <mutation script over tools/skill-usage/skill_usage.py, the minimal tree, GITHUB_ACTIONS=true, idle-class>  ->  37 of 37 killed
$ <the native /skill-doctor of 2.1.294, 2.1.295 and 2.1.296 over a synthetic usage store, the module's argv, a dead API address>  ->  exit 0, total_cost_usd 0, num_turns 0 on each; 14 rows each; 42 of 42 kept by the parser
$ python3 scripts/validate.py  ->  exit 0 (11,925 files hashed); python3 scripts/evidence_manifest.py --check  ->  passed; git merge-tree --write-tree origin/main HEAD  ->  exit status 0, main 329985de1
round 12 (the lines below are at the round-12 head):
$ python3 -m unittest tests.test_skill_usage.InstalledTimePublication  ->  Ran 4 tests, OK; on 8ec8ebc37 with the module copied in: 3 of 4 fail, the valid-time control passes (receipts/fail-before-round12.txt)
$ python3 -I -B REVIEW-PRIVACY-REGRESSION.py  # the fix10 verdict file's regression, unchanged, its tree linked to each head  ->  rc 1, [1, 0, 1] and [0, 0, 1] at 8ec8ebc37; rc 0 here
$ python3 -I <mutation script over the fix in tools/skill-usage/skill_usage.py, idle-class>  ->  5 of 5 killed (receipts/mutants-round12-5.txt)
$ python3 scripts/validate.py  ->  exit 0 (12,054 files hashed); python3 scripts/evidence_manifest.py --check  ->  passed; git merge-tree --write-tree origin/main HEAD  ->  exit status 0, main 8c11dac09

Decision record

docs/decisions/2026-10-12-skill-invoke-rate-timer.md carries the measurements, the decision, the alternatives (attach the live clone to main, one unit with a 90-minute timeout, a second clone, refresh the clone in the unit), 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 changed.
  • New/changed workflows declare top-level permissions: {} and grant each job only what it needs. None changed.
  • No secrets are printed, logged or committed; no new required secret was added without a documented owner. The records hold skill names and counts only; the publish path refuses a destination inside a git work tree, the units run with UMask=0077 and, since round 2, with a read-only file system except the record directory.
  • No new paid hosting, subscription or billing surface was introduced. The capture costs 0 USD (0 turns).
  • Peer-owned untracked files and worktrees were preserved (not deleted, moved or overwritten).

Host steps for the command center (nothing is installed by this PR)

  1. Create the record directory first: mkdir -m 0700 -p ~/.local/state/native-agent-stack/coordination/command-center/skills. Each service has ReadWritePaths= on it, so a missing directory fails the unit at start; a manager that cannot create the user namespace fails it too (PrivateUsers=true, status 217/USER); a manager that cannot create the mount namespace stops it as well (status 226/NAMESPACE: the marker TemporaryFileSystem=/srv:ro makes the manager insist, round 5; without it the manager started the unit without the directives and said nothing, round 3), so the unit shows as failed and never runs unconfined. The clone, the interpreter and node must be outside /srv, which the marker hides (rendering refuses them). If units of an earlier head were installed, render and install them again: the marker is new and --refuse-writable is gone.
  2. Render for the live clone: python3 tools/skill-usage/daily_skill_usage.py --render-units <review dir> --repository ~/code/native-agent-stack-live --python <python> --node <node> (refused unless the clone is at a commit of origin/main); verify with systemd-analyze --user verify; install the four files under ~/.config/systemd/user, systemctl --user daemon-reload, systemctl --user enable --now skill-invoke-rate-claude.timer skill-invoke-rate-codex.timer.
  3. The audit's settling rule stands for the Claude half: two consecutive daily records with measured.host true (in skill-invoke-rate-claude-latest.json, or the combined skill-invoke-rate-latest.json) before 2026-10-25.
  4. The combined file is what Fleet reads; until both halves have run once it is measured only for the half that has.

Review round 2 (one fast-forward commit on 6c4bbd0db): the first full read and the Claude tests-only check

The first full read of 6c4bbd0db (three P1, four P3) and the Claude tests-only check of the same head (PASS_WITH_P3); each finding was reproduced and has a test that fails on the sources of 6c4bbd0db:

Finding Fix Test
P1 both services could write outside their record directory, the live clone included (UMask= limits a mode, not a place; ordinary Python start-up wrote __pycache__ into the clone) ProtectSystem=strict, ProtectHome=read-only, ReadWritePaths= the record directory (the one directory written: each half's record, the combined record and its lock), PrivateTmp=true, PYTHONDONTWRITEBYTECODE=1; measured as in the evidence table ServiceSandboxTests, ServiceSandboxRuntimeTests
P1 a valid JSON record of the other half with another shape ended in an AttributeError, a traceback with the home path and exit 1 after the half's own record was written half_record takes a record only when every field the merge reads has its type, the merge copies typed values only, and the refresh never raises (a fixed message) OtherRecordTests (11 shapes through the CLI, the combined record offered as a half, the error boundary)
P1 the Git fixture's identity [email protected] (the fixture)
P3 both timers are Persistent=true, so a catch-up can overlap the two refreshes and publish an older read over a newer one the read of the other record and the publication of the combined one happen under an flock in the record directory, after the half has published its own record RefreshLockTests (a refresh waits and then reads the record the other half published; gives up after 60 s; the lock is dropped when its holder fails; no lock file inside a worktree)
P3 the systemd source label and the stale base metadata e846bbad is the mirror's development head (meson.version 263~devel), not v259.5: relabelled, each behaviour read again in the installed 259.5 manuals; the base is 703d588c5 (the record, this description)
P3 the measurements had no receipts evidence/artifacts/skill-invoke-rate-timer-20261012/ (value-free captures, the sandbox probe, the analyzer rows, the mutant runs); its README lists the claims that still have none (5 of the 13 quiet captures, the Prometheus load table) (the artifact)

Not changed: the hostname lines that already stand in manifests/evidence.json (none is in this change's added lines).

Review round 3 (one fast-forward commit on 68d4f4343): the continuous integration of that head and Astra's delta read, one new P1 and one P2

Superseded in part by review round 5: the inside check that this round added (--refuse-writable, require_confined) was removed there.

The first runtime test failed on a GitHub-hosted runner (run 38325995828, job 115064673607): the manager accepted the directives, started the probe, and the probe wrote everywhere. Astra's delta read of the same head (2026-10-13T01:21Z) found the same claim false from the source (blocker 1, P2) and a new reproduced P1. The command center ruled that the CI finding is round 3 (a new, reproduced, in-scope P1 reopens a head whoever finds it) and put the P1 in the same commit. Each row has tests that fail on the sources of 68d4f4343:

Finding Fix Test
CI and blocker 1 (P2): decision 7 and the unit comments said a host that cannot create the namespaces fails the unit with 226/NAMESPACE and never runs it unconfined. The source says the opposite: a mount namespace that cannot be created is skipped without a word unless the unit also uses a different root, temporary file systems, mount images, a dynamic user, extension images or a non-identity bind mount (systemd-stable@387a14a7b67b exec-invoke.c L3212-L3237, namespace.c L2488-L2497; the manual: "gracefully turned off") the program asks the file system before any work: --refuse-writable DIR creates a file in each DIR and exits 2 with a fixed message if it can; both units pass the home directory and the clone as their first options; PrivateUsers=true makes a user namespace that cannot be created fail the unit (217/USER; an explicit request fails, an implicit one is ignored, and both map the same way, so it changes nothing else); the claims are corrected in the unit comments, decision 7, the READMEs and this description; the runtime tests are rebuilt on a stand-in manager that accepts every property and applies none of the sandboxing ones StubManagerTests (3), NativeManagerTests (2), RequireConfinedTests (6), ConfinementOptionTests (3), ServiceSandboxTests
P1: a native report whose claude value is [] raised AttributeError in project_reports; main caught neither it nor the other malformed shapes, and the traceback printed the account's name and home path (the same on the previous head) every container of a native report is read through table() (json.loads gives an array as a list, Doc/library/json.rst L385-L387), so a field of the wrong shape reads as absent and the half is unmeasured, never newly measured; unknown stays unknown (a count of None, not zero); main answers TypeError, AttributeError and KeyError with a fixed line and exit 2 NativeReportShapeTests (30 shapes, the command line from a copy of the tool under the home directory, the fixed diagnostic)

The decision record has a "Review round 3" section and the corrected claims; evidence/artifacts/skill-invoke-rate-timer-20261012/receipts/ has the CI excerpt, the fail-before run, the 35 mutants, the regenerated sandbox probe (this host's manager and the stand-in) and the analyzer rows with PrivateUsers=true. On the runner the class of the manager is printed by the suite (manager class: ...); on this host it applies the directives.

Review round 4 (one fast-forward commit on 9caafa601): Astra's delta read of that head, a new P1 in the confinement check and a P2

Superseded in part by review round 5: the check that this round tightened (EROFS only) was removed there; the lanes-report fix stands.

The command center reopened the head for this round and scope-locked it to the two findings below (a new, reproduced, in-scope P1 reopens a head). The round-3 check was itself new code, and each of its two defects is a case of accepting something other than what the mechanism produces, so the fix starts from what systemd says confinement looks like: the directives set MS_RDONLY on the mounts (namespace.c make_read_only L1708-L1754) and "writing will be refused even if the usual file access controls would permit this" (man/systemd.exec.xml L1630-L1632, v255.4); the kernel's answer to a write there is EROFS, while EACCES and EPERM are the usual file access controls, which refuse on a host whose directives were never applied. Each row has tests that fail on the sources of 9caafa601:

Finding Fix Test (fails before the fix)
P1 (Astra): the check continued on EACCES and EPERM, so an unconfined process passed when the probed directories refused by mode: the reviewer's reproduction has a home and a clone of mode 0500, a writable directory below the home, a manager that applied no directive, and a child that creates a directory and a marker below the home after the check (rc 0, marker present) the check accepts only EROFS (READ_ONLY_REFUSAL); a refusal by EACCES or EPERM is Unverified (a refusal of the run with a fixed message that names no path); a file that could be created is Unconfined; any other answer is a ValueError. The limits are stated where the check is: it probes the top of each directory once, when it runs; it does not show that every path under the home is read-only and is no boundary against a mount or a permission change after it ran; the directives are the boundary StubManagerTests.test_a_permission_refusal_does_not_pass_for_a_process_the_manager_left_unconfined (the reviewer's reproduction under the stand-in manager: exit 3 and no marker), RequireConfinedTests (only EROFS passes; EACCES and EPERM are Unverified; a real mode-0500 directory is Unverified; one directory that is not read-only is enough to refuse), ConfinementOptionTests.test_a_directory_that_only_its_mode_protects_stops_the_run_before_any_work
P2 (Astra): a lanes report of the right kind was a measured success whatever its containers: groups: [] and main: [] published measured.claude: true with no lane; the Codex side the same a lanes report is usable only when the containers the projection reads have their shape (claude_lanes_usable: groups.by_spawn_and_agent_type an object of objects of lane aggregates that hold skill_calls as an object, and main, when present, such an aggregate; codex_lanes_usable: groups an object whose workers, negative_controls and unclassified, when present, and every role and kind aggregate hold skill_md_reads as an object). An unusable report is not measured, publishes no lane, and main prints one fixed line to stderr naming the half; exit 1. A valid empty object is still measured NativeReportShapeTests.test_a_lanes_report_with_a_container_of_the_wrong_shape_is_never_published_as_measured (9 shapes and the valid empty control), test_the_command_line_publishes_a_malformed_lanes_report_as_not_measured_with_a_fixed_reason (the actual command line from a copy of the tool under the home directory, with --combined-out: the own and the combined record say not measured, no traceback, no account name or home path), the 30-shape test with its expected flags corrected

Receipts: receipts/fail-before-round4.txt (the 65 tests against the sources of 9caafa601: 28 failures and 8 errors in 9 tests; the stand-in manager's regression fails on 0 != 3) and receipts/mutants-round4-24.txt (24 of 24 killed); the decision record has a "Review round 4" section and the corrected claims (the tool README and both unit comments say that the check goes on only for EROFS and what it does not show).

Review round 5 (one fast-forward commit on 322fe463d): route B, the manager enforces the confinement and fails closed

Astra's read of 322fe463d found that EROFS is also what a read-only mount that someone else made returns, so the inside check says nothing about who applied the directives. Rounds 3 and 4 each fixed what the check accepts and the next read found another way to satisfy it, so the design record (routes A, an inside probe; B, the manager insists; C, narrow the claim) went to Astra, a third family (advisory) and the command center's Claude read; all three said B and the command center ruled it (ruling-pr1043-route-b-20261013). This is a design change approved through that record, not a sixth fix round; the one commit implements the ruling's five points:

Finding (the ruling's point) Fix Test (fails before the fix) or mutants
Enforcement was judged from the file system and from the test process's namespace; on the first host the manager lives in a namespace of its own (1) both units carry TemporaryFileSystem=/srv:ro, which makes the manager insist; the inside check is removed (require_confined, Unconfined, Unverified, the errno constants, --refuse-writable); classify(control, run) compares the child's mount namespace with a control unit's that the same manager starts with no directive, on both host classes ServiceSandboxTests (marker, no probe option, only the marker among the insisting directives, the comment's content), NativeManagerTests, ClassifierTests, StubManagerTests; 13 tests fail on the sources of 322fe463d
"Fails closed" named one status and no proof that the payload never ran (2) 226/NAMESPACE and 217/USER, each only when the payload printed nothing and the manager's own words, when present, match; any other status is a failure ClassifierTests (both refusals, any sign that the payload ran, the manager's words, every other status); the mutants of each branch
Claims labelled measured had no receipt (3) the marker and the missing mount point are measured on both host classes (receipts/marker-probe-*.json), the alternatives are receipted (receipts/design-probe-*.json), the source behaviour is labelled cited, 217/USER is read from the source and has not been seen RetainedReceiptTests classify the retained observations with the current classifier
A skipped native test was a green one (4) unavailable() skips on a developer's machine and fails under GITHUB_ACTIONS=true (the pattern of tests/test_token_full_save_evidence.py) UnavailableTests; the mutant that skips under Actions
The design record's P3s (5), and the reads' P3s the insist branch was exercised on 255.4 only; the function is exec_invoke at v255.4; "two runs" is two invocations in one job; a /srv layout is refused at rendering; the guarantee and its limits (outside systemd, privileged mount changes, an explicit bind) are written in the record and the unit comments (the record, the unit comments)

Receipts: receipts/marker-probe-first-host.json, receipts/marker-probe-hosted-runner.json, receipts/design-probe-first-host.json, receipts/design-probe-hosted-runner.json, receipts/fail-before-round5.txt, receipts/mutants-round5-45.txt, receipts/sandboxed-halves-end-to-end-round5.txt; the decision record has a "Review round 5" section, and rounds 3 and 4 are marked as superseded in their mechanism. The scratch branch that ran the probe on a hosted runner is deleted.

Review round 6 (one fast-forward commit on da4895b5): the local codex review (two P2) and Astra's read (one P1, five follow-ups)

The local codex review of da4895b5 said "patch is incorrect" (two P2) and the lander gate blocked; Astra's read of the same head is CHANGES_REQUESTED (one P1; follow-ups 2 to 6). The command center ruled one bounded, scope-locked round with no waiver: each finding was reproduced on da4895b5 and has a test or a disposition, nothing else changes:

Finding Fix Test (fails before the fix)
P2 (review), daily_skill_usage.py:306-307: the combined record listed the Claude lanes first and the Fleet reader keeps only the first 200 lanes (tools/local-pages/fleet_data.py _skill_usage, lanes[:200]); the Claude half alone held 564 lanes in the end-to-end run of round 5, so every Codex lane was cut although measured.codex was true. This came in with the split (round 1) merge_halves emits the Codex lanes first, as the single record that the split replaced did (703d588c5, project_reports) HalvesTests.test_the_codex_lanes_come_first_so_the_fleet_readers_cut_of_the_lane_list_keeps_them (fails on the sources of da4895b5)
P2 (review), tests/test_skill_usage_daily.py:1386-1388, and the same case as Astra's follow-up 2 (P3): on a host where PrivateUsers=true is refused with 217/USER the marked units fail closed, the marker comparison was made for that result too, and removing only the marker left the unit failing closed with 217 again, so the native test asserted "ran unconfined" and failed although the services behave as specified marker_comparison makes the comparison only where every marked unit fails closed with 226/NAMESPACE; otherwise the native test says which classes the marked units gave and returns; the stand-in manager gains the modes insist and refuse-user-namespace StubManagerTests.test_the_marker_comparison_is_made_only_where_the_manager_refuses_for_the_mount_namespace (four managers); the test of da4895b5 on the 217/USER stand-in fails with 'fails_closed' != 'fail'
P1 (Astra), daily_skill_usage.py:419: retiring --refuse-writable made argparse print unrecognized arguments: --refuse-writable <home> --refuse-writable <clone> when a unit rendered by an earlier version is replayed, so the account's home path reached stderr (the same command line refused with fixed text at 322fe463d); reproduced here: the account name twice and the home path twice FixedErrorParser.error ends every command line the program cannot use in one fixed line and exit 2, naming no value (python/cpython@cbc944f4bc59 Lib/argparse.py L1925-L1932 builds the message from the unusable values and calls error; L2675-L2686 says an override should exit or raise); the inside probe and its option stay removed test_replaying_the_retired_probe_option_refuses_without_the_account_or_a_path (eight framings; fails on the sources of da4895b5), test_the_program_has_no_confinement_probe_option
P3 (Astra, follow-up 3): the reader of the payload's output took contradictory framing (a malformed line, a start in the manager's namespace, a second start in another namespace and a valid write map classified as enforced: the later start replaced the earlier) observe takes the output only as one start line and one write map; any duplicate, stray line, frame of another kind or write map without a start makes the facts that depended on it missing, and the class is fail ObserveTests.test_output_that_is_not_the_payloads_two_lines_is_a_missing_observation (seven framings and the payload's own two lines with blank lines around them)
P3 (Astra, verification gaps): decision 7 attached the October 12 bytecode observation to the round-5 native tests, which do not reproduce it; the older design-probe rows said "the manager's own namespace" for two sets whose reports did not observe it decision 7 cites the probe of that day and its receipt and says what the present tests hold; the alternatives table says the child shared the starter's namespace and that the manager's own is inferred (the record)
(correction) the round-5 count of the tests that fail on the sources of 322fe463d included a test that errored in the harness for lack of node on its PATH receipts/fail-before-round5.txt regenerated: 13 tests, errors=19, not 14 and errors=20; the record, the README and this description quote the corrected count (the receipt)

Dispositions of the rest of Astra's read, none changing code in this round: item 4 (P2, inherited) the exact-head OSV policy module fails on the expiry of two ignores at .github/osv-scanner.toml lines this pull request does not change; the base renews them and the landing carries it (the command center's incident). Item 5 (P3) the publication, freshness, timeout and fixture limits of rounds 3 and 4 stay at their triggers below. Item 6 (P3) custody: the pinned sources the read lacked are staged for the next brief (UPSTREAM-SRC copies of the stable systemd exec-invoke.c, namespace.c, man/systemd.exec.xml and exit-status.h at 387a14a7b67b, and the runner image readme at 2c421200c9e6, with their sha256 in the author's research record); the private mutation and probe drivers stay in the author's record (the receipts README lists what has none); the 18 hostname lines wait for the registry work. The old payload's negative control (the clone import that leaves no __pycache__) is not reproduced by the native tests; it is a follow-up below.

Receipts: receipts/fail-before-round6.txt, receipts/mutants-round6-19.txt; the decision record has a "Review round 6" section.

Review round 7 (one fast-forward commit on c56121e4a): the local codex re-review (two P2) and Astra's second read (one P1, four follow-ups)

The local codex re-review of c56121e4 said "patch is incorrect" (two P2; the review file is 73e49e8a, sha256 prefix) and Astra's second read (verdict file 38926a40, prefix) is CHANGES_REQUESTED for one P1. The command center ruled one scope-locked commit for the three, with Astra's four P3 in the follow-ups and nothing else changing. c56121e4a merges clean against main 993cc890b (git merge-tree --write-tree, exit status read directly), so this is a plain fast-forward of one commit and no rebase.

Finding Fix Test (fails before the fix)
P2 (review), the offline analyzer test: with no XDG_RUNTIME_DIR in the caller's environment the systemd-analyze security --offline=yes --user call stops at Failed to lookup RuntimeDirectory path: No such device or address; reproduced on the host (systemd 259.5) the call gets its own environment, a private HOME and a private, existing runtime directory, as the native verification test does; the mechanism is cited at systemd/systemd@b3d8fc43e9cb531d958c17ef2cd93b374bc14e8a (tag v259.5): sd-path.c L20-L39 and L244-L245, manager.c L708-L742 and L960, analyze-security.c L2677-L2710, man/sd_path_lookup.xml L186-L194 the analyzer test in the reviewer's environment (HOME and PATH only): fails on the module of c56121e4, passes here; dropping the variable, inheriting the caller's environment and not creating the directory are killed there. Not claimed: the hosted runner's behavior for this case (c56121e4 passed there; its systemd 255.4 was not measured for it)
P2 (review), on_reviewed_main: no pinned Git source for the return codes of git merge-base --is-ancestor in the helper, its tests or the record the pin git/git@67ad42147a7acc2af6074753ebd03d904476118f (tag v2.53.0, the installed Git) and the locations (Documentation/git-merge-base.adoc L55-L58, builtin/merge-base.c L108-L124 and L44-L56, usage.c L82-L87) in the helper's docstring, the tests' class docstring and decision 2 test_the_clone_test_cites_the_pinned_git_source_where_it_relies_on_it (10 subtests fail on the helper and the record of c56121e4); test_the_statuses_the_clone_test_reads_are_the_ones_git_documents measures 0, 1 and 128 on the installed Git and holds at both heads (evidence, not a fail-before)
P1 (Astra), daily_skill_usage.py:451, :473, :261-264: a report nested beyond the JSON decoder's limit raises RecursionError, which is neither a ValueError nor an OSError, so --host-report of a 20,002-byte file ended in a traceback naming the account, and a native command's output of the same shape raised out of collect_half (reproduced: rc 1, empty stdout, the account name in stderr) collect_half catches RecursionError with the other decoding errors (the slot is unmeasured, the other command's report is kept); main catches it with the errors that end in the fixed line (daily skill usage unavailable; inspect the native collector separately, exit 2, no value, no path); cited at python/cpython@cbc944f4bc59639a444dd971c737788ba2283a91, Modules/_json.c L1031-L1046 and Lib/json/decoder.py L360-L363 test_a_report_file_nested_beyond_the_decoders_limit_is_a_fixed_diagnostic_and_names_nothing (three report options; controls: a valid empty report, ordinary malformed text) fails on the program of c56121e4 (3 subtests); test_a_native_output_nested_beyond_the_decoders_limit_leaves_only_its_own_slot_unmeasured (both halves; same controls) raises there (2 errors). The reviewer's command: rc 1 and a traceback on c56121e4, rc 2 and the fixed line here

The cases were reproduced on c56121e4 and pass here (receipts/fail-before-round7.txt); 12 mutants of the round's changes are killed (receipts/mutants-round7-12.txt). The third place a report is decoded, the other half's record in refresh_combined, is not changed: its catch-all already ends a RecursionError there without a traceback or a change to the half's exit status (follow-ups below). Dispositions of the rest of Astra's read, none changing code: item 2 (P3, observation frames out of order and duplicate keys), item 3 (P3, publication, freshness, timeout and fixture limits), item 4 (P3, custody) and item 5 (P3, the hostname lines of the registry) are in the follow-ups below.

Receipts: receipts/fail-before-round7.txt, receipts/mutants-round7-12.txt; the decision record has a "Review round 7" section.

Review round 8 (one fast-forward commit on 0383243e4): the local codex re-review (three P2) and Astra's third read (one P1, four follow-ups); the command center decided the route: close the class

The local codex re-review of 0383243e said "patch is incorrect" (three P2; the review file is b40b5867, sha256 prefix) and Astra's third read (verdict file fix6-pr1043-0383243e4/GPT-VERDICT.md, sha256 7e298e28..., read from the file in round 9; the round-8 text cited e345f8d1..., which the file no longer has) is CHANGES_REQUESTED for one P1: the same fixed-diagnostic boundary, one exception class further (OverflowError, after ValueError and RecursionError). The command center adjudicated the P2 and, under its limit on a further P1 of this class, decided the route: close the class instead of enumerating it, in the one commit with the P2. 0383243e4 merges clean against main ba6afd8d7 (git merge-tree --write-tree, exit status read directly): a plain fast-forward.

Finding Fix Test (fails before the fix)
P1 (Astra), daily_skill_usage.py:452, :478: --now 0001-01-01T00:00:00+01:00 (and 9999-12-31T23:59:59-01:00) is a valid timestamp whose conversion to UTC overflows: datetime.astimezone subtracts the offset and raises OverflowError (python/cpython@cbc944f4bc59639a444dd971c737788ba2283a91 Modules/_datetimemodule.c L6510-L6515 and L805-L808; Lib/_pydatetime.py L2034 and L2230), which the handler of main did not list: the command line ended in a traceback naming the paths (reproduced: rc 1; Astra: the account name three times) the class is closed: main, collect_half and the reader of the other half's record catch Exception with the fixed line (as refresh_combined did); parse_iso reports an out-of-range timestamp as a ValueError with no value in its text (so the age of an installation, --since and --until handle it too); half_record treats a stored time that cannot be read as an invalid record; a stored record nested beyond the decoder's limit is an absent half, as one that is not JSON is (the follow-up of round 7) test_no_input_of_the_command_line_ends_in_a_traceback_or_names_the_account (43 hostile inputs over every option, run from a copy of the program under the home directory), test_a_timestamp_whose_conversion_to_utc_overflows_is_a_fixed_diagnostic_and_a_value_error, test_a_stored_half_whose_time_is_out_of_range_is_an_invalid_record, test_an_exception_nobody_listed_is_the_fixed_diagnostic_at_both_boundaries, test_a_stored_other_record_nested_beyond_the_decoders_limit_is_an_absent_half_not_a_failed_refresh
P2 (review), :313-314: a stored half's lanes were copied whole, so a prompt or a path in a lane or a skill, and counts that are not counts, reached the counts-only combined record lane_record rebuilds lanes and skills from the fields the projection writes test_a_stored_half_cannot_carry_extra_fields_into_the_combined_record, test_a_stored_half_with_extra_fields_publishes_a_counts_only_combined_file
P2 (review), :319: the combined record took window_days from the Claude half, so a Codex run of --window 30 beside a stored Claude half of 7 published 30-day counts as seven-day counts halves of different windows are never merged: the later record stands, the other half is absent, unmeasured test_halves_of_different_windows_are_never_merged_under_one_window (five cases), test_a_refresh_does_not_merge_a_stored_half_of_another_window
P2 (review), :398-399: PrivateTmp=true replaces /tmp and /var/tmp (systemd/systemd@b3d8fc43e9cb531d958c17ef2cd93b374bc14e8a man/systemd.exec.xml L1865-L1870), so a clone, interpreter or node there rendered and the units failed on every run rendering refuses a path at or below them, or one that resolves there, with a fixed message of its own test_rendering_refuses_a_path_that_private_tmp_hides, test_a_path_that_resolves_into_a_hidden_directory_is_hidden_whatever_its_own_name, test_the_command_line_refuses_to_render_for_a_path_that_private_tmp_hides

The cases were reproduced on 0383243e and pass on this head (receipts/fail-before-round8.txt: 14 tests fail or raise there, two of them only because they patch the new layout rule; the reviewer's command gives rc 1 and a traceback there and rc 2 and the fixed line here); 17 of 17 mutants of the round's changes are killed (receipts/mutants-round8-17.txt). skill_usage.py moves by five lines, so the two decision records that cite its line numbers follow (2026-10-04-pwsh7-windows-guidance.md, 2026-10-10-daily-skill-invoke-rate.md), as their tests require. A later PR that edits those lines (V-07 #1074 sits its new helper beside claude_row for that reason) merges clean.

Checks at c23b9ce48 (idle-class, read at this head): python3 scripts/validate.py exit 0 (11,918 files); tests.test_skill_usage_daily 103 OK under GITHUB_ACTIONS=true; the modules that name a changed file, one process each: 67 modules, 63 exit 0 at the first sweep, the four others rerun at this head: test_skill_usage 221 OK (its locator test failed on the five-line shift until the two records followed), test_workflow_hardening 118 OK (a nested gitleaks scan failed once under three concurrent modules and passes alone), and test_token_e2e_grader (a test clone hard-links across devices: the same failure at every earlier head); tests.test_runtime_worker_openhands_push_gate 127 OK (no advisory delta needed); gitleaks on the commit: no leaks; the privacy scan of the lines added since 0383243e: 0 account, host, home-path, e-mail, non-ASCII or token hits.

Review round 9 (one fast-forward commit on c23b9ce48): Astra's fourth read (two P1, three P3); the command center decided the design: close the record by a schema and the collector's diagnostics by fixed text

Astra's fourth read (verdict file fix7-pr1043-c23b9ce48/GPT-VERDICT.md, sha256 8476b655...) is CHANGES_REQUESTED for two P1 of the class of rounds 7 and 8: an input reaches something the tool emits. Neither is a write into a published location (the tool writes only to the unit's one writable private directory, and publish() refuses a destination in a git work tree or the clone), so the command center decided to close the two boundaries structurally, in one commit: (a) one closed-schema check at the one place the record is written, to the standard output and the file alike, and (b) fixed text for every diagnostic of the native collector's command line, each with a test that fails before it. It declined a further restriction of --out and --combined-out: publish()'s refusal of a git work tree is the publication boundary. cb42f789c merges clean against main 77cc7ef42 (merge base 3f1a921dd, merged tree a98e399b4f88: git merge-tree --write-tree, exit status read directly); the branch update itself is a fast-forward of the remote head. (Corrected in round 10: this sentence called the merge a plain fast-forward.)

Finding Fix Test (fails before the fix)
P1 (Astra, 1), daily_skill_usage.py:185, :498, :509-513, :415-423: a scalar of the native report's policy block was copied into the record without the count check, so the record (standard output, or the private file) carried an object where a count belongs (the field that the lane fix of round 8 did not cover) field level: the projection reads that scalar through count() like every other. Structural: the record is written through counts_only at the one place that writes it (render_record, used by publish and by the standard output). Every key must be one of the schema's and every leaf null, a boolean, a non-negative integer (at most 10^12), an ISO timestamp of the program's own shape that exists, a name of the allowed pattern or one of a few fixed words; anything else is null and never copied, an unlisted key is dropped, a measured flag that is not true is false. The projection and the merge keep their own checks; this is the one that does not depend on them ClosedRecordTests (7 tests): every scalar the projection reads (each leaf of the fixtures' native reports, replaced by twelve hostile values, and each object given a key of its own) and every scalar of a stored half in the merge, each through the check and an independent statement of the contract; the schema alone, with the producers' checks out of the path; the policy scalar through both outputs (the reviewer's case); both outputs as the one checked rendering
P1 (Astra, 2), skill_usage.py:97, :2422, :2578: the native collector's --now and --since/--until diagnostics printed the parser's exception text, which holds the argument (python/cpython@cbc944f4bc59639a444dd971c737788ba2283a91 Modules/_datetimemodule.c L5774-L5775) every diagnostic of that command line is fixed text: argparse's own errors (unrecognized arguments, invalid int value) end in one fixed line (FixedErrorParser, as the daily program's; Lib/argparse.py L1925-L1932 and L2675-L2686 at the same pin), the manifest names the class of the error only, --now and --since/--until name the option only, --out no path, the call-ledger refusal no text, and the reason of a refused capture (which is also a field of that program's report) is built from closed vocabularies (superseded in round 10: the character filter that this row first described is replaced) NativeCliDiagnosticTests (3 tests): 22 cases over every option, each with a private-looking value (the marker reaches neither stream and no traceback does), the fixed lines and that --help still works, the reason of a refused capture

The cases were reproduced on c23b9ce48 and pass on this head (receipts/fail-before-round9.txt: ClosedRecordTests 7 tests, 3 failures and 5 errors there; NativeCliDiagnosticTests 3 tests, 16 failures and 11 errors there; 10 tests fail or raise in all; the reviewer's commands give a record with the object in its limit field and invalid --now: Invalid isoformat string: <value> there, and null and invalid --now here); 25 of 25 mutants of the round's changes are killed (receipts/mutants-round9-25.txt; the check absent at the file, at the standard output and in the renderer, each leaf kind let through, the unlisted key kept, the bound of a count, the policy scalar, the parser and each diagnostic naming its value). The new helpers of skill_usage.py are at the end of the module, so no line number cited by another record moves. What the schema does not do: a skill's name is a name by definition, so a name of the allowed pattern that someone chose is written; the contract is that nothing but a name, a count, a time, a flag or a fixed word is, and that a field holding anything else is unknown.

Dispositions of the rest of Astra's read, none changing code: item 3 (P3, the observation reader of the test module) keeps its follow-up below and now also names the nested framing case; item 4 (P3): whole-lane copying and the deep other-half record are corrected (round 8), the remaining limits stand at their triggers (a missing timestamp is accepted, the publication shares the timeout margin, the native fixtures need a writable home); item 5 (P3): the pinned sources of the round-8 citations are staged in the author's record, the older ones and the independent originals of the receipts are not; item 6 (P3): the hostname lines of the registry wait for the registry work. Two corrections of this body: the round-8 citation of the third read's verdict file carried a hash that the file no longer has (now 7e298e28..., read from the file), and the follow-up that listed the expired OSV exceptions is closed (below).

Checks at cb42f789c (idle-class, read at this head; the working tree and the commit are the same tree): python3 scripts/validate.py exit 0 (11,920 files hashed); tests.test_skill_usage_daily 110 OK under GITHUB_ACTIONS=true; the modules that name a changed file, one process each: 67 modules, 4,623 tests, 65 exit 0 at the first run, the two others: test_workflow_hardening failed once (a nested Rust test's 2nd pipe should have enough spare while three modules and validate.py ran at once) and passes alone (118 OK, twice), and test_token_e2e_grader (a test clone hard-links across devices: 624 tests, the same one failure at every earlier head); tests.test_skill_usage 224 OK (skipped 8), tests.test_runtime_worker_openhands_push_gate 127 OK on the committed tree (no advisory delta needed), test_final_stack 75 OK, test_adoption_docs_consistency 39 OK, test_validate 86 OK; gitleaks on the commit: no leaks; the privacy scan of the lines added since c23b9ce48: 0 account, host, home-path, e-mail, non-ASCII or token hits (the nine 64-hex strings are the registry rows of the seven changed files and the two new receipts).

Receipts: receipts/fail-before-round9.txt, receipts/mutants-round9-25.txt; the decision record has a "Review round 9" section.

Review round 10 (one fast-forward commit on cb42f789c): Astra's fifth read (two P1 blockers, six P3); the command center's adjudication: one final commit

Astra's fifth read (verdict file fix8-pr1043-cb42f789c/GPT-VERDICT.md, sha256 8e6e45d6...) is CHANGES_REQUESTED for its blockers 1 and 2, both privacy class and both in the native collector's diagnostic boundary. They are cited by file and number here; their shapes are in that file and its reproductions and are not restated. The command center adjudicated one final commit, each fix with a test that fails first, no rewrite of a pushed head, and any further P2 or P3 a follow-up. The commit merges clean against main 04f64a056 (merge base 3f1a921dd, merged tree 1658147edbb7: git merge-tree --write-tree, exit status read directly).

Finding Fix Test (fails before the fix)
Blocker 1 (verdict file; skill_usage.py:2646, the refusal messages at :195 and :199, the report's claude block): a character filter is not a vocabulary the values a refused capture may carry come from closed sets and the diagnostic is built from them: the subtype one of the words the SDK documents for a result (anthropics/claude-agent-sdk-python@f7529348747235da34af3ed202251228ff31db08 src/claude_agent_sdk/_errors.py:71 and :78-80, types.py:2057-2060; ResultMessage.subtype is an open str, types.py:1343, so any other word is other), a flag true, false or other, a count a finite number that is not a boolean (else other in the diagnostic and null in the report); safe_scalar is removed test_a_refused_capture_carries_only_closed_words_numbers_and_flags and test_a_bare_name_in_any_field_of_a_capture_reaches_no_stream_and_no_file (the four fields through the command line, both streams and the --out file, with the clean and documented-error controls)
Blocker 2 (verdict file; skill_usage.py:2413, :2443, :175): the readers' failures below the argument parser were not all inside the fixed-diagnostic boundary structural: main is the public entry point and catches Exception around the whole run, ending in one fixed line and exit 2 (no exception text, no value, no traceback; SystemExit and the interrupt pass); a capture that is JSON but cannot be decoded is a refused capture, never a successful one, and text that is not JSON keeps the plain-text route (python/cpython@cbc944f4bc59639a444dd971c737788ba2283a91 Modules/_json.c L1031-L1046, Lib/json/decoder.py L360-L363) test_an_exception_below_the_parser_ends_in_the_fixed_line_whatever_it_is_and_holds_its_text_back and test_a_manifest_and_a_capture_that_the_decoder_cannot_read_end_without_a_traceback (both readers, with ordinary malformed JSON and clean controls)

The cases were reproduced on cb42f789c and pass on this head (receipts/fail-before-round10.txt: four tests fail or raise there; the reviewer's commands, as counts, give the account's name in the streams and the file and a traceback there and neither here); 19 of 19 mutants of the round's changes are killed (receipts/mutants-round10-19.txt). The new code is at the end of skill_usage.py (run keeps the lines of the former main), so no line number cited by another record moves. Documented statuses: an argument that argparse refuses, a manifest that cannot be read or decoded and any exception below the parser end in one fixed line and exit 2; a refused capture is reported (claude.refused) with exit 0, as before.

Checks at 8e93415fe (idle-class, read at this head): python3 scripts/validate.py exit 0 (11,922 files); the modules that name a changed file, one process each: 65 modules, 3,864 tests, 64 exit 0 and one that cannot run at this base (tests.test_registry_rows does not exist here: it landed with #1070); tests.test_skill_usage 227 OK (skipped 8), tests.test_skill_usage_daily 110 OK under GITHUB_ACTIONS=true, tests.test_runtime_worker_openhands_push_gate 127 OK on the committed tree (no advisory delta needed), tests.test_skill_correction_claims 5, tests.test_rtk_receipt_locators 6, tests.test_adoption_docs_consistency 39, tests.test_workflow_hardening 118 OK; test_token_e2e_grader and test_claude_usage_metrics were not run in this sweep (the first: a test clone hard-links across devices at every head; the second: prometheus_client is not installed on this host); gitleaks on the commit: no leaks; the privacy scan of the lines added since cb42f789c: 0 account, host, home-path, e-mail, non-ASCII or token hits (7 digests: the registry rows).

Review round 11 (one fast-forward commit on 8e93415fe): Astra's sixth read (one P1 blocker, five P3); the command center's adjudication: one more commit

Astra's sixth read (verdict file fix9-pr1043-8e93415fe/GPT-VERDICT.md, sha256 975cc574...) is CHANGES_REQUESTED for its blocker 1, privacy class, in the native collector's report; the two blockers of the read before it are fixed at that head. It is cited by file and number here; its shape is in that file and its reproduction and is not restated. The command center adjudicated one more commit, the fix with a test that fails first, no rewrite of a pushed head, and items 2 to 6 follow-ups. The commit merges clean against main 329985de1 (merge base 3f1a921dd: git merge-tree --write-tree, exit status read directly); the branch update itself is a fast-forward of 8e93415fe.

Finding Fix Test (fails before the fix)
Blocker 1 (verdict file; skill_usage.py:137 and :645, the report's skills[].claude.last_used): the last-used column of a successful capture is a time/status field of the native table, and the report carried its text as it came the column is one of the spellings the client prints, else unknown: closed_last_used returns it when it is never, today, 1 day, N days (1 to 12 digits, no leading zero), -N days (a use time ahead of the clock) or the older 1 day ago / N days ago of the retained fixture, and None for every other text; the set is the client's own table cell, not a character filter; the parser closes the column when it reads the row (:137) and the report closes it again where it enters the published structure (:645), so a caller that builds a row by hand is held too LastUsedBoundaryTests (6): the set and its near misses; the parser in both capture shapes; the report with hand-built rows; the actual collector in a plain-text and in a JSON capture, through --json and --out, with the clean controls; and the tables the three installed clients printed (the receipt)

Where the set comes from, primary source first: the client's table cell in the installed 2.1.294, 2.1.295 and 2.1.296 (the same text in each binary, read without running it: lastUsed: daysSinceUse === null ? "never" : daysSinceUse === 0 ? "today" : ${daysSinceUse} ${I(daysSinceUse, "day")}``, daysSinceUse = Math.floor((Date.now() - lastUsedAt) / 86400000)), and measured on each of the three at $0 and 0 turns over a synthetic usage store in a scratch home (the module's own argv, a dead loopback API address, a placeholder key; `receipts/native-last-used-spellings.txt`): `never`, `today`, `1 day`, `2 days` up to `1234567890 days`, and `-1 days`, `-3 days`, `-1001 days` for a use time ahead of the clock; none carries `ago`, so `N days ago` stays only as the retained fixture's older spelling. The module keeps all 42 printed rows. Not covered: an absolute time (no source that the client prints one; unknown), older clients (not run; an unlisted spelling reads as unknown, never as text).

The cases were reproduced on 8e93415fe and pass on this head (receipts/fail-before-round11.txt: five of the class's six tests fail or raise there, and the reproduction, as counts, gives the account's name once in the standard output and once in the --out file there and nowhere here; the clean controls hold at both heads); 37 of 37 mutants of the round's change are killed (receipts/mutants-round11-37.txt). The two call sites change in place and the new code follows decode_capture, so no line number cited by another record moves.

Checks at 8ec8ebc37 (idle-class, read at this head): python3 scripts/validate.py exit 0 (11,925 files); python3 scripts/evidence_manifest.py --check passed; the 13 modules that name a changed file, one process each: 1,441 tests, 12 exit 0 and one that fails for a reason outside this change (tests.test_token_e2e_grader, 1 of 624: git clone --local across devices into the scratch tmp, an environment failure that names no changed file); tests.test_skill_usage 233 OK (skipped 8), tests.test_skill_usage_daily 110 OK, tests.test_runtime_worker_openhands_push_gate 127 OK on the committed tree (the commit adds no read: no advisory delta needed); the privacy scan of the 343 added lines: 0 account, host, e-mail or token hits, 2 home-path matches that are the synthetic /home/example of the tests, and the table's own multiplication sign in the receipt; gitleaks on the commit: no leaks.

Review round 12 (one commit on the rebased round 11, d2f96f5be): Astra's seventh read (one P1 blocker, one P2, five P3); the command center's instruction: the fix against that verdict

Astra's seventh read (verdict file fix10-pr1043-8ec8ebc37/GPT-VERDICT.md, sha256 e095d2f3...) is CHANGES_REQUESTED for its blocker 1, privacy class, in the native collector's report; the previous read's blocker 1 is fixed at that head. It is cited by file and number here; its shape is in that file and its reproduction and is not restated. The command center's 06:09Z instruction released the PR into the co-op's queue after this fix. Round 12 is one commit on the rebased round 11 (the rebase below); git merge-tree --write-tree origin/main HEAD exit status 0 against main 8c11dac09.

Finding Fix Test (fails before the fix)
Blocker 1 (verdict file; skill_usage.py:567-568, :619-625, :693, the report's skills[].installed_at): the lock's installedAt reached the report as it came, whatever its text; an unparseable value only made age_days unknown validated_installed_at returns the value only when parse_iso accepts it, else None; load_lock_installed_at keeps only such values, and build_report validates again where the value enters the published structure, so a file-fed and a programmatic caller agree; age_days is derived from the validated value. The skills CLI writes an ISO time (vercel-labs/skills@7407f3893ad4dceab546ac002c3ef806e4000c73 v1.7.0, src/skill-lock.ts L30-L31 and L182-L188, the verdict's pinned source), so no valid value is lost InstalledTimePublication (4): the CLI on both routes (JSON and text) with a synthetic marker in a scratch lock publishes it in no stream and no --out file and reports installed_at and age_days as null; a valid time keeps its value and age; the lock reader drops the marker; the report builder drops it from a programmatic caller. Three fail at 8ec8ebc37 and the fourth, the valid-time control, passes at both

The verdict's own regression (REVIEW-PRIVACY-REGRESSION.py, unchanged, its tree linked to each head) gives the marker counts [1, 0, 1] (JSON: stdout, stderr, file) and [0, 0, 1] (text) at 8ec8ebc37 and passes here (receipts/fail-before-round12.txt); 5 of 5 mutants of the fix are killed (receipts/mutants-round12-5.txt). The fix adds 11 lines ahead of ledger_path_issue, so the pwsh7 decision's citation of that function moves from skill_usage.py:2531-2537 to :2542-2548 (test_the_windows_decision_cites_the_ledger_refusal_lines failed until it moved).

Checks at e217e1ef0 (idle-class, read at this head): python3 scripts/validate.py exit 0 (12,054 files); python3 scripts/evidence_manifest.py --check passed; the 13 modules that name a changed file, one process each under GITHUB_ACTIONS=true: 947 tests, all 13 exit 0 (test_skill_usage 237 OK skipped 8, test_skill_usage_daily 120, test_runtime_worker_openhands_push_gate 127 on the committed tree (the commit adds no read: no advisory delta needed), test_freeze_snapshot 152, test_token_measurement 115 skipped 14, test_validate 86, test_local_pages_fleet_data 38, test_runtime_profiles 32, test_transcript_audit 17, test_skill_routing 7, test_rtk_receipt_locators 6, test_skill_correction_claims 5, test_evidence_manifest 5); gitleaks on the commit: no leaks.

Landing rebase onto 8c11dac09 (2026-10-14), before round 12

The pull request was DIRTY against main on the registry only. It was rebased once with the registry-only script: each commit is applied to main's registry and the branch's files are re-registered. The two rows of the deleted skill-invoke-rate.{service,timer} templates, which main still carries, are dropped in the round-12 commit (the 2026-10-13 rebase dropped them in the first commit).

  • Per file (git patch-id --verbatim of the -U0 --binary diff against each side's own base, 3f1a921dd..8ec8ebc37 against origin/main..d2f96f5be): 54 of 55 files have the same patch; the one other is manifests/evidence.json, whose rows now hash main's tree.
  • Registry rows: 54 PR-owned rows, 0 missing at the new head; one carries another hash outside round 12, tests/test_skill_usage_daily.py, whose file main also changed (same patch, new base); 0 rows of a deleted file remain; 0 of main's rows are missing.
  • python3 scripts/validate.py exit 0 at the round-12 head (12,054 files). The fix10 verdict's item 2 (that diagnostic, model currency of claude-fable-5-1) came with the old base: it fails at the round-12 commit on 8ec8ebc37 and does not occur on main's tree: validate exit 0 here and on Session relaunch after an unplanned WSL restart: a roster timer and a boot oneshot on the systemd user manager #1116's branch, which is main plus new files (the cause on main was not read).

Landing rebase onto 3f1a921dd (2026-10-13), after the scope-locked round

The scope-locked round was pushed as a fast-forward of da4895b51 (e181c3ff2); the pull request then went DIRTY on manifests/evidence.json against main as it had moved (the registry is the only conflicting file). It was rebased once, with the owning tools and no hand edit of a derived file, under the standing rule for a registry-only conflict: each commit is applied to main's registry and the rows that commit changed are re-registered with host_receipts.register_file; the two rows of the deleted skill-invoke-rate.{service,timer} templates are dropped in the first commit.

  • Per file (git patch-id --verbatim of the -U0 --binary diff against each side's own merge base): 43 of 43 files have the same patch, manifests/evidence.json included.
  • Registry rows (registry_rows_check.py): 40 PR-owned rows at the old head, 0 missing at the new one, 0 with another hash.
  • Checks on the rebased head (idle-class): python3 scripts/validate.py exit 0 (11,914 files); the modules of the PR (test_skill_usage_daily 87, test_runtime_worker_openhands_push_gate 127, test_evidence_manifest, test_upstream_surface_watch, test_adoption_docs_consistency, test_validate, test_workflow_security_coverage, test_credential_tools, test_credential_status, test_host_baseline_probe, test_local_pages_fleet_regressions, test_secret_path_guard (which passes now that main carries the guard of Credential inventory: github-review-app key file and a --from-file custody mode #1057), test_final_stack) OK; test_workflow_hardening failed once inside a nested gitleaks scan while three modules ran at a time and passes alone (118 OK); git merge-tree --write-tree origin/main HEAD clean (exit status read directly); identities all the no-reply one; the privacy scan of the added lines: 0 hits.

Follow-ups

  • From the round-3 read (P3, not changed in this round): a lane dictionary is copied whole (corrected in round 8: lane_record) and a measured half without a timestamp is accepted (daily_skill_usage.py half_record, merge_halves; trigger: extending record validation: project lane fields through an allowlist and decide whether a measured half without a timestamp is absent); the refresh lock uses the whole of the service timeout margin (960 + 60 s and 3,720 + 60 s; trigger: exercising catch-up under near-limit collection and lock contention: budget publication separately); the no-reply identity change has no assertion of its own (a literal correction, inspected); the captures that have no receipt and the mutation drivers (trigger: the next measurement; the README lists what has none); the 18 hostname lines that already stand in manifests/evidence.json (trigger: a separately authorized registry change); the Git-dependent modules were not run on the merged tree in a genuine worktree (trigger: the co-op has the merged tree).
  • From the round-4 read (not changed in this round, each with its trigger): the empty --refuse-writable list vanished with the option in round 5; the lane-record and freshness limits of round 3 stand; the stand-in tests build their fixtures under the home directory and skip where it is not writable (trigger: portability work on the harness: use a writable fixture root for the stand-in tests and keep the native-home test separate); the check that probed the top of each directory is removed.
  • From the round-5 reads (each with its trigger): keep a real namespace-denied regression environment (when the hosted runner's image, kernel or AppArmor policy changes so that its manager makes the namespace, NativeManagerTests prints namespace-denied branch exercised: no and nothing exercises the refusal: add a container or a machine with user namespaces restricted and recheck the pinned source; Astra P3-4); a program or unit started outside systemd is unconfined and no longer checks (any new caller of daily_skill_usage.py outside the shipped units); a new supported host class (its native run is the check); a unit change that adds a bind or a read-write path (a new hole to justify); the third family's P3s on the probe script and on the marker's portability edges are stated in the record (reasoned from the source, not measured).
  • From Astra's read of da4895b5 (also in the record): restore a native import control (the clone import that leaves no __pycache__, held by the payload of round 4) when the runtime harness is next extended; the third family's P3s and the namespace-denied environment stand as in round 5.
  • From Astra's second read of c56121e4 (also in the record): the observation reader of the test module takes the frames as independent collections, so a write map before the start and a start that holds ns twice classify as enforced, and a framing nested beyond the decoder's limit raises RecursionError in the reader instead of being an incomplete observation (synthetic framings, no unsafe service); when it is next extended it checks the order of the frames, classifies a decoder failure as an incomplete observation and rejects duplicate keys with object_pairs_hook, with the clean control retained. Custody (P3 4): the open(2) page and the independent originals of the published receipts are not staged and the private drivers stay in the author's record; the next measurement supplies them. The 18 hostname lines of the registry stand as before.
  • Corrected in round 8 (it was a follow-up here): the other half's record in refresh_combined that was nested beyond the decoder's limit ended in the catch-all; every record that cannot be read is now an absent half. ServiceSandboxTests cites the systemd.exec(5) locations of round 1 at systemd/systemd@e846bbad, a commit of main, beside "systemd 259.5 installed" (tag v259.5 is b3d8fc43e9cb); re-pin the four locations at the next edit of that docstring.
  • Resolved on main (it was inherited here): the two oauthlib exceptions ended with the OpenHands recipe's relock onto oauthlib 4.0.0 (Relock OpenHands onto oauthlib 4.0.0 and delete the expired oauthlib ignores #1060); .github/osv-scanner.toml at main 77cc7ef42 lists no entry (L17: "No entry as of 2026-10-13 (Relock OpenHands onto oauthlib 4.0.0 and delete the expired oauthlib ignores #1060)").
  • From Astra's fourth read of c23b9ce48 (P3, also in the record; each with its trigger): the observation reader of the test module also raises on a framing nested beyond the decoder's limit (one reader change with the frame-order and duplicate-key items above); a measured half without a timestamp is still accepted, the publication shares the service's timeout margin and the native fixtures need a writable home (trigger: the next extension of record validation or of the harness); the pinned sources of the round-8 citations are staged in the author's record, the older ones and the independent originals of the receipts are not (the next measurement supplies them); the 18 hostname lines of the registry stand as before.
  • From Astra's fifth read of cb42f789c (items 3 to 8 of the verdict file, also in the record; each with its trigger, none changing code in round 10): (3, P3) the observation reader of the test module accepts frames out of order and duplicate keys and raises on a framing nested beyond the decoder's limit: the follow-up above, at its trigger; (4, P3) the closed schema's time leaf accepts spellings that the independent contract of the tests does not (a date without a time, a time without a zone): normalize the accepted times to the program's spelling or make the schema and the contract agree, with those two cases added, at the next change of the schema; (5, P3) missing timestamps, the publication's share of the timeout margin and the native fixtures' home dependence stay at their recorded triggers; (6, P3) custody: the cited sanitized captures and source references of the older rows and a distinct pinned reference for the round-9 schema are not in the packet (the next measurement supplies them); (7, P3 hygiene) the 18 inherited hostname lines of the registry wait for the separately authorized registry cleanup; (8, P3) the body's description of the live-base merge is corrected above.
  • From Astra's sixth read of 8e93415fe (items 2 to 6 of the verdict file, also in the record; each with its trigger, none changing code in round 11): (2, P3) the observation reader of the test module accepts frames out of order and duplicate keys and raises on a framing nested beyond the decoder's limit: the follow-up above, at its trigger; (3, P3) the closed schema's time leaf accepts spellings that the independent contract of the tests does not: the follow-up of round 10, at its trigger; (4, P3) missing timestamps, the publication's share of the timeout margin and the native fixtures' home dependence stay at their recorded triggers; (5, P3) custody: the pages, pinned files and private drivers that the packet lacked are supplied at the next measurement; (6, P3 hygiene) the 18 inherited hostname lines of the registry wait for the separately authorized registry cleanup.
  • From Astra's seventh read of 8ec8ebc37 (items 2 to 8 of the verdict file; none changes code in round 12): (2, P2) the publication validator's model-currency diagnostic: it came with the old base and does not occur after the rebase onto 8c11dac09 (validate exit 0 above); (3, P3) the required osv-scanner check: classified at the landing gate from the new head's run and its base-tip run; (4, P3) the observation reader of the test module: the follow-up above, at its trigger; (5, P3) the time leaf's accepted spellings: the follow-up of round 10, at its trigger; (6, P3) missing timestamps, the timeout margin and the native fixtures' home dependence stay at their recorded triggers; (7, P3) custody: supplied at the next measurement; (8, P3 hygiene) the 18 inherited hostname lines of the registry wait for the separately authorized registry cleanup.
  • An incremental Codex scan, reading only the rollouts newer than the last record (the command center's direction): the Codex half would take the day's new files instead of 26 minutes; the 3,120 s limit is then a ceiling, not a working figure.
  • The quiet hours rest on a sparse series (281 five-minute samples over seven days); revise the two OnCalendar lines when a week of samples exists.
  • A Codex lanes scan slower than 52 minutes at three runnable threads per CPU (about 77 minutes by the same ratio) leaves that half unmeasured for the day and the Claude half unaffected.

🤖 Generated with Claude Code

@seathatflowsinourveins seathatflowsinourveins added the lane:foundation Foundation lane: Claude/Codex setup, hosts, memory, RAG, research, workers label Oct 12, 2026
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@seathatflowsinourveins

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@seathatflowsinourveins
seathatflowsinourveins force-pushed the claude/cc-native-practice-skill-record-halves-20261012 branch 2 times, most recently from 1249acf to 6c4bbd0 Compare October 12, 2026 21:02
@seathatflowsinourveins

Copy link
Copy Markdown
Collaborator Author

@codex review

seathatflowsinourveins and others added 12 commits October 14, 2026 09:24
… measured limits, and run it from the live clone

CPA-C-002 of the Claude-family practice audit (claude-practice-audit-claude-20261012, finding G3-01) and the command center's ruling of 2026-10-12: the per-skill invoke record of #970 was not running, and
would not have run as templated. Three things failed, each measured on the host:

- The /skill-doctor capture hit the 30 s default of skill_usage.py (it takes about 19 s on a quiet host and about 60 s at three runnable threads per CPU) and the daily run had no way to give it more. The
  default is now 300 s (DEFAULT_CLAUDE_TIMEOUT) and the daily run takes --claude-timeout.
- The run exited 2 unless `git branch --show-current` printed `main`, and the clone the unit renders for, ~/code/native-agent-stack-live, is a detached worktree of origin/main
  (docs/decisions/2026-10-09-claude-review-worker.md). The test is now "HEAD is a commit of origin/main" (`git merge-base --is-ancestor HEAD origin/main`, git-merge-base.adoc at git/git@67ad42147a), for
  the collection and for --render-units alike: a feature branch, a commit of the clone's own and a clone with no origin/main are refused with a fixed message.
- One run held the Claude and the Codex scans behind one exit status: as a transient user unit it took 23 min 57 s and exited 1, because the Codex lanes scan alone takes 25 min 47 s. The daily run is now two
  halves, `--half claude` (the /skill-doctor capture and the Claude lanes sweep) and `--half codex` (the host report's Codex scan and the lanes report), each its own user unit with its own record
  (skill-invoke-rate-claude-latest.json, skill-invoke-rate-codex-latest.json), `measured` holding only its own keys and the exit status standing for them. After its record, a half refreshes the combined
  skill-invoke-rate-latest.json that the Fleet page reads, merged from its own record and the other half's newest (the older of the two stamps, a missing or foreign record is an absent half). A slow Codex scan
  can no longer leave the Claude half unmeasured, and a timeout marks only its own half.

Limits per command, from transient-unit measurements on the host: the capture 19.9 s (limit 360 s with its margin), the Claude lanes sweep 2 min 18 s (600 s), the host report's Codex scan 1 min 10 s (600 s), the
Codex lanes scan 25 min 47 s (3,120 s, twice its wall, the command center's rule). TimeoutStartSec of each unit exceeds the sum of its half's limits by a minute (17 min and 63 min; a test pins it;
oneshot units have no start timeout unless one is set: systemd/systemd@e846bbad04 man/systemd.service.xml L682-L689). The units run at 09:30 and 10:00 UTC (OnCalendar with the literal UTC timezone,
man/systemd.time.xml L254-L256), inside the quietest span of the host's load by hour (08:00 to 13:00 UTC).

End to end, rendered from a detached worktree of origin/main and run as transient units: the Claude half 2 min 43.9 s (exit 0, host and claude measured), the Codex half 28 min 3.6 s (exit 0, codex measured), the
combined record measured for all three keys, mode 0600.

Fail-first: the 28 tests of tests.test_skill_usage_daily give FAILED (errors=23) on origin/main (d8f7e20; 23 of the 28 tests error there) and pass here; 34 of 34 mutants of the change killed. The unit templates skill-invoke-rate.service and
.timer, which were never installed, are replaced by skill-invoke-rate-{claude,codex}.{service,timer}; installing them is the command center's act. Two line locators that the eight added lines of
skill_usage.py moved are updated, and the evidence rows are written again.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
… record directory, treat a malformed other-half record as an absent half, serialise the combined refresh

The first full read of 6c4bbd0 (three P1, four P3) and the Claude tests-only check of the same head (PASS_WITH_P3). Each finding was reproduced; the round-2 tests fail on the sources of
6c4bbd0 (25 failures and 9 errors of the 41 tests) and 26 of 26 mutants of the changes are killed, with the 34 mutants of the split killed again on the new tests.

- P1 the services could write anywhere (UMask= restricts a mode, not a place; ordinary Python start-up wrote __pycache__ into the live clone). Both services now carry ProtectSystem=strict,
  ProtectHome=read-only, ReadWritePaths= the record directory (the one directory written: each half's record, the combined record and its lock), PrivateTmp=true and
  PYTHONDONTWRITEBYTECODE=1 (systemd.exec(5), systemd 259.5 installed; systemd/systemd@e846bbad man/systemd.exec.xml L1497, L1526, L1809, L1919 and man/system-or-user-ns.xml L11-L14). Measured:
  a probe started with the rendered directives is refused writes to the clone and the home (EROFS), writes its records and no bytecode; the /skill-doctor capture measures under them; both
  rendered units ran end to end from a scratch live clone, exit 0, the clone untouched. Tests: the directives and the record paths of the rendered units, the analyzer's reading of them, and
  the probe under the user manager (ServiceSandboxTests, ServiceSandboxRuntimeTests).
- P1 a valid JSON record of the other half with another shape raised in the merge, printed a traceback with the home path and exited 1 after the own record was written. half_record now
  takes a record only when every field the merge reads has its type, the merge copies counts and the wiring name only when typed, and the refresh never raises (a fixed message).
- P1 the Git fixture's identity is [email protected].
- P3 the two Persistent=true timers can start both units at once, and their refreshes could publish an older read over a newer one: the read of the other record and the publication of the
  combined one now happen under an flock in the record directory (util-linux sys-utils/flock.1.adoc L78, L84; python/cpython Doc/library/fcntl.rst L176-L183).
- P3 the systemd source label: e846bbad is the mirror's development head (meson.version 263~devel), not v259.5; each cited behaviour was read again in the installed 259.5 manuals.
- P3 the measurements have receipts: evidence/artifacts/skill-invoke-rate-timer-20261012/ (value-free captures, the sandbox probe, the analyzer rows, the mutant runs), and its README lists what
  has none. Not changed: the hostname lines that already stand in manifests/evidence.json.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…ystem whether it is confined, the unconfined-host claim is corrected from the systemd source, and a malformed native report is unmeasured, not a traceback

The continuous integration of 68d4f43 (a GitHub-hosted runner) and Astra's delta read of the same head (one new P1, one P2), put in one round by the command center's ruling.

- CI and P2: the first runtime test failed on the runner (ubuntu-24.04 image 20261004.327.1, systemd 255.4-1ubuntu8.17): the manager accepted the rendered directives, started the probe, and the probe wrote to the
  clone, a sibling directory and the home directory. The claim of decision 7 and of the unit comments, that a host which cannot create the namespaces fails the unit with 226/NAMESPACE and never runs it
  unconfined, is false: when unshare(CLONE_NEWNS) fails with a privilege or support error the manager proceeds without the namespace and says nothing, unless the unit also uses a different root, temporary
  file systems, mount images, a dynamic user, extension images or a bind mount that is not onto itself (systemd/systemd-stable@387a14a7b67b v255.4, src/core/namespace.c L2488-L2497 and
  src/core/exec-invoke.c L3212-L3237; the development head and the 259.5 manual say the same, "gracefully turned off"). The program now asks the file system before any work: --refuse-writable DIR creates a
  file in each DIR and exits 2 with a fixed message if it can; both units pass the home directory and the clone as their first options. PrivateUsers=true makes a user namespace that cannot be created fail the
  unit (status 217/USER, exec-invoke.c L4595-L4609), the only case systemd itself can see. The claims are corrected where they stood (the unit comments, decision 7, the README, the artifact README), and
  the runtime tests are rebuilt on a stand-in manager that accepts every property and applies none of the sandboxing ones, which reproduces the runner's manager on any host.
- P1: a native report whose claude value is [] raised AttributeError in project_reports and the traceback printed the account's name and home path (the same on the previous head). Every container of a native
  report is read through table(), so a field of the wrong shape reads as absent and the half is unmeasured (json.loads gives an array as a list: python/cpython@cbc944f4bc59 Doc/library/json.rst L385-L387);
  main answers an error nobody foresaw with a fixed line and exit status 2, never its text or a traceback.

Tests (59): the 19 new tests and the updated unit-content test fail on the sources of 68d4f43 (FAILED failures=11, errors=39). 35 of 35 mutants killed. The other findings (lane-dictionary shapes, the lock's
share of the timeout margin, provenance and custody gaps, hostnames, the merged-tree run) are in the pull request's Follow-ups.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…ts only the answer of a read-only mount, and a lanes report with a container of the wrong shape is not measured

Astra's delta read of 9caafa6 (a new P1 in the confinement check, a P2 in the projection), reopened by the command center's ruling and scope-locked to those two findings. Each defect is a case of accepting
something other than what the mechanism produces; the fix starts from what systemd says confinement looks like.

- P1: require_confined accepted EACCES and EPERM, so an unconfined process passed when the probed directories refused by their mode (the reviewer's reproduction: a home and a clone of mode 0500, a writable
  directory below the home, a manager that applied no directive; the child then wrote a marker below the home). The directives make the file system read-only by setting MS_RDONLY on the mounts
  (systemd/systemd-stable@387a14a7b67b, v255.4: src/core/namespace.c make_read_only L1708-L1754; man/systemd.exec.xml L1381-L1382 and L1630-L1632, "writing will be refused even if the usual file access
  controls would permit this"), and the kernel answers a write there with EROFS (open(2), man-pages 6.19); EACCES is the usual file access controls, which refuse on a host without the directives too.
  The check now goes on only for EROFS. EACCES and EPERM are a new refusal, Unverified (fixed message, no path); a file that could be created is Unconfined; any other answer is a ValueError. The docstring and
  the unit comments state the limits: one probe of the top of each directory when it runs, no boundary against later changes.
- P2: a lanes report of the right kind was a measured success whatever its containers (groups: [] and main: [] published measured.claude: true with no lane; the Codex side the same). A report is usable only
  when the containers the projection reads have their shape (claude_lanes_usable, codex_lanes_usable); an unusable one is not measured, publishes no lane, and main prints one fixed line naming the half
  (exit 1). A valid empty object is still measured.

Tests (65): nine tests fail on the sources of 9caafa6 (FAILED failures=28, errors=8), among them the stand-in manager's two-root regression, which fails on 0 != 3 (the process got past the check). 24 of 24
mutants killed. The four Follow-ups of the read (an empty option list, the lane-record and freshness limits, the stand-in fixtures under the home, the OSV expiry) are in the pull request body.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…nfinement and fails closed (route B), and the inside check is removed

Astra's read of 322fe46 found that EROFS is also what a read-only mount that someone else made returns, so the program's inside check says nothing about who applied the directives. Rounds 3 and 4 had each fixed what
the check accepts and the next read found another way to satisfy it. The design record (A inside probe, B the manager insists, C narrow the claim) was read by Astra, a third family and the command center's Claude
read; all three said B and the command center ruled it. This commit implements the ruling's five points.

- Both units carry TemporaryFileSystem=/srv:ro, a member of systemd's insist_on_sandboxing() (systemd-stable@387a14a7b67b, v255.4, src/core/exec-invoke.c L2849-L2888), so a manager that cannot make the mount
  namespace stops the unit with 226/NAMESPACE (exec_invoke L4655-L4664) instead of starting it without the directives (L3212-L3237); PrivateUsers=true does the same for the user namespace (217/USER, L4595-L4609).
  The program no longer asks the file system anything: require_confined, Unconfined, Unverified, the errno constants and --refuse-writable are removed. Rendering refuses a clone, an interpreter or node at or below /srv
  (LayoutRefused, a fixed message), which the marker hides.
- The native tests judge the manager's own namespace, not the test process's: classify(control, run) compares the child's mount namespace with a control unit started by the same manager with no directive, and has three
  classes (enforced; fails closed with 226 or 217 and nothing printed; fail). A skipped native test fails under GitHub Actions (the pattern of tests/test_token_full_save_evidence.py).
- Measured on both host classes through each one's user manager (the first host: systemd 259.5, enforced; the hosted runner: systemd 255.4, the shipped directives without the marker ran unconfined and with it end in 226,
  runs 38362998961 and 38365391814 of a scratch branch that is deleted): receipts/marker-probe-*.json, with the design round's probe of the alternatives in receipts/design-probe-*.json. 217/USER is read from the source and
  has not been seen.
- The decision record has a Review round 5 section (the source citations by line range, the guarantee and its limits, the alternatives, the follow-ups) and rounds 3 and 4 are marked as superseded in their mechanism.

Tests: 83 in tests.test_skill_usage_daily; on the sources of 322fe46 the same file gives failures=6, errors=20 in 14 tests; 45 of 45 mutants killed (the first run left 3 survivors, each now covered). The two
rendered units ran end to end on the first host with the marker, both exit 0. A namespace-denied regression environment and the third family's P3s are follow-ups in the pull request body.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…view and Astra's read of da4895b: the Codex lanes lead the combined record, the replay of the retired option prints no path, the marker comparison and the payload reader are exact

The local codex review of da4895b said "patch is incorrect" (two P2) and Astra's read of the same head is CHANGES_REQUESTED (one P1, follow-ups 2 to 6); the command center ruled one bounded round with no waiver. Each
finding was reproduced on da4895b (receipts/fail-before-round6.txt) and has a test or a disposition; nothing else changes.

- P2 (review), daily_skill_usage.py:306-307: the combined record listed the Claude lanes first and the Fleet reader keeps only the first 200 (fleet_data.py _skill_usage, lanes[:200]), so with 566 Claude lanes none of the
  Codex lanes reached the page although measured.codex was true. merge_halves emits the Codex lanes first, as the single record that the split replaced did.
- P1 (Astra): retiring --refuse-writable made argparse print "unrecognized arguments: --refuse-writable <home> --refuse-writable <clone>" when a unit rendered by an earlier version is replayed, so the account's home path
  reached stderr. FixedErrorParser.error (argparse's documented hook; python/cpython@cbc944f4bc59 Lib/argparse.py L1925-L1932 and L2675-L2686) ends every unusable command line in one fixed line and exit 2.
- P2 (review) and P3 (Astra): the marker comparison of the native test is made only where every marked unit fails closed with 226/NAMESPACE; on 217/USER the unmarked unit also fails closed and nothing is compared. The
  stand-in manager gains insist and refuse-user-namespace modes, and the 217/217 transition has its test.
- P3 (Astra): observe takes the payload's output only as one start line and one write map; a duplicate, a stray line, a frame of another kind or a write map without a start makes the facts missing (a contradictory framing
  classified as enforced).
- The record: decision 7 cites the probe of 2026-10-12 for its bytecode observation instead of the round-5 native tests, the older design-probe rows no longer say "the manager's own namespace" where the reports did not observe
  it, and the round-5 fail-before count is corrected (13 tests, errors=19, not 14 and 20: one test had errored in the harness for lack of node on its PATH).
- Dispositions, no code: the inherited OSV expiry (the base renews it), the existing publication and fixture limits (their triggers), custody (the pinned sources are staged for the next brief; the hostname lines wait for the
  registry work).

Tests: 87 in tests.test_skill_usage_daily; 19 of 19 mutants killed; the four cases fail on da4895b (the lane-order and replay tests on its sources; the others by reproduction, since the marker comparison and the payload reader
live in the test module).

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…irectory, the clone test cites its pinned Git source, and a report nested beyond the JSON decoder's limit is a fixed diagnostic

The local codex re-review of c56121e said "patch is incorrect" (two P2) and Astra's second read is CHANGES_REQUESTED for one P1; the command center ruled one scope-locked commit for the three, with Astra's four
P3 in the follow-ups. Each was reproduced on c56121e and has a test that fails there (receipts/fail-before-round7.txt).

- tests/test_skill_usage_daily.py (P2): the offline `systemd-analyze security --user` call gets its own environment, a private HOME and an existing private runtime directory, as the native verification test
  does. Without XDG_RUNTIME_DIR the user manager cannot look up its runtime directory and the call stops at "Failed to lookup RuntimeDirectory path: No such device or address" (reproduced with systemd 259.5;
  systemd/systemd@b3d8fc43e9cb, tag v259.5: sd-path.c L20-L39 and L244-L245, manager.c L708-L742, analyze-security.c L2677-L2710, man/sd_path_lookup.xml L186-L194).
- tools/skill-usage/daily_skill_usage.py, tests and the record (P2): the pinned Git source for the return codes of `git merge-base --is-ancestor` that the clone test reads (git/git@67ad42147a7a, tag v2.53.0:
  Documentation/git-merge-base.adoc L55-L58, builtin/merge-base.c L108-L124 and L44-L56, usage.c L82-L87), held by a test, and a test that measures the installed Git at 0, 1 and 128.
- tools/skill-usage/daily_skill_usage.py (P1, Astra): `json.loads` of a report nested beyond the decoder's limit raises RecursionError, which neither handler listed, so `--host-report` of a 20,002-byte file ended in
  a traceback naming the account and a native command's output of the same shape raised out of collect_half. Both handlers now catch it: the command line gives its fixed line and exit 2, the collection leaves the
  slot unmeasured (python/cpython@cbc944f4bc59, Modules/_json.c L1031-L1046, Lib/json/decoder.py L360-L363). The controls (a valid empty report, ordinary malformed text) keep their behavior.
- The record has a review round 7 and three follow-ups, the receipts README two rows and the evidence folder fail-before-round7.txt and mutants-round7-12.txt (12 of 12 killed). Nothing else changes.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…f listing exception classes, an out-of-range timestamp is a ValueError, and the three P2 of the local re-review

The local codex re-review of 0383243 said "patch is incorrect" (three P2) and Astra's third read is CHANGES_REQUESTED for one P1: the same boundary again, one exception class further. The command center adjudicated the
P2 and decided the route for the P1: close the class instead of enumerating it. Each case was reproduced on 0383243 and has a test that fails there (receipts/fail-before-round8.txt).

- tools/skill-usage/skill_usage.py (P1, Astra) parse_iso: a valid timestamp whose conversion to UTC leaves the supported range (0001-01-01T00:00:00+01:00, 9999-12-31T23:59:59-01:00) raised OverflowError, which the
  callers' handlers of ValueError did not cover; it is now the ValueError of any unusable timestamp, with no value in its text (python/cpython@cbc944f4bc59: Modules/_datetimemodule.c L6510-L6515 and L805-L808,
  Lib/_pydatetime.py L2034 and L2230).
- tools/skill-usage/daily_skill_usage.py (the class): main, collect_half and the reader of the other half's record catch Exception with the fixed line (refresh_combined already did); a stored record nested beyond
  the decoder's limit is an absent half, as one that is not JSON is. The corpus test drives 43 hostile inputs through every option and the three report options from a copy of the program under the home directory,
  where a traceback names the account.
- daily_skill_usage.py (P2) lane_record: a stored half's lanes and skills are rebuilt from the fields the projection writes; nothing else crosses into the counts-only combined record.
- daily_skill_usage.py (P2): halves of different windows are never merged under one window_days; the later record stands and the other half is absent, unmeasured.
- daily_skill_usage.py (P2): rendering refuses a clone, an interpreter or a node at or below /tmp or /var/tmp, or one that resolves there, which PrivateTmp=true replaces (systemd/systemd@b3d8fc43e9cb,
  man/systemd.exec.xml L1865-L1870). Two earlier tests whose fixture paths are in a temporary directory patch the rule.
- tools/skill-usage/skill_usage.py moves by five lines: the two decision records that cite its line numbers (pwsh7-windows-guidance, daily-skill-invoke-rate) follow, as their tests require.
- The record has a review round 8, the receipts README two rows and the evidence folder fail-before-round8.txt and mutants-round8-17.txt (17 of 17 killed). Nothing else changes.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…a, and every diagnostic of the native collector's command line is fixed text

Astra's read of c23b9ce is CHANGES_REQUESTED with two P1 (the fix7 verdict file, GPT-VERDICT.md of that read, sha256 8476b655...), and the command center's design decision after it
is to close the two boundaries structurally, in one commit: one closed-schema check where the record is written, and fixed text for every diagnostic of the native collector's command line.
It declined a further restriction of --out and --combined-out: publish()'s refusal of a git work tree is the publication boundary. Each case was reproduced on c23b9ce and has a test that fails
there (receipts/fail-before-round9.txt).

- tools/skill-usage/daily_skill_usage.py (P1 1): a scalar of the native report's policy block was copied into the record without the count check, so the record carried an object holding a prompt
  and a path. The projection reads that scalar through count() like every other, and the record is written through counts_only at the one place that writes it (render_record, used by publish() and
  by the standard output): only the schema's keys, and leaves that are null, a boolean, a count (at most 10^12), an ISO timestamp of the program's own shape, a name of the allowed pattern or a
  fixed word; anything else is null and never copied.
- tools/skill-usage/skill_usage.py (P1 2): --now and --since/--until printed the parser's exception text, which holds the argument (Invalid isoformat string: <value>). Every diagnostic of that
  command line is now fixed text: argparse's own errors end in one fixed line (FixedErrorParser), the manifest names the class of the error only, --now and --since/--until name the option only,
  --out no path, the call-ledger refusal no text, and the reason of a refused capture shows a boolean, a finite number or a short word and otherwise "other". The helpers are at the end of the
  module so that no line number cited by another record moves.
- tests: ClosedRecordTests (every scalar the projection reads and every scalar of a stored half in the merge, through the check and an independent statement of the contract; the schema alone; the
  policy scalar through both outputs; both outputs as one checked rendering) and NativeCliDiagnosticTests (22 cases over every option, the fixed lines and --help, the refused capture's reason).
- 25 of 25 mutants of the round's changes are killed (receipts/mutants-round9-25.txt). The push gate's read inventory is unchanged (the module passes on the committed tree).
- Truth fixes: the record names the lane copy of round 8 as corrected (lane_record) where a follow-up still listed it. Nothing else changes.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…sed vocabularies, and the native collector's public boundary catches whatever a reader or a processing step raises

Astra's read of cb42f78 (the fix8 verdict file, GPT-VERDICT.md of that read, sha256 8e6e45d6...) is CHANGES_REQUESTED for its blockers 1 and 2, both privacy class, and the command center adjudicated
one final commit for the two, each with a test that fails first, no rewrite of a pushed head, and any further P2 or P3 a follow-up. The blockers are cited by file and number; their shapes are in that file
and its reproductions and are not restated here. Each case was reproduced on cb42f78 and has a test that fails there (receipts/fail-before-round10.txt).

- tools/skill-usage/skill_usage.py, blocker 1 (the refusal messages and the report's claude block): a character filter is not a vocabulary. The values of a capture that a diagnostic or the report may carry
  now come from closed sets: a subtype is one of the words the SDK documents for a result (success, error_max_turns, error_during_execution, error_max_budget_usd: anthropics/claude-agent-sdk-python@f7529348
  src/claude_agent_sdk/_errors.py:71 and :78-80, types.py:2057-2060; ResultMessage.subtype is an open str, types.py:1343, so any other word is "other"), a flag is true, false or other, a count is a finite
  number that is not a boolean (otherwise "other" in the diagnostic and null in the report). safe_scalar is removed.
- tools/skill-usage/skill_usage.py, blocker 2 (the readers below the argument parser): the boundary is structural, not another enumerated string. main is the public entry point and catches Exception around
  the whole run (the former body is run, at the same lines): one fixed line and exit 2, with no exception text, no value and no traceback; SystemExit and the interrupt pass. A capture that is JSON but
  cannot be decoded (python/cpython@cbc944f4bc59 Modules/_json.c L1031-L1046; Lib/json/decoder.py L360-L363) is a refused capture, never a successful one, and text that is not JSON keeps the plain-text route.
- tests/test_skill_usage.py: the closed sets, the four capture fields through the command line (both streams and the --out file, with the clean and the documented-error controls), the boundary for any
  exception class below the parser, and both readers with ordinary malformed JSON and clean controls. 19 of 19 mutants are killed (receipts/mutants-round10-19.txt).
- The decision record has a "Review round 10" section, the earlier claim of the character filter is marked superseded, and the verdict's items 3 to 8 are in the follow-ups; the cited line numbers of
  skill_usage.py do not move (the new code is at the end of the module).
- Nothing else changes.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…successful capture is one of the spellings the client prints, else unknown (fix9 verdict file, blocker 1)

Astra's read of 8e93415 (verdict file GPT-VERDICT.md of that read, sha256 975cc574...) is CHANGES_REQUESTED for its blocker 1, of the privacy class, in the native collector's report. The shape of the
input is in that file and its reproduction and is not restated here. The command center ruled one more commit, with a test that fails before it, no rewrite of a pushed head, and items 2 to 6 follow-ups.

- tools/skill-usage/skill_usage.py: closed_last_used and LAST_USED_SPELLING. The column is one of the spellings the client prints, else None (unknown): never, today, 1 day, N days, -N days (a use time ahead of
  the clock) and the older "N day(s) ago" of the retained fixture. The set is the client's own table cell, not a character filter (anthropics/claude-code, the installed 2.1.294, 2.1.295 and 2.1.296: the same
  formatter in each binary, and the same words printed by each at $0 and 0 turns over a synthetic usage store, receipts/native-last-used-spellings.txt). The parser closes the column when it reads a row (:137) and the
  report closes it again where it enters the published structure (:645); both edits are in place and the new code follows decode_capture, so no cited line moves.
- tools/skill-usage/README.md: the privacy boundary names what the report carries from the table.
- tests/test_skill_usage.py: LastUsedBoundaryTests (6): the set and its near misses; the parser in both capture shapes; the report with hand-built rows; the actual collector in a plain-text and in a JSON capture
  through --json and --out, with the clean controls; the tables the three installed clients printed. Five of the six fail or raise on 8e93415 (receipts/fail-before-round11.txt, with the reproduction as counts of
  the account's name per stream); 37 of 37 mutants are killed (receipts/mutants-round11-37.txt; the first run left two, one equivalent and one now killed by a new case).
- docs/decisions/2026-10-12-skill-invoke-rate-timer.md: "Review round 11", the follow-up for items 2 to 6 of the verdict file, an overturn condition; the evidence README: three rows.

Items 2 to 6 of the verdict file are follow-ups, each with its trigger in the record.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…blished only after it parses, at the lock reader and at the report builder (fix10 verdict file, blocker 1)

The lock's installedAt reached skills[].installed_at as it came, whatever its text. validated_installed_at
returns the value only when parse_iso accepts it (the skills CLI writes an ISO time: vercel-labs/skills@7407f389
v1.7.0 src/skill-lock.ts L30-L31, L182-L188), else None; load_lock_installed_at keeps only such values and
build_report validates again, so file-fed and programmatic callers agree and age_days comes from the validated
value. InstalledTimePublication (4 tests): 3 fail at 8ec8ebc, the fourth keeps a valid time; the verdict's
own regression gives [1, 0, 1] and [0, 0, 1] there and passes here; 5 of 5 mutants killed. The pwsh7 decision's
citation of ledger_path_issue moves to skill_usage.py:2542-2548. The registry drops the rows of the two unit
templates this branch deletes (the rebase onto 8c11dac kept main's rows).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@seathatflowsinourveins
seathatflowsinourveins force-pushed the claude/cc-native-practice-skill-record-halves-20261012 branch from 8ec8ebc to e217e1e Compare October 14, 2026 13:35
@seathatflowsinourveins

Copy link
Copy Markdown
Collaborator Author

Parked at e217e1e (2026-10-14 20:17Z). The co-op is recording the command center's ruling. The command center parked this PR under its bounded-rounds rule, after the review of fix round 11 returned a P1.

The P1, by locator only: tools/skill-usage/skill_usage.py:557-567 republishes protected text that the timestamp parser ignored.

What happens now:

  • This PR gets no further fix round, and it stays a draft.
  • It can re-open only as a new, re-scoped PR, on the command center's cue.

@seathatflowsinourveins
seathatflowsinourveins marked this pull request as draft October 14, 2026 20:18

This branch has not been deployed

No deployments
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