Skip to content

docs(release): the release runbook for builders and the release-path pattern doc (#4805) - #4835

Merged
usirin merged 3 commits into
mainfrom
usirin/release-runbook-4805-3136F7E8
Aug 9, 2026
Merged

docs(release): the release runbook for builders and the release-path pattern doc (#4805)#4835
usirin merged 3 commits into
mainfrom
usirin/release-runbook-4805-3136F7E8

Conversation

@usirin

@usirin usirin commented Aug 3, 2026

Copy link
Copy Markdown
Member

Fixes #4805

Writes the release procedure down where the two audiences read it, grounded against the landed artifacts rather than the epic's prose — ADR 0239, .github/workflows/release-please.yml, .github/workflows/publish.yml, release-please-config.json, .release-please-manifest.json, and PR #4833 (the first real Release PR, read but not touched).

What changed

File What it carries
DEVELOPMENT.md → new ## Releasing The builder path: how a version is derived, what the standing Release PR is, a pre-merge checklist, what the merge does (tags → publish.yml → registry), and a fail-site table for reading a red publish run — including how to read a 403.
.patterns/release-path.md (new) The shape of the path (four surfaces, the tag seam) plus nine constraints a future change must not break, each with its enforcement site or ruling, and the ordered procedure for adding a third published package.
.patterns/index.md Lists the new doc under CI / pipeline.

Both docs point at ADR 0239 for the reasoning rather than restating it.

Facts the runbook carries

  • Routing is by changed path, not commit scope. Stated with the concrete failure case: fix(pipeline-cli): with a fabrika-only diff bumps fabrika-cli.
  • npm versions are immutable, and a release firing before a rename or manifest fix has landed bakes the error in permanently.
  • pnpm publish, never npm publish — npm cannot resolve catalog: and would ship a literal "catalog:".
  • Compiled JS, never raw .ts — Node refuses to strip types under node_modules.
  • releases_created is repo-wide; the workflow gates on the per-path <path>--release_created outputs, and the pattern doc says why so nobody "simplifies" it back.
  • ADR 0069 boundary — per-package changelogs vs the root CHANGELOG.md; pointing release-please at the root is banned.
  • How to read a 403 (corrected in repair round 1, re-corrected in round 2). Registered is not exercised, and the docs now assert no outcome in either direction. pipeline-cli's Trusted Publisher registration is proven by exercise — two green publish.yml release runs, and the published artifact carries the attestations only an OIDC publish stamps. fabrika-cli's is recorded on HUMAN: bootstrap-publish @kampus/fabrika-cli and register it as an npm Trusted Publisher for publish.yml #4800 but never exercised: its first release run is the first use of it, and that run is the proof. So a 403 means the registration does not match — either it never did (a first use) or it stopped, and both branches are readable by someone staring at a red run. Both docs keep the mechanism that makes a real one recognisable: publish.yml resolves → installs → typechecks → builds → publishes, so an OIDC failure lands after a green build and consumes no version — no version number is burned, and re-running after fixing the registration recovers cleanly. Both also state that registration state is a web-UI fact npm exposes over no CLI or registry API, so npmjs.com (Settings → Publishing) is the only authority.
  • Two ways to silently invalidate a registration. Trusted Publishing binds to publish.yml's exact filename — renaming or splitting it invalidates every registration. And the registration's environment field is deliberately empty because publish.yml declares no environment: key: a change that adds one must edit the registrations in the same change, or publishing breaks with a 403 that looks like nothing is wrong.
  • The pnpm pin correction from this issue's plan-gate amendment: the pin lives once in the root packageManager, and pnpm/action-setup must not gain a version: input. Recorded with the explicit "do not reintroduce it on ADR 0076's authority" warning, so a builder reconciling doc against file cannot manufacture the second pin.

Deviations

  • Declined guidanceSaid: the dispatch brief said the npm-version-immutability constraint has no ADR home. Did: both docs cite ADR 0239 for it, and the pattern doc states that ADR 0076 does not rule it. Why: 0239's Context rules it explicitly ("No ADR has ruled this before — this one does") on the @kampus/fabrika-cli@0.1.0 bootstrap near-miss, and its Records section notes an earlier draft's attribution to 0076 as corrected. Citing "no home" would have been wrong in the other direction. Disposition: no action needed — the citation is the accurate one.

  • Declined guidanceSaid: the dispatch brief said the compiled-JS-never-raw-.ts constraint has no ADR home. Did: the doc says "no live ADR rules this" and homes it to ADR 0076 §1 (superseded by 0103, which does not restate it) plus publish.yml's comment citing Published @kampus/epic-ledger@0.1.0 is unrunnable — ships src/*.ts bin, no build (ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING) #405. Why: the constraint is written down, just not in a live ADR; a flat "no home" would have over-claimed, and inventing a live citation would have been worse. Disposition: no action needed; nothing fabricated.

  • Declined guidanceSaid: Write the release runbook: the DEVELOPMENT.md section for builders and the .patterns/ doc for the release path constraints #4805's original constraint list asks the runbook to record that the pnpm/action-setup pin must stay on 10.x. Did: the docs record the pin as living once in the root packageManager, and warn against ever adding a version: input to pnpm/action-setup. Why: Write the release runbook: the DEVELOPMENT.md section for builders and the .patterns/ doc for the release path constraints #4805's 2026-08-03 plan-gate amendment corrects the original constraint, and the workflows agree — no pnpm/action-setup use in this repo carries a version: input. Disposition: the plan-gate amendment on Write the release runbook: the DEVELOPMENT.md section for builders and the .patterns/ doc for the release path constraints #4805 is the authority; no ADR needed.

  • (repair round 1) Declined guidanceSaid: the review-doc FAIL's remedy asked for the Trusted Publisher claim to be reworded as unconfirmed and needing a human eyeball, explicitly declining to resolve which way the registration question lands. Did: corrected the claim outright instead of hedging it — both docs now state that both packages are registered, cite HUMAN: bootstrap-publish @kampus/fabrika-cli and register it as an npm Trusted Publisher for publish.yml #4800 as the record, and delete the expected-403 guidance rather than soften it. The mechanism paragraph the gate asked to preserve is preserved, and the empty-environment hazard is now prominent in both docs. Why: HUMAN: bootstrap-publish @kampus/fabrika-cli and register it as an npm Trusted Publisher for publish.yml #4800 is closed/completed and pastes npm's own confirmation string with the field table (repository kamp-us/phoenix, workflow filename publish.yml, environment deliberately empty), which settles the fact the gate correctly declined to settle from the CLI. A hedge would still have sent a builder hunting for a failure that should not occur. Disposition: no ADR needed; both docs cite HUMAN: bootstrap-publish @kampus/fabrika-cli and register it as an npm Trusted Publisher for publish.yml #4800 as the record and state that registration status is a web-UI fact no CLI can confirm, so nothing here is asserted as agent-verified.

  • (repair round 1) Out-of-scope changeSaid: the repair brief scoped the round to the two flagged sites. Did: also amended .patterns/release-path.md §9 (the workflow-filename constraint) to carry the empty-environment: half of the same binding. Why: §9 is where that constraint is stated for a future change to read; leaving the hazard only in DEVELOPMENT.md would put it where a builder reads it and not where an editor of publish.yml does. Disposition: for the reviewer to judge; it is the same constraint, not a new one.

  • (repair round 2) Retracted premiseSaid: round 1's dispatch brief asserted that "both packages are registered, no expected 403, both runs should succeed", and the round implemented it faithfully (see the round-1 entry above). That premise has since been retracted by the dispatcher as an over-correction. Did: replaced both assertions with the observed distinction — pipeline-cli's registration is proven by exercise (two green publish runs; attestations on the artifact), fabrika-cli's is recorded on HUMAN: bootstrap-publish @kampus/fabrika-cli and register it as an npm Trusted Publisher for publish.yml #4800 but never exercised, so its first release run is the first use and therefore the proof. Neither doc now predicts an outcome in either direction, and the 403 diagnosis widens from "stopped matching" to "does not match — either it never did (first use) or it stopped". Why: on fabrika's first-ever OIDC publish, "a registration stopped matching" is wrong by construction — nothing had started matching — so the fail-site guidance misdiagnosed the single most likely 403 moment and would send a builder hunting a regression in a path that had never run. Disposition: no ADR needed; the correction is stated with its own re-derivable check (dist.attestations on the published artifact) rather than an asserted authority, and the empty-environment: hazard, the mechanism paragraph and the HUMAN: bootstrap-publish @kampus/fabrika-cli and register it as an npm Trusted Publisher for publish.yml #4800 citation are all preserved unchanged.

  • (repair round 2) Out-of-scope changeSaid: the round-2 brief scoped the fix to the two flagged doc passages. Did: also updated the How to read a 403 bullet in this body's Facts the runbook carries list, which restated the retracted claim. Why: that bullet is this PR's own summary of its diff; leaving it asserting "a 403 is not expected" would have left the body describing a head it no longer matches. Disposition: for the reviewer to judge — no ## Deviations entry was rewritten, and no doc content beyond the two flagged passages changed.

Verification

  • Control-plane classifier run, not assumed: pipeline-cli cp-classify classify over the three changed paths → not-control-plane [path-clear-no-content-source] (exit 3) — "no path matched the live CONTROL_PLANE_RE and no .decisions/** file is present … Proven ordinary."
  • The registration correction re-derived independently, not taken on report: HUMAN: bootstrap-publish @kampus/fabrika-cli and register it as an npm Trusted Publisher for publish.yml #4800 read directly — state: closed, state_reason: completed, closed 2026-08-03T04:35:00Z, with the 04:22:42Z comment carrying npm's confirmation and the four discharged ACs.
  • Every relative link in all three files checked to resolve on disk; the two cross-doc anchors (DEVELOPMENT.md#releasing, release-path.md#adding-a-third-published-package) match real headings.
  • Pre-commit leak-guard clean over all 3 doc surfaces; every path cited is repo-relative.
  • Docs-only diff: no code, no tests, no workflow files touched. PR chore: release main #4833 was read only, and neither modified nor merged.

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

No preview deploy

  • No preview deploy for this PR — its diff touches no deploy-relevant path, so no preview stack was minted and e2e is not applicable. (b22fd6a)
  • web — Stage pr-4835 torn down.

@usirin

usirin commented Aug 3, 2026

Copy link
Copy Markdown
Member Author

review-doc: FAIL @ b0d8581 — changes-requested

Reviewed-head: @ b0d8581

Verified PR #4835 against the acceptance criteria of #4805 (incl. its 2026-08-03 plan-gate amendment) + the doc-hygiene checklist. Class: non-blocking, doc-only. pipeline-cli cp-classify classify run independently over the three changed paths → not-control-plane [path-clear-no-content-source] (exit 3), confirming the author's report rather than inheriting it; pipeline-cli class-probe classify --namespacesreview-doc only (1 required namespace, no code/skill/UI class present).

This is a documentation PR whose value is accuracy, and it was gated as a factual document. The substance is unusually well-grounded — every mechanism claim below was checked against the committed artifacts on main, not the PR's prose, and every one of them holds. Two findings block, and neither touches the runbook's mechanics.

Acceptance criteria

  • [PASS] AC1 — DEVELOPMENT.md release section readable by a builder who has never cut a release — DEVELOPMENT.md:166-260 (## Releasing): version derivation, the standing Release PR, a pre-merge checklist, what merging does (tags → publish.yml → registry), and a fail-site table.
  • [PASS] AC2 — .patterns/ doc records the shape + each constraint — .patterns/release-path.md (new, 186 lines): four surfaces + the tag seam, then nine numbered constraints each with its enforcement site or ruling, incl. the workflow-filename binding (§9), the pnpm pin as amended (§4), pnpm publish pack-time catalog resolution (§2), and the publish-isolation-guard coupling (§6).
  • [PASS] AC3 — third-published-package procedure written down — .patterns/release-path.md:152-179, ordered 1–5, with the one-time human Trusted Publisher registration (step 5) and the bootstrap publish (step 4) that CI cannot perform, and the reason (a package absent from the registry cannot have a publisher registered).
  • [PASS] AC4 — both docs point at the ADR rather than restating it, and .patterns/index.md lists the doc — both open by routing the why to ADR 0239; the index row lands under ## CI / pipeline in the matching 3-column shape.
  • [PASS] AC5 — every path repo-relative and resolvable — 17/17 relative link targets present in the head tree; both cross-doc anchors (DEVELOPMENT.md#releasing, release-path.md#adding-a-third-published-package) match real headings. No home, absolute, scratch, or sibling-clone path anywhere.
  • [PASS] Amendment (the pnpm pin) — verified against the committed workflows, not taken on report. publish.yml:61 is a bare - uses: pnpm/action-setup@v4.1.0 with no with: block; a repo-wide sweep of all 44 pnpm/action-setup uses finds no version: input anywhere. The single pin is root package.json:6 "packageManager": "pnpm@10.27.0". The doc's "do not reintroduce it on ADR 0076's authority" warning is well-founded: 0076's title does claim the action was pinned to 10.27.0, and 0076 is superseded by [0103].

Provenance — both deviations from the dispatch brief landed accurately; no citation fabricated

Mechanism claims — each checked against the committed files on main

  • [PASS] Path-not-scope routing incl. the fix(pipeline-cli):-with-a-fabrika-diff case — release-please.yml header comment and ADR 0239 §1 (grounded in CommitSplit.split()); the doc's concrete failure case is faithful.
  • [PASS] pnpm publish never npm publish, and the catalog: reason — publish.yml final step is pnpm publish --access public --no-git-checks; its header comment gives the identical reason (npm would ship a literal "catalog:").
  • [PASS] Per-path <path>--release_created used instead of repo-wide releases_created, with the reason stated — release-please.yml's only if: keys on packages/pipeline-cli--release_created / packages/fabrika-cli--release_created; releases_created appears nowhere in the file. The doc records the non-obvious reason (it is releases.length > 0, so it looks trustworthy while gating one package on another's release) and names it as ADR 0239 §5 Hazard A — which is exactly what §5 says. A later "simplification" is guarded against.
  • [PASS] ADR 0069 root-changelog ban — ADR 0239 §7 + its Banned list; release-please.yml's header carries the same boundary.
  • [PASS] separate-pull-requests: false yielding one green and one red run — confirmed in release-please-config.json.
  • [PASS] Trusted Publishing binds to publish.yml's exact filename, a rename silently invalidating it — publish.yml's one-time-human-setup header states it in the same terms.
  • [PASS] The tag grammar is machine-read — publish-isolation-guard/gate.ts:31,138-153 reads the publish workflow, parses the anchored tag prefixes, and fails closed on drift and on zero published packages (ADR 0092), exactly as release-path.md §6 describes.
  • [PASS] 403 ordering — publish.yml step order is checkout → action-setup → setup-node → resolve tagpnpm install → typecheck → build → publish. So the resolve failure really does fire before install (as the fail-site table says) and the publish 403 really does fire after install/typecheck/build go green. The paragraph's whole value is this ordering, and it is right.
  • [PASS] PR chore: release main #4833 read-only — this PR's diff is three doc files; it neither modified nor merged chore: release main #4833, which is still OPEN on release-please--branches--main. Its stated contents (pipeline-cli 0.3.0, fabrika-cli 0.1.1) match chore: release main #4833's body. The runbook describes the Release PR as re-groomed on every push and sitting "until a human merges it" — consistent with the standing-open ruling — and nowhere instructs anyone to merge it or says when to cut.

Doc hygiene

  • [PASS] House-format — the .patterns/ doc reads as how-the-path-is-shaped, with the why routed to ADR 0239; DEVELOPMENT.md states current build/dev state for builders. Surfaces respected.
  • [PASS] Index row — under ## CI / pipeline, 3-column shape matching its neighbours; meets the index's own "when to add" criteria (2+ surfaces, non-obvious, and a future agent would otherwise invent a worse version — the doc exists precisely to stop the releases_created and second-pnpm-pin regressions).
  • [PASS] Links resolve — 17/17 targets + 2 anchors.
  • [PASS] No leaked local/home paths — added-lines scan via the shared matcher: clean (exit 0).
  • [N/A] Supersession — no prior decision replaced.
  • [N/A] Status sanity — no ADR in this diff.
  • [PASS] Single Diátaxis mode — the DEVELOPMENT.md release section is single-mode how-to. release-path.md is host-mode reference with one bounded, AC-required how-to section; the split is deliberate and well-drawn (it explicitly routes the builder-facing procedure to DEVELOPMENT.md and keeps only the third-package procedure, which has no other home). Not type-mixing.
  • [PASS] Clear, concise prose — active voice, concrete, no AI-tell density.
  • [N/A] ADR contradiction sweep — no .decisions/** file in this diff.

Blocking findings

  • [FAIL] claim-vs-ground-truth — the Trusted Publisher claim is asserted as settled fact at two sites, and the repo's own record contradicts it.

    .patterns/release-path.md:180-186 (## Current state): "fabrika-cli has no Trusted Publisher registration yet (step 5 above is outstanding), so a fabrika-cli-v* release 403s."
    DEVELOPMENT.md:240-247: "fabrika-cli has no registration yet, so any fabrika-cli-v* release 403s at the publish step."

    Both state a flat negative with no hedge, no as-of date, and no instruction to confirm — the reader is handed a settled fact.

    I am not resolving whether the registration exists, and this FAIL is not for being on either side of that question. Three things make the confidence framing itself the defect:

    1. The fact is not agent-verifiable. Independently confirmed: npm view @kampus/fabrika-cli --json exposes no trusted-publisher or provenance key at all — dist carries only {fileCount, integrity, shasum, signatures, tarball, unpackedSize}, and the full document contains zero occurrences of "trusted" or "provenance". Trusted Publisher status is a web-UI-only fact. Neither the author nor this gate can settle it from the CLI.
    2. The repo's own landed record points the other way. Issue HUMAN: bootstrap-publish @kampus/fabrika-cli and register it as an npm Trusted Publisher for publish.yml #4800"HUMAN: bootstrap-publish @kampus/fabrika-cli and register it as an npm Trusted Publisher for publish.yml" — is CLOSED as COMPLETED, and its 2026-08-03T04:22:42Z comment records npm's own confirmation string with a field table naming repository kamp-us/phoenix, workflow filename publish.yml, environment deliberately empty. The closing comment discharges that criterion explicitly.
    3. The doc was written after that record landed. The registration comment is 04:22:42Z and the close is 04:35:00Z; this head was authored 06:20:10Z — nearly two hours later.

    So a builder following this runbook today is told to expect a 403 that the tracker says was resolved before the sentence was written. That is exactly the failure mode the PR body itself names for the other constraints: a doc that reads clean and tells the reader something false.

    Remedy (wording, not a fact-finding): at both sites, state it as unconfirmed and needing a human eyeball rather than as settled — name the registration as reported complete on HUMAN: bootstrap-publish @kampus/fabrika-cli and register it as an npm Trusted Publisher for publish.yml #4800 but not verifiable from the CLI, date the claim, and point the reader at npmjs.com (Settings → Publishing) as the only authority. Keep the mechanism paragraph exactly as written: the shape of the failure (a 403 arriving late, after green install/typecheck/build, consuming no version) is correct and valuable regardless of which way the registration question lands, and should survive the rewording. The author already recognised both spots as time-sensitive — that recognition needs to live in the artifact, which is the surface a builder reads, not only in the PR conversation.

  • [FAIL] deviation-disclosure (§DEV) — the ## Deviations section is absent.

    The PR body carries ## What changed, ## Facts the runbook carries, ## Two provenance calls, stated rather than fudged, and ## Verification — and no ## Deviations heading; the string "deviation" does not appear anywhere in it. This PR owes the section: it has a linked issue (Fixes #4805), it is not an issueless carve-out, and no positively-established non-obligation applies (§DEV Who owes the section). Per §DEV, absent is not None. — a body with no heading fails closed, because the gate cannot tell "nothing to disclose" from "never considered it".

    Credit where due: the two departures from the dispatch brief are disclosed in substance, with Said/Did/Why, under ## Two provenance calls, stated rather than fudged, and both are correct. This is a structural miss, not a concealment — the cheapest possible repair. Add the ## Deviations heading and move those two entries under it in the §DEV four-field shape (Said / Did / Why / Disposition), naming the plan-gate amendment as the authority for the pnpm-pin wording.

Routing note. The claim-vs-ground-truth finding traces to #4805's stated goal (a runbook a builder can follow), so it is in-scope and blocks here rather than being filed separately. It is captured as the FAIL row above — the seam the repair round reads — and no acceptance criterion was appended to #4805.

Read the PR head (§HEAD): all files under review sourced from b0d8581eb9e98f0c8a3e1b89091be75bb8221023 via a per-run ref, never the launched checkout's working copy. No working tree was switched.

CI at head: green — 39 success, 7 skipped, zero failing or pending; ci-required success. Run-evidence present and head-bound (run-evidence run 30789988204, conclusion: success, head_sha = this head).

Failing items above must be addressed before this PR can merge. The PR stays open and unmerged; #4805 stays open and assigned. Re-request review once they're satisfied. review-doc does not mergeship-it is the authorized merge step.

Verdict-written: 2026-08-03T06:36:43Z

usirin added a commit that referenced this pull request Aug 3, 2026
… the empty-environment hazard (#4805)

The runbook stated at two sites that `@kampus/fabrika-cli` has no Trusted
Publisher registration and that a `fabrika-cli-v*` release therefore 403s. That
is false: the registration exists, recorded on #4800 with npm's own confirmation
(this repo, workflow filename `publish.yml`). Both published packages are
registered, so no 403 is expected, and the old text sent a builder hunting for a
failure that should not occur.

Corrected rather than softened, at both sites:

- `.patterns/release-path.md` `## Current state` and `DEVELOPMENT.md`'s 403
  paragraph now state both packages are registered, cite #4800 as the record,
  and say plainly that registration state is a web-UI fact — npm exposes no
  trusted-publisher field over the CLI or registry API, so npmjs.com
  (Settings -> Publishing) is the only authority and nothing here re-derives it.
- The mechanism explanation survives unchanged: publish.yml resolves ->
  installs -> typechecks -> builds -> publishes, so an OIDC failure lands after
  a green build and consumes no version. That is how you recognise a real
  failure; it is just no longer predicted.
- The standing hazard is now prominent in both docs: the registration's
  environment field is deliberately empty because publish.yml declares no
  `environment:` key. A change that adds one must edit the registrations in the
  same change or publishing silently breaks with a 403 that looks like nothing
  is wrong.

The fail-site table row for a 403 now reads as a registration that stopped
matching the workflow, not a missing one.

Addresses the review-doc FAIL on PR #4835.
@usirin

usirin commented Aug 3, 2026

Copy link
Copy Markdown
Member Author

review-doc: FAIL @ c70dbcf — changes-requested

Reviewed-head: @ c70dbcf

Re-gate of PR #4835 against #4805's acceptance criteria + the doc-hygiene checklist, at the new head. The prior FAIL was bound to b0d8581e and no longer binds; this verdict replaces it.

Class (probed, not assumed). pipeline-cli class-probe classify --namespaces over the 3 changed files → has-docsreview-doc only (no code, skills, or UI path). The control-plane classifier → CONTROL_PLANE_TOUCHED='', GUARD_TOUCHING='', 3 files scanned, 0 .decisions/** ADRs content-probed → not-control-plane, non-blocking. This marker binds ship-it.

Read the PR head (§HEAD). Every file under review was read from c70dbcfe61f3fb12f148831523216030c9668a75 through this run's own read-only PR ref, never from a local working copy, and no local tree was mutated. The head was re-resolved before and after posting.


Acceptance criteria

  • [PASS] DEVELOPMENT.md release section — ## Releasing carries derivation (path-not-scope routing), the standing Release PR (chore: release main, release-please--branches--main, github-actions[bot], chore: release main #4833 named), a three-item pre-merge checklist, what merging does (bumps → per-package tags → publish.yml → registry, plus changelog.yml), and the fail-site table. Reads for a builder who has never cut a release.
  • [PASS] .patterns/ doc records shape + constraints — .patterns/release-path.md carries the four surfaces + the tag seam and nine numbered constraints, each with an enforcement site or ruling: the workflow-filename binding (§9), the single-root pnpm pin with the "must not add a version: input" prohibition (§4), pnpm publish pack-time catalog: resolution (§2), and the publish-isolation-guard grammar coupling (§6). Verified §4 and §6 against publish.yml at head: pnpm/action-setup@v4.1.0 carries no version: input, and the two literal anchored arms (^pipeline-cli-v([0-9].*)$, ^fabrika-cli-v([0-9].*)$) are present as described.
  • [PASS] Third-package procedure — release-path.mdAdding a third published package, five ordered steps including the bootstrap pnpm publish (step 4) and the one-time human Trusted Publisher registration (step 5), with the explicit "CI cannot perform this first publish" reason.
  • [PASS] Both docs point at ADR 0239 rather than restating it; .patterns/index.md lists the new doc under CI / pipeline (index.md:146).
  • [PASS] Every cited path repo-relative and resolvable — all 14 relative link targets in release-path.md and all ./-relative targets in the ## Releasing section confirmed present in the head tree; the two cross-doc anchors (DEVELOPMENT.md#releasing, release-path.md#adding-a-third-published-package) match real headings. CI's check docs have no dead internal links is green at head.

Doc hygiene

  • [PASS] House-format — release-path.md reads as how-the-path-is-shaped with the why delegated to ADR 0239; DEVELOPMENT.md reads as current-state-for-builders.
  • [PASS] Index row — .patterns/index.md row present and consistent with the doc's contents.
  • [PASS] Links resolve — see AC5 evidence.
  • [PASS] No leaked local/home paths — added-lines scan via the shared matcher: clean.
  • [N/A] Supersession — no prior decision replaced.
  • [N/A] Status sanity — no ADR frontmatter in this diff.
  • [PASS] Single Diátaxis mode — release-path.md is reference throughout; the ## Releasing section is a how-to with a reference table, held to its own surface. No type-mixing.
  • [PASS] Clear, concise prose — active voice, concrete, no AI-tell density.
  • [PASS] deviation-disclosure (§DEV) — see F2 below.

F1 — [FAIL] claim-vs-ground-truth: the docs now assert an unobserved outcome (the other direction of the same defect)

The prior FAIL was right that the docs asserted a registration state they could not support. The repair corrected the fact — and then over-shot into a second unsupported claim, in the opposite direction.

What the docs say at head:

  • .patterns/release-path.md## Current state: "Both published packages are registered … so no publish 403 is expected" and "two publish runs, both expected green."
  • DEVELOPMENT.mdReading a 403: "Both published packages are registered, so a 403 is not expected — it means a registration stopped matching this workflow."
  • Fail-site table row: "Publish, with a 403 | An OIDC failure: the Trusted Publisher registration no longer matches this workflow."

What is verified. Registration is real and correctly bound — #4800 is closed/completed and its 04:22:42Z comment pastes npm's own confirmation with the field table (repository kamp-us/phoenix, workflow filename publish.yml, environment deliberately empty). That half of the repair is sound and the citation is not invented.

But registered is not exercised, and this gate verified that independently rather than taking it on report:

  • npm view @kampus/pipeline-cli --jsondist keys include attestations.
  • npm view @kampus/fabrika-cli --jsondist keys do not include attestations.

An OIDC/Trusted-Publishing publish stamps attestations; a token publish does not. So fabrika-cli@0.1.0 on the registry came from the manual bootstrap token, not from this path. Run history agrees: publish.yml has exactly two runs, both release-triggered and both green — pipeline-cli v0.2.0 (2026-07-23) and pipeline-cli v0.2.1 (2026-07-24). Zero fabrika runs. #4800's own closing comment says the same in words: "the workflow that would exercise that trust does not yet know about this package." (#4801 has since landed the resolve arm, so it now does — and it still has never fired.)

Why that makes the current wording a defect, not a nitpick. The docs' load-bearing job here is telling a builder how to read a red publish run. At the exact moment that guidance is most likely to be needed — fabrika's first-ever OIDC publish, the first use of a registration nobody has watched work — the docs say a 403 is not expected and, if one fires, that a registration stopped matching. For fabrika that diagnosis is wrong by construction: nothing had started matching yet. The doc would send a builder hunting a regression in a path that had never run.

Attribution — this is the operator's defect, not the author's. The repair brief handed to the lane over-corrected ("both packages are registered, there is no expected 403, both runs should succeed") and has since been retracted. The author implemented that brief faithfully and disclosed the reasoning under ## Deviations. Nothing here reflects on the lane's work.

Remedy (both sites). State only what is observed, in neither direction:

  • fabrika-cli's Trusted Publisher registration exists and is recorded on HUMAN: bootstrap-publish @kampus/fabrika-cli and register it as an npm Trusted Publisher for publish.yml #4800 — and is unexercised. Its first release run is the first use of it, and therefore the proof.
  • pipeline-cli's registration is proven by exercise — two green publish.yml release runs.
  • Keep the fail-closed framing and attach it to that first run: a 403 publishes nothing, so no version number is burned, and re-running the release after fixing the registration recovers cleanly.
  • Widen the fail-site row's reading of a 403 from "stopped matching" to "does not match — either it never did (a registration's first use) or it stopped (one of the two hazards below)."

Everything else in the repaired paragraphs is correct and should be preserved as-is: the mechanism (publish.yml resolves → installs → typechecks → builds → publishes, so an OIDC failure lands after a green build and consumes no version), the "registration state is a web-UI fact, npmjs.com is the only authority, never restate it as tool-verified" framing, and the #4800 citation.

F2 — [PASS] ## Deviations (§DEV) landed and is honest

The PR body carries ## Deviations with five entries in the Said/Did/Why/Disposition shape, two tagged (repair round 1). Checked each against the diff:

No undisclosed departure this gate could see.

Out-of-scope addition judged: the §9 duplication is warranted, not drift

The empty-environment: hazard now sits in both .patterns/release-path.md §9 and DEVELOPMENT.md. That is the repo's doc-surface contract working as intended, not duplication: §9 states the binding constraint where an editor of publish.yml reads it ("a change that adds an environment: must edit the registrations in the same change"), while DEVELOPMENT.md states the diagnostic where a builder reading a red run reads it ("two ways to invalidate a registration, both silent until a release 403s"). The two texts are not copies — they carry the same fact at different granularity for different readers. Keep both.

Nothing else moved — confirmed

Comparing the prior head to this one: 2 files, +45/−19, and every hunk is one of the three disclosed changes (the two flagged sites plus the §9 paragraph). .patterns/index.md is unchanged since the prior head. Everything the prior gate verified is intact at head: path-not-scope routing, pnpm publish + the catalog: reason, per-path <path>--release_created over repo-wide releases_created with its reason, the ADR 0069 root-changelog ban, separate-pull-requests: false, the filename-binding hazard, the ADR 0239 immutability citation with the "ADR 0076 does not rule this" correction, the compiled-JS "no live ADR rules this" framing homed to 0076 §1 + publish.yml's #405 comment, and the pnpm-pin amendment.

Adjacent state


Only F1 blocks. It is one paragraph in each of two files, and the remedy is fully specified above. The PR stays open and unmerged; #4805 stays open and assigned. No acceptance criterion was appended to #4805 — the remedy is fully specified here and the repair round is live, so a body append would add a second, redundant carrier.

review-doc does not merge. Re-request review once F1 is addressed.

Verdict-written: 2026-08-03T07:00:30Z

@usirin

usirin commented Aug 3, 2026

Copy link
Copy Markdown
Member Author

review-doc: PASS @ 655594e — merge-ready

Reviewed-head: @ 655594e

Re-gate of PR #4835 against the acceptance criteria of #4805 (incl. its 2026-08-03 plan-gate amendment) + the doc-hygiene checklist, at the new head. The prior FAIL was bound to c70dbcfe and no longer binds.

Routing, re-run here — not inherited.

  • pipeline-cli class-probe classify --namespaces over the 3 changed paths → has-docsreview-doc only (1 required namespace). No code root, no claude-plugins/** skills source, no apps/web/src/** UI path — no sibling gate is required.
  • pipeline-cli cp-classify classifynot-control-plane [path-clear-no-content-source]"no path matched the live CONTROL_PLANE_RE and no .decisions/** file is present … Proven ordinary." Non-blocking, so this PASS is a real ship-it go-ahead.

The one thing gated: the docs must assert no outcome in either direction

Ground truth re-derived here, independently, before reading the diff:

Check Result
npm view @kampus/pipeline-cli --jsondist keys include attestations
npm view @kampus/fabrika-cli --jsondist keys no attestations (only version 0.1.0 exists)
publish.yml run history exactly 2 runs — pipeline-cli v0.2.0 (2026-07-23) and v0.2.1 (2026-07-24), both green. Zero fabrika runs.
.github/workflows/publish.yml on main both resolve arms present; pnpm/action-setup@v4.1.0 with no version: input; no environment: key

An OIDC publish stamps attestations; a manual token publish does not. So the pipeline-cli registration is proven exercised, fabrika-cli@0.1.0 reached the registry through the bootstrap pnpm publish, and its registration has never fired.

The five required properties, each verified at the head:

  • [PASS] 1 — pipeline-cli registration proven by exercise. .patterns/release-path.md § Current state: "proven by exercise … two green publish.yml release runs, and the published artifact carries the publish attestations … that only an OIDC publish stamps." Matches ground truth exactly (2 runs, attestations present).

  • [PASS] 2 — fabrika-cli recorded but never exercised. Same section: "recorded, never exercised … no publish run has ever fired for the package. fabrika-cli@0.1.0 reached the registry through the bootstrap pnpm publish of step 4 above, not through this path (its artifact carries no attestations). Its first release run is the first use of that registration, and that run is the proof." Mirrored in DEVELOPMENT.md § Reading a 403, bullet 1. Both halves match ground truth.

  • [PASS] 3 — no outcome asserted in either direction. release-path.md:210 "So expect no outcome in either direction for that first run."; DEVELOPMENT.md:251 "Expect nothing either way for it." The retracted "no publish 403 is expected" and "two publish runs, both expected green" are both deleted — the closing sentence now reads "one over a registration proven by exercise and one over a registration being used for the first time." A sweep for outcome-predicting language across both files at the head returns exactly one other hit, release-path.md:184 ("between steps 3 and 5 the path is fail-closed and expected to be red"), scoped to the pre-registration window of a hypothetical third package — a mechanical certainty about a package with no registration at all, not a prediction about fabrika-cli, which is past step 5. Correctly scoped; not a residue of the retracted claim.

  • [PASS] 4 — the 403 diagnostic covers BOTH branches, readable at a red run. The fail-site table now reads "the Trusted Publisher registration does not match this workflow — either it never did, or it stopped." The prose below splits that into two labelled bullets, "It never matched" and "It stopped matching", states the discriminator in each (which package is past first use, and why), and tells the reader to "read which one you are in before hunting a regression". The prior round framing — stopped matching, wrong by construction for a first-ever publish — is gone from both the table row and the prose. A builder staring at a red fabrika-cli publish run lands on bullet 1 and is told it is the first exercise, not a regression.

  • [PASS] 5 — fail-closed framing intact. DEVELOPMENT.md: "nothing is corrupted by it — no version number is burned, because nothing was published, and re-running the release after fixing the registration recovers cleanly." release-path.md: "If it 403s, the path failed closed: nothing was published, no version number is burned, and re-running the release after fixing the registration recovers cleanly." Present at both sites, with the recovery half now explicit where round 1 carried only the no-version-consumed half.

  • [PASS] Written as a re-derivable check, not asserted authority. The registered-but-unexercised claim carries its own check inline: "(npm view @kampus/pipeline-cli --json → a dist.attestations key)". I ran that exact command; it agrees.

  • [PASS] Registration state still framed as a web-UI fact. Both sites retain "Registration state is a web-UI fact: npm exposes no trusted-publisher field over the CLI or the registry API" plus npmjs.com (Settings → Publishing) as the only authority, and release-path.md keeps "Never restate it as tool-verified." The attestation check does not violate that: it re-derives exercise (an OIDC publish happened), which the passage holds apart from registration state — that distinction is the whole point of the rewrite.

The author-disclosed extra: the "Facts the runbook carries" bullet in the PR body

Correct call. That bullet is this PR summarising its own diff, and it still restated the retracted "a 403 is not expected". Leaving it would have left the body describing a head it no longer matches — a reader arriving at the PR would have been handed the exact claim the diff landed to remove. It is disclosed as its own (repair round 2) Out-of-scope change entry with the reasoning stated, which is what §DEV asks for. Not a scope violation.

Confirming nothing else moved

The delta c70dbcfe..655594e8 is a single commit whose parent is c70dbcfe (no rebase, no force-move of content), touching 2 files, +40/−16.patterns/release-path.md (+26) and DEVELOPMENT.md (+30). .patterns/index.md is not in the delta; its row is unchanged from the previously-verified head and still resolves.

Every previously-verified claim re-read at this head and intact:

  • [PASS] Mechanism paragraph — resolve → install → typecheck → build → pnpm publish; the 403 lands after a green build; consumes no version. Both sites.
  • [PASS] Web-UI-fact framing — both sites (above).
  • [PASS] HUMAN: bootstrap-publish @kampus/fabrika-cli and register it as an npm Trusted Publisher for publish.yml #4800 cited as the record — both sites, and in release-path.md §9.
  • [PASS] Empty-environment: hazard — present in both sites (release-path.md §9 "The environment field is part of that binding, and it is the easier half to break"; DEVELOPMENT.md § Two ways to invalidate a registration). Re-verified against the workflow on main: it declares no environment: key.
  • [PASS] Path-not-scope routing — both sites, with the concrete fix(pipeline-cli):-bumps-fabrika failure case.
  • [PASS] pnpm publish over npm publish + the catalog: reason — release-path.md constraint 2, DEVELOPMENT.md merge step 3.
  • [PASS] Per-path --release_created, and releases_created being repo-wide — release-path.md constraint 5, verbatim as before.
  • [PASS] ADR 0069 changelog boundary + the root-CHANGELOG.md ban — constraint 7.
  • [PASS] separate-pull-requests: false — both sites; the closing sentence is reworded, the config fact is not.
  • [PASS] Trusted-Publishing filename binding — constraint 9, unchanged.
  • [PASS] ADR 0239 immutability citation + the explicit "ADR 0076 does not rule this" correction — constraint 1, unchanged.
  • [PASS] Compiled-JS "no live ADR rules this" framing — constraint 3, unchanged; still homed to superseded 0076 §1 plus the Published @kampus/epic-ledger@0.1.0 is unrunnable — ships src/*.ts bin, no build (ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING) #405 comment in publish.yml.
  • [PASS] pnpm-pin amendment — constraint 4, unchanged: the single root packageManager pin, the "must not add a version: input" warning, pnpm#11513, and the do-not-reintroduce-it-on-ADR-0076-authority block-quote. Re-verified against publish.yml on main.

The ## Deviations section: the five earlier entries are present and unmodified, and only two (repair round 2) entries were appended. The strongest evidence they were not rewritten is that the round-1 entry still records the now-retracted "both docs now state that both packages are registered" as its historical Did:, rather than being back-edited to agree with round 2. Append-only held.

Acceptance criteria (#4805 — unchanged this round, re-confirmed at the new head)

  • [PASS] AC1 — DEVELOPMENT.md § Releasing carries version derivation, the standing Release PR, the pre-merge checklist, what merging does, and what publishes.
  • [PASS] AC2 — .patterns/release-path.md records the four surfaces + the tag seam and nine constraints, incl. the filename binding, the (amended) pnpm pin, pnpm publish pack-time catalog resolution, and the publish-isolation-guard coupling.
  • [PASS] AC3 — the ordered "Adding a third published package" procedure, incl. the bootstrap publish and the one-time human Trusted Publisher registration, with the pre-registration 403 window stated.
  • [PASS] AC4 — both docs point at ADR 0239 for the reasoning rather than restating it; .patterns/index.md lists the new doc.
  • [PASS] AC5 — every cited path repo-relative and resolvable; the round-2 delta adds no new relative link.

Doc hygiene

  • [PASS] House-format — the .patterns/ doc reads as how-the-path-is-shaped, not a why-essay; DEVELOPMENT.md is current-state-for-builders.
  • [PASS] Index row — the .patterns/index.md row is present, consistent, and untouched this round.
  • [PASS] Links resolve — no new relative links in the delta; the two cross-doc anchors still match real headings.
  • [PASS] No leaked local/home paths — the added-lines scan is clean, and leak-guard scan-pr is clean over all landed comments.
  • [PASS] Supersession — n/a (no ADR touched; not a .decisions/** diff, so the ADR contradiction sweep does not fire).
  • [PASS] Status sanity — n/a for these surfaces.
  • [PASS] Single Diátaxis mode — single-mode: how-to (DEVELOPMENT.md § Releasing) and single-mode: reference (.patterns/release-path.md); the delta changes claims inside each, not the mode.
  • [PASS] Clear, concise prose — the two-branch 403 split is the clearest form that passage has taken across all three heads; no AI-tell density.
  • [PASS] deviation-disclosure (§DEV) — the round-2 correction and the out-of-scope PR-body edit are both disclosed, each with Said/Did/Why/Disposition. Nothing undisclosed that this gate could see.

Branch mechanics (verified, deliberately not gated — tracked as #4826)

The head branch was still pinned by an earlier lane worktree, so the scripted rebase refused. The author used the --ignore-other-worktrees escape in its own lane and committed on the real branch — confirmed here: the head commit parent is c70dbcfe, it sits on usirin/release-runbook-4805-3136F7E8, and it was never detached (so verified-push ran). No other worktree was removed or mutated; the primary checkout is on main. Sanctioned shape; not a finding.

Merge-readiness at this head

  • CI: 46 checks, 0 failed, 0 incomplete; mergeStateStatus: CLEAN, mergeable: MERGEABLE.
  • Run-evidence: the run-evidence workflow run succeeded with head_sha = 655594e8c6e3fc14fd12b0d8c7e43c4051093f5d, and its bundle artifact is present (97,786 bytes, unexpired). Head-bound.
  • PR chore: release main #4833 (chore: release main) and PR feat(fabrika): the /triage skill and its derived CLI contract (#4706) #4832 are both still open and untouched by this diff; this PR changes 3 doc files and no workflow, config, or manifest.

Read the PR head (§HEAD): every file under review was sourced from 655594e8c6e3fc14fd12b0d8c7e43c4051093f5d through a read-only per-run ref, never the launched checkout working copy. The head was re-resolved before and after composing this verdict; unchanged.

All checks pass. This PR is merge-ready. review-doc does not mergeship-it is the authorized merge step; merging will auto-close #4805 via Fixes #4805.

Verdict-written: 2026-08-03T07:25:45Z

@usirin
usirin added this pull request to the merge queue Aug 3, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 3, 2026
@usirin

usirin commented Aug 3, 2026

Copy link
Copy Markdown
Member Author

ship-it: merge-queue ejection detected — PR #4835 was enqueued but the queue dropped it without merging (still open, no longer queued, not merged). Enqueued 2026-08-03T07:29:45Z, removed_from_merge_queue 2026-08-03T07:35:55Z with no paired merged event. Likely a textual conflict on the batch ref or a combined-batch CI failure (ADR 0132; GitHub "Managing a merge queue"). Routing back to repair/re-queue — this is NOT a shipped state.

@usirin

usirin commented Aug 3, 2026

Copy link
Copy Markdown
Member Author

ship-it: merge-queue ejection detected — PR #4835 was enqueued but the queue dropped it without merging (still open, no longer queued, not merged). Likely a textual conflict on the batch ref or a combined-batch CI failure (ADR 0132; GitHub "Managing a merge queue"). Routing back to repair/re-queue — this is NOT a shipped state.

usirin and others added 3 commits August 9, 2026 10:28
…-path pattern doc (#4805)

Cutting a release stopped being knowledge held in an issue thread. Two surfaces,
each with its own job per the repo doc contract:

- `DEVELOPMENT.md` gains a `## Releasing` section for a builder who has never cut
  one: what derives a version (path routing, not commit scope), what the standing
  Release PR is, what to check before merging it, what the merge does (tags →
  publish.yml → registry), and how to read a red publish run.
- `.patterns/release-path.md` records the shape of the path and each constraint a
  future change must not break, with its enforcement site or ruling — and says so
  plainly where a constraint has no live ADR home.
- `.patterns/index.md` lists the new doc under CI / pipeline.

Both point at ADR 0239 for the reasoning rather than restating it. Grounded
against the landed artifacts (release-please.yml, publish.yml, the config and
manifest, PR #4833), not against the epic's prose.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… the empty-environment hazard (#4805)

The runbook stated at two sites that `@kampus/fabrika-cli` has no Trusted
Publisher registration and that a `fabrika-cli-v*` release therefore 403s. That
is false: the registration exists, recorded on #4800 with npm's own confirmation
(this repo, workflow filename `publish.yml`). Both published packages are
registered, so no 403 is expected, and the old text sent a builder hunting for a
failure that should not occur.

Corrected rather than softened, at both sites:

- `.patterns/release-path.md` `## Current state` and `DEVELOPMENT.md`'s 403
  paragraph now state both packages are registered, cite #4800 as the record,
  and say plainly that registration state is a web-UI fact — npm exposes no
  trusted-publisher field over the CLI or registry API, so npmjs.com
  (Settings -> Publishing) is the only authority and nothing here re-derives it.
- The mechanism explanation survives unchanged: publish.yml resolves ->
  installs -> typechecks -> builds -> publishes, so an OIDC failure lands after
  a green build and consumes no version. That is how you recognise a real
  failure; it is just no longer predicted.
- The standing hazard is now prominent in both docs: the registration's
  environment field is deliberately empty because publish.yml declares no
  `environment:` key. A change that adds one must edit the registrations in the
  same change or publishing silently breaks with a 403 that looks like nothing
  is wrong.

The fail-site table row for a 403 now reads as a registration that stopped
matching the workflow, not a missing one.

Addresses the review-doc FAIL on PR #4835.
…ther way (#4805)

The gate's F1: both docs asserted an unobserved outcome. pipeline-cli's
Trusted Publisher registration is proven by exercise (two green publish.yml
runs, attestations on the artifact); fabrika-cli's is recorded on #4800 but
has never fired, so its first release run is the first use of it.

Widen the 403 diagnosis from 'stopped matching' to 'does not match — either
it never did (first use) or it stopped', so a red first run does not send a
builder hunting a regression in a path that never ran. Keep the fail-closed
framing: a 403 publishes nothing, no version number is burned, re-running
after fixing the registration recovers cleanly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@usirin
usirin force-pushed the usirin/release-runbook-4805-3136F7E8 branch from 655594e to b22fd6a Compare August 9, 2026 17:30
@usirin

usirin commented Aug 9, 2026

Copy link
Copy Markdown
Member Author

review-doc: PASS @ b22fd6a — merge-ready

Reviewed-head: @ b22fd6a

Re-gate after the rebase onto current main. The prior review-doc: PASS @ 655594e8 is void by SHA-binding (ADR 0058); this verdict replaces it at the new head.

Class: non-blocking. pipeline-cli class-probe classify --namespaces over the 3 changed files → has-docsreview-doc only (no code, no skills, no UI path). §CP classifier: CONTROL_PLANE_TOUCHED='', GUARD_TOUCHING='', 3 files scanned, 0 .decisions/** ADRs — proven ordinary, so this marker binds ship-it.

Rebase integrity (the specific ask). git diff --stat origin/main...b22fd6a2 = 3 files changed, 333 insertions(+)zero deletions. That mechanically proves nothing was dropped from main's side of the conflict: main's new fabrika subsection (ending at the ADR 0242 paragraph) survives untouched as context immediately above the inserted ## Releasing. The PR's own side is complete too — all five subsections present (How a version gets derived / The standing Release PR / What merging it does / Reading a red publish run / Adding a third published package).

Verified PR #4835 against the acceptance criteria of #4805 + the doc-hygiene checklist:

Acceptance criteria

  • [PASS] AC1 — DEVELOPMENT.md release section readable by a first-time releaser: version derivation (release-please.yml, path-not-scope routing), the standing Release PR (chore: release main on release-please--branches--main, bot-authored) with a 3-item pre-merge checklist, what merging does (4 numbered steps: bumps → per-package tags → publish.yml on release: publishedchangelog.yml), and what publishes. DEVELOPMENT.md +116, new ## Releasing.
  • [PASS] AC2 — .patterns/release-path.md (new, 216 lines) records the shape (4-surface table + the tag seam) and 9 numbered constraints, each with an enforcement site or ruling. The four the AC names: workflow-filename binding = §9; the pnpm pin = §4, in the corrected form the 2026-08-03 plan-gate amendment mandates (single pin in the root packageManager, pnpm/action-setup must not gain a version: input, plus the explicit "do not reintroduce it on 0076's authority" warning); pnpm publish pack-time catalog resolution = §2; publish-isolation-guard coupling = §6.
  • [PASS] AC3 — "Adding a third published package": 5 ordered steps, with the bootstrap pnpm publish (step 4) explicitly stated as something CI cannot perform (Trusted Publishing cannot be registered for a package not yet on the registry) and the one-time human registration as step 5, plus the "expected to be red between steps 3 and 5" note. DEVELOPMENT.md links to it by anchor.
  • [PASS] AC4 — Both docs point at ADR 0239 for the reasoning instead of restating it (release-path.md intro + per-constraint ADR 0239 §N cites; DEVELOPMENT.md "The why is ADR 0239"). .patterns/index.md gains the row, in the CI / pipeline section, in the house 3-column shape.
  • [PASS] AC5 — Every cited path repo-relative and resolvable at head. Checked against the head tree: the 7 ADRs (0053/0069/0076/0083/0092/0201/0239), the 3 workflows, release-please-config.json, .release-please-manifest.json, publish-isolation-guard/gate.ts, both package dirs. Both cross-doc anchors match real headings (DEVELOPMENT.md#releasing## Releasing; release-path.md#adding-a-third-published-package## Adding a third published package). No absolute or machine-local path anywhere.

Doc hygiene

  • [PASS] House-format — .patterns/release-path.md reads as how-the-path-is-shaped (surfaces, constraints, enforcement sites), not a why-essay; the why is delegated to ADR 0239. DEVELOPMENT.md states current-state-for-builders, no retired context.
  • [PASS] Index row — pattern row present in .patterns/index.md under CI / pipeline, Doc/Topic/Read-when columns filled. No ADR in this diff, so the ADR-index rule is not engaged.
  • [PASS] Links resolve — every relative link target exists in the head tree (list above); no wikilinks; both in-repo anchors resolve.
  • [PASS] No leaked local/home paths — leak-guard over the added lines: clean, exit 0.
  • [N/A] Supersession noted + cross-linked — this diff supersedes no prior decision.
  • [N/A] Status sanity — no ADR frontmatter in this diff; .patterns/ docs carry no status field.
  • [PASS] Single Diátaxis mode — DEVELOPMENT.md ## Releasing: single-mode how-to (the fail-site table is how-to's diagnostic aid, not a second mode). .patterns/release-path.md: single-mode reference, with the "Adding a third published package" procedure as a bounded, separately-headed section — that co-location is what AC3 asks for, and DEVELOPMENT.md points at it rather than duplicating, so the split is the one the issue prescribed, not a drift.
  • [PASS] Clear, concise prose — active voice, concrete, no AI-tell density. House idioms (fail-closed, §N, ADR cites) read as established vocabulary.
  • [PASS] deviation-disclosure (§DEV) — the body carries a populated ## Deviations section: three declined-guidance entries (the two "no ADR home" claims, and the pnpm-pin wording), the round-1 correction, the round-2 retracted premise, and two out-of-scope changes each disposed. Nothing this gate detected is undisclosed.
  • [N/A] ADR contradiction sweep (Step 4a) — no .decisions/** file in the diff.

Specialist fan-out (Step 4b) — claim-vs-ground-truth, re-derived from source, not from the PR body

Every load-bearing factual claim was checked against the authoritative file or live state, since this doc's whole value is being true:

  • publish.yml — no NPM_TOKEN and no token fallback ✓; no environment: key ✓ (the §9 hazard is real); pnpm/action-setup@v4.1.0 with no version: input ✓; tag arms are literal anchored regexes ^pipeline-cli-v([0-9].*)$ / ^fabrika-cli-v([0-9].*)$ ✓; explicit build-before-publish and pnpm publish --access public ✓.
  • release-please.yml — gates on the per-path packages/*--release_created outputs; the string releases_created appears nowhere in the file ✓ (constraint 5 accurate).
  • release-please-config.jsonseparate-pull-requests: false ✓, both components ✓, last-release-sha/bootstrap-sha at the same commit ✓.
  • ADR citations — 0239 is accepted, its Context rules npm immutability and its Records section records the corrected 0076 attribution ✓ (so §1's "0076 does not rule this" is right); 0076 is superseded by [0103] and its title does say the 10.27.0 pin, which is exactly what §4's warning box guards against ✓; 0201 §4 is "Independent release cadence" ✓; 0239 §§1/4/5/7 all exist and say what is cited ✓.
  • The registration distinction, the passage repaired twice — verified independently and it now matches reality precisely. publish.yml has exactly two completed runs, both success, both pipeline-cli (v0.2.0, v0.2.1); zero for fabrika-cli. And the artifacts agree: @kampus/pipeline-cli carries dist.attestations, @kampus/fabrika-cli@0.1.0 does not. So "proven by exercise" vs "recorded but never exercised", and the widened 403 reading ("does not match — either it never did, or it stopped"), are both correct as written. The round-1 over-correction is fully retracted from the head.
  • Release PR chore: release main #4833 — title chore: release main, branch release-please--branches--main, github-actions[bot], open ✓.

Non-blocking observation, not a gate finding and no AC appended: DEVELOPMENT.md illustrates the standing Release PR with "#4833 … proposes pipeline-cli 0.3.0 and fabrika-cli 0.1.1", and #4833 now proposes fabrika-cli 0.2.0 — it re-groomed after this doc was written. The load-bearing claim (one Release PR carries all packages) is verified true, and the doc itself states the PR is re-groomed on every push to main, so an illustrative version number is a moving target by the mechanism being documented. Not worth a round-trip.

dangling-reference: none. omitted-case: none.

Read the PR head (§HEAD): all files under review sourced from b22fd6a2b02d6cd2e603cc1ad68e3bf33d874983 via a per-run fetched ref and git show "$PR_REF:<path>" — never the launched checkout's working copy, and never a checkout of the head.

All checks pass. This PR is merge-ready. review-doc does not mergeship-it is the authorized merge step; merging will auto-close #4805 via Fixes #4805.

Verdict-written: 2026-08-09T17:36:01Z

@usirin
usirin added this pull request to the merge queue Aug 9, 2026
Merged via the queue into main with commit f08e905 Aug 9, 2026
46 checks passed
@usirin
usirin deleted the usirin/release-runbook-4805-3136F7E8 branch August 9, 2026 17:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Write the release runbook: the DEVELOPMENT.md section for builders and the .patterns/ doc for the release path constraints

1 participant