plumbbob 0.9.3 → 0.10.0
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.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +92 -89
- package/dist/cli-core.js +25 -8
- package/dist/cli.js +1 -1
- package/dist/lib/agents.js +26 -26
- package/dist/lib/buildlog.js +11 -11
- package/dist/lib/buildlogsync.js +8 -8
- package/dist/lib/check.js +55 -22
- package/dist/lib/commitbody.js +72 -0
- package/dist/lib/commitmsg.js +7 -7
- package/dist/lib/git.js +44 -15
- package/dist/lib/intent.js +21 -20
- package/dist/lib/latch.js +19 -19
- package/dist/lib/orient.js +36 -17
- package/dist/lib/reentry.js +70 -0
- package/dist/lib/settings.js +16 -16
- package/dist/lib/sidecar.js +54 -54
- package/dist/lib/templates.js +1 -1
- package/dist/verbs/agent.js +27 -27
- package/dist/verbs/build.js +9 -9
- package/dist/verbs/check.js +6 -6
- package/dist/verbs/checkpoint.js +48 -47
- package/dist/verbs/doctor.js +31 -30
- package/dist/verbs/finish.js +28 -41
- package/dist/verbs/handoff.js +9 -8
- package/dist/verbs/init.js +9 -9
- package/dist/verbs/park.js +3 -3
- package/dist/verbs/recover.js +297 -0
- package/dist/verbs/revert.js +13 -13
- package/dist/verbs/spike.js +25 -19
- package/dist/verbs/start.js +49 -19
- package/dist/verbs/status.js +8 -8
- package/dist/verbs/turn.js +16 -16
- package/dist/verbs/use.js +4 -4
- package/hooks/post-edit.sh +4 -4
- package/hooks/pre-bash-commit.sh +3 -3
- package/node_modules/checkride/.claude-plugin/plugin.json +11 -0
- package/node_modules/checkride/CHANGELOG.md +1404 -0
- package/node_modules/checkride/LICENSE +201 -21
- package/node_modules/checkride/README.md +197 -38
- package/node_modules/checkride/dist/__tests__/fixtures/released-stanzas.d.ts +15 -0
- package/node_modules/checkride/dist/__tests__/fixtures/released-stanzas.d.ts.map +1 -0
- package/node_modules/checkride/dist/__tests__/fixtures/released-stanzas.js +17 -0
- package/node_modules/checkride/dist/__tests__/fixtures/released-stanzas.js.map +1 -0
- package/node_modules/checkride/dist/adapters.d.ts +30 -10
- package/node_modules/checkride/dist/adapters.d.ts.map +1 -1
- package/node_modules/checkride/dist/adapters.js +62 -23
- package/node_modules/checkride/dist/adapters.js.map +1 -1
- package/node_modules/checkride/dist/agent-setup/claude.d.ts +88 -0
- package/node_modules/checkride/dist/agent-setup/claude.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/claude.js +305 -0
- package/node_modules/checkride/dist/agent-setup/claude.js.map +1 -0
- package/node_modules/checkride/dist/agent-setup/command.d.ts +43 -0
- package/node_modules/checkride/dist/agent-setup/command.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/command.js +77 -0
- package/node_modules/checkride/dist/agent-setup/command.js.map +1 -0
- package/node_modules/checkride/dist/agent-setup/cursor.d.ts +69 -0
- package/node_modules/checkride/dist/agent-setup/cursor.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/cursor.js +247 -0
- package/node_modules/checkride/dist/agent-setup/cursor.js.map +1 -0
- package/node_modules/checkride/dist/agent-setup/files.d.ts +50 -0
- package/node_modules/checkride/dist/agent-setup/files.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/files.js +80 -0
- package/node_modules/checkride/dist/agent-setup/files.js.map +1 -0
- package/node_modules/checkride/dist/agent-setup/harness.d.ts +101 -0
- package/node_modules/checkride/dist/agent-setup/harness.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/harness.js +93 -0
- package/node_modules/checkride/dist/agent-setup/harness.js.map +1 -0
- package/node_modules/checkride/dist/agent-setup/hook.d.ts +45 -52
- package/node_modules/checkride/dist/agent-setup/hook.d.ts.map +1 -1
- package/node_modules/checkride/dist/agent-setup/hook.js +64 -95
- package/node_modules/checkride/dist/agent-setup/hook.js.map +1 -1
- package/node_modules/checkride/dist/agent-setup/index.d.ts +10 -4
- package/node_modules/checkride/dist/agent-setup/index.d.ts.map +1 -1
- package/node_modules/checkride/dist/agent-setup/index.js +9 -4
- package/node_modules/checkride/dist/agent-setup/index.js.map +1 -1
- package/node_modules/checkride/dist/agent-setup/scripts.d.ts +81 -0
- package/node_modules/checkride/dist/agent-setup/scripts.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/scripts.js +604 -0
- package/node_modules/checkride/dist/agent-setup/scripts.js.map +1 -0
- package/node_modules/checkride/dist/agent-setup/skills.d.ts +31 -0
- package/node_modules/checkride/dist/agent-setup/skills.d.ts.map +1 -0
- package/node_modules/checkride/dist/agent-setup/skills.js +56 -0
- package/node_modules/checkride/dist/agent-setup/skills.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/configured.d.ts +30 -0
- package/node_modules/checkride/dist/artifacts/configured.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/configured.js +45 -0
- package/node_modules/checkride/dist/artifacts/configured.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/format.d.ts +33 -0
- package/node_modules/checkride/dist/artifacts/format.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/format.js +56 -0
- package/node_modules/checkride/dist/artifacts/format.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/freshness.d.ts +32 -0
- package/node_modules/checkride/dist/artifacts/freshness.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/freshness.js +37 -0
- package/node_modules/checkride/dist/artifacts/freshness.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/index.d.ts +22 -0
- package/node_modules/checkride/dist/artifacts/index.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/index.js +19 -0
- package/node_modules/checkride/dist/artifacts/index.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/json.d.ts +31 -0
- package/node_modules/checkride/dist/artifacts/json.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/json.js +47 -0
- package/node_modules/checkride/dist/artifacts/json.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/raw.d.ts +60 -0
- package/node_modules/checkride/dist/artifacts/raw.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/raw.js +91 -0
- package/node_modules/checkride/dist/artifacts/raw.js.map +1 -0
- package/node_modules/checkride/dist/artifacts/summary.d.ts +69 -0
- package/node_modules/checkride/dist/artifacts/summary.d.ts.map +1 -0
- package/node_modules/checkride/dist/artifacts/summary.js +108 -0
- package/node_modules/checkride/dist/artifacts/summary.js.map +1 -0
- package/node_modules/checkride/dist/atomic.d.ts +16 -1
- package/node_modules/checkride/dist/atomic.d.ts.map +1 -1
- package/node_modules/checkride/dist/atomic.js +25 -3
- package/node_modules/checkride/dist/atomic.js.map +1 -1
- package/node_modules/checkride/dist/baseline/fallow.d.ts +9 -5
- package/node_modules/checkride/dist/baseline/fallow.d.ts.map +1 -1
- package/node_modules/checkride/dist/baseline/fallow.js +163 -60
- package/node_modules/checkride/dist/baseline/fallow.js.map +1 -1
- package/node_modules/checkride/dist/baseline/fingerprint.d.ts +20 -11
- package/node_modules/checkride/dist/baseline/fingerprint.d.ts.map +1 -1
- package/node_modules/checkride/dist/baseline/fingerprint.js +73 -23
- package/node_modules/checkride/dist/baseline/fingerprint.js.map +1 -1
- package/node_modules/checkride/dist/baseline/history.d.ts +98 -0
- package/node_modules/checkride/dist/baseline/history.d.ts.map +1 -0
- package/node_modules/checkride/dist/baseline/history.js +232 -0
- package/node_modules/checkride/dist/baseline/history.js.map +1 -0
- package/node_modules/checkride/dist/baseline/index.d.ts +7 -5
- package/node_modules/checkride/dist/baseline/index.d.ts.map +1 -1
- package/node_modules/checkride/dist/baseline/index.js +5 -4
- package/node_modules/checkride/dist/baseline/index.js.map +1 -1
- package/node_modules/checkride/dist/baseline/store.d.ts +41 -10
- package/node_modules/checkride/dist/baseline/store.d.ts.map +1 -1
- package/node_modules/checkride/dist/baseline/store.js +65 -24
- package/node_modules/checkride/dist/baseline/store.js.map +1 -1
- package/node_modules/checkride/dist/baseline-command.d.ts +5 -5
- package/node_modules/checkride/dist/baseline-command.js +7 -7
- package/node_modules/checkride/dist/baseline-command.js.map +1 -1
- package/node_modules/checkride/dist/cli.d.ts +21 -4
- package/node_modules/checkride/dist/cli.d.ts.map +1 -1
- package/node_modules/checkride/dist/cli.js +350 -36
- package/node_modules/checkride/dist/cli.js.map +1 -1
- package/node_modules/checkride/dist/config.d.ts +114 -9
- package/node_modules/checkride/dist/config.d.ts.map +1 -1
- package/node_modules/checkride/dist/config.js +142 -23
- package/node_modules/checkride/dist/config.js.map +1 -1
- package/node_modules/checkride/dist/digest/digest.d.ts +5 -5
- package/node_modules/checkride/dist/digest/digest.js +6 -6
- package/node_modules/checkride/dist/digest/index.d.ts +2 -2
- package/node_modules/checkride/dist/digest/index.js +2 -2
- package/node_modules/checkride/dist/doctor.d.ts +27 -6
- package/node_modules/checkride/dist/doctor.d.ts.map +1 -1
- package/node_modules/checkride/dist/doctor.js +231 -27
- package/node_modules/checkride/dist/doctor.js.map +1 -1
- package/node_modules/checkride/dist/gate.d.ts +180 -0
- package/node_modules/checkride/dist/gate.d.ts.map +1 -0
- package/node_modules/checkride/dist/gate.js +617 -0
- package/node_modules/checkride/dist/gate.js.map +1 -0
- package/node_modules/checkride/dist/init.d.ts +79 -12
- package/node_modules/checkride/dist/init.d.ts.map +1 -1
- package/node_modules/checkride/dist/init.js +385 -81
- package/node_modules/checkride/dist/init.js.map +1 -1
- package/node_modules/checkride/dist/json.d.ts +21 -0
- package/node_modules/checkride/dist/json.d.ts.map +1 -0
- package/node_modules/checkride/dist/json.js +21 -0
- package/node_modules/checkride/dist/json.js.map +1 -0
- package/node_modules/checkride/dist/links.d.ts +31 -5
- package/node_modules/checkride/dist/links.d.ts.map +1 -1
- package/node_modules/checkride/dist/links.js +141 -32
- package/node_modules/checkride/dist/links.js.map +1 -1
- package/node_modules/checkride/dist/node-pin.d.ts +129 -0
- package/node_modules/checkride/dist/node-pin.d.ts.map +1 -0
- package/node_modules/checkride/dist/node-pin.js +211 -0
- package/node_modules/checkride/dist/node-pin.js.map +1 -0
- package/node_modules/checkride/dist/orchestrator.d.ts +53 -10
- package/node_modules/checkride/dist/orchestrator.d.ts.map +1 -1
- package/node_modules/checkride/dist/orchestrator.js +312 -113
- package/node_modules/checkride/dist/orchestrator.js.map +1 -1
- package/node_modules/checkride/dist/pack.d.ts +8 -9
- package/node_modules/checkride/dist/pack.d.ts.map +1 -1
- package/node_modules/checkride/dist/pack.js +53 -17
- package/node_modules/checkride/dist/pack.js.map +1 -1
- package/node_modules/checkride/dist/pm/detect.d.ts +6 -1
- package/node_modules/checkride/dist/pm/detect.d.ts.map +1 -1
- package/node_modules/checkride/dist/pm/detect.js +6 -1
- package/node_modules/checkride/dist/pm/detect.js.map +1 -1
- package/node_modules/checkride/dist/pm/index.d.ts +7 -2
- package/node_modules/checkride/dist/pm/index.d.ts.map +1 -1
- package/node_modules/checkride/dist/pm/index.js +6 -2
- package/node_modules/checkride/dist/pm/index.js.map +1 -1
- package/node_modules/checkride/dist/pm/launch.d.ts +72 -0
- package/node_modules/checkride/dist/pm/launch.d.ts.map +1 -0
- package/node_modules/checkride/dist/pm/launch.js +108 -0
- package/node_modules/checkride/dist/pm/launch.js.map +1 -0
- package/node_modules/checkride/dist/pm/tools.d.ts +73 -0
- package/node_modules/checkride/dist/pm/tools.d.ts.map +1 -0
- package/node_modules/checkride/dist/pm/tools.js +124 -0
- package/node_modules/checkride/dist/pm/tools.js.map +1 -0
- package/node_modules/checkride/dist/pm/translate.d.ts +35 -9
- package/node_modules/checkride/dist/pm/translate.d.ts.map +1 -1
- package/node_modules/checkride/dist/pm/translate.js +78 -18
- package/node_modules/checkride/dist/pm/translate.js.map +1 -1
- package/node_modules/checkride/dist/proc.d.ts +30 -0
- package/node_modules/checkride/dist/proc.d.ts.map +1 -0
- package/node_modules/checkride/dist/proc.js +42 -0
- package/node_modules/checkride/dist/proc.js.map +1 -0
- package/node_modules/checkride/dist/qa/cli.d.ts +20 -0
- package/node_modules/checkride/dist/qa/cli.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/cli.js +24 -0
- package/node_modules/checkride/dist/qa/cli.js.map +1 -0
- package/node_modules/checkride/dist/qa/dead.d.ts +39 -0
- package/node_modules/checkride/dist/qa/dead.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/dead.js +88 -0
- package/node_modules/checkride/dist/qa/dead.js.map +1 -0
- package/node_modules/checkride/dist/qa/dupes.d.ts +45 -0
- package/node_modules/checkride/dist/qa/dupes.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/dupes.js +62 -0
- package/node_modules/checkride/dist/qa/dupes.js.map +1 -0
- package/node_modules/checkride/dist/qa/health.d.ts +85 -0
- package/node_modules/checkride/dist/qa/health.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/health.js +109 -0
- package/node_modules/checkride/dist/qa/health.js.map +1 -0
- package/node_modules/checkride/dist/qa/index.d.ts +23 -0
- package/node_modules/checkride/dist/qa/index.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/index.js +22 -0
- package/node_modules/checkride/dist/qa/index.js.map +1 -0
- package/node_modules/checkride/dist/qa/mutation.d.ts +80 -0
- package/node_modules/checkride/dist/qa/mutation.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/mutation.js +195 -0
- package/node_modules/checkride/dist/qa/mutation.js.map +1 -0
- package/node_modules/checkride/dist/qa/qa.d.ts +71 -0
- package/node_modules/checkride/dist/qa/qa.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/qa.js +158 -0
- package/node_modules/checkride/dist/qa/qa.js.map +1 -0
- package/node_modules/checkride/dist/qa/read.d.ts +40 -0
- package/node_modules/checkride/dist/qa/read.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/read.js +47 -0
- package/node_modules/checkride/dist/qa/read.js.map +1 -0
- package/node_modules/checkride/dist/qa/render.d.ts +26 -0
- package/node_modules/checkride/dist/qa/render.d.ts.map +1 -0
- package/node_modules/checkride/dist/qa/render.js +310 -0
- package/node_modules/checkride/dist/qa/render.js.map +1 -0
- package/node_modules/checkride/dist/recover.d.ts +48 -0
- package/node_modules/checkride/dist/recover.d.ts.map +1 -0
- package/node_modules/checkride/dist/recover.js +220 -0
- package/node_modules/checkride/dist/recover.js.map +1 -0
- package/node_modules/checkride/dist/security.d.ts +45 -0
- package/node_modules/checkride/dist/security.d.ts.map +1 -0
- package/node_modules/checkride/dist/security.js +86 -0
- package/node_modules/checkride/dist/security.js.map +1 -0
- package/node_modules/checkride/dist/smoke.d.ts +6 -6
- package/node_modules/checkride/dist/smoke.js +6 -6
- package/node_modules/checkride/dist/snippets.d.ts +27 -30
- package/node_modules/checkride/dist/snippets.d.ts.map +1 -1
- package/node_modules/checkride/dist/snippets.js +26 -30
- package/node_modules/checkride/dist/snippets.js.map +1 -1
- package/node_modules/checkride/dist/tool-json.d.ts +33 -0
- package/node_modules/checkride/dist/tool-json.d.ts.map +1 -0
- package/node_modules/checkride/dist/tool-json.js +58 -0
- package/node_modules/checkride/dist/tool-json.js.map +1 -0
- package/node_modules/checkride/dist/triage/cli.d.ts +21 -0
- package/node_modules/checkride/dist/triage/cli.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/cli.js +26 -0
- package/node_modules/checkride/dist/triage/cli.js.map +1 -0
- package/node_modules/checkride/dist/triage/doctor-fold.d.ts +51 -0
- package/node_modules/checkride/dist/triage/doctor-fold.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/doctor-fold.js +88 -0
- package/node_modules/checkride/dist/triage/doctor-fold.js.map +1 -0
- package/node_modules/checkride/dist/triage/env.d.ts +54 -0
- package/node_modules/checkride/dist/triage/env.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/env.js +81 -0
- package/node_modules/checkride/dist/triage/env.js.map +1 -0
- package/node_modules/checkride/dist/triage/gate.d.ts +61 -0
- package/node_modules/checkride/dist/triage/gate.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/gate.js +121 -0
- package/node_modules/checkride/dist/triage/gate.js.map +1 -0
- package/node_modules/checkride/dist/triage/index.d.ts +22 -0
- package/node_modules/checkride/dist/triage/index.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/index.js +20 -0
- package/node_modules/checkride/dist/triage/index.js.map +1 -0
- package/node_modules/checkride/dist/triage/render.d.ts +19 -0
- package/node_modules/checkride/dist/triage/render.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/render.js +419 -0
- package/node_modules/checkride/dist/triage/render.js.map +1 -0
- package/node_modules/checkride/dist/triage/triage.d.ts +83 -0
- package/node_modules/checkride/dist/triage/triage.d.ts.map +1 -0
- package/node_modules/checkride/dist/triage/triage.js +131 -0
- package/node_modules/checkride/dist/triage/triage.js.map +1 -0
- package/node_modules/checkride/package.json +18 -15
- package/node_modules/checkride/schema/checkride.config.schema.json +70 -5
- package/node_modules/checkride/schema/checkride.summary.schema.json +1 -1
- package/node_modules/checkride/skills/check/SKILL.md +265 -0
- package/node_modules/checkride/skills/qa/SKILL.md +231 -0
- package/node_modules/checkride/templates/shared/cspell.json +1 -0
- package/node_modules/checkride/templates/shared/oxlintrc.json +1 -1
- package/node_modules/checkride/templates/shared/styles/Repo/Drift.yml +41 -0
- package/node_modules/checkride/templates/shared/styles/Repo/Latin.yml +14 -0
- package/node_modules/checkride/templates/shared/styles/Repo/LyHyphen.yml +28 -0
- package/node_modules/checkride/templates/shared/styles/Repo/Minted.yml +33 -0
- package/node_modules/checkride/templates/shared/styles/Repo/ThereIs.yml +16 -0
- package/node_modules/checkride/templates/shared/styles/Repo/Weasel.yml +26 -0
- package/node_modules/checkride/templates/shared/vale.ini +34 -0
- package/package.json +10 -9
- package/skills/build/SKILL.md +85 -76
- package/skills/doctor/SKILL.md +11 -11
- package/skills/finish/SKILL.md +21 -18
- package/skills/harvest/SKILL.md +13 -11
- package/skills/park/SKILL.md +10 -10
- package/skills/plan/SKILL.md +69 -59
- package/skills/recover/SKILL.md +28 -0
- package/skills/refine/SKILL.md +21 -18
- package/skills/revert/SKILL.md +6 -6
- package/skills/spike/SKILL.md +9 -9
- package/skills/status/SKILL.md +5 -5
- package/skills/step/SKILL.md +20 -15
- package/skills/verify/SKILL.md +56 -46
- package/templates/build-log.md +7 -7
- package/templates/intent.md +52 -41
- package/templates/spike-report.md +10 -10
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `mutation.json` — 2.3 MB and 4453 mutants in this repo — folded into a short
|
|
3
|
+
* list an agent can act on.
|
|
4
|
+
*
|
|
5
|
+
* The scores are stryker's own, computed the way stryker computes them, so the
|
|
6
|
+
* numbers here match the ones its reporter printed: `Ignored` (and any error
|
|
7
|
+
* status) leaves the denominator entirely, `Killed` and `Timeout` are detected,
|
|
8
|
+
* `Survived` and `NoCoverage` are not. Inventing a differently defined score
|
|
9
|
+
* than the tool prints is the confidently wrong answer this reader exists to
|
|
10
|
+
* prevent, so the arithmetic is pinned to the tool's.
|
|
11
|
+
*
|
|
12
|
+
* **The ranking is the judgment here.** 777 survivors is not a list anyone
|
|
13
|
+
* reads, so files rank by *undetected* count (`Survived` + `NoCoverage`)
|
|
14
|
+
* descending — that is what "where would tests buy the most" means — with the
|
|
15
|
+
* per-file score shown beside it so a small badly tested file is still legible
|
|
16
|
+
* next to a large mostly tested one. Ranking by score alone would put a
|
|
17
|
+
* two-mutant file at the top of a 4453-mutant report; ranking by count alone
|
|
18
|
+
* would say nothing but "the big files are big". Ties break on the worse score,
|
|
19
|
+
* then the path, so the same report always renders the same bytes.
|
|
20
|
+
*
|
|
21
|
+
* Two further cuts carry most of the signal at almost no byte cost. Splitting
|
|
22
|
+
* `NoCoverage` out from `Survived` separates "no test reaches this line" (add
|
|
23
|
+
* any test) from "a test reaches it and asserts nothing about it" (strengthen
|
|
24
|
+
* an assertion). And tallying undetected mutants *by mutator kind* separates
|
|
25
|
+
* real logic gaps (`ConditionalExpression`, `EqualityOperator`) from the
|
|
26
|
+
* `StringLiteral` churn in error messages that no one intends to assert on.
|
|
27
|
+
*
|
|
28
|
+
* The `../qa` barrel is this module's only public surface.
|
|
29
|
+
*/
|
|
30
|
+
import { asStringOrNull, isRecord } from '../artifacts/index.js';
|
|
31
|
+
/** Files listed; the rest are counted, never silently dropped. */
|
|
32
|
+
const TOP_FILES = 8;
|
|
33
|
+
/** Mutator kinds listed repo-wide, and per file. */
|
|
34
|
+
const TOP_MUTATORS = 8;
|
|
35
|
+
const TOP_FILE_MUTATORS = 3;
|
|
36
|
+
/**
|
|
37
|
+
* Located samples come only from the worst few files, because a line number is
|
|
38
|
+
* the one thing a reader cannot get back without opening the 2.3 MB file.
|
|
39
|
+
*/
|
|
40
|
+
const SAMPLE_FILES = 3;
|
|
41
|
+
const SAMPLES_PER_FILE = 3;
|
|
42
|
+
/** A replacement is source text and can be a whole block; only a taste fits. */
|
|
43
|
+
const MAX_REPLACEMENT_CHARS = 48;
|
|
44
|
+
/** Stryker status → the bucket it counts in. Anything unlisted is `other`. */
|
|
45
|
+
const TALLY_KEY = {
|
|
46
|
+
Killed: 'killed',
|
|
47
|
+
Timeout: 'timeout',
|
|
48
|
+
Survived: 'survived',
|
|
49
|
+
NoCoverage: 'noCoverage',
|
|
50
|
+
Ignored: 'ignored',
|
|
51
|
+
};
|
|
52
|
+
/** Every {@link Tally} bucket, so summing two of them needs no type assertion. */
|
|
53
|
+
const TALLY_KEYS = ['killed', 'timeout', 'survived', 'noCoverage', 'ignored', 'other'];
|
|
54
|
+
function emptyTally() {
|
|
55
|
+
return { killed: 0, timeout: 0, survived: 0, noCoverage: 0, ignored: 0, other: 0 };
|
|
56
|
+
}
|
|
57
|
+
function tested(tally) {
|
|
58
|
+
return tally.killed + tally.timeout + tally.survived + tally.noCoverage;
|
|
59
|
+
}
|
|
60
|
+
/** Detected ÷ tested, as stryker computes it. `null` when nothing was tested. */
|
|
61
|
+
function scoreOf(tally) {
|
|
62
|
+
const denominator = tested(tally);
|
|
63
|
+
return denominator === 0 ? null : ((tally.killed + tally.timeout) / denominator) * 100;
|
|
64
|
+
}
|
|
65
|
+
/** The same score with `NoCoverage` left out — stryker's second column. */
|
|
66
|
+
function coveredScoreOf(tally) {
|
|
67
|
+
const denominator = tally.killed + tally.timeout + tally.survived;
|
|
68
|
+
return denominator === 0 ? null : ((tally.killed + tally.timeout) / denominator) * 100;
|
|
69
|
+
}
|
|
70
|
+
/** Descending by count, then by name — the same tallies always sort the same. */
|
|
71
|
+
function rankMutators(counts, limit) {
|
|
72
|
+
return [...counts]
|
|
73
|
+
.map(([mutator, count]) => ({ mutator, count }))
|
|
74
|
+
.toSorted((a, b) => b.count - a.count || a.mutator.localeCompare(b.mutator))
|
|
75
|
+
.slice(0, limit);
|
|
76
|
+
}
|
|
77
|
+
function bump(counts, key) {
|
|
78
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
79
|
+
}
|
|
80
|
+
/** The `mutants` array of one `files` entry, with non-object entries dropped. */
|
|
81
|
+
function mutantsOf(entry) {
|
|
82
|
+
if (!isRecord(entry))
|
|
83
|
+
return [];
|
|
84
|
+
const mutants = entry['mutants'];
|
|
85
|
+
return Array.isArray(mutants) ? mutants.filter(isRecord) : [];
|
|
86
|
+
}
|
|
87
|
+
/** `location.start.line`, or `null` when the mutant carries no usable position. */
|
|
88
|
+
function lineOf(mutant) {
|
|
89
|
+
const location = mutant['location'];
|
|
90
|
+
if (!isRecord(location))
|
|
91
|
+
return null;
|
|
92
|
+
const start = location['start'];
|
|
93
|
+
if (!isRecord(start))
|
|
94
|
+
return null;
|
|
95
|
+
return typeof start['line'] === 'number' ? start['line'] : null;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The first line of a replacement, clipped. A trailing `…` covers both cuts —
|
|
99
|
+
* a long first line and any lines after it — so the excerpt never reads as the
|
|
100
|
+
* whole replacement.
|
|
101
|
+
*/
|
|
102
|
+
function excerptReplacement(mutant) {
|
|
103
|
+
const text = asStringOrNull(mutant['replacement']) ?? '';
|
|
104
|
+
const [firstLine = ''] = text.split('\n');
|
|
105
|
+
const clipped = firstLine.slice(0, MAX_REPLACEMENT_CHARS);
|
|
106
|
+
return clipped.length < text.length ? `${clipped}…` : clipped;
|
|
107
|
+
}
|
|
108
|
+
function scanFile(mutants) {
|
|
109
|
+
const tally = emptyTally();
|
|
110
|
+
const mutators = new Map();
|
|
111
|
+
const undetected = [];
|
|
112
|
+
for (const mutant of mutants) {
|
|
113
|
+
const status = asStringOrNull(mutant['status']) ?? '';
|
|
114
|
+
tally[TALLY_KEY[status] ?? 'other'] += 1;
|
|
115
|
+
if (status !== 'Survived' && status !== 'NoCoverage')
|
|
116
|
+
continue;
|
|
117
|
+
bump(mutators, asStringOrNull(mutant['mutatorName']) ?? 'unknown');
|
|
118
|
+
if (undetected.length < SAMPLES_PER_FILE)
|
|
119
|
+
undetected.push(mutant);
|
|
120
|
+
}
|
|
121
|
+
return { tally, mutators, undetected };
|
|
122
|
+
}
|
|
123
|
+
function undetectedOf(file) {
|
|
124
|
+
return file.survived + file.noCoverage;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Worst first: most undetected mutants, then the lower score, then the path.
|
|
128
|
+
* See this module's header for why count leads and score breaks the tie.
|
|
129
|
+
*/
|
|
130
|
+
function byUrgency(a, b) {
|
|
131
|
+
return (undetectedOf(b) - undetectedOf(a) ||
|
|
132
|
+
(a.score ?? 0) - (b.score ?? 0) ||
|
|
133
|
+
a.path.localeCompare(b.path));
|
|
134
|
+
}
|
|
135
|
+
function sampleFrom(path, mutant) {
|
|
136
|
+
return {
|
|
137
|
+
path,
|
|
138
|
+
line: lineOf(mutant),
|
|
139
|
+
mutator: asStringOrNull(mutant['mutatorName']) ?? 'unknown',
|
|
140
|
+
status: asStringOrNull(mutant['status']) ?? 'unknown',
|
|
141
|
+
replacement: excerptReplacement(mutant),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function scanAll(files) {
|
|
145
|
+
const total = emptyTally();
|
|
146
|
+
const mutators = new Map();
|
|
147
|
+
const scanned = [];
|
|
148
|
+
for (const [path, entry] of Object.entries(files)) {
|
|
149
|
+
const { tally, mutators: fileMutators, undetected } = scanFile(mutantsOf(entry));
|
|
150
|
+
for (const key of TALLY_KEYS)
|
|
151
|
+
total[key] += tally[key];
|
|
152
|
+
for (const [mutator, count] of fileMutators)
|
|
153
|
+
mutators.set(mutator, (mutators.get(mutator) ?? 0) + count);
|
|
154
|
+
scanned.push({
|
|
155
|
+
file: {
|
|
156
|
+
path,
|
|
157
|
+
survived: tally.survived,
|
|
158
|
+
noCoverage: tally.noCoverage,
|
|
159
|
+
tested: tested(tally),
|
|
160
|
+
score: scoreOf(tally),
|
|
161
|
+
mutators: rankMutators(fileMutators, TOP_FILE_MUTATORS),
|
|
162
|
+
},
|
|
163
|
+
undetected,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
return { scanned, total, mutators };
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Fold a parsed `mutation.json` into the report model, or `null` when it is not
|
|
170
|
+
* a stryker report (no `files` object) — the ledger reports that rather than
|
|
171
|
+
* this returning an empty extract that would read as "nothing survived".
|
|
172
|
+
*/
|
|
173
|
+
export function extractMutation(value) {
|
|
174
|
+
const files = value['files'];
|
|
175
|
+
if (!isRecord(files))
|
|
176
|
+
return null;
|
|
177
|
+
const { scanned, total, mutators } = scanAll(files);
|
|
178
|
+
const ranked = scanned.toSorted((a, b) => byUrgency(a.file, b.file));
|
|
179
|
+
const shown = ranked.slice(0, TOP_FILES);
|
|
180
|
+
const samples = ranked
|
|
181
|
+
.slice(0, SAMPLE_FILES)
|
|
182
|
+
.flatMap(({ file, undetected }) => undetected.map((mutant) => sampleFrom(file.path, mutant)));
|
|
183
|
+
return {
|
|
184
|
+
...total,
|
|
185
|
+
total: tested(total) + total.ignored + total.other,
|
|
186
|
+
score: scoreOf(total),
|
|
187
|
+
scoreCovered: coveredScoreOf(total),
|
|
188
|
+
totalFiles: ranked.length,
|
|
189
|
+
files: shown.map(({ file }) => file),
|
|
190
|
+
omittedFiles: ranked.length - shown.length,
|
|
191
|
+
mutators: rankMutators(mutators, TOP_MUTATORS),
|
|
192
|
+
samples,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=mutation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mutation.js","sourceRoot":"","sources":["../../src/qa/mutation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assembling the quality report — the four artifacts checkride already writes,
|
|
3
|
+
* each folded to a short list, behind a ledger saying which of them is actually
|
|
4
|
+
* evidence.
|
|
5
|
+
*
|
|
6
|
+
* This reader runs nothing (D2): it opens `.check/` and reports. That makes the
|
|
7
|
+
* ledger the load-bearing part rather than a preamble. Three of the four
|
|
8
|
+
* artifacts come from opt-in slots and this repo's own gate never runs
|
|
9
|
+
* `mutation` at all, so *partial data is the normal case*, and a reader that
|
|
10
|
+
* treated a gap as an edge case would spend its life reporting edge cases. Each
|
|
11
|
+
* artifact is therefore placed in one of four honest states — read, stale, not
|
|
12
|
+
* opted in, or missing — with the command or config entry that would produce it
|
|
13
|
+
* (D14), and the ones that are missing are as much of the answer as the ones
|
|
14
|
+
* that are not.
|
|
15
|
+
*
|
|
16
|
+
* Freshness is the same window triage uses (D11): an artifact belongs to the
|
|
17
|
+
* run that wrote the summary beside it only if its mtime is at or after that
|
|
18
|
+
* run's *start*. `mutation.json` in this repo is routinely days older than
|
|
19
|
+
* everything else, and reporting it as current is the exact wrong answer this
|
|
20
|
+
* module exists to prevent.
|
|
21
|
+
*
|
|
22
|
+
* Because nothing is run, the summary on disk may describe a narrow run — so
|
|
23
|
+
* the report leads with which slots it covered and how old it is (D12), and
|
|
24
|
+
* every finding below that is qualified by it.
|
|
25
|
+
*/
|
|
26
|
+
import type { ArtifactFile, SummaryRead } from '../artifacts/index.js';
|
|
27
|
+
import type { DeadExtract } from './dead.js';
|
|
28
|
+
import type { DupesExtract } from './dupes.js';
|
|
29
|
+
import type { HealthExtract } from './health.js';
|
|
30
|
+
import type { MutationExtract } from './mutation.js';
|
|
31
|
+
/** Why an artifact produced no extract. `null` means it produced one. */
|
|
32
|
+
export type LedgerProblem = 'absent' | 'too-large' | 'unreadable' | 'unrecognized';
|
|
33
|
+
/** One artifact's standing — the honest four-state answer D14 asks for. */
|
|
34
|
+
export type LedgerEntry = {
|
|
35
|
+
slot: string;
|
|
36
|
+
/** File name within `.check/`. */
|
|
37
|
+
artifact: string;
|
|
38
|
+
/** Named in `checkride.config.json`; `null` when the repo has no config. */
|
|
39
|
+
optedIn: boolean | null;
|
|
40
|
+
/** Whether the slot has an entry in the summary on disk. */
|
|
41
|
+
ranThisRun: boolean;
|
|
42
|
+
/** Measured and freshness-judged; `null` when nothing is on disk. */
|
|
43
|
+
file: ArtifactFile | null;
|
|
44
|
+
problem: LedgerProblem | null;
|
|
45
|
+
/** What `unreadable` means in this case. */
|
|
46
|
+
detail: string | null;
|
|
47
|
+
/** The command or config entry that would produce this artifact. */
|
|
48
|
+
remedy: string;
|
|
49
|
+
};
|
|
50
|
+
export type QaReport = {
|
|
51
|
+
cwd: string;
|
|
52
|
+
summary: SummaryRead;
|
|
53
|
+
windowStart: number | null;
|
|
54
|
+
/** Age of the run that wrote the summary, measured from its end. */
|
|
55
|
+
summaryAgeMs: number | null;
|
|
56
|
+
/** Slots the summary says actually ran — the covered list D12 requires. */
|
|
57
|
+
covered: string[];
|
|
58
|
+
skipped: string[];
|
|
59
|
+
configured: string[] | null;
|
|
60
|
+
ledger: LedgerEntry[];
|
|
61
|
+
mutation: MutationExtract | null;
|
|
62
|
+
health: HealthExtract | null;
|
|
63
|
+
dead: DeadExtract | null;
|
|
64
|
+
dupes: DupesExtract | null;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Read `.check/`'s four quality artifacts and fold each to a short list.
|
|
68
|
+
* Never throws and never runs anything: every failure is a ledger state.
|
|
69
|
+
*/
|
|
70
|
+
export declare function qaExtract(cwd: string, now?: () => number): Promise<QaReport>;
|
|
71
|
+
//# sourceMappingURL=qa.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qa.d.ts","sourceRoot":"","sources":["../../src/qa/qa.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEvE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAE7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAKrD,yEAAyE;AACzE,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,WAAW,GAAG,YAAY,GAAG,cAAc,CAAC;AAEnF,2EAA2E;AAC3E,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,4EAA4E;IAC5E,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IACxB,4DAA4D;IAC5D,UAAU,EAAE,OAAO,CAAC;IACpB,qEAAqE;IACrE,IAAI,EAAE,YAAY,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,aAAa,GAAG,IAAI,CAAC;IAC9B,4CAA4C;IAC5C,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,WAAW,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,oEAAoE;IACpE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,2EAA2E;IAC3E,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC5B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;IACjC,MAAM,EAAE,aAAa,GAAG,IAAI,CAAC;IAC7B,IAAI,EAAE,WAAW,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;CAC5B,CAAC;AAkGF;;;GAGG;AACH,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,MAAiB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAoD5F"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assembling the quality report — the four artifacts checkride already writes,
|
|
3
|
+
* each folded to a short list, behind a ledger saying which of them is actually
|
|
4
|
+
* evidence.
|
|
5
|
+
*
|
|
6
|
+
* This reader runs nothing (D2): it opens `.check/` and reports. That makes the
|
|
7
|
+
* ledger the load-bearing part rather than a preamble. Three of the four
|
|
8
|
+
* artifacts come from opt-in slots and this repo's own gate never runs
|
|
9
|
+
* `mutation` at all, so *partial data is the normal case*, and a reader that
|
|
10
|
+
* treated a gap as an edge case would spend its life reporting edge cases. Each
|
|
11
|
+
* artifact is therefore placed in one of four honest states — read, stale, not
|
|
12
|
+
* opted in, or missing — with the command or config entry that would produce it
|
|
13
|
+
* (D14), and the ones that are missing are as much of the answer as the ones
|
|
14
|
+
* that are not.
|
|
15
|
+
*
|
|
16
|
+
* Freshness is the same window triage uses (D11): an artifact belongs to the
|
|
17
|
+
* run that wrote the summary beside it only if its mtime is at or after that
|
|
18
|
+
* run's *start*. `mutation.json` in this repo is routinely days older than
|
|
19
|
+
* everything else, and reporting it as current is the exact wrong answer this
|
|
20
|
+
* module exists to prevent.
|
|
21
|
+
*
|
|
22
|
+
* Because nothing is run, the summary on disk may describe a narrow run — so
|
|
23
|
+
* the report leads with which slots it covered and how old it is (D12), and
|
|
24
|
+
* every finding below that is qualified by it.
|
|
25
|
+
*/
|
|
26
|
+
import { join } from 'node:path';
|
|
27
|
+
import { CHECK_DIR, configuredSlots, readSummary, runWindowStart } from '../artifacts/index.js';
|
|
28
|
+
import { extractDead } from './dead.js';
|
|
29
|
+
import { extractDupes } from './dupes.js';
|
|
30
|
+
import { extractHealth } from './health.js';
|
|
31
|
+
import { extractMutation } from './mutation.js';
|
|
32
|
+
import { readJsonArtifact } from './read.js';
|
|
33
|
+
/**
|
|
34
|
+
* The four artifacts, in the order the skill reasons about them: surviving
|
|
35
|
+
* mutants, then dead code, then duplication, then the complexity score.
|
|
36
|
+
*
|
|
37
|
+
* Each remedy names something real. `dead` is in the default catalogue and the
|
|
38
|
+
* other three are opt-in slots, which is why only they mention `--include`; and
|
|
39
|
+
* `checkride init --add` scaffolds `fallow.toml` but has no stryker template,
|
|
40
|
+
* so `mutation` says its config is hand-written rather than implying otherwise.
|
|
41
|
+
*/
|
|
42
|
+
const ARTIFACTS = [
|
|
43
|
+
{
|
|
44
|
+
slot: 'mutation',
|
|
45
|
+
artifact: 'mutation.json',
|
|
46
|
+
remedy: 'Opt-in slot. Run `checkride --include mutation` (or add `"mutation": { "use": "stryker" }` to ' +
|
|
47
|
+
'`checkride.config.json`). It detects on a stryker config — `stryker.config.mjs` — which `checkride init` ' +
|
|
48
|
+
'does not scaffold, so that file is written by hand.',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
slot: 'dead',
|
|
52
|
+
artifact: 'dead.json',
|
|
53
|
+
remedy: 'Default-catalogue slot, so `checkride` already runs it — unless no dead-code tool is detected. Add ' +
|
|
54
|
+
'`fallow.toml` (`checkride init --add dead` scaffolds one) or a knip config, then `checkride --only dead`.',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
slot: 'dupes',
|
|
58
|
+
artifact: 'dupes.json',
|
|
59
|
+
remedy: 'Opt-in slot. Run `checkride --include dupes` (or add `"dupes": { "use": "fallow" }` to ' +
|
|
60
|
+
'`checkride.config.json`). Needs `fallow.toml` — `checkride init --add dupes` scaffolds one.',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
slot: 'health',
|
|
64
|
+
artifact: 'health.json',
|
|
65
|
+
remedy: 'Opt-in slot. Run `checkride --include health` (or add `"health": { "use": "fallow" }` to ' +
|
|
66
|
+
'`checkride.config.json`). Needs `fallow.toml` — `checkride init --add health` scaffolds one.',
|
|
67
|
+
},
|
|
68
|
+
];
|
|
69
|
+
/** The read's failure state, or `null` when it produced a value to extract. */
|
|
70
|
+
function problemOf(read) {
|
|
71
|
+
if (read.state === 'missing')
|
|
72
|
+
return 'absent';
|
|
73
|
+
return read.state === 'ok' ? null : read.state;
|
|
74
|
+
}
|
|
75
|
+
function detailOf(read) {
|
|
76
|
+
return read.state === 'unreadable' ? read.detail : null;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* One ledger row. `recognized` is false when the bytes parsed but the fold did
|
|
80
|
+
* not find the report it expected — a distinct state from unreadable, because
|
|
81
|
+
* the file is fine and it is the *kind* that is wrong.
|
|
82
|
+
*/
|
|
83
|
+
function entryFor(spec, read, context, recognized) {
|
|
84
|
+
const problem = problemOf(read);
|
|
85
|
+
return {
|
|
86
|
+
slot: spec.slot,
|
|
87
|
+
artifact: spec.artifact,
|
|
88
|
+
optedIn: context.configured === null ? null : context.configured.includes(spec.slot),
|
|
89
|
+
ranThisRun: context.ran.has(spec.slot),
|
|
90
|
+
file: read.file,
|
|
91
|
+
problem: problem === null && !recognized ? 'unrecognized' : problem,
|
|
92
|
+
detail: detailOf(read),
|
|
93
|
+
remedy: spec.remedy,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/** The slots the summary on disk says actually ran, and the ones it skipped. */
|
|
97
|
+
function coverage(summary) {
|
|
98
|
+
if (summary.state !== 'ok')
|
|
99
|
+
return { covered: [], skipped: [] };
|
|
100
|
+
const checks = summary.summary.checks;
|
|
101
|
+
return {
|
|
102
|
+
covered: checks.filter((c) => c.skipped !== true).map((c) => c.name),
|
|
103
|
+
skipped: checks.filter((c) => c.skipped === true).map((c) => c.name),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/** How long ago the run that wrote the summary finished. */
|
|
107
|
+
function summaryAge(summary, now) {
|
|
108
|
+
if (summary.state !== 'ok')
|
|
109
|
+
return null;
|
|
110
|
+
const ended = Date.parse(summary.summary.timestamp);
|
|
111
|
+
return Number.isNaN(ended) ? null : now - ended;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Read `.check/`'s four quality artifacts and fold each to a short list.
|
|
115
|
+
* Never throws and never runs anything: every failure is a ledger state.
|
|
116
|
+
*/
|
|
117
|
+
export async function qaExtract(cwd, now = Date.now) {
|
|
118
|
+
const checkDir = join(cwd, CHECK_DIR);
|
|
119
|
+
const summary = await readSummary(cwd);
|
|
120
|
+
const windowStart = summary.state === 'ok' ? runWindowStart(summary.summary) : null;
|
|
121
|
+
const { covered, skipped } = coverage(summary);
|
|
122
|
+
const context = { configured: configuredSlots(cwd), ran: new Set(covered) };
|
|
123
|
+
const reads = new Map(await Promise.all(ARTIFACTS.map(async (spec) => [spec.slot, await readJsonArtifact(checkDir, spec.artifact, windowStart)])));
|
|
124
|
+
const MISSING = { state: 'missing', file: null };
|
|
125
|
+
const readOf = (slot) => reads.get(slot) ?? MISSING;
|
|
126
|
+
/** Fold a slot's parsed bytes, or `null` when there are none to fold. */
|
|
127
|
+
const fold = (slot, extract) => {
|
|
128
|
+
const read = readOf(slot);
|
|
129
|
+
return read.state === 'ok' ? extract(read.value) : null;
|
|
130
|
+
};
|
|
131
|
+
// Named one by one rather than dispatched: each artifact has its own fold
|
|
132
|
+
// with its own return type, and there are exactly four of them.
|
|
133
|
+
const mutation = fold('mutation', extractMutation);
|
|
134
|
+
const dead = fold('dead', extractDead);
|
|
135
|
+
const dupes = fold('dupes', extractDupes);
|
|
136
|
+
const health = fold('health', extractHealth);
|
|
137
|
+
const recognized = {
|
|
138
|
+
mutation: mutation !== null,
|
|
139
|
+
dead: dead !== null,
|
|
140
|
+
dupes: dupes !== null,
|
|
141
|
+
health: health !== null,
|
|
142
|
+
};
|
|
143
|
+
return {
|
|
144
|
+
cwd,
|
|
145
|
+
summary,
|
|
146
|
+
windowStart,
|
|
147
|
+
summaryAgeMs: summaryAge(summary, now()),
|
|
148
|
+
covered,
|
|
149
|
+
skipped,
|
|
150
|
+
configured: context.configured,
|
|
151
|
+
ledger: ARTIFACTS.map((spec) => entryFor(spec, readOf(spec.slot), context, recognized[spec.slot] ?? false)),
|
|
152
|
+
mutation,
|
|
153
|
+
health,
|
|
154
|
+
dead,
|
|
155
|
+
dupes,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=qa.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qa.js","sourceRoot":"","sources":["../../src/qa/qa.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opening a quality artifact — the one place this module reads bytes.
|
|
3
|
+
*
|
|
4
|
+
* Triage never opens a `.check/` file; it reports sizes and points. The quality
|
|
5
|
+
* extractor has to open four of them, which makes *bounding* the read this
|
|
6
|
+
* module's whole discipline: `mutation.json` is 2.3 MB in this repo and its
|
|
7
|
+
* parsed form is an order of magnitude more. Nothing here escapes into the
|
|
8
|
+
* report — each extractor folds a parsed artifact into counts and a capped
|
|
9
|
+
* short list, and the parsed object is dropped on the way out.
|
|
10
|
+
*
|
|
11
|
+
* A file past {@link MAX_PARSE_BYTES} is *reported*, never parsed: a reader
|
|
12
|
+
* that exhausts memory on a pathological artifact is worse than one that says
|
|
13
|
+
* how big the file got and stops.
|
|
14
|
+
*
|
|
15
|
+
* The `../qa` barrel is this module's only public surface.
|
|
16
|
+
*/
|
|
17
|
+
import type { ArtifactFile } from '../artifacts/index.js';
|
|
18
|
+
/** The outcome of opening one artifact. Every state is reportable. */
|
|
19
|
+
export type ArtifactRead = {
|
|
20
|
+
state: 'ok';
|
|
21
|
+
file: ArtifactFile;
|
|
22
|
+
value: Record<string, unknown>;
|
|
23
|
+
} | {
|
|
24
|
+
state: 'too-large';
|
|
25
|
+
file: ArtifactFile;
|
|
26
|
+
} | {
|
|
27
|
+
state: 'unreadable';
|
|
28
|
+
file: ArtifactFile;
|
|
29
|
+
detail: string;
|
|
30
|
+
} | {
|
|
31
|
+
state: 'missing';
|
|
32
|
+
file: null;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Measure `<checkDir>/<name>`, then parse it when it is small enough to be
|
|
36
|
+
* worth parsing. Never throws: an unreadable artifact is a finding about the
|
|
37
|
+
* artifact, not a crash.
|
|
38
|
+
*/
|
|
39
|
+
export declare function readJsonArtifact(checkDir: string, name: string, windowStart: number | null, maxBytes?: number): Promise<ArtifactRead>;
|
|
40
|
+
//# sourceMappingURL=read.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../src/qa/read.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAW1D,sEAAsE;AACtE,MAAM,MAAM,YAAY,GACpB;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GACnE;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,YAAY,CAAA;CAAE,GAC1C;IAAE,KAAK,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC3D;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,IAAI,CAAA;CAAE,CAAC;AAErC;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,QAAQ,GAAE,MAAwB,GACjC,OAAO,CAAC,YAAY,CAAC,CAWvB"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opening a quality artifact — the one place this module reads bytes.
|
|
3
|
+
*
|
|
4
|
+
* Triage never opens a `.check/` file; it reports sizes and points. The quality
|
|
5
|
+
* extractor has to open four of them, which makes *bounding* the read this
|
|
6
|
+
* module's whole discipline: `mutation.json` is 2.3 MB in this repo and its
|
|
7
|
+
* parsed form is an order of magnitude more. Nothing here escapes into the
|
|
8
|
+
* report — each extractor folds a parsed artifact into counts and a capped
|
|
9
|
+
* short list, and the parsed object is dropped on the way out.
|
|
10
|
+
*
|
|
11
|
+
* A file past {@link MAX_PARSE_BYTES} is *reported*, never parsed: a reader
|
|
12
|
+
* that exhausts memory on a pathological artifact is worse than one that says
|
|
13
|
+
* how big the file got and stops.
|
|
14
|
+
*
|
|
15
|
+
* The `../qa` barrel is this module's only public surface.
|
|
16
|
+
*/
|
|
17
|
+
import { readFile } from 'node:fs/promises';
|
|
18
|
+
import { isRecord, parseJson, statArtifact } from '../artifacts/index.js';
|
|
19
|
+
/**
|
|
20
|
+
* The largest artifact this reader will parse. Set well above the 2.3 MB
|
|
21
|
+
* `mutation.json` that motivated the bound — the ceiling exists to stop a
|
|
22
|
+
* runaway file, not to second-guess a normal one — and low enough that parsing
|
|
23
|
+
* cannot take the process down.
|
|
24
|
+
*/
|
|
25
|
+
const MAX_PARSE_BYTES = 64 * 1024 * 1024;
|
|
26
|
+
/**
|
|
27
|
+
* Measure `<checkDir>/<name>`, then parse it when it is small enough to be
|
|
28
|
+
* worth parsing. Never throws: an unreadable artifact is a finding about the
|
|
29
|
+
* artifact, not a crash.
|
|
30
|
+
*/
|
|
31
|
+
export async function readJsonArtifact(checkDir, name, windowStart, maxBytes = MAX_PARSE_BYTES) {
|
|
32
|
+
const file = await statArtifact(checkDir, name, windowStart);
|
|
33
|
+
if (file === null)
|
|
34
|
+
return { state: 'missing', file: null };
|
|
35
|
+
if (file.bytes > maxBytes)
|
|
36
|
+
return { state: 'too-large', file };
|
|
37
|
+
try {
|
|
38
|
+
const value = parseJson(await readFile(file.path, 'utf8'));
|
|
39
|
+
if (!isRecord(value))
|
|
40
|
+
return { state: 'unreadable', file, detail: 'not a JSON object' };
|
|
41
|
+
return { state: 'ok', file, value };
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return { state: 'unreadable', file, detail: 'could not be read from disk' };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=read.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read.js","sourceRoot":"","sources":["../../src/qa/read.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG5C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1E;;;;;GAKG;AACH,MAAM,eAAe,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AASzC;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,QAAgB,EAChB,IAAY,EACZ,WAA0B,EAC1B,WAAmB,eAAe;IAElC,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,QAAQ,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;IAC7D,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAC3D,IAAI,IAAI,CAAC,KAAK,GAAG,QAAQ;QAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;IAC/D,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;QAC3D,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,mBAAmB,EAAE,CAAC;QACxF,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,6BAA6B,EAAE,CAAC;IAC9E,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rendering a {@link QaReport} as Markdown, under the same 8 KB ceiling
|
|
3
|
+
* `--digest` uses.
|
|
4
|
+
*
|
|
5
|
+
* The ledger is the head and never drops: which artifacts are evidence, which
|
|
6
|
+
* are days old, and which were never produced is the answer, not the preamble
|
|
7
|
+
* (D14). Each ledger row carries its own headline number, so even if the byte
|
|
8
|
+
* ceiling trims a detail section the score behind it survives.
|
|
9
|
+
*
|
|
10
|
+
* Detail sections follow in the order the skill reasons about them — surviving
|
|
11
|
+
* mutants, then dead code, then duplication, then complexity — and each is
|
|
12
|
+
* internally capped by its extractor, so the ceiling is a backstop rather than
|
|
13
|
+
* the usual path. When it does bind, whole sections drop from the end and are
|
|
14
|
+
* named; nothing is silently truncated mid-thought.
|
|
15
|
+
*
|
|
16
|
+
* No artifact's *contents* appear here beyond located samples and file names.
|
|
17
|
+
* The one place source text surfaces at all is a clipped mutant replacement,
|
|
18
|
+
* which is the only way a line number becomes actionable without opening a
|
|
19
|
+
* 2.3 MB file.
|
|
20
|
+
*/
|
|
21
|
+
import type { QaReport } from './qa.js';
|
|
22
|
+
/** Matches `--digest`'s ceiling: a report that reads in one gulp. */
|
|
23
|
+
export declare const QA_MAX_BYTES = 8000;
|
|
24
|
+
/** Render the whole report. Deterministic: same report in, same bytes out. */
|
|
25
|
+
export declare function renderQa(report: QaReport, maxBytes?: number): string;
|
|
26
|
+
//# sourceMappingURL=render.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/qa/render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAQH,OAAO,KAAK,EAAe,QAAQ,EAAE,MAAM,SAAS,CAAC;AAErD,qEAAqE;AACrE,eAAO,MAAM,YAAY,OAAO,CAAC;AA8TjC,8EAA8E;AAC9E,wBAAgB,QAAQ,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAE,MAAqB,GAAG,MAAM,CAQlF"}
|