♻️ Run doc-test generation as an explicit CI step

Drop the `pretest:ci` lifecycle hook and call `create:doc-tests` from the
`build` job before `test:ci`. The hook hid the step from the job log and
made `test:ci` behave differently under npm than when run directly.
This commit is contained in:
tmu committed 2026-09-24 13:08:45 +00:00
1 parent 4dc58395c0
commit 400fe902e0
6 files changed
+13 -6

No files matched your search

+4
View File
@@ -77,6 +77,10 @@ jobs:
- run: npm ci - run: npm ci
- run: npm run build - run: npm run build
- run: npm run check - run: npm run check
# Compile the prose examples into gitignored tests. Kept as an
# explicit step (not a `pretest:ci` hook) so it is visible in the
# job log and `test:ci` stays a plain command.
- run: npm run create:doc-tests
# Fails the build below 100% coverage on `src/` (`c8 --all --100`); # Fails the build below 100% coverage on `src/` (`c8 --all --100`);
# the same run produces the report published below. See # the same run produces the report published below. See
# development/ci.md § Coverage threshold. # development/ci.md § Coverage threshold.
+1 -1
View File
@@ -11,7 +11,7 @@ first-action facts. Do not restate evolving prose here — it will drift.
- **While iterating:** `npm run test` (`check:tsc` + the unit suite) for fast feedback on the files you changed. - **While iterating:** `npm run test` (`check:tsc` + the unit suite) for fast feedback on the files you changed.
- **Optional code intelligence:** this repo installs `@spences10/pi-lsp` (pinned in `.pi/settings.json`) as a project-local pi extension. It talks to the repo's own TypeScript 7 via `tsc --lsp --stdio` and exposes **read-only** tools — `lsp_hover`, `lsp_definition`, `lsp_references`, `lsp_find_symbol`, `lsp_document_symbols`, `lsp_diagnostics(_many)`. **When you are looking for a symbol, reach for the LSP before `rg`/`grep`** — `lsp_references` / `lsp_find_symbol` / `lsp_definition` / `lsp_document_symbols` are semantic and cross-file, so they see shadowing, imports and overloads that a text search cannot; use `lsp_hover` to read inferred types on generic-heavy code. Use `rg` for what the LSP cannot see — doc prose, string literals, config, task lists, file discovery — and reconcile the two sets before editing (symbols from the LSP, strings and prose from `rg`). It has no rename / code-action / apply-edit surface — the write side is pi's `edit` tool + `check:tsc`. Treat empty LSP output as _inconclusive_, not success: **`npm run test` / `npm run verify` remain the sole authoritative gate** (see the next bullet). The server keeps running across that gate with a ~5 min idle timeout and registers no file watchers, so if you change `tsconfig.json` / `package.json` mid-session its diagnostics can be stale — when LSP output disagrees with `check:tsc`, trust `check:tsc` and restart pi (or wait out the idle timeout) before concluding the LSP is wrong. - **Optional code intelligence:** this repo installs `@spences10/pi-lsp` (pinned in `.pi/settings.json`) as a project-local pi extension. It talks to the repo's own TypeScript 7 via `tsc --lsp --stdio` and exposes **read-only** tools — `lsp_hover`, `lsp_definition`, `lsp_references`, `lsp_find_symbol`, `lsp_document_symbols`, `lsp_diagnostics(_many)`. **When you are looking for a symbol, reach for the LSP before `rg`/`grep`** — `lsp_references` / `lsp_find_symbol` / `lsp_definition` / `lsp_document_symbols` are semantic and cross-file, so they see shadowing, imports and overloads that a text search cannot; use `lsp_hover` to read inferred types on generic-heavy code. Use `rg` for what the LSP cannot see — doc prose, string literals, config, task lists, file discovery — and reconcile the two sets before editing (symbols from the LSP, strings and prose from `rg`). It has no rename / code-action / apply-edit surface — the write side is pi's `edit` tool + `check:tsc`. Treat empty LSP output as _inconclusive_, not success: **`npm run test` / `npm run verify` remain the sole authoritative gate** (see the next bullet). The server keeps running across that gate with a ~5 min idle timeout and registers no file watchers, so if you change `tsconfig.json` / `package.json` mid-session its diagnostics can be stale — when LSP output disagrees with `check:tsc`, trust `check:tsc` and restart pi (or wait out the idle timeout) before concluding the LSP is wrong.
- **Definition of done — run this before you call the work finished:** `npm run verify`. If all green, commit. If red, look at the output, fix the root cause, and re-run. - **Definition of done — run this before you call the work finished:** `npm run verify`. If all green, commit. If red, look at the output, fix the root cause, and re-run.
- **Touched a `ts`-tagged fence in `README.md` / `CONTRIBUTING.md`?** Run `npm run create:doc-tests` first: it regenerates the gitignored tests under `src/doc-test/__generated__/`, formats and type-checks them, so `verify` executes the documented example. CI does this automatically via `pretest:ci`; the fast local tiers do not. See [development/docs.md](./development/docs.md). - **Touched a `ts`-tagged fence in `README.md` / `CONTRIBUTING.md`?** Run `npm run create:doc-tests` first: it regenerates the gitignored tests under `src/doc-test/__generated__/`, formats and type-checks them, so `verify` executes the documented example. CI runs it in an explicit step before `test:ci`; the fast local tiers do not. See [development/docs.md](./development/docs.md).
- **On commit:** write a good message (see [CONTRIBUTING.md § Commit messages](./CONTRIBUTING.md#commit-messages)). Lefthook's pre-commit hook already runs the fast, offline, staged-file checks — don't run them by hand. If the hook fails on style, `npm run fix`, restage, recommit. - **On commit:** write a good message (see [CONTRIBUTING.md § Commit messages](./CONTRIBUTING.md#commit-messages)). Lefthook's pre-commit hook already runs the fast, offline, staged-file checks — don't run them by hand. If the hook fails on style, `npm run fix`, restage, recommit.
- **Document decisions where the next maintainer will look:** rationale, rejected alternatives and known issues go in `development/<category>.md` (see [development/README.md](./development/README.md)); the actionable rule stays in [CONTRIBUTING.md](./CONTRIBUTING.md) and links to it. Write each fact once — never copy the rule into `development/` or the reason into `CONTRIBUTING.md` — and change both in the same commit when a rule changes. - **Document decisions where the next maintainer will look:** rationale, rejected alternatives and known issues go in `development/<category>.md` (see [development/README.md](./development/README.md)); the actionable rule stays in [CONTRIBUTING.md](./CONTRIBUTING.md) and links to it. Write each fact once — never copy the rule into `development/` or the reason into `CONTRIBUTING.md` — and change both in the same commit when a rule changes.
- **`npm run maintain` is NOT part of the feature loop.** Its scans are advisory, never a gate; run them only on an explicit maintenance / update-deps branch. - **`npm run maintain` is NOT part of the feature loop.** Its scans are advisory, never a gate; run them only on an explicit maintenance / update-deps branch.
+2 -2
View File
@@ -96,8 +96,8 @@ executed test, so a documented example cannot drift from the API. Describe each
fence with the paragraph directly above it (that text becomes the test title), fence with the paragraph directly above it (that text becomes the test title),
and keep its library import self-contained; and keep its library import self-contained;
`npm run create:doc-tests` regenerates, formats and type-checks the tests under `npm run create:doc-tests` regenerates, formats and type-checks the tests under
`src/doc-test/__generated__/`. CI runs it automatically via `pretest:ci`, so run `src/doc-test/__generated__/`. CI runs it in an explicit step before `test:ci`,
it yourself before `npm run verify` when you touched a fence. Why: so run it yourself before `npm run verify` when you touched a fence. Why:
[development/docs.md](./development/docs.md). [development/docs.md](./development/docs.md).
## Code style and formatting ## Code style and formatting
+4
View File
@@ -55,3 +55,7 @@ Compile every `ts / `typescript fence in the prose docs into a real
(`git ls-files 'src/*.test.ts'` — the generated files are untracked), and (`git ls-files 'src/*.test.ts'` — the generated files are untracked), and
`test:doc` runs the examples in a separate process without c8. `test:ci` `test:doc` runs the examples in a separate process without c8. `test:ci`
chains the two, so correctness and coverage stay independent. chains the two, so correctness and coverage stay independent.
- The CI `build` job runs `npm run create:doc-tests` as an explicit step before
`npm run test:ci`, not a `pretest:ci` lifecycle hook: the hook hides the step
from the job log and makes `test:ci` behave differently under npm than when
run directly.
-1
View File
@@ -60,7 +60,6 @@
"maintain:knip": "knip --include dependencies,exports,files", "maintain:knip": "knip --include dependencies,exports,files",
"maintain:outdated": "check-outdated --ignore-pre-releases --ignore-packages @types/node", "maintain:outdated": "check-outdated --ignore-pre-releases --ignore-packages @types/node",
"test": "npm run check:tsc && node --test --strip-types \"src/**/*.test.ts\"", "test": "npm run check:tsc && node --test --strip-types \"src/**/*.test.ts\"",
"pretest:ci": "npm run create:doc-tests",
"test:ci": "npm run test:coverage && npm run test:doc", "test:ci": "npm run test:coverage && npm run test:doc",
"test:coverage": "c8 --all --include \"src/**/*.ts\" --reporter=text --reporter=lcov --reporter=html --100 node --test --strip-types $(git ls-files 'src/*.test.ts')", "test:coverage": "c8 --all --include \"src/**/*.ts\" --reporter=text --reporter=lcov --reporter=html --100 node --test --strip-types $(git ls-files 'src/*.test.ts')",
"test:doc": "node --test --strip-types \"src/doc-test/__generated__/*.test.ts\"", "test:doc": "node --test --strip-types \"src/doc-test/__generated__/*.test.ts\"",
+2 -2
View File
@@ -7,6 +7,6 @@ formats them with `oxfmt`, then type-checks them with
[`tsconfig.json`](./tsconfig.json) — the scoped config that relaxes [`tsconfig.json`](./tsconfig.json) — the scoped config that relaxes
`noUnusedLocals` so example-only locals (and the injected `assert`) compile. `noUnusedLocals` so example-only locals (and the injected `assert`) compile.
CI regenerates them first via `pretest:ci`; the fast local `test` / `verify` CI regenerates them in an explicit step before `test:ci`; the fast local `test` /
tiers do not. See [development/docs.md](../../development/docs.md) for why the `verify` tiers do not. See [development/docs.md](../../development/docs.md) for why the
generator is shaped this way. generator is shaped this way.