♻️ Single-home the actionable rules and the rationale

development/ had restated the actionables that CONTRIBUTING.md owns: the
branching step list, the script prefix list, the feedback-tier rule of thumb,
the commit convention and the type-driven test loop. Those now live only in
CONTRIBUTING.md; development/ keeps the decision blocks and links to the rule.
development/README.md, CONTRIBUTING.md and AGENTS.md state the 'write each fact
once' principle explicitly.
This commit is contained in:
tmu committed 2026-09-15 13:46:31 +00:00
1 parent fe02317fc8
commit 95d73d11b6
5 files changed
+89 -126

No files matched your search

+10 -31
View File
@@ -8,26 +8,9 @@ the way it is.
## Type-driven development
New behavior follows **type-driven development** (in Edwin Brady's sense):
_treat the type as the plan for a program, and use the compiler and type checker
as your assistant, guiding you to a complete program that satisfies the type_
([idris-lang.org](https://www.idris-lang.org/)). Here that plan is the
`expectTypeOf` assertion, written first. The loop is **type → red → green →
refactor**:
1. **Type** — write the compile-time expectation first
(`expectTypeOf(...).toEqualTypeOf<…>()`) and let `npm run check:tsc` fail on
the _type_. The type error is the spec you want to hit before the runtime
logic exists.
2. **Red** — add the matching runtime assertion (`assert.*`) so
`npm run test:unit` now fails on behavior.
3. **Green** — implement in `src/*.ts` until both the type check and the test
pass.
4. **Refactor** — with the type system and the tests as the safety net, then
`npm run verify` as the definition-of-done gate.
This is why every test in the suite pairs an `expectTypeOf(...)` with an
`assert.*` — keep them together.
The rules — the loop and the pairing rule — are in
[CONTRIBUTING.md § Testing discipline (type-driven)](../CONTRIBUTING.md#testing-discipline-type-driven).
What follows is why the loop is type-driven and what was rejected.
#### Decision (2026-09)
@@ -56,18 +39,14 @@ Per [AGENTS.md § Never do](../AGENTS.md#never-do), reach green honestly — fix
types so both the type check and the runtime assertion pass, never suppress the
ones you can't make pass.
## Test tiers
The runner is `node --test --strip-types "src/**/*.test.ts"` and the tiers are
listed in
[CONTRIBUTING.md § Development commands](../CONTRIBUTING.md#development-commands).
`c8` uses V8 coverage, so the `--strip-types` source is instrumented without a
build step. The runner relies on the `.ts` import-extension convention (see
[tooling.md](./tooling.md#source-imports-use-ts-extensions)).
- `npm run test:unit` — the test runner alone, for the fast local loop.
- `npm test` — `check:tsc` + the unit suite; this is what pre-push and the
baseline check run.
- `npm run test:ci` — adds c8 coverage; used by CI. `c8` uses V8 coverage, so
the `--strip-types` source is instrumented without a build step.
The runner is `node --test --strip-types "src/**/*.test.ts"`. It relies on the
`.ts` import-extension convention (see [tooling.md](./tooling.md#source-imports-use-ts-extensions)).
#### Known issue
## Known issues
- The type-aware linter misidentifies `expectTypeOf()` as a floating promise,
so `src/index.test.ts` carries a file-level `oxlint-disable