Repository navigation
Daily skill record: a Claude and a Codex unit with measured limits, run from the live clone (CPA-C-002) - #1043
seathatflowsinourveins wants to merge 12 commits into
Conversation
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
@codex review |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
1249acf to
6c4bbd0
Compare
|
@codex review |
e181c3f to
c56121e
Compare
… 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]>
8ec8ebc to
e217e1e
Compare
|
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: What happens now:
|
Scope
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-doctorcapture gets a 300 s limit and the daily run a--claude-timeout. Installing the units is the command center's act after this lands.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 from703d588c5660d698a87b622efe0e3bd8c22f7e29, main after Add isolated Pi and OpenCode trial profiles and native invocation receipts #1033; the first head was validated ond8f7e2051, main after Apply verified event scopes to CI secret scans #949).lane:foundationtools/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.8c11dac09on 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 were615198b67,cba514600,0aa343171,3efec45ed,0ebbcdb98,c56121e4a,0383243e4,c23b9ce48,cb42f789c,8e93415feand8ec8ebc37, and before the 2026-10-13 rebase the first six were6c4bbd0db,68d4f4343,9caafa601,322fe463d,da4895b51ande181c3ff2; the reads and the rounds below name the heads they read.What was wrong, each measured on the first host (2026-10-12)
daily_skill_usage.pyranskill_usage.py --run-skill-doctorwith no timeout argument, so the 30 s default applied; the last manual host report (2026-10-11T16:13:49Z) refused its Claude half withTimeoutExpired. 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.git branch --show-currentprintedmain; the unit renders for~/code/native-agent-stack-live, a detached worktree oforigin/mainkept by the review worker's tick (docs/decisions/2026-10-09-claude-review-worker.md), whose branch name is empty.The change
skill_usage.pyDEFAULT_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 onlyon_reviewed_main:git merge-base --is-ancestor HEAD origin/main; a feature branch, a commit of the clone's own and a clone with noorigin/mainare refused with a fixed message, in the collection and in--render-units(a unit that would fail every day is never rendered)--half clauderuns the capture andchild-usage.mjs --lanes-sweep;--half codexruns the host report's Codex scan andskill_usage.py --lanes. A half'smeasuredholds only its own keys (Claude:host,claude; Codex:codex) and the exit status stands for themskill-invoke-rate-claude-latest.jsonandskill-invoke-rate-codex-latest.json; after its own record a half refreshes the combinedskill-invoke-rate-latest.jsonthat 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 itTimeoutStartSecof 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 minutesskill-invoke-rate-claude.timer09:30 UTC andskill-invoke-rate-codex.timer10:00 UTC (OnCalendarwith the literalUTC),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)skill-invoke-rate-{claude,codex}.{service,timer}replace the never-installedskill-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)/skill-doctorcapturechild-usage.mjs --lanes-sweep(7 days)skill_usage.py --lanesorigin/maincarrying this changeBoth halves exited 0; the combined record was
measuredtrue forhost,codexandclaude, carried both halves' stamps and was mode 0600.SOTA sources
CHANGELOG.mdL2439:/skill-doctor"show which loaded skills go unused and what they cost in context"; the installed client is 2.1.296, whose-p /skill-doctorcapture is the measured command (0 turns, 0 USD every run).meson.versionreads263~develand the mirror has no v259.5 ref; each behaviour cited was read again in the installed systemd 259.5 manuals):man/systemd.service.xmlL682-L689 (TimeoutStartSec=: aType=oneshotservice has no start timeout unless one is set),man/systemd.time.xmlL254-L256 (a calendar event may name the timezoneUTC),man/systemd.timer.xmlL190 (OnCalendar=), L265 (RandomizedDelaySec=), L384 (Persistent=),man/systemd.exec.xmlL1356 (Nice=), L1497 (ProtectSystem=:strictmakes the whole hierarchy read-only), L1526 (ProtectHome=), L1809 (ReadWritePaths=), L1919 (PrivateTmp=);man/system-or-user-ns.xmlL11-L14 (in a user instancePrivateUsers=is implicit and needs unprivileged user namespaces).sys-utils/flock.1.adocL78 (a lock is dropped when the file is closed) and L84 (flock(2) locks belong to the open file description); python/cpython@cbc944f4bc59Doc/library/fcntl.rstL176-L183 (fcntl.flock, anOSErroron failure): the refresh lock.Documentation/git-merge-base.adocL55:--is-ancestorexits 0 when the first commit is an ancestor of the second, 1 when not, and another status for an error.src/core/namespace.cL2488-L2497 (unshare(CLONE_NEWNS)refused with a privilege or support error returns-ENOANO),src/core/exec-invoke.cL3212-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 withEXIT_USERonly whenPrivateUsers=was set),src/shared/exit-status.hL30, L47, L56 (200; USER 217; NAMESPACE 226). The development heade846bbadhas the same logic (exec-invoke.cL4083-L4105 and L6060-L6088,namespace.cL3098-L3107) andman/systemd.exec.xmlL1476 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@cbc944f4bc59Doc/library/json.rstL385-L387:json.loadsgives an array as a list and an object as a dict.man/systemd.exec.xmlL1381-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.cmake_read_onlyL1708-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 heade846bbadhas the same two manual passages (man/systemd.exec.xmlL1503-L1504 and L1823-L1825). The kernel's answer to a write on a read-only file system isEROFS, andEACCESis the usual file access controls: the installedopen(2)manual (man-pages 6.19, 2026-02-08), theEROFSandEACCESentries of ERRORS.src/core/exec-invoke.cL2849-L2888 (insist_on_sandboxing():TemporaryFileSystem=is a member,ProtectSystem=,ProtectHome=,ReadWritePaths=andPrivateTmp=are not), L3212-L3237 (the caller continues whensetup_namespaceanswers-ENOANO, unless it insists), L4595-L4609 (PrivateUsers=truethat cannot be set up:EXIT_USER, 217) and L4655-L4664 (apply_mount_namespacefailing:EXIT_NAMESPACE, 226);src/core/namespace.cL1284-L1293 (mount_tmpfscreates the mount point and ignores that result, then mounts), L1708-L1754 and L1736-L1741 (make_read_onlyand 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.xmlL1359-L1364 ("gracefully turned off"), L1366-L1370 (user services work "in conjunction withPrivateUsers=true"), L1381-L1384 (/dev,/procand/sysare exempt fromProtectSystem=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 inreceipts/marker-probe-*.jsonandreceipts/design-probe-*.json.actions/runner-images@2c421200c9e6) is where the refusal path was exercised: runs 38362998961 and 38365391814 of a scratch branch that is deleted and not inmain.docs/decisions/2026-10-09-claude-review-worker.md(the live clone is a detached worktree oforigin/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
/skill-doctorcapture 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 holddocs/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 175evidence/artifacts/skill-invoke-rate-timer-20261012/receipts/(the README lists what has none)origin/mainand write a combined record that is measured for all three keys--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)EROFS), writes its records and writes no bytecode; without them it writes everywhere and creates__pycache__in the clone; the/skill-doctorcapture measures under themNativeManagerTestsunder the user manager of the first host;receipts/sandbox-probe.json; the analyzer reads the directives as protection (ServiceSandboxTests,receipts/analyze-security.txt)68d4f4343(run 38325995828, job 115064673607;receipts/ci-runner-unconfined-20261013.txt); the systemd source and manual cited aboveTemporaryFileSystem=/srv:romakes 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 writeEROFS, 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 checksreceipts/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)--refuse-writable <home> --refuse-writable <clone>) prints neither the account nor a pathtest_replaying_the_retired_probe_option_refuses_without_the_account_or_a_path(eight framings; the replay printed the account twice onda4895b5),test_the_program_has_no_confinement_probe_optionUnavailableTests(the pattern oftests/test_token_full_save_evidence.py, which readsGITHUB_ACTIONSon every call); the mutant that skips under Actions is killed/srv, by its path or the file it resolves to, with a fixed message that names no path;/serve,/srvxand asrvbelow another directory passServiceSandboxTests(test_rendering_refuses_a_path_that_the_marker_hides, the command line's exit 2)groups: [],main: []) is unmeasured too, publishes no lane and says why on stderr, where in round 3 it publishedmeasured.claude: truewith 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)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_usableandcodex_lanes_usablerun on the two real reports (outside the suite: the reports are not committed)mainand a detached worktree oforigin/main, and refuses a feature branch, a clone's own commit and a clone with noorigin/mainReviewedMainCloneTests(a bare repository and a clone, realgit)HalvesTests,CaptureTimeoutTests,DailyProjectionTests(tests/test_skill_usage_daily.py, 28 tests)FAILED (errors=23)ond8f7e2051and pass; round 2: the 41 tests giveFAILED (failures=25, errors=9)on the sources of6c4bbd0dband 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 giveFAILED (failures=11, errors=39)on the sources of68d4f4343, 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 giveFAILED (failures=28, errors=8)on the sources of9caafa601, 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 giveFAILED (failures=6, errors=19)on the sources of322fe463d, 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 onda4895b5(0 of the Codex lanes on the Fleet page;'fails_closed' != 'fail'for the marker comparison on a manager that refusesPrivateUsers=truewith 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)never,today,1 day,N daysand-N daysreceipts/native-last-used-spellings.txt(42 printed rows kept),LastUsedBoundaryTests,receipts/fail-before-round11.txt(5 of 6 fail on8e93415fe),receipts/mutants-round11-37.txt(37 of 37 killed)Local commands run
Decision record
docs/decisions/2026-10-12-skill-invoke-rate-timer.mdcarries the measurements, the decision, the alternatives (attach the live clone tomain, 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
permissions: {}and grant each job only what it needs. None changed.UMask=0077and, since round 2, with a read-only file system except the record directory.Host steps for the command center (nothing is installed by this PR)
mkdir -m 0700 -p ~/.local/state/native-agent-stack/coordination/command-center/skills. Each service hasReadWritePaths=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 markerTemporaryFileSystem=/srv:romakes 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-writableis gone.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 oforigin/main); verify withsystemd-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.measured.hosttrue (inskill-invoke-rate-claude-latest.json, or the combinedskill-invoke-rate-latest.json) before 2026-10-25.Review round 2 (one fast-forward commit on
6c4bbd0db): the first full read and the Claude tests-only checkThe 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 of6c4bbd0db: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 tableServiceSandboxTests,ServiceSandboxRuntimeTestsAttributeError, a traceback with the home path and exit 1 after the half's own record was writtenhalf_recordtakes 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)[email protected]Persistent=true, so a catch-up can overlap the two refreshes and publish an older read over a newer oneflockin the record directory, after the half has published its own recordRefreshLockTests(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)e846bbadis the mirror's development head (meson.version263~devel), not v259.5: relabelled, each behaviour read again in the installed 259.5 manuals; the base is703d588c5evidence/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)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 P2Superseded 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:systemd-stable@387a14a7b67bexec-invoke.cL3212-L3237,namespace.cL2488-L2497; the manual: "gracefully turned off")--refuse-writable DIRcreates 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=truemakes 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 onesStubManagerTests(3),NativeManagerTests(2),RequireConfinedTests(6),ConfinementOptionTests(3),ServiceSandboxTestsclaudevalue is[]raisedAttributeErrorinproject_reports;maincaught neither it nor the other malformed shapes, and the traceback printed the account's name and home path (the same on the previous head)table()(json.loadsgives an array as a list,Doc/library/json.rstL385-L387), so a field of the wrong shape reads as absent and the half is unmeasured, never newly measured; unknown stays unknown (a count ofNone, not zero);mainanswersTypeError,AttributeErrorandKeyErrorwith a fixed line and exit 2NativeReportShapeTests(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 withPrivateUsers=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 P2Superseded in part by review round 5: the check that this round tightened (
EROFSonly) 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_RDONLYon the mounts (namespace.cmake_read_onlyL1708-L1754) and "writing will be refused even if the usual file access controls would permit this" (man/systemd.exec.xmlL1630-L1632, v255.4); the kernel's answer to a write there isEROFS, whileEACCESandEPERMare the usual file access controls, which refuse on a host whose directives were never applied. Each row has tests that fail on the sources of9caafa601:EACCESandEPERM, 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)EROFS(READ_ONLY_REFUSAL); a refusal byEACCESorEPERMisUnverified(a refusal of the run with a fixed message that names no path); a file that could be created isUnconfined; any other answer is aValueError. 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 boundaryStubManagerTests.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(onlyEROFSpasses;EACCESandEPERMareUnverified; a real mode-0500 directory isUnverified; 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_workgroups: []andmain: []publishedmeasured.claude: truewith no lane; the Codex side the sameclaude_lanes_usable:groups.by_spawn_and_agent_typean object of objects of lane aggregates that holdskill_callsas an object, andmain, when present, such an aggregate;codex_lanes_usable:groupsan object whoseworkers,negative_controlsandunclassified, when present, and every role and kind aggregate holdskill_md_readsas an object). An unusable report is not measured, publishes no lane, andmainprints one fixed line to stderr naming the half; exit 1. A valid empty object is still measuredNativeReportShapeTests.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 correctedReceipts:
receipts/fail-before-round4.txt(the 65 tests against the sources of9caafa601: 28 failures and 8 errors in 9 tests; the stand-in manager's regression fails on0 != 3) andreceipts/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 forEROFSand what it does not show).Review round 5 (one fast-forward commit on
322fe463d): route B, the manager enforces the confinement and fails closedAstra's read of
322fe463dfound thatEROFSis 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: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 classesServiceSandboxTests(marker, no probe option, only the marker among the insisting directives, the comment's content),NativeManagerTests,ClassifierTests,StubManagerTests; 13 tests fail on the sources of322fe463dClassifierTests(both refusals, any sign that the payload ran, the manager's words, every other status); the mutants of each branchreceipts/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 seenRetainedReceiptTestsclassify the retained observations with the current classifierunavailable()skips on a developer's machine and fails underGITHUB_ACTIONS=true(the pattern oftests/test_token_full_save_evidence.py)UnavailableTests; the mutant that skips under Actionsexec_invokeat v255.4; "two runs" is two invocations in one job; a/srvlayout 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 commentsReceipts:
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
da4895b5said "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 onda4895b5and has a test or a disposition, nothing else changes: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 althoughmeasured.codexwas true. This came in with the split (round 1)merge_halvesemits 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 ofda4895b5)tests/test_skill_usage_daily.py:1386-1388, and the same case as Astra's follow-up 2 (P3): on a host wherePrivateUsers=trueis 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 specifiedmarker_comparisonmakes 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 modesinsistandrefuse-user-namespaceStubManagerTests.test_the_marker_comparison_is_made_only_where_the_manager_refuses_for_the_mount_namespace(four managers); the test ofda4895b5on the 217/USER stand-in fails with'fails_closed' != 'fail'daily_skill_usage.py:419: retiring--refuse-writablemade argparse printunrecognized 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 at322fe463d); reproduced here: the account name twice and the home path twiceFixedErrorParser.errorends every command line the program cannot use in one fixed line and exit 2, naming no value (python/cpython@cbc944f4bc59Lib/argparse.pyL1925-L1932 builds the message from the unusable values and callserror; L2675-L2686 says an override should exit or raise); the inside probe and its option stay removedtest_replaying_the_retired_probe_option_refuses_without_the_account_or_a_path(eight framings; fails on the sources ofda4895b5),test_the_program_has_no_confinement_probe_optionobservetakes 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 isfailObserveTests.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)322fe463dincluded a test that errored in the harness for lack ofnodeon its PATHreceipts/fail-before-round5.txtregenerated: 13 tests, errors=19, not 14 and errors=20; the record, the README and this description quote the corrected countDispositions 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.tomllines 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-SRCcopies of the stable systemdexec-invoke.c,namespace.c,man/systemd.exec.xmlandexit-status.hat387a14a7b67b, and the runner image readme at2c421200c9e6, 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
c56121e4said "patch is incorrect" (two P2; the review file is73e49e8a, sha256 prefix) and Astra's second read (verdict file38926a40, 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.c56121e4amerges clean against main993cc890b(git merge-tree --write-tree, exit status read directly), so this is a plain fast-forward of one commit and no rebase.XDG_RUNTIME_DIRin the caller's environment thesystemd-analyze security --offline=yes --usercall stops atFailed to lookup RuntimeDirectory path: No such device or address; reproduced on the host (systemd 259.5)HOMEand a private, existing runtime directory, as the native verification test does; the mechanism is cited atsystemd/systemd@b3d8fc43e9cb531d958c17ef2cd93b374bc14e8a(tag v259.5):sd-path.cL20-L39 and L244-L245,manager.cL708-L742 and L960,analyze-security.cL2677-L2710,man/sd_path_lookup.xmlL186-L194c56121e4, 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 (c56121e4passed there; its systemd 255.4 was not measured for it)on_reviewed_main: no pinned Git source for the return codes ofgit merge-base --is-ancestorin the helper, its tests or the recordgit/git@67ad42147a7acc2af6074753ebd03d904476118f(tag v2.53.0, the installed Git) and the locations (Documentation/git-merge-base.adocL55-L58,builtin/merge-base.cL108-L124 and L44-L56,usage.cL82-L87) in the helper's docstring, the tests' class docstring and decision 2test_the_clone_test_cites_the_pinned_git_source_where_it_relies_on_it(10 subtests fail on the helper and the record ofc56121e4);test_the_statuses_the_clone_test_reads_are_the_ones_git_documentsmeasures 0, 1 and 128 on the installed Git and holds at both heads (evidence, not a fail-before)daily_skill_usage.py:451,:473,:261-264: a report nested beyond the JSON decoder's limit raisesRecursionError, which is neither aValueErrornor anOSError, so--host-reportof a 20,002-byte file ended in a traceback naming the account, and a native command's output of the same shape raised out ofcollect_half(reproduced: rc 1, empty stdout, the account name in stderr)collect_halfcatchesRecursionErrorwith the other decoding errors (the slot is unmeasured, the other command's report is kept);maincatches 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 atpython/cpython@cbc944f4bc59639a444dd971c737788ba2283a91,Modules/_json.cL1031-L1046 andLib/json/decoder.pyL360-L363test_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 ofc56121e4(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 onc56121e4, rc 2 and the fixed line hereThe cases were reproduced on
c56121e4and 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 inrefresh_combined, is not changed: its catch-all already ends aRecursionErrorthere 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 classThe local codex re-review of
0383243esaid "patch is incorrect" (three P2; the review file isb40b5867, sha256 prefix) and Astra's third read (verdict filefix6-pr1043-0383243e4/GPT-VERDICT.md, sha2567e298e28..., read from the file in round 9; the round-8 text citede345f8d1..., which the file no longer has) is CHANGES_REQUESTED for one P1: the same fixed-diagnostic boundary, one exception class further (OverflowError, afterValueErrorandRecursionError). 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.0383243e4merges clean against mainba6afd8d7(git merge-tree --write-tree, exit status read directly): a plain fast-forward.daily_skill_usage.py:452,:478:--now 0001-01-01T00:00:00+01:00(and9999-12-31T23:59:59-01:00) is a valid timestamp whose conversion to UTC overflows:datetime.astimezonesubtracts the offset and raisesOverflowError(python/cpython@cbc944f4bc59639a444dd971c737788ba2283a91Modules/_datetimemodule.cL6510-L6515 and L805-L808;Lib/_pydatetime.pyL2034 and L2230), which the handler ofmaindid not list: the command line ended in a traceback naming the paths (reproduced: rc 1; Astra: the account name three times)main,collect_halfand the reader of the other half's record catchExceptionwith the fixed line (asrefresh_combineddid);parse_isoreports an out-of-range timestamp as aValueErrorwith no value in its text (so the age of an installation,--sinceand--untilhandle it too);half_recordtreats 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:313-314: a stored half's lanes were copied whole, so apromptor apathin a lane or a skill, and counts that are not counts, reached the counts-only combined recordlane_recordrebuilds lanes and skills from the fields the projection writestest_a_stored_half_cannot_carry_extra_fields_into_the_combined_record,test_a_stored_half_with_extra_fields_publishes_a_counts_only_combined_file:319: the combined record tookwindow_daysfrom the Claude half, so a Codex run of--window 30beside a stored Claude half of 7 published 30-day counts as seven-day countstest_halves_of_different_windows_are_never_merged_under_one_window(five cases),test_a_refresh_does_not_merge_a_stored_half_of_another_window:398-399:PrivateTmp=truereplaces/tmpand/var/tmp(systemd/systemd@b3d8fc43e9cb531d958c17ef2cd93b374bc14e8aman/systemd.exec.xmlL1865-L1870), so a clone, interpreter or node there rendered and the units failed on every runtest_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_hidesThe cases were reproduced on
0383243eand 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.pymoves 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 besideclaude_rowfor that reason) merges clean.Checks at
c23b9ce48(idle-class, read at this head):python3 scripts/validate.pyexit 0 (11,918 files);tests.test_skill_usage_daily103 OK underGITHUB_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_usage221 OK (its locator test failed on the five-line shift until the two records followed),test_workflow_hardening118 OK (a nested gitleaks scan failed once under three concurrent modules and passes alone), andtest_token_e2e_grader(a test clone hard-links across devices: the same failure at every earlier head);tests.test_runtime_worker_openhands_push_gate127 OK (no advisory delta needed); gitleaks on the commit: no leaks; the privacy scan of the lines added since0383243e: 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 textAstra's fourth read (verdict file
fix7-pr1043-c23b9ce48/GPT-VERDICT.md, sha2568476b655...) 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, andpublish()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--outand--combined-out:publish()'s refusal of a git work tree is the publication boundary.cb42f789cmerges clean against main77cc7ef42(merge base3f1a921dd, merged treea98e399b4f88: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.)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)count()like every other. Structural: the record is written throughcounts_onlyat the one place that writes it (render_record, used bypublishand 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, ameasuredflag that is not true is false. The projection and the merge keep their own checks; this is the one that does not depend on themClosedRecordTests(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 renderingskill_usage.py:97,:2422,:2578: the native collector's--nowand--since/--untildiagnostics printed the parser's exception text, which holds the argument (python/cpython@cbc944f4bc59639a444dd971c737788ba2283a91Modules/_datetimemodule.cL5774-L5775)unrecognized arguments,invalid int value) end in one fixed line (FixedErrorParser, as the daily program's;Lib/argparse.pyL1925-L1932 and L2675-L2686 at the same pin), the manifest names the class of the error only,--nowand--since/--untilname the option only,--outno 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--helpstill works, the reason of a refused captureThe cases were reproduced on
c23b9ce48and 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 andinvalid --now: Invalid isoformat string: <value>there, andnullandinvalid --nowhere); 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 ofskill_usage.pyare 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.pyexit 0 (11,920 files hashed);tests.test_skill_usage_daily110 OK underGITHUB_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_hardeningfailed once (a nested Rust test's2nd pipe should have enough sparewhile three modules andvalidate.pyran at once) and passes alone (118 OK, twice), andtest_token_e2e_grader(a test clone hard-links across devices: 624 tests, the same one failure at every earlier head);tests.test_skill_usage224 OK (skipped 8),tests.test_runtime_worker_openhands_push_gate127 OK on the committed tree (no advisory delta needed),test_final_stack75 OK,test_adoption_docs_consistency39 OK,test_validate86 OK; gitleaks on the commit: no leaks; the privacy scan of the lines added sincec23b9ce48: 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 commitAstra's fifth read (verdict file
fix8-pr1043-cb42f789c/GPT-VERDICT.md, sha2568e6e45d6...) 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 main04f64a056(merge base3f1a921dd, merged tree1658147edbb7:git merge-tree --write-tree, exit status read directly).skill_usage.py:2646, the refusal messages at:195and:199, the report'sclaudeblock): a character filter is not a vocabularyanthropics/claude-agent-sdk-python@f7529348747235da34af3ed202251228ff31db08src/claude_agent_sdk/_errors.py:71and:78-80,types.py:2057-2060;ResultMessage.subtypeis an openstr,types.py:1343, so any other word isother), a flagtrue,falseorother, a count a finite number that is not a boolean (elseotherin the diagnostic and null in the report);safe_scalaris removedtest_a_refused_capture_carries_only_closed_words_numbers_and_flagsandtest_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--outfile, with the clean and documented-error controls)skill_usage.py:2413,:2443,:175): the readers' failures below the argument parser were not all inside the fixed-diagnostic boundarymainis the public entry point and catchesExceptionaround the whole run, ending in one fixed line and exit 2 (no exception text, no value, no traceback;SystemExitand 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@cbc944f4bc59639a444dd971c737788ba2283a91Modules/_json.cL1031-L1046,Lib/json/decoder.pyL360-L363)test_an_exception_below_the_parser_ends_in_the_fixed_line_whatever_it_is_and_holds_its_text_backandtest_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
cb42f789cand 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 ofskill_usage.py(runkeeps the lines of the formermain), 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.pyexit 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_rowsdoes not exist here: it landed with #1070);tests.test_skill_usage227 OK (skipped 8),tests.test_skill_usage_daily110 OK underGITHUB_ACTIONS=true,tests.test_runtime_worker_openhands_push_gate127 OK on the committed tree (no advisory delta needed),tests.test_skill_correction_claims5,tests.test_rtk_receipt_locators6,tests.test_adoption_docs_consistency39,tests.test_workflow_hardening118 OK;test_token_e2e_graderandtest_claude_usage_metricswere not run in this sweep (the first: a test clone hard-links across devices at every head; the second:prometheus_clientis not installed on this host); gitleaks on the commit: no leaks; the privacy scan of the lines added sincecb42f789c: 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 commitAstra's sixth read (verdict file
fix9-pr1043-8e93415fe/GPT-VERDICT.md, sha256975cc574...) 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 main329985de1(merge base3f1a921dd:git merge-tree --write-tree, exit status read directly); the branch update itself is a fast-forward of8e93415fe.skill_usage.py:137and:645, the report'sskills[].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 cameclosed_last_usedreturns it when it isnever,today,1 day,N days(1 to 12 digits, no leading zero),-N days(a use time ahead of the clock) or the older1 day ago/N days agoof 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 tooLastUsedBoundaryTests(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--jsonand--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
8e93415feand 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--outfile 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 followsdecode_capture, so no line number cited by another record moves.Checks at
8ec8ebc37(idle-class, read at this head):python3 scripts/validate.pyexit 0 (11,925 files);python3 scripts/evidence_manifest.py --checkpassed; 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 --localacross devices into the scratch tmp, an environment failure that names no changed file);tests.test_skill_usage233 OK (skipped 8),tests.test_skill_usage_daily110 OK,tests.test_runtime_worker_openhands_push_gate127 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/exampleof 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 verdictAstra's seventh read (verdict file
fix10-pr1043-8ec8ebc37/GPT-VERDICT.md, sha256e095d2f3...) 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 HEADexit status 0 against main8c11dac09.skill_usage.py:567-568,:619-625,:693, the report'sskills[].installed_at): the lock'sinstalledAtreached the report as it came, whatever its text; an unparseable value only madeage_daysunknownvalidated_installed_atreturns the value only whenparse_isoaccepts it, else None;load_lock_installed_atkeeps only such values, andbuild_reportvalidates again where the value enters the published structure, so a file-fed and a programmatic caller agree;age_daysis derived from the validated value. The skills CLI writes an ISO time (vercel-labs/skills@7407f3893ad4dceab546ac002c3ef806e4000c73v1.7.0,src/skill-lock.tsL30-L31 and L182-L188, the verdict's pinned source), so no valid value is lostInstalledTimePublication(4): the CLI on both routes (JSON and text) with a synthetic marker in a scratch lock publishes it in no stream and no--outfile and reportsinstalled_atandage_daysas 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 at8ec8ebc37and the fourth, the valid-time control, passes at bothThe 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) at8ec8ebc37and 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 ofledger_path_issue, so the pwsh7 decision's citation of that function moves fromskill_usage.py:2531-2537to:2542-2548(test_the_windows_decision_cites_the_ledger_refusal_linesfailed until it moved).Checks at
e217e1ef0(idle-class, read at this head):python3 scripts/validate.pyexit 0 (12,054 files);python3 scripts/evidence_manifest.py --checkpassed; the 13 modules that name a changed file, one process each underGITHUB_ACTIONS=true: 947 tests, all 13 exit 0 (test_skill_usage237 OK skipped 8,test_skill_usage_daily120,test_runtime_worker_openhands_push_gate127 on the committed tree (the commit adds no read: no advisory delta needed),test_freeze_snapshot152,test_token_measurement115 skipped 14,test_validate86,test_local_pages_fleet_data38,test_runtime_profiles32,test_transcript_audit17,test_skill_routing7,test_rtk_receipt_locators6,test_skill_correction_claims5,test_evidence_manifest5); gitleaks on the commit: no leaks.Landing rebase onto
8c11dac09(2026-10-14), before round 12The pull request was
DIRTYagainst 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 deletedskill-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).git patch-id --verbatimof the-U0 --binarydiff against each side's own base,3f1a921dd..8ec8ebc37againstorigin/main..d2f96f5be): 54 of 55 files have the same patch; the one other ismanifests/evidence.json, whose rows now hash main's tree.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.pyexit 0 at the round-12 head (12,054 files). The fix10 verdict's item 2 (that diagnostic, model currency ofclaude-fable-5-1) came with the old base: it fails at the round-12 commit on8ec8ebc37and 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 roundThe scope-locked round was pushed as a fast-forward of
da4895b51(e181c3ff2); the pull request then wentDIRTYonmanifests/evidence.jsonagainst 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 withhost_receipts.register_file; the two rows of the deletedskill-invoke-rate.{service,timer}templates are dropped in the first commit.git patch-id --verbatimof the-U0 --binarydiff against each side's own merge base): 43 of 43 files have the same patch,manifests/evidence.jsonincluded.registry_rows_check.py): 40 PR-owned rows at the old head, 0 missing at the new one, 0 with another hash.python3 scripts/validate.pyexit 0 (11,914 files); the modules of the PR (test_skill_usage_daily87,test_runtime_worker_openhands_push_gate127,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_hardeningfailed 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 HEADclean (exit status read directly); identities all the no-reply one; the privacy scan of the added lines: 0 hits.Follow-ups
lane_record) and a measured half without a timestamp is accepted (daily_skill_usage.pyhalf_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 inmanifests/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).--refuse-writablelist 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.NativeManagerTestsprintsnamespace-denied branch exercised: noand 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 ofdaily_skill_usage.pyoutside 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).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.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 holdsnstwice classify as enforced, and a framing nested beyond the decoder's limit raisesRecursionErrorin 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 withobject_pairs_hook, with the clean control retained. Custody (P3 4): theopen(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.refresh_combinedthat was nested beyond the decoder's limit ended in the catch-all; every record that cannot be read is now an absent half.ServiceSandboxTestscites thesystemd.exec(5)locations of round 1 atsystemd/systemd@e846bbad, a commit of main, beside "systemd 259.5 installed" (tag v259.5 isb3d8fc43e9cb); re-pin the four locations at the next edit of that docstring..github/osv-scanner.tomlat main77cc7ef42lists no entry (L17: "No entry as of 2026-10-13 (Relock OpenHands onto oauthlib 4.0.0 and delete the expired oauthlib ignores #1060)").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.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.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.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 onto8c11dac09(validate exit 0 above); (3, P3) the requiredosv-scannercheck: 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.OnCalendarlines when a week of samples exists.🤖 Generated with Claude Code