Skip to content

feat(release): canonical immutable GitHub Release mechanism (ADR 0129) - #1030

Draft
seonghobae wants to merge 46 commits into
mainfrom
feat/canonical-immutable-release-20260902
Draft

feat(release): canonical immutable GitHub Release mechanism (ADR 0129)#1030
seonghobae wants to merge 46 commits into
mainfrom
feat/canonical-immutable-release-20260902

Conversation

@seonghobae

@seonghobae seonghobae commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Current verified release repair — 2026-09-08

  • Integrated head: b0e3d62390894af105a3577eb7daf97221330e28; merged main a080297d2546bb61e89520d637cabc202db331ec normally, preserving predecessor 27293e637103da78b3c53ff7dc30c9b5763e67ce.
  • Fresh local checks on integrated head: release contracts 89 passed, provider reliability 32 passed, git diff --check passed. These are scoped local checks, not a fresh dependency install, full hosted acceptance, or publication.
  • Hosted run 34179014058 SBOM/security job succeeded; tests/fuzz and required reviews were not yet terminal-valid at this observation. Earlier runs below are historical and do not transfer to this head.
  • Live registry preflight on 2026-09-08: PyPI fast-mlsirm JSON endpoint returned HTTP 404. Existing owner #1692 remains open and #1471 remains Draft. CO still declares the v0.9.1 direct archive URL.
  • Organization secret metadata was rechecked successfully: the three publisher secret names below remain visible to all repositories. No permission expansion or secret value inspection was needed.
  • Draft. No protected merge, registry publication, or deployment is claimed.
  • 34cf7dec replaces six obsolete required check names with the three actual jobs in the integrated security.yml. Its regression compares the release inventory against that workflow, rather than repeating stale constants. RED: one failure; GREEN: 64 release tests.
  • 92ca4cf3 downloads the published SBOM and compares bytes against the verified artifact. Same-name/different-content and download-failure cases first reproduced two false successes. Existing assets are never overwritten; --clobber was removed.
  • 84437193 adds successful-download-but-missing/empty-file cases and keeps test temporary files inside pytest cleanup ownership. Combined local result: 71 passed in 12.11s, session 34492 terminal exit 0. This is mocked GitHub CLI execution of the real attachment step, not live publication.
  • Exact-head hosted run 34083174853 completed successfully: Tests and package quality; Property and coverage-guided fuzzing; CodeQL, supply chain, and SBOM. Other required review/security workflows remain queued; this is not all-check acceptance or permission to merge. The latest commit updates gap evidence only. Predecessor 84437193 run 34081995823 also completed successfully and is historical evidence, not a live wait.
  • Remaining boundaries: concurrent asset changes and the interval where a public Release lacks its mandatory SBOM are not solved by byte comparison. Exact-head checks, independent required reviews, and protected integration still apply.
  • Registry prerequisite: this lane creates GitHub Releases, not PyPI distributions. The direct-URL fast-mlsirm dependency needs the canonical owner's registry release before a PyPI-compatible dependency can be adopted. Existing owner work is fast-mlsirm #1692 and #1471; no duplicate publisher or dependency removal.
  • User has authorized eligible automatic publication. Organization secret metadata confirms PIPY_TOKEN, PIPY_USERNAME, and CARGO_REGISTRY_TOKEN with all-repository visibility. Values were not inspected; metadata alone does not prove token validity.

The older snapshots below preserve lineage and must not override the current head or be interpreted as current prerequisite status.

목적

contextual-orchestrator가 소비자에게 제공할 canonical immutable GitHub Release 경로를 소유합니다. vX.Y.Z annotated tag, GitHub Release, exact-commit CycloneDX SBOM을 하나의 재개 가능한 publication 계약으로 묶습니다. 상업 release-candidate 평가는 별도 bounded context입니다.

Historical authority snapshot (superseded below)

  • protected base: main@2e414d15ba58f28597751b625a8a2f00fc9fadcf
  • exact release-lane head: f753f453ce4fc3dbc612bb9bdbb8db4cbfd93c16
  • exact release-lane tree: 2b84a752b1b5924743664181d7dcb95dab34104c
  • lifecycle: Draft / prerequisite integration and hosted exact-head evidence incomplete
  • current canonical GitHub Releases: 0

Publication invariants

  • fresh publication starts only from the current protected main tip; version input, [project].version, and CHANGELOG release section must agree;
  • tag lookup is confined to refs/tags/vX.Y.Z; annotated tags peel to the exact commit and are never moved or reused;
  • interrupted publication can resume only when an existing tag is the expected version and an ancestor of the current protected main;
  • validation is read-only; publish rechecks main tip and check rollup before creating tag/Release;
  • a non-empty CycloneDX SBOM from the exact protected commit is mandatory release evidence, not best-effort output.

Current prerequisite order — repaired 2026-09-05

Fresh repository execution proved the prior #1066 → #1068 order was wrong for the time-bounded NIM evidence contract. #1066 run 33923471573, job 101186730296, materialized successfully on a GitHub-hosted runner but the full suite failed 5 failed, 3390 passed, 2 skipped because protected main's NIM hosted-access evidence expired after 2026-09-04 before child #1068 could supply the refresh.

The corrected foundation order is:

  1. fix(nim): refresh hosted access evidence before security stack #1069 b0bd71dfa5d6ca065052bc951ad9c9209b88413b: direct child of protected main, exactly six NIM evidence/test/doc files. NVIDIA's official Run NIM Anywhere terms were re-reviewed 2026-09-05; the bounded prototype-access evidence remains fail-closed after 2026-10-04. This does not assert production pricing, licensing, support, or deployed capacity.
  2. ci(security): materialize stacked-PR validation on trusted branch #1066 51c8824e219c887425d2e7147e87f088d5f1a77d: two-file stacked-PR security validation repair. It is Draft after the exact-head test failure and must non-force restack only after fix(nim): refresh hosted access evidence before security stack #1069 reaches protected main.
  3. fix(nim): preserve current hosted-access evidence on trusted branch #1068 0cdc0307f0fd9f368f89eba3036c5538edbefb72: existing trusted successor for fork fix(nim): refresh hosted access evidence #1063. After fix(nim): refresh hosted access evidence before security stack #1069 and the restacked ci(security): materialize stacked-PR validation on trusted branch #1066 are integrated, reconcile it again; close only if fix(nim): refresh hosted access evidence before security stack #1069/protected main fully inherits every valid semantic/test/doc delta and the effective child delta is verified empty.
  4. fix(packaging): require fast-mlsirm on every supported runtime #995 e2df7803b5cb77b65ffd3f2a2ce57b2a2cda5a0a: Python 3.12+ / fast-mlsirm runtime prerequisite remains independent and must satisfy then-live protected governance.

Fork #1063 remains lineage until trusted protected-main equivalence is verified. Predecessor checks and local test counts do not transfer across the reordered stack.

Actual consumer RED

ContextualWisdomLab/ThreadWeave#43@fe8a9acb2df10ee3a73ac361461c63ee4ef3d01a has converged on orchestrator/free but still clones CO source, installs requirements.lock, bootstraps the gateway, and owns provider credential inventory because no canonical immutable CO Release + central reusable consumer contract exists. The consumer is Draft. Dedicated .github#1661 already has the owner-path RED/GREEN requiring an immutable reusable workflow_call that exposes only gateway token + orchestrator/free, with leaf provider secrets/source checkout reduced to zero.

Release boundary

Only after all prerequisites reach protected main normally do we non-force reconcile this release lane to the then-current protected tip. On one unchanged exact successor head, all required tests/security/CodeQL/SAST/review plus SBOM/provenance must be terminal-valid before Ready/merge/tag/publish. Actual canonical Release asset, reproducibility/rollback evidence, and at least one thin consumer canary are required before ADR 0129 can move beyond Proposed.

No force push, destructive rebase, self-approval, administrator bypass, tag movement, SBOM weakening, no-op retrigger, or predecessor-evidence transfer is acceptance evidence.

seonghobae and others added 12 commits September 1, 2026 18:15
…st yet

scripts/ci/release_notes.py is not implemented yet, so
read_declared_version/extract_changelog_section/render_release_notes/main
all fail to import (FileNotFoundError via the importlib-by-path loader this
repository's scripts/ci contract tests use). Confirmed failing:
`python -m pytest tests/test_release_notes.py -q` -> 10 failed.

Part of the canonical immutable release mechanism
(docs/planning/adrs/0127-canonical-immutable-release.md).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4
…project.toml

Implements scripts/ci/release_notes.py per the RED test in
ecac722: read_declared_version() reads pyproject.toml's [project]
version field only (never guesses from a tag or CHANGELOG heading);
extract_changelog_section() returns the body of the matching
'## [X.Y.Z]' section, regardless of an '- Unreleased' or dated suffix, and
fails closed on a missing or empty section; render_release_notes() adds
commit/repository provenance; main() wires both together for
.github/workflows/release.yml and fails closed (exit 2) when the requested
--version does not match pyproject.toml.

Confirmed: `python -m pytest tests/test_release_notes.py -q` -> 10 passed.
`python -m interrogate -f 100 scripts/ci/release_notes.py` -> PASSED
(100.0%).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4
.github/workflows/release.yml is absent from this branch's parent (and from
origin/main) at this point, so every structural assertion in
tests/test_release_workflow_contract.py fails on FileNotFoundError.
Confirmed failing both by direct pytest collection and by checking
`git show origin/main:.github/workflows/release.yml` (no such file).

Part of the canonical immutable release mechanism
(docs/planning/adrs/0127-canonical-immutable-release.md).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4
Implements .github/workflows/release.yml per the RED test in
c9b0154 and the design in
docs/planning/adrs/0127-canonical-immutable-release.md:

- workflow_dispatch only, with a required version input and no default —
  never push/schedule/pull_request-triggered, so a release is always a
  deliberate maintainer action;
- runs only against github.ref == 'refs/heads/main';
- fails closed unless the dispatched commit equals main's live tip
  (gh api repos/$REPO/commits/main), guarding against a race with a
  concurrent merge;
- fails closed unless pyproject.toml's declared version matches the
  dispatch input;
- fails closed if tag v${VERSION} already exists locally or on origin —
  a release is never republished or moved onto a different commit;
- reruns the full test suite fresh on the exact commit (the same
  `uv run --locked ... pytest -q` invocation as Makefile's `make test`
  and ci.yml's Tests job) rather than trusting a prior run's status;
- renders release notes via the tested scripts/ci/release_notes.py;
- creates and pushes an annotated tag, then publishes a GitHub Release,
  best-effort attaching the CycloneDX SBOM from the matching successful
  Security workflow run for this commit (a missing SBOM warns, it never
  blocks the release);
- contents: write is scoped to the release job only; every other default
  stays contents: read.

Also lists the two new release-mechanism test files in README.md's Check
section, next to the existing release_authorization/release_authority
tests they sit beside.

Confirmed: `python -m pytest tests/test_release_workflow_contract.py -q`
-> 15 passed. `python tests/test_release_workflow_contract.py` and
`python tests/test_release_notes.py` (direct-execution wrapper) both pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4
…n evidence

Records the design decided before implementation: a new, distinct concern
from contextual_orchestrator/release_authorization.py (which is a
PR-scoped, buyer-facing product-evidence report never wired to any GitHub
Actions workflow), sharing its fail-closed spirit without duplicating
GitHub governance already enforced by branch protection at merge time.
Cites the direct consumer evidence (keyverse#132, bandscope#881, the
Wardnet handoff, EgressWeave#235) from contextual-orchestrator#971, and
scopes out PyPI publishing, release-on-every-merge, automatic version
bumps, and dynamic ruleset-derived required-check names as non-goals for
this first mechanism.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4
Documents preconditions, the dispatch steps, what happens after a release,
and rollback policy (a mistake gets a new patch release, never a routine
moved/deleted tag) for a human cutting a real contextual-orchestrator
release. Explicitly distinguishes this from
docs/commercial_release_candidate.md, which answers a different question
(buyer/procurement readiness) and is untouched by this change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4
- product-technical-gap-baseline.md: dated 2026-09-02 entry with the
  Phase 1 research finding (release_authorization.py is a distinct,
  PR-scoped buyer-evidence concern, not a release publisher), the direct
  consumer evidence from contextual-orchestrator#971
  (keyverse#132/bandscope#881/Wardnet/EgressWeave#235), what was built,
  and verification evidence. Explicitly notes no real release was
  triggered and the resumable-long-running-execution half of the gap is
  unaddressed here.
- CHANGELOG.md: new [0.2.0] Added entry for the release mechanism itself.
- conductor/tracks.md: new active track
  005-canonical-immutable-release.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4
@coderabbitai

coderabbitai Bot commented Sep 2, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

Next included review available in 4 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 2a47ec7e-d74f-46ff-9952-634f6e28c481

📥 Commits

Reviewing files that changed from the base of the PR and between b7a044d and c525ae4.

⛔ Files ignored due to path filters (2)
  • requirements.lock is excluded by !**/*.lock
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (19)
  • .github/workflows/release.yml
  • CHANGELOG.md
  • conductor/tech-stack.md
  • contextual_orchestrator/nim_benchmark.py
  • docs/RELEASING.md
  • docs/doctoring/nim-benchmark-evidence-grade.md
  • docs/nim_benchmark.md
  • docs/planning/adrs/0129-canonical-immutable-release.md
  • docs/product-technical-gap-baseline.md
  • pyproject.toml
  • scripts/ci/release_checks_gate.sh
  • scripts/ci/release_notes.py
  • tests/test_fast_mlsirm_runtime_contract.py
  • tests/test_nim_benchmark_release_acceptance.py
  • tests/test_release_immutable_publication.py
  • tests/test_release_notes.py
  • tests/test_release_supply_chain_contract.py
  • tests/test_release_workflow_contract.py
  • tests/test_release_workflow_idempotency_contract.py
📝 Walkthrough

Walkthrough

보호된 main의 최신 커밋과 check-run을 검증하는 verify 작업과 태그·Release를 생성하는 publish 작업으로 릴리스 워크플로우를 분리했습니다. 릴리스 노트 생성기, 멱등성·권한 계약 테스트, 릴리스 운영 문서를 추가했습니다.

Changes

정식 불변 릴리스

Layer / File(s) Summary
릴리스 진입 및 fail-closed 게이트
.github/workflows/release.yml, tests/test_release_workflow_contract.py, tests/test_release_workflow_idempotency_contract.py
verify가 main tip, 예상 push check-run, 프로젝트 버전, 태그와 Release 상태를 검증합니다. 확인된 404만 부재로 처리하고, 다른 오류와 불일치 상태는 실패 처리합니다.
CHANGELOG 기반 릴리스 노트 생성
scripts/ci/release_notes.py, tests/test_release_notes.py, .github/workflows/release.yml
[project] 범위의 버전과 버전별 CHANGELOG 섹션을 검증하고 Release 본문을 생성합니다. CLI는 파일 또는 stdout으로 결과를 출력합니다.
테스트 후 태그 및 Release 게시
.github/workflows/release.yml, tests/test_release_workflow_contract.py, tests/test_release_workflow_idempotency_contract.py
publish가 게시 직전에 main과 check-run을 재검증합니다. 필요한 경우 annotated tag와 GitHub Release를 생성하고 SBOM 자산을 best-effort로 첨부합니다.
릴리스 운영 계약과 기록
docs/RELEASING.md, docs/planning/adrs/0129-canonical-immutable-release.md, CHANGELOG.md, docs/product-technical-gap-baseline.md, README.md, conductor/tracks.md
불변 태그 URL, 재개·롤백 정책, 권한 분리, 알려진 한계와 릴리스 절차를 문서화하고 관련 테스트 실행 항목과 트랙 기록을 갱신했습니다.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant 운영자
  participant verify
  participant publish
  participant GitHub
  운영자->>verify: version으로 workflow_dispatch 실행
  verify->>GitHub: main tip과 push check-run 검증
  GitHub-->>verify: 검증 결과
  verify->>publish: 릴리스 노트·SBOM·재개 상태 전달
  publish->>GitHub: main과 check-run 재검증
  publish->>GitHub: tag와 Release 생성 또는 재개
Loading

Merge Risk: 🟡 Moderate · up to b7a04

The release workflow may mishandle valid project-version declarations and either reject a release or validate the wrong version; retries can also accept public release metadata that does not match the intended release. These are bounded but concrete correctness and release-integrity risks, so merge should wait for remediation or explicit owner acceptance.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 97.62% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 84 functions across 4 files. (6 skipped: 6 …
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 ADR 0129에 따른 canonical immutable GitHub Release mechanism 도입이라는 주요 변경을 정확하고 구체적으로 요약합니다.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/canonical-immutable-release-20260902

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

devin-ai-integration[bot]

This comment was marked as resolved.

PR #1020 (#1020,
`docs/planning/adrs/0127-evidence-based-per-model-timeout-allocator.md`)
independently claimed ADR number 0127 before this PR was created, and
PR #1012 (#1012) already holds 0128 from an earlier same-day collision
fix. Per the ADR-uniqueness convention in this repo's CLAUDE.md
("a same-number collision is a rename, not a redesign") and the
tie-breaker already applied once today (the more-recently-created/
updated PR bumps further), this PR's ADR renumbers to 0129 -- verified
free against origin/main and all 38 currently open PRs by walking each
PR's head tree for new docs/planning/adrs/*.md files not present on
main.

- Rename docs/planning/adrs/0127-canonical-immutable-release.md to
  0129-canonical-immutable-release.md; update its front-matter id.
- Update every in-repo reference to the old number:
  .github/workflows/release.yml, tests/test_release_workflow_contract.py,
  conductor/tracks.md, CHANGELOG.md, docs/product-technical-gap-baseline.md,
  docs/RELEASING.md.

Verified: tests/test_release_notes.py, tests/test_release_workflow_contract.py,
tests/test_planning_adr_identifiers.py, tests/test_product_planning_contract.py
(30 passed); python -m interrogate -c pyproject.toml . (100.0%); a scratch-
directory side-by-side check against PR #1020's 0127 file confirmed no
residual collision at the new number.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4

Copy link
Copy Markdown
Contributor Author

ADR number collision resolved: 0127 -> 0129

This PR's docs/planning/adrs/0127-canonical-immutable-release.md collided with #1020 (docs/planning/adrs/0127-evidence-based-per-model-timeout-allocator.md), which independently claimed ADR number 0127 before this PR existed. Since this PR (#1030) was created after #1020 already held 0127, the same "most-recently-updated bumps further" tie-breaker used earlier today for the #1012/#1020 double-collision on 0127 (resolved by moving #1012 to 0128) applies here: this PR renumbers, not #1020.

New number: 0129 (not 0128 — #1012 already holds 0128 as an open PR, not yet on main).

Verification performed before picking 0129:

Commit 615cb556 on this branch (non-force push, new commit, no rebase):

  • Renames docs/planning/adrs/0127-canonical-immutable-release.md -> 0129-canonical-immutable-release.md and updates its front-matter id.
  • Updates every in-repo reference to the old number: .github/workflows/release.yml, tests/test_release_workflow_contract.py, conductor/tracks.md, CHANGELOG.md, docs/product-technical-gap-baseline.md, docs/RELEASING.md.

Re-verified locally: tests/test_release_notes.py, tests/test_release_workflow_contract.py, tests/test_planning_adr_identifiers.py, tests/test_product_planning_contract.py (30 passed); python -m interrogate -c pyproject.toml . (100.0%).

Note for whoever reviews #1020 next: this repo also has an unrelated second collision in flight today — #1008 and #972 both independently added a new ADR at 0042 (different topics). Out of scope for this PR/comment, flagging only so it doesn't get missed.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4


Generated by Claude Code

@seonghobae seonghobae changed the title feat(release): canonical immutable GitHub Release mechanism (ADR 0127) feat(release): canonical immutable GitHub Release mechanism (ADR 0129) Sep 2, 2026
coderabbitai[bot]

This comment was marked as resolved.

seonghobae pushed a commit that referenced this pull request Sep 2, 2026
PR #972 (feat(discovery): record parallel tool-call capability and
exclude single-tool models from orchestrator/free) independently added
docs/planning/adrs/0042-parallel-tool-call-capability.md, colliding
with this PR's 0042-opencode-go-provider-discovery.md -- neither
number exists on main yet. Per this repo's CLAUDE.md, "a same-number
collision is a rename, not a redesign," and PR #972 was updated
earlier than this PR, so this PR's ADR renumbers instead.

0130 is the next free number after checking docs/planning/adrs/ on
current origin/main (highest: 0126) and every other open PR's added
ADR files (0127 PR #1020, 0128 PR #1012, 0129 PR #1030, plus PR #972's
untouched 0042).

Renamed docs/planning/adrs/0042-opencode-go-provider-discovery.md to
0130-opencode-go-provider-discovery.md, updated its front-matter id,
and updated the three in-repo prose references to "ADR 0042" for this
ADR (contextual_orchestrator/model_discovery.py comment,
tests/test_model_discovery.py docstring, docs/kv-credentials.md,
CHANGELOG.d/opencode-go-provider-discovery.md). Left the coincidental
"0042" substring in docs/planning/adrs/0004-pr-review-merge-loop.md
(part of an unrelated git SHA) untouched.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4
…check

Addresses Devin findings 1-3 on PR #1030 (all sharing one root cause: the
tag is pushed before the fallible SBOM/GitHub-Release steps), plus the
overlapping CodeRabbit findings and its CWE-269 least-privilege suggestion:

- Finding 1 (concurrent merges produce stale releases): add a second,
  authoritative main-tip check in the `publish` job, immediately before tag
  creation and after every long-running gate (fresh test suite, note
  rendering) -- the original early check stays as a fast-fail.
- Finding 2 (SBOM lookup strands every release): grant `actions: read` at
  the `verify` job's scope, and make every SBOM discovery call genuinely
  non-fatal via explicit `if !` guards instead of a bare `set -e` that
  aborted the whole job on the permission gap.
- Finding 3 (publication failures block automated recovery): resolve an
  existing `vX.Y.Z` tag through the GitHub commits API into resume (points
  at this exact commit, no Release published yet -- skip re-tagging) vs.
  reject (points elsewhere, or already has a published Release), replacing
  the old any-existing-tag hard fail.
- CodeRabbit's CWE-269 suggestion: split the job into a read-only,
  credential-less `verify` job (runs all repository-controlled code -- the
  test suite and note rendering -- with no write token or persisted git
  credential present) and a write-scoped `publish` job (tag + Release only,
  `needs: verify`). This also gives finding 1's final tip check its natural
  placement: the first thing `publish` does. Notes/SBOM cross the job
  boundary via upload-artifact/download-artifact.

Finding 4 (tests miss operational invariants): `tests/
test_release_workflow_contract.py` gained a `_job_block` helper to bound
assertions to one job's own steps instead of loose substring presence, plus
new tests for job-scoped permissions and the final-check ordering. The
resume/reject branching and least-privilege split get their own file,
`tests/test_release_workflow_idempotency_contract.py` (12 tests), since they
share the one root cause above. Verified the new/changed assertions actually
fail against the pre-fix workflow (not vacuous) by running both files
against a stash of the original file.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4
Addresses two CodeRabbit findings on PR #1030:

- Real bug: `read_declared_version`'s regex matched the first
  `version = "..."` line anywhere in pyproject.toml, so a same-named
  `version` key under an unrelated table declared earlier (e.g.
  `[tool.some_tool]`) would be read instead of `[project]`'s real version --
  both by this script's own CLI and by release.yml's version-match gate
  (which now calls this same tested function instead of duplicating the
  regex, so both are fixed by one change). Bounds the search to the span
  between the `[project]` header and the next `[...]` table header. Added
  regression tests for a same-named key both before and after `[project]`'s
  body, plus a missing-`[project]`-table case.
- Minor test bug: `pytest.raises(ValueError, match="0.9.9")` left the dots
  as unescaped regex metacharacters (RUF043); escaped to `match=r"0\.9\.9"`.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4
Addresses Devin finding 5 and CodeRabbit's documentation-accuracy finding
on PR #1030:

- `/releases/latest` is a mutable alias that repoints to the newest release,
  not itself an immutable pin -- only `/releases/tag/vX.Y.Z` is. Both
  `docs/RELEASING.md` and ADR 0129's Consequences section previously
  described them as equally immutable pin targets, which undermines this
  PR's whole stated purpose if left wrong. Fixed in both places, and
  `docs/RELEASING.md`'s "Cutting a release" walkthrough and preconditions
  are updated to describe the two-job, idempotent-resume behavior from the
  companion workflow fix.
- ADR 0129 gained a "Research grounding" section: this is release-
  engineering/DevOps process tooling, not a novel algorithm, so it cites the
  normative standards it implements -- SemVer 2.0.0, Keep a Changelog 1.1.0,
  and the GitHub Releases API -- instead of an academic literature review,
  per this org's research-grounding convention.

CHANGELOG.md's `## [0.2.0] - Unreleased` entry and
docs/product-technical-gap-baseline.md's narrative are updated to describe
the mechanism's actual, fixed behavior (still unreleased, so this amends
the existing entries rather than adding a separate "Fixed" bullet).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4

Copy link
Copy Markdown
Contributor Author

Devin + CodeRabbit findings addressed

Fixed all 5 Devin Review findings plus 4 additional CodeRabbit findings (3 of CodeRabbit's overlapped Devin's; those needed no extra work beyond what's below). Three commits, verified against current code (not blindly applying suggested prompts):

eea42bd6fix(release): idempotent publish + least-privilege split + final tip check

  • Devin finding 1 / CodeRabbit (concurrent-merge staleness): added a second, authoritative main-tip check in the publish job, immediately before tag creation and after every long-running gate (fresh test suite, note rendering). Original early check kept for fast failure.
  • Devin finding 2 / CodeRabbit (SBOM lookup strands every release): actions: read granted at the verify job's scope; gh run list/gh run download failures are now each guarded by an explicit if ! instead of a bare set -e that aborted the whole job on the permission gap.
  • Devin finding 3 / CodeRabbit (publication failures block automated recovery): an existing vX.Y.Z tag is resolved via the GitHub commits API into resume (same commit, no Release published yet — skip re-tagging) vs. reject (different commit, or an already-published Release), replacing the old any-existing-tag hard fail that stranded a half-published release.
  • CodeRabbit CWE-269 (least privilege, "heavy lift" — did it, fit naturally with finding 1's restructuring): split into a read-only, credential-less verify job (runs all repository-controlled code — tests, note rendering — with no write token or persisted git credential present) and a write-scoped publish job (needs: verify; tag + Release only). This also gives the final tip check its natural placement as literally the first thing publish does. Notes/SBOM cross the job boundary via upload-artifact/download-artifact.
  • Devin finding 4 (tests miss operational invariants): tests/test_release_workflow_contract.py gained a _job_block helper bounding assertions to one job's own steps instead of loose substring presence, plus new tests for job-scoped permissions and the final-check ordering. The resume/reject branching and least-privilege split got their own file, tests/test_release_workflow_idempotency_contract.py (12 tests). Confirmed these aren't vacuous by running both files against a stash of the pre-fix workflow — all 12 new idempotency tests fail there as expected.

00fe2949fix(release-notes): bound version parsing to [project] table

  • CodeRabbit (TOML table-boundary bug): read_declared_version no longer matches the first version = "..." line anywhere in pyproject.toml — it's now bounded to the [project] table's own body, so a same-named version key under an earlier unrelated table (e.g. [tool.some_tool]) can't be mistaken for it. The workflow's version-match step now calls this same tested function instead of duplicating the regex, so one fix covers both. Added regression tests for a same-named key both before and after [project]'s body, plus a missing-[project]-table case.
  • CodeRabbit (regex escape, RUF043): pytest.raises(ValueError, match="0.9.9")match=r"0\.9\.9".

a869042bdocs(release): fix /releases/latest mutability claim + add ADR grounding

  • Devin finding 5 (research grounding): ADR 0129 gained a "Research grounding" section citing SemVer 2.0.0, Keep a Changelog 1.1.0, and the GitHub Releases API — the normative standards this process tooling implements, since this is release-engineering tooling, not a novel algorithm.
  • CodeRabbit (/releases/latest mutability): fixed in both docs/RELEASING.md and ADR 0129's Consequences section — only /releases/tag/vX.Y.Z is the immutable pin; /releases/latest is a mutable discovery alias. docs/RELEASING.md's walkthrough also updated to describe the two-job/idempotent-resume behavior.

Verification

  • python -m pytest tests/test_release_notes.py tests/test_release_workflow_contract.py tests/test_release_workflow_idempotency_contract.py tests/test_planning_adr_identifiers.py tests/test_product_planning_contract.py -q47 passed
  • python -m pytest tests -q (full suite) → 3390 passed, 2 skipped, 3 failed — the 3 failures (test_admin_contract.py, test_psychometric_routing.py, test_spend_analytics.py) are pre-existing and unrelated (confirmed by re-running them against a stash of the pre-fix branch — same 3 failures there too; missing optional fast_mlsirm module, a JS admin-console harness issue, and a tokenizer-availability difference).
  • python -m interrogate -c pyproject.toml .100.0%
  • .github/workflows/release.yml YAML-validated (yaml.safe_load) and its two-job step order manually confirmed to match the intended design.
  • ruff check --select RUF043 on the changed files → clean.

Pushed non-force to feat/canonical-immutable-release-20260902.


Generated by Claude Code

devin-ai-integration[bot]

This comment was marked as resolved.

…idual race

Three Devin follow-up findings on this PR's release workflow:

1. "Unchecked main checks permit releases" -- the release gate only
   compared GITHUB_SHA to main's current tip; it never verified that
   push-triggered workflows (Security, Fuzz, ...) re-running on that new
   tip commit had actually finished green. Adds a check-runs verification
   step (querying commits/$SHA/check-runs, excluding this release run's
   own checks via GITHUB_RUN_ID) to both verify (fail fast) and publish
   (authoritative recheck immediately after the final main-tip check).
   Deliberately checks every reported check rather than a ruleset-derived
   "required" subset, per ADR 0129's existing rationale for avoiding
   administration:read.

2. "Failed asset upload strands releases" -- `gh release create` can
   publish the Release object and then fail partway through uploading the
   SBOM asset, but the existing resume logic treated "Release already
   exists" as nothing left to do and rejected the retry outright. Splits
   the previous single tag_resume output into tag_resume and
   release_resume, splits "Publish the GitHub Release" into a
   conditionally-skipped "Create the GitHub Release" step and an always-run
   best-effort "Attach any still-missing release assets" step so a retry
   can complete a stranded release's asset upload.

3. "Final tip check leaves race" (the original TOCTOU finding this PR's
   final main-tip recheck already addressed) -- assessed the residual
   check-then-act window after that recheck and concluded it is an
   inherent limitation (GitHub exposes no atomic check-then-tag primitive)
   that is already close to minimal for a workflow_dispatch-only,
   maintainer-triggered path. Documents it as an accepted, bounded residual
   risk in ADR 0129's new "Known limitations" section and docs/RELEASING.md,
   including the recovery guidance (cut a new release from the real tip;
   never retag) if it is ever actually hit, rather than adding
   disproportionate complexity chasing it to zero.

Also fixed a real jq context bug found while testing the new check-runs
filter by hand (index(.conclusion) inside a piped array literal indexed
the array, not the check-run object).

tests/test_release_workflow_contract.py and
tests/test_release_workflow_idempotency_contract.py updated/extended to
match; ADR/RELEASING.md/CHANGELOG.md prose kept in sync with the new
resume and checks-gate behavior.

Verified: tests/test_release_notes.py,
tests/test_release_workflow_contract.py,
tests/test_release_workflow_idempotency_contract.py,
tests/test_planning_adr_identifiers.py,
tests/test_product_planning_contract.py (53 passed); manual bash+jq
simulation of both new gate/attach steps against a stubbed gh CLI covering
all-green/pending/failed/empty/multi-page and
missing/new/already-attached/upload-failed cases;
python -m interrogate -c pyproject.toml . -> 100.0%; release.yml parses as
valid YAML.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4

Copy link
Copy Markdown
Contributor Author

Pushed cf69dc3 addressing the latest Devin round on .github/workflows/release.yml, plus follow-through on the earlier "Final tip check leaves race" thread. Replied on each specific thread with the details; summary here for anyone scanning at a glance:

  1. "Unchecked main checks permit releases" (real, fixed) — added a checks-green verification (commits/$SHA/check-runs, excluding this run's own checks) to both verify (fail-fast) and publish (final recheck, right after the main-tip recheck). Resolved that thread.
  2. "Failed asset upload strands releases" (real, fixed) — split tag_resume into tag_resume + release_resume; a Release already existing for this commit is now a safe resume that still attempts the best-effort SBOM asset attach, instead of being rejected as "nothing left to resume."
  3. "Final tip check leaves race" (original TOCTOU finding) — assessed honestly: the residual check-then-act window is real and can't be made literally zero (GitHub has no atomic check-then-tag primitive), but it's already close to minimal here (the recheck is the first thing publish does, before anything else) and this is a manual, maintainer-triggered workflow_dispatch path, not a high-frequency automated one. Documented as an accepted, bounded residual risk — with recovery guidance — in ADR 0129's new "Known limitations" section and docs/RELEASING.md, rather than adding more complexity chasing it to zero.
  4. Research-grounding waiver / text-only test coverage (soft findings) — replied reaffirming the existing reasoning; no code change.

Verification: tests/test_release_notes.py, tests/test_release_workflow_contract.py, tests/test_release_workflow_idempotency_contract.py, tests/test_planning_adr_identifiers.py, tests/test_product_planning_contract.py (53 passed); python -m interrogate -c pyproject.toml . → 100.0%; release.yml parses as valid YAML; both new bash+jq blocks hand-simulated end-to-end against a stubbed gh CLI (caught and fixed a real jq context bug along the way).

🤖 Generated with Claude Code

https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4


Generated by Claude Code

devin-ai-integration[bot]

This comment was marked as resolved.

Two more genuine Devin findings on the release workflow's checks-green
gate and tag/release lookup logic, both real gaps left after cf69dc3:

1. "Missing checks pass release gate" (checks-green gate, still a real
   gap despite the earlier reply on this thread claiming it was fully
   fixed): a dispatch fired moments after a merge can race GitHub's own
   registration of the push-triggered check-runs for the new main tip.
   `commits/$SHA/check-runs` can legitimately come back empty or partial
   before Security/Fuzz/CodeQL even exist as entries, and filtering an
   empty/partial list for "not complete+green" is vacuously empty too --
   so the gate could PASS before those checks had even started. Adds a
   top-level `RELEASE_EXPECTED_PUSH_CHECKS` env: this repository's own
   known push-triggered job names (ci.yml's two jobs, fuzz.yml's two,
   security.yml's two). Both the `verify` and `publish` checks-green
   steps now compute `missing_checks` against that list *before* the
   existing not-ready/green check, and fail closed if any expected check
   has not registered yet. Still no ruleset/`administration: read`
   lookup, consistent with ADR 0129's existing rationale for avoiding it
   -- just a fixed, repository-owned name list cross-referenced against
   the real API response.

2. "API failures block release recovery": the tag-existence lookup (via
   the commits API) and the `gh release view` release-existence lookup
   both treated any nonzero exit as "absent," so a transient rate-limit,
   auth, network, or 5xx failure during a resume attempt would have been
   misread as "safe to create fresh." Both lookups now distinguish a
   confirmed absence (an HTTP 404 from the commits API; "release not
   found" from `gh release view`, with the raw HTTP-404 rendering also
   matched as a defensive fallback) from every other failure class. Only
   a confirmed absence proceeds as a fresh publish/resume; anything else
   fails the step closed with a clear re-dispatch-to-retry message.

3. "ADR retains obsolete tag policy": ADR 0129's Gate section still
   described tag existence as an unconditional reject, contradicting the
   tag_resume/release_resume policy this workflow has actually
   implemented since an earlier fix pass. Updated alongside fix 2 above,
   since both touch the same paragraph.

docs/planning/adrs/0129-canonical-immutable-release.md and
docs/RELEASING.md updated to describe both refinements; CHANGELOG.md's
existing Unreleased entry for this mechanism folds them in (this feature
has not shipped yet, so amending its one entry in place matches this
branch's established convention rather than adding separate Fixed
entries).

tests/test_release_workflow_contract.py: adds
test_expected_push_checks_matches_this_repositorys_actual_push_triggered_jobs
(drift guard against ci.yml/fuzz.yml/security.yml's real job names),
test_checks_gate_requires_expected_checks_before_checking_they_are_green,
and four tests that execute the checks-green step's real, unmodified
script under bash against a stubbed `gh` CLI (zero/partial/complete
registration, plus a still-pending check after full registration) --
asserting real exit codes and error text, not just YAML text/order.

tests/test_release_workflow_idempotency_contract.py: restructures the
Release-lookup branch test for the new three-way (success / confirmed
absent / fail-closed) structure, adds matching tag-lookup branch tests,
and adds six tests that execute the tag_state step's real script under
bash against a stubbed `gh` covering confirmed-404, rate-limited, and
network-error tag lookups plus confirmed-absent/rate-limited/exists
release lookups -- asserting the real exit code and the actual
GITHUB_OUTPUT lines written for each.

Verified: tests/test_release_notes.py,
tests/test_release_workflow_contract.py (26 passed),
tests/test_release_workflow_idempotency_contract.py (25 passed),
tests/test_planning_adr_identifiers.py, tests/test_product_planning_contract.py
(69 passed total); hand-simulated both new bash+jq/gh blocks against
stubbed data before encoding them as tests (zero/partial/complete
check-runs registration; confirmed-404/rate-limited/network-error tag
lookup; confirmed-absent/rate-limited/exists release lookup -- all six
tag/release scenarios behaved exactly as intended); `bash -n` on every
extracted step script; `python -m interrogate -c pyproject.toml .` ->
100.0%; release.yml parses as valid YAML (PyYAML ad hoc check, matching
this repo's existing no-new-dependency workflow-contract convention).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4

Copy link
Copy Markdown
Contributor Author

Devin follow-up review round: two more real fixes on release.yml

Commit b7a044db (fast-forward on top of cf69dc39) closes two genuine gaps Devin's next review pass found in the checks-green gate and the tag/release lookup logic, plus one documentation-accuracy finding that fell out of the same fix.

1. Checks-registration race (thread on "Missing checks pass release gate")

The checks-green gate added in cf69dc39 only verified "every check GitHub reports is complete+green." It never handled a dispatch fired moments after a merge racing GitHub's own registration of the push-triggered check-runs for the new main tip -- commits/$SHA/check-runs can legitimately come back empty or partial before Security/Fuzz/CodeQL even exist as entries, and filtering an empty list is vacuously empty too, so the gate could PASS before those checks had even started.

Fix: a new top-level RELEASE_EXPECTED_PUSH_CHECKS env lists this repository's own known push-triggered job names (ci.yml's two jobs, fuzz.yml's two, security.yml's two -- derived straight from their real name: fields). Both verify and publish's checks-green steps now compute missing_checks against that list before the existing not-ready/green check, and fail closed if any expected check hasn't registered yet. Still no ruleset/administration: read lookup -- consistent with ADR 0129's existing rationale for avoiding it.

2. Confirmed absence vs transient failure (thread "API failures block release recovery")

Both the tag-existence lookup (commits API) and gh release view treated any nonzero exit as "absent" -- a rate-limit, auth, network, or 5xx blip during a resume attempt would have been misread as "safe to create fresh."

Fix: both lookups now distinguish a confirmed absence (HTTP 404 from the commits API; release not found from gh release view, with the raw HTTP-404 text also matched as a fallback) from every other failure. Only confirmed absence proceeds as fresh-publish/resume; anything else fails the step closed with a re-dispatch-to-retry message.

3. ADR staleness (thread "ADR retains obsolete tag policy")

ADR 0129's Gate section still described tag existence as an unconditional reject, contradicting the tag_resume/release_resume policy already implemented. Updated the same paragraph while fixing #2 above; docs/RELEASING.md updated to match.

Verification

  • python -m pytest tests/test_release_notes.py tests/test_release_workflow_contract.py tests/test_release_workflow_idempotency_contract.py tests/test_planning_adr_identifiers.py tests/test_product_planning_contract.py -q69 passed.
  • Real execution, not just text assertions: both test files now extract the workflow's actual, unmodified step scripts and run them under bash against a stub gh CLI -- 4 new scenarios for the checks-registration gate (zero/partial/complete-registered, plus still-pending-after-complete-registration) and 6 for the tag/release lookups (confirmed-404, rate-limited, network-error tag lookup; confirmed-absent, rate-limited, exists release lookup) -- asserting real exit codes, GITHUB_OUTPUT contents, and error text. I hand-ran all ten scenarios manually first (real jq/bash/stub-gh) to build confidence before encoding them as tests.
  • python -m interrogate -c pyproject.toml .PASSED (100.0%).
  • release.yml parses as valid YAML; bash -n passes on every extracted step script.
  • Full python -m pytest tests -q was also kicked off; it runs long (~850s per this PR's own prior verification notes) and was still in progress (17%+, one early failure consistent with the PR description's documented pre-existing test_psychometric_routing.py sandbox gap, unrelated to this diff) at the time of this comment -- the targeted release-suite run above is the primary evidence for this change, per this repo's own tests/ convention of each file being independently runnable.

Replied on all three review threads individually with the specific fix; the two Devin threads that were still open ("API failures block release recovery", "ADR retains obsolete tag policy") are now resolved. The older, already-resolved "Missing checks pass release gate" thread got a follow-up reply only (per this repo's convention, a resolved thread is answered, not re-resolved).

Non-force push, no commits rewritten, no merge tool invoked -- per this org's "OpenCode judges PRs; GitHub Actions performs mechanical merges" governance.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

CO 자동 배포/release adoption 선행조건을 연결합니다.

현재 fast-mlsirm의 release foundation은 #1692@a6ac0f49d5123244fe89f26748a65f551ad9d514이고, release-cut owner는 #1471입니다. #1692의 현재 protected CodeQL blocker는 leaf attempt 2 job 101562653014에서 Exact CodeQL job was rerun without an authenticated terminal verdict.이며 canonical repair owner는 .github#1902입니다. 해당 exact tuple을 #1902 comment 5564859885로 연결했습니다. fast-mlsirm 쪽 empty commit/manual blind rerun으로 우회하지 않습니다.

배포 순서는 fail closed로 유지합니다: #1902 terminal-verdict recovery → #1692 protected integration/independent approval → #1471 exact v0.9.2 recut/version consistency/protected integration → existing fast-mlsirm publish workflow로 registry publication 및 exact filename/digest verification → 그 immutable released package/revision을 CO consumer/release lane에서 채택 → 실제 consumer install/runtime smoke 및 CO service deployment를 별도 검증.

현재 PyPI package publication이 없고 GitHub release는 v0.9.1만 존재하는 전제이므로, CO #1030에서 source checkout/branch를 release처럼 소비하거나 미게시 버전을 설치 성공으로 간주하지 마세요. Package registry publish success와 CO/service deployment success는 별도 acceptance evidence입니다. Secrets 값, provider credentials, protection settings는 변경하지 않습니다.

Signed-off-by: Seongho Bae <me@seonghobae.me>
Signed-off-by: Seongho Bae <me@seonghobae.me>
@seonghobae

Copy link
Copy Markdown
Contributor Author

독립 검증: 원격과 동일한 8443719334d31012d8306dbb517cce6e023443c7의 깨끗한 격리 작업 트리에서 release workflow, supply-chain, idempotency 검사 71개를 직접 실행해 19.47초, exit 0을 확인했습니다. 실행 전후 HEAD와 clean 상태가 일치합니다. 실제 attachment shell을 실행하는 테스트는 동일 바이트, 다른 바이트, 최초 업로드, 다운로드/업로드 실패, 성공 응답 뒤 파일 누락/빈 파일을 구분합니다. 후속 수정은 기존 자산을 덮어쓰지 않고 바이트 일치 여부를 검사합니다. 이 검증은 로컬 모의 GitHub 응답 기반이며 실제 registry 게시, 독립 GitHub 승인, 보호 병합 또는 배포 성공은 아닙니다. 공개 Release 생성과 SBOM 부착 사이의 중간 상태 및 검증 직후 원격 변경 가능성도 별도 미해결 경계입니다.

Signed-off-by: Seongho Bae <me@seonghobae.me>
@seonghobae

Copy link
Copy Markdown
Contributor Author

Completed verification and successor boundary

Exact source head 8443719334d31012d8306dbb517cce6e023443c7:
Security and Quality run 34081995823
completed successfully, including all three jobs.

  • Hosted full suite: 3485 passed, 2 skipped, 731.92 seconds.
  • Benchmark subset: 134 passed, 10.20 seconds; 1205 statements and 436 branches,
    100% coverage; public docstrings 100%.
  • Wheel build, installation and import step succeeded.
  • Actual artifact 10004081382 is bound to this head: cyclonedx-sbom archive,
    SHA256 40264bf9102f7ebdeb37d64a850a29ccfcdc493b2f13230c04fe5292e069cc29.
    It contains a 103965-byte JSON file reporting CycloneDX 1.6 and 83 components.
    This is not a signature/attestation or complete schema-conformance claim.

Local full session14009 separately completed with 3484 passed, 2 skipped in
866.84 seconds. Source stayed at 844, but the Gap document was edited while
that run was active; do not describe it as a clean 844-tree validation. The
one-test difference from hosted execution has not been explained.

Doc-only successor 27293e637103da78b3c53ff7dc30c9b5763e67ce was pushed only
after the hosted predecessor finished. Its 75 focused hourly-loop and release
contracts passed in 27.77 seconds. New exact-head hosted validation is required.
No protected merge, package registry publication, operational deployment or
buyer accuracy/latency improvement is claimed by these receipts.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Integration checkpoint: b0e3d62390894af105a3577eb7daf97221330e28 normally merges the fetched main into the existing release branch, preserving predecessor 27293e637103da78b3c53ff7dc30c9b5763e67ce. The merge had no conflicts and carries provider reliability source and regression changes.

Focused local verification on the integrated source completed: release notes, supply-chain, workflow, and idempotency contracts 89 passed in 106.62 seconds; provider reliability 32 passed in 86.93 seconds. Both commands used the existing main-checkout project virtual environment from the release worktree. This is not a fresh dependency installation or a full-suite result. git diff --check passed and the normal push completed.

The PR remains Draft. Required hosted checks, independent review, protected merge, immutable package publication, schema provenance, and installed-consumer proof remain outstanding; the integration does not establish a published artifact.

Copy link
Copy Markdown
Contributor Author

긴급 복구 병합 뒤의 release/adoption 인계입니다. 이 PR의 소스·브랜치는 변경하지 않았습니다.

CO #1105는 650bb67dd9cb9b0846a9026dd829c82f43b839e8, #1082는 9c83530f5860aaee6e324f9a84e7ce3107eeba92, 기본 timeout=None만 분리한 #1118은 9c9873a97770e35f949597d939077829af5ba3e4, stacked CI #1066은 314645dcf7d3ef07581433ce56840372fdeb178b, runner pin #1072는 42f9d905b2f8a09aa8ed80303d892fae4d0398d7로 병합했습니다. 원래 #1053의 관리자 정책과 다섯 미해결 지적은 이 release 후보에 포함하지 않았으며 #1053을 닫지 않았습니다.

실제 통합 검증: 314645dc push run 34420642748의 Tests/package 102695071836, fuzz 102695071648, CodeQL/supply-chain/SBOM 102695071899 모두 terminal SUCCESS입니다. runner pin까지 포함한 42f9d905 push run 34421405343은 조회 당시 queued이므로 이전 결과를 새 head의 GREEN으로 이전하지 않습니다.

중앙 collector .github#1978/#2053은 f578d8d960177ff113c25fd740619b4a483df300까지 보호 main에 반영됐습니다. 그러나 그 버전의 scripts/ci/contextual_orchestrator_review_sidecar.sh는 여전히 ORCHESTRATOR_PIN_SHA 기본값을 414f22973658c4ddc3d4320fcf7acd9b4e8ba991로 둡니다. CO releases API도 빈 배열을 반환했습니다. 따라서 소스 병합은 완료했지만 중앙 runtime은 새 timeout 수정 채택이 입증되지 않았습니다.

이 기존 release lane에서 필요한 다음 완료 조건을 연결합니다: 현재 main을 유효 delta 손실 없이 통합 → 실제 exact-head 검증 → 공개 Release에 필수 SBOM이 빠지는 구간과 동시 asset 변경 처리 수리 → 같은 커밋의 버전/CHANGELOG/불변 artifact/provenance 발행 → 중앙 .github#1661의 released API/consumer 계약으로 채택 → OpenCode/Strix/Noema 실제 canary에서 설치 revision, 90초를 넘는 정상 요청, 서버 request_id와 bounded collector receipt를 확인. probe/retention을 모델 공통 timeout으로 재사용하거나 paid fallback으로 우회하지 않습니다.

docs/product-technical-gap-baseline.md의 현행 판정도 이 exact revision과 run을 기준으로 갱신해야 합니다. 이 인계는 release, 배포, 전체 인프라 복구가 완료됐다는 보고가 아닙니다.

Copy link
Copy Markdown
Contributor Author

Release-unblock reconciliation, 2026-09-10, exact release head b0e3d62390894af105a3577eb7daf97221330e28.

The Python-floor prerequisite is already carried here: live comparison from #995 e2df7803b5cb77b65ffd3f2a2ce57b2a2cda5a0a is ahead 32 / behind 0, and this PR's pyproject.toml actually declares >=3.12. The earlier tomllib/Python-3.10 discussion must not be treated as an unfixed source defect on this head. Protected-main integration still must preserve main's later immutable fast-mlsirm Git pin 09f762ded35786dd1078222a4577ff09d649816f; #995's direct merge was rejected with real lock/manifest conflicts, so do not restore its old tag URL wholesale.

One release-identity finding is still supported by current source: .github/workflows/release.yml tag_state has commit) tag_commit="${tag_object_sha}" ;;, accepting lightweight tags despite the annotated-tag contract. Repair this actual case test-first (lightweight tag rejected; a valid annotated tag and fresh confirmed-404 publish remain accepted). Do not treat outdated test-location discussions or old approval counts as evidence that this path is repaired. Retain mandatory SBOM byte verification and no-clobber behavior.

New protected owner merges now include #1007 40800770, #1065 2271d139, #1085 3ba25419, #1050 1e25c718, in addition to the earlier timeout/correlation fixes. The prior owner 42f9d905 run 34421405343 now has all three local workflow jobs successful. Current #1050 merged-head run 34424893677 and #1065 run 34423989287 were queued at re-read; they are not GREEN. Central review-sidecar default still reads 414f22973658c4ddc3d4320fcf7acd9b4e8ba991 (blob 38d9551a3273d9da8562efee1079f47acdfb819a). This remains an owner release -> immutable consumer adoption gap, not deployed restoration. The user's emergency bypass authorizes eligible merges, not discarding a validated pin, publishing incomplete artifacts, fabricating approval, or claiming missing runtime evidence.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Implemented the specific annotated-tag repair requested in the September 10 checkpoint as stacked contribution #1147, preserving this PR's exact b0e3d62 source and all existing delta.

Child head 7a37d59 rejects lightweight tags before writing resume outputs. The existing bash fixture now returns a real annotated-tag ref and a commit on dereference. The new regression was RED before the production guard; the complete idempotency contract file is GREEN (33 tests). Read-only agent review found no actionable defects; no independent GitHub approval or hosted Security success is claimed.

Please integrate this two-file child patch through the existing release lane. Fresh confirmed-404 publishing, annotated-tag ancestry checks, SBOM and immutable artifact requirements remain in force. No release was published.

Copy link
Copy Markdown
Contributor Author

Fresh release-consumer acceptance evidence from central Noema makes this release lane more urgent, but not merge-ready as-is.

Protected CO is now main@012beaacd0631f8cd3391c77744eeb626269b5de; this PR remains b0e3d62390894af105a3577eb7daf97221330e28, currently 39 ahead / 175 behind with merge base 414f22973658c4ddc3d4320fcf7acd9b4e8ba991. Preserve the release mechanism delta, but reconcile it by ordinary non-force merge with current protected main before any publication claim. Predecessor checks do not transfer.

Why the consumer canary matters: .github#2126@51299f1a4398ce8481d14bbb0515dd5367aefaee, Required Noema run 34707140933 attempt 3 / job 103635607612, still consumed .github's hard-pinned CO 414f2297.... Its retained sidecar artifact 10307437409 reached ready_count=4 but shows repeated provider TimeoutError boundaries at ~90.05–90.10 seconds and ends the verdict path with 502 provider_connection_error. Protected CO has already advanced beyond that runtime behavior, but the central consumer cannot adopt mutable main under the current ownership contract.

Add this unchanged #2126 head as a post-publication consumer canary: after an immutable release is created from the then-current protected CO exact head, the central consumer must pin that released identity (not mutable main), then rerun #2126 without source churn and demonstrate that the legacy implicit 90-second runtime is absent and the review path yields an authenticated exact-head verdict or an explicit typed unavailable outcome. This does not waive the current 175-commit reconciliation, release checks, SBOM/provenance, independent review, or normal protected integration.

Copy link
Copy Markdown
Contributor Author

Current-head documentation reconciliation after #1147 normal carryover:

  • b73b5fed144dedf4bd4e4fb2bf5dbf886414ee40 changed the executable contract so a pre-existing lightweight vX.Y.Z tag fails closed instead of being accepted as a resumable canonical release identity.
  • ADR 0129 and docs/RELEASING.md still described lightweight tags as resumable. I repaired that code/docs contradiction by ordinary-forward commits 1e7f198735ae767b1553d703bb1f15fbee669cbc and b51009c8b5b6c9e79672e412a87e5b4609f42173. The ADR now records annotated-tag-only resume, the rejected lightweight-tag alternative, rollback/recovery consequences, and the fix(release): reject lightweight tags before publication resume #1147 TDD/merge lineage; the operator runbook now fails lightweight tags closed at precondition and verify stages.
  • Fresh exact-head hosted checks on b51009c8… are authoritative and are still settling; no thread or merge/release authority is inferred from predecessor GREEN.
  • CHANGELOG.md still contains historical release-mechanism prose that says tag lookup uses the generic commits API and SBOM is best-effort. That text is now a remaining code-current documentation finding. Do not treat the branch as documentation-complete until the canonical release writer reconciles that paragraph (plus the still-required fix(packaging): require fast-mlsirm on every supported runtime #995/protected-main non-force reconciliation).

No force update, protection weakening, release publication, or predecessor-evidence transfer was performed.

Copy link
Copy Markdown
Contributor Author

Foundation order correction from fresh live refs: #995 is no longer the stale e2df7803... prerequisite. It has been ordinarily reconciled with protected main@012beaacd0631f8cd3391c77744eeb626269b5de and now sits at 29b7f5457ee6a9c2a1f25f1e564f798d419bacc9, 10 ahead / 0 behind, Ready for fresh current-head governance.

This release lane's current b51009c8b5b6c9e79672e412a87e5b4609f42173 still descends from old #995 head e2df7803...; compared to the new #995 head it is diverged (34 ahead / 176 behind, merge base e2df7803...). Do not treat the embedded old #995 delta as satisfying the prerequisite, and do not force-rebase. Preserve #1030 while #995 reaches protected main normally, then ordinary/non-force reconcile #1030 to that new protected tip and re-run exact-head release acceptance.

The #995 conflict resolution is source-stable: immutable fast-mlsirm v0.9.1 resolves to the same commit 09f762ded35786dd1078222a4577ff09d649816f used by main's temporary #1111 recovery pin. The change is release-contract identity + Python 3.12 support-floor enforcement, not an unreviewed dependency source swap.

Copy link
Copy Markdown
Contributor Author

Prerequisite order correction from fresh #995 review evidence:

contextual-orchestrator#995@29b7f5457ee6a9c2a1f25f1e564f798d419bacc9 is source-stable, but its central OpenCode coverage dispatch 34730081810 is fail-closed before pytest because protected .github rejects the legitimate changed uv.lock as merged_base_fingerprint_drift. Canonical repair owner is .github#1398, which already owns authenticated exact-HEAD Python lock trust; #1398 is now Draft because its exact head 8ff7cc0969860a1473a57bbfe500ce3a023a41be is 20 ahead / 87 behind protected .github/main@fb17ef556f94f673234aa557254ae52779e9a7b0 and must ordinary/non-force reconcile before protected integration.

Therefore release order is now .github#1398 integration → unchanged #995 exact-head coverage/review replay and qualifying approval → normal #995 protected merge → this #1030 ordinary/non-force reconciliation onto the resulting protected CO main → fresh release/SBOM/provenance/review evidence → immutable CO GitHub Release. Do not reconcile or publish #1030 ahead of #995, and do not transfer predecessor review/check evidence.

Copy link
Copy Markdown
Contributor Author

LifeOS release-unblock repair, scoped writer claim (2026-09-14 KST).

Fresh owner head inspected: b51009c. GitHub Releases inventory is empty. The current publisher creates a public Release before attaching its mandatory SBOM; GitHub release immutability forbids that later upload. I am preparing a bounded test-first repair on this existing release lane: draft-before-assets, content verification before finalization, and fail-closed verification of the immutable published result. Scope: .github/workflows/release.yml, focused release workflow tests, and docs/RELEASING.md. No source/runtime/provider/credential changes, no rebase or protected-main reconciliation, no publication or integration claim. Existing .github#1398 → #995#1030 prerequisite order remains intact.

This claim applies only to this interactive repair; exact branch head/blob will be checked before a non-force update. Other active source writers should not overlap these files until the repair is published or this claim is explicitly released.

Copy link
Copy Markdown
Contributor Author

Immutable-publication repair delivered — 2026-09-14 KST

The scoped writer claim in #issuecomment-5654583092 is RELEASED. Non-force changes are published on this existing lane; no protected-main reconciliation or publication was attempted.

  • RED tests: 1f218cb86a36997279f075a7583ec9c4fadb78cc.
  • Publisher fix: fdc6d4a25e061299fca5261f0f000bfb4620191c.
  • Runbook/current head: c525ae41a7bb6b6816551094742ea0ef96b48544, freshly confirmed by the PR API.
  • Exact tested and remotely written workflow blob: 5a95aed1dcba82530a99dd500c27fad53fae4e71.

The old workflow created a public release before uploading its mandatory SBOM, which cannot work when GitHub release immutability is enabled. It now creates a Draft, completes mandatory attachment and byte comparison first, validates existing lifecycle before resume mutations, then publishes and requires actual immutable: true plus gh release verify / gh release verify-asset. Complete published immutable releases are verify-only; incomplete/mutable public releases, wrong tags, malformed lifecycle metadata and unverifiable assets fail closed. No published assets are clobbered.

Executed locally against the actual workflow shell with a stateful remote-CLI boundary: original exact workflow 16 failed, 2 passed; fixed workflow 18 passed. YAML parse, all bash run-step syntax, new-test compileall and diff whitespace checks passed. This was a hash-verified partial fixture, not a full dependency-installed checkout or hosted acceptance. Existing full repository tests and exact-head security/review gates still apply.

Fresh prerequisite reads still show .github#1398@8ff7cc0969860a1473a57bbfe500ce3a023a41be open/Draft and #995@29b7f5457ee6a9c2a1f25f1e564f798d419bacc9 open/unmerged. Retain the recorded .github#1398 → #995 → #1030 normal integration order and regenerate exact-head evidence after reconciliation. Do not transfer predecessor greens.

Companion EgressWeave repair is ContextualWisdomLab/EgressWeave#247. Neither this repair nor that PR is evidence that LifeOS can consume a released owner: real immutable publication and the executable API/authentication/deployment-identity contract in #1023 remain required.

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 13, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== HEAD AND DIFF STAT =='
git rev-parse HEAD
git status --short
git diff --stat origin/main...HEAD
git diff --name-status origin/main...HEAD

echo '== WORKFLOW OUTLINE / JOB MARKERS =='
rg -n -C 3 '^(name:|on:|env:|jobs:|  (verify|publish):|    [A-Za-z][^:]*:)$|gh release|release (create|edit|verify|verify-asset)|SBOM|TARGET_SHA|tag_state|release_state' .github/workflows/release.yml

echo '== RELEASE SCRIPTS =='
fd . scripts/ci -t f | sort
ast-grep outline scripts/ci --items all --type function

echo '== TEST FILES =='
fd 'test_release.*\.py' tests -t f | sort
ast-grep outline tests/test_release_workflow_idempotency_contract.py --items all --type function
ast-grep outline tests/test_release_workflow_contract.py --items all --type function

echo '== RUNBOOK LOCATIONS =='
rg -n -C 3 'draft|Draft|immutable|SBOM|resume|asset|publish|release' docs/RELEASING.md

echo '== REPAIR-RELATED DIFF =='
git diff --find-renames origin/main...HEAD -- .github/workflows/release.yml docs/RELEASING.md tests/test_release_workflow_idempotency_contract.py tests/test_release_workflow_contract.py scripts/ci

Length of output: 145971


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== PUBLISH LIFECYCLE BLOCKS =='
nl -ba .github/workflows/release.yml | sed -n '330,456p'

echo '== IMMUTABLE PUBLICATION TEST OUTLINE =='
ast-grep outline tests/test_release_immutable_publication.py --items all --type function

echo '== IMMUTABLE PUBLICATION TEST: FIXTURE AND LIFECYCLE TESTS =='
rg -n -C 3 'def _|def test_|release edit|release upload|release verify|immutable|draft|asset|clobber|state' tests/test_release_immutable_publication.py

echo '== SUPPLY CHAIN CONTRACT =='
ast-grep outline tests/test_release_supply_chain_contract.py --items all --type function
rg -n -C 3 'def test_|SBOM|attest|provenance|release verify|verify-asset|immutable' tests/test_release_supply_chain_contract.py

echo '== CHANGELOG RELEASE CLAIMS =='
rg -n -C 3 'release|Release|SBOM|tag|immutable|best-effort|commit' CHANGELOG.md

Length of output: 34655


🤖 web_search executed:

GitHub CLI manual gh release edit --draft=false syntax and gh release verify-asset command syntax

Length of output: 9520


@seonghobae: I found one documentation issue.

CHANGELOG.md Lines 29-46 still describes the old release contract. It says tag lookup uses the commits API, that a tag must match the dispatch commit, and that SBOM lookup and attachment are best effort. The current workflow instead uses the exact tag-ref API, rejects lightweight tags, supports annotated-tag ancestor resumes, and fails closed when mandatory SBOM retrieval, attachment, or byte verification fails.

Update this changelog entry to match .github/workflows/release.yml and docs/RELEASING.md. Otherwise, the changelog gives maintainers and downstream consumers an unsafe description of the canonical release process.


✏️ Learnings added
Learnt from: seonghobae
URL: https://github.com/ContextualWisdomLab/contextual-orchestrator/pull/1030

Timestamp: 2026-09-13T17:03:52.343Z
Learning: In `contextual-orchestrator`, the canonical GitHub Release process requires an annotated tag, a mandatory CycloneDX SBOM with byte verification before publication, and a final immutable Release verification. Documentation for this process must not describe SBOM handling as best effort or describe the superseded generic commits-API tag lookup.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request priority: medium Normal-priority or P2 work status: draft type: feature New or expanded product capability

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants