forked from heavy-duty/ceremony
docs: add release-management doctrine
This commit is contained in:
parent
d225c68fa4
commit
ab60709f49
5 changed files with 112 additions and 2 deletions
|
|
@ -11,6 +11,8 @@ triage bug, and the move is to say so on the issue, not to guess.
|
||||||
- Respect dependency order: inside an epic, take the earliest unblocked
|
- Respect dependency order: inside an epic, take the earliest unblocked
|
||||||
unclaimed child. Between epics and strays, prefer the issue that unblocks
|
unclaimed child. Between epics and strays, prefer the issue that unblocks
|
||||||
the most other work.
|
the most other work.
|
||||||
|
- In a repository that adopts version epics, read [RELEASES.md](RELEASES.md)
|
||||||
|
before choosing among release-window members.
|
||||||
- **Your own red head outranks a new claim.** A failing check at the head
|
- **Your own red head outranks a new claim.** A failing check at the head
|
||||||
of a PR you authored is picked up **before claiming another issue** —
|
of a PR you authored is picked up **before claiming another issue** —
|
||||||
repairing your own red PR comes ahead of new work, which is why the
|
repairing your own red PR comes ahead of new work, which is why the
|
||||||
|
|
|
||||||
100
RELEASES.md
Normal file
100
RELEASES.md
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
# Release management
|
||||||
|
|
||||||
|
This file describes the release-management pattern available to governed
|
||||||
|
repositories. Adoption is per repository and operator-ruled: a repository
|
||||||
|
without version epics is not out of compliance. A repo-local roadmap is the
|
||||||
|
map; each epic remains the source of truth for its own release. Where an older
|
||||||
|
repo-local description differs from this file, this file governs.
|
||||||
|
|
||||||
|
## The ladder
|
||||||
|
|
||||||
|
Represent each planned release with one version epic. The epic is the working
|
||||||
|
surface for that release: it states the goal, names the members, and records
|
||||||
|
the ordered waves as checklists. Keep the machine-readable progress checklist
|
||||||
|
under the exact heading `## Task list`; the issue-flow sweep reads that heading
|
||||||
|
when it decides whether to nudge triage about a completed epic.
|
||||||
|
|
||||||
|
Keep a short repo-local roadmap beside the epics. The roadmap shows the whole
|
||||||
|
ladder and points to each working surface; it does not duplicate the live
|
||||||
|
member lists or ordering. crew's roadmap discussion [heavy-duty/crew#338](https://github.com/heavy-duty/crew/discussions/338)
|
||||||
|
maps the ladder whose `0.1.2` working surface moved from the crufty ledger
|
||||||
|
[heavy-duty/crew#162](https://github.com/heavy-duty/crew/issues/162) to
|
||||||
|
[heavy-duty/crew#346](https://github.com/heavy-duty/crew/issues/346).
|
||||||
|
|
||||||
|
## Gates
|
||||||
|
|
||||||
|
Each version epic declares the predecessor that opens it. Special ordering —
|
||||||
|
a double gate or an out-of-chain gate — is written explicitly on that epic;
|
||||||
|
there is no hidden global schedule. Shipping closes the current epic, and its
|
||||||
|
close is the signal for triage to open the next release-init cycle by hand.
|
||||||
|
Version epics carry `epic`, not a queue label: `ready` offers work to builders,
|
||||||
|
and builders never pick epics.
|
||||||
|
|
||||||
|
The gate orders windows, not their contents. Members enter a release only by
|
||||||
|
decision during release-init. The double gate on heavy-duty/crew#163 and the
|
||||||
|
out-of-chain track on [heavy-duty/crew#348](https://github.com/heavy-duty/crew/issues/348)
|
||||||
|
are worked examples of exceptions declared where they apply.
|
||||||
|
|
||||||
|
## Release-init
|
||||||
|
|
||||||
|
The preceding epic's close is the trigger. Triage then runs five steps:
|
||||||
|
|
||||||
|
1. Mint the epic's “to mint when this arc opens” list together with findings,
|
||||||
|
deferred work, and discussion outcomes accumulated since the epic was
|
||||||
|
written. Each member initially declares `Blocked by <the epic>`.
|
||||||
|
2. Graph hard `Blocked by` edges and same-file clusters on the epic.
|
||||||
|
3. Write the waves into the epic body as checklists in claim order, with a
|
||||||
|
separate verification lane and the progress view under `## Task list`.
|
||||||
|
4. Ask the operator to bless the order, then have triage open the first wave
|
||||||
|
by applying the flip mechanics below. The operator's blessing is the one
|
||||||
|
step this chain never automates.
|
||||||
|
5. Ship through the repository's cut process, close the epic, and treat that
|
||||||
|
close as the trigger for the next window.
|
||||||
|
|
||||||
|
heavy-duty/crew#346 is the worked wave plan; its graph made both hard edges
|
||||||
|
and shared-file contention visible before builders entered the queue. If init
|
||||||
|
finds no work worth minting, the operator either folds the empty window into a
|
||||||
|
later release or skips the version, recording that ruling on the epic before
|
||||||
|
closing it unshipped.
|
||||||
|
|
||||||
|
## One primary window, declared parallel tracks
|
||||||
|
|
||||||
|
Run one primary release window by default. A cut takes whatever has landed, so
|
||||||
|
interleaving unrelated windows blurs both the release story and the evidence
|
||||||
|
behind it. Gates open windows; they do not silently admit members, so builders
|
||||||
|
still see one deliberately ordered queue.
|
||||||
|
|
||||||
|
The operator may declare a parallel track at init when its footprint is
|
||||||
|
disjoint from the primary window: another repository, another artifact, or
|
||||||
|
provably non-overlapping clusters. The declaration names the boundary and any
|
||||||
|
bridge work that must rejoin the primary. [heavy-duty/crew#348](https://github.com/heavy-duty/crew/issues/348)
|
||||||
|
is the worked example: its app and artifact form a parallel track while its
|
||||||
|
small crew-side bridge remains in the primary window.
|
||||||
|
|
||||||
|
## Flip mechanics
|
||||||
|
|
||||||
|
To admit a member, strike its live `Blocked by <the epic>` declaration and
|
||||||
|
swap `blocked` to `ready` in the same edit. Never preserve history by negating
|
||||||
|
the marker phrase — the blocker parser unions declarations even when prose
|
||||||
|
says they no longer apply. Preserve the old text only after striking or
|
||||||
|
rewriting the parseable clause, then verify the parser's resulting set.
|
||||||
|
|
||||||
|
Release membership is a decision, never a sweep default. Triage performs each
|
||||||
|
flip only after the operator blesses the wave; the issue-flow sweep may resolve
|
||||||
|
ordinary issue dependencies, but it does not choose a release's contents.
|
||||||
|
heavy-duty/crew#346 records the member-by-member flip that opened its first
|
||||||
|
wave.
|
||||||
|
|
||||||
|
## The ledger pattern
|
||||||
|
|
||||||
|
When a release epic has become too crufty to remain a clear working surface,
|
||||||
|
create a replacement and treat the old epic as a ledger. Do not close the old
|
||||||
|
epic until every live member declaration points at the replacement and the
|
||||||
|
blocker parser verifies the new set. Closing early can release every member
|
||||||
|
that still names the old issue.
|
||||||
|
|
||||||
|
The [heavy-duty/crew#162](https://github.com/heavy-duty/crew/issues/162) to
|
||||||
|
[heavy-duty/crew#346](https://github.com/heavy-duty/crew/issues/346)
|
||||||
|
transition is the worked example: all member declarations were re-pointed and
|
||||||
|
parse-verified before #162 closed; #162 remains the historical record while
|
||||||
|
#346 is the release's working surface.
|
||||||
|
|
@ -122,6 +122,7 @@ label): the approach, the decisions, the constraint list, and a
|
||||||
dependency-ordered task list of child issues. Children reference the epic;
|
dependency-ordered task list of child issues. Children reference the epic;
|
||||||
the epic's checklist is the progress view. Builders never pick the epic
|
the epic's checklist is the progress view. Builders never pick the epic
|
||||||
itself. Keep the checklist current — a stale epic misleads every scan.
|
itself. Keep the checklist current — a stale epic misleads every scan.
|
||||||
|
Repositories that adopt version epics follow [RELEASES.md](RELEASES.md).
|
||||||
|
|
||||||
## Backlog hygiene
|
## Backlog hygiene
|
||||||
|
|
||||||
|
|
|
||||||
3
changelog.d/248.md
Normal file
3
changelog.d/248.md
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Document the optional, operator-ruled release-epic flow for governed repositories.
|
||||||
|
|
@ -550,14 +550,18 @@ when the pinned taxonomy declares a core label the repository lacks.
|
||||||
Machinery is consumed by reference — GitHub fetches the workflows and
|
Machinery is consumed by reference — GitHub fetches the workflows and
|
||||||
actions above from the pin at run time — but documents have no runtime: an
|
actions above from the pin at run time — but documents have no runtime: an
|
||||||
agent reads the working tree it stands in. So the agent-facing doc set
|
agent reads the working tree it stands in. So the agent-facing doc set
|
||||||
(ceremony's `docs/VENDORED.txt`: AGENTS.md, TRIAGE.md, BUILDER.md,
|
declared by ceremony's `docs/VENDORED.txt` is vendored into each consumer at **`.ceremony/`**,
|
||||||
REVIEWER.md, LABELS.md) is vendored into each consumer at **`.ceremony/`**,
|
|
||||||
byte-identical to ceremony at the pin, plus a generated `.ceremony/README.md`
|
byte-identical to ceremony at the pin, plus a generated `.ceremony/README.md`
|
||||||
marking the directory machine-managed. `actions/docs-sync` owns the copy:
|
marking the directory machine-managed. `actions/docs-sync` owns the copy:
|
||||||
`--fix` writes it (and deletes what the manifest dropped — mirror means
|
`--fix` writes it (and deletes what the manifest dropped — mirror means
|
||||||
mirror), `--check` re-diffs it in CI on every PR, so a hand edit or a stale
|
mirror), `--check` re-diffs it in CI on every PR, so a hand edit or a stale
|
||||||
pin goes red instead of quietly governing.
|
pin goes red instead of quietly governing.
|
||||||
|
|
||||||
|
`RELEASES.md` joins that mirror with the first tag carrying ceremony#248.
|
||||||
|
It is **unreleased** until that tag exists: consumers add
|
||||||
|
`.ceremony/RELEASES.md` only with the ordinary pin bump and re-sync, never by
|
||||||
|
copying it ahead of their pinned doctrine set.
|
||||||
|
|
||||||
The consumer's ci.yml gains the guard alongside the others:
|
The consumer's ci.yml gains the guard alongside the others:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue