docs: correct the version-bump gate and branch-refresh documentation
ADR-0022 and gates.md now state the merge-base baseline, the fail-closed cases, the PyYAML requirement and the multi-ref push gap (shared with check-release-needed); the new hook gets its own gates.md group. Both ADR additions follow each file's amendment format. README and AGENTS.md now say to discard a refreshed apm.lock.yaml on a feature branch, and that an .apm/ edit is live only once it is on the remote's main. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -77,38 +77,49 @@ presence is no longer in question. `.pre-commit-config.yaml`'s `skill-frontmatte
|
||||
to require the field, closing the gap #113 and #118 both named in the same audit pass: a stated rule
|
||||
with nothing enforcing it drifts the same way an unstated one does.
|
||||
|
||||
## Amendment (2026-09-16) — the bump is enforced at push, not only required to exist
|
||||
## Amendment (2026-09-16): the bump is enforced at push, not only required to exist
|
||||
|
||||
**Context.** Making the field mandatory did not make it move. The only thing that bumped it was
|
||||
`skill-author` Step 4, so every hand edit and every trim pass skipped the bump: on
|
||||
`docs/simplification-audit`, 17 of the 40 skill directories that changed against `main` carried
|
||||
the same `metadata.version` as `main`, and `gitea` alone sat at six different values. Both
|
||||
validators checked presence and semver shape, never movement, so the field could not answer the
|
||||
question this ADR gives it — "did this change since I last read it". (Simplification audit
|
||||
finding 33.)
|
||||
Making the field mandatory did not make it move. The only thing that bumped it was `skill-author`
|
||||
Step 4, so every hand edit and every trim pass skipped the bump: on `docs/simplification-audit`, 17
|
||||
of the 40 skill directories that changed against `main` carried the same `metadata.version` as
|
||||
`main`, and `gitea` alone sat at six different values. Both validators checked presence and semver
|
||||
shape, never movement, so the field could not answer the question this ADR gives it — "did this
|
||||
change since I last read it". (Simplification audit finding 33.)
|
||||
|
||||
**Decision.** `scripts/check-skill-version-bump.sh` runs as a pre-push hook on every push. For each
|
||||
skill directory under `plugins/*/.apm/skills/` that differs between the pushed ref and its
|
||||
merge-base with `main`, ignoring `tests/`, the pushed `metadata.version` must be strictly greater
|
||||
than `main`'s.
|
||||
`scripts/check-skill-version-bump.sh` now runs as a pre-push hook on every push, whatever the
|
||||
target branch. It takes its baseline from the merge-base of the pushed commit with `origin/main`
|
||||
(local `main` if `origin/main` does not resolve). For each skill directory under
|
||||
`plugins/*/.apm/skills/` that differs between the pushed commit and that merge-base, ignoring
|
||||
`tests/`, the pushed `metadata.version` must be strictly greater than the version the skill had at
|
||||
the merge-base — not the version on `main`'s current tip.
|
||||
|
||||
- **Baseline is the merge-base with `main`, not the previous commit.** Readers only ever see
|
||||
`main` — installs resolve against the default branch (ADR-0018) — so one bump per branch is what
|
||||
the field owes them. A per-commit check would bump a skill once per commit and inflate the
|
||||
number past meaning.
|
||||
- **"Greater", not "exactly one patch higher".** A second `skill-author` pass on the same branch
|
||||
that bumps again still passes.
|
||||
- **`tests/` is excluded.** No agent loads it; a fixture-only change does not change the skill.
|
||||
- **Skills absent from either side are exempt.** New, renamed and merged skills start fresh under
|
||||
the rules above; deleted skills have nothing to check.
|
||||
- **Every plugin is covered, `bin` included,** and the gate is repo-local — it is not exported
|
||||
through `.pre-commit-hooks.yaml`.
|
||||
The baseline is the merge-base, not the previous commit. Readers only ever see `main` — installs
|
||||
resolve against the default branch (ADR-0018) — so one bump per branch is what the field owes
|
||||
them. A per-commit check would bump a skill once per commit and inflate the number past meaning.
|
||||
The rule is "greater", not "exactly one patch higher", so a second `skill-author` pass on the same
|
||||
branch that bumps again still passes. `tests/` is excluded because no agent loads it; a
|
||||
fixture-only change does not change the skill. Skills absent from either side are exempt: new,
|
||||
renamed and merged skills start fresh under the rules above, and deleted skills have nothing to
|
||||
check. Every plugin is covered, `bin` included, and the gate is repo-local — it is not exported
|
||||
through `.pre-commit-hooks.yaml`.
|
||||
|
||||
**Considered options.** *Declare the field advisory* — cheapest, but concedes the field cannot do
|
||||
its job. *Drop the field* — rejected by this ADR already, and costlier now. *Check at commit
|
||||
time against `HEAD`* — rejected for the inflation above.
|
||||
The gate fails closed rather than passing when it has no trustworthy baseline: when neither
|
||||
`origin/main` nor `main` resolves, when the pushed commit shares no merge-base with it, and when
|
||||
only local `main` resolves and the pushed commit is that merge-base, since a local `main` the
|
||||
pushed commit already contains is no independent record of what shipped. It reads versions with
|
||||
`python3` and PyYAML and fails with a clear message if either is missing.
|
||||
|
||||
**Consequences.** The 17 unbumped skills took a patch bump in the commit that added the gate.
|
||||
Like `check-release-needed`, the gate only fires on a local `git push` through pre-commit: a merge
|
||||
made with Gitea's merge button runs no local hooks and is not checked. A typo fix in a skill now
|
||||
costs a version bump; that is the rule working, not noise.
|
||||
Three alternatives were rejected. Declaring the field advisory is the cheapest, but concedes the
|
||||
field cannot do its job. Dropping the field was rejected by this ADR already, and costs more now.
|
||||
Checking at commit time against `HEAD` was rejected for the inflation described above.
|
||||
|
||||
The 17 unbumped skills took a patch bump in the commit that added the gate. A typo fix in a skill
|
||||
now costs a version bump; that is the rule working, not noise.
|
||||
|
||||
The gate differs from `check-release-needed` in when it runs: that hook acts only when pre-commit
|
||||
reports a push to `main`, so a manual `pre-commit run --hook-stage pre-push` skips it, while this
|
||||
gate runs there too and checks `HEAD`. The two hooks share both known gaps. A merge made with
|
||||
Gitea's merge button runs no local hooks, so it is not checked. And pre-commit's pre-push
|
||||
integration checks only the first ref with new commits in a multi-ref push (`git push origin a b`,
|
||||
`git push --all`): `_pre_push_ns` in pre-commit's `hook_impl.py` returns after that ref, so the
|
||||
other refs are pushed unchecked.
|
||||
|
||||
Reference in New Issue
Block a user