diff --git a/.agents/pm/chores/pm-graph-at6i.toon b/.agents/pm/chores/pm-graph-at6i.toon new file mode 100644 index 0000000..cd0c152 --- /dev/null +++ b/.agents/pm/chores/pm-graph-at6i.toon @@ -0,0 +1,28 @@ +id: pm-graph-at6i +title: Certify pm CLI 2026.9.23 and adopt the guarded pm-ops merge-driver launcher +description: "Fleet wave 2026-09-25 (companion epic pm-cli-website-5s6z, launcher rollout pm-cli-website-xy19). Pins the toolchain to 2026.9.23 and replaces the prepare hook with the launcher template pm-ops ships: it imports nothing from pm-ops, so a production install of a clone (npm ci --omit=dev) skips with one notice instead of failing, while a stale or broken pm-ops still fails the install. A test keeps the copy byte-identical to the pinned template, whose branches pm-ops covers with real fixtures." +type: Chore +status: closed +priority: 1 +tags[4]: certify,merge-driver,multi-agent,pm-cli-2026.9.23 +created_at: "2026-09-25T08:15:39.057Z" +updated_at: "2026-09-25T09:51:35.809Z" +closed_at: "2026-09-25T09:36:22.305Z" +completed_at: "2026-09-25T09:36:22.305Z" +author: fleet-wave-script +acceptance_criteria: "package.json and package-lock.json pin pm-cli 2026.9.23 and pm-ops 2026.9.23 (pm-changelog 2026.9.23 where used); scripts/prepare-merge-driver.ts is byte-identical to the pinned pm-ops template, enforced by a test; CI runs pm health --strict-exit --require-merge-drivers and release:check exits 0" +resolution: Pinned the toolchain to 2026.9.23 and adopted the guarded pm-ops launcher +expected_result: "release:check and the CI health gate pass on 2026.9.23 with the launcher byte-identical to the template" +actual_result: "release:check exits 0; the launcher test and the CI health block pass" +comments[1]{created_at,author,text}: + "2026-09-25T09:51:35.809Z",fleet-wave-script,"Review round 1 (Greptile, pm-graph#114): the launcher is back in coverageGate.sources where it was on main; npm run coverage reports it at 100/100/100 through the fixture suite's child processes." +files[5]{path,scope}: + package-lock.json,project + package.json,project + README.md,project + scripts/prepare-merge-driver.ts,project + test/prepare-merge-driver.test.ts,project +tests[1]{command,scope,provenance{author,created_at,source_kind,source_ref}}: + "npm run release:check",project,fleet-wave-script,"2026-09-25T08:15:57.266Z",local_mutation,certify-pm-cli-2026-9-23-and-roll-out-guarded-merge-driver-launcher +close_reason: "Pins: @unbrained/pm-cli 2026.9.21 -> 2026.9.23, pm-changelog 2026.9.18 -> 2026.9.23, pm-ops 2026.9.18 -> 2026.9.23 (package.json and package-lock.json). Launcher: pm-ops template copied unchanged; test/prepare-merge-driver.test.ts proves byte identity and exercises every launcher branch in isolated checkouts. The wave's first gate run was cut off by its 40-minute timeout on a saturated 4-core host; this run finished it. The CI health block runs green with --require-merge-drivers; release:check exits 0." +body: "" diff --git a/.agents/pm/history/pm-graph-1k7d.jsonl b/.agents/pm/history/pm-graph-1k7d.jsonl new file mode 100644 index 0000000..74160f9 --- /dev/null +++ b/.agents/pm/history/pm-graph-1k7d.jsonl @@ -0,0 +1,8 @@ +{"hash_algorithm":"sha256","ts":"2026-09-25T08:15:41.019Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"e0d15f3cda6eb998be619306","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.282","source":"probe"}},"op":"create","patch":[{"op":"add","path":"/metadata/id","value":"pm-graph-1k7d"},{"op":"add","path":"/metadata/title","value":"A publish that npm accepts late is reported as failed and the GitHub Release is skipped on bun mirror lag"},{"op":"add","path":"/metadata/description","value":"Companion item pm-cli-website-3y5d. pm-csv 2026.9.23 is the live case: npm accepted the publish with provenance and the tag was pushed, but bun still answered No version matching after 4 minutes, so the job failed before creating the GitHub Release. This repository's own release.yml gets a 10-minute npm visibility window with --prefer-online reads and an honest never-visible message, a ~10-minute bun window, and a GitHub Release that depends only on the publish and tag-push outcomes, with a gate step that fails the job visibly when bun verification failed."},{"op":"add","path":"/metadata/type","value":"Issue"},{"op":"add","path":"/metadata/status","value":"open"},{"op":"add","path":"/metadata/priority","value":1},{"op":"add","path":"/metadata/tags","value":["release","reliability"]},{"op":"add","path":"/metadata/created_at","value":"2026-09-25T08:15:41.019Z"},{"op":"add","path":"/metadata/updated_at","value":"2026-09-25T08:15:41.019Z"},{"op":"add","path":"/metadata/author","value":"fleet-wave-script"}],"before_hash":"3cc22dff72be7b14824654a7a64ea62b04799939b2fee54c1b5f52ca60bf6df0","after_hash":"500d4b84f4c976f275155fb84e19f7314745bffc48972a2ce289596f784ba3d2","item_hash_version":3,"message":"","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"e08838754acc1a9de68e6f032c8d0f0c92963a661a2967b2cc058bffa68d9139"} +{"hash_algorithm":"sha256","ts":"2026-09-25T08:15:43.752Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"e0d15f3cda6eb998be619306","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.282","source":"probe"}},"op":"claim","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-25T08:15:43.752Z"},{"op":"add","path":"/metadata/assignee","value":"fleet-wave-script"},{"op":"add","path":"/metadata/claim_principal","value":"fleet-wave-script"}],"before_hash":"500d4b84f4c976f275155fb84e19f7314745bffc48972a2ce289596f784ba3d2","after_hash":"daf44aef455eba6c04ca6d5f3b933ec3cf6edca85bd5df201365d84b85066865","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"27727bc5a83232ff6fc22c6f03d0e4168dabbfc80257d218448dfea08fc552a9"} +{"hash_algorithm":"sha256","ts":"2026-09-25T08:15:45.096Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"e0d15f3cda6eb998be619306","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.282","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-25T08:15:45.096Z"},{"op":"replace","path":"/metadata/status","value":"in_progress"}],"before_hash":"daf44aef455eba6c04ca6d5f3b933ec3cf6edca85bd5df201365d84b85066865","after_hash":"a51d7c4ef6e855d09b4f2179d391235afd89d67ba02555bd2290ef48b8ee818d","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"8010cd4c9f7910b8cb966df78e86e4a7ca3b5012bcd0babc697c550dac6fe427"} +{"hash_algorithm":"sha256","ts":"2026-09-25T08:15:47.211Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"e0d15f3cda6eb998be619306","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.282","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-25T08:15:47.211Z"},{"op":"add","path":"/metadata/files","value":[{"path":".github/workflows/release.yml","scope":"project"}]}],"before_hash":"a51d7c4ef6e855d09b4f2179d391235afd89d67ba02555bd2290ef48b8ee818d","after_hash":"27d661928a2140d87f5989666c2c9e5fbf8a2174bcc7d6c760e6db6270a4d345","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"dd1fe1970cb6f332a9caf61b8df7de7f162cb0dd2e09d25a507bcbe0b9826316"} +{"hash_algorithm":"sha256","ts":"2026-09-25T08:15:58.810Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"e0d15f3cda6eb998be619306","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.282","source":"probe"}},"op":"tests_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-25T08:15:58.810Z"},{"op":"add","path":"/metadata/tests","value":[{"command":"npm run release:check","scope":"project","provenance":{"author":"fleet-wave-script","created_at":"2026-09-25T08:15:58.727Z","source_kind":"local_mutation","source_ref":"certify-pm-cli-2026-9-23-and-roll-out-guarded-merge-driver-launcher"}}]}],"before_hash":"27d661928a2140d87f5989666c2c9e5fbf8a2174bcc7d6c760e6db6270a4d345","after_hash":"5df66129c8489a754383a49d80cbab3e7bac1fc111a280a0583d8dbbf07711af","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"55ecc06559f9a7d8e85f403d55180e449c3228079fc8a92b077936b23675a94d"} +{"hash_algorithm":"sha256","ts":"2026-09-25T09:36:22.863Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"e0d15f3cda6eb998be619306","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.282","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-25T09:36:22.863Z"},{"op":"add","path":"/metadata/repro_steps","value":"1. A Daily Release publishes to npm and pushes the tag. 2. bun still answers No version matching after 8 attempts (pm-csv 2026.9.23). 3. The job fails before Create GitHub release."}],"before_hash":"5df66129c8489a754383a49d80cbab3e7bac1fc111a280a0583d8dbbf07711af","after_hash":"a09c853835326f3eeea0679d1bc8cdeb05eca3f68437a7644ff17c9a69957e61","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"dd13e37bf510fc588f6fb9677b9fbf52c9ee0929b6b3ef66798ecbf6be140bdc"} +{"hash_algorithm":"sha256","ts":"2026-09-25T09:36:23.479Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"e0d15f3cda6eb998be619306","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.282","source":"probe"}},"op":"close","patch":[{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-25T09:36:23.479Z"},{"op":"replace","path":"/metadata/status","value":"closed"},{"op":"add","path":"/metadata/closed_at","value":"2026-09-25T09:36:23.464Z"},{"op":"add","path":"/metadata/completed_at","value":"2026-09-25T09:36:23.464Z"},{"op":"add","path":"/metadata/resolution","value":"Release-window fix applied"},{"op":"add","path":"/metadata/expected_result","value":"A late-visible publish is reconciled and bun lag no longer skips the Release"},{"op":"add","path":"/metadata/actual_result","value":"release:check exits 0"},{"op":"add","path":"/metadata/close_reason","value":"Applied to this repository's own release.yml by anchored replacements; release:check exits 0."}],"before_hash":"a09c853835326f3eeea0679d1bc8cdeb05eca3f68437a7644ff17c9a69957e61","after_hash":"19e5c9000e6998e5b3997c281aeab30d632e2293fbc68760f499561e1f84df32","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"139408a23d66ad6605946042b506650b54e3bfe6adb09343742246784820ba67"} +{"hash_algorithm":"sha256","ts":"2026-09-25T09:36:24.722Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"e0d15f3cda6eb998be619306","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.282","source":"probe"}},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-25T09:36:24.722Z"}],"before_hash":"19e5c9000e6998e5b3997c281aeab30d632e2293fbc68760f499561e1f84df32","after_hash":"238522a680e1f5866375ef717500bb4f63e6a446ceca1c4dae3a8ae99dc09b7f","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"fb4b800b1d86b69860bf75063ca59520a42488feac3535facae1d71f27521f97"} diff --git a/.agents/pm/history/pm-graph-at6i.jsonl b/.agents/pm/history/pm-graph-at6i.jsonl new file mode 100644 index 0000000..c52e948 --- /dev/null +++ b/.agents/pm/history/pm-graph-at6i.jsonl @@ -0,0 +1,12 @@ +{"hash_algorithm":"sha256","ts":"2026-09-25T08:15:39.057Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"e0d15f3cda6eb998be619306","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.282","source":"probe"}},"op":"create","patch":[{"op":"add","path":"/metadata/id","value":"pm-graph-at6i"},{"op":"add","path":"/metadata/title","value":"Certify pm CLI 2026.9.23 and adopt the guarded pm-ops merge-driver launcher"},{"op":"add","path":"/metadata/description","value":"Fleet wave 2026-09-25 (companion epic pm-cli-website-5s6z, launcher rollout pm-cli-website-xy19). Pins the toolchain to 2026.9.23 and replaces the prepare hook with the launcher template pm-ops ships: it imports nothing from pm-ops, so a production install of a clone (npm ci --omit=dev) skips with one notice instead of failing, while a stale or broken pm-ops still fails the install. A test keeps the copy byte-identical to the pinned template, whose branches pm-ops covers with real fixtures."},{"op":"add","path":"/metadata/type","value":"Chore"},{"op":"add","path":"/metadata/status","value":"open"},{"op":"add","path":"/metadata/priority","value":1},{"op":"add","path":"/metadata/tags","value":["certify","merge-driver","multi-agent","pm-cli-2026.9.23"]},{"op":"add","path":"/metadata/created_at","value":"2026-09-25T08:15:39.057Z"},{"op":"add","path":"/metadata/updated_at","value":"2026-09-25T08:15:39.057Z"},{"op":"add","path":"/metadata/author","value":"fleet-wave-script"},{"op":"add","path":"/metadata/acceptance_criteria","value":"package.json and package-lock.json pin pm-cli 2026.9.23 and pm-ops 2026.9.23 (pm-changelog 2026.9.23 where used); scripts/prepare-merge-driver.ts is byte-identical to the pinned pm-ops template, enforced by a test; CI runs pm health --strict-exit --require-merge-drivers and release:check exits 0"}],"before_hash":"3cc22dff72be7b14824654a7a64ea62b04799939b2fee54c1b5f52ca60bf6df0","after_hash":"ac466ba4a7f262f5848cf95a4b329bfe433f4a2e26b045e6125c9d3cb1569593","item_hash_version":3,"message":"","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"373555a7966e246fe03c13b05434cb060dc93019fb41038ff15d43ea5b796313"} +{"hash_algorithm":"sha256","ts":"2026-09-25T08:15:41.889Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"e0d15f3cda6eb998be619306","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.282","source":"probe"}},"op":"claim","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-25T08:15:41.889Z"},{"op":"add","path":"/metadata/assignee","value":"fleet-wave-script"},{"op":"add","path":"/metadata/claim_principal","value":"fleet-wave-script"}],"before_hash":"ac466ba4a7f262f5848cf95a4b329bfe433f4a2e26b045e6125c9d3cb1569593","after_hash":"ad1875124c41f49ce654d801df84301fa590400ad5f5d3abb0214407ac84288d","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"11ba1887f28bcde915652fa5a45cb3716e2b9614d908d88a13fbf0269f99801f"} +{"hash_algorithm":"sha256","ts":"2026-09-25T08:15:42.852Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"e0d15f3cda6eb998be619306","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.282","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-25T08:15:42.852Z"},{"op":"replace","path":"/metadata/status","value":"in_progress"}],"before_hash":"ad1875124c41f49ce654d801df84301fa590400ad5f5d3abb0214407ac84288d","after_hash":"8144657513e42474bb221f434b6a2822d9d4c9952ff8d896a01e0409fc44d216","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"0b13db681a383c029720504a39ddb7795e53e0b7da0f0629e6e358c4689eb768"} +{"hash_algorithm":"sha256","ts":"2026-09-25T08:15:49.774Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"e0d15f3cda6eb998be619306","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.282","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-25T08:15:49.774Z"},{"op":"add","path":"/metadata/files","value":[{"path":"README.md","scope":"project"}]}],"before_hash":"8144657513e42474bb221f434b6a2822d9d4c9952ff8d896a01e0409fc44d216","after_hash":"5d59e4bb6bbead140b8fba29d578da0e4b0282d76324ba6abce452107c308a74","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"c803ea480f7eb8d71dc6a70ec8c6ddd98ccd2292c8c27f2ff52b894050a1f57d"} +{"hash_algorithm":"sha256","ts":"2026-09-25T08:15:51.472Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"e0d15f3cda6eb998be619306","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.282","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/0/path","value":"package-lock.json"},{"op":"add","path":"/metadata/files/1","value":{"path":"README.md","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-25T08:15:51.472Z"}],"before_hash":"5d59e4bb6bbead140b8fba29d578da0e4b0282d76324ba6abce452107c308a74","after_hash":"eebfb541ff9ffe9f00031ccaf6b71377f70f3867307cb7cc258354ef95c41151","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"2aca3e641555571921767d30dc1c89bf5772e86522a135457350e2fb6dacc5f2"} +{"hash_algorithm":"sha256","ts":"2026-09-25T08:15:52.784Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"e0d15f3cda6eb998be619306","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.282","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/1/path","value":"package.json"},{"op":"add","path":"/metadata/files/2","value":{"path":"README.md","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-25T08:15:52.784Z"}],"before_hash":"eebfb541ff9ffe9f00031ccaf6b71377f70f3867307cb7cc258354ef95c41151","after_hash":"dc5d0ca8b97852307e75da083332edd51d934db67a9ee3796358c1320fb6a104","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"d87bf5c870710b5dbbde377056be905b395eb3fab753293a8e01fa14c6329496"} +{"hash_algorithm":"sha256","ts":"2026-09-25T08:15:54.317Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"e0d15f3cda6eb998be619306","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.282","source":"probe"}},"op":"files_add","patch":[{"op":"add","path":"/metadata/files/3","value":{"path":"scripts/prepare-merge-driver.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-25T08:15:54.317Z"}],"before_hash":"dc5d0ca8b97852307e75da083332edd51d934db67a9ee3796358c1320fb6a104","after_hash":"6d581f9cae4a9ddef51ad84f82fbd10fb87d8b06346237fd725c8439ff656441","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"d18ab00f7a3a627afd0b1cedc0d6cd85351f1f4c44b581c76949f04bc7356e87"} +{"hash_algorithm":"sha256","ts":"2026-09-25T08:15:55.684Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"e0d15f3cda6eb998be619306","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.282","source":"probe"}},"op":"files_add","patch":[{"op":"add","path":"/metadata/files/4","value":{"path":"test/prepare-merge-driver.test.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-25T08:15:55.684Z"}],"before_hash":"6d581f9cae4a9ddef51ad84f82fbd10fb87d8b06346237fd725c8439ff656441","after_hash":"d398bf6dac498045008e6428ff9b7c757dc71e169c7c97d924446703ffb236e3","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"f3fc88db6b1102d0f6dbf7075b5bdafd625b72952fea777553ef82553eaf8b3c"} +{"hash_algorithm":"sha256","ts":"2026-09-25T08:15:57.343Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"e0d15f3cda6eb998be619306","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.282","source":"probe"}},"op":"tests_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-25T08:15:57.343Z"},{"op":"add","path":"/metadata/tests","value":[{"command":"npm run release:check","scope":"project","provenance":{"author":"fleet-wave-script","created_at":"2026-09-25T08:15:57.266Z","source_kind":"local_mutation","source_ref":"certify-pm-cli-2026-9-23-and-roll-out-guarded-merge-driver-launcher"}}]}],"before_hash":"d398bf6dac498045008e6428ff9b7c757dc71e169c7c97d924446703ffb236e3","after_hash":"f681f26163a390edfce9835479fc6fdf0d41030940919ddd7ac414df98b42adf","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"6a457967cec1146e6c21c037ae3832a688b638fecc888a4627ce5235bfeefd33"} +{"hash_algorithm":"sha256","ts":"2026-09-25T09:36:22.319Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"e0d15f3cda6eb998be619306","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.282","source":"probe"}},"op":"close","patch":[{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-25T09:36:22.319Z"},{"op":"replace","path":"/metadata/status","value":"closed"},{"op":"add","path":"/metadata/closed_at","value":"2026-09-25T09:36:22.305Z"},{"op":"add","path":"/metadata/completed_at","value":"2026-09-25T09:36:22.305Z"},{"op":"add","path":"/metadata/resolution","value":"Pinned the toolchain to 2026.9.23 and adopted the guarded pm-ops launcher"},{"op":"add","path":"/metadata/expected_result","value":"release:check and the CI health gate pass on 2026.9.23 with the launcher byte-identical to the template"},{"op":"add","path":"/metadata/actual_result","value":"release:check exits 0; the launcher test and the CI health block pass"},{"op":"add","path":"/metadata/close_reason","value":"Pins: @unbrained/pm-cli 2026.9.21 -> 2026.9.23, pm-changelog 2026.9.18 -> 2026.9.23, pm-ops 2026.9.18 -> 2026.9.23 (package.json and package-lock.json). Launcher: pm-ops template copied unchanged; test/prepare-merge-driver.test.ts proves byte identity and exercises every launcher branch in isolated checkouts. The wave's first gate run was cut off by its 40-minute timeout on a saturated 4-core host; this run finished it. The CI health block runs green with --require-merge-drivers; release:check exits 0."}],"before_hash":"f681f26163a390edfce9835479fc6fdf0d41030940919ddd7ac414df98b42adf","after_hash":"47c48be5d0233b6e246d6585e2cb01e582ce9b0160047d3d095b8ad301b56ad4","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"96dbb1f6f3b6f13f4d3b3294e27c32c349fe2ce761b3e8646bab1bf6b0debfe4"} +{"hash_algorithm":"sha256","ts":"2026-09-25T09:36:24.160Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"e0d15f3cda6eb998be619306","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.282","source":"probe"}},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-25T09:36:24.160Z"}],"before_hash":"47c48be5d0233b6e246d6585e2cb01e582ce9b0160047d3d095b8ad301b56ad4","after_hash":"0d57ccfac16c6cdb9124f364764359457368a367ee6196692d55152de21c04b1","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"67395f58cedafff96e217910c91313eb0218ab62b7b96aa0246165a149f7c58c"} +{"hash_algorithm":"sha256","ts":"2026-09-25T09:51:35.809Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"e0d15f3cda6eb998be619306","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.282","source":"probe"}},"op":"comment_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-25T09:51:35.809Z"},{"op":"add","path":"/metadata/comments","value":[{"created_at":"2026-09-25T09:51:35.809Z","author":"fleet-wave-script","text":"Review round 1 (Greptile, pm-graph#114): the launcher is back in coverageGate.sources where it was on main; npm run coverage reports it at 100/100/100 through the fixture suite's child processes."}]}],"before_hash":"0d57ccfac16c6cdb9124f364764359457368a367ee6196692d55152de21c04b1","after_hash":"157cb79a92339b2b449f9192007a68d3d8014f0c54ecad980bdf7d9f20dc5617","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"cb128699156479812df7ee87ad80497001cb4b4457f268819f0d3a9c17e17ec1"} diff --git a/.agents/pm/history/pm-graph-yuzr.jsonl b/.agents/pm/history/pm-graph-yuzr.jsonl index 8548f59..9562a0c 100644 --- a/.agents/pm/history/pm-graph-yuzr.jsonl +++ b/.agents/pm/history/pm-graph-yuzr.jsonl @@ -7,3 +7,5 @@ {"hash_algorithm":"sha256","ts":"2026-09-23T00:03:40.281Z","author":"pi-glm-pm-graph","author_source":"asserted","agent_harness":"pi","agent_model":"glm-5.2:cloud","agent_model_source":"environment","agent_instance":"bbe3833f6765fc35cafe0956","agent_provenance":{"model":{"value":"glm-5.2:cloud","source":"environment"},"effort":null,"role":null,"topic":null},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/0/path","value":"src/index.ts"},{"op":"add","path":"/metadata/files/1","value":{"path":"test/helpers.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-23T00:03:40.281Z"}],"before_hash":"6661ef592892fe85aab5d25500076964c9f4d778fa77aee95d6863001ace25ab","after_hash":"c99589e85bc84860c4d0a60336ed9ffdfbd089809e2b63abab00f397e8f6c6a9","item_hash_version":3,"event_class":"maintenance","record_hash_version":1,"record_hash":"c2be4e53758a79302454cb0f73c1d93c80a4b11d9a69bf25c606fce34dd7b15f"} {"hash_algorithm":"sha256","ts":"2026-09-23T00:03:40.850Z","author":"pi-glm-pm-graph","author_source":"asserted","agent_harness":"pi","agent_model":"glm-5.2:cloud","agent_model_source":"environment","agent_instance":"bbe3833f6765fc35cafe0956","agent_provenance":{"model":{"value":"glm-5.2:cloud","source":"environment"},"effort":null,"role":null,"topic":null},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/1/path","value":"test/command-surface.test.ts"},{"op":"add","path":"/metadata/files/2","value":{"path":"test/helpers.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-23T00:03:40.850Z"}],"before_hash":"c99589e85bc84860c4d0a60336ed9ffdfbd089809e2b63abab00f397e8f6c6a9","after_hash":"3651dfd3dd8fba35585afc263a502521dfece40a852d6498556021ef4fd1a250","item_hash_version":3,"event_class":"maintenance","record_hash_version":1,"record_hash":"8761eeda51670806fe0d215aff53c42a51fb374807153f7e11feb10eb047148a"} {"hash_algorithm":"sha256","ts":"2026-09-23T00:03:44.244Z","author":"pi-glm-pm-graph","author_source":"asserted","agent_harness":"pi","agent_model":"glm-5.2:cloud","agent_model_source":"environment","agent_instance":"bbe3833f6765fc35cafe0956","agent_provenance":{"model":{"value":"glm-5.2:cloud","source":"environment"},"effort":null,"role":null,"topic":null},"op":"tests_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-23T00:03:44.244Z"},{"op":"add","path":"/metadata/tests","value":[{"command":"npm run release:check","scope":"project","provenance":{"author":"pi-glm-pm-graph","created_at":"2026-09-23T00:03:44.219Z","source_kind":"local_mutation","source_ref":"pm-cli-2026-9-21-canonical-pm-ops-gates"}}]}],"before_hash":"3651dfd3dd8fba35585afc263a502521dfece40a852d6498556021ef4fd1a250","after_hash":"f081632a526100b2a8e7c5034bec9a7cd00e2c0d260174a68b5132d079cecf43","item_hash_version":3,"event_class":"maintenance","record_hash_version":1,"record_hash":"61ff3342399198dee7cffb24dbf07f6e42920a7f4670e6a8153b608be980d731"} +{"hash_algorithm":"sha256","ts":"2026-09-25T08:15:38.318Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"e0d15f3cda6eb998be619306","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.282","source":"probe"}},"op":"update","patch":[{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-25T08:15:38.318Z"},{"op":"replace","path":"/metadata/status","value":"canceled"}],"before_hash":"f081632a526100b2a8e7c5034bec9a7cd00e2c0d260174a68b5132d079cecf43","after_hash":"d1a80b692dd5de2fd00f4965846c0d2a9c92c1c4beb0c67e019cdb6cfb7df433","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"6bdcf26b94100b96813f59ee2ead0a1a28553d88d6a33f2575df1c3a122cb549"} +{"hash_algorithm":"sha256","ts":"2026-09-25T08:15:39.944Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"e0d15f3cda6eb998be619306","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.282","source":"probe"}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/2","value":{"created_at":"2026-09-25T08:15:39.944Z","author":"fleet-wave-script","text":"Superseded by pm-graph-at6i (2026.9.23 certify)"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-25T08:15:39.944Z"}],"before_hash":"d1a80b692dd5de2fd00f4965846c0d2a9c92c1c4beb0c67e019cdb6cfb7df433","after_hash":"42a8330b888b08e71e286561b7c7239f6703f88f5838bb3863b5e304ec4f28c3","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"416dd98f1fcfefd8f69bdfe389295eeefabe2efc47c30309ce4fd7cc49b00f4d"} diff --git a/.agents/pm/issues/pm-graph-1k7d.toon b/.agents/pm/issues/pm-graph-1k7d.toon new file mode 100644 index 0000000..4006f7d --- /dev/null +++ b/.agents/pm/issues/pm-graph-1k7d.toon @@ -0,0 +1,22 @@ +id: pm-graph-1k7d +title: A publish that npm accepts late is reported as failed and the GitHub Release is skipped on bun mirror lag +description: "Companion item pm-cli-website-3y5d. pm-csv 2026.9.23 is the live case: npm accepted the publish with provenance and the tag was pushed, but bun still answered No version matching after 4 minutes, so the job failed before creating the GitHub Release. This repository's own release.yml gets a 10-minute npm visibility window with --prefer-online reads and an honest never-visible message, a ~10-minute bun window, and a GitHub Release that depends only on the publish and tag-push outcomes, with a gate step that fails the job visibly when bun verification failed." +type: Issue +status: closed +priority: 1 +tags[2]: release,reliability +created_at: "2026-09-25T08:15:41.019Z" +updated_at: "2026-09-25T09:36:24.722Z" +closed_at: "2026-09-25T09:36:23.464Z" +completed_at: "2026-09-25T09:36:23.464Z" +author: fleet-wave-script +repro_steps: 1. A Daily Release publishes to npm and pushes the tag. 2. bun still answers No version matching after 8 attempts (pm-csv 2026.9.23). 3. The job fails before Create GitHub release. +resolution: Release-window fix applied +expected_result: A late-visible publish is reconciled and bun lag no longer skips the Release +actual_result: "release:check exits 0" +files[1]{path,scope}: + .github/workflows/release.yml,project +tests[1]{command,scope,provenance{author,created_at,source_kind,source_ref}}: + "npm run release:check",project,fleet-wave-script,"2026-09-25T08:15:58.727Z",local_mutation,certify-pm-cli-2026-9-23-and-roll-out-guarded-merge-driver-launcher +close_reason: "Applied to this repository's own release.yml by anchored replacements; release:check exits 0." +body: "" diff --git a/.agents/pm/tasks/pm-graph-yuzr.toon b/.agents/pm/tasks/pm-graph-yuzr.toon index 2d437cc..42ac0df 100644 --- a/.agents/pm/tasks/pm-graph-yuzr.toon +++ b/.agents/pm/tasks/pm-graph-yuzr.toon @@ -2,18 +2,18 @@ id: pm-graph-yuzr title: "Certify pm CLI 2026.9.21 and move onto the canonical pm-ops merge-driver, lint and duplication gates" description: "Adopt pm CLI 2026.9.21, pin @unbrained/pm-cli 2026.9.21 / pm-changelog / pm-ops exactly, absorb open Dependabot PRs, replace the vendored merge-driver prepare hook with the thin pm-ops/merge-driver launcher, add the canonical lint (pm-ops/eslint) and duplication (pm-ops/jscpd) gates with zero findings, fix the ~81 'any' type sites against real neo4j-driver types, and wire CI to require merge drivers. Companion epic pm-cli-website-5s6z." type: Task -status: in_progress +status: canceled priority: 1 tags[4]: certify,multi-agent,pm-cli-2026.9.21,quality created_at: "2026-09-22T05:08:23.357Z" -updated_at: "2026-09-23T00:03:44.244Z" -assignee: pi-agent +updated_at: "2026-09-25T08:15:39.944Z" claim_principal: pi-agent author: pi-agent acceptance_criteria: "All three devDependency pins exact and verified in package.json + package-lock.json; npm run lint and npm run duplication exit 0 with 0 findings; pm health --strict-exit --require-merge-drivers exits 0; npm run release:check exits 0" -comments[2]{created_at,author,text}: +comments[3]{created_at,author,text}: "2026-09-22T05:09:44.627Z",pi-agent,"Baseline measured with canonical pm-ops 2026.9.18 gates run directly (launchers not yet created): lint 86 errors (83 no-restricted-syntax explicit-any, 1 no-duplicate-imports in test/impact-command.test.ts:16 area, 2 require-atomic-updates in src/index.ts); duplication 6.22% duplicated lines (789/12675), 44 sources, 80 clone pairs, threshold 0. Findings concentrated in test/*.test.ts and src/index.ts. Pins verified: @unbrained/pm-cli 2026.9.21, pm-changelog 2026.9.18, pm-ops 2026.9.18 in package.json + package-lock.json; @types/node lockfile 26.6.1 (Dependabot #107 landed exactly, range ^26.2.0 restored); codeql-action SHAs bumped per #111." "2026-09-23T00:03:36.230Z",pi-glm-pm-graph,"Duplication gate reduced from 5.6% to 0% (74 to 0 clone pairs) by extracting shared helpers into test/helpers.ts (withNeo4jQueryTest, withCommandWorkspace, setupChainWorkspace, setupCycleWorkspace, setupBareWorkspace, activateWithRecording, captureStdoutThrow, expectCommandError, expectCommandErrorMulti, expectExporterReject, runExportRaw, createChain, synthNode/synthRel/synthGraph, NEO4J_FAIL_ENV, setNeo4jTestEnv, applyEnv, CmdResult/CommandError types). src/index.ts: extracted unwrapNeo4jInteger (toNumber+readNumberProperty), nodeLabelParts (renderDot+renderPlantuml), orderedNodeSet+collectBidirectionalEdges (impactSubgraph+impactSubgraphFromNodeSet), loadShapedAnalytics (cycles+critical-path+topo-sort), wrapCommandError (export catch blocks). Added docstrings to 8 type aliases and 3 functions. Lint exit 0, duplication 0%, release:check exit 0 (287 tests, coverage 100/100/100), pm health --strict-exit exit 0." + "2026-09-25T08:15:39.944Z",fleet-wave-script,Superseded by pm-graph-at6i (2026.9.23 certify) files[3]{path,scope}: src/index.ts,project test/command-surface.test.ts,project diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c8312ce..b170f70 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -600,6 +600,7 @@ jobs: # publishing must be configured for this package on npmjs.com against # unbraind/pm-graph and this workflow filename, or publish fails closed. - name: Publish npm package + id: publish if: steps.decide.outputs.should_release == 'true' shell: bash env: @@ -665,11 +666,11 @@ jobs: # coordinate" and is refused rather than reconciled. registry_version_is_attested() { local attestations - attestations="$(npm view "${pkg_name}@${NPM_VERSION}" dist.attestations --json 2>/dev/null || true)" + attestations="$(npm view "${pkg_name}@${NPM_VERSION}" dist.attestations --prefer-online --json 2>/dev/null || true)" [[ -n "${attestations}" && "${attestations}" != "null" && "${attestations}" != "{}" && "${attestations}" != "[]" ]] } registry_has_version() { - npm view "${pkg_name}@${NPM_VERSION}" version --json >/dev/null 2>&1 + npm view "${pkg_name}@${NPM_VERSION}" version --prefer-online --json >/dev/null 2>&1 } # Answers one question and nothing else: is an attested copy of this # exact version visible right now? It must never terminate the step @@ -688,11 +689,16 @@ jobs: # forbids overwriting a published version. It needs a human, and saying # so is more useful than a green run over an artifact the release notes # will misdescribe. + # Declared before any function that expands it: bindings are established + # before use so visibility never depends on call-time reasoning. + max_attempts=3 refuse_unattested_or_fail() { if registry_has_version; then echo "::error::${pkg_name}@${NPM_VERSION} exists on the registry WITHOUT a visible provenance attestation. This workflow only ever publishes with --provenance, so either that artifact did not come from this job, or its attestation never became visible. Refusing to tag and release around it; investigate before re-running." else - echo "::error::Publish with provenance failed after ${max_attempts} attempts. Refusing to downgrade supply-chain attestations; retry the release transaction." + # Never claim the publish failed when only visibility was not + # confirmed: say exactly what this run knows (pm-cli-website-3y5d). + echo "::error::npm did not confirm ${pkg_name}@${NPM_VERSION} is published after ${max_attempts} publish attempts and a 10-minute visibility window. The registry shows nothing at that coordinate right now: either the publish genuinely failed, or propagation outlasted the window. Refusing to downgrade supply-chain attestations; retry the release transaction." fi exit 1 } @@ -710,7 +716,6 @@ jobs: npm publish --access public --provenance --ignore-scripts } attempt=0 - max_attempts=3 while (( attempt < max_attempts )); do attempt=$(( attempt + 1 )) if publish_with_provenance; then @@ -735,24 +740,32 @@ jobs: # version that is on the registry, which is the "npm ahead of git" # split the release ordering exists to prevent. Poll instead. # - # 5 attempts, 30s apart. The bun verification step further down this - # file already retries the same registry on the same 30s schedule, - # because a version the registry has just accepted is not immediately - # visible; the npm read here needs the same grace for the same reason. - # A shorter window would fail a release that the very next step would - # then find. The cost is paid only on the path where npm has already - # reported an error, never on a successful publish. - reconcile_attempts=5 + # 20 reads, 30 s apart: a 10-minute visibility window. The old + # 5 x 30 s window closed 2.5 minutes in while npm had already + # ACCEPTED the publish, printed the false "failed after 3 attempts", + # and skipped the tag and the GitHub release for a version the + # registry then served moments later (pm-slack/pm-web 2026-09-18: + # visible 35 s after the window closed). Ten minutes absorbs the + # observed propagation; --prefer-online on the registry reads keeps + # a stale cache answer from faking or hiding visibility. The cost is + # paid only on the path where npm has already reported an error, + # never on a successful publish. + reconcile_attempts=20 for reconcile_attempt in $(seq 1 "${reconcile_attempts}"); do if reconciled_attested; then echo "::notice::Version landed attested after the final reported error; treating as success." exit 0 fi - if (( reconcile_attempt < reconcile_attempts )); then - echo "Not yet visible on the registry; re-reading in 30s (${reconcile_attempt}/${reconcile_attempts})..." - sleep 30 - fi + echo "Not yet visible on the registry; re-reading in 30s (${reconcile_attempt}/${reconcile_attempts})..." + sleep 30 done + # The 20th sleep completes the 10-minute window; read once more so a + # version that became visible during that final 30 s is caught too, + # not failed on an arithmetic edge. + if reconciled_attested; then + echo "::notice::Version landed attested at the end of the 10-minute visibility window; treating as success." + exit 0 + fi refuse_unattested_or_fail # Tag the exact merged/verified main commit AFTER a successful publish. @@ -763,6 +776,7 @@ jobs: # retains the prepared metadata and the next run resumes the same version # instead of inventing another release. - name: Push release tag + id: push_tag if: steps.decide.outputs.should_release == 'true' shell: bash env: @@ -801,6 +815,7 @@ jobs: git push origin "refs/tags/${release_tag}" - name: Verify bun install of published package + id: verify_bun if: steps.decide.outputs.should_release == 'true' env: NPM_VERSION: ${{ steps.decide.outputs.npm_version }} @@ -815,31 +830,61 @@ jobs: bun init -y > /dev/null # Smoke-test that the just-published version installs via bun. # Retry to absorb npm registry propagation (~60s typical). - for attempt in 1 2 3 4 5 6 7 8; do + # 21 attempts with a 30 s pause BETWEEN them: the same 10-minute + # window the npm reconcile uses. The last attempt runs after the + # final pause, so a version that becomes installable at the very end + # of the window still passes instead of failing on a trailing sleep. + bun_attempts=21 + for attempt in $(seq 1 "${bun_attempts}"); do if bun add "${pkg_name}@${pkg_version}"; then echo "bun add succeeded on attempt $attempt" exit 0 fi - echo "bun add failed on attempt $attempt, sleeping 30s..." - sleep 30 + if (( attempt < bun_attempts )); then + echo "bun add failed on attempt $attempt, sleeping 30s..." + sleep 30 + fi done - # bun's registry mirror can lag well past npm's own propagation, - # especially for prerelease (-N) versions. The npm registry is - # authoritative for what we just published: if it confirms the - # version, the release genuinely succeeded and the bun failure is - # mirror lag, not a publish failure. Do not let it block the - # GitHub release / post-publish steps that follow. - echo "bun could not resolve ${pkg_name}@${pkg_version}; checking npm registry authoritatively..." - if npm view "${pkg_name}@${pkg_version}" version --registry="https://registry.npmjs.org" > /dev/null 2>&1; then - echo "::warning::bun has not mirrored ${pkg_name}@${pkg_version} yet, but npm confirms it is published (registry mirror lag). Treating verification as successful." - exit 0 - fi - echo "npm registry does not show ${pkg_name}@${pkg_version} - real publish failure." + # The GitHub release below is now created whenever the publish and + # the tag push succeeded, regardless of this step, so a bun failure + # must NOT be papered over as success. The old fallback here (treat + # mirror lag as a passing verification once `npm view` confirmed the + # version) still let a total failure skip the Release: pm-linear run + # 35323736826 (2026-09-18) failed this step, the Release was skipped, + # and tag v2026.09.18 has had no Release since. npm acceptance is + # already proven by the publish step above; this step verifies bun + # alone, so a failure here is reported as a failure and the gate + # step below fails the job visibly. + echo "::error::bun could not resolve ${pkg_name}@${pkg_version} after ${bun_attempts} attempts across a 10-minute window. npm accepted the publish and the GitHub release is created regardless of this step; this failure keeps the bun mirror problem visible instead of silent." exit 1 - name: Create GitHub release - if: steps.decide.outputs.should_release == 'true' + # Created even when bun verification failed: this Release used to be + # skipped behind that step, which is how pm-linear v2026.09.18 ended + # up tagged with no Release. It depends only on the publish and the + # tag push; a bun failure is surfaced by the gate step below so + # nothing goes silent. !cancelled() is required: with the default + # success() condition any earlier failure would skip this step. + if: >- + !cancelled() && + steps.publish.outcome == 'success' && + steps.push_tag.outcome == 'success' env: REPO_NAME: ${{ github.event.repository.name }} RELEASE_TAG: ${{ steps.decide.outputs.tag }} GH_TOKEN: ${{ github.token }} run: gh release create "${RELEASE_TAG}" --title "${REPO_NAME} ${RELEASE_TAG}" --notes-file RELEASE_NOTES.md --verify-tag + + # The visible half of the bun decoupling: the Release above is created + # regardless of bun verification, so without this gate a bun failure + # would end in a green run and mirror lag would be invisible. Only a + # bun FAILURE trips it - a skipped bun step means the publish or the + # tag push already failed the job on its own. + - name: Fail the job on bun verification failure + if: >- + !cancelled() && + steps.verify_bun.outcome == 'failure' + shell: bash + run: | + set -euo pipefail + echo "::error::bun install verification failed (see the step log above); the npm publish, the tag push and the GitHub release were not affected. Failing the job so the bun mirror problem is not silent." + exit 1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 039e193..7782a7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## Unreleased + +### Fixed + +- A publish that npm accepts late is reported as failed and the GitHub Release is skipped on bun mirror lag ([pm-graph-1k7d](https://github.com/unbraind/pm-graph/blob/main/.agents/pm/issues/pm-graph-1k7d.toon)) + +### Other + +- Certify pm CLI 2026.9.23 and adopt the guarded pm-ops merge-driver launcher ([pm-graph-at6i](https://github.com/unbraind/pm-graph/blob/main/.agents/pm/chores/pm-graph-at6i.toon)) + ## 2026.9.18 - 2026-09-18 ### Other diff --git a/README.md b/README.md index c5e9bcf..965f9ce 100644 --- a/README.md +++ b/README.md @@ -491,13 +491,7 @@ This package supports GitHub, npm, and Bun-compatible installs. Publication rema This repo tracks its project management in `.agents/pm/` and ships a committed `.gitattributes` that maps those tracker artifacts to pm-cli's field-aware Git merge drivers, so concurrent-branch tracker edits merge cleanly instead of hard-conflicting. The driver definitions live in per-clone -Git config; `npm install` / `npm ci` wires them automatically via the `prepare` script (a thin TypeScript -launcher, `scripts/prepare-merge-driver.ts`, over the canonical `pm-ops/merge-driver` export: it runs -`pm merge install` only when the `pm` CLI is on `PATH`, and no-ops cleanly when `pm` is absent. -Registry installs of this package never run `prepare`; a production install of a clone -(`npm ci --omit=dev`) omits `pm-ops` too, so it must pass `--ignore-scripts` (the guarded launcher is -tracked as companion item pm-cli-website-xy19); being Node-based it behaves identically -on POSIX shells and Windows `cmd.exe`). To (re)run manually: `npm run merge:install`. +Git config; `npm install` / `npm ci` wires them automatically via the `prepare` script, `scripts/prepare-merge-driver.ts`: the launcher template pm-ops ships, copied unchanged, which a test compares byte for byte with the pinned template. It runs pm-ops's installer, which calls `pm merge install` when the `pm` CLI is on `PATH` and skips with a notice when it is not. A production install of a clone (`npm ci --omit=dev`) has no `pm-ops`, so the launcher skips with one notice, while a stale or broken `pm-ops` fails the install. Registry installs of this package never run `prepare`. Being Node-based, it behaves identically on POSIX shells and Windows `cmd.exe`. To (re)run manually: `npm run merge:install`. After merging a branch that touched `.agents/pm/`, reconcile any residual history-hash drift with **`pm merge reconcile`** (pm-cli ≥ 2026.7.22): preview with `pm merge reconcile --dry-run`, apply with diff --git a/package-lock.json b/package-lock.json index a318519..a084cd9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,12 +15,12 @@ "@babel/eslint-parser": "^8.0.5", "@babel/plugin-syntax-typescript": "^8.0.3", "@types/node": "^26.2.0", - "@unbrained/pm-cli": "2026.9.21", + "@unbrained/pm-cli": "2026.9.23", "eslint": "^10.10.0", "fast-glob": "^3.3.3", "jscpd": "^4.3.0", - "pm-changelog": "2026.9.18", - "pm-ops": "2026.9.18", + "pm-changelog": "2026.9.23", + "pm-ops": "2026.9.23", "typescript": "^7.0.2" }, "engines": { @@ -1331,9 +1331,9 @@ } }, "node_modules/@unbrained/pm-cli": { - "version": "2026.9.21", - "resolved": "https://registry.npmjs.org/@unbrained/pm-cli/-/pm-cli-2026.9.21.tgz", - "integrity": "sha512-HPgGm/pU81Avtty9lVYqYh0jxKzNyQjHnvwQrRjSYNHaVQeJ3xNM/VV9i0CCPL0zLxRjdriYj/PnNDIMC2Ur2Q==", + "version": "2026.9.23", + "resolved": "https://registry.npmjs.org/@unbrained/pm-cli/-/pm-cli-2026.9.23.tgz", + "integrity": "sha512-tL1u+NDa6UkJyJaJR3j7noqi6byFlQdgWSZsFDG/k6k31LTBCQJNfX42EhgBE8kR100O61DleJSaMCtPq+LT9g==", "dev": true, "license": "MIT", "dependencies": { @@ -3242,9 +3242,9 @@ } }, "node_modules/pm-changelog": { - "version": "2026.9.18", - "resolved": "https://registry.npmjs.org/pm-changelog/-/pm-changelog-2026.9.18.tgz", - "integrity": "sha512-UJekN8TZUk/Q9Ri7pMl+EEtPqaGG5ePs/3/hS5JCx7w78dZAamfH7lUA+wUjJtoyKzLMX3XxTkCWpe61CquXdg==", + "version": "2026.9.23", + "resolved": "https://registry.npmjs.org/pm-changelog/-/pm-changelog-2026.9.23.tgz", + "integrity": "sha512-hrHFbRgz/LyiCBN9ic0I1C//SRL/zjU5fxcyCeCAnM2qTrz14A7f9ayMXeiu/ChDkVFMxfz8Ps2j3TuUG6K1QA==", "dev": true, "license": "MIT", "bin": { @@ -3258,9 +3258,9 @@ } }, "node_modules/pm-ops": { - "version": "2026.9.18", - "resolved": "https://registry.npmjs.org/pm-ops/-/pm-ops-2026.9.18.tgz", - "integrity": "sha512-x4KWL0Y9y6Sz2Hw9LDJmZPoZmbruKJ1x9Z9QCq2/W/yAG5Lujl5HiyrE9Er6Ne7gKFWJp3kxywYqUWDnLHEyMQ==", + "version": "2026.9.23", + "resolved": "https://registry.npmjs.org/pm-ops/-/pm-ops-2026.9.23.tgz", + "integrity": "sha512-FglZHOXjsuG8WWf9Ca90/IX1u4ZCxVTvHpwMaiA0TAWBx/lLalM2Ic12gtWZvm3OAJsMMuXvfRBHWxwYLzESsg==", "dev": true, "license": "MIT", "dependencies": { @@ -3276,7 +3276,7 @@ "@unbrained/pm-cli": ">=2026.8.20", "eslint": "^10.10.0", "fast-glob": "^3.3.3", - "jscpd": "^4.3.0" + "jscpd": ">=4.3.0 <6.0.0" }, "peerDependenciesMeta": { "@babel/eslint-parser": { diff --git a/package.json b/package.json index 874c3a3..42696c7 100644 --- a/package.json +++ b/package.json @@ -34,12 +34,12 @@ "@babel/eslint-parser": "^8.0.5", "@babel/plugin-syntax-typescript": "^8.0.3", "@types/node": "^26.2.0", - "@unbrained/pm-cli": "2026.9.21", + "@unbrained/pm-cli": "2026.9.23", "eslint": "^10.10.0", "fast-glob": "^3.3.3", "jscpd": "^4.3.0", - "pm-changelog": "2026.9.18", - "pm-ops": "2026.9.18", + "pm-changelog": "2026.9.23", + "pm-ops": "2026.9.23", "typescript": "^7.0.2" }, "files": [ diff --git a/scripts/prepare-merge-driver.ts b/scripts/prepare-merge-driver.ts index 5ac0834..66e512e 100644 --- a/scripts/prepare-merge-driver.ts +++ b/scripts/prepare-merge-driver.ts @@ -1,10 +1,42 @@ /** - * npm `prepare` hook that installs pm's field-aware Git merge drivers. + * npm `prepare` hook that registers pm's field-aware Git merge drivers. * - * Git never clones `.git/config`, so every clone must register the drivers that - * `.gitattributes` declares. The canonical implementation lives in - * `pm-ops/merge-driver`; this file stays a thin launcher over it. + * Git never clones `.git/config`, so every clone must register the drivers + * `.gitattributes` declares. The installer lives in the devDependency pm-ops, + * which an `npm install --omit=dev` checkout does not have. This launcher + * therefore imports nothing from pm-ops: it resolves the installer entry from + * the package root and runs it in a child process. Only a missing pm-ops package skips, with one + * notice; any other resolution failure (for example a pm-ops too old to export + * the entry) and any installer failure fail the install. + * + * Canonical copy: `pm-ops/templates/prepare-merge-driver.ts`. Copy it + * unchanged to `scripts/prepare-merge-driver.ts`. */ -import { runPrepareMergeDriver } from "pm-ops/merge-driver"; -process.exitCode = runPrepareMergeDriver(); \ No newline at end of file +import { spawnSync } from "node:child_process"; +import { createRequire } from "node:module"; +import { join } from "node:path"; + +// npm runs `prepare` from the package root, so pm-ops is resolved from there. +const resolver = createRequire(join(process.cwd(), "package.json")); +let installer: string | undefined; +try { + installer = resolver.resolve("pm-ops/merge-driver/prepare"); +} catch (error) { + // Only an absent pm-ops package may skip. Probing its package.json tells that + // apart from an installed pm-ops that cannot serve the entry (exports without + // it, no exports map, a missing file): those resolve or fail differently, and + // the original error is rethrown. + let packagePresent = true; + try { + resolver.resolve("pm-ops/package.json"); + } catch (probe) { + packagePresent = !(probe instanceof Error && "code" in probe && probe.code === "MODULE_NOT_FOUND"); + } + if (packagePresent) throw error; +} +if (installer === undefined) { + console.error("pm-ops is not installed (omit-dev install); skipping merge-driver install"); +} else { + process.exitCode = spawnSync(process.execPath, [installer], { stdio: "inherit" }).status ?? 1; +} diff --git a/test/prepare-merge-driver.test.ts b/test/prepare-merge-driver.test.ts index bcb1230..c3bb230 100644 --- a/test/prepare-merge-driver.test.ts +++ b/test/prepare-merge-driver.test.ts @@ -1,11 +1,121 @@ -/** Tests the thin `prepare` launcher over the canonical `pm-ops/merge-driver`. */ +/** + * Tests for the npm `prepare` hook `scripts/prepare-merge-driver.ts`. + * + * The hook must stay the canonical pm-ops launcher byte for byte, and it is + * exercised the way npm runs it: as the entry point of a child process whose + * working directory is a consumer checkout. Every checkout is a fresh + * `git init` with its own local config, so the drivers this repository's own + * `npm ci` registered cannot mask a launcher that registers none. + */ import assert from "node:assert/strict"; -import test from "node:test"; +import { spawnSync, type SpawnSyncReturns } from "node:child_process"; +import { chmodSync, copyFileSync, mkdirSync, mkdtempSync, readFileSync, rmSync, symlinkSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { delimiter, join } from "node:path"; +import test, { after } from "node:test"; -// Importing the launcher executes it (it is the prepare hook). Absent `pm` is a supported -// 0-exit state and a clean `pm merge install` is 0, so only a broken CLI fails this. -import "../scripts/prepare-merge-driver.ts"; +// npm runs tests from the package root, which is also where it runs `prepare`. +const root = process.cwd(); +const launcher = join(root, "scripts", "prepare-merge-driver.ts"); +const hostPath = `${join(root, "node_modules", ".bin")}${delimiter}${process.env.PATH ?? ""}`; +const scratch = mkdtempSync(join(tmpdir(), "prepare-merge-driver-")); +after(() => rmSync(scratch, { recursive: true, force: true })); -test("the prepare launcher delegates to the canonical pm-ops merge-driver export", () => { - assert.strictEqual(process.exitCode, 0); -}); \ No newline at end of file +// The fixtures use POSIX stub executables and directory symlinks, like pm-ops's own launcher suite. +const posixOnly = { skip: process.platform === "win32" }; + +/** Every merge driver `.gitattributes` asks Git to use, e.g. `pm-history` from `merge=pm-history`. */ +const declaredDrivers = [ + ...new Set([...readFileSync(join(root, ".gitattributes"), "utf8").matchAll(/\bmerge=([\w-]+)/g)].map((match) => match[1])), +].sort(); + +/** + * Create a consumer checkout: a fresh Git repository carrying this + * repository's `.gitattributes` and tracker settings. `pmOps` selects what + * `node_modules/pm-ops` is: absent (an omit-dev install), the pinned package, + * or a stale pm-ops whose exports predate the launcher entry. + */ +function checkout(name: string, pmOps: "absent" | "pinned" | "stale"): string { + const directory = join(scratch, name); + mkdirSync(join(directory, ".agents", "pm"), { recursive: true }); + assert.equal(spawnSync("git", ["init", "-q"], { cwd: directory }).status, 0); + writeFileSync(join(directory, "package.json"), JSON.stringify({ name, type: "module" })); + copyFileSync(join(root, ".gitattributes"), join(directory, ".gitattributes")); + copyFileSync(join(root, ".agents", "pm", "settings.json"), join(directory, ".agents", "pm", "settings.json")); + if (pmOps === "pinned") { + mkdirSync(join(directory, "node_modules")); + symlinkSync(join(root, "node_modules", "pm-ops"), join(directory, "node_modules", "pm-ops"), "dir"); + } + if (pmOps === "stale") { + mkdirSync(join(directory, "node_modules", "pm-ops"), { recursive: true }); + writeFileSync( + join(directory, "node_modules", "pm-ops", "package.json"), + JSON.stringify({ name: "pm-ops", type: "module", exports: { "./merge-driver": "./merge-driver.js" } }), + ); + } + return directory; +} + +/** Put a stub `pm` that runs `body` then exits with `status` alone on a fresh PATH directory. */ +function stubPm(name: string, status: number, body = ""): string { + const bin = join(scratch, `${name}-bin`); + mkdirSync(bin); + writeFileSync(join(bin, "pm"), `#!/bin/sh\n${body}\nexit ${status}\n`); + chmodSync(join(bin, "pm"), 0o755); + return bin; +} + +/** Run the launcher as npm's `prepare` hook would: as the entry point, from `cwd`, with `path` as PATH. */ +function prepare(cwd: string, path: string): SpawnSyncReturns { + return spawnSync(process.execPath, [launcher], { cwd, encoding: "utf8", env: { ...process.env, PATH: path } }); +} + +/** The merge drivers registered in a checkout's LOCAL Git config, by name. */ +function registeredDrivers(cwd: string): string[] { + const config = spawnSync("git", ["config", "--local", "--name-only", "--get-regexp", "^merge\\..*\\.driver$"], { + cwd, + encoding: "utf8", + }); + // git exits 1 when no key matches, which is a genuine "none registered". + assert.ok(config.status === 0 || config.status === 1, config.stderr); + return config.stdout.split("\n").filter(Boolean).map((key) => key.split(".")[1]).sort(); +} + +test("the prepare launcher is the unmodified pm-ops template", () => { + const canonical = readFileSync(join(root, "node_modules", "pm-ops", "templates", "prepare-merge-driver.ts"), "utf8"); + assert.equal(readFileSync(launcher, "utf8"), canonical); +}); + +test("a full install registers every merge driver .gitattributes declares", posixOnly, () => { + const directory = checkout("full", "pinned"); + assert.deepEqual(registeredDrivers(directory), []); + const result = prepare(directory, hostPath); + assert.equal(result.status, 0, result.stderr); + assert.ok(declaredDrivers.length > 0, ".gitattributes declares no pm merge drivers"); + assert.deepEqual(registeredDrivers(directory), declaredDrivers); +}); + +test("an omit-dev checkout without pm-ops skips with one notice and registers nothing", posixOnly, () => { + const directory = checkout("omit-dev", "absent"); + const result = prepare(directory, hostPath); + assert.equal(result.status, 0, result.stderr); + assert.equal(result.stderr, "pm-ops is not installed (omit-dev install); skipping merge-driver install\n"); + assert.deepEqual(registeredDrivers(directory), []); +}); + +test("a pm-ops too old to export the launcher entry fails the install", posixOnly, () => { + const result = prepare(checkout("stale", "stale"), hostPath); + assert.notEqual(result.status, 0); + assert.match(result.stderr, /ERR_PACKAGE_PATH_NOT_EXPORTED/); +}); + +test("a failing pm merge install fails the install with the same status", posixOnly, () => { + const result = prepare(checkout("failing-pm", "pinned"), stubPm("failing-pm", 7)); + assert.equal(result.status, 7, result.stderr); +}); + +test("an installer killed by a signal fails the install instead of reporting success", posixOnly, () => { + // The stub's parent is the pm-ops installer process the launcher spawned. + const result = prepare(checkout("killed", "pinned"), stubPm("killed", 0, "kill -9 $PPID")); + assert.equal(result.status, 1, result.stderr); +});