Example
ephor has not been released yet: there is no vX.Y.Z tag and Cargo.toml says 0.1.0-dev. At origin/main 85dfd8a, before the first release can be cut, someone has to write one entry file per change under docs/changelog/unreleased/. That directory holds 251 of them today, for example feat-clean.added.md:
- **`ephor clean` gives back what builds took in every idle branch checkout,
through each project's own clean verb** ([§FS-017-clean](...)). The verb is bound
the way a check verb is: ...
release-minor.yml runs scripts/prepare_changelog_release.py stamp and then prepare <version> over those files. When the directory is empty, prepare refuses:
docs/changelog/unreleased/ holds no entry to release; its README.md is not one.
Write the release section first: one entry per change merged since the last tag, ...
The scheduled auto-bump.yml holds on the same condition, through prepare_changelog_release.py pending:
::notice::Changes since <tag> wait for their release section: docs/changelog/unreleased/ holds no entry — skipping.
After the change, prepare <version> writes the numbered section itself. It reads from the forge the pull requests merged since the previous tag (since the start of history for the first release), lists them newest first, and skips the docs- and CI-only ones the schedule already skips:
## 0.1.0
- [test: wait for a dropped job lock to read released instead of racing a forked child](https://github.com/agent-grounds/ephor/pull/179) (PR #179)
- [fix: a registry the schema refuses is refused whole](https://github.com/agent-grounds/ephor/pull/177) (PR #177)
- [fix: a runner stand-in is settled before the board runs it, so E2E-008's listing no longer flakes](https://github.com/agent-grounds/ephor/pull/176) (PR #176)
Nobody writes anything before a release. docs/changelog/unreleased/, stamp and pending are gone.
What is wanted
A release's notes are the titles of the pull requests merged since the previous tag, each linked to its pull request, newest first, as - [<title>](<url>) (PR #N), and the release writes them. prepare <version> reads them from the forge, notes <version> keeps publishing the section as the GitHub release notes, and the scheduled release runs whenever the substantive-paths gate passes. The fragment store, stamp, pending, the contributor rule that describes the store, and every hook and CI check that reads it go, and the spec says so. A maintainer, or the schedule, could then cut any release without writing a line first. This is the owner's direction for every repository under agent-grounds. rhei 0.6.0 was cut this way by hand (agent-grounds/rhei#437).
Why the contract cannot express it today
Under the spec, the current behaviour is correct. §FS-002-release opens with "a changelog written before each release". §FS-002-release.1 says that what has merged is written down before the release is cut, as one entry file per change under docs/changelog/unreleased/, sorted into Keep-a-Changelog sections. §FS-002-release.2 fixes the script's commands as stamp, prepare, notes and pending, and holds the scheduled release while pending prints 0. §FS-002-release.2.3 says an empty directory "means the write-up has not been done, not that nothing changed". §FS-002-release.1.1, .1.2, .2.1 and .2.2 exist only to serve the store. The store was a deliberate decision, reached through #149 and #156, and #156 explicitly left the write-up tooling out of scope. Removing the hand-written step means reversing those points.
What it would change for existing callers
The changed points are §FS-002-release (the lead, §1, §2 and §2.3), plus docs/changelog.md §1.1–1.3. §1.1, §1.2, §2.1 and §2.2 would be deleted and §6 narrowed. grund refs at 85dfd8a counts about 85 citing sites across 27 files. Most of them are in the store, its scripts (changelog_stamp.py, changelog_unreleased.py, changelog_history.py, changelog_switchover.py) and their tests, which are deleted rather than edited. FS-006-project-interface and FS-012-file-size each cite §1 from outside and need a re-read. The workflows change: auto-bump.yml loses gate_pending and the stamp step, release-minor.yml loses the stamp step, and ci.yml loses the scratch-release check.
Two rules have to be decided rather than inherited. First, prepare now needs the forge. §2.2 says forge failures "never fail a release", but without the forge there are no notes, so an unreachable forge and an empty range each need a stated rule (probably a refusal). Second, §REQ-002-parity.4 says a --json field is renamed or removed "only as a release notes it". Today the write-up keeps that duty by reading git diff <tag>..main -- assets/*.schema.json, and a list of pull request titles does not reliably carry it. The replacement has to name how that change still reaches the notes, for example a line prepare adds from the schema diff. The 251 entries already in docs/changelog/unreleased/ should be released once, in the release that switches over. Contributors lose nothing, because since #156 no pull request writes an entry.
Alternatives
One option is to keep the store and have an agent write the entries before each release. That works, but the same write-up cost an agent about 232k tokens for rhei 0.6.0, and the result was then replaced by the plain list. It also still needs someone to start it, so the scheduled release still cannot run unattended. A second option, the cheapest half-fix, is to write the list by hand into fragment files before dispatching release-minor.yml. The notes come out right, but every release still waits on a person. A third is to generate fragments from pull requests in a pull request at release time and then promote them. That keeps stamp, pending and the hold, and gains nothing.
Why it is worth it
The hand-written step is the only thing that keeps an ephor release, scheduled or manual, from running on its own, and §GOAL-004-handover asks that a mechanical next move be handed to a script wherever an algorithm can finish it. With 251 entries pending and no release yet, it is also what stands between ephor and its first tag. The same request is filed for rhei (agent-grounds/rhei#439), grund (agent-grounds/grund#433) and fissile (agent-grounds/fissile#88). Doing all four keeps the format they share the same.
Filed by grounded-ticket intake from ephor.30 (kind: feature).
Example
ephor has not been released yet: there is no
vX.Y.Ztag andCargo.tomlsays0.1.0-dev. At origin/main 85dfd8a, before the first release can be cut, someone has to write one entry file per change underdocs/changelog/unreleased/. That directory holds 251 of them today, for examplefeat-clean.added.md:release-minor.ymlrunsscripts/prepare_changelog_release.py stampand thenprepare <version>over those files. When the directory is empty,preparerefuses:The scheduled
auto-bump.ymlholds on the same condition, throughprepare_changelog_release.py pending:After the change,
prepare <version>writes the numbered section itself. It reads from the forge the pull requests merged since the previous tag (since the start of history for the first release), lists them newest first, and skips the docs- and CI-only ones the schedule already skips:Nobody writes anything before a release.
docs/changelog/unreleased/,stampandpendingare gone.What is wanted
A release's notes are the titles of the pull requests merged since the previous tag, each linked to its pull request, newest first, as
- [<title>](<url>) (PR #N), and the release writes them.prepare <version>reads them from the forge,notes <version>keeps publishing the section as the GitHub release notes, and the scheduled release runs whenever the substantive-paths gate passes. The fragment store,stamp,pending, the contributor rule that describes the store, and every hook and CI check that reads it go, and the spec says so. A maintainer, or the schedule, could then cut any release without writing a line first. This is the owner's direction for every repository under agent-grounds. rhei 0.6.0 was cut this way by hand (agent-grounds/rhei#437).Why the contract cannot express it today
Under the spec, the current behaviour is correct. §FS-002-release opens with "a changelog written before each release". §FS-002-release.1 says that what has merged is written down before the release is cut, as one entry file per change under
docs/changelog/unreleased/, sorted into Keep-a-Changelog sections. §FS-002-release.2 fixes the script's commands asstamp,prepare,notesandpending, and holds the scheduled release whilependingprints 0. §FS-002-release.2.3 says an empty directory "means the write-up has not been done, not that nothing changed". §FS-002-release.1.1, .1.2, .2.1 and .2.2 exist only to serve the store. The store was a deliberate decision, reached through #149 and #156, and #156 explicitly left the write-up tooling out of scope. Removing the hand-written step means reversing those points.What it would change for existing callers
The changed points are §FS-002-release (the lead, §1, §2 and §2.3), plus
docs/changelog.md§1.1–1.3. §1.1, §1.2, §2.1 and §2.2 would be deleted and §6 narrowed.grund refsat 85dfd8a counts about 85 citing sites across 27 files. Most of them are in the store, its scripts (changelog_stamp.py,changelog_unreleased.py,changelog_history.py,changelog_switchover.py) and their tests, which are deleted rather than edited. FS-006-project-interface and FS-012-file-size each cite §1 from outside and need a re-read. The workflows change:auto-bump.ymllosesgate_pendingand the stamp step,release-minor.ymlloses the stamp step, andci.ymlloses the scratch-release check.Two rules have to be decided rather than inherited. First,
preparenow needs the forge. §2.2 says forge failures "never fail a release", but without the forge there are no notes, so an unreachable forge and an empty range each need a stated rule (probably a refusal). Second, §REQ-002-parity.4 says a--jsonfield is renamed or removed "only as a release notes it". Today the write-up keeps that duty by readinggit diff <tag>..main -- assets/*.schema.json, and a list of pull request titles does not reliably carry it. The replacement has to name how that change still reaches the notes, for example a lineprepareadds from the schema diff. The 251 entries already indocs/changelog/unreleased/should be released once, in the release that switches over. Contributors lose nothing, because since #156 no pull request writes an entry.Alternatives
One option is to keep the store and have an agent write the entries before each release. That works, but the same write-up cost an agent about 232k tokens for rhei 0.6.0, and the result was then replaced by the plain list. It also still needs someone to start it, so the scheduled release still cannot run unattended. A second option, the cheapest half-fix, is to write the list by hand into fragment files before dispatching
release-minor.yml. The notes come out right, but every release still waits on a person. A third is to generate fragments from pull requests in a pull request at release time and then promote them. That keepsstamp,pendingand the hold, and gains nothing.Why it is worth it
The hand-written step is the only thing that keeps an ephor release, scheduled or manual, from running on its own, and §GOAL-004-handover asks that a mechanical next move be handed to a script wherever an algorithm can finish it. With 251 entries pending and no release yet, it is also what stands between ephor and its first tag. The same request is filed for rhei (agent-grounds/rhei#439), grund (agent-grounds/grund#433) and fissile (agent-grounds/fissile#88). Doing all four keeps the format they share the same.
Filed by grounded-ticket intake from
ephor.30(kind: feature).