From a782879c6d65ad5e71883ded2160ca07993e6843 Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Mon, 19 Oct 2026 08:31:53 +0200 Subject: [PATCH 01/12] Select the newest eligible release PR and close superseded candidates --- .agents/pm/history/pm-github-l1ot.jsonl | 7 + .agents/pm/issues/pm-github-l1ot.toon | 35 ++++- .github/workflows/release.yml | 16 ++- CHANGELOG.md | 2 +- docs/daily-release-refresh.md | 8 ++ scripts/daily-release-plan.ts | 12 +- test/daily-release-plan.test.ts | 164 +++++++++++++++++++----- 7 files changed, 203 insertions(+), 41 deletions(-) diff --git a/.agents/pm/history/pm-github-l1ot.jsonl b/.agents/pm/history/pm-github-l1ot.jsonl index 4079ee74..21dc8836 100644 --- a/.agents/pm/history/pm-github-l1ot.jsonl +++ b/.agents/pm/history/pm-github-l1ot.jsonl @@ -22,3 +22,10 @@ {"hash_algorithm":"sha256","ts":"2026-10-18T23:32:50.519Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/10","value":{"created_at":"2026-10-18T23:32:50.519Z","author":"codex-agent","text":"Review code-head CI is green at febd1215f941369be9e2de2e69dcfc0fc84f25be: Node22 and Node26, CodeQL and Semgrep passed within 40 minutes with 60-second polling. Node26 logs confirm 903/903 Node tests, exact 100/100/100/100 over 15 authored modules, and 903 native Bun tests across 41 files. CI receipt: https://github.com/unbraind/pm-github/actions/runs/39174854832 . Both explicit local release checks are still running, so their completion is not claimed. Review threads have not been replied to or resolved."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-18T23:32:50.519Z"}],"before_hash":"209f6918543c1ce885950b52ca9ca55fac335c856ffa0b76be339762d7b2e3b4","after_hash":"75c6b572fc2d9a6f183ad208a9c10f95e3a6ee63ebf0b85497c2ca5ff2299d27","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"0fa706e0a395f1df03ae3279c5cd29f7f08b80be54fd573139f584bcb142592b"} {"hash_algorithm":"sha256","ts":"2026-10-19T00:15:30.141Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/11","value":{"created_at":"2026-10-19T00:15:30.141Z","author":"codex-agent","text":"Review round 1 full local gates completed: npm run release:check and bun run release:check both exited 0 at code head febd1215f941369be9e2de2e69dcfc0fc84f25be. Each passed 903/903 tests, zero failures/skips/cancellations, exact 100/100/100/100 over 15 authored modules at unchanged thresholds and pins. Lines/statements 10471/10471; functions 275/275; branches npm 3177/3177 and Bun invocation 3175/3175. Typecheck, build, docstrings, lint, duplication, privacy, production audit, pack dry run, changelog check and publication attestation passed. Focused suite 27/27; each of the three planner safety predicates independently reverted causes its regression to fail. Legitimate stale bot PR reuse, single-day cap, GitHub read failure propagation and ref collision refusal remain covered. docs/daily-release-refresh.md links prior completed receipts and the current review receipt, distinguishing historical pending comments from completion. Code-head CI: https://github.com/unbraind/pm-github/actions/runs/39174854832 . Receipt-only head CI and independent fresh-clone strict health follow in the PR. No review-thread replies/resolutions or release actions occurred."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T00:15:30.141Z"}],"before_hash":"75c6b572fc2d9a6f183ad208a9c10f95e3a6ee63ebf0b85497c2ca5ff2299d27","after_hash":"a9d7651b6a226160e067f3d128b2b3b7e0044834722ef16839e828ab7d6cdb9a","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"ecc5bdf24b4b11818335acfa339f57a52aaad1a23e525cad143308edf2505a3f"} {"hash_algorithm":"sha256","ts":"2026-10-19T00:15:34.559Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T00:15:34.559Z"}],"before_hash":"a9d7651b6a226160e067f3d128b2b3b7e0044834722ef16839e828ab7d6cdb9a","after_hash":"273cfdf9c1c85cf55bb28e4f45550605918f6095b5593e777483c1cbc6fdab1e","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"c4c7e9ab81e1f35ccba06bff852c69074fd83539935a5c59c98b37f2f60ea258"} +{"hash_algorithm":"sha256","ts":"2026-10-19T06:28:37.016Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"claim","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T06:28:37.016Z"},{"op":"add","path":"/metadata/assignee","value":"codex-agent"},{"op":"add","path":"/metadata/claim_principal","value":"codex-agent"}],"before_hash":"273cfdf9c1c85cf55bb28e4f45550605918f6095b5593e777483c1cbc6fdab1e","after_hash":"102f71ac91e974dc9354fed8bca0510598850ff6ad06b45cdcf0c0a7a81dfba6","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"8ebaa511b25268830f719d9574c4be2ea9fe054e4ee6864e0d396452ee7a65eb"} +{"hash_algorithm":"sha256","ts":"2026-10-19T06:28:39.809Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/12","value":{"created_at":"2026-10-19T06:28:39.809Z","author":"codex-agent","text":"Post-merge review follow-up for #151 and #149: expand release PR discovery beyond the default page; deterministically keep the newest eligible date branch and close superseded candidates by number; parse workflow YAML in regression tests. Preserve all ownership filters and release gates."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T06:28:39.809Z"}],"before_hash":"102f71ac91e974dc9354fed8bca0510598850ff6ad06b45cdcf0c0a7a81dfba6","after_hash":"c5e3c4d51618872d11c5067dd309847c30d7aee2a7e041293d9498a06251cb74","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"8aeb5de3ebb5835dd6082332bcbc32a4398c9b317cb021e44792339e5209c555"} +{"hash_algorithm":"sha256","ts":"2026-10-19T06:30:54.016Z","author":"codex","author_source":"configured","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"tests_add","patch":[{"op":"add","path":"/metadata/tests/2","value":{"command":"node --test test/daily-release-plan.test.ts","scope":"project","timeout_seconds":120,"provenance":{"author":"codex","created_at":"2026-10-19T06:30:53.988Z","source_kind":"local_mutation","source_ref":"fix/release-pr-selection-followup"}}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T06:30:54.016Z"}],"before_hash":"c5e3c4d51618872d11c5067dd309847c30d7aee2a7e041293d9498a06251cb74","after_hash":"2d71115357bc19e908c9075eeb9bba019bdf50e61bb69f3a5fe167e851434442","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"1e0ddd989d79719fe9349deb750aeeb82d77da55ad2e84186053136e5be2ffc4"} +{"hash_algorithm":"sha256","ts":"2026-10-19T06:31:11.132Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/13","value":{"created_at":"2026-10-19T06:31:11.132Z","author":"codex-agent","text":"Behavioral revert proofs for #151: removing only --limit 1000 makes the complete decision-step beyond-first-30 regression fail (ERR_ASSERTION, exit 1). Removing only candidate sorting makes the newest-date/order-independence regression fail (exit 1). Reverting only superseded output or only the shell close loop independently makes the eligible-only close-by-number regression fail (exit 1 each). All implementations restored; focused decision/workflow/publication tests pass 30/30, with typecheck, lint, docstrings and duplication checks passing. YAML is loaded from the pinned SDK's bundled parser, with no dependency changes."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T06:31:11.132Z"}],"before_hash":"2d71115357bc19e908c9075eeb9bba019bdf50e61bb69f3a5fe167e851434442","after_hash":"f3f3079b8c8a810cec28a0c0f9e9f34b23b982e1d48fef2e6a635c4a465f722b","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"9f3337f8709cea6665e94e926d665ef828cee0bc5eb94e854dacc959ab07312b"} +{"hash_algorithm":"sha256","ts":"2026-10-19T06:31:24.860Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"tests_remove","patch":[{"op":"remove","path":"/metadata/tests/2"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T06:31:24.860Z"}],"before_hash":"f3f3079b8c8a810cec28a0c0f9e9f34b23b982e1d48fef2e6a635c4a465f722b","after_hash":"0ade772bd0164070de91741eedd6fbe22d68553b4301c596d6fbf127a6ee4829","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"b4a71809489543f1a842b8eee629321ee89b9b5618ed19e8de2ef46321306755"} +{"hash_algorithm":"sha256","ts":"2026-10-19T06:31:26.248Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"tests_add","patch":[{"op":"add","path":"/metadata/tests/2","value":{"command":"node --test test/daily-release-plan.test.ts","scope":"project","timeout_seconds":120,"provenance":{"author":"codex-agent","created_at":"2026-10-19T06:31:26.212Z","source_kind":"local_mutation","source_ref":"fix/release-pr-selection-followup"}}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T06:31:26.248Z"}],"before_hash":"0ade772bd0164070de91741eedd6fbe22d68553b4301c596d6fbf127a6ee4829","after_hash":"99ff79a2b36c6be0193fedee3601ea93bbf4ca0a7880f69a82a1a58e6fb92ae8","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"3ac3d5334fda683b05d2e9c2b78b837c36ce5521bd3ba8e42d3a3cc3063535e8"} +{"hash_algorithm":"sha256","ts":"2026-10-19T06:31:51.611Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/14","value":{"created_at":"2026-10-19T06:31:51.611Z","author":"codex-agent","text":"Starting unchanged-threshold npm run release:check followed sequentially by bun run release:check, each with isolated Git identity. Linked decision suite executed through pm test: 13/13 passed; result storage is disabled by existing project policy, so this comment is the durable receipt. Registry pm-cli is 2026.10.19; the repository pin remains 2026.10.13."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T06:31:51.611Z"}],"before_hash":"99ff79a2b36c6be0193fedee3601ea93bbf4ca0a7880f69a82a1a58e6fb92ae8","after_hash":"803af2e0aab28c7307c0aac54c88974fcc835a7ed144984db0662613724079a3","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"0189fc551e508c8ceb4eca547ee909999d6c8f15b69adbc474b1abbb4b46802a"} diff --git a/.agents/pm/issues/pm-github-l1ot.toon b/.agents/pm/issues/pm-github-l1ot.toon index ca30cb37..d626dd9c 100644 --- a/.agents/pm/issues/pm-github-l1ot.toon +++ b/.agents/pm/issues/pm-github-l1ot.toon @@ -6,10 +6,12 @@ status: in_progress priority: 1 tags[3]: changelog,changelog-unreleased,release created_at: "2026-10-15T09:25:36.259Z" -updated_at: "2026-10-19T00:15:34.559Z" +updated_at: "2026-10-19T06:31:51.611Z" +assignee: codex-agent +claim_principal: codex-agent author: claude-hub acceptance_criteria: "release.yml generates CHANGELOG.md and the release notes through the package's own changelog scripts with the same status filter as changelog:check; a test fails if the two disagree; a release run with an in_progress item tagged changelog-unreleased and delivered since the last tag either succeeds or fails before any bump with a clear reason" -comments[12]{created_at,author,text}: +comments[15]{created_at,author,text}: "2026-10-18T18:19:00.873Z",codex-agent,"Reproduced the release generator split: release.yml uses pm-changelog closed-only output while changelog:check includes tagged pending work. The next run fails before refreshing the protected release PR. Plan: one canonical generator, rebuild from freshly fetched main, refresh or supersede stale PRs, and enforce one release per calendar day with pure decision regressions. Related: https://github.com/unbraind/pm-github/pull/144 and https://github.com/unbraind/pm-ado/issues/33" "2026-10-18T18:22:11.207Z",codex-agent,"Behavioral function-only revert proof: keeping the same decision function and tests, restored the old unconditional same-day eligibility and today-only branch selection. node --test test/daily-release-plan.test.ts failed 2/4 assertions: the old PR branch was abandoned and a second same-day release was allowed. Restored implementation: focused release suites passed 13/13 and the linked PM command passed. The release generator now invokes changelog:full, changelog:check and release:notes; lease-protected refresh updates the reused PR title/version to today." "2026-10-18T18:32:32.900Z",codex-agent,"Consumer context: https://github.com/unbraind/pm-beads/pull/145 https://github.com/unbraind/pm-brief/pull/154 https://github.com/unbraind/pm-context/pull/147 https://github.com/unbraind/pm-csv/pull/163 https://github.com/unbraind/pm-linear/pull/150 https://github.com/unbraind/pm-slack-standup/pull/127. Separate attribution fix: https://github.com/unbraind/pm-ado/issues/33. The proof report contains all 8 real imported terminal rows, exact before/main lifecycle fields, explicit completed_at projection limits, historical release placement and momentum. Six baseline entries are under Unreleased; all 8 projected rows render under historical releases when terminal statuses are selected." @@ -22,13 +24,36 @@ comments[12]{created_at,author,text}: "2026-10-18T22:53:06.496Z",codex-agent,"Review security regression proof: reverting only the same-repository predicate makes the cross-repository test fail with ERR_ASSERTION; reverting only exact workflow-author equality makes the foreign-author test fail; reverting only the date-branch predicate makes the non-date branch test fail. Each isolated run exits 1, with tests and remaining implementation retained. Correct implementation passes the 27-test decision/workflow/publication suite, including legitimate stale PR reuse, unpadded dates, GitHub lookup failure propagation and existing-ref collision refusal before push or cleanup. Report now links immutable completed local receipts and final-head CI for the prior heads: https://github.com/unbraind/pm-github/blob/3de8cde015db5979d6c0ebc9a749bbccd5656d7d/.agents/pm/history/pm-github-l1ot.jsonl and https://github.com/unbraind/pm-github/actions/runs/39170908685 . Review-round full gates remain pending." "2026-10-18T23:32:50.519Z",codex-agent,"Review code-head CI is green at febd1215f941369be9e2de2e69dcfc0fc84f25be: Node22 and Node26, CodeQL and Semgrep passed within 40 minutes with 60-second polling. Node26 logs confirm 903/903 Node tests, exact 100/100/100/100 over 15 authored modules, and 903 native Bun tests across 41 files. CI receipt: https://github.com/unbraind/pm-github/actions/runs/39174854832 . Both explicit local release checks are still running, so their completion is not claimed. Review threads have not been replied to or resolved." "2026-10-19T00:15:30.141Z",codex-agent,"Review round 1 full local gates completed: npm run release:check and bun run release:check both exited 0 at code head febd1215f941369be9e2de2e69dcfc0fc84f25be. Each passed 903/903 tests, zero failures/skips/cancellations, exact 100/100/100/100 over 15 authored modules at unchanged thresholds and pins. Lines/statements 10471/10471; functions 275/275; branches npm 3177/3177 and Bun invocation 3175/3175. Typecheck, build, docstrings, lint, duplication, privacy, production audit, pack dry run, changelog check and publication attestation passed. Focused suite 27/27; each of the three planner safety predicates independently reverted causes its regression to fail. Legitimate stale bot PR reuse, single-day cap, GitHub read failure propagation and ref collision refusal remain covered. docs/daily-release-refresh.md links prior completed receipts and the current review receipt, distinguishing historical pending comments from completion. Code-head CI: https://github.com/unbraind/pm-github/actions/runs/39174854832 . Receipt-only head CI and independent fresh-clone strict health follow in the PR. No review-thread replies/resolutions or release actions occurred." + "2026-10-19T06:28:39.809Z",codex-agent,"Post-merge review follow-up for #151 and #149: expand release PR discovery beyond the default page; deterministically keep the newest eligible date branch and close superseded candidates by number; parse workflow YAML in regression tests. Preserve all ownership filters and release gates." + "2026-10-19T06:31:11.132Z",codex-agent,"Behavioral revert proofs for #151: removing only --limit 1000 makes the complete decision-step beyond-first-30 regression fail (ERR_ASSERTION, exit 1). Removing only candidate sorting makes the newest-date/order-independence regression fail (exit 1). Reverting only superseded output or only the shell close loop independently makes the eligible-only close-by-number regression fail (exit 1 each). All implementations restored; focused decision/workflow/publication tests pass 30/30, with typecheck, lint, docstrings and duplication checks passing. YAML is loaded from the pinned SDK's bundled parser, with no dependency changes." + "2026-10-19T06:31:51.611Z",codex-agent,"Starting unchanged-threshold npm run release:check followed sequentially by bun run release:check, each with isolated Git identity. Linked decision suite executed through pm test: 13/13 passed; result storage is disabled by existing project policy, so this comment is the durable receipt. Registry pm-cli is 2026.10.19; the repository pin remains 2026.10.13." files[3]{path,scope}: .github/workflows/release.yml,project scripts/daily-release-plan.ts,project test/daily-release-plan.test.ts,project -tests[2]{command,scope,provenance{author,created_at,source_kind,source_ref}}: - node --test test/daily-release-plan.test.ts test/release-workflow.test.ts,project,codex-agent,"2026-10-18T18:20:50.608Z",local_mutation,fix/daily-release-refreshes-stale-pr - node --test test/daily-release-plan.test.ts test/release-workflow.test.ts test/verify-release-publish-attestation.test.ts,project,codex-agent,"2026-10-18T19:11:03.374Z",local_mutation,fix/daily-release-refreshes-stale-pr +tests[3]: + - command: node --test test/daily-release-plan.test.ts test/release-workflow.test.ts + scope: project + provenance: + author: codex-agent + created_at: "2026-10-18T18:20:50.608Z" + source_kind: local_mutation + source_ref: fix/daily-release-refreshes-stale-pr + - command: node --test test/daily-release-plan.test.ts test/release-workflow.test.ts test/verify-release-publish-attestation.test.ts + scope: project + provenance: + author: codex-agent + created_at: "2026-10-18T19:11:03.374Z" + source_kind: local_mutation + source_ref: fix/daily-release-refreshes-stale-pr + - command: node --test test/daily-release-plan.test.ts + scope: project + timeout_seconds: 120 + provenance: + author: codex-agent + created_at: "2026-10-19T06:31:26.212Z" + source_kind: local_mutation + source_ref: fix/release-pr-selection-followup docs[1]{path,scope}: docs/daily-release-refresh.md,project body: "" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 70f98bff..0e0108d1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -95,7 +95,7 @@ jobs: if [[ -n "$latest_tag" ]]; then export RELEASE_LAST_SHA="$(git rev-list -n 1 "$latest_tag")" fi - RELEASE_EXISTING_PRS="$(gh pr list --repo "$GITHUB_REPOSITORY" --base main --state open \ + RELEASE_EXISTING_PRS="$(gh pr list --repo "$GITHUB_REPOSITORY" --base main --state open --limit 1000 \ --json number,headRefName,isCrossRepository,author)" export RELEASE_EXISTING_PRS node --input-type=module -e ' @@ -277,6 +277,20 @@ jobs: git commit -m "Release ${REPO_NAME} ${RELEASE_TAG}" fi + - name: Close superseded release PRs + if: steps.decide.outputs.should_release == 'true' + shell: bash + env: + GH_TOKEN: ${{ github.token }} + RELEASE_PR_NUMBER: ${{ steps.decide.outputs.pr_number }} + SUPERSEDED_PR_NUMBERS: ${{ steps.decide.outputs.superseded_pr_numbers }} + run: | + set -euo pipefail + for pr_number in $SUPERSEDED_PR_NUMBERS; do + gh pr close "$pr_number" --repo "$GITHUB_REPOSITORY" \ + --comment "Superseded by release PR #${RELEASE_PR_NUMBER}." + done + - name: Merge release metadata through protected PR if: steps.decide.outputs.should_release == 'true' id: release_pr diff --git a/CHANGELOG.md b/CHANGELOG.md index 6db32d5f..0c6367cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,8 @@ ### Fixed -- Require explicit delivery attribution before merged-PR closeout ([pm-github-uqjf](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-uqjf.toon)) - The daily release regenerates the changelog with a different status filter than release:check, so an open changelog-unreleased item delivered since the last tag fails the release ([pm-github-l1ot](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-l1ot.toon)) +- Require explicit delivery attribution before merged-PR closeout ([pm-github-uqjf](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-uqjf.toon)) - Release timeout must report current workflow approval state ([pm-github-b145](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-b145.toon)) - Preserve legacy GitHub ownership across copied provider examples ([pm-github-b143](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-b143.toon)) - Reject malformed effective-rule discriminators before closeout ([pm-github-b141](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-b141.toon)) diff --git a/docs/daily-release-refresh.md b/docs/daily-release-refresh.md index 440d00f7..482041bc 100644 --- a/docs/daily-release-refresh.md +++ b/docs/daily-release-refresh.md @@ -135,3 +135,11 @@ Review round 1 restricts reusable PRs to the same repository, the exact GITHUB_T At review code head febd1215f941369be9e2de2e69dcfc0fc84f25be, both npm run release:check and bun run release:check exited 0. Each passed 903/903 tests with zero failures, cancellations or skips and exact 100/100/100/100 across 15 authored modules. Both measured 10471/10471 lines and statements and 275/275 functions; branches were 3177/3177 (npm) and 3175/3175 (Bun invocation). All release-check stages passed with unchanged thresholds and dependency pins. The [review-round tracker receipt](../.agents/pm/history/pm-github-l1ot.jsonl) records these completed local results; earlier pending comments describe earlier runs. [Code-head CI](https://github.com/unbraind/pm-github/actions/runs/39174854832) passed Node 22/26, each with 903 Node tests, 903 native Bun tests across 41 files and exact authored-source coverage, plus CodeQL and Semgrep. Receipt-head CI and fresh-clone health are recorded in the PR after that head is pushed. GitHub documents that GITHUB_TOKEN-created pull-request runs may require approval; existing bounded approval diagnostics remain: https://docs.github.com/en/actions/concepts/security/github_token. Refresh correctness does not certify GitHub approval or npm publisher policy. + +## Post-merge selection follow-up + +The follow-up to [#151](https://github.com/unbraind/pm-github/pull/151) and [#149](https://github.com/unbraind/pm-github/pull/149) requests up to 1,000 open PRs instead of the CLI default of 30. The existing repository, workflow author and exact date-branch filters remain mandatory. Candidates are sorted by numeric branch date, newest first; equal dates use descending PR number. This also compares padded and unpadded dates consistently. The selected PR is refreshed, and the planner reports only the other eligible PR numbers for a separate superseded-close step after release checks. Their branches are retained. + +The tests parse the workflow with the YAML parser already bundled in the pinned SDK, assert named steps and environment mappings, and execute complete decision, close and merge-boundary scripts against synthetic command responses. A release candidate at position 31 is found. Reordered candidates, month/year boundaries, same-date ties, unsafe candidates, empty cleanup, lookup failure and existing branch refusal are covered. No release workflow is dispatched during these tests. + +With all tests retained, removing only the increased lookup limit fails the position-31 regression. Removing only sorting fails the newest-date regression. Removing only superseded output or the shell close loop independently fails the eligible-only close regression. Each control exits 1 with ERR_ASSERTION; restored focused decision/workflow/publication tests pass 30/30. Completed full-gate and final-head CI receipts are recorded in the [PM history](../.agents/pm/history/pm-github-l1ot.jsonl) and follow-up PR. diff --git a/scripts/daily-release-plan.ts b/scripts/daily-release-plan.ts index 1aab4496..cdb86be3 100644 --- a/scripts/daily-release-plan.ts +++ b/scripts/daily-release-plan.ts @@ -9,9 +9,11 @@ export interface DailyReleaseInput { /** Rebuild from current main on every eligible run, reusing even yesterday's open PR. */ export function decideDailyRelease(input: DailyReleaseInput) { // gh represents the GITHUB_TOKEN author as app/github-actions. - const existingPr = input.existingPrs.find(pr => pr.isCrossRepository === false + const candidates = input.existingPrs.filter(pr => pr.isCrossRepository === false && pr.author.login === "app/github-actions" - && /^release\/\d{4}\.\d{1,2}\.\d{1,2}$/.test(pr.headRefName)); + && /^release\/\d{4}\.\d{1,2}\.\d{1,2}$/.test(pr.headRefName)) + .sort((a, b) => releaseDate(b.headRefName) - releaseDate(a.headRefName) || b.number - a.number); + const existingPr = candidates[0]; const version = input.today.split(".").map(Number).join("."); const releasedVersion = input.lastRelease?.tag.replace(/^v/, "").split("-")[0]?.split(".").map(Number).join("."); const shouldRelease = releasedVersion !== version && input.lastRelease?.sha !== input.mainSha; @@ -23,5 +25,11 @@ export function decideDailyRelease(input: DailyReleaseInput) { base_sha: input.mainSha, release_branch: existingPr?.headRefName ?? `release/${input.today}`, pr_number: existingPr?.number ?? "", + superseded_pr_numbers: candidates.slice(1).map(pr => pr.number).join(" "), }; } + +/** Compare date-shaped release branches numerically, including unpadded dates. */ +function releaseDate(branch: string): number { + return branch.slice("release/".length).split(".").reduce((date, part) => date * 100 + Number(part), 0); +} diff --git a/test/daily-release-plan.test.ts b/test/daily-release-plan.test.ts index 10e12734..9740043b 100644 --- a/test/daily-release-plan.test.ts +++ b/test/daily-release-plan.test.ts @@ -1,7 +1,10 @@ /** Daily release policy is independent of GitHub and the wall clock. */ import assert from "node:assert/strict"; -import { spawnSync } from "node:child_process"; -import { readFileSync } from "node:fs"; +import { spawnSync, type SpawnSyncReturns } from "node:child_process"; +import { mkdtempSync, readFileSync, rmSync } from "node:fs"; +import { createRequire } from "node:module"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; import test from "node:test"; import { decideDailyRelease, type DailyReleaseInput } from "../scripts/daily-release-plan.ts"; @@ -11,7 +14,7 @@ const workflowPr = { number: 144, headRefName: "release/2026.10.17", isCrossRepo test("first release uses today's stable npm version and current main", () => { assert.deepEqual(decideDailyRelease(input), { should_release: true, action: "create", tag: "v2026.10.18", - npm_version: "2026.10.18", base_sha: "new-main", release_branch: "release/2026.10.18", pr_number: "" }); + npm_version: "2026.10.18", base_sha: "new-main", release_branch: "release/2026.10.18", pr_number: "", superseded_pr_numbers: "" }); assert.equal(decideDailyRelease({ ...input, today: "2026.01.02" }).npm_version, "2026.1.2"); }); @@ -66,42 +69,139 @@ test("an unchanged release or a publication already dated today skips, even afte assert.equal(decideDailyRelease({ ...input, lastRelease: { tag: "v2026.10.17", sha: input.mainSha } }).action, "skip"); }); +/** Workflow fields exercised by the shell and configuration regressions. */ +interface WorkflowStep { name: string; run?: string; env?: Record; if?: string } +/** Resolve the YAML parser already bundled with the pinned SDK. */ +const { parse }: { parse: (source: string) => { jobs: { release: { steps: WorkflowStep[] } } } } = + createRequire(createRequire(import.meta.url).resolve("@unbrained/pm-cli/package.json"))("yaml"); +const steps = parse(readFileSync(new URL("../.github/workflows/release.yml", import.meta.url), "utf8")).jobs.release.steps; + +/** Fail clearly when a named shell step disappears or loses its script. */ +function step(name: string): WorkflowStep & { run: string } { + const found = steps.find(candidate => candidate.name === name); + assert.ok(found, `Missing workflow step: ${name}`); + assert.equal(typeof found.run, "string", `${name} must contain a shell script`); + return found as WorkflowStep & { run: string }; +} + +/** Execute the complete decision step against bounded synthetic GitHub responses. */ +function runDecision(prs: DailyReleaseInput["existingPrs"], failedLookup = false): SpawnSyncReturns { + const root = mkdtempSync(join(tmpdir(), "pm-github-release-plan-")); + try { + return spawnSync("bash", ["-c", ` + git() { if [[ "$1" == rev-parse ]]; then printf new-main; fi; } + date() { printf 2026.10.18; } + gh() { + if [[ "$FAILED_LOOKUP" == true ]]; then return 47; fi + local limit=30 + while (( $# )); do + if [[ "$1" == --limit ]]; then limit="$2"; shift; fi + shift + done + node --input-type=module -e 'console.log(JSON.stringify(JSON.parse(process.env.TEST_PRS).slice(0, Number(process.argv[1]))))' "$limit" + } + ${step("Decide release").run} + cat "$GITHUB_OUTPUT" + `], { encoding: "utf8", env: { ...process.env, RELEASE_TIMEZONE: "UTC", GITHUB_REPOSITORY: "example/package", + GITHUB_OUTPUT: join(root, "outputs"), TEST_PRS: JSON.stringify(prs), FAILED_LOOKUP: String(failedLookup) } }); + } finally { rmSync(root, { recursive: true, force: true }); } +} + +test("the complete workflow decision finds an eligible PR beyond the default first 30", () => { + const prs = Array.from({ length: 30 }, (_, index) => ({ ...workflowPr, number: index + 1, headRefName: `feature/${index}` })); + const result = runDecision([...prs, workflowPr]); + assert.equal(result.status, 0, result.stderr); + assert.match(result.stdout, /^pr_number=144$/m); + assert.match(result.stdout, /^action=refresh$/m); +}); + +test("newest branch date wins independently of input order and PR creation order", () => { + const candidates = [ + { ...workflowPr, number: 300, headRefName: "release/2026.9.30" }, + { ...workflowPr, number: 100, headRefName: "release/2026.10.1" }, + { ...workflowPr, number: 200, headRefName: "release/2025.12.31" }, + { ...workflowPr, number: 400, headRefName: "release/2026.10.01" }, + ]; + for (const existingPrs of [candidates, [...candidates].reverse(), [...candidates.slice(2), ...candidates.slice(0, 2)]]) { + const plan = decideDailyRelease({ ...input, existingPrs }); + assert.equal(plan.pr_number, 400); + assert.equal(plan.release_branch, "release/2026.10.01"); + assert.equal(plan.superseded_pr_numbers, "100 300 200"); + assert.equal(plan.should_release, true); + } + assert.deepEqual(candidates.map(pr => pr.number), [300, 100, 200, 400], "planning does not mutate its input"); +}); + +test("only eligible older release PRs are reported and closed as superseded by number", () => { + const plan = decideDailyRelease({ ...input, existingPrs: [workflowPr, + { ...workflowPr, number: 145, headRefName: "release/2026.10.18" }, + { ...workflowPr, number: 999, isCrossRepository: true }, + { ...workflowPr, number: 998, author: { login: "maintainer" } }, + { ...workflowPr, number: 997, headRefName: "release/v2-notes" }, + ] }); + assert.equal(plan.pr_number, 145); + assert.equal(plan.superseded_pr_numbers, "144"); + const cleanup = step("Close superseded release PRs"); + assert.equal(cleanup.env?.SUPERSEDED_PR_NUMBERS, "${{ steps.decide.outputs.superseded_pr_numbers }}"); + assert.equal(cleanup.env?.RELEASE_PR_NUMBER, "${{ steps.decide.outputs.pr_number }}"); + assert.equal(cleanup.env?.GH_TOKEN, "${{ github.token }}"); + assert.equal(cleanup.if, "steps.decide.outputs.should_release == 'true'"); + for (const superseded of [plan.superseded_pr_numbers, ""]) { + const result: SpawnSyncReturns = spawnSync("bash", ["-c", `gh() { printf '%s\\n' "$*"; }; ${cleanup.run}`], { + encoding: "utf8", env: { ...process.env, GITHUB_REPOSITORY: "example/package", + RELEASE_PR_NUMBER: String(plan.pr_number), SUPERSEDED_PR_NUMBERS: superseded }, + }); + assert.equal(result.status, 0, result.stderr); + assert.equal(result.stdout.trim(), superseded ? "pr close 144 --repo example/package --comment Superseded by release PR #145." : ""); + } +}); + test("workflow rebuilds before preparation and runs the canonical generator before checks and leased PR refresh", () => { - const workflow = readFileSync(new URL("../.github/workflows/release.yml", import.meta.url), "utf8"); - assert.match(workflow, /import \{ decideDailyRelease \} from "\.\/scripts\/daily-release-plan\.ts"/); - assert.ok(workflow.indexOf("git checkout --detach origin/main") < workflow.indexOf("- name: Install dependencies")); - const generation = workflow.slice(workflow.indexOf("- name: Generate changelog"), workflow.indexOf("- name: Run release checks")); - assert.match(generation, /npm run changelog:full/); - assert.match(generation, /npm run changelog:check/); - assert.match(generation, /npm run --silent release:notes/); - assert.doesNotMatch(generation, /npx pm-changelog/); - assert.match(workflow, /release_branch="\$RELEASE_BRANCH"/); - assert.match(workflow, /--force-with-lease=/); - assert.match(workflow, /gh pr edit "\$pr_number"/); - assert.match(workflow, /pr_number="\$RELEASE_PR_NUMBER"/); - assert.match(workflow, /RELEASE_PR_NUMBER: \$\{\{ steps.decide.outputs.pr_number \}\}/); - assert.equal(workflow.match(/gh pr list/g)?.length, 1); - assert.match(workflow, /--json number,headRefName,isCrossRepository,author/); - assert.doesNotMatch(workflow, /export RELEASE_EXISTING_PRS=/); - assert.ok(workflow.indexOf("Refusing to overwrite a release branch") < workflow.indexOf('origin ":refs/heads/${release_branch}"')); + const decision = step("Decide release"); + const generation = step("Generate changelog and release notes"); + const refresh = step("Merge release metadata through protected PR"); + assert.match(decision.run, /import \{ decideDailyRelease \} from "\.\/scripts\/daily-release-plan\.ts"/); + assert.ok(steps.indexOf(step("Refresh release base")) < steps.indexOf(step("Install dependencies"))); + assert.match(step("Refresh release base").run, /git checkout --detach origin\/main/); + assert.ok(steps.indexOf(generation) < steps.indexOf(step("Run release checks"))); + assert.match(generation.run, /npm run changelog:full/); + assert.match(generation.run, /npm run changelog:check/); + assert.match(generation.run, /npm run --silent release:notes/); + assert.doesNotMatch(generation.run, /npx pm-changelog/); + assert.match(refresh.run, /release_branch="\$RELEASE_BRANCH"/); + assert.match(refresh.run, /--force-with-lease=/); + assert.match(refresh.run, /gh pr edit "\$pr_number"/); + assert.match(refresh.run, /pr_number="\$RELEASE_PR_NUMBER"/); + assert.equal(refresh.env?.RELEASE_PR_NUMBER, "${{ steps.decide.outputs.pr_number }}"); + assert.equal(steps.filter(candidate => candidate.run?.includes("gh pr list")).length, 1); + assert.match(decision.run, /--json number,headRefName,isCrossRepository,author/); + assert.doesNotMatch(decision.run, /export RELEASE_EXISTING_PRS=/); + assert.ok(steps.indexOf(step("Close superseded release PRs")) > steps.indexOf(step("Run release checks"))); }); test("a failed GitHub candidate lookup aborts instead of exporting an empty success", () => { - const workflow = readFileSync(new URL("../.github/workflows/release.yml", import.meta.url), "utf8"); - const lookup = workflow.slice(workflow.indexOf(' RELEASE_EXISTING_PRS="$('), workflow.indexOf(" node --input-type=module -e '")); - const result = spawnSync("bash", ["-c", `set -eu; gh() { return 47; }; ${lookup}\nprintf continued`], - { encoding: "utf8", env: { ...process.env, GITHUB_REPOSITORY: "example/package" } }); + const result = runDecision([], true); assert.equal(result.status, 47); assert.equal(result.stdout, ""); }); test("the push and cleanup boundary refuses an existing ref without a selected workflow PR", () => { - const workflow = readFileSync(new URL("../.github/workflows/release.yml", import.meta.url), "utf8"); - const guard = workflow.slice(workflow.indexOf(' release_branch="$RELEASE_BRANCH"'), workflow.indexOf(' release_commit="$(git rev-parse HEAD)"')); - for (const [prNumber, ref, status] of [["", "existing-sha", 1], ["144", "existing-sha", 0], ["", "", 0]] as const) { - const result = spawnSync("bash", ["-c", `set -eu; git() { printf '%s' "$TEST_REF"; }; ${guard}`], - { encoding: "utf8", env: { ...process.env, RELEASE_BRANCH: "release/2026.10.18", RELEASE_PR_NUMBER: prNumber, TEST_REF: ref } }); - assert.equal(result.status, status, result.stderr); - if (status === 1) assert.match(result.stdout, /Refusing to overwrite/); - } + const root = mkdtempSync(join(tmpdir(), "pm-github-release-guard-")); + try { + for (const [prNumber, ref, status] of [["", "existing-sha", 1], ["144", "existing-sha", 0], ["", "", 0]] as const) { + const result: SpawnSyncReturns = spawnSync("bash", ["-c", ` + git() { + case "$1" in + rev-parse) printf new-main ;; + ls-remote) printf '%s' "$TEST_REF" ;; + esac + } + gh() { printf 'Unexpected GitHub mutation' >&2; return 99; } + ${step("Merge release metadata through protected PR").run} + `], { encoding: "utf8", env: { ...process.env, RELEASE_BRANCH: "release/2026.10.18", RELEASE_PR_NUMBER: prNumber, + RELEASE_BASE_SHA: "new-main", GITHUB_OUTPUT: join(root, "outputs"), TEST_REF: ref } }); + assert.equal(result.status, status, result.stderr); + if (status === 1) assert.match(result.stdout, /Refusing to overwrite/); + } + } finally { rmSync(root, { recursive: true, force: true }); } }); From d2abfe6de9452903f6ad6c4ac3fb8e1b0860bd5f Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Mon, 19 Oct 2026 09:23:55 +0200 Subject: [PATCH 02/12] Record release selection follow-up validation --- .agents/pm/history/pm-github-l1ot.jsonl | 2 ++ .agents/pm/issues/pm-github-l1ot.toon | 7 +++---- docs/daily-release-refresh.md | 2 ++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.agents/pm/history/pm-github-l1ot.jsonl b/.agents/pm/history/pm-github-l1ot.jsonl index 21dc8836..8dcb065b 100644 --- a/.agents/pm/history/pm-github-l1ot.jsonl +++ b/.agents/pm/history/pm-github-l1ot.jsonl @@ -29,3 +29,5 @@ {"hash_algorithm":"sha256","ts":"2026-10-19T06:31:24.860Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"tests_remove","patch":[{"op":"remove","path":"/metadata/tests/2"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T06:31:24.860Z"}],"before_hash":"f3f3079b8c8a810cec28a0c0f9e9f34b23b982e1d48fef2e6a635c4a465f722b","after_hash":"0ade772bd0164070de91741eedd6fbe22d68553b4301c596d6fbf127a6ee4829","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"b4a71809489543f1a842b8eee629321ee89b9b5618ed19e8de2ef46321306755"} {"hash_algorithm":"sha256","ts":"2026-10-19T06:31:26.248Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"tests_add","patch":[{"op":"add","path":"/metadata/tests/2","value":{"command":"node --test test/daily-release-plan.test.ts","scope":"project","timeout_seconds":120,"provenance":{"author":"codex-agent","created_at":"2026-10-19T06:31:26.212Z","source_kind":"local_mutation","source_ref":"fix/release-pr-selection-followup"}}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T06:31:26.248Z"}],"before_hash":"0ade772bd0164070de91741eedd6fbe22d68553b4301c596d6fbf127a6ee4829","after_hash":"99ff79a2b36c6be0193fedee3601ea93bbf4ca0a7880f69a82a1a58e6fb92ae8","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"3ac3d5334fda683b05d2e9c2b78b837c36ce5521bd3ba8e42d3a3cc3063535e8"} {"hash_algorithm":"sha256","ts":"2026-10-19T06:31:51.611Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/14","value":{"created_at":"2026-10-19T06:31:51.611Z","author":"codex-agent","text":"Starting unchanged-threshold npm run release:check followed sequentially by bun run release:check, each with isolated Git identity. Linked decision suite executed through pm test: 13/13 passed; result storage is disabled by existing project policy, so this comment is the durable receipt. Registry pm-cli is 2026.10.19; the repository pin remains 2026.10.13."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T06:31:51.611Z"}],"before_hash":"99ff79a2b36c6be0193fedee3601ea93bbf4ca0a7880f69a82a1a58e6fb92ae8","after_hash":"803af2e0aab28c7307c0aac54c88974fcc835a7ed144984db0662613724079a3","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"0189fc551e508c8ceb4eca547ee909999d6c8f15b69adbc474b1abbb4b46802a"} +{"hash_algorithm":"sha256","ts":"2026-10-19T07:23:50.176Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/15","value":{"created_at":"2026-10-19T07:23:50.176Z","author":"codex-agent","text":"Completed follow-up gates at code head a782879c6d65ad5e71883ded2160ca07993e6843: npm run release:check and bun run release:check each exited 0 with 913/913 tests, zero failures/cancellations/skips, and exact 100/100/100/100 across all 15 authored modules. Each measured 10535/10535 lines and statements, 277/277 functions, and 3225/3225 branches, with zero skipped coverage. All remaining gate stages passed at unchanged thresholds, source scope, budgets and pins. Native bun test test/daily-release-plan.test.ts also passed 13/13. Four independent behavioral revert proofs are recorded above. PM linked-test result storage remains disabled by existing policy; completed results are recorded here. No new PM CLI/SDK defects found. Preparing the new follow-up PR; exact-head CI evidence will be recorded in its body: https://github.com/unbraind/pm-github/compare/main...fix/release-pr-selection-followup"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T07:23:50.176Z"}],"before_hash":"803af2e0aab28c7307c0aac54c88974fcc835a7ed144984db0662613724079a3","after_hash":"859ab1f726ee29c5d3a477d94c45fb8ce0a35c08c75433377726f83bfe90f1a5","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"799e63ad099bacf5b1d28574a095a4a46524c758fb34ed677c3108940ada6d25"} +{"hash_algorithm":"sha256","ts":"2026-10-19T07:23:51.772Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T07:23:51.772Z"}],"before_hash":"859ab1f726ee29c5d3a477d94c45fb8ce0a35c08c75433377726f83bfe90f1a5","after_hash":"757df29cfe0fd4ab8d232d5549a778247da353d9f53f563f22bcfc3650dd17a5","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"2bc819ab641d539ae1a16589e8e827d5c4d85db2bfd0cec2624b527de7c60943"} diff --git a/.agents/pm/issues/pm-github-l1ot.toon b/.agents/pm/issues/pm-github-l1ot.toon index d626dd9c..748f94ae 100644 --- a/.agents/pm/issues/pm-github-l1ot.toon +++ b/.agents/pm/issues/pm-github-l1ot.toon @@ -6,12 +6,10 @@ status: in_progress priority: 1 tags[3]: changelog,changelog-unreleased,release created_at: "2026-10-15T09:25:36.259Z" -updated_at: "2026-10-19T06:31:51.611Z" -assignee: codex-agent -claim_principal: codex-agent +updated_at: "2026-10-19T07:23:51.772Z" author: claude-hub acceptance_criteria: "release.yml generates CHANGELOG.md and the release notes through the package's own changelog scripts with the same status filter as changelog:check; a test fails if the two disagree; a release run with an in_progress item tagged changelog-unreleased and delivered since the last tag either succeeds or fails before any bump with a clear reason" -comments[15]{created_at,author,text}: +comments[16]{created_at,author,text}: "2026-10-18T18:19:00.873Z",codex-agent,"Reproduced the release generator split: release.yml uses pm-changelog closed-only output while changelog:check includes tagged pending work. The next run fails before refreshing the protected release PR. Plan: one canonical generator, rebuild from freshly fetched main, refresh or supersede stale PRs, and enforce one release per calendar day with pure decision regressions. Related: https://github.com/unbraind/pm-github/pull/144 and https://github.com/unbraind/pm-ado/issues/33" "2026-10-18T18:22:11.207Z",codex-agent,"Behavioral function-only revert proof: keeping the same decision function and tests, restored the old unconditional same-day eligibility and today-only branch selection. node --test test/daily-release-plan.test.ts failed 2/4 assertions: the old PR branch was abandoned and a second same-day release was allowed. Restored implementation: focused release suites passed 13/13 and the linked PM command passed. The release generator now invokes changelog:full, changelog:check and release:notes; lease-protected refresh updates the reused PR title/version to today." "2026-10-18T18:32:32.900Z",codex-agent,"Consumer context: https://github.com/unbraind/pm-beads/pull/145 https://github.com/unbraind/pm-brief/pull/154 https://github.com/unbraind/pm-context/pull/147 https://github.com/unbraind/pm-csv/pull/163 https://github.com/unbraind/pm-linear/pull/150 https://github.com/unbraind/pm-slack-standup/pull/127. Separate attribution fix: https://github.com/unbraind/pm-ado/issues/33. The proof report contains all 8 real imported terminal rows, exact before/main lifecycle fields, explicit completed_at projection limits, historical release placement and momentum. Six baseline entries are under Unreleased; all 8 projected rows render under historical releases when terminal statuses are selected." @@ -27,6 +25,7 @@ comments[15]{created_at,author,text}: "2026-10-19T06:28:39.809Z",codex-agent,"Post-merge review follow-up for #151 and #149: expand release PR discovery beyond the default page; deterministically keep the newest eligible date branch and close superseded candidates by number; parse workflow YAML in regression tests. Preserve all ownership filters and release gates." "2026-10-19T06:31:11.132Z",codex-agent,"Behavioral revert proofs for #151: removing only --limit 1000 makes the complete decision-step beyond-first-30 regression fail (ERR_ASSERTION, exit 1). Removing only candidate sorting makes the newest-date/order-independence regression fail (exit 1). Reverting only superseded output or only the shell close loop independently makes the eligible-only close-by-number regression fail (exit 1 each). All implementations restored; focused decision/workflow/publication tests pass 30/30, with typecheck, lint, docstrings and duplication checks passing. YAML is loaded from the pinned SDK's bundled parser, with no dependency changes." "2026-10-19T06:31:51.611Z",codex-agent,"Starting unchanged-threshold npm run release:check followed sequentially by bun run release:check, each with isolated Git identity. Linked decision suite executed through pm test: 13/13 passed; result storage is disabled by existing project policy, so this comment is the durable receipt. Registry pm-cli is 2026.10.19; the repository pin remains 2026.10.13." + "2026-10-19T07:23:50.176Z",codex-agent,"Completed follow-up gates at code head a782879c6d65ad5e71883ded2160ca07993e6843: npm run release:check and bun run release:check each exited 0 with 913/913 tests, zero failures/cancellations/skips, and exact 100/100/100/100 across all 15 authored modules. Each measured 10535/10535 lines and statements, 277/277 functions, and 3225/3225 branches, with zero skipped coverage. All remaining gate stages passed at unchanged thresholds, source scope, budgets and pins. Native bun test test/daily-release-plan.test.ts also passed 13/13. Four independent behavioral revert proofs are recorded above. PM linked-test result storage remains disabled by existing policy; completed results are recorded here. No new PM CLI/SDK defects found. Preparing the new follow-up PR; exact-head CI evidence will be recorded in its body: https://github.com/unbraind/pm-github/compare/main...fix/release-pr-selection-followup" files[3]{path,scope}: .github/workflows/release.yml,project scripts/daily-release-plan.ts,project diff --git a/docs/daily-release-refresh.md b/docs/daily-release-refresh.md index 482041bc..b3f4ef13 100644 --- a/docs/daily-release-refresh.md +++ b/docs/daily-release-refresh.md @@ -143,3 +143,5 @@ The follow-up to [#151](https://github.com/unbraind/pm-github/pull/151) and [#14 The tests parse the workflow with the YAML parser already bundled in the pinned SDK, assert named steps and environment mappings, and execute complete decision, close and merge-boundary scripts against synthetic command responses. A release candidate at position 31 is found. Reordered candidates, month/year boundaries, same-date ties, unsafe candidates, empty cleanup, lookup failure and existing branch refusal are covered. No release workflow is dispatched during these tests. With all tests retained, removing only the increased lookup limit fails the position-31 regression. Removing only sorting fails the newest-date regression. Removing only superseded output or the shell close loop independently fails the eligible-only close regression. Each control exits 1 with ERR_ASSERTION; restored focused decision/workflow/publication tests pass 30/30. Completed full-gate and final-head CI receipts are recorded in the [PM history](../.agents/pm/history/pm-github-l1ot.jsonl) and follow-up PR. + +At follow-up code head a782879c6d65ad5e71883ded2160ca07993e6843, npm run release:check and bun run release:check each exited 0 with 913/913 tests and zero failures, cancellations or skips. Both measured exact 100/100/100/100 across all 15 authored modules: 10535/10535 lines and statements, 277/277 functions and 3225/3225 branches, with zero skipped coverage. All other release-check stages passed with unchanged pins, scope and budgets. Native bun test test/daily-release-plan.test.ts passed 13/13. The final receipt changes only tracker evidence and documentation; final-head CI evidence is recorded in the follow-up PR. From 1e603be1a8ef793ceaf6911fae1ca4406336de43 Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Mon, 19 Oct 2026 10:21:53 +0200 Subject: [PATCH 03/12] Keep release housekeeping failures from blocking publication --- .agents/pm/history/pm-github-l1ot.jsonl | 5 + .agents/pm/issues/pm-github-l1ot.toon | 12 ++- .github/workflows/release.yml | 103 +++++++++++++----- docs/daily-release-refresh.md | 20 ++++ package.json | 2 +- test/daily-release-plan.test.ts | 138 +++++++++++++++++++++++- 6 files changed, 246 insertions(+), 34 deletions(-) diff --git a/.agents/pm/history/pm-github-l1ot.jsonl b/.agents/pm/history/pm-github-l1ot.jsonl index 8dcb065b..1e165aa7 100644 --- a/.agents/pm/history/pm-github-l1ot.jsonl +++ b/.agents/pm/history/pm-github-l1ot.jsonl @@ -31,3 +31,8 @@ {"hash_algorithm":"sha256","ts":"2026-10-19T06:31:51.611Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/14","value":{"created_at":"2026-10-19T06:31:51.611Z","author":"codex-agent","text":"Starting unchanged-threshold npm run release:check followed sequentially by bun run release:check, each with isolated Git identity. Linked decision suite executed through pm test: 13/13 passed; result storage is disabled by existing project policy, so this comment is the durable receipt. Registry pm-cli is 2026.10.19; the repository pin remains 2026.10.13."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T06:31:51.611Z"}],"before_hash":"99ff79a2b36c6be0193fedee3601ea93bbf4ca0a7880f69a82a1a58e6fb92ae8","after_hash":"803af2e0aab28c7307c0aac54c88974fcc835a7ed144984db0662613724079a3","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"0189fc551e508c8ceb4eca547ee909999d6c8f15b69adbc474b1abbb4b46802a"} {"hash_algorithm":"sha256","ts":"2026-10-19T07:23:50.176Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/15","value":{"created_at":"2026-10-19T07:23:50.176Z","author":"codex-agent","text":"Completed follow-up gates at code head a782879c6d65ad5e71883ded2160ca07993e6843: npm run release:check and bun run release:check each exited 0 with 913/913 tests, zero failures/cancellations/skips, and exact 100/100/100/100 across all 15 authored modules. Each measured 10535/10535 lines and statements, 277/277 functions, and 3225/3225 branches, with zero skipped coverage. All remaining gate stages passed at unchanged thresholds, source scope, budgets and pins. Native bun test test/daily-release-plan.test.ts also passed 13/13. Four independent behavioral revert proofs are recorded above. PM linked-test result storage remains disabled by existing policy; completed results are recorded here. No new PM CLI/SDK defects found. Preparing the new follow-up PR; exact-head CI evidence will be recorded in its body: https://github.com/unbraind/pm-github/compare/main...fix/release-pr-selection-followup"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T07:23:50.176Z"}],"before_hash":"803af2e0aab28c7307c0aac54c88974fcc835a7ed144984db0662613724079a3","after_hash":"859ab1f726ee29c5d3a477d94c45fb8ce0a35c08c75433377726f83bfe90f1a5","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"799e63ad099bacf5b1d28574a095a4a46524c758fb34ed677c3108940ada6d25"} {"hash_algorithm":"sha256","ts":"2026-10-19T07:23:51.772Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T07:23:51.772Z"}],"before_hash":"859ab1f726ee29c5d3a477d94c45fb8ce0a35c08c75433377726f83bfe90f1a5","after_hash":"757df29cfe0fd4ab8d232d5549a778247da353d9f53f563f22bcfc3650dd17a5","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"2bc819ab641d539ae1a16589e8e827d5c4d85db2bfd0cec2624b527de7c60943"} +{"hash_algorithm":"sha256","ts":"2026-10-19T08:13:08.891Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"claim","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T08:13:08.891Z"},{"op":"add","path":"/metadata/assignee","value":"codex-agent"},{"op":"add","path":"/metadata/claim_principal","value":"codex-agent"}],"before_hash":"757df29cfe0fd4ab8d232d5549a778247da353d9f53f563f22bcfc3650dd17a5","after_hash":"0ba00f7fe0ae76abc5314394874ec483e4ec7e227e1ed31c8512cc19b88247d5","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"ebc9120f76db83effb956b2b9d15edb5fb8ccfb670bcce3258f7313104667dc0"} +{"hash_algorithm":"sha256","ts":"2026-10-19T08:13:11.052Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/16","value":{"created_at":"2026-10-19T08:13:11.052Z","author":"codex-agent","text":"Review round 1 of #152, thread PRRT_kwDOTbN4pM6t1WSO: make superseded release PR cleanup best-effort, move it after the selected release metadata is merged, and test that individual close failures warn with PR number and error while later candidates and the release continue. Sweep every release workflow step for non-essential operations that can block publication; preserve fail-closed publication, provenance and verification boundaries."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T08:13:11.052Z"}],"before_hash":"0ba00f7fe0ae76abc5314394874ec483e4ec7e227e1ed31c8512cc19b88247d5","after_hash":"49c291b70dbdeffc53879de56e01ab01ad82e09ab618d067e8ffb2adb1808622","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"f074dd47026da191ca6028ae285ad42fed9c8d6381ea1f7a38621346bf98430f"} +{"hash_algorithm":"sha256","ts":"2026-10-19T08:21:09.595Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/17","value":{"created_at":"2026-10-19T08:21:09.595Z","author":"codex-agent","text":"Review-round sweep covered all 23 release steps. Additional non-essential blockers fixed: selected branch deletion in normal/resume paths, PR title/body refresh, merge diagnostic-file deletion, and implicit/explicit step-summary writes. These now warn; summaries are separate from required changelog/notes generation. All seven identity/verification/publish/tag/Bun/GitHub-release/final-gate step definitions are structurally identical to d2abfe6; 20 shell scripts pass bash -n. Focused suites pass 36/36 and native Bun decisions 19/19. Revert controls retained all tests: six runtime reverts (close failure handler, metadata edit handler, diagnostic deletion handler, branch deletion handler, resume branch deletion, summary handler) each fail with ERR_ASSERTION and exit 1. Three configuration reverts (cleanup order, generator summary flag, notes-script summary flag) independently fail their contract assertions, exit 1. Each implementation restored. Publication failure remains an executed negative case: an unconfirmed registry exits 1 without continuation."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T08:21:09.595Z"}],"before_hash":"49c291b70dbdeffc53879de56e01ab01ad82e09ab618d067e8ffb2adb1808622","after_hash":"20c39d7c0eea5c550025423ec76074194e7162759af36af1cc5897c8589fd69b","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"95ad675e919b4074ff7a12cce0f7a3451a4f524f2ccae3df6fb7414683a92e13"} +{"hash_algorithm":"sha256","ts":"2026-10-19T08:21:49.170Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/2/path","value":"scripts/daily-release-plan.ts"},{"op":"replace","path":"/metadata/files/1/path","value":"package.json"},{"op":"add","path":"/metadata/files/3","value":{"path":"test/daily-release-plan.test.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T08:21:49.170Z"}],"before_hash":"20c39d7c0eea5c550025423ec76074194e7162759af36af1cc5897c8589fd69b","after_hash":"def9074b98099f2c271067f7de812665bcb9ba5baa44b1effacef50247f860b2","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"333fe09812b56d11386e6b236a9b9f7d7f7359b02fdd637cddd39562e5d1db3c"} +{"hash_algorithm":"sha256","ts":"2026-10-19T08:21:51.882Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/18","value":{"created_at":"2026-10-19T08:21:51.882Z","author":"codex-agent","text":"Starting npm run release:check then bun run release:check sequentially for the cleanup correction, each with isolated Git identity. Linked PM decision suite passed 19/19; tracking-disabled policy remains unchanged, so results are recorded in comments. No dependency pins, coverage thresholds, source scope, test time budgets or publication policy changed."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T08:21:51.882Z"}],"before_hash":"def9074b98099f2c271067f7de812665bcb9ba5baa44b1effacef50247f860b2","after_hash":"5a6a149125a85252331358e186d014c4cadfef945490c5ae773f944f3dd56009","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"0e09a6178ba2bc7318c9c5537288214216ac6c124c9f76eb9e0cab91122b2bde"} diff --git a/.agents/pm/issues/pm-github-l1ot.toon b/.agents/pm/issues/pm-github-l1ot.toon index 748f94ae..8731221f 100644 --- a/.agents/pm/issues/pm-github-l1ot.toon +++ b/.agents/pm/issues/pm-github-l1ot.toon @@ -6,10 +6,12 @@ status: in_progress priority: 1 tags[3]: changelog,changelog-unreleased,release created_at: "2026-10-15T09:25:36.259Z" -updated_at: "2026-10-19T07:23:51.772Z" +updated_at: "2026-10-19T08:21:51.882Z" +assignee: codex-agent +claim_principal: codex-agent author: claude-hub acceptance_criteria: "release.yml generates CHANGELOG.md and the release notes through the package's own changelog scripts with the same status filter as changelog:check; a test fails if the two disagree; a release run with an in_progress item tagged changelog-unreleased and delivered since the last tag either succeeds or fails before any bump with a clear reason" -comments[16]{created_at,author,text}: +comments[19]{created_at,author,text}: "2026-10-18T18:19:00.873Z",codex-agent,"Reproduced the release generator split: release.yml uses pm-changelog closed-only output while changelog:check includes tagged pending work. The next run fails before refreshing the protected release PR. Plan: one canonical generator, rebuild from freshly fetched main, refresh or supersede stale PRs, and enforce one release per calendar day with pure decision regressions. Related: https://github.com/unbraind/pm-github/pull/144 and https://github.com/unbraind/pm-ado/issues/33" "2026-10-18T18:22:11.207Z",codex-agent,"Behavioral function-only revert proof: keeping the same decision function and tests, restored the old unconditional same-day eligibility and today-only branch selection. node --test test/daily-release-plan.test.ts failed 2/4 assertions: the old PR branch was abandoned and a second same-day release was allowed. Restored implementation: focused release suites passed 13/13 and the linked PM command passed. The release generator now invokes changelog:full, changelog:check and release:notes; lease-protected refresh updates the reused PR title/version to today." "2026-10-18T18:32:32.900Z",codex-agent,"Consumer context: https://github.com/unbraind/pm-beads/pull/145 https://github.com/unbraind/pm-brief/pull/154 https://github.com/unbraind/pm-context/pull/147 https://github.com/unbraind/pm-csv/pull/163 https://github.com/unbraind/pm-linear/pull/150 https://github.com/unbraind/pm-slack-standup/pull/127. Separate attribution fix: https://github.com/unbraind/pm-ado/issues/33. The proof report contains all 8 real imported terminal rows, exact before/main lifecycle fields, explicit completed_at projection limits, historical release placement and momentum. Six baseline entries are under Unreleased; all 8 projected rows render under historical releases when terminal statuses are selected." @@ -26,8 +28,12 @@ comments[16]{created_at,author,text}: "2026-10-19T06:31:11.132Z",codex-agent,"Behavioral revert proofs for #151: removing only --limit 1000 makes the complete decision-step beyond-first-30 regression fail (ERR_ASSERTION, exit 1). Removing only candidate sorting makes the newest-date/order-independence regression fail (exit 1). Reverting only superseded output or only the shell close loop independently makes the eligible-only close-by-number regression fail (exit 1 each). All implementations restored; focused decision/workflow/publication tests pass 30/30, with typecheck, lint, docstrings and duplication checks passing. YAML is loaded from the pinned SDK's bundled parser, with no dependency changes." "2026-10-19T06:31:51.611Z",codex-agent,"Starting unchanged-threshold npm run release:check followed sequentially by bun run release:check, each with isolated Git identity. Linked decision suite executed through pm test: 13/13 passed; result storage is disabled by existing project policy, so this comment is the durable receipt. Registry pm-cli is 2026.10.19; the repository pin remains 2026.10.13." "2026-10-19T07:23:50.176Z",codex-agent,"Completed follow-up gates at code head a782879c6d65ad5e71883ded2160ca07993e6843: npm run release:check and bun run release:check each exited 0 with 913/913 tests, zero failures/cancellations/skips, and exact 100/100/100/100 across all 15 authored modules. Each measured 10535/10535 lines and statements, 277/277 functions, and 3225/3225 branches, with zero skipped coverage. All remaining gate stages passed at unchanged thresholds, source scope, budgets and pins. Native bun test test/daily-release-plan.test.ts also passed 13/13. Four independent behavioral revert proofs are recorded above. PM linked-test result storage remains disabled by existing policy; completed results are recorded here. No new PM CLI/SDK defects found. Preparing the new follow-up PR; exact-head CI evidence will be recorded in its body: https://github.com/unbraind/pm-github/compare/main...fix/release-pr-selection-followup" -files[3]{path,scope}: + "2026-10-19T08:13:11.052Z",codex-agent,"Review round 1 of #152, thread PRRT_kwDOTbN4pM6t1WSO: make superseded release PR cleanup best-effort, move it after the selected release metadata is merged, and test that individual close failures warn with PR number and error while later candidates and the release continue. Sweep every release workflow step for non-essential operations that can block publication; preserve fail-closed publication, provenance and verification boundaries." + "2026-10-19T08:21:09.595Z",codex-agent,"Review-round sweep covered all 23 release steps. Additional non-essential blockers fixed: selected branch deletion in normal/resume paths, PR title/body refresh, merge diagnostic-file deletion, and implicit/explicit step-summary writes. These now warn; summaries are separate from required changelog/notes generation. All seven identity/verification/publish/tag/Bun/GitHub-release/final-gate step definitions are structurally identical to d2abfe6; 20 shell scripts pass bash -n. Focused suites pass 36/36 and native Bun decisions 19/19. Revert controls retained all tests: six runtime reverts (close failure handler, metadata edit handler, diagnostic deletion handler, branch deletion handler, resume branch deletion, summary handler) each fail with ERR_ASSERTION and exit 1. Three configuration reverts (cleanup order, generator summary flag, notes-script summary flag) independently fail their contract assertions, exit 1. Each implementation restored. Publication failure remains an executed negative case: an unconfirmed registry exits 1 without continuation." + "2026-10-19T08:21:51.882Z",codex-agent,"Starting npm run release:check then bun run release:check sequentially for the cleanup correction, each with isolated Git identity. Linked PM decision suite passed 19/19; tracking-disabled policy remains unchanged, so results are recorded in comments. No dependency pins, coverage thresholds, source scope, test time budgets or publication policy changed." +files[4]{path,scope}: .github/workflows/release.yml,project + package.json,project scripts/daily-release-plan.ts,project test/daily-release-plan.test.ts,project tests[3]: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0e0108d1..c5c0590b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -249,7 +249,7 @@ jobs: shell: bash run: | set -euo pipefail - npm run changelog:full -- --github-step-summary + npm run changelog:full npm run changelog:check npm run --silent release:notes > RELEASE_NOTES.md @@ -277,20 +277,6 @@ jobs: git commit -m "Release ${REPO_NAME} ${RELEASE_TAG}" fi - - name: Close superseded release PRs - if: steps.decide.outputs.should_release == 'true' - shell: bash - env: - GH_TOKEN: ${{ github.token }} - RELEASE_PR_NUMBER: ${{ steps.decide.outputs.pr_number }} - SUPERSEDED_PR_NUMBERS: ${{ steps.decide.outputs.superseded_pr_numbers }} - run: | - set -euo pipefail - for pr_number in $SUPERSEDED_PR_NUMBERS; do - gh pr close "$pr_number" --repo "$GITHUB_REPOSITORY" \ - --comment "Superseded by release PR #${RELEASE_PR_NUMBER}." - done - - name: Merge release metadata through protected PR if: steps.decide.outputs.should_release == 'true' id: release_pr @@ -321,10 +307,6 @@ jobs: release_commit="$(git rev-parse HEAD)" if [[ "$release_commit" == "$current_main_sha" ]]; then echo "Release metadata is already present on main; resuming the publish/tag transaction." - if [[ -n "$remote_branch_sha" ]]; then - git push --force-with-lease="refs/heads/${release_branch}:${remote_branch_sha}" \ - origin ":refs/heads/${release_branch}" - fi { echo "merged_sha=$current_main_sha" echo "pr_number=" @@ -344,9 +326,14 @@ jobs: --body "Automated daily release metadata for \`${RELEASE_TAG}\`. The release gate passed before this PR was created; npm publication and tagging remain blocked until this protected PR is merged.")" pr_number="${pr_url##*/}" else - gh pr edit "$pr_number" --repo "$GITHUB_REPOSITORY" \ + if ! edit_error="$(gh pr edit "$pr_number" --repo "$GITHUB_REPOSITORY" \ --title "Release pm-github ${RELEASE_TAG}" \ - --body "Automated daily release metadata for \`${RELEASE_TAG}\`, rebuilt from current main. The release gate passed before refresh; publication and tagging wait for the protected PR." + --body "Automated daily release metadata for \`${RELEASE_TAG}\`, rebuilt from current main. The release gate passed before refresh; publication and tagging wait for the protected PR." 2>&1)"; then + edit_error="${edit_error//$'\n'/ }" + edit_error="${edit_error//$'\r'/ }" + edit_error="${edit_error//%/%25}" + echo "::warning::Could not refresh title/body of release PR #${pr_number}: ${edit_error}" + fi fi owner="${GITHUB_REPOSITORY%/*}" @@ -499,21 +486,85 @@ jobs: fi sleep 20 done - rm -f "$merge_err" "$pr_view_err" + if ! cleanup_error="$(rm -f "$merge_err" "$pr_view_err" 2>&1)"; then + cleanup_error="${cleanup_error//$'\n'/ }" + cleanup_error="${cleanup_error//$'\r'/ }" + cleanup_error="${cleanup_error//%/%25}" + echo "::warning::Could not remove release merge diagnostic files: ${cleanup_error}" + fi if [[ -z "$merged_sha" || "$merged_sha" == "null" ]]; then echo "::error::GitHub did not return the merged main SHA for release PR #${pr_number}." exit 1 fi - if git ls-remote --exit-code --heads origin "refs/heads/${release_branch}" > /dev/null 2>&1; then - git push origin --delete "$release_branch" - fi { echo "merged_sha=$merged_sha" echo "pr_number=$pr_number" } >> "$GITHUB_OUTPUT" - echo "Merged release metadata PR #${pr_number} at ${merged_sha}." >> "$GITHUB_STEP_SUMMARY" + + - name: Close superseded release PRs + if: steps.decide.outputs.should_release == 'true' + shell: bash + env: + GH_TOKEN: ${{ github.token }} + RELEASE_PR_NUMBER: ${{ steps.decide.outputs.pr_number }} + SUPERSEDED_PR_NUMBERS: ${{ steps.decide.outputs.superseded_pr_numbers }} + run: | + set -euo pipefail + for pr_number in $SUPERSEDED_PR_NUMBERS; do + if close_output="$(gh pr close "$pr_number" --repo "$GITHUB_REPOSITORY" \ + --comment "Superseded by release PR #${RELEASE_PR_NUMBER}." 2>&1)"; then + printf '%s\n' "$close_output" + else + close_output="${close_output//$'\n'/ }" + close_output="${close_output//$'\r'/ }" + close_output="${close_output//%/%25}" + echo "::warning::Could not close superseded release PR #${pr_number}: ${close_output}" + fi + done + + - name: Clean up merged release branch + if: steps.decide.outputs.should_release == 'true' + shell: bash + env: + RELEASE_BRANCH: ${{ steps.decide.outputs.release_branch }} + run: | + set -euo pipefail + if remote_branch_sha="$(git ls-remote --heads origin "refs/heads/${RELEASE_BRANCH}" 2>&1)"; then + remote_branch_sha="${remote_branch_sha%%$'\t'*}" + if [[ -n "$remote_branch_sha" ]]; then + if ! cleanup_error="$(git push --force-with-lease="refs/heads/${RELEASE_BRANCH}:${remote_branch_sha}" \ + origin ":refs/heads/${RELEASE_BRANCH}" 2>&1)"; then + cleanup_error="${cleanup_error//$'\n'/ }" + cleanup_error="${cleanup_error//$'\r'/ }" + cleanup_error="${cleanup_error//%/%25}" + echo "::warning::Could not delete merged release branch ${RELEASE_BRANCH}: ${cleanup_error}" + fi + fi + else + remote_branch_sha="${remote_branch_sha//$'\n'/ }" + remote_branch_sha="${remote_branch_sha//$'\r'/ }" + remote_branch_sha="${remote_branch_sha//%/%25}" + echo "::warning::Could not inspect merged release branch ${RELEASE_BRANCH} for cleanup: ${remote_branch_sha}" + fi + + - name: Write release summary + if: steps.decide.outputs.should_release == 'true' + shell: bash + env: + MERGED_SHA: ${{ steps.release_pr.outputs.merged_sha }} + run: | + set -euo pipefail + if ! summary_error="$({ + printf 'Release metadata at %s.\n\n' "$MERGED_SHA" && + cat CHANGELOG.md RELEASE_NOTES.md + } 2>&1 >> "$GITHUB_STEP_SUMMARY")"; then + summary_error="${summary_error//$'\n'/ }" + summary_error="${summary_error//$'\r'/ }" + summary_error="${summary_error//%/%25}" + echo "::warning::Could not write release summary: ${summary_error}" + fi # The release PR can only contain the prepared release commit because the # base SHA is checked immediately before creation. Re-check the exact diff --git a/docs/daily-release-refresh.md b/docs/daily-release-refresh.md index b3f4ef13..4e7cf44a 100644 --- a/docs/daily-release-refresh.md +++ b/docs/daily-release-refresh.md @@ -145,3 +145,23 @@ The tests parse the workflow with the YAML parser already bundled in the pinned With all tests retained, removing only the increased lookup limit fails the position-31 regression. Removing only sorting fails the newest-date regression. Removing only superseded output or the shell close loop independently fails the eligible-only close regression. Each control exits 1 with ERR_ASSERTION; restored focused decision/workflow/publication tests pass 30/30. Completed full-gate and final-head CI receipts are recorded in the [PM history](../.agents/pm/history/pm-github-l1ot.jsonl) and follow-up PR. At follow-up code head a782879c6d65ad5e71883ded2160ca07993e6843, npm run release:check and bun run release:check each exited 0 with 913/913 tests and zero failures, cancellations or skips. Both measured exact 100/100/100/100 across all 15 authored modules: 10535/10535 lines and statements, 277/277 functions and 3225/3225 branches, with zero skipped coverage. All other release-check stages passed with unchanged pins, scope and budgets. Native bun test test/daily-release-plan.test.ts passed 13/13. The final receipt changes only tracker evidence and documentation; final-head CI evidence is recorded in the follow-up PR. + +## Review round 1: cleanup cannot block publication + +Thread PRRT_kwDOTbN4pM6t1WSO identified superseded-PR cleanup stopping the job before the selected metadata PR merged. Cleanup now follows the successful protected merge. Each failed close emits one warning containing the PR number and captured error, then attempts later candidates and continues the release. Error text is flattened and percent-escaped for a single [workflow warning](https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-commands#setting-a-warning-message). + +The sweep covered all 23 authored steps: + +| Steps or nested operation | Result | +| --- | --- | +| Checkout; Refresh release base; Setup Node.js; Use an npm that supports trusted publishing; Setup Bun; Install dependencies | Required execution environment and current source; remain fail-closed. | +| Decide release; Check release ref; Verify npm will accept this workflow's OIDC identity | Daily eligibility, ownership, trigger and identity protections remain fail-closed. Credential-file removal remains mandatory. | +| Update release version; Generate changelog and release notes; Run release checks; Commit release files | Required artifacts and gates remain fail-closed. Summary flags were removed from generation and the release:notes script, so an optional summary write cannot invalidate otherwise valid artifacts. | +| Merge release metadata through protected PR | Core branch write, protection checks, authoritative merge and merged SHA output remain fail-closed. PR title/body refresh and diagnostic-file removal now warn on failure. Existing optional bot-thread resolution and workflow-approval attempts were already guarded; required merge approval is still enforced by GitHub. | +| Close superseded release PRs | Runs after the protected merge; each failure warns and continues. | +| Clean up merged release branch | Optional deletion moved out of both the normal merge and metadata-resume paths. Lookup and leased deletion failures warn. A failed lookup never supplies a deletion lease. | +| Write release summary | Separate optional step; write failure warns without affecting the release. | +| Verify merged release; Publish npm package; Push release tag | Exact merged source, clean artifacts, provenance, registry confirmation and tag identity remain fail-closed. | +| Verify bun install of published package; Create GitHub release; Fail the job on bun verification failure | Existing policy remains: Bun mirror failure cannot prevent GitHub release creation after successful publication and tagging, but the final gate still reports failed Bun verification. GitHub release creation itself remains a required delivery operation. | + +All seven existing identity, merged-artifact, publication, tag, Bun-verification, GitHub-release and final Bun-failure step definitions were compared structurally with d2abfe6 and are unchanged. All 20 shell scripts pass bash syntax validation. Tests execute complete named steps against synthetic command transports: first close fails and a later close succeeds; branch lookup/deletion, title/body edit, diagnostic cleanup and summary writes can fail; the verified merged SHA still reaches the next step; a registry that never confirms an attested artifact still causes publication failure. Restored focused suites pass 36/36 and the native Bun decision suite passes 19/19. Independent runtime and configuration revert controls are recorded in the PM history; completed full gates and final-head CI are recorded after execution. diff --git a/package.json b/package.json index 42da101c..c525c1c9 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "changelog:check": "npm run changelog:full -- --check", "release:check": "npm run typecheck && npm run build && npm run docstring && npm run lint && npm run duplication && npm run privacy && npm run coverage && npm run audit:prod && npm run pack:dry-run && npm run changelog:check && npm run verify:release-publish-attestation", "prepublishOnly": "npm run release:check", - "release:notes": "pm-changelog --pm-root .agents/pm --pm-arg=--output-limit --pm-arg=unbounded --pm-arg=--output-budget --pm-arg=unbounded --stdout --since-previous-tag --until-release-tag --release-version-from-package --date-from-version --item-url-base https://github.com/unbraind/pm-github/blob/main/.agents/pm --respect-item-release --pm-bin ./node_modules/.bin/pm --github-step-summary", + "release:notes": "pm-changelog --pm-root .agents/pm --pm-arg=--output-limit --pm-arg=unbounded --pm-arg=--output-budget --pm-arg=unbounded --stdout --since-previous-tag --until-release-tag --release-version-from-package --date-from-version --item-url-base https://github.com/unbraind/pm-github/blob/main/.agents/pm --respect-item-release --pm-bin ./node_modules/.bin/pm", "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", diff --git a/test/daily-release-plan.test.ts b/test/daily-release-plan.test.ts index 9740043b..ee2ca3ac 100644 --- a/test/daily-release-plan.test.ts +++ b/test/daily-release-plan.test.ts @@ -1,9 +1,9 @@ /** Daily release policy is independent of GitHub and the wall clock. */ import assert from "node:assert/strict"; import { spawnSync, type SpawnSyncReturns } from "node:child_process"; -import { mkdtempSync, readFileSync, rmSync } from "node:fs"; +import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import { createRequire } from "node:module"; -import { tmpdir } from "node:os"; +import { devNull, tmpdir } from "node:os"; import { join } from "node:path"; import test from "node:test"; import { decideDailyRelease, type DailyReleaseInput } from "../scripts/daily-release-plan.ts"; @@ -70,7 +70,7 @@ test("an unchanged release or a publication already dated today skips, even afte }); /** Workflow fields exercised by the shell and configuration regressions. */ -interface WorkflowStep { name: string; run?: string; env?: Record; if?: string } +interface WorkflowStep { name: string; run?: string; env?: Record; if?: string; "continue-on-error"?: boolean } /** Resolve the YAML parser already bundled with the pinned SDK. */ const { parse }: { parse: (source: string) => { jobs: { release: { steps: WorkflowStep[] } } } } = createRequire(createRequire(import.meta.url).resolve("@unbrained/pm-cli/package.json"))("yaml"); @@ -176,7 +176,7 @@ test("workflow rebuilds before preparation and runs the canonical generator befo assert.equal(steps.filter(candidate => candidate.run?.includes("gh pr list")).length, 1); assert.match(decision.run, /--json number,headRefName,isCrossRepository,author/); assert.doesNotMatch(decision.run, /export RELEASE_EXISTING_PRS=/); - assert.ok(steps.indexOf(step("Close superseded release PRs")) > steps.indexOf(step("Run release checks"))); + assert.ok(steps.indexOf(step("Close superseded release PRs")) > steps.indexOf(refresh)); }); test("a failed GitHub candidate lookup aborts instead of exporting an empty success", () => { @@ -205,3 +205,133 @@ test("the push and cleanup boundary refuses an existing ref without a selected w } } finally { rmSync(root, { recursive: true, force: true }); } }); + +/** Execute a complete named step with isolated files and synthetic command transports. */ +function runReleaseStep(name: string, commands: string, extraEnv: NodeJS.ProcessEnv = {}): SpawnSyncReturns & { outputs: string; summary: string } { + const root = mkdtempSync(join(tmpdir(), "pm-github-release-cleanup-")); + const output = join(root, "outputs"); + const summary = join(root, "summary"); + writeFileSync(join(root, "package.json"), JSON.stringify({ name: "example-package" })); + writeFileSync(join(root, "CHANGELOG.md"), "Canonical changelog\n"); + writeFileSync(join(root, "RELEASE_NOTES.md"), "Release notes\n"); + try { + const result: SpawnSyncReturns = spawnSync("bash", ["-c", `${commands}\n${step(name).run}\nprintf 'release continues\n'`], { + encoding: "utf8", cwd: root, env: { ...process.env, HOME: root, GIT_CONFIG_GLOBAL: devNull, GIT_CONFIG_NOSYSTEM: "1", + GITHUB_REPOSITORY: "example/package", GITHUB_OUTPUT: output, + GITHUB_STEP_SUMMARY: extraEnv.TEST_SUMMARY_FAILURE ? join(root, "missing", "summary") : summary, + RELEASE_BRANCH: "release/2026.10.18", RELEASE_PR_NUMBER: "145", RELEASE_BASE_SHA: "base-main", + RELEASE_TAG: "v2026.10.19", NPM_VERSION: "2026.10.19", MERGED_SHA: "merged-main", ...extraEnv }, + }); + return { ...result, outputs: existsSync(output) ? readFileSync(output, "utf8") : "", + summary: existsSync(summary) ? readFileSync(summary, "utf8") : "" }; + } finally { rmSync(root, { recursive: true, force: true }); } +} + +/** GitHub and Git command responses for a verified merge, including optional failures. */ +const mergedReleaseCommands = ` + git() { + case "$1" in + rev-parse) if [[ "$2" == origin/main ]]; then printf base-main; else printf release-commit; fi ;; + ls-remote) printf remote-sha ;; + esac + } + gh() { + case "$1 $2" in + "pr edit") printf 'metadata edit denied' >&2; return 47 ;; + "pr view") printf '{"merge_state":"CLEAN","unsettled":[],"failing":[]}' ;; + "api graphql") return 0 ;; + "api --method") printf '{"sha":"merged-main"}' ;; + *) printf 'Unexpected GitHub command' >&2; return 99 ;; + esac + } + rm() { printf 'diagnostic cleanup denied' >&2; return 48; } +`; + +test("superseded close failures warn per PR and still try later PRs before continuing the release", () => { + const result = runReleaseStep("Close superseded release PRs", ` + gh() { + if [[ "$3" == 144 ]]; then printf 'close denied\\nsecond line 50%% unavailable\\r' >&2; return 47; fi + printf 'closed PR #%s\\n' "$3" + } + `, { SUPERSEDED_PR_NUMBERS: "144 143" }); + assert.equal(result.status, 0, result.stderr); + assert.match(result.stdout, /::warning::Could not close superseded release PR #144: close denied second line 50%25 unavailable/); + assert.equal((result.stdout.match(/::warning::/g) ?? []).length, 1); + assert.match(result.stdout, /closed PR #143/); + assert.match(result.stdout, /release continues/); +}); + +test("optional branch lookup or leased deletion failures warn and cannot stop the release", () => { + const cleanup = step("Clean up merged release branch"); + assert.ok(steps.indexOf(cleanup) > steps.indexOf(step("Merge release metadata through protected PR"))); + for (const failure of ["lookup", "delete", "none", "absent"]) { + const result = runReleaseStep(cleanup.name, ` + git() { + if [[ "$1" == ls-remote ]]; then + if [[ "$TEST_FAILURE" == lookup ]]; then printf 'lookup denied' >&2; return 47; fi + if [[ "$TEST_FAILURE" != absent ]]; then printf 'remote-sha\\trefs/heads/release/2026.10.18'; fi + elif [[ "$1" == push ]]; then + [[ "$*" == 'push --force-with-lease=refs/heads/release/2026.10.18:remote-sha origin :refs/heads/release/2026.10.18' ]] || return 99 + if [[ "$TEST_FAILURE" == delete ]]; then printf 'delete denied' >&2; return 48; fi + fi + } + `, { TEST_FAILURE: failure }); + assert.equal(result.status, 0, result.stderr); + assert.match(result.stdout, /release continues/); + if (failure === "lookup" || failure === "delete") assert.match(result.stdout, new RegExp(`::warning::.*release/2026\\.10\\.18.*${failure} denied`)); + else assert.doesNotMatch(result.stdout, /::warning::/); + } +}); + +test("metadata edit and diagnostic cleanup failures preserve the verified merged output", () => { + const result = runReleaseStep("Merge release metadata through protected PR", mergedReleaseCommands); + assert.equal(result.status, 0, result.stderr); + assert.match(result.stdout, /::warning::Could not refresh title\/body of release PR #145: metadata edit denied/); + assert.match(result.stdout, /::warning::Could not remove release merge diagnostic files: diagnostic cleanup denied/); + assert.match(result.stdout, /release continues/); + assert.equal(result.outputs, "merged_sha=merged-main\npr_number=145\n"); +}); + +test("resuming existing main metadata does not perform branch cleanup before publishing", () => { + const result = runReleaseStep("Merge release metadata through protected PR", ` + git() { + if [[ "$1" == rev-parse ]]; then printf base-main; + elif [[ "$1" == ls-remote ]]; then printf remote-sha; + elif [[ "$1" == push ]]; then printf 'unexpected branch deletion' >&2; return 47; fi + } + `); + assert.equal(result.status, 0, result.stderr); + assert.equal(result.outputs, "merged_sha=base-main\npr_number=\n"); + assert.doesNotMatch(result.stderr, /unexpected branch deletion/); +}); + +test("summary failure only warns and artifact generation has no implicit summary writes", () => { + const generation = step("Generate changelog and release notes"); + const manifest = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")) as { scripts: Record }; + assert.doesNotMatch(generation.run, /--github-step-summary/); + assert.doesNotMatch(manifest.scripts["release:notes"]!, /--github-step-summary/); + assert.doesNotMatch(step("Merge release metadata through protected PR").run, /GITHUB_STEP_SUMMARY/); + for (const failure of ["", "true"]) { + const result = runReleaseStep("Write release summary", "", { TEST_SUMMARY_FAILURE: failure }); + assert.equal(result.status, 0, result.stderr); + assert.match(result.stdout, /release continues/); + if (failure) assert.match(result.stdout, /::warning::Could not write release summary: .*/); + else assert.equal(result.summary, "Release metadata at merged-main.\n\nCanonical changelog\nRelease notes\n"); + } +}); + +test("publication still fails closed when the registry never confirms an attested publish", () => { + const publish = step("Publish npm package"); + assert.notEqual(publish["continue-on-error"], true); + const result = runReleaseStep(publish.name, ` + npm() { + if [[ "$1" == --version ]]; then printf 11.19.0; + elif [[ "$1" == config ]]; then return 0; + else printf 'registry unavailable' >&2; return 47; fi + } + sleep() { :; } + `); + assert.equal(result.status, 1, result.stderr); + assert.match(result.stdout, /Refusing to downgrade supply-chain attestations/); + assert.doesNotMatch(result.stdout, /release continues/); +}); From 79e909de87950d7d6718b3d15aa7344d35bc8e8c Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Mon, 19 Oct 2026 10:23:07 +0200 Subject: [PATCH 04/12] Isolate release-step diagnostic files in each test fixture --- .agents/pm/history/pm-github-l1ot.jsonl | 1 + .agents/pm/issues/pm-github-l1ot.toon | 5 +++-- test/daily-release-plan.test.ts | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.agents/pm/history/pm-github-l1ot.jsonl b/.agents/pm/history/pm-github-l1ot.jsonl index 1e165aa7..6d9707b8 100644 --- a/.agents/pm/history/pm-github-l1ot.jsonl +++ b/.agents/pm/history/pm-github-l1ot.jsonl @@ -36,3 +36,4 @@ {"hash_algorithm":"sha256","ts":"2026-10-19T08:21:09.595Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/17","value":{"created_at":"2026-10-19T08:21:09.595Z","author":"codex-agent","text":"Review-round sweep covered all 23 release steps. Additional non-essential blockers fixed: selected branch deletion in normal/resume paths, PR title/body refresh, merge diagnostic-file deletion, and implicit/explicit step-summary writes. These now warn; summaries are separate from required changelog/notes generation. All seven identity/verification/publish/tag/Bun/GitHub-release/final-gate step definitions are structurally identical to d2abfe6; 20 shell scripts pass bash -n. Focused suites pass 36/36 and native Bun decisions 19/19. Revert controls retained all tests: six runtime reverts (close failure handler, metadata edit handler, diagnostic deletion handler, branch deletion handler, resume branch deletion, summary handler) each fail with ERR_ASSERTION and exit 1. Three configuration reverts (cleanup order, generator summary flag, notes-script summary flag) independently fail their contract assertions, exit 1. Each implementation restored. Publication failure remains an executed negative case: an unconfirmed registry exits 1 without continuation."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T08:21:09.595Z"}],"before_hash":"49c291b70dbdeffc53879de56e01ab01ad82e09ab618d067e8ffb2adb1808622","after_hash":"20c39d7c0eea5c550025423ec76074194e7162759af36af1cc5897c8589fd69b","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"95ad675e919b4074ff7a12cce0f7a3451a4f524f2ccae3df6fb7414683a92e13"} {"hash_algorithm":"sha256","ts":"2026-10-19T08:21:49.170Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/2/path","value":"scripts/daily-release-plan.ts"},{"op":"replace","path":"/metadata/files/1/path","value":"package.json"},{"op":"add","path":"/metadata/files/3","value":{"path":"test/daily-release-plan.test.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T08:21:49.170Z"}],"before_hash":"20c39d7c0eea5c550025423ec76074194e7162759af36af1cc5897c8589fd69b","after_hash":"def9074b98099f2c271067f7de812665bcb9ba5baa44b1effacef50247f860b2","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"333fe09812b56d11386e6b236a9b9f7d7f7359b02fdd637cddd39562e5d1db3c"} {"hash_algorithm":"sha256","ts":"2026-10-19T08:21:51.882Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/18","value":{"created_at":"2026-10-19T08:21:51.882Z","author":"codex-agent","text":"Starting npm run release:check then bun run release:check sequentially for the cleanup correction, each with isolated Git identity. Linked PM decision suite passed 19/19; tracking-disabled policy remains unchanged, so results are recorded in comments. No dependency pins, coverage thresholds, source scope, test time budgets or publication policy changed."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T08:21:51.882Z"}],"before_hash":"def9074b98099f2c271067f7de812665bcb9ba5baa44b1effacef50247f860b2","after_hash":"5a6a149125a85252331358e186d014c4cadfef945490c5ae773f944f3dd56009","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"0e09a6178ba2bc7318c9c5537288214216ac6c124c9f76eb9e0cab91122b2bde"} +{"hash_algorithm":"sha256","ts":"2026-10-19T08:23:02.308Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/19","value":{"created_at":"2026-10-19T08:23:02.308Z","author":"codex-agent","text":"Tightened the new release-step test fixture so deliberate diagnostic cleanup failures leave all temporary files inside that fixture's TMPDIR, which final fixture teardown removes. Restarted the unfinished npm gate before coverage began; no completed gate was repeated or counted as a failure. The same 36 focused and 19 native Bun checks remain required on the final code."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T08:23:02.308Z"}],"before_hash":"5a6a149125a85252331358e186d014c4cadfef945490c5ae773f944f3dd56009","after_hash":"9d096d671a1f4e90bbd59384eef57393224439c123b1f1e99790104d95c6018b","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"150928b98f914cc00ec0b6c44f045af6087ce0d91729c7d7f8955201296a28c6"} diff --git a/.agents/pm/issues/pm-github-l1ot.toon b/.agents/pm/issues/pm-github-l1ot.toon index 8731221f..960e4309 100644 --- a/.agents/pm/issues/pm-github-l1ot.toon +++ b/.agents/pm/issues/pm-github-l1ot.toon @@ -6,12 +6,12 @@ status: in_progress priority: 1 tags[3]: changelog,changelog-unreleased,release created_at: "2026-10-15T09:25:36.259Z" -updated_at: "2026-10-19T08:21:51.882Z" +updated_at: "2026-10-19T08:23:02.308Z" assignee: codex-agent claim_principal: codex-agent author: claude-hub acceptance_criteria: "release.yml generates CHANGELOG.md and the release notes through the package's own changelog scripts with the same status filter as changelog:check; a test fails if the two disagree; a release run with an in_progress item tagged changelog-unreleased and delivered since the last tag either succeeds or fails before any bump with a clear reason" -comments[19]{created_at,author,text}: +comments[20]{created_at,author,text}: "2026-10-18T18:19:00.873Z",codex-agent,"Reproduced the release generator split: release.yml uses pm-changelog closed-only output while changelog:check includes tagged pending work. The next run fails before refreshing the protected release PR. Plan: one canonical generator, rebuild from freshly fetched main, refresh or supersede stale PRs, and enforce one release per calendar day with pure decision regressions. Related: https://github.com/unbraind/pm-github/pull/144 and https://github.com/unbraind/pm-ado/issues/33" "2026-10-18T18:22:11.207Z",codex-agent,"Behavioral function-only revert proof: keeping the same decision function and tests, restored the old unconditional same-day eligibility and today-only branch selection. node --test test/daily-release-plan.test.ts failed 2/4 assertions: the old PR branch was abandoned and a second same-day release was allowed. Restored implementation: focused release suites passed 13/13 and the linked PM command passed. The release generator now invokes changelog:full, changelog:check and release:notes; lease-protected refresh updates the reused PR title/version to today." "2026-10-18T18:32:32.900Z",codex-agent,"Consumer context: https://github.com/unbraind/pm-beads/pull/145 https://github.com/unbraind/pm-brief/pull/154 https://github.com/unbraind/pm-context/pull/147 https://github.com/unbraind/pm-csv/pull/163 https://github.com/unbraind/pm-linear/pull/150 https://github.com/unbraind/pm-slack-standup/pull/127. Separate attribution fix: https://github.com/unbraind/pm-ado/issues/33. The proof report contains all 8 real imported terminal rows, exact before/main lifecycle fields, explicit completed_at projection limits, historical release placement and momentum. Six baseline entries are under Unreleased; all 8 projected rows render under historical releases when terminal statuses are selected." @@ -31,6 +31,7 @@ comments[19]{created_at,author,text}: "2026-10-19T08:13:11.052Z",codex-agent,"Review round 1 of #152, thread PRRT_kwDOTbN4pM6t1WSO: make superseded release PR cleanup best-effort, move it after the selected release metadata is merged, and test that individual close failures warn with PR number and error while later candidates and the release continue. Sweep every release workflow step for non-essential operations that can block publication; preserve fail-closed publication, provenance and verification boundaries." "2026-10-19T08:21:09.595Z",codex-agent,"Review-round sweep covered all 23 release steps. Additional non-essential blockers fixed: selected branch deletion in normal/resume paths, PR title/body refresh, merge diagnostic-file deletion, and implicit/explicit step-summary writes. These now warn; summaries are separate from required changelog/notes generation. All seven identity/verification/publish/tag/Bun/GitHub-release/final-gate step definitions are structurally identical to d2abfe6; 20 shell scripts pass bash -n. Focused suites pass 36/36 and native Bun decisions 19/19. Revert controls retained all tests: six runtime reverts (close failure handler, metadata edit handler, diagnostic deletion handler, branch deletion handler, resume branch deletion, summary handler) each fail with ERR_ASSERTION and exit 1. Three configuration reverts (cleanup order, generator summary flag, notes-script summary flag) independently fail their contract assertions, exit 1. Each implementation restored. Publication failure remains an executed negative case: an unconfirmed registry exits 1 without continuation." "2026-10-19T08:21:51.882Z",codex-agent,"Starting npm run release:check then bun run release:check sequentially for the cleanup correction, each with isolated Git identity. Linked PM decision suite passed 19/19; tracking-disabled policy remains unchanged, so results are recorded in comments. No dependency pins, coverage thresholds, source scope, test time budgets or publication policy changed." + "2026-10-19T08:23:02.308Z",codex-agent,"Tightened the new release-step test fixture so deliberate diagnostic cleanup failures leave all temporary files inside that fixture's TMPDIR, which final fixture teardown removes. Restarted the unfinished npm gate before coverage began; no completed gate was repeated or counted as a failure. The same 36 focused and 19 native Bun checks remain required on the final code." files[4]{path,scope}: .github/workflows/release.yml,project package.json,project diff --git a/test/daily-release-plan.test.ts b/test/daily-release-plan.test.ts index ee2ca3ac..c5a19a17 100644 --- a/test/daily-release-plan.test.ts +++ b/test/daily-release-plan.test.ts @@ -216,7 +216,7 @@ function runReleaseStep(name: string, commands: string, extraEnv: NodeJS.Process writeFileSync(join(root, "RELEASE_NOTES.md"), "Release notes\n"); try { const result: SpawnSyncReturns = spawnSync("bash", ["-c", `${commands}\n${step(name).run}\nprintf 'release continues\n'`], { - encoding: "utf8", cwd: root, env: { ...process.env, HOME: root, GIT_CONFIG_GLOBAL: devNull, GIT_CONFIG_NOSYSTEM: "1", + encoding: "utf8", cwd: root, env: { ...process.env, HOME: root, TMPDIR: root, GIT_CONFIG_GLOBAL: devNull, GIT_CONFIG_NOSYSTEM: "1", GITHUB_REPOSITORY: "example/package", GITHUB_OUTPUT: output, GITHUB_STEP_SUMMARY: extraEnv.TEST_SUMMARY_FAILURE ? join(root, "missing", "summary") : summary, RELEASE_BRANCH: "release/2026.10.18", RELEASE_PR_NUMBER: "145", RELEASE_BASE_SHA: "base-main", From 9a312326e9211dfc9702332b5f486059d498a3b8 Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Mon, 19 Oct 2026 10:27:55 +0200 Subject: [PATCH 05/12] Contain npm configuration inside release-step test fixtures --- .agents/pm/history/pm-github-l1ot.jsonl | 1 + .agents/pm/issues/pm-github-l1ot.toon | 5 +++-- test/daily-release-plan.test.ts | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.agents/pm/history/pm-github-l1ot.jsonl b/.agents/pm/history/pm-github-l1ot.jsonl index 6d9707b8..dbb97780 100644 --- a/.agents/pm/history/pm-github-l1ot.jsonl +++ b/.agents/pm/history/pm-github-l1ot.jsonl @@ -37,3 +37,4 @@ {"hash_algorithm":"sha256","ts":"2026-10-19T08:21:49.170Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/2/path","value":"scripts/daily-release-plan.ts"},{"op":"replace","path":"/metadata/files/1/path","value":"package.json"},{"op":"add","path":"/metadata/files/3","value":{"path":"test/daily-release-plan.test.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T08:21:49.170Z"}],"before_hash":"20c39d7c0eea5c550025423ec76074194e7162759af36af1cc5897c8589fd69b","after_hash":"def9074b98099f2c271067f7de812665bcb9ba5baa44b1effacef50247f860b2","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"333fe09812b56d11386e6b236a9b9f7d7f7359b02fdd637cddd39562e5d1db3c"} {"hash_algorithm":"sha256","ts":"2026-10-19T08:21:51.882Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/18","value":{"created_at":"2026-10-19T08:21:51.882Z","author":"codex-agent","text":"Starting npm run release:check then bun run release:check sequentially for the cleanup correction, each with isolated Git identity. Linked PM decision suite passed 19/19; tracking-disabled policy remains unchanged, so results are recorded in comments. No dependency pins, coverage thresholds, source scope, test time budgets or publication policy changed."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T08:21:51.882Z"}],"before_hash":"def9074b98099f2c271067f7de812665bcb9ba5baa44b1effacef50247f860b2","after_hash":"5a6a149125a85252331358e186d014c4cadfef945490c5ae773f944f3dd56009","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"0e09a6178ba2bc7318c9c5537288214216ac6c124c9f76eb9e0cab91122b2bde"} {"hash_algorithm":"sha256","ts":"2026-10-19T08:23:02.308Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/19","value":{"created_at":"2026-10-19T08:23:02.308Z","author":"codex-agent","text":"Tightened the new release-step test fixture so deliberate diagnostic cleanup failures leave all temporary files inside that fixture's TMPDIR, which final fixture teardown removes. Restarted the unfinished npm gate before coverage began; no completed gate was repeated or counted as a failure. The same 36 focused and 19 native Bun checks remain required on the final code."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T08:23:02.308Z"}],"before_hash":"5a6a149125a85252331358e186d014c4cadfef945490c5ae773f944f3dd56009","after_hash":"9d096d671a1f4e90bbd59384eef57393224439c123b1f1e99790104d95c6018b","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"150928b98f914cc00ec0b6c44f045af6087ce0d91729c7d7f8955201296a28c6"} +{"hash_algorithm":"sha256","ts":"2026-10-19T08:27:49.591Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/20","value":{"created_at":"2026-10-19T08:27:49.591Z","author":"codex-agent","text":"The publication failure fixture now explicitly isolates uppercase and lowercase npm user/global configuration paths as well as HOME and TMPDIR. This prevents its real credential-scrub code from touching inherited runner configuration. Restarted the unfinished gate against this final fixture isolation; no successful gate was repeated or counted as a failure."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T08:27:49.591Z"}],"before_hash":"9d096d671a1f4e90bbd59384eef57393224439c123b1f1e99790104d95c6018b","after_hash":"1ee1b0a1576f6d7cabe737b59b429a627ac9cdd36bedc51bbf5b9b59acbfece7","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"8a155cea801af4b3f1443a8b28fd28c03d048d028828372f8db0d5a93b02fbdf"} diff --git a/.agents/pm/issues/pm-github-l1ot.toon b/.agents/pm/issues/pm-github-l1ot.toon index 960e4309..3a2f76b7 100644 --- a/.agents/pm/issues/pm-github-l1ot.toon +++ b/.agents/pm/issues/pm-github-l1ot.toon @@ -6,12 +6,12 @@ status: in_progress priority: 1 tags[3]: changelog,changelog-unreleased,release created_at: "2026-10-15T09:25:36.259Z" -updated_at: "2026-10-19T08:23:02.308Z" +updated_at: "2026-10-19T08:27:49.591Z" assignee: codex-agent claim_principal: codex-agent author: claude-hub acceptance_criteria: "release.yml generates CHANGELOG.md and the release notes through the package's own changelog scripts with the same status filter as changelog:check; a test fails if the two disagree; a release run with an in_progress item tagged changelog-unreleased and delivered since the last tag either succeeds or fails before any bump with a clear reason" -comments[20]{created_at,author,text}: +comments[21]{created_at,author,text}: "2026-10-18T18:19:00.873Z",codex-agent,"Reproduced the release generator split: release.yml uses pm-changelog closed-only output while changelog:check includes tagged pending work. The next run fails before refreshing the protected release PR. Plan: one canonical generator, rebuild from freshly fetched main, refresh or supersede stale PRs, and enforce one release per calendar day with pure decision regressions. Related: https://github.com/unbraind/pm-github/pull/144 and https://github.com/unbraind/pm-ado/issues/33" "2026-10-18T18:22:11.207Z",codex-agent,"Behavioral function-only revert proof: keeping the same decision function and tests, restored the old unconditional same-day eligibility and today-only branch selection. node --test test/daily-release-plan.test.ts failed 2/4 assertions: the old PR branch was abandoned and a second same-day release was allowed. Restored implementation: focused release suites passed 13/13 and the linked PM command passed. The release generator now invokes changelog:full, changelog:check and release:notes; lease-protected refresh updates the reused PR title/version to today." "2026-10-18T18:32:32.900Z",codex-agent,"Consumer context: https://github.com/unbraind/pm-beads/pull/145 https://github.com/unbraind/pm-brief/pull/154 https://github.com/unbraind/pm-context/pull/147 https://github.com/unbraind/pm-csv/pull/163 https://github.com/unbraind/pm-linear/pull/150 https://github.com/unbraind/pm-slack-standup/pull/127. Separate attribution fix: https://github.com/unbraind/pm-ado/issues/33. The proof report contains all 8 real imported terminal rows, exact before/main lifecycle fields, explicit completed_at projection limits, historical release placement and momentum. Six baseline entries are under Unreleased; all 8 projected rows render under historical releases when terminal statuses are selected." @@ -32,6 +32,7 @@ comments[20]{created_at,author,text}: "2026-10-19T08:21:09.595Z",codex-agent,"Review-round sweep covered all 23 release steps. Additional non-essential blockers fixed: selected branch deletion in normal/resume paths, PR title/body refresh, merge diagnostic-file deletion, and implicit/explicit step-summary writes. These now warn; summaries are separate from required changelog/notes generation. All seven identity/verification/publish/tag/Bun/GitHub-release/final-gate step definitions are structurally identical to d2abfe6; 20 shell scripts pass bash -n. Focused suites pass 36/36 and native Bun decisions 19/19. Revert controls retained all tests: six runtime reverts (close failure handler, metadata edit handler, diagnostic deletion handler, branch deletion handler, resume branch deletion, summary handler) each fail with ERR_ASSERTION and exit 1. Three configuration reverts (cleanup order, generator summary flag, notes-script summary flag) independently fail their contract assertions, exit 1. Each implementation restored. Publication failure remains an executed negative case: an unconfirmed registry exits 1 without continuation." "2026-10-19T08:21:51.882Z",codex-agent,"Starting npm run release:check then bun run release:check sequentially for the cleanup correction, each with isolated Git identity. Linked PM decision suite passed 19/19; tracking-disabled policy remains unchanged, so results are recorded in comments. No dependency pins, coverage thresholds, source scope, test time budgets or publication policy changed." "2026-10-19T08:23:02.308Z",codex-agent,"Tightened the new release-step test fixture so deliberate diagnostic cleanup failures leave all temporary files inside that fixture's TMPDIR, which final fixture teardown removes. Restarted the unfinished npm gate before coverage began; no completed gate was repeated or counted as a failure. The same 36 focused and 19 native Bun checks remain required on the final code." + "2026-10-19T08:27:49.591Z",codex-agent,The publication failure fixture now explicitly isolates uppercase and lowercase npm user/global configuration paths as well as HOME and TMPDIR. This prevents its real credential-scrub code from touching inherited runner configuration. Restarted the unfinished gate against this final fixture isolation; no successful gate was repeated or counted as a failure. files[4]{path,scope}: .github/workflows/release.yml,project package.json,project diff --git a/test/daily-release-plan.test.ts b/test/daily-release-plan.test.ts index c5a19a17..a17066fc 100644 --- a/test/daily-release-plan.test.ts +++ b/test/daily-release-plan.test.ts @@ -217,6 +217,8 @@ function runReleaseStep(name: string, commands: string, extraEnv: NodeJS.Process try { const result: SpawnSyncReturns = spawnSync("bash", ["-c", `${commands}\n${step(name).run}\nprintf 'release continues\n'`], { encoding: "utf8", cwd: root, env: { ...process.env, HOME: root, TMPDIR: root, GIT_CONFIG_GLOBAL: devNull, GIT_CONFIG_NOSYSTEM: "1", + NPM_CONFIG_USERCONFIG: join(root, ".npmrc"), npm_config_userconfig: join(root, ".npmrc"), + NPM_CONFIG_GLOBALCONFIG: join(root, "global.npmrc"), npm_config_globalconfig: join(root, "global.npmrc"), GITHUB_REPOSITORY: "example/package", GITHUB_OUTPUT: output, GITHUB_STEP_SUMMARY: extraEnv.TEST_SUMMARY_FAILURE ? join(root, "missing", "summary") : summary, RELEASE_BRANCH: "release/2026.10.18", RELEASE_PR_NUMBER: "145", RELEASE_BASE_SHA: "base-main", From cea1d6fbd39864455d29a8d205012fb829c97d76 Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Mon, 19 Oct 2026 10:53:16 +0200 Subject: [PATCH 06/12] Parse the complete release approval step in its regression fixture --- .agents/pm/history/pm-github-l1ot.jsonl | 3 ++ .agents/pm/issues/pm-github-l1ot.toon | 18 ++++++++--- docs/daily-release-refresh.md | 4 ++- test/release-approval.test.ts | 43 +++++++++++++++++++------ 4 files changed, 53 insertions(+), 15 deletions(-) diff --git a/.agents/pm/history/pm-github-l1ot.jsonl b/.agents/pm/history/pm-github-l1ot.jsonl index dbb97780..ea9d7974 100644 --- a/.agents/pm/history/pm-github-l1ot.jsonl +++ b/.agents/pm/history/pm-github-l1ot.jsonl @@ -38,3 +38,6 @@ {"hash_algorithm":"sha256","ts":"2026-10-19T08:21:51.882Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/18","value":{"created_at":"2026-10-19T08:21:51.882Z","author":"codex-agent","text":"Starting npm run release:check then bun run release:check sequentially for the cleanup correction, each with isolated Git identity. Linked PM decision suite passed 19/19; tracking-disabled policy remains unchanged, so results are recorded in comments. No dependency pins, coverage thresholds, source scope, test time budgets or publication policy changed."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T08:21:51.882Z"}],"before_hash":"def9074b98099f2c271067f7de812665bcb9ba5baa44b1effacef50247f860b2","after_hash":"5a6a149125a85252331358e186d014c4cadfef945490c5ae773f944f3dd56009","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"0e09a6178ba2bc7318c9c5537288214216ac6c124c9f76eb9e0cab91122b2bde"} {"hash_algorithm":"sha256","ts":"2026-10-19T08:23:02.308Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/19","value":{"created_at":"2026-10-19T08:23:02.308Z","author":"codex-agent","text":"Tightened the new release-step test fixture so deliberate diagnostic cleanup failures leave all temporary files inside that fixture's TMPDIR, which final fixture teardown removes. Restarted the unfinished npm gate before coverage began; no completed gate was repeated or counted as a failure. The same 36 focused and 19 native Bun checks remain required on the final code."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T08:23:02.308Z"}],"before_hash":"5a6a149125a85252331358e186d014c4cadfef945490c5ae773f944f3dd56009","after_hash":"9d096d671a1f4e90bbd59384eef57393224439c123b1f1e99790104d95c6018b","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"150928b98f914cc00ec0b6c44f045af6087ce0d91729c7d7f8955201296a28c6"} {"hash_algorithm":"sha256","ts":"2026-10-19T08:27:49.591Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/20","value":{"created_at":"2026-10-19T08:27:49.591Z","author":"codex-agent","text":"The publication failure fixture now explicitly isolates uppercase and lowercase npm user/global configuration paths as well as HOME and TMPDIR. This prevents its real credential-scrub code from touching inherited runner configuration. Restarted the unfinished gate against this final fixture isolation; no successful gate was repeated or counted as a failure."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T08:27:49.591Z"}],"before_hash":"9d096d671a1f4e90bbd59384eef57393224439c123b1f1e99790104d95c6018b","after_hash":"1ee1b0a1576f6d7cabe737b59b429a627ac9cdd36bedc51bbf5b9b59acbfece7","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"8a155cea801af4b3f1443a8b28fd28c03d048d028828372f8db0d5a93b02fbdf"} +{"hash_algorithm":"sha256","ts":"2026-10-19T08:53:06.331Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"add","path":"/metadata/files/4","value":{"path":"test/release-approval.test.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T08:53:06.331Z"}],"before_hash":"1ee1b0a1576f6d7cabe737b59b429a627ac9cdd36bedc51bbf5b9b59acbfece7","after_hash":"48c0a76c09088f9baeb06d546b8fb75bfdac91a2fd51ffbbf931bc41392a11f3","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"933ce958e3e5689ea37dc27a60171ba45ec25ab12e1e84721589113bdd6907f2"} +{"hash_algorithm":"sha256","ts":"2026-10-19T08:53:07.943Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"tests_add","patch":[{"op":"add","path":"/metadata/tests/3","value":{"command":"node --test test/daily-release-plan.test.ts test/release-approval.test.ts test/release-workflow.test.ts test/verify-release-publish-attestation.test.ts","scope":"project","timeout_seconds":120,"provenance":{"author":"codex-agent","created_at":"2026-10-19T08:53:07.848Z","source_kind":"local_mutation","source_ref":"fix/release-pr-selection-followup"}}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T08:53:07.943Z"}],"before_hash":"48c0a76c09088f9baeb06d546b8fb75bfdac91a2fd51ffbbf931bc41392a11f3","after_hash":"f82c2dd97395868c417220a3a8c2536256c2e0a0e54b6f99bffcff8461f9be2f","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"dfa0962febe8ac6883c6964c1d883d26aa167ca1fb73ec1f17aa494215f75911"} +{"hash_algorithm":"sha256","ts":"2026-10-19T08:53:09.473Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/21","value":{"created_at":"2026-10-19T08:53:09.473Z","author":"codex-agent","text":"The first complete npm gate on cleanup head 9a312326e9211dfc9702332b5f486059d498a3b8 exited 1: 918/919 tests passed, one failed, zero cancellations/skips. Failure was the existing approval test's raw slice losing its old rm anchor and reaching GITHUB_OUTPUT without fixture configuration. Fixed the fixture by parsing the complete named YAML step and supplying isolated Git/GitHub transports, HOME, TMPDIR and output files. Kept all four approval assertions and added exact merged-SHA output verification. Expanded focused suites now pass 40/40; native Bun passes 19 decision tests plus four approval tests. The failed gate is not accepted or waived; npm and Bun full gates will run again at unchanged thresholds."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T08:53:09.473Z"}],"before_hash":"f82c2dd97395868c417220a3a8c2536256c2e0a0e54b6f99bffcff8461f9be2f","after_hash":"1a2ba8c5871122f24c137e360deda1bdcef3db6eff2a68b6f38c8eaa8782454d","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"2ead774aef0ccf888fec73a42e3d6e5612e313fb8c67921ad39bfb7da3bb4b22"} diff --git a/.agents/pm/issues/pm-github-l1ot.toon b/.agents/pm/issues/pm-github-l1ot.toon index 3a2f76b7..4ce9d084 100644 --- a/.agents/pm/issues/pm-github-l1ot.toon +++ b/.agents/pm/issues/pm-github-l1ot.toon @@ -6,12 +6,12 @@ status: in_progress priority: 1 tags[3]: changelog,changelog-unreleased,release created_at: "2026-10-15T09:25:36.259Z" -updated_at: "2026-10-19T08:27:49.591Z" +updated_at: "2026-10-19T08:53:09.473Z" assignee: codex-agent claim_principal: codex-agent author: claude-hub acceptance_criteria: "release.yml generates CHANGELOG.md and the release notes through the package's own changelog scripts with the same status filter as changelog:check; a test fails if the two disagree; a release run with an in_progress item tagged changelog-unreleased and delivered since the last tag either succeeds or fails before any bump with a clear reason" -comments[21]{created_at,author,text}: +comments[22]{created_at,author,text}: "2026-10-18T18:19:00.873Z",codex-agent,"Reproduced the release generator split: release.yml uses pm-changelog closed-only output while changelog:check includes tagged pending work. The next run fails before refreshing the protected release PR. Plan: one canonical generator, rebuild from freshly fetched main, refresh or supersede stale PRs, and enforce one release per calendar day with pure decision regressions. Related: https://github.com/unbraind/pm-github/pull/144 and https://github.com/unbraind/pm-ado/issues/33" "2026-10-18T18:22:11.207Z",codex-agent,"Behavioral function-only revert proof: keeping the same decision function and tests, restored the old unconditional same-day eligibility and today-only branch selection. node --test test/daily-release-plan.test.ts failed 2/4 assertions: the old PR branch was abandoned and a second same-day release was allowed. Restored implementation: focused release suites passed 13/13 and the linked PM command passed. The release generator now invokes changelog:full, changelog:check and release:notes; lease-protected refresh updates the reused PR title/version to today." "2026-10-18T18:32:32.900Z",codex-agent,"Consumer context: https://github.com/unbraind/pm-beads/pull/145 https://github.com/unbraind/pm-brief/pull/154 https://github.com/unbraind/pm-context/pull/147 https://github.com/unbraind/pm-csv/pull/163 https://github.com/unbraind/pm-linear/pull/150 https://github.com/unbraind/pm-slack-standup/pull/127. Separate attribution fix: https://github.com/unbraind/pm-ado/issues/33. The proof report contains all 8 real imported terminal rows, exact before/main lifecycle fields, explicit completed_at projection limits, historical release placement and momentum. Six baseline entries are under Unreleased; all 8 projected rows render under historical releases when terminal statuses are selected." @@ -33,12 +33,14 @@ comments[21]{created_at,author,text}: "2026-10-19T08:21:51.882Z",codex-agent,"Starting npm run release:check then bun run release:check sequentially for the cleanup correction, each with isolated Git identity. Linked PM decision suite passed 19/19; tracking-disabled policy remains unchanged, so results are recorded in comments. No dependency pins, coverage thresholds, source scope, test time budgets or publication policy changed." "2026-10-19T08:23:02.308Z",codex-agent,"Tightened the new release-step test fixture so deliberate diagnostic cleanup failures leave all temporary files inside that fixture's TMPDIR, which final fixture teardown removes. Restarted the unfinished npm gate before coverage began; no completed gate was repeated or counted as a failure. The same 36 focused and 19 native Bun checks remain required on the final code." "2026-10-19T08:27:49.591Z",codex-agent,The publication failure fixture now explicitly isolates uppercase and lowercase npm user/global configuration paths as well as HOME and TMPDIR. This prevents its real credential-scrub code from touching inherited runner configuration. Restarted the unfinished gate against this final fixture isolation; no successful gate was repeated or counted as a failure. -files[4]{path,scope}: + "2026-10-19T08:53:09.473Z",codex-agent,"The first complete npm gate on cleanup head 9a312326e9211dfc9702332b5f486059d498a3b8 exited 1: 918/919 tests passed, one failed, zero cancellations/skips. Failure was the existing approval test's raw slice losing its old rm anchor and reaching GITHUB_OUTPUT without fixture configuration. Fixed the fixture by parsing the complete named YAML step and supplying isolated Git/GitHub transports, HOME, TMPDIR and output files. Kept all four approval assertions and added exact merged-SHA output verification. Expanded focused suites now pass 40/40; native Bun passes 19 decision tests plus four approval tests. The failed gate is not accepted or waived; npm and Bun full gates will run again at unchanged thresholds." +files[5]{path,scope}: .github/workflows/release.yml,project package.json,project scripts/daily-release-plan.ts,project test/daily-release-plan.test.ts,project -tests[3]: + test/release-approval.test.ts,project +tests[4]: - command: node --test test/daily-release-plan.test.ts test/release-workflow.test.ts scope: project provenance: @@ -61,6 +63,14 @@ tests[3]: created_at: "2026-10-19T06:31:26.212Z" source_kind: local_mutation source_ref: fix/release-pr-selection-followup + - command: node --test test/daily-release-plan.test.ts test/release-approval.test.ts test/release-workflow.test.ts test/verify-release-publish-attestation.test.ts + scope: project + timeout_seconds: 120 + provenance: + author: codex-agent + created_at: "2026-10-19T08:53:07.848Z" + source_kind: local_mutation + source_ref: fix/release-pr-selection-followup docs[1]{path,scope}: docs/daily-release-refresh.md,project body: "" diff --git a/docs/daily-release-refresh.md b/docs/daily-release-refresh.md index 4e7cf44a..e84614e6 100644 --- a/docs/daily-release-refresh.md +++ b/docs/daily-release-refresh.md @@ -164,4 +164,6 @@ The sweep covered all 23 authored steps: | Verify merged release; Publish npm package; Push release tag | Exact merged source, clean artifacts, provenance, registry confirmation and tag identity remain fail-closed. | | Verify bun install of published package; Create GitHub release; Fail the job on bun verification failure | Existing policy remains: Bun mirror failure cannot prevent GitHub release creation after successful publication and tagging, but the final gate still reports failed Bun verification. GitHub release creation itself remains a required delivery operation. | -All seven existing identity, merged-artifact, publication, tag, Bun-verification, GitHub-release and final Bun-failure step definitions were compared structurally with d2abfe6 and are unchanged. All 20 shell scripts pass bash syntax validation. Tests execute complete named steps against synthetic command transports: first close fails and a later close succeeds; branch lookup/deletion, title/body edit, diagnostic cleanup and summary writes can fail; the verified merged SHA still reaches the next step; a registry that never confirms an attested artifact still causes publication failure. Restored focused suites pass 36/36 and the native Bun decision suite passes 19/19. Independent runtime and configuration revert controls are recorded in the PM history; completed full gates and final-head CI are recorded after execution. +All seven existing identity, merged-artifact, publication, tag, Bun-verification, GitHub-release and final Bun-failure step definitions were compared structurally with d2abfe6 and are unchanged. All 20 shell scripts pass bash syntax validation. Tests execute complete named steps against synthetic command transports: first close fails and a later close succeeds; branch lookup/deletion, title/body edit, diagnostic cleanup and summary writes can fail; the verified merged SHA still reaches the next step; a registry that never confirms an attested artifact still causes publication failure. Expanded focused suites pass 40/40; native Bun passes 19 decision tests and four approval tests. Independent runtime and configuration revert controls are recorded in the PM history; completed full gates and final-head CI are recorded after execution. + +The first full npm gate on the cleanup correction failed with 918/919 passing and one approval-fixture failure. Its raw text slice lost the old diagnostic-deletion anchor and reached an output variable absent from that fixture. The fixture now parses and executes the complete named merge step, isolates its output and temporary files, preserves all four approval assertions, and additionally verifies the exact merged SHA output. No product guard or gate threshold was weakened. Corrected full-gate receipts follow after execution. diff --git a/test/release-approval.test.ts b/test/release-approval.test.ts index 47d49290..520af65a 100644 --- a/test/release-approval.test.ts +++ b/test/release-approval.test.ts @@ -1,24 +1,30 @@ /** Run the actual protected release wait loop against synthetic GitHub transport. */ import assert from "node:assert/strict"; import { spawnSync } from "node:child_process"; -import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; -import { tmpdir } from "node:os"; +import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { createRequire } from "node:module"; +import { devNull, tmpdir } from "node:os"; import { join } from "node:path"; import test from "node:test"; -/** Execute only the merge-wait loop; no publication, Git pushes or live requests are possible. */ +/** Resolve workflow structure using the parser already bundled with the pinned SDK. */ +const { parse }: { parse: (source: string) => { jobs: { release: { steps: { name: string; run?: string }[] } } } } = + createRequire(createRequire(import.meta.url).resolve("@unbrained/pm-cli/package.json"))("yaml"); + +/** Execute the complete protected merge step against synthetic Git/GitHub transports. */ function waiting(scenario: string) { const directory = mkdtempSync(join(tmpdir(), "release-approval-")); try { const source = readFileSync(join(import.meta.dirname, "../.github/workflows/release.yml"), "utf8"); - const loop = source.slice(source.indexOf(" deadline=$(( SECONDS + 1800 ))"), source.indexOf(' rm -f "$merge_err" "$pr_view_err"')) - .split("\n").map(line => line.slice(10)).join("\n"); - assert.ok(loop.startsWith("deadline=")); + const merge = parse(source).jobs.release.steps.find(step => step.name === "Merge release metadata through protected PR"); + assert.ok(merge, "Missing protected release merge step"); + assert.equal(typeof merge.run, "string", "Protected release merge step must contain its script"); mkdirSync(join(directory, "bin")); writeFileSync(join(directory, "bin/gh"), `#!/usr/bin/env bash set -eu poll=0; [[ ! -f "$POLL" ]] || read -r poll < "$POLL" case "$*" in + 'pr edit '*) ;; 'pr view '*) printf '{"merge_state":"BLOCKED","unsettled":["test (22)"],"failing":[]}' ;; *'/merge '*) if [[ "$SCENARIO" == merged && "$poll" == 2 ]]; then printf '{"sha":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"}'; else exit 1; fi ;; @@ -31,10 +37,26 @@ case "$*" in *) exit 99 ;; esac `, { mode: 0o755 }); - const script = 'set -euo pipefail\npr_number=42; release_commit=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa; owner=fixture; repo=project\nsleep() { SECONDS=$((SECONDS + 900)); }\n' + loop; - return spawnSync("bash", ["-c", script], { cwd: directory, encoding: "utf8", timeout: 30_000, - env: { ...process.env, PATH: join(directory, "bin") + ":" + process.env.PATH, POLL: join(directory, "poll"), SCENARIO: scenario, - GITHUB_REPOSITORY: "fixture/project", GITHUB_SERVER_URL: "https://github.com" } }); + const script = ` + git() { + case "$1" in + fetch | push) return 0 ;; + rev-parse) if [[ "$2" == origin/main ]]; then printf base-main; else printf aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa; fi ;; + ls-remote) printf remote-sha ;; + *) printf 'Unexpected Git command' >&2; return 99 ;; + esac + } + sleep() { SECONDS=$((SECONDS + 900)); } + ${merge.run} + `; + const output = join(directory, "outputs"); + const result = spawnSync("bash", ["-c", script], { cwd: directory, encoding: "utf8", timeout: 30_000, + env: { ...process.env, HOME: directory, TMPDIR: directory, GIT_CONFIG_GLOBAL: devNull, GIT_CONFIG_NOSYSTEM: "1", + PATH: join(directory, "bin") + ":" + process.env.PATH, POLL: join(directory, "poll"), SCENARIO: scenario, + GITHUB_REPOSITORY: "fixture/project", GITHUB_SERVER_URL: "https://github.com", GITHUB_OUTPUT: output, + RELEASE_BRANCH: "release/2026.10.19", RELEASE_PR_NUMBER: "42", RELEASE_BASE_SHA: "base-main", + RELEASE_TAG: "v2026.10.19", NPM_VERSION: "2026.10.19" } }); + return { ...result, outputs: existsSync(output) ? readFileSync(output, "utf8") : "" }; } finally { rmSync(directory, { recursive: true, force: true }); } } @@ -64,4 +86,5 @@ test("release approval history does not block a subsequent verified merge", () = assert.equal(result.status, 0, result.stderr); assert.match(result.stdout, /Merged release PR #42 via merge API/); assert.doesNotMatch(result.stdout, /::error::/); + assert.equal(result.outputs, "merged_sha=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\npr_number=42\n"); }); From b54ed62af05226d8d67f9ea882eaa92d4006aaf4 Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Mon, 19 Oct 2026 11:41:56 +0200 Subject: [PATCH 07/12] Preserve unverified and subsequently updated release refs during cleanup --- .agents/pm/history/pm-github-l1ot.jsonl | 1 + .agents/pm/issues/pm-github-l1ot.toon | 5 ++-- .github/workflows/release.yml | 21 +++++++------ docs/daily-release-refresh.md | 6 ++-- test/daily-release-plan.test.ts | 39 ++++++++++++++++++++++--- 5 files changed, 55 insertions(+), 17 deletions(-) diff --git a/.agents/pm/history/pm-github-l1ot.jsonl b/.agents/pm/history/pm-github-l1ot.jsonl index ea9d7974..42ae00cc 100644 --- a/.agents/pm/history/pm-github-l1ot.jsonl +++ b/.agents/pm/history/pm-github-l1ot.jsonl @@ -41,3 +41,4 @@ {"hash_algorithm":"sha256","ts":"2026-10-19T08:53:06.331Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"add","path":"/metadata/files/4","value":{"path":"test/release-approval.test.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T08:53:06.331Z"}],"before_hash":"1ee1b0a1576f6d7cabe737b59b429a627ac9cdd36bedc51bbf5b9b59acbfece7","after_hash":"48c0a76c09088f9baeb06d546b8fb75bfdac91a2fd51ffbbf931bc41392a11f3","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"933ce958e3e5689ea37dc27a60171ba45ec25ab12e1e84721589113bdd6907f2"} {"hash_algorithm":"sha256","ts":"2026-10-19T08:53:07.943Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"tests_add","patch":[{"op":"add","path":"/metadata/tests/3","value":{"command":"node --test test/daily-release-plan.test.ts test/release-approval.test.ts test/release-workflow.test.ts test/verify-release-publish-attestation.test.ts","scope":"project","timeout_seconds":120,"provenance":{"author":"codex-agent","created_at":"2026-10-19T08:53:07.848Z","source_kind":"local_mutation","source_ref":"fix/release-pr-selection-followup"}}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T08:53:07.943Z"}],"before_hash":"48c0a76c09088f9baeb06d546b8fb75bfdac91a2fd51ffbbf931bc41392a11f3","after_hash":"f82c2dd97395868c417220a3a8c2536256c2e0a0e54b6f99bffcff8461f9be2f","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"dfa0962febe8ac6883c6964c1d883d26aa167ca1fb73ec1f17aa494215f75911"} {"hash_algorithm":"sha256","ts":"2026-10-19T08:53:09.473Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/21","value":{"created_at":"2026-10-19T08:53:09.473Z","author":"codex-agent","text":"The first complete npm gate on cleanup head 9a312326e9211dfc9702332b5f486059d498a3b8 exited 1: 918/919 tests passed, one failed, zero cancellations/skips. Failure was the existing approval test's raw slice losing its old rm anchor and reaching GITHUB_OUTPUT without fixture configuration. Fixed the fixture by parsing the complete named YAML step and supplying isolated Git/GitHub transports, HOME, TMPDIR and output files. Kept all four approval assertions and added exact merged-SHA output verification. Expanded focused suites now pass 40/40; native Bun passes 19 decision tests plus four approval tests. The failed gate is not accepted or waived; npm and Bun full gates will run again at unchanged thresholds."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T08:53:09.473Z"}],"before_hash":"f82c2dd97395868c417220a3a8c2536256c2e0a0e54b6f99bffcff8461f9be2f","after_hash":"1a2ba8c5871122f24c137e360deda1bdcef3db6eff2a68b6f38c8eaa8782454d","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"2ead774aef0ccf888fec73a42e3d6e5612e313fb8c67921ad39bfb7da3bb4b22"} +{"hash_algorithm":"sha256","ts":"2026-10-19T09:41:48.729Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/22","value":{"created_at":"2026-10-19T09:41:48.729Z","author":"codex-agent","text":"The npm gate at cea1d6f passed 919/919 with exact 100/100/100/100 across 15 modules (10535 lines/statements, 277 functions, 3225 branches; all covered, zero skips). Its unfinished Bun run was superseded before acceptance after two branch-ownership regressions exposed cleanup adopting a newer remote SHA or deleting an unverified ref during metadata resume. Both tests failed on the earlier cleanup, with simulated deletions proving the issue. Cleanup now requires the actual merged-PR output and leases deletion to the prepared local commit; it never adopts a newly read remote SHA. Reverting only that cleanup function to cea1d6f fails both ownership cases (2 failures, ERR_ASSERTION, exit 1). All nine earlier runtime/configuration controls were repeated and fail as intended. Restored focused suites pass 42/42, native Bun decisions 21/21 and approvals 4/4. Fresh npm and Bun full gates will verify this final safety correction at unchanged thresholds."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T09:41:48.729Z"}],"before_hash":"1a2ba8c5871122f24c137e360deda1bdcef3db6eff2a68b6f38c8eaa8782454d","after_hash":"7fc3f887a2f07e204b9568cff14fbd8ca622e0d371d17a14906556aa7bc10733","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"f347118909925623eeda11172b7f3b0d184e5074fe33ae9a83b89c364147fb1a"} diff --git a/.agents/pm/issues/pm-github-l1ot.toon b/.agents/pm/issues/pm-github-l1ot.toon index 4ce9d084..5c2953d7 100644 --- a/.agents/pm/issues/pm-github-l1ot.toon +++ b/.agents/pm/issues/pm-github-l1ot.toon @@ -6,12 +6,12 @@ status: in_progress priority: 1 tags[3]: changelog,changelog-unreleased,release created_at: "2026-10-15T09:25:36.259Z" -updated_at: "2026-10-19T08:53:09.473Z" +updated_at: "2026-10-19T09:41:48.729Z" assignee: codex-agent claim_principal: codex-agent author: claude-hub acceptance_criteria: "release.yml generates CHANGELOG.md and the release notes through the package's own changelog scripts with the same status filter as changelog:check; a test fails if the two disagree; a release run with an in_progress item tagged changelog-unreleased and delivered since the last tag either succeeds or fails before any bump with a clear reason" -comments[22]{created_at,author,text}: +comments[23]{created_at,author,text}: "2026-10-18T18:19:00.873Z",codex-agent,"Reproduced the release generator split: release.yml uses pm-changelog closed-only output while changelog:check includes tagged pending work. The next run fails before refreshing the protected release PR. Plan: one canonical generator, rebuild from freshly fetched main, refresh or supersede stale PRs, and enforce one release per calendar day with pure decision regressions. Related: https://github.com/unbraind/pm-github/pull/144 and https://github.com/unbraind/pm-ado/issues/33" "2026-10-18T18:22:11.207Z",codex-agent,"Behavioral function-only revert proof: keeping the same decision function and tests, restored the old unconditional same-day eligibility and today-only branch selection. node --test test/daily-release-plan.test.ts failed 2/4 assertions: the old PR branch was abandoned and a second same-day release was allowed. Restored implementation: focused release suites passed 13/13 and the linked PM command passed. The release generator now invokes changelog:full, changelog:check and release:notes; lease-protected refresh updates the reused PR title/version to today." "2026-10-18T18:32:32.900Z",codex-agent,"Consumer context: https://github.com/unbraind/pm-beads/pull/145 https://github.com/unbraind/pm-brief/pull/154 https://github.com/unbraind/pm-context/pull/147 https://github.com/unbraind/pm-csv/pull/163 https://github.com/unbraind/pm-linear/pull/150 https://github.com/unbraind/pm-slack-standup/pull/127. Separate attribution fix: https://github.com/unbraind/pm-ado/issues/33. The proof report contains all 8 real imported terminal rows, exact before/main lifecycle fields, explicit completed_at projection limits, historical release placement and momentum. Six baseline entries are under Unreleased; all 8 projected rows render under historical releases when terminal statuses are selected." @@ -34,6 +34,7 @@ comments[22]{created_at,author,text}: "2026-10-19T08:23:02.308Z",codex-agent,"Tightened the new release-step test fixture so deliberate diagnostic cleanup failures leave all temporary files inside that fixture's TMPDIR, which final fixture teardown removes. Restarted the unfinished npm gate before coverage began; no completed gate was repeated or counted as a failure. The same 36 focused and 19 native Bun checks remain required on the final code." "2026-10-19T08:27:49.591Z",codex-agent,The publication failure fixture now explicitly isolates uppercase and lowercase npm user/global configuration paths as well as HOME and TMPDIR. This prevents its real credential-scrub code from touching inherited runner configuration. Restarted the unfinished gate against this final fixture isolation; no successful gate was repeated or counted as a failure. "2026-10-19T08:53:09.473Z",codex-agent,"The first complete npm gate on cleanup head 9a312326e9211dfc9702332b5f486059d498a3b8 exited 1: 918/919 tests passed, one failed, zero cancellations/skips. Failure was the existing approval test's raw slice losing its old rm anchor and reaching GITHUB_OUTPUT without fixture configuration. Fixed the fixture by parsing the complete named YAML step and supplying isolated Git/GitHub transports, HOME, TMPDIR and output files. Kept all four approval assertions and added exact merged-SHA output verification. Expanded focused suites now pass 40/40; native Bun passes 19 decision tests plus four approval tests. The failed gate is not accepted or waived; npm and Bun full gates will run again at unchanged thresholds." + "2026-10-19T09:41:48.729Z",codex-agent,"The npm gate at cea1d6f passed 919/919 with exact 100/100/100/100 across 15 modules (10535 lines/statements, 277 functions, 3225 branches; all covered, zero skips). Its unfinished Bun run was superseded before acceptance after two branch-ownership regressions exposed cleanup adopting a newer remote SHA or deleting an unverified ref during metadata resume. Both tests failed on the earlier cleanup, with simulated deletions proving the issue. Cleanup now requires the actual merged-PR output and leases deletion to the prepared local commit; it never adopts a newly read remote SHA. Reverting only that cleanup function to cea1d6f fails both ownership cases (2 failures, ERR_ASSERTION, exit 1). All nine earlier runtime/configuration controls were repeated and fail as intended. Restored focused suites pass 42/42, native Bun decisions 21/21 and approvals 4/4. Fresh npm and Bun full gates will verify this final safety correction at unchanged thresholds." files[5]{path,scope}: .github/workflows/release.yml,project package.json,project diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c5c0590b..089e39f9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -529,24 +529,27 @@ jobs: shell: bash env: RELEASE_BRANCH: ${{ steps.decide.outputs.release_branch }} + RELEASE_PR_NUMBER: ${{ steps.release_pr.outputs.pr_number }} run: | set -euo pipefail - if remote_branch_sha="$(git ls-remote --heads origin "refs/heads/${RELEASE_BRANCH}" 2>&1)"; then - remote_branch_sha="${remote_branch_sha%%$'\t'*}" - if [[ -n "$remote_branch_sha" ]]; then - if ! cleanup_error="$(git push --force-with-lease="refs/heads/${RELEASE_BRANCH}:${remote_branch_sha}" \ + # A metadata-only resume did not merge a PR and owns no cleanup ref. + if [[ -n "${RELEASE_PR_NUMBER:-}" ]]; then + # HEAD is still the prepared commit pushed by the merge step. Never + # adopt a newer remote SHA as permission to delete somebody's update. + if expected_sha="$(git rev-parse HEAD 2>&1)"; then + if ! cleanup_error="$(git push --force-with-lease="refs/heads/${RELEASE_BRANCH}:${expected_sha}" \ origin ":refs/heads/${RELEASE_BRANCH}" 2>&1)"; then cleanup_error="${cleanup_error//$'\n'/ }" cleanup_error="${cleanup_error//$'\r'/ }" cleanup_error="${cleanup_error//%/%25}" echo "::warning::Could not delete merged release branch ${RELEASE_BRANCH}: ${cleanup_error}" fi + else + expected_sha="${expected_sha//$'\n'/ }" + expected_sha="${expected_sha//$'\r'/ }" + expected_sha="${expected_sha//%/%25}" + echo "::warning::Could not inspect prepared commit for cleanup of ${RELEASE_BRANCH}: ${expected_sha}" fi - else - remote_branch_sha="${remote_branch_sha//$'\n'/ }" - remote_branch_sha="${remote_branch_sha//$'\r'/ }" - remote_branch_sha="${remote_branch_sha//%/%25}" - echo "::warning::Could not inspect merged release branch ${RELEASE_BRANCH} for cleanup: ${remote_branch_sha}" fi - name: Write release summary diff --git a/docs/daily-release-refresh.md b/docs/daily-release-refresh.md index e84614e6..d2aa6103 100644 --- a/docs/daily-release-refresh.md +++ b/docs/daily-release-refresh.md @@ -159,11 +159,13 @@ The sweep covered all 23 authored steps: | Update release version; Generate changelog and release notes; Run release checks; Commit release files | Required artifacts and gates remain fail-closed. Summary flags were removed from generation and the release:notes script, so an optional summary write cannot invalidate otherwise valid artifacts. | | Merge release metadata through protected PR | Core branch write, protection checks, authoritative merge and merged SHA output remain fail-closed. PR title/body refresh and diagnostic-file removal now warn on failure. Existing optional bot-thread resolution and workflow-approval attempts were already guarded; required merge approval is still enforced by GitHub. | | Close superseded release PRs | Runs after the protected merge; each failure warns and continues. | -| Clean up merged release branch | Optional deletion moved out of both the normal merge and metadata-resume paths. Lookup and leased deletion failures warn. A failed lookup never supplies a deletion lease. | +| Clean up merged release branch | Optional deletion moved out of the merge step. It runs only with a verified merged PR number, so metadata-only resumes own no cleanup ref. Deletion is leased to the prepared local commit, never to a freshly queried remote SHA; a later writer is retained. Local commit lookup and deletion failures warn. | | Write release summary | Separate optional step; write failure warns without affecting the release. | | Verify merged release; Publish npm package; Push release tag | Exact merged source, clean artifacts, provenance, registry confirmation and tag identity remain fail-closed. | | Verify bun install of published package; Create GitHub release; Fail the job on bun verification failure | Existing policy remains: Bun mirror failure cannot prevent GitHub release creation after successful publication and tagging, but the final gate still reports failed Bun verification. GitHub release creation itself remains a required delivery operation. | -All seven existing identity, merged-artifact, publication, tag, Bun-verification, GitHub-release and final Bun-failure step definitions were compared structurally with d2abfe6 and are unchanged. All 20 shell scripts pass bash syntax validation. Tests execute complete named steps against synthetic command transports: first close fails and a later close succeeds; branch lookup/deletion, title/body edit, diagnostic cleanup and summary writes can fail; the verified merged SHA still reaches the next step; a registry that never confirms an attested artifact still causes publication failure. Expanded focused suites pass 40/40; native Bun passes 19 decision tests and four approval tests. Independent runtime and configuration revert controls are recorded in the PM history; completed full gates and final-head CI are recorded after execution. +All seven existing identity, merged-artifact, publication, tag, Bun-verification, GitHub-release and final Bun-failure step definitions were compared structurally with d2abfe6 and are unchanged. All 20 shell scripts pass bash syntax validation. Tests execute complete named steps against synthetic command transports: first close fails and a later close succeeds; branch lookup/deletion, title/body edit, diagnostic cleanup and summary writes can fail; the verified merged SHA still reaches the next step; a registry that never confirms an attested artifact still causes publication failure. Expanded focused suites pass 42/42; native Bun passes 21 decision tests and four approval tests. Independent runtime and configuration revert controls are recorded in the PM history; completed full gates and final-head CI are recorded after execution. The first full npm gate on the cleanup correction failed with 918/919 passing and one approval-fixture failure. Its raw text slice lost the old diagnostic-deletion anchor and reached an output variable absent from that fixture. The fixture now parses and executes the complete named merge step, isolates its output and temporary files, preserves all four approval assertions, and additionally verifies the exact merged SHA output. No product guard or gate threshold was weakened. Corrected full-gate receipts follow after execution. + +Ownership regressions additionally prove that metadata-only resume cannot delete a ref created later, and cleanup cannot adopt a later writer's SHA as permission to delete. Reverting only the cleanup function to cea1d6f fails both tests (exit 1, ERR_ASSERTION). The restored function skips unverified cleanup and leases deletion to the prepared commit. The nine earlier runtime/configuration controls were repeated successfully against this correction. The completed npm gate at cea1d6f passed 919/919 with exact coverage; its unfinished Bun run was superseded before acceptance when these ownership cases were identified. Full gates for the final correction are recorded after completion. diff --git a/test/daily-release-plan.test.ts b/test/daily-release-plan.test.ts index a17066fc..8fcf8de0 100644 --- a/test/daily-release-plan.test.ts +++ b/test/daily-release-plan.test.ts @@ -266,18 +266,19 @@ test("superseded close failures warn per PR and still try later PRs before conti test("optional branch lookup or leased deletion failures warn and cannot stop the release", () => { const cleanup = step("Clean up merged release branch"); assert.ok(steps.indexOf(cleanup) > steps.indexOf(step("Merge release metadata through protected PR"))); + assert.equal(cleanup.env?.RELEASE_PR_NUMBER, "${{ steps.release_pr.outputs.pr_number }}"); for (const failure of ["lookup", "delete", "none", "absent"]) { const result = runReleaseStep(cleanup.name, ` git() { - if [[ "$1" == ls-remote ]]; then + if [[ "$1" == rev-parse ]]; then if [[ "$TEST_FAILURE" == lookup ]]; then printf 'lookup denied' >&2; return 47; fi - if [[ "$TEST_FAILURE" != absent ]]; then printf 'remote-sha\\trefs/heads/release/2026.10.18'; fi + printf release-sha elif [[ "$1" == push ]]; then - [[ "$*" == 'push --force-with-lease=refs/heads/release/2026.10.18:remote-sha origin :refs/heads/release/2026.10.18' ]] || return 99 + [[ "$*" == 'push --force-with-lease=refs/heads/release/2026.10.18:release-sha origin :refs/heads/release/2026.10.18' ]] || return 99 if [[ "$TEST_FAILURE" == delete ]]; then printf 'delete denied' >&2; return 48; fi fi } - `, { TEST_FAILURE: failure }); + `, { TEST_FAILURE: failure, RELEASE_PR_NUMBER: failure === "absent" ? "" : "145" }); assert.equal(result.status, 0, result.stderr); assert.match(result.stdout, /release continues/); if (failure === "lookup" || failure === "delete") assert.match(result.stdout, new RegExp(`::warning::.*release/2026\\.10\\.18.*${failure} denied`)); @@ -337,3 +338,33 @@ test("publication still fails closed when the registry never confirms an atteste assert.match(result.stdout, /Refusing to downgrade supply-chain attestations/); assert.doesNotMatch(result.stdout, /release continues/); }); + + +test("branch cleanup never deletes an unverified ref appearing during metadata resume", () => { + const result = runReleaseStep("Clean up merged release branch", ` + git() { + if [[ "$1" == ls-remote ]]; then printf 'unrelated-sha\\trefs/heads/release/2026.10.18'; + elif [[ "$1" == push ]]; then printf 'unverified ref deleted' >> "$GITHUB_OUTPUT"; fi + } + `, { RELEASE_PR_NUMBER: "" }); + assert.equal(result.status, 0, result.stderr); + assert.equal(result.outputs, "", "an unverified ref must never be deleted"); + assert.match(result.stdout, /release continues/); +}); + +test("branch cleanup keeps a ref updated after merge instead of adopting its new SHA", () => { + const result = runReleaseStep("Clean up merged release branch", ` + git() { + if [[ "$1" == rev-parse ]]; then printf release-sha; + elif [[ "$1" == ls-remote ]]; then printf 'unrelated-sha\\trefs/heads/release/2026.10.18'; + elif [[ "$1" == push ]]; then + if [[ "$*" == *':unrelated-sha '* ]]; then printf 'updated ref deleted' >> "$GITHUB_OUTPUT"; + else printf 'stale info' >&2; return 47; fi + fi + } + `); + assert.equal(result.status, 0, result.stderr); + assert.equal(result.outputs, "", "a new remote SHA must not be adopted as a cleanup lease"); + assert.match(result.stdout, /::warning::.*stale info/); + assert.match(result.stdout, /release continues/); +}); From 7483fedddf4cb7cfddad62b24833feeac5d793f9 Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Mon, 19 Oct 2026 12:29:05 +0200 Subject: [PATCH 08/12] Record cleanup correction gates and ownership regression evidence --- .agents/pm/history/pm-github-l1ot.jsonl | 2 ++ .agents/pm/issues/pm-github-l1ot.toon | 7 +++---- docs/daily-release-refresh.md | 2 ++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.agents/pm/history/pm-github-l1ot.jsonl b/.agents/pm/history/pm-github-l1ot.jsonl index 42ae00cc..e2918d2c 100644 --- a/.agents/pm/history/pm-github-l1ot.jsonl +++ b/.agents/pm/history/pm-github-l1ot.jsonl @@ -42,3 +42,5 @@ {"hash_algorithm":"sha256","ts":"2026-10-19T08:53:07.943Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"tests_add","patch":[{"op":"add","path":"/metadata/tests/3","value":{"command":"node --test test/daily-release-plan.test.ts test/release-approval.test.ts test/release-workflow.test.ts test/verify-release-publish-attestation.test.ts","scope":"project","timeout_seconds":120,"provenance":{"author":"codex-agent","created_at":"2026-10-19T08:53:07.848Z","source_kind":"local_mutation","source_ref":"fix/release-pr-selection-followup"}}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T08:53:07.943Z"}],"before_hash":"48c0a76c09088f9baeb06d546b8fb75bfdac91a2fd51ffbbf931bc41392a11f3","after_hash":"f82c2dd97395868c417220a3a8c2536256c2e0a0e54b6f99bffcff8461f9be2f","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"dfa0962febe8ac6883c6964c1d883d26aa167ca1fb73ec1f17aa494215f75911"} {"hash_algorithm":"sha256","ts":"2026-10-19T08:53:09.473Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/21","value":{"created_at":"2026-10-19T08:53:09.473Z","author":"codex-agent","text":"The first complete npm gate on cleanup head 9a312326e9211dfc9702332b5f486059d498a3b8 exited 1: 918/919 tests passed, one failed, zero cancellations/skips. Failure was the existing approval test's raw slice losing its old rm anchor and reaching GITHUB_OUTPUT without fixture configuration. Fixed the fixture by parsing the complete named YAML step and supplying isolated Git/GitHub transports, HOME, TMPDIR and output files. Kept all four approval assertions and added exact merged-SHA output verification. Expanded focused suites now pass 40/40; native Bun passes 19 decision tests plus four approval tests. The failed gate is not accepted or waived; npm and Bun full gates will run again at unchanged thresholds."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T08:53:09.473Z"}],"before_hash":"f82c2dd97395868c417220a3a8c2536256c2e0a0e54b6f99bffcff8461f9be2f","after_hash":"1a2ba8c5871122f24c137e360deda1bdcef3db6eff2a68b6f38c8eaa8782454d","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"2ead774aef0ccf888fec73a42e3d6e5612e313fb8c67921ad39bfb7da3bb4b22"} {"hash_algorithm":"sha256","ts":"2026-10-19T09:41:48.729Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/22","value":{"created_at":"2026-10-19T09:41:48.729Z","author":"codex-agent","text":"The npm gate at cea1d6f passed 919/919 with exact 100/100/100/100 across 15 modules (10535 lines/statements, 277 functions, 3225 branches; all covered, zero skips). Its unfinished Bun run was superseded before acceptance after two branch-ownership regressions exposed cleanup adopting a newer remote SHA or deleting an unverified ref during metadata resume. Both tests failed on the earlier cleanup, with simulated deletions proving the issue. Cleanup now requires the actual merged-PR output and leases deletion to the prepared local commit; it never adopts a newly read remote SHA. Reverting only that cleanup function to cea1d6f fails both ownership cases (2 failures, ERR_ASSERTION, exit 1). All nine earlier runtime/configuration controls were repeated and fail as intended. Restored focused suites pass 42/42, native Bun decisions 21/21 and approvals 4/4. Fresh npm and Bun full gates will verify this final safety correction at unchanged thresholds."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T09:41:48.729Z"}],"before_hash":"1a2ba8c5871122f24c137e360deda1bdcef3db6eff2a68b6f38c8eaa8782454d","after_hash":"7fc3f887a2f07e204b9568cff14fbd8ca622e0d371d17a14906556aa7bc10733","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"f347118909925623eeda11172b7f3b0d184e5074fe33ae9a83b89c364147fb1a"} +{"hash_algorithm":"sha256","ts":"2026-10-19T10:28:58.843Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/23","value":{"created_at":"2026-10-19T10:28:58.843Z","author":"codex-agent","text":"Final cleanup correction gates at code head b54ed62af05226d8d67f9ea882eaa92d4006aaf4: npm run release:check and bun run release:check each exited 0 with 921/921 tests, zero failures/cancellations/skips, and exact 100/100/100/100 across all 15 authored modules. Both measured 10535/10535 lines and statements, 277/277 functions, 3225/3225 branches, zero skipped coverage. All release-check stages passed with unchanged pins, coverage scope, thresholds and budgets. Focused linked suite passes 42/42; native Bun decision and approval suites pass 21/21 and 4/4. Nine cleanup/configuration revert controls fail as intended; reverting only cleanup to cea1d6f additionally fails both ownership regressions. The initial approval-fixture failure and superseded intermediate runs remain documented above. No new PM CLI/SDK defects; linked-result storage remains disabled by existing policy, so these are durable result comments. Final receipt changes only documentation/tracker state; CI evidence on the pushed head will be recorded in https://github.com/unbraind/pm-github/pull/152."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T10:28:58.843Z"}],"before_hash":"7fc3f887a2f07e204b9568cff14fbd8ca622e0d371d17a14906556aa7bc10733","after_hash":"ceab917cdfd375cbfdd33435d6421142f2bb46579cd9d90b6585fa9d566a926a","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"e8889c91bd84b45db68e3c687b94f6fb6280792aa7bcadcfeac6875ef3b36f94"} +{"hash_algorithm":"sha256","ts":"2026-10-19T10:29:01.495Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T10:29:01.495Z"}],"before_hash":"ceab917cdfd375cbfdd33435d6421142f2bb46579cd9d90b6585fa9d566a926a","after_hash":"e5c4ac547fba56118dc304859f5d3fc3d8fc6ccfead3e805b5cbd7f6f516758d","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"fd2a4093f2bb2a2f7312f4dbcfa77cf8d8a7c792a61c2fcccf4031b97c0f9d3d"} diff --git a/.agents/pm/issues/pm-github-l1ot.toon b/.agents/pm/issues/pm-github-l1ot.toon index 5c2953d7..92ded2ee 100644 --- a/.agents/pm/issues/pm-github-l1ot.toon +++ b/.agents/pm/issues/pm-github-l1ot.toon @@ -6,12 +6,10 @@ status: in_progress priority: 1 tags[3]: changelog,changelog-unreleased,release created_at: "2026-10-15T09:25:36.259Z" -updated_at: "2026-10-19T09:41:48.729Z" -assignee: codex-agent -claim_principal: codex-agent +updated_at: "2026-10-19T10:29:01.495Z" author: claude-hub acceptance_criteria: "release.yml generates CHANGELOG.md and the release notes through the package's own changelog scripts with the same status filter as changelog:check; a test fails if the two disagree; a release run with an in_progress item tagged changelog-unreleased and delivered since the last tag either succeeds or fails before any bump with a clear reason" -comments[23]{created_at,author,text}: +comments[24]{created_at,author,text}: "2026-10-18T18:19:00.873Z",codex-agent,"Reproduced the release generator split: release.yml uses pm-changelog closed-only output while changelog:check includes tagged pending work. The next run fails before refreshing the protected release PR. Plan: one canonical generator, rebuild from freshly fetched main, refresh or supersede stale PRs, and enforce one release per calendar day with pure decision regressions. Related: https://github.com/unbraind/pm-github/pull/144 and https://github.com/unbraind/pm-ado/issues/33" "2026-10-18T18:22:11.207Z",codex-agent,"Behavioral function-only revert proof: keeping the same decision function and tests, restored the old unconditional same-day eligibility and today-only branch selection. node --test test/daily-release-plan.test.ts failed 2/4 assertions: the old PR branch was abandoned and a second same-day release was allowed. Restored implementation: focused release suites passed 13/13 and the linked PM command passed. The release generator now invokes changelog:full, changelog:check and release:notes; lease-protected refresh updates the reused PR title/version to today." "2026-10-18T18:32:32.900Z",codex-agent,"Consumer context: https://github.com/unbraind/pm-beads/pull/145 https://github.com/unbraind/pm-brief/pull/154 https://github.com/unbraind/pm-context/pull/147 https://github.com/unbraind/pm-csv/pull/163 https://github.com/unbraind/pm-linear/pull/150 https://github.com/unbraind/pm-slack-standup/pull/127. Separate attribution fix: https://github.com/unbraind/pm-ado/issues/33. The proof report contains all 8 real imported terminal rows, exact before/main lifecycle fields, explicit completed_at projection limits, historical release placement and momentum. Six baseline entries are under Unreleased; all 8 projected rows render under historical releases when terminal statuses are selected." @@ -35,6 +33,7 @@ comments[23]{created_at,author,text}: "2026-10-19T08:27:49.591Z",codex-agent,The publication failure fixture now explicitly isolates uppercase and lowercase npm user/global configuration paths as well as HOME and TMPDIR. This prevents its real credential-scrub code from touching inherited runner configuration. Restarted the unfinished gate against this final fixture isolation; no successful gate was repeated or counted as a failure. "2026-10-19T08:53:09.473Z",codex-agent,"The first complete npm gate on cleanup head 9a312326e9211dfc9702332b5f486059d498a3b8 exited 1: 918/919 tests passed, one failed, zero cancellations/skips. Failure was the existing approval test's raw slice losing its old rm anchor and reaching GITHUB_OUTPUT without fixture configuration. Fixed the fixture by parsing the complete named YAML step and supplying isolated Git/GitHub transports, HOME, TMPDIR and output files. Kept all four approval assertions and added exact merged-SHA output verification. Expanded focused suites now pass 40/40; native Bun passes 19 decision tests plus four approval tests. The failed gate is not accepted or waived; npm and Bun full gates will run again at unchanged thresholds." "2026-10-19T09:41:48.729Z",codex-agent,"The npm gate at cea1d6f passed 919/919 with exact 100/100/100/100 across 15 modules (10535 lines/statements, 277 functions, 3225 branches; all covered, zero skips). Its unfinished Bun run was superseded before acceptance after two branch-ownership regressions exposed cleanup adopting a newer remote SHA or deleting an unverified ref during metadata resume. Both tests failed on the earlier cleanup, with simulated deletions proving the issue. Cleanup now requires the actual merged-PR output and leases deletion to the prepared local commit; it never adopts a newly read remote SHA. Reverting only that cleanup function to cea1d6f fails both ownership cases (2 failures, ERR_ASSERTION, exit 1). All nine earlier runtime/configuration controls were repeated and fail as intended. Restored focused suites pass 42/42, native Bun decisions 21/21 and approvals 4/4. Fresh npm and Bun full gates will verify this final safety correction at unchanged thresholds." + "2026-10-19T10:28:58.843Z",codex-agent,"Final cleanup correction gates at code head b54ed62af05226d8d67f9ea882eaa92d4006aaf4: npm run release:check and bun run release:check each exited 0 with 921/921 tests, zero failures/cancellations/skips, and exact 100/100/100/100 across all 15 authored modules. Both measured 10535/10535 lines and statements, 277/277 functions, 3225/3225 branches, zero skipped coverage. All release-check stages passed with unchanged pins, coverage scope, thresholds and budgets. Focused linked suite passes 42/42; native Bun decision and approval suites pass 21/21 and 4/4. Nine cleanup/configuration revert controls fail as intended; reverting only cleanup to cea1d6f additionally fails both ownership regressions. The initial approval-fixture failure and superseded intermediate runs remain documented above. No new PM CLI/SDK defects; linked-result storage remains disabled by existing policy, so these are durable result comments. Final receipt changes only documentation/tracker state; CI evidence on the pushed head will be recorded in https://github.com/unbraind/pm-github/pull/152." files[5]{path,scope}: .github/workflows/release.yml,project package.json,project diff --git a/docs/daily-release-refresh.md b/docs/daily-release-refresh.md index d2aa6103..eb2c422a 100644 --- a/docs/daily-release-refresh.md +++ b/docs/daily-release-refresh.md @@ -169,3 +169,5 @@ All seven existing identity, merged-artifact, publication, tag, Bun-verification The first full npm gate on the cleanup correction failed with 918/919 passing and one approval-fixture failure. Its raw text slice lost the old diagnostic-deletion anchor and reached an output variable absent from that fixture. The fixture now parses and executes the complete named merge step, isolates its output and temporary files, preserves all four approval assertions, and additionally verifies the exact merged SHA output. No product guard or gate threshold was weakened. Corrected full-gate receipts follow after execution. Ownership regressions additionally prove that metadata-only resume cannot delete a ref created later, and cleanup cannot adopt a later writer's SHA as permission to delete. Reverting only the cleanup function to cea1d6f fails both tests (exit 1, ERR_ASSERTION). The restored function skips unverified cleanup and leases deletion to the prepared commit. The nine earlier runtime/configuration controls were repeated successfully against this correction. The completed npm gate at cea1d6f passed 919/919 with exact coverage; its unfinished Bun run was superseded before acceptance when these ownership cases were identified. Full gates for the final correction are recorded after completion. + +The final cleanup code head b54ed62af05226d8d67f9ea882eaa92d4006aaf4 passes both npm run release:check and bun run release:check. Each exited 0 with 921/921 tests, zero failures/cancellations/skips and exact 100/100/100/100 across all 15 authored modules: 10535/10535 lines and statements, 277/277 functions, 3225/3225 branches, zero skipped coverage. All release-check stages passed with unchanged pins, scope and budgets. The linked focused suite passes 42/42, with native Bun decisions 21/21 and approvals 4/4. The receipt changes only documentation and tracker evidence; CI evidence for its pushed head is recorded in [PR #152](https://github.com/unbraind/pm-github/pull/152). Earlier failed, pending and superseded runs above remain historical evidence rather than acceptance of this correction. From dbb16d8d9924395f24895ffd172d0917c6baacb3 Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Mon, 19 Oct 2026 12:45:46 +0200 Subject: [PATCH 09/12] Refresh changelog after the new main release --- .agents/pm/history/pm-github-l1ot.jsonl | 3 +++ .agents/pm/issues/pm-github-l1ot.toon | 8 ++++++-- CHANGELOG.md | 7 ++++++- docs/daily-release-refresh.md | 2 ++ 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.agents/pm/history/pm-github-l1ot.jsonl b/.agents/pm/history/pm-github-l1ot.jsonl index e2918d2c..72ed0c9b 100644 --- a/.agents/pm/history/pm-github-l1ot.jsonl +++ b/.agents/pm/history/pm-github-l1ot.jsonl @@ -44,3 +44,6 @@ {"hash_algorithm":"sha256","ts":"2026-10-19T09:41:48.729Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/22","value":{"created_at":"2026-10-19T09:41:48.729Z","author":"codex-agent","text":"The npm gate at cea1d6f passed 919/919 with exact 100/100/100/100 across 15 modules (10535 lines/statements, 277 functions, 3225 branches; all covered, zero skips). Its unfinished Bun run was superseded before acceptance after two branch-ownership regressions exposed cleanup adopting a newer remote SHA or deleting an unverified ref during metadata resume. Both tests failed on the earlier cleanup, with simulated deletions proving the issue. Cleanup now requires the actual merged-PR output and leases deletion to the prepared local commit; it never adopts a newly read remote SHA. Reverting only that cleanup function to cea1d6f fails both ownership cases (2 failures, ERR_ASSERTION, exit 1). All nine earlier runtime/configuration controls were repeated and fail as intended. Restored focused suites pass 42/42, native Bun decisions 21/21 and approvals 4/4. Fresh npm and Bun full gates will verify this final safety correction at unchanged thresholds."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T09:41:48.729Z"}],"before_hash":"1a2ba8c5871122f24c137e360deda1bdcef3db6eff2a68b6f38c8eaa8782454d","after_hash":"7fc3f887a2f07e204b9568cff14fbd8ca622e0d371d17a14906556aa7bc10733","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"f347118909925623eeda11172b7f3b0d184e5074fe33ae9a83b89c364147fb1a"} {"hash_algorithm":"sha256","ts":"2026-10-19T10:28:58.843Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/23","value":{"created_at":"2026-10-19T10:28:58.843Z","author":"codex-agent","text":"Final cleanup correction gates at code head b54ed62af05226d8d67f9ea882eaa92d4006aaf4: npm run release:check and bun run release:check each exited 0 with 921/921 tests, zero failures/cancellations/skips, and exact 100/100/100/100 across all 15 authored modules. Both measured 10535/10535 lines and statements, 277/277 functions, 3225/3225 branches, zero skipped coverage. All release-check stages passed with unchanged pins, coverage scope, thresholds and budgets. Focused linked suite passes 42/42; native Bun decision and approval suites pass 21/21 and 4/4. Nine cleanup/configuration revert controls fail as intended; reverting only cleanup to cea1d6f additionally fails both ownership regressions. The initial approval-fixture failure and superseded intermediate runs remain documented above. No new PM CLI/SDK defects; linked-result storage remains disabled by existing policy, so these are durable result comments. Final receipt changes only documentation/tracker state; CI evidence on the pushed head will be recorded in https://github.com/unbraind/pm-github/pull/152."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T10:28:58.843Z"}],"before_hash":"7fc3f887a2f07e204b9568cff14fbd8ca622e0d371d17a14906556aa7bc10733","after_hash":"ceab917cdfd375cbfdd33435d6421142f2bb46579cd9d90b6585fa9d566a926a","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"e8889c91bd84b45db68e3c687b94f6fb6280792aa7bcadcfeac6875ef3b36f94"} {"hash_algorithm":"sha256","ts":"2026-10-19T10:29:01.495Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T10:29:01.495Z"}],"before_hash":"ceab917cdfd375cbfdd33435d6421142f2bb46579cd9d90b6585fa9d566a926a","after_hash":"e5c4ac547fba56118dc304859f5d3fc3d8fc6ccfead3e805b5cbd7f6f516758d","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"fd2a4093f2bb2a2f7312f4dbcfa77cf8d8a7c792a61c2fcccf4031b97c0f9d3d"} +{"hash_algorithm":"sha256","ts":"2026-10-19T10:45:15.858Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"claim","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T10:45:15.858Z"},{"op":"add","path":"/metadata/assignee","value":"codex-agent"},{"op":"add","path":"/metadata/claim_principal","value":"codex-agent"}],"before_hash":"e5c4ac547fba56118dc304859f5d3fc3d8fc6ccfead3e805b5cbd7f6f516758d","after_hash":"9124930ffd40dbafa11528a6ba46382b3475bca12e5ac0b2febe3e374a4064c2","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"95b9e6f52d842aff5229e1ddb1fd5c4056a9089b6fc847cfca6eb69a7aeabec0"} +{"hash_algorithm":"sha256","ts":"2026-10-19T10:45:17.694Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/24","value":{"created_at":"2026-10-19T10:45:17.694Z","author":"codex-agent","text":"Pushed cleanup head 7483fed passed CI coverage, but Node 26 failed changelog:check after main gained release v2026.10.19. The combined changelog puts the still-open tagged item in the release instead of Unreleased. Fetched the new release tag; will regenerate canonical changelog, retain dependency pins, and verify the corrected branch and combined result. No PR merge or release operation is being performed."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T10:45:17.694Z"}],"before_hash":"9124930ffd40dbafa11528a6ba46382b3475bca12e5ac0b2febe3e374a4064c2","after_hash":"c31bae0b9c9d8dd6bf62877bcfc5fee14aa42413aa8b96a228c4dfa37f73793c","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"d7402b1d0d6d56d6324b406e7ff7c445a27e8f1dc4164b3ca19e88343f8a5bbb"} +{"hash_algorithm":"sha256","ts":"2026-10-19T10:45:43.616Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/25","value":{"created_at":"2026-10-19T10:45:43.615Z","author":"codex-agent","text":"Canonical regeneration after fetching v2026.10.19 passes changelog:check: released entries now belong to 2026.10.19 and the open tagged release item remains Unreleased. No source, test, workflow, package pin or threshold changes were needed for this CI correction. Will rerun npm and Bun release:check and verify the combined-tree changelog before a corrective push."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T10:45:43.616Z"}],"before_hash":"c31bae0b9c9d8dd6bf62877bcfc5fee14aa42413aa8b96a228c4dfa37f73793c","after_hash":"7f2a1c39ca03c62727726b72cf0272e17f6240f232f8ce3e403084aa621a955c","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"e26549532f342f0908e0bf666395d32477df43f5cac98b4224c16cedd7ce1ac4"} diff --git a/.agents/pm/issues/pm-github-l1ot.toon b/.agents/pm/issues/pm-github-l1ot.toon index 92ded2ee..81247848 100644 --- a/.agents/pm/issues/pm-github-l1ot.toon +++ b/.agents/pm/issues/pm-github-l1ot.toon @@ -6,10 +6,12 @@ status: in_progress priority: 1 tags[3]: changelog,changelog-unreleased,release created_at: "2026-10-15T09:25:36.259Z" -updated_at: "2026-10-19T10:29:01.495Z" +updated_at: "2026-10-19T10:45:43.616Z" +assignee: codex-agent +claim_principal: codex-agent author: claude-hub acceptance_criteria: "release.yml generates CHANGELOG.md and the release notes through the package's own changelog scripts with the same status filter as changelog:check; a test fails if the two disagree; a release run with an in_progress item tagged changelog-unreleased and delivered since the last tag either succeeds or fails before any bump with a clear reason" -comments[24]{created_at,author,text}: +comments[26]{created_at,author,text}: "2026-10-18T18:19:00.873Z",codex-agent,"Reproduced the release generator split: release.yml uses pm-changelog closed-only output while changelog:check includes tagged pending work. The next run fails before refreshing the protected release PR. Plan: one canonical generator, rebuild from freshly fetched main, refresh or supersede stale PRs, and enforce one release per calendar day with pure decision regressions. Related: https://github.com/unbraind/pm-github/pull/144 and https://github.com/unbraind/pm-ado/issues/33" "2026-10-18T18:22:11.207Z",codex-agent,"Behavioral function-only revert proof: keeping the same decision function and tests, restored the old unconditional same-day eligibility and today-only branch selection. node --test test/daily-release-plan.test.ts failed 2/4 assertions: the old PR branch was abandoned and a second same-day release was allowed. Restored implementation: focused release suites passed 13/13 and the linked PM command passed. The release generator now invokes changelog:full, changelog:check and release:notes; lease-protected refresh updates the reused PR title/version to today." "2026-10-18T18:32:32.900Z",codex-agent,"Consumer context: https://github.com/unbraind/pm-beads/pull/145 https://github.com/unbraind/pm-brief/pull/154 https://github.com/unbraind/pm-context/pull/147 https://github.com/unbraind/pm-csv/pull/163 https://github.com/unbraind/pm-linear/pull/150 https://github.com/unbraind/pm-slack-standup/pull/127. Separate attribution fix: https://github.com/unbraind/pm-ado/issues/33. The proof report contains all 8 real imported terminal rows, exact before/main lifecycle fields, explicit completed_at projection limits, historical release placement and momentum. Six baseline entries are under Unreleased; all 8 projected rows render under historical releases when terminal statuses are selected." @@ -34,6 +36,8 @@ comments[24]{created_at,author,text}: "2026-10-19T08:53:09.473Z",codex-agent,"The first complete npm gate on cleanup head 9a312326e9211dfc9702332b5f486059d498a3b8 exited 1: 918/919 tests passed, one failed, zero cancellations/skips. Failure was the existing approval test's raw slice losing its old rm anchor and reaching GITHUB_OUTPUT without fixture configuration. Fixed the fixture by parsing the complete named YAML step and supplying isolated Git/GitHub transports, HOME, TMPDIR and output files. Kept all four approval assertions and added exact merged-SHA output verification. Expanded focused suites now pass 40/40; native Bun passes 19 decision tests plus four approval tests. The failed gate is not accepted or waived; npm and Bun full gates will run again at unchanged thresholds." "2026-10-19T09:41:48.729Z",codex-agent,"The npm gate at cea1d6f passed 919/919 with exact 100/100/100/100 across 15 modules (10535 lines/statements, 277 functions, 3225 branches; all covered, zero skips). Its unfinished Bun run was superseded before acceptance after two branch-ownership regressions exposed cleanup adopting a newer remote SHA or deleting an unverified ref during metadata resume. Both tests failed on the earlier cleanup, with simulated deletions proving the issue. Cleanup now requires the actual merged-PR output and leases deletion to the prepared local commit; it never adopts a newly read remote SHA. Reverting only that cleanup function to cea1d6f fails both ownership cases (2 failures, ERR_ASSERTION, exit 1). All nine earlier runtime/configuration controls were repeated and fail as intended. Restored focused suites pass 42/42, native Bun decisions 21/21 and approvals 4/4. Fresh npm and Bun full gates will verify this final safety correction at unchanged thresholds." "2026-10-19T10:28:58.843Z",codex-agent,"Final cleanup correction gates at code head b54ed62af05226d8d67f9ea882eaa92d4006aaf4: npm run release:check and bun run release:check each exited 0 with 921/921 tests, zero failures/cancellations/skips, and exact 100/100/100/100 across all 15 authored modules. Both measured 10535/10535 lines and statements, 277/277 functions, 3225/3225 branches, zero skipped coverage. All release-check stages passed with unchanged pins, coverage scope, thresholds and budgets. Focused linked suite passes 42/42; native Bun decision and approval suites pass 21/21 and 4/4. Nine cleanup/configuration revert controls fail as intended; reverting only cleanup to cea1d6f additionally fails both ownership regressions. The initial approval-fixture failure and superseded intermediate runs remain documented above. No new PM CLI/SDK defects; linked-result storage remains disabled by existing policy, so these are durable result comments. Final receipt changes only documentation/tracker state; CI evidence on the pushed head will be recorded in https://github.com/unbraind/pm-github/pull/152." + "2026-10-19T10:45:17.694Z",codex-agent,"Pushed cleanup head 7483fed passed CI coverage, but Node 26 failed changelog:check after main gained release v2026.10.19. The combined changelog puts the still-open tagged item in the release instead of Unreleased. Fetched the new release tag; will regenerate canonical changelog, retain dependency pins, and verify the corrected branch and combined result. No PR merge or release operation is being performed." + "2026-10-19T10:45:43.615Z",codex-agent,"Canonical regeneration after fetching v2026.10.19 passes changelog:check: released entries now belong to 2026.10.19 and the open tagged release item remains Unreleased. No source, test, workflow, package pin or threshold changes were needed for this CI correction. Will rerun npm and Bun release:check and verify the combined-tree changelog before a corrective push." files[5]{path,scope}: .github/workflows/release.yml,project package.json,project diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c6367cc..6bbb33d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,13 +2,18 @@ ## Unreleased +### Fixed + +- The daily release regenerates the changelog with a different status filter than release:check, so an open changelog-unreleased item delivered since the last tag fails the release ([pm-github-l1ot](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-l1ot.toon)) + +## 2026.10.19 - 2026-10-19 + ### Added - Preserve imported GitHub lifetimes, closure evidence and release attribution while linking cross-provider items ([pm-github-c093](https://github.com/unbraind/pm-github/blob/main/.agents/pm/features/pm-github-c093.toon)) ### Fixed -- The daily release regenerates the changelog with a different status filter than release:check, so an open changelog-unreleased item delivered since the last tag fails the release ([pm-github-l1ot](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-l1ot.toon)) - Require explicit delivery attribution before merged-PR closeout ([pm-github-uqjf](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-uqjf.toon)) - Release timeout must report current workflow approval state ([pm-github-b145](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-b145.toon)) - Preserve legacy GitHub ownership across copied provider examples ([pm-github-b143](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-b143.toon)) diff --git a/docs/daily-release-refresh.md b/docs/daily-release-refresh.md index eb2c422a..aae3955e 100644 --- a/docs/daily-release-refresh.md +++ b/docs/daily-release-refresh.md @@ -171,3 +171,5 @@ The first full npm gate on the cleanup correction failed with 918/919 passing an Ownership regressions additionally prove that metadata-only resume cannot delete a ref created later, and cleanup cannot adopt a later writer's SHA as permission to delete. Reverting only the cleanup function to cea1d6f fails both tests (exit 1, ERR_ASSERTION). The restored function skips unverified cleanup and leases deletion to the prepared commit. The nine earlier runtime/configuration controls were repeated successfully against this correction. The completed npm gate at cea1d6f passed 919/919 with exact coverage; its unfinished Bun run was superseded before acceptance when these ownership cases were identified. Full gates for the final correction are recorded after completion. The final cleanup code head b54ed62af05226d8d67f9ea882eaa92d4006aaf4 passes both npm run release:check and bun run release:check. Each exited 0 with 921/921 tests, zero failures/cancellations/skips and exact 100/100/100/100 across all 15 authored modules: 10535/10535 lines and statements, 277/277 functions, 3225/3225 branches, zero skipped coverage. All release-check stages passed with unchanged pins, scope and budgets. The linked focused suite passes 42/42, with native Bun decisions 21/21 and approvals 4/4. The receipt changes only documentation and tracker evidence; CI evidence for its pushed head is recorded in [PR #152](https://github.com/unbraind/pm-github/pull/152). Earlier failed, pending and superseded runs above remain historical evidence rather than acceptance of this correction. + +CI at receipt head 7483fed passed the full Node 26 coverage gate, then failed changelog:check after main gained v2026.10.19 during the run. The combined changelog incorrectly placed the still-open tagged item in that release instead of Unreleased. Fetching the new tag and regenerating with the canonical changelog script corrects the history without merging main, changing source, or changing dependency pins. Corrected gate and CI results are recorded in the PM history and PR. From 777a13f2b7eed537f7b469dd0c1b5306ed94fd83 Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Mon, 19 Oct 2026 13:26:41 +0200 Subject: [PATCH 10/12] Record integrated cleanup gates and release the PM claim --- .agents/pm/history/pm-github-l1ot.jsonl | 2 ++ .agents/pm/issues/pm-github-l1ot.toon | 7 +++---- docs/daily-release-refresh.md | 2 ++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.agents/pm/history/pm-github-l1ot.jsonl b/.agents/pm/history/pm-github-l1ot.jsonl index 11369ee0..cfb747d7 100644 --- a/.agents/pm/history/pm-github-l1ot.jsonl +++ b/.agents/pm/history/pm-github-l1ot.jsonl @@ -49,3 +49,5 @@ {"hash_algorithm":"sha256","ts":"2026-10-19T10:45:43.616Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/25","value":{"created_at":"2026-10-19T10:45:43.615Z","author":"codex-agent","text":"Canonical regeneration after fetching v2026.10.19 passes changelog:check: released entries now belong to 2026.10.19 and the open tagged release item remains Unreleased. No source, test, workflow, package pin or threshold changes were needed for this CI correction. Will rerun npm and Bun release:check and verify the combined-tree changelog before a corrective push."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T10:45:43.616Z"}],"before_hash":"c31bae0b9c9d8dd6bf62877bcfc5fee14aa42413aa8b96a228c4dfa37f73793c","after_hash":"7f2a1c39ca03c62727726b72cf0272e17f6240f232f8ce3e403084aa621a955c","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"e26549532f342f0908e0bf666395d32477df43f5cac98b4224c16cedd7ce1ac4"} {"hash_algorithm":"sha256","ts":"2026-10-19T10:46:44.021Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/26","value":{"created_at":"2026-10-19T10:46:44.020Z","author":"codex-agent","text":"The three-way preview showed a changelog-only patch would produce duplicate release headings against main. Integrated main in the normal environment with merge drivers: no textual conflicts and no stale clone-local receipts. Main brings the orchestrator's release metadata and Dependabot-owned toolchain pins; no independent dependency bump was made. Took main's changelog before canonical regeneration. Will require clean PM health and both full release gates before pushing."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T10:46:44.021Z"}],"before_hash":"7f2a1c39ca03c62727726b72cf0272e17f6240f232f8ce3e403084aa621a955c","after_hash":"783607cf29f0901b503363c36b62f1321a1a15bf1aec60f45a02d44bb7661f40","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"7e5a59b6c77b4db9b4a5116606b137b0b20be826ae4840e9f0b045481324ef17"} {"hash_algorithm":"sha256","ts":"2026-10-19T10:47:11.522Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/27","value":{"created_at":"2026-10-19T10:47:11.521Z","author":"codex-agent","text":"Main integration has no textual conflicts, no clone-local receipts and no merge_receipts_pending finding, so reconcile is unnecessary. Normal-home health reports a host-global extension/SDK version skew; repository integrity and history are clean. The inherited SDK updates its generated extension merge fence. Will prove isolated-home health and fresh-clone health, without changing host configuration or telemetry."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T10:47:11.522Z"}],"before_hash":"783607cf29f0901b503363c36b62f1321a1a15bf1aec60f45a02d44bb7661f40","after_hash":"3cdb4af076e64b1bbb420b6a79b07cc92a338df69671c4e2277d4e1d47962cca","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"600f5b6e0cead1c5cc881fd94eeef635abc8035572d88ed92298169fe14e9e8c"} +{"hash_algorithm":"sha256","ts":"2026-10-19T11:26:38.298Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/28","value":{"created_at":"2026-10-19T11:26:38.298Z","author":"codex-agent","text":"Corrected integrated code head 3886f90197efd5149a4c1e24b7bfe3731fc8b409: npm run release:check and bun run release:check both exit 0, 921/921 tests each, zero failures/cancellations/skips. Both retain exact 100/100/100/100 across all 15 authored modules: 10535/10535 lines and statements, 277/277 functions, 3225/3225 branches, zero skipped coverage. Every release-check stage passes, including canonical changelog and publication attestation. Previous pushed 7483fed passed coverage in both CI jobs but failed the changelog after main released; integration and regeneration correct it. No textual merge conflicts, stale receipts or pending receipts; no reconciliation needed. Fresh non-local clone passes isolated-home strict full PM health with required drivers, validation and privacy. No new CLI/SDK defects; host-global version skew is avoided by isolation and linked result storage remains disabled by policy. Existing cleanup revert proofs remain in this history. Final receipt changes only tracker evidence and documentation, and final-head CI will be recorded in https://github.com/unbraind/pm-github/pull/152."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T11:26:38.298Z"}],"before_hash":"3cdb4af076e64b1bbb420b6a79b07cc92a338df69671c4e2277d4e1d47962cca","after_hash":"0ca210899ec467d62271ffec7aa92ca15853ce67985e1373938e6c201155bd79","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"138e606d0e63435631b1102a482ba69813c8cf44feb73e0a31775ae46738ba38"} +{"hash_algorithm":"sha256","ts":"2026-10-19T11:26:39.311Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T11:26:39.311Z"}],"before_hash":"0ca210899ec467d62271ffec7aa92ca15853ce67985e1373938e6c201155bd79","after_hash":"c4e227cb3f74aa474947156b2936971767e8e765912852350ad01ac3453aca7d","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"4e98315ccfd371d4bce34d810ba3edc3132f2aa80418d25dd1345227b759dce4"} diff --git a/.agents/pm/issues/pm-github-l1ot.toon b/.agents/pm/issues/pm-github-l1ot.toon index f992edb0..83dc4e26 100644 --- a/.agents/pm/issues/pm-github-l1ot.toon +++ b/.agents/pm/issues/pm-github-l1ot.toon @@ -6,12 +6,10 @@ status: in_progress priority: 1 tags[3]: changelog,changelog-unreleased,release created_at: "2026-10-15T09:25:36.259Z" -updated_at: "2026-10-19T10:47:11.522Z" -assignee: codex-agent -claim_principal: codex-agent +updated_at: "2026-10-19T11:26:39.311Z" author: claude-hub acceptance_criteria: "release.yml generates CHANGELOG.md and the release notes through the package's own changelog scripts with the same status filter as changelog:check; a test fails if the two disagree; a release run with an in_progress item tagged changelog-unreleased and delivered since the last tag either succeeds or fails before any bump with a clear reason" -comments[28]{created_at,author,text}: +comments[29]{created_at,author,text}: "2026-10-18T18:19:00.873Z",codex-agent,"Reproduced the release generator split: release.yml uses pm-changelog closed-only output while changelog:check includes tagged pending work. The next run fails before refreshing the protected release PR. Plan: one canonical generator, rebuild from freshly fetched main, refresh or supersede stale PRs, and enforce one release per calendar day with pure decision regressions. Related: https://github.com/unbraind/pm-github/pull/144 and https://github.com/unbraind/pm-ado/issues/33" "2026-10-18T18:22:11.207Z",codex-agent,"Behavioral function-only revert proof: keeping the same decision function and tests, restored the old unconditional same-day eligibility and today-only branch selection. node --test test/daily-release-plan.test.ts failed 2/4 assertions: the old PR branch was abandoned and a second same-day release was allowed. Restored implementation: focused release suites passed 13/13 and the linked PM command passed. The release generator now invokes changelog:full, changelog:check and release:notes; lease-protected refresh updates the reused PR title/version to today." "2026-10-18T18:32:32.900Z",codex-agent,"Consumer context: https://github.com/unbraind/pm-beads/pull/145 https://github.com/unbraind/pm-brief/pull/154 https://github.com/unbraind/pm-context/pull/147 https://github.com/unbraind/pm-csv/pull/163 https://github.com/unbraind/pm-linear/pull/150 https://github.com/unbraind/pm-slack-standup/pull/127. Separate attribution fix: https://github.com/unbraind/pm-ado/issues/33. The proof report contains all 8 real imported terminal rows, exact before/main lifecycle fields, explicit completed_at projection limits, historical release placement and momentum. Six baseline entries are under Unreleased; all 8 projected rows render under historical releases when terminal statuses are selected." @@ -40,6 +38,7 @@ comments[28]{created_at,author,text}: "2026-10-19T10:45:43.615Z",codex-agent,"Canonical regeneration after fetching v2026.10.19 passes changelog:check: released entries now belong to 2026.10.19 and the open tagged release item remains Unreleased. No source, test, workflow, package pin or threshold changes were needed for this CI correction. Will rerun npm and Bun release:check and verify the combined-tree changelog before a corrective push." "2026-10-19T10:46:44.020Z",codex-agent,"The three-way preview showed a changelog-only patch would produce duplicate release headings against main. Integrated main in the normal environment with merge drivers: no textual conflicts and no stale clone-local receipts. Main brings the orchestrator's release metadata and Dependabot-owned toolchain pins; no independent dependency bump was made. Took main's changelog before canonical regeneration. Will require clean PM health and both full release gates before pushing." "2026-10-19T10:47:11.521Z",codex-agent,"Main integration has no textual conflicts, no clone-local receipts and no merge_receipts_pending finding, so reconcile is unnecessary. Normal-home health reports a host-global extension/SDK version skew; repository integrity and history are clean. The inherited SDK updates its generated extension merge fence. Will prove isolated-home health and fresh-clone health, without changing host configuration or telemetry." + "2026-10-19T11:26:38.298Z",codex-agent,"Corrected integrated code head 3886f90197efd5149a4c1e24b7bfe3731fc8b409: npm run release:check and bun run release:check both exit 0, 921/921 tests each, zero failures/cancellations/skips. Both retain exact 100/100/100/100 across all 15 authored modules: 10535/10535 lines and statements, 277/277 functions, 3225/3225 branches, zero skipped coverage. Every release-check stage passes, including canonical changelog and publication attestation. Previous pushed 7483fed passed coverage in both CI jobs but failed the changelog after main released; integration and regeneration correct it. No textual merge conflicts, stale receipts or pending receipts; no reconciliation needed. Fresh non-local clone passes isolated-home strict full PM health with required drivers, validation and privacy. No new CLI/SDK defects; host-global version skew is avoided by isolation and linked result storage remains disabled by policy. Existing cleanup revert proofs remain in this history. Final receipt changes only tracker evidence and documentation, and final-head CI will be recorded in https://github.com/unbraind/pm-github/pull/152." files[5]{path,scope}: .github/workflows/release.yml,project package.json,project diff --git a/docs/daily-release-refresh.md b/docs/daily-release-refresh.md index 482294f8..2cf08ec4 100644 --- a/docs/daily-release-refresh.md +++ b/docs/daily-release-refresh.md @@ -173,3 +173,5 @@ Ownership regressions additionally prove that metadata-only resume cannot delete The final cleanup code head b54ed62af05226d8d67f9ea882eaa92d4006aaf4 passes both npm run release:check and bun run release:check. Each exited 0 with 921/921 tests, zero failures/cancellations/skips and exact 100/100/100/100 across all 15 authored modules: 10535/10535 lines and statements, 277/277 functions, 3225/3225 branches, zero skipped coverage. All release-check stages passed with unchanged pins, scope and budgets. The linked focused suite passes 42/42, with native Bun decisions 21/21 and approvals 4/4. The receipt changes only documentation and tracker evidence; CI evidence for its pushed head is recorded in [PR #152](https://github.com/unbraind/pm-github/pull/152). Earlier failed, pending and superseded runs above remain historical evidence rather than acceptance of this correction. CI at receipt head 7483fed passed the full Node 26 coverage gate, then failed changelog:check after main gained v2026.10.19 during the run. The combined changelog incorrectly placed the still-open tagged item in that release instead of Unreleased. A changelog-only correction still produced duplicate release headings in the three-way preview, so the working branch integrated main in the normal environment and regenerated from main's changelog. There were no textual conflicts or pending merge receipts. This incorporates the release metadata and Dependabot-owned toolchain pins without an independent dependency bump. The pinned SDK refreshes its generated merge fence. Normal-home strict health reports a host-global extension version skew; isolated-home health is the repository acceptance check. Corrected gate and CI results are recorded in the PM history and PR. + +At integrated code head 3886f90197efd5149a4c1e24b7bfe3731fc8b409, npm run release:check and bun run release:check both exit 0 with 921/921 tests each, zero failures/cancellations/skips, and exact 100/100/100/100 across all 15 authored modules. Both measure 10535/10535 lines and statements, 277/277 functions and 3225/3225 branches with zero skipped coverage; every release-check stage, including the regenerated changelog and publication-attestation checks, passes. A fresh non-local clone passes strict full PM health with required merge drivers, validation and privacy. The final receipt changes only tracker evidence and documentation; final-head CI is recorded in PR #152. From 17a267ea76b05f989710c8c7af5dc66ff24c6f8c Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Mon, 19 Oct 2026 14:12:45 +0200 Subject: [PATCH 11/12] Pass release PR candidates through a temporary file --- .agents/pm/history/pm-github-l1ot.jsonl | 3 ++ .agents/pm/issues/pm-github-l1ot.toon | 8 +++- .github/workflows/release.yml | 14 ++++--- docs/daily-release-refresh.md | 8 ++++ test/daily-release-plan.test.ts | 49 +++++++++++++++++++++---- 5 files changed, 67 insertions(+), 15 deletions(-) diff --git a/.agents/pm/history/pm-github-l1ot.jsonl b/.agents/pm/history/pm-github-l1ot.jsonl index cfb747d7..2198cbcc 100644 --- a/.agents/pm/history/pm-github-l1ot.jsonl +++ b/.agents/pm/history/pm-github-l1ot.jsonl @@ -51,3 +51,6 @@ {"hash_algorithm":"sha256","ts":"2026-10-19T10:47:11.522Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/27","value":{"created_at":"2026-10-19T10:47:11.521Z","author":"codex-agent","text":"Main integration has no textual conflicts, no clone-local receipts and no merge_receipts_pending finding, so reconcile is unnecessary. Normal-home health reports a host-global extension/SDK version skew; repository integrity and history are clean. The inherited SDK updates its generated extension merge fence. Will prove isolated-home health and fresh-clone health, without changing host configuration or telemetry."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T10:47:11.522Z"}],"before_hash":"783607cf29f0901b503363c36b62f1321a1a15bf1aec60f45a02d44bb7661f40","after_hash":"3cdb4af076e64b1bbb420b6a79b07cc92a338df69671c4e2277d4e1d47962cca","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"600f5b6e0cead1c5cc881fd94eeef635abc8035572d88ed92298169fe14e9e8c"} {"hash_algorithm":"sha256","ts":"2026-10-19T11:26:38.298Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/28","value":{"created_at":"2026-10-19T11:26:38.298Z","author":"codex-agent","text":"Corrected integrated code head 3886f90197efd5149a4c1e24b7bfe3731fc8b409: npm run release:check and bun run release:check both exit 0, 921/921 tests each, zero failures/cancellations/skips. Both retain exact 100/100/100/100 across all 15 authored modules: 10535/10535 lines and statements, 277/277 functions, 3225/3225 branches, zero skipped coverage. Every release-check stage passes, including canonical changelog and publication attestation. Previous pushed 7483fed passed coverage in both CI jobs but failed the changelog after main released; integration and regeneration correct it. No textual merge conflicts, stale receipts or pending receipts; no reconciliation needed. Fresh non-local clone passes isolated-home strict full PM health with required drivers, validation and privacy. No new CLI/SDK defects; host-global version skew is avoided by isolation and linked result storage remains disabled by policy. Existing cleanup revert proofs remain in this history. Final receipt changes only tracker evidence and documentation, and final-head CI will be recorded in https://github.com/unbraind/pm-github/pull/152."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T11:26:38.298Z"}],"before_hash":"3cdb4af076e64b1bbb420b6a79b07cc92a338df69671c4e2277d4e1d47962cca","after_hash":"0ca210899ec467d62271ffec7aa92ca15853ce67985e1373938e6c201155bd79","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"138e606d0e63435631b1102a482ba69813c8cf44feb73e0a31775ae46738ba38"} {"hash_algorithm":"sha256","ts":"2026-10-19T11:26:39.311Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T11:26:39.311Z"}],"before_hash":"0ca210899ec467d62271ffec7aa92ca15853ce67985e1373938e6c201155bd79","after_hash":"c4e227cb3f74aa474947156b2936971767e8e765912852350ad01ac3453aca7d","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"4e98315ccfd371d4bce34d810ba3edc3132f2aa80418d25dd1345227b759dce4"} +{"hash_algorithm":"sha256","ts":"2026-10-19T12:06:42.803Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"claim","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T12:06:42.803Z"},{"op":"add","path":"/metadata/assignee","value":"codex-agent"},{"op":"add","path":"/metadata/claim_principal","value":"codex-agent"}],"before_hash":"c4e227cb3f74aa474947156b2936971767e8e765912852350ad01ac3453aca7d","after_hash":"ee14147eee43119d5b21900d08460abde039c9c8cb62c1f21e403744e0f5d8d1","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"c70bc689e52a1325da825d9553b223855814368f76763f8367e2aec295d1d349"} +{"hash_algorithm":"sha256","ts":"2026-10-19T12:06:44.824Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/29","value":{"created_at":"2026-10-19T12:06:44.823Z","author":"codex-agent","text":"Review round 2 confirms the candidate response is exported as RELEASE_EXISTING_PRS, so a sufficiently large list can prevent Node from starting with E2BIG. Plan: keep GitHub JSON in a temporary file, pass only its path, remove the file on success or failure, and retain fail-closed lookup behavior. The named-step harness must also read its large fixture from a file. Will test 1,000 candidates above 128 KiB and sweep the other workflow environment transfers. Prior round 1 gates and CI remain completed evidence for 777a13f."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T12:06:44.824Z"}],"before_hash":"ee14147eee43119d5b21900d08460abde039c9c8cb62c1f21e403744e0f5d8d1","after_hash":"ba9c8bc2f01c787553aad7a769848ccb4ab4bee5298f972fbb5b54ac46c43f5d","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"9eb88e054308567e5c8626b8bd6ac427e81476ca0c0aefd6cb171befa5b5afca"} +{"hash_algorithm":"sha256","ts":"2026-10-19T12:11:59.137Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/30","value":{"created_at":"2026-10-19T12:11:59.137Z","author":"codex-agent","text":"Round 2 behavioral revert proofs: with the file-backed harness and tests retained, reverting only the Decide release step to 777a13f fails the 1,000-PR regression with ERR_ASSERTION and Bash status 126, Argument list too long before Node starts. Restoring the file-based step passes. Removing only its EXIT cleanup trap fails both the successful-plan and failed-lookup file-removal assertions; restored cleanup passes and a failed lookup retains exit 47. Cleanup-denied tests preserve success or the original lookup failure while warning. Focused release suites pass 44/44; native Bun decisions pass 23/23. The sweep covered all 23 steps and 20 shell scripts, all bash syntax checks pass. No other unbounded exported payload was found: registry/review JSON stays in files, shell variables or stdin, summaries/notes use files, and other env values are scalar identities/paths/tokens. The 1,000-candidate numeric-ID stress probe yields 999 superseded IDs in 16,982 bytes using maximum safe integer IDs. No package pin, threshold or publication-path change is needed."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T12:11:59.137Z"}],"before_hash":"ba9c8bc2f01c787553aad7a769848ccb4ab4bee5298f972fbb5b54ac46c43f5d","after_hash":"e07d82183f0d172ad1e338fad1e81616c43bd5ed1696cfdb8a0b1c98ac70bc30","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"bea248f69fc460a83c07e936577e07f6e67bb405929a35bc6a2ecaf8b8b130a5"} diff --git a/.agents/pm/issues/pm-github-l1ot.toon b/.agents/pm/issues/pm-github-l1ot.toon index 83dc4e26..60a3ee27 100644 --- a/.agents/pm/issues/pm-github-l1ot.toon +++ b/.agents/pm/issues/pm-github-l1ot.toon @@ -6,10 +6,12 @@ status: in_progress priority: 1 tags[3]: changelog,changelog-unreleased,release created_at: "2026-10-15T09:25:36.259Z" -updated_at: "2026-10-19T11:26:39.311Z" +updated_at: "2026-10-19T12:11:59.137Z" +assignee: codex-agent +claim_principal: codex-agent author: claude-hub acceptance_criteria: "release.yml generates CHANGELOG.md and the release notes through the package's own changelog scripts with the same status filter as changelog:check; a test fails if the two disagree; a release run with an in_progress item tagged changelog-unreleased and delivered since the last tag either succeeds or fails before any bump with a clear reason" -comments[29]{created_at,author,text}: +comments[31]{created_at,author,text}: "2026-10-18T18:19:00.873Z",codex-agent,"Reproduced the release generator split: release.yml uses pm-changelog closed-only output while changelog:check includes tagged pending work. The next run fails before refreshing the protected release PR. Plan: one canonical generator, rebuild from freshly fetched main, refresh or supersede stale PRs, and enforce one release per calendar day with pure decision regressions. Related: https://github.com/unbraind/pm-github/pull/144 and https://github.com/unbraind/pm-ado/issues/33" "2026-10-18T18:22:11.207Z",codex-agent,"Behavioral function-only revert proof: keeping the same decision function and tests, restored the old unconditional same-day eligibility and today-only branch selection. node --test test/daily-release-plan.test.ts failed 2/4 assertions: the old PR branch was abandoned and a second same-day release was allowed. Restored implementation: focused release suites passed 13/13 and the linked PM command passed. The release generator now invokes changelog:full, changelog:check and release:notes; lease-protected refresh updates the reused PR title/version to today." "2026-10-18T18:32:32.900Z",codex-agent,"Consumer context: https://github.com/unbraind/pm-beads/pull/145 https://github.com/unbraind/pm-brief/pull/154 https://github.com/unbraind/pm-context/pull/147 https://github.com/unbraind/pm-csv/pull/163 https://github.com/unbraind/pm-linear/pull/150 https://github.com/unbraind/pm-slack-standup/pull/127. Separate attribution fix: https://github.com/unbraind/pm-ado/issues/33. The proof report contains all 8 real imported terminal rows, exact before/main lifecycle fields, explicit completed_at projection limits, historical release placement and momentum. Six baseline entries are under Unreleased; all 8 projected rows render under historical releases when terminal statuses are selected." @@ -39,6 +41,8 @@ comments[29]{created_at,author,text}: "2026-10-19T10:46:44.020Z",codex-agent,"The three-way preview showed a changelog-only patch would produce duplicate release headings against main. Integrated main in the normal environment with merge drivers: no textual conflicts and no stale clone-local receipts. Main brings the orchestrator's release metadata and Dependabot-owned toolchain pins; no independent dependency bump was made. Took main's changelog before canonical regeneration. Will require clean PM health and both full release gates before pushing." "2026-10-19T10:47:11.521Z",codex-agent,"Main integration has no textual conflicts, no clone-local receipts and no merge_receipts_pending finding, so reconcile is unnecessary. Normal-home health reports a host-global extension/SDK version skew; repository integrity and history are clean. The inherited SDK updates its generated extension merge fence. Will prove isolated-home health and fresh-clone health, without changing host configuration or telemetry." "2026-10-19T11:26:38.298Z",codex-agent,"Corrected integrated code head 3886f90197efd5149a4c1e24b7bfe3731fc8b409: npm run release:check and bun run release:check both exit 0, 921/921 tests each, zero failures/cancellations/skips. Both retain exact 100/100/100/100 across all 15 authored modules: 10535/10535 lines and statements, 277/277 functions, 3225/3225 branches, zero skipped coverage. Every release-check stage passes, including canonical changelog and publication attestation. Previous pushed 7483fed passed coverage in both CI jobs but failed the changelog after main released; integration and regeneration correct it. No textual merge conflicts, stale receipts or pending receipts; no reconciliation needed. Fresh non-local clone passes isolated-home strict full PM health with required drivers, validation and privacy. No new CLI/SDK defects; host-global version skew is avoided by isolation and linked result storage remains disabled by policy. Existing cleanup revert proofs remain in this history. Final receipt changes only tracker evidence and documentation, and final-head CI will be recorded in https://github.com/unbraind/pm-github/pull/152." + "2026-10-19T12:06:44.823Z",codex-agent,"Review round 2 confirms the candidate response is exported as RELEASE_EXISTING_PRS, so a sufficiently large list can prevent Node from starting with E2BIG. Plan: keep GitHub JSON in a temporary file, pass only its path, remove the file on success or failure, and retain fail-closed lookup behavior. The named-step harness must also read its large fixture from a file. Will test 1,000 candidates above 128 KiB and sweep the other workflow environment transfers. Prior round 1 gates and CI remain completed evidence for 777a13f." + "2026-10-19T12:11:59.137Z",codex-agent,"Round 2 behavioral revert proofs: with the file-backed harness and tests retained, reverting only the Decide release step to 777a13f fails the 1,000-PR regression with ERR_ASSERTION and Bash status 126, Argument list too long before Node starts. Restoring the file-based step passes. Removing only its EXIT cleanup trap fails both the successful-plan and failed-lookup file-removal assertions; restored cleanup passes and a failed lookup retains exit 47. Cleanup-denied tests preserve success or the original lookup failure while warning. Focused release suites pass 44/44; native Bun decisions pass 23/23. The sweep covered all 23 steps and 20 shell scripts, all bash syntax checks pass. No other unbounded exported payload was found: registry/review JSON stays in files, shell variables or stdin, summaries/notes use files, and other env values are scalar identities/paths/tokens. The 1,000-candidate numeric-ID stress probe yields 999 superseded IDs in 16,982 bytes using maximum safe integer IDs. No package pin, threshold or publication-path change is needed." files[5]{path,scope}: .github/workflows/release.yml,project package.json,project diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 089e39f9..856230d0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -95,20 +95,24 @@ jobs: if [[ -n "$latest_tag" ]]; then export RELEASE_LAST_SHA="$(git rev-list -n 1 "$latest_tag")" fi - RELEASE_EXISTING_PRS="$(gh pr list --repo "$GITHUB_REPOSITORY" --base main --state open --limit 1000 \ - --json number,headRefName,isCrossRepository,author)" - export RELEASE_EXISTING_PRS + release_prs_file="$(mktemp "${RUNNER_TEMP}/release-prs.XXXXXX")" + trap 'if ! rm -f -- "$release_prs_file"; then + echo "::warning::Could not remove release PR candidate file." + fi' EXIT + gh pr list --repo "$GITHUB_REPOSITORY" --base main --state open --limit 1000 \ + --json number,headRefName,isCrossRepository,author > "$release_prs_file" node --input-type=module -e ' + import { readFileSync } from "node:fs"; import { decideDailyRelease } from "./scripts/daily-release-plan.ts"; const e = process.env; const plan = decideDailyRelease({ - existingPrs: JSON.parse(e.RELEASE_EXISTING_PRS), + existingPrs: JSON.parse(readFileSync(process.argv[1], "utf8")), mainSha: e.RELEASE_MAIN_SHA, today: e.RELEASE_TODAY, lastRelease: e.RELEASE_LAST_TAG ? { tag: e.RELEASE_LAST_TAG, sha: e.RELEASE_LAST_SHA } : null, }); for (const [key, value] of Object.entries(plan)) console.log(key + "=" + value); console.log("latest_tag=" + e.RELEASE_LAST_TAG); - ' >> "$GITHUB_OUTPUT" + ' "$release_prs_file" >> "$GITHUB_OUTPUT" # Refuse non-main release runs BEFORE publishing. `github.ref` is the # authoritative workflow trigger ref: schedule -> refs/heads/main; diff --git a/docs/daily-release-refresh.md b/docs/daily-release-refresh.md index 2cf08ec4..9f36a0b4 100644 --- a/docs/daily-release-refresh.md +++ b/docs/daily-release-refresh.md @@ -175,3 +175,11 @@ The final cleanup code head b54ed62af05226d8d67f9ea882eaa92d4006aaf4 passes both CI at receipt head 7483fed passed the full Node 26 coverage gate, then failed changelog:check after main gained v2026.10.19 during the run. The combined changelog incorrectly placed the still-open tagged item in that release instead of Unreleased. A changelog-only correction still produced duplicate release headings in the three-way preview, so the working branch integrated main in the normal environment and regenerated from main's changelog. There were no textual conflicts or pending merge receipts. This incorporates the release metadata and Dependabot-owned toolchain pins without an independent dependency bump. The pinned SDK refreshes its generated merge fence. Normal-home strict health reports a host-global extension version skew; isolated-home health is the repository acceptance check. Corrected gate and CI results are recorded in the PM history and PR. At integrated code head 3886f90197efd5149a4c1e24b7bfe3731fc8b409, npm run release:check and bun run release:check both exit 0 with 921/921 tests each, zero failures/cancellations/skips, and exact 100/100/100/100 across all 15 authored modules. Both measure 10535/10535 lines and statements, 277/277 functions and 3225/3225 branches with zero skipped coverage; every release-check stage, including the regenerated changelog and publication-attestation checks, passes. A fresh non-local clone passes strict full PM health with required merge drivers, validation and privacy. The final receipt changes only tracker evidence and documentation; final-head CI is recorded in PR #152. + +## Review round 2: file-backed candidate responses + +Thread PRRT_kwDOTbN4pM6t6JSR found that exporting up to 1,000 candidate PRs could exceed the process environment limit before Node starts. The Decide release step now writes the GitHub response directly into a unique file under [RUNNER_TEMP](https://docs.github.com/en/actions/reference/workflows-and-actions/variables#default-environment-variables), passes only its path to the Node planning entry point, and reads JSON there. The decision function remains pure. An EXIT trap removes the file after success or failure; cleanup failure warns without masking the original result. GitHub lookup and JSON/read failures remain fatal. + +The named-step harness also keeps its fixture in a file. A 1,000-PR response above 128 KiB selects the eligible final candidate and leaves no candidate file behind. Existing failed-lookup coverage proves exit 47 and file removal. Cleanup-denied cases preserve either planning success or the original lookup failure while warning. Reverting only the decision step to 777a13f reproduces Argument list too long and status 126 before Node starts. Removing only the EXIT trap fails both successful-plan and failed-lookup cleanup assertions. The restored focused suites pass 44/44; native Bun decisions pass 23/23. + +The environment sweep covers all 23 steps. Registry and PR-review JSON remains in files, unexported shell variables or stdin; notes and summaries already use files. The remaining exported values are scalar dates, identifiers, refs, paths and tokens. The one numeric superseded-PR list is capped at 999 entries: the 1,000-candidate probe using maximum safe integer IDs produces 16,982 bytes. No other environment payload needs changing. All 20 shell scripts pass bash syntax validation. Dependency pins, the publication path, coverage scope, thresholds and budgets are unchanged. Final gate receipts and pushed-head CI will be recorded in PM history and PR #152 after execution. diff --git a/test/daily-release-plan.test.ts b/test/daily-release-plan.test.ts index 8fcf8de0..5c22891b 100644 --- a/test/daily-release-plan.test.ts +++ b/test/daily-release-plan.test.ts @@ -1,7 +1,7 @@ /** Daily release policy is independent of GitHub and the wall clock. */ import assert from "node:assert/strict"; import { spawnSync, type SpawnSyncReturns } from "node:child_process"; -import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs"; import { createRequire } from "node:module"; import { devNull, tmpdir } from "node:os"; import { join } from "node:path"; @@ -84,11 +84,15 @@ function step(name: string): WorkflowStep & { run: string } { return found as WorkflowStep & { run: string }; } -/** Execute the complete decision step against bounded synthetic GitHub responses. */ -function runDecision(prs: DailyReleaseInput["existingPrs"], failedLookup = false): SpawnSyncReturns { +/** Execute the complete decision step against file-backed synthetic GitHub responses. */ +function runDecision(prs: DailyReleaseInput["existingPrs"], failedLookup = false, failedCleanup = false): SpawnSyncReturns & { candidateFiles: string[] } { const root = mkdtempSync(join(tmpdir(), "pm-github-release-plan-")); + const fixture = join(root, "prs.json"); + const runnerTemp = join(root, "runner"); + writeFileSync(fixture, JSON.stringify(prs)); + mkdirSync(runnerTemp); try { - return spawnSync("bash", ["-c", ` + const result = spawnSync("bash", ["-c", ` git() { if [[ "$1" == rev-parse ]]; then printf new-main; fi; } date() { printf 2026.10.18; } gh() { @@ -98,12 +102,16 @@ function runDecision(prs: DailyReleaseInput["existingPrs"], failedLookup = false if [[ "$1" == --limit ]]; then limit="$2"; shift; fi shift done - node --input-type=module -e 'console.log(JSON.stringify(JSON.parse(process.env.TEST_PRS).slice(0, Number(process.argv[1]))))' "$limit" + node --input-type=module -e 'import { readFileSync } from "node:fs"; console.log(JSON.stringify(JSON.parse(readFileSync(process.env.TEST_PRS_FILE, "utf8")).slice(0, Number(process.argv[1]))))' "$limit" } + rm() { if [[ "$FAILED_CLEANUP" == true ]]; then printf 'cleanup denied' >&2; return 48; fi; command rm "$@"; } ${step("Decide release").run} cat "$GITHUB_OUTPUT" - `], { encoding: "utf8", env: { ...process.env, RELEASE_TIMEZONE: "UTC", GITHUB_REPOSITORY: "example/package", - GITHUB_OUTPUT: join(root, "outputs"), TEST_PRS: JSON.stringify(prs), FAILED_LOOKUP: String(failedLookup) } }); + `], { encoding: "utf8", env: { ...process.env, HOME: root, TMPDIR: root, GIT_CONFIG_GLOBAL: devNull, GIT_CONFIG_NOSYSTEM: "1", + RELEASE_TIMEZONE: "UTC", GITHUB_REPOSITORY: "example/package", RUNNER_TEMP: runnerTemp, + GITHUB_OUTPUT: join(root, "outputs"), TEST_PRS_FILE: fixture, + FAILED_LOOKUP: String(failedLookup), FAILED_CLEANUP: String(failedCleanup) } }); + return { ...result, candidateFiles: readdirSync(runnerTemp) }; } finally { rmSync(root, { recursive: true, force: true }); } } @@ -115,6 +123,30 @@ test("the complete workflow decision finds an eligible PR beyond the default fir assert.match(result.stdout, /^action=refresh$/m); }); +test("the complete workflow decision reads 1,000 PRs above 128 KiB through a file", () => { + const prs = [...Array.from({ length: 999 }, (_, index) => ({ ...workflowPr, number: index + 1000, + headRefName: `feature/${index}/${"x".repeat(100)}` })), workflowPr]; + assert.ok(Buffer.byteLength(JSON.stringify(prs)) > 128 * 1024); + const result = runDecision(prs); + assert.equal(result.status, 0, result.stderr || String(result.error)); + assert.match(result.stdout, /^pr_number=144$/m); + assert.match(result.stdout, /^action=refresh$/m); + assert.match(result.stdout, /^base_sha=new-main$/m); + assert.deepEqual(result.candidateFiles, [], "the candidate file is removed after planning"); +}); + +test("candidate-file cleanup failure warns without changing planning or lookup exit status", () => { + for (const failedLookup of [false, true]) { + const result = runDecision([workflowPr], failedLookup, true); + assert.equal(result.status, failedLookup ? 47 : 0, result.stderr); + assert.match(result.stdout, /::warning::Could not remove release PR candidate file/); + assert.match(result.stderr, /cleanup denied/); + if (!failedLookup) assert.match(result.stdout, /^pr_number=144$/m); + else assert.doesNotMatch(result.stdout, /^should_release=/m); + assert.equal(result.candidateFiles.length, 1); + } +}); + test("newest branch date wins independently of input order and PR creation order", () => { const candidates = [ { ...workflowPr, number: 300, headRefName: "release/2026.9.30" }, @@ -175,7 +207,7 @@ test("workflow rebuilds before preparation and runs the canonical generator befo assert.equal(refresh.env?.RELEASE_PR_NUMBER, "${{ steps.decide.outputs.pr_number }}"); assert.equal(steps.filter(candidate => candidate.run?.includes("gh pr list")).length, 1); assert.match(decision.run, /--json number,headRefName,isCrossRepository,author/); - assert.doesNotMatch(decision.run, /export RELEASE_EXISTING_PRS=/); + assert.doesNotMatch(decision.run, /\bRELEASE_EXISTING_PRS\b/); assert.ok(steps.indexOf(step("Close superseded release PRs")) > steps.indexOf(refresh)); }); @@ -183,6 +215,7 @@ test("a failed GitHub candidate lookup aborts instead of exporting an empty succ const result = runDecision([], true); assert.equal(result.status, 47); assert.equal(result.stdout, ""); + assert.deepEqual(result.candidateFiles, [], "the candidate file is removed after a failed lookup"); }); test("the push and cleanup boundary refuses an existing ref without a selected workflow PR", () => { From 14b533a3dc944b245af6787628883e475446800b Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Mon, 19 Oct 2026 15:33:14 +0200 Subject: [PATCH 12/12] Record large PR response gate evidence and release the claim --- .agents/pm/history/pm-github-l1ot.jsonl | 2 ++ .agents/pm/issues/pm-github-l1ot.toon | 7 +++---- docs/daily-release-refresh.md | 2 ++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.agents/pm/history/pm-github-l1ot.jsonl b/.agents/pm/history/pm-github-l1ot.jsonl index 2198cbcc..d810f5f9 100644 --- a/.agents/pm/history/pm-github-l1ot.jsonl +++ b/.agents/pm/history/pm-github-l1ot.jsonl @@ -54,3 +54,5 @@ {"hash_algorithm":"sha256","ts":"2026-10-19T12:06:42.803Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"claim","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T12:06:42.803Z"},{"op":"add","path":"/metadata/assignee","value":"codex-agent"},{"op":"add","path":"/metadata/claim_principal","value":"codex-agent"}],"before_hash":"c4e227cb3f74aa474947156b2936971767e8e765912852350ad01ac3453aca7d","after_hash":"ee14147eee43119d5b21900d08460abde039c9c8cb62c1f21e403744e0f5d8d1","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"c70bc689e52a1325da825d9553b223855814368f76763f8367e2aec295d1d349"} {"hash_algorithm":"sha256","ts":"2026-10-19T12:06:44.824Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/29","value":{"created_at":"2026-10-19T12:06:44.823Z","author":"codex-agent","text":"Review round 2 confirms the candidate response is exported as RELEASE_EXISTING_PRS, so a sufficiently large list can prevent Node from starting with E2BIG. Plan: keep GitHub JSON in a temporary file, pass only its path, remove the file on success or failure, and retain fail-closed lookup behavior. The named-step harness must also read its large fixture from a file. Will test 1,000 candidates above 128 KiB and sweep the other workflow environment transfers. Prior round 1 gates and CI remain completed evidence for 777a13f."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T12:06:44.824Z"}],"before_hash":"ee14147eee43119d5b21900d08460abde039c9c8cb62c1f21e403744e0f5d8d1","after_hash":"ba9c8bc2f01c787553aad7a769848ccb4ab4bee5298f972fbb5b54ac46c43f5d","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"9eb88e054308567e5c8626b8bd6ac427e81476ca0c0aefd6cb171befa5b5afca"} {"hash_algorithm":"sha256","ts":"2026-10-19T12:11:59.137Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/30","value":{"created_at":"2026-10-19T12:11:59.137Z","author":"codex-agent","text":"Round 2 behavioral revert proofs: with the file-backed harness and tests retained, reverting only the Decide release step to 777a13f fails the 1,000-PR regression with ERR_ASSERTION and Bash status 126, Argument list too long before Node starts. Restoring the file-based step passes. Removing only its EXIT cleanup trap fails both the successful-plan and failed-lookup file-removal assertions; restored cleanup passes and a failed lookup retains exit 47. Cleanup-denied tests preserve success or the original lookup failure while warning. Focused release suites pass 44/44; native Bun decisions pass 23/23. The sweep covered all 23 steps and 20 shell scripts, all bash syntax checks pass. No other unbounded exported payload was found: registry/review JSON stays in files, shell variables or stdin, summaries/notes use files, and other env values are scalar identities/paths/tokens. The 1,000-candidate numeric-ID stress probe yields 999 superseded IDs in 16,982 bytes using maximum safe integer IDs. No package pin, threshold or publication-path change is needed."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T12:11:59.137Z"}],"before_hash":"ba9c8bc2f01c787553aad7a769848ccb4ab4bee5298f972fbb5b54ac46c43f5d","after_hash":"e07d82183f0d172ad1e338fad1e81616c43bd5ed1696cfdb8a0b1c98ac70bc30","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"bea248f69fc460a83c07e936577e07f6e67bb405929a35bc6a2ecaf8b8b130a5"} +{"hash_algorithm":"sha256","ts":"2026-10-19T13:33:05.204Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/31","value":{"created_at":"2026-10-19T13:33:05.203Z","author":"codex-agent","text":"Round 2 final gates at code head 17a267ea76b05f989710c8c7af5dc66ff24c6f8c: npm run release:check and bun run release:check each exit 0 with 923/923 tests, zero failures/cancellations/skips, and exact 100/100/100/100 across all 15 authored modules. Both measure 10535/10535 lines and statements, 277/277 functions and 3225/3225 branches, zero skipped coverage; every release-check stage passes. Focused and linked commands pass 44/44; native Bun decisions pass 23/23. The 1,000-PR fixture is 210,796 bytes. The original decision-step revert reproduces E2BIG before the planner starts, and removing only the EXIT trap fails both file-removal assertions; restored tests pass. All 22 other step definitions match 777a13f structurally; no further env transfer needs changing. Thresholds, budgets, source inventory, dependency pins and publication protections are unchanged. No new CLI/SDK defects: pm context automatically rebuilt a stale context cache, and linked result storage remains disabled by policy. Final receipt changes only tracker evidence and documentation; fresh-clone health/validation/privacy and final-head CI evidence will be recorded in https://github.com/unbraind/pm-github/pull/152."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T13:33:05.204Z"}],"before_hash":"e07d82183f0d172ad1e338fad1e81616c43bd5ed1696cfdb8a0b1c98ac70bc30","after_hash":"820ec98465de02d83136bfe3222594e634a90b087dc7ad450b361476ccf84018","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"907b4551485a084b71cf302a9f0eaa9f5529c2d05ec392c607444b3b30ffd965"} +{"hash_algorithm":"sha256","ts":"2026-10-19T13:33:07.859Z","author":"codex-agent","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"e4446d2a1278b42fdf0b9f7f","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"release","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-19T13:33:07.859Z"}],"before_hash":"820ec98465de02d83136bfe3222594e634a90b087dc7ad450b361476ccf84018","after_hash":"7462e204b9092cb04884c67b8e59c774019f7ed14b10dd4edd903f757c1f70f1","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"dc46ca541629d65ce8710b8b567acfd62b66eb3968c41e93bb64d3e75b627b07"} diff --git a/.agents/pm/issues/pm-github-l1ot.toon b/.agents/pm/issues/pm-github-l1ot.toon index 60a3ee27..0406939d 100644 --- a/.agents/pm/issues/pm-github-l1ot.toon +++ b/.agents/pm/issues/pm-github-l1ot.toon @@ -6,12 +6,10 @@ status: in_progress priority: 1 tags[3]: changelog,changelog-unreleased,release created_at: "2026-10-15T09:25:36.259Z" -updated_at: "2026-10-19T12:11:59.137Z" -assignee: codex-agent -claim_principal: codex-agent +updated_at: "2026-10-19T13:33:07.859Z" author: claude-hub acceptance_criteria: "release.yml generates CHANGELOG.md and the release notes through the package's own changelog scripts with the same status filter as changelog:check; a test fails if the two disagree; a release run with an in_progress item tagged changelog-unreleased and delivered since the last tag either succeeds or fails before any bump with a clear reason" -comments[31]{created_at,author,text}: +comments[32]{created_at,author,text}: "2026-10-18T18:19:00.873Z",codex-agent,"Reproduced the release generator split: release.yml uses pm-changelog closed-only output while changelog:check includes tagged pending work. The next run fails before refreshing the protected release PR. Plan: one canonical generator, rebuild from freshly fetched main, refresh or supersede stale PRs, and enforce one release per calendar day with pure decision regressions. Related: https://github.com/unbraind/pm-github/pull/144 and https://github.com/unbraind/pm-ado/issues/33" "2026-10-18T18:22:11.207Z",codex-agent,"Behavioral function-only revert proof: keeping the same decision function and tests, restored the old unconditional same-day eligibility and today-only branch selection. node --test test/daily-release-plan.test.ts failed 2/4 assertions: the old PR branch was abandoned and a second same-day release was allowed. Restored implementation: focused release suites passed 13/13 and the linked PM command passed. The release generator now invokes changelog:full, changelog:check and release:notes; lease-protected refresh updates the reused PR title/version to today." "2026-10-18T18:32:32.900Z",codex-agent,"Consumer context: https://github.com/unbraind/pm-beads/pull/145 https://github.com/unbraind/pm-brief/pull/154 https://github.com/unbraind/pm-context/pull/147 https://github.com/unbraind/pm-csv/pull/163 https://github.com/unbraind/pm-linear/pull/150 https://github.com/unbraind/pm-slack-standup/pull/127. Separate attribution fix: https://github.com/unbraind/pm-ado/issues/33. The proof report contains all 8 real imported terminal rows, exact before/main lifecycle fields, explicit completed_at projection limits, historical release placement and momentum. Six baseline entries are under Unreleased; all 8 projected rows render under historical releases when terminal statuses are selected." @@ -43,6 +41,7 @@ comments[31]{created_at,author,text}: "2026-10-19T11:26:38.298Z",codex-agent,"Corrected integrated code head 3886f90197efd5149a4c1e24b7bfe3731fc8b409: npm run release:check and bun run release:check both exit 0, 921/921 tests each, zero failures/cancellations/skips. Both retain exact 100/100/100/100 across all 15 authored modules: 10535/10535 lines and statements, 277/277 functions, 3225/3225 branches, zero skipped coverage. Every release-check stage passes, including canonical changelog and publication attestation. Previous pushed 7483fed passed coverage in both CI jobs but failed the changelog after main released; integration and regeneration correct it. No textual merge conflicts, stale receipts or pending receipts; no reconciliation needed. Fresh non-local clone passes isolated-home strict full PM health with required drivers, validation and privacy. No new CLI/SDK defects; host-global version skew is avoided by isolation and linked result storage remains disabled by policy. Existing cleanup revert proofs remain in this history. Final receipt changes only tracker evidence and documentation, and final-head CI will be recorded in https://github.com/unbraind/pm-github/pull/152." "2026-10-19T12:06:44.823Z",codex-agent,"Review round 2 confirms the candidate response is exported as RELEASE_EXISTING_PRS, so a sufficiently large list can prevent Node from starting with E2BIG. Plan: keep GitHub JSON in a temporary file, pass only its path, remove the file on success or failure, and retain fail-closed lookup behavior. The named-step harness must also read its large fixture from a file. Will test 1,000 candidates above 128 KiB and sweep the other workflow environment transfers. Prior round 1 gates and CI remain completed evidence for 777a13f." "2026-10-19T12:11:59.137Z",codex-agent,"Round 2 behavioral revert proofs: with the file-backed harness and tests retained, reverting only the Decide release step to 777a13f fails the 1,000-PR regression with ERR_ASSERTION and Bash status 126, Argument list too long before Node starts. Restoring the file-based step passes. Removing only its EXIT cleanup trap fails both the successful-plan and failed-lookup file-removal assertions; restored cleanup passes and a failed lookup retains exit 47. Cleanup-denied tests preserve success or the original lookup failure while warning. Focused release suites pass 44/44; native Bun decisions pass 23/23. The sweep covered all 23 steps and 20 shell scripts, all bash syntax checks pass. No other unbounded exported payload was found: registry/review JSON stays in files, shell variables or stdin, summaries/notes use files, and other env values are scalar identities/paths/tokens. The 1,000-candidate numeric-ID stress probe yields 999 superseded IDs in 16,982 bytes using maximum safe integer IDs. No package pin, threshold or publication-path change is needed." + "2026-10-19T13:33:05.203Z",codex-agent,"Round 2 final gates at code head 17a267ea76b05f989710c8c7af5dc66ff24c6f8c: npm run release:check and bun run release:check each exit 0 with 923/923 tests, zero failures/cancellations/skips, and exact 100/100/100/100 across all 15 authored modules. Both measure 10535/10535 lines and statements, 277/277 functions and 3225/3225 branches, zero skipped coverage; every release-check stage passes. Focused and linked commands pass 44/44; native Bun decisions pass 23/23. The 1,000-PR fixture is 210,796 bytes. The original decision-step revert reproduces E2BIG before the planner starts, and removing only the EXIT trap fails both file-removal assertions; restored tests pass. All 22 other step definitions match 777a13f structurally; no further env transfer needs changing. Thresholds, budgets, source inventory, dependency pins and publication protections are unchanged. No new CLI/SDK defects: pm context automatically rebuilt a stale context cache, and linked result storage remains disabled by policy. Final receipt changes only tracker evidence and documentation; fresh-clone health/validation/privacy and final-head CI evidence will be recorded in https://github.com/unbraind/pm-github/pull/152." files[5]{path,scope}: .github/workflows/release.yml,project package.json,project diff --git a/docs/daily-release-refresh.md b/docs/daily-release-refresh.md index 9f36a0b4..739287a4 100644 --- a/docs/daily-release-refresh.md +++ b/docs/daily-release-refresh.md @@ -183,3 +183,5 @@ Thread PRRT_kwDOTbN4pM6t6JSR found that exporting up to 1,000 candidate PRs coul The named-step harness also keeps its fixture in a file. A 1,000-PR response above 128 KiB selects the eligible final candidate and leaves no candidate file behind. Existing failed-lookup coverage proves exit 47 and file removal. Cleanup-denied cases preserve either planning success or the original lookup failure while warning. Reverting only the decision step to 777a13f reproduces Argument list too long and status 126 before Node starts. Removing only the EXIT trap fails both successful-plan and failed-lookup cleanup assertions. The restored focused suites pass 44/44; native Bun decisions pass 23/23. The environment sweep covers all 23 steps. Registry and PR-review JSON remains in files, unexported shell variables or stdin; notes and summaries already use files. The remaining exported values are scalar dates, identifiers, refs, paths and tokens. The one numeric superseded-PR list is capped at 999 entries: the 1,000-candidate probe using maximum safe integer IDs produces 16,982 bytes. No other environment payload needs changing. All 20 shell scripts pass bash syntax validation. Dependency pins, the publication path, coverage scope, thresholds and budgets are unchanged. Final gate receipts and pushed-head CI will be recorded in PM history and PR #152 after execution. + +At round 2 code head 17a267ea76b05f989710c8c7af5dc66ff24c6f8c, npm run release:check and bun run release:check both exit 0 with 923/923 tests each and zero failures/cancellations/skips. Both retain exact 100/100/100/100 across all 15 authored modules: 10535/10535 lines and statements, 277/277 functions, 3225/3225 branches, zero skipped coverage. Every release-check stage passes. The named-step large fixture is 210,796 bytes; focused and linked suites pass 44/44 and native Bun decisions pass 23/23. All 22 other workflow steps match 777a13f structurally. The final receipt changes only tracker evidence and documentation; fresh-clone checks and final-head CI will be recorded in PR #152.