CI/CD Pipeline

April 30, 2026 · View on GitHub

Maestro uses seven GitHub Actions workflows. Six are organized around a source-of-truth enforcement model — each regenerates runtime adapters from canonical src/ and verifies zero drift before proceeding. The seventh, Commit Message Check, validates semantic git conventions for PR branch names, PR titles, and commit subjects targeting main. Together they span continuous integration on every push/PR through automated release publishing to npm.

Workflow Overview

graph LR
    subgraph "Continuous Integration"
        A["Push / PR to main"] --> B["Source Of Truth Check"]
        A --> M["Commit Message Check"]
    end

    subgraph "Pre-release Publishing"
        C["PR labeled 'preview'"] --> D["Preview Build"]
        E["Manual dispatch from main"] --> F["Prepare Release"]
        F --> |"Creates release/vX.Y.Z branch<br/>Opens PR to main with<br/>'release' + 'rc' labels"| G["Release Candidate"]
    end

    subgraph "Release Publishing"
        G --> |"PR merged to main"| H["Release"]
    end

    subgraph "Scheduled"
        J["Cron: daily 06:00 UTC"] --> K["Nightly Build"]
    end

The six source-of-truth workflows share a common validation core: generate runtime adapters, check for drift, and run the full test suite. Source Of Truth Check and stable Release also verify npm package contents and the self-contained release archive. Nightly Build, Preview Build, and Release Candidate keep best-effort npm publishing gated on NPM_TOKEN; stable Release publishes through npm Trusted Publishing with GitHub Actions OIDC. Commit Message Check is independent: it does not regenerate or test, it only validates branch naming, PR-title formatting, and commit-subject formatting.

Source Of Truth Check

Purpose

The foundational CI gate. Enforces that all generated runtime adapters (Gemini CLI, Claude Code, Codex, and Qwen Code) are in sync with canonical source in src/, and that the full test suite passes. Runs on every push and pull request targeting main.

Trigger

EventBranches
pushmain
pull_requestmain

Flow

graph TD
    A["Push or PR to main"] --> B["Checkout repository"]
    B --> C["Setup Node.js 20"]
    C --> D["Generate runtime adapters"]
    D --> E{"Adapter drift?"}
    E --> |"Yes"| F["Fail: adapters out of sync"]
    E --> |"No"| G["Run full test suite"]
    G --> H{"Tests pass?"}
    H --> |"Yes"| I["Verify npm pack<br/>and release artifact"]
    H --> |"No"| J["Fail: test failures"]
    I --> K["Check passes"]

Job Breakdown

Job: check-architecture (name: source-of-truth-check)

StepDescription
CheckoutPins actions/checkout to SHA 11bd71901bbe5b1630ceea73d27597364c9af683 (v4.2.2)
Setup Node.jsInstalls Node.js 20 via actions/setup-node@v4
Generate runtime adaptersRuns node scripts/generate.js to rebuild all runtime outputs
Check adapter driftRuns git diff --exit-code --name-only; fails with annotation if any generated file differs from what is committed
Run full test suiteExecutes node --test tests/unit/*.test.js tests/transforms/*.test.js tests/integration/*.test.js
Verify npm package contentsRuns npm run pack:verify to ensure npm dry-run packaging contains required runtime files and no test-only directories
Package and verify release artifactRuns npm run release:artifacts and npm run release:verify-artifacts to validate the generic GitHub Release archive

Environment and Secrets

Uses default permissions (read-only). No secrets or environment variables required.

Artifacts

Creates an ignored local dist/release/maestro-vX.Y.Z-extension.tar.gz during validation, but does not upload it from PR CI.

Key Behaviors

  • The drift check emits a GitHub Actions error annotation (::error::) with a git diff --stat summary on failure, providing immediate visibility into which files drifted.
  • This workflow serves as the required status check that blocks PR merges.

Commit Message Check

Purpose

Enforces semantic git conventions for work targeting main: PR branch names must use the repo's semantic branch format, PR titles must follow Conventional Commits, and every commit subject in the PR range must be conventional. This closes the local-hook bypass (git commit --no-verify) by re-validating in CI. The workflow also runs on direct pushes to main to cover admin overrides and emergency hotfixes that bypass the PR flow.

Trigger

EventBranchesTypes
pull_requestmainopened, edited, reopened, synchronize
pushmain

Flow

graph TD
    A["Push / PR to main"] --> B{"Event type?"}
    B --> |"pull_request"| C["Job: pr-title-check"]
    B --> |"pull_request"| C2["Job: branch-name-check"]
    B --> |"push or pull_request"| D["Job: commit-subjects-check"]
    C --> E{"PR title matches<br/>Conventional Commits?"}
    E --> |"No"| F["Fail with::error annotation<br/>and remediation hint"]
    E --> |"Yes"| G["Pass"]
    C2 --> L{"PR branch matches<br/>semantic branch convention?"}
    L --> |"No"| M["Fail with::error annotation<br/>and rename hint"]
    L --> |"Yes"| N["Pass"]
    D --> H["Resolve commit range<br/>(PR base..head, or push before..after)"]
    H --> I{"All subjects match<br/>(skipping Merge / Revert / fixup!)?"}
    I --> |"No"| J["Fail with per-commit annotations"]
    I --> |"Yes"| K["Pass"]

Job Breakdown

Job: check-pr-title (name: pr-title-check) — runs only on pull_request events.

StepDescription
Validate PR titleReads github.event.pull_request.title via the PR_TITLE env var; tests it against the Conventional Commits regex; emits ::error:: annotation with format guidance on failure

No checkout step — the job reads only event-payload metadata.

Job: check-pr-branch (name: branch-name-check) — runs only on pull_request events.

StepDescription
Validate PR branchReads github.event.pull_request.head.ref; accepts <type>/<slug>, <namespace>/<type>/<slug>, release/vX.Y.Z, protected branch names, and known automation prefixes; emits ::error:: annotation with rename guidance on failure

No checkout step — the job reads only event-payload metadata.

Job: check-commits (name: commit-subjects-check) — runs on both event types.

StepDescription
CheckoutPins actions/checkout to SHA 11bd71901bbe5b1630ceea73d27597364c9af683 (v4.2.2) with fetch-depth: 0 to access the full history
Validate commit subjectsResolves the commit range from the event (PR: base.sha..head.sha; push: before..after, or after~1..after on initial push); iterates git log --format='%H %s'; tests each subject against the regex with pass-throughs for Merge, Revert, fixup!, squash!, amend!

Environment and Secrets

permissions: contents: read (least privilege; only needs to read the repository). No secrets required. Safe for fork PRs.

Artifacts

None produced or consumed.

Key Behaviors

  • The PR-title regex matches the local hook regex character-for-character, ensuring local-pass and CI-pass agree.
  • The PR-branch regex mirrors .githooks/pre-commit and .githooks/pre-push, so branch names are checked locally before committing, locally before pushing, and in CI before merging.
  • Pass-throughs (Merge , Revert , fixup!, squash!, amend!) match those in .githooks/commit-msg, so git-generated subjects (interactive rebases, merges, reverts) are accepted in both layers.
  • Bot-authored release commits (release: vX.Y.Z from Prepare Release) match the regex without exception.
  • The push trigger means even a direct admin push to main is validated post-hoc — failures appear as red checks on the commit on main.

Nightly Build

Purpose

Publishes a nightly snapshot of the main branch to npm under the nightly dist-tag. Validates the main branch has no drift and passes all tests before publishing. Runs on a daily schedule and can be triggered manually.

Trigger

EventDetails
scheduleCron: 0 6 * * * (daily at 06:00 UTC)
workflow_dispatchManual trigger with no inputs

Flow

graph TD
    A["Schedule or manual dispatch"] --> B["Checkout main branch"]
    B --> C["Setup Node.js 20 with npm registry"]
    C --> D["Generate runtime adapters"]
    D --> E{"Adapter drift?"}
    E --> |"Yes"| F["Fail: nightly drift on main"]
    E --> |"No"| G["Run full test suite"]
    G --> H{"Tests pass?"}
    H --> |"No"| I["Fail: test failures"]
    H --> |"Yes"| J{"NPM_TOKEN available?"}
    J --> |"No"| K["Skip publish"]
    J --> |"Yes"| L["Set nightly version<br/>X.Y.Z-nightly.YYYYMMDD"]
    L --> M["Publish to npm<br/>--tag nightly"]

Job Breakdown

Job: nightly

StepDescription
CheckoutChecks out refs/heads/main explicitly, pinned to SHA 11bd71901bbe5b1630ceea73d27597364c9af683
Setup Node.jsNode.js 20 with registry-url set to https://registry.npmjs.org
Generate runtime adaptersRuns node scripts/generate.js
Check adapter driftFails with ::error::Nightly drift detected on main if generated files differ
Run full test suiteExecutes the full test suite
Determine publish eligibilitySets enabled=true output if NPM_TOKEN secret is present
Set nightly versionComputes version as {base}-nightly.{YYYYMMDD} using npm version --no-git-tag-version
Regenerate runtime metadataRuns node scripts/generate.js so runtime manifests and MCP package specs use the nightly version
Verify npm package contentsRuns npm run pack:verify against the nightly package surface
Publish nightlyPublishes through node scripts/npm-publish-idempotent.js --tag nightly --access public, skipping if the exact version already exists

Environment and Secrets

ItemTypePurpose
NPM_TOKENSecretAuthenticates with npm registry for publishing
NODE_AUTH_TOKENEnv (derived)Set to $NPM_TOKEN value for npm CLI authentication

Permissions: contents: read

Artifacts

Publishes @josstei/maestro@X.Y.Z-nightly.YYYYMMDD to npm with the nightly dist-tag.

Key Behaviors

  • Always checks out the main branch regardless of what triggered it.
  • The version string includes the date stamp, so each nightly replaces the previous day's version on the nightly tag.
  • When NPM_TOKEN is absent, the workflow still validates drift and runs tests, providing health checks for forks.

Preview Build

Purpose

Publishes a preview package from a pull request so reviewers can install and test changes before merging. Activated by adding the preview label to any PR.

Trigger

EventDetails
pull_requestTypes: labeled, synchronize, reopened
ConditionPR must have the preview label

Flow

graph TD
    A["PR labeled/synchronize/reopened"] --> B{"Has 'preview' label?"}
    B --> |"No"| C["Skip workflow"]
    B --> |"Yes"| D["Checkout PR head SHA"]
    D --> E["Setup Node.js 20 with npm registry"]
    E --> F["Generate runtime adapters"]
    F --> G{"Adapter drift?"}
    G --> |"Yes"| H["Fail: preview drift detected"]
    G --> |"No"| I["Run full test suite"]
    I --> J{"Tests pass?"}
    J --> |"No"| K["Fail: test failures"]
    J --> |"Yes"| L{"NPM_TOKEN available?"}
    L --> |"No"| M["Skip publish"]
    L --> |"Yes"| N["Set preview version<br/>X.Y.Z-preview.SHORT_SHA"]
    N --> O["Publish to npm<br/>--tag preview"]
    O --> P["Upsert PR comment<br/>with install command"]

Job Breakdown

Job: preview

StepDescription
CheckoutChecks out the PR head repository and SHA (supports fork PRs)
Setup Node.jsNode.js 20 with npm registry URL
Generate runtime adaptersRuns node scripts/generate.js
Check adapter driftFails with ::error::Preview drift detected if drift exists
Run full test suiteExecutes the full test suite
Determine publish eligibilityGates on NPM_TOKEN presence
Set preview versionComputes version as {base}-preview.{7-char SHA}
Regenerate runtime metadataRuns node scripts/generate.js so runtime manifests and MCP package specs use the preview version
Verify npm package contentsRuns npm run pack:verify against the preview package surface
Publish previewPublishes through node scripts/npm-publish-idempotent.js --tag preview --access public, skipping if the exact version already exists
Upsert PR commentPosts or updates a PR comment with the install command

Environment and Secrets

ItemTypePurpose
NPM_TOKENSecretnpm registry authentication
NODE_AUTH_TOKENEnv (derived)Set to $NPM_TOKEN for npm CLI
GH_TOKENEnv (derived)Set to ${{ github.token }} for PR comment API calls

Permissions: contents: read, pull-requests: write

Artifacts

Publishes @josstei/maestro@X.Y.Z-preview.SHORT_SHA to npm with the preview dist-tag.

Key Behaviors

  • Uses concurrency group preview-{PR number} with cancel-in-progress: true, so pushing new commits cancels any in-flight preview build for the same PR.
  • The PR comment is upserted: if a comment starting with "Preview published:" already exists, it is updated in place rather than posting a duplicate.
  • The version embeds the first 7 characters of the commit SHA, making each preview uniquely traceable to a specific commit.

Prepare Release

Purpose

Orchestrates the release preparation process. Creates a release branch from main, bumps version numbers, and opens a pull request targeting main (the actual release PR). This is the entry point for the release pipeline.

Trigger

EventDetails
workflow_dispatchManual trigger from the main branch only
Inputversion (optional string): explicit version to release; when omitted, the version is inferred from commit history

Flow

graph TD
    A["Manual dispatch from main"] --> B{"Running on main branch?"}
    B --> |"No"| C["Fail: must run from main"]
    B --> |"Yes"| D["Checkout with full history"]
    D --> E{"Version input provided?"}
    E --> |"Yes"| F["Use provided version"]
    E --> |"No"| G["Infer version from commits<br/>major/minor/patch"]
    F --> H["Validate version format<br/>X.Y.Z"]
    G --> H
    H --> I["Validate CHANGELOG<br/>has unreleased content"]
    I --> J["Generate and check drift"]
    J --> K["Run full test suite"]
    K --> L["Create release/vX.Y.Z branch"]
    L --> M["Update canonical release inputs<br/>via scripts/update-versions.js"]
    M --> N["Regenerate with new version"]
    N --> O["Commit: 'release: vX.Y.Z'"]
    O --> P["Push release branch"]
    P --> Q["Open PR to main<br/>with CHANGELOG excerpt"]
    Q --> R["Label main PR:<br/>'release' + 'rc'"]

Job Breakdown

Job: prepare

StepDescription
Validate branchFails if not running from refs/heads/main
CheckoutFull history (fetch-depth: 0) using RELEASE_TOKEN for push permissions
Setup Node.jsInstalls Node.js 20 via actions/setup-node@v4
Infer version from commitsScans commit logs since last tag; determines bump level: BREAKING CHANGE or !: triggers major, feat triggers minor, otherwise patch
Resolve target versionUses explicit input if provided, otherwise uses inferred version
Validate target versionEnsures version matches X.Y.Z semver pattern
Validate CHANGELOGFails if the [Unreleased] section in CHANGELOG.md has no content
Generate and check driftRuns generator and fails if main has uncommitted drift
Run full test suiteExecutes the full test suite
Create release branchCreates release/vX.Y.Z from current main
Update canonical release inputsRuns node scripts/update-versions.js with the target version to update package.json, README badges, and CHANGELOG
Regenerate with new versionReruns generator to derive runtime metadata from package.json, then runs npm install --package-lock-only to update lockfile
Commit releaseCommits all changes as release: vX.Y.Z using the github-actions[bot] identity
Push release branchPushes release/vX.Y.Z to origin
Open PR to mainCreates a PR merging release/vX.Y.Z into main with CHANGELOG excerpt as body
Label release PRAdds release and rc labels to the main-targeting PR

Environment and Secrets

ItemTypePurpose
RELEASE_TOKENSecretPersonal access token with write permissions; used for checkout, branch push, PR creation, and PR labeling. Required because the default GITHUB_TOKEN cannot trigger downstream workflows.

Permissions: contents: write, pull-requests: write

Artifacts

Creates the release/vX.Y.Z branch and a pull request targeting main with the CHANGELOG excerpt.

Key Behaviors

  • The version inference algorithm uses conventional commit patterns: BREAKING CHANGE or !: in commit messages triggers a major bump, feat commits trigger minor, everything else triggers patch.
  • The CHANGELOG validation ensures no release ships without documented changes.
  • The release and rc labels on the main-targeting PR are what trigger the Release Candidate workflow.
  • Uses RELEASE_TOKEN (not the default GITHUB_TOKEN) so that the push and PR creation events trigger downstream workflow runs (Source Of Truth Check on the new PR, and Release Candidate when labels are applied).

Release Candidate

Purpose

Publishes release candidate packages to npm from release PRs targeting main. Activates automatically when the Prepare Release workflow labels a PR with both release and rc. Provides an installable RC package for final validation before the release merges.

Trigger

EventDetails
pull_requestTypes: labeled, synchronize, reopened
Conditions (all must be true)Base branch is main
PR has both rc and release labels
Head branch starts with release/v

Flow

graph TD
    A["PR event on release/vX.Y.Z to main"] --> B{"Base is main?<br/>Has 'rc' + 'release' labels?<br/>Head starts with 'release/v'?"}
    B -->|"Any condition false"| C["Skip workflow"]
    B -->|"All true"| D["Checkout PR head SHA"]
    D --> E["Setup Node.js 20 with npm registry"]
    E --> F["Generate runtime adapters"]
    F --> G{"Adapter drift?"}
    G --> |"Yes"| H["Fail: RC drift detected"]
    G --> |"No"| I["Run full test suite"]
    I --> J{"Tests pass?"}
    J --> |"No"| K["Fail: test failures"]
    J --> |"Yes"| L{"NPM_TOKEN available?"}
    L --> |"No"| M["Skip publish"]
    L --> |"Yes"| N["Determine RC version<br/>Query npm for existing RCs<br/>Increment RC number"]
    N --> O["Publish to npm<br/>--tag rc"]
    O --> P["Upsert PR comment<br/>with install command"]

Job Breakdown

Job: rc

StepDescription
CheckoutChecks out the PR head repository and SHA
Setup Node.jsNode.js 20 with npm registry URL
Generate runtime adaptersRuns node scripts/generate.js
Check adapter driftFails with ::error::RC drift detected
Run full test suiteExecutes the full test suite
Determine publish eligibilityGates on NPM_TOKEN presence
Determine RC versionReads base version from package.json, queries npm registry for existing RC versions of this base, increments the RC number to avoid collisions
Regenerate runtime metadataRuns node scripts/generate.js so runtime manifests and MCP package specs use the RC version
Verify npm package contentsRuns npm run pack:verify against the RC package surface
Publish RCPublishes through node scripts/npm-publish-idempotent.js --tag rc --access public, skipping if the exact version already exists
Upsert PR commentPosts or updates a comment with install command and short SHA

Environment and Secrets

ItemTypePurpose
NPM_TOKENSecretnpm registry authentication
NODE_AUTH_TOKENEnv (derived)Set to $NPM_TOKEN for npm CLI
GH_TOKENEnv (derived)Set to ${{ github.token }} for PR comment API calls

Permissions: contents: read, pull-requests: write

Artifacts

Publishes @josstei/maestro@X.Y.Z-rc.N to npm with the rc dist-tag, where N is auto-incremented.

Key Behaviors

  • Uses concurrency group rc-{PR number} with cancel-in-progress: true to avoid duplicate RC publishes when new commits are pushed to the release branch.
  • The RC number auto-increments by querying npm view for existing versions, ensuring no version collision even when the workflow runs multiple times for the same base version.
  • The PR comment is upserted (update existing or create new) to keep a single, current RC reference on the PR.

Release

Purpose

The final step of the release pipeline. Triggers on any push to main, but only acts when the push is a merged pull request carrying the release label. It can also be manually dispatched to recover a failed post-tag stable publish. Validates package and archive contents, creates or reuses the matching Git tag, publishes the stable package to npm, then publishes a GitHub Release with CHANGELOG notes and the self-contained extension archive attached.

Trigger

EventDetails
pushBranch: main
workflow_dispatchManual stable recovery with version and optional target_sha; when target_sha is omitted, the workflow checks out refs/tags/v<version>
ConditionThe pushed commit must be the merge commit of a PR labeled release that targeted main

Flow

graph TD
    A["Push to main"] --> B["Checkout with full history"]
    AA["Manual recovery<br/>version + target_sha"] --> BB["Checkout target SHA<br/>or refs/tags/vX.Y.Z"]
    B --> C["Resolve release context"]
    BB --> C
    C --> D{"Merged PR with<br/>'release' label found?"}
    D --> |"No"| E["Skip: not a release commit"]
    D --> |"Yes"| F["Setup Node.js 24 with npm registry"]
    F --> G["Verify NPM_TOKEN"]
    G --> I["Extract and validate version,<br/>target SHA, and tag"]
    I --> J["Generate runtime adapters"]
    J --> K{"Adapter drift?"}
    K --> |"Yes"| L["Fail: adapters out of sync"]
    K --> |"No"| M["Run full test suite"]
    M --> N{"Tests pass?"}
    N --> |"No"| O["Fail: test failures"]
    N --> |"Yes"| P["Verify npm pack<br/>and release artifact"]
    P --> Q["Create and push Git tag<br/>vX.Y.Z"]
    Q --> R["Publish to npm<br/>--access public"]
    R --> S["Extract CHANGELOG<br/>for this version"]
    S --> T["Create GitHub Release<br/>with archive attached"]

Job Breakdown

Job: release

StepDescription
CheckoutFull history (fetch-depth: 0) for tag operations. Push releases check out the pushed commit; manual recovery checks out target_sha or refs/tags/v<version>.
Resolve release contextPush releases query the GitHub API for PRs associated with the current commit SHA and filter for merged PRs targeting main with the release label. Manual recovery sets the release context from the supplied version. If no push release is found, sets is_release=false and all subsequent steps are skipped.
Setup Node.jsConditional on is_release=true; Node.js 24 with npm registry URL
Verify npm tokenFails before tag or publish work unless NPM_TOKEN is configured
Extract and validate versionReads version from package.json and cross-validates: the version must be stable semver, the CHANGELOG must have a matching section (unconditional), any existing vX.Y.Z tag must point at the checked-out target SHA, and manual recovery must match both the requested version and target_sha when supplied. When the release branch name matches release/vX.Y.Z and the PR title matches release: vX.Y.Z, their embedded versions must agree with package.json.
Generate runtime adaptersRuns node scripts/generate.js
Check adapter driftFinal drift check before release; fails with error annotation
Run full test suiteFinal test gate before release
Verify npm package contentsRuns npm run pack:verify before any tag or publish operation
Package release artifactRuns npm run release:artifacts to create dist/release/maestro-vX.Y.Z-extension.tar.gz
Verify release artifactRuns npm run release:verify-artifacts against the generated archive
Create and push tagCreates Git tag vX.Y.Z at the checked-out target SHA; handles idempotency (skips if tag exists at same SHA, fails if tag exists at different SHA)
Publish to npmPublishes stable release through node scripts/npm-publish-idempotent.js --access public with NODE_AUTH_TOKEN derived from NPM_TOKEN, skipping if the exact version already exists
Extract changelogExtracts the version-specific section from CHANGELOG.md using awk
Create GitHub ReleaseUses softprops/action-gh-release (pinned to SHA c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda, v2.2.1) with CHANGELOG excerpt as body and the generic extension archive attached

Environment and Secrets

ItemTypePurpose
GH_TOKENEnv (derived)Set to ${{ github.token }} for PR creation and GitHub API calls
NPM_TOKENSecretnpm registry authentication for stable publish and dist-tag repair
NODE_AUTH_TOKENEnv (derived)Set to $NPM_TOKEN for npm CLI

Permissions: contents: write, pull-requests: write

Artifacts

  • Git tag vX.Y.Z pushed to origin
  • Stable npm package @josstei/maestro@X.Y.Z published with the latest dist-tag
  • GitHub Release with CHANGELOG body and maestro-vX.Y.Z-extension.tar.gz

Key Behaviors

  • The release detection uses the GitHub API to find the PR associated with the merge commit, filtering for the release label. Non-release pushes to main exit early and cleanly.
  • Manual recovery is only for stable versions and requires the checked-out commit, package.json, CHANGELOG.md, and existing vX.Y.Z tag to agree. This recovers failures after tag creation without creating a new version or publishing from a maintainer laptop.
  • Version validation cross-checks package.json against the CHANGELOG (unconditional), the existing tag when present, and, when applicable, against the release branch name (release/vX.Y.Z) and the PR title (release: vX.Y.Z). A mismatch in any available source fails the workflow.
  • Stable releases require NPM_TOKEN; npm Trusted Publishing should only replace it after the package owner explicitly configures and tests that path.
  • npm latest is stable-only. The publish helper rejects prereleases published without an explicit prerelease tag or with latest, rejects stable publishes with prerelease dist-tags, removes a stale latest tag when it points at a prerelease and no stable exists, and moves latest to the stable version after a stable publish or idempotent skip.
  • Tag creation is idempotent: if the tag already exists at the same commit, the step is skipped. If it exists at a different commit, the workflow fails to prevent overwriting a release.

Build Commands

The justfile provides local development commands that mirror CI behavior.

Command Reference

CommandDescriptionCI Equivalent
just generateGenerate all runtime files from src/Used in all 6 generator workflows
just dry-runPreview changes without writingNo CI equivalent
just diffShow unified diff of pending changesNo CI equivalent
just cleanDelete generated files and regenerate from scratchNo CI equivalent
just testRun all tests (unit + transform + integration)Used in all 6 generator workflows
just test-unitRun only unit testsNo CI equivalent
just test-transformsRun only transform unit testsNo CI equivalent
just test-integrationRun only integration testsNo CI equivalent
just checkGenerate + verify zero driftReplicated in all 6 generator workflows
just check-layersVerify lib/ layer boundary importsNo CI workflow equivalent
just ciFull CI equivalent: check + check-layers + testSuperset of CI (includes check-layers)
just cleanup-branchesDelete local branches whose remote is goneNo CI equivalent

CI Mapping

The workflows replicate the following just commands:

just generate  -->  node scripts/generate.js
just check     -->  git diff --exit-code --name-only (after generate)
just test      -->  node --test tests/unit/*.test.js tests/transforms/*.test.js tests/integration/*.test.js
pack verify    -->  npm run pack:verify
artifact check -->  npm run release:artifacts && npm run release:verify-artifacts

The local just ci recipe runs check, check-layers, and test. The GitHub source-of-truth workflow runs generate, drift check, test, npm pack verification, and release artifact verification, but does not run check-layers (node scripts/check-layer-boundaries.js). The layer boundary check is a local-only validation.


Workflow Relationships

Release Pipeline Chain

The release pipeline is a multi-workflow chain where each stage triggers the next through Git events and PR labels.

graph LR
    A["Developer triggers<br/>Prepare Release<br/>on main branch"] --> B["Prepare Release<br/>creates release/vX.Y.Z"]
    B --> D["PR to main<br/>labeled 'release' + 'rc'"]
    D --> E["Source Of Truth Check<br/>runs on PR"]
    D --> F["Release Candidate<br/>publishes X.Y.Z-rc.N"]
    F --> |"RC validated<br/>PR merged to main"| G["Release<br/>publishes X.Y.Z"]

Step-by-Step Release Flow

  1. A maintainer manually triggers Prepare Release from the main branch (optionally specifying a version).
  2. The workflow validates main (drift check, tests, CHANGELOG content), creates a release/vX.Y.Z branch, bumps version files, and pushes the branch.
  3. One PR is opened:
    • PR to main: the release PR, labeled with release and rc, containing the CHANGELOG excerpt.
  4. The release PR triggers Source Of Truth Check (standard CI on PRs to main).
  5. The release + rc labels on a PR from release/v* to main trigger the Release Candidate workflow, which publishes an RC to npm.
  6. If additional commits are pushed to the release branch, both Source Of Truth Check and Release Candidate re-run (RC number auto-increments).
  7. When the release PR is merged to main, the push event triggers Release.
  8. Release detects the merged release PR via the GitHub API, validates version consistency, runs final checks, creates a Git tag, publishes to npm, and creates a GitHub Release with the generic extension archive attached.

Branch Strategy

graph LR
    A["main<br/>primary development + releases"] --> |"Prepare Release"| B["release/vX.Y.Z<br/>short-lived"]
    B --> |"Release PR"| A
BranchPurposeProtected
mainPrimary development and release branch; all feature work and releases merge hereYes (CI required)
<type>/<slug>Contributor work branches using the same type vocabulary as Conventional CommitsPR check required
<namespace>/<type>/<slug>Namespaced contributor/tool work branches, such as codex/chore/enforce-git-conventionsPR check required
release/vX.Y.ZShort-lived release branches created by Prepare ReleaseTransient

Permissions and Secrets Summary

WorkflowPermissionsSecrets
Source Of Truth CheckDefault (read)None
Commit Message Checkcontents: readNone
Nightly Buildcontents: readNPM_TOKEN
Preview Buildcontents: read, pull-requests: writeNPM_TOKEN
Prepare Releasecontents: write, pull-requests: writeRELEASE_TOKEN
Release Candidatecontents: read, pull-requests: writeNPM_TOKEN
Releasecontents: write, pull-requests: writeNPM_TOKEN

The RELEASE_TOKEN used by Prepare Release is a personal access token with elevated permissions. The default GITHUB_TOKEN does not trigger downstream workflow runs, so RELEASE_TOKEN is required for branch pushes and PR creation that need to activate Source Of Truth Check and Release Candidate on the newly created PR.

npm Dist-Tags

TagSourceVersion PatternWorkflow
latestStable release from mainX.Y.ZRelease
rcRelease candidate from release PRX.Y.Z-rc.NRelease Candidate
previewPR preview buildX.Y.Z-preview.SHORT_SHAPreview Build
nightlyDaily main snapshotX.Y.Z-nightly.YYYYMMDDNightly Build

latest must never intentionally point at rc, preview, or nightly. If a prerelease publish or idempotent skip sees latest pointing to a prerelease and at least one stable version exists, the helper repairs it by moving latest back to the highest published stable version. If no stable version exists yet, the helper logs a warning and defers repair until the stable Release workflow publishes X.Y.Z; it does not delete latest.