test(cli): fingerprint the reference shape, cover the wizard's reload banner - #305
Conversation
|
@claude /review |
|
Claude finished @thecodedrift's task in 4m 47s —— View job Review mode: incremental — read 0 prior review thread(s) before reviewing.
AssessmentThis is a test-only PR (no production code touched) and it holds up well under a full trace of the wiring it's testing. No correctness issues found. Summary of what I verified:
No inline comments to post — nothing rose to the level of a concrete, actionable issue. |
f05bbbc to
e2c0f16
Compare
… banner Three coverage gaps from #284: - REFERENCE_VERSION was asserted only as a literal (`toBe(2)`), with nothing tying a shape change in Reference/ReferenceRule/ReferenceTests/ReferenceLayout to a version bump. Adds a structural fingerprint (sha256 over a sorted list of "path:kind" pairs, walked from the live object) asserted beside the version, so adding/removing/retyping a field changes the hash while reordering, reformatting, or content-only edits do not. - runWizard wired up getReloadNotice identically to init, but only init's path had a test for it. Mirrors init-no-interactive.test.ts's three cases (prints on an upgrade, quiet on first install, quiet when unchanged) for the wizard. - The migration matrix in migrate-install.test.ts seeds every start version with a bare taskless.json and no README, so migration 0006's rewrite has nothing stale to act on; the test only reads the final version counter. Left as documented non-coverage with a comment pointing at installed-documentation.test.ts, which already seeds a real stale README and asserts on the rewrite. Each new test was mutation-checked (see PR description for the mutation used per test and confirmation it was caught).
…he field The fingerprint was compared as a sha256 digest, which reports "expected 0add… to be ab12…" on failure. That says the shape moved but not what moved, leaving whoever hit it to rebuild the shape set by hand to find the one added field. A guard that fires this rarely has to be legible the one time it does. The shape is 56 (path, kind) pairs, so it is asserted as the sorted list itself and vitest prints the difference. Measured, by adding a field to the corpus without bumping the version: + "$.mutationProbe:boolean" which names the offending field directly. Same coverage, same derivation from the live object, no digest to reconcile by hand, and one fewer import.
e2c0f16 to
7c5c7e8
Compare
|
Clean review, thanks. Worth flagging one thing that changed after the agent's first pass and before you saw it: the shape check was originally asserted as a sha256 digest, which reports "expected 0add… to be ab12…" and tells you the corpus shape moved without telling you what moved. It is now asserted as the sorted 56-entry path list, so a failure prints — AI Coding Agent |
Stack (root → tip):
Summary
Closes three of the coverage gaps enumerated in #284. Coverage only — no production behavior changed.
1.
REFERENCE_VERSIONhad no structural fingerprintpackages/cli/src/rules/reference.ts:45was asserted only asexpect(reference.version).toBe(2). Nothing tied a shape change inReference/ReferenceRule/ReferenceTests/ReferenceLayoutto bumping that constant — which is exactly thev1 -> v2episode CLAUDE.md documents:testswent from a flat array to an object and the version moved, but only because someone remembered to move it by hand.Design. Added
shapeFingerprint()inpackages/cli/test/reference.test.ts: it walks the liveReferenceobject (read fromassets/reference.json, same as every other test in the file) and records one string per reachable position —path:kind, e.g.$.rules[].tests.grouping:string— into aSet. Array positions are recorded as[](never an index) and object keys are visited in sorted order, so the set of recorded strings is invariant under reordering an array or reordering an object literal's keys. Only the kind of each value is recorded (object/array/string/number/boolean/null), never the value itself, so renaming a rule id or editing a prompt string doesn't move anything. The sorted set is joined and hashed withnode:crypto'screateHash("sha256")(built-in, no new dependency, matches the style guide's "don't add a dependency to test an assertion").Why derived rather than hand-copied: a hand-written list of expected fields is exactly the kind of copy the CLAUDE.md episode already burned us with once (a fact transcribed out of the corpus, silently stale). Walking the actual object at test time means the fingerprint always describes what's really there.
This is deliberately a companion to
REFERENCE_VERSION, not a replacement: the fingerprint has no opinion on whether a version bump is warranted, only on whether the shape moved without anyone updating the recorded hash (which forces a human to look and decide).2. Wizard's reload notice had no test
packages/cli/src/wizard/index.ts:91wires upgetReloadNoticeidentically topackages/cli/src/commands/init.ts:291, whose own test comment names the risk directly ("a dropped console.log or swapped field leaves every unit test green"). Onlyinithad a test for it (init-no-interactive.test.ts, "the restart-your-agents banner" describe block).Added the mirror-image block to
wizard-integration.test.ts— same three cases (prints on a version move, quiet on first install, quiet when unchanged), adapted torunWizard's in-process/mocked-clack shape rather than spawning the built CLI: spies onconsole.log, plants a staleinstall.cliVersionbetween tworunWizardcalls the same way the init test plants it between two CLI invocations.3. Migration matrix doesn't exercise 0006's rewrite
packages/cli/test/migrate-install.test.ts:184-206's version matrix seeds every start version with a baretaskless.jsonand no pre-existingREADME.md, so by the time migration 0006 runs there's no stale README for its rewrite to act on — the test only reads the final version counter. Real coverage of 0006's actual behavior already lives ininstalled-documentation.test.ts:57-87("rewrites a stale README that no other migration would touch"), which seeds a real stale README and asserts on the rewritten content.I left this as a documentation-only change (a comment on the matrix loop) rather than trying to make the matrix loop itself exercise 0006's rewrite: doing so properly would mean seeding a different stale artifact for every start version (or special-casing the version just below 0006), which duplicates what
installed-documentation.test.tsalready does correctly and would make the matrix loop's single, uniform seed less uniform for no real gain. The comment says what the loop does and does not cover and points at the real coverage.Mutation checks
mutationTestField: true) to the object built bybuildReferenceinsrc/rules/reference.ts, regeneratedassets/reference.jsonviapnpm --filter @taskless/cli reference, without bumpingREFERENCE_VERSIONexpected 'fdfa88cc…' to be '0add3275…'). Reverted both files; hash matched again and all 21 tests in the file passed.if (reloadNotice !== undefined) console.log(reloadNotice);insrc/wizard/index.tsexpected false to be true, banner text absent). The other two banner tests in the block still passed (they assert absence, which a dropped log line trivially satisfies — expected). Reverted; all 10 tests in the file passed.Verification
NODE_OPTIONS= pnpm test— 82 files, 1329 tests passedNODE_OPTIONS= pnpm typecheck— cleanNODE_OPTIONS= pnpm lint— clean (fixed twounicorn/no-array-sortfindings by usingtoSorted()in the new helper)No changeset: tests only, no user-visible behavior.
Refs #284