Skip to content

Streamline release pipeline — changeset-per-PR, 2-workflow (version→release), open-source changelogs #366

Description

@rucka

Story Statement

As a Pair maintainer
I want the release pipeline reduced to changeset-per-PR plus a two-workflow, dispatch-only model (version.ymlrelease.yml) with open-source changelogs
So that releases are maintainer-controlled and reproducible, and exactly two workflow runs happen per release instead of a chain triggered by merges

Where: .github/workflows/{ci,version,release,tag-on-changeset-merge}.yml, .changeset/config.json, and the /pair-process-implement skill (changeset bump-type inference).

Epic Context

Parent Epic: none — standalone (maintainer decision, 2026-08-05). It was filed under #68, an epic of the frozen Knowledge Service initiative, while its subject belonged to #211 (release management), now closed: orphaned on both sides. It is real work and does not need an epic to exist.
Status: Refined
Priority: P1 (Should-Have) — the pipeline is the path every release takes
Builds on: #148 / PR #333 (release scripts + tested determine-version, shipped)

Classification

risk:yellow · cost: green (not projected — Active: risk only)

Matrix — per dimension
Dimension Tier Source Note
Service/domain criticality green Criticality Table Touched deployables: packages/dev-tools (owns repo-wide CI gates + release-pipeline logic, incl. determine-version invoked by release.yml) and packages/knowledge-hub (dataset source of the pair-process-implement skill mirror) — both listed Low
Change/diff risk yellow story scope Workflows + .changeset/config.json are shared CI/release infra touched on every PR. The skill change is a dataset↔mirror pair (change-risk.dataset-mirror-pairs override collapses it to one module), but the workflow/config changes remain separate, genuinely-shared modules — still "touches multiple modules or shared code"
Business impact yellow subdomain class Integration & Process Standardization (Supporting) — distribution/release process. The newer Development Tooling Standards (Generic) subdomain covers lint/format/TypeScript/markdown/brand only, not release/CI, so it does not apply here
Security relevance yellow path heuristic Touches release credentials: dropping the PAT in favour of GITHUB_TOKEN reduces the permission surface, but any error in a release workflow's token wiring is a publishing-authority mistake
Coupling balance green subdomain volatility + integrations No new cross-context integration

Tier = yellow (max rule). tier-resolution.default-artifact-downgrade checked and does not apply: Change/diff risk and Security relevance are yellow, and that override only downgrades to green when every present yellow is confined to Service/domain-criticality and/or Business-impact. Gate checks for 🟡: lint + type + build + unit.

Reclassified 2026-08-14 against corrected adoption inputs (tech/risk-matrix.md Criticality Table + tier-resolution.default-artifact-downgrade override, both added after this story's original refinement, 2026-08-05): Service/domain criticality moves from yellow (KB default) to green. Overall tier is unchanged at yellow — Change/diff risk and Security relevance remain genuinely observed, non-artifact yellows, so the new override does not fire.

Context

Split from #148: the tooling slice shipped (PR #333, #148 closed); this story carries the remaining pipeline rewrite.

State verified against the real workflows (2026-08-05)

The original AC set asked to "confirm each still applies". Confirmed, and half of them are already done — the story shrinks accordingly:

Original AC Status today Evidence
1. changeset-per-PR check in ci.yml, with escape-hatch label still needed ci.yml mentions .changeset twice, and both are the paths-ignore on its triggers — there is no check
2. version.yml dispatch-only + PR-linked, type-grouped changelogs still needed version.yml triggers on push to main filtered to .changeset/**, not workflow_dispatch; .changeset/config.json uses the default @changesets/cli/changelog, not @changesets/changelog-github
3. release.yml dispatch-only already done on: workflow_dispatch with a version input, no tag-push or release-event trigger
4. delete tag-on-changeset-merge.yml still needed the file is present
5. remove || echo fallbacks masking failures in ci.yml already done zero occurrences
6. eliminate the PAT (GH_RELEASE_TOKEN) 🔄 consequence of 4 the token appears in exactly one file: tag-on-changeset-merge.yml. Deleting that workflow removes the PAT
7. /implement infers the bump type when creating the changeset still needed skill-layer change on pair-process-implement

So the work is: the missing CI check, making version.yml dispatch-only with proper changelogs, deleting one workflow (which retires the PAT), and one skill change.

Acceptance Criteria

Functional Requirements

Given-When-Then Format:

  1. Given a pull request with no .changeset/*.md
    When CI runs
    Then the changeset check fails — unless the PR carries the documented escape-hatch label, in which case it passes with that reason visible

  2. Given accumulated changesets on main
    When version.yml is dispatched manually
    Then it bumps the fixed-group package versions atomically, syncs the root package.json, generates PR-linked, type-grouped CHANGELOGs, commits, and pushes the v{X.Y.Z} tag

  3. Given version.yml
    When its triggers are inspected
    Then it is workflow_dispatch-only — a merge to main no longer starts a release chain by itself

  4. Given a non-changeset PR merged to main
    When the merge completes
    Then no release workflow runs, and a full release consists of exactly two runs (one version, one release)

  5. Given tag-on-changeset-merge.yml deleted
    When the release path is exercised
    Then nothing depends on it, and GH_RELEASE_TOKEN is no longer referenced anywhere — cross-workflow triggering uses GITHUB_TOKEN

  6. Given /pair-process-implement creating a changeset
    When it infers the bump type
    Then it derives patch/minor/major from the story type and commit convention, and states what it inferred so a wrong inference is visible before merge

  7. Given the whole rewrite
    When the first release after it runs
    Then it is verified end to end on a real release (or a throwaway repo), because the only honest test of a release pipeline is a release

Business Rules

  • Releases are dispatched, never triggered by a merge. A merge accumulates intent (a changeset); a human decides when it ships.
  • Two runs per release, always — one version, one release. More runs means a chain nobody asked for.
  • Least privilege: GITHUB_TOKEN over a PAT. The PAT retires with the workflow that needs it.
  • A skipped changeset is explicit: the escape-hatch label is the only way past the check, and it leaves a trace.
  • An inferred bump type is stated, not assumed — the inference is a convenience, and a wrong one must be visible before it ships.

Edge Cases and Error Handling

  • PR that legitimately needs no changeset (docs-only, CI-only): the escape-hatch label, documented, with the reason on the PR.
  • Two dispatches of version.yml in a row: the second must be a no-op when no changesets remain, not an empty version bump.
  • Fixed-group drift: the group in .changeset/config.json lists seven packages; a new package added without joining the group would version independently. Assert the group matches the published set.
  • Dispatch of release.yml with a version that was never tagged: fail loudly rather than publishing something that does not exist.
  • Changeset written by hand with the wrong bump type: AC6's stated inference makes the mismatch visible; the human wins.
  • The escape-hatch label applied by an agent: it must be as visible as any other approval-like act (relates to the identity question in Solo-maintainer explicit-approval token for 🔴 PRs (verified human token instead of a second account) #398).

Definition of Done Checklist

  • All acceptance criteria implemented and verified
  • Test written first where testable: the changeset check (fixture PR payloads with and without a changeset, and with the label)
  • version.yml converted to workflow_dispatch-only
  • .changeset/config.json switched to @changesets/changelog-github (PR-linked, type-grouped)
  • tag-on-changeset-merge.yml deleted; no reference to GH_RELEASE_TOKEN remains anywhere
  • /pair-process-implement infers and states the bump type; dataset edited as source, mirror regenerated, skills:conformance green
  • Fixed-group assertion covering the published package set
  • One real release performed through the new pipeline, with the run count verified (exactly two)
  • DEVELOPMENT.md release section updated to the dispatch-only flow
  • 🟡 gate checks green: lint + type + build + unit

Story Sizing and Sprint Readiness

Refined Story Points

Final Story Points: 5 (L)
Confidence Level: Medium
Sizing Justification: Four workflow/config changes plus a skill change — each small. The size comes from verification: a release pipeline is only honestly tested by releasing, so AC7 requires a real (or throwaway-repo) release, and a mistake here is felt as "we cannot publish". Two of the seven original criteria being already satisfied keeps this at 5 rather than 8.

Sprint Capacity Validation

Sprint Fit Assessment: Yes
Development Time Estimate: ~0.75 day
Testing Time Estimate: ~0.5 day, including the release run

Dependencies and Coordination

Story Dependencies

Prerequisite Stories: none — #148 / PR #333 already shipped the tooling this builds on
Dependent Stories: none
Shared Components: ci.yml → coordinates with #400 (adds a smoke job) and #416 (wires a check into the gate); pair-process-implementmutex with any card touching that skill. Different lines of ci.yml, so sequencing is enough

External Dependencies

Related: #148 / PR #333 (tooling slice) · #211 (closed: the release-management epic this thematically belonged to) · #68 (former parent, frozen initiative) · @changesets/changelog-github

Validation and Testing Strategy

Acceptance Testing Approach

Testing Methods: unit-testable parts (the changeset check) get fixture-driven tests; the pipeline itself is verified by performing a release — on a throwaway repository first if a real one is not due, which is the pattern github-implementation.md already uses for branch-protection work. Run count is the assertion that proves the chain is gone.
Test Data Requirements: fixture PR payloads (with changeset, without, with label); a throwaway repo mirroring the fixed group.

Success Metrics

  • A merge to main starts nothing; a dispatch starts exactly two runs
  • CHANGELOGs link PRs and group by type
  • No PAT remains in any workflow

Notes and Additional Context

Refinement Session Insights (2026-08-05):

  1. Detached from Platform Hardening & Enterprise Readiness #68 and made standalone by maintainer decision. It was formally a child of a frozen initiative and thematically part of ⏳ Release management & changelog (product process, placeholder) #211 (now closed) — orphaned twice, while being ordinary, real work.
  2. Verification cut the scope roughly in half. Two criteria are already satisfied (release.yml dispatch-only, || echo removed) and one is a consequence of another (the PAT lives only in the workflow being deleted). Had this been implemented from the card as written, three of seven criteria would have been re-done or puzzled over.
  3. AC7 is not ceremony: a release pipeline that has never released is untested, and the failure mode is discovered at the worst possible moment.

Team Concerns: three cards now touch ci.yml in the same period (#400, #416, this one). The lines differ, but the file does not — sequence them.

Technical Analysis

Implementation Approach

Technical Strategy: make release triggering explicit (dispatch-only), delete the merge-triggered workflow and the PAT with it, add the missing changeset check with a documented escape hatch, and switch the changelog generator to the PR-linked one.

Key Components:

  • ci.yml — changeset-per-PR check + escape-hatch label
  • version.ymlworkflow_dispatch-only, atomic fixed-group bump, tag push
  • release.yml — unchanged (already dispatch-only)
  • tag-on-changeset-merge.yml — deleted (retires GH_RELEASE_TOKEN)
  • .changeset/config.json@changesets/changelog-github
  • pair-process-implement — bump-type inference, stated in output
  • DEVELOPMENT.md — release flow section

Data Flow: PR (+changeset) → merge accumulates → human dispatches version.yml → bump + changelog + tag → release.yml dispatched with that version → publish.

Integration Points: CI, the changeset toolchain, the implement skill, release credentials.

Design: not required

Technical Risks and Mitigation

Risk Impact Mitigation
Pipeline rewritten and never exercised Discovered broken when a release is actually needed AC7: a real or throwaway-repo release, with the two-run count asserted
PAT removed while something still needs it Cross-workflow triggering silently stops working AC5: assert no reference remains and the dispatch chain works in the verification release
Changeset check blocks legitimate PRs Contributors reach for --no-verify or an unlabelled bypass Documented escape-hatch label, visible on the PR
Fixed group drifts from the published set A package versions independently and ships out of step Assertion over the group vs the published packages
Bump-type inference wrong and silent A breaking change ships as a patch AC6: the inference is stated in the output, before merge
Landing simultaneously with #400/#416 on ci.yml Conflicts on the same workflow file Sequenced, noted in Dependencies

Task Breakdown

  • T-1: Changeset-per-PR CI check with escape-hatch label (ci.yml)
  • T-2: Rewrite version.ymlworkflow_dispatch-only, direct commit + tag push, fixed-group drift assertion
  • T-3: Switch .changeset/config.json to @changesets/changelog-github
  • T-4: Delete tag-on-changeset-merge.yml; remove all GH_RELEASE_TOKEN references
  • T-5: /pair-process-implement — infer and state changeset bump type
  • T-6: Update RELEASE.md (and DEVELOPMENT.md pointer) to the two-workflow dispatch-only flow
  • T-7: Perform and verify one real release through the new pipeline (exactly two runs)

Dependency Graph

T-1 ──┐
T-2 ──┼── T-4 ── T-6 ── T-7
T-3 ──┘

T-5 (independent — different file surface, mutex with other cards touching
     pair-process-implement per the story's Shared Components note)

AC Coverage

AC Tasks
AC-1 (changeset check + escape hatch) T-1
AC-2 (version.yml: bump, changelog, commit, tag) T-2, T-3
AC-3 (version.yml is dispatch-only) T-2
AC-4 (non-changeset merge starts nothing; 2 runs per release) T-2, T-4
AC-5 (tag-on-changeset-merge.yml deleted; no PAT remains) T-4
AC-6 (implement states inferred bump type) T-5
AC-7 (verified on a real/throwaway release) T-7
DoD: RELEASE.md/DEVELOPMENT.md updated T-6
DoD: fixed-group assertion T-2
DoD: test-first changeset check T-1

T-1: Changeset-per-PR CI check with escape-hatch label

Priority: P0 | Estimated Hours: 4h | Bounded Context: Integration & Process Standardization

Summary: Add a ci.yml job that fails a PR carrying no .changeset/*.md file, unless the PR carries a documented escape-hatch label (e.g. no-changeset), in which case the job passes and the reason is visible on the PR.

Type: Feature Implementation

Description: ci.yml currently has no changeset check at all — its two .changeset mentions are only the paths-ignore on the push/pull_request triggers. This task adds the missing gate: a job that inspects the PR's changed files (or diff against origin/main) for .changeset/*.md, and short-circuits green when the PR has the escape-hatch label attached.

BLOCKING — surface before implementing: .pair/adoption/decision-log/2026-08-20-a-changeset-is-added-when-a-release-is-scheduled.md (dated after this story's last reclassification) adopts the opposite convention: a changeset is added by the release author when a release is scheduled, not per PR that changes behaviour. PRs #420/#424/#432/#437/#440 already merged with no changeset under that rule. Implementing AC1 literally (fail every PR without a changeset) would make the escape-hatch label the routine case for ordinary behaviour PRs, contradicting both the ADL and the story's own title ("changeset-per-PR"). Resolve this conflict — narrow the check's scope (e.g. only PRs that touch published packages AND declare a user-visible change in the description, or drop AC1 as superseded) — before writing the check, not after.

Acceptance Criteria:

  • Primary deliverable: a ci.yml job/step implementing the check + documented escape-hatch label (name and meaning recorded in RELEASE.md or CONTRIBUTING.md)
  • Quality standard: fixture-driven unit tests written first (failing), per the repo's Bug/Feature test-first convention
  • Integration requirement: runs on pull_request, does not fire on push/workflow_dispatch
  • Verification method: three fixture PR payloads — no changeset (red), no changeset + label (green, reason visible in log/summary), with changeset (green)

Technical Requirements:

  • Functionality: detect .changeset/*.md added/modified in the PR diff; detect the escape-hatch label via github.event.pull_request.labels
  • Performance: single fast job, no build/install beyond what's already cached
  • Security: no new token/permission needed (label + diff are both public PR metadata)
  • Compatibility: n/a

Implementation Approach:

  • Technical Design: a dedicated job (parallel to build/smoke), gated on github.event_name == 'pull_request', using git diff --name-only against the PR base or the GitHub API's changed-files list
  • Bounded Context & Modules: packages/dev-tools (if the check logic is factored into a testable script) or an inline ci.yml step for the label/diff check itself
  • Files to Modify/Create:
    • .github/workflows/ci.yml - add the changeset-check job
    • packages/dev-tools/src/** (or a scripts/workflows/ script) - if extracted for unit testability
  • Technical Standards References: .pair/knowledge/guidelines/testing/README.md (test-first); resolution of the ADL conflict above

Dependencies:

Implementation Steps:

  1. Resolve the AC1/ADL-2026-08-20 conflict (see Blocking note) and record the resolution (ADL/comment on this story)
  2. Write failing fixture tests for the check logic (no changeset / no changeset+label / with changeset)
  3. Implement the check as a ci.yml job (or extracted script + thin CI step)
  4. Document the escape-hatch label in RELEASE.md/CONTRIBUTING.md
  5. Verify against a real PR without a changeset (should fail) and with the label (should pass)

Testing Strategy:

  • Unit Tests: fixture PR payloads (no changeset, no changeset + label, with changeset) if logic is extracted to a testable module
  • Integration Tests: a throwaway PR against a fork/branch exercising the real ci.yml job
  • Manual Testing: confirm the escape-hatch reason renders visibly in the PR (label + optionally a job-summary line)

Notes: Do not merge before the ADL conflict is resolved — implementing this as literally specified would fight the currently adopted release convention.


T-2: Rewrite version.yml — dispatch-only, direct commit + tag push, fixed-group drift assertion

Priority: P0 | Estimated Hours: 6h | Bounded Context: Integration & Process Standardization

Summary: Convert version.yml from its current push-to-main-on-.changeset/** trigger (which opens a release PR that a human merges, later tagged by tag-on-changeset-merge.yml) to a workflow_dispatch-only workflow that bumps the fixed group atomically, syncs root package.json, generates the changelog, commits directly, and pushes the v{X.Y.Z} tag itself — folding in the tagging responsibility that tag-on-changeset-merge.yml (deleted in T-4) currently owns.

Type: Feature Implementation

Description: Today's version.yml: trigger is push to main filtered on .changeset/** + workflow_dispatch; on a changeset it runs pnpm changeset version, syncs versions, commits chore: release v$CLI_VERSION, pushes a changeset-release/v{X}-{run} branch, and opens a PR via actions/github-script. A human merging that PR is what tag-on-changeset-merge.yml watches for to create the tag. This task removes the PR-and-merge step entirely: the workflow becomes workflow_dispatch-only, commits straight to main, and pushes the tag in the same run — so "two runs per release" (this + release.yml) holds with no intermediate human-merge step.

Acceptance Criteria:

  • Primary deliverable: version.yml with on: workflow_dispatch only (no push trigger); same run bumps, commits, and pushes the tag
  • Quality standard: idempotent — a second dispatch with no remaining changesets is a no-op (not an empty bump/tag)
  • Integration requirement: fixed-group bump stays atomic; a group-membership drift (new package added, not joined to the group) fails loudly
  • Verification method: dispatch against a branch/throwaway repo with 0 and >0 changesets; inspect the resulting commit + tag

Technical Requirements:

  • Functionality: pnpm changeset version + sync-version (existing script) + commit + git tag/git push --tags; skip cleanly when no changesets
  • Performance: single job, no new heavy steps
  • Security: keep using secrets.GITHUB_TOKEN (no PAT) for the push; needs contents: write (already granted)
  • Compatibility: same Node 20 / pnpm 10.15.0 pins as ci.yml/release.yml

Implementation Approach:

  • Technical Design: reuse the existing "Check for changeset files" + "Create version commits" steps; replace the branch+PR steps with a direct git push origin main and git tag v$CLI_VERSION && git push origin v$CLI_VERSION; add a fixed-group assertion step before the bump
  • Bounded Context & Modules: .github/workflows/version.yml; possibly packages/dev-tools for a testable "fixed-group matches published set" assertion script (reused by T-4's verification)
  • Files to Modify/Create:
    • .github/workflows/version.yml - trigger + direct-commit-and-tag rewrite
    • packages/dev-tools/src/** (optional) - fixed-group drift assertion, unit-testable
  • Technical Standards References: .pair/adoption/tech/architecture.md § Tooling Package Boundaries (dev-tools owns release-pipeline logic)

Dependencies:

Implementation Steps:

  1. Add the fixed-group-vs-published-set assertion (see Open Question below on what "published set" means) as a pre-bump step
  2. Remove the push trigger; keep workflow_dispatch
  3. Replace the branch-creation + PR-creation steps with a direct commit to main + tag creation + push
  4. Confirm the "no changesets" path is a true no-op (exits 0, no commit, no tag)
  5. Dispatch against a throwaway repo/branch to confirm bump + changelog + commit + tag in one run

Testing Strategy:

  • Unit Tests: fixed-group assertion script, if extracted
  • Integration Tests: workflow dispatch dry-runs on a throwaway repo (paired with T-7)
  • Manual Testing: inspect the resulting commit and tag after a dispatch with real accumulated changesets

Notes: Open question — "published set" is ambiguous. .changeset/config.json's fixed group lists 8 packages (@pair/pair-cli, @pair/brand, @pair/content-ops, @pair/knowledge-hub, @pair/eslint-config, @pair/prettier-config, @pair/website, @pair/ts-config), not the 7 the story text states. Of those 8, only @pair/pair-cli and @pair/eslint-config have private: false (i.e. are actually npm-publishable) — the other 6 are private: true. Confirm with the maintainer whether the assertion should cover (a) the 8-package fixed group as a monorepo-versioning invariant, or (b) the 2 actually-published packages, before writing the assertion.


T-3: Switch .changeset/config.json to @changesets/changelog-github

Priority: P1 | Estimated Hours: 2h | Bounded Context: Integration & Process Standardization

Summary: Replace the default "changelog": "@changesets/cli/changelog" entry in .changeset/config.json with @changesets/changelog-github, so generated CHANGELOGs link the originating PR and author per entry (type-grouping under Major/Minor/Patch headings is already the default Changesets behaviour, unaffected by this swap).

Type: Configuration

Description: .changeset/config.json currently uses the plain changelog generator (no PR links). @changesets/changelog-github needs adding as a dependency and configuring with the repo field (["@changesets/changelog-github", { "repo": "foomakers/pair" }]), and needs a GITHUB_TOKEN/GH_TOKEN with read access to PRs available in version.yml's environment at generation time (already has secrets.GITHUB_TOKEN).

Acceptance Criteria:

  • Primary deliverable: .changeset/config.json's changelog field set to ["@changesets/changelog-github", { "repo": "foomakers/pair" }]; package added to a relevant package.json's devDependencies
  • Quality standard: pnpm install succeeds; no lockfile drift beyond the new dependency
  • Integration requirement: version.yml's changeset-version step still runs cleanly with the new generator and existing GITHUB_TOKEN
  • Verification method: dispatch version.yml (or run pnpm changeset version locally with a token) against a changeset referencing a real merged PR and confirm the generated CHANGELOG entry links that PR

Technical Requirements:

  • Functionality: PR-linked, author-linked changelog entries
  • Performance: negligible — one extra GitHub API call per changeset at version time
  • Security: uses the existing GITHUB_TOKEN, no new secret
  • Compatibility: n/a

Implementation Approach:

  • Technical Design: config + dependency change only
  • Bounded Context & Modules: .changeset/config.json, root or packages/dev-tools devDependencies
  • Files to Modify/Create:
    • .changeset/config.json - swap changelog field
    • package.json (root, or wherever changesets deps live) - add @changesets/changelog-github
  • Technical Standards References: .pair/adoption/tech/tech-stack.md (Changesets already adopted; this task doesn't change the tool, only the changelog generator)

Dependencies:

  • Technical: @changesets/changelog-github npm package
  • Tasks: none blocking; verified together with T-2
  • Resource: none new

Implementation Steps:

  1. pnpm add -D @changesets/changelog-github at the appropriate workspace root
  2. Update .changeset/config.json's changelog field with the repo option
  3. Run pnpm changeset version locally (with a GITHUB_TOKEN) against an existing changeset to confirm PR-linked output
  4. Confirm version.yml's environment provides a token with sufficient scope

Testing Strategy:

  • Unit Tests: n/a (config change)
  • Integration Tests: exercised inside T-2/T-7's workflow dispatch verification
  • Manual Testing: inspect one generated CHANGELOG entry for a PR link + author

Notes: None.


T-4: Delete tag-on-changeset-merge.yml; remove all GH_RELEASE_TOKEN references

Priority: P0 | Estimated Hours: 2h | Bounded Context: Integration & Process Standardization

Summary: Delete .github/workflows/tag-on-changeset-merge.yml and confirm GH_RELEASE_TOKEN is referenced nowhere else in the repository — its only reference today is that workflow's "Create monorepo tag via REST API" step.

Type: Refactoring

Description: With T-2 making version.yml push its own tag directly, tag-on-changeset-merge.yml (which watches for a changeset-release/* PR merge and tags the merge commit via a PAT) becomes dead code. Deleting it retires the PAT per AC5/AC6's "consequence of AC4" relationship already noted in the story.

Acceptance Criteria:

  • Primary deliverable: .github/workflows/tag-on-changeset-merge.yml removed from the repo
  • Quality standard: grep -r GH_RELEASE_TOKEN (repo-wide, excluding .git) returns zero matches
  • Integration requirement: nothing else references this workflow (no workflow_call, no doc pointing users at merging a release PR)
  • Verification method: the grep above, plus a successful version.ymlrelease.yml dispatch chain (T-7) with no PAT anywhere in the run logs

Technical Requirements:

  • Functionality: n/a (deletion)
  • Performance: n/a
  • Security: removes a repo secret's last consumer — flag GH_RELEASE_TOKEN for removal from repo secrets once this merges (not part of this task's file changes, but note it for the maintainer)
  • Compatibility: n/a

Implementation Approach:

  • Technical Design: straightforward deletion, gated on T-2 having shipped (or shipping in the same PR)
  • Bounded Context & Modules: .github/workflows/
  • Files to Modify/Create:
    • .github/workflows/tag-on-changeset-merge.yml - delete
  • Technical Standards References: story AC5/AC6, edge case "Fixed-group drift"

Dependencies:

  • Technical: T-2 must already push tags directly, or this deletion breaks tagging entirely
  • Tasks: T-2
  • Resource: repo-secret removal for GH_RELEASE_TOKEN is a maintainer action outside this task's scope, but should be tracked (e.g. a comment on this story or a follow-up)

Implementation Steps:

  1. Confirm T-2's version.yml rewrite already pushes the tag directly (or land both in the same PR)
  2. Delete tag-on-changeset-merge.yml
  3. grep -r "GH_RELEASE_TOKEN" repo-wide to confirm zero remaining references
  4. Note to the maintainer: remove the GH_RELEASE_TOKEN secret from repo settings (outside this task's file scope)

Testing Strategy:

  • Unit Tests: n/a
  • Integration Tests: T-7's end-to-end release run is the real test that nothing depended on the deleted workflow
  • Manual Testing: repo-wide grep for the token name

Notes: Land in the same PR as T-2 to avoid an interval where releases can't be tagged.


T-5: /pair-process-implement — infer and state changeset bump type

Priority: P1 | Estimated Hours: 4h | Bounded Context: Integration & Process Standardization (Development Collaboration for skill authorship, per the dataset/mirror split)

Summary: When /pair-process-implement's closing phase creates a changeset for a story's changes, infer the bump type (patch/minor/major) from the story type and commit convention, and state the inferred type in the skill's output so a wrong inference is visible before merge.

Type: Feature Implementation

Description: Today the pair-process-implement skill (dataset source: packages/knowledge-hub/dataset/.skills/process/implement/SKILL.md) contains no changeset-creation logic at all — this is net-new, not a modification of existing bump-inference code. Given AC5's ADL context (changesets are now added by the release author at release time, not per-PR — see T-1's blocking note), confirm with the maintainer whether this skill still creates a changeset per implemented story (as AC6 assumes) or whether AC6's scope changes to "the release author's workflow infers the bump type when batching changesets" instead.

Acceptance Criteria:

  • Primary deliverable: bump-type inference logic (patch/minor/major) added where changesets are authored, stating what was inferred and why (story type / commit convention signal)
  • Quality standard: dataset edited as source (packages/knowledge-hub/dataset/.skills/process/implement/SKILL.md), never the mirror directly; mirror regenerated; pnpm skills:conformance green
  • Integration requirement: inference is a suggestion surfaced to the human, not a silent write — a human can override before commit
  • Verification method: run the skill (or its dataset content review) against a fixture story of each type (bug fix → patch, feature → minor, breaking change → major) and confirm the stated inference matches

Technical Requirements:

  • Functionality: heuristic mapping story-type/commit-convention → bump type; output states the inference
  • Performance: n/a (authoring-time skill logic)
  • Security: n/a
  • Compatibility: n/a

Implementation Approach:

  • Technical Design: extend the implement skill's closing/PR-publish phase with a changeset-authoring step that derives and states the bump type
  • Bounded Context & Modules: packages/knowledge-hub/dataset/.skills/process/implement/SKILL.md (source); mirrored to .claude/skills/pair-process-implement/SKILL.md and the other 5 target directories
  • Files to Modify/Create:
    • packages/knowledge-hub/dataset/.skills/process/implement/SKILL.md - add bump-type inference step
    • (generated) all mirror targets, via the existing distribution/regeneration tooling
  • Technical Standards References: .pair/adoption/tech/architecture.md § Skills Distribution (dataset-as-source, mirrors regenerated, never hand-edited)

Dependencies:

  • Technical: none new
  • Tasks: none — independent of T-1..T-4
  • Resource: mutex with any other card touching pair-process-implement (per this story's own Shared Components note)

Implementation Steps:

  1. Confirm scope against the AC1/ADL-2026-08-20 conflict (does this skill still author a changeset per story, or does inference move to the release-author flow?)
  2. Edit the dataset source SKILL.md to add the bump-type inference + stated-output step
  3. Regenerate mirrors via the existing distribution tooling
  4. Run pnpm skills:conformance and confirm green
  5. Validate against one fixture story per bump type

Testing Strategy:

  • Unit Tests: n/a (skill content, not code) — conformance check is the automated gate
  • Integration Tests: pnpm skills:conformance
  • Manual Testing: dry-run the skill's changeset-authoring step against fixture stories of each type

Notes: Scope depends on resolving the same ADL conflict flagged in T-1 — do not implement in isolation from that resolution.


T-6: Update RELEASE.md (and DEVELOPMENT.md pointer) to the two-workflow dispatch-only flow

Priority: P1 | Estimated Hours: 2h | Bounded Context: Integration & Process Standardization

Summary: Rewrite RELEASE.md's "Overview" diagram, "Creating a Release" steps, and "Workflows" table to describe the new two-workflow (version.ymlrelease.yml) dispatch-only flow, removing all mention of the release PR and tag-on-changeset-merge.yml. DEVELOPMENT.md only points at RELEASE.md for the release process, so the substantive rewrite happens there.

Type: Documentation

Description: RELEASE.md currently documents the pre-story 4-workflow chain (pnpm changeset add → merge → version.yml opens a PR → merging that PR triggers tag-on-changeset-merge.yml → tag push triggers release.yml). This entire document needs rewriting to reflect: dispatch version.yml manually → it commits + tags directly on main → dispatch release.yml with that version. The DoD checklist item names DEVELOPMENT.md, but that file only links to RELEASE.md — the actual content to update lives there.

Acceptance Criteria:

  • Primary deliverable: RELEASE.md's Overview diagram, "Creating a Release" steps 2-4, and "Workflows" table rewritten for the two-workflow dispatch-only model; DEVELOPMENT.md's pointer sentence checked for accuracy
  • Quality standard: no references remain to a "version PR", "Tag workflow", or the release-PR merge step
  • Integration requirement: content matches the actual version.yml/release.yml behaviour after T-2/T-3/T-4 land
  • Verification method: manual read-through matched against the shipped workflow files

Technical Requirements:

  • Functionality: n/a (documentation)
  • Performance: n/a
  • Security: n/a
  • Compatibility: n/a

Implementation Approach:

  • Technical Design: rewrite in place
  • Bounded Context & Modules: repo root docs
  • Files to Modify/Create:
    • RELEASE.md - rewrite Overview, Creating a Release, Workflows table
    • DEVELOPMENT.md - verify its RELEASE.md pointer still reads correctly
  • Technical Standards References: none — plain docs, but must reflect the ADL 2026-08-20 changeset-timing convention too (step 1's "when" language is already correct there and should not regress)

Dependencies:

  • Technical: none
  • Tasks: T-2, T-3, T-4 must have landed (or be in final form) so the doc describes real behaviour, not the plan
  • Resource: none

Implementation Steps:

  1. Rewrite the Overview ASCII diagram to two workflows
  2. Rewrite "Creating a Release" steps 2-4 (dispatch version.yml, then dispatch release.yml — no PR-merge step)
  3. Update the "Workflows" table (remove the tag-on-changeset-merge.yml row)
  4. Cross-check against T-2/T-3/T-4's actual shipped files
  5. Confirm DEVELOPMENT.md's pointer sentence still makes sense

Testing Strategy:

  • Unit Tests: n/a
  • Integration Tests: pnpm docs:staleness if it covers this file
  • Manual Testing: read-through against the real workflow YAML

Notes: None.


T-7: Perform and verify one real release through the new pipeline (exactly two runs)

Priority: P0 | Estimated Hours: 3h | Bounded Context: Integration & Process Standardization

Summary: Exercise the rewritten pipeline end to end — on a throwaway repo mirroring the fixed group if a real release isn't due, otherwise a real one — and verify exactly two workflow runs occur (one version, one release) with no PAT involved anywhere.

Type: Testing

Description: AC7 exists because a release pipeline is only honestly tested by releasing. This task dispatches version.yml, confirms the commit+tag land correctly, dispatches release.yml with that version, confirms the GitHub Release + artifacts are produced, and confirms the Actions run list shows exactly two runs for the release (no tag-on-changeset-merge.yml run, no extra build triggered by the version commit beyond what's expected).

Acceptance Criteria:

  • Primary deliverable: a completed release (real or throwaway-repo) through the new pipeline
  • Quality standard: exactly two workflow runs attributable to the release (version, release)
  • Integration requirement: release.yml dispatched with a version that was actually tagged by the preceding version.yml run succeeds; a version that was never tagged fails loudly (edge case check)
  • Verification method: Actions run history for the release window, cross-checked against the two expected runs

Technical Requirements:

  • Functionality: full pipeline correctness
  • Performance: n/a
  • Security: confirm no GH_RELEASE_TOKEN/PAT appears in any run's logs or permissions: block
  • Compatibility: n/a

Implementation Approach:

  • Technical Design: use a throwaway repo cloned with the fixed-group structure if no real release is due yet (pattern already used for branch-protection work per github-implementation.md); otherwise perform the real release
  • Bounded Context & Modules: GitHub Actions run history, version.yml, release.yml
  • Files to Modify/Create: none (verification task; may add notes to RELEASE.md if T-6 needs a correction discovered here)
  • Technical Standards References: story AC7, github-implementation.md's throwaway-repo pattern

Dependencies:

  • Technical: T-2, T-3, T-4 fully landed
  • Tasks: T-2, T-3, T-4 (and ideally T-6, so the doc matches what's verified)
  • Resource: dispatch permissions on the target repo (real or throwaway)

Implementation Steps:

  1. Accumulate or write test changesets covering the fixed group
  2. Dispatch version.yml; confirm commit + tag on main
  3. Dispatch release.yml with the resulting version; confirm GitHub Release + artifacts
  4. Count workflow runs in the Actions history for the window — must be exactly 2
  5. Attempt release.yml dispatch with an untagged version — confirm it fails loudly (edge case)
  6. Attempt a second version.yml dispatch with no remaining changesets — confirm no-op (edge case)

Testing Strategy:

  • Unit Tests: n/a
  • Integration Tests: this task is itself the integration test
  • Manual Testing: Actions UI run-count inspection; GitHub Release page inspection

Notes: This is the story's real proof of done — do not mark the story complete without this task's evidence recorded (run URLs/IDs) in the PR or a comment on this issue.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    risk:yellowClassification: medium risk tieruser storyWork item representing a user story

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions