The exact-equality `toEqualTypeOf` assertions already reject `any`, so the `.not.toBeAny()` guards were redundant. Refresh the fixture header and development/ci.md to match.
Development documentation
Why this project works the way it does: the decisions, what was rejected, and the shortcomings and known issues we carry. Written for maintainers and contributors.
The actionable rules — setup, running, testing, submitting — live in CONTRIBUTING.md. Each fact is written once: the rule there, the reason here; neither restates the other, and where a fact is useful in both they link. Read the relevant file before changing an area, and when a rule changes update its rationale here in the same commit.
User-facing documentation is README.md. docs/ is deliberately
unused: that name is reserved for the future user documentation site, and
deploying it is out of scope. These files are not part of that site.
Layout
One file per category:
| File | Covers |
|---|---|
| library.md | Public API design, the type-level contract, and its limitations |
| workflow.md | Branching and merging, script prefixes, feedback tiers, commit messages |
| tooling.md | Toolchain choices and configuration, editor setup |
| testing.md | Test strategy and type-driven development |
| docs.md | Validating the Markdown code fences in the prose docs |
| ci.md | CI pipeline, runner image, coverage serving |
| publishing.md | Release and npm publishing |
We start with one file per category so each area stays small enough to hold in mind; a category that outgrows it becomes a folder with an index, and the links in CONTRIBUTING.md and README.md point at the category, not a single decision.
Decision blocks
Record every non-obvious choice as a block in the relevant category file:
## Runner image
#### Decision (2026-09)
Bake Node into the CI job image at the setup-node tool-cache layout instead
of downloading per job.
#### Why
- ...
#### Rejected
- Gitea Pages / per-job download
- force-pull
#### Known issue
- a Dockerfile-only change re-pushed under an unchanged tag is invisible to the runner
- recover with `docker rmi <image>`
- The date is the month the decision was made, not when the file was edited — the anchor for "current" versus "was current once".
Rejectedstops the project re-litigating the same alternatives; an empty one usually means they were never written down.Known issueis where shortcomings live. A caveat not tied to one decision goes under a## Known issuessection at the end of the file.- Replace a superseded decision in place rather than archiving it; git history is the archive.
- Terse is the point: humans skim and agents imitate the style already in the file, so verbosity compounds edit over edit. Grammar loses to density here on purpose.
Adding to these docs
- Pick the category:
library,workflow,tooling,testing,ci,publishing. - Add or update a decision block; keep existing text unless the decision changed.
- If an actionable rule changes, update CONTRIBUTING.md in the same commit and cross-link. Never change a rule there without updating its rationale here.