diff --git a/.agents/pm/features/pm-rust-crash-harness.toon b/.agents/pm/features/pm-rust-crash-harness.toon new file mode 100644 index 0000000..1ea3ab0 --- /dev/null +++ b/.agents/pm/features/pm-rust-crash-harness.toon @@ -0,0 +1,50 @@ +id: pm-rust-crash-harness +title: Compare native and published crash recovery with a reusable kill corpus +description: "Reusable TypeScript process-kill/restart differential SDK and command against the locked published oracle; compare acknowledged durable bytes, append-only histories and restart receipts on disposable plain-directory trackers." +type: Feature +status: in_progress +priority: 2 +tags[3]: conformance,crash-consistency,harness +created_at: "2026-10-21T21:59:35.474Z" +updated_at: "2026-10-22T16:47:30.014Z" +author: codex-agent +acceptance_criteria: Every declared crash boundary preserves acknowledged writes; failures retain minimized synthetic reproductions and never rewrite original history.; Typed SDK and CLI with bounded TOON/JSON receipts and actionable refusals; documented example; public tracker read-copy validation with measured counts; exact-head CI release-check passes. +comments[14]{created_at,author,text}: + "2026-10-21T21:59:37.843Z",codex-agent,"Overlap recheck: published CLI 2026.10.21 matches the locked oracle. Native tests cover normal command differentials and manually reconstructed durable journals; they do not run a reusable live kill/restart corpus. pm-ops assurance reports measurements; pm-vcs writer publication is backend-specific. This feature adds cross-implementation crash evidence beyond lock/parity work. Reconciliation PR 103 closures and performance PR 102 items are excluded. TypeScript-only SDK/command; synthetic failures; targeted local checks; full gate in CI." + "2026-10-21T22:16:09.535Z",codex-agent,"Implemented the typed TypeScript SDK and standalone just crash CLI. Declared boundaries are all observed rename/fsync task-local ordinals: event-loop write calls were rejected as unstable storage boundaries after real tracing. Two mutation families run on disposable non-git clones; same-family restart checks exact durable bytes and raw per-runtime restart receipts, with explicit shared cross-runtime acknowledgement projections. Synthetic probing measured 20 real kills, 20 preserved acknowledged writes/history prefixes, two successful baseline byte/acknowledgement comparisons and two early native lock-initialization restart refusals. Those refusals are retained as minimal one-item synthetic cases; no original stream is repaired. Full runtime crash conformance is not claimed. Adding independent replay/no-git regressions and public-copy measurements before CI." + "2026-10-21T22:19:48.804Z",codex-agent,"Read-only public-copy validation: public main c0154dbd7cd8071ac52b891be13f956f7a0f21fd, 74 items and 154 authoritative files in a non-git copy with read-only permissions. Measured 20 injected kills, 20 acknowledged-write preservations, 20 exact retained-history prefixes, two cross-runtime baseline byte/acknowledgement matches, 18 successful restart state/receipt checks, two independently reproduced native incomplete-lock restart refusals. Source fingerprint unchanged. Retained evidence is synthetic only. Follow-up pm-rust-crash-lock-restart covers the safe recovery policy, outside this harness change. Behavioral controls disabling history-prefix checking, exact inventory comparison and injected-kill admission each fail their targeted regression with exit 1 and pass after exact byte restoration." + "2026-10-21T22:21:46.519Z",codex-agent,"Final targeted checks: pinned PM linked runner passes 14/14 Node regressions with no skips, including real native/published kills, read-only source-output guards, a PATH with no git executable, and independent one-case replay. Strict erasable TypeScript typecheck, ESLint, Rust fmt and targeted binary Clippy pass. Four behavioral negative controls now fail independently and pass after byte restoration, including rejecting a successful restart that lost its own history. Full unchanged-threshold Rust release-check is reserved for CI; no local full release suite was run. Local health retains existing guidance/provenance/stale-item advisories and shared configuration drift; history was not repaired. CI uses its existing clone-local configuration setup. Full CI run results and exact counts will be linked in the PR." + "2026-10-22T10:38:52.442Z",codex-agent,"PR 104 review round: pulled remote head and merged origin/main. PRRT_kwDOTwgMRs6vBg8Y is valid: oracle metadata read/parse errors bypass oracle_version. Adding public SDK/CLI regressions for missing, unreadable, malformed, invalid-shape and wrong-version metadata. Full-diff self-review found the same parse-before-typed-refusal pattern in mutation acknowledgements: malformed successful stdout bypasses baseline_failed. Covering and correcting that instance too. Replay-manifest parsing already has its typed invalid_options catch. Latest substantive CodeRabbit review has no Outside diff range findings. No thread replies or resolutions." + "2026-10-22T10:42:18.350Z",codex-agent,"Full-diff validation review also found malformed source settings JSON bypassed its unsafe_source guard. Corrected all three parse-before-validation paths: oracle metadata maps to oracle_version, source settings to unsafe_source, and malformed successful mutation stdout to baseline_failed. Added ten SDK oracle metadata cases, bounded JSON/default CLI refusal checks, source immutability checks and a real published-CLI baseline with an incompatible executable. No PM SDK mocks; all fixtures are disposable non-git directories. Replay-manifest parsing already preserves invalid_options. Original histories and all gate thresholds remain unchanged." + "2026-10-22T10:43:02.665Z",codex-agent,"Independent revert proofs complete with exact source-byte restoration. Oracle metadata: replacing only its guarded read/parse with the original statement makes both SDK and CLI regressions fail (2/2, io_error); restored both pass. Source settings: reverting only its guarded parse fails 1/1 with io_error; restored passes 1/1. Mutation acknowledgement: reverting only its guarded parse fails 1/1 with io_error; restored passes 1/1 using real published init/create processes and an incompatible native executable. Exact commands: node --test --test-name-pattern=\"oracle metadata\" scripts/crash-harness.test.mts; node --test --test-name-pattern=\"malformed source settings\" scripts/crash-harness.test.mts; node --test --test-name-pattern=\"malformed mutation acknowledgements\" scripts/crash-harness.test.mts. Each negative run exits 1 from the intended assertion; each restored run exits 0." + "2026-10-22T10:44:51.308Z",codex-agent,"Final local targeted validation passed: strict erasable TypeScript compiler and ESLint checks; full crash harness 18/18 with zero skips; linked new regressions 4/4. All three independent behavior reverts failed exactly their intended assertions (2+1+1), then passed restored (2+1+1). Isolated pinned-PM health passed. Starting unchanged-threshold just release-check with the existing 35-minute release CI budget as the local wall-time bound." + "2026-10-22T10:56:48.191Z",codex-agent,"Local full release attempt is incomplete, not failed: dependency install, fmt, strict Clippy and private-item rustdoc passed; the cold Rust test build was stopped under shared-host contention (main compiler about 7 percent CPU over six minutes). No compiler or test assertion failed. Using exact-head CI for the full unchanged-threshold release gate; this PR remains the active round until that gate passes. Targeted TypeScript compiler/lint checks, full Linux crash harness 18/18, linked new controls 4/4 and all independent revert proofs already pass." + "2026-10-22T11:23:46.735Z",codex-agent,"Full CI release evidence at code head 5b84803625d18e3ab3b7fdb4454a49448a628bf6: Linux just release-check passed (https://github.com/unbraind/pm-rust/actions/runs/39767043319/job/119784971422). Ordinary and instrumented Rust tests each passed 578 across 22 suites, zero failed or ignored; Rust lines 14025/14025, functions 1466/1466, regions 23438/23438, branches 3296/3296, all 100 percent. TypeScript compiler, ESLint and Linux crash harness 18/18 with zero skips also passed inside the gate. Audit, changelog and strict tracker health passed; only the pre-existing allowed RUSTSEC-2024-0449 advisory remains. macOS full release gate also passed: 567 ordinary and 567 instrumented Rust tests; all four required Rust metrics 100 percent; Node harness 11 passed with 7 declared Linux-only skips. The stopped local cold-build attempt is incomplete evidence; CI supplies the full pass. Releasing the claim and retaining in_progress for orchestrator verification. Final receipt-head CI evidence will be linked from PR 104; no thread replies/resolutions, PR merge or release." + "2026-10-22T16:37:02.495Z",codex-agent,"New PR 104 review round: pulled remote head 328ff00, then merged origin/main a600bd8 because the branch was one commit behind. Thread PRRT_kwDOTwgMRs6vRw2E is valid: the retained-case example claims every replay selects one implementation/syscall/ordinal, but baseline_parity evidence reruns its scenario across both implementations and all observed boundaries. The public SDK replay docstring has the same overstatement. Will correct both descriptions and add a regression tied to the replay contract with a documentation revert proof. Latest CodeRabbit review has no Outside diff range findings. Existing implementation, completed review fixes and append-only histories remain intact." + "2026-10-22T16:41:16.374Z",codex-agent,"Documentation revert proof for PRRT_kwDOTwgMRs6vRw2E: node --test --test-name-pattern=\"replay guidance\" scripts/crash-harness.test.mts checks the public guide and SDK docstring. Reverting only docs/CRASH_HARNESS.md yields one intended missing-baseline_parity-exception failure and one unaffected pass. Restoring it and reverting only the replay API docstring yields the other intended failure and one unaffected pass. Both exact byte sequences restored: 2/2 pass. These are documentation-contract controls; runtime replay behavior was already correct and was not changed. The complete just crash-check passes strict erasable TypeScript, ESLint and 20/20 Linux tests with zero failed/canceled/skipped; current native build also passes. Pinned-PM linked controls pass 2/2; result storage remains disabled by project policy, so results are retained here." + "2026-10-22T16:41:17.977Z",codex-agent,"Self-review of the whole PR confirms the only overstatements were the retained-case guide and public replay docstring; README, CLI help, manifest validation and the single-boundary selection type do not claim that every replay selects one case. Original histories, stored commands and implementation behavior remain untouched. Releasing the implementation claim with status retained in_progress. Final exact-head unchanged just release-check CI receipts and counts will be recorded in PR https://github.com/unbraind/pm-rust/pull/104; pending CI is not approval. The original stacked-PR instruction still requires merging this completed update and main into PR 105 after PR 104 has passed and been pushed. No review replies/resolutions, PR merge or release." + "2026-10-22T16:47:29.332Z",codex-agent,"Additional self-review caught an LF-only assumption in the new guide regression. The matcher now accepts both LF and CRLF and exercises both spellings using the real guide contents. Reverting only the optional carriage-return matcher makes the CRLF assertion fail; restoring it passes both guidance tests 2/2. Strict TypeScript and ESLint pass. No documentation, runtime function, input fixture or gate threshold changed in this follow-up. Superseded pending CI at 84ddc56 is not used as approval; the corrected final head needs fresh full release CI." +files[5]: + - path: scripts/crash-cli.mts + scope: project + note: Compact TOON and JSON command + - path: scripts/crash-harness.mts + scope: project + note: Typed reusable crash corpus SDK + - path: scripts/crash-harness.test.mts + scope: project + - path: scripts/crash-process.mts + scope: project + note: Real bounded syscall kill adapters + - path: scripts/crash-storage.mts + scope: project + note: Byte inventories and immutable history checks +tests[3]{command,scope,provenance{author,created_at,source_kind,source_ref}}: + node --test scripts/crash-harness.test.mts,project,codex-agent,"2026-10-21T22:17:14.334Z",local_mutation,feat/crash-consistency-harness + "node --test --test-name-pattern=\"oracle metadata|malformed source settings|malformed mutation acknowledgements\" scripts/crash-harness.test.mts",project,codex-agent,"2026-10-22T10:42:11.763Z",local_mutation,feat/crash-consistency-harness + "node --test --test-name-pattern=\"replay guidance\" scripts/crash-harness.test.mts",project,codex-agent,"2026-10-22T16:40:13.066Z",local_mutation,feat/crash-consistency-harness +docs[3]{path,scope,note}: + docs/CRASH_HARNESS.md,project,SDK CLI replay and no-git examples + docs/CRASH_PUBLIC_COPY_2026_10_22.json,project,Measured public tracker copy receipt + docs/CRASH_REVERT_PROOF_2026_10_22.json,project,Independent behavioral negative controls +body: "" diff --git a/.agents/pm/history/pm-rust-comment-lock-receipt.jsonl b/.agents/pm/history/pm-rust-comment-lock-receipt.jsonl new file mode 100644 index 0000000..cf53f96 --- /dev/null +++ b/.agents/pm/history/pm-rust-comment-lock-receipt.jsonl @@ -0,0 +1 @@ +{"hash_algorithm":"sha256","ts":"2026-10-21T23:26:23.084Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"066c6d55f017007f41e896af","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"create","patch":[{"op":"replace","path":"/body","value":"Found while verifying pm-rust-crash-lock-restart. Reproduce in a disposable non-git tracker with one sample-probe Task, a fresh locks/sample-probe.lock.stale-cleanup directory and a sample-probe.lock JSON receipt containing id, a live fixture PID, owner test-holder, token test-held, ttl_seconds 1800 and a current created_at, without host/start identity. Set PM_LOCK_WAIT_MS=0. Compare native comment sample-probe \"refused note\" --author test-agent with the oracle invocation plus --json. Normalize only that encoding flag in retry metadata. Both refuse safely; detail, required and refusal.surface differ. The ownership JSON refusal comparison on the shared gate path passes exactly and is retained with the recovery regression."},{"op":"add","path":"/metadata/id","value":"pm-rust-comment-lock-receipt"},{"op":"add","path":"/metadata/title","value":"Match native comment lock-conflict JSON diagnostics to the oracle"},{"op":"add","path":"/metadata/description","value":"A fresh initialization gate and a valid unverified owner receipt cause both native comment and oracle 2026.10.21 comment to refuse with exit 4 and code lock_conflict, preserving item and history bytes. After removing the oracle-only --json encoding switch, native detail and required guidance remain generic and refusal.surface is comment instead of comments. The generic formatter predates and is unchanged by the crash restart fix. This differs from pm-rust-4jq3, which covers text output for direct claim/release. Add a dedicated complete-receipt regression and repair the native diagnostic surface separately."},{"op":"add","path":"/metadata/type","value":"Task"},{"op":"add","path":"/metadata/status","value":"open"},{"op":"add","path":"/metadata/priority","value":3},{"op":"add","path":"/metadata/tags","value":["conformance","follow-up","locks"]},{"op":"add","path":"/metadata/created_at","value":"2026-10-21T23:26:23.084Z"},{"op":"add","path":"/metadata/updated_at","value":"2026-10-21T23:26:23.084Z"},{"op":"add","path":"/metadata/author","value":"codex-agent"}],"before_hash":"3cc22dff72be7b14824654a7a64ea62b04799939b2fee54c1b5f52ca60bf6df0","after_hash":"59d4bbe0652e38a4cb3351a57f574d9650dbb2f04df2fa1613c2e1f04cc39bfc","item_hash_version":3,"message":"","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"79339ee4649710e53329a4c5e072a269a3b84e8ebca6774e422eb29dc0151105"} diff --git a/.agents/pm/history/pm-rust-crash-harness.jsonl b/.agents/pm/history/pm-rust-crash-harness.jsonl new file mode 100644 index 0000000..afbcefa --- /dev/null +++ b/.agents/pm/history/pm-rust-crash-harness.jsonl @@ -0,0 +1,35 @@ +{"hash_algorithm":"sha256","ts":"2026-10-21T21:59:35.474Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"7a6225902f81bc40eed1bcd8","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"create","patch":[{"op":"add","path":"/metadata/id","value":"pm-rust-crash-harness"},{"op":"add","path":"/metadata/title","value":"Compare native and published crash recovery with a reusable kill corpus"},{"op":"add","path":"/metadata/description","value":"Reusable TypeScript process-kill/restart differential SDK and command against the locked published oracle; compare acknowledged durable bytes, append-only histories and restart receipts on disposable plain-directory trackers."},{"op":"add","path":"/metadata/type","value":"Feature"},{"op":"add","path":"/metadata/status","value":"open"},{"op":"add","path":"/metadata/priority","value":2},{"op":"add","path":"/metadata/tags","value":["conformance","crash-consistency","harness"]},{"op":"add","path":"/metadata/created_at","value":"2026-10-21T21:59:35.474Z"},{"op":"add","path":"/metadata/updated_at","value":"2026-10-21T21:59:35.474Z"},{"op":"add","path":"/metadata/author","value":"codex-agent"},{"op":"add","path":"/metadata/acceptance_criteria","value":"Every declared crash boundary preserves acknowledged writes; failures retain minimized synthetic reproductions and never rewrite original history.; Typed SDK and CLI with bounded TOON/JSON receipts and actionable refusals; documented example; public tracker read-copy validation with measured counts; exact-head CI release-check passes."}],"before_hash":"3cc22dff72be7b14824654a7a64ea62b04799939b2fee54c1b5f52ca60bf6df0","after_hash":"8e1f670ca81a6d238930adc15d6761f4546e07ee126e5484a8b18e542a059d7c","item_hash_version":3,"message":"","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"d6f8184c3613afda85e48f00b3df55e86e31fee5bf9ecec84f077733513c35fd"} +{"hash_algorithm":"sha256","ts":"2026-10-21T21:59:36.236Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"7a6225902f81bc40eed1bcd8","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"claim","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T21:59:36.236Z"},{"op":"add","path":"/metadata/assignee","value":"codex-agent"},{"op":"add","path":"/metadata/claim_principal","value":"codex-agent"}],"before_hash":"8e1f670ca81a6d238930adc15d6761f4546e07ee126e5484a8b18e542a059d7c","after_hash":"905ff535be5ae00a6a409c771aa71a435bdd2ad2f8a47f23bf5abf6dddb90041","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"ccd03b5fdcd44ca9e20712780731e32a83ffbadf48f36a06387cf18772e0a09d"} +{"hash_algorithm":"sha256","ts":"2026-10-21T21:59:37.044Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"7a6225902f81bc40eed1bcd8","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T21:59:37.044Z"},{"op":"replace","path":"/metadata/status","value":"in_progress"}],"before_hash":"905ff535be5ae00a6a409c771aa71a435bdd2ad2f8a47f23bf5abf6dddb90041","after_hash":"9361a97ad6df97b84a3c1e96a4dd147f4e34b53dbc8580eadb00e912826d76f5","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"b45b9fa7f8fed31eca02004b2ce337b560e38d496ccc43bd4acd8df517c43b6e"} +{"hash_algorithm":"sha256","ts":"2026-10-21T21:59:37.844Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"7a6225902f81bc40eed1bcd8","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T21:59:37.844Z"},{"op":"add","path":"/metadata/comments","value":[{"created_at":"2026-10-21T21:59:37.843Z","author":"codex-agent","text":"Overlap recheck: published CLI 2026.10.21 matches the locked oracle. Native tests cover normal command differentials and manually reconstructed durable journals; they do not run a reusable live kill/restart corpus. pm-ops assurance reports measurements; pm-vcs writer publication is backend-specific. This feature adds cross-implementation crash evidence beyond lock/parity work. Reconciliation PR 103 closures and performance PR 102 items are excluded. TypeScript-only SDK/command; synthetic failures; targeted local checks; full gate in CI."}]}],"before_hash":"9361a97ad6df97b84a3c1e96a4dd147f4e34b53dbc8580eadb00e912826d76f5","after_hash":"83c7380e2346d35bed7a78863a172cac06bc9865791b45c0d63340850d5f6427","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"4b87244775a4ce1c87aacc04a2d52081c31de8a0d807a7896164b79afcebbb65"} +{"hash_algorithm":"sha256","ts":"2026-10-21T22:16:09.536Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"7a6225902f81bc40eed1bcd8","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/1","value":{"created_at":"2026-10-21T22:16:09.535Z","author":"codex-agent","text":"Implemented the typed TypeScript SDK and standalone just crash CLI. Declared boundaries are all observed rename/fsync task-local ordinals: event-loop write calls were rejected as unstable storage boundaries after real tracing. Two mutation families run on disposable non-git clones; same-family restart checks exact durable bytes and raw per-runtime restart receipts, with explicit shared cross-runtime acknowledgement projections. Synthetic probing measured 20 real kills, 20 preserved acknowledged writes/history prefixes, two successful baseline byte/acknowledgement comparisons and two early native lock-initialization restart refusals. Those refusals are retained as minimal one-item synthetic cases; no original stream is repaired. Full runtime crash conformance is not claimed. Adding independent replay/no-git regressions and public-copy measurements before CI."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T22:16:09.536Z"}],"before_hash":"83c7380e2346d35bed7a78863a172cac06bc9865791b45c0d63340850d5f6427","after_hash":"9985e10419614c48372edb655c34bc7e87e772d84e8709598ed527c77cddc851","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"4069d780939f74450abcf502a20d86d81dea60cc14d9d766cfec84909b32452d"} +{"hash_algorithm":"sha256","ts":"2026-10-21T22:17:03.402Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"7a6225902f81bc40eed1bcd8","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T22:17:03.402Z"},{"op":"add","path":"/metadata/files","value":[{"path":"scripts/crash-harness.mts","scope":"project","note":"Typed reusable crash corpus SDK"}]}],"before_hash":"9985e10419614c48372edb655c34bc7e87e772d84e8709598ed527c77cddc851","after_hash":"1ce3be396867355b33bbaf3c0f94c2aa76f6eb8fe495eee39ef03704bc470293","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"d177be839eefcb4e12bedc31390d656072f74ebfa6eb7cb51be3bf037124dcb8"} +{"hash_algorithm":"sha256","ts":"2026-10-21T22:17:05.759Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"7a6225902f81bc40eed1bcd8","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"add","path":"/metadata/files/1","value":{"path":"scripts/crash-process.mts","scope":"project","note":"Real bounded syscall kill adapters"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T22:17:05.759Z"}],"before_hash":"1ce3be396867355b33bbaf3c0f94c2aa76f6eb8fe495eee39ef03704bc470293","after_hash":"407c104b16e6db0770c6a20485de2d16c6f420edc944d559522dc6df99156bad","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"5c22cd75dc8280ea7db25cd574e8555ba7212238d42995509051cd80657de0cf"} +{"hash_algorithm":"sha256","ts":"2026-10-21T22:17:08.697Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"7a6225902f81bc40eed1bcd8","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"add","path":"/metadata/files/2","value":{"path":"scripts/crash-storage.mts","scope":"project","note":"Byte inventories and immutable history checks"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T22:17:08.697Z"}],"before_hash":"407c104b16e6db0770c6a20485de2d16c6f420edc944d559522dc6df99156bad","after_hash":"5e38e746ecf02f68ca87ba61402641f06f85f8cb29b645c1a94e9f2af2b8b7df","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"9c72b2afa6a1c621850ad51bccdaceac29abe38eee7fa7fbe2278b71a5fce63b"} +{"hash_algorithm":"sha256","ts":"2026-10-21T22:17:10.875Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"7a6225902f81bc40eed1bcd8","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/2/note","value":"Real bounded syscall kill adapters"},{"op":"replace","path":"/metadata/files/2/path","value":"scripts/crash-process.mts"},{"op":"replace","path":"/metadata/files/1/note","value":"Typed reusable crash corpus SDK"},{"op":"replace","path":"/metadata/files/1/path","value":"scripts/crash-harness.mts"},{"op":"replace","path":"/metadata/files/0/note","value":"Compact TOON and JSON command"},{"op":"replace","path":"/metadata/files/0/path","value":"scripts/crash-cli.mts"},{"op":"add","path":"/metadata/files/3","value":{"path":"scripts/crash-storage.mts","scope":"project","note":"Byte inventories and immutable history checks"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T22:17:10.875Z"}],"before_hash":"5e38e746ecf02f68ca87ba61402641f06f85f8cb29b645c1a94e9f2af2b8b7df","after_hash":"e6b35403a79d53a397f1afbab820949be024d85d231f894368ac4d7f7a2fe2ef","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"e76e185e57b09299f7fb7d6aa3030751097e7cd1826e09f987fe0bd32b90760b"} +{"hash_algorithm":"sha256","ts":"2026-10-21T22:17:12.618Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"7a6225902f81bc40eed1bcd8","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"docs_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T22:17:12.618Z"},{"op":"add","path":"/metadata/docs","value":[{"path":"docs/CRASH_HARNESS.md","scope":"project","note":"SDK CLI replay and no-git examples"}]}],"before_hash":"e6b35403a79d53a397f1afbab820949be024d85d231f894368ac4d7f7a2fe2ef","after_hash":"804ce1f4cf235ea88120d0e4a96eed6924bcb686d3628ded8315c4ebd8c05a06","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"f72e867f6c842b7a0329e709725ac238f10f3d68d81370be9dae953753acedcd"} +{"hash_algorithm":"sha256","ts":"2026-10-21T22:17:14.411Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"7a6225902f81bc40eed1bcd8","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"tests_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T22:17:14.411Z"},{"op":"add","path":"/metadata/tests","value":[{"command":"node --test scripts/crash-harness.test.mts","scope":"project","provenance":{"author":"codex-agent","created_at":"2026-10-21T22:17:14.334Z","source_kind":"local_mutation","source_ref":"feat/crash-consistency-harness"}}]}],"before_hash":"804ce1f4cf235ea88120d0e4a96eed6924bcb686d3628ded8315c4ebd8c05a06","after_hash":"1334fc5f7bf17e9ab224d2b2df7854b64e2966c24973074a87d2ff04bc15fed7","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"4e8c9c7274cc6f890223271bfd78f494e76d82b76100768898146896e19d193d"} +{"hash_algorithm":"sha256","ts":"2026-10-21T22:19:48.805Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"7a6225902f81bc40eed1bcd8","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/2","value":{"created_at":"2026-10-21T22:19:48.804Z","author":"codex-agent","text":"Read-only public-copy validation: public main c0154dbd7cd8071ac52b891be13f956f7a0f21fd, 74 items and 154 authoritative files in a non-git copy with read-only permissions. Measured 20 injected kills, 20 acknowledged-write preservations, 20 exact retained-history prefixes, two cross-runtime baseline byte/acknowledgement matches, 18 successful restart state/receipt checks, two independently reproduced native incomplete-lock restart refusals. Source fingerprint unchanged. Retained evidence is synthetic only. Follow-up pm-rust-crash-lock-restart covers the safe recovery policy, outside this harness change. Behavioral controls disabling history-prefix checking, exact inventory comparison and injected-kill admission each fail their targeted regression with exit 1 and pass after exact byte restoration."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T22:19:48.805Z"}],"before_hash":"1334fc5f7bf17e9ab224d2b2df7854b64e2966c24973074a87d2ff04bc15fed7","after_hash":"f17b4115940329581053ba77eb64e955d5be02a6af2cc0f2f95acf3332efe367","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"015e0d38a7027cd8c780795a5ddcc2e545ae10e966b02b82c157cccfbcfd2b20"} +{"hash_algorithm":"sha256","ts":"2026-10-21T22:19:50.144Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"7a6225902f81bc40eed1bcd8","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"docs_add","patch":[{"op":"add","path":"/metadata/docs/1","value":{"path":"docs/CRASH_PUBLIC_COPY_2026_10_22.json","scope":"project","note":"Measured public tracker copy receipt"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T22:19:50.144Z"}],"before_hash":"f17b4115940329581053ba77eb64e955d5be02a6af2cc0f2f95acf3332efe367","after_hash":"7e88009806e98188eaf2929dee14f8cebf38ffa7aab7415d794374ebbf4346e4","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"49cf0f718505e9a251c4567e696529df0c107ba298129e7f73da1c164062b77c"} +{"hash_algorithm":"sha256","ts":"2026-10-21T22:19:51.361Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"7a6225902f81bc40eed1bcd8","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"docs_add","patch":[{"op":"add","path":"/metadata/docs/2","value":{"path":"docs/CRASH_REVERT_PROOF_2026_10_22.json","scope":"project","note":"Independent behavioral negative controls"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T22:19:51.361Z"}],"before_hash":"7e88009806e98188eaf2929dee14f8cebf38ffa7aab7415d794374ebbf4346e4","after_hash":"789cecc2c663f4c1233c7459b2d7e54a80e58c4ecca14c2f92fca9e6c0dffc71","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"7a665d8cd5e872652132668953472e2b64ca662892f91913c550bbcf8d44a662"} +{"hash_algorithm":"sha256","ts":"2026-10-21T22:21:46.519Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"7a6225902f81bc40eed1bcd8","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/3","value":{"created_at":"2026-10-21T22:21:46.519Z","author":"codex-agent","text":"Final targeted checks: pinned PM linked runner passes 14/14 Node regressions with no skips, including real native/published kills, read-only source-output guards, a PATH with no git executable, and independent one-case replay. Strict erasable TypeScript typecheck, ESLint, Rust fmt and targeted binary Clippy pass. Four behavioral negative controls now fail independently and pass after byte restoration, including rejecting a successful restart that lost its own history. Full unchanged-threshold Rust release-check is reserved for CI; no local full release suite was run. Local health retains existing guidance/provenance/stale-item advisories and shared configuration drift; history was not repaired. CI uses its existing clone-local configuration setup. Full CI run results and exact counts will be linked in the PR."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T22:21:46.519Z"}],"before_hash":"789cecc2c663f4c1233c7459b2d7e54a80e58c4ecca14c2f92fca9e6c0dffc71","after_hash":"6b0ba0ece22d00f1e6da6dfbeb6223105db7f2ec6c2b2f5bc9240046e0e822a4","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"535eda1828ac8f2bec07c85f0fd91c4f06f3238aa56400044292ff51bff2ff17"} +{"hash_algorithm":"sha256","ts":"2026-10-21T22:21:47.994Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"7a6225902f81bc40eed1bcd8","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T22:21:47.994Z"}],"before_hash":"6b0ba0ece22d00f1e6da6dfbeb6223105db7f2ec6c2b2f5bc9240046e0e822a4","after_hash":"c69757dfb574b5f56f1a463f92b594d8860f7e3c364e3de518fba9ac4d0376cd","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"69da1e40bab6dada4e4dfc62c2208f3dc7ecd8528e2782ffb8a43898d3a608c8"} +{"hash_algorithm":"sha256","ts":"2026-10-22T10:38:45.211Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_instance":"9cd644682823b57766dff4f3","agent_provenance":{"model":null,"effort":null,"role":{"value":"implementer","source":"argv"},"topic":null},"op":"claim","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-22T10:38:45.211Z"},{"op":"add","path":"/metadata/assignee","value":"codex-agent"},{"op":"add","path":"/metadata/claim_principal","value":"codex-agent"}],"before_hash":"c69757dfb574b5f56f1a463f92b594d8860f7e3c364e3de518fba9ac4d0376cd","after_hash":"c0f966e39cb7fdd6aac20728214a6ddd67d044a8bb2ab9add0066c8e8d23b565","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"d3e06a47e6952437bc68840af0c6500fb64c9fbaf31152e1839d36b0187af554"} +{"hash_algorithm":"sha256","ts":"2026-10-22T10:38:52.443Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_instance":"9cd644682823b57766dff4f3","agent_provenance":{"model":null,"effort":null,"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/4","value":{"created_at":"2026-10-22T10:38:52.442Z","author":"codex-agent","text":"PR 104 review round: pulled remote head and merged origin/main. PRRT_kwDOTwgMRs6vBg8Y is valid: oracle metadata read/parse errors bypass oracle_version. Adding public SDK/CLI regressions for missing, unreadable, malformed, invalid-shape and wrong-version metadata. Full-diff self-review found the same parse-before-typed-refusal pattern in mutation acknowledgements: malformed successful stdout bypasses baseline_failed. Covering and correcting that instance too. Replay-manifest parsing already has its typed invalid_options catch. Latest substantive CodeRabbit review has no Outside diff range findings. No thread replies or resolutions."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-22T10:38:52.443Z"}],"before_hash":"c0f966e39cb7fdd6aac20728214a6ddd67d044a8bb2ab9add0066c8e8d23b565","after_hash":"b1886052dd84ed2ee9263306253ce3a0ca22a55f2e3cda2d045fd09cf32dbe47","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"e3728323af834729821e57ed7ed1b320a89a6aa5cb75c02aed27b0ee6fe1bd89"} +{"hash_algorithm":"sha256","ts":"2026-10-22T10:42:07.761Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_instance":"9cd644682823b57766dff4f3","agent_provenance":{"model":null,"effort":null,"role":null,"topic":null},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/3/note","value":"Real bounded syscall kill adapters"},{"op":"replace","path":"/metadata/files/3/path","value":"scripts/crash-process.mts"},{"op":"remove","path":"/metadata/files/2/note"},{"op":"replace","path":"/metadata/files/2/path","value":"scripts/crash-harness.test.mts"},{"op":"add","path":"/metadata/files/4","value":{"path":"scripts/crash-storage.mts","scope":"project","note":"Byte inventories and immutable history checks"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-22T10:42:07.761Z"}],"before_hash":"b1886052dd84ed2ee9263306253ce3a0ca22a55f2e3cda2d045fd09cf32dbe47","after_hash":"d25c395f591659d337639938d4ef64c3c9a594055938fbbd0b9e79c70b25355e","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"0f2125a6861fe9eda2187301cac564918886495e8ec69f3fd718e8b634019cb8"} +{"hash_algorithm":"sha256","ts":"2026-10-22T10:42:11.946Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_instance":"9cd644682823b57766dff4f3","agent_provenance":{"model":null,"effort":null,"role":null,"topic":null},"op":"tests_add","patch":[{"op":"add","path":"/metadata/tests/1","value":{"command":"node --test --test-name-pattern=\"oracle metadata|malformed source settings|malformed mutation acknowledgements\" scripts/crash-harness.test.mts","scope":"project","provenance":{"author":"codex-agent","created_at":"2026-10-22T10:42:11.763Z","source_kind":"local_mutation","source_ref":"feat/crash-consistency-harness"}}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-22T10:42:11.946Z"}],"before_hash":"d25c395f591659d337639938d4ef64c3c9a594055938fbbd0b9e79c70b25355e","after_hash":"d063bc0d14d4bef250b067ca480d1c932a5c232709a07e2ffdccba04ddb0dacf","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"2f661b78b7f6274d27f6e3d790427c41359c6dbd0a44a14bc66a79629c9611c2"} +{"hash_algorithm":"sha256","ts":"2026-10-22T10:42:18.350Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_instance":"9cd644682823b57766dff4f3","agent_provenance":{"model":null,"effort":null,"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/5","value":{"created_at":"2026-10-22T10:42:18.350Z","author":"codex-agent","text":"Full-diff validation review also found malformed source settings JSON bypassed its unsafe_source guard. Corrected all three parse-before-validation paths: oracle metadata maps to oracle_version, source settings to unsafe_source, and malformed successful mutation stdout to baseline_failed. Added ten SDK oracle metadata cases, bounded JSON/default CLI refusal checks, source immutability checks and a real published-CLI baseline with an incompatible executable. No PM SDK mocks; all fixtures are disposable non-git directories. Replay-manifest parsing already preserves invalid_options. Original histories and all gate thresholds remain unchanged."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-22T10:42:18.350Z"}],"before_hash":"d063bc0d14d4bef250b067ca480d1c932a5c232709a07e2ffdccba04ddb0dacf","after_hash":"eb4c898761c6a1048bd10815c69a6df7967c44e867dc32a0a6a8a4d01b25365b","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"1c415892b334b681b552dfddace61f6a521146827c0167d355d482dbf25074da"} +{"hash_algorithm":"sha256","ts":"2026-10-22T10:43:02.665Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_instance":"9cd644682823b57766dff4f3","agent_provenance":{"model":null,"effort":null,"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/6","value":{"created_at":"2026-10-22T10:43:02.665Z","author":"codex-agent","text":"Independent revert proofs complete with exact source-byte restoration. Oracle metadata: replacing only its guarded read/parse with the original statement makes both SDK and CLI regressions fail (2/2, io_error); restored both pass. Source settings: reverting only its guarded parse fails 1/1 with io_error; restored passes 1/1. Mutation acknowledgement: reverting only its guarded parse fails 1/1 with io_error; restored passes 1/1 using real published init/create processes and an incompatible native executable. Exact commands: node --test --test-name-pattern=\"oracle metadata\" scripts/crash-harness.test.mts; node --test --test-name-pattern=\"malformed source settings\" scripts/crash-harness.test.mts; node --test --test-name-pattern=\"malformed mutation acknowledgements\" scripts/crash-harness.test.mts. Each negative run exits 1 from the intended assertion; each restored run exits 0."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-22T10:43:02.665Z"}],"before_hash":"eb4c898761c6a1048bd10815c69a6df7967c44e867dc32a0a6a8a4d01b25365b","after_hash":"1453efc166c2dd451c88c55f278fcc3584c789b0f936717ec06a9969bfbf29c6","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"9a37927d857861d644e79cbaf776447fbdbe13fb070ea2c531ce37497c601a74"} +{"hash_algorithm":"sha256","ts":"2026-10-22T10:44:51.308Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_instance":"9cd644682823b57766dff4f3","agent_provenance":{"model":null,"effort":null,"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/7","value":{"created_at":"2026-10-22T10:44:51.308Z","author":"codex-agent","text":"Final local targeted validation passed: strict erasable TypeScript compiler and ESLint checks; full crash harness 18/18 with zero skips; linked new regressions 4/4. All three independent behavior reverts failed exactly their intended assertions (2+1+1), then passed restored (2+1+1). Isolated pinned-PM health passed. Starting unchanged-threshold just release-check with the existing 35-minute release CI budget as the local wall-time bound."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-22T10:44:51.308Z"}],"before_hash":"1453efc166c2dd451c88c55f278fcc3584c789b0f936717ec06a9969bfbf29c6","after_hash":"d0dddffcf447490a6a4966777095512ec532bd161925381b14da71b4455049d2","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"0a0ac2b420999dda1f60bd93836486213568d0d40ef80fc72a051c339ad1cb29"} +{"hash_algorithm":"sha256","ts":"2026-10-22T10:56:48.199Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_instance":"9cd644682823b57766dff4f3","agent_provenance":{"model":null,"effort":null,"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/8","value":{"created_at":"2026-10-22T10:56:48.191Z","author":"codex-agent","text":"Local full release attempt is incomplete, not failed: dependency install, fmt, strict Clippy and private-item rustdoc passed; the cold Rust test build was stopped under shared-host contention (main compiler about 7 percent CPU over six minutes). No compiler or test assertion failed. Using exact-head CI for the full unchanged-threshold release gate; this PR remains the active round until that gate passes. Targeted TypeScript compiler/lint checks, full Linux crash harness 18/18, linked new controls 4/4 and all independent revert proofs already pass."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-22T10:56:48.199Z"}],"before_hash":"d0dddffcf447490a6a4966777095512ec532bd161925381b14da71b4455049d2","after_hash":"dcb633b6cbdb5a98bb461cc0fba7628686206fe45269f39f664c02de519a6d5a","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"9dd706a1a191a07cbb4223a279c3f674ba2f2e66f5b62260cff9d10f6c1d1b61"} +{"hash_algorithm":"sha256","ts":"2026-10-22T11:23:46.735Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_instance":"9cd644682823b57766dff4f3","agent_provenance":{"model":null,"effort":null,"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/9","value":{"created_at":"2026-10-22T11:23:46.735Z","author":"codex-agent","text":"Full CI release evidence at code head 5b84803625d18e3ab3b7fdb4454a49448a628bf6: Linux just release-check passed (https://github.com/unbraind/pm-rust/actions/runs/39767043319/job/119784971422). Ordinary and instrumented Rust tests each passed 578 across 22 suites, zero failed or ignored; Rust lines 14025/14025, functions 1466/1466, regions 23438/23438, branches 3296/3296, all 100 percent. TypeScript compiler, ESLint and Linux crash harness 18/18 with zero skips also passed inside the gate. Audit, changelog and strict tracker health passed; only the pre-existing allowed RUSTSEC-2024-0449 advisory remains. macOS full release gate also passed: 567 ordinary and 567 instrumented Rust tests; all four required Rust metrics 100 percent; Node harness 11 passed with 7 declared Linux-only skips. The stopped local cold-build attempt is incomplete evidence; CI supplies the full pass. Releasing the claim and retaining in_progress for orchestrator verification. Final receipt-head CI evidence will be linked from PR 104; no thread replies/resolutions, PR merge or release."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-22T11:23:46.735Z"}],"before_hash":"dcb633b6cbdb5a98bb461cc0fba7628686206fe45269f39f664c02de519a6d5a","after_hash":"b0bf9b9f56e5aca05aeef4f33668816860a50a6ee49d9161d15ce3642ced0e8d","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"bd9dacb0b680c11b142c7f14b5075fa6bfd05ee0e2b0e132dbb21b6217b11baf"} +{"hash_algorithm":"sha256","ts":"2026-10-22T11:23:55.320Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_instance":"9cd644682823b57766dff4f3","agent_provenance":{"model":null,"effort":null,"role":{"value":"implementer","source":"argv"},"topic":null},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-22T11:23:55.320Z"}],"before_hash":"b0bf9b9f56e5aca05aeef4f33668816860a50a6ee49d9161d15ce3642ced0e8d","after_hash":"8298587058cc3412cc1f43d31e0b68d0d8e7199675c77cee6983396f79aa6ffc","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"63ca93664b4282ba89e6e0227f9cd4c1ca98444f94de9392d1a7f8a997e08ecb"} +{"hash_algorithm":"sha256","ts":"2026-10-22T16:36:59.617Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_instance":"9cd644682823b57766dff4f3","agent_provenance":{"model":null,"effort":null,"role":{"value":"implementer","source":"argv"},"topic":null},"op":"claim","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-22T16:36:59.617Z"},{"op":"add","path":"/metadata/assignee","value":"codex-agent"},{"op":"add","path":"/metadata/claim_principal","value":"codex-agent"}],"before_hash":"8298587058cc3412cc1f43d31e0b68d0d8e7199675c77cee6983396f79aa6ffc","after_hash":"8284b4d4cc5c1a1870c977087bdfa5f74bdb83a1016130a175f563672b52b447","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"c7a8067ff258527b6c82accb0f42ba7f0b1772ea55e1ec2b2f371fd5ff6ed189"} +{"hash_algorithm":"sha256","ts":"2026-10-22T16:37:02.495Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_instance":"9cd644682823b57766dff4f3","agent_provenance":{"model":null,"effort":null,"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/10","value":{"created_at":"2026-10-22T16:37:02.495Z","author":"codex-agent","text":"New PR 104 review round: pulled remote head 328ff00, then merged origin/main a600bd8 because the branch was one commit behind. Thread PRRT_kwDOTwgMRs6vRw2E is valid: the retained-case example claims every replay selects one implementation/syscall/ordinal, but baseline_parity evidence reruns its scenario across both implementations and all observed boundaries. The public SDK replay docstring has the same overstatement. Will correct both descriptions and add a regression tied to the replay contract with a documentation revert proof. Latest CodeRabbit review has no Outside diff range findings. Existing implementation, completed review fixes and append-only histories remain intact."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-22T16:37:02.495Z"}],"before_hash":"8284b4d4cc5c1a1870c977087bdfa5f74bdb83a1016130a175f563672b52b447","after_hash":"52ddb438d8c9fc4bf1073c6924b003dec706ffe68d87cad1a7ce2e19635229cc","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"f37e2d464783d111bafb72b5271aeaaf831471ff4cd5d871a55f6836c1124775"} +{"hash_algorithm":"sha256","ts":"2026-10-22T16:40:13.115Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_instance":"9cd644682823b57766dff4f3","agent_provenance":{"model":null,"effort":null,"role":null,"topic":null},"op":"tests_add","patch":[{"op":"add","path":"/metadata/tests/2","value":{"command":"node --test --test-name-pattern=\"replay guidance\" scripts/crash-harness.test.mts","scope":"project","provenance":{"author":"codex-agent","created_at":"2026-10-22T16:40:13.066Z","source_kind":"local_mutation","source_ref":"feat/crash-consistency-harness"}}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-22T16:40:13.115Z"}],"before_hash":"52ddb438d8c9fc4bf1073c6924b003dec706ffe68d87cad1a7ce2e19635229cc","after_hash":"9c3a3ecafc2da4fc7ab0a7728e648b6046e9739a9fc0aeb23c8ddf4b00c6673d","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"a832d6caced03f1479a3be320a105351c5b096d184fdce117f6db7127fc08f09"} +{"hash_algorithm":"sha256","ts":"2026-10-22T16:41:16.374Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_instance":"9cd644682823b57766dff4f3","agent_provenance":{"model":null,"effort":null,"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/11","value":{"created_at":"2026-10-22T16:41:16.374Z","author":"codex-agent","text":"Documentation revert proof for PRRT_kwDOTwgMRs6vRw2E: node --test --test-name-pattern=\"replay guidance\" scripts/crash-harness.test.mts checks the public guide and SDK docstring. Reverting only docs/CRASH_HARNESS.md yields one intended missing-baseline_parity-exception failure and one unaffected pass. Restoring it and reverting only the replay API docstring yields the other intended failure and one unaffected pass. Both exact byte sequences restored: 2/2 pass. These are documentation-contract controls; runtime replay behavior was already correct and was not changed. The complete just crash-check passes strict erasable TypeScript, ESLint and 20/20 Linux tests with zero failed/canceled/skipped; current native build also passes. Pinned-PM linked controls pass 2/2; result storage remains disabled by project policy, so results are retained here."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-22T16:41:16.374Z"}],"before_hash":"9c3a3ecafc2da4fc7ab0a7728e648b6046e9739a9fc0aeb23c8ddf4b00c6673d","after_hash":"ec55fa45b7604d575e52b7c37e633d477469380c96c8d8d6644fdab7f9476c7b","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"c77b6745e4f32729ae3ddb96776d89f8de090ef5982c2e2d4975dbd9cd22fc24"} +{"hash_algorithm":"sha256","ts":"2026-10-22T16:41:17.978Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_instance":"9cd644682823b57766dff4f3","agent_provenance":{"model":null,"effort":null,"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/12","value":{"created_at":"2026-10-22T16:41:17.977Z","author":"codex-agent","text":"Self-review of the whole PR confirms the only overstatements were the retained-case guide and public replay docstring; README, CLI help, manifest validation and the single-boundary selection type do not claim that every replay selects one case. Original histories, stored commands and implementation behavior remain untouched. Releasing the implementation claim with status retained in_progress. Final exact-head unchanged just release-check CI receipts and counts will be recorded in PR https://github.com/unbraind/pm-rust/pull/104; pending CI is not approval. The original stacked-PR instruction still requires merging this completed update and main into PR 105 after PR 104 has passed and been pushed. No review replies/resolutions, PR merge or release."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-22T16:41:17.978Z"}],"before_hash":"ec55fa45b7604d575e52b7c37e633d477469380c96c8d8d6644fdab7f9476c7b","after_hash":"b938090f790628c8f45bb60d705b0d512bbce8da2f7818fb98012ba7cfce11a2","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"160a001c32a83306f045dad2dba30cc27731d944237b5c598e653d0cec75c6ee"} +{"hash_algorithm":"sha256","ts":"2026-10-22T16:41:19.128Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_instance":"9cd644682823b57766dff4f3","agent_provenance":{"model":null,"effort":null,"role":{"value":"implementer","source":"argv"},"topic":null},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-22T16:41:19.128Z"}],"before_hash":"b938090f790628c8f45bb60d705b0d512bbce8da2f7818fb98012ba7cfce11a2","after_hash":"8ff8538f4d29be4f79c2d1986f36336e0b6f069545556415f3873c0ee8a5a714","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"0bd5fa5bc211f652b6007ee1f090717c7ad54c4308d547771f103f283a831098"} +{"hash_algorithm":"sha256","ts":"2026-10-22T16:47:28.503Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_instance":"9cd644682823b57766dff4f3","agent_provenance":{"model":null,"effort":null,"role":{"value":"implementer","source":"argv"},"topic":null},"op":"claim","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-22T16:47:28.503Z"},{"op":"add","path":"/metadata/assignee","value":"codex-agent"},{"op":"add","path":"/metadata/claim_principal","value":"codex-agent"}],"before_hash":"8ff8538f4d29be4f79c2d1986f36336e0b6f069545556415f3873c0ee8a5a714","after_hash":"2a1609dc8119e9de0500613ed33ebea562a1271508b4db428a12d90aa7768b55","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"f9591ac740e83d28665a9dfd2a873552ef3811b960c876a820cd7e5e2b15bdbd"} +{"hash_algorithm":"sha256","ts":"2026-10-22T16:47:29.333Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_instance":"9cd644682823b57766dff4f3","agent_provenance":{"model":null,"effort":null,"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/13","value":{"created_at":"2026-10-22T16:47:29.332Z","author":"codex-agent","text":"Additional self-review caught an LF-only assumption in the new guide regression. The matcher now accepts both LF and CRLF and exercises both spellings using the real guide contents. Reverting only the optional carriage-return matcher makes the CRLF assertion fail; restoring it passes both guidance tests 2/2. Strict TypeScript and ESLint pass. No documentation, runtime function, input fixture or gate threshold changed in this follow-up. Superseded pending CI at 84ddc56 is not used as approval; the corrected final head needs fresh full release CI."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-22T16:47:29.333Z"}],"before_hash":"2a1609dc8119e9de0500613ed33ebea562a1271508b4db428a12d90aa7768b55","after_hash":"83037d46c61d87ab8453636cfd3c88e01469e44d6f148b25675d89b1b1b86f73","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"46a3d1ba57fa48da1ec04d0c71bc279221d3fc5c701ccfa760b569d7ab2d79ef"} +{"hash_algorithm":"sha256","ts":"2026-10-22T16:47:30.014Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_instance":"9cd644682823b57766dff4f3","agent_provenance":{"model":null,"effort":null,"role":{"value":"implementer","source":"argv"},"topic":null},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-22T16:47:30.014Z"}],"before_hash":"83037d46c61d87ab8453636cfd3c88e01469e44d6f148b25675d89b1b1b86f73","after_hash":"b2b4f9ac3d7daac208fb57e1b7e3e022fa734cf1d99add9d55b62bcc2f26ed06","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"9626a8e574048e48725d5dd6ca08c1e5d75995ad91a8d9983608af28786bc13c"} diff --git a/.agents/pm/history/pm-rust-crash-lock-restart.jsonl b/.agents/pm/history/pm-rust-crash-lock-restart.jsonl new file mode 100644 index 0000000..d3e8fd9 --- /dev/null +++ b/.agents/pm/history/pm-rust-crash-lock-restart.jsonl @@ -0,0 +1,47 @@ +{"hash_algorithm":"sha256","ts":"2026-10-21T22:16:11.158Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"7a6225902f81bc40eed1bcd8","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"create","patch":[{"op":"add","path":"/metadata/id","value":"pm-rust-crash-lock-restart"},{"op":"add","path":"/metadata/title","value":"Recover incomplete native lock initialization after process death"},{"op":"add","path":"/metadata/description","value":"The live crash corpus kills the native writer at its first lock fsync. The acknowledged item/history remain byte exact, but same-family restart exits 4 because the incomplete lock cannot establish owner identity. Reproduce with just crash and the retained synthetic fsync ordinal 1 case; do not repair or rewrite histories. Implement a separately reviewed safe recovery policy without deleting live owners."},{"op":"add","path":"/metadata/type","value":"Task"},{"op":"add","path":"/metadata/status","value":"open"},{"op":"add","path":"/metadata/priority","value":2},{"op":"add","path":"/metadata/tags","value":["crash-consistency","follow-up","locks"]},{"op":"add","path":"/metadata/created_at","value":"2026-10-21T22:16:11.158Z"},{"op":"add","path":"/metadata/updated_at","value":"2026-10-21T22:16:11.158Z"},{"op":"add","path":"/metadata/author","value":"codex-agent"}],"before_hash":"3cc22dff72be7b14824654a7a64ea62b04799939b2fee54c1b5f52ca60bf6df0","after_hash":"19fe72bec5b90c039f6ce32f774be43785b1c1db59f3220794c165f49ab04f7a","item_hash_version":3,"message":"","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"359f405279271c6311a9dbc55b44c73bc578a86cebe6549dc91be191cc15c688"} +{"hash_algorithm":"sha256","ts":"2026-10-21T22:19:52.271Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"7a6225902f81bc40eed1bcd8","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T22:19:52.271Z"},{"op":"add","path":"/metadata/files","value":[{"path":"scripts/crash-harness.test.mts","scope":"project","note":"Real incomplete-lock crash and replay reproduction"}]}],"before_hash":"19fe72bec5b90c039f6ce32f774be43785b1c1db59f3220794c165f49ab04f7a","after_hash":"6518eaab7666e197bfb37a59d19b1da40a2fb61dd94ffb4dde947d5df4012bad","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"275b67e661f1e8e69ca7fc4373b9c16caacd8c600ef3555866ccac402b6a8861"} +{"hash_algorithm":"sha256","ts":"2026-10-21T22:19:53.176Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"7a6225902f81bc40eed1bcd8","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"docs_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T22:19:53.176Z"},{"op":"add","path":"/metadata/docs","value":[{"path":"docs/CRASH_HARNESS.md","scope":"project","note":"Replay protocol and safe history retention"}]}],"before_hash":"6518eaab7666e197bfb37a59d19b1da40a2fb61dd94ffb4dde947d5df4012bad","after_hash":"db27715e00c2d5012414e76fa47457cb44a41ed6249b2a5d192da2a86c52b2b2","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"3d906c4bfec46b84b36e9e91f1529870a579af97c65079606b3a1681181a896b"} +{"hash_algorithm":"sha256","ts":"2026-10-21T22:19:53.971Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"7a6225902f81bc40eed1bcd8","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T22:19:53.971Z"},{"op":"add","path":"/metadata/comments","value":[{"created_at":"2026-10-21T22:19:53.971Z","author":"codex-agent","text":"Overlap check: lock and initialization searches found no open item covering safe recovery from incomplete lock publication. This is distinct from text ownership refusal parity and completed owner-death locking work. The two measured failures are native update/comment at fsync ordinal 1, restart exit 4; all acknowledged item/history bytes remain intact. Repair policy is deliberately separate from the crash evidence harness."}]}],"before_hash":"db27715e00c2d5012414e76fa47457cb44a41ed6249b2a5d192da2a86c52b2b2","after_hash":"b39c4bf687b31e5f707380fa4dcecee223b9f2cb6f40dc2afda50eef081023ae","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"c19988e167b58bf649246b5811c5cb1e65842fabaaf266bc118d881156ec601a"} +{"hash_algorithm":"sha256","ts":"2026-10-21T23:03:18.649Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"066c6d55f017007f41e896af","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"claim","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T23:03:18.649Z"},{"op":"add","path":"/metadata/assignee","value":"codex-agent"},{"op":"add","path":"/metadata/claim_principal","value":"codex-agent"}],"before_hash":"b39c4bf687b31e5f707380fa4dcecee223b9f2cb6f40dc2afda50eef081023ae","after_hash":"016995b7c7f9dddb277d66c47fd78fa108f64f23712e95a0dc8e5f0758c20cb5","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"3a553679a04dc2aba19fc47ff9f2200769fa67278f8125e29adb89775ac64e39"} +{"hash_algorithm":"sha256","ts":"2026-10-21T23:03:23.471Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"066c6d55f017007f41e896af","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T23:03:23.471Z"},{"op":"replace","path":"/metadata/status","value":"in_progress"}],"before_hash":"016995b7c7f9dddb277d66c47fd78fa108f64f23712e95a0dc8e5f0758c20cb5","after_hash":"54932410de76fea18565c154eacc12a04716a649c8ace69b693500c9c4ec7186","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"3a3fafcb36a05de13b7e12444c4e171081607fbff9df707cc4c9311ae196fbf8"} +{"hash_algorithm":"sha256","ts":"2026-10-21T23:04:13.762Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"066c6d55f017007f41e896af","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/1","value":{"created_at":"2026-10-21T23:04:13.757Z","author":"codex-agent","text":"Read both retained same-family update/comment fsync ordinal 1 cases and #104. The first fsync follows a complete owner-payload write; the fresh empty publication gate prevents the existing confirmed-dead-owner recovery. Plan: while holding the existing kernel lease, admit identity-checked empty-gate reclamation only for a confirmed dead same-host owner; retain TTL and unknown/live/nonempty-gate refusals. Add permanent independent replay cases, require every measured boundary to recover, and verify 50 simultaneous writers without Git. Targeted checks locally; unchanged-threshold release-check matrix remains the full CI gate. This branch builds on #104 and must merge after it."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T23:04:13.762Z"}],"before_hash":"54932410de76fea18565c154eacc12a04716a649c8ace69b693500c9c4ec7186","after_hash":"947b499d3b6c760b7fc8d7e5be5962f0802b152e20a31c3474a49e39b3e7df24","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"da8a3886e2451056bc2414ba0113ef9eb78d20963fcf702970e6e0abaa880a30"} +{"hash_algorithm":"sha256","ts":"2026-10-21T23:07:56.575Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"066c6d55f017007f41e896af","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"add","path":"/metadata/files/1","value":{"path":"src/mutation.rs","scope":"project","note":"Lease-protected early empty-gate recovery for confirmed dead owners"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T23:07:56.575Z"}],"before_hash":"947b499d3b6c760b7fc8d7e5be5962f0802b152e20a31c3474a49e39b3e7df24","after_hash":"c93e87670e140a07c719c0c80140ba6376b0c2fcdccb503d9de5bd08d0ac8b6a","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"1a7eb2d86cc7697bb4ed95394c31a2c1c7c02d318506f8d85d0a65c0738d0656"} +{"hash_algorithm":"sha256","ts":"2026-10-21T23:08:12.986Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"066c6d55f017007f41e896af","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"add","path":"/metadata/files/2","value":{"path":"tests/support/mutation_unit.rs","scope":"project","note":"Live unknown occupied and held-lease refusal controls"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T23:08:12.986Z"}],"before_hash":"c93e87670e140a07c719c0c80140ba6376b0c2fcdccb503d9de5bd08d0ac8b6a","after_hash":"6a5193cf392d573d066f907a1d2ee58d336528c739e3994813dbe34df8588047","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"6c60946a8833dc5a43c0b92efceba604b050fafffc55b060788971e32f5ee959"} +{"hash_algorithm":"sha256","ts":"2026-10-21T23:08:27.877Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"066c6d55f017007f41e896af","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/2/note","value":"Exact oracle refusal receipts and authoritative byte preservation"},{"op":"replace","path":"/metadata/files/2/path","value":"tests/parity_1021_contract.rs"},{"op":"add","path":"/metadata/files/3","value":{"path":"tests/support/mutation_unit.rs","scope":"project","note":"Live unknown occupied and held-lease refusal controls"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T23:08:27.877Z"}],"before_hash":"6a5193cf392d573d066f907a1d2ee58d336528c739e3994813dbe34df8588047","after_hash":"14c579c0c87be918686213fbe1fdf3f012acddcafdc5885c39ab8aa1e7d59bcd","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"a50db296cec54fa95f6a31a9b78ccf646621e71f3fc609968e71f5ec786051ee"} +{"hash_algorithm":"sha256","ts":"2026-10-21T23:08:39.152Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"066c6d55f017007f41e896af","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/3/note","value":"Exact oracle refusal receipts and authoritative byte preservation"},{"op":"replace","path":"/metadata/files/3/path","value":"tests/parity_1021_contract.rs"},{"op":"replace","path":"/metadata/files/2/note","value":"Fifty writers retain all 150 comments and history chain"},{"op":"replace","path":"/metadata/files/2/path","value":"tests/concurrency_contract.rs"},{"op":"add","path":"/metadata/files/4","value":{"path":"tests/support/mutation_unit.rs","scope":"project","note":"Live unknown occupied and held-lease refusal controls"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T23:08:39.152Z"}],"before_hash":"14c579c0c87be918686213fbe1fdf3f012acddcafdc5885c39ab8aa1e7d59bcd","after_hash":"00049dd0816332424a21d8f3b7df519a77c91f6a0c69a9c173576d67df6619ce","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"c82ddb79d56d99580dadc8cda49cd1c8cb84dca5dac0114c113e55610caa8155"} +{"hash_algorithm":"sha256","ts":"2026-10-21T23:08:49.208Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"066c6d55f017007f41e896af","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/4/note","value":"Exact oracle refusal receipts and authoritative byte preservation"},{"op":"replace","path":"/metadata/files/4/path","value":"tests/parity_1021_contract.rs"},{"op":"replace","path":"/metadata/files/3/note","value":"Permanent first-fsync update reproduction"},{"op":"replace","path":"/metadata/files/3/path","value":"tests/fixtures/crash-restart/update.json"},{"op":"add","path":"/metadata/files/5","value":{"path":"tests/support/mutation_unit.rs","scope":"project","note":"Live unknown occupied and held-lease refusal controls"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T23:08:49.208Z"}],"before_hash":"00049dd0816332424a21d8f3b7df519a77c91f6a0c69a9c173576d67df6619ce","after_hash":"35793d9174999fc3e4efa0ef9eedd3b97568707a83e706ad988347d3a9ecacaa","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"f0a1cb98c4fc2c28ab79acb04b58dcb2d6e24f6e3ca2aca04c8d2ec3037637df"} +{"hash_algorithm":"sha256","ts":"2026-10-21T23:09:02.019Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"066c6d55f017007f41e896af","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/5/note","value":"Exact oracle refusal receipts and authoritative byte preservation"},{"op":"replace","path":"/metadata/files/5/path","value":"tests/parity_1021_contract.rs"},{"op":"replace","path":"/metadata/files/4/note","value":"Permanent first-fsync update reproduction"},{"op":"replace","path":"/metadata/files/4/path","value":"tests/fixtures/crash-restart/update.json"},{"op":"replace","path":"/metadata/files/3/note","value":"Permanent first-fsync comment reproduction"},{"op":"replace","path":"/metadata/files/3/path","value":"tests/fixtures/crash-restart/comment.json"},{"op":"add","path":"/metadata/files/6","value":{"path":"tests/support/mutation_unit.rs","scope":"project","note":"Live unknown occupied and held-lease refusal controls"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T23:09:02.019Z"}],"before_hash":"35793d9174999fc3e4efa0ef9eedd3b97568707a83e706ad988347d3a9ecacaa","after_hash":"860c963fb03c345074086d033577d4e49f76fb306feef5b0eaf198c4c36ec455","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"455f8233f341e16a0595c901ce05595baf4f85e5e7d5796d438b78f1ab496aa2"} +{"hash_algorithm":"sha256","ts":"2026-10-21T23:09:43.440Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"066c6d55f017007f41e896af","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":null,"topic":null},"op":"docs_add","patch":[{"op":"replace","path":"/metadata/docs/0/note","value":"Early gate recovery policy independent replay commands and fifty-writer acceptance"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T23:09:43.440Z"}],"before_hash":"860c963fb03c345074086d033577d4e49f76fb306feef5b0eaf198c4c36ec455","after_hash":"8135dcb74b6f98a24929d85f5c96a7ac7efa560cc2ca5c2a5f50d976edd14a05","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"7f2d1c98eebe759a356c75f3c9a4db3c49700568ab3d6be073df64c1c6693ddc"} +{"hash_algorithm":"sha256","ts":"2026-10-21T23:09:52.999Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"066c6d55f017007f41e896af","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/6/note","value":"Exact oracle refusal receipts and authoritative byte preservation"},{"op":"replace","path":"/metadata/files/6/path","value":"tests/parity_1021_contract.rs"},{"op":"replace","path":"/metadata/files/5/note","value":"Permanent first-fsync update reproduction"},{"op":"replace","path":"/metadata/files/5/path","value":"tests/fixtures/crash-restart/update.json"},{"op":"replace","path":"/metadata/files/4/note","value":"Minimal synthetic fixture shared by permanent reproductions"},{"op":"replace","path":"/metadata/files/4/path","value":"tests/fixtures/crash-restart/fixture/.agents/pm/settings.json"},{"op":"add","path":"/metadata/files/7","value":{"path":"tests/support/mutation_unit.rs","scope":"project","note":"Live unknown occupied and held-lease refusal controls"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T23:09:52.999Z"}],"before_hash":"8135dcb74b6f98a24929d85f5c96a7ac7efa560cc2ca5c2a5f50d976edd14a05","after_hash":"1959afda71a15f40d75e45ab902100af2f3fd6d937d66544da61ea3f0c339d53","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"f22cad102f4dce53e32aa230864b0dbbcd3eb91e1668b6c598fb306b79566b48"} +{"hash_algorithm":"sha256","ts":"2026-10-21T23:11:32.546Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"066c6d55f017007f41e896af","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":null,"topic":null},"op":"tests_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T23:11:32.546Z"},{"op":"add","path":"/metadata/tests","value":[{"command":"node --test scripts/crash-harness.test.mts","scope":"project","timeout_seconds":300,"provenance":{"author":"codex-agent","created_at":"2026-10-21T23:11:31.260Z","source_kind":"local_mutation","source_ref":"fix/crash-restart-recovery"}}]}],"before_hash":"1959afda71a15f40d75e45ab902100af2f3fd6d937d66544da61ea3f0c339d53","after_hash":"1b8b9dd02e137081a3947f8734dc206a455a3ff89b50cfb38d589bef61df89d7","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"366cb9f239d22f0d1baa358c96e5b5096542c46bbee6b633f017029744f5c92d"} +{"hash_algorithm":"sha256","ts":"2026-10-21T23:11:44.556Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"066c6d55f017007f41e896af","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":null,"topic":null},"op":"tests_add","patch":[{"op":"add","path":"/metadata/tests/1","value":{"command":"env CARGO_BUILD_JOBS=2 PM_RUST_REQUIRE_PUBLISHED_CLI=1 cargo +1.90.0 test --locked --test concurrency_contract --test parity_1021_contract","scope":"project","timeout_seconds":900,"provenance":{"author":"codex-agent","created_at":"2026-10-21T23:11:44.224Z","source_kind":"local_mutation","source_ref":"fix/crash-restart-recovery"}}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T23:11:44.556Z"}],"before_hash":"1b8b9dd02e137081a3947f8734dc206a455a3ff89b50cfb38d589bef61df89d7","after_hash":"27853488b2fd44d3189622f7982536d3bcc52208cfa01c9a7492a78ae679bd89","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"0f3072e1ec1322ed3ff49279d0c0d48981d49540b97d5c022d8c30fe7fd3ef02"} +{"hash_algorithm":"sha256","ts":"2026-10-21T23:12:23.323Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"066c6d55f017007f41e896af","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":null,"topic":null},"op":"tests_add","patch":[{"op":"add","path":"/metadata/tests/2","value":{"command":"env CARGO_BUILD_JOBS=2 cargo +1.90.0 test --locked --lib gate","scope":"project","timeout_seconds":900,"provenance":{"author":"codex-agent","created_at":"2026-10-21T23:12:22.808Z","source_kind":"local_mutation","source_ref":"fix/crash-restart-recovery"}}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T23:12:23.323Z"}],"before_hash":"27853488b2fd44d3189622f7982536d3bcc52208cfa01c9a7492a78ae679bd89","after_hash":"866846708f72419b5aa836fb6ff9fff1cefda745c1ebee8e12a8cb4465cb3dfa","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"78268d12d6b24850827e6bd8d8dd4e1aacb698777562f05357c3befe5e723653"} +{"hash_algorithm":"sha256","ts":"2026-10-21T23:18:56.124Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"066c6d55f017007f41e896af","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":null,"topic":null},"op":"docs_add","patch":[{"op":"add","path":"/metadata/docs/1","value":{"path":"docs/CRASH_RESTART_RECOVERY_2026_10_22.json","scope":"project","note":"Twenty measured SIGKILL recoveries preserve exact state history prefixes and raw restart receipt hashes"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T23:18:56.124Z"}],"before_hash":"866846708f72419b5aa836fb6ff9fff1cefda745c1ebee8e12a8cb4465cb3dfa","after_hash":"392534b2bac486b533ce86ade5464a49c55641c49e8613d3d55cc0a7fae548e2","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"621a392fc054bf570b8b260c3bc601d71068bebcde0c4a0573c7e3ea0241969f"} +{"hash_algorithm":"sha256","ts":"2026-10-21T23:19:03.468Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"066c6d55f017007f41e896af","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/2","value":{"created_at":"2026-10-21T23:19:03.467Z","author":"codex-agent","text":"The corrected synthetic corpus passes 20/20 injected kills with 20/20 acknowledged writes, history prefixes, exact reference states and restart receipts; both cross-runtime baseline byte and acknowledgement comparisons pass. The separate permanent update/comment first-fsync replay tests pass 2/2; the fresh-gate owner and lease guard unit test passes. The initial minimal fixture failed oracle settings validation and was corrected before proof. A default-budget standalone run timed out under local load; the supported 60000 ms process budget completed the corpus. No recovery invariant or coverage threshold was relaxed. Targeted Rust acceptance and behavioral revert proof are next."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T23:19:03.468Z"}],"before_hash":"392534b2bac486b533ce86ade5464a49c55641c49e8613d3d55cc0a7fae548e2","after_hash":"03f7831c35f29f1783a988dee4e26107b652056ebf92c8a7e505f9f89220908f","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"941e8a626779c3f3bd651b8df6d9c1fa0e8c3754772c1ac4097137902f47fd17"} +{"hash_algorithm":"sha256","ts":"2026-10-21T23:26:28.014Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"066c6d55f017007f41e896af","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T23:26:28.014Z"},{"op":"replace","path":"/metadata/description","value":"The first native lock fsync follows a complete owner payload but leaves a fresh empty publication gate on process death. That gate blocks existing confirmed-dead-owner recovery and causes update/comment restart exit 4 while acknowledged item/history bytes remain intact. Recover only under the kernel lease with verified same-host original-process death and identity-checked empty-gate reclamation. Preserve unknown/live-owner refusals, immutable histories, exact restart references and the 50-writer no-loss contract; builds on #104."}],"before_hash":"03f7831c35f29f1783a988dee4e26107b652056ebf92c8a7e505f9f89220908f","after_hash":"92f84f4995b65eb4aed70f133c6ac9146af6b3a96a4121c03eb3e9ba43abd478","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"67d3607606b306af8a6688791af4a1cad152355bc936576927d7ced36c0c9bc7"} +{"hash_algorithm":"sha256","ts":"2026-10-21T23:27:16.217Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"066c6d55f017007f41e896af","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":null,"topic":null},"op":"docs_add","patch":[{"op":"add","path":"/metadata/docs/2","value":{"path":"docs/CRASH_RESTART_REVERT_PROOF_2026_10_22.json","scope":"project","note":"Only acquire_lock_attempt reverted and rebuilt yields two restart-exit-4 failures; restored and rebuilt yields two passes"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T23:27:16.217Z"}],"before_hash":"92f84f4995b65eb4aed70f133c6ac9146af6b3a96a4121c03eb3e9ba43abd478","after_hash":"c1f14c5104cb9a3dd46e0f05ced2d02d3c440a1ab05599750adb6e75fc987e46","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"d956a042d4b7da5577f96b5594898ab191b3a93eebbc52132746e469ef40acaa"} +{"hash_algorithm":"sha256","ts":"2026-10-21T23:27:21.449Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"066c6d55f017007f41e896af","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/3","value":{"created_at":"2026-10-21T23:27:21.449Z","author":"codex-agent","text":"Behavioral revert proof complete: replaced only mutation::acquire_lock_attempt with the #104 implementation, rebuilt using CARGO_BUILD_JOBS=2 cargo +1.90.0 build --locked --bin pm-rust, and ran node --test --test-name-pattern='retained (update|comment)' scripts/crash-harness.test.mts. Reverted: 0 passed, 2 failed, both actual restart exits 4. Restored and rebuilt: 2 passed, 0 failed. Tests and fixtures were unchanged. Full harness via the pinned PM linked runner passes 16/16 with zero skips; gate-focused unit selection passes 6/6; 50 writers preserve all 150 comments and the exact history chain. Shared-path ownership JSON refusal comparison passes exactly. The separate pre-existing comment diagnostic mismatch is retained on pm-rust-comment-lock-receipt; native legacy formatting is unchanged here. No PM CLI/SDK defect was identified."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T23:27:21.449Z"}],"before_hash":"c1f14c5104cb9a3dd46e0f05ced2d02d3c440a1ab05599750adb6e75fc987e46","after_hash":"5f3ff4b96911d6ad69dcece431b7a8418d6b644b5a8f90d1d57515da42cdd0c7","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"d07c03b079659067c04280901c26492a830ea6cbf0e82216cc10db95c235eae4"} +{"hash_algorithm":"sha256","ts":"2026-10-21T23:28:46.933Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"066c6d55f017007f41e896af","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/4","value":{"created_at":"2026-10-21T23:28:46.932Z","author":"codex-agent","text":"Final pinned PM linked Rust acceptance passes: cargo +1.90.0 test --locked --test concurrency_contract --test parity_1021_contract with CARGO_BUILD_JOBS=2 and PM_RUST_REQUIRE_PUBLISHED_CLI=1 runs 2/2 concurrency tests plus 14/14 October 21 contract tests, zero failed or ignored. All 50 simultaneous writers retain 150/150 comments and history links without Git. Full harness 16/16, gate-focused unit selection 6/6 and restored independent replay tests 2/2 pass. Strict targeted Clippy, formatting, TypeScript checking, ESLint, changelog generation/check and immutable tracker-history prefix checks pass. Full unchanged-threshold release-check is delegated to the Linux/macOS/Windows CI matrix as requested; no merge or release."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T23:28:46.933Z"}],"before_hash":"5f3ff4b96911d6ad69dcece431b7a8418d6b644b5a8f90d1d57515da42cdd0c7","after_hash":"836e541fc930a461bf1765de8b8b6869bc83c525a334049620af8cdedd386c8a","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"e6dc6452fd76bfb60602626814689b050756390f241e242db60ff0a37ea85107"} +{"hash_algorithm":"sha256","ts":"2026-10-21T23:29:45.183Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"066c6d55f017007f41e896af","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/5","value":{"created_at":"2026-10-21T23:29:45.182Z","author":"codex-agent","text":"Local check-only health reports inherited history-chain warnings for pm-rust-bj7a and pm-rust-unih. An independent plain-directory archive of the unchanged #104 head reports the same warnings. Guidance/provenance/stale-item advisories and shared configuration drift also remain. No existing history was repaired or rewritten; the original restart-item stream remains an exact prefix of the appended history. CI's existing clone-local health setup remains authoritative for its gate. This is separate from the passing recovery and concurrency evidence."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T23:29:45.183Z"}],"before_hash":"836e541fc930a461bf1765de8b8b6869bc83c525a334049620af8cdedd386c8a","after_hash":"b4824d430f9b9dc1c6aa08b1432ac5bf39e790389726f06a0ddadffc7d435109","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"899cb6eb0850c38acdcfabe29e6c9c9088c7d1be618d1a5266ac26ebaad2398a"} +{"hash_algorithm":"sha256","ts":"2026-10-21T23:35:14.434Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"066c6d55f017007f41e896af","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/6","value":{"created_at":"2026-10-21T23:35:14.434Z","author":"codex-agent","text":"Implementation and local verification are complete in PR https://github.com/unbraind/pm-rust/pull/105, based on main and explicitly dependent on #104 merging first. The final verification receipt will be kept in that PR with the exact-head CI run URL, per-platform test totals and all four coverage counts, linked from docs/CRASH_HARNESS.md. The Linux/macOS/Windows release-check matrix is still pending; no pending, canceled, skipped or unavailable reviewer result is treated as approval. Releasing the implementation claim for orchestrator verification while retaining in_progress status. No merge or release."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T23:35:14.434Z"}],"before_hash":"b4824d430f9b9dc1c6aa08b1432ac5bf39e790389726f06a0ddadffc7d435109","after_hash":"101f4a7ac401be0b650d896f26db3f4e9ab17fa20edf3b1fa9a1637b4d1b5fa1","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"696a22e6b85196fdc9693c8d54b95ba69d0da764de76e9dc64e1836bf9b3a0a1"} +{"hash_algorithm":"sha256","ts":"2026-10-21T23:35:15.935Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"066c6d55f017007f41e896af","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-21T23:35:15.935Z"}],"before_hash":"101f4a7ac401be0b650d896f26db3f4e9ab17fa20edf3b1fa9a1637b4d1b5fa1","after_hash":"e4b720f8c0349a34d68988cba2939200d4f1e65503b1d79d273d7bc31be44d3f","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"0b3d60d0ab67cc9aa990b7547fc9f7d67c9e155792c2d340792656c71cfa8154"} +{"hash_algorithm":"sha256","ts":"2026-10-22T11:33:30.725Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_instance":"9cd644682823b57766dff4f3","agent_provenance":{"model":null,"effort":null,"role":{"value":"implementer","source":"argv"},"topic":null},"op":"claim","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-22T11:33:30.725Z"},{"op":"add","path":"/metadata/assignee","value":"codex-agent"},{"op":"add","path":"/metadata/claim_principal","value":"codex-agent"}],"before_hash":"e4b720f8c0349a34d68988cba2939200d4f1e65503b1d79d273d7bc31be44d3f","after_hash":"559aac54c82cb604d60c11b3a70f0453edca82fc2d788235265f3ff449ce0088","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"e8dfc8e8b8c1a72e2d33a3fd1286b289553cea98c4afc36f992816f3c6039af8"} +{"hash_algorithm":"sha256","ts":"2026-10-22T11:34:12.929Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_instance":"9cd644682823b57766dff4f3","agent_provenance":{"model":null,"effort":null,"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/7","value":{"created_at":"2026-10-22T11:34:12.928Z","author":"codex-agent","text":"PR 105 integration round: pulled the remote head, confirmed zero review threads and no substantive CodeRabbit review body, then merged current origin/main a600bd8 and the completed PR 104 branch at 328ff009483d754b3e25412580495378025d5d87. Both merges were clean; main contributes its audited tracker reconciliation and the harness contributes the three typed-refusal fixes with four regressions. No recovery code or thresholds were changed in this round. Preserved the strict every-boundary recovery assertions and both permanent first-fsync replays. Running current-binary build, strict TypeScript/lint and the pinned-PM linked full crash harness, then the unchanged full release gate in CI given the demonstrated local cold-build contention. Existing original recovery revert receipts remain immutable."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-22T11:34:12.929Z"}],"before_hash":"559aac54c82cb604d60c11b3a70f0453edca82fc2d788235265f3ff449ce0088","after_hash":"93aae2012354ff97171c84b5fe668780116fde363a1efb76c2cf5b4b3bf7354a","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"520d348446b151ae94c69bd081088a4e4a58ad11f68977b8369bb1a348b0eeba"} +{"hash_algorithm":"sha256","ts":"2026-10-22T11:42:02.323Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_instance":"9cd644682823b57766dff4f3","agent_provenance":{"model":null,"effort":null,"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/8","value":{"created_at":"2026-10-22T11:42:02.323Z","author":"codex-agent","text":"Merged-head local acceptance passes: current native binary build; strict erasable TypeScript, ESLint, Rust fmt, diff check and isolated pinned-PM health. The existing linked full crash-harness command passed 20/20, zero failed or skipped, in about 215 seconds within its unchanged stored 300-second budget. This includes every measured crash boundary, exact acknowledged bytes/history/receipts, both permanent first-fsync replays and all four merged refusal regressions. No recovery function, original history, fixture, dependency pin or threshold was changed by this integration round, so no new behavioral-revert control is required; the original recovery proof and PR 104 guard proofs remain linked. Pushing the integrated candidate for unchanged just release-check in CI; the full gate is not yet claimed as passed."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-22T11:42:02.323Z"}],"before_hash":"93aae2012354ff97171c84b5fe668780116fde363a1efb76c2cf5b4b3bf7354a","after_hash":"451ecb9946a45fcad074158fbf5eecf8bdcf5aa2b0d9ccea990a39294077acf3","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"5fa598b12e7a168f91eaa84d8f596626d3f62c2eda940e8380a950ca0e5cfbbf"} +{"hash_algorithm":"sha256","ts":"2026-10-22T12:12:14.935Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_instance":"9cd644682823b57766dff4f3","agent_provenance":{"model":null,"effort":null,"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/9","value":{"created_at":"2026-10-22T12:12:14.934Z","author":"codex-agent","text":"Integrated code head 46178e2dae6468d1f71e18a23c9e608e19a48a56 passed the full Linux just release-check gate (https://github.com/unbraind/pm-rust/actions/runs/39772686608/job/119803873398). Ordinary and instrumented Rust runs each passed 580 tests across 22 suites, zero failed or ignored. Rust lines 14030/14030, functions 1467/1467, regions 23449/23449 and branches 3298/3298 are all 100 percent at unchanged thresholds. Strict TypeScript, ESLint and the merged Linux crash harness passed 20/20, zero failed or skipped, inside that gate. Audit, changelog and strict tracker health passed; the existing allowed RUSTSEC-2024-0449 advisory remains. macOS full gate also passed. This round has no review findings and no new recovery-code edits; original two-failure/two-restored recovery proof and merged PR 104 guard proofs remain intact. Releasing the claim and retaining in_progress; final receipt-head CI will be linked from PR 105. No replies/resolutions, PR merge or release."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-22T12:12:14.935Z"}],"before_hash":"451ecb9946a45fcad074158fbf5eecf8bdcf5aa2b0d9ccea990a39294077acf3","after_hash":"d928aa7af191f3ace4e6cda08e756311e62a58232e817cc0a91b9685d948df4d","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"b40fff829bc44c35fef9ef4b9a26f0c17bc6a0ba496bc6c38ea7b3aa8e7afab3"} +{"hash_algorithm":"sha256","ts":"2026-10-22T12:12:20.661Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_instance":"9cd644682823b57766dff4f3","agent_provenance":{"model":null,"effort":null,"role":{"value":"implementer","source":"argv"},"topic":null},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-22T12:12:20.661Z"}],"before_hash":"d928aa7af191f3ace4e6cda08e756311e62a58232e817cc0a91b9685d948df4d","after_hash":"468ee4a645fe0f420baca7fe4a7d0cc54572ada3a809357a4f7f76641085c71d","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"9cc79551d46a3d25afc38ee2fe3211837ed3ba4a1b19fbbef10f25dde4361525"} +{"hash_algorithm":"sha256","ts":"2026-10-22T17:21:55.729Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_instance":"9cd644682823b57766dff4f3","agent_provenance":{"model":null,"effort":null,"role":{"value":"implementer","source":"argv"},"topic":null},"op":"claim","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-22T17:21:55.729Z"},{"op":"add","path":"/metadata/assignee","value":"codex-agent"},{"op":"add","path":"/metadata/claim_principal","value":"codex-agent"}],"before_hash":"468ee4a645fe0f420baca7fe4a7d0cc54572ada3a809357a4f7f76641085c71d","after_hash":"cca47a1b3209a7c4e513a46220bc843038b0b585acb5830b4f455d66ac5d2005","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"4cbf9ce970d1b3448b2a5060ce3533b76b0f76f866ca82d4f56e75ba1f70abb6"} +{"hash_algorithm":"sha256","ts":"2026-10-22T17:21:57.057Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_instance":"9cd644682823b57766dff4f3","agent_provenance":{"model":null,"effort":null,"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/10","value":{"created_at":"2026-10-22T17:21:57.057Z","author":"codex-agent","text":"Original stacked-PR instruction, follow-up to the completed PR 104 review round: pulled PR 105 remote head 53e4988, confirmed zero review threads and no substantive CodeRabbit review, verified main is already an ancestor, then merged PR 104 final head f555a4238fc4d0fa5768feaee0e81dce66031202 after all nine of its exact-head CI jobs passed. The merge is clean and brings the baseline_parity replay guide/docstring correction plus two documentation regressions and LF/CRLF checks. No Rust source changed in this integration. Existing recovery invariants, two permanent first-fsync replays and original independent recovery revert proof remain intact. Running current-binary build, strict TypeScript/lint and the unchanged 300-second linked full crash harness, followed by the full unchanged release gate."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-22T17:21:57.057Z"}],"before_hash":"cca47a1b3209a7c4e513a46220bc843038b0b585acb5830b4f455d66ac5d2005","after_hash":"793703bab84add76f5673941b9e6e2ffa71077f7a4c0a57350c8eef91b4f8ba6","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"ab41b719737ca5b29149d42f3884d512805b123ba47ff2718b3bb04b72683037"} +{"hash_algorithm":"sha256","ts":"2026-10-22T17:23:47.550Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_instance":"9cd644682823b57766dff4f3","agent_provenance":{"model":null,"effort":null,"role":null,"topic":null},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/7/note","value":"Exact oracle refusal receipts and authoritative byte preservation"},{"op":"replace","path":"/metadata/files/7/path","value":"tests/parity_1021_contract.rs"},{"op":"replace","path":"/metadata/files/6/note","value":"Permanent first-fsync update reproduction"},{"op":"replace","path":"/metadata/files/6/path","value":"tests/fixtures/crash-restart/update.json"},{"op":"replace","path":"/metadata/files/5/note","value":"Minimal synthetic fixture shared by permanent reproductions"},{"op":"replace","path":"/metadata/files/5/path","value":"tests/fixtures/crash-restart/fixture/.agents/pm/settings.json"},{"op":"replace","path":"/metadata/files/4/note","value":"Permanent first-fsync comment reproduction"},{"op":"replace","path":"/metadata/files/4/path","value":"tests/fixtures/crash-restart/comment.json"},{"op":"replace","path":"/metadata/files/3/note","value":"Fifty writers retain all 150 comments and history chain"},{"op":"replace","path":"/metadata/files/3/path","value":"tests/concurrency_contract.rs"},{"op":"replace","path":"/metadata/files/2/note","value":"Lease-protected early empty-gate recovery for confirmed dead owners"},{"op":"replace","path":"/metadata/files/2/path","value":"src/mutation.rs"},{"op":"replace","path":"/metadata/files/1/note","value":"Real incomplete-lock crash and replay reproduction"},{"op":"replace","path":"/metadata/files/1/path","value":"scripts/crash-harness.test.mts"},{"op":"remove","path":"/metadata/files/0/note"},{"op":"replace","path":"/metadata/files/0/path","value":"scripts/crash-harness.mts"},{"op":"add","path":"/metadata/files/8","value":{"path":"tests/support/mutation_unit.rs","scope":"project","note":"Live unknown occupied and held-lease refusal controls"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-22T17:23:47.550Z"}],"before_hash":"793703bab84add76f5673941b9e6e2ffa71077f7a4c0a57350c8eef91b4f8ba6","after_hash":"5340556d00d3968af87c0ae2f144e44ee9fda43362809cfc92f29aad30a15541","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"e0b76740f022c6b9f62c0fc3516aefc119713255c726f5df4e463a31bdc673bc"} +{"hash_algorithm":"sha256","ts":"2026-10-22T17:23:48.746Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_instance":"9cd644682823b57766dff4f3","agent_provenance":{"model":null,"effort":null,"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/11","value":{"created_at":"2026-10-22T17:23:48.746Z","author":"codex-agent","text":"Merged-head acceptance passes: current native build, strict erasable TypeScript, ESLint, required check-only tracker health, and the existing pinned-PM linked full crash suite. node --test scripts/crash-harness.test.mts passes 22/22 with zero failed/canceled/skipped in about 16 seconds inside its unchanged 300-second budget. Every measured crash boundary, both permanent update/comment first-fsync replays, exact bytes/history/receipts and the two merged guidance checks pass. All Rust source is byte-identical to 53e4988, so this integration adds no runtime fix or new runtime revert requirement; original recovery and inherited PR 104 documentation revert proofs remain valid and linked. Existing result-tracking policy is disabled; results are recorded here without changing it."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-22T17:23:48.746Z"}],"before_hash":"5340556d00d3968af87c0ae2f144e44ee9fda43362809cfc92f29aad30a15541","after_hash":"debf4faa413526903a47d523187b041f0fd838f50b919abb62d67ea2f4772ac0","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"73ebe88377b15049ad97a92f5e5601a8c9ff4de94ea953be8f54f79d120d63cf"} +{"hash_algorithm":"sha256","ts":"2026-10-22T17:23:49.343Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_instance":"9cd644682823b57766dff4f3","agent_provenance":{"model":null,"effort":null,"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/12","value":{"created_at":"2026-10-22T17:23:49.343Z","author":"codex-agent","text":"Releasing the integration claim with status retained in_progress for orchestrator verification. This branch now includes the completed PR 104 head f555a4238fc4d0fa5768feaee0e81dce66031202 and current main a600bd8. Fresh unchanged-threshold full just release-check CI will validate the pushed integration head; its exact receipts and counts will be recorded in PR https://github.com/unbraind/pm-rust/pull/105. Pending CI is not approval. No review reply/resolution, PR merge, release, dependency/threshold change or history rewrite."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-22T17:23:49.343Z"}],"before_hash":"debf4faa413526903a47d523187b041f0fd838f50b919abb62d67ea2f4772ac0","after_hash":"5b8e3cec535f2f09ef56a7de8b0460cf0375d8e154ffcfda7acef24682d226d7","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"cbff3dc8957cb52cdab8ac46cae81083e2280969f30c5dafa7507265d0e9eeb6"} +{"hash_algorithm":"sha256","ts":"2026-10-22T17:23:49.897Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_instance":"9cd644682823b57766dff4f3","agent_provenance":{"model":null,"effort":null,"role":{"value":"implementer","source":"argv"},"topic":null},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-22T17:23:49.897Z"}],"before_hash":"5b8e3cec535f2f09ef56a7de8b0460cf0375d8e154ffcfda7acef24682d226d7","after_hash":"ae21cd9f8076b750eed29f9604d22b79a640c07dec08fc85ad08340299f010f9","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"failed","reason":"resolver_failed","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"8c407573bb237caf90bc6b1478825b0e2cec4c14f29545aa48ee9de87c720f67"} +{"hash_algorithm":"sha256","ts":"2026-10-23T20:08:48.415Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"71244730038862278cb4ef7c","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"claim","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-23T20:08:48.415Z"},{"op":"add","path":"/metadata/assignee","value":"codex-agent"},{"op":"add","path":"/metadata/claim_principal","value":"codex-agent"}],"before_hash":"ae21cd9f8076b750eed29f9604d22b79a640c07dec08fc85ad08340299f010f9","after_hash":"981bd172ff5998cf31a685b168b3c34681e5f84b8f318a37af216ba4e4354d78","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"3a73cade3ceae95588d34572ff5cfa5e2592320b426de7c2e340bad8f9e8aa20"} +{"hash_algorithm":"sha256","ts":"2026-10-23T20:08:49.194Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"71244730038862278cb4ef7c","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-23T20:08:49.194Z"},{"op":"replace","path":"/metadata/tags/2","value":"gh:unbraind/pm-rust#105"},{"op":"add","path":"/metadata/tags/3","value":"locks"}],"before_hash":"981bd172ff5998cf31a685b168b3c34681e5f84b8f318a37af216ba4e4354d78","after_hash":"d75f9d9744237313559d80b6930690de0086c0b087b8055a01e79a2c07bac953","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"c7ae0661d7bc12aa1e91c2a3f3554ee65f8ed6ce8540bfa8f4e261399c32f793"} +{"hash_algorithm":"sha256","ts":"2026-10-23T20:08:49.952Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"71244730038862278cb4ef7c","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/13","value":{"created_at":"2026-10-23T20:08:49.952Z","author":"codex-agent","text":"PR 105 review round starts only after PR 110 completed its full exact-head CI gates and push. Pulled remote recovery head ca52b3608505542519781d6709799c163d9e01de. GraphQL returns zero review threads and no CodeRabbit review body; the latest CodeRabbit issue comment explicitly skips automatic review. The branch is four main commits behind and GitHub reports conflicts. Merge origin/main before code review, preserve both append-only tracker streams and the recovery/harness stack, regenerate the changelog inside the merge, then verify the complete PR diff, focused regressions and unchanged native release gate. Existing recovery and crash-boundary evidence remains historical until checked against the integrated candidate."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-23T20:08:49.952Z"}],"before_hash":"d75f9d9744237313559d80b6930690de0086c0b087b8055a01e79a2c07bac953","after_hash":"aca1e913031eab87c65daac321685ee927f8f87a33d12d788b8ec773fce6a654","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"2524904ff4ac4b56031bf28734872a14e022d5210126d742089c8f442ce15e8e"} +{"hash_algorithm":"sha256","ts":"2026-10-23T20:17:48.878Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"71244730038862278cb4ef7c","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/8/note","value":"Exact oracle refusal receipts and authoritative byte preservation"},{"op":"replace","path":"/metadata/files/8/path","value":"tests/parity_1021_contract.rs"},{"op":"replace","path":"/metadata/files/7/note","value":"Permanent first-fsync update reproduction"},{"op":"replace","path":"/metadata/files/7/path","value":"tests/fixtures/crash-restart/update.json"},{"op":"replace","path":"/metadata/files/6/note","value":"Minimal synthetic fixture shared by permanent reproductions"},{"op":"replace","path":"/metadata/files/6/path","value":"tests/fixtures/crash-restart/fixture/.agents/pm/settings.json"},{"op":"replace","path":"/metadata/files/5/note","value":"Permanent first-fsync comment reproduction"},{"op":"replace","path":"/metadata/files/5/path","value":"tests/fixtures/crash-restart/comment.json"},{"op":"replace","path":"/metadata/files/4/note","value":"Fifty writers retain all 150 comments and history chain"},{"op":"replace","path":"/metadata/files/4/path","value":"tests/concurrency_contract.rs"},{"op":"replace","path":"/metadata/files/3/note","value":"Lease-protected early empty-gate recovery for confirmed dead owners"},{"op":"replace","path":"/metadata/files/3/path","value":"src/mutation.rs"},{"op":"replace","path":"/metadata/files/2/note","value":"Real incomplete-lock crash and replay reproduction"},{"op":"replace","path":"/metadata/files/2/path","value":"scripts/crash-harness.test.mts"},{"op":"remove","path":"/metadata/files/1/note"},{"op":"replace","path":"/metadata/files/1/path","value":"scripts/crash-harness.mts"},{"op":"replace","path":"/metadata/files/0/path","value":"justfile"},{"op":"add","path":"/metadata/files/9","value":{"path":"tests/support/mutation_unit.rs","scope":"project","note":"Live unknown occupied and held-lease refusal controls"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-23T20:17:48.878Z"}],"before_hash":"aca1e913031eab87c65daac321685ee927f8f87a33d12d788b8ec773fce6a654","after_hash":"4e3c32e9056df0f3d0e384b240f59e35bd01b18f6c34a0b6cf1bb16831da18cb","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"5dabe997ceea5c31c72bbd3a9264006a938986f201a91680a9846b0e63a376dd"} +{"hash_algorithm":"sha256","ts":"2026-10-23T20:17:49.609Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"71244730038862278cb4ef7c","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/9/note","value":"Exact oracle refusal receipts and authoritative byte preservation"},{"op":"replace","path":"/metadata/files/9/path","value":"tests/parity_1021_contract.rs"},{"op":"replace","path":"/metadata/files/8/note","value":"Permanent first-fsync update reproduction"},{"op":"replace","path":"/metadata/files/8/path","value":"tests/fixtures/crash-restart/update.json"},{"op":"replace","path":"/metadata/files/7/note","value":"Minimal synthetic fixture shared by permanent reproductions"},{"op":"replace","path":"/metadata/files/7/path","value":"tests/fixtures/crash-restart/fixture/.agents/pm/settings.json"},{"op":"replace","path":"/metadata/files/6/note","value":"Permanent first-fsync comment reproduction"},{"op":"replace","path":"/metadata/files/6/path","value":"tests/fixtures/crash-restart/comment.json"},{"op":"replace","path":"/metadata/files/5/note","value":"Fifty writers retain all 150 comments and history chain"},{"op":"replace","path":"/metadata/files/5/path","value":"tests/concurrency_contract.rs"},{"op":"replace","path":"/metadata/files/4/note","value":"Lease-protected early empty-gate recovery for confirmed dead owners"},{"op":"replace","path":"/metadata/files/4/path","value":"src/mutation.rs"},{"op":"replace","path":"/metadata/files/3/note","value":"Real incomplete-lock crash and replay reproduction"},{"op":"replace","path":"/metadata/files/3/path","value":"scripts/crash-harness.test.mts"},{"op":"remove","path":"/metadata/files/2/note"},{"op":"replace","path":"/metadata/files/2/path","value":"scripts/crash-harness.mts"},{"op":"replace","path":"/metadata/files/1/path","value":"justfile"},{"op":"replace","path":"/metadata/files/0/path","value":".gitignore"},{"op":"add","path":"/metadata/files/10","value":{"path":"tests/support/mutation_unit.rs","scope":"project","note":"Live unknown occupied and held-lease refusal controls"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-23T20:17:49.609Z"}],"before_hash":"4e3c32e9056df0f3d0e384b240f59e35bd01b18f6c34a0b6cf1bb16831da18cb","after_hash":"a2944f6bcdb33ada6ff5c92c917ab19a7e2d4dca3ed163a4ddde19f3a6a5cd22","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"e3c43a5df93c266853dd650674dc1dc567c97351cab529bf1c4435d7a40ab84a"} +{"hash_algorithm":"sha256","ts":"2026-10-23T20:17:50.338Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"71244730038862278cb4ef7c","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"docs_add","patch":[{"op":"add","path":"/metadata/docs/3","value":{"path":"docs/CROSS_OPERATION_JOURNAL_RECOVERY.md","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-23T20:17:50.338Z"}],"before_hash":"a2944f6bcdb33ada6ff5c92c917ab19a7e2d4dca3ed163a4ddde19f3a6a5cd22","after_hash":"32d5446bddb923319b43699ed3672a288bf786607b920a2d11f52c0aca02b4e5","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"b9b88b1a15a6c3c698eacaef5ff28ab22f934aaff28937e9afcf2ccc7c485de2"} +{"hash_algorithm":"sha256","ts":"2026-10-23T20:17:51.120Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"71244730038862278cb4ef7c","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/14","value":{"created_at":"2026-10-23T20:17:51.120Z","author":"codex-agent","text":"Merged main 300eaa9a9d06f0e2cfc6afcfa8b66492a960a77b with three conflicts resolved: retain both ignore lists and crash/benchmark recipes; retain the main non-git native process helper plus the branch requirement of 50 concurrent writers and three comments each. Expanded the expected marker set to all 150 entries so the merged test remains coherent and strict. Behavioral negative control restored only the old expected-set behavior in the concurrency test function: it compiled and failed with exit 101, actual 150 markers versus expected 50; restored the full set afterward. The integrated recovery negative control removed only the early confirmed-dead-owner gate admission from acquire_lock_attempt, rebuilt, and ran node --test --test-name-pattern=\"retained (update|comment)\" scripts/crash-harness.test.mts: two failures, both restart exit 4. Restored source/binary passed both cases (2/2). Whole-PR review retained lock identity/lease/refusal controls, exact receipt/history checks, typed harness refusals and no-git execution. Updated one stale harness comment to describe fixed reference invocations after main introduced shared journal recovery. All 78 branch and 80 main history streams remain exact byte prefixes; no history rewrite or repair. Strict TypeScript, ESLint, merge-time changelog check, and isolated required check-only health pass. Full linked acceptance and unchanged full CI gate follow."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-23T20:17:51.120Z"}],"before_hash":"32d5446bddb923319b43699ed3672a288bf786607b920a2d11f52c0aca02b4e5","after_hash":"83ab2a6cd3a1ed960bd0877413f6320b9dd70067c5377ee941dc01460c3ed375","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"7fb9f7ce0b6e7dfab43319dcbb938b42a30617a03346f3665cd4fa5796ac58e5"} +{"hash_algorithm":"sha256","ts":"2026-10-23T20:23:14.015Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"71244730038862278cb4ef7c","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/15","value":{"created_at":"2026-10-23T20:23:14.015Z","author":"codex-agent","text":"Final integrated acceptance: all three pinned-PM linked commands passed, totaling 22 Linux crash-harness tests, 2 concurrency tests, 14 parity tests and 6 gate-focused unit tests (44 cases, zero failed or skipped). The concurrency assertion covers all 150 unique comments exactly once plus the complete 151-record history chain. Clippy retained its 100-line function limit: extracted the full expected marker set into documented expected_markers rather than relaxing lint. Repeating the helper-only stale-set behavioral control compiled and failed with exit 101, actual 150 versus expected 50; restored helper passed the linked 2+14 Rust acceptance again. Recovery proof remains two restart-exit-4 failures with only early dead-owner admission reverted, then two passes after restoring/rebuilding. Final strict all-target/all-feature Clippy, formatting, private rustdoc, strict erasable TypeScript, ESLint and isolated required tracker health pass. Merge-time changelog generation/check will be repeated after this final tracker write. Original JSON receipts and fixtures are untouched. The full unchanged just release-check matrix will run on the pushed integration head; no pending or skipped reviewer is counted as approval. Release the claim and retain in_progress for orchestrator verification."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-23T20:23:14.015Z"}],"before_hash":"83ab2a6cd3a1ed960bd0877413f6320b9dd70067c5377ee941dc01460c3ed375","after_hash":"ee3df2cead36f94d9f99c8a9c0b39888bf239de9d53fa0de3924c801e4222c19","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"6111b7f46fece7d04959bd80fb5ea6a115c60e547dfd7d682d8d5ab066076ea9"} +{"hash_algorithm":"sha256","ts":"2026-10-23T20:23:16.259Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"71244730038862278cb4ef7c","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-23T20:23:16.259Z"}],"before_hash":"ee3df2cead36f94d9f99c8a9c0b39888bf239de9d53fa0de3924c801e4222c19","after_hash":"598283bd0c0da67e946c722a58f9ba62f7248c865cd643acf3b5d5c7b79987d7","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"851d6766403b6d1be211af41f0344fc44ee912daa0530169b2b3eba714206dc0"} diff --git a/.agents/pm/tasks/pm-rust-comment-lock-receipt.toon b/.agents/pm/tasks/pm-rust-comment-lock-receipt.toon new file mode 100644 index 0000000..632020a --- /dev/null +++ b/.agents/pm/tasks/pm-rust-comment-lock-receipt.toon @@ -0,0 +1,11 @@ +id: pm-rust-comment-lock-receipt +title: Match native comment lock-conflict JSON diagnostics to the oracle +description: "A fresh initialization gate and a valid unverified owner receipt cause both native comment and oracle 2026.10.21 comment to refuse with exit 4 and code lock_conflict, preserving item and history bytes. After removing the oracle-only --json encoding switch, native detail and required guidance remain generic and refusal.surface is comment instead of comments. The generic formatter predates and is unchanged by the crash restart fix. This differs from pm-rust-4jq3, which covers text output for direct claim/release. Add a dedicated complete-receipt regression and repair the native diagnostic surface separately." +type: Task +status: open +priority: 3 +tags[3]: conformance,follow-up,locks +created_at: "2026-10-21T23:26:23.084Z" +updated_at: "2026-10-21T23:26:23.084Z" +author: codex-agent +body: "Found while verifying pm-rust-crash-lock-restart. Reproduce in a disposable non-git tracker with one sample-probe Task, a fresh locks/sample-probe.lock.stale-cleanup directory and a sample-probe.lock JSON receipt containing id, a live fixture PID, owner test-holder, token test-held, ttl_seconds 1800 and a current created_at, without host/start identity. Set PM_LOCK_WAIT_MS=0. Compare native comment sample-probe \"refused note\" --author test-agent with the oracle invocation plus --json. Normalize only that encoding flag in retry metadata. Both refuse safely; detail, required and refusal.surface differ. The ownership JSON refusal comparison on the shared gate path passes exactly and is retained with the recovery regression." diff --git a/.agents/pm/tasks/pm-rust-crash-lock-restart.toon b/.agents/pm/tasks/pm-rust-crash-lock-restart.toon new file mode 100644 index 0000000..7afdf78 --- /dev/null +++ b/.agents/pm/tasks/pm-rust-crash-lock-restart.toon @@ -0,0 +1,75 @@ +id: pm-rust-crash-lock-restart +title: Recover incomplete native lock initialization after process death +description: "The first native lock fsync follows a complete owner payload but leaves a fresh empty publication gate on process death. That gate blocks existing confirmed-dead-owner recovery and causes update/comment restart exit 4 while acknowledged item/history bytes remain intact. Recover only under the kernel lease with verified same-host original-process death and identity-checked empty-gate reclamation. Preserve unknown/live-owner refusals, immutable histories, exact restart references and the 50-writer no-loss contract; builds on #104." +type: Task +status: in_progress +priority: 2 +tags[4]: crash-consistency,follow-up,"gh:unbraind/pm-rust#105",locks +created_at: "2026-10-21T22:16:11.158Z" +updated_at: "2026-10-23T20:23:16.259Z" +author: codex-agent +comments[16]{created_at,author,text}: + "2026-10-21T22:19:53.971Z",codex-agent,"Overlap check: lock and initialization searches found no open item covering safe recovery from incomplete lock publication. This is distinct from text ownership refusal parity and completed owner-death locking work. The two measured failures are native update/comment at fsync ordinal 1, restart exit 4; all acknowledged item/history bytes remain intact. Repair policy is deliberately separate from the crash evidence harness." + "2026-10-21T23:04:13.757Z",codex-agent,"Read both retained same-family update/comment fsync ordinal 1 cases and #104. The first fsync follows a complete owner-payload write; the fresh empty publication gate prevents the existing confirmed-dead-owner recovery. Plan: while holding the existing kernel lease, admit identity-checked empty-gate reclamation only for a confirmed dead same-host owner; retain TTL and unknown/live/nonempty-gate refusals. Add permanent independent replay cases, require every measured boundary to recover, and verify 50 simultaneous writers without Git. Targeted checks locally; unchanged-threshold release-check matrix remains the full CI gate. This branch builds on #104 and must merge after it." + "2026-10-21T23:19:03.467Z",codex-agent,"The corrected synthetic corpus passes 20/20 injected kills with 20/20 acknowledged writes, history prefixes, exact reference states and restart receipts; both cross-runtime baseline byte and acknowledgement comparisons pass. The separate permanent update/comment first-fsync replay tests pass 2/2; the fresh-gate owner and lease guard unit test passes. The initial minimal fixture failed oracle settings validation and was corrected before proof. A default-budget standalone run timed out under local load; the supported 60000 ms process budget completed the corpus. No recovery invariant or coverage threshold was relaxed. Targeted Rust acceptance and behavioral revert proof are next." + "2026-10-21T23:27:21.449Z",codex-agent,"Behavioral revert proof complete: replaced only mutation::acquire_lock_attempt with the #104 implementation, rebuilt using CARGO_BUILD_JOBS=2 cargo +1.90.0 build --locked --bin pm-rust, and ran node --test --test-name-pattern='retained (update|comment)' scripts/crash-harness.test.mts. Reverted: 0 passed, 2 failed, both actual restart exits 4. Restored and rebuilt: 2 passed, 0 failed. Tests and fixtures were unchanged. Full harness via the pinned PM linked runner passes 16/16 with zero skips; gate-focused unit selection passes 6/6; 50 writers preserve all 150 comments and the exact history chain. Shared-path ownership JSON refusal comparison passes exactly. The separate pre-existing comment diagnostic mismatch is retained on pm-rust-comment-lock-receipt; native legacy formatting is unchanged here. No PM CLI/SDK defect was identified." + "2026-10-21T23:28:46.932Z",codex-agent,"Final pinned PM linked Rust acceptance passes: cargo +1.90.0 test --locked --test concurrency_contract --test parity_1021_contract with CARGO_BUILD_JOBS=2 and PM_RUST_REQUIRE_PUBLISHED_CLI=1 runs 2/2 concurrency tests plus 14/14 October 21 contract tests, zero failed or ignored. All 50 simultaneous writers retain 150/150 comments and history links without Git. Full harness 16/16, gate-focused unit selection 6/6 and restored independent replay tests 2/2 pass. Strict targeted Clippy, formatting, TypeScript checking, ESLint, changelog generation/check and immutable tracker-history prefix checks pass. Full unchanged-threshold release-check is delegated to the Linux/macOS/Windows CI matrix as requested; no merge or release." + "2026-10-21T23:29:45.182Z",codex-agent,Local check-only health reports inherited history-chain warnings for pm-rust-bj7a and pm-rust-unih. An independent plain-directory archive of the unchanged #104 head reports the same warnings. Guidance/provenance/stale-item advisories and shared configuration drift also remain. No existing history was repaired or rewritten; the original restart-item stream remains an exact prefix of the appended history. CI's existing clone-local health setup remains authoritative for its gate. This is separate from the passing recovery and concurrency evidence. + "2026-10-21T23:35:14.434Z",codex-agent,"Implementation and local verification are complete in PR https://github.com/unbraind/pm-rust/pull/105, based on main and explicitly dependent on #104 merging first. The final verification receipt will be kept in that PR with the exact-head CI run URL, per-platform test totals and all four coverage counts, linked from docs/CRASH_HARNESS.md. The Linux/macOS/Windows release-check matrix is still pending; no pending, canceled, skipped or unavailable reviewer result is treated as approval. Releasing the implementation claim for orchestrator verification while retaining in_progress status. No merge or release." + "2026-10-22T11:34:12.928Z",codex-agent,"PR 105 integration round: pulled the remote head, confirmed zero review threads and no substantive CodeRabbit review body, then merged current origin/main a600bd8 and the completed PR 104 branch at 328ff009483d754b3e25412580495378025d5d87. Both merges were clean; main contributes its audited tracker reconciliation and the harness contributes the three typed-refusal fixes with four regressions. No recovery code or thresholds were changed in this round. Preserved the strict every-boundary recovery assertions and both permanent first-fsync replays. Running current-binary build, strict TypeScript/lint and the pinned-PM linked full crash harness, then the unchanged full release gate in CI given the demonstrated local cold-build contention. Existing original recovery revert receipts remain immutable." + "2026-10-22T11:42:02.323Z",codex-agent,"Merged-head local acceptance passes: current native binary build; strict erasable TypeScript, ESLint, Rust fmt, diff check and isolated pinned-PM health. The existing linked full crash-harness command passed 20/20, zero failed or skipped, in about 215 seconds within its unchanged stored 300-second budget. This includes every measured crash boundary, exact acknowledged bytes/history/receipts, both permanent first-fsync replays and all four merged refusal regressions. No recovery function, original history, fixture, dependency pin or threshold was changed by this integration round, so no new behavioral-revert control is required; the original recovery proof and PR 104 guard proofs remain linked. Pushing the integrated candidate for unchanged just release-check in CI; the full gate is not yet claimed as passed." + "2026-10-22T12:12:14.934Z",codex-agent,"Integrated code head 46178e2dae6468d1f71e18a23c9e608e19a48a56 passed the full Linux just release-check gate (https://github.com/unbraind/pm-rust/actions/runs/39772686608/job/119803873398). Ordinary and instrumented Rust runs each passed 580 tests across 22 suites, zero failed or ignored. Rust lines 14030/14030, functions 1467/1467, regions 23449/23449 and branches 3298/3298 are all 100 percent at unchanged thresholds. Strict TypeScript, ESLint and the merged Linux crash harness passed 20/20, zero failed or skipped, inside that gate. Audit, changelog and strict tracker health passed; the existing allowed RUSTSEC-2024-0449 advisory remains. macOS full gate also passed. This round has no review findings and no new recovery-code edits; original two-failure/two-restored recovery proof and merged PR 104 guard proofs remain intact. Releasing the claim and retaining in_progress; final receipt-head CI will be linked from PR 105. No replies/resolutions, PR merge or release." + "2026-10-22T17:21:57.057Z",codex-agent,"Original stacked-PR instruction, follow-up to the completed PR 104 review round: pulled PR 105 remote head 53e4988, confirmed zero review threads and no substantive CodeRabbit review, verified main is already an ancestor, then merged PR 104 final head f555a4238fc4d0fa5768feaee0e81dce66031202 after all nine of its exact-head CI jobs passed. The merge is clean and brings the baseline_parity replay guide/docstring correction plus two documentation regressions and LF/CRLF checks. No Rust source changed in this integration. Existing recovery invariants, two permanent first-fsync replays and original independent recovery revert proof remain intact. Running current-binary build, strict TypeScript/lint and the unchanged 300-second linked full crash harness, followed by the full unchanged release gate." + "2026-10-22T17:23:48.746Z",codex-agent,"Merged-head acceptance passes: current native build, strict erasable TypeScript, ESLint, required check-only tracker health, and the existing pinned-PM linked full crash suite. node --test scripts/crash-harness.test.mts passes 22/22 with zero failed/canceled/skipped in about 16 seconds inside its unchanged 300-second budget. Every measured crash boundary, both permanent update/comment first-fsync replays, exact bytes/history/receipts and the two merged guidance checks pass. All Rust source is byte-identical to 53e4988, so this integration adds no runtime fix or new runtime revert requirement; original recovery and inherited PR 104 documentation revert proofs remain valid and linked. Existing result-tracking policy is disabled; results are recorded here without changing it." + "2026-10-22T17:23:49.343Z",codex-agent,"Releasing the integration claim with status retained in_progress for orchestrator verification. This branch now includes the completed PR 104 head f555a4238fc4d0fa5768feaee0e81dce66031202 and current main a600bd8. Fresh unchanged-threshold full just release-check CI will validate the pushed integration head; its exact receipts and counts will be recorded in PR https://github.com/unbraind/pm-rust/pull/105. Pending CI is not approval. No review reply/resolution, PR merge, release, dependency/threshold change or history rewrite." + "2026-10-23T20:08:49.952Z",codex-agent,"PR 105 review round starts only after PR 110 completed its full exact-head CI gates and push. Pulled remote recovery head ca52b3608505542519781d6709799c163d9e01de. GraphQL returns zero review threads and no CodeRabbit review body; the latest CodeRabbit issue comment explicitly skips automatic review. The branch is four main commits behind and GitHub reports conflicts. Merge origin/main before code review, preserve both append-only tracker streams and the recovery/harness stack, regenerate the changelog inside the merge, then verify the complete PR diff, focused regressions and unchanged native release gate. Existing recovery and crash-boundary evidence remains historical until checked against the integrated candidate." + "2026-10-23T20:17:51.120Z",codex-agent,"Merged main 300eaa9a9d06f0e2cfc6afcfa8b66492a960a77b with three conflicts resolved: retain both ignore lists and crash/benchmark recipes; retain the main non-git native process helper plus the branch requirement of 50 concurrent writers and three comments each. Expanded the expected marker set to all 150 entries so the merged test remains coherent and strict. Behavioral negative control restored only the old expected-set behavior in the concurrency test function: it compiled and failed with exit 101, actual 150 markers versus expected 50; restored the full set afterward. The integrated recovery negative control removed only the early confirmed-dead-owner gate admission from acquire_lock_attempt, rebuilt, and ran node --test --test-name-pattern=\"retained (update|comment)\" scripts/crash-harness.test.mts: two failures, both restart exit 4. Restored source/binary passed both cases (2/2). Whole-PR review retained lock identity/lease/refusal controls, exact receipt/history checks, typed harness refusals and no-git execution. Updated one stale harness comment to describe fixed reference invocations after main introduced shared journal recovery. All 78 branch and 80 main history streams remain exact byte prefixes; no history rewrite or repair. Strict TypeScript, ESLint, merge-time changelog check, and isolated required check-only health pass. Full linked acceptance and unchanged full CI gate follow." + "2026-10-23T20:23:14.015Z",codex-agent,"Final integrated acceptance: all three pinned-PM linked commands passed, totaling 22 Linux crash-harness tests, 2 concurrency tests, 14 parity tests and 6 gate-focused unit tests (44 cases, zero failed or skipped). The concurrency assertion covers all 150 unique comments exactly once plus the complete 151-record history chain. Clippy retained its 100-line function limit: extracted the full expected marker set into documented expected_markers rather than relaxing lint. Repeating the helper-only stale-set behavioral control compiled and failed with exit 101, actual 150 versus expected 50; restored helper passed the linked 2+14 Rust acceptance again. Recovery proof remains two restart-exit-4 failures with only early dead-owner admission reverted, then two passes after restoring/rebuilding. Final strict all-target/all-feature Clippy, formatting, private rustdoc, strict erasable TypeScript, ESLint and isolated required tracker health pass. Merge-time changelog generation/check will be repeated after this final tracker write. Original JSON receipts and fixtures are untouched. The full unchanged just release-check matrix will run on the pushed integration head; no pending or skipped reviewer is counted as approval. Release the claim and retain in_progress for orchestrator verification." +files[11]: + - path: .gitignore + scope: project + - path: justfile + scope: project + - path: scripts/crash-harness.mts + scope: project + - path: scripts/crash-harness.test.mts + scope: project + note: Real incomplete-lock crash and replay reproduction + - path: src/mutation.rs + scope: project + note: Lease-protected early empty-gate recovery for confirmed dead owners + - path: tests/concurrency_contract.rs + scope: project + note: Fifty writers retain all 150 comments and history chain + - path: tests/fixtures/crash-restart/comment.json + scope: project + note: Permanent first-fsync comment reproduction + - path: tests/fixtures/crash-restart/fixture/.agents/pm/settings.json + scope: project + note: Minimal synthetic fixture shared by permanent reproductions + - path: tests/fixtures/crash-restart/update.json + scope: project + note: Permanent first-fsync update reproduction + - path: tests/parity_1021_contract.rs + scope: project + note: Exact oracle refusal receipts and authoritative byte preservation + - path: tests/support/mutation_unit.rs + scope: project + note: Live unknown occupied and held-lease refusal controls +tests[3]{command,scope,timeout_seconds,provenance{author,created_at,source_kind,source_ref}}: + node --test scripts/crash-harness.test.mts,project,300,codex-agent,"2026-10-21T23:11:31.260Z",local_mutation,fix/crash-restart-recovery + env CARGO_BUILD_JOBS=2 PM_RUST_REQUIRE_PUBLISHED_CLI=1 cargo +1.90.0 test --locked --test concurrency_contract --test parity_1021_contract,project,900,codex-agent,"2026-10-21T23:11:44.224Z",local_mutation,fix/crash-restart-recovery + env CARGO_BUILD_JOBS=2 cargo +1.90.0 test --locked --lib gate,project,900,codex-agent,"2026-10-21T23:12:22.808Z",local_mutation,fix/crash-restart-recovery +docs[4]: + - path: docs/CRASH_HARNESS.md + scope: project + note: Early gate recovery policy independent replay commands and fifty-writer acceptance + - path: docs/CRASH_RESTART_RECOVERY_2026_10_22.json + scope: project + note: Twenty measured SIGKILL recoveries preserve exact state history prefixes and raw restart receipt hashes + - path: docs/CRASH_RESTART_REVERT_PROOF_2026_10_22.json + scope: project + note: Only acquire_lock_attempt reverted and rebuilt yields two restart-exit-4 failures; restored and rebuilt yields two passes + - path: docs/CROSS_OPERATION_JOURNAL_RECOVERY.md + scope: project +body: "" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ada3fc0..4cf06a4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -180,6 +180,11 @@ jobs: npm ci --prefix tests/oracle --ignore-scripts oracle_root=$(node -p "require('node:path').resolve('tests/oracle/node_modules/@unbrained/pm-cli')") echo "PM_NODE_CLI=$oracle_root" >> "$GITHUB_ENV" + - name: Install syscall injection for the Linux crash corpus + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install --yes strace - name: Run release:check aggregate gate run: just release-check diff --git a/.gitignore b/.gitignore index 3a55871..b3b340d 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,7 @@ cov-no-branch.json # Locked published parity oracle installation /tests/oracle/node_modules/ +/tests/harness/node_modules/ +/.crash-failures/ + /tests/bench-oracle/node_modules/ diff --git a/README.md b/README.md index 18c744c..9a47848 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,38 @@ Linked-test branch provenance is optional and inspects Git only when a repositor marker exists. GitHub synchronization and Git history tooling require a Git repository and are outside the distributed native command slice. +## Crash-consistency differential corpus + +The TypeScript SDK in [`scripts/crash-harness.mts`](scripts/crash-harness.mts) +and `just crash` compare real native processes with the locked pm-cli 2026.10.21 +oracle. On Linux, strace injects SIGKILL at every declared per-task `rename` and +`fsync` entry ordinal observed in uninterrupted update/comment reference runs. +Each case starts after an acknowledged synthetic create, restarts through the +same operation family, and checks exact item/history bytes, immutable history +prefixes and recovery receipts. These commands work in plain directories without +a Git executable. The source tracker is read only; all mutations use disposable +non-git clones. + +```bash +just oracle-install +cargo build --locked --bin pm-rust +just crash --native target/debug/pm-rust --artifacts .crash-failures --json +``` + +Default TOON and compact JSON contain bounded counts and per-case outcomes; +`--full --json` adds durable-byte and receipt hashes. Exit codes are 0 for full +conformance, 1 for measured failures, and 2 for typed setup/boundary refusals with +remedies. Failures retain a minimal synthetic tracker, the acknowledged/recovered +probe bytes and a replay manifest under the ignored artifact directory. Original +histories are never repaired or rewritten. A read-copy failure is independently +replayed on a synthetic tracker before retaining evidence. + +See the [SDK, read-copy and replay example](docs/CRASH_HARNESS.md) for the declared +boundary scope and measured results. Process-kill evidence does not establish +power-loss durability. Different syscall ordinals across runtimes do not denote +the same semantic phase; the receipt reports each runtime's observed counts, +including zero `fsync` calls in the published implementation. + ## Current native slice ```bash diff --git a/docs/CRASH_HARNESS.md b/docs/CRASH_HARNESS.md new file mode 100644 index 0000000..ee59d4d --- /dev/null +++ b/docs/CRASH_HARNESS.md @@ -0,0 +1,140 @@ +# Crash corpus SDK and examples + +The corpus requires Node 24 or later, Linux, strace with syscall injection and +`--kill-on-exit`, the locked oracle (`just oracle-install`), and a compatible +native executable. `just crash-check` typechecks erasable strict TypeScript, +lints it and runs targeted regression tests. The existing CI `just release-check` +also runs these checks after its Rust tests. Rust's four 100% coverage thresholds +remain unchanged; those percentages describe Rust source, not the TypeScript +harness. + +On Linux, missing, unreadable, malformed or mismatched oracle metadata refuses +with `oracle_version` and the `just oracle-install` remedy. Malformed source +settings refuse with `unsafe_source`; malformed mutation acknowledgements +refuse with `baseline_failed`. These refusals preserve the input tracker and +do not retain failure artifacts. + +```typescript +import { runCrashCorpus, replayCrashFailure } from '../scripts/crash-harness.mts'; +import type { CrashOptions, CrashReport } from '../scripts/crash-harness.mts'; + +const options: CrashOptions = { + native: 'target/debug/pm-rust', + artifacts: '.crash-failures', + scenarios: ['update', 'comment'], + maxBoundaries: 64, + timeoutMs: 15000, +}; +const receipt: CrashReport = runCrashCorpus(options); +console.log(receipt.ok, receipt.killed, receipt.parity); +// After a failure, select its retained case.json. Boundary failures replay only +// the whitelisted scenario/implementation/syscall/ordinal, never stored commands. +// Exception: kind: 'baseline_parity' replays the scenario across both implementations +// and all their observed boundaries, rather than selecting a single case. +// replayCrashFailure(options, '.crash-failures/failure-example/case.json'); +``` + +Each fixture has one synthetic Task and its acknowledged create history. +Update changes its description; comment appends a synthetic comment. Restart +re-enters update with the acknowledged priority, or comment with a distinct +sentinel. Both uninterrupted alternatives (before/committed) run that identical +restart command. No history hash, patch, receipt or existing stream is normalized +or repaired. Item and history bytes must match one complete reference state; +each implementation's raw restart receipt must match its corresponding reference. +The cross-runtime acknowledgement projection is explicitly update `id/status` +or comment `id/comments`, because native mutation commands emit full legacy JSON +while the published CLI emits flat mutation or collection receipts. Durable +item/history comparisons remain exact bytes. + +The declared boundaries are all observed task-local entry ordinals of `rename` +and `fsync` for each runtime/scenario, up to the configured maximum. A zero +syscall count is reported, and a missing requested boundary or truncated matrix +refuses. A kill is counted only when the trace shows an interrupted selected +syscall and a fatal SIGKILL. strace kills remaining tracees on controller death; +commands also have time and output bounds. The corpus does not cover every +syscall, torn kernel writes, filesystem/power failures, network backends or +cross-runtime journal recovery. Socket/event-loop `write` calls are excluded: +they do not provide stable storage boundaries in Node's worker scheduler. + +Public tracker validation takes a stable regular-file copy of a publicly +published `.agents/pm` tree. The harness never runs a command in that input tree: + +```bash +just crash --native target/debug/pm-rust --source public-copy/.agents/pm \ + --artifacts .crash-failures --json --full +just crash --native target/debug/pm-rust --artifacts .crash-failures \ + --replay .crash-failures/failure-example/case.json --json +``` + +Root caches (`runtime`, `locks`, `transactions`, `checkpoints`, `search`) are +excluded from the authoritative copy. Nested names remain authoritative. Regular +config/schema files, items and history streams are copied byte-for-byte; symlinks +and special files refuse. Output inside the input tracker refuses, including +symlink aliases. Source fingerprints are checked again after execution. No raw +public tracker content is retained in failure artifacts: an independent minimal +synthetic run supplies the retained reproduction. A source-specific failure that +does not reproduce synthetically remains a failure with zero reproductions. + +The corpus is observational and never invokes Git. All children receive an +isolated environment and a Git discovery ceiling. Test telemetry isolation uses +the existing oracle environment contract; workspace telemetry configuration is +untouched. Inputs, original histories and retained evidence are never overwritten. + +The [read-only public-copy receipt](CRASH_PUBLIC_COPY_2026_10_22.json) uses public +pm-rust main `c0154dbd7cd8071ac52b891be13f956f7a0f21fd`: 74 items and 154 +authoritative files. All 20 real kills preserve acknowledged item bytes and +history prefixes, both baseline byte/acknowledgement comparisons pass, and 18 +restart state/receipt comparisons pass. Two native first-fsync lock-initialization +kills refuse restart with exit 4; both reproduce on minimal synthetic fixtures. +The source fingerprint remains unchanged. The safe recovery policy is tracked +in `pm-rust-crash-lock-restart`. That receipt records the original failures before +the recovery fix; it remains unchanged as historical evidence. +Four [behavioral negative controls](CRASH_REVERT_PROOF_2026_10_22.json) +fail their regression with the production invariant disabled and pass restored. + +The recovery fix permits an unexpired empty publication gate to be reclaimed only +while holding its kernel lease and after confirming the lock's original process +is dead on the same Linux host/namespace. The lock payload is already complete at +the first fsync entry. Reclamation checks the gate's identity and emptiness; +nonempty, replaced, live-owner and unverified gates retain their refusal policy. +The existing durable recovery-intent receipt is written before removing the dead +owner's lock. Item/history serialization and journal recovery are unchanged. + +Both minimized reproductions are permanent, independent replay cases: + +```bash +just crash --native target/debug/pm-rust --artifacts .crash-failures \ + --replay tests/fixtures/crash-restart/update.json --json +just crash --native target/debug/pm-rust --artifacts .crash-failures \ + --replay tests/fixtures/crash-restart/comment.json --json +``` + +The fixtures contain only the whitelisted selection and minimal synthetic +settings. The locked oracle creates their acknowledged seed, and each real +injected kill must restart successfully with exact reference item/history bytes +and the corresponding raw restart receipt. The full Linux regression now requires +every measured boundary to pass; expected native restart failures are no longer +allowed. Separate ownership JSON refusal comparisons on the shared gate path +retain the oracle's complete error receipt and authoritative bytes. The no-git +contention contract runs 50 simultaneous +writers, three comments each, and verifies all 150 comments and the history chain. + +The [post-fix synthetic receipt](CRASH_RESTART_RECOVERY_2026_10_22.json) records +20/20 measured kills with exact reference states, preserved acknowledged history +and matching raw restart receipt hashes, including both first-fsync cases. +Both cross-runtime baseline byte and acknowledgement comparisons pass. A 60000 ms +per-process timeout was used for this local run; the SDK default remains 15000 ms. +The [rebuild-based revert proof](CRASH_RESTART_REVERT_PROOF_2026_10_22.json) +replaces only the recovery function with its original implementation: both +permanent replays fail with restart exit 4, then both pass after restoration. + +Pre-existing native comment lock-conflict diagnostic differences are tracked in +`pm-rust-comment-lock-receipt`: admission and bytes agree, but detail, guidance and +the refusal surface label differ. This corpus uses each runtime's own raw restart +reference and the declared shared acknowledgement projection; it does not certify +every CLI diagnostic surface. + +Exact-head recovery CI evidence, including all platform test and coverage counts, +is recorded in [PR #105](https://github.com/unbraind/pm-rust/pull/105). +Full-gate success tests the harness implementation and existing package contracts; +it does not establish power-loss durability or coverage of unmeasured syscalls. diff --git a/docs/CRASH_PUBLIC_COPY_2026_10_22.json b/docs/CRASH_PUBLIC_COPY_2026_10_22.json new file mode 100644 index 0000000..cf3382e --- /dev/null +++ b/docs/CRASH_PUBLIC_COPY_2026_10_22.json @@ -0,0 +1,392 @@ +{ + "sourceRepository": "https://github.com/unbraind/pm-rust", + "sourceCommit": "c0154dbd7cd8071ac52b891be13f956f7a0f21fd", + "inputReadOnly": true, + "inputHasGit": false, + "exitCode": 1, + "report": { + "schema": "pm-rust/crash-v1", + "ok": false, + "oracleVersion": "2026.10.21", + "nativeSha256": "dc0e5403c895603b6e2502a2e6a025de705c9281ab607f9999214ac78c4fd623", + "sourceFiles": 154, + "sourceItems": 74, + "sourceSha256": "b44e20078c74caaa6454f42d2efe53dcf9bae79d95411c9daf438a84f3d282f8", + "sourceUnchanged": true, + "acknowledgedComparisons": 2, + "killed": 20, + "boundaries": [ + { + "scenario": "update", + "implementation": "native", + "syscall": "rename", + "count": 1 + }, + { + "scenario": "update", + "implementation": "native", + "syscall": "fsync", + "count": 8 + }, + { + "scenario": "update", + "implementation": "published", + "syscall": "rename", + "count": 1 + }, + { + "scenario": "update", + "implementation": "published", + "syscall": "fsync", + "count": 0 + }, + { + "scenario": "comment", + "implementation": "native", + "syscall": "rename", + "count": 1 + }, + { + "scenario": "comment", + "implementation": "native", + "syscall": "fsync", + "count": 8 + }, + { + "scenario": "comment", + "implementation": "published", + "syscall": "rename", + "count": 1 + }, + { + "scenario": "comment", + "implementation": "published", + "syscall": "fsync", + "count": 0 + } + ], + "cases": [ + { + "scenario": "update", + "implementation": "native", + "syscall": "rename", + "boundary": 1, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "committed", + "durableSha256": "e0cee618d47156f213de7567a68c956fcef83eb4b068c791bf9d9591a6b5b966", + "receiptSha256": "e8c4f2b2891d546b595f854d28606a5682c10924d882335f16c1279ca2d84215", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "update", + "implementation": "native", + "syscall": "fsync", + "boundary": 1, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 4, + "state": "before", + "durableSha256": "662d7f91f2c17e5ac3e6f35f57b4ab7949558c6b8ee6850feba574ee3e5092af", + "receiptSha256": "5cd5401089e98fa6a28c7bc873f514b2b3a8cccc95f72094c04a7f1cbad393db", + "receiptMatches": false, + "ok": false, + "failure": { + "code": "restart_refused", + "remedy": "Replay the retained synthetic case and inspect its lock, journal, bytes and receipt; preserve all original history. Runtime repairs need separate implementation work." + } + }, + { + "scenario": "update", + "implementation": "native", + "syscall": "fsync", + "boundary": 2, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "before", + "durableSha256": "b83cb62111c7b5776eadb4df36ab0bba93918622a2d744e2e608c2f053269a15", + "receiptSha256": "634c13746a1e370864e636573bed64631d9d30fbd954771d2c82b349c9afd4d8", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "update", + "implementation": "native", + "syscall": "fsync", + "boundary": 3, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "committed", + "durableSha256": "e0cee618d47156f213de7567a68c956fcef83eb4b068c791bf9d9591a6b5b966", + "receiptSha256": "e8c4f2b2891d546b595f854d28606a5682c10924d882335f16c1279ca2d84215", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "update", + "implementation": "native", + "syscall": "fsync", + "boundary": 4, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "committed", + "durableSha256": "e0cee618d47156f213de7567a68c956fcef83eb4b068c791bf9d9591a6b5b966", + "receiptSha256": "e8c4f2b2891d546b595f854d28606a5682c10924d882335f16c1279ca2d84215", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "update", + "implementation": "native", + "syscall": "fsync", + "boundary": 5, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "committed", + "durableSha256": "e0cee618d47156f213de7567a68c956fcef83eb4b068c791bf9d9591a6b5b966", + "receiptSha256": "e8c4f2b2891d546b595f854d28606a5682c10924d882335f16c1279ca2d84215", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "update", + "implementation": "native", + "syscall": "fsync", + "boundary": 6, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "committed", + "durableSha256": "e0cee618d47156f213de7567a68c956fcef83eb4b068c791bf9d9591a6b5b966", + "receiptSha256": "e8c4f2b2891d546b595f854d28606a5682c10924d882335f16c1279ca2d84215", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "update", + "implementation": "native", + "syscall": "fsync", + "boundary": 7, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "committed", + "durableSha256": "e0cee618d47156f213de7567a68c956fcef83eb4b068c791bf9d9591a6b5b966", + "receiptSha256": "e8c4f2b2891d546b595f854d28606a5682c10924d882335f16c1279ca2d84215", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "update", + "implementation": "native", + "syscall": "fsync", + "boundary": 8, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "committed", + "durableSha256": "e0cee618d47156f213de7567a68c956fcef83eb4b068c791bf9d9591a6b5b966", + "receiptSha256": "e8c4f2b2891d546b595f854d28606a5682c10924d882335f16c1279ca2d84215", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "update", + "implementation": "published", + "syscall": "rename", + "boundary": 1, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "before", + "durableSha256": "662d7f91f2c17e5ac3e6f35f57b4ab7949558c6b8ee6850feba574ee3e5092af", + "receiptSha256": "f90c7ce15f575cf159f00dd22cbff98e5a231e10e8ee79cb266c9efa07f3482e", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "comment", + "implementation": "native", + "syscall": "rename", + "boundary": 1, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "committed", + "durableSha256": "1e292e3410bd9be51d9c715193123f466a471ed78953d6be0ab8fcb6b766682e", + "receiptSha256": "813e21db67958f2e9d9f11ebcc9adf30769e459f16f925a10d55d9ac603425a2", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "comment", + "implementation": "native", + "syscall": "fsync", + "boundary": 1, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 4, + "state": "before", + "durableSha256": "662d7f91f2c17e5ac3e6f35f57b4ab7949558c6b8ee6850feba574ee3e5092af", + "receiptSha256": "b12a1f58a23f36aa2ccaf683a4ff68369094c57102a39ba2c1db6aace2e35579", + "receiptMatches": false, + "ok": false, + "failure": { + "code": "restart_refused", + "remedy": "Replay the retained synthetic case and inspect its lock, journal, bytes and receipt; preserve all original history. Runtime repairs need separate implementation work." + } + }, + { + "scenario": "comment", + "implementation": "native", + "syscall": "fsync", + "boundary": 2, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "before", + "durableSha256": "bbfa1f48b522b71d7232fc1cbf4ea8d65e0a4a7acc05bfc8265e9c41832a3859", + "receiptSha256": "f0c3e717500261f6cee7d8b0e19bfd9a5da3594e4745baa54c576341ddce2e47", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "comment", + "implementation": "native", + "syscall": "fsync", + "boundary": 3, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "committed", + "durableSha256": "1e292e3410bd9be51d9c715193123f466a471ed78953d6be0ab8fcb6b766682e", + "receiptSha256": "813e21db67958f2e9d9f11ebcc9adf30769e459f16f925a10d55d9ac603425a2", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "comment", + "implementation": "native", + "syscall": "fsync", + "boundary": 4, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "committed", + "durableSha256": "1e292e3410bd9be51d9c715193123f466a471ed78953d6be0ab8fcb6b766682e", + "receiptSha256": "813e21db67958f2e9d9f11ebcc9adf30769e459f16f925a10d55d9ac603425a2", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "comment", + "implementation": "native", + "syscall": "fsync", + "boundary": 5, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "committed", + "durableSha256": "1e292e3410bd9be51d9c715193123f466a471ed78953d6be0ab8fcb6b766682e", + "receiptSha256": "813e21db67958f2e9d9f11ebcc9adf30769e459f16f925a10d55d9ac603425a2", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "comment", + "implementation": "native", + "syscall": "fsync", + "boundary": 6, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "committed", + "durableSha256": "1e292e3410bd9be51d9c715193123f466a471ed78953d6be0ab8fcb6b766682e", + "receiptSha256": "813e21db67958f2e9d9f11ebcc9adf30769e459f16f925a10d55d9ac603425a2", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "comment", + "implementation": "native", + "syscall": "fsync", + "boundary": 7, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "committed", + "durableSha256": "1e292e3410bd9be51d9c715193123f466a471ed78953d6be0ab8fcb6b766682e", + "receiptSha256": "813e21db67958f2e9d9f11ebcc9adf30769e459f16f925a10d55d9ac603425a2", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "comment", + "implementation": "native", + "syscall": "fsync", + "boundary": 8, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "committed", + "durableSha256": "1e292e3410bd9be51d9c715193123f466a471ed78953d6be0ab8fcb6b766682e", + "receiptSha256": "813e21db67958f2e9d9f11ebcc9adf30769e459f16f925a10d55d9ac603425a2", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "comment", + "implementation": "published", + "syscall": "rename", + "boundary": 1, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "before", + "durableSha256": "bbfa1f48b522b71d7232fc1cbf4ea8d65e0a4a7acc05bfc8265e9c41832a3859", + "receiptSha256": "4079f1dc3ff9abce9c03bcbea8ca4a6e8f5589c1b9e3d460e085f0f833fb72af", + "receiptMatches": true, + "ok": true + } + ], + "failuresRetained": 2, + "parity": [ + { + "scenario": "update", + "bytes": true, + "acknowledgement": true + }, + { + "scenario": "comment", + "bytes": true, + "acknowledgement": true + } + ] + } +} diff --git a/docs/CRASH_RESTART_RECOVERY_2026_10_22.json b/docs/CRASH_RESTART_RECOVERY_2026_10_22.json new file mode 100644 index 0000000..83bed06 --- /dev/null +++ b/docs/CRASH_RESTART_RECOVERY_2026_10_22.json @@ -0,0 +1,377 @@ +{ + "schema": "pm-rust/crash-v1", + "ok": true, + "oracleVersion": "2026.10.21", + "nativeSha256": "3115bd74bf6aa977cae05b2850f64fea895cbd287c2439f2c32fb15f00e0b48a", + "sourceFiles": 6, + "sourceItems": 0, + "sourceSha256": "10f46c6ff91a09fb7d0339d5af6c0ff52590dad6f653843b0793af057cd653a5", + "sourceUnchanged": true, + "acknowledgedComparisons": 2, + "killed": 20, + "boundaries": [ + { + "scenario": "update", + "implementation": "native", + "syscall": "rename", + "count": 1 + }, + { + "scenario": "update", + "implementation": "native", + "syscall": "fsync", + "count": 8 + }, + { + "scenario": "update", + "implementation": "published", + "syscall": "rename", + "count": 1 + }, + { + "scenario": "update", + "implementation": "published", + "syscall": "fsync", + "count": 0 + }, + { + "scenario": "comment", + "implementation": "native", + "syscall": "rename", + "count": 1 + }, + { + "scenario": "comment", + "implementation": "native", + "syscall": "fsync", + "count": 8 + }, + { + "scenario": "comment", + "implementation": "published", + "syscall": "rename", + "count": 1 + }, + { + "scenario": "comment", + "implementation": "published", + "syscall": "fsync", + "count": 0 + } + ], + "cases": [ + { + "scenario": "update", + "implementation": "native", + "syscall": "rename", + "boundary": 1, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "committed", + "durableSha256": "adeb06d560fb35efc022a7969df98e74116700c6302089ba0ca192275bf7e0ef", + "receiptSha256": "628b0a35ae6d404816abab3091ca48773ec26c3784feda289e9c999356eeebdc", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "update", + "implementation": "native", + "syscall": "fsync", + "boundary": 1, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "before", + "durableSha256": "519aea1d810de1f5bacd20958951f90a783432edfa65f49869c00c1835cf2804", + "receiptSha256": "763c40a09e1e223f469749d4bdcde886f5416a71b83d61c95484c4a0a8a8b357", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "update", + "implementation": "native", + "syscall": "fsync", + "boundary": 2, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "before", + "durableSha256": "519aea1d810de1f5bacd20958951f90a783432edfa65f49869c00c1835cf2804", + "receiptSha256": "763c40a09e1e223f469749d4bdcde886f5416a71b83d61c95484c4a0a8a8b357", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "update", + "implementation": "native", + "syscall": "fsync", + "boundary": 3, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "committed", + "durableSha256": "adeb06d560fb35efc022a7969df98e74116700c6302089ba0ca192275bf7e0ef", + "receiptSha256": "628b0a35ae6d404816abab3091ca48773ec26c3784feda289e9c999356eeebdc", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "update", + "implementation": "native", + "syscall": "fsync", + "boundary": 4, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "committed", + "durableSha256": "adeb06d560fb35efc022a7969df98e74116700c6302089ba0ca192275bf7e0ef", + "receiptSha256": "628b0a35ae6d404816abab3091ca48773ec26c3784feda289e9c999356eeebdc", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "update", + "implementation": "native", + "syscall": "fsync", + "boundary": 5, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "committed", + "durableSha256": "adeb06d560fb35efc022a7969df98e74116700c6302089ba0ca192275bf7e0ef", + "receiptSha256": "628b0a35ae6d404816abab3091ca48773ec26c3784feda289e9c999356eeebdc", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "update", + "implementation": "native", + "syscall": "fsync", + "boundary": 6, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "committed", + "durableSha256": "adeb06d560fb35efc022a7969df98e74116700c6302089ba0ca192275bf7e0ef", + "receiptSha256": "628b0a35ae6d404816abab3091ca48773ec26c3784feda289e9c999356eeebdc", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "update", + "implementation": "native", + "syscall": "fsync", + "boundary": 7, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "committed", + "durableSha256": "adeb06d560fb35efc022a7969df98e74116700c6302089ba0ca192275bf7e0ef", + "receiptSha256": "628b0a35ae6d404816abab3091ca48773ec26c3784feda289e9c999356eeebdc", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "update", + "implementation": "native", + "syscall": "fsync", + "boundary": 8, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "committed", + "durableSha256": "adeb06d560fb35efc022a7969df98e74116700c6302089ba0ca192275bf7e0ef", + "receiptSha256": "628b0a35ae6d404816abab3091ca48773ec26c3784feda289e9c999356eeebdc", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "update", + "implementation": "published", + "syscall": "rename", + "boundary": 1, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "before", + "durableSha256": "a21c0fa7897484a6f20ca0dd0dce877dc7d295e6166cda62ffcb69835ba3f890", + "receiptSha256": "3d2226c5b0ccdf721186d93c2bc9489ed642122efd91487d452cdeb3981c2160", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "comment", + "implementation": "native", + "syscall": "rename", + "boundary": 1, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "committed", + "durableSha256": "bd5cc5b55f30ba7c31d7bd91d0f8d7812f4ec6891d1088b30a358e00d2cee87f", + "receiptSha256": "2fa3bc275b61a8ce9adcc721c7dd66354f175f4811a030d252ceacf286faa547", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "comment", + "implementation": "native", + "syscall": "fsync", + "boundary": 1, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "before", + "durableSha256": "ef61148556497bdc82eb44942a1708662bc22be15b03dbd07da3efc44f991f51", + "receiptSha256": "7f0f3b6c17f2ae7602041f8bfd8a221c25731e6cf04e2f4f7102650a70bcd59d", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "comment", + "implementation": "native", + "syscall": "fsync", + "boundary": 2, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "before", + "durableSha256": "ef61148556497bdc82eb44942a1708662bc22be15b03dbd07da3efc44f991f51", + "receiptSha256": "7f0f3b6c17f2ae7602041f8bfd8a221c25731e6cf04e2f4f7102650a70bcd59d", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "comment", + "implementation": "native", + "syscall": "fsync", + "boundary": 3, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "committed", + "durableSha256": "bd5cc5b55f30ba7c31d7bd91d0f8d7812f4ec6891d1088b30a358e00d2cee87f", + "receiptSha256": "2fa3bc275b61a8ce9adcc721c7dd66354f175f4811a030d252ceacf286faa547", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "comment", + "implementation": "native", + "syscall": "fsync", + "boundary": 4, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "committed", + "durableSha256": "bd5cc5b55f30ba7c31d7bd91d0f8d7812f4ec6891d1088b30a358e00d2cee87f", + "receiptSha256": "2fa3bc275b61a8ce9adcc721c7dd66354f175f4811a030d252ceacf286faa547", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "comment", + "implementation": "native", + "syscall": "fsync", + "boundary": 5, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "committed", + "durableSha256": "bd5cc5b55f30ba7c31d7bd91d0f8d7812f4ec6891d1088b30a358e00d2cee87f", + "receiptSha256": "2fa3bc275b61a8ce9adcc721c7dd66354f175f4811a030d252ceacf286faa547", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "comment", + "implementation": "native", + "syscall": "fsync", + "boundary": 6, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "committed", + "durableSha256": "bd5cc5b55f30ba7c31d7bd91d0f8d7812f4ec6891d1088b30a358e00d2cee87f", + "receiptSha256": "2fa3bc275b61a8ce9adcc721c7dd66354f175f4811a030d252ceacf286faa547", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "comment", + "implementation": "native", + "syscall": "fsync", + "boundary": 7, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "committed", + "durableSha256": "bd5cc5b55f30ba7c31d7bd91d0f8d7812f4ec6891d1088b30a358e00d2cee87f", + "receiptSha256": "2fa3bc275b61a8ce9adcc721c7dd66354f175f4811a030d252ceacf286faa547", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "comment", + "implementation": "native", + "syscall": "fsync", + "boundary": 8, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "committed", + "durableSha256": "bd5cc5b55f30ba7c31d7bd91d0f8d7812f4ec6891d1088b30a358e00d2cee87f", + "receiptSha256": "2fa3bc275b61a8ce9adcc721c7dd66354f175f4811a030d252ceacf286faa547", + "receiptMatches": true, + "ok": true + }, + { + "scenario": "comment", + "implementation": "published", + "syscall": "rename", + "boundary": 1, + "killed": true, + "acknowledged": true, + "historyPreserved": true, + "restartStatus": 0, + "state": "before", + "durableSha256": "ef61148556497bdc82eb44942a1708662bc22be15b03dbd07da3efc44f991f51", + "receiptSha256": "019020693805187d2d29dbf57d7e7202c38260f16c0ecd42bd0f0ddcc591b40c", + "receiptMatches": true, + "ok": true + } + ], + "failuresRetained": 0, + "parity": [ + { + "scenario": "update", + "bytes": true, + "acknowledgement": true + }, + { + "scenario": "comment", + "bytes": true, + "acknowledgement": true + } + ] +} diff --git a/docs/CRASH_RESTART_REVERT_PROOF_2026_10_22.json b/docs/CRASH_RESTART_REVERT_PROOF_2026_10_22.json new file mode 100644 index 0000000..419347f --- /dev/null +++ b/docs/CRASH_RESTART_REVERT_PROOF_2026_10_22.json @@ -0,0 +1,21 @@ +{ + "schema": "pm-rust/crash-restart-revert-v1", + "function": "mutation::acquire_lock_attempt", + "control": "Replace only this function with the #104 version; rebuild the native binary; keep all regression tests and fixtures unchanged.", + "build_command": "CARGO_BUILD_JOBS=2 cargo +1.90.0 build --locked --bin pm-rust", + "test_command": "node --test --test-name-pattern='retained (update|comment)' scripts/crash-harness.test.mts", + "reverted": { + "test_exit": 1, + "passed": 0, + "failed": 2, + "restart_exit_codes": [ + 4, + 4 + ] + }, + "restored": { + "test_exit": 0, + "passed": 2, + "failed": 0 + } +} diff --git a/docs/CRASH_REVERT_PROOF_2026_10_22.json b/docs/CRASH_REVERT_PROOF_2026_10_22.json new file mode 100644 index 0000000..e2f3525 --- /dev/null +++ b/docs/CRASH_REVERT_PROOF_2026_10_22.json @@ -0,0 +1,29 @@ +{ + "controls": [ + { + "function": "history preservation", + "negativeExit": 1, + "command": "node --test --test-name-pattern \"history preservation\" scripts/crash-harness.test.mts", + "restoredExit": 0 + }, + { + "function": "inventories include", + "negativeExit": 1, + "command": "node --test --test-name-pattern \"inventories include\" scripts/crash-harness.test.mts", + "restoredExit": 0 + }, + { + "function": "ordinary exits", + "negativeExit": 1, + "command": "node --test --test-name-pattern \"ordinary exits\" scripts/crash-harness.test.mts", + "restoredExit": 0 + }, + { + "function": "recoveredState", + "negativeExit": 1, + "restoredExit": 0, + "command": "node --test --test-name-pattern \"successful restart\" scripts/crash-harness.test.mts" + } + ], + "note": "Each control disables one production invariant; original bytes restored before its passing rerun." +} diff --git a/justfile b/justfile index 19f0882..e8dce18 100644 --- a/justfile +++ b/justfile @@ -41,6 +41,18 @@ export PM_NODE_CLI := env_var_or_default("PM_NODE_CLI", justfile_directory() / " oracle-install: npm ci --prefix tests/oracle --ignore-scripts +# Run the standalone typed crash-conformance CLI (Linux and strace required). +[positional-arguments] +crash *args: + node scripts/crash-cli.mts "$@" + +# Targeted TypeScript harness checks; Rust tests/build remain separate. +crash-check: + npm ci --prefix tests/harness --ignore-scripts + tests/harness/node_modules/.bin/tsc -p tests/harness/tsconfig.json + tests/harness/node_modules/.bin/eslint -c tests/harness/eslint.config.mjs scripts/crash-*.mts + node --test scripts/crash-harness.test.mts + # Reproduce large synthetic non-git trackers against the requested current oracle. bench-oracle-install: npm ci --prefix tests/bench-oracle --ignore-scripts @@ -118,6 +130,7 @@ release-check: oracle-install cargo +1.90.0 clippy --locked --all-targets --all-features -- -D warnings RUSTDOCFLAGS='--document-private-items -D missing-docs' cargo +1.90.0 doc --locked --all-features --no-deps PM_RUST_REQUIRE_PUBLISHED_CLI=1 cargo +1.90.0 test --locked --all-targets --all-features + just crash-check test ! -e coverage-branch.json || unlink coverage-branch.json PM_RUST_REQUIRE_PUBLISHED_CLI=1 cargo +nightly-2026-08-06 llvm-cov --locked --branch --all-targets --all-features --json --output-path coverage-branch.json jq '.data[0].totals' coverage-branch.json diff --git a/scripts/crash-cli.mts b/scripts/crash-cli.mts new file mode 100644 index 0000000..2065db5 --- /dev/null +++ b/scripts/crash-cli.mts @@ -0,0 +1,41 @@ +import { parseArgs } from 'node:util'; +import { CrashRefusal, replayCrashFailure, runCrashCorpus } from './crash-harness.mts'; + +/** Execute the standalone CLI and emit bounded, privacy-safe TOON or JSON. */ +function main(): void { + let json = process.argv.includes('--json'); + try { + const { values } = parseArgs({ options: { + native: { type: 'string' }, oracle: { type: 'string' }, source: { type: 'string' }, + artifacts: { type: 'string' }, 'max-boundaries': { type: 'string' }, timeout: { type: 'string' }, + json: { type: 'boolean' }, full: { type: 'boolean' }, replay: { type: 'string' }, help: { type: 'boolean' }, + } }); + json = values.json ?? false; + if (values.help) { + console.log('Usage: node scripts/crash-cli.mts --native --artifacts [--source | --replay ] [--oracle ] [--max-boundaries <1..64>] [--timeout ] [--json] [--full]'); + return; + } + if (!values.native || !values.artifacts) throw new CrashRefusal('invalid_options', 'Supply --native and --artifacts; use --help for the full command.'); + if (values.source && values.replay) throw new CrashRefusal('invalid_options', 'Supply only one of --source and --replay.'); + const options = { native: values.native, artifacts: values.artifacts, oracle: values.oracle, + source: values.source, maxBoundaries: values['max-boundaries'] === undefined ? undefined : Number(values['max-boundaries']), + timeoutMs: values.timeout === undefined ? undefined : Number(values.timeout) }; + const report = values.replay ? replayCrashFailure(options, values.replay) : runCrashCorpus(options); + if (json) console.log(JSON.stringify(values.full ? report : { ...report, + cases: report.cases.map(row => ({ scenario: row.scenario, implementation: row.implementation, + syscall: row.syscall, boundary: row.boundary, state: row.state, acknowledged: row.acknowledged, + historyPreserved: row.historyPreserved, restartStatus: row.restartStatus, receiptMatches: row.receiptMatches, failure: row.failure, ok: row.ok })) })); + else { + console.log(`schema: ${JSON.stringify(report.schema)}\nok: ${report.ok}\noracle_version: ${JSON.stringify(report.oracleVersion)}\nsource_items: ${report.sourceItems}\nsource_unchanged: ${report.sourceUnchanged}\nacknowledged_comparisons: ${report.acknowledgedComparisons}\nkilled: ${report.killed}\nfailures_retained: ${report.failuresRetained}`); + console.log(`cases[${report.cases.length}]{scenario,implementation,syscall,boundary,state,history_preserved,ok}:`); + for (const row of report.cases) console.log(` ${row.scenario},${row.implementation},${row.syscall},${row.boundary},${row.state},${row.historyPreserved},${row.ok}`); + } + process.exitCode = report.ok ? 0 : 1; + } catch (error) { + const refusal = error instanceof CrashRefusal ? error : new CrashRefusal('invalid_options', 'Check option spelling and values; run --help.'); + const payload = { ok: false, code: refusal.code, remedy: refusal.remedy }; + console.log(json ? JSON.stringify(payload) : `ok: false\ncode: ${JSON.stringify(payload.code)}\nremedy: ${JSON.stringify(payload.remedy)}`); + process.exitCode = 2; + } +} +main(); diff --git a/scripts/crash-clock.mts b/scripts/crash-clock.mts new file mode 100644 index 0000000..c72f98d --- /dev/null +++ b/scripts/crash-clock.mts @@ -0,0 +1,11 @@ +/** Pin wall-clock metadata in fresh published CLI processes, as in the Rust differentials. */ +const instant = Date.parse('2026-10-21T12:00:00.000Z'); +const OriginalDate = Date; +const PinnedDate = class extends OriginalDate { + /** Construct a deterministic clock while preserving explicitly supplied instants. */ + constructor(value?: string | number) { super(value ?? instant); } + /** Return the corpus clock. */ + static now(): number { return instant; } +}; +// Date's callable legacy signature is outside CLI metadata generation. +globalThis.Date = PinnedDate as unknown as DateConstructor; diff --git a/scripts/crash-harness.mts b/scripts/crash-harness.mts new file mode 100644 index 0000000..9c50380 --- /dev/null +++ b/scripts/crash-harness.mts @@ -0,0 +1,344 @@ +import { createHash } from 'node:crypto'; +import { existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { CRASH_SYSCALLS, boundaryCount, execute, killedAtBoundary } from './crash-process.mts'; +import type { Boundary, Implementation, ProcessReceipt } from './crash-process.mts'; +import { fingerprint, materialize, preservesHistory, recoveredState, sameBytes, snapshot } from './crash-storage.mts'; +import type { Snapshot } from './crash-storage.mts'; + +/** Public clock used by both real implementations. */ +export const CRASH_CLOCK = '2026-10-21T12:00:00.000Z'; +/** Supported independent corpus mutations, each following an acknowledged seed write. */ +export type CrashScenario = 'update' | 'comment'; +/** Exact single-case selection for independently replaying a retained synthetic failure. */ +export interface CrashSelection { scenario: CrashScenario; implementation: 'native' | 'published'; syscall: Boundary['syscall']; ordinal: number } +/** Machine-readable refusals deliberately omit paths and process output. */ +export type RefusalCode = 'unsupported_platform' | 'invalid_options' | 'process_unavailable' | + 'timeout' | 'oracle_version' | 'unsafe_source' | 'baseline_failed' | 'boundary_unreached' | 'io_error'; +/** A refusal is not a successful conformance receipt. */ +export class CrashRefusal extends Error { + readonly code: RefusalCode; + readonly remedy: string; + /** Construct an actionable, privacy-safe refusal. */ + constructor(code: RefusalCode, remedy: string) { super(code); this.code = code; this.remedy = remedy; } +} +/** SDK configuration. Input trackers are read only; every child works in a fresh non-git clone. */ +export interface CrashOptions { + native: string; + oracle?: string; + source?: string; + artifacts: string; + scenarios?: readonly CrashScenario[]; + /** Maximum per-task ordinal for each syscall. Default 64; truncation refuses. */ + maxBoundaries?: number; + timeoutMs?: number; + strace?: string; + only?: CrashSelection; +} +/** One individually measured kill/restart result, with no raw tracker or process contents. */ +export interface CrashCase { + scenario: CrashScenario; + implementation: 'native' | 'published'; + syscall: Boundary['syscall']; + boundary: number; + killed: boolean; + acknowledged: boolean; + historyPreserved: boolean; + restartStatus: number | null; + state: 'before' | 'committed' | 'diverged'; + durableSha256: string; + receiptSha256: string; + receiptMatches: boolean; + failure?: { code: 'restart_refused' | 'history_rewritten' | 'durable_divergence' | 'receipt_mismatch'; remedy: string }; + ok: boolean; +} +/** Compact versioned agent receipt; every declared boundary has a corresponding row. */ +export interface CrashReport { + schema: 'pm-rust/crash-v1'; + ok: boolean; + oracleVersion: string; + nativeSha256: string; + sourceFiles: number; + sourceItems: number; + sourceSha256: string; + sourceUnchanged: boolean; + acknowledgedComparisons: number; + killed: number; + boundaries: { scenario: CrashScenario; implementation: string; syscall: Boundary['syscall']; count: number }[]; + cases: CrashCase[]; + failuresRetained: number; + parity: { scenario: CrashScenario; bytes: boolean; acknowledgement: boolean }[]; +} + +/** Resolve symlinks in an existing ancestor before checking a future output directory. */ +function physicalPath(path: string): string { + return existsSync(path) ? realpathSync(path) : join(physicalPath(dirname(path)), basename(path)); +} + +/** Hash a receipt without leaking contents to agent output. */ +function receiptHash(receipt: ProcessReceipt): string { + return createHash('sha256').update(JSON.stringify([receipt.status, receipt.signal])) + .update(receipt.stdout).update('\0').update(receipt.stderr).digest('hex'); +} + +/** Build native or published command adapters with clocks pinned before CLI evaluation. */ +function adapters(options: CrashOptions): Record<'native' | 'published', Implementation> { + const oracle = resolve(options.oracle ?? fileURLToPath(new URL('../tests/oracle/node_modules/@unbrained/pm-cli', import.meta.url))); + let metadata: unknown; + try { metadata = JSON.parse(readFileSync(join(oracle, 'package.json'), 'utf8')); } catch { metadata = undefined; } + if (typeof metadata !== 'object' || metadata === null || !('version' in metadata) || metadata.version !== '2026.10.21') { + throw new CrashRefusal('oracle_version', 'Run just oracle-install to install the locked pm-cli 2026.10.21 oracle.'); + } + return { + native: { executable: resolve(options.native), args: [], trailing: ['--timestamp', CRASH_CLOCK] }, + published: { executable: process.execPath, args: ['--import', fileURLToPath(new URL('./crash-clock.mts', import.meta.url)), join(oracle, 'dist/cli.js')], trailing: ['--json', '--no-extensions'] }, + }; +} + +/** Require a successful, acknowledged real CLI command. */ +function acknowledged(receipt: ProcessReceipt): ProcessReceipt { + if (receipt.status !== 0 || receipt.stdout.length === 0) { + const refusal = new CrashRefusal('baseline_failed', 'Inspect the isolated baseline with the locked oracle and a compatible native executable.'); + refusal.cause = receipt; + throw refusal; + } + return receipt; +} + +/** Select only the probe item and stream, preserving all other source bytes separately. */ +function probe(files: Snapshot, id: string): Map { + return new Map([...files].filter(([name]) => name === `tasks/${id}.toon` || name === `history/${id}.jsonl`)); +} + +/** Require all pre-existing non-probe files, including original history, to remain exact. */ +function originalsUnchanged(original: Snapshot, current: Snapshot, id: string): boolean { + return [...original].every(([name, bytes]) => current.get(name)?.equals(bytes)) && + [...current.keys()].every(name => original.has(name) || name === `tasks/${id}.toon` || name === `history/${id}.jsonl` || !/\.(toon|jsonl)$/.test(name)); +} + +/** Validate options before reading source data or launching processes. */ +function validate(options: CrashOptions): { timeout: number; maximum: number; scenarios: readonly CrashScenario[] } { + const timeout = options.timeoutMs ?? 15000; + const maximum = options.maxBoundaries ?? 64; + const scenarios = options.scenarios ?? ['update', 'comment']; + if (!options.native || !options.artifacts || !Number.isSafeInteger(timeout) || timeout < 100 || timeout > 120000 || + !Number.isSafeInteger(maximum) || maximum < 1 || maximum > 64 || scenarios.length === 0 || + new Set(scenarios).size !== scenarios.length || scenarios.some(scenario => !['update', 'comment'].includes(scenario))) { + throw new CrashRefusal('invalid_options', 'Supply native and artifacts paths, distinct update/comment scenarios, timeout 100..120000 ms and maxBoundaries 1..64.'); + } + if (options.only && (!scenarios.includes(options.only.scenario) || !['native', 'published'].includes(options.only.implementation) || + !CRASH_SYSCALLS.includes(options.only.syscall) || !Number.isSafeInteger(options.only.ordinal) || options.only.ordinal < 1 || options.only.ordinal > maximum)) { + throw new CrashRefusal('invalid_options', 'Select one declared scenario, implementation, syscall and positive ordinal within maxBoundaries.'); + } + if (process.platform !== 'linux') throw new CrashRefusal('unsupported_platform', 'Run the live rename/fsync SIGKILL corpus on Linux with strace syscall injection enabled.'); + return { timeout, maximum, scenarios }; +} + +/** Run the real kill/restart corpus. It never mutates its input tracker or invokes Git. */ +export function runCrashCorpus(options: CrashOptions): CrashReport { + const { timeout, maximum, scenarios } = validate(options); + const root = mkdtempSync(join(tmpdir(), 'pm-rust-crash-')); + try { + return run(options, root, timeout, maximum, scenarios); + } catch (error) { + if (error instanceof CrashRefusal) throw error; + const message = error instanceof Error ? error.message : ''; + if (message === 'timeout' || message === 'process_unavailable') { + throw new CrashRefusal(message, 'Install executable native/Node/strace tools, allow ptrace, and increase timeoutMs within its documented bound if needed.'); + } + if (message === 'symlink' || message === 'special_file') throw new CrashRefusal('unsafe_source', 'Use a stable regular-file public tracker copy without symlinks or special files.'); + throw new CrashRefusal('io_error', 'Check readable input files and writable scratch/artifact directories; retry with a stable public tracker copy.'); + } finally { + rmSync(root, { recursive: true, force: true }); + } +} + +/** + * Replay a retained failure on its minimal synthetic fixture, never stored commands. + * Boundary failures use their whitelisted scenario/implementation/syscall/ordinal. + * A baseline_parity failure replays the scenario across both implementations and + * all their observed boundaries, rather than selecting a single case. + */ +export function replayCrashFailure(options: Omit, evidence: string): CrashReport { + let manifest: unknown; + try { manifest = JSON.parse(readFileSync(evidence, 'utf8')); } catch { + throw new CrashRefusal('invalid_options', 'Supply a readable retained case.json from this harness.'); + } + const record = typeof manifest === 'object' && manifest !== null ? manifest as Record : {}; + const selection = typeof record.only === 'object' && record.only !== null ? record.only as Record : {}; + if (record.schema === 'pm-rust/crash-v1' && record.kind === 'baseline_parity' && (record.scenario === 'update' || record.scenario === 'comment')) { + return runCrashCorpus({ ...options, scenarios: [record.scenario], source: join(dirname(resolve(evidence)), 'fixture/.agents/pm') }); + } + if (record.schema !== 'pm-rust/crash-v1' || !['update', 'comment'].includes(String(selection.scenario)) || + !['native', 'published'].includes(String(selection.implementation)) || !['rename', 'fsync'].includes(String(selection.syscall)) || + typeof selection.ordinal !== 'number') throw new CrashRefusal('invalid_options', 'Use a crash-v1 case with a declared synthetic scenario and syscall selection.'); + const only = selection as unknown as CrashSelection; + return runCrashCorpus({ ...options, only, scenarios: [only.scenario], source: join(dirname(resolve(evidence)), 'fixture/.agents/pm') }); +} + +/** Execute measured cases from a private baseline and retain only synthetic failure fixtures. */ +function run(options: CrashOptions, root: string, timeout: number, maximum: number, scenarios: readonly CrashScenario[]): CrashReport { + const implementations = adapters(options); + const base = join(root, 'base'); + mkdirSync(base); + let source: Snapshot | undefined; + const sourceRoot = options.source ? resolve(options.source) : undefined; + if (sourceRoot) { + const inside = relative(physicalPath(sourceRoot), physicalPath(resolve(options.artifacts))); + if (inside !== '..' && !inside.startsWith(`..${sep}`) && !isAbsolute(inside)) throw new CrashRefusal('unsafe_source', 'Keep the artifact output directory outside the read-only source tracker.'); + if (!existsSync(join(sourceRoot, 'settings.json'))) throw new CrashRefusal('unsafe_source', 'Point source at the .agents/pm directory of a stable public tracker copy.'); + source = snapshot(sourceRoot); + materialize(source, join(base, '.agents/pm')); + } else { + acknowledged(execute(implementations.published, ['init', '--defaults', '--prefix', 'crash-', '--agent-guidance', 'skip'], base, timeout)); + } + const tracker = join(base, '.agents/pm'); + const original = snapshot(tracker); + let settings: unknown; + try { settings = JSON.parse(readFileSync(join(tracker, 'settings.json'), 'utf8')); } catch { settings = undefined; } + const prefix = typeof settings === 'object' && settings !== null && 'id_prefix' in settings ? settings.id_prefix : undefined; + if (typeof prefix !== 'string' || !/^[a-z0-9-]+$/.test(prefix)) throw new CrashRefusal('unsafe_source', 'Use a tracker with a lowercase ASCII id_prefix.'); + const id = `${prefix}crash-probe`; + if ([...original.keys()].some(name => name.endsWith(`/${id}.toon`) || name === `history/${id}.jsonl`)) { + throw new CrashRefusal('unsafe_source', 'Use a public tracker copy without the reserved crash-probe item.'); + } + const common = ['--author', 'conformance-agent']; + acknowledged(execute(implementations.published, ['create', 'task', 'Synthetic crash probe', '--id', id, + '--description', 'Acknowledged synthetic write', '--priority', '1', '--create-mode', 'progressive', ...common], base, timeout)); + const seed = snapshot(tracker); + const report: CrashReport = { + schema: 'pm-rust/crash-v1', ok: true, oracleVersion: '2026.10.21', + nativeSha256: createHash('sha256').update(readFileSync(resolve(options.native))).digest('hex'), + sourceFiles: original.size, sourceItems: [...original.keys()].filter(name => name.endsWith('.toon')).length, sourceSha256: fingerprint(original), + sourceUnchanged: true, acknowledgedComparisons: 0, killed: 0, boundaries: [], cases: [], failuresRetained: 0, parity: [], + }; + let sequence = 0; + /** Make a unique workspace from an immutable snapshot. */ + function clone(files: Snapshot): string { + const directory = join(root, `case-${sequence++}`); + materialize(files, join(directory, '.agents/pm')); + return directory; + } + for (const scenario of scenarios) { + const mutation = scenario === 'update' ? ['update', id, '--description', 'Interrupted synthetic write'] : ['comment', id, 'Interrupted synthetic comment']; + // Use the same restart invocation for interrupted and uninterrupted references. + const restart = scenario === 'update' ? ['update', id, '--priority', '1', ...common] : + ['comment', id, 'Restart synthetic sentinel', ...common]; + const baselines = new Map(); + const receipts = new Map(); + for (const [label, implementation] of Object.entries(implementations)) { + const directory = clone(seed); + const trace = join(directory, 'baseline.trace'); + const receipt = acknowledged(execute(implementation, [...mutation, ...common], directory, timeout, trace, undefined, options.strace)); + baselines.set(label, snapshot(join(directory, '.agents/pm'))); + // Native update/comment expose legacy full JSON; published mutations expose flat JSON. + // Compare the shared update id/status or comment id/comments acknowledgement projection. + let decoded: unknown; + try { decoded = JSON.parse(receipt.stdout.toString()); } catch { decoded = undefined; } + const record = typeof decoded === 'object' && decoded !== null ? decoded as Record : {}; + const identity = typeof record.item === 'object' && record.item !== null ? record.item as Record : record; + if (identity.id !== id || (scenario === 'update' ? typeof identity.status !== 'string' : !Array.isArray(identity.comments))) { + const refusal = new CrashRefusal('baseline_failed', 'Use implementations that emit an update id/status or comment id/comments acknowledgement.'); + refusal.cause = { label, scenario, decoded }; + throw refusal; + } + receipts.set(label, JSON.stringify([identity.id, scenario === 'update' ? identity.status : identity.comments])); + const counts = CRASH_SYSCALLS.map(syscall => ({ syscall, count: boundaryCount(receipt.trace, syscall) })); + report.boundaries.push(...counts.map(entry => ({ scenario, implementation: label, ...entry }))); + if (counts.every(entry => entry.count === 0) || counts.some(entry => entry.count > maximum)) { + const refusal = new CrashRefusal('boundary_unreached', 'Allow ptrace/syscall tracing and set maxBoundaries high enough for every measured per-task rename/fsync ordinal (maximum 64).'); + refusal.cause = { label, counts, receipt }; + throw refusal; + } + // The reference restart uses precisely the same invocation as interrupted cases. + const committedReceipt = acknowledged(execute(implementation, restart, directory, timeout)); + const committed = snapshot(join(directory, '.agents/pm')); + const beforeDirectory = clone(seed); + const beforeReceipt = acknowledged(execute(implementation, restart, beforeDirectory, timeout)); + const before = snapshot(join(beforeDirectory, '.agents/pm')); + for (const { syscall, count } of counts) { + for (let ordinal = 1; ordinal <= count; ordinal++) { + if (options.only && (options.only.scenario !== scenario || options.only.implementation !== label || options.only.syscall !== syscall || options.only.ordinal !== ordinal)) continue; + const killedDirectory = clone(seed); + const crashed = execute(implementation, [...mutation, ...common], killedDirectory, timeout, + join(killedDirectory, 'kill.trace'), { syscall, ordinal }, options.strace); + if (!killedAtBoundary(crashed, { syscall, ordinal })) { + const refusal = new CrashRefusal('boundary_unreached', 'Use strace with SIGKILL injection support; a requested boundary must be observed, never skipped.'); + refusal.cause = { label, syscall, ordinal, receipt: crashed }; + throw refusal; + } + const recovered = execute(implementation, restart, killedDirectory, timeout); + const after = snapshot(join(killedDirectory, '.agents/pm')); + const state = recoveredState(probe(seed, id), probe(before, id), probe(committed, id), probe(after, id), recovered.status); + const historyPreserved = preservesHistory(seed, after); + const itemName = `tasks/${id}.toon`; + const itemPreserved = [seed, before, committed, baselines.get(label)!].some(reference => after.get(itemName)?.equals(reference.get(itemName)!)); + const acknowledgedWrite = originalsUnchanged(original, after, id) && historyPreserved && itemPreserved; + const receiptMatches = state === 'before' ? receiptHash(recovered) === receiptHash(beforeReceipt) : + state === 'committed' && receiptHash(recovered) === receiptHash(committedReceipt); + const row: CrashCase = { + scenario, implementation: label as 'native' | 'published', syscall, boundary: ordinal, killed: true, + acknowledged: acknowledgedWrite, historyPreserved, restartStatus: recovered.status, state, + durableSha256: fingerprint(probe(after, id)), receiptSha256: receiptHash(recovered), + receiptMatches, ok: recovered.status === 0 && acknowledgedWrite && state !== 'diverged' && receiptMatches, + }; + if (!row.ok) row.failure = { + code: recovered.status !== 0 ? 'restart_refused' : !historyPreserved ? 'history_rewritten' : state === 'diverged' ? 'durable_divergence' : 'receipt_mismatch', + remedy: 'Replay the retained synthetic case and inspect its lock, journal, bytes and receipt; preserve all original history. Runtime repairs need separate implementation work.', + }; + report.cases.push(row); + report.killed++; + if (!row.ok) { + report.ok = false; + // Never retain source data: the independent one-item synthetic run is the reduction corpus. + if (!source) { + mkdirSync(resolve(options.artifacts), { recursive: true }); + const retained = mkdtempSync(join(resolve(options.artifacts), 'failure-')); + const minimal = new Map([...original].filter(([name]) => !name.endsWith('.toon') && !name.endsWith('.jsonl'))); + materialize(minimal, join(retained, 'fixture/.agents/pm')); + materialize(probe(seed, id), join(retained, 'acknowledged')); + materialize(probe(after, id), join(retained, 'recovered')); + const only: CrashSelection = { scenario, implementation: row.implementation, syscall, ordinal }; + writeFileSync(join(retained, 'case.json'), JSON.stringify({ schema: report.schema, only, row, mutation, restart, clock: CRASH_CLOCK }, null, 2)); + writeFileSync(join(retained, 'kill.trace'), crashed.trace); + report.failuresRetained++; + } + } + } + } + } + const native = baselines.get('native')!; + const published = baselines.get('published')!; + const bytes = sameBytes(probe(native, id), probe(published, id)); + const acknowledgement = receipts.get('native') === receipts.get('published'); + const parity = bytes && acknowledgement; + report.parity.push({ scenario, bytes, acknowledgement }); + report.acknowledgedComparisons++; + if (!parity) { + report.ok = false; + if (!source) { + mkdirSync(resolve(options.artifacts), { recursive: true }); + const retained = mkdtempSync(join(resolve(options.artifacts), 'failure-')); + materialize(new Map([...original].filter(([name]) => !name.endsWith('.toon') && !name.endsWith('.jsonl'))), join(retained, 'fixture/.agents/pm')); + materialize(probe(seed, id), join(retained, 'acknowledged')); + materialize(probe(native, id), join(retained, 'native')); + materialize(probe(published, id), join(retained, 'published')); + writeFileSync(join(retained, 'case.json'), JSON.stringify({ schema: report.schema, kind: 'baseline_parity', scenario, bytes, acknowledgement, clock: CRASH_CLOCK }, null, 2)); + report.failuresRetained++; + } + } + } + if (options.only && report.cases.length !== 1) throw new CrashRefusal('boundary_unreached', 'The retained boundary was not observed by these executable versions; use the original pinned implementations.'); + if (sourceRoot && source) { + report.sourceUnchanged = sameBytes(source, snapshot(sourceRoot)); + report.ok &&= report.sourceUnchanged; + if (!report.ok) { + // Independently replay the same corpus on a minimal synthetic tracker, never copy public histories into evidence. + const reduced = runCrashCorpus({ ...options, source: undefined }); + report.failuresRetained = reduced.failuresRetained; + } + } + return report; +} diff --git a/scripts/crash-harness.test.mts b/scripts/crash-harness.test.mts new file mode 100644 index 0000000..196bf97 --- /dev/null +++ b/scripts/crash-harness.test.mts @@ -0,0 +1,298 @@ +import assert from 'node:assert/strict'; +import { spawnSync } from 'node:child_process'; +import { chmodSync, existsSync, mkdtempSync, mkdirSync, readdirSync, readFileSync, rmSync, symlinkSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join, resolve } from 'node:path'; +import { test } from 'node:test'; +import { CrashRefusal, replayCrashFailure, runCrashCorpus } from './crash-harness.mts'; +import { boundaryCount, execute, killedAtBoundary } from './crash-process.mts'; +import { fingerprint, materialize, preservesHistory, recoveredState, sameBytes, snapshot } from './crash-storage.mts'; + +/** Own and clean a real plain-directory tracker fixture. */ +function temporary(callback: (root: string) => void): void { + const root = mkdtempSync(join(tmpdir(), 'pm-rust-crash-test-')); + try { callback(root); } finally { rmSync(root, { recursive: true, force: true }); } +} + +test('history preservation rejects truncation, replacement and missing streams byte-exactly', () => { + const before = new Map([['history/probe.jsonl', Buffer.from('{"op":"create"}\n')]]); + assert.equal(preservesHistory(before, before), true); + assert.equal(preservesHistory(before, new Map([['history/probe.jsonl', Buffer.from('{"op":"create"}\n{}\n')]])), true); + for (const replacement of ['', '{"op":"update"}\n', '{"op":"create"}']) { + assert.equal(preservesHistory(before, new Map([['history/probe.jsonl', Buffer.from(replacement)]])), false); + } + assert.equal(preservesHistory(before, new Map()), false); +}); + +test('successful restart cannot lose its own history while a safe refusal may retain the seed', () => temporary(root => { + materialize(new Map([['history/probe.jsonl', Buffer.from('seed\n')]]), root); + const seed = snapshot(root); + writeFileSync(join(root, 'history/probe.jsonl'), 'seed\nrestart\n'); + const before = snapshot(root); + writeFileSync(join(root, 'history/probe.jsonl'), 'seed\nmutation\nrestart\n'); + const committed = snapshot(root); + assert.equal(recoveredState(seed, before, committed, seed, 0), 'diverged'); + assert.equal(recoveredState(seed, before, committed, seed, 4), 'before'); + assert.equal(recoveredState(seed, before, committed, before, 0), 'before'); + assert.equal(recoveredState(seed, before, committed, committed, 0), 'committed'); +})); + +test('inventories include nested custom items but exclude only root caches', () => temporary(root => { + const files = new Map([ + ['settings.json', Buffer.from('{}')], ['tasks/probe.toon', Buffer.from('id: probe\n')], + ['history/probe.jsonl', Buffer.from('{}\n')], ['custom/runtime/probe.toon', Buffer.from('id: nested\n')], + ]); + materialize(files, root); + for (const cache of ['runtime', 'locks', 'transactions', 'checkpoints', 'search']) { + mkdirSync(join(root, cache)); + writeFileSync(join(root, cache, 'cache.json'), 'private'); + } + assert.equal(sameBytes(files, snapshot(root)), true); + assert.equal(fingerprint(files), fingerprint(new Map([...files].reverse()))); + assert.notEqual(fingerprint(files), fingerprint(new Map([...files, ['extra', Buffer.from('')]]))); + assert.equal(sameBytes(files, new Map([...files, ['extra', Buffer.from('')]])), false); + assert.equal(sameBytes(files, new Map([...files].slice(1))), false); + assert.equal(sameBytes(files, new Map([...files].map(([key]) => [key, Buffer.from('changed')]))), false); +})); + +test('snapshot refuses symlinks without following their target', { skip: process.platform === 'win32' }, () => temporary(root => { + writeFileSync(join(root, 'real'), 'untouched'); + symlinkSync(join(root, 'real'), join(root, 'link')); + assert.throws(() => snapshot(root), /symlink/); + assert.equal(readFileSync(join(root, 'real'), 'utf8'), 'untouched'); +})); + +test('materialization refuses traversal before creating any files', () => temporary(root => { + for (const name of ['../outside', '/absolute', 'tasks/../outside', 'a\\b', 'C:/drive']) { + assert.throws(() => materialize(new Map([[name, Buffer.from('bad')]]), root), /unsafe_filename/); + } + assert.equal(readdirSync(root).length, 0); +})); + +test('boundary inventory counts task-local entries and ignores resumed or unrelated calls', () => { + const trace = ' 10 write(3, "a", 1) = 1\n10 fsync(3) = 0\n11 fsync(4 \n11 <... fsync resumed>) = 0\n10 fsync(3) = 0\n12 rename("a", "b") = 0\n'; + assert.equal(boundaryCount(trace, 'fsync'), 2); + assert.equal(boundaryCount(trace, 'rename'), 1); + assert.equal(boundaryCount('', 'fsync'), 0); +}); + +test('ordinary exits and non-boundary deaths cannot masquerade as injected kills', () => { + const receipt = { status: null, signal: 'SIGKILL', stdout: Buffer.alloc(0), stderr: Buffer.alloc(0), trace: '10 fsync(3) = ?\n10 +++ killed by SIGKILL +++\n' }; + assert.equal(killedAtBoundary(receipt, { syscall: 'fsync', ordinal: 1 }), true); + assert.equal(killedAtBoundary({ ...receipt, status: 0 }, { syscall: 'fsync', ordinal: 1 }), false); + assert.equal(killedAtBoundary({ ...receipt, trace: '10 +++ killed by SIGKILL +++\n' }, { syscall: 'fsync', ordinal: 1 }), false); + assert.equal(killedAtBoundary(receipt, { syscall: 'rename', ordinal: 1 }), false); +}); + +test('invalid SDK options refuse before launching or reading any tracker', () => { + for (const patch of [{ native: '' }, { artifacts: '' }, { maxBoundaries: 0 }, { maxBoundaries: 65 }, + { timeoutMs: 99 }, { timeoutMs: Infinity }, { scenarios: [] }, { scenarios: ['update', 'update'] as const }]) { + assert.throws(() => runCrashCorpus({ native: 'missing', artifacts: 'unused', ...patch }), + (error: unknown) => error instanceof CrashRefusal && error.code === 'invalid_options' && error.remedy.length > 0); + } +}); + +test('oracle metadata failures retain the typed SDK refusal and remedy', { skip: process.platform !== 'linux' }, () => temporary(root => { + const cases: { name: string; content?: string; absent?: boolean; directory?: boolean }[] = [ + { name: 'missing-root', absent: true }, { name: 'missing-package' }, + { name: 'unreadable-package', directory: true }, { name: 'malformed', content: '{"version":' }, + { name: 'null', content: 'null' }, { name: 'array', content: '[]' }, + { name: 'scalar', content: '"sensitive-sentinel"' }, { name: 'missing-version', content: '{}' }, + { name: 'numeric-version', content: '{"version":42}' }, + { name: 'wrong-version', content: '{"version":"2026.10.20"}' }, + ]; + const source = join(root, 'source'); + mkdirSync(source); + writeFileSync(join(source, 'sentinel'), 'unchanged'); + for (const entry of cases) { + const oracle = join(root, entry.name); + if (!entry.absent) mkdirSync(oracle); + if (entry.directory) mkdirSync(join(oracle, 'package.json')); + if (entry.content !== undefined) writeFileSync(join(oracle, 'package.json'), entry.content); + const artifacts = join(root, 'evidence'); + assert.throws(() => runCrashCorpus({ native: 'unused', source, artifacts, oracle }), + (error: unknown) => error instanceof CrashRefusal && error.code === 'oracle_version' && + error.remedy === 'Run just oracle-install to install the locked pm-cli 2026.10.21 oracle.', entry.name); + assert.equal(existsSync(artifacts), false); + assert.deepEqual(readdirSync(source), ['sentinel']); + assert.equal(readFileSync(join(source, 'sentinel'), 'utf8'), 'unchanged'); + } +})); + +test('oracle metadata CLI refusals expose the remedy without leaking inputs', { skip: process.platform !== 'linux' }, () => temporary(root => { + const oracle = join(root, 'sensitive-sentinel'); + mkdirSync(oracle); + const cli = resolve('scripts/crash-cli.mts'); + for (const malformed of [false, true]) { + if (malformed) writeFileSync(join(oracle, 'package.json'), '{"version":"private-sentinel"'); + for (const json of [false, true]) { + const result = spawnSync(process.execPath, [cli, '--native', 'unused', '--oracle', oracle, + '--artifacts', join(root, 'evidence'), ...(json ? ['--json'] : [])], { encoding: 'utf8' }); + assert.equal(result.error, undefined); + assert.equal(result.status, 2); + assert.equal(result.stderr, ''); + assert.doesNotMatch(result.stdout, /sensitive-sentinel|private-sentinel|io_error/); + if (json) assert.deepEqual(JSON.parse(result.stdout), { ok: false, code: 'oracle_version', + remedy: 'Run just oracle-install to install the locked pm-cli 2026.10.21 oracle.' }); + else assert.equal(result.stdout, 'ok: false\ncode: "oracle_version"\nremedy: "Run just oracle-install to install the locked pm-cli 2026.10.21 oracle."\n'); + } + } + assert.equal(existsSync(join(root, 'evidence')), false); +})); + +test('malformed source settings retain the typed source refusal without writes', { skip: process.platform !== 'linux' }, () => temporary(root => { + const source = join(root, 'source'); + mkdirSync(source); + const artifacts = join(root, 'evidence'); + for (const content of ['{"id_prefix":', 'null', '[]', '{}', '{"id_prefix":42}', '{"id_prefix":"INVALID-"}']) { + writeFileSync(join(source, 'settings.json'), content); + const before = snapshot(source); + assert.throws(() => runCrashCorpus({ native: 'unused', source, artifacts }), + (error: unknown) => error instanceof CrashRefusal && error.code === 'unsafe_source' && + error.remedy === 'Use a tracker with a lowercase ASCII id_prefix.'); + assert.equal(sameBytes(before, snapshot(source)), true); + assert.equal(existsSync(artifacts), false); + } +})); + +test('malformed mutation acknowledgements retain the typed baseline refusal', + { skip: process.platform !== 'linux', timeout: 180000 }, () => temporary(root => { + const native = join(root, 'incompatible-native'); + writeFileSync(native, '#!/bin/sh\nprintf "%s\\n" "synthetic invalid JSON"\n'); + chmodSync(native, 0o755); + const artifacts = join(root, 'evidence'); + assert.throws(() => runCrashCorpus({ native, artifacts, scenarios: ['update'], timeoutMs: 120000 }), + (error: unknown) => error instanceof CrashRefusal && error.code === 'baseline_failed' && + error.remedy.includes('update id/status or comment id/comments acknowledgement')); + assert.equal(existsSync(artifacts), false); + assert.equal(existsSync(join(root, '.git')), false); + })); + +test('CLI help and refusals are bounded, structured and omit input path contents', () => { + const cli = resolve('scripts/crash-cli.mts'); + const help = spawnSync(process.execPath, [cli, '--help'], { encoding: 'utf8' }); + assert.equal(help.status, 0); + assert.match(help.stdout, /Usage:/); + for (const args of [[], ['--unknown'], ['--native'], ['--native', 'sensitive-sentinel']]) { + const result = spawnSync(process.execPath, [cli, ...args, '--json'], { encoding: 'utf8' }); + assert.equal(result.status, 2); + assert.equal(JSON.parse(result.stdout).code, 'invalid_options'); + assert.doesNotMatch(result.stdout, /sensitive-sentinel/); + assert.equal(result.stderr, ''); + assert.ok(result.stdout.length < 500); + } +}); + +test('real child timeout and missing executable are explicit failures', () => temporary(root => { + assert.throws(() => execute({ executable: process.execPath, args: ['-e', 'setTimeout(()=>{},10000)'] }, [], root, 100), /timeout/); + assert.throws(() => execute({ executable: join(root, 'absent'), args: [] }, [], root, 100), /process_unavailable/); +})); + +test('replay refuses unknown manifests without executing their stored commands', () => temporary(root => { + const manifest = join(root, 'case.json'); + const options = { native: 'unused', artifacts: join(root, 'evidence') }; + for (const value of ['invalid', '{}', '{"schema":"pm-rust/crash-v1","only":{"scenario":"shell"},"command":"danger"}']) { + writeFileSync(manifest, value); + assert.throws(() => replayCrashFailure(options, manifest), (error: unknown) => error instanceof CrashRefusal && error.code === 'invalid_options'); + assert.equal(existsSync(options.artifacts), false); + } +})); + +test('retained replay guidance distinguishes boundary cases from baseline parity', () => { + const guide = readFileSync(new URL('../docs/CRASH_HARNESS.md', import.meta.url), 'utf8').replace(/\r\n/g, '\n'); + for (const text of [guide, guide.replace(/\n/g, '\r\n')]) { + const example = /```typescript\r?\n([\s\S]*?)```/.exec(text)?.[1]; + assert.ok(example, 'the retained-case SDK example must remain documented with LF or CRLF'); + assert.match(example, /kind: 'baseline_parity'[\s\S]*?scenario across both implementations[\s\S]*?all their observed boundaries/); + assert.match(example, /whitelisted scenario\/implementation\/syscall\/ordinal/); + assert.match(example, /never stored commands/); + } +}); + +test('SDK replay guidance includes the baseline parity scenario exception', () => { + const sdk = readFileSync(new URL('./crash-harness.mts', import.meta.url), 'utf8'); + const guidance = /\/\*\*((?:(?!\*\/)[\s\S])*)\*\/\s*export function replayCrashFailure/.exec(sdk)?.[1]; + assert.ok(guidance, 'the public replay API must retain its docstring'); + assert.match(guidance, /baseline_parity failure replays the scenario across both implementations[\s\S]*?all their observed boundaries/); + assert.match(guidance, /whitelisted scenario\/implementation\/syscall\/ordinal/); + assert.match(guidance, /never stored commands/); +}); + +test('artifact destinations cannot mutate the source through relative names or symlink aliases', + { skip: process.platform !== 'linux' }, () => temporary(root => { + const source = join(root, 'source'); + mkdirSync(source); + writeFileSync(join(source, 'settings.json'), '{"id_prefix":"safe-"}'); + const alias = join(root, 'alias'); + symlinkSync(source, alias); + for (const artifacts of [source, join(source, 'evidence'), join(source, '..named'), join(alias, 'evidence')]) { + assert.throws(() => runCrashCorpus({ source, artifacts, native: 'unused' }), + (error: unknown) => error instanceof CrashRefusal && error.code === 'unsafe_source' && error.remedy.includes('outside the read-only')); + } + assert.deepEqual(readdirSync(source), ['settings.json']); + assert.equal(readFileSync(join(source, 'settings.json'), 'utf8'), '{"id_prefix":"safe-"}'); + })); + +test('live syscall injection kills a real writing child and restart preserves earlier acknowledged bytes', + { skip: process.platform !== 'linux' }, () => temporary(root => { + const durable = join(root, 'acknowledged'); + writeFileSync(durable, 'acknowledged\n'); + const program = { executable: process.execPath, args: ['-e', 'const fs=require("node:fs");const f=fs.openSync("candidate","w");fs.writeSync(f,"candidate");fs.fsyncSync(f);'] }; + const reference = execute(program, [], root, 5000, join(root, 'reference.trace')); + assert.equal(reference.status, 0); + assert.equal(boundaryCount(reference.trace, 'fsync'), 1); + const killed = execute(program, [], root, 5000, join(root, 'kill.trace'), { syscall: 'fsync', ordinal: 1 }); + assert.equal(killedAtBoundary(killed, { syscall: 'fsync', ordinal: 1 }), true); + const restarted = execute({ executable: process.execPath, args: ['-e', 'process.stdout.write(require("node:fs").readFileSync("acknowledged"))'] }, [], root, 5000); + assert.equal(restarted.status, 0); + assert.equal(restarted.stdout.toString(), 'acknowledged\n'); + assert.equal(existsSync(join(root, '.git')), false); + })); + +test('real pm corpus recovers every measured boundary with exact history and receipts without git', + { skip: process.platform !== 'linux', timeout: 120000 }, () => temporary(root => { + const tools = join(root, 'tools'); + mkdirSync(tools); + symlinkSync(process.execPath, join(tools, 'node')); + const located = spawnSync('which', ['strace'], { encoding: 'utf8' }); + assert.equal(located.status, 0, 'Linux crash tests require strace'); + symlinkSync(located.stdout.trim(), join(tools, 'strace')); + const savedPath = process.env.PATH; + process.env.PATH = tools; + try { + const options = { native: resolve('target/debug/pm-rust'), artifacts: join(root, 'evidence') }; + const report = runCrashCorpus(options); + assert.equal(report.killed, report.boundaries.reduce((count, row) => count + row.count, 0)); + assert.ok(report.killed > 0); + assert.ok(report.cases.every(row => row.historyPreserved && row.acknowledged)); + assert.equal(report.ok, true, JSON.stringify(report.cases.filter(row => !row.ok))); + assert.ok(report.cases.every(row => row.ok && row.restartStatus === 0 && row.receiptMatches)); + assert.ok(report.parity.every(row => row.bytes && row.acknowledgement)); + assert.equal(report.failuresRetained, 0); + assert.equal(existsSync(join(root, '.git')), false); + } finally { process.env.PATH = savedPath; } + })); + +for (const scenario of ['update', 'comment'] as const) { + test(`retained ${scenario} first-fsync reproduction recovers before TTL with exact bytes and receipt`, + { skip: process.platform !== 'linux', timeout: 120000 }, () => temporary(root => { + const evidence = resolve(`tests/fixtures/crash-restart/${scenario}.json`); + const before = snapshot(resolve('tests/fixtures/crash-restart')); + const report = replayCrashFailure({ native: resolve('target/debug/pm-rust'), artifacts: join(root, 'evidence') }, evidence); + assert.equal(report.ok, true, JSON.stringify(report.cases)); + assert.equal(report.killed, 1); + assert.equal(report.cases.length, 1); + const row = report.cases[0]; + assert.equal(row.scenario, scenario); + assert.equal(row.implementation, 'native'); + assert.equal(row.syscall, 'fsync'); + assert.equal(row.boundary, 1); + assert.equal(row.state, 'before'); + assert.equal(row.restartStatus, 0); + assert.ok(row.acknowledged && row.historyPreserved && row.receiptMatches); + assert.ok(report.parity.every(parity => parity.bytes && parity.acknowledgement)); + assert.equal(report.sourceUnchanged, true); + assert.equal(report.failuresRetained, 0); + assert.equal(sameBytes(before, snapshot(resolve('tests/fixtures/crash-restart'))), true); + })); +} diff --git a/scripts/crash-process.mts b/scripts/crash-process.mts new file mode 100644 index 0000000..81d6c27 --- /dev/null +++ b/scripts/crash-process.mts @@ -0,0 +1,55 @@ +import { spawnSync } from 'node:child_process'; +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; + +/** Observable syscall classes shared by real native and published processes. */ +export const CRASH_SYSCALLS = ['rename', 'fsync'] as const; +/** One syscall's per-task entry ordinal; ordinals are not cross-runtime semantic phases. */ +export interface Boundary { syscall: typeof CRASH_SYSCALLS[number]; ordinal: number } +/** Complete child receipt, retained privately and reduced to hashes in public output. */ +export interface ProcessReceipt { status: number | null; signal: string | null; stdout: Buffer; stderr: Buffer; trace: string } +/** Executable argument-vector adapter; never evaluated by a shell. */ +export interface Implementation { executable: string; args: readonly string[]; trailing?: readonly string[] } + +/** Spawn one bounded isolated process, optionally injecting SIGKILL at a per-task syscall entry. */ +export function execute(implementation: Implementation, args: readonly string[], cwd: string, + timeout: number, traceFile?: string, boundary?: Boundary, strace = 'strace'): ProcessReceipt { + const argv = [...implementation.args, ...args, ...(implementation.trailing ?? [])]; + const traced = traceFile !== undefined; + const result = spawnSync(traced ? strace : implementation.executable, + traced ? ['-f', '--kill-on-exit', '-qq', '-o', traceFile, '-e', `trace=${CRASH_SYSCALLS.join(',')}`, + ...(boundary ? ['-e', `inject=${boundary.syscall}:signal=SIGKILL:when=${boundary.ordinal}`] : []), + '--', implementation.executable, ...argv] : argv, { + cwd, timeout, killSignal: 'SIGKILL', maxBuffer: 4 * 1024 * 1024, + env: { + PATH: process.env.PATH, HOME: join(cwd, 'isolated-home'), + SystemRoot: process.env.SystemRoot, SystemDrive: process.env.SystemDrive, + COMSPEC: process.env.COMSPEC, PATHEXT: process.env.PATHEXT, + TEMP: process.env.TEMP, TMP: process.env.TMP, + PM_AUTHOR: 'conformance-agent', PM_SENTRY_DISABLED: '1', PM_TELEMETRY_SOURCE_CONTEXT: 'test', + DO_NOT_TRACK: '1', NODE_DISABLE_COMPILE_CACHE: '1', + GIT_CEILING_DIRECTORIES: cwd, GIT_CONFIG_GLOBAL: '/dev/null', GIT_CONFIG_NOSYSTEM: '1', + LC_ALL: 'C', TZ: 'UTC', + }, + }); + if (result.error) throw new Error('code' in result.error && result.error.code === 'ETIMEDOUT' ? 'timeout' : 'process_unavailable'); + return { status: result.status, signal: result.signal, stdout: result.stdout, stderr: result.stderr, + trace: traceFile ? readFileSync(traceFile, 'utf8') : '' }; +} + +/** Determine the largest syscall ordinal reached by any individual traced task. */ +export function boundaryCount(trace: string, syscall: Boundary['syscall']): number { + const counts = new Map(); + for (const line of trace.split('\n')) { + const match = /^\s*(\d+)\s+(rename|fsync)\(/.exec(line); + if (match?.[2] === syscall) counts.set(match[1], (counts.get(match[1]) ?? 0) + 1); + } + return Math.max(0, ...counts.values()); +} + +/** Require evidence of an injected fatal signal, rather than counting an ordinary exit as a crash. */ +export function killedAtBoundary(receipt: ProcessReceipt, boundary: Boundary): boolean { + const interrupted = new RegExp(`${boundary.syscall}\\([^\\n]*(?:= \\?|; + +/** Inventory regular tracker files, refusing symlinks and ignoring only known root caches. */ +export function snapshot(root: string, prefix = ''): Map { + if (!prefix && lstatSync(root).isSymbolicLink()) throw new Error('symlink'); + const result = new Map(); + for (const entry of readdirSync(join(root, prefix), { withFileTypes: true })) { + if (!prefix && ['runtime', 'locks', 'transactions', 'checkpoints', 'search'].includes(entry.name)) continue; + if (entry.isSymbolicLink()) throw new Error('symlink'); + const key = prefix ? `${prefix}/${entry.name}` : entry.name; + if (entry.isDirectory()) { + for (const [name, bytes] of snapshot(root, key)) result.set(name, bytes); + } else if (entry.isFile()) result.set(key, readFileSync(join(root, key))); + else throw new Error('special_file'); + } + return result; +} + +/** Hash both filenames and exact bytes with length framing and stable byte ordering. */ +export function fingerprint(files: Snapshot): string { + const hash = createHash('sha256'); + for (const key of [...files.keys()].sort()) { + const bytes = files.get(key)!; + hash.update(`${Buffer.byteLength(key)}:${key}${bytes.length}:`).update(bytes); + } + return hash.digest('hex'); +} + +/** Materialize a snapshot into a new private tracker; callers own the fresh destination. */ +export function materialize(files: Snapshot, destination: string): void { + if ([...files.keys()].some(name => name.split('/').some(part => !part || part === '.' || part === '..') || name.includes('\\') || /^[A-Za-z]:/.test(name))) throw new Error('unsafe_filename'); + for (const [name, bytes] of files) { + const target = join(destination, name); + mkdirSync(dirname(target), { recursive: true }); + writeFileSync(target, bytes, { flag: 'wx', mode: 0o600 }); + } +} + +/** Require each retained history prefix to remain exactly byte-for-byte intact. */ +export function preservesHistory(before: Snapshot, after: Snapshot): boolean { + for (const [name, bytes] of before) { + if (name.startsWith('history/') && name.endsWith('.jsonl') && + !after.get(name)?.subarray(0, bytes.length).equals(bytes)) return false; + } + return true; +} + +/** Compare the complete inventory, including deletions and newly created files. */ +export function sameBytes(left: Snapshot, right: Snapshot): boolean { + return left.size === right.size && [...left].every(([key, bytes]) => right.get(key)?.equals(bytes)); +} + +/** Accept seed bytes only after a refused restart; successful restarts must match a complete reference. */ +export function recoveredState(seed: Snapshot, before: Snapshot, committed: Snapshot, after: Snapshot, status: number | null): 'before' | 'committed' | 'diverged' { + if (sameBytes(before, after) || status !== 0 && sameBytes(seed, after)) return 'before'; + return sameBytes(committed, after) ? 'committed' : 'diverged'; +} diff --git a/src/mutation.rs b/src/mutation.rs index 4c202be..13206da 100644 --- a/src/mutation.rs +++ b/src/mutation.rs @@ -960,6 +960,16 @@ fn acquire_lock_attempt( .and_then(|observed| observed.metadata.modified().ok()) .and_then(|modified| SystemTime::now().duration_since(modified).ok()) .is_some_and(|age| age > Duration::from_secs(ttl_seconds)); + // A crash during lock fsync leaves a fresh, empty gate beside the + // complete receipt. The kernel lease excludes a live native initializer; + // confirmed original-process death admits the existing audited recovery + // before TTL expiry. Unknown owners still require the ordinary policy, + // and reclaim_stale_gate retains nonempty or replaced gates. + #[cfg(target_os = "linux")] + let abandoned = abandoned + || read_lock_owner(&path).is_ok_and(|(raw, _)| { + confirmed_dead_owner(&serde_json::from_str(&raw).unwrap_or(Value::Null)) + }); if !abandoned { return Err(PmRustError::LockConflict { id: id.to_owned() }); } diff --git a/tests/concurrency_contract.rs b/tests/concurrency_contract.rs index b5427f2..db5010a 100644 --- a/tests/concurrency_contract.rs +++ b/tests/concurrency_contract.rs @@ -1,8 +1,9 @@ //! Multi-process no-lost-update acceptance for native comment mutations. //! //! The test spawns many concurrent native CLI processes that each append a -//! comment to one shared item, relying on the per-item lock and its configured -//! wait budget rather than any test-side coordination. It then proves that +//! comment to one shared item. A start barrier releases the writer threads +//! together; each process relies on the per-item lock and its configured wait +//! budget. The test then proves that //! every accepted mutation survived: the item's comment rows, the history //! stream length, and the history hash chain all agree with the set of //! processes that reported success. @@ -21,7 +22,7 @@ mod process_environment; mod published_cli; const PROCESSES: usize = 50; -const MUTATIONS_PER_PROCESS: usize = 1; +const MUTATIONS_PER_PROCESS: usize = 3; /// Run the native executable in a plain tracker with no Git executable available. fn native_command(workspace: &Path) -> Command { @@ -132,6 +133,16 @@ fn history_records(workspace: &Path) -> Result, Box, _>>()?) } +/// Enumerates every writer and mutation marker in the complete contention workload. +fn expected_markers() -> std::collections::BTreeSet { + (0..PROCESSES) + .flat_map(|process| { + (0..MUTATIONS_PER_PROCESS) + .map(move |mutation| format!("concurrent note {process}-{mutation}")) + }) + .collect() +} + #[test] /// Proves concurrent native processes never lose an accepted mutation. fn concurrent_comment_processes_preserve_every_accepted_mutation() @@ -189,9 +200,7 @@ fn concurrent_comment_processes_preserve_every_accepted_mutation() .iter() .filter_map(|comment| comment["text"].as_str()) .collect(); - let expected: std::collections::BTreeSet<_> = (0..PROCESSES) - .map(|process| format!("concurrent note {process}-0")) - .collect(); + let expected = expected_markers(); assert_eq!(actual, expected.iter().map(String::as_str).collect()); let document = pm_rust::Workspace::discover(directory.path())?.get("sample-load")?; diff --git a/tests/fixtures/crash-restart/comment.json b/tests/fixtures/crash-restart/comment.json new file mode 100644 index 0000000..adf7065 --- /dev/null +++ b/tests/fixtures/crash-restart/comment.json @@ -0,0 +1,4 @@ +{ + "schema": "pm-rust/crash-v1", + "only": { "scenario": "comment", "implementation": "native", "syscall": "fsync", "ordinal": 1 } +} diff --git a/tests/fixtures/crash-restart/fixture/.agents/pm/settings.json b/tests/fixtures/crash-restart/fixture/.agents/pm/settings.json new file mode 100644 index 0000000..ec3dd65 --- /dev/null +++ b/tests/fixtures/crash-restart/fixture/.agents/pm/settings.json @@ -0,0 +1,80 @@ +{ + "version": 1, + "id_prefix": "crash-", + "author_default": "conformance-agent", + "item_format": "toon", + "locks": { + "ttl_seconds": 1800, + "wait_ms": 3000 + }, + "output": { + "default_format": "toon" + }, + "extensions": { + "enabled": [], + "disabled": [], + "policy": { + "mode": "off", + "trust_mode": "off", + "require_provenance": false, + "trusted_extensions": [], + "default_sandbox_profile": "none", + "allowed_extensions": [], + "blocked_extensions": [], + "allowed_capabilities": [], + "blocked_capabilities": [], + "allowed_surfaces": [], + "blocked_surfaces": [], + "allowed_commands": [], + "blocked_commands": [], + "allowed_actions": [], + "blocked_actions": [], + "allowed_services": [], + "blocked_services": [], + "extension_overrides": [] + } + }, + "search": { + "score_threshold": 0, + "hybrid_semantic_weight": 0.7, + "max_results": 50, + "embedding_model": "", + "embedding_batch_size": 32, + "embedding_timeout_ms": 30000, + "scanner_max_batch_retries": 3, + "provider": "", + "default_mode": "auto", + "mutation_refresh_policy": "semantic_configured", + "query_expansion": { + "enabled": false, + "provider": "" + }, + "rerank": { + "enabled": false, + "model": "", + "top_k": 20 + } + }, + "providers": { + "openai": { + "base_url": "", + "api_key": "", + "model": "" + }, + "ollama": { + "base_url": "", + "model": "" + } + }, + "vector_store": { + "adapter": "", + "collection_name": "pm_items", + "qdrant": { + "url": "", + "api_key": "" + }, + "lancedb": { + "path": "" + } + } +} diff --git a/tests/fixtures/crash-restart/update.json b/tests/fixtures/crash-restart/update.json new file mode 100644 index 0000000..bada2ad --- /dev/null +++ b/tests/fixtures/crash-restart/update.json @@ -0,0 +1,4 @@ +{ + "schema": "pm-rust/crash-v1", + "only": { "scenario": "update", "implementation": "native", "syscall": "fsync", "ordinal": 1 } +} diff --git a/tests/harness/eslint.config.mjs b/tests/harness/eslint.config.mjs new file mode 100644 index 0000000..cbea818 --- /dev/null +++ b/tests/harness/eslint.config.mjs @@ -0,0 +1,6 @@ +import tseslint from 'typescript-eslint'; + +export default tseslint.config( + { files: ['scripts/crash-*.mts'], extends: [tseslint.configs.recommended], + rules: { '@typescript-eslint/no-non-null-assertion': 'off' } }, +); diff --git a/tests/harness/package-lock.json b/tests/harness/package-lock.json new file mode 100644 index 0000000..7992386 --- /dev/null +++ b/tests/harness/package-lock.json @@ -0,0 +1,1333 @@ +{ + "name": "pm-rust-crash-harness-tools", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "pm-rust-crash-harness-tools", + "version": "1.0.0", + "devDependencies": { + "@types/node": "24.19.2", + "eslint": "10.13.0", + "typescript": "6.0.3", + "typescript-eslint": "8.72.0" + } + }, + "node_modules/@cacheable/memory": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cacheable/memory/-/memory-2.2.0.tgz", + "integrity": "sha512-CTLKqLItRCEixEAewD3/j9DB3/o96gpTPD4eJ1v+DGOlxZRZncRQkGYqqnAGCscYd6RNeXfGeiuCphsPtqyIfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cacheable/utils": "^2.5.0", + "@keyv/bigmap": "^1.3.1", + "hookified": "^1.15.1", + "keyv": "^5.6.0" + } + }, + "node_modules/@cacheable/utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@cacheable/utils/-/utils-2.5.0.tgz", + "integrity": "sha512-buipgOVDkkPXNR5+xBpDw7Zk2n1EvU7qBJCNUcL7rhQ//kfpOXPAvQ511Os0vpLYJ1pZnvudNytkQt2hst3wqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hashery": "^1.5.1", + "keyv": "^5.6.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.7.0.tgz", + "integrity": "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.3.tgz", + "integrity": "sha512-IkO+/KEUvwbVpiURZg+P7zF74z5Jxe0UgJxVni+RtoHQ6IZieXaO02kmadomap/q+l6bc/jdPGGqTjhuZnuz1Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@keyv/bigmap": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@keyv/bigmap/-/bigmap-1.3.1.tgz", + "integrity": "sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "hashery": "^1.4.0", + "hookified": "^1.15.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "keyv": "^5.6.0" + } + }, + "node_modules/@keyv/serialize": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz", + "integrity": "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.19.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.19.2.tgz", + "integrity": "sha512-93d49wCWJkCyN87MxLpNN6KXPCwFw7bVUg79mGUV1OS40JGWwVCr8n61DXCSqz7CV6/VfPkb7xec7uP2M+QHWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": ">=7.24.0 <7.24.7" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.72.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.72.0.tgz", + "integrity": "sha512-aysKgysPX9XOv/dk95UkvUDe++Q3n3dJPG9CEWT3RUccxfkPJ+Qn6nhKPDhZXU6Ofo9kgwGWwKyQCnZIXTgngg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.72.0", + "@typescript-eslint/type-utils": "8.72.0", + "@typescript-eslint/utils": "8.72.0", + "@typescript-eslint/visitor-keys": "8.72.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.72.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.13.tgz", + "integrity": "sha512-2E8Q6rh+1RRSq/YdaGsNfXsEZy9FVqv6BSSrkmZJP7BnaoPnRfKL+YaofPvP+5rzdk144qSm6i4+hvPgUcbV4Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.72.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.72.0.tgz", + "integrity": "sha512-0O+x3ynciD6zuXb1CKSu165haQV6ey53DKhHHUnujP7U+XlltJG+/keT0Xhd1HXCze+IHf7Tvt39ovyepX/LNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.72.0", + "@typescript-eslint/types": "8.72.0", + "@typescript-eslint/typescript-estree": "8.72.0", + "@typescript-eslint/visitor-keys": "8.72.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.72.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.72.0.tgz", + "integrity": "sha512-bzHngBbmIgzj6TsTjVUCntGx6FFLgE7mPjpO0PzHycOaZHXqS43XnrkFIHRS16LLr/xO6FHSXG3GJFo/F4bSBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.72.0", + "@typescript-eslint/types": "^8.72.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.72.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.72.0.tgz", + "integrity": "sha512-JewPyJOYcXBSAS7PIgufscHOwZqhTGyBRiQJhhuDln66tMchxvvt7nJ90JKKIoOelmdHA3kAQbm9cEUHc4JSEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.72.0", + "@typescript-eslint/visitor-keys": "8.72.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.72.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.72.0.tgz", + "integrity": "sha512-0vbxCf7xLVPSjcOdm6eQbgViH8EDJ8xnl1laLT9yoqSz2YmcSfSg5Vdl0JUvZf6rcuJ0ZivJIuMD4edTg8obaA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.72.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.72.0.tgz", + "integrity": "sha512-gEXxmmbSTSOVTC37IYYvremOM31v+UN2W772bJxmVYPhAOPSUxtacyb7wkm1EhkYKU6KzFJsdU44DCV4to0LWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.72.0", + "@typescript-eslint/typescript-estree": "8.72.0", + "@typescript-eslint/utils": "8.72.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.72.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.72.0.tgz", + "integrity": "sha512-Adfoy59kxNEjT5E5fnyrp7GpHYuWs8ONbdohu4ez6IU46qDq8LltG90tLmkucZB2uhYL8mqBjtXqGd8yhfjXqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.72.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.72.0.tgz", + "integrity": "sha512-gwLfFgWcw15tN2KXDCJvxEOQjPn+P3j/n3eSqkZ5sX4VpW9Cosodr+tWwBZRvJT3TcKx6HnPRGZMCNAiP1Xhug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.72.0", + "@typescript-eslint/tsconfig-utils": "8.72.0", + "@typescript-eslint/types": "8.72.0", + "@typescript-eslint/visitor-keys": "8.72.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.72.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.72.0.tgz", + "integrity": "sha512-0up3oArJZBYnzOaEXKo8DmI43QvMZyPoFEt9LWzRJcyl/RcJjfKKPebknN1VbTgXBf3M/KggUh5uUMdURLnseg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.72.0", + "@typescript-eslint/types": "8.72.0", + "@typescript-eslint/typescript-estree": "8.72.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.72.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.72.0.tgz", + "integrity": "sha512-E2mroviasaWecDzTGiGMtuEguuLVOeRggnrIn93CJuxYJzQBHbyymlT08aVQydbBal5538o+JkBKXh2sRuCyIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.72.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/acorn": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.19.0.tgz", + "integrity": "sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/cacheable": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-2.5.0.tgz", + "integrity": "sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cacheable/memory": "^2.2.0", + "@cacheable/utils": "^2.5.0", + "hookified": "^1.15.0", + "keyv": "^5.6.0", + "qified": "^0.10.1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "10.13.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.13.0.tgz", + "integrity": "sha512-UohDjD4zSzEwhf58M3O7vc/ycc8O1I7dNlhtD0WxE8Px8EHNJKtUwzBqrzeGhisRuv2ag4sAYswszCJRgKXYUg==", + "dev": true, + "license": "MIT", + "workspaces": [ + "packages/*" + ], + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.7.0", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.3", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "11.1.5 || >11.1.6 <12", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-11.1.5.tgz", + "integrity": "sha512-+PFTHITI08JIGhnNpGNI8T8inUpgZfk3GNEqfT9R2zZV2iFXg3CvqzSl/uEhs7TSGujYRELEANyDvS8Fj7+S7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^6.1.23" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "6.1.23", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.23.tgz", + "integrity": "sha512-f++BY9pTk+983xK1FLzlLpmM0i0z+jHmx3QESGkURMXujQZz1k5wzwX6hjnQ8goaD0B+sYnDK1yZ6MTyZfUaqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cacheable": "^2.5.0", + "flatted": "^3.4.2", + "hookified": "^1.15.0" + } + }, + "node_modules/flatted": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/hashery": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/hashery/-/hashery-1.5.1.tgz", + "integrity": "sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "hookified": "^1.15.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/hookified": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.15.1.tgz", + "integrity": "sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", + "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@keyv/serialize": "^1.1.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qified": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/qified/-/qified-0.10.1.tgz", + "integrity": "sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hookified": "^2.1.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/qified/node_modules/hookified": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-2.2.0.tgz", + "integrity": "sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA==", + "dev": true, + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.72.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.72.0.tgz", + "integrity": "sha512-r26Fkc8As+mZvqp7SLsMmNFyYUup/6CjK7/eHBHmhOw6GH6n6YFxHVFrG0jN1c5CMpDzelLPxvgeCOzx7BjN+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.72.0", + "@typescript-eslint/parser": "8.72.0", + "@typescript-eslint/typescript-estree": "8.72.0", + "@typescript-eslint/utils": "8.72.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/undici-types": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "dev": true, + "license": "MIT" + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/tests/harness/package.json b/tests/harness/package.json new file mode 100644 index 0000000..ae66f3f --- /dev/null +++ b/tests/harness/package.json @@ -0,0 +1,12 @@ +{ + "name": "pm-rust-crash-harness-tools", + "private": true, + "version": "1.0.0", + "type": "module", + "devDependencies": { + "@types/node": "24.19.2", + "eslint": "10.13.0", + "typescript": "6.0.3", + "typescript-eslint": "8.72.0" + } +} diff --git a/tests/harness/tsconfig.json b/tests/harness/tsconfig.json new file mode 100644 index 0000000..3120212 --- /dev/null +++ b/tests/harness/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "noEmit": true, + "strict": true, + "erasableSyntaxOnly": true, + "allowImportingTsExtensions": true, + "module": "nodenext", + "target": "es2023", + "types": ["node"], + "skipLibCheck": true + }, + "include": ["../../scripts/crash-*.mts"] +} diff --git a/tests/parity_1021_contract.rs b/tests/parity_1021_contract.rs index c323202..f14f112 100644 --- a/tests/parity_1021_contract.rs +++ b/tests/parity_1021_contract.rs @@ -172,6 +172,65 @@ fn dead_same_host_owner_is_recovered_before_ttl_with_audit() -> TestResult { Ok(()) } +#[test] +/// Fresh empty and occupied gates preserve the oracle's complete refusal receipt and bytes. +fn fresh_gate_refusals_match_oracle_and_preserve_authoritative_bytes() -> TestResult { + let Some(directory) = tracker()? else { + return Ok(()); + }; + let published = published_cli::locate_published_cli().ok_or("oracle")?; + let root = directory.path().join(".agents/pm"); + let locks = root.join("locks"); + fs::create_dir_all(&locks)?; + let lock = locks.join("sample-probe.lock"); + let gate = locks.join("sample-probe.lock.stale-cleanup"); + fs::create_dir(&gate)?; + let receipt = serde_json::to_vec(&json!({ + "id":"sample-probe", "pid":std::process::id(), "owner":"test-holder", + "token":"test-held", "ttl_seconds":1800, + "created_at":time::OffsetDateTime::now_utc().format(&time::format_description::well_known::Rfc3339)? + }))?; + fs::write(&lock, &receipt)?; + let item = root.join("tasks/sample-probe.toon"); + let history = root.join("history/sample-probe.jsonl"); + let before = (fs::read(&item)?, fs::read(&history)?); + let args = ["claim", "sample-probe", "--author", "test-agent", "--json"]; + for contents in [None, Some("foreign owner evidence")] { + if let Some(contents) = contents { + fs::write(gate.join("owner.json"), contents)?; + } + #[cfg(unix)] + fs::File::open(&gate)?.set_times( + fs::FileTimes::new() + .set_modified(std::time::SystemTime::now() + std::time::Duration::from_secs(60)), + )?; + let expected = oracle_command() + .arg(&published.entry) + .current_dir(directory.path()) + .env("PM_LOCK_WAIT_MS", "0") + .args(args) + .output()?; + let actual = native_command(directory.path()) + .env("PM_LOCK_WAIT_MS", "0") + .args(args) + .output()?; + assert_eq!(expected.status.code(), Some(4)); + assert_eq!(actual.status.code(), expected.status.code()); + let expected: Value = serde_json::from_slice(&expected.stderr)?; + let actual: Value = serde_json::from_slice(&actual.stderr)?; + assert_eq!(actual, expected); + assert_eq!(actual["code"], "lock_conflict"); + assert_eq!(fs::read(&lock)?, receipt); + assert_eq!((fs::read(&item)?, fs::read(&history)?), before); + assert!(gate.is_dir()); + } + assert_eq!( + fs::read_to_string(gate.join("owner.json"))?, + "foreign owner evidence" + ); + Ok(()) +} + #[test] /// Retry advice retains the operation and workspace without introducing a force override. fn lock_contention_preserves_the_actual_invocation_without_force_advice() -> TestResult { diff --git a/tests/support/mutation_unit.rs b/tests/support/mutation_unit.rs index d18b920..c90fc91 100644 --- a/tests/support/mutation_unit.rs +++ b/tests/support/mutation_unit.rs @@ -517,6 +517,108 @@ fn initialization_gate_protects_a_lock_before_its_payload_is_published() Ok(()) } +/// Fresh gates need a verified dead owner; live, unknown and nonempty gates stay intact. +#[cfg(target_os = "linux")] +#[test] +fn fresh_initialization_gate_requires_confirmed_death_and_an_empty_unchanged_gate() +-> Result<(), Box> { + let (_directory, pm_root) = root("{}")?; + let guard = acquire_lock( + &pm_root, + "sample-crash", + "original", + &locks(1_800), + false, + TS, + 0, + )?; + let path = pm_root.join("locks/sample-crash.lock"); + let mut owner: Value = serde_json::from_str(&fs::read_to_string(&path)?)?; + drop(guard); + let gate = pm_root.join("locks/sample-crash.lock.stale-cleanup"); + fs::create_dir(&gate)?; + for raw in [ + "{".to_owned(), + "{}".to_owned(), + serde_json::to_string(&owner)?, + ] { + fs::write(&path, &raw)?; + assert!(matches!( + acquire_lock_attempt( + &pm_root, + "sample-crash", + "successor", + 1_800, + false, + TS, + false + ), + Err(PmRustError::LockConflict { .. }) + )); + assert_eq!(fs::read_to_string(&path)?, raw); + assert!(gate.is_dir()); + } + owner["process_start_identity"] = serde_json::json!("0"); + let raw = serde_json::to_string(&owner)?; + fs::write(&path, &raw)?; + let evidence = gate.join("owner.json"); + fs::write(&evidence, "foreign owner evidence")?; + assert!(matches!( + acquire_lock_attempt( + &pm_root, + "sample-crash", + "successor", + 1_800, + false, + TS, + false + ), + Err(PmRustError::LockConflict { .. }) + )); + assert_eq!(fs::read_to_string(&path)?, raw); + assert_eq!(fs::read_to_string(&evidence)?, "foreign owner evidence"); + fs::remove_file(&evidence)?; + let lease = File::options() + .read(true) + .write(true) + .open(gate.with_extension("gate-lease"))?; + lease.try_lock()?; + assert!(matches!( + acquire_lock_attempt( + &pm_root, + "sample-crash", + "successor", + 1_800, + false, + TS, + false + ), + Err(PmRustError::LockConflict { .. }) + )); + assert_eq!(fs::read_to_string(&path)?, raw); + drop(GateLease(lease)); + let guard = acquire_lock_attempt( + &pm_root, + "sample-crash", + "successor", + 1_800, + false, + TS, + false, + )?; + assert!(!gate.exists()); + let current: Value = serde_json::from_str(&fs::read_to_string(&path)?)?; + assert_eq!(current["owner"], "successor"); + let receipts = + fs::read_dir(pm_root.join("locks/recoveries"))?.collect::, _>>()?; + assert_eq!(receipts.len(), 1); + let receipt: Value = serde_json::from_slice(&fs::read(receipts[0].path())?)?; + assert_eq!(receipt["event"], "owner_dead_recovery_attempt"); + assert_eq!(receipt["lock"], owner); + drop(guard); + Ok(()) +} + /// A paused stale observer cannot reclaim the gate installed by another writer. #[test] fn stale_gate_reclamation_preserves_a_concurrent_replacement()