Skip to content

fix(release): repair the release-please pipeline onto the 1.0.0 line (ADR-1151) - #1216

Merged
lusoris merged 4 commits into
masterfrom
fix/release-please-setup
Sep 3, 2026
Merged

lusoris merged 4 commits into
masterfrom
fix/release-please-setup

Conversation

@lusoris

@lusoris lusoris commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Repairs the release-please pipeline and retargets it onto a fresh VMAFx number line. The fork has never released — zero GitHub releases, zero fork-made tags, and every vX.Y.Z tag reachable from master belongs to Netflix upstream history (git merge-base --is-ancestor v3.2.0 origin/master is false). ADR-1127's "first release is v3.2.1" rested on the 3.2.x manifest baseline, which was a source-version alignment with Netflix's SONAME, not a release. ADR-1151 supersedes that choice: the first release is v1.0.0.

An audit of the pipeline that would have produced that tag found it unable to release correctly at all — four P0s. This PR fixes what a code change can fix and leaves the pipeline correct and idle. It does not tag, publish, merge, reopen #1175, or trigger supply-chain.yml / docker-publish-*. It stays DRAFT until the open questions below are answered.

Maintainer-requested evidence — the dry run now reads 1.0.0

--config-file is resolved on the remote branch, so a local edit cannot be dry-run tested. Run against this pushed branch:

$ npx --yes release-please@latest release-pr --repo-url VMAFx/vmafx \
    --token "$(gh auth token)" --dry-run \
    --target-branch fix/release-please-setup \
    --config-file release-please-config.json \
    --manifest-file .release-please-manifest.json
...
⚠ Setting version for . from release-as configuration
❯ running plugin: SentenceCase
Would open 1 pull requests
fork: false
title: chore(fix/release-please-setup): release 1.0.0
branch: release-please--branches--fix/release-please-setup--components--vmafx
draft: false
body: :robot: I have created a release *beep* *boop*
---
## 1.0.0 (2026-09-02)

Exit 0. The component in the title is the --target-branch argument; on master it renders chore(master): release 1.0.0. Before this PR the same command emitted title: chore(master): release 3.2.1.

The same log also confirms docker/Dockerfile.node has left the coordinated-marker set (it no longer appears in the updater list) and that release-please parses the config unchanged with the new _comment_* keys present.

Type

  • feat — new feature
  • fix — bug fix
  • perf — performance improvement
  • refactor — no behavior change
  • docs — documentation only
  • test — test-only
  • build / ci — tooling / infra
  • port — cherry-pick from upstream Netflix/vmaf
  • sycl / cuda / simd — backend-specific

Finding → fix mapping

# Sev Finding Fix in this PR
1 P0 Release PRs get zero check runs — release-please.yml authenticated with secrets.GITHUB_TOKEN, GitHub suppresses follow-on workflow events from it, so the sole required context could never report and the PR sat BLOCKED. Fixed. The job mints a scoped installation token with SHA-pinned actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 (v3.2.0); both release-please invocations and both read-only gh api probes consume steps.app-token.outputs.token, and the job's own GITHUB_TOKEN drops from contents: write to contents: read. Creating the App and its two secrets stays a repo-admin action (Q6), and a preflight step fails the job with a remediation message while RELEASE_BOT_APP_ID / RELEASE_BOT_PRIVATE_KEY are absent — the audit's explicit "fail loudly rather than fall back" requirement. Documented in docs/development/release.md §Release-bot identity; the remaining admin half is tracked as T-RELEASE-BOT-IDENTITY-2026-09-03 in docs/state.md.
2 P0 Config forced 3.2.1, contradicting the binding 1.0.0 decision. release-as → "1.0.0", manifest → "0.0.0" so 0.0.0 → 1.0.0 is a monotone forward bump. Proven by the dry run above.
3 P0 No gate proved the changelog fragment cut ran — a published release could ship a CHANGELOG.md whose newest section was still ## [Unreleased] over 1,550 live fragments. scripts/release/verify-release-version.sh (the tag-time preflight all three publication workflows run) gains five fail-closed assertions: exactly one ## [X.Y.Z] - YYYY-MM-DD heading; changelog.d/releases/X.Y.Z.json exists with matching .version; zero active fragments across the six section dirs; no _pre_fragment_legacy.md; no surviving release-as / bootstrap-sha. 11 new regression cases.
4 P0 release-publish does not exist and pypi-publish has no rules; GitHub auto-creates a referenced environment with an empty rule set, so twelve write-bearing jobs ran with no approval gate — while release.md:56 claimed the opposite. supply-chain.yml's validate-release gains a read-only gh api .../environments/{release-publish,pypi-publish} preflight that fails closed unless each carries a required_reviewers rule. release.md corrected. Creating the environments is a repo-admin action (T-RELEASE-PUBLISH-ENVIRONMENTS-MISSING-2026-09-03).
5 P1 release-as is a deprecated, persistent override applied after commit analysis — left in place it pins every future release. Kept for the first cut only, with an inline _comment_release_as naming it one-shot. The Release Script Contract (ADR-1128) job now fails if either one-shot field survives once the manifest reaches 1.0.0 (inert at 0.0.0, arms the moment the first release lands). rollover-changelog-fragments.sh now also deletes the two _comment_* keys so no orphan explanation survives.
6 P1 Release Script Contract (ADR-1128) and five sibling rule-enforcement gates were reporting but not required — a red release-script contract blocked nothing, and no admin bypass was needed to merge past them. All six appended to the aggregator's required array. They trigger on pull_request and carry the same draft gate as the existing entries, so they genuinely report on every non-draft PR. Required-check inventory in release.md corrected (it claimed 25 named contexts; protection names exactly one, and the aggregator's own list is the real 34-entry inventory).
7 P1 release-please force-recreates the release branch on every push to master, destroying the documented hand-added rollover commits — which is also what deletes release-as. New Detect a hand-finished release PR frozen for merge step; the PR-update invocation is skipped while any release-please-- PR carries the autorelease: cut label. Freeze procedure documented in release.md.
8 P1 Absent-means-pass green-lights a manifest-only release PR having verified nothing, while the docs claimed the opposite. Aggregator gains a mustReport list — Release Script Contract (ADR-1128), Netflix CPU Golden Tests (D24), Build — Ubuntu gcc (CPU) + DNN — that fails on absence when the head ref starts with release-please--. .release-please-manifest.json and release-please-config.json joined the c_core selector in .github/ci-impact.json so those jobs do real work instead of a no-op success. False claim at release.md:53-54 corrected.
9 P2 The 1.0.0 switch downgrades the advertised pkg-config version and the SONAME / product-version split is undocumented. No SONAME code change (it is already structurally independent). The split is stated in ADR-1151, in a new release.md table, and in an ADR-citing comment directly above vmaf_soname_version at core/meson.build:19.
10 P2 master's coordinated markers advertise an unreleased 3.2.1. Not changed — open question Q2. Now nine markers (see #17). No CI gate compares markers to the manifest outside tag time, so the current 0.0.0-manifest / 3.2.1-marker skew is inert. Tracked as T-RELEASE-MASTER-MARKERS-3-2-1-2026-09-03.
11 P2 bootstrap-sha truncates the first release's generated notes to 31 commits. Kept — open question Q3. Now carries an inline _comment_bootstrap_sha citing ADR-1151, and is covered by the finding-5 guard. Blast radius stays bounded by skip-changelog: true: only the PR/draft body, never CHANGELOG.md.
12 P2 The fork releases into Netflix's tag namespace. Not changed — open question Q5. Adopting +refs/tags/*:refs/tags/upstream/* plus a "tag must not exist on Netflix/vmaf" guard is a policy call. Tracked as T-RELEASE-NETFLIX-TAG-NAMESPACE-2026-09-03.
13 P2 sentence-case plugin mangles camelCase identifiers in published release notes. Not changed — open question Q7. Drop-vs-specialWords is a policy call; a custom list replaces the built-in ['gRPC','npm'] defaults.
14 P2 Recovery workflow_dispatch never restores the latest container tags. All four enable= sites now read a new validate-release output resolved from gh api repos/$GITHUB_REPOSITORY/releases/latest, so the guard is identical on both trigger paths. Documented in release.md's recovery section.
15 P3 release-type: simple warns on a missing version.txt every run. Not changed — open question Q8. Adding one makes it an eleventh coordinated marker that must join extra-files.
16 P3 pkg/version/version.go documents a repo-root VERSION file that does not exist. Doc comment rewritten to describe the real VMAFX_VERSION build-arg → -ldflags -X flow; the stale v3.x.y example updated to v1.x.y.
17 P3 docker/Dockerfile.node is the only annotated Dockerfile; its siblings default to dev. ARG VMAFX_VERSION=dev like its siblings, marker comment removed, and its extra-files row dropped — verify-release-version.sh derives its list from that array, so the two stay consistent automatically. Ten coordinated markers → nine.
18 P3 Helm chart version, three Cargo.tomls and the root pyproject.toml are uncoupled and unexplained. Versions left alone; each now carries a one-line ADR-citing comment. The Chart.yaml comment explicitly warns that annotating version: alongside appVersion: would hard-fail every release preflight (both scripts require exactly one marker per file).

Open policy questions (why this stays DRAFT)

Each was listed as not-for-implementation in the brief. None is implemented; all are documented here, in ADR-1151's follow-ups, and in docs/state.md.

  • Q1 — manifest pre-release value. Set to 0.0.0 (the audit's recommendation) so 0.0.0 → 1.0.0 is a monotone forward bump rather than a release-as-forced downgrade from 3.2.0. The maintainer's binding decision required some value that makes release-please compute 1.0.0, so a value had to be chosen to produce the requested evidence; the dry run above proves this one works. Say the word and it flips to 3.2.0.
  • Q2 — master's nine markers. Left at 3.2.1. Reset now (stops master-built artefacts advertising an unreleased, Netflix-adjacent 3.2.1) or leave for the release PR to rewrite?
  • Q3 — bootstrap-sha for the 1.0.0 cut. Kept at 98dc0b2b, so generated notes cover ~31 commits from 2026-08-31 and the fragment-rendered ## [1.0.0] section is the authoritative notes. Repoint further back, or remove it and let release-please walk the whole fork history?
  • Q4 — erroneous v4.0.0-lusoris.0 artefacts. No such git tag exists on origin or locally, and there have been zero GitHub releases, so nothing to delete on those surfaces. GHCR could not be checked — this token lacks read:packages (HTTP 403). Should ghcr.io/vmafx/* be enumerated and any 4.0.0-lusoris.0 image (plus any latest pointing at it) deleted, and by whom?
  • Q5 — Netflix tag-namespace collision. Adopt +refs/tags/*:refs/tags/upstream/* with tagOpt = --no-tags plus a "tag must not exist on Netflix/vmaf" guard, or accept the shared namespace and rely on 1.x-vs-3.x divergence?
  • Q6 — release-bot identity (workflow half now implemented). The workflow assumes a GitHub App, which is the audit's recommendation: durable, non-expiring, scoped to two permissions on one repo, minted and revoked per run. Creating the App and adding RELEASE_BOT_APP_ID / RELEASE_BOT_PRIVATE_KEY is the maintainer action that remains; release-please.yml is deliberately hard-down until then. Say so if you would rather use a PAT and the two with: inputs change; the rest of the wiring is identical.
  • Q7 — sentence-case plugin. Drop it ("plugins": []), or keep it with an explicit specialWords list that must re-list gRPC and npm because a custom list replaces the defaults (and will raise an editor schema warning, since specialWords is documented only under linked-versions)?
  • Q8 — version.txt. Add a real one-liner (silences the every-run ⚠ file version.txt did not exist, but becomes an eleventh coordinated marker), or leave it absent and document the warning as expected output?

Deliberately out of scope

Repo-admin or live-release actions, not code: creating the GitHub App and its two secrets; creating release-publish / pypi-publish with a required reviewer and a v* tag policy; any tag / release / publish action.

Checklist

  • Commits follow Conventional Commits (the commit-msg hook enforces this).
  • make format && make lint is green locally — pre-commit run --files over all 34 changed paths is green (shfmt, shellcheck, markdownlint-cli2, check-json/yaml/toml, gitleaks, ADR collision guard). No C/C++/Python source changed, so the clang-tidy / cppcheck / ruff / black / semgrep lanes have no files to check.
  • Unit tests pass: the four release-script harnesses are the relevant suites — test-verify-release-version.sh 18/18, test-rollover-changelog-fragments.sh 11/11, test-concat-changelog-fragments.sh 5/5, test-verify-native-release-artifacts.sh 7/7, plus test-docker-publish-source-binding.sh and test-publication-environment-binding.sh. No C is touched, so meson test -C build is unaffected.
  • If I touched any SIMD/GPU code path, I ran /cross-backend-diff and the worst ULP is ≤ 2 — N/A, no SIMD/GPU code path touched (core/meson.build gains a comment block only).
  • If I touched a feature extractor with SIMD/GPU twins, I either updated every twin or listed the gap under "Known follow-ups" below — N/A, no feature extractor touched.
  • If I added a new .c / .cpp / .cu / .h / .hpp, it has the appropriate license header — N/A, no new source files.
  • If this is a breaking change, the commit message uses ! or BREAKING CHANGE: and the migration path is documented below — not a code-breaking change; the version-line change is documented in ADR-1151 and release.md.
  • If this PR adds an ADR, the ADR row lives in docs/adr/_index_fragments/<NNNN-slug>.md and the slug is appended to docs/adr/_index_fragments/_order.txt — 1151-vmafx-first-release-1-0-0; docs/adr/README.md regenerated with scripts/docs/concat-adr-index.sh --write (--check green).

Bug-status hygiene (ADR-0165)

  • docs/state.md updated in this PR — two rows under Recently closed (T-RELEASE-PIPELINE-1-0-0-LINE-2026-09-03, T-RELEASE-STALE-VERSION-DOCS-2026-09-03) and four under Deferred (waiting on external trigger) for the repo-admin blockers and open policy questions (T-RELEASE-BOT-IDENTITY-2026-09-03, T-RELEASE-PUBLISH-ENVIRONMENTS-MISSING-2026-09-03, T-RELEASE-MASTER-MARKERS-3-2-1-2026-09-03, T-RELEASE-NETFLIX-TAG-NAMESPACE-2026-09-03).

Netflix golden-data gate (ADR-0024)

  • I did not modify any assertAlmostEqual(...) score in the Netflix golden Python tests.
  • If I believe a golden value must change, I have explained why below AND pinged @lusoris for a CODEOWNERS exception — N/A, no golden value changes. This PR in fact makes the golden gate more binding on release PRs (finding 8).

Cross-backend numerical results

Not applicable — no SIMD, GPU, or numeric code path is touched by this PR.

n/a  n/a  n/a  n/a

Deep-dive deliverables (ADR-0108)

  • Research digest — docs/research/1151-release-please-audit-2026-09-02.md (the full 18-finding read-only audit, preserved verbatim as the audit trail).
  • Decision matrix — docs/adr/1151-vmafx-first-release-1-0-0.md ## Alternatives considered (v1.0.0 / continue v3.2.1 / start v4.0.0 / keep -lusoris.N).
  • AGENTS.md invariant note — scripts/release/AGENTS.md gains a "Release-line invariants (ADR-1151)" section: the 1.0.0 line, release-as / bootstrap-sha are one-shot, product version vs ABI SONAME, extra-files is the single marker list with exactly one marker per file, and release-please force-recreates its own branch.
  • Reproducer / smoke-test command — pasted below under "Reproducer".
  • CHANGELOG fragment — changelog.d/fixed/1151-release-please-setup.md; CHANGELOG.md regenerated via scripts/release/concat-changelog-fragments.sh --write (--check green).
  • Rebase note — docs/rebase-notes.md entry fix/release-please-setup — 1.0.0 release line + pipeline repair (2026-09-03). core/meson.build is the only upstream-mirrored file touched (a comment block above vmaf_soname_version, plus the pre-existing release marker on line 2); everything else is fork-only release tooling with no upstream counterpart.

Reproducer

# 1. The maintainer-requested evidence: the cut now computes 1.0.0.
#    --config-file is resolved on the REMOTE branch, so this must target the
#    pushed branch. Read-only: --dry-run never writes to the repo.
npx --yes release-please@latest release-pr --repo-url VMAFx/vmafx \
  --token "$(gh auth token)" --dry-run \
  --target-branch fix/release-please-setup \
  --config-file release-please-config.json \
  --manifest-file .release-please-manifest.json \
  | grep -E 'Setting version|^title:'
# -> ⚠ Setting version for . from release-as configuration
# -> title: chore(fix/release-please-setup): release 1.0.0

# 2. The new fail-closed changelog-cut assertions (18 cases, 11 of them new).
bash scripts/release/tests/test-verify-release-version.sh

# 3. The rollover now retires the one-shot comment keys with their fields.
bash scripts/release/tests/test-rollover-changelog-fragments.sh

# 4. A release PR's manifest-only diff now selects the C build + golden gate.
python3 - <<'PY'
import sys, pathlib, shutil, tempfile
d = tempfile.mkdtemp(); shutil.copy("scripts/ci/plan-ci-impact.py", d + "/planmod.py")
sys.path.insert(0, d); import planmod as m
cfg = m.load_config(pathlib.Path(".github/ci-impact.json")); sel = cfg["selectors"]
for p in [".release-please-manifest.json", "renovate.json"]:
    print(p, "c_core:", m._selector_value("c_core", (p,), sel, {}, set()),
             "golden_harness:", m._selector_value("golden_harness", (p,), sel, {}, set()))
PY
# -> .release-please-manifest.json c_core: True  golden_harness: True
# -> renovate.json                 c_core: False golden_harness: False

# 5. The regression this round fixed: the six process gates became REQUIRED,
#    and two of them are unpassable on a machine-generated release PR.
#    Before the fix (release-please-shaped body, this branch's diff):
#      -> EXIT=1, six '::error title=ADR-0108 missing deliverable' lines
#    and the doc-substance gate maps the release PR's
#    mcp-server/vmaf-mcp/pyproject.toml marker to a mandatory ^docs/mcp/ edit.
#    After: the four authoring-discipline gates consult the new predicate.
bash scripts/ci/tests/test-release-pr-exempt.sh          # 9/9

HEAD_REF=release-please--branches--master--components--vmafx \
  PR_AUTHOR='vmafx-release-bot[bot]' PR_AUTHOR_TYPE=Bot \
  bash scripts/ci/release-pr-exempt.sh                   # exempt=true

HEAD_REF=release-please--branches--master--components--vmafx \
  PR_AUTHOR=someone PR_AUTHOR_TYPE=User \
  bash scripts/ci/release-pr-exempt.sh                   # exempt=false — branch name alone never disarms a gate

# 6. Workflows and renderers.
actionlint .github/workflows/release-please.yml .github/workflows/supply-chain.yml \
  .github/workflows/required-aggregator.yml .github/workflows/rule-enforcement.yml \
  .github/workflows/docker-publish-production.yml \
  .github/workflows/docker-publish-operator-node.yml
bash scripts/release/concat-changelog-fragments.sh --check
bash scripts/docs/concat-adr-index.sh --check

Review round 2 (2026-09-03)

Two defects found reviewing the first push, both fixed at the root cause.

  1. Finding 1 was prose-only. release-please.yml still carried
    token: ${{ secrets.GITHUB_TOKEN }} at both release-please steps and
    contents: write on the job; grep -rn create-github-app-token .github/
    matched nothing. The App-token step is now actually there, SHA-pinned, with
    both gh api probes routed through it and a fail-loud preflight.

  2. The required-array promotion was a regression. Making
    Deep-Dive Deliverables Checklist (ADR-0108) and
    Doc-Substance Gate (ADR-0100 / 0167) required guarantees a red check on
    every release PR — the exact unmergeable-without-admin-bypass outcome this
    PR claims to remove. Proven, not inferred: deliverables-check.sh exits 1
    with all six missing-deliverable errors on a release-please-shaped body (its
    only exemption was a port: title / port/ branch), and the release PR
    updates mcp-server/vmaf-mcp/pyproject.toml, which doc-substance path-maps
    to a mandatory ^docs/mcp/ edit. Two more of the six are latent: the
    docs/state.md gate trips on a closes #N a changelog entry can inherit
    from a commit subject, and the ffmpeg-patch gate is diff-driven over a
    surface list a future marker could intersect.

    All four authoring-discipline gates now call the new
    scripts/ci/release-pr-exempt.sh first and skip their work step on a
    machine-generated release PR — reporting green, not absent, so a genuine
    path-filter skip stays distinguishable. The predicate needs a bot author as
    well as a release-please-- head ref, so nobody disarms a required gate by
    naming a branch. Release Script Contract and ADR Number Collision Guard
    stay armed on release PRs, and the former runs the predicate's own 9-case
    test suite, so the exemption cannot rot unnoticed. Documented in
    docs/development/release.md §"Process gates on the release PR",
    docs/adr/1151-*.md (decision text + six alternatives rows),
    scripts/ci/AGENTS.md (invariants), and an addendum to the research digest.

Rebased onto origin/master (was two commits behind and CONFLICTING;
conflicts were docs/adr/README.md and docs/adr/_index_fragments/_order.txt,
both additive index rows — kept both, in ADR-number order).
concat-adr-index.sh --check and concat-changelog-fragments.sh --check are
green on the rebased tree, and the dry run still reads
title: chore(fix/release-please-setup): release 1.0.0, updates: 11.

Known follow-ups

  • Findings 10, 11, 12, 13, 15 are deliberately unimplemented pending Q2, Q3, Q5, Q7, Q8 above. Finding 1's workflow half is implemented here; only the App/secret creation (repo-admin, Q6) remains.
  • Creating the release-bot App/secrets and the two protected environments are repo-admin actions. Until the environments exist, supply-chain.yml fails closed by design — that is the intended state while the release is deferred.
  • The release itself stays deferred until the fork's trained models are retrained for the Netflix VMAF v1.0.16 default. Nothing here triggers one.
  • This checkout was shallow at the start of the session (git rev-parse --is-shallow-repository → true), which distorts every v3.2.0..HEAD range query; it was unshallowed before the history claims above were made (4,520 commits reachable).

lusoris added a commit that referenced this pull request Sep 2, 2026
The ADR-0334 state.md convention wants the merged PR number, not a
branch name or a placeholder. Backfilled now that gh pr create has
returned the number.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
lusoris added a commit that referenced this pull request Sep 2, 2026
The ADR-0334 state.md convention wants the merged PR number, not a
branch name or a placeholder. Backfilled now that gh pr create has
returned the number.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@lusoris
lusoris force-pushed the fix/release-please-setup branch from 98149a4 to efdd410 Compare September 2, 2026 22:51
@lusoris
lusoris marked this pull request as ready for review September 3, 2026 02:02
lusoris and others added 4 commits September 3, 2026 07:11
…(ADR-1151)

The fork has never released: zero GitHub releases, zero fork-made tags, and
every `vX.Y.Z` tag reachable from master belongs to Netflix upstream history
(`git merge-base --is-ancestor v3.2.0 origin/master` is false). ADR-1127's
"first release is v3.2.1" rested on the 3.2.x manifest baseline, which was a
source-version alignment with Netflix's SONAME, not a release. ADR-1151
supersedes that choice: the first release is v1.0.0 on a fresh number line.

An audit of the pipeline that would have produced that tag found it unable to
release correctly at all. This change repairs it and leaves it idle.

- Retarget the cut: one-shot `release-as: "1.0.0"` on a `0.0.0` manifest, so
  0.0.0 -> 1.0.0 is a monotone forward bump. Both one-shot cutover fields
  carry an inline `_comment_*` explaining that they are deleted by the
  rollover, and the rollover now deletes those comments with them.
- Prove the changelog cut ran. `verify-release-version.sh` (the tag-time
  preflight for all three publication workflows) gains five fail-closed
  assertions: exactly one `## [X.Y.Z] - YYYY-MM-DD` heading, a matching
  `changelog.d/releases/X.Y.Z.json` receipt, zero active fragments, no
  `_pre_fragment_legacy.md`, and no surviving `release-as` / `bootstrap-sha`.
  Nothing checked this before — `concat --check` passes identically before
  and after a cut, so a tag could have published a CHANGELOG whose newest
  section was still `## [Unreleased]` over 1,550 live fragments.
- Stop `release-as` from rotting. The `Release Script Contract (ADR-1128)`
  job fails if either one-shot field survives once the manifest reaches
  1.0.0. It is a persistent, deprecated override applied after commit
  analysis; left in place it pins every future release.
- Stop the release branch being force-rewritten mid-cut. release-please
  recreates `release-please--branches--...` on every push to master, which
  would destroy the hand-added rollover commits. The PR-update invocation is
  now skipped while the PR carries `autorelease: cut`.
- Make the release-critical gates actually required. Six rule-enforcement
  jobs (including Release Script Contract) were reporting but absent from the
  aggregator's `required` array, so a red release-script contract blocked
  nothing. A release PR's manifest-only diff also let every gate resolve
  absent-or-skipped, so the aggregator gains a `release-please--` `mustReport`
  list and both release-please files join the `c_core` selector.
- Require the publication environments to exist. Twelve write-bearing jobs
  name `release-publish`; GitHub auto-creates a referenced environment with
  an empty rule set, so they ran with no approval gate. `supply-chain.yml`
  now queries both environments and fails closed without a required reviewer.
- Make the `latest` container tag tag-aware at all four sites, so a recovery
  dispatch no longer leaves `latest` on a broken digest.
- Hygiene: `pkg/version/version.go` documented a repo-root VERSION file that
  does not exist; `docker/Dockerfile.node` baked a coordinated version marker
  nothing in the release path reads; the SONAME / product-version split and
  the deliberately-independent Cargo / Helm-chart / root-pyproject versions
  now carry ADR-citing comments at the source.

Docs: `docs/development/release.md` rewritten (1.0.0 first cut, product
version vs `libvmaf.so.3` SONAME, corrected 34-entry required-check
inventory, the `autorelease: cut` freeze procedure, the release-bot and
environment prerequisites). Research digest, rebase note, state.md rows and a
changelog fragment ship with it.

Does not tag, publish, merge, or trigger any release workflow.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The ADR-0334 state.md convention wants the merged PR number, not a
branch name or a placeholder. Backfilled now that gh pr create has
returned the number.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…lease PR

Two repairs on top of the ADR-1151 pipeline work.

1. Finding 1 was documented but not implemented. `release-please.yml` still
   passed `secrets.GITHUB_TOKEN` to both release-please invocations and held
   `contents: write`. GitHub suppresses follow-on workflow events for anything
   that token creates, so release PRs received zero check runs and the sole
   required context could never report. The job now mints a scoped installation
   token with SHA-pinned `actions/create-github-app-token` v3.2.0 and routes
   both release-please steps and both read-only `gh api` probes through it; the
   job's own token drops to `contents: read`. A preflight step fails with a
   remediation message when `RELEASE_BOT_APP_ID` or `RELEASE_BOT_PRIVATE_KEY`
   is absent, so the workflow can never silently fall back.

2. Promoting the six rule-enforcement gates into the aggregator's `required`
   array made two of them permanently red on a release PR — the same
   unmergeable-without-admin-bypass outcome the change exists to remove.
   Reproduced: `deliverables-check.sh` exits 1 with six missing-deliverable
   errors on a release-please-shaped body (its only exemption was `port:`),
   and the doc-substance gate path-maps the `mcp-server/vmaf-mcp/pyproject.toml`
   version marker to a mandatory `docs/mcp/` edit a release PR never has. The
   four authoring-discipline gates now consult the new
   `scripts/ci/release-pr-exempt.sh` and skip their work step on a
   machine-generated release PR, reporting green rather than absent. The
   predicate requires a bot author as well as a `release-please--` head ref, so
   branch naming alone cannot disarm a required gate. `Release Script Contract`
   and `ADR Number Collision Guard` stay armed there, and the former runs the
   predicate's own 9-case test suite.

Docs: `docs/development/release.md` gains "Release-bot identity" (the one-time
App setup) and "Process gates on the release PR" (which four stand down, why,
and the local dry-run command); ADR-1151 gains the decision text plus six
alternatives rows; the research digest gains a review-round addendum recording
both the unimplemented half and the regression; `scripts/ci/AGENTS.md` records
the predicate's invariants.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The ADR-1151 explanatory comment spelled out the literal
x-release-please-version token while explaining that the packaging version
deliberately does not carry it. verify-release-version.sh and
rollover-changelog-fragments.sh both count marker occurrences per file, so
the comment itself registered as a second marker and would have hard-failed
every release preflight — the exact failure the comment warned about.

Reworded so the file carries exactly one marker, on appVersion.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@lusoris
lusoris force-pushed the fix/release-please-setup branch from 64150bb to 8fd1f7a Compare September 3, 2026 05:14
@lusoris
lusoris merged commit b0d307d into master Sep 3, 2026
80 checks passed
@lusoris
lusoris deleted the fix/release-please-setup branch September 3, 2026 05:43
@lusoris lusoris mentioned this pull request Sep 3, 2026
5 of 7 tasks
@lusoris lusoris added this to the 1.0.0 — First release milestone Sep 4, 2026
@lusoris lusoris added the type:bug Something isn't working label Sep 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type:bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant