diff --git a/.agents/pm/history/pm-context-7yrb.jsonl b/.agents/pm/history/pm-context-7yrb.jsonl index 3b86ef4..a4c65b8 100644 --- a/.agents/pm/history/pm-context-7yrb.jsonl +++ b/.agents/pm/history/pm-context-7yrb.jsonl @@ -1,2 +1,27 @@ {"ts":"2026-08-09T13:46:47.076Z","author":"claude","author_source":"asserted","agent_harness":"claude-code","agent_instance":"6c62aab177c939482aa0465b","agent_provenance":{"model":null,"effort":{"value":"xhigh","source":"environment"},"role":{"value":"1","source":"environment"},"topic":null},"op":"create","patch":[{"op":"add","path":"/metadata/id","value":"pm-context-7yrb"},{"op":"add","path":"/metadata/title","value":"A release that merges but fails to publish is abandoned by the next day's run"},{"op":"add","path":"/metadata/description","value":"Decide release computes the version from today's date and the existing tags, and the tag is pushed only after npm publish succeeds. If the protected release PR merges and publication then fails, main carries release metadata for a version that was never tagged or published. The next day's run sees the old latest tag, computes a new version from the new date, and commits it over the merged metadata, so the earlier version is silently skipped on npm. Needs a guard in Decide release that detects a committed-but-untagged version and resumes it rather than computing a new one."},{"op":"add","path":"/metadata/type","value":"Issue"},{"op":"add","path":"/metadata/status","value":"open"},{"op":"add","path":"/metadata/priority","value":2},{"op":"add","path":"/metadata/tags","value":[]},{"op":"add","path":"/metadata/created_at","value":"2026-08-09T13:46:47.076Z"},{"op":"add","path":"/metadata/updated_at","value":"2026-08-09T13:46:47.076Z"},{"op":"add","path":"/metadata/author","value":"claude"}],"before_hash":"3cc22dff72be7b14824654a7a64ea62b04799939b2fee54c1b5f52ca60bf6df0","after_hash":"a60dfe9b93db0a9a17b3a4bbf0c3b0f4b6c6bcd512ab9a8de608973641b8ae66","message":""} {"ts":"2026-08-28T19:54:24.340Z","author":"pi-agent","author_source":"asserted","agent_harness":"pi","op":"history_repair","patch":[],"before_hash":"a60dfe9b93db0a9a17b3a4bbf0c3b0f4b6c6bcd512ab9a8de608973641b8ae66","after_hash":"a60dfe9b93db0a9a17b3a4bbf0c3b0f4b6c6bcd512ab9a8de608973641b8ae66","message":"Append-only correction: preserve the preceding 1 immutable event records and record the legacy role normalization explicitly for event 1.","context":{"provenance_normalization":{"changed":true,"events_changed":1,"observations_removed":1,"invalid_values":[{"harness":"claude-code","dimension":"role","kind":"single_digit","count":1}],"removed_observations":[{"event_index":1,"path":"/agent_provenance/role"}],"preserved_events":1}}} +{"hash_algorithm":"sha256","ts":"2026-09-29T15:44:37.191Z","author":"codex","author_source":"configured","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"8631999cd2a9166b79919f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T15:44:37.191Z"},{"op":"add","path":"/metadata/acceptance_criteria","value":"A prepared release commit without its matching tag is selected before a new calendar version, even after later main commits; The resume path publishes and tags the exact prepared commit without a new version bump or release PR; Missing or inconsistent release metadata fails closed with an actionable diagnostic; Fixture tests prove first-run, failed-publish retry, later commits, and tagged release behavior"},{"op":"add","path":"/metadata/outcome","value":"Retry the same immutable release coordinate after a publish failure"},{"op":"add","path":"/metadata/risk","value":"high"}],"before_hash":"a60dfe9b93db0a9a17b3a4bbf0c3b0f4b6c6bcd512ab9a8de608973641b8ae66","after_hash":"3ccbaf999fc4b36b2819c6f382e8273fb23bdd6ec8366ea787a81c707a618127","item_hash_version":3,"message":"Define recovery acceptance before implementation","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":"3e9bd30f0918bf5a120c29299a8b413b54f6b37df933eb4c3537ffbb175bda9d"} +{"hash_algorithm":"sha256","ts":"2026-09-29T15:44:44.609Z","author":"codex","author_source":"configured","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"8631999cd2a9166b79919f8d","agent_provenance":{"model":{"value":"gpt-6-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-09-29T15:44:44.609Z"},{"op":"add","path":"/metadata/assignee","value":"codex"},{"op":"add","path":"/metadata/claim_principal","value":"codex"}],"before_hash":"3ccbaf999fc4b36b2819c6f382e8273fb23bdd6ec8366ea787a81c707a618127","after_hash":"c4c5e6ac0da352d8569f3b677a82ab25527728f03f55653642307d02c300eded","item_hash_version":3,"message":"Implement and validate pending release recovery","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":"ba6c982e2581e6586a9b053ddd165c509d420312a95cb4f427d0143ea7da808a"} +{"hash_algorithm":"sha256","ts":"2026-09-29T15:44:44.657Z","author":"codex","author_source":"configured","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"8631999cd2a9166b79919f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T15:44:44.657Z"},{"op":"replace","path":"/metadata/status","value":"in_progress"}],"before_hash":"c4c5e6ac0da352d8569f3b677a82ab25527728f03f55653642307d02c300eded","after_hash":"e753bf5ab268a9fd6edceb1a5cf264ffd723441128dca5b6fb7bcf9a166b90e2","item_hash_version":3,"message":"Implement and validate pending release recovery","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":"0e362cb38a7bd95b3ed1c1a9d7dfbd1394ded415b66cbc85fa2137e9965c50ac"} +{"hash_algorithm":"sha256","ts":"2026-09-29T15:53:11.217Z","author":"codex","author_source":"configured","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"8631999cd2a9166b79919f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"update","patch":[{"op":"replace","path":"/body","value":"Resume the oldest untagged release commit after the latest merged tag. Select its immutable SHA and verify package, manifest, and lock versions before any new version is considered. Resume skips metadata mutation and protected PR creation, validates the prepared commit, restores notes from its committed changelog, then uses the existing attested npm publish, tag, and GitHub Release steps. Rollout is the reviewed workflow change only; rollback is reverting that workflow commit before a run. No production data migration."},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T15:53:11.217Z"},{"op":"add","path":"/metadata/files","value":[{"path":".github/workflows/release.yml","scope":"project","note":"release decision and resume path"},{"path":"test/release-workflow.test.ts","scope":"project","note":"disposable Git history and notes recovery fixtures"}]},{"op":"add","path":"/metadata/tests","value":[{"command":"node --test test/release-workflow.test.ts","path":"test/release-workflow.test.ts","scope":"project","timeout_seconds":120,"provenance":{"author":"codex","created_at":"2026-09-29T15:53:11.190Z","source_kind":"local_mutation","source_ref":"fix/resume-untagged-release-2026-09-29"}},{"command":"npm run release:check","path":"package.json","scope":"project","timeout_seconds":600,"provenance":{"author":"codex","created_at":"2026-09-29T15:53:11.190Z","source_kind":"local_mutation","source_ref":"fix/resume-untagged-release-2026-09-29"}}]}],"before_hash":"e753bf5ab268a9fd6edceb1a5cf264ffd723441128dca5b6fb7bcf9a166b90e2","after_hash":"7a9bd00d977a3f4485fda8f83e52717130b2d948912a5deeac4b05360b513996","item_hash_version":3,"message":"Link implementation, acceptance fixtures, rollout, and rollback","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":"2ccfcbe809bb86906ae7224be4ebf4571da8e0ab9d4dc66c473214c9350304c0"} +{"hash_algorithm":"sha256","ts":"2026-09-29T15:53:11.783Z","author":"codex","author_source":"configured","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"8631999cd2a9166b79919f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"note_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T15:53:11.783Z"},{"op":"add","path":"/metadata/notes","value":[{"created_at":"2026-09-29T15:53:11.783Z","author":"codex","text":"Local candidate: 13 focused release-workflow tests passed; full release:check passed 282 tests with V8 lines/branches/functions at 100%, 27 documented declarations, npm audit zero production vulnerabilities, pack and attestation checks. Changelog check passed. Clean npm and Bun installs of the packed pm-context@2026.9.26 tarball with @unbrained/pm-cli@2026.9.29 loaded default.activate and buildContextPack. Statement coverage and all executable script inclusion remain separate quality gaps; this item stays open through PR review."}]}],"before_hash":"7a9bd00d977a3f4485fda8f83e52717130b2d948912a5deeac4b05360b513996","after_hash":"bb0d0924f5a729a839a96a78e12adbd34075c942d155a56fbd7309ad4f3b1442","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":"1ce55c786d5bc95b1fe9fa4cad8dc397307ba4db728bd95f5fa8dd8efeac8175"} +{"hash_algorithm":"sha256","ts":"2026-09-29T16:02:04.755Z","author":"codex","author_source":"configured","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"8631999cd2a9166b79919f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"update","patch":[{"op":"add","path":"/metadata/tests/2","value":{"command":"./node_modules/.bin/c8 --all --include=index.ts --include=context-usage.ts --include=scripts/*.ts --exclude=test/** --reporter=text-summary node --test test/*.test.ts","path":"package.json","scope":"project","timeout_seconds":180,"provenance":{"author":"codex","created_at":"2026-09-29T16:02:04.727Z","source_kind":"local_mutation","source_ref":"fix/resume-untagged-release-2026-09-29"}}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T16:02:04.755Z"},{"op":"add","path":"/metadata/docs","value":[{"path":"https://github.com/unbraind/pm-context/pull/122","scope":"global","note":"review candidate"}]}],"before_hash":"bb0d0924f5a729a839a96a78e12adbd34075c942d155a56fbd7309ad4f3b1442","after_hash":"ecf69187757e56dd09a1ee849fb1b728b623953376f9a1ad72ad3eb5884f234b","item_hash_version":3,"message":"Link PR and independent all-source coverage measurement","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":"25b8de1b5455f91e7e0be6741e51dcce68e512b27a7bc5c9d1cbfad230281db6"} +{"hash_algorithm":"sha256","ts":"2026-09-29T16:02:05.306Z","author":"codex","author_source":"configured","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"8631999cd2a9166b79919f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"note_add","patch":[{"op":"add","path":"/metadata/notes/1","value":{"created_at":"2026-09-29T16:02:05.306Z","author":"codex","text":"Greptile review of c641931 found a valid body-grep defect: a source commit body quoting Release pm-context v... was selected as a release. A disposable Git regression failed before the fix; the decision now filters subjects only. Its fixture-remote objection did not reproduce locally, but the fixture now declares a self remote and the inconsistent-metadata assertion checks the actual diagnostic. New local release:check passed; independent c8 across eight authored TypeScript source files ran 283 tests and measured statements 3393/3393 (100%), branches 904/909 (99.44%), functions 91/91 (100%), lines 3393/3393 (100%). Five branch arms remain, so coverage is a merge blocker."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T16:02:05.306Z"}],"before_hash":"ecf69187757e56dd09a1ee849fb1b728b623953376f9a1ad72ad3eb5884f234b","after_hash":"1314ebe12913faacfcc7fd338c2caa5e6600ae39d7d5e35ac3c158434aa3e8ac","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":"b3eab2bd46b19bd2db2315a6117c8e8818171ffdbeebcbf584e41564aa284d3c"} +{"hash_algorithm":"sha256","ts":"2026-09-29T16:07:43.548Z","author":"codex","author_source":"configured","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"8631999cd2a9166b79919f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"note_add","patch":[{"op":"add","path":"/metadata/notes/2","value":{"created_at":"2026-09-29T16:07:43.548Z","author":"codex","text":"Review round 2 on de7e19c: required CI test (22)/(26), CodeQL, Semgrep and Greptile passed. CodeRabbit correctly noted the fixture used an unpadded tag; it now uses v2026.09.27 and asserts npm_version 2026.9.27. Its proposed alternate changelog heading was rejected with evidence: pinned pm-changelog 2026.9.23 resolveReleaseTagWindowResolution on pendingVersion v2026.09.29 returned 2026.9.29 - 2026-09-29, and formatTagVersion strips the v and calendar padding. No production tag-form heading is generated on this workflow path. Sourcery reported weekly review quota exhaustion and supplied no substantive review."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T16:07:43.548Z"}],"before_hash":"1314ebe12913faacfcc7fd338c2caa5e6600ae39d7d5e35ac3c158434aa3e8ac","after_hash":"d8637b1a26a49d62a11053450859c2a42de1d653f675f196b214d0ea7a6a4110","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":"dbfbc99efdbb2f3a4248049c086685d7cece92de848e6b2d4f25e9f09a174599"} +{"hash_algorithm":"sha256","ts":"2026-09-29T16:11:12.972Z","author":"codex","author_source":"configured","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"8631999cd2a9166b79919f8d","agent_provenance":{"model":{"value":"gpt-6-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-29T16:11:12.972Z"},{"op":"replace","path":"/metadata/status","value":"blocked"},{"op":"add","path":"/metadata/blocked_by","value":"pm-context-3s5f"},{"op":"add","path":"/metadata/blocked_reason","value":"All authored source: c8 branches 904/909 (99.44%); required 100% branch gate is not met. PR #122 remains unmerged."},{"op":"add","path":"/metadata/dependencies","value":[{"id":"pm-context-3s5f","kind":"blocked_by","created_at":"2026-09-29T16:11:12.924Z","author":"codex","source_kind":"cli:update:blocked_by","author_source":"detected"}]}],"before_hash":"d8637b1a26a49d62a11053450859c2a42de1d653f675f196b214d0ea7a6a4110","after_hash":"c1ea2d413b42233fc8a614c6b637007471cbd028b04adefead9dbd52e12a35cf","item_hash_version":3,"message":"Implementation and fixture acceptance complete; wait for independent all-source coverage gate","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":"a5f2fbb30e4f4ee535cd489a666c59301141431c5f66bc81999a7a6e06545bd2"} +{"hash_algorithm":"sha256","ts":"2026-09-29T16:11:17.200Z","author":"codex","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6-sol","agent_model_source":"probe","agent_instance":"8631999cd2a9166b79919f8d","agent_provenance":{"model":{"value":"gpt-6-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-09-29T16:11:17.200Z"}],"before_hash":"c1ea2d413b42233fc8a614c6b637007471cbd028b04adefead9dbd52e12a35cf","after_hash":"06e112de496e97e8e4519aa62899f663b37b17d722a6d76d420404554606e7c3","item_hash_version":3,"message":"Blocked on repository-wide branch coverage and final review; PR #122 left open","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":"5e2acb597f4dc7ff0b70ea34cab40a4bab462022024f4de11068a0d0b9fab2fd"} +{"hash_algorithm":"sha256","ts":"2026-10-02T22:09:48.478Z","author":"codex-sol","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"b2389d19e26c4d93974a8087","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-02T22:09:48.478Z"},{"op":"add","path":"/metadata/assignee","value":"codex-sol"},{"op":"add","path":"/metadata/claim_principal","value":"codex-sol"}],"before_hash":"06e112de496e97e8e4519aa62899f663b37b17d722a6d76d420404554606e7c3","after_hash":"8845b8ffde55f47add661ad7f3f8d314c4e6e7ac7437b75187e0994ba802c87b","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":"4d1c68d766fb3d280b0d8982b011203c7e39111aebe0acd3be1bfd97dfc2f61b"} +{"hash_algorithm":"sha256","ts":"2026-10-02T22:09:49.383Z","author":"codex-sol","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"b2389d19e26c4d93974a8087","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-02T22:09:49.383Z"},{"op":"replace","path":"/metadata/status","value":"in_progress"}],"before_hash":"8845b8ffde55f47add661ad7f3f8d314c4e6e7ac7437b75187e0994ba802c87b","after_hash":"1bf4ad6ee514bce386627f09da39982c686423200beddec82ce9305b6461fd90","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":"950cfc6d03b08d182824df24d0579cbd2939cbe4300bcf8871eeccc6ab6c67a6"} +{"hash_algorithm":"sha256","ts":"2026-10-02T22:09:50.013Z","author":"codex-sol","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"b2389d19e26c4d93974a8087","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-02T22:09:50.013Z"},{"op":"add","path":"/metadata/comments","value":[{"created_at":"2026-10-02T22:09:50.013Z","author":"codex-sol","text":"Restart by codex-sol: preserved remote PR #122 and all existing bot replies/reactions were inspected. Branch is already current with origin/main. No new actionable unresolved inline finding is present. Retain documented coverage/health/publication blockers; rerun unchanged full CI gate with the host-wide flock. No merge, publish or item closure."}]}],"before_hash":"1bf4ad6ee514bce386627f09da39982c686423200beddec82ce9305b6461fd90","after_hash":"73a4c75a893eb9142b138acf3bbdce814fb1c00300ea265bfc7aa7b14a608ba8","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":"58007267a935d15003d7195390687986449e99fc1aac1bebe77ec2bcb0a4fb14"} +{"hash_algorithm":"sha256","ts":"2026-10-02T22:09:50.926Z","author":"codex-sol","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"b2389d19e26c4d93974a8087","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"files_add","patch":[{"op":"remove","path":"/metadata/files/1/note"},{"op":"replace","path":"/metadata/files/1/path","value":"package.json"},{"op":"add","path":"/metadata/files/2","value":{"path":"test/release-workflow.test.ts","scope":"project","note":"disposable Git history and notes recovery fixtures"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-02T22:09:50.926Z"}],"before_hash":"73a4c75a893eb9142b138acf3bbdce814fb1c00300ea265bfc7aa7b14a608ba8","after_hash":"7136f16faf12549c8b7f6246788a1aa2a00d12785b4d0ae8b8479b061abb1b3a","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":"d8956fb298a7247aa33acfef4e585d327decdc410b583ef3fd3841372a7a7e82"} +{"hash_algorithm":"sha256","ts":"2026-10-02T22:09:51.689Z","author":"codex-sol","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"b2389d19e26c4d93974a8087","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"docs_add","patch":[{"op":"add","path":"/metadata/docs/1","value":{"path":"README.md","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-02T22:09:51.689Z"}],"before_hash":"7136f16faf12549c8b7f6246788a1aa2a00d12785b4d0ae8b8479b061abb1b3a","after_hash":"cf6c4bcaac06315f9eca47c496a204a10ffa442106eaadbb2c8d9844732d98a9","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":"2c8ed2dff2c44748277270969ddc4ccdd9ed17e00d75ba9f4067e8e4305fccb1"} +{"hash_algorithm":"sha256","ts":"2026-10-02T22:09:52.345Z","author":"codex-sol","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"b2389d19e26c4d93974a8087","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/release-workflow.test.ts","scope":"project","provenance":{"author":"codex-sol","created_at":"2026-10-02T22:09:52.307Z","source_kind":"local_mutation","source_ref":"land-restart/pm-context-122"}}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-02T22:09:52.345Z"}],"before_hash":"cf6c4bcaac06315f9eca47c496a204a10ffa442106eaadbb2c8d9844732d98a9","after_hash":"b71d19265728d29db2c314154b379d4289b90f8b61a546615dcbafcbc40b2168","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":"91cd5d76ced8a552cd36baca6cfe896abfb061601b5aff0703203e2b82b305b1"} +{"hash_algorithm":"sha256","ts":"2026-10-02T22:22:40.756Z","author":"codex-sol","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"b2389d19e26c4d93974a8087","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/1","value":{"created_at":"2026-10-02T22:22:40.756Z","author":"codex-sol","text":"Restart validation: flock-protected npm ci and unchanged release:check pass, 283/283 tests and zero skips; configured coverage reports 100/100/100 over five sources, without a separate statements dimension. PM-linked 14-test actual release-workflow Git fixtures, extra CI changelog:check, and strict health with merge drivers pass. Prior subject-only scan and production padded-tag fixes remain intact; normalized-heading refusal remains justified by the pinned generator. Whole-authored-source coverage requirement remains tracked as pm-context-3s5f. No merge, publication or close; claim released for orchestrator."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-02T22:22:40.756Z"}],"before_hash":"b71d19265728d29db2c314154b379d4289b90f8b61a546615dcbafcbc40b2168","after_hash":"a8ec8e596b7d00b2f64d233aeba86d9fb2c52dc03bc13b45440d5a523890a983","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":"4b71d0a6ce21de47ebe826baa009307303cbd348d02b5cb72a8a79100a44b039"} +{"hash_algorithm":"sha256","ts":"2026-10-02T22:22:41.294Z","author":"codex-sol","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"b2389d19e26c4d93974a8087","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-02T22:22:41.294Z"},{"op":"replace","path":"/metadata/status","value":"blocked"}],"before_hash":"a8ec8e596b7d00b2f64d233aeba86d9fb2c52dc03bc13b45440d5a523890a983","after_hash":"80bb8bfafec2127fa694ef147e890a29f962828f32ca85c562f21c9e646a438f","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":"d6709e501eb17963b79118698d549874ea7187d85398d4dc5aacfba10e1feee6"} +{"hash_algorithm":"sha256","ts":"2026-10-02T22:22:41.749Z","author":"codex-sol","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"b2389d19e26c4d93974a8087","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-02T22:22:41.749Z"}],"before_hash":"80bb8bfafec2127fa694ef147e890a29f962828f32ca85c562f21c9e646a438f","after_hash":"6aef856ab091bed348e23d5ac30f303814b4d7ead0739cda8996e705bef5f2af","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":"05b7e35ad88ae824fb2d448afe6db76feed9ee32c58527faf6e70e701c81074b"} +{"hash_algorithm":"sha256","ts":"2026-10-02T22:49:40.511Z","author":"codex-sol","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"b2389d19e26c4d93974a8087","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-02T22:49:40.511Z"},{"op":"add","path":"/metadata/assignee","value":"codex-sol"},{"op":"add","path":"/metadata/claim_principal","value":"codex-sol"}],"before_hash":"6aef856ab091bed348e23d5ac30f303814b4d7ead0739cda8996e705bef5f2af","after_hash":"5c4fbfcddc911f8293100885aad5f39b43c05bc6f0ccb93e7a20823b9d3a13b4","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":"86d413401b002fb5c3a1775057eee0c8a38b287ee429784f918b758eedeeecda"} +{"hash_algorithm":"sha256","ts":"2026-10-02T22:49:41.364Z","author":"codex-sol","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"b2389d19e26c4d93974a8087","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-02T22:49:41.364Z"},{"op":"replace","path":"/metadata/status","value":"in_progress"}],"before_hash":"5c4fbfcddc911f8293100885aad5f39b43c05bc6f0ccb93e7a20823b9d3a13b4","after_hash":"514fdace93cae999a6cf4d169aaef1eef1ac939310d6f79ad76eb78bb2c836eb","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":"899491cd586af801fd14829d109d16d3cbec533dea442cd565ad575e3aeabe83"} +{"hash_algorithm":"sha256","ts":"2026-10-02T22:49:42.260Z","author":"codex-sol","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"b2389d19e26c4d93974a8087","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/2","value":{"created_at":"2026-10-02T22:49:42.260Z","author":"codex-sol","text":"Cubic review round: strengthen resume guards against commented/env-only tokens with an actual mutated-workflow regression. Retain immutable-checkout release:check: it is validation, not metadata generation, and skipping it would weaken a required gate. Refuse author-identity filtering because Git identity is not authenticity and ignoring malformed release metadata would bypass fail-closed recovery."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-02T22:49:42.260Z"}],"before_hash":"514fdace93cae999a6cf4d169aaef1eef1ac939310d6f79ad76eb78bb2c836eb","after_hash":"3df54d02ae467cd0ca3b9e738030ba0543f03b4eea9ede321ee2b380bba51e93","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":"870dbe029cf06ba06700e5bd89732641188a7f309f3d23875d361a398e22b6fc"} +{"hash_algorithm":"sha256","ts":"2026-10-02T23:08:57.199Z","author":"codex-sol","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"b2389d19e26c4d93974a8087","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/3","value":{"created_at":"2026-10-02T23:08:57.198Z","author":"codex-sol","text":"Cubic guard assertion repair now anchors the resume exclusion to an executable step if directive. Commenting out the actual generation guard fails the focused assertion; restored real workflow and PM-linked suite pass 14/14. Full locked release:check passes 283/283, zero skips, configured five-source L/B/F 100/100/100; separate CI changelog:check passes. Refuse skipping release:check on resume: it validates immutable checkout and never regenerates metadata. Refuse workflow-author filtering: spoofable Git identity is not authenticity, and ignoring malformed prepared-release metadata would evade the fail-closed boundary. No gate was weakened. All-authored-source/statements scope pm-context-3s5f and substantive review prerequisites remain open."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-02T23:08:57.199Z"}],"before_hash":"3df54d02ae467cd0ca3b9e738030ba0543f03b4eea9ede321ee2b380bba51e93","after_hash":"17d03c81856ed0803acbb45b386e6cde063f893cc0dae239baf020055fb56b02","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":"26dc8ecc0b0738583a94aa87d53781419d542cd71d54d4199360aa0f3c025416"} +{"hash_algorithm":"sha256","ts":"2026-10-02T23:08:58.045Z","author":"codex-sol","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"b2389d19e26c4d93974a8087","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"high","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-02T23:08:58.045Z"},{"op":"replace","path":"/metadata/status","value":"blocked"}],"before_hash":"17d03c81856ed0803acbb45b386e6cde063f893cc0dae239baf020055fb56b02","after_hash":"74cd1d3d4af8691bb9cc457906ff34508b1064b8bbc9c1765bd927ab76d84089","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":"fdaedbd3d70031e70829b1f3034bb55214357bd76dd170ff3deed17021b8d8e6"} +{"hash_algorithm":"sha256","ts":"2026-10-02T23:08:58.658Z","author":"codex-sol","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"b2389d19e26c4d93974a8087","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-02T23:08:58.658Z"}],"before_hash":"74cd1d3d4af8691bb9cc457906ff34508b1064b8bbc9c1765bd927ab76d84089","after_hash":"28eb20da69e24d54241bddba2c4e243dc634c42c9b206b82dac49ace5e614edd","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":"a39af09dbe40b523f13cff644c74216398e2605e05583c647f8a405d3639d250"} diff --git a/.agents/pm/issues/pm-context-7yrb.toon b/.agents/pm/issues/pm-context-7yrb.toon index 09f3154..dbab1ff 100644 --- a/.agents/pm/issues/pm-context-7yrb.toon +++ b/.agents/pm/issues/pm-context-7yrb.toon @@ -2,10 +2,76 @@ id: pm-context-7yrb title: A release that merges but fails to publish is abandoned by the next day's run description: "Decide release computes the version from today's date and the existing tags, and the tag is pushed only after npm publish succeeds. If the protected release PR merges and publication then fails, main carries release metadata for a version that was never tagged or published. The next day's run sees the old latest tag, computes a new version from the new date, and commits it over the merged metadata, so the earlier version is silently skipped on npm. Needs a guard in Decide release that detects a committed-but-untagged version and resumes it rather than computing a new one." type: Issue -status: open +status: blocked priority: 2 tags: [] created_at: "2026-08-09T13:46:47.076Z" -updated_at: "2026-08-09T13:46:47.076Z" +updated_at: "2026-10-02T23:08:58.658Z" author: claude -body: "" +acceptance_criteria: "A prepared release commit without its matching tag is selected before a new calendar version, even after later main commits; The resume path publishes and tags the exact prepared commit without a new version bump or release PR; Missing or inconsistent release metadata fails closed with an actionable diagnostic; Fixture tests prove first-run, failed-publish retry, later commits, and tagged release behavior" +outcome: Retry the same immutable release coordinate after a publish failure +risk: high +blocked_by: pm-context-3s5f +blocked_reason: "All authored source: c8 branches 904/909 (99.44%); required 100% branch gate is not met. PR #122 remains unmerged." +dependencies[1]{id,kind,created_at,author,source_kind,author_source}: + pm-context-3s5f,blocked_by,"2026-09-29T16:11:12.924Z",codex,"cli:update:blocked_by",detected +comments[4]{created_at,author,text}: + "2026-10-02T22:09:50.013Z",codex-sol,"Restart by codex-sol: preserved remote PR #122 and all existing bot replies/reactions were inspected. Branch is already current with origin/main. No new actionable unresolved inline finding is present. Retain documented coverage/health/publication blockers; rerun unchanged full CI gate with the host-wide flock. No merge, publish or item closure." + "2026-10-02T22:22:40.756Z",codex-sol,"Restart validation: flock-protected npm ci and unchanged release:check pass, 283/283 tests and zero skips; configured coverage reports 100/100/100 over five sources, without a separate statements dimension. PM-linked 14-test actual release-workflow Git fixtures, extra CI changelog:check, and strict health with merge drivers pass. Prior subject-only scan and production padded-tag fixes remain intact; normalized-heading refusal remains justified by the pinned generator. Whole-authored-source coverage requirement remains tracked as pm-context-3s5f. No merge, publication or close; claim released for orchestrator." + "2026-10-02T22:49:42.260Z",codex-sol,"Cubic review round: strengthen resume guards against commented/env-only tokens with an actual mutated-workflow regression. Retain immutable-checkout release:check: it is validation, not metadata generation, and skipping it would weaken a required gate. Refuse author-identity filtering because Git identity is not authenticity and ignoring malformed release metadata would bypass fail-closed recovery." + "2026-10-02T23:08:57.198Z",codex-sol,"Cubic guard assertion repair now anchors the resume exclusion to an executable step if directive. Commenting out the actual generation guard fails the focused assertion; restored real workflow and PM-linked suite pass 14/14. Full locked release:check passes 283/283, zero skips, configured five-source L/B/F 100/100/100; separate CI changelog:check passes. Refuse skipping release:check on resume: it validates immutable checkout and never regenerates metadata. Refuse workflow-author filtering: spoofable Git identity is not authenticity, and ignoring malformed prepared-release metadata would evade the fail-closed boundary. No gate was weakened. All-authored-source/statements scope pm-context-3s5f and substantive review prerequisites remain open." +notes[3]{created_at,author,text}: + "2026-09-29T15:53:11.783Z",codex,"Local candidate: 13 focused release-workflow tests passed; full release:check passed 282 tests with V8 lines/branches/functions at 100%, 27 documented declarations, npm audit zero production vulnerabilities, pack and attestation checks. Changelog check passed. Clean npm and Bun installs of the packed pm-context@2026.9.26 tarball with @unbrained/pm-cli@2026.9.29 loaded default.activate and buildContextPack. Statement coverage and all executable script inclusion remain separate quality gaps; this item stays open through PR review." + "2026-09-29T16:02:05.306Z",codex,"Greptile review of c641931 found a valid body-grep defect: a source commit body quoting Release pm-context v... was selected as a release. A disposable Git regression failed before the fix; the decision now filters subjects only. Its fixture-remote objection did not reproduce locally, but the fixture now declares a self remote and the inconsistent-metadata assertion checks the actual diagnostic. New local release:check passed; independent c8 across eight authored TypeScript source files ran 283 tests and measured statements 3393/3393 (100%), branches 904/909 (99.44%), functions 91/91 (100%), lines 3393/3393 (100%). Five branch arms remain, so coverage is a merge blocker." + "2026-09-29T16:07:43.548Z",codex,"Review round 2 on de7e19c: required CI test (22)/(26), CodeQL, Semgrep and Greptile passed. CodeRabbit correctly noted the fixture used an unpadded tag; it now uses v2026.09.27 and asserts npm_version 2026.9.27. Its proposed alternate changelog heading was rejected with evidence: pinned pm-changelog 2026.9.23 resolveReleaseTagWindowResolution on pendingVersion v2026.09.29 returned 2026.9.29 - 2026-09-29, and formatTagVersion strips the v and calendar padding. No production tag-form heading is generated on this workflow path. Sourcery reported weekly review quota exhaustion and supplied no substantive review." +files[3]: + - path: .github/workflows/release.yml + scope: project + note: release decision and resume path + - path: package.json + scope: project + - path: test/release-workflow.test.ts + scope: project + note: disposable Git history and notes recovery fixtures +tests[4]: + - command: node --test test/release-workflow.test.ts + path: test/release-workflow.test.ts + scope: project + timeout_seconds: 120 + provenance: + author: codex + created_at: "2026-09-29T15:53:11.190Z" + source_kind: local_mutation + source_ref: fix/resume-untagged-release-2026-09-29 + - command: "npm run release:check" + path: package.json + scope: project + timeout_seconds: 600 + provenance: + author: codex + created_at: "2026-09-29T15:53:11.190Z" + source_kind: local_mutation + source_ref: fix/resume-untagged-release-2026-09-29 + - command: ./node_modules/.bin/c8 --all --include=index.ts --include=context-usage.ts --include=scripts/*.ts --exclude=test/** --reporter=text-summary node --test test/*.test.ts + path: package.json + scope: project + timeout_seconds: 180 + provenance: + author: codex + created_at: "2026-09-29T16:02:04.727Z" + source_kind: local_mutation + source_ref: fix/resume-untagged-release-2026-09-29 + - command: node --test test/release-workflow.test.ts + scope: project + provenance: + author: codex-sol + created_at: "2026-10-02T22:09:52.307Z" + source_kind: local_mutation + source_ref: land-restart/pm-context-122 +docs[2]: + - path: "https://github.com/unbraind/pm-context/pull/122" + scope: global + note: review candidate + - path: README.md + scope: project +body: "Resume the oldest untagged release commit after the latest merged tag. Select its immutable SHA and verify package, manifest, and lock versions before any new version is considered. Resume skips metadata mutation and protected PR creation, validates the prepared commit, restores notes from its committed changelog, then uses the existing attested npm publish, tag, and GitHub Release steps. Rollout is the reviewed workflow change only; rollback is reverting that workflow commit before a run. No production data migration." diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 73ca3d8..106555a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -78,12 +78,59 @@ jobs: run: | set -euo pipefail git fetch --force --tags - latest_tag="$(git tag --sort=-creatordate | head -n 1 || true)" + latest_tag="$(git tag --merged HEAD --sort=-creatordate | head -n 1 || true)" if [[ -n "$latest_tag" ]] && git diff --quiet "$latest_tag"..HEAD -- .; then echo "should_release=false" >> "$GITHUB_OUTPUT" echo "No changes since $latest_tag; skipping release." >> "$GITHUB_STEP_SUMMARY" exit 0 fi + # A protected release PR can merge before npm accepts the artifact. + # Find the oldest release commit after the last tag and complete that + # immutable transaction before choosing another calendar version. + # The commit can be behind HEAD when source work landed meanwhile. + range="${latest_tag:+${latest_tag}..}HEAD" + while IFS= read -r candidate_sha; do + [[ -z "$candidate_sha" ]] && continue + subject="$(git show -s --format=%s "$candidate_sha")" + candidate_tag="${subject#Release pm-context }" + if [[ "$candidate_tag" == "$subject" ]] || ! grep -Eq '^v[0-9]{4}\.[0-9]{1,2}\.[0-9]{1,2}(-[0-9]+)?$' <<< "$candidate_tag"; then + echo "::error::Release commit ${candidate_sha} has an invalid version title: ${subject}" + exit 1 + fi + if git show-ref --verify --quiet "refs/tags/${candidate_tag}"; then + tagged_sha="$(git rev-list -n 1 "$candidate_tag")" + if [[ "$tagged_sha" != "$candidate_sha" ]]; then + echo "::error::${candidate_tag} points to ${tagged_sha}, not prepared release ${candidate_sha}." + exit 1 + fi + continue + fi + version_core="${candidate_tag#v}" + year="${version_core%%.*}" + version_tail="${version_core#*.}" + month="${version_tail%%.*}" + day_and_suffix="${version_tail#*.}" + day="${day_and_suffix%%-*}" + suffix="${day_and_suffix#"$day"}" + candidate_version="$((10#$year)).$((10#$month)).$((10#$day))${suffix}" + package_version="$(git show "${candidate_sha}:package.json" | jq -r .version)" + manifest_version="$(git show "${candidate_sha}:manifest.json" | jq -r .version)" + lock_version="$(git show "${candidate_sha}:package-lock.json" | jq -r .version)" + if [[ "$package_version" != "$candidate_version" || "$manifest_version" != "$candidate_version" || "$lock_version" != "$candidate_version" ]]; then + echo "::error::Prepared release ${candidate_sha} (${candidate_tag}) has inconsistent package, manifest or lock versions; refusing to mint a newer release." + exit 1 + fi + { + echo "should_release=true" + echo "resume=true" + echo "tag=$candidate_tag" + echo "npm_version=$candidate_version" + echo "release_sha=$candidate_sha" + echo "base_sha=$(git rev-parse HEAD)" + } >> "$GITHUB_OUTPUT" + echo "Resuming untagged ${candidate_tag} from ${candidate_sha}." >> "$GITHUB_STEP_SUMMARY" + exit 0 + done < <(git log --reverse --format='%H%x09%s' "$range" | awk -F '\t' '$2 ~ /^Release pm-context v/ { print $1 }') release_date="$(TZ="$RELEASE_TIMEZONE" date +%Y.%m.%d)" base_tag="v${release_date}" max_suffix=-1 @@ -113,6 +160,7 @@ jobs: suffix="${day_and_suffix#"$day"}" npm_version="$((10#$year)).$((10#$month)).$((10#$day))${suffix}" echo "should_release=true" >> "$GITHUB_OUTPUT" + echo "resume=false" >> "$GITHUB_OUTPUT" echo "tag=$tag" >> "$GITHUB_OUTPUT" echo "npm_version=$npm_version" >> "$GITHUB_OUTPUT" echo "base_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" @@ -249,7 +297,7 @@ jobs: exit 1 - name: Update release version - if: steps.decide.outputs.should_release == 'true' + if: steps.decide.outputs.should_release == 'true' && steps.decide.outputs.resume != 'true' env: NPM_VERSION: ${{ steps.decide.outputs.npm_version }} shell: bash @@ -267,7 +315,7 @@ jobs: npm run build - name: Generate changelog and release notes - if: steps.decide.outputs.should_release == 'true' + if: steps.decide.outputs.should_release == 'true' && steps.decide.outputs.resume != 'true' shell: bash env: # RELEASE_TAG flows through env rather than inline template expansion @@ -318,7 +366,7 @@ jobs: fi - name: Run release checks - if: steps.decide.outputs.should_release == 'true' + if: steps.decide.outputs.should_release == 'true' && steps.decide.outputs.resume != 'true' shell: bash run: | set -euo pipefail @@ -333,7 +381,7 @@ jobs: npm run release:check - name: Commit release files - if: steps.decide.outputs.should_release == 'true' + if: steps.decide.outputs.should_release == 'true' && steps.decide.outputs.resume != 'true' env: REPO_NAME: ${{ github.event.repository.name }} RELEASE_TAG: ${{ steps.decide.outputs.tag }} @@ -355,7 +403,7 @@ jobs: fi - name: Merge release metadata through protected PR - if: steps.decide.outputs.should_release == 'true' + if: steps.decide.outputs.should_release == 'true' && steps.decide.outputs.resume != 'true' id: release_pr shell: bash env: @@ -622,17 +670,22 @@ jobs: if: steps.decide.outputs.should_release == 'true' shell: bash env: - MERGED_SHA: ${{ steps.release_pr.outputs.merged_sha }} + MERGED_SHA: ${{ steps.decide.outputs.release_sha || steps.release_pr.outputs.merged_sha }} + RESUME_RELEASE: ${{ steps.decide.outputs.resume }} NPM_VERSION: ${{ steps.decide.outputs.npm_version }} run: | set -euo pipefail git fetch origin main --force - git checkout --detach origin/main - actual_sha="$(git rev-parse HEAD)" - if [[ "$actual_sha" != "$MERGED_SHA" ]]; then - echo "::error::main advanced from merged release ${MERGED_SHA} to ${actual_sha} before publication. Retry to rebuild release metadata on the new head." + if [[ "$RESUME_RELEASE" == "true" ]]; then + if ! git merge-base --is-ancestor "$MERGED_SHA" origin/main; then + echo "::error::Prepared release ${MERGED_SHA} is no longer an ancestor of main; refusing the release." + exit 1 + fi + elif [[ "$(git rev-parse origin/main)" != "$MERGED_SHA" ]]; then + echo "::error::main advanced from merged release ${MERGED_SHA} before publication. Retry from the new main head." exit 1 fi + git checkout --detach "$MERGED_SHA" actual_version="$(npm pkg get version | tr -d '"')" if [[ "$actual_version" != "$NPM_VERSION" ]]; then @@ -659,6 +712,22 @@ jobs: fi fi + - name: Restore prepared release notes + if: steps.decide.outputs.should_release == 'true' && steps.decide.outputs.resume == 'true' + shell: bash + env: + NPM_VERSION: ${{ steps.decide.outputs.npm_version }} + run: | + set -euo pipefail + awk -v version="$NPM_VERSION" ' + /^## / { if (found) exit; if (index($0, "## " version " - ") == 1) found = 1 } + found { print } + ' CHANGELOG.md > RELEASE_NOTES.md + if [[ ! -s RELEASE_NOTES.md ]]; then + echo "::error::The prepared release ${NPM_VERSION} has no changelog section; refusing to create empty release notes." + exit 1 + fi + # Authentication is npm trusted publishing (OIDC), not a long-lived token. # The registry mints a short-lived credential from this workflow's id-token, # so no NODE_AUTH_TOKEN is set here on purpose: a stored token is the thing diff --git a/test/release-workflow.test.ts b/test/release-workflow.test.ts index 5d100f5..46786ed 100644 --- a/test/release-workflow.test.ts +++ b/test/release-workflow.test.ts @@ -1,7 +1,10 @@ import assert from "node:assert/strict"; -import { readFileSync } from "node:fs"; +import { execFileSync, spawnSync } from "node:child_process"; +import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; import { resolve } from "node:path"; import test from "node:test"; +import { parse } from "yaml"; /** The release workflow source, read once and asserted against as text. */ const workflow = readFileSync( @@ -9,6 +12,113 @@ const workflow = readFileSync( "utf-8" ); +/** Execute the real Decide release script against a disposable Git history. */ +function decideRelease(commits: Array<{ version: string; title: string; body?: string; tag?: string; manifestVersion?: string }>): Record { + const root = mkdtempSync(resolve(tmpdir(), "pm-context-release-resume-")); + try { + execFileSync("git", ["init", "-q", "-b", "main"], { cwd: root }); + execFileSync("git", ["remote", "add", "origin", root], { cwd: root }); + execFileSync("git", ["config", "user.name", "Release Fixture"], { cwd: root }); + execFileSync("git", ["config", "user.email", "release-fixture@example.invalid"], { cwd: root }); + for (const commit of commits) { + writeFileSync(resolve(root, "package.json"), JSON.stringify({ name: "pm-context", version: commit.version })); + writeFileSync(resolve(root, "manifest.json"), JSON.stringify({ version: commit.manifestVersion ?? commit.version })); + writeFileSync(resolve(root, "package-lock.json"), JSON.stringify({ version: commit.version })); + writeFileSync(resolve(root, "change.txt"), commit.title); + execFileSync("git", ["add", "."], { cwd: root }); + execFileSync("git", ["commit", "-qm", commit.title, ...(commit.body ? ["-m", commit.body] : [])], { cwd: root }); + if (commit.tag) execFileSync("git", ["tag", commit.tag], { cwd: root }); + } + const document = parse(workflow) as { jobs: { release: { steps: Array<{ name?: string; run?: string }> } } }; + const script = document.jobs.release.steps.find((step) => step.name === "Decide release")?.run; + assert.ok(script, "workflow must expose its release decision script"); + const output = resolve(root, "github-output"); + writeFileSync(output, ""); + const run = spawnSync("bash", ["-e", "-c", script], { + cwd: root, + encoding: "utf8", + env: { ...process.env, GITHUB_OUTPUT: output, GITHUB_STEP_SUMMARY: resolve(root, "summary"), RELEASE_TIMEZONE: "Europe/Vienna" }, + }); + if (run.error) throw run.error; + if (run.status !== 0) throw new Error(`${run.stdout}\n${run.stderr}`); + return Object.fromEntries(readFileSync(output, "utf8").trim().split("\n").map((line) => line.split("=", 2))); + } finally { + rmSync(root, { recursive: true, force: true }); + } +} + +test("a failed publish resumes its prepared commit after the calendar day and later source commits", () => { + const result = decideRelease([ + { version: "2026.9.26", title: "Release pm-context v2026.09.26", tag: "v2026.09.26" }, + { version: "2026.9.27", title: "Release pm-context v2026.09.27" }, + { version: "2026.9.27", title: "Document a later feature" }, + ]); + assert.equal(result.should_release, "true"); + assert.equal(result.resume, "true"); + assert.equal(result.tag, "v2026.09.27"); + assert.equal(result.npm_version, "2026.9.27"); + assert.match(result.release_sha ?? "", /^[0-9a-f]{40}$/); + assert.notEqual(result.release_sha, result.base_sha, "later source commits must not change the release artifact"); +}); + +test("a tagged release followed by new work chooses a new version", () => { + const result = decideRelease([ + { version: "2026.9.26", title: "Release pm-context v2026.9.26", tag: "v2026.9.26" }, + { version: "2026.9.26", title: "Add context usage fixture" }, + ]); + assert.equal(result.should_release, "true"); + assert.equal(result.resume, "false"); + assert.notEqual(result.tag, "v2026.9.26"); +}); + +test("a quoted release title in an ordinary commit body does not block a new release", () => { + const result = decideRelease([ + { version: "2026.9.26", title: "Release pm-context v2026.9.26", tag: "v2026.9.26" }, + { version: "2026.9.26", title: "Document recovery behavior", body: "Release pm-context v2026.9.27" }, + ]); + assert.equal(result.should_release, "true"); + assert.equal(result.resume, "false"); +}); + +test("inconsistent prepared metadata stops the release instead of minting another version", () => { + assert.throws( + () => decideRelease([ + { version: "2026.9.26", title: "Release pm-context v2026.9.26", tag: "v2026.9.26" }, + { version: "2026.9.27", manifestVersion: "2026.9.26", title: "Release pm-context v2026.9.27" }, + ]), + /inconsistent package, manifest or lock versions/, + ); +}); + +test("resume notes are extracted from the prepared version and absent notes fail closed", () => { + const root = mkdtempSync(resolve(tmpdir(), "pm-context-release-notes-")); + try { + const document = parse(workflow) as { jobs: { release: { steps: Array<{ name?: string; run?: string }> } } }; + const script = document.jobs.release.steps.find((step) => step.name === "Restore prepared release notes")?.run; + assert.ok(script); + writeFileSync(resolve(root, "CHANGELOG.md"), "# Changelog\n\n## 2026.9.27 - 2026-09-27\nRecovered fix\n\n## 2026.9.26 - 2026-09-26\nOld fix\n"); + execFileSync("bash", ["-e", "-c", script], { cwd: root, env: { ...process.env, NPM_VERSION: "2026.9.27" } }); + assert.equal(readFileSync(resolve(root, "RELEASE_NOTES.md"), "utf8"), "## 2026.9.27 - 2026-09-27\nRecovered fix\n\n"); + const missing = spawnSync("bash", ["-e", "-c", script], { + cwd: root, + encoding: "utf8", + env: { ...process.env, NPM_VERSION: "2026.9.25" }, + }); + assert.equal(missing.status, 1); + assert.match(missing.stdout, /has no changelog section/); + } finally { + rmSync(root, { recursive: true, force: true }); + } +}); + +test("a prepared version cannot flow through metadata generation or another release PR", () => { + for (const step of ["Update release version", "Generate changelog and release notes", "Run release checks", "Commit release files", "Merge release metadata through protected PR"]) { + assert.match(executable(stepSource(step)), /^ *if: .*steps\.decide\.outputs\.resume != 'true'/m); + } + assert.match(stepSource("Verify merged release"), /steps\.decide\.outputs\.release_sha/); + assert.match(stepSource("Restore prepared release notes"), /CHANGELOG\.md/); +}); + /** * Locate a named workflow step so tests can assert on ordering between steps. * @@ -441,7 +551,10 @@ test("publication is proven possible before anything is mutated", () => { 1, "the preflight must carry exactly one condition, so none can shadow the release condition" ); - assert.match(executable(stepSource("Update release version")), preflightCondition); + assert.match( + executable(stepSource("Update release version")), + /^ *if: steps\.decide\.outputs\.should_release == 'true' && steps\.decide\.outputs\.resume != 'true'$/m, + ); // A second `trap ... EXIT` REPLACES the first, so appending one is enough to // keep the credential file on disk while every assertion above still passes.