From d49ade144abdc18935b26c12dd70bd974116e6bd Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Tue, 22 Sep 2026 07:59:34 +0200 Subject: [PATCH 1/6] Certify pm CLI 2026.9.21 and install merge drivers through the canonical pm-ops launcher - Pins @unbrained/pm-cli 2026.9.21, pm-ops 2026.9.18 and pm-changelog 2026.9.18 exactly (package.json and package-lock.json). - scripts/prepare-merge-driver.ts is now a thin launcher over pm-ops/merge-driver, replacing the untyped prepare-merge-driver.mjs; one canonical, tested installer instead of a private copy per repository. - CI installs the drivers before `pm health --strict-exit --require-merge-drivers`, so a clone without them fails the gate instead of hard-conflicting tracker files on the next merge. - Release workflow: 10-minute npm visibility window, GitHub Release decoupled from bun mirror lag with a visible gate step, and a best-effort backfill of missing Releases (companion pm-cli-website-3y5d). pm items: pm-slack-h3ba, pm-slack-x0wg. Companion epic pm-cli-website-5s6z. release:check exits 0. --- .agents/pm/history/pm-slack-h3ba.jsonl | 12 ++ .agents/pm/history/pm-slack-x0wg.jsonl | 8 + .agents/pm/issues/pm-slack-x0wg.toon | 21 +++ .agents/pm/tasks/pm-slack-h3ba.toon | 26 ++++ .github/workflows/ci.yml | 5 +- .github/workflows/release.yml | 203 +++++++++++++++++++++---- CHANGELOG.md | 10 ++ README.md | 2 +- package-lock.json | 96 +++++++----- package.json | 8 +- scripts/prepare-merge-driver.mjs | 67 -------- scripts/prepare-merge-driver.ts | 10 ++ 12 files changed, 327 insertions(+), 141 deletions(-) create mode 100644 .agents/pm/history/pm-slack-h3ba.jsonl create mode 100644 .agents/pm/history/pm-slack-x0wg.jsonl create mode 100644 .agents/pm/issues/pm-slack-x0wg.toon create mode 100644 .agents/pm/tasks/pm-slack-h3ba.toon delete mode 100644 scripts/prepare-merge-driver.mjs create mode 100644 scripts/prepare-merge-driver.ts diff --git a/.agents/pm/history/pm-slack-h3ba.jsonl b/.agents/pm/history/pm-slack-h3ba.jsonl new file mode 100644 index 0000000..2a95fa2 --- /dev/null +++ b/.agents/pm/history/pm-slack-h3ba.jsonl @@ -0,0 +1,12 @@ +{"hash_algorithm":"sha256","ts":"2026-09-22T05:58:10.946Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"create","patch":[{"op":"add","path":"/metadata/id","value":"pm-slack-h3ba"},{"op":"add","path":"/metadata/title","value":"Certify pm CLI 2026.9.21 and install merge drivers through the canonical pm-ops launcher"},{"op":"add","path":"/metadata/description","value":"Fleet wave 2026-09-22 (companion epic pm-cli-website-5s6z). Pins @unbrained/pm-cli 2026.9.21, pm-ops 2026.9.18 and pm-changelog 2026.9.18 exactly. The prepare hook becomes a thin launcher over pm-ops/merge-driver instead of a vendored implementation, and CI installs the drivers before pm health --strict-exit --require-merge-drivers, so a fresh clone that lacks them fails the gate instead of silently hard-conflicting tracker files."},{"op":"add","path":"/metadata/type","value":"Task"},{"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.21"]},{"op":"add","path":"/metadata/created_at","value":"2026-09-22T05:58:10.946Z"},{"op":"add","path":"/metadata/updated_at","value":"2026-09-22T05:58:10.946Z"},{"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.21, pm-ops 2026.9.18, pm-changelog 2026.9.18; scripts/prepare-merge-driver.ts is the thin pm-ops/merge-driver launcher; no vendored implementation remains; CI runs pm merge install before pm health --strict-exit --require-merge-drivers"}],"before_hash":"3cc22dff72be7b14824654a7a64ea62b04799939b2fee54c1b5f52ca60bf6df0","after_hash":"2146901d2192ef9dc0e1ef14a7e5afc34c3119dd40aa62d481788800897ccf5f","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":"411be312d8f7ef3e87fc2fd9548ce290aee66515b5ae84ce591160823a7c8510"} +{"hash_algorithm":"sha256","ts":"2026-09-22T05:58:13.213Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"claim","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T05:58:13.213Z"},{"op":"add","path":"/metadata/assignee","value":"fleet-wave-script"},{"op":"add","path":"/metadata/claim_principal","value":"fleet-wave-script"}],"before_hash":"2146901d2192ef9dc0e1ef14a7e5afc34c3119dd40aa62d481788800897ccf5f","after_hash":"06ae8ad8a1a151e1877b72bfc7aa286dfee5df67808ab44b69298e3bf369c5b6","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":"44227a509a441dbf0af5e904a5a5472a43885bfe478cfd7ff6114060b1d2547b"} +{"hash_algorithm":"sha256","ts":"2026-09-22T05:58:14.161Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T05:58:14.161Z"},{"op":"replace","path":"/metadata/status","value":"in_progress"}],"before_hash":"06ae8ad8a1a151e1877b72bfc7aa286dfee5df67808ab44b69298e3bf369c5b6","after_hash":"49b15f044bb95d85cebcb47116e050c3e1fe63af343026817d4d128ae606be2c","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":"92cf0c8728688d9e7e9127d71da338fabf3d83a8e70cc300642ea8dafdf82c65"} +{"hash_algorithm":"sha256","ts":"2026-09-22T05:58:17.016Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T05:58:17.016Z"},{"op":"add","path":"/metadata/files","value":[{"path":".github/workflows/ci.yml","scope":"project"}]}],"before_hash":"49b15f044bb95d85cebcb47116e050c3e1fe63af343026817d4d128ae606be2c","after_hash":"57942b4889f302ffae252f87a7f16ae0868873ce7fb4b97dfd32d9beb949a864","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":"28d71cbb9f3ba7ec623367d2965e8c77b23047fbe90b1ee52fdba9d1e7c7ea56"} +{"hash_algorithm":"sha256","ts":"2026-09-22T05:58:18.546Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"files_add","patch":[{"op":"add","path":"/metadata/files/1","value":{"path":"README.md","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T05:58:18.546Z"}],"before_hash":"57942b4889f302ffae252f87a7f16ae0868873ce7fb4b97dfd32d9beb949a864","after_hash":"cc25e2e7e2343fdc1e528d817d272d3c7cf367e01be3525453b361a403c3a31f","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":"68b1d90ee45b08ca1ee161e703d9e30eaa7b2fe5fc2204be1d9c1e2c70a1d7ef"} +{"hash_algorithm":"sha256","ts":"2026-09-22T05:58:19.188Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/1/path","value":"package-lock.json"},{"op":"add","path":"/metadata/files/2","value":{"path":"README.md","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T05:58:19.188Z"}],"before_hash":"cc25e2e7e2343fdc1e528d817d272d3c7cf367e01be3525453b361a403c3a31f","after_hash":"f8dfd130191914d0013894ab027d8ada7955d769c2c897ac21ae4c443d9191d8","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":"1d15d6d7f780127684a92086159f36724fcc34534e1d95aed65bf14443908f99"} +{"hash_algorithm":"sha256","ts":"2026-09-22T05:58:19.954Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/2/path","value":"package.json"},{"op":"add","path":"/metadata/files/3","value":{"path":"README.md","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T05:58:19.954Z"}],"before_hash":"f8dfd130191914d0013894ab027d8ada7955d769c2c897ac21ae4c443d9191d8","after_hash":"4eb78594553b2107111e3beb10539b04c0293755c92dc52c3d61dc0c2d20ad21","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":"f935261f822116f9d1e0fadbfc00633d60aff74d4e2cdb50a5a1712dcb3c3449"} +{"hash_algorithm":"sha256","ts":"2026-09-22T05:58:20.697Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"files_add","patch":[{"op":"add","path":"/metadata/files/4","value":{"path":"scripts/prepare-merge-driver.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T05:58:20.697Z"}],"before_hash":"4eb78594553b2107111e3beb10539b04c0293755c92dc52c3d61dc0c2d20ad21","after_hash":"ac33c4a43dd81fad02ceb95396940e36373aa3c380e253341ca3013cd0778d8f","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":"a626daf3d3b39dc5d1cc65433df2934514f740906fdcffd27e25eb8838d07038"} +{"hash_algorithm":"sha256","ts":"2026-09-22T05:58:21.583Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"tests_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T05:58:21.583Z"},{"op":"add","path":"/metadata/tests","value":[{"command":"npm run release:check","scope":"project","provenance":{"author":"fleet-wave-script","created_at":"2026-09-22T05:58:21.551Z","source_kind":"local_mutation","source_ref":"pm-cli-2026-9-21-canonical-merge-driver-release-window"}}]}],"before_hash":"ac33c4a43dd81fad02ceb95396940e36373aa3c380e253341ca3013cd0778d8f","after_hash":"cdd9d0c975e19c5ec02ce2cef196405ee7e113fb54166992f3783e968aa21e69","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":"9e440f3cbace569e5a51400b050da53cfe9a11b401bfb306925075338316142a"} +{"hash_algorithm":"sha256","ts":"2026-09-22T05:59:08.902Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"close","patch":[{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T05:59:08.902Z"},{"op":"replace","path":"/metadata/status","value":"closed"},{"op":"add","path":"/metadata/closed_at","value":"2026-09-22T05:59:08.862Z"},{"op":"add","path":"/metadata/completed_at","value":"2026-09-22T05:59:08.862Z"},{"op":"add","path":"/metadata/close_reason","value":"Pins: @unbrained/pm-cli 2026.9.17 -> 2026.9.21, pm-changelog 2026.9.16 -> 2026.9.18, pm-ops 2026.9.13 -> 2026.9.18 (package.json and package-lock.json verified). Launcher replaces scripts/prepare-merge-driver.mjs; removed: scripts/prepare-merge-driver.mjs. git config lists the pm merge drivers after npm ci; pm health --strict-exit --require-merge-drivers exits 0; release:check exits 0."}],"before_hash":"cdd9d0c975e19c5ec02ce2cef196405ee7e113fb54166992f3783e968aa21e69","after_hash":"8ec739fa8f9f75a059c190b204f657b24fffbcfa1f2b4cebf80db56cd2af8ad7","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":"cee37544748ada17a9af48df6786e3599ae18dde4ebdc69376c680a44682b08b"} +{"hash_algorithm":"sha256","ts":"2026-09-22T05:59:10.430Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T05:59:10.430Z"},{"op":"add","path":"/metadata/resolution","value":"Pinned the fleet toolchain and moved the prepare hook onto pm-ops/merge-driver; CI now requires the drivers."},{"op":"add","path":"/metadata/expected_result","value":"A fresh clone registers the merge drivers from npm ci, and CI fails if they are missing."},{"op":"add","path":"/metadata/actual_result","value":"Pins: @unbrained/pm-cli 2026.9.17 -> 2026.9.21, pm-changelog 2026.9.16 -> 2026.9.18, pm-ops 2026.9.13 -> 2026.9.18 (package.json and package-lock.json verified). Launcher replaces scripts/prepare-merge-driver.mjs; removed: scripts/prepare-merge-driver.mjs. git config lists the pm merge drivers after npm ci; pm health --strict-exit --require-merge-drivers exits 0; release:check exits 0."}],"before_hash":"8ec739fa8f9f75a059c190b204f657b24fffbcfa1f2b4cebf80db56cd2af8ad7","after_hash":"882cdb81d1ffcf510e45eeb64a6aad5ccf813bba4c4ce2c0593849fac651b809","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":"db702a52647d0e4b38566ab733805e8288ec288959dfd74708d4b0569d34c066"} +{"hash_algorithm":"sha256","ts":"2026-09-22T05:59:13.728Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T05:59:13.728Z"}],"before_hash":"882cdb81d1ffcf510e45eeb64a6aad5ccf813bba4c4ce2c0593849fac651b809","after_hash":"f0842563583dcd49aef3b0c38c71fb8276515119e5e313c0863750e60859d904","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":"886898c94834913f50a550e98d5eb5b7a56136f0fda044df7400a87c8988f25f"} diff --git a/.agents/pm/history/pm-slack-x0wg.jsonl b/.agents/pm/history/pm-slack-x0wg.jsonl new file mode 100644 index 0000000..05ce511 --- /dev/null +++ b/.agents/pm/history/pm-slack-x0wg.jsonl @@ -0,0 +1,8 @@ +{"hash_algorithm":"sha256","ts":"2026-09-22T05:58:12.009Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"create","patch":[{"op":"add","path":"/metadata/id","value":"pm-slack-x0wg"},{"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. Three changes to this repository's own release.yml: a 10-minute npm visibility window with --prefer-online and an honest never-visible message (attestation refusal unchanged); the GitHub Release depends only on the publish and tag-push outcomes, bun gets a 21-attempt 10-minute window with a final re-check and fails the job visibly through a gate step; a continue-on-error backfill step creates missing Releases for attested fleet-shaped tags (vYYYY.MM.DD[-N]) with tag-scoped pm-changelog notes. Proven by a stub harness executing the changed run blocks: 7/7 scenarios, and the unpatched file fails the fix scenarios."},{"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-22T05:58:12.009Z"},{"op":"add","path":"/metadata/updated_at","value":"2026-09-22T05:58:12.009Z"},{"op":"add","path":"/metadata/author","value":"fleet-wave-script"}],"before_hash":"3cc22dff72be7b14824654a7a64ea62b04799939b2fee54c1b5f52ca60bf6df0","after_hash":"a1b56f124627d535a694cb30210d2f0768a9253962a9a349246dc3867191da9f","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":"297ce99f3eb1b336126d3edd02091eebbb222af95a5a968ce3dbb656d1525b8a"} +{"hash_algorithm":"sha256","ts":"2026-09-22T05:58:14.828Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"claim","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T05:58:14.828Z"},{"op":"add","path":"/metadata/assignee","value":"fleet-wave-script"},{"op":"add","path":"/metadata/claim_principal","value":"fleet-wave-script"}],"before_hash":"a1b56f124627d535a694cb30210d2f0768a9253962a9a349246dc3867191da9f","after_hash":"f2ef7f252623a1688204301d93a1669ba8ef5af3bd09fb82bb660fe715216fa2","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":"314dd3222c3411c17eb98a6dacbaf8aca941471c31ddc7820ef70e99aadc5674"} +{"hash_algorithm":"sha256","ts":"2026-09-22T05:58:16.073Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T05:58:16.073Z"},{"op":"replace","path":"/metadata/status","value":"in_progress"}],"before_hash":"f2ef7f252623a1688204301d93a1669ba8ef5af3bd09fb82bb660fe715216fa2","after_hash":"c82d37e9a540801b69c352e21521ff82a94b0bf3612418177ad0bf03d1f39f65","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":"66714fd94d43dbff636028184a134058e160ba837e683fe11a2586ac7b837083"} +{"hash_algorithm":"sha256","ts":"2026-09-22T05:58:17.668Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T05:58:17.668Z"},{"op":"add","path":"/metadata/files","value":[{"path":".github/workflows/release.yml","scope":"project"}]}],"before_hash":"c82d37e9a540801b69c352e21521ff82a94b0bf3612418177ad0bf03d1f39f65","after_hash":"c8e3f2b96842beddc17e662420e77386520fd9febf58fe3f5812f60a7b6289da","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":"fc4b2909991040c21bd48dbc4abb5d948f5e20702dc7ada5a97556807c45f0a2"} +{"hash_algorithm":"sha256","ts":"2026-09-22T05:58:22.536Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"tests_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T05:58:22.536Z"},{"op":"add","path":"/metadata/tests","value":[{"command":"npm run release:check","scope":"project","provenance":{"author":"fleet-wave-script","created_at":"2026-09-22T05:58:22.495Z","source_kind":"local_mutation","source_ref":"pm-cli-2026-9-21-canonical-merge-driver-release-window"}}]}],"before_hash":"c8e3f2b96842beddc17e662420e77386520fd9febf58fe3f5812f60a7b6289da","after_hash":"dd339e74500a53d01361f96cc15ef429490bf4fc5e5935d0c6ff50c424ac0a2e","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":"62356bff887de5d57ecdcc760cb395312f64a348e60012a3bbbbe678e40a0e52"} +{"hash_algorithm":"sha256","ts":"2026-09-22T05:59:11.763Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"close","patch":[{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T05:59:11.763Z"},{"op":"replace","path":"/metadata/status","value":"closed"},{"op":"add","path":"/metadata/closed_at","value":"2026-09-22T05:59:11.720Z"},{"op":"add","path":"/metadata/completed_at","value":"2026-09-22T05:59:11.720Z"},{"op":"add","path":"/metadata/close_reason","value":"Applied by the anchored applier to this repository's own release.yml (matched, identity asserted: the file names only unbraind/pm-slack). Harness 7/7 on the same patch; release:check exits 0."}],"before_hash":"dd339e74500a53d01361f96cc15ef429490bf4fc5e5935d0c6ff50c424ac0a2e","after_hash":"4493ea6a65e920b18860b016d41578de599b839ed06cf5f6628f41d460f18c4f","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":"5f6f369fad132ae68c856e723cf895a95d67758ab3b1d35bc3fa148901d7b662"} +{"hash_algorithm":"sha256","ts":"2026-09-22T05:59:12.877Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T05:59:12.877Z"},{"op":"add","path":"/metadata/resolution","value":"Widened the npm visibility window, decoupled the GitHub Release from bun mirror lag with a visible gate, and added a best-effort backfill."},{"op":"add","path":"/metadata/expected_result","value":"A late-visible publish is reconciled, the Release is created whenever publish and tag succeed, and missing Releases for attested tags are backfilled."},{"op":"add","path":"/metadata/actual_result","value":"Applied by the anchored applier to this repository's own release.yml (matched, identity asserted: the file names only unbraind/pm-slack). Harness 7/7 on the same patch; release:check exits 0."}],"before_hash":"4493ea6a65e920b18860b016d41578de599b839ed06cf5f6628f41d460f18c4f","after_hash":"5ab6a2c8ec395b4e70ec55bfdaf93ca923b0908547455216bf16c8bd9a6a9de7","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":"c3836ca758ea050d5e67760bfa75a79b462bfd10bc4b710f0a5b5f8b168b3fb7"} +{"hash_algorithm":"sha256","ts":"2026-09-22T05:59:14.743Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T05:59:14.743Z"}],"before_hash":"5ab6a2c8ec395b4e70ec55bfdaf93ca923b0908547455216bf16c8bd9a6a9de7","after_hash":"a039af4a32aff0d38940c83682aeb39f3760d1c250191e7932a9e22faf0c4417","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":"7c16834254beb6c13d5f554e626d40a7244c7d0fe262c1162386291d91360e34"} diff --git a/.agents/pm/issues/pm-slack-x0wg.toon b/.agents/pm/issues/pm-slack-x0wg.toon new file mode 100644 index 0000000..4918aa8 --- /dev/null +++ b/.agents/pm/issues/pm-slack-x0wg.toon @@ -0,0 +1,21 @@ +id: pm-slack-x0wg +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. Three changes to this repository's own release.yml: a 10-minute npm visibility window with --prefer-online and an honest never-visible message (attestation refusal unchanged); the GitHub Release depends only on the publish and tag-push outcomes, bun gets a 21-attempt 10-minute window with a final re-check and fails the job visibly through a gate step; a continue-on-error backfill step creates missing Releases for attested fleet-shaped tags (vYYYY.MM.DD[-N]) with tag-scoped pm-changelog notes. Proven by a stub harness executing the changed run blocks: 7/7 scenarios, and the unpatched file fails the fix scenarios." +type: Issue +status: closed +priority: 1 +tags[2]: release,reliability +created_at: "2026-09-22T05:58:12.009Z" +updated_at: "2026-09-22T05:59:14.743Z" +closed_at: "2026-09-22T05:59:11.720Z" +completed_at: "2026-09-22T05:59:11.720Z" +author: fleet-wave-script +resolution: "Widened the npm visibility window, decoupled the GitHub Release from bun mirror lag with a visible gate, and added a best-effort backfill." +expected_result: "A late-visible publish is reconciled, the Release is created whenever publish and tag succeed, and missing Releases for attested tags are backfilled." +actual_result: "Applied by the anchored applier to this repository's own release.yml (matched, identity asserted: the file names only unbraind/pm-slack). Harness 7/7 on the same patch; 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-22T05:58:22.495Z",local_mutation,pm-cli-2026-9-21-canonical-merge-driver-release-window +close_reason: "Applied by the anchored applier to this repository's own release.yml (matched, identity asserted: the file names only unbraind/pm-slack). Harness 7/7 on the same patch; release:check exits 0." +body: "" diff --git a/.agents/pm/tasks/pm-slack-h3ba.toon b/.agents/pm/tasks/pm-slack-h3ba.toon new file mode 100644 index 0000000..a1ee7a2 --- /dev/null +++ b/.agents/pm/tasks/pm-slack-h3ba.toon @@ -0,0 +1,26 @@ +id: pm-slack-h3ba +title: Certify pm CLI 2026.9.21 and install merge drivers through the canonical pm-ops launcher +description: "Fleet wave 2026-09-22 (companion epic pm-cli-website-5s6z). Pins @unbrained/pm-cli 2026.9.21, pm-ops 2026.9.18 and pm-changelog 2026.9.18 exactly. The prepare hook becomes a thin launcher over pm-ops/merge-driver instead of a vendored implementation, and CI installs the drivers before pm health --strict-exit --require-merge-drivers, so a fresh clone that lacks them fails the gate instead of silently hard-conflicting tracker files." +type: Task +status: closed +priority: 1 +tags[4]: certify,merge-driver,multi-agent,pm-cli-2026.9.21 +created_at: "2026-09-22T05:58:10.946Z" +updated_at: "2026-09-22T05:59:13.728Z" +closed_at: "2026-09-22T05:59:08.862Z" +completed_at: "2026-09-22T05:59:08.862Z" +author: fleet-wave-script +acceptance_criteria: "package.json and package-lock.json pin pm-cli 2026.9.21, pm-ops 2026.9.18, pm-changelog 2026.9.18; scripts/prepare-merge-driver.ts is the thin pm-ops/merge-driver launcher; no vendored implementation remains; CI runs pm merge install before pm health --strict-exit --require-merge-drivers" +resolution: Pinned the fleet toolchain and moved the prepare hook onto pm-ops/merge-driver; CI now requires the drivers. +expected_result: "A fresh clone registers the merge drivers from npm ci, and CI fails if they are missing." +actual_result: "Pins: @unbrained/pm-cli 2026.9.17 -> 2026.9.21, pm-changelog 2026.9.16 -> 2026.9.18, pm-ops 2026.9.13 -> 2026.9.18 (package.json and package-lock.json verified). Launcher replaces scripts/prepare-merge-driver.mjs; removed: scripts/prepare-merge-driver.mjs. git config lists the pm merge drivers after npm ci; pm health --strict-exit --require-merge-drivers exits 0; release:check exits 0." +files[5]{path,scope}: + .github/workflows/ci.yml,project + package-lock.json,project + package.json,project + README.md,project + scripts/prepare-merge-driver.ts,project +tests[1]{command,scope,provenance{author,created_at,source_kind,source_ref}}: + "npm run release:check",project,fleet-wave-script,"2026-09-22T05:58:21.551Z",local_mutation,pm-cli-2026-9-21-canonical-merge-driver-release-window +close_reason: "Pins: @unbrained/pm-cli 2026.9.17 -> 2026.9.21, pm-changelog 2026.9.16 -> 2026.9.18, pm-ops 2026.9.13 -> 2026.9.18 (package.json and package-lock.json verified). Launcher replaces scripts/prepare-merge-driver.mjs; removed: scripts/prepare-merge-driver.mjs. git config lists the pm merge drivers after npm ci; pm health --strict-exit --require-merge-drivers exits 0; release:check exits 0." +body: "" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1e37571..355ad1f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,6 +37,9 @@ jobs: - name: Install dependencies run: npm ci + - name: Install pm merge drivers + run: ./node_modules/.bin/pm merge install + - name: Verify tracked pm project health shell: bash run: | @@ -53,7 +56,7 @@ jobs: # # `--strict-exit` is load-bearing: non-strict health may report findings while # still exiting successfully. - ./node_modules/.bin/pm health --strict-exit + ./node_modules/.bin/pm health --strict-exit --require-merge-drivers - name: Type check run: npm run typecheck diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2f91535..94d6221 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -72,6 +72,104 @@ jobs: - name: Install dependencies run: npm ci + # Resume: a release can land on npm and as a pushed tag but still miss + # its GitHub Release (the bun-verification ordering this file used to + # have did exactly that to pm-linear v2026.09.18), and the next day's + # run releases the NEXT version and never goes back, so the gap never + # closes on its own. Walk every release tag on origin and close each + # gap BEFORE deciding today's release, using the same rule the publish + # path uses to reconcile: a Release is created only when the tag's npm + # version is visible WITH attestations; a missing or unattested + # coordinate is skipped, never released around. Notes come from the + # same pm-changelog invocation the release-notes step below uses, + # retargeted at the tag's own version (--release-version, not + # package.json, which by now holds a newer version) and scoped to that + # tag's range (--since-previous-tag --until-release-tag). Best-effort + # on purpose: a backfill failure is reported but must never block + # today's release, or one gap would be traded for a new one. + - name: Backfill missing GitHub releases + # Best-effort by construction, not only by intent: an unexpected + # failure here (a git or gh outage) is shown on the step but must not + # cancel today's release, or one gap would be traded for another. + continue-on-error: true + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: | + set -euo pipefail + pkg_name="$(node -p "require('./package.json').name")" + repo_name="${GITHUB_REPOSITORY#*/}" + git fetch origin --force --tags + # Fleet CalVer arithmetic, shared with `Decide release`: leading + # zeros off, an -N suffix preserved, so v2026.09.18-2 addresses npm + # version 2026.9.18-2. + tag_to_npm_version() { + local core year tail month day_and_suffix day suffix + core="${1#v}" + year="${core%%.*}" + tail="${core#*.}" + month="${tail%%.*}" + day_and_suffix="${tail#*.}" + day="${day_and_suffix%%-*}" + suffix="${day_and_suffix#"$day"}" + printf '%s.%s.%s%s\n' "$((10#$year))" "$((10#$month))" "$((10#$day))" "$suffix" + } + # The item URL is derived rather than hardcoded so this block is + # byte-identical across the fleet; for this repository it expands + # to the same URL the release-notes step below hardcodes. + backfill_common=( + --pm-root .agents/pm + --date-from-version + --item-url-base "https://github.com/${GITHUB_REPOSITORY}/blob/main/.agents/pm" + --respect-item-release + --pm-bin ./node_modules/.bin/pm + --pm-arg=--output-budget + --pm-arg=unbounded + --pm-arg=--output-limit + --pm-arg=unbounded + ) + while IFS= read -r release_tag; do + [[ -n "${release_tag}" ]] || continue + if gh release view "${release_tag}" > /dev/null 2>&1; then + continue + fi + npm_version="$(tag_to_npm_version "${release_tag}")" + # Freshness matters here exactly as in the publish reconcile: + # --prefer-online so a cached answer cannot invent a Release. + attestations="$(npm view "${pkg_name}@${npm_version}" dist.attestations --prefer-online --json 2>/dev/null || true)" + if [[ -z "${attestations}" || "${attestations}" == "null" || "${attestations}" == "{}" || "${attestations}" == "[]" ]]; then + echo "::warning::${release_tag} has no GitHub Release, but ${pkg_name}@${npm_version} is not confirmed on npm with attestations; not backfilling." + continue + fi + echo "Backfilling the missing GitHub Release for ${release_tag} (${pkg_name}@${npm_version} is published and attested)." + notes="$(mktemp)" + if ! npx pm-changelog "${backfill_common[@]}" \ + --release-version "${npm_version}" \ + --since-previous-tag --until-release-tag \ + --stdout > "${notes}"; then + echo "::warning::Could not generate release notes for ${release_tag}; leaving the gap for a maintainer and continuing today's release." + rm -f "${notes}" + continue + fi + if gh release create "${release_tag}" \ + --title "${repo_name} ${release_tag}" \ + --notes-file "${notes}" \ + --verify-tag; then + echo "Backfilled the GitHub Release for ${release_tag}." + else + echo "::warning::Could not create the GitHub Release for ${release_tag}; leaving the gap for a maintainer and continuing today's release." + fi + rm -f "${notes}" + # Only the fleet's release tag shapes: vYYYY.MM.DD and its numeric + # same-day suffix (-2, -3, ...). A glob would also admit + # v2026.09.18-rc.1 (an unintended prerelease backfill) or + # v2026.09.18foo, whose day part breaks the 10# arithmetic. + done < <( + git tag -l 'v*' --sort=-creatordate | + grep -E '^v[0-9]{4}\.[0-9]{2}\.[0-9]{2}(-[0-9]+)?$' || + true + ) + - name: Decide release id: decide shell: bash @@ -562,6 +660,7 @@ jobs: # publishing must be configured for this package on npmjs.com against # unbraind/pm-slack and this workflow filename, or publish fails closed. - name: Publish npm package + id: publish if: steps.decide.outputs.should_release == 'true' shell: bash env: @@ -627,11 +726,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 @@ -654,7 +753,9 @@ jobs: 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 } @@ -698,24 +799,32 @@ jobs: # 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. @@ -726,6 +835,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: @@ -764,6 +874,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 }} @@ -778,31 +889,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 e1b6603..6f2df97 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-slack-x0wg](https://github.com/unbraind/pm-slack/blob/main/.agents/pm/issues/pm-slack-x0wg.toon)) + +### Other + +- Certify pm CLI 2026.9.21 and install merge drivers through the canonical pm-ops launcher ([pm-slack-h3ba](https://github.com/unbraind/pm-slack/blob/main/.agents/pm/tasks/pm-slack-h3ba.toon)) + ## 2026.9.18 - 2026-09-18 ### Other diff --git a/README.md b/README.md index e2860e5..0c147d0 100644 --- a/README.md +++ b/README.md @@ -371,7 +371,7 @@ This package is release-ready for GitHub, npm, and Bun-compatible installs. CI r 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 portable Node guard, `scripts/prepare-merge-driver.mjs`: it runs +per-clone Git config; `npm install` / `npm ci` wires them automatically via the `prepare` script (a portable Node guard, `scripts/prepare-merge-driver.ts`, a thin launcher over `pm-ops/merge-driver`: it runs `pm merge install` only when the `pm` CLI is on `PATH`, and no-ops cleanly otherwise so production / `--omit=dev` installs are not broken; being Node-based it behaves identically on POSIX shells and Windows `cmd.exe`). To (re)run manually: `npm run merge:install`. diff --git a/package-lock.json b/package-lock.json index b6475db..16436ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,9 +10,9 @@ "license": "MIT", "devDependencies": { "@types/node": "^26.1.1", - "@unbrained/pm-cli": "2026.9.17", - "pm-changelog": "2026.9.16", - "pm-ops": "2026.9.13", + "@unbrained/pm-cli": "2026.9.21", + "pm-changelog": "2026.9.18", + "pm-ops": "2026.9.18", "typescript": "^7.0.2" }, "engines": { @@ -205,9 +205,9 @@ } }, "node_modules/@sentry/core": { - "version": "10.74.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.74.0.tgz", - "integrity": "sha512-u9rY8vcZfktccwm6LznfCZlqP5C9A+p76r4/pFS1grqpuTO0m21Cl8rosnlESrDGP/Xd9tfr91rWYk0jPH8jeQ==", + "version": "10.75.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.75.0.tgz", + "integrity": "sha512-5wDQpQqjJ6RHdPR+z6Q+47XlwoxRKBv0yyB0LKHqL/1azPOQbR+nllyLmmQDcoJpaksuLSmLA1q6hFX7gjDT2w==", "dev": true, "license": "MIT", "dependencies": { @@ -218,9 +218,9 @@ } }, "node_modules/@sentry/node": { - "version": "10.74.0", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-10.74.0.tgz", - "integrity": "sha512-u1wsarTOWHn9CCev81Da5T4IQHZgdcosXRfX2+4DMII/lVJMBYesKixTQuMwXexjVG2+pkf16zTmgjZDa+75jA==", + "version": "10.75.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-10.75.0.tgz", + "integrity": "sha512-XdYW+SEiscQnuugh1hMldfnHurmleKSTSDqgfro6Y1yd7s0r2csnZ5/SEYxIeL4lSl1QXg1lIA1pBmAXjcdnKA==", "dev": true, "license": "MIT", "dependencies": { @@ -228,10 +228,10 @@ "@opentelemetry/instrumentation": "^0.220.0", "@opentelemetry/sdk-trace-base": "^2.9.0", "@sentry/conventions": "^0.16.0", - "@sentry/core": "10.74.0", - "@sentry/node-core": "10.74.0", - "@sentry/opentelemetry": "10.74.0", - "@sentry/server-utils": "10.74.0", + "@sentry/core": "10.75.0", + "@sentry/node-core": "10.75.0", + "@sentry/opentelemetry": "10.75.0", + "@sentry/server-utils": "10.75.0", "import-in-the-middle": "^3.0.0" }, "engines": { @@ -239,15 +239,15 @@ } }, "node_modules/@sentry/node-core": { - "version": "10.74.0", - "resolved": "https://registry.npmjs.org/@sentry/node-core/-/node-core-10.74.0.tgz", - "integrity": "sha512-btgZXcGmkOGgojbxHo/gfGiyqYpzC9E8zVR78Q3MtM6Xnemk9gwJiQlhNmEX/FM+C36WBRPZrdcZcnMaJhbfJw==", + "version": "10.75.0", + "resolved": "https://registry.npmjs.org/@sentry/node-core/-/node-core-10.75.0.tgz", + "integrity": "sha512-+E3KSX1oMqhpWQ23hj6NblIVUzLy3T2oceU7DfMX1s1ar+npaQZxR5K0/cMGcQgS1LLm8Jqwqo/lrZlKTCDm5A==", "dev": true, "license": "MIT", "dependencies": { "@sentry/conventions": "^0.16.0", - "@sentry/core": "10.74.0", - "@sentry/opentelemetry": "10.74.0", + "@sentry/core": "10.75.0", + "@sentry/opentelemetry": "10.75.0", "import-in-the-middle": "^3.0.0" }, "engines": { @@ -279,14 +279,14 @@ } }, "node_modules/@sentry/opentelemetry": { - "version": "10.74.0", - "resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-10.74.0.tgz", - "integrity": "sha512-ua5mt0NDBfye+/ACKjAw9Ad2i+y42lOLvhxXZepZXjszblMz80MEsIZflMB7uLZUCTNH7MbZN8tlzCy8KsJKmQ==", + "version": "10.75.0", + "resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-10.75.0.tgz", + "integrity": "sha512-reJoMtuHMuaiztoDVoaeitc22eHlTAvz2qpFhibWyzukJlF8oXB+o8EvDN9mmDDMedU89c//cXRcnPBlJvUxcw==", "dev": true, "license": "MIT", "dependencies": { "@sentry/conventions": "^0.16.0", - "@sentry/core": "10.74.0" + "@sentry/core": "10.75.0" }, "engines": { "node": ">=18" @@ -298,14 +298,14 @@ } }, "node_modules/@sentry/server-utils": { - "version": "10.74.0", - "resolved": "https://registry.npmjs.org/@sentry/server-utils/-/server-utils-10.74.0.tgz", - "integrity": "sha512-AHmPIGE8yVRyywpZRhAkm/H0cHTgsQSPhFZFbaeQLcYHFE/eOP9bAMh8Nj/Apr7WMagFXYHd04tR/63BxtLgKw==", + "version": "10.75.0", + "resolved": "https://registry.npmjs.org/@sentry/server-utils/-/server-utils-10.75.0.tgz", + "integrity": "sha512-7fIa9vFGNzB13hmxl8Sip3xImSkqpc4wETzuQ7CLzVOwPvwI6y/gVEm0pxRhtJSq8SzfOp26eJWl80u8v78Osg==", "dev": true, "license": "MIT", "dependencies": { "@sentry/conventions": "^0.16.0", - "@sentry/core": "10.74.0" + "@sentry/core": "10.75.0" }, "engines": { "node": ">=18" @@ -669,13 +669,13 @@ } }, "node_modules/@unbrained/pm-cli": { - "version": "2026.9.17", - "resolved": "https://registry.npmjs.org/@unbrained/pm-cli/-/pm-cli-2026.9.17.tgz", - "integrity": "sha512-o8fTLNJmTJFSH56g3bPdlknY0DR+X/THI1QspBi35vbFHYkNO3GdbsI5A/U1r5em5Ra7iX44d7t8Dz6IKhjL7w==", + "version": "2026.9.21", + "resolved": "https://registry.npmjs.org/@unbrained/pm-cli/-/pm-cli-2026.9.21.tgz", + "integrity": "sha512-HPgGm/pU81Avtty9lVYqYh0jxKzNyQjHnvwQrRjSYNHaVQeJ3xNM/VV9i0CCPL0zLxRjdriYj/PnNDIMC2Ur2Q==", "dev": true, "license": "MIT", "dependencies": { - "@sentry/node": "10.74.0", + "@sentry/node": "10.75.0", "@toon-format/toon": "^4.1.1", "@types/node": ">=22", "commander": "^15.0.0", @@ -982,9 +982,9 @@ } }, "node_modules/pm-changelog": { - "version": "2026.9.16", - "resolved": "https://registry.npmjs.org/pm-changelog/-/pm-changelog-2026.9.16.tgz", - "integrity": "sha512-Cm6fQxDwjsl6x9Y6tu0AC99N6Cq/oyhfN3NI/qshHB2sRGtl00k63NrcRdqBjG9LBkX0o35d9nWMVyyA5Pb0gg==", + "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==", "dev": true, "license": "MIT", "bin": { @@ -998,9 +998,9 @@ } }, "node_modules/pm-ops": { - "version": "2026.9.13", - "resolved": "https://registry.npmjs.org/pm-ops/-/pm-ops-2026.9.13.tgz", - "integrity": "sha512-H/6pxsBXBpxH267dlYSpZY+bjDYR+JtZtDTc9eznZiFHI4dt07Qt142zd614jby+Ep/MXfs/+u7yo8GJEaTz6g==", + "version": "2026.9.18", + "resolved": "https://registry.npmjs.org/pm-ops/-/pm-ops-2026.9.18.tgz", + "integrity": "sha512-x4KWL0Y9y6Sz2Hw9LDJmZPoZmbruKJ1x9Z9QCq2/W/yAG5Lujl5HiyrE9Er6Ne7gKFWJp3kxywYqUWDnLHEyMQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1011,7 +1011,29 @@ "node": ">=22.18.0" }, "peerDependencies": { - "@unbrained/pm-cli": ">=2026.8.20" + "@babel/eslint-parser": "^8.0.5", + "@babel/plugin-syntax-typescript": "^8.0.3", + "@unbrained/pm-cli": ">=2026.8.20", + "eslint": "^10.10.0", + "fast-glob": "^3.3.3", + "jscpd": "^4.3.0" + }, + "peerDependenciesMeta": { + "@babel/eslint-parser": { + "optional": true + }, + "@babel/plugin-syntax-typescript": { + "optional": true + }, + "eslint": { + "optional": true + }, + "fast-glob": { + "optional": true + }, + "jscpd": { + "optional": true + } } }, "node_modules/proc-log": { diff --git a/package.json b/package.json index c0508cc..fc6fe84 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "release:check": "npm run typecheck && npm run build && npm run docstring && npm run coverage && npm run audit:prod && npm run pack:dry-run && npm run changelog:check && npm run verify:release-publish-attestation && npm run verify:release-changelog-date", "prepublishOnly": "npm run release:check", "release:notes": "pm-changelog --pm-root .agents/pm --stdout --since-previous-tag --until-release-tag --release-version-from-package --date-from-version --item-url-base https://github.com/unbraind/pm-slack/blob/main/.agents/pm --respect-item-release --pm-bin ./node_modules/.bin/pm --github-step-summary --pm-arg=--output-budget --pm-arg=unbounded --pm-arg=--output-limit --pm-arg=unbounded", - "prepare": "node scripts/prepare-merge-driver.mjs", + "prepare": "node scripts/prepare-merge-driver.ts", "merge:install": "pm merge install", "coverage": "npm run build && npm run build:test && node scripts/coverage-gate.ts", "verify:release-publish-attestation": "node scripts/verify-release-publish-attestation.ts", @@ -47,9 +47,9 @@ }, "devDependencies": { "@types/node": "^26.1.1", - "@unbrained/pm-cli": "2026.9.17", - "pm-changelog": "2026.9.16", - "pm-ops": "2026.9.13", + "@unbrained/pm-cli": "2026.9.21", + "pm-changelog": "2026.9.18", + "pm-ops": "2026.9.18", "typescript": "^7.0.2" }, "engines": { diff --git a/scripts/prepare-merge-driver.mjs b/scripts/prepare-merge-driver.mjs deleted file mode 100644 index 68caddb..0000000 --- a/scripts/prepare-merge-driver.mjs +++ /dev/null @@ -1,67 +0,0 @@ -import { execSync } from 'node:child_process'; -import { accessSync, statSync, constants } from 'node:fs'; -import { join, delimiter } from 'node:path'; - -// Wire pm-cli's field-aware Git merge drivers into this clone's local Git config on -// install/clone, but only when the `pm` CLI is actually available. Implemented in Node -// (not a POSIX `if ...; then ...; fi` shell guard) so it runs identically on POSIX shells -// and Windows cmd.exe (npm's default script shell) with no shell-operator parsing. - -const isWindows = process.platform === 'win32'; - -/** A PATH candidate counts only if it is a regular, executable file — mirroring how a - * shell resolves a bare command name. Rejects directories and (on POSIX) non-executable - * files, so a stray `pm` dir/data file never makes `execSync` fail the whole install. */ -function isExecutableFile(p) { - try { - if (!statSync(p).isFile()) return false; - } catch { - return false; // ENOENT / not accessible - } - if (isWindows) return true; // Windows keys executability off PATHEXT, not a mode bit - try { - accessSync(p, constants.X_OK); - return true; - } catch { - return false; - } -} - -/** Is the `pm` executable resolvable on PATH? Resolved by inspecting PATH directly - * (never by executing `pm`), so a present-but-broken CLI is NOT mistaken for "absent": - * absence => silent skip, presence => run fail-loud below. npm prepends - * `node_modules/.bin` to PATH for lifecycle scripts, so a devDep-installed pm is found. - * PATH parsing mirrors shell semantics: an empty POSIX entry means the current - * directory, and Windows entries may be wrapped in double quotes. */ -function pmOnPath() { - const dirs = (process.env.PATH || '') - .split(delimiter) - .map((dir) => { - let d = dir; - if (isWindows && d.length >= 2 && d.startsWith('"') && d.endsWith('"')) { - d = d.slice(1, -1); - } - // Empty component: current dir on POSIX; ignored on Windows. - return d === '' ? (isWindows ? '' : '.') : d; - }) - .filter((d) => d !== ''); - const exts = isWindows - ? (process.env.PATHEXT || '.COM;.EXE;.BAT;.CMD').split(';').map((e) => e.trim()).filter(Boolean) - : ['']; - for (const dir of dirs) { - for (const ext of exts) { - if (isExecutableFile(join(dir, `pm${ext}`))) return true; - } - } - return false; -} - -if (!pmOnPath()) { - // `pm` is not installed (e.g. a production / `--omit=dev` install, or a consumer - // machine without the CLI) — skip merge-driver wiring silently, don't fail install. - process.exit(0); -} - -// `pm` IS present: wire the drivers. If this genuinely fails (e.g. a broken or -// incompatible CLI), surface it fail-loud (non-zero exit) rather than swallowing it. -execSync('pm merge install', { stdio: 'inherit' }); diff --git a/scripts/prepare-merge-driver.ts b/scripts/prepare-merge-driver.ts new file mode 100644 index 0000000..d513e7f --- /dev/null +++ b/scripts/prepare-merge-driver.ts @@ -0,0 +1,10 @@ +/** + * npm `prepare` hook that installs 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. + */ +import { runPrepareMergeDriver } from "pm-ops/merge-driver"; + +process.exitCode = runPrepareMergeDriver(); From edd602442e7b01bdec40c91b3506c9b3417927d2 Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Tue, 22 Sep 2026 08:10:46 +0200 Subject: [PATCH 2/6] State the real install contract of the canonical merge-driver launcher The prepare launcher statically imports pm-ops, a devDependency, so the README's promise that production / --omit=dev installs cannot break was only true for registry installs (npm never runs prepare for a registry tarball). A production install of a clone omits pm-ops as well and must pass --ignore-scripts, which is what this fleet's own Dockerfiles do. Raised by Greptile and Sourcery on pm-starter#113. The canonical guarded launcher is tracked as companion item pm-cli-website-xy19. --- .agents/pm/history/pm-slack-h3ba.jsonl | 1 + .agents/pm/tasks/pm-slack-h3ba.toon | 4 +++- README.md | 3 +-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.agents/pm/history/pm-slack-h3ba.jsonl b/.agents/pm/history/pm-slack-h3ba.jsonl index 2a95fa2..d697b0a 100644 --- a/.agents/pm/history/pm-slack-h3ba.jsonl +++ b/.agents/pm/history/pm-slack-h3ba.jsonl @@ -10,3 +10,4 @@ {"hash_algorithm":"sha256","ts":"2026-09-22T05:59:08.902Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"close","patch":[{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T05:59:08.902Z"},{"op":"replace","path":"/metadata/status","value":"closed"},{"op":"add","path":"/metadata/closed_at","value":"2026-09-22T05:59:08.862Z"},{"op":"add","path":"/metadata/completed_at","value":"2026-09-22T05:59:08.862Z"},{"op":"add","path":"/metadata/close_reason","value":"Pins: @unbrained/pm-cli 2026.9.17 -> 2026.9.21, pm-changelog 2026.9.16 -> 2026.9.18, pm-ops 2026.9.13 -> 2026.9.18 (package.json and package-lock.json verified). Launcher replaces scripts/prepare-merge-driver.mjs; removed: scripts/prepare-merge-driver.mjs. git config lists the pm merge drivers after npm ci; pm health --strict-exit --require-merge-drivers exits 0; release:check exits 0."}],"before_hash":"cdd9d0c975e19c5ec02ce2cef196405ee7e113fb54166992f3783e968aa21e69","after_hash":"8ec739fa8f9f75a059c190b204f657b24fffbcfa1f2b4cebf80db56cd2af8ad7","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":"cee37544748ada17a9af48df6786e3599ae18dde4ebdc69376c680a44682b08b"} {"hash_algorithm":"sha256","ts":"2026-09-22T05:59:10.430Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T05:59:10.430Z"},{"op":"add","path":"/metadata/resolution","value":"Pinned the fleet toolchain and moved the prepare hook onto pm-ops/merge-driver; CI now requires the drivers."},{"op":"add","path":"/metadata/expected_result","value":"A fresh clone registers the merge drivers from npm ci, and CI fails if they are missing."},{"op":"add","path":"/metadata/actual_result","value":"Pins: @unbrained/pm-cli 2026.9.17 -> 2026.9.21, pm-changelog 2026.9.16 -> 2026.9.18, pm-ops 2026.9.13 -> 2026.9.18 (package.json and package-lock.json verified). Launcher replaces scripts/prepare-merge-driver.mjs; removed: scripts/prepare-merge-driver.mjs. git config lists the pm merge drivers after npm ci; pm health --strict-exit --require-merge-drivers exits 0; release:check exits 0."}],"before_hash":"8ec739fa8f9f75a059c190b204f657b24fffbcfa1f2b4cebf80db56cd2af8ad7","after_hash":"882cdb81d1ffcf510e45eeb64a6aad5ccf813bba4c4ce2c0593849fac651b809","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":"db702a52647d0e4b38566ab733805e8288ec288959dfd74708d4b0569d34c066"} {"hash_algorithm":"sha256","ts":"2026-09-22T05:59:13.728Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T05:59:13.728Z"}],"before_hash":"882cdb81d1ffcf510e45eeb64a6aad5ccf813bba4c4ce2c0593849fac651b809","after_hash":"f0842563583dcd49aef3b0c38c71fb8276515119e5e313c0863750e60859d904","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":"886898c94834913f50a550e98d5eb5b7a56136f0fda044df7400a87c8988f25f"} +{"hash_algorithm":"sha256","ts":"2026-09-22T06:10:06.550Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"comment_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T06:10:06.550Z"},{"op":"add","path":"/metadata/comments","value":[{"created_at":"2026-09-22T06:10:06.550Z","author":"fleet-wave-script","text":"Review follow-up (Greptile P1 + Sourcery on pm-starter#113): the launcher statically imports the devDependency pm-ops, so the README's promise that production / --omit=dev installs cannot break held only for registry installs, which never run prepare. README now states the real contract: a production install of a clone must pass --ignore-scripts. Canonical guarded launcher tracked as companion item pm-cli-website-xy19."}]}],"before_hash":"f0842563583dcd49aef3b0c38c71fb8276515119e5e313c0863750e60859d904","after_hash":"15b819d83b9a0cef6377d9f991f198891ac5f675a98404190353d17738aabc0e","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":"f392dc6c30c15a22650d488807e19aa65547eb205bc2c3ddca70e08c82eabe40"} diff --git a/.agents/pm/tasks/pm-slack-h3ba.toon b/.agents/pm/tasks/pm-slack-h3ba.toon index a1ee7a2..babc64b 100644 --- a/.agents/pm/tasks/pm-slack-h3ba.toon +++ b/.agents/pm/tasks/pm-slack-h3ba.toon @@ -6,7 +6,7 @@ status: closed priority: 1 tags[4]: certify,merge-driver,multi-agent,pm-cli-2026.9.21 created_at: "2026-09-22T05:58:10.946Z" -updated_at: "2026-09-22T05:59:13.728Z" +updated_at: "2026-09-22T06:10:06.550Z" closed_at: "2026-09-22T05:59:08.862Z" completed_at: "2026-09-22T05:59:08.862Z" author: fleet-wave-script @@ -14,6 +14,8 @@ acceptance_criteria: "package.json and package-lock.json pin pm-cli 2026.9.21, p resolution: Pinned the fleet toolchain and moved the prepare hook onto pm-ops/merge-driver; CI now requires the drivers. expected_result: "A fresh clone registers the merge drivers from npm ci, and CI fails if they are missing." actual_result: "Pins: @unbrained/pm-cli 2026.9.17 -> 2026.9.21, pm-changelog 2026.9.16 -> 2026.9.18, pm-ops 2026.9.13 -> 2026.9.18 (package.json and package-lock.json verified). Launcher replaces scripts/prepare-merge-driver.mjs; removed: scripts/prepare-merge-driver.mjs. git config lists the pm merge drivers after npm ci; pm health --strict-exit --require-merge-drivers exits 0; release:check exits 0." +comments[1]{created_at,author,text}: + "2026-09-22T06:10:06.550Z",fleet-wave-script,"Review follow-up (Greptile P1 + Sourcery on pm-starter#113): the launcher statically imports the devDependency pm-ops, so the README's promise that production / --omit=dev installs cannot break held only for registry installs, which never run prepare. README now states the real contract: a production install of a clone must pass --ignore-scripts. Canonical guarded launcher tracked as companion item pm-cli-website-xy19." files[5]{path,scope}: .github/workflows/ci.yml,project package-lock.json,project diff --git a/README.md b/README.md index 0c147d0..ef41d9a 100644 --- a/README.md +++ b/README.md @@ -372,8 +372,7 @@ This repo tracks its project management in `.agents/pm/` and ships a committed ` 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 portable Node guard, `scripts/prepare-merge-driver.ts`, a thin launcher over `pm-ops/merge-driver`: it runs -`pm merge install` only when the `pm` CLI is on `PATH`, and no-ops cleanly otherwise so -production / `--omit=dev` installs are not broken; being Node-based it behaves identically +`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`; 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 From 9df292dc316baf20e059add1b0fd13e7738d1311 Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Tue, 22 Sep 2026 08:10:57 +0200 Subject: [PATCH 3/6] Let CI prove that npm ci's prepare hook installs the merge drivers CI ran an explicit pm merge install right before pm health --require-merge-drivers, so the gate only verified the step before it and would have passed with a broken prepare hook. Without that step, the health gate asserts what a fresh clone actually relies on: npm ci runs the prepare launcher, which installs the drivers through pm-ops/merge-driver. Verified on a fresh git clone: no merge.pm* keys before npm ci, all of them after, and pm health --strict-exit --require-merge-drivers exits 1 once they are removed. Raised by Greptile on pm-github#93. --- .agents/pm/history/pm-slack-h3ba.jsonl | 1 + .agents/pm/tasks/pm-slack-h3ba.toon | 5 +++-- .github/workflows/ci.yml | 3 --- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.agents/pm/history/pm-slack-h3ba.jsonl b/.agents/pm/history/pm-slack-h3ba.jsonl index d697b0a..6677541 100644 --- a/.agents/pm/history/pm-slack-h3ba.jsonl +++ b/.agents/pm/history/pm-slack-h3ba.jsonl @@ -11,3 +11,4 @@ {"hash_algorithm":"sha256","ts":"2026-09-22T05:59:10.430Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T05:59:10.430Z"},{"op":"add","path":"/metadata/resolution","value":"Pinned the fleet toolchain and moved the prepare hook onto pm-ops/merge-driver; CI now requires the drivers."},{"op":"add","path":"/metadata/expected_result","value":"A fresh clone registers the merge drivers from npm ci, and CI fails if they are missing."},{"op":"add","path":"/metadata/actual_result","value":"Pins: @unbrained/pm-cli 2026.9.17 -> 2026.9.21, pm-changelog 2026.9.16 -> 2026.9.18, pm-ops 2026.9.13 -> 2026.9.18 (package.json and package-lock.json verified). Launcher replaces scripts/prepare-merge-driver.mjs; removed: scripts/prepare-merge-driver.mjs. git config lists the pm merge drivers after npm ci; pm health --strict-exit --require-merge-drivers exits 0; release:check exits 0."}],"before_hash":"8ec739fa8f9f75a059c190b204f657b24fffbcfa1f2b4cebf80db56cd2af8ad7","after_hash":"882cdb81d1ffcf510e45eeb64a6aad5ccf813bba4c4ce2c0593849fac651b809","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":"db702a52647d0e4b38566ab733805e8288ec288959dfd74708d4b0569d34c066"} {"hash_algorithm":"sha256","ts":"2026-09-22T05:59:13.728Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T05:59:13.728Z"}],"before_hash":"882cdb81d1ffcf510e45eeb64a6aad5ccf813bba4c4ce2c0593849fac651b809","after_hash":"f0842563583dcd49aef3b0c38c71fb8276515119e5e313c0863750e60859d904","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":"886898c94834913f50a550e98d5eb5b7a56136f0fda044df7400a87c8988f25f"} {"hash_algorithm":"sha256","ts":"2026-09-22T06:10:06.550Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"comment_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T06:10:06.550Z"},{"op":"add","path":"/metadata/comments","value":[{"created_at":"2026-09-22T06:10:06.550Z","author":"fleet-wave-script","text":"Review follow-up (Greptile P1 + Sourcery on pm-starter#113): the launcher statically imports the devDependency pm-ops, so the README's promise that production / --omit=dev installs cannot break held only for registry installs, which never run prepare. README now states the real contract: a production install of a clone must pass --ignore-scripts. Canonical guarded launcher tracked as companion item pm-cli-website-xy19."}]}],"before_hash":"f0842563583dcd49aef3b0c38c71fb8276515119e5e313c0863750e60859d904","after_hash":"15b819d83b9a0cef6377d9f991f198891ac5f675a98404190353d17738aabc0e","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":"f392dc6c30c15a22650d488807e19aa65547eb205bc2c3ddca70e08c82eabe40"} +{"hash_algorithm":"sha256","ts":"2026-09-22T06:10:48.872Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/1","value":{"created_at":"2026-09-22T06:10:48.872Z","author":"fleet-wave-script","text":"Review follow-up (Greptile P2 on pm-github#93, 'driver check is tautological'): CI no longer runs an explicit pm merge install before pm health --require-merge-drivers, because that made the gate verify only the step before it and would have masked a broken prepare hook. Proven on a fresh git clone of a wave branch: 0 merge.pm* keys before npm ci, 10 after (the prepare launcher installs them), health exits 0; with every merge.pm* key removed, pm health --strict-exit --require-merge-drivers exits 1. So the CI gate now proves the mechanism a fresh clone actually relies on."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T06:10:48.872Z"}],"before_hash":"15b819d83b9a0cef6377d9f991f198891ac5f675a98404190353d17738aabc0e","after_hash":"fb92b7daa53b475e7b32be24950d0fea674293910e2553af2f4bbeb0c96c9849","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":"8ae9569f023409e2f09e716425f3e952e2be535c7ac54bcdddaf5a85ede0d9dc"} diff --git a/.agents/pm/tasks/pm-slack-h3ba.toon b/.agents/pm/tasks/pm-slack-h3ba.toon index babc64b..6e5e07d 100644 --- a/.agents/pm/tasks/pm-slack-h3ba.toon +++ b/.agents/pm/tasks/pm-slack-h3ba.toon @@ -6,7 +6,7 @@ status: closed priority: 1 tags[4]: certify,merge-driver,multi-agent,pm-cli-2026.9.21 created_at: "2026-09-22T05:58:10.946Z" -updated_at: "2026-09-22T06:10:06.550Z" +updated_at: "2026-09-22T06:10:48.872Z" closed_at: "2026-09-22T05:59:08.862Z" completed_at: "2026-09-22T05:59:08.862Z" author: fleet-wave-script @@ -14,8 +14,9 @@ acceptance_criteria: "package.json and package-lock.json pin pm-cli 2026.9.21, p resolution: Pinned the fleet toolchain and moved the prepare hook onto pm-ops/merge-driver; CI now requires the drivers. expected_result: "A fresh clone registers the merge drivers from npm ci, and CI fails if they are missing." actual_result: "Pins: @unbrained/pm-cli 2026.9.17 -> 2026.9.21, pm-changelog 2026.9.16 -> 2026.9.18, pm-ops 2026.9.13 -> 2026.9.18 (package.json and package-lock.json verified). Launcher replaces scripts/prepare-merge-driver.mjs; removed: scripts/prepare-merge-driver.mjs. git config lists the pm merge drivers after npm ci; pm health --strict-exit --require-merge-drivers exits 0; release:check exits 0." -comments[1]{created_at,author,text}: +comments[2]{created_at,author,text}: "2026-09-22T06:10:06.550Z",fleet-wave-script,"Review follow-up (Greptile P1 + Sourcery on pm-starter#113): the launcher statically imports the devDependency pm-ops, so the README's promise that production / --omit=dev installs cannot break held only for registry installs, which never run prepare. README now states the real contract: a production install of a clone must pass --ignore-scripts. Canonical guarded launcher tracked as companion item pm-cli-website-xy19." + "2026-09-22T06:10:48.872Z",fleet-wave-script,"Review follow-up (Greptile P2 on pm-github#93, 'driver check is tautological'): CI no longer runs an explicit pm merge install before pm health --require-merge-drivers, because that made the gate verify only the step before it and would have masked a broken prepare hook. Proven on a fresh git clone of a wave branch: 0 merge.pm* keys before npm ci, 10 after (the prepare launcher installs them), health exits 0; with every merge.pm* key removed, pm health --strict-exit --require-merge-drivers exits 1. So the CI gate now proves the mechanism a fresh clone actually relies on." files[5]{path,scope}: .github/workflows/ci.yml,project package-lock.json,project diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 355ad1f..d0619b0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,9 +37,6 @@ jobs: - name: Install dependencies run: npm ci - - name: Install pm merge drivers - run: ./node_modules/.bin/pm merge install - - name: Verify tracked pm project health shell: bash run: | From 13adbd4c77328c82edeb249f09a40f8819d7b092 Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Tue, 22 Sep 2026 08:17:36 +0200 Subject: [PATCH 4/6] Drop the release backfill step until it can verify provenance ancestry The backfill created a GitHub Release for any fleet-shaped tag whose npm version carried some attestation, without proving the tag's commit produced that artifact, so a stale, moved or hand-made tag could get a misleading Release. Comparing the attested commit with the tag commit is not the fix either: this fleet's provenance names the workflow trigger commit, measured as the tag's direct parent on three real releases. The correct check (same repository and workflow, attested commit an ancestor of the tag) belongs in the canonical pm-ops release verifier. The 10-minute npm visibility window and the Release decoupled from bun mirror lag remain; they fix the root causes. Raised by Greptile on pm-brief#124 and pm-linear#121. --- .agents/pm/history/pm-slack-x0wg.jsonl | 1 + .agents/pm/issues/pm-slack-x0wg.toon | 4 +- .github/workflows/release.yml | 98 -------------------------- 3 files changed, 4 insertions(+), 99 deletions(-) diff --git a/.agents/pm/history/pm-slack-x0wg.jsonl b/.agents/pm/history/pm-slack-x0wg.jsonl index 05ce511..0327c8c 100644 --- a/.agents/pm/history/pm-slack-x0wg.jsonl +++ b/.agents/pm/history/pm-slack-x0wg.jsonl @@ -6,3 +6,4 @@ {"hash_algorithm":"sha256","ts":"2026-09-22T05:59:11.763Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"close","patch":[{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T05:59:11.763Z"},{"op":"replace","path":"/metadata/status","value":"closed"},{"op":"add","path":"/metadata/closed_at","value":"2026-09-22T05:59:11.720Z"},{"op":"add","path":"/metadata/completed_at","value":"2026-09-22T05:59:11.720Z"},{"op":"add","path":"/metadata/close_reason","value":"Applied by the anchored applier to this repository's own release.yml (matched, identity asserted: the file names only unbraind/pm-slack). Harness 7/7 on the same patch; release:check exits 0."}],"before_hash":"dd339e74500a53d01361f96cc15ef429490bf4fc5e5935d0c6ff50c424ac0a2e","after_hash":"4493ea6a65e920b18860b016d41578de599b839ed06cf5f6628f41d460f18c4f","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":"5f6f369fad132ae68c856e723cf895a95d67758ab3b1d35bc3fa148901d7b662"} {"hash_algorithm":"sha256","ts":"2026-09-22T05:59:12.877Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T05:59:12.877Z"},{"op":"add","path":"/metadata/resolution","value":"Widened the npm visibility window, decoupled the GitHub Release from bun mirror lag with a visible gate, and added a best-effort backfill."},{"op":"add","path":"/metadata/expected_result","value":"A late-visible publish is reconciled, the Release is created whenever publish and tag succeed, and missing Releases for attested tags are backfilled."},{"op":"add","path":"/metadata/actual_result","value":"Applied by the anchored applier to this repository's own release.yml (matched, identity asserted: the file names only unbraind/pm-slack). Harness 7/7 on the same patch; release:check exits 0."}],"before_hash":"4493ea6a65e920b18860b016d41578de599b839ed06cf5f6628f41d460f18c4f","after_hash":"5ab6a2c8ec395b4e70ec55bfdaf93ca923b0908547455216bf16c8bd9a6a9de7","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":"c3836ca758ea050d5e67760bfa75a79b462bfd10bc4b710f0a5b5f8b168b3fb7"} {"hash_algorithm":"sha256","ts":"2026-09-22T05:59:14.743Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T05:59:14.743Z"}],"before_hash":"5ab6a2c8ec395b4e70ec55bfdaf93ca923b0908547455216bf16c8bd9a6a9de7","after_hash":"a039af4a32aff0d38940c83682aeb39f3760d1c250191e7932a9e22faf0c4417","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":"7c16834254beb6c13d5f554e626d40a7244c7d0fe262c1162386291d91360e34"} +{"hash_algorithm":"sha256","ts":"2026-09-22T06:17:22.875Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"comment_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T06:17:22.875Z"},{"op":"add","path":"/metadata/comments","value":[{"created_at":"2026-09-22T06:17:22.875Z","author":"fleet-wave-script","text":"Review follow-up (Greptile on pm-brief#124 and pm-linear#121): the backfill step is removed from this wave. It treated any npm attestation as proof that a tag's artifact was ours. The right check is not attested-commit equals tag-commit: this fleet's provenance records the workflow trigger commit (companion item pm-cli-website-nodo), measured as the tag's direct parent on pm-linear 2026.9.18, pm-github 2026.9.11 and pm-todos 2026.9.11. The check has to be repository plus workflow path plus ancestry, and it returns in the canonical pm-ops release verifier (companion pm-cli-website-mxrp). The other two changes, the 10-minute npm visibility window and the Release decoupled from bun lag, fix the root causes on their own. The three historical orphans were backfilled by hand on 2026-09-22."}]}],"before_hash":"a039af4a32aff0d38940c83682aeb39f3760d1c250191e7932a9e22faf0c4417","after_hash":"da59524189f7d55f7c1ec5c36f01e060a9e3743c06489cbd3c45cc4f0d144aa2","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":"1c96ff6bbcb2c22ca4c956eeb97be020ce4c2efb820772361de836b8d9c7e538"} diff --git a/.agents/pm/issues/pm-slack-x0wg.toon b/.agents/pm/issues/pm-slack-x0wg.toon index 4918aa8..a405e92 100644 --- a/.agents/pm/issues/pm-slack-x0wg.toon +++ b/.agents/pm/issues/pm-slack-x0wg.toon @@ -6,13 +6,15 @@ status: closed priority: 1 tags[2]: release,reliability created_at: "2026-09-22T05:58:12.009Z" -updated_at: "2026-09-22T05:59:14.743Z" +updated_at: "2026-09-22T06:17:22.875Z" closed_at: "2026-09-22T05:59:11.720Z" completed_at: "2026-09-22T05:59:11.720Z" author: fleet-wave-script resolution: "Widened the npm visibility window, decoupled the GitHub Release from bun mirror lag with a visible gate, and added a best-effort backfill." expected_result: "A late-visible publish is reconciled, the Release is created whenever publish and tag succeed, and missing Releases for attested tags are backfilled." actual_result: "Applied by the anchored applier to this repository's own release.yml (matched, identity asserted: the file names only unbraind/pm-slack). Harness 7/7 on the same patch; release:check exits 0." +comments[1]{created_at,author,text}: + "2026-09-22T06:17:22.875Z",fleet-wave-script,"Review follow-up (Greptile on pm-brief#124 and pm-linear#121): the backfill step is removed from this wave. It treated any npm attestation as proof that a tag's artifact was ours. The right check is not attested-commit equals tag-commit: this fleet's provenance records the workflow trigger commit (companion item pm-cli-website-nodo), measured as the tag's direct parent on pm-linear 2026.9.18, pm-github 2026.9.11 and pm-todos 2026.9.11. The check has to be repository plus workflow path plus ancestry, and it returns in the canonical pm-ops release verifier (companion pm-cli-website-mxrp). The other two changes, the 10-minute npm visibility window and the Release decoupled from bun lag, fix the root causes on their own. The three historical orphans were backfilled by hand on 2026-09-22." files[1]{path,scope}: .github/workflows/release.yml,project tests[1]{command,scope,provenance{author,created_at,source_kind,source_ref}}: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 94d6221..99793f4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -72,104 +72,6 @@ jobs: - name: Install dependencies run: npm ci - # Resume: a release can land on npm and as a pushed tag but still miss - # its GitHub Release (the bun-verification ordering this file used to - # have did exactly that to pm-linear v2026.09.18), and the next day's - # run releases the NEXT version and never goes back, so the gap never - # closes on its own. Walk every release tag on origin and close each - # gap BEFORE deciding today's release, using the same rule the publish - # path uses to reconcile: a Release is created only when the tag's npm - # version is visible WITH attestations; a missing or unattested - # coordinate is skipped, never released around. Notes come from the - # same pm-changelog invocation the release-notes step below uses, - # retargeted at the tag's own version (--release-version, not - # package.json, which by now holds a newer version) and scoped to that - # tag's range (--since-previous-tag --until-release-tag). Best-effort - # on purpose: a backfill failure is reported but must never block - # today's release, or one gap would be traded for a new one. - - name: Backfill missing GitHub releases - # Best-effort by construction, not only by intent: an unexpected - # failure here (a git or gh outage) is shown on the step but must not - # cancel today's release, or one gap would be traded for another. - continue-on-error: true - env: - GH_TOKEN: ${{ github.token }} - shell: bash - run: | - set -euo pipefail - pkg_name="$(node -p "require('./package.json').name")" - repo_name="${GITHUB_REPOSITORY#*/}" - git fetch origin --force --tags - # Fleet CalVer arithmetic, shared with `Decide release`: leading - # zeros off, an -N suffix preserved, so v2026.09.18-2 addresses npm - # version 2026.9.18-2. - tag_to_npm_version() { - local core year tail month day_and_suffix day suffix - core="${1#v}" - year="${core%%.*}" - tail="${core#*.}" - month="${tail%%.*}" - day_and_suffix="${tail#*.}" - day="${day_and_suffix%%-*}" - suffix="${day_and_suffix#"$day"}" - printf '%s.%s.%s%s\n' "$((10#$year))" "$((10#$month))" "$((10#$day))" "$suffix" - } - # The item URL is derived rather than hardcoded so this block is - # byte-identical across the fleet; for this repository it expands - # to the same URL the release-notes step below hardcodes. - backfill_common=( - --pm-root .agents/pm - --date-from-version - --item-url-base "https://github.com/${GITHUB_REPOSITORY}/blob/main/.agents/pm" - --respect-item-release - --pm-bin ./node_modules/.bin/pm - --pm-arg=--output-budget - --pm-arg=unbounded - --pm-arg=--output-limit - --pm-arg=unbounded - ) - while IFS= read -r release_tag; do - [[ -n "${release_tag}" ]] || continue - if gh release view "${release_tag}" > /dev/null 2>&1; then - continue - fi - npm_version="$(tag_to_npm_version "${release_tag}")" - # Freshness matters here exactly as in the publish reconcile: - # --prefer-online so a cached answer cannot invent a Release. - attestations="$(npm view "${pkg_name}@${npm_version}" dist.attestations --prefer-online --json 2>/dev/null || true)" - if [[ -z "${attestations}" || "${attestations}" == "null" || "${attestations}" == "{}" || "${attestations}" == "[]" ]]; then - echo "::warning::${release_tag} has no GitHub Release, but ${pkg_name}@${npm_version} is not confirmed on npm with attestations; not backfilling." - continue - fi - echo "Backfilling the missing GitHub Release for ${release_tag} (${pkg_name}@${npm_version} is published and attested)." - notes="$(mktemp)" - if ! npx pm-changelog "${backfill_common[@]}" \ - --release-version "${npm_version}" \ - --since-previous-tag --until-release-tag \ - --stdout > "${notes}"; then - echo "::warning::Could not generate release notes for ${release_tag}; leaving the gap for a maintainer and continuing today's release." - rm -f "${notes}" - continue - fi - if gh release create "${release_tag}" \ - --title "${repo_name} ${release_tag}" \ - --notes-file "${notes}" \ - --verify-tag; then - echo "Backfilled the GitHub Release for ${release_tag}." - else - echo "::warning::Could not create the GitHub Release for ${release_tag}; leaving the gap for a maintainer and continuing today's release." - fi - rm -f "${notes}" - # Only the fleet's release tag shapes: vYYYY.MM.DD and its numeric - # same-day suffix (-2, -3, ...). A glob would also admit - # v2026.09.18-rc.1 (an unintended prerelease backfill) or - # v2026.09.18foo, whose day part breaks the 10# arithmetic. - done < <( - git tag -l 'v*' --sort=-creatordate | - grep -E '^v[0-9]{4}\.[0-9]{2}\.[0-9]{2}(-[0-9]+)?$' || - true - ) - - name: Decide release id: decide shell: bash From 620b60c585f3aa2a42d32df0b4d0a64325527205 Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Tue, 22 Sep 2026 08:23:54 +0200 Subject: [PATCH 5/6] Declare max_attempts before use and correct the wave's pm records - release.yml: max_attempts is declared before refuse_unattested_or_fail, which expands it, so its visibility no longer depends on call-time reasoning (the fleet's bindings-before-use rule; Greptile on pm-todos#99). Behaviour is unchanged. - pm records: the release Issue no longer claims the backfill that review removed, and the certify Task describes CI as it now is (health gate right after npm ci, no separate install step). The final release.yml is byte-identical to a fresh run of the anchored applier on origin/main (identical). --- .agents/pm/history/pm-slack-h3ba.jsonl | 1 + .agents/pm/history/pm-slack-x0wg.jsonl | 1 + .agents/pm/issues/pm-slack-x0wg.toon | 6 +++--- .agents/pm/tasks/pm-slack-h3ba.toon | 6 +++--- .github/workflows/release.yml | 4 +++- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/.agents/pm/history/pm-slack-h3ba.jsonl b/.agents/pm/history/pm-slack-h3ba.jsonl index 6677541..b7d5b65 100644 --- a/.agents/pm/history/pm-slack-h3ba.jsonl +++ b/.agents/pm/history/pm-slack-h3ba.jsonl @@ -12,3 +12,4 @@ {"hash_algorithm":"sha256","ts":"2026-09-22T05:59:13.728Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T05:59:13.728Z"}],"before_hash":"882cdb81d1ffcf510e45eeb64a6aad5ccf813bba4c4ce2c0593849fac651b809","after_hash":"f0842563583dcd49aef3b0c38c71fb8276515119e5e313c0863750e60859d904","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":"886898c94834913f50a550e98d5eb5b7a56136f0fda044df7400a87c8988f25f"} {"hash_algorithm":"sha256","ts":"2026-09-22T06:10:06.550Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"comment_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T06:10:06.550Z"},{"op":"add","path":"/metadata/comments","value":[{"created_at":"2026-09-22T06:10:06.550Z","author":"fleet-wave-script","text":"Review follow-up (Greptile P1 + Sourcery on pm-starter#113): the launcher statically imports the devDependency pm-ops, so the README's promise that production / --omit=dev installs cannot break held only for registry installs, which never run prepare. README now states the real contract: a production install of a clone must pass --ignore-scripts. Canonical guarded launcher tracked as companion item pm-cli-website-xy19."}]}],"before_hash":"f0842563583dcd49aef3b0c38c71fb8276515119e5e313c0863750e60859d904","after_hash":"15b819d83b9a0cef6377d9f991f198891ac5f675a98404190353d17738aabc0e","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":"f392dc6c30c15a22650d488807e19aa65547eb205bc2c3ddca70e08c82eabe40"} {"hash_algorithm":"sha256","ts":"2026-09-22T06:10:48.872Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/1","value":{"created_at":"2026-09-22T06:10:48.872Z","author":"fleet-wave-script","text":"Review follow-up (Greptile P2 on pm-github#93, 'driver check is tautological'): CI no longer runs an explicit pm merge install before pm health --require-merge-drivers, because that made the gate verify only the step before it and would have masked a broken prepare hook. Proven on a fresh git clone of a wave branch: 0 merge.pm* keys before npm ci, 10 after (the prepare launcher installs them), health exits 0; with every merge.pm* key removed, pm health --strict-exit --require-merge-drivers exits 1. So the CI gate now proves the mechanism a fresh clone actually relies on."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T06:10:48.872Z"}],"before_hash":"15b819d83b9a0cef6377d9f991f198891ac5f675a98404190353d17738aabc0e","after_hash":"fb92b7daa53b475e7b32be24950d0fea674293910e2553af2f4bbeb0c96c9849","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":"8ae9569f023409e2f09e716425f3e952e2be535c7ac54bcdddaf5a85ede0d9dc"} +{"hash_algorithm":"sha256","ts":"2026-09-22T06:23:41.528Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/acceptance_criteria","value":"package.json and package-lock.json pin pm-cli 2026.9.21, pm-ops 2026.9.18, pm-changelog 2026.9.18; scripts/prepare-merge-driver.ts is the thin pm-ops/merge-driver launcher; no vendored implementation remains; CI runs pm health --strict-exit --require-merge-drivers after npm ci with no separate install step"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T06:23:41.528Z"},{"op":"replace","path":"/metadata/description","value":"Fleet wave 2026-09-22 (companion epic pm-cli-website-5s6z). Pins @unbrained/pm-cli 2026.9.21, pm-ops 2026.9.18 and pm-changelog 2026.9.18 exactly. The prepare hook becomes a thin launcher over pm-ops/merge-driver instead of a vendored implementation. CI runs pm health --strict-exit --require-merge-drivers right after npm ci, with no separate install step, so the gate proves the prepare hook installed the drivers (verified on a fresh clone: 0 merge.pm keys before npm ci, 10 after; health exits 1 once they are removed). The README states the real install contract: registry installs never run prepare, and a production install of a clone must pass --ignore-scripts (guarded launcher tracked as pm-cli-website-xy19)."}],"before_hash":"fb92b7daa53b475e7b32be24950d0fea674293910e2553af2f4bbeb0c96c9849","after_hash":"e9ad84bd67b9267eb5b99dea11f8f20dd89e2a7ba2cf18d47d8ace0ae3519997","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":"7b59610c6922484aa1a4e65ebf87a697620276407e7fce30cb9519126b6c178f"} diff --git a/.agents/pm/history/pm-slack-x0wg.jsonl b/.agents/pm/history/pm-slack-x0wg.jsonl index 0327c8c..f59eb5d 100644 --- a/.agents/pm/history/pm-slack-x0wg.jsonl +++ b/.agents/pm/history/pm-slack-x0wg.jsonl @@ -7,3 +7,4 @@ {"hash_algorithm":"sha256","ts":"2026-09-22T05:59:12.877Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T05:59:12.877Z"},{"op":"add","path":"/metadata/resolution","value":"Widened the npm visibility window, decoupled the GitHub Release from bun mirror lag with a visible gate, and added a best-effort backfill."},{"op":"add","path":"/metadata/expected_result","value":"A late-visible publish is reconciled, the Release is created whenever publish and tag succeed, and missing Releases for attested tags are backfilled."},{"op":"add","path":"/metadata/actual_result","value":"Applied by the anchored applier to this repository's own release.yml (matched, identity asserted: the file names only unbraind/pm-slack). Harness 7/7 on the same patch; release:check exits 0."}],"before_hash":"4493ea6a65e920b18860b016d41578de599b839ed06cf5f6628f41d460f18c4f","after_hash":"5ab6a2c8ec395b4e70ec55bfdaf93ca923b0908547455216bf16c8bd9a6a9de7","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":"c3836ca758ea050d5e67760bfa75a79b462bfd10bc4b710f0a5b5f8b168b3fb7"} {"hash_algorithm":"sha256","ts":"2026-09-22T05:59:14.743Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T05:59:14.743Z"}],"before_hash":"5ab6a2c8ec395b4e70ec55bfdaf93ca923b0908547455216bf16c8bd9a6a9de7","after_hash":"a039af4a32aff0d38940c83682aeb39f3760d1c250191e7932a9e22faf0c4417","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":"7c16834254beb6c13d5f554e626d40a7244c7d0fe262c1162386291d91360e34"} {"hash_algorithm":"sha256","ts":"2026-09-22T06:17:22.875Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"comment_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T06:17:22.875Z"},{"op":"add","path":"/metadata/comments","value":[{"created_at":"2026-09-22T06:17:22.875Z","author":"fleet-wave-script","text":"Review follow-up (Greptile on pm-brief#124 and pm-linear#121): the backfill step is removed from this wave. It treated any npm attestation as proof that a tag's artifact was ours. The right check is not attested-commit equals tag-commit: this fleet's provenance records the workflow trigger commit (companion item pm-cli-website-nodo), measured as the tag's direct parent on pm-linear 2026.9.18, pm-github 2026.9.11 and pm-todos 2026.9.11. The check has to be repository plus workflow path plus ancestry, and it returns in the canonical pm-ops release verifier (companion pm-cli-website-mxrp). The other two changes, the 10-minute npm visibility window and the Release decoupled from bun lag, fix the root causes on their own. The three historical orphans were backfilled by hand on 2026-09-22."}]}],"before_hash":"a039af4a32aff0d38940c83682aeb39f3760d1c250191e7932a9e22faf0c4417","after_hash":"da59524189f7d55f7c1ec5c36f01e060a9e3743c06489cbd3c45cc4f0d144aa2","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":"1c96ff6bbcb2c22ca4c956eeb97be020ce4c2efb820772361de836b8d9c7e538"} +{"hash_algorithm":"sha256","ts":"2026-09-22T06:23:40.728Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/actual_result","value":"Final release.yml is byte-identical to a fresh run of the anchored applier on origin/main (identical), names only unbraind/pm-slack, and passes the stub harness 5/5 (the unpatched file fails the four fix scenarios). Review follow-ups folded in: bun end-of-window re-check, backfill removed, max_attempts declared before use."},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T06:23:40.728Z"},{"op":"replace","path":"/metadata/description","value":"Companion item pm-cli-website-3y5d. Two changes to this repository's own release.yml: (1) a 10-minute npm visibility window after a reported publish error, read with --prefer-online, and an honest message when nothing becomes visible (the unattested-occupant refusal is unchanged; max_attempts is declared before the function that expands it); (2) the GitHub Release depends only on the publish and tag-push outcomes, and bun gets a 21-attempt 10-minute window with a final re-check, failing the job visibly through a separate gate step. A backfill step was proposed and removed after review: an npm attestation alone does not prove a tag's commit produced the artifact, and this fleet's provenance names the trigger commit (pm-cli-website-nodo), so provenance-ancestry verification moves to pm-cli-website-mxrp."}],"before_hash":"da59524189f7d55f7c1ec5c36f01e060a9e3743c06489cbd3c45cc4f0d144aa2","after_hash":"a21e7971f187aa86bac9904dca66493e28525cc3931a866f6888592689912a89","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":"b5f2fb89a0df5a6b9bbf6271154380ec991e803305ac9d2b8a8a23b35db8a4d8"} diff --git a/.agents/pm/issues/pm-slack-x0wg.toon b/.agents/pm/issues/pm-slack-x0wg.toon index a405e92..60b9ad9 100644 --- a/.agents/pm/issues/pm-slack-x0wg.toon +++ b/.agents/pm/issues/pm-slack-x0wg.toon @@ -1,18 +1,18 @@ id: pm-slack-x0wg 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. Three changes to this repository's own release.yml: a 10-minute npm visibility window with --prefer-online and an honest never-visible message (attestation refusal unchanged); the GitHub Release depends only on the publish and tag-push outcomes, bun gets a 21-attempt 10-minute window with a final re-check and fails the job visibly through a gate step; a continue-on-error backfill step creates missing Releases for attested fleet-shaped tags (vYYYY.MM.DD[-N]) with tag-scoped pm-changelog notes. Proven by a stub harness executing the changed run blocks: 7/7 scenarios, and the unpatched file fails the fix scenarios." +description: "Companion item pm-cli-website-3y5d. Two changes to this repository's own release.yml: (1) a 10-minute npm visibility window after a reported publish error, read with --prefer-online, and an honest message when nothing becomes visible (the unattested-occupant refusal is unchanged; max_attempts is declared before the function that expands it); (2) the GitHub Release depends only on the publish and tag-push outcomes, and bun gets a 21-attempt 10-minute window with a final re-check, failing the job visibly through a separate gate step. A backfill step was proposed and removed after review: an npm attestation alone does not prove a tag's commit produced the artifact, and this fleet's provenance names the trigger commit (pm-cli-website-nodo), so provenance-ancestry verification moves to pm-cli-website-mxrp." type: Issue status: closed priority: 1 tags[2]: release,reliability created_at: "2026-09-22T05:58:12.009Z" -updated_at: "2026-09-22T06:17:22.875Z" +updated_at: "2026-09-22T06:23:40.728Z" closed_at: "2026-09-22T05:59:11.720Z" completed_at: "2026-09-22T05:59:11.720Z" author: fleet-wave-script resolution: "Widened the npm visibility window, decoupled the GitHub Release from bun mirror lag with a visible gate, and added a best-effort backfill." expected_result: "A late-visible publish is reconciled, the Release is created whenever publish and tag succeed, and missing Releases for attested tags are backfilled." -actual_result: "Applied by the anchored applier to this repository's own release.yml (matched, identity asserted: the file names only unbraind/pm-slack). Harness 7/7 on the same patch; release:check exits 0." +actual_result: "Final release.yml is byte-identical to a fresh run of the anchored applier on origin/main (identical), names only unbraind/pm-slack, and passes the stub harness 5/5 (the unpatched file fails the four fix scenarios). Review follow-ups folded in: bun end-of-window re-check, backfill removed, max_attempts declared before use." comments[1]{created_at,author,text}: "2026-09-22T06:17:22.875Z",fleet-wave-script,"Review follow-up (Greptile on pm-brief#124 and pm-linear#121): the backfill step is removed from this wave. It treated any npm attestation as proof that a tag's artifact was ours. The right check is not attested-commit equals tag-commit: this fleet's provenance records the workflow trigger commit (companion item pm-cli-website-nodo), measured as the tag's direct parent on pm-linear 2026.9.18, pm-github 2026.9.11 and pm-todos 2026.9.11. The check has to be repository plus workflow path plus ancestry, and it returns in the canonical pm-ops release verifier (companion pm-cli-website-mxrp). The other two changes, the 10-minute npm visibility window and the Release decoupled from bun lag, fix the root causes on their own. The three historical orphans were backfilled by hand on 2026-09-22." files[1]{path,scope}: diff --git a/.agents/pm/tasks/pm-slack-h3ba.toon b/.agents/pm/tasks/pm-slack-h3ba.toon index 6e5e07d..21c37d2 100644 --- a/.agents/pm/tasks/pm-slack-h3ba.toon +++ b/.agents/pm/tasks/pm-slack-h3ba.toon @@ -1,16 +1,16 @@ id: pm-slack-h3ba title: Certify pm CLI 2026.9.21 and install merge drivers through the canonical pm-ops launcher -description: "Fleet wave 2026-09-22 (companion epic pm-cli-website-5s6z). Pins @unbrained/pm-cli 2026.9.21, pm-ops 2026.9.18 and pm-changelog 2026.9.18 exactly. The prepare hook becomes a thin launcher over pm-ops/merge-driver instead of a vendored implementation, and CI installs the drivers before pm health --strict-exit --require-merge-drivers, so a fresh clone that lacks them fails the gate instead of silently hard-conflicting tracker files." +description: "Fleet wave 2026-09-22 (companion epic pm-cli-website-5s6z). Pins @unbrained/pm-cli 2026.9.21, pm-ops 2026.9.18 and pm-changelog 2026.9.18 exactly. The prepare hook becomes a thin launcher over pm-ops/merge-driver instead of a vendored implementation. CI runs pm health --strict-exit --require-merge-drivers right after npm ci, with no separate install step, so the gate proves the prepare hook installed the drivers (verified on a fresh clone: 0 merge.pm keys before npm ci, 10 after; health exits 1 once they are removed). The README states the real install contract: registry installs never run prepare, and a production install of a clone must pass --ignore-scripts (guarded launcher tracked as pm-cli-website-xy19)." type: Task status: closed priority: 1 tags[4]: certify,merge-driver,multi-agent,pm-cli-2026.9.21 created_at: "2026-09-22T05:58:10.946Z" -updated_at: "2026-09-22T06:10:48.872Z" +updated_at: "2026-09-22T06:23:41.528Z" closed_at: "2026-09-22T05:59:08.862Z" completed_at: "2026-09-22T05:59:08.862Z" author: fleet-wave-script -acceptance_criteria: "package.json and package-lock.json pin pm-cli 2026.9.21, pm-ops 2026.9.18, pm-changelog 2026.9.18; scripts/prepare-merge-driver.ts is the thin pm-ops/merge-driver launcher; no vendored implementation remains; CI runs pm merge install before pm health --strict-exit --require-merge-drivers" +acceptance_criteria: "package.json and package-lock.json pin pm-cli 2026.9.21, pm-ops 2026.9.18, pm-changelog 2026.9.18; scripts/prepare-merge-driver.ts is the thin pm-ops/merge-driver launcher; no vendored implementation remains; CI runs pm health --strict-exit --require-merge-drivers after npm ci with no separate install step" resolution: Pinned the fleet toolchain and moved the prepare hook onto pm-ops/merge-driver; CI now requires the drivers. expected_result: "A fresh clone registers the merge drivers from npm ci, and CI fails if they are missing." actual_result: "Pins: @unbrained/pm-cli 2026.9.17 -> 2026.9.21, pm-changelog 2026.9.16 -> 2026.9.18, pm-ops 2026.9.13 -> 2026.9.18 (package.json and package-lock.json verified). Launcher replaces scripts/prepare-merge-driver.mjs; removed: scripts/prepare-merge-driver.mjs. git config lists the pm merge drivers after npm ci; pm health --strict-exit --require-merge-drivers exits 0; release:check exits 0." diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 99793f4..257e8d4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -651,6 +651,9 @@ 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." @@ -675,7 +678,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 From bf45345f1aa297392d6d23a7b1a803e1788eaaa5 Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Tue, 22 Sep 2026 08:27:45 +0200 Subject: [PATCH 6/6] Make every closure field of the release Issue match the final scope The resolution, expected result and close reason still described the backfill step that review removed, and the close reason cited the earlier 7-scenario harness run. All three now state the two changes that ship, the 5 applicable harness scenarios, and that the backfill moved to companion item pm-cli-website-mxrp. Raised by Greptile on pm-slack#115. --- .agents/pm/history/pm-slack-x0wg.jsonl | 1 + .agents/pm/issues/pm-slack-x0wg.toon | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.agents/pm/history/pm-slack-x0wg.jsonl b/.agents/pm/history/pm-slack-x0wg.jsonl index f59eb5d..d3db860 100644 --- a/.agents/pm/history/pm-slack-x0wg.jsonl +++ b/.agents/pm/history/pm-slack-x0wg.jsonl @@ -8,3 +8,4 @@ {"hash_algorithm":"sha256","ts":"2026-09-22T05:59:14.743Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T05:59:14.743Z"}],"before_hash":"5ab6a2c8ec395b4e70ec55bfdaf93ca923b0908547455216bf16c8bd9a6a9de7","after_hash":"a039af4a32aff0d38940c83682aeb39f3760d1c250191e7932a9e22faf0c4417","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":"7c16834254beb6c13d5f554e626d40a7244c7d0fe262c1162386291d91360e34"} {"hash_algorithm":"sha256","ts":"2026-09-22T06:17:22.875Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"comment_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T06:17:22.875Z"},{"op":"add","path":"/metadata/comments","value":[{"created_at":"2026-09-22T06:17:22.875Z","author":"fleet-wave-script","text":"Review follow-up (Greptile on pm-brief#124 and pm-linear#121): the backfill step is removed from this wave. It treated any npm attestation as proof that a tag's artifact was ours. The right check is not attested-commit equals tag-commit: this fleet's provenance records the workflow trigger commit (companion item pm-cli-website-nodo), measured as the tag's direct parent on pm-linear 2026.9.18, pm-github 2026.9.11 and pm-todos 2026.9.11. The check has to be repository plus workflow path plus ancestry, and it returns in the canonical pm-ops release verifier (companion pm-cli-website-mxrp). The other two changes, the 10-minute npm visibility window and the Release decoupled from bun lag, fix the root causes on their own. The three historical orphans were backfilled by hand on 2026-09-22."}]}],"before_hash":"a039af4a32aff0d38940c83682aeb39f3760d1c250191e7932a9e22faf0c4417","after_hash":"da59524189f7d55f7c1ec5c36f01e060a9e3743c06489cbd3c45cc4f0d144aa2","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":"1c96ff6bbcb2c22ca4c956eeb97be020ce4c2efb820772361de836b8d9c7e538"} {"hash_algorithm":"sha256","ts":"2026-09-22T06:23:40.728Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/actual_result","value":"Final release.yml is byte-identical to a fresh run of the anchored applier on origin/main (identical), names only unbraind/pm-slack, and passes the stub harness 5/5 (the unpatched file fails the four fix scenarios). Review follow-ups folded in: bun end-of-window re-check, backfill removed, max_attempts declared before use."},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T06:23:40.728Z"},{"op":"replace","path":"/metadata/description","value":"Companion item pm-cli-website-3y5d. Two changes to this repository's own release.yml: (1) a 10-minute npm visibility window after a reported publish error, read with --prefer-online, and an honest message when nothing becomes visible (the unattested-occupant refusal is unchanged; max_attempts is declared before the function that expands it); (2) the GitHub Release depends only on the publish and tag-push outcomes, and bun gets a 21-attempt 10-minute window with a final re-check, failing the job visibly through a separate gate step. A backfill step was proposed and removed after review: an npm attestation alone does not prove a tag's commit produced the artifact, and this fleet's provenance names the trigger commit (pm-cli-website-nodo), so provenance-ancestry verification moves to pm-cli-website-mxrp."}],"before_hash":"da59524189f7d55f7c1ec5c36f01e060a9e3743c06489cbd3c45cc4f0d144aa2","after_hash":"a21e7971f187aa86bac9904dca66493e28525cc3931a866f6888592689912a89","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":"b5f2fb89a0df5a6b9bbf6271154380ec991e803305ac9d2b8a8a23b35db8a4d8"} +{"hash_algorithm":"sha256","ts":"2026-09-22T06:27:34.274Z","author":"fleet-wave-script","author_source":"asserted","agent_harness":"claude-code","agent_model":"claude-opus-5","agent_model_source":"probe","agent_instance":"ffb32f8226e16470148a926d","agent_provenance":{"model":{"value":"claude-opus-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.278","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/close_reason","value":"Final release.yml is byte-identical to a fresh run of the anchored applier on origin/main and names only unbraind/pm-slack. The stub harness passes the 5 applicable scenarios on it (late-visible publish, never-visible, unattested occupant, bun failure with Release plus red job, bun resolving at the 10-minute mark), and the unpatched file fails the four fix scenarios. The backfill was removed in review."},{"op":"replace","path":"/metadata/expected_result","value":"A publish that npm accepts late is reconciled instead of reported as failed, and the GitHub Release is created whenever the publish and the tag push succeed, even when bun's mirror lags; a bun failure still fails the job visibly."},{"op":"replace","path":"/metadata/resolution","value":"Widened the npm visibility window after a reported publish error to 10 minutes and made the GitHub Release depend only on the publish and tag-push outcomes, with bun verification failing the job visibly through a gate step. A proposed backfill step was removed after review (provenance-ancestry verification tracked as pm-cli-website-mxrp)."},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-22T06:27:34.274Z"}],"before_hash":"a21e7971f187aa86bac9904dca66493e28525cc3931a866f6888592689912a89","after_hash":"98e1f87c2636a395eb8fd8e28b84a9bdfe2546669c538f4c008a69ef4c4f1688","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":"fbcf4ace19745eea7a15dfe89ff41557d8d7522533ac9ab1739be2dc1df31f3d"} diff --git a/.agents/pm/issues/pm-slack-x0wg.toon b/.agents/pm/issues/pm-slack-x0wg.toon index 60b9ad9..167ee0b 100644 --- a/.agents/pm/issues/pm-slack-x0wg.toon +++ b/.agents/pm/issues/pm-slack-x0wg.toon @@ -6,12 +6,12 @@ status: closed priority: 1 tags[2]: release,reliability created_at: "2026-09-22T05:58:12.009Z" -updated_at: "2026-09-22T06:23:40.728Z" +updated_at: "2026-09-22T06:27:34.274Z" closed_at: "2026-09-22T05:59:11.720Z" completed_at: "2026-09-22T05:59:11.720Z" author: fleet-wave-script -resolution: "Widened the npm visibility window, decoupled the GitHub Release from bun mirror lag with a visible gate, and added a best-effort backfill." -expected_result: "A late-visible publish is reconciled, the Release is created whenever publish and tag succeed, and missing Releases for attested tags are backfilled." +resolution: "Widened the npm visibility window after a reported publish error to 10 minutes and made the GitHub Release depend only on the publish and tag-push outcomes, with bun verification failing the job visibly through a gate step. A proposed backfill step was removed after review (provenance-ancestry verification tracked as pm-cli-website-mxrp)." +expected_result: "A publish that npm accepts late is reconciled instead of reported as failed, and the GitHub Release is created whenever the publish and the tag push succeed, even when bun's mirror lags; a bun failure still fails the job visibly." actual_result: "Final release.yml is byte-identical to a fresh run of the anchored applier on origin/main (identical), names only unbraind/pm-slack, and passes the stub harness 5/5 (the unpatched file fails the four fix scenarios). Review follow-ups folded in: bun end-of-window re-check, backfill removed, max_attempts declared before use." comments[1]{created_at,author,text}: "2026-09-22T06:17:22.875Z",fleet-wave-script,"Review follow-up (Greptile on pm-brief#124 and pm-linear#121): the backfill step is removed from this wave. It treated any npm attestation as proof that a tag's artifact was ours. The right check is not attested-commit equals tag-commit: this fleet's provenance records the workflow trigger commit (companion item pm-cli-website-nodo), measured as the tag's direct parent on pm-linear 2026.9.18, pm-github 2026.9.11 and pm-todos 2026.9.11. The check has to be repository plus workflow path plus ancestry, and it returns in the canonical pm-ops release verifier (companion pm-cli-website-mxrp). The other two changes, the 10-minute npm visibility window and the Release decoupled from bun lag, fix the root causes on their own. The three historical orphans were backfilled by hand on 2026-09-22." @@ -19,5 +19,5 @@ 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-22T05:58:22.495Z",local_mutation,pm-cli-2026-9-21-canonical-merge-driver-release-window -close_reason: "Applied by the anchored applier to this repository's own release.yml (matched, identity asserted: the file names only unbraind/pm-slack). Harness 7/7 on the same patch; release:check exits 0." +close_reason: "Final release.yml is byte-identical to a fresh run of the anchored applier on origin/main and names only unbraind/pm-slack. The stub harness passes the 5 applicable scenarios on it (late-visible publish, never-visible, unattested occupant, bun failure with Release plus red job, bun resolving at the 10-minute mark), and the unpatched file fails the four fix scenarios. The backfill was removed in review." body: ""