Skip to content

docs: the limits page catches up, and a Linux reader installs without Homebrew (#857, #843) - #878

Merged
stephrobert merged 3 commits into
mainfrom
fix/857-the-flagged-limits-sections-were-read
Nov 11, 2026
Merged

stephrobert merged 3 commits into
mainfrom
fix/857-the-flagged-limits-sections-were-read

Conversation

@stephrobert

@stephrobert stephrobert commented Nov 11, 2026 •

Copy link
Copy Markdown
Owner

Summary

This pull request carries two subjects, and that is worth saying first. #857 (the limits page catching up) and #843 (a Linux reader installing without Homebrew) were committed to the same branch by mistake, and separating them afterwards needs a reset --hard plus a force-push that this repository's hooks refuse — correctly. They are reviewable apart: the first two commits are #857, the last is #843.

They do belong to one goal, which is the next release being consistent: one is the page that states what feint does not do, the other is the first wall a stranger meets.


Part one — #857: eleven flagged sections read, three false

mise run limits:check had flagged eleven sections of docs/limits.md whose cited issues closed after anyone last looked. Eleven were read. Three were false.

The rule attached to that signal is that a closed issue is not a lifted limit, so each section is read and then either corrected or re-dated — and tools/docs/limits-acks.py refresh, which re-dates every section at once, is exactly what #857 exists to prevent.

The testplan's own caveat for a documentation diff is the subject of this pull request, word for word: "a document can be wrong in ways no gate reads; three of fifty limits had gone false on 2026-08-27." Three of eleven today.

The three that were false

"The Exoscale Terraform provider is refused, and why" — Terraform and OpenTofu have driven that pack since 2026-09-05, from the published v0.71.0, with no fork and no dev_overrides. The body already recorded the lifting, eighty lines down, and that is the defect: a reader who skims headings got the opposite of the truth, and a measurement under a false title is not one anybody finds. Retitled to the floor it actually is, with the refusal kept — a floor is meaningless without what a provider below it does, or a version check reads as friction.

"Identifiers are not checked against anything" — false since #126. --strict-catalog <file> refuses a create naming an image, a template or a machine type outside a declared catalogue, in all three packs (internal/providers/*/strict.go), and a declaration naming a kind no pack checks is itself refused. The default is unchanged and the cost paragraph now says so explicitly.

"A run presented as local can still reach the real cloud" — understated on its most load-bearing row. The Scaleway Object Storage escape was listed as live with a warning; since #849 it can be closed instead, and the section now names two boundaries that do not depend on the client cooperating rather than one. It also says plainly what has not changed: the interception is opt-in, so a client the run was not told about is exactly as free to leave. The default did not become safe; there is now a way to be sure.

Which document was stale, and how that was found

internal/cli's TestEveryConfidenceRowCarriesItsProof reddened on the renamed anchor before the push — and it settled the direction nobody would have checked first. That row in docs/confidence.md already read "with serve --strict-catalog, against the catalogue you declared". The confidence page had been right since #126 and limits.md was the one denying it.

Six anchors follow the two renamed headings, including the one under docs/history/: that snapshot must not read as the present, and a pointer that no longer resolves is a defect rather than a period detail.

The other eight

Read and re-dated, nothing changed. Four had simply never been acknowledged — the two snapshot-import sections (#850, #807), Elastic Metal (#631) and the reply-door measurement (#672) — and each is contemporaneous with the work it describes. Three absorb their cited issues inline already: the route-out section names #660, #684, #694, #696 and #697 in its own text, and the DHCP section names #395. The lifecycle section gained one line for the second opt-out it had not mentioned, the Outscale volume creating chain, which is what makes the 409 on a snapshot of a still-creating volume reachable at all.

What this is NOT

It is not the adoption item. "Is my stack in scope" over a 4,850-line page with no index is #873, opened separately and deliberately so: letting #857 stand in for it is how the reader-facing work disappears.

Type of change

  • A route added or changed
  • Bug fix
  • Refactor
  • Documentation
  • Chore / tooling
  • Security / supply chain

Checklist

Always

  • mise run check passes
  • No new external Go dependency
  • internal/core still knows no provider — untouched
  • Nothing in the diff could be written identically for another provider: the corrected sections are each about one provider's measured behaviour, and the identifier section states the rule for all three because the guard is in all three
  • mise run testplan was run, and what it printed was run — it prescribes docs:check and limits:check for this diff, both green

When a model wrote a substantive part of this

  • An Assisted-by: trailer names the tool and the model version
  • No conformance suite is touched and none is claimed; every measurement cited here is one already recorded in this repository, re-read rather than re-run, and the commit says which
  • No provider field name is added or changed

When a route is added or changed

N/A.

When behaviour a client can observe changes

  • No behaviour changes: this is the limits page catching up with behaviour that changed months ago
  • docs/limits.md updated — it is the subject
  • Both READMEs unchanged, and that is checked rather than assumed: the safety banner counts limits sections and the count did not move, so feint docs --check passes

When .github/workflows/ is touched

N/A.

When a machine runtime is involved

N/A.


Part two — #843: a first Linux apply from the published artefacts

The Quick Start opened with brew install stephrobert/feint/feint, and this project's audience is Linux engineers who mostly do not run Homebrew. Their route was docs/install.md: correct, forty lines, needs cosign, behind a link.

Six lines now sit under the Homebrew block, generated in both locales from the same version the install page is pinned to, so the two cannot drift. They still verify — the bytes are checked against the published checksums.txt before anything is installed, because what people reach for when a verified path is long is curl-to-shell. What the checksum does not prove is said rather than implied: it proves the bytes match the list, not who wrote the list, and the signature over the list stays in the install page.

Two details that are not decoration: the architecture is a case with a refusal rather than a default, and the block is wrapped in ( set -e … ) because a bare return outside a function is an error in an interactive shell and a bare exit closes the terminal somebody just pasted into.

tools/conformance/released-linux.sh takes those commands out of README.md and runs them, in a throwaway HOME, then hands the installed binary to the existing quickstart gate. Extraction rather than a copy, for the reason quickstart.sh reads its expected Apply complete! line out of the README. It refuses to start if any provider credential is visible.

Measured on 2026-11-11 against the published v0.15.0: the block installs, the binary reports 0.15.0, both quickstart stacks apply, both second plans are empty, both come down, the port is silent.

released-linux.yml runs on release, nightly and on demand — not on pull requests, because a pull request's README can legitimately pin a version that is not published yet, so gating one on it would make every release-preparation branch red by construction. It adopts night-report.yml, and that was not foresight: TestEveryScheduledWorkflowReportsOrSaysWhyNot reddened on the file before the job existed.

What it must never be read as proving: the example declares runtime: mode: off. This is a control plane, an address and a server the API describes. It is not Incus and not OVN, and the script's last line and the job summary both say so.

The acceptance record follows the merge. #843 asks for a clean, disposable Linux host; that is the workflow's first dispatch, which cannot happen until the file is on the default branch.

Related issues

Closes #857. Closes #843 once the first clean-host dispatch is recorded on it.

limits:check now answers green: 60 sections, 116 issues cited, every one confirmed after it closed. It also prints 6 issue(s) could not be asked on this run — a network refusal while reading GitHub, and the checker says so rather than treating silence as freshness, which is the third-branch discipline this repository asks of every measurement.

🤖 Generated with Claude Code

…alse (#857)

`mise run limits:check` had flagged eleven sections whose cited issues closed
after anyone last looked. The rule attached to that signal is that a closed
issue is not a lifted limit, so each one is read and then either corrected or
re-dated — and `tools/docs/limits-acks.py refresh`, which re-dates every
section at once, is exactly what #857 exists to prevent.

Eleven read. **Three were false**, and the three are worth naming because they
are the failure this page is supposed to catch rather than commit.

## "The Exoscale Terraform provider is refused, and why"

The heading and its first paragraph said the provider does not work and that
the reason is not something this emulator can fix. Terraform and OpenTofu have
driven the Exoscale pack since 2026-09-05, from the published v0.71.0, with no
fork and no `dev_overrides`.

The body already recorded the lifting, eighty lines down. That is the defect:
a reader who skims headings got the opposite of the truth, and a measurement
buried under a false title is not a measurement anybody finds. Retitled to the
floor it actually is, with the refusal kept — a floor is meaningless without
what a provider below it does, or a version check reads as friction.

## "Identifiers are not checked against anything"

False since #126. `--strict-catalog <file>` refuses a create naming an image,
a template or a machine type outside a declared catalogue, in all three packs
(`internal/providers/*/strict.go`), and `feint status` prints the declaration
so the mode is never silent. A declaration naming a kind no pack checks is
itself refused, so a line in that file cannot read as enforced while checking
nothing.

The default is unchanged and the cost paragraph now says so explicitly: a typo
in an image id is still not caught by default, and the reason it is not is
still the right one.

## "A run presented as local can still reach the real cloud"

Understated rather than wrong, on its most load-bearing row. The Scaleway
Object Storage escape was listed as live with a warning, and since #849 it can
be closed instead: `feint run` terminates that host itself and refuses any
host it was not told about rather than tunnelling it. Measured on provider
2.82.0, host network, no sudo, no namespace: 139,264 identical bytes by
sha256, an empty second plan.

The section now names two boundaries that do not depend on the client
cooperating instead of one, and says plainly what has not changed: it is
opt-in, so a client the run was not told about is exactly as free to leave.
The default did not become safe; there is now a way to be sure.

## The other eight

Read and re-dated, nothing changed. Four had simply never been acknowledged —
the two snapshot-import sections (#850, #807), Elastic Metal (#631) and the
reply-door measurement (#672) — and they are contemporaneous with the work
they describe. Three absorb their cited issues inline already: the route-out
section names #660, #684, #694, #696 and #697 in its own text, the DHCP
section names #395, and the lifecycle section gained a line for the second
opt-out it had not mentioned (the Outscale volume `creating` chain, which is
what makes the 409 on a snapshot of a still-creating volume reachable at all).

## What this is not

It is not the adoption item. "Is my stack in scope" over a 4,850-line page
with no index is #873, opened today and deliberately separate: letting #857
stand in for it is how the reader-facing work disappears.

`limits:check` now answers green — 60 sections, 116 issues cited, every one
confirmed after it closed. Both READMEs are unchanged: the safety banner
counts sections, and the count did not move.

Assisted-by: Claude Code (claude-opus-5)
Renaming a heading in `docs/limits.md` breaks every anchor pointing at it, and
internal/cli's TestEveryConfidenceRowCarriesItsProof caught the first one
before the push: the confidence page linked
`limits.md#identifiers-are-not-checked-against-anything`, which no heading
produces any more.

It also confirmed which of the two documents was stale. That row already read
"with \`serve --strict-catalog\`, against the catalogue you declared" — so the
confidence page had been right since #126 and `limits.md` was the one denying
it, which is the opposite of the direction anybody would check first.

Five more references followed, in tools/conformance, examples/stacks,
docs/environment.md and both roadmaps. The one under docs/history/ is updated
too: that snapshot must not read as the present, and a pointer that no longer
resolves is a defect rather than a period detail.

Assisted-by: Claude Code (claude-opus-5)
…e replays it (#843)

The README's Quick Start opened with `brew install stephrobert/feint/feint`.
This project's audience is Linux engineers, and most of them do not run
Homebrew: their route was `docs/install.md`, which is correct, forty lines
long, needs `cosign`, and sat behind a link. The first wall a stranger met was
the install, not the emulator.

## The short route, generated rather than typed

Six lines under the Homebrew block, in both locales, pinned to the same
version the install page is pinned to — so the two cannot drift, which is why
it goes in `renderQuickstart` and not in the README.

It still verifies. The bytes are checked against the published `checksums.txt`
before anything is installed, because what people reach for when a verified
path is long is curl-to-shell, and offering that here would teach it. What the
checksum does NOT prove is said rather than implied: it proves the bytes match
the list, not who wrote the list. The signature over the list is one step
further and stays in the install page.

Two details that are not decoration. The architecture is a `case` with a
refusal rather than a default, for the reason the install page gives at length:
the wrong binary fails at exec with a message naming neither the file nor the
machine. And the whole thing is wrapped in `( set -e … )`, because the two
ordinary ways to stop a chain are both wrong where this block is read — a bare
`return` outside a function is an error in an interactive shell, and a bare
`exit` closes the terminal somebody just pasted into.

## The gate, and what it measures that nothing else did

`tools/conformance/quickstart.sh` already drives the four commands the README
prints, with the binary this repository just built. That proves the code and
says nothing about whether a stranger can obtain it.

`tools/conformance/released-linux.sh` takes the install commands **out of
README.md**, runs them in a throwaway HOME, checks the installed binary reports
the version the README pins, and then hands that binary to the quickstart gate.
Extraction rather than a copy, for the reason quickstart.sh reads its expected
`Apply complete!` line out of the README: a copy is a second source that drifts
the day somebody improves the first, and then the gate proves the copy works.

It refuses to start if any provider credential is visible. An acceptance that
could have reached a real account proves nothing about locality, and asserting
that is cheaper than arguing it.

Measured here on 2026-11-11 against the published v0.15.0: the block installs,
the binary reports 0.15.0, both quickstart stacks apply, both second plans are
empty, both come down and the port is silent afterwards.

## Why the workflow is not on pull requests

`released-linux.yml` replays a PUBLISHED release. A pull request's README can
legitimately pin a version that is not published yet — that is exactly what
`feint docs --check` enforces when the CHANGELOG moves — so gating a pull
request on it would make every release-preparation branch red by construction.
It runs when a release is published, nightly so the route cannot rot between
releases, and on demand.

It adopts `night-report.yml`, and that was not foresight:
`TestEveryScheduledWorkflowReportsOrSaysWhyNot` reddened on this file before
the job existed. falsify.yml was killed by its own timeout for twenty-four
consecutive nights with nobody told, and that test exists so a new scheduled
workflow cannot repeat it.

## What it must never be read as proving

The example declares `runtime: mode: off` and nothing overrides it. This proves
a control plane, an address and a server the API describes. It proves nothing
about Incus or OVN networking, and both the script's last line and the job
summary say so rather than letting a green tick imply it.

## The acceptance record

#843 asks for the run on a clean, disposable Linux host. That is the workflow's
first dispatch, which can only happen once the file is on the default branch —
so this change carries the gate and the local measurement, and the clean-host
record follows the merge rather than preceding it.

mise run prepush green.

Assisted-by: Claude Code (claude-opus-5)
@stephrobert stephrobert changed the title docs(limits): the eleven flagged sections were read, and three were false (#857) docs: the limits page catches up, and a Linux reader installs without Homebrew (#857, #843) Nov 11, 2026
@stephrobert
stephrobert merged commit 6dd5be3 into main Nov 11, 2026
31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant