diff --git a/.agents/skills/verify/SKILL.md b/.agents/skills/verify/SKILL.md deleted file mode 100644 index 035d77f..0000000 --- a/.agents/skills/verify/SKILL.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -name: verify -description: Run tiny-pattern-ts's fast, offline correctness gates — `npm run test` (mandatory type check + unit suite), then an optional whole-project `npm run check` — and recover from failures without suppressing the type system. Use when verifying TypeScript/ESM changes before committing, when asked to check or run tests, or when a pre-commit hook fails. ---- - -# Verify (tiny-pattern-ts) - -The repo's correctness ladder. `test` is the gate you are responsible for; `check` is a -whole-project confirmation; both are fast and offline. Do not pull repo-maintenance scans -into the feature loop. - -## Procedure - -1. `npm run test` — type check + unit suite. This is the gate. -2. If it fails: read the error, then fix the **root cause in the types/code**. Do not - silence it with `@ts-nocheck` / `@ts-ignore` / `oxlint-disable` / an `as` cast (see - [AGENTS.md § Never do](../../../AGENTS.md#never-do)). Re-run. -3. Optional whole-project pass: `npm run check` (`tsc → oxlint → oxfmt → cspell`). - Pre-commit already runs these on staged files; `check` confirms the entire tree. -4. If `check` flags style or format: `npm run fix`, then repeat from step 1. -5. Commit normally — the pre-commit hook re-runs the staged-file checks. Never bypass a - hook with `git commit --no-verify`. - -## What not to run here - -- `npm run maintain` (`maintain:knip` + `maintain:outdated`) — advisory scans, not - correctness. Feature work must not gate on a stale dependency or an unused export; CI - runs it as a non-blocking job. Run it only on an explicit maintenance / update-deps branch. -- `npm run build` — emit is exercised by the CI publish job, not the dev loop. -- `npm run watch` — a persistent, never-returning process (`--watch`) meant for a - human dev loop (stop with Ctrl-C). Never run it as an agent: it blocks the turn - and streams continuous output. Use `npm run test` for feedback instead. - -## The ladder (fastest → most thorough) - -pre-commit (~1.3s, staged files) → `npm run test` (~3.5s) → `npm run check` (~3s, -whole project). - -Source of truth for every command: [`package.json#scripts`](../../../package.json). -Why each tier exists: [CONTRIBUTING.md § Feedback tiers](../../../CONTRIBUTING.md#feedback-tiers). diff --git a/AGENTS.md b/AGENTS.md index 0ae623b..e415413 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,20 +8,16 @@ first-action facts. Do not restate evolving prose here — it will drift. ## First action - Project: F#-style pattern matching for TypeScript/ESM. Node `>=26` (pinned via `.node-version`), ESM-only (no CommonJS shim). -- **Mandatory while iterating:** `npm run test` (runs `check:tsc`, then the unit suite). This is the gate you are responsible for. +- **While iterating:** `npm run test` (`check:tsc` + the unit suite) for fast feedback on the files you changed. +- **Definition of done — run this before you call the work finished:** `npm run verify`. It is one shot of the whole-project correctness ladder (`npm run check`: `tsc → oxlint → oxfmt → cspell`, then the unit suite; tsc runs once), excluding advisory maintenance. If all green, commit. If red, look at the output, fix the root cause, and re-run. - **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 (`tsc` + `oxlint` + `oxfmt` + `cspell`) — don't run them by hand. If the hook fails on style, `npm run fix`, restage, recommit. -- **Optional final gate:** `npm run check` (the fast, offline, whole-project correctness ladder: `tsc → oxlint → oxfmt → cspell`). Safe to run whenever you want a project-wide confirmation; pre-commit already covers staged files. - **`npm run maintain` is NOT part of the feature loop.** `maintain:knip` (dead-code/deps) and `maintain:outdated` (registry) are advisory maintenance scans. Run them only on an explicit maintenance / update-deps branch; CI surfaces them via a non-blocking job, never as a gate. ```sh - npm run test # mandatory gate - npm run check # optional project-wide confirmation + npm run test # while iterating (fast feedback) + npm run verify # definition of done: whole-project correctness, one shot ``` - The bot's definition of done: `npm run test` green, commit normally after. - -- A project skill wraps this loop as an on-demand procedure: `/skill:verify` → [`.agents/skills/verify/SKILL.md`](./.agents/skills/verify/SKILL.md). - ## Never do Don't silence the type system to force a green run. As an agent these are forbidden: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 804238c..8c3acbd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,6 +31,8 @@ Script names in `package.json` use a prefix that signals _when_ the script is in A new script should pick the prefix that matches its lifecycle, not invent a new one. If no existing prefix fits, that's a signal the script doesn't belong in the standard pipeline. +Separately, some top-level scripts are **bare** (no prefix): the entry points that either run a single tool (`build`, `clean`) or aggregate a `prefix:*` family (`check`, `fix`, `test`, `watch`, `maintain`), plus `verify`, a cross-cutting convenience that composes `check` + `test:unit` into one whole-project correctness gate. Bare commands are how you invoke a tier; the `prefix:*` scripts are what those tiers are made of. + ## Feedback tiers The tools are organized into a feedback ladder. Each tier catches different things at different costs; the rule of thumb is "earlier tiers fire more often, faster tiers catch less, slower tiers are more thorough": @@ -41,6 +43,7 @@ The tools are organized into a feedback ladder. Each tier catches different thin | Pre-commit (auto) | on stage | tsc + oxlint + oxfmt + cspell (staged files only) | ~1.3s | | Pre-push (auto) | on push | `npm test` (full tsc + unit tests) | ~3.5s | | `npm run check` | manual | Correctness gates: tsc + oxlint + oxfmt + cspell (whole project) | ~3s | +| `npm run verify` | manual | Definition of done: `npm run check` + unit tests, one shot | ~6s | | `npm run fix` | manual | Auto-resolve fixable issues (lint, format) | ~3s | | `npm run maintain` | manual / CI (advisory) | `maintain:knip` + `maintain:outdated` (whole-project + network scans) | ~10s | | CI build (auto) | on push/PR | `npm run check` + `npm run test:ci` | ~30s+ | @@ -54,10 +57,11 @@ The tools are organized into a feedback ladder. Each tier catches different thin - **`test` (and the `tsc` it includes) is in pre-push** because it runs the whole test suite across the whole project. The pre-commit `LEFTHOOK_FILES` convention doesn't apply to the test runner, so pre-commit isn't the right home. Pre-push runs after all commits are made but before the push leaves the machine, catching regressions that span multiple commits. - **`maintain:knip` and `maintain:outdated` are advisory, not correctness** — knip scans the whole project (~4s) and `check-outdated` queries the npm registry (~6.5s, network-dependent, and it exits non-zero whenever any dep is behind). That's why they live under `maintain:`, are excluded from pre-commit and from `check`, and run in CI as a **non-blocking** job: a stale dependency must never block an unrelated feature PR. - **`publish:publint` and `publish:attw` are NOT in `check`** — they belong to the `publish:` prefix because they validate the _publishable artifact_ (`dist/`) and require a fresh build. Running them on every commit would be wasteful. They run in the CI `publish` job immediately before `npm publish`. +- **`verify` is a convenience umbrella, not a new tier.** `npm run verify` = `npm run check` + `npm run test:unit`, so a human or agent gets the whole-project correctness answer in one command. It uses `test:unit` (not `test`) because `check` already runs `tsc`, so the type checker runs exactly once. It excludes `maintain` by design, and CI still runs `check` + `test:ci` separately (to also collect coverage), so `verify` is a local/dev affordance. ### Before pushing -Run `npm run check && npm run test` locally — both are the fast, offline correctness gates, safe to run any time. Anything `maintain:knip` or `maintain:outdated` would catch is reported by the non-blocking CI `maintain` job; run `npm run maintain` yourself only on a maintenance / update-deps branch. +Run `npm run verify` locally — it is the one-shot whole-project correctness gate (`check` + unit tests). Anything `maintain:knip` or `maintain:outdated` would catch is reported by the non-blocking CI `maintain` job; run `npm run maintain` yourself only on a maintenance / update-deps branch. ## Publishing workflow diff --git a/README.md b/README.md index 1371614..3898b42 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ Pattern matching for TypeScript/ESM environments (F#-style, not regex). - **Test:** `npm run test`, `npm run test:ci` - **Watch:** `npm run watch` (re-runs tests on file save; the earliest feedback tier) - **Checks:** `npm run check`, `npm run fix` +- **Verify (definition of done):** `npm run verify` — `npm run check` + the unit suite in one shot; the whole-project correctness gate (excludes advisory `maintain`) - **Maintenance (advisory):** `npm run maintain` — `knip` + `check-outdated`; run on a maintenance / update-deps branch, not part of the feature loop - **Individual fixes:** `npm run fix:oxfmt`, `npm run fix:oxlint` @@ -37,6 +38,7 @@ The choice and configuration of each tool above is the result of deliberate trad - **`attw --profile esm-only`** is semantically correct: this package is intentionally ESM-only (no CommonJS shim), so CJS resolution scenarios are out of scope by design, not a bug. - **The `publish:` prefix has no local aggregator.** `publint` and `attw` validate the _publishable artifact_ (`dist/`), not the source, and require a fresh build. They run only in the CI `publish` job immediately before `npm publish` — there is intentionally no `npm run publish`. - **`check:tsc` runs first** in the `npm run check` chain so a type error short-circuits the rest (faster feedback than letting oxlint/oxfmt run and then failing on tsc at the end). +- **`verify` is the one-shot definition of done.** `npm run verify` composes `npm run check` with the unit suite (`test:unit`) into a single whole-project correctness gate, so a human or an agent reaches for one command instead of re-deriving the sequence. It deliberately uses `test:unit` (not `test`) because `check` already runs `check:tsc` — so tsc runs exactly once. It excludes `maintain` (advisory) by design. CI is not switched to it: the build job runs `check` + `test:ci` to also collect coverage. - **The `check:` / `maintain:` split is correctness gates vs. advisory scans.** `npm run check` is the fast, offline, whole-project correctness ladder (`tsc → oxlint → oxfmt → cspell`) and can run anywhere, including the agent loop. `knip` (~4s, whole project) and `check-outdated` (~6.5s, queries the npm registry) are advisory, not correctness — a stale dependency or an unused export must not fail a feature PR — so they moved to `npm run maintain`, kept out of pre-commit, and run in CI as a **non-blocking** job (see `.github/workflows/ci.yml`). Note `check-outdated` exits non-zero whenever any dep is outdated, which is exactly why it must not gate merges. - **The pre-commit hook sets the `LEFTHOOK_FILES` env var** to the staged-files list, and the affected scripts use `${LEFTHOOK_FILES:-}` to default to the whole project when invoked manually. This keeps `package.json#scripts` as the single source of truth for the underlying commands — `lefthook.yml` only describes _what to run on which files_. - **`tslib` and `type-fest` are deliberately not used.** `tslib` is a runtime helper for old ES3/ES5 targets (the project targets ES2024); `type-fest` was never imported. knip caught both. @@ -66,6 +68,8 @@ Script names follow a prefix convention that signals _when_ they run: - `maintain:*` — advisory repo-maintenance scans (dead code, dependency freshness). Aggregated by `npm run maintain`. Whole-project and/or network-bound, so **not** correctness gates: run on a maintenance branch, and in CI as a non-blocking job that reports without failing. - `publish:*` — runs only at publish time, in the CI `publish` job (immediately before `npm publish`). There is no local `npm run publish` script — publishing is CI-only by policy. +Bare, prefix-free top-level commands are the entry points: `build`, `clean`, `check`, `fix`, `test`, `watch`, `maintain`, and `verify`. `verify` (`check` + `test:unit`) is the one-shot "whole-project correctness" gate; `maintain` is the advisory counterpart that never gates a merge. + ## Contributing For maintainer and contributor docs — the script prefix convention, the feedback-tier system, the rules the tools don't enforce, and the publishing workflow — see [CONTRIBUTING.md](./CONTRIBUTING.md). AI coding agents: your entry point is [AGENTS.md](./AGENTS.md), which points back to CONTRIBUTING.md. diff --git a/package.json b/package.json index b9c9b88..60190b5 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "test": "npm run check:tsc && node --test --strip-types \"src/**/*.test.ts\"", "test:ci": "c8 --reporter=text --reporter=lcov --reporter=html node --test --strip-types \"src/**/*.test.ts\"", "test:unit": "node --test --strip-types \"src/**/*.test.ts\"", + "verify": "npm run check && npm run test:unit", "watch": "npm run watch:test", "watch:test": "node --test --watch --strip-types \"src/**/*.test.ts\"", "publish:attw": "attw . --pack --profile esm-only",