♻️ Replace verify skill with an npm verify script

A skill duplicated AGENTS.md policy and only worked in Agent-Skills harnesses.
A bare top-level script is the repo-native affordance: every harness (and CI,
and a human) reads package.json#scripts as the source of truth. Add
`npm run verify` = `npm run check` + `test:unit` (tsc runs once, since check
already type-checks) as the one-shot whole-project correctness gate.

Delete .agents/skills/verify/SKILL.md. Document verify in README (Development +
a Tooling-decisions bullet + bare-command note in the prefix list), CONTRIBUTING
(feedback-tier table, "Before pushing", the bare-command paragraph, a "why these
splits" bullet), and AGENTS.md (test = fast iterating gate, verify = definition
of done; skill pointer removed).
This commit is contained in:
tmu committed 2026-09-05 21:46:59 +02:00
1 parent 3d0fe18d6a
commit da5ab79f9b
5 files changed
+14 -49

No files matched your search

-40
View File
@@ -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).
+4 -8
View File
@@ -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:
+5 -1
View File
@@ -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
+4
View File
@@ -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:-<default>}` 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.
+1
View File
@@ -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",