From 7c52bf33e84650c16ed09174a13a17d7c320ab0b Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Thu, 22 Oct 2026 00:22:50 +0200 Subject: [PATCH 01/10] feat: compare native and published crash recovery with replayable evidence --- .../pm/features/pm-rust-crash-harness.toon | 28 + .../pm/history/pm-rust-crash-harness.jsonl | 16 + .../history/pm-rust-crash-lock-restart.jsonl | 4 + .../pm/tasks/pm-rust-crash-lock-restart.toon | 17 + .github/workflows/ci.yml | 5 + .gitignore | 2 + README.md | 32 + docs/CRASH_HARNESS.md | 88 ++ docs/CRASH_PUBLIC_COPY_2026_10_22.json | 392 +++++ docs/CRASH_REVERT_PROOF_2026_10_22.json | 29 + justfile | 13 + scripts/crash-cli.mts | 41 + scripts/crash-clock.mts | 11 + scripts/crash-harness.mts | 336 +++++ scripts/crash-harness.test.mts | 191 +++ scripts/crash-process.mts | 55 + scripts/crash-storage.mts | 62 + tests/harness/eslint.config.mjs | 6 + tests/harness/package-lock.json | 1333 +++++++++++++++++ tests/harness/package.json | 12 + tests/harness/tsconfig.json | 13 + 21 files changed, 2686 insertions(+) create mode 100644 .agents/pm/features/pm-rust-crash-harness.toon create mode 100644 .agents/pm/history/pm-rust-crash-harness.jsonl create mode 100644 .agents/pm/history/pm-rust-crash-lock-restart.jsonl create mode 100644 .agents/pm/tasks/pm-rust-crash-lock-restart.toon create mode 100644 docs/CRASH_HARNESS.md create mode 100644 docs/CRASH_PUBLIC_COPY_2026_10_22.json create mode 100644 docs/CRASH_REVERT_PROOF_2026_10_22.json create mode 100644 scripts/crash-cli.mts create mode 100644 scripts/crash-clock.mts create mode 100644 scripts/crash-harness.mts create mode 100644 scripts/crash-harness.test.mts create mode 100644 scripts/crash-process.mts create mode 100644 scripts/crash-storage.mts create mode 100644 tests/harness/eslint.config.mjs create mode 100644 tests/harness/package-lock.json create mode 100644 tests/harness/package.json create mode 100644 tests/harness/tsconfig.json 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..bb6ba4d --- /dev/null +++ b/.agents/pm/features/pm-rust-crash-harness.toon @@ -0,0 +1,28 @@ +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-21T22:21:47.994Z" +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[4]{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." +files[4]{path,scope,note}: + scripts/crash-cli.mts,project,Compact TOON and JSON command + scripts/crash-harness.mts,project,Typed reusable crash corpus SDK + scripts/crash-process.mts,project,Real bounded syscall kill adapters + scripts/crash-storage.mts,project,Byte inventories and immutable history checks +tests[1]{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 +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-crash-harness.jsonl b/.agents/pm/history/pm-rust-crash-harness.jsonl new file mode 100644 index 0000000..faef4ab --- /dev/null +++ b/.agents/pm/history/pm-rust-crash-harness.jsonl @@ -0,0 +1,16 @@ +{"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"} 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..c713f84 --- /dev/null +++ b/.agents/pm/history/pm-rust-crash-lock-restart.jsonl @@ -0,0 +1,4 @@ +{"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"} 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..28ef81b --- /dev/null +++ b/.agents/pm/tasks/pm-rust-crash-lock-restart.toon @@ -0,0 +1,17 @@ +id: pm-rust-crash-lock-restart +title: Recover incomplete native lock initialization after process death +description: "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." +type: Task +status: open +priority: 2 +tags[3]: crash-consistency,follow-up,locks +created_at: "2026-10-21T22:16:11.158Z" +updated_at: "2026-10-21T22:19:53.971Z" +author: codex-agent +comments[1]{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." +files[1]{path,scope,note}: + scripts/crash-harness.test.mts,project,Real incomplete-lock crash and replay reproduction +docs[1]{path,scope,note}: + docs/CRASH_HARNESS.md,project,Replay protocol and safe history retention +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 be40de8..bb239d6 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,5 @@ cov-no-branch.json # Locked published parity oracle installation /tests/oracle/node_modules/ +/tests/harness/node_modules/ +/.crash-failures/ diff --git a/README.md b/README.md index 8dd0fb0..fdc7872 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,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..cae74f1 --- /dev/null +++ b/docs/CRASH_HARNESS.md @@ -0,0 +1,88 @@ +# 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. + +```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; this executes only the +// whitelisted scenario/implementation/syscall/ordinal, never stored commands. +// 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 +separately in `pm-rust-crash-lock-restart`; complete runtime crash conformance +remains unproven. Four [behavioral negative controls](CRASH_REVERT_PROOF_2026_10_22.json) +fail their regression with the production invariant disabled and pass restored. + +Exact-head CI evidence is recorded in the PR. +Full-gate success tests the harness implementation and existing package contracts; +it does not turn a failed crash conformance report into runtime approval. 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_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 1aa5220..b23d053 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 + # Run the locked oracle through its shared test-environment launcher. [positional-arguments] oracle *args: @@ -120,6 +132,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..52352e8 --- /dev/null +++ b/scripts/crash-harness.mts @@ -0,0 +1,336 @@ +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))); + const metadata: unknown = JSON.parse(readFileSync(join(oracle, 'package.json'), 'utf8')); + 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 only a retained failure's whitelisted selection on its minimal synthetic fixture. */ +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); + const settings: unknown = JSON.parse(readFileSync(join(tracker, 'settings.json'), 'utf8')); + 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']; + // Re-enter the same operation family: native journals are operation-specific. + 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. + const decoded: unknown = JSON.parse(receipt.stdout.toString()); + 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..bd01c02 --- /dev/null +++ b/scripts/crash-harness.test.mts @@ -0,0 +1,191 @@ +import assert from 'node:assert/strict'; +import { spawnSync } from 'node:child_process'; +import { 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('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('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 preserves acknowledged history and independently replays retained failures 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.ok(report.cases.some(row => row.ok)); + assert.ok(report.parity.every(row => row.bytes && row.acknowledgement)); + const failed = report.cases.filter(row => !row.ok); + assert.ok(failed.every(row => row.implementation === 'native' && row.syscall === 'fsync' && row.boundary === 1 && row.restartStatus === 4), 'Only independently reproduced incomplete-lock restart refusals are expected'); + assert.equal(report.failuresRetained, failed.length); + if (failed.length > 0) { + assert.equal(report.ok, false); + const caseName = readdirSync(options.artifacts)[0]; + const evidence = join(options.artifacts, caseName, 'case.json'); + const before = readFileSync(evidence); + const replay = replayCrashFailure({ ...options, artifacts: join(root, 'replay') }, evidence); + assert.equal(replay.killed, 1); + assert.equal(replay.ok, false); + assert.equal(replay.cases[0].historyPreserved, true); + assert.equal(replay.cases[0].durableSha256, JSON.parse(before.toString()).row.durableSha256); + assert.deepEqual(readFileSync(evidence), before); + } + assert.equal(existsSync(join(root, '.git')), false); + } finally { process.env.PATH = savedPath; } + })); 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/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"] +} From dd75cf56893a6a7e5771faf1bd5d6c6a56791bab Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Thu, 22 Oct 2026 01:30:18 +0200 Subject: [PATCH 02/10] fix: recover native first-fsync lock crashes before TTL expiry --- .../pm-rust-comment-lock-receipt.jsonl | 1 + .../history/pm-rust-crash-lock-restart.jsonl | 21 + .../tasks/pm-rust-comment-lock-receipt.toon | 11 + .../pm/tasks/pm-rust-crash-lock-restart.toon | 34 +- docs/CRASH_HARNESS.md | 49 ++- docs/CRASH_RESTART_RECOVERY_2026_10_22.json | 377 ++++++++++++++++++ ...CRASH_RESTART_REVERT_PROOF_2026_10_22.json | 21 + scripts/crash-harness.test.mts | 45 ++- src/mutation.rs | 10 + tests/concurrency_contract.rs | 7 +- tests/fixtures/crash-restart/comment.json | 4 + .../fixture/.agents/pm/settings.json | 80 ++++ tests/fixtures/crash-restart/update.json | 4 + tests/parity_1021_contract.rs | 59 +++ tests/support/mutation_unit.rs | 102 +++++ 15 files changed, 796 insertions(+), 29 deletions(-) create mode 100644 .agents/pm/history/pm-rust-comment-lock-receipt.jsonl create mode 100644 .agents/pm/tasks/pm-rust-comment-lock-receipt.toon create mode 100644 docs/CRASH_RESTART_RECOVERY_2026_10_22.json create mode 100644 docs/CRASH_RESTART_REVERT_PROOF_2026_10_22.json create mode 100644 tests/fixtures/crash-restart/comment.json create mode 100644 tests/fixtures/crash-restart/fixture/.agents/pm/settings.json create mode 100644 tests/fixtures/crash-restart/update.json 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-lock-restart.jsonl b/.agents/pm/history/pm-rust-crash-lock-restart.jsonl index c713f84..d94f904 100644 --- a/.agents/pm/history/pm-rust-crash-lock-restart.jsonl +++ b/.agents/pm/history/pm-rust-crash-lock-restart.jsonl @@ -2,3 +2,24 @@ {"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"} 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 index 28ef81b..f9eea4a 100644 --- a/.agents/pm/tasks/pm-rust-crash-lock-restart.toon +++ b/.agents/pm/tasks/pm-rust-crash-lock-restart.toon @@ -1,17 +1,37 @@ id: pm-rust-crash-lock-restart title: Recover incomplete native lock initialization after process death -description: "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." +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: open +status: in_progress priority: 2 tags[3]: crash-consistency,follow-up,locks created_at: "2026-10-21T22:16:11.158Z" -updated_at: "2026-10-21T22:19:53.971Z" +updated_at: "2026-10-21T23:29:45.183Z" +assignee: codex-agent +claim_principal: codex-agent author: codex-agent -comments[1]{created_at,author,text}: +comments[6]{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." -files[1]{path,scope,note}: + "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. +files[8]{path,scope,note}: scripts/crash-harness.test.mts,project,Real incomplete-lock crash and replay reproduction -docs[1]{path,scope,note}: - docs/CRASH_HARNESS.md,project,Replay protocol and safe history retention + src/mutation.rs,project,Lease-protected early empty-gate recovery for confirmed dead owners + tests/concurrency_contract.rs,project,Fifty writers retain all 150 comments and history chain + tests/fixtures/crash-restart/comment.json,project,Permanent first-fsync comment reproduction + tests/fixtures/crash-restart/fixture/.agents/pm/settings.json,project,Minimal synthetic fixture shared by permanent reproductions + tests/fixtures/crash-restart/update.json,project,Permanent first-fsync update reproduction + tests/parity_1021_contract.rs,project,Exact oracle refusal receipts and authoritative byte preservation + tests/support/mutation_unit.rs,project,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[3]{path,scope,note}: + docs/CRASH_HARNESS.md,project,Early gate recovery policy independent replay commands and fifty-writer acceptance + docs/CRASH_RESTART_RECOVERY_2026_10_22.json,project,Twenty measured SIGKILL recoveries preserve exact state history prefixes and raw restart receipt hashes + docs/CRASH_RESTART_REVERT_PROOF_2026_10_22.json,project,Only acquire_lock_attempt reverted and rebuilt yields two restart-exit-4 failures; restored and rebuilt yields two passes body: "" diff --git a/docs/CRASH_HARNESS.md b/docs/CRASH_HARNESS.md index cae74f1..a244d8c 100644 --- a/docs/CRASH_HARNESS.md +++ b/docs/CRASH_HARNESS.md @@ -79,10 +79,53 @@ 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 -separately in `pm-rust-crash-lock-restart`; complete runtime crash conformance -remains unproven. Four [behavioral negative controls](CRASH_REVERT_PROOF_2026_10_22.json) +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 CI evidence is recorded in the PR. Full-gate success tests the harness implementation and existing package contracts; -it does not turn a failed crash conformance report into runtime approval. +it does not establish power-loss durability or coverage of unmeasured syscalls. 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/scripts/crash-harness.test.mts b/scripts/crash-harness.test.mts index bd01c02..9ac55c8 100644 --- a/scripts/crash-harness.test.mts +++ b/scripts/crash-harness.test.mts @@ -153,7 +153,7 @@ test('live syscall injection kills a real writing child and restart preserves ea assert.equal(existsSync(join(root, '.git')), false); })); -test('real pm corpus preserves acknowledged history and independently replays retained failures without git', +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); @@ -169,23 +169,34 @@ test('real pm corpus preserves acknowledged history and independently replays re 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.ok(report.cases.some(row => row.ok)); + 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)); - const failed = report.cases.filter(row => !row.ok); - assert.ok(failed.every(row => row.implementation === 'native' && row.syscall === 'fsync' && row.boundary === 1 && row.restartStatus === 4), 'Only independently reproduced incomplete-lock restart refusals are expected'); - assert.equal(report.failuresRetained, failed.length); - if (failed.length > 0) { - assert.equal(report.ok, false); - const caseName = readdirSync(options.artifacts)[0]; - const evidence = join(options.artifacts, caseName, 'case.json'); - const before = readFileSync(evidence); - const replay = replayCrashFailure({ ...options, artifacts: join(root, 'replay') }, evidence); - assert.equal(replay.killed, 1); - assert.equal(replay.ok, false); - assert.equal(replay.cases[0].historyPreserved, true); - assert.equal(replay.cases[0].durableSha256, JSON.parse(before.toString()).row.durableSha256); - assert.deepEqual(readFileSync(evidence), before); - } + 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/src/mutation.rs b/src/mutation.rs index f103a70..2470fd4 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 9ec57f3..32961ae 100644 --- a/tests/concurrency_contract.rs +++ b/tests/concurrency_contract.rs @@ -10,7 +10,7 @@ use std::fs; use std::path::Path; use std::process::Command; -use std::sync::Arc; +use std::sync::{Arc, Barrier}; use std::thread; use serde_json::Value; @@ -20,7 +20,7 @@ mod process_environment; #[allow(dead_code)] mod published_cli; -const PROCESSES: usize = 8; +const PROCESSES: usize = 50; const MUTATIONS_PER_PROCESS: usize = 3; /// Builds the fixture workspace and seeds one shared item for contention. @@ -125,11 +125,14 @@ fn concurrent_comment_processes_preserve_every_accepted_mutation() return Ok(()); }; let workspace = Arc::new(directory.path().to_path_buf()); + let start = Arc::new(Barrier::new(PROCESSES)); let mut handles = Vec::new(); for process in 0..PROCESSES { let workspace = Arc::clone(&workspace); + let start = Arc::clone(&start); handles.push(thread::spawn(move || { + start.wait(); let mut accepted = 0; let mut rejections = Vec::new(); for mutation in 0..MUTATIONS_PER_PROCESS { 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/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 41d9217..08755d8 100644 --- a/tests/support/mutation_unit.rs +++ b/tests/support/mutation_unit.rs @@ -459,6 +459,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() From 1dea72ce14fc1d0522918509a21ae5f6c53148ce Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Thu, 22 Oct 2026 01:35:41 +0200 Subject: [PATCH 03/10] docs: hand off crash recovery verification with retained evidence --- .agents/pm/history/pm-rust-crash-lock-restart.jsonl | 2 ++ .agents/pm/tasks/pm-rust-crash-lock-restart.toon | 7 +++---- docs/CRASH_HARNESS.md | 3 ++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.agents/pm/history/pm-rust-crash-lock-restart.jsonl b/.agents/pm/history/pm-rust-crash-lock-restart.jsonl index d94f904..f165e4e 100644 --- a/.agents/pm/history/pm-rust-crash-lock-restart.jsonl +++ b/.agents/pm/history/pm-rust-crash-lock-restart.jsonl @@ -23,3 +23,5 @@ {"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"} diff --git a/.agents/pm/tasks/pm-rust-crash-lock-restart.toon b/.agents/pm/tasks/pm-rust-crash-lock-restart.toon index f9eea4a..52a3bd7 100644 --- a/.agents/pm/tasks/pm-rust-crash-lock-restart.toon +++ b/.agents/pm/tasks/pm-rust-crash-lock-restart.toon @@ -6,17 +6,16 @@ status: in_progress priority: 2 tags[3]: crash-consistency,follow-up,locks created_at: "2026-10-21T22:16:11.158Z" -updated_at: "2026-10-21T23:29:45.183Z" -assignee: codex-agent -claim_principal: codex-agent +updated_at: "2026-10-21T23:35:15.935Z" author: codex-agent -comments[6]{created_at,author,text}: +comments[7]{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." files[8]{path,scope,note}: scripts/crash-harness.test.mts,project,Real incomplete-lock crash and replay reproduction src/mutation.rs,project,Lease-protected early empty-gate recovery for confirmed dead owners diff --git a/docs/CRASH_HARNESS.md b/docs/CRASH_HARNESS.md index a244d8c..bb11421 100644 --- a/docs/CRASH_HARNESS.md +++ b/docs/CRASH_HARNESS.md @@ -126,6 +126,7 @@ the refusal surface label differ. This corpus uses each runtime's own raw restar reference and the declared shared acknowledgement projection; it does not certify every CLI diagnostic surface. -Exact-head CI evidence is recorded in the PR. +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. From 5b84803625d18e3ab3b7fdb4454a49448a628bf6 Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Thu, 22 Oct 2026 12:57:19 +0200 Subject: [PATCH 04/10] fix(crash): preserve typed refusals for invalid JSON --- .../pm/features/pm-rust-crash-harness.toon | 34 ++++++-- .../pm/history/pm-rust-crash-harness.jsonl | 8 ++ docs/CRASH_HARNESS.md | 6 ++ scripts/crash-harness.mts | 9 ++- scripts/crash-harness.test.mts | 78 ++++++++++++++++++- 5 files changed, 123 insertions(+), 12 deletions(-) diff --git a/.agents/pm/features/pm-rust-crash-harness.toon b/.agents/pm/features/pm-rust-crash-harness.toon index bb6ba4d..da94b85 100644 --- a/.agents/pm/features/pm-rust-crash-harness.toon +++ b/.agents/pm/features/pm-rust-crash-harness.toon @@ -6,21 +6,39 @@ status: in_progress priority: 2 tags[3]: conformance,crash-consistency,harness created_at: "2026-10-21T21:59:35.474Z" -updated_at: "2026-10-21T22:21:47.994Z" +updated_at: "2026-10-22T10:56:48.199Z" +assignee: codex-agent +claim_principal: codex-agent 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[4]{created_at,author,text}: +comments[9]{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." -files[4]{path,scope,note}: - scripts/crash-cli.mts,project,Compact TOON and JSON command - scripts/crash-harness.mts,project,Typed reusable crash corpus SDK - scripts/crash-process.mts,project,Real bounded syscall kill adapters - scripts/crash-storage.mts,project,Byte inventories and immutable history checks -tests[1]{command,scope,provenance{author,created_at,source_kind,source_ref}}: + "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." +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[2]{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 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 diff --git a/.agents/pm/history/pm-rust-crash-harness.jsonl b/.agents/pm/history/pm-rust-crash-harness.jsonl index faef4ab..c10e766 100644 --- a/.agents/pm/history/pm-rust-crash-harness.jsonl +++ b/.agents/pm/history/pm-rust-crash-harness.jsonl @@ -14,3 +14,11 @@ {"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"} diff --git a/docs/CRASH_HARNESS.md b/docs/CRASH_HARNESS.md index cae74f1..f91327a 100644 --- a/docs/CRASH_HARNESS.md +++ b/docs/CRASH_HARNESS.md @@ -8,6 +8,12 @@ also runs these checks after its Rust tests. Rust's four 100% coverage threshold 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'; diff --git a/scripts/crash-harness.mts b/scripts/crash-harness.mts index 52352e8..069d49b 100644 --- a/scripts/crash-harness.mts +++ b/scripts/crash-harness.mts @@ -86,7 +86,8 @@ function receiptHash(receipt: ProcessReceipt): string { /** 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))); - const metadata: unknown = JSON.parse(readFileSync(join(oracle, 'package.json'), 'utf8')); + 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.'); } @@ -190,7 +191,8 @@ function run(options: CrashOptions, root: string, timeout: number, maximum: numb } const tracker = join(base, '.agents/pm'); const original = snapshot(tracker); - const settings: unknown = JSON.parse(readFileSync(join(tracker, 'settings.json'), 'utf8')); + 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`; @@ -228,7 +230,8 @@ function run(options: CrashOptions, root: string, timeout: number, maximum: numb 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. - const decoded: unknown = JSON.parse(receipt.stdout.toString()); + 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))) { diff --git a/scripts/crash-harness.test.mts b/scripts/crash-harness.test.mts index bd01c02..3d345b6 100644 --- a/scripts/crash-harness.test.mts +++ b/scripts/crash-harness.test.mts @@ -1,6 +1,6 @@ import assert from 'node:assert/strict'; import { spawnSync } from 'node:child_process'; -import { existsSync, mkdtempSync, mkdirSync, readdirSync, readFileSync, rmSync, symlinkSync, writeFileSync } from 'node:fs'; +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'; @@ -92,6 +92,82 @@ test('invalid SDK options refuse before launching or reading any tracker', () => } }); +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' }); From 328ff009483d754b3e25412580495378025d5d87 Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Thu, 22 Oct 2026 13:24:57 +0200 Subject: [PATCH 05/10] docs(pm): record crash harness review validation --- .agents/pm/features/pm-rust-crash-harness.toon | 7 +++---- .agents/pm/history/pm-rust-crash-harness.jsonl | 2 ++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.agents/pm/features/pm-rust-crash-harness.toon b/.agents/pm/features/pm-rust-crash-harness.toon index da94b85..4ec4ad1 100644 --- a/.agents/pm/features/pm-rust-crash-harness.toon +++ b/.agents/pm/features/pm-rust-crash-harness.toon @@ -6,12 +6,10 @@ status: in_progress priority: 2 tags[3]: conformance,crash-consistency,harness created_at: "2026-10-21T21:59:35.474Z" -updated_at: "2026-10-22T10:56:48.199Z" -assignee: codex-agent -claim_principal: codex-agent +updated_at: "2026-10-22T11:23:55.320Z" 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[9]{created_at,author,text}: +comments[10]{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." @@ -21,6 +19,7 @@ comments[9]{created_at,author,text}: "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." files[5]: - path: scripts/crash-cli.mts scope: project diff --git a/.agents/pm/history/pm-rust-crash-harness.jsonl b/.agents/pm/history/pm-rust-crash-harness.jsonl index c10e766..f571903 100644 --- a/.agents/pm/history/pm-rust-crash-harness.jsonl +++ b/.agents/pm/history/pm-rust-crash-harness.jsonl @@ -22,3 +22,5 @@ {"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"} From 46178e2dae6468d1f71e18a23c9e608e19a48a56 Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Thu, 22 Oct 2026 13:42:58 +0200 Subject: [PATCH 06/10] docs(pm): record stacked crash recovery integration checks --- .agents/pm/history/pm-rust-crash-lock-restart.jsonl | 3 +++ .agents/pm/tasks/pm-rust-crash-lock-restart.toon | 8 ++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.agents/pm/history/pm-rust-crash-lock-restart.jsonl b/.agents/pm/history/pm-rust-crash-lock-restart.jsonl index f165e4e..442ee8c 100644 --- a/.agents/pm/history/pm-rust-crash-lock-restart.jsonl +++ b/.agents/pm/history/pm-rust-crash-lock-restart.jsonl @@ -25,3 +25,6 @@ {"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"} diff --git a/.agents/pm/tasks/pm-rust-crash-lock-restart.toon b/.agents/pm/tasks/pm-rust-crash-lock-restart.toon index 52a3bd7..846692d 100644 --- a/.agents/pm/tasks/pm-rust-crash-lock-restart.toon +++ b/.agents/pm/tasks/pm-rust-crash-lock-restart.toon @@ -6,9 +6,11 @@ status: in_progress priority: 2 tags[3]: crash-consistency,follow-up,locks created_at: "2026-10-21T22:16:11.158Z" -updated_at: "2026-10-21T23:35:15.935Z" +updated_at: "2026-10-22T11:42:02.323Z" +assignee: codex-agent +claim_principal: codex-agent author: codex-agent -comments[7]{created_at,author,text}: +comments[9]{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." @@ -16,6 +18,8 @@ comments[7]{created_at,author,text}: "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." files[8]{path,scope,note}: scripts/crash-harness.test.mts,project,Real incomplete-lock crash and replay reproduction src/mutation.rs,project,Lease-protected early empty-gate recovery for confirmed dead owners From 53e4988177a2298f28f14e49406768484ec4cd0e Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Thu, 22 Oct 2026 14:13:03 +0200 Subject: [PATCH 07/10] docs(pm): record stacked recovery release gate --- .agents/pm/history/pm-rust-crash-lock-restart.jsonl | 2 ++ .agents/pm/tasks/pm-rust-crash-lock-restart.toon | 7 +++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.agents/pm/history/pm-rust-crash-lock-restart.jsonl b/.agents/pm/history/pm-rust-crash-lock-restart.jsonl index 442ee8c..5938b91 100644 --- a/.agents/pm/history/pm-rust-crash-lock-restart.jsonl +++ b/.agents/pm/history/pm-rust-crash-lock-restart.jsonl @@ -28,3 +28,5 @@ {"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"} diff --git a/.agents/pm/tasks/pm-rust-crash-lock-restart.toon b/.agents/pm/tasks/pm-rust-crash-lock-restart.toon index 846692d..0f2d16f 100644 --- a/.agents/pm/tasks/pm-rust-crash-lock-restart.toon +++ b/.agents/pm/tasks/pm-rust-crash-lock-restart.toon @@ -6,11 +6,9 @@ status: in_progress priority: 2 tags[3]: crash-consistency,follow-up,locks created_at: "2026-10-21T22:16:11.158Z" -updated_at: "2026-10-22T11:42:02.323Z" -assignee: codex-agent -claim_principal: codex-agent +updated_at: "2026-10-22T12:12:20.661Z" author: codex-agent -comments[9]{created_at,author,text}: +comments[10]{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." @@ -20,6 +18,7 @@ comments[9]{created_at,author,text}: "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." files[8]{path,scope,note}: scripts/crash-harness.test.mts,project,Real incomplete-lock crash and replay reproduction src/mutation.rs,project,Lease-protected early empty-gate recovery for confirmed dead owners From 84ddc56c334a5cafb9661c8c5c575fe1a2682e6a Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Thu, 22 Oct 2026 18:41:51 +0200 Subject: [PATCH 08/10] docs(crash): explain baseline parity replay scope --- .agents/pm/features/pm-rust-crash-harness.toon | 10 +++++++--- .agents/pm/history/pm-rust-crash-harness.jsonl | 6 ++++++ docs/CRASH_HARNESS.md | 6 ++++-- scripts/crash-harness.mts | 7 ++++++- scripts/crash-harness.test.mts | 18 ++++++++++++++++++ 5 files changed, 41 insertions(+), 6 deletions(-) diff --git a/.agents/pm/features/pm-rust-crash-harness.toon b/.agents/pm/features/pm-rust-crash-harness.toon index 4ec4ad1..4ab4861 100644 --- a/.agents/pm/features/pm-rust-crash-harness.toon +++ b/.agents/pm/features/pm-rust-crash-harness.toon @@ -6,10 +6,10 @@ status: in_progress priority: 2 tags[3]: conformance,crash-consistency,harness created_at: "2026-10-21T21:59:35.474Z" -updated_at: "2026-10-22T11:23:55.320Z" +updated_at: "2026-10-22T16:41:19.128Z" 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[10]{created_at,author,text}: +comments[13]{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." @@ -20,6 +20,9 @@ comments[10]{created_at,author,text}: "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." files[5]: - path: scripts/crash-cli.mts scope: project @@ -35,9 +38,10 @@ files[5]: - path: scripts/crash-storage.mts scope: project note: Byte inventories and immutable history checks -tests[2]{command,scope,provenance{author,created_at,source_kind,source_ref}}: +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 diff --git a/.agents/pm/history/pm-rust-crash-harness.jsonl b/.agents/pm/history/pm-rust-crash-harness.jsonl index f571903..2834183 100644 --- a/.agents/pm/history/pm-rust-crash-harness.jsonl +++ b/.agents/pm/history/pm-rust-crash-harness.jsonl @@ -24,3 +24,9 @@ {"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"} diff --git a/docs/CRASH_HARNESS.md b/docs/CRASH_HARNESS.md index f91327a..55a69be 100644 --- a/docs/CRASH_HARNESS.md +++ b/docs/CRASH_HARNESS.md @@ -27,8 +27,10 @@ const options: CrashOptions = { }; const receipt: CrashReport = runCrashCorpus(options); console.log(receipt.ok, receipt.killed, receipt.parity); -// After a failure, select its retained case.json; this executes only the -// whitelisted scenario/implementation/syscall/ordinal, never stored commands. +// 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'); ``` diff --git a/scripts/crash-harness.mts b/scripts/crash-harness.mts index 069d49b..47b318f 100644 --- a/scripts/crash-harness.mts +++ b/scripts/crash-harness.mts @@ -155,7 +155,12 @@ export function runCrashCorpus(options: CrashOptions): CrashReport { } } -/** Replay only a retained failure's whitelisted selection on its minimal synthetic fixture. */ +/** + * 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 { diff --git a/scripts/crash-harness.test.mts b/scripts/crash-harness.test.mts index 3d345b6..4ec8f5b 100644 --- a/scripts/crash-harness.test.mts +++ b/scripts/crash-harness.test.mts @@ -198,6 +198,24 @@ test('replay refuses unknown manifests without executing their stored commands', } })); +test('retained replay guidance distinguishes boundary cases from baseline parity', () => { + const guide = readFileSync(new URL('../docs/CRASH_HARNESS.md', import.meta.url), 'utf8'); + const example = /```typescript\n([\s\S]*?)```/.exec(guide)?.[1]; + assert.ok(example, 'the retained-case SDK example must remain documented'); + 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'); From f555a4238fc4d0fa5768feaee0e81dce66031202 Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Thu, 22 Oct 2026 18:47:50 +0200 Subject: [PATCH 09/10] test(crash): check replay guidance with both newline formats --- .agents/pm/features/pm-rust-crash-harness.toon | 5 +++-- .agents/pm/history/pm-rust-crash-harness.jsonl | 3 +++ scripts/crash-harness.test.mts | 14 ++++++++------ 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/.agents/pm/features/pm-rust-crash-harness.toon b/.agents/pm/features/pm-rust-crash-harness.toon index 4ab4861..1ea3ab0 100644 --- a/.agents/pm/features/pm-rust-crash-harness.toon +++ b/.agents/pm/features/pm-rust-crash-harness.toon @@ -6,10 +6,10 @@ status: in_progress priority: 2 tags[3]: conformance,crash-consistency,harness created_at: "2026-10-21T21:59:35.474Z" -updated_at: "2026-10-22T16:41:19.128Z" +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[13]{created_at,author,text}: +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." @@ -23,6 +23,7 @@ comments[13]{created_at,author,text}: "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 diff --git a/.agents/pm/history/pm-rust-crash-harness.jsonl b/.agents/pm/history/pm-rust-crash-harness.jsonl index 2834183..afbcefa 100644 --- a/.agents/pm/history/pm-rust-crash-harness.jsonl +++ b/.agents/pm/history/pm-rust-crash-harness.jsonl @@ -30,3 +30,6 @@ {"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/scripts/crash-harness.test.mts b/scripts/crash-harness.test.mts index 4ec8f5b..6a9de73 100644 --- a/scripts/crash-harness.test.mts +++ b/scripts/crash-harness.test.mts @@ -199,12 +199,14 @@ test('replay refuses unknown manifests without executing their stored commands', })); test('retained replay guidance distinguishes boundary cases from baseline parity', () => { - const guide = readFileSync(new URL('../docs/CRASH_HARNESS.md', import.meta.url), 'utf8'); - const example = /```typescript\n([\s\S]*?)```/.exec(guide)?.[1]; - assert.ok(example, 'the retained-case SDK example must remain documented'); - 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/); + 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', () => { From ca52b3608505542519781d6709799c163d9e01de Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Thu, 22 Oct 2026 19:24:17 +0200 Subject: [PATCH 10/10] docs(pm): record stacked replay guidance integration --- .../history/pm-rust-crash-lock-restart.jsonl | 6 +++ .../pm/tasks/pm-rust-crash-lock-restart.toon | 43 ++++++++++++++----- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/.agents/pm/history/pm-rust-crash-lock-restart.jsonl b/.agents/pm/history/pm-rust-crash-lock-restart.jsonl index 5938b91..23311a9 100644 --- a/.agents/pm/history/pm-rust-crash-lock-restart.jsonl +++ b/.agents/pm/history/pm-rust-crash-lock-restart.jsonl @@ -30,3 +30,9 @@ {"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"} diff --git a/.agents/pm/tasks/pm-rust-crash-lock-restart.toon b/.agents/pm/tasks/pm-rust-crash-lock-restart.toon index 0f2d16f..5fa3aeb 100644 --- a/.agents/pm/tasks/pm-rust-crash-lock-restart.toon +++ b/.agents/pm/tasks/pm-rust-crash-lock-restart.toon @@ -6,9 +6,9 @@ status: in_progress priority: 2 tags[3]: crash-consistency,follow-up,locks created_at: "2026-10-21T22:16:11.158Z" -updated_at: "2026-10-22T12:12:20.661Z" +updated_at: "2026-10-22T17:23:49.897Z" author: codex-agent -comments[10]{created_at,author,text}: +comments[13]{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." @@ -19,15 +19,36 @@ comments[10]{created_at,author,text}: "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." -files[8]{path,scope,note}: - scripts/crash-harness.test.mts,project,Real incomplete-lock crash and replay reproduction - src/mutation.rs,project,Lease-protected early empty-gate recovery for confirmed dead owners - tests/concurrency_contract.rs,project,Fifty writers retain all 150 comments and history chain - tests/fixtures/crash-restart/comment.json,project,Permanent first-fsync comment reproduction - tests/fixtures/crash-restart/fixture/.agents/pm/settings.json,project,Minimal synthetic fixture shared by permanent reproductions - tests/fixtures/crash-restart/update.json,project,Permanent first-fsync update reproduction - tests/parity_1021_contract.rs,project,Exact oracle refusal receipts and authoritative byte preservation - tests/support/mutation_unit.rs,project,Live unknown occupied and held-lease refusal controls + "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." +files[9]: + - 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