Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
062c4ae
docs(specs): the house form for rules — compression conventions
nedtwigg Aug 30, 2026
330bfe1
docs(specs): pilot the house form on transport.md
nedtwigg Aug 30, 2026
f952c64
docs(specs): house-form compression for terminal-escapes.md
nedtwigg Aug 30, 2026
b91713a
docs(specs): house-form compression for the three remote specs
nedtwigg Aug 30, 2026
90323d0
docs(specs): house-form compression for alert.md
nedtwigg Aug 30, 2026
97cdb00
docs(specs): house-form compression for server.md
nedtwigg Aug 30, 2026
2f13239
docs(specs): house-form compression for tiling-engine.md
nedtwigg Aug 30, 2026
c057991
docs(specs): house-form compression for mouse-and-clipboard.md
nedtwigg Aug 30, 2026
39f66f8
docs(specs): house-form compression for terminal-state, theme, tutori…
nedtwigg Aug 30, 2026
f4bc0d2
docs(specs): house-form compression for deploy, auto-update, webgl-text
nedtwigg Aug 30, 2026
13fd0f8
docs(specs): house-form compression for layout.md
nedtwigg Aug 30, 2026
3251873
docs(specs): house-form compression for vscode.md
nedtwigg Aug 30, 2026
410da4d
docs(specs): house-form compression + rationale split for dor-browser.md
nedtwigg Aug 30, 2026
4af5190
docs(specs): house-form compression + rationale split for dor-cli.md
nedtwigg Aug 30, 2026
15a9efa
docs(specs): house-form compression + rationale split for standalone.md
nedtwigg Aug 30, 2026
a14d06a
docs(storybook): carry the ring-fade snapshot consequence at the pin
nedtwigg Aug 30, 2026
723ca88
feat(spec-lint): word-budget ratchet for specs, rationale files, and …
nedtwigg Aug 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,11 @@ When updating code covered by a spec, update the spec to match. When the two spe

Rationale entries are keyed by the spec's own headings: each `## X` in `<foo>.rationale.md` is a heading that exists in `<foo>.md`. In the spec, a rule whose evidence moved gets a trailing `(rationale)` marker — a hint, not a link; the pairing is by heading. Rationale files are informative, not normative: no `## Future`, no `Reserved:`, no `Source of truth:` obligations, and measurements should be dated (`measured in Safari 26.5, 2026-08`) so later pruning is safe. Within spec paragraphs, the normative statement leads and any remaining inline why follows — rule first, rationale after (applied opportunistically, not as a corpus rewrite).

**House form for rules.** A rule leads with a bolded imperative — **Never …**, **Must …**, **May …** — and at most one clause of why; the bold carries the emphasis, so scaffolding words ("deliberately", "note that", "it is worth stating") are deleted rather than kept. A section that is really a rule list, precedence ladder, or flow renders as invariant bullets or a table, not narrative prose; number rules only where another spec cites them. Mechanism that constrains a single module lives as a comment at that code, with the spec keeping the one-line rule and a `Source of truth:` pointer; mechanism that constrains editors of *other* files stays in the spec. Consolidate `Source of truth:` pointers at the end of a section rather than per paragraph.

When editing specs, keep them concise but do not replace invariants or edge cases with only a code pointer. Use `Source of truth:` for implementation references, and include direction/scope for protocols, command orchestration, and cross-package boundaries. For docs-only compression, spot-check referenced symbols, message directions, and root-vs-package script ownership against code before committing.

Every spec that uses Session / Pane / Door / baseboard / passthrough vocabulary leads with a `> See \`docs/specs/glossary.md\` for ...` blockquote (see `layout.md`, `alert.md`, `terminal-state.md`). When introducing glossary vocabulary into a spec that lacks the callout, add it in the same edit.
Every spec that uses Session / Pane / Door / baseboard / passthrough vocabulary leads with a `> See \`docs/specs/glossary.md\` for ...` blockquote (see `layout.md`, `alert.md`, `terminal-state.md`). When introducing glossary vocabulary into a spec that lacks the callout, add it in the same edit. The callout licenses using glossary terms bare — do not re-explain them locally. A spec's opening blockquotes are its front-matter: the callout plus, where useful, one line each for what the spec owns, what it defers and to whom, and what to read first; ownership is stated there once, not re-disclaimed per section.

### Spec lifecycle

Expand All @@ -86,7 +88,7 @@ Specs are written ahead of the code on purpose: a new component's spec starts as
- **Reservations.** When unbuilt design constrains present code — a reserved wire field, a reserved ref grammar, an additive-evolution guarantee — state that constraint in the body, marked `Reserved:`, pointing at the `## Future` item it serves. Test: if deleting the sentence would let someone break future compatibility today, it belongs in the body.
- **Promotion is part of done.** Implementing a staged item is not finished until its text moves above the fold — rewritten from "will" to "is", with `Source of truth:` added — and the built portion is deleted from `## Future`. Never leave completed plan text (build orders, phase lists) below the fold; delete it — git history keeps the record.

The mechanically checkable parts of these conventions are enforced by `scripts/spec-lint.mjs` (`pnpm lint:specs`, also the first step of the root `pnpm test`): every spec indexed here, `## Future` last, relative links/anchors resolving, backticked repo paths existing on disk, the leading glossary callout wherever its vocabulary is used, scopes defined exactly once with references resolving, `Reserved:` paragraphs naming `## Future` or a scope, and every `*.rationale.md` pairing with its spec, keyed by that spec's headings, with no `## Future`. `SELF_HOST.md` — the one spec living outside `docs/specs/` — rides the same checks.
The mechanically checkable parts of these conventions are enforced by `scripts/spec-lint.mjs` (`pnpm lint:specs`, also the first step of the root `pnpm test`): every spec indexed here, `## Future` last, relative links/anchors resolving, backticked repo paths existing on disk, the leading glossary callout wherever its vocabulary is used, scopes defined exactly once with references resolving, `Reserved:` paragraphs naming `## Future` or a scope, and every `*.rationale.md` pairing with its spec, keyed by that spec's headings, with no `## Future`. It also ratchets file size: every spec, rationale file, and this file carries a word budget in `scripts/spec-word-budgets.json`, and growth past it fails the lint — cut, or raise the budget deliberately in the same PR. `SELF_HOST.md` — the one spec living outside `docs/specs/` — rides the same checks.

Three sibling lints run alongside it in `pnpm test`, each enforcing one invariant a spec states in prose: `scripts/xterm-lint.mjs` (`pnpm lint:xterm`) for the `@xterm/*` version lockstep in `docs/specs/webgl-text.md`; `scripts/loopback-lint.mjs` (`pnpm lint:loopback`) for the rule in `SECURITY.md` -> "Loopback Listeners" that a loopback bind is not an access control — a new listener must reference a guard module or be allowlisted with a reason; and `scripts/deploy-lint.mjs` (`pnpm lint:deploy`) for the installer controls in `SECURITY.md` -> "Credentials at rest" and "Network posture (self-hosted)", which bind all three of `deploy/local/install-{macos,windows,linux}` and were previously enforced by nothing. Its companion `scripts/deploy-lint-selftest.mjs` proves each rule is load-bearing by deleting the control — and, for exact-count rules, adding a copy — and requiring the lint to fail.

Expand Down
Loading