Skip to content

[Release] Establish changelog-driven fleet release pipeline #1552

Description

@seonghobae

Buyer / operator problem

ContextualWisdomLab repositories use a mix of Git Flow (develop as the default integration branch) and GitHub Flow (main/master as the default branch). Many repositories have CI/security/package gates and CHANGELOG.md, but there is no single protected-main organization contract that turns an eligible CHANGELOG.md release section into an immutable software release across the fleet. Repository-local implementations must not drift into dozens of unrelated release engines.

Authority and DDD boundary

.github owns the generic release-governance and reusable GitHub Actions delivery boundary. Product repositories own product version files, packaging/build commands, migration compatibility, buyer acceptance and the decision that a given integrated head is release-eligible. Do not move product/domain behavior into .github and do not duplicate generic changelog parsing/tag/release logic in every product.

If a repository has a product-specific publisher (PyPI/npm/crates.io/GHCR/desktop updater/etc.), keep that adapter in the owning product or an already-existing canonical publishing library; the central workflow should invoke a versioned published contract rather than copy it.

Required branch strategies

GitHub Flow

When the repository default branch is main or master:

  • only the exact protected default-branch integrated head may release;
  • all applicable current-head CI/security/SAST/coverage/package/SBOM/provenance/review gates must be terminal-success before release authority is granted;
  • the top release section in CHANGELOG.md is the release-notes and version source;
  • no release is emitted while only Unreleased exists.

Git Flow

When the repository default branch is develop:

  • development remains on protected develop;
  • production release must be represented by a reviewed integration/release PR from develop (or a bounded release branch cut from exact develop) into the repository's production branch main or master;
  • the release tag/GitHub Release is created only from the exact protected production-branch integrated head after all applicable release gates succeed there;
  • do not tag develop as the production release merely because it is the default branch;
  • if main/master is missing, stale, unprotected, or not an admissible production branch, fail closed and open/maintain a repository-scoped branch-governance gap instead of inventing a new convention.

Non-standard defaults

Repositories whose default branch is not develop, main, or master (developmental, release/*, version branch, gh-pages, etc.) must not be auto-released until an ADR/repository contract proves the branch model and a migration or explicit exception is accepted.

CHANGELOG.md contract

Adopt a strict, executable Keep-a-Changelog/SemVer-compatible release contract:

  • ## [Unreleased] may exist but is never released;
  • the next releasable section must carry one immutable SemVer version and release date;
  • release notes must be non-empty and customer/developer meaningful;
  • version must be newer than the highest compatible release tag;
  • a tag that already exists with a different target fails closed;
  • package/application version declarations must agree with the changelog release version when the repository has such declarations;
  • release note extraction must be deterministic and covered by tests;
  • no heuristic version bumping from commit messages is authoritative when it conflicts with CHANGELOG.md.

Central reusable release workflow

Create a reusable workflow and executable validator owned here, with tests and doctoring, that at minimum:

  1. resolves and verifies the exact caller repository/ref/SHA;
  2. classifies Git Flow vs GitHub Flow from live repository metadata and declared release contract;
  3. validates branch protection/release eligibility fail-closed;
  4. parses CHANGELOG.md deterministically;
  5. validates version coherence and existing tags/releases;
  6. consumes repository-provided build/package evidence rather than rebuilding with guessed commands;
  7. creates an immutable vX.Y.Z tag and GitHub Release from the same exact integrated SHA;
  8. records source SHA, tree SHA, changelog section hash, artifact digests, SBOM/provenance references and workflow-source SHA in a release receipt;
  9. supports dry-run/audit mode and idempotent retries;
  10. never force-pushes, rewrites tags, self-approves, weakens rulesets, fabricates check success, or treats queued/skipped/neutral/cancelled evidence as passing.

Prefer GitHub OIDC/trusted publishing for package registries where supported. Keep registry credentials and provider-specific publication outside the generic domain unless .github is already the canonical owner.

Fleet adoption

After the reusable contract lands:

  • inventory every non-archived ContextualWisdomLab repository;
  • classify software/package/service vs docs/site/data/reference-only repositories;
  • for software repositories, record default branch strategy, production branch, CHANGELOG.md, current version, release workflows, package/publish surfaces, latest release/tag, and blockers;
  • add only a thin caller/release contract to repositories lacking a pipeline;
  • migrate duplicated generic release logic to the central reusable contract;
  • update docs/product-technical-gap-baseline.md in each owning repository with release status and branch-model gaps;
  • release only repositories whose exact integrated head is actually release-ready; do not manufacture releases to satisfy an inventory count.

DDD fitness during adoption

Every release-gap repair must inspect repository/package/module paths against the owning bounded context. If release work exposes generic dumping paths, foreign product code copied locally, outdated product names, cross-context database access, or a locally reimplemented library that has a canonical CWL owner, repair/migrate that responsibility in the same bounded slice when safe; otherwise record the owner/callers/migration sequence and acceptance evidence in the product gap baseline.

Acceptance evidence

  • central validator/reusable workflow: 100% production statement/branch coverage and public docstrings;
  • deterministic fixture coverage for GitHub Flow, Git Flow, existing-tag idempotency, malformed/future/duplicate changelog versions, stale/unprotected production branch, non-standard default branch, mismatched package version, and missing/queued/failed release gates;
  • action dependencies pinned immutably;
  • shell/YAML/Python/Rust as applicable pass static analysis;
  • no release write from PR/fork-untrusted code;
  • at least one real GitHub-Flow repository and one real Git-Flow repository complete a changelog-driven release through the new contract before the fleet capability is considered proven;
  • every actual release receipt is bound to one exact source SHA and artifact digest set.

First follow-up inventory

A live 2026-09-01 organization read shows both default-branch families plus non-standard exceptions. Start with the lowest-risk, independently release-ready libraries/services that already have clean packaging and release evidence; do not start with repositories carrying large unresolved PR/release backlogs merely to demonstrate the pipeline.

This issue is the canonical central owner for the generic release-pipeline capability. Product-specific readiness remains owned by each repository.

Activity

  1. seonghobae commented on Sep 1, 2026

    @seonghobae
    ContributorAuthor

    2026-09-01 live fleet evidence

    The first audit pass confirms that release adoption must distinguish missing pipeline, pipeline present but blocked, and branch-governance migration rather than creating tags indiscriminately.

    • EgressWeave — GitHub Flow (main): repository already has a substantial product-specific release.yml and CHANGELOG.md/SemVer contract. GitHub Releases is still empty, while the protected main package version remains 0.3.0 and substantial later work is still in Unreleased. There are currently 27 open PRs, including multiple Draft release-evidence/supply-chain hardening PRs. Do not manufacture a 0.3.0 tag from current source and do not cut 0.4.0 until the release-hardening chain and exact release gates are accepted. This is pipeline-present / release-not-ready, not a missing-pipeline repository.

    • ThreadWeave — GitHub Flow (main): repository-side 0.2.0 release readiness is integrated. Issue Stop OpenCode bridge waiting after requested changes #17 records the sole remaining prerequisite as external GitHub pypi environment protection plus the PyPI Trusted Publisher identity; the repository explicitly forbids long-lived-token/manual-upload bypass. This is pipeline-present / external-publisher-identity-blocked. Central adoption must surface this class without trying to solve account authority in source.

    • context-graph-contracts — Git Flow migration (develop default): live repository metadata still reports develop as default while the accepted program integration/release target is protected main; current stacked PRs explicitly cite .github#1137 as owner of that administrative transition. Treat this as a concrete Git Flow proof case only after the production branch is protected and the dependency stack integrates. Never tag develop simply because it is the default branch.

    • Non-standard defaults observed in the organization include developmental, release/v3.8.50, v8, and gh-pages; keep them fail-closed until each repository has an explicit branch/release ADR.

    Implementation priority remains: central deterministic changelog/branch eligibility validator + reusable release workflow first; thin repository callers second; first proof releases only from repositories that are independently release-ready. Product-specific PyPI/npm/crates/GHCR publishers stay in their owning repositories/adapters and consume the central eligibility/receipt contract rather than being copied into .github.

  2. seonghobae commented on Sep 1, 2026

    @seonghobae
    ContributorAuthor

    PyPI credential policy correction — 2026-09-01

    The organization already has approved PyPI publishing credentials in GitHub Secrets under the exact existing names PIPY_TOKEN and PIPY_USERNAME. Do not read or expose their values.

    Update the fleet release contract accordingly:

    • PyPI Trusted Publishing/OIDC remains the preferred credential-minimizing path, but its absence is not a release blocker when the approved PIPY_USERNAME + PIPY_TOKEN pair is available to the protected release job.
    • The central release contract must support provider-specific publisher adapters declaring trusted_publishing or github_secret_credentials explicitly. Do not guess/fallback after irreversible side effects.
    • For github_secret_credentials, inject only through GitHub Actions secrets; never print, persist, include in receipts/provenance/cache keys/job outputs, or expose in command diagnostics.
    • Validate the selected publisher mode before tag/GitHub Release/package-publish side effects. If neither approved mode is available, fail closed.
    • Keep exact-head CI/security/coverage/review/changelog/version/digest/SBOM/provenance requirements unchanged. Existing credentials authorize publication; they do not waive release-readiness gates.
    • Product-specific PyPI adapters may use the exact existing PIPY_* secret names until an accepted migration replaces them. Do not rename the organization secrets merely to correct the historical spelling.

    ThreadWeave issue #17 has been corrected to this dual-mode contract and should no longer wait solely for external Trusted Publisher configuration.

  3. seonghobae commented on Sep 1, 2026

    @seonghobae
    ContributorAuthor

    2026-09-01 registry credential update — crates.io

    CARGO_REGISTRY_TOKEN is now registered in ContextualWisdomLab GitHub Secrets and is an approved crates.io publisher credential. Treat its value as non-observable secret material: never print, echo, persist, hash into receipts/provenance, expose through shell tracing, cache keys, job outputs, model jobs, build/test jobs, or diagnostics.

    Fleet release adoption must now classify Rust packages explicitly:

    • public crate: owning repository/ADR and Cargo.toml permit public registry distribution; version must agree with the dated CHANGELOG.md release and immutable tag; require cargo package --locked / cargo publish --dry-run --locked, full test/clippy/doc/security/license/SBOM/provenance gates, then isolate CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} to the final cargo publish --locked publisher job;
    • non-publishable package: publish = false, fuzz crate, Tauri/application-only crate, internal workspace support crate, example/bench, or package without an accepted public-crate contract. Record this as an intentional distribution boundary, not a failed release;
    • before any first publication, verify crates.io name availability/ownership and dependency-aware workspace publish order. Published versions are immutable; never overwrite. Do not use --no-verify as the normal release path;
    • crates.io Trusted Publishing may be adopted where account ownership permits. If a crate is configured Trusted-Publishing-only, use that declared mode instead of the API token. Do not silently switch modes after a failed publication attempt.

    Current org search found no existing workflow reference to CARGO_REGISTRY_TOKEN; therefore the first Rust adopter must add the contract deliberately rather than assuming legacy behavior. Existing examples such as Wardnet fuzz packages and DiskSage Tauri application packages with publish = false are intentionally excluded from crates.io publication.

  4. seonghobae commented on Sep 1, 2026

    @seonghobae
    ContributorAuthor

    Concrete first-consumer handoff from ThreadWeave PR #35 (current release branch work, 2026-09-01):

    • PyPI API-token publication is an approved publisher mode. PIPY_TOKEN is materialized only in the fully SHA-pinned pypa/gh-action-pypi-publish job; PIPY_USERNAME is intentionally not injected because API-token publication uses __token__.
    • Release runs are repository-serialized (concurrency independent of source SHA) and the credential-minimal readiness stage is the release-authority linearization point. It binds one exact protected integrated source SHA, verifies the current protected production ref at that point, verifies exact integrated checks plus the exact authorizing PR/check identity, resolves version/changelog/public-registry state, and only then permits build/attestation/tag/release/publish.
    • Do not claim a later branch read plus tag push is an atomic cross-ref CAS. GitHub Flow may advance after the serialized readiness decision; the already-authorized version/source pair remains immutable. A later candidate for the same already-public version is an idempotent no-op.
    • Before irreversible release side effects, compare any existing public registry filenames/digests with the reviewed bundle; publish only matching missing distributions; unexpected files or digest mismatch fail closed. Public completion requires registry digest equality plus clean-install smoke.
    • Rust: CARGO_REGISTRY_TOKEN is available, but token availability must not turn internal crates into public products. fast-mlsirm owner work (fix(noema-review): bound both jobs to a job-level timeout-minutes #1715 / docs: add reusable README template and evidence standard #1694 / ADR-0027) explicitly classifies mlsirm-core as an internal Maturin/PyPI numerical core and adds publish = false; therefore it is a negative fixture for the central crates.io classifier, not a publish candidate. Require an explicit accepted public-crate contract before enabling cargo publish.

    Please incorporate these as executable fixtures/contract tests in the #1552 central reusable release implementation rather than duplicating ThreadWeave-specific workflow code.

  5. seonghobae commented on Sep 1, 2026

    @seonghobae
    ContributorAuthor

    Quarantine Runtime Git-Flow release-governance canary (fresh 2026-09-01): ContextualWisdomLab/quarantine-sandbox-runtime default/integration branch is develop; production main exists at 6d27dba3d5b013b922e21757431330d048a91d70 but live branch metadata reports protected=false. Stacked release work quarantine-sandbox-runtime#10 correctly fail-closes its tag-driven preflight by requiring the tagged SHA to equal origin/main and branches/main.protected == true; therefore the first commercial release cannot be authorized while main remains unprotected. This is exactly the Git-Flow branch-governance gap described by #1552, not a reason to tag develop or weaken release preflight. RED: default=develop, candidate production=main, main.protected=false, release preflight necessarily rejects. GREEN: establish the repository’s production-branch protection/ruleset for main to the release-grade standard while preserving protected develop integration; then a reviewed develop/release→main integration can produce one exact protected-main SHA and the product-owned release gate can bind version/CHANGELOG/package/SBOM/provenance/hostile-runtime evidence to it. Do not change quarantine source from the central writer; advance only the generic/repository-governance surface owned here.

  6. seonghobae commented on Sep 1, 2026

    @seonghobae
    ContributorAuthor

    Quarantine Git-Flow release-governance handoff — fresh 2026-09-02 KST evidence.

    ContextualWisdomLab/quarantine-sandbox-runtime currently reports default/protected integration develop@60a85c7633e03b425b67159ec6822c8178cf87ea. Production branch main exists only at initial commit 6d27dba3d5b013b922e21757431330d048a91d70 and GitHub reports protected=false / protection disabled. The product release slice quarantine-sandbox-runtime#10 is Draft and its tag-driven .github/workflows/release.yml correctly fails closed unless the tagged exact SHA equals protected main; its hostile-runtime release gate additionally requires the dedicated rootless Podman + SELinux runner.

    This matches #1552's Git-Flow branch-governance gap class: do not tag develop, do not weaken the release preflight, and do not treat the stale unprotected main as production authority.

    RED: a release-eligible exact develop integration cannot be promoted into an admissible production branch because current main is stale and unprotected. GREEN for the central owner path: before the first release candidate, establish protected main as the production branch under the repository's live deterministic/security/review policy; require a reviewed exact-develop -> main release integration (or the centrally approved bounded release branch model); after integration, run release gates on the exact protected main SHA and only then create the immutable tag/GitHub Release. Preserve develop as the integration default unless a separate accepted branch migration changes that contract. Product-specific readiness and hostile-runner evidence remain owned by the quarantine writer.

  7. seonghobae commented on Sep 20, 2026

    @seonghobae
    ContributorAuthor

    Fresh Git Flow consumer evidence from ContextualWisdomLab/quarantine-sandbox-runtime (2026-09-20): repository metadata still reports default_branch=develop; develop@60a85c7633e03b425b67159ec6822c8178cf87ea is protected, while main@6d27dba3d5b013b922e21757431330d048a91d70 is unprotected. The two refs currently resolve to the same tree, but main is not admissible release authority.

    The repository’s Draft release owner #10 is currently test-only bb587667e049c900f0dec578f083b35ed50f21c7. Its witness was being hardened toward “release from the live default branch,” while production .github/workflows/release.yml is still hard-coded to develop. That direction conflicts with this central issue’s Git Flow contract: develop is development integration; production tag/GitHub Release must come from an exact protected production main/master integrated head. I opened product-scoped quarantine-sandbox-runtime#140 and am keeping #10 Draft/RED rather than implementing the wrong local GREEN.

    Fresh QSR ruleset inventory currently exposes only organization branch ruleset 18156473 (CWL Central required workflows, target branch). This does not establish release/tag immutability. GitHub’s current immutable-release control is separate from branch/tag rulesets and, when enabled, locks the published release assets and associated tag while producing release attestation. The available QSR repository connector cannot read the authenticated immutable-release setting endpoint, so enablement is recorded as unverified rather than guessed.

    Please include QSR as a concrete Git Flow adoption fixture for #1552: classify develop + unprotected main as fail-closed, establish/release the central production-branch contract, protect/admit the production branch before use, then let QSR replace duplicated local branch classification with a thin released-contract caller while retaining product-specific Rust package/SBOM/provenance/SELinux evidence. No tag from develop, no mutable central source consumption, and no predecessor status transfer.

  8. seonghobae commented on Sep 20, 2026

    @seonghobae
    ContributorAuthor

    QSR consumer evidence update: central release implementation has now surfaced as Draft PR #2260 at exact dccacc77cd7be310d443b126ea98aec19216c816, and it currently contradicts this issue's Git Flow rule.

    #2260's reusable release-tag.yml requires dispatch on github.event.repository.default_branch and only proves release_commit ancestry to that default-branch dispatch SHA. ADR-0032/tests intentionally preserve that default-branch provenance model; package doctoring likewise calls control_plane_commit the protected default-branch SHA. For quarantine-sandbox-runtime, live authority remains default/protected develop with unprotected main, so adopting #2260 as written could authorize a release from develop lineage instead of failing closed on missing protected production authority.

    I recorded the exact-head finding on #2260 as review 5259865339. Required central RED is a Git Flow consumer fixture (default=develop, production=main`) where develop-only ancestry is rejected; GREEN must distinguish dispatch/control-plane authority from production-release authority and prove the release source exact SHA is on the protected production branch lineage. GitHub Flow repos where default==production should continue to work. Missing/unprotected production authority must remain fail closed.

    QSR issue #140 now tracks #2260 as the implementation candidate but will not consume the mutable head or copy classification logic locally. #2260 also remains independently blocked by #2261 and existing CO/Noema owner prerequisites.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions