Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
44b540e
docs(automations): record Coven Automations v1 program status (coven#…
CompleteDotTech Aug 30, 2026
fb302f2
docs: plan cryptographic fresh-user and biometric assurance proofs (c…
CompleteDotTech Aug 30, 2026
fcc026c
docs: operationalize Coven Automations v1 tracker roadmap and drift c…
CompleteDotTech Aug 30, 2026
957715f
docs: consolidate Coven security policy, threat boundary, and support…
CompleteDotTech Aug 30, 2026
9847960
docs(pairing): plan TUI QR bootstrap and E2EE mobile pairing (refs #7…
CompleteDotTech Aug 30, 2026
d07bf61
docs: remove duplicate local public documentation (#870)
CompleteDotTech Aug 30, 2026
a781c9e
docs(807): record shipped reliability scorecard status decision (#863)
CompleteDotTech Aug 30, 2026
c703693
docs: record issue 670 docs program status on main
CompleteDotTech Aug 30, 2026
753f4bb
fix(agents): enforce target input-guardrail parity across handoffs
CompleteDotTech Aug 30, 2026
ba3a154
docs(cli): document the deterministic JSON help contract (#868)
CompleteDotTech Aug 30, 2026
c4c9ccb
docs(automations): specify coven.automations.v1 schemas, state machin…
CompleteDotTech Aug 30, 2026
39feb6d
refactor: extract route/version authority gate from coven-cli api
CompleteDotTech Aug 30, 2026
9329a67
feat(release): add exact-commit required-checks gate and manifest
CompleteDotTech Aug 30, 2026
a0681dd
feat(release): gate npm and GitHub release publication on the exact c…
CompleteDotTech Aug 30, 2026
9af5915
docs(release): add release governance and shipped-truth attestation spec
CompleteDotTech Aug 30, 2026
7791f8f
test(release): expect the exact-source gate in the publish needs cont…
CompleteDotTech Aug 30, 2026
9e0136b
feat(release): bind exact-commit gate evidence to the selected run at…
CompleteDotTech Aug 31, 2026
c98e621
feat(release): require complete manifest coverage and separate pr/rel…
CompleteDotTech Aug 31, 2026
9876727
feat(release): serialize npm publication with one stable-channel lock
CompleteDotTech Aug 31, 2026
efa6674
feat(release): revalidate tag and exact checks immediately before npm…
CompleteDotTech Aug 31, 2026
4bd6448
feat(release): split read-only release verification from the write-en…
CompleteDotTech Aug 31, 2026
f9336bb
feat(release): persist the tag object and gate receipt as a checksumm…
CompleteDotTech Aug 31, 2026
523d7a8
docs(release): specify tag rulesets, audited bypass, and corrected pi…
CompleteDotTech Aug 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ jobs:
- run: python3 scripts/check-workflows-test.py
- run: python3 scripts/check-ci-workflow-test.py
- run: node --test scripts/package-github-release-test.mjs
- run: node --test scripts/verify-release-commit-gate-test.mjs
- run: node --test scripts/release-stress-test.mjs
- run: scripts/check-workflows.sh

Expand Down
149 changes: 140 additions & 9 deletions .github/workflows/release-github.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,37 @@ on:
permissions:
contents: read

# One stable-channel lock for ALL tags: the complete npm-to-GitHub release
# transaction is serialized (the npm pipeline holds its own stable-channel
# lock first), so GitHub Release publications for different versions can
# never interleave either. Pending runs queue without cancelling.
concurrency:
group: release-github-${{ github.event.workflow_run.head_branch || inputs.release_tag }}
group: release-github-stable-channel
cancel-in-progress: false

env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

jobs:
publish-release:
name: Publish GitHub Release assets
# Read-only verification and packaging: proves the source run, the signed
# tag, the exact-commit required checks, npm provenance and registry
# signatures, then packages deterministic assets — all with contents: read
# and no persisted git credentials. Nothing here can mutate a release.
verify-and-package:
name: Verify source and package release assets
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest
permissions:
actions: read
contents: write
checks: read
contents: read
outputs:
release_tag: ${{ steps.source.outputs.release_tag }}
npm_version: ${{ steps.source.outputs.npm_version }}
head_sha: ${{ steps.source.outputs.head_sha }}
tag_object_sha: ${{ steps.source.outputs.tag_object_sha }}
source_run_id: ${{ steps.source.outputs.source_run_id }}
source_run_attempt: ${{ steps.source.outputs.source_run_attempt }}
steps:
- name: Refuse non-default-branch manual recovery
if: ${{ github.event_name == 'workflow_dispatch' && github.ref_name != github.event.repository.default_branch }}
Expand All @@ -51,6 +67,8 @@ jobs:
with:
fetch-depth: 0
ref: ${{ github.event.repository.default_branch }}
# Verification is read-only; no git credentials may outlive checkout.
persist-credentials: false
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v6.0.0
with:
node-version: 24
Expand All @@ -68,6 +86,34 @@ jobs:
--release-tag "$RELEASE_TAG" \
--source-run-id "$SOURCE_RUN_ID" \
--source-run-attempt "$SOURCE_RUN_ATTEMPT" >> "$GITHUB_OUTPUT"
# Policy-as-data: the required-checks manifest is digested from the
# verified source SHA itself (git show <release sha>:...), not from the
# default-branch working tree, so what the release must prove is exactly
# what the released source declared — never a newer or older policy.
- name: Digest required-checks manifest from the verified source commit
env:
HEAD_SHA: ${{ steps.source.outputs.head_sha }}
run: |
set -euo pipefail
git show "$HEAD_SHA:scripts/release-required-checks.json" > verified-release-required-checks.json
- name: Verify exact source commit required checks
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
node scripts/verify-release-commit-gate.mjs verify \
--repository "$GITHUB_REPOSITORY" \
--commit-sha "${{ steps.source.outputs.head_sha }}" \
--manifest verified-release-required-checks.json \
--release-tag "${{ steps.source.outputs.release_tag }}" \
--tag-object-sha "${{ steps.source.outputs.tag_object_sha }}" \
--receipt-output release-commit-gate-receipt.json
- name: Upload exact source acceptance receipt
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.0
with:
name: coven-release-commit-gate-${{ steps.source.outputs.release_tag }}
path: release-commit-gate-receipt.json
if-no-files-found: error
- name: Verify npm provenance for every published package
env:
RELEASE_TAG: ${{ steps.source.outputs.release_tag }}
Expand Down Expand Up @@ -132,6 +178,16 @@ jobs:
--release-tag "$RELEASE_TAG" \
--source-run-id "$SOURCE_RUN_ID" \
--source-run-attempt "$SOURCE_RUN_ATTEMPT"
# Persist what authorized this release: the GitHub-verified annotated tag
# object, bundled with the exact-commit gate receipt into a checksummed
# release asset (coven-v<version>-release-evidence.json).
- name: Fetch verified tag object payload
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG_OBJECT_SHA: ${{ steps.source.outputs.tag_object_sha }}
run: |
set -euo pipefail
gh api "/repos/$GITHUB_REPOSITORY/git/tags/$TAG_OBJECT_SHA" > verified-release-tag-object.json
- name: Package deterministic GitHub release assets
env:
RELEASE_TAG: ${{ steps.source.outputs.release_tag }}
Expand All @@ -142,18 +198,93 @@ jobs:
--release-tag "$RELEASE_TAG" \
--artifacts-dir github-release-source \
--output-dir github-release-assets \
--source-date-epoch "$SOURCE_DATE_EPOCH"
--source-date-epoch "$SOURCE_DATE_EPOCH" \
--gate-receipt release-commit-gate-receipt.json \
--tag-object verified-release-tag-object.json
- name: Upload packaged release assets for the publication job
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.0
with:
name: coven-release-assets-${{ steps.source.outputs.release_tag }}
path: github-release-assets
if-no-files-found: error

# Minimal write-enabled publication: the only mutating step in this workflow
# is the release create/upload here. It re-derives the verified context,
# re-validates the exact-commit gate and the remote signed tag immediately
# before the mutation, and then synchronizes the already-packaged,
# already-verified assets.
publish-release:
name: Publish GitHub Release
needs: [verify-and-package]
runs-on: ubuntu-latest
permissions:
actions: read
checks: read
contents: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6.0.0
with:
fetch-depth: 0
ref: ${{ github.event.repository.default_branch }}
# The publication job only runs verifier scripts and gh release
# calls; no git credentials may outlive checkout.
persist-credentials: false
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v6.0.0
with:
node-version: 24
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.0
with:
name: coven-release-assets-${{ needs.verify-and-package.outputs.release_tag }}
path: github-release-assets
- name: Digest required-checks manifest from the verified source commit
env:
HEAD_SHA: ${{ needs.verify-and-package.outputs.head_sha }}
run: |
set -euo pipefail
git show "$HEAD_SHA:scripts/release-required-checks.json" > verified-release-required-checks.json
# Point-of-mutation revalidation: everything verified in the read-only
# job could have changed while packaging ran. Immediately before the
# release mutation, re-decide the exact-commit gate on fresh check
# evidence, re-read the remote signed tag, and reconfirm the source run
# attempt, so a stale authorization can never be spent.
- name: Revalidate exact checks and signed tag immediately before publication
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_TAG: ${{ needs.verify-and-package.outputs.release_tag }}
HEAD_SHA: ${{ needs.verify-and-package.outputs.head_sha }}
TAG_OBJECT_SHA: ${{ needs.verify-and-package.outputs.tag_object_sha }}
SOURCE_RUN_ID: ${{ needs.verify-and-package.outputs.source_run_id }}
SOURCE_RUN_ATTEMPT: ${{ needs.verify-and-package.outputs.source_run_attempt }}
run: |
set -euo pipefail
node scripts/verify-release-commit-gate.mjs verify \
--repository "$GITHUB_REPOSITORY" \
--commit-sha "$HEAD_SHA" \
--manifest verified-release-required-checks.json \
--release-tag "$RELEASE_TAG" \
--tag-object-sha "$TAG_OBJECT_SHA"
node scripts/package-github-release.mjs revalidate-tag \
--repository "$GITHUB_REPOSITORY" \
--release-tag "$RELEASE_TAG" \
--expected-tag-object-sha "$TAG_OBJECT_SHA" \
--expected-head-sha "$HEAD_SHA"
node scripts/package-github-release.mjs verify-source-run-attempt \
--repository "$GITHUB_REPOSITORY" \
--release-tag "$RELEASE_TAG" \
--source-run-id "$SOURCE_RUN_ID" \
--source-run-attempt "$SOURCE_RUN_ATTEMPT"
- name: Synchronize canonical GitHub release assets
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_TAG: ${{ steps.source.outputs.release_tag }}
TAG_OBJECT_SHA: ${{ steps.source.outputs.tag_object_sha }}
HEAD_SHA: ${{ steps.source.outputs.head_sha }}
RELEASE_TAG: ${{ needs.verify-and-package.outputs.release_tag }}
TAG_OBJECT_SHA: ${{ needs.verify-and-package.outputs.tag_object_sha }}
HEAD_SHA: ${{ needs.verify-and-package.outputs.head_sha }}
run: |
set -euo pipefail
node scripts/package-github-release.mjs sync-release \
--repository "$GITHUB_REPOSITORY" \
--release-tag "$RELEASE_TAG" \
--output-dir github-release-assets \
--expected-tag-object-sha "$TAG_OBJECT_SHA" \
--expected-head-sha "$HEAD_SHA"
--expected-head-sha "$HEAD_SHA" \
--include-release-evidence
86 changes: 81 additions & 5 deletions .github/workflows/release-npm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,13 @@ on:
permissions:
contents: read

# Never run two release pipelines for the same tag at the same time. We don't cancel an in-flight
# release because that could leave the registry in a partially-published state.
# One stable-channel lock for ALL tags: npm publication mutates the shared
# `latest` dist-tag, so two stable versions must never interleave their
# package publications. Pending runs for newer tags queue behind an in-flight
# release without cancelling it (a cancelled release could leave the registry
# in a partially-published state).
concurrency:
group: release-npm-${{ github.ref }}
group: release-npm-stable-channel
cancel-in-progress: false

env:
Expand Down Expand Up @@ -57,6 +60,8 @@ jobs:
npm_version: ${{ steps.release-context.outputs.npm_version }}
native_package_set: post-intel
platform_matrix: ${{ steps.platform-matrix.outputs.platform_matrix }}
head_sha: ${{ steps.tag-target.outputs.tagged_commit_sha }}
tag_object_sha: ${{ steps.tag-target.outputs.tag_object_sha }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6.0.0
with:
Expand All @@ -65,6 +70,7 @@ jobs:
id: release-context
run: node scripts/release-npm-context.mjs "$GITHUB_REF_NAME" >> "$GITHUB_OUTPUT"
- name: Confirm tag is annotated and signer-authorized
id: tag-target
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_RELEASE_ALLOWED_SIGNERS: ${{ vars.NPM_RELEASE_ALLOWED_SIGNERS }}
Expand Down Expand Up @@ -117,6 +123,8 @@ jobs:
echo "::error::Refusing to release: local tag $TAG_NAME resolves to $LOCAL_TAGGED_COMMIT_SHA, not the GitHub-verified commit $TAGGED_COMMIT_SHA."
exit 1
fi
echo "tagged_commit_sha=$TAGGED_COMMIT_SHA" >> "$GITHUB_OUTPUT"
echo "tag_object_sha=$TAG_OBJECT_SHA" >> "$GITHUB_OUTPUT"
allowed_signers_raw="${NPM_RELEASE_ALLOWED_SIGNERS:-}"
if [ -z "${allowed_signers_raw//[[:space:]]/}" ]; then
echo "::error::Repository variable NPM_RELEASE_ALLOWED_SIGNERS must contain one or more SSH allowed-signers lines."
Expand Down Expand Up @@ -151,6 +159,42 @@ jobs:
NATIVE_PACKAGE_SET: post-intel
run: node scripts/release-npm-platform-matrix.mjs "$NATIVE_PACKAGE_SET" >> "$GITHUB_OUTPUT"

# Exact source acceptance (issue #805): publication may not proceed until the
# CI run for the *exact* tagged commit completed successfully and every
# required check for that SHA reported success. A green ancestor commit, an
# unrelated workflow, a missing/stale/cancelled run, or a skipped required
# check all refuse here, before any registry mutation.
exact-source-gate:
name: Verify exact source commit checks
needs: [verify-tag]
runs-on: ubuntu-latest
permissions:
contents: read
actions: read
checks: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6.0.0
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v6.0.0
with:
node-version: 24
- name: Require every required check to have succeeded on the tagged commit
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
node scripts/verify-release-commit-gate.mjs verify \
--repository "$GITHUB_REPOSITORY" \
--commit-sha "${{ needs.verify-tag.outputs.head_sha }}" \
--manifest scripts/release-required-checks.json \
--release-tag "${{ needs.verify-tag.outputs.release_tag }}" \
--receipt-output release-commit-gate-receipt.json
- name: Upload exact source acceptance receipt
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.0
with:
name: coven-release-commit-gate-${{ needs.verify-tag.outputs.release_tag }}
path: release-commit-gate-receipt.json
if-no-files-found: error

performance-baseline:
name: Release performance baseline
runs-on: ubuntu-latest
Expand Down Expand Up @@ -287,15 +331,23 @@ jobs:
npm-publish:
name: npm publish
runs-on: ubuntu-latest
needs: [build-platform, npm-dry-run, performance-baseline, verify-tag]
needs: [build-platform, npm-dry-run, performance-baseline, verify-tag, exact-source-gate]
# OIDC trusted publishing: id-token:write lets the job mint a short-lived OIDC token
# that `npm publish --provenance` exchanges with the npm registry for a one-shot
# publish credential. No NPM_TOKEN secret is read or written.
# publish credential. No NPM_TOKEN secret is read or written. actions/checks read
# access exists only so the point-of-mutation revalidation below can re-verify the
# exact-commit gate immediately before the first registry mutation.
permissions:
id-token: write
contents: read
actions: read
checks: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6.0.0
with:
# The publication job runs scripts from the signed tag checkout and
# never pushes; no git credentials may outlive the checkout step.
persist-credentials: false
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v6.0.0
with:
node-version: 24
Expand Down Expand Up @@ -349,6 +401,30 @@ jobs:
require_package @opencoven/cli-windows
require_package @opencoven/cli-macos-x64
require_package @opencoven/cli-macos
# Point-of-mutation revalidation: the early gate (exact-source-gate) and
# tag verification ran minutes ago. Authorization is re-proven here,
# immediately before the first registry mutation, so a required check
# that re-ran to red, a moved/replaced tag, or a stale authorization can
# never be spent on a publication.
- name: Revalidate exact checks and signed tag immediately before publication
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_TAG: ${{ needs.verify-tag.outputs.release_tag }}
HEAD_SHA: ${{ needs.verify-tag.outputs.head_sha }}
TAG_OBJECT_SHA: ${{ needs.verify-tag.outputs.tag_object_sha }}
run: |
set -euo pipefail
node scripts/package-github-release.mjs revalidate-tag \
--repository "$GITHUB_REPOSITORY" \
--release-tag "$RELEASE_TAG" \
--expected-tag-object-sha "$TAG_OBJECT_SHA" \
--expected-head-sha "$HEAD_SHA"
node scripts/verify-release-commit-gate.mjs verify \
--repository "$GITHUB_REPOSITORY" \
--commit-sha "$HEAD_SHA" \
--manifest scripts/release-required-checks.json \
--release-tag "$RELEASE_TAG" \
--tag-object-sha "$TAG_OBJECT_SHA"
- run: node scripts/publish-npm.mjs --target=linux-x64 --skip-build --publish --skip-wrapper
env:
COVEN_NPM_VERSION: ${{ needs.verify-tag.outputs.npm_version }}
Expand Down
Loading
Loading