Skip to content

Release: prepare and rehearse the immutable v1.5.0 candidate - #1033

Merged
jeffhuber merged 8 commits into
mainfrom
codex/1027-v150-release-prep
Sep 18, 2026
Merged

jeffhuber merged 8 commits into
mainfrom
codex/1027-v150-release-prep

Conversation

@jeffhuber

Copy link
Copy Markdown
Contributor

Closes #1027.

Prepare the 1.5.0 source/package identity, current installation guidance, release notes and qualification contract, including merged #1007 Graphify compatibility, #1031 reader/readiness parity and #1024 Slack setup/doctor. Historical v1.4.x release evidence remains unchanged.

The candidate workflow builds and inspects a wheel/sdist pair from the release preparation PR's actual merge SHA, records SHA-256/inventory evidence, and rehearses that exact wheel. Publication retrieves the qualified artifact pair instead of rebuilding. The documented order is candidate → private acceptance #918 → explicitly capped completion/cancellation #920 → owner-authorized unchanged-SHA tag/publication and independent reinstall #923.

Validation on the preparation head:

  • Clean exact-SHA rehearsal build and twine check; required Graphify/Slack modules, hosted manifest and documentation inspected in both artifacts and byte-compared to source.
  • Fresh Slack-free install, explicit local Slack setup, offline doctor refusing live readiness, 1.4.2 upgrade, digest-verified disposable 1.4.2 rollback and uninstall all passed with synthetic state preserved.
  • Release identity/readiness (22 checks), candidate regressions, lint, privacy, package guards and workflow actionlint passed. Full suite and normal CI results will be recorded in a head-bound follow-up.

Pre-merge artifacts are explicitly kind=rehearsal and cannot be published. The final candidate cannot be bound until this PR merges. Private administration/live disable/uninstall, paid canaries, tagging, publication and hosted deployment were not performed. This PR requests independent Claude audit and the normal authoritative gate; it does not authorize those later owner-controlled actions.

@jeffhuber jeffhuber added needs-claude-audit builder:codex Code Mower generated label labels Sep 18, 2026
@gitar-bot

gitar-bot Bot commented Sep 18, 2026

Copy link
Copy Markdown

Note

Automatic reviews are paused because your team has used its included automatic processing for this billing period (headroom scales with your seat count). You can still comment "Gitar review" to run one anytime, and automatic reviews resume on their own by October 1. Add seats for more headroom.
Learn more

Code Review 👍 Approved with suggestions 0 closed / 2 findings

🟡 Medium risk

Release v1.5.0 candidate preparation with comprehensive validation, build rehearsal, and artifact verification. Two minor version-pinning issues: wheel filename hardcoded in release.yml:124 duplicates the NAMES constant in scripts/release_candidate.py, and the distribution-build-and-verify check in src/code_mower/release_readiness.py branches on a literal version != "1.5.0" comparison that will silently break for future releases—consider extracting these version strings to a single source of truth to prevent silent failures on version bumps.

💡 Quality: Wheel filename for 1.5.0 hardcoded again in release.yml verification

📄 .github/workflows/release.yml:124

release.yml:124 hardcodes code_mower-1.5.0-py3-none-any.whl to check the rehearsal evidence's artifact digest, duplicating the NAMES constant already defined in scripts/release_candidate.py. Every future version bump now requires updating this string in the workflow in addition to the script's VERSION/NAMES, and if it's missed the assertion silently falls back to a KeyError (since candidate['artifacts'] is a dict keyed by version-specific filenames) rather than a clear "version drift" error.

Derive the wheel key by name pattern instead of hardcoding the version string.
wheel_name = next(n for n in candidate['artifacts'] if n.endswith('-py3-none-any.whl'))
assert evidence['artifact_sha256'] == candidate['artifacts'][wheel_name]
💡 Quality: release-readiness distribution check hardcodes version != "1.5.0"

📄 src/code_mower/release_readiness.py:1766-1773

src/code_mower/release_readiness.py's distribution-build-and-verify check (around line 1766) branches on version != "1.5.0" to decide whether release.yml should still contain python -m build or the new candidate-retrieval commands. Once the source version moves past 1.5.0 (e.g. to 1.5.1 or 1.6.0), this check will incorrectly require the legacy python -m build path again unless someone remembers to update this literal, silently breaking release-readiness for the next release without any explicit version-based extensibility mechanism.

Gate on whether the candidate workflow file exists/is referenced, rather than an exact version string, so the candidate-based flow persists for later versions too.
candidate_workflow_used = (repo_path / ".github/workflows/release-candidate.yml").exists()
... and ("python -m build" in workflow if not candidate_workflow_used else (...))
🤖 Prompt for agents
Code Review: Release v1.5.0 candidate preparation with comprehensive validation, build rehearsal, and artifact verification. Two minor version-pinning issues: wheel filename hardcoded in `release.yml:124` duplicates the `NAMES` constant in `scripts/release_candidate.py`, and the `distribution-build-and-verify` check in `src/code_mower/release_readiness.py` branches on a literal `version != "1.5.0"` comparison that will silently break for future releases—consider extracting these version strings to a single source of truth to prevent silent failures on version bumps.

1. 💡 Quality: Wheel filename for 1.5.0 hardcoded again in release.yml verification
   Files: .github/workflows/release.yml:124

   release.yml:124 hardcodes `code_mower-1.5.0-py3-none-any.whl` to check the rehearsal evidence's artifact digest, duplicating the `NAMES` constant already defined in scripts/release_candidate.py. Every future version bump now requires updating this string in the workflow in addition to the script's `VERSION`/`NAMES`, and if it's missed the assertion silently falls back to a `KeyError` (since `candidate['artifacts']` is a dict keyed by version-specific filenames) rather than a clear "version drift" error.

   Fix (Derive the wheel key by name pattern instead of hardcoding the version string.):
   wheel_name = next(n for n in candidate['artifacts'] if n.endswith('-py3-none-any.whl'))
   assert evidence['artifact_sha256'] == candidate['artifacts'][wheel_name]

2. 💡 Quality: release-readiness distribution check hardcodes version != "1.5.0"
   Files: src/code_mower/release_readiness.py:1766-1773

   src/code_mower/release_readiness.py's `distribution-build-and-verify` check (around line 1766) branches on `version != "1.5.0"` to decide whether release.yml should still contain `python -m build` or the new candidate-retrieval commands. Once the source version moves past 1.5.0 (e.g. to 1.5.1 or 1.6.0), this check will incorrectly require the legacy `python -m build` path again unless someone remembers to update this literal, silently breaking release-readiness for the next release without any explicit version-based extensibility mechanism.

   Fix (Gate on whether the candidate workflow file exists/is referenced, rather than an exact version string, so the candidate-based flow persists for later versions too.):
   candidate_workflow_used = (repo_path / ".github/workflows/release-candidate.yml").exists()
   ... and ("python -m build" in workflow if not candidate_workflow_used else (...))

Review coverage

Rules No rules evaluated

Functional validation Not enabled · Set up

Options

Display: compact → Counting what did not apply, without listing it.

Comment with these commands to change the behavior for this request:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@github-actions

Copy link
Copy Markdown

Claude audit unavailable

Head SHA: 74d227edb32f0f06c9a7d8ef1c12c524ac1e759f
Verdict: UNKNOWN
No merge-authority verdict was published. The local artifact was quarantined, stale, or inconclusive. Check the local runner and requeue this audit.

@jeffhuber

Copy link
Copy Markdown
Contributor Author

Pre-merge release review found two blocking evidence/integrity gaps and the two bounded maintainability findings already identified by Gitar. Please address all four on this same Codex-owned branch, add focused regressions/static workflow assertions, push, and re-request the independent Claude audit on the final head.

  1. [P2] “Build once” is not enforced and the candidate workflow code is not exact-SHA bound. .github/workflows/release-candidate.yml accepts any SOURCE_SHA that is merely an ancestor of current main, and a GitHub rerun (GITHUB_RUN_ATTEMPT > 1) rebuilds the artifact pair even though the runbook says it must never be regenerated. Before checkout/build, require GITHUB_SHA == SOURCE_SHA and GITHUB_RUN_ATTEMPT == 1. In .github/workflows/release.yml, require the selected candidate run's head_sha == SOURCE_SHA and run_attempt == 1 in addition to the existing path/event/branch/status/repository checks. Add regressions that fail if either binding disappears. If you bind run id/attempt into candidate.json, keep pre-merge rehearsal support explicit and fail closed on mismatch.

  2. [P2] The exact installed candidate wheel does not exercise the Graphify compatibility behavior that motivated Fix Graphify inventory limits and frontend test discovery #1007/Graphify: validate query-reader compatibility before reporting search available #1031. Inventory and editable-source CI prove the files exist, but they do not prove that the retained wheel accepts a complete doc_ref generation or preserves readiness/query semantics. Extend scripts/rehearse_v150.py with synthetic public data executed through the installed wheel (no checkout/test imports): at minimum prove doc_ref is accepted/excluded as non-code and the actual reader reports available; an ambiguity-only result remains partial-but-usable with complete generation; and a same-version/wrong-distribution unknown type yields bounded reader_incompatible without type/path/content leakage. Record named checks in rehearsal.json and assert them before publication. This must remain offline and contain no private graph/adoption data.

  3. [P3] Remove the hard-coded wheel key code_mower-1.5.0-py3-none-any.whl from the publication workflow. Select and validate the single wheel key from the verified candidate manifest (or a shared version-derived source) so a later version bump fails with a clear identity error rather than a KeyError.

  4. [P3] Remove the one-release behavior switch version != "1.5.0" from release_readiness.py. Detect and validate the candidate workflow contract directly so v1.5.1 does not silently revert to requiring python -m build in the publication workflow.

The current head's general candidate/rehearsal separation, merged-PR SHA check in release_candidate.py, artifact digests/inventory, Slack-free default, offline-doctor posture, 1.4.2 rollback boundary, private-acceptance ordering, and canary authorization language look sound. Keep historical v1.4.x evidence unchanged.

@jeffhuber

Copy link
Copy Markdown
Contributor Author

Fix round pushed to the existing Codex-owned branch for PR #1033. Head: 29116f00cd183475c5fd20a16236360dbc63c4a4.

All four requested findings are addressed: candidate dispatch rejects a workflow/source SHA mismatch and any rerun before checkout; publication checks the selected run head and first attempt; the installed-wheel rehearsal exercises synthetic doc_ref/readiness, ambiguity-only usable partial results, and same-version/wrong-distribution bounded incompatibility without leakage; publication now requires those named checks and a single manifest-derived wheel identity. Readiness validates the candidate workflow directly, including for future versions.

Added behavioral workflow regressions, missing-evidence rejection tests, and a normal CI job that builds the exact PR head as kind=rehearsal, runs the installed wheel, and gates the package check. This job cannot produce a publishable candidate.

Validation so far: 20 focused release regressions pass; Ruff on source/scripts/tests, privacy scan, package workflow guard, actionlint on changed workflows, and all 22 release-readiness checks pass. A clean build at the preceding implementation commit passed twine and both artifact inventories. Final-head CI and the installed-wheel rehearsal are running: https://github.com/codemower-ai/code-mower/actions/runs/35315135955 .

Local full-suite/graph validation is constrained by the runner sandbox: the command python - <<'PY' containing tempfile.mkdtemp(prefix='code-mower-1033-', dir='/tmp') was denied with Operation not permitted. The product requires graph state outside Git repositories. No local retry or product-boundary bypass was attempted; the normal CI runner supplies an external temporary directory for the same rehearsal. Final CI results will follow. Re-requesting independent Claude audit on this head; authoritative gate still required. No tag, publication, paid canary, hosted deployment, or live Slack action was performed.

@github-actions

Copy link
Copy Markdown

Claude audit unavailable

Head SHA: 29116f00cd183475c5fd20a16236360dbc63c4a4
Verdict: UNKNOWN
No merge-authority verdict was published. The local artifact was quarantined, stale, or inconclusive. Check the local runner and requeue this audit.

@jeffhuber

Copy link
Copy Markdown
Contributor Author

Updated PR #1033 to final fix head f1362a17efa65e6c47b35d473c62505629b33cff and re-requested Claude audit.

The exact installed-wheel rehearsal passed all ten named checks at 35077495942a267ea3b831dbbdfdb693f10250ee in CI run 35315313345, including all three requested Graphify behaviors, Slack-free startup, offline doctor, upgrade, digest-verified rollback, and uninstall/state preservation. That run caught and resolved installer-home contamination in the new Linux rehearsal. The final commit only updates the workflow inventory regression: the prior preparation head's CI was failing because its frozen inventory did not account for #1027's release workflows; the historical fixture remains unchanged. The affected inventory test and all 20 focused release tests pass locally.

Final-head CI run 35315495642 will repeat the wheel rehearsal and full Python 3.12/3.13/3.14 suites. Final head-bound evidence will follow. Independent audit and the authoritative gate remain required; this is pre-merge rehearsal evidence, not a merged candidate or release authorization.

@jeffhuber

Copy link
Copy Markdown
Contributor Author

One final bounded fix round is required on the existing branch at f1362a17efa65e6c47b35d473c62505629b33cff.

  1. [P3] Complete the future-release generalization in render_release_readiness. The exact-head Claude audit passed with zero P0-P2 but found that two remaining branches still use if version == "1.5.0": candidate runbook check selection and the immutable-candidate-first next action. Replace those exact-version branches with the same candidate-workflow/candidate-mode contract detection already used for the distribution flow. Add focused regression coverage simulating v1.5.1 that proves candidate runbook checks remain active, legacy v1.4 checks are not selected, and the candidate-first action remains. Preserve the historical v1.4 path when the candidate workflow is absent.

  2. Fix the final CI workflow assertion. Runs 35315495642 on Python 3.13 and 3.14 fail only because test_ci_workflow_tests_supported_python_minors still expects the aggregate package job to need ["package_matrix", "board_qualification"]; the workflow now correctly also requires release_rehearsal. Update the assertion to include release_rehearsal and run the focused release-hygiene/release-readiness tests before pushing.

Keep the scope to these two items. Re-run the exact installed-wheel rehearsal and request a fresh exact-head independent audit after pushing. Do not tag, publish, dispatch paid providers, touch a live Slack workspace, or create a recovery session.

@jeffhuber

Copy link
Copy Markdown
Contributor Author

Addressed the two remaining fix-round items on PR #1033. New head: 9ab4d9c38ca330484dabd441aee81d36f23e5d41.

Both remaining release-readiness branches now use the same candidate-workflow detection as the distribution check. Regressions simulate v1.5.1 and verify candidate runbook checks, rejection of missing acceptance/budget gates, candidate-first next actions, and candidate run IDs on publication dispatches. The historical v1.4.2 tests now use a checkout fixture without the candidate workflow/consumer and still verify the legacy runbook. Historical evidence files are unchanged. The aggregate CI dependency assertion now includes release_rehearsal.

Validation on the pushed tree:

  • 21 tests in tests/test_release_v150.py: pass.
  • 363 tests in tests/test_release_hygiene.py: pass.
  • All 22 release-readiness checks: pass.
  • Ruff on the three changed files, privacy scan, package workflow guard, and git diff --check: pass.

Re-requested needs-claude-audit for this exact head. Normal CI will rebuild the explicitly nonpublishable pre-merge rehearsal pair and repeat the installed-wheel rehearsal on this SHA; full matrix CI, fresh independent audit, and the authoritative gate remain pending. No release candidate dispatch, tag, publication, paid canary, live Slack action, or recovery session was performed. Final head-bound results will follow.

@jeffhuber

Copy link
Copy Markdown
Contributor Author

Exact-head release review evidence and owner gate decision

Exact head: 9ab4d9c38ca330484dabd441aee81d36f23e5d41

  • Independent Claude merge-authority audit completed PASS with P0=0, P1=0, P2=0, P3=0. Source run: https://github.com/codemower-ai/code-mower/actions/runs/35316519812. The sealed local verdict artifact binds both start and end SHA to this exact head and records first attempt 1.
  • Full CI passed on this head: https://github.com/codemower-ai/code-mower/actions/runs/35316512299. This includes Python 3.12/3.13/3.14, the aggregate package gate, Linux/macOS graph containment, Board qualification, and the exact installed-wheel release rehearsal.
  • The installed-wheel rehearsal passed all ten named checks: Slack-free default, explicit private Slack setup, bounded offline readiness, manifest removal, the three Graphify compatibility cases, upgrade from 1.4.2, digest-verified rollback to 1.4.2, and uninstall/state preservation.
  • The final fix preserves the historical v1.4 path and proves that v1.5.1 retains candidate-first runbook, acceptance, budget, and publication bindings. Focused validation passed 21 v1.5 tests, 363 release-hygiene tests, and all 22 release-readiness checks.

The hosted audit publisher again remained in its wait step after producing and sealing the exact-head PASS artifact, consistent with tracked publisher issue #1032. I cancelled the stalled source workflow after preserving the artifact to free the self-hosted runner. Applying the documented owner gate override to this PR only; this does not replace or weaken the exact-head independent audit, CI, or installed-wheel evidence. No tag, release candidate, package publication, paid provider run, or live Slack action has occurred.

@jeffhuber jeffhuber added the gate:override Code Mower generated label label Sep 18, 2026
@jeffhuber
jeffhuber merged commit d66e01d into main Sep 18, 2026
20 checks passed
@jeffhuber
jeffhuber deleted the codex/1027-v150-release-prep branch September 18, 2026 06:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

builder:codex Code Mower generated label gate:override Code Mower generated label needs-claude-audit

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Release: build and rehearse the immutable v1.5.0 candidate

1 participant