From c64193178f047f58d6879440aff44032f28f12a7 Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Tue, 29 Sep 2026 17:53:44 +0200 Subject: [PATCH 1/6] Resume untagged pm-context releases from prepared commit Select the oldest untagged protected release commit before choosing a new calendar version. Verify its package, manifest and lock metadata, run the existing release checks against the exact commit, restore its committed release notes, and reuse the attested publish and tag transaction. Exercise the decision script against disposable Git histories, including later source commits, tagged releases and inconsistent metadata. Validate notes recovery and keep pm-context-7yrb as the source tracker. Validation: npm run release:check (282 tests; V8 lines/branches/functions 100%), npm run changelog:check, pm health --strict-exit, clean packed npm and Bun imports with pm CLI 2026.9.29. Statement coverage and whole-source gate remain open quality blockers. --- .agents/pm/history/pm-context-7yrb.jsonl | 5 ++ .agents/pm/issues/pm-context-7yrb.toon | 19 ++++- .github/workflows/release.yml | 91 +++++++++++++++++--- test/release-workflow.test.ts | 101 ++++++++++++++++++++++- 4 files changed, 200 insertions(+), 16 deletions(-) diff --git a/.agents/pm/history/pm-context-7yrb.jsonl b/.agents/pm/history/pm-context-7yrb.jsonl index 3b86ef4..3ab77b5 100644 --- a/.agents/pm/history/pm-context-7yrb.jsonl +++ b/.agents/pm/history/pm-context-7yrb.jsonl @@ -1,2 +1,7 @@ {"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"} diff --git a/.agents/pm/issues/pm-context-7yrb.toon b/.agents/pm/issues/pm-context-7yrb.toon index 09f3154..abdaadd 100644 --- a/.agents/pm/issues/pm-context-7yrb.toon +++ b/.agents/pm/issues/pm-context-7yrb.toon @@ -2,10 +2,23 @@ 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: in_progress priority: 2 tags: [] created_at: "2026-08-09T13:46:47.076Z" -updated_at: "2026-08-09T13:46:47.076Z" +updated_at: "2026-09-29T15:53:11.783Z" +assignee: codex +claim_principal: codex 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 +notes[1]{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." +files[2]{path,scope,note}: + .github/workflows/release.yml,project,release decision and resume path + test/release-workflow.test.ts,project,disposable Git history and notes recovery fixtures +tests[2]{command,path,scope,timeout_seconds,provenance{author,created_at,source_kind,source_ref}}: + node --test test/release-workflow.test.ts,test/release-workflow.test.ts,project,120,codex,"2026-09-29T15:53:11.190Z",local_mutation,fix/resume-untagged-release-2026-09-29 + "npm run release:check",package.json,project,600,codex,"2026-09-29T15:53:11.190Z",local_mutation,fix/resume-untagged-release-2026-09-29 +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..e2a3316 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 "$range" --grep='^Release pm-context v') 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..c112270 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 } 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,97 @@ const workflow = readFileSync( "utf-8" ); +/** Execute the real Decide release script against a disposable Git history. */ +function decideRelease(commits: Array<{ version: string; title: 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", ["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], { 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, ""); + execFileSync("bash", ["-e", "-c", script], { + cwd: root, + env: { ...process.env, GITHUB_OUTPUT: output, GITHUB_STEP_SUMMARY: resolve(root, "summary"), RELEASE_TIMEZONE: "Europe/Vienna" }, + }); + 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.9.26", tag: "v2026.9.26" }, + { version: "2026.9.27", title: "Release pm-context v2026.9.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.9.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("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" }, + ]), + /Command failed/, + ); +}); + +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"); + assert.throws( + () => execFileSync("bash", ["-e", "-c", script], { cwd: root, env: { ...process.env, NPM_VERSION: "2026.9.25" } }), + /Command failed/, + ); + } 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(stepSource(step), /steps\.decide\.outputs\.resume != 'true'/); + } + 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 +535,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. From de7e19c95d61ffacfaec86523810a0eded649892 Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Tue, 29 Sep 2026 18:02:51 +0200 Subject: [PATCH 2/6] Filter pending releases by subject and harden recovery fixtures Fix the review finding where a quoted release title in an ordinary commit body stopped the daily release. The regression failed before the subject-only scan and passes after it. Give disposable Git fixtures an explicit remote, check the actual inconsistent-metadata diagnostic rather than any child-process failure, and record the independent 100/99.44/100/100 all-source coverage measurement in pm-context-7yrb. Validation: 283 tests in release:check, 14 focused workflow tests, c8 all-source, changelog check, strict health and attestation gate. --- .agents/pm/history/pm-context-7yrb.jsonl | 2 ++ .agents/pm/issues/pm-context-7yrb.toon | 10 ++++--- .github/workflows/release.yml | 2 +- test/release-workflow.test.ts | 34 +++++++++++++++++------- 4 files changed, 35 insertions(+), 13 deletions(-) diff --git a/.agents/pm/history/pm-context-7yrb.jsonl b/.agents/pm/history/pm-context-7yrb.jsonl index 3ab77b5..75bec33 100644 --- a/.agents/pm/history/pm-context-7yrb.jsonl +++ b/.agents/pm/history/pm-context-7yrb.jsonl @@ -5,3 +5,5 @@ {"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"} diff --git a/.agents/pm/issues/pm-context-7yrb.toon b/.agents/pm/issues/pm-context-7yrb.toon index abdaadd..b8bbfbd 100644 --- a/.agents/pm/issues/pm-context-7yrb.toon +++ b/.agents/pm/issues/pm-context-7yrb.toon @@ -6,19 +6,23 @@ status: in_progress priority: 2 tags: [] created_at: "2026-08-09T13:46:47.076Z" -updated_at: "2026-09-29T15:53:11.783Z" +updated_at: "2026-09-29T16:02:05.306Z" assignee: codex claim_principal: codex author: claude 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 -notes[1]{created_at,author,text}: +notes[2]{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." files[2]{path,scope,note}: .github/workflows/release.yml,project,release decision and resume path test/release-workflow.test.ts,project,disposable Git history and notes recovery fixtures -tests[2]{command,path,scope,timeout_seconds,provenance{author,created_at,source_kind,source_ref}}: +tests[3]{command,path,scope,timeout_seconds,provenance{author,created_at,source_kind,source_ref}}: node --test test/release-workflow.test.ts,test/release-workflow.test.ts,project,120,codex,"2026-09-29T15:53:11.190Z",local_mutation,fix/resume-untagged-release-2026-09-29 "npm run release:check",package.json,project,600,codex,"2026-09-29T15:53:11.190Z",local_mutation,fix/resume-untagged-release-2026-09-29 + ./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,package.json,project,180,codex,"2026-09-29T16:02:04.727Z",local_mutation,fix/resume-untagged-release-2026-09-29 +docs[1]{path,scope,note}: + "https://github.com/unbraind/pm-context/pull/122",global,review candidate 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 e2a3316..106555a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -130,7 +130,7 @@ jobs: } >> "$GITHUB_OUTPUT" echo "Resuming untagged ${candidate_tag} from ${candidate_sha}." >> "$GITHUB_STEP_SUMMARY" exit 0 - done < <(git log --reverse --format=%H "$range" --grep='^Release pm-context v') + 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 diff --git a/test/release-workflow.test.ts b/test/release-workflow.test.ts index c112270..84bdfcc 100644 --- a/test/release-workflow.test.ts +++ b/test/release-workflow.test.ts @@ -1,5 +1,5 @@ import assert from "node:assert/strict"; -import { execFileSync } from "node:child_process"; +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"; @@ -13,10 +13,11 @@ const workflow = readFileSync( ); /** Execute the real Decide release script against a disposable Git history. */ -function decideRelease(commits: Array<{ version: string; title: string; tag?: string; manifestVersion?: string }>): Record { +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) { @@ -25,7 +26,7 @@ function decideRelease(commits: Array<{ version: string; title: string; tag?: st 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], { 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 }> } } }; @@ -33,10 +34,13 @@ function decideRelease(commits: Array<{ version: string; title: string; tag?: st assert.ok(script, "workflow must expose its release decision script"); const output = resolve(root, "github-output"); writeFileSync(output, ""); - execFileSync("bash", ["-e", "-c", script], { + 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 }); @@ -67,13 +71,22 @@ test("a tagged release followed by new work chooses a new version", () => { 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" }, ]), - /Command failed/, + /inconsistent package, manifest or lock versions/, ); }); @@ -86,10 +99,13 @@ test("resume notes are extracted from the prepared version and absent notes fail 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"); - assert.throws( - () => execFileSync("bash", ["-e", "-c", script], { cwd: root, env: { ...process.env, NPM_VERSION: "2026.9.25" } }), - /Command failed/, - ); + 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 }); } From 691b105e775e4846f90ca02f1d65d08346091e62 Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Tue, 29 Sep 2026 18:08:27 +0200 Subject: [PATCH 3/6] Model padded release tags in recovery acceptance Use the actual vYYYY.MM.DD commit/tag spelling in the failed-publication fixture while asserting npm_version remains normalized. Record CodeRabbit review dispositions and pinned pm-changelog heading evidence in pm-context-7yrb. Validation: full release:check, focused workflow tests and changelog check. --- .agents/pm/history/pm-context-7yrb.jsonl | 1 + .agents/pm/issues/pm-context-7yrb.toon | 5 +++-- test/release-workflow.test.ts | 6 +++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.agents/pm/history/pm-context-7yrb.jsonl b/.agents/pm/history/pm-context-7yrb.jsonl index 75bec33..7df304f 100644 --- a/.agents/pm/history/pm-context-7yrb.jsonl +++ b/.agents/pm/history/pm-context-7yrb.jsonl @@ -7,3 +7,4 @@ {"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"} diff --git a/.agents/pm/issues/pm-context-7yrb.toon b/.agents/pm/issues/pm-context-7yrb.toon index b8bbfbd..5b0bf59 100644 --- a/.agents/pm/issues/pm-context-7yrb.toon +++ b/.agents/pm/issues/pm-context-7yrb.toon @@ -6,16 +6,17 @@ status: in_progress priority: 2 tags: [] created_at: "2026-08-09T13:46:47.076Z" -updated_at: "2026-09-29T16:02:05.306Z" +updated_at: "2026-09-29T16:07:43.548Z" assignee: codex claim_principal: codex author: claude 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 -notes[2]{created_at,author,text}: +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[2]{path,scope,note}: .github/workflows/release.yml,project,release decision and resume path test/release-workflow.test.ts,project,disposable Git history and notes recovery fixtures diff --git a/test/release-workflow.test.ts b/test/release-workflow.test.ts index 84bdfcc..2482f6a 100644 --- a/test/release-workflow.test.ts +++ b/test/release-workflow.test.ts @@ -49,13 +49,13 @@ function decideRelease(commits: Array<{ version: string; title: string; body?: s 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.9.26", tag: "v2026.9.26" }, - { version: "2026.9.27", title: "Release pm-context v2026.9.27" }, + { 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.9.27"); + 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"); From e9c4e46d176d51333701864a6aed9443c50aac9e Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Tue, 29 Sep 2026 18:11:32 +0200 Subject: [PATCH 4/6] Record pm-context-7yrb coverage blocker and release claim Keep the prepared-release recovery PR open while repository-wide branch coverage remains 904/909. Link the existing coverage owner item pm-context-3s5f, preserve the implementation evidence, and release inactive ownership. Validation: pm health --strict-exit --json; pm validate --json (existing advisory warnings); git diff --check. --- .agents/pm/history/pm-context-7yrb.jsonl | 2 ++ .agents/pm/issues/pm-context-7yrb.toon | 10 ++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.agents/pm/history/pm-context-7yrb.jsonl b/.agents/pm/history/pm-context-7yrb.jsonl index 7df304f..4d192bc 100644 --- a/.agents/pm/history/pm-context-7yrb.jsonl +++ b/.agents/pm/history/pm-context-7yrb.jsonl @@ -8,3 +8,5 @@ {"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"} diff --git a/.agents/pm/issues/pm-context-7yrb.toon b/.agents/pm/issues/pm-context-7yrb.toon index 5b0bf59..2bac716 100644 --- a/.agents/pm/issues/pm-context-7yrb.toon +++ b/.agents/pm/issues/pm-context-7yrb.toon @@ -2,17 +2,19 @@ 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: in_progress +status: blocked priority: 2 tags: [] created_at: "2026-08-09T13:46:47.076Z" -updated_at: "2026-09-29T16:07:43.548Z" -assignee: codex -claim_principal: codex +updated_at: "2026-09-29T16:11:17.200Z" author: claude 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 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." From c78f7b177003b4d5f2354dd17268ed7bdd370805 Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Sat, 3 Oct 2026 00:22:41 +0200 Subject: [PATCH 5/6] docs(context): record release retry validation and remaining gates --- .agents/pm/history/pm-context-7yrb.jsonl | 9 ++++ .agents/pm/issues/pm-context-7yrb.toon | 64 ++++++++++++++++++++---- 2 files changed, 63 insertions(+), 10 deletions(-) diff --git a/.agents/pm/history/pm-context-7yrb.jsonl b/.agents/pm/history/pm-context-7yrb.jsonl index 4d192bc..155dba5 100644 --- a/.agents/pm/history/pm-context-7yrb.jsonl +++ b/.agents/pm/history/pm-context-7yrb.jsonl @@ -10,3 +10,12 @@ {"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"} diff --git a/.agents/pm/issues/pm-context-7yrb.toon b/.agents/pm/issues/pm-context-7yrb.toon index 2bac716..a64ba41 100644 --- a/.agents/pm/issues/pm-context-7yrb.toon +++ b/.agents/pm/issues/pm-context-7yrb.toon @@ -6,7 +6,7 @@ status: blocked priority: 2 tags: [] created_at: "2026-08-09T13:46:47.076Z" -updated_at: "2026-09-29T16:11:17.200Z" +updated_at: "2026-10-02T22:22:41.749Z" author: claude 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 @@ -15,17 +15,61 @@ 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[2]{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." 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[2]{path,scope,note}: - .github/workflows/release.yml,project,release decision and resume path - test/release-workflow.test.ts,project,disposable Git history and notes recovery fixtures -tests[3]{command,path,scope,timeout_seconds,provenance{author,created_at,source_kind,source_ref}}: - node --test test/release-workflow.test.ts,test/release-workflow.test.ts,project,120,codex,"2026-09-29T15:53:11.190Z",local_mutation,fix/resume-untagged-release-2026-09-29 - "npm run release:check",package.json,project,600,codex,"2026-09-29T15:53:11.190Z",local_mutation,fix/resume-untagged-release-2026-09-29 - ./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,package.json,project,180,codex,"2026-09-29T16:02:04.727Z",local_mutation,fix/resume-untagged-release-2026-09-29 -docs[1]{path,scope,note}: - "https://github.com/unbraind/pm-context/pull/122",global,review candidate +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." From 6ba95327910d025d6eec390c0f28e049807a1c11 Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Sat, 3 Oct 2026 01:09:00 +0200 Subject: [PATCH 6/6] test(context): anchor resume guards to executable conditions --- .agents/pm/history/pm-context-7yrb.jsonl | 6 ++++++ .agents/pm/issues/pm-context-7yrb.toon | 6 ++++-- test/release-workflow.test.ts | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.agents/pm/history/pm-context-7yrb.jsonl b/.agents/pm/history/pm-context-7yrb.jsonl index 155dba5..a4c65b8 100644 --- a/.agents/pm/history/pm-context-7yrb.jsonl +++ b/.agents/pm/history/pm-context-7yrb.jsonl @@ -19,3 +19,9 @@ {"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 a64ba41..dbab1ff 100644 --- a/.agents/pm/issues/pm-context-7yrb.toon +++ b/.agents/pm/issues/pm-context-7yrb.toon @@ -6,7 +6,7 @@ status: blocked priority: 2 tags: [] created_at: "2026-08-09T13:46:47.076Z" -updated_at: "2026-10-02T22:22:41.749Z" +updated_at: "2026-10-02T23:08:58.658Z" author: claude 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 @@ -15,9 +15,11 @@ 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[2]{created_at,author,text}: +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." diff --git a/test/release-workflow.test.ts b/test/release-workflow.test.ts index 2482f6a..46786ed 100644 --- a/test/release-workflow.test.ts +++ b/test/release-workflow.test.ts @@ -113,7 +113,7 @@ test("resume notes are extracted from the prepared version and absent notes fail 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(stepSource(step), /steps\.decide\.outputs\.resume != 'true'/); + 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/);