plumbbob 0.8.13 → 0.8.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/dist/lib/agents.js +1 -1
  3. package/dist/lib/check.js +9 -6
  4. package/dist/lib/intent.js +20 -0
  5. package/dist/lib/orient.js +4 -3
  6. package/dist/verbs/checkpoint.js +30 -11
  7. package/dist/verbs/finish.js +24 -8
  8. package/node_modules/checkride/CHANGELOG.md +234 -0
  9. package/node_modules/checkride/README.md +134 -31
  10. package/node_modules/checkride/dist/adapters.d.ts +50 -1
  11. package/node_modules/checkride/dist/adapters.d.ts.map +1 -1
  12. package/node_modules/checkride/dist/adapters.js +123 -7
  13. package/node_modules/checkride/dist/adapters.js.map +1 -1
  14. package/node_modules/checkride/dist/agent-setup/hook.d.ts.map +1 -1
  15. package/node_modules/checkride/dist/agent-setup/hook.js +15 -1
  16. package/node_modules/checkride/dist/agent-setup/hook.js.map +1 -1
  17. package/node_modules/checkride/dist/agent-setup/index.d.ts +0 -1
  18. package/node_modules/checkride/dist/agent-setup/index.d.ts.map +1 -1
  19. package/node_modules/checkride/dist/agent-setup/index.js.map +1 -1
  20. package/node_modules/checkride/dist/baseline/fallow.d.ts +64 -0
  21. package/node_modules/checkride/dist/baseline/fallow.d.ts.map +1 -0
  22. package/node_modules/checkride/dist/baseline/fallow.js +256 -0
  23. package/node_modules/checkride/dist/baseline/fallow.js.map +1 -0
  24. package/node_modules/checkride/dist/baseline/fingerprint.d.ts +4 -6
  25. package/node_modules/checkride/dist/baseline/fingerprint.d.ts.map +1 -1
  26. package/node_modules/checkride/dist/baseline/fingerprint.js +9 -4
  27. package/node_modules/checkride/dist/baseline/fingerprint.js.map +1 -1
  28. package/node_modules/checkride/dist/baseline/index.d.ts +4 -5
  29. package/node_modules/checkride/dist/baseline/index.d.ts.map +1 -1
  30. package/node_modules/checkride/dist/baseline/index.js +2 -2
  31. package/node_modules/checkride/dist/baseline/index.js.map +1 -1
  32. package/node_modules/checkride/dist/{baseline/command.d.ts → baseline-command.d.ts} +11 -9
  33. package/node_modules/checkride/dist/baseline-command.d.ts.map +1 -0
  34. package/node_modules/checkride/dist/{baseline/command.js → baseline-command.js} +9 -7
  35. package/node_modules/checkride/dist/baseline-command.js.map +1 -0
  36. package/node_modules/checkride/dist/cli.d.ts.map +1 -1
  37. package/node_modules/checkride/dist/cli.js +95 -8
  38. package/node_modules/checkride/dist/cli.js.map +1 -1
  39. package/node_modules/checkride/dist/config.d.ts +37 -12
  40. package/node_modules/checkride/dist/config.d.ts.map +1 -1
  41. package/node_modules/checkride/dist/config.js +268 -60
  42. package/node_modules/checkride/dist/config.js.map +1 -1
  43. package/node_modules/checkride/dist/digest/digest.d.ts +0 -6
  44. package/node_modules/checkride/dist/digest/digest.d.ts.map +1 -1
  45. package/node_modules/checkride/dist/digest/digest.js +3 -3
  46. package/node_modules/checkride/dist/digest/digest.js.map +1 -1
  47. package/node_modules/checkride/dist/digest/index.d.ts +1 -2
  48. package/node_modules/checkride/dist/digest/index.d.ts.map +1 -1
  49. package/node_modules/checkride/dist/digest/index.js +1 -1
  50. package/node_modules/checkride/dist/digest/index.js.map +1 -1
  51. package/node_modules/checkride/dist/doctor.d.ts +14 -1
  52. package/node_modules/checkride/dist/doctor.d.ts.map +1 -1
  53. package/node_modules/checkride/dist/doctor.js +124 -55
  54. package/node_modules/checkride/dist/doctor.js.map +1 -1
  55. package/node_modules/checkride/dist/init.d.ts +6 -0
  56. package/node_modules/checkride/dist/init.d.ts.map +1 -1
  57. package/node_modules/checkride/dist/init.js +347 -121
  58. package/node_modules/checkride/dist/init.js.map +1 -1
  59. package/node_modules/checkride/dist/links.d.ts.map +1 -1
  60. package/node_modules/checkride/dist/links.js +8 -9
  61. package/node_modules/checkride/dist/links.js.map +1 -1
  62. package/node_modules/checkride/dist/orchestrator.d.ts +56 -1
  63. package/node_modules/checkride/dist/orchestrator.d.ts.map +1 -1
  64. package/node_modules/checkride/dist/orchestrator.js +506 -138
  65. package/node_modules/checkride/dist/orchestrator.js.map +1 -1
  66. package/node_modules/checkride/dist/pack.d.ts +76 -0
  67. package/node_modules/checkride/dist/pack.d.ts.map +1 -0
  68. package/node_modules/checkride/dist/pack.js +217 -0
  69. package/node_modules/checkride/dist/pack.js.map +1 -0
  70. package/node_modules/checkride/dist/pm/translate.d.ts +18 -10
  71. package/node_modules/checkride/dist/pm/translate.d.ts.map +1 -1
  72. package/node_modules/checkride/dist/pm/translate.js +27 -13
  73. package/node_modules/checkride/dist/pm/translate.js.map +1 -1
  74. package/node_modules/checkride/dist/smoke.d.ts +86 -0
  75. package/node_modules/checkride/dist/smoke.d.ts.map +1 -0
  76. package/node_modules/checkride/dist/smoke.js +346 -0
  77. package/node_modules/checkride/dist/smoke.js.map +1 -0
  78. package/node_modules/checkride/dist/snippets.d.ts +215 -0
  79. package/node_modules/checkride/dist/snippets.d.ts.map +1 -0
  80. package/node_modules/checkride/dist/snippets.js +360 -0
  81. package/node_modules/checkride/dist/snippets.js.map +1 -0
  82. package/node_modules/checkride/package.json +4 -2
  83. package/node_modules/checkride/schema/checkride.config.schema.json +11 -6
  84. package/package.json +3 -2
  85. package/skills/pb-build/SKILL.md +7 -0
  86. package/skills/pb-park/SKILL.md +3 -2
  87. package/skills/pb-plan/SKILL.md +30 -9
  88. package/skills/pb-refine/SKILL.md +21 -6
  89. package/skills/pb-step/SKILL.md +10 -5
  90. package/skills/pb-verify/SKILL.md +12 -1
  91. package/templates/intent.md +55 -11
  92. package/node_modules/checkride/dist/baseline/command.d.ts.map +0 -1
  93. package/node_modules/checkride/dist/baseline/command.js.map +0 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "plumbbob",
3
3
  "displayName": "PlumbBob",
4
- "version": "0.8.13",
4
+ "version": "0.8.17",
5
5
  "description": "Attention-first build process — driver skills + a CLI that keep you the decider: guidance on the work, a latch on the record.",
6
6
  "author": { "name": "Rob McLarty", "email": "hello@robmclarty.com", "url": "https://robmclarty.com" },
7
7
  "homepage": "https://github.com/robmclarty/plumbbob#readme",
@@ -327,7 +327,7 @@ function skipWarnings(heading, skipped) {
327
327
  // is the author's error and is refused loud. Slot *contents* stay lenient (D61): a
328
328
  // slot value may be one name or a list, and blanks/non-strings drop rather than
329
329
  // refuse, because bindings feed a spawn, not a git-gating parse.
330
- export function parseHarness(raw) {
330
+ function parseHarness(raw) {
331
331
  if (!isObject(raw)) {
332
332
  return { ok: false, error: 'harness.json must be a JSON object.' };
333
333
  }
package/dist/lib/check.js CHANGED
@@ -31,12 +31,15 @@ export async function detectGate(root) {
31
31
  }
32
32
  }
33
33
  // Checkride's always-on repo checks: the adapters its doctor reports even for
34
- // an EMPTY directory (links, the pnpm-audit security scan, and the
35
- // publint/attw package-shape probes). They exercise the repo's plumbing, not
36
- // its code — a gate made only of these green-lights every checkpoint while the
37
- // human believes their work is being checked. Coupled to checkride's built-in
38
- // set by construction; the doctor.test.ts probe pins the coupling.
39
- const ALWAYS_ON_ADAPTERS = ['links', 'pnpm-audit', 'publint', 'attw'];
34
+ // an EMPTY directory — the built-in `links` slot, the pnpm-audit security
35
+ // scan, the publint/attw package-shape probes, and (checkride 0.5.0's
36
+ // publish bundle) the built-in pack/smoke/snippets slots, which resolve on any
37
+ // package with no tool config of their own. They exercise the repo's plumbing
38
+ // or its shipped artifact, not its code — a gate made only of these green-lights
39
+ // every checkpoint while the human believes their work is being checked. Coupled
40
+ // to checkride's built-in set by construction; the check.test.ts and
41
+ // doctor.test.ts probes pin the coupling.
42
+ const ALWAYS_ON_ADAPTERS = ['links', 'pnpm-audit', 'publint', 'attw', 'pack', 'smoke', 'snippets'];
40
43
  // The detection rule, shared by `start`'s probe and doctor's callout: some tool
41
44
  // slot beyond the always-on family has an adapter — i.e. checkride can see the
42
45
  // CODE (a tsconfig, a test runner, a linter…), not just the repo.
@@ -149,6 +149,26 @@ export function parseBuildTitle(content) {
149
149
  }
150
150
  return '';
151
151
  }
152
+ const SCOPE_HEADER = /^\*\*Scope:\*\*\s*(.*)$/;
153
+ const SCOPE_PLACEHOLDER = /^<.*>$/;
154
+ // The build's default Conventional-Commit scope (D4): the `**Scope:**` header
155
+ // field authored once at plan time, its value read verbatim off the line with any
156
+ // trailing `<!-- ... -->` guidance note stripped. An unfilled placeholder — an
157
+ // empty value or an angle-bracket placeholder like `<feature>` — parses as absent
158
+ // (D7), so a freshly scaffolded, not-yet-edited build falls through to the next
159
+ // scope rung (the build slug) rather than landing commits scoped `(<feature>)`.
160
+ // null when the header is absent entirely — the pre-D4 shape (C2 back-compat).
161
+ export function parseBuildScope(content) {
162
+ for (const line of content.split('\n')) {
163
+ const m = SCOPE_HEADER.exec(line.trim());
164
+ if (m === null) {
165
+ continue;
166
+ }
167
+ const value = (m[1] ?? '').split('<!--')[0]?.trim() ?? '';
168
+ return value === '' || SCOPE_PLACEHOLDER.test(value) ? null : value;
169
+ }
170
+ return null;
171
+ }
152
172
  export function scrapeBullets(content, heading) {
153
173
  const lines = content.split('\n');
154
174
  const start = lines.findIndex((l) => l.trim() === heading);
@@ -73,10 +73,11 @@ export function markStepDone(intent, n) {
73
73
  })
74
74
  .join('\n');
75
75
  }
76
- // Open questions still open: `- Q\d+:` lines that do not say "resolved".
76
+ // Open questions still open: `- Q\d+:` openers that do not say "resolved". The
77
+ // opener may carry a slug-at-birth gloss — `- Q2 (some-slug): ...` (D3) — which
78
+ // the count reads through; sub-lines (`*plain:*`/`*lean:*`) never match.
77
79
  export function parseOpenQuestions(intent) {
78
- return sectionLines(intent, '## Open questions').filter((l) => /^- Q\d+:/.test(l.trim()) && !/resolved/i.test(l))
79
- .length;
80
+ return sectionLines(intent, '## Open questions').filter((l) => /^- Q\d+(?: \([^)]+\))?:/.test(l.trim()) && !/resolved/i.test(l)).length;
80
81
  }
81
82
  // Open parked items: `- [ ]` lines under `## Park list` (the `park` verb's format).
82
83
  // A harvested item is flipped to `- [x]` by `/pb-harvest` and no longer counts; the
@@ -13,7 +13,7 @@ import { activeBuild, buildFolder, buildLogPath, buildScope, bumpStepStat, check
13
13
  import { runCheck } from "../lib/check.js";
14
14
  import { checkLatch } from "../lib/latch.js";
15
15
  import { markStepDone, parseSteps } from "../lib/orient.js";
16
- import { parseStepSeam, scopeDrift } from "../lib/intent.js";
16
+ import { parseBuildScope, parseStepSeam, scopeDrift } from "../lib/intent.js";
17
17
  import { conventionalSubject, subjectFromTitle, withMarker } from "../lib/commitmsg.js";
18
18
  import { appendToSection, checkpointLogLine } from "../lib/buildlog.js";
19
19
  import { AT_BOUNDARY, syncBuildLogState } from "../lib/buildlogsync.js";
@@ -101,11 +101,12 @@ function checkpointPlan(root, args) {
101
101
  process.stdout.write(`plumbbob: plan committed — ${sha.slice(0, 9)}. Baseline → plan → steps.\n`);
102
102
  return 0;
103
103
  }
104
- // The plan commit's CLI-owned subject (D68): `chore(<scope>): plan`, the scope drawn
105
- // from the active build's slug (a bare `chore: plan` when none resolves). The
106
- // `plumbbob plan` identifier rides the body marker, not the subject.
104
+ // The plan commit's CLI-owned subject (D68): `chore(<scope>): plan`, the scope
105
+ // resolved through the build-default fallback chain (a bare `chore: plan` when
106
+ // none resolves). The `plumbbob plan` identifier rides the body marker, not the
107
+ // subject.
107
108
  function planSubject(root) {
108
- return conventionalSubject({ type: 'chore', scope: buildScope(activeBuild(root)), description: 'plan' });
109
+ return conventionalSubject({ type: 'chore', scope: buildDefaultScope(root), description: 'plan' });
109
110
  }
110
111
  // Step resolution (D3): explicit arg > in-flight STEP file > first undone step in
111
112
  // intent.md. Returns null when none can be determined. A `-m` value is a message,
@@ -210,18 +211,36 @@ function statsSuffix(root, step) {
210
211
  return parts.length > 0 ? parts.join(', ') : null;
211
212
  }
212
213
  // The CLI-owned, deterministic commit subject (D68): a Conventional `type(scope):
213
- // description`. The scope comes from the active build's slug; the type and wording
214
- // come from the step's title — an author-written prefix (`fix(parser): …`) is honoured
215
- // verbatim, a bare title defaults to `feat`. A titleless step falls back to
216
- // `chore(<scope>): checkpoint`. The `plumbbob`/`step N` identifiers ride the body
217
- // marker, not the subject; a `-m` override or `--body` prose is a separate concern.
214
+ // description`. The type and wording come from the step's title — an author-written
215
+ // prefix (`fix(parser): …`) is honoured verbatim, a bare title defaults to `feat`.
216
+ // The scope falls back through the chain (D3): the title's own `(scope)` wins
217
+ // (handled inside `subjectFromTitle`), else `buildDefaultScope`. A titleless step
218
+ // falls back to `chore(<scope>): checkpoint`. The `plumbbob`/`step N` identifiers
219
+ // ride the body marker, not the subject; a `-m` override or `--body` prose is a
220
+ // separate concern.
218
221
  function subjectForStep(root, step) {
219
- const scope = buildScope(activeBuild(root));
222
+ const scope = buildDefaultScope(root);
220
223
  const title = titleForStep(root, step);
221
224
  return title !== null && title.trim().length > 0
222
225
  ? subjectFromTitle(title, 'feat', scope)
223
226
  : conventionalSubject({ type: 'chore', scope, description: 'checkpoint' });
224
227
  }
228
+ // The build-default Conventional scope (D3/D4): the `**Scope:**` header field in
229
+ // intent.md when authored and filled, else the build slug (D68's original rung,
230
+ // now the penultimate one), else null — a bare subject (C2 back-compat: a build
231
+ // with neither field behaves exactly as before D4).
232
+ function buildDefaultScope(root) {
233
+ try {
234
+ const fromHeader = parseBuildScope(readFileSync(intentPath(root), 'utf8'));
235
+ if (fromHeader !== null) {
236
+ return fromHeader;
237
+ }
238
+ }
239
+ catch {
240
+ // no intent.md yet — fall through to the slug rung.
241
+ }
242
+ return buildScope(activeBuild(root));
243
+ }
225
244
  function titleForStep(root, step) {
226
245
  try {
227
246
  return parseSteps(readFileSync(intentPath(root), 'utf8')).find((s) => s.n === step)?.title ?? null;
@@ -9,8 +9,9 @@
9
9
  import { appendFileSync, existsSync, readFileSync, rmSync } from 'node:fs';
10
10
  import { join } from 'node:path';
11
11
  import { commit, findRepoRoot, isDirty, stageAll } from "../lib/git.js";
12
- import { buildScope, checkpointsPath, clearTick, hasSession, readStats, reportPath, resolveBuild, seamPath, setGrant, sidecarDir, spikePath, stepPath, } from "../lib/sidecar.js";
12
+ import { buildScope, checkpointsPath, clearTick, hasSession, intentPath, readStats, reportPath, resolveBuild, seamPath, setGrant, sidecarDir, spikePath, stepPath, } from "../lib/sidecar.js";
13
13
  import { conventionalSubject, withMarker } from "../lib/commitmsg.js";
14
+ import { parseBuildScope } from "../lib/intent.js";
14
15
  export function finish(cwd, args = []) {
15
16
  const root = findRepoRoot(cwd);
16
17
  if (root === null || !hasSession(root)) {
@@ -34,7 +35,7 @@ export function finish(cwd, args = []) {
34
35
  if (isDirty(root)) {
35
36
  stageAll(root);
36
37
  }
37
- const sha = commit(root, subject(slug), withMarker('plumbbob finish', bodyArg(args) ?? undefined));
38
+ const sha = commit(root, subject(root, slug), withMarker('plumbbob finish', bodyArg(args) ?? undefined));
38
39
  // Clear the control state: the in-flight markers first, then the session sentinel
39
40
  // last (so "no session" flips exactly at the end). Deleting STATE also drops the
40
41
  // cursor (D28) — cursor and session share the one file now, so a single delete does
@@ -54,12 +55,27 @@ export function finish(cwd, args = []) {
54
55
  'Run `/pb-plan` (or `plumbbob start "<title>"`) to frame the next goal.\n');
55
56
  return 0;
56
57
  }
57
- // The CLI-owned final-commit subject (D68): `chore(<scope>): finish`, the scope drawn
58
- // from the build's slug (a bare `chore: finish` when none resolves, e.g. `--local`),
59
- // mirroring the plan/step Conventional shape so one grammar spans the whole history.
60
- // The `plumbbob finish` identifier rides the body marker, not the subject.
61
- function subject(slug) {
62
- return conventionalSubject({ type: 'chore', scope: buildScope(slug), description: 'finish' });
58
+ // The CLI-owned final-commit subject (D68): `chore(<scope>): finish`, the scope
59
+ // resolved through the same build-default fallback chain as plan/step subjects
60
+ // (D3/D4): the `**Scope:**` header field, else the build slug, else bare (e.g.
61
+ // `--local`). The `plumbbob finish` identifier rides the body marker, not the
62
+ // subject.
63
+ function subject(root, slug) {
64
+ return conventionalSubject({ type: 'chore', scope: buildDefaultScope(root, slug), description: 'finish' });
65
+ }
66
+ // See checkpoint.ts's twin of this helper (same fallback, this verb's own `slug`
67
+ // resolution instead of the active-build cursor).
68
+ function buildDefaultScope(root, slug) {
69
+ try {
70
+ const fromHeader = parseBuildScope(readFileSync(intentPath(root, slug), 'utf8'));
71
+ if (fromHeader !== null) {
72
+ return fromHeader;
73
+ }
74
+ }
75
+ catch {
76
+ // no intent.md — fall through to the slug rung.
77
+ }
78
+ return buildScope(slug);
63
79
  }
64
80
  // `--body` reads the final-commit body from stdin (the single-quoted heredoc of
65
81
  // D34), so the pb-finish skill can compose a proportional close-out message. Returns
@@ -4,8 +4,235 @@ All notable changes to this project are documented here. The format is based on
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
5
5
  adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.5.2] - 2026-07-19
8
+
9
+ ### Internal
10
+
11
+ - Split the release workflow into a gated `publish.yaml` (npm publish with
12
+ provenance, now paused behind a required-reviewer approval on the
13
+ `npm-publish` GitHub Environment) and a separate `release.yaml` (GitHub
14
+ Release from the CHANGELOG), matching the fascicle/plumbbob pattern so the
15
+ npm publish credential and the release-authoring permission never share a
16
+ job.
17
+
18
+ ## [0.5.1] - 2026-07-19
19
+
20
+ ### Added
21
+
22
+ - **A GitHub Pages marketing site** under `site/` — a homepage pitching the
23
+ elevator case for checkride, a docs hub aggregating the existing
24
+ `docs/*.md` guides into a concise sidebar reference, and a full
25
+ CLI/config/programmatic API reference — plus the GitHub Actions workflow
26
+ that deploys it.
27
+ - `docs/why.md` — the case for adopting checkride: what it's selling, the ROI
28
+ against ad-hoc scripts and task runners, and straight answers to the
29
+ objections that come up in an adoption conversation.
30
+
31
+ ## [0.5.0] - 2026-07-19
32
+
33
+ ### Contract
34
+
35
+ - **The `order` field is now a first-class, promised scheduling surface.** A
36
+ config entry's `order` accepts a **number** — a wave, where distinct wave
37
+ numbers run in ascending order with a barrier between them, checks sharing a
38
+ number run concurrently, and decimals sequence steps within a wave (`1` before
39
+ `1.1`) — or one of `first`, `last`, `middle`, `single`, `any`. It is honored
40
+ on every object-form entry: a slot's `{ use, order }` and a custom check
41
+ alike. `first`/`last` keep their exact historical meaning, pinned by a
42
+ backward-compat contract test. See `docs/contract.md` §"Check ordering and
43
+ concurrency".
44
+ - **Two deliberate default-placement changes**, both noted in `docs/contract.md`:
45
+ a config-only custom check with **no** `order` now defaults to `any` (the main
46
+ group) instead of the old implicit `last` — set `"order": "last"` to restore
47
+ the previous placement — and a catalogue-filling custom entry's `order`, which
48
+ earlier releases documented as ignored, is now **honored**. A sequential
49
+ default run is unaffected in verdicts and summary order; the difference is
50
+ visible only under concurrency or beside a numbered wave.
51
+ - **New `--concurrency <n>` flag.** Sets the size of the pool that runs a wave's
52
+ checks concurrently (`1` = sequential; the default is a conservative cap
53
+ derived from the CPU count). `--bail` overrides it: the run goes fully
54
+ sequential and a one-line stderr note reports that `--concurrency` was ignored
55
+ (the combination is safe, just slower — not a usage error). Additive to the
56
+ flag contract and contract-tested.
57
+ - **`total_duration_ms` is now the run's wall-clock duration.** Under
58
+ concurrency the summary's total is measured wall-to-wall rather than summed
59
+ across checks; the two are identical for any sequential run (including
60
+ `--bail`). The `checks` array stays in deterministic group order — the run's
61
+ scheduling sequence, never completion order. `schema_version` is unchanged.
62
+
63
+ ### Added
64
+
65
+ - **A publish-ready bundle of four opt-in slots** that take the definition of
66
+ done past static publishing lint (`publint`, `attw`) and out to the shipped
67
+ artifact. Each is a built-in or runs the consumer's own `build`/`tsc`, so
68
+ enabling them adds **zero devDependencies**:
69
+ - **`build`** (wave 10) runs the consumer's `build` script, so the artifact
70
+ checks below inspect fresh output rather than a stale `dist/`. An opted-in
71
+ `build` on a repo with no build script stands down as a skip, never a red
72
+ check.
73
+ - **`pack`** (wave 20) packs the tarball with a dry-run and fails if a required
74
+ file (a resolved `exports`/`main`/`types`/`bin` target, or `README`) is
75
+ missing or a forbidden one (`src/`, tests, `.ts` sources — the
76
+ `dist/**/*.d.ts` declarations excepted) is shipped. npm/pnpm only; yarn/bun
77
+ report **unavailable** until a per-manager adapter lands, like `security`.
78
+ - **`smoke`** (wave 20) imports every `exports` entry of the built package
79
+ through its own resolution map and asserts each declared value export is live
80
+ at runtime — a liveness check, not a type check.
81
+ - **`snippets`** (wave 20 / `any`) type-checks the fenced code blocks tagged
82
+ `<!-- snippet: check -->` in `README.md` and `docs/*.md`. The default
83
+ `snippets` adapter checks against source; a second `snippets-dist` adapter
84
+ checks against the built `.d.ts`. A slot opted in with zero tagged fences is
85
+ a hard error.
86
+
87
+ All four are opt-in (`--all`, `--include`, or config), so a default run is
88
+ byte-for-byte unchanged. They **order themselves**: `build` at wave 10 precedes
89
+ `pack`/`smoke`/`snippets`/`publint`/`attw` at wave 20, so `checkride --all`
90
+ builds before it inspects the artifact with no ordering config. `checkride
91
+ init` on a library can scaffold the bundle.
92
+
93
+ ### Changed
94
+
95
+ - **A dependency can now activate a slot, not just its config file.** Adapters
96
+ whose tool runs correctly with zero config — `oxlint` (lint), `knip` (dead),
97
+ `vitest` (test), `cspell` (spell), and `prettier` (the opt-in format slot) —
98
+ now also activate when the package appears in `dependencies`/`devDependencies`,
99
+ as a backup to the detect-file signal. A repo that installed one of these but
100
+ never wrote its config file gains that check on upgrade; `doctor` names which
101
+ signal matched. This widens the default run for those slots and is a
102
+ deliberate, noted behavior change.
103
+ - **Checks now run concurrently within a wave by default.** The orchestrator
104
+ schedules the wave sequence — `first`s, the numeric line ascending (equal
105
+ values through a bounded pool, a barrier between distinct values), `single`s
106
+ exclusively, then `last`s — instead of strictly one-at-a-time cheapest-first.
107
+ `--bail` keeps the sequential fail-fast path. The catalogue ships ordered so
108
+ existing default runs produce the same verdicts and the same summary order as
109
+ before, only faster; `mutation` runs as a `single` (exclusive) because Stryker
110
+ saturates every core and races the real test run's cache.
111
+ - **`mutation` now ships uncapped (`timeout: 0`) by default.** A real Stryker run
112
+ legitimately outlives the 600s per-check cap; because `mutation` is opt-in and
113
+ never part of the definition-of-done gate the cap protects, its adapter runs to
114
+ completion instead of tripping a timeout under `checkride --all`. Every other
115
+ slot keeps the safe-by-default cap; override per check or globally with
116
+ `timeout`.
117
+
118
+ ## [0.4.3] - 2026-07-18
119
+
120
+ ### Internal
121
+
122
+ - Refactored for maintainability: decomposed high-complexity functions across
123
+ the orchestrator, `init`, `doctor`, `config`, and the baseline code into small
124
+ single-purpose helpers, extracted shared option/stanza helpers, and broke
125
+ internal import cycles. No change to runtime behavior or the public API.
126
+ - Zeroed the project's grandfathered debt: cleared the `lint`, `dead`, `dupes`,
127
+ and `health` baselines and removed checkride's own `checkride.baseline.json`,
128
+ so the repo passes a full `pnpm check` with no suppressions.
129
+ - The repo's own default `pnpm check` now also runs the `dupes` and `health`
130
+ slots. Mutation stays opt-in (`pnpm mutation`): a cold full pass has no warm
131
+ incremental cache in CI and would time out the gate.
132
+
133
+ ## [0.4.2] - 2026-07-18
134
+
135
+ ### Fixed
136
+
137
+ - **The `dead` (fallow) slot now actually gates.** checkride ran fallow in JSON
138
+ mode, which exits `0` even with findings, so the slot reported ✔ while fallow
139
+ had real issues. checkride now reads fallow's JSON report and derives the
140
+ verdict from the issue count instead of the (unreliable) exit code — so the
141
+ slot fails `pnpm check` on new findings and passes only when clean or fully
142
+ baselined. An **unrecognized fallow report fails loudly** (explicit
143
+ "unsupported schema_version" / "unrecognized kind") rather than passing
144
+ silently.
145
+ - **fallow ≥ 3.5 support.** The dead-code parser reads fallow's current
146
+ `schema_version` 7 JSON (2.x emitted schema 4 with an incompatible layout).
147
+ The pinned devDependency moves from `fallow@2.48.0` to `fallow@3.5.0`.
148
+
149
+ ### Added
150
+
151
+ - **`checkride baseline` now grandfathers fallow findings.** A fingerprint
152
+ extractor keys each fallow finding by kind + file + symbol, so fallow slots
153
+ participate in `checkride.baseline.json` like `lint`/`struct`/`spell` (and
154
+ ratchet the same way). Repos that prefer fallow's native `--save-baseline`
155
+ suppression can still use it — see `docs/tools.md`.
156
+ - **New opt-in `dupes` and `health` slots.** fallow's duplication and
157
+ complexity analyses are now first-class checks (`--include dupes,health` or
158
+ `"dupes": "fallow"` in config), each with gating and baselines. They stay
159
+ opt-in so adopting checkride never fails a repo on duplication/complexity it
160
+ never signed up for.
161
+
162
+ ## [0.4.1] - 2026-07-11
163
+
164
+ ### Internal
165
+
166
+ - Docs refresh: updated the reported mutation score to 69% and bumped the README `$schema` example to v0.4.0.
167
+
168
+ ## [0.4.0] - 2026-07-11
169
+
170
+ ### Contract
171
+
172
+ - **An unknown slot name is now a usage error.** An unrecognized slot in
173
+ `--only`, `--skip`, or `--include` (e.g. `checkride --only lints`) exits **2**,
174
+ naming the bad slot and the valid set (catalogue slots plus config
175
+ custom-check names). It previously matched nothing and exited **0** — a typo
176
+ could silently disable the gate, the worst vacuous green in a definition-of-done
177
+ check. See `docs/contract.md` §CLI.
178
+
179
+ ### Added
180
+
181
+ - New-project `checkride init` refuses to overwrite existing files, listing
182
+ every collision, and writes nothing; `--force` overrides. Existing-project
183
+ mode (additive-only) is unchanged.
184
+ - Per-command `--help` (`checkride init --help`, etc.); new-project `init` ends
185
+ by printing the next command to run; `checkride baseline` now rejects stray
186
+ flags instead of ignoring them.
187
+
188
+ ### Changed
189
+
190
+ - `checkride init` scaffolds an exact checkride version (no caret), and the
191
+ README install uses `pnpm add -D -E checkride`, matching the pre-1.0
192
+ exact-pin policy consumers are told to follow.
193
+ - Malformed consumer JSON (`.claude/settings.json`, a project `package.json`)
194
+ now produces an error naming the offending file instead of a bare stack trace.
195
+
196
+ ### Fixed
197
+
198
+ - **Interrupts no longer orphan checks.** Ctrl-C (SIGINT) or SIGTERM on a
199
+ running `checkride` is forwarded to every in-flight check and group-kills its
200
+ whole process tree before exit, then re-raises so the shell still sees the
201
+ conventional signal exit (130/143). Since checks run in detached process
202
+ groups (for the timeout kill), a plain interrupt previously left them running.
203
+ - `doctor` distinguishes a version probe that **timed out** from one that
204
+ **could not be parsed** (30s probe), so a slow `pnpm --version` is no longer
205
+ misdiagnosed.
206
+ - `checkride init --baseline --dry-run` no longer writes a real
207
+ `checkride.baseline.json` — a dry run now truly writes nothing.
208
+ - A timed-out check's whole process group is killed (wrapper-spawned
209
+ grandchildren included), and output is captured with a UTF-8 decoder so a
210
+ multibyte character split across read chunks survives intact.
211
+ - `checkride fix` runs under the detected package manager (e.g. `npx` under
212
+ npm), matching the run path instead of assuming pnpm.
213
+ - A slot's stale `.check/` artifacts are cleared before it re-runs, so a leaner
214
+ or empty run can't leave the previous run's output behind as authoritative.
215
+
216
+ ### Internal
217
+
218
+ - Docs pass: README restructure linking all six `docs/` files and splitting the
219
+ existing-repo vs new-project install paths, getting-started/tools sync,
220
+ reconciliation of the "locked by `test/contract/`" claim with real tests, and
221
+ a batch of drift corrections.
222
+ - Release automation: tag↔version guard on release, CI concurrency group,
223
+ security-only Dependabot; `publint` and `attw` added as dev checks and
224
+ dogfooded; explicit test timeouts for slow-spawn machines; npm publishing
225
+ switched to Trusted Publishing (OIDC).
226
+
7
227
  ## [0.3.0] - 2026-07-10
8
228
 
229
+ ### Contract
230
+
231
+ - Three additions to the promised surfaces, each recorded in `docs/contract.md`
232
+ and locked by `test/contract/`: the `summary.json` `checks_run` field, the
233
+ `--strict` flag, and the `DEFAULT_TIMEOUT_SECONDS` export. (Heading added
234
+ retroactively — the full entries are under **Added** below.)
235
+
9
236
  ### Added
10
237
 
11
238
  - **Vacuous-green signal.** `summary.json` gains a top-level `checks_run` count
@@ -238,6 +465,13 @@ The first real release. (`0.0.0` was a name-claim placeholder.)
238
465
  - Flags: `--only`, `--skip`, `--bail`, `--json`, `--changed`, `--all`,
239
466
  `--include`.
240
467
 
468
+ [0.5.2]: https://www.npmjs.com/package/checkride/v/0.5.2
469
+ [0.5.1]: https://www.npmjs.com/package/checkride/v/0.5.1
470
+ [0.5.0]: https://www.npmjs.com/package/checkride/v/0.5.0
471
+ [0.4.3]: https://www.npmjs.com/package/checkride/v/0.4.3
472
+ [0.4.2]: https://www.npmjs.com/package/checkride/v/0.4.2
473
+ [0.4.1]: https://www.npmjs.com/package/checkride/v/0.4.1
474
+ [0.4.0]: https://www.npmjs.com/package/checkride/v/0.4.0
241
475
  [0.3.0]: https://www.npmjs.com/package/checkride/v/0.3.0
242
476
  [0.2.1]: https://www.npmjs.com/package/checkride/v/0.2.1
243
477
  [0.2.0]: https://www.npmjs.com/package/checkride/v/0.2.0