docs(invariants): the forge-state acquisition model, decided once
Four queued checks (REMOTE-SYNC, PIN-DEAD, RELEASE-CURRENT, TAG-SIGNED) need
state a local clone does not have. STATE.md carried the mechanism as an open
question per check, which is how a two-call budget grows by accretion — asked
four times, answered four ways.
Decided once, from measurement:
- Refs go over the git protocol. `git ls-remote --tags` is anonymous, reports
annotated-vs-lightweight for free via the peeled `^{}` ref, and does not
share the API's rate-limit bucket: 20 rapid calls all returned, and an API
call immediately after still got 200. Stated as "does not consume the API
budget", not "unlimited" — the run was sequential and stopped at 20.
- Releases have no git equivalent, so RELEASE-CURRENT is the third API call
and the only new one.
- The URL comes from the register (`forge` + `org` + name), never `origin`.
`app-creator`'s origin is `ssh://git@…`; using it would work on this machine
and fail for every other reader — the anonymity invariant failing silently.
- TAG-SIGNED answers "is it signed" (`git cat-file tag`, local), never "does
the forge vouch for it" — the Verified badge needs a key registered there.
Also corrects the rate-limiter note: a steady 1 request/second tripped 429 at
around request 40, so the burst description was not the whole limit. Pacing
alone does not buy immunity; a smaller total does.
VERSION-DRIFT is recorded as approved, measured, and NOT built. The rule fires
on 15 of 18 tagged repos, twelve of them merely between releases. Its two
motivating cases are already answered elsewhere or need judgement no classifier
has. Recorded rather than left open, so the next session reads the measurement
instead of repeating it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015AkHEqTSr1k3HbeiHu1ggW
This commit is contained in:
parent
fe0d039de2
commit
425e7e9bdf
1 changed files with 36 additions and 0 deletions
36
CLAUDE.md
36
CLAUDE.md
|
|
@ -113,6 +113,28 @@ would recreate, in data, exactly the drift this plugin exists to remove.
|
|||
is a leaky bucket, not a fixed ban — a 20-25 request burst took up to ~15s
|
||||
to fully drain. `fetchWithRetry` defaults to `retries: 5` /
|
||||
`maxDelayMs: 8000` (23s worst case) to cover that.
|
||||
**Burst is not the only way in** (2026-08-12): a *steady* 1 request/second
|
||||
tripped 429 at around request 40, so pacing alone does not buy immunity —
|
||||
only a smaller total does. Recovery measured at 35s drain plus 3s pacing:
|
||||
12/12 through.
|
||||
- **Two acquisition channels, and refs are not one of the API calls.** Four
|
||||
queued checks need state the local clone does not have, and answering the
|
||||
mechanism question once is the point — asked per check, it gets four
|
||||
inconsistent answers and the budget grows by accretion. Anything answerable
|
||||
from *refs* goes over the git protocol: `git ls-remote --tags` is anonymous,
|
||||
reports annotated-vs-lightweight for free (the peeled `^{}` ref), and does
|
||||
**not** share the API's bucket — 20 rapid calls all returned, and an API call
|
||||
immediately after still got 200. Measured sequentially and only to 20, so it
|
||||
is "does not consume the API budget", not "unlimited". Releases have no git
|
||||
equivalent, so `RELEASE-CURRENT` is the third API call and the only new one.
|
||||
**Derive the URL from the register (`forge` + `org` + name), never from
|
||||
`origin`.** `app-creator`'s origin is `ssh://git@…`, which needs the
|
||||
operator's key — it would work on this machine and fail for every other
|
||||
reader, which is the anonymity invariant failing silently rather than loudly.
|
||||
Signature status splits the same way: `git cat-file tag` reads the signature
|
||||
block locally, while the forge's "Verified" badge needs a key registered
|
||||
there — so `TAG-SIGNED` answers *is it signed*, never *does the forge vouch
|
||||
for it*.
|
||||
- **Codepoints, not bytes, not UTF-16 units.** Use `[...s].length`. An em-dash
|
||||
exposes only the byte layer; astral characters expose the rest.
|
||||
- **The reader decides a link's level, not just what is required.** Root
|
||||
|
|
@ -168,6 +190,20 @@ would recreate, in data, exactly the drift this plugin exists to remove.
|
|||
once, as a count (`WARN`), never as fourteen findings. Left unrecorded, that
|
||||
`WARN` can never be cleared, which is the `titles` problem again; no
|
||||
acceptance record is built until a repo actually needs one.
|
||||
- **`VERSION-DRIFT` was approved, measured, and not built.** The proposed rule
|
||||
— the manifest carries a released version string while HEAD sits N commits
|
||||
past that tag — fires on **15 of the 18 tagged repos**, twelve of which are
|
||||
simply between releases (ms-ai-architect 131 commits, the catalog 262). A
|
||||
gate that fails a correct repository is the mechanism that gets gates
|
||||
switched off, and this one fails almost all of them. The two repos that
|
||||
motivated it are both already answered: guard's manifest claims `0.7.0` with
|
||||
no such tag, which is an existing `VERSION-TAG` `ERROR` (and the same root
|
||||
cause as org-ops' `PIN-DEAD` — the README pins `@v0.7.0` too); okf's case
|
||||
turns on *behaviour-changing* commits past the tag, which no classifier reads
|
||||
off git. Two legitimate conventions coexist here — bump-at-release and
|
||||
bump-first — and nothing in a clone says which one a repo follows. Recorded
|
||||
rather than deferred: a decision that is wrong is worse than no record, and
|
||||
the next session should not re-derive this measurement.
|
||||
- **No hook until the rule is precise.** A blocking gate that fails a correct
|
||||
repository is the mechanism that gets gates switched off.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue