From dd341879ddf067bea21aad0842e1ce64448a0a66 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 24 Aug 2026 10:03:04 +0000 Subject: [PATCH] docs(qa): refresh cli.doctor-deprecation-scan against head (#11420) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #10680 removed the dead `objectstack codemod v2-to-v3` prescription this item's expected-fail described, and #10679 moved every doctor.ts anchor it quoted. The clause inverts — but not into "the hint now names a registered command": the route taken was prescribe-nothing, so revision 2's assertion was the wrong SHAPE for head, where the hint deliberately names none. Six texts changed beyond the named clause, all re-derived at this base: the title, fixtures.knownGaps (revision 2's "no test anywhere references --scan-deprecations" is false — #10680 added a pin), steps 3 and 6, three new negatives, and every line anchor. Of the eleven numbers revision 2 quoted, only walkDir's node_modules prune at :1130 still held. Verified accurate at head and left alone: backward-compatibility.mdx:126-134, packages/cli/README.md §os doctor, the blank template, #9591, sibling cli.migrate-meta-codemod, the DEPRECATED_PATTERNS count of 8, both personas, both traps, and the four original negatives. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_015ahemw8RcTgqtxrj15PEZx --- docs/qa/platform-checklist/areas/cli.json | 42 +++++++++++++---------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/docs/qa/platform-checklist/areas/cli.json b/docs/qa/platform-checklist/areas/cli.json index b09ac288d3..03b4b37048 100644 --- a/docs/qa/platform-checklist/areas/cli.json +++ b/docs/qa/platform-checklist/areas/cli.json @@ -962,10 +962,10 @@ }, { "id": "cli.doctor-deprecation-scan", - "title": "os doctor --scan-deprecations: every retired pattern is found with file:line attribution, decoys are skipped, the run warns without gating — and the remediation hint prescribes a command that does not exist (expected-fail)", + "title": "os doctor --scan-deprecations: every retired pattern is found with file:line attribution, decoys are skipped, the run warns without gating — and the remediation hint prescribes NO command, routing the operator to the per-finding → replacement instead", "since": "v16", "status": "active", - "revision": 2, + "revision": 3, "priority": "P2", "surface": "cli", "personas": ["operator (local shell)", "upgrading author (pre-major sweep)"], @@ -976,64 +976,69 @@ "decoy files staged per run: a src/*.test.ts twin, a src/node_modules/dep/index.ts, and a src/*.json metadata file carrying a retired key — all three must stay unreported" ], "knownGaps": [ - "no test anywhere in the repo references --scan-deprecations — this item is the surface's first coverage of any kind, so there is no automated entry and no pin to lean on" + "revision 2's claim that 'no test anywhere in the repo references --scan-deprecations' is FALSE at this base: #10680 added packages/cli/src/commands/doctor-deprecation-hint-commands.test.ts, which drives Doctor.run(['--scan-deprecations']) against a temp cwd and pins the closing hint in BOTH --verbose branches, file:line attribution, the advisory exit code, and the clean-run success line", + "what that pin does NOT reach — and what this item therefore still owns: it stages no decoys, it mkdirs src/ on every case so the absent-src probe is never taken, it seeds two lines (three hits) rather than one per DEPRECATED_PATTERNS entry, and it calls the command CLASS in-process under vitest rather than the shipped binary — so the stale-dist trap is untested by it and a real scaffold run remains the only evidence `os` reports this way" ] }, "steps": [ - "scaffold and seed src/legacy.ts with one line per DEPRECATED_PATTERNS entry — count the table yourself at doctor.ts:1186-1234 (8 entries at authoring time): EnhancedObjectKernel, max_length, default_value, min_length, the retired lookup-scoping pair (one entry, BOTH spellings: reference_filters AND referenceFilters — seed both lines), unique_name, `import ... from '@objectstack/core/enhanced'`, `import ... from '@objectstack/spec/dist/x'`", + "scaffold and seed src/legacy.ts with one line per DEPRECATED_PATTERNS entry — count the table yourself at doctor.ts:1262-1310 (recounted at this revision's base: still 8 entries): EnhancedObjectKernel, max_length, default_value, min_length, the retired lookup-scoping pair (one entry, BOTH spellings: reference_filters AND referenceFilters — seed both lines), unique_name, `import ... from '@objectstack/core/enhanced'`, `import ... from '@objectstack/spec/dist/x'`", "stage the decoys: src/legacy.test.ts with the same content, src/node_modules/dep/index.ts with the same content, and src/meta.json carrying \"referenceFilters\"", - "run `os doctor --scan-deprecations; echo $?` and capture the per-hit rows and the closing hint line verbatim; re-run with `--scan-deprecations --verbose` and capture the replacement prescriptions", + "run `os doctor --scan-deprecations; echo $?` and capture the per-hit rows and the closing hint line verbatim; re-run with `--scan-deprecations --verbose` and capture the replacement prescriptions AND that run's closing hint line too — the hint is a two-branch template (doctor.ts:2283-2288) and the two runs do not print the same sentence", "clean pass: delete the seeds (keep the decoys) and re-run — expect '✓ Deprecation scan No deprecated patterns found' and exit 0", "absent-src probe: run `os doctor --scan-deprecations` from a scratch dir with NO src/ at all and capture that the SAME success line prints", - "record `os help codemod; echo $?` (or `os codemod --help`) from the same shell — the command the hint prescribes" + "record `os codemod --help; echo $?` from the same shell — NOT because the hint prescribes it (it prescribes nothing), but to corroborate the hint's factual claim: no `codemod` command may be registered, oclif exiting nonzero with 'command codemod not found'. A registered codemod would make 'No automated codemod ships with the CLI' the new false statement" ], "acceptance": [ { - "clause": "every seeded pattern is reported with file:line attribution matching the seeded lines — 8 registry entries, with the retired-lookup entry attributing BOTH its spellings (reference_filters and referenceFilters land on one regex, doctor.ts:1215)", + "clause": "every seeded pattern is reported with file:line attribution matching the seeded lines — 8 registry entries, with the retired-lookup entry attributing BOTH its spellings (reference_filters and referenceFilters land on one regex, doctor.ts:1291)", "oracle": "log", - "verify": "each warning row reads ':' (doctor.ts:2143) and the file:line pairs match where the seeds were written; no seeded line is missing and no unseeded line is reported", + "verify": "each warning row reads ':' (doctor.ts:2252) and the file:line pairs match where the seeds were written; no seeded line is missing and no unseeded line is reported", "evidence": "the seeded file with line numbers + the transcript rows" }, { "clause": "--verbose prints each hit's replacement prescription ('Use maxLength (camelCase)', 'Use lookupFilters … removed in #2377', …); the bare run withholds them", "oracle": "log", - "verify": "the dim '→ ' lines appear under --verbose only (doctor.ts:2144-2146)", + "verify": "the dim '→ ' lines appear under --verbose only (doctor.ts:2253-2255 — the `if (flags.verbose)` guard and the dim line it wraps)", "evidence": "the two transcripts diffed" }, { "clause": "the scan WARNS and never gates: a run with hits prints the warning rows and the count line yet exits 0 (hits feed hasWarnings only, never hasErrors), and the clean run prints the success line and exits 0 — both sides captured", "oracle": "log", - "verify": "echo $? is 0 for both the seeded and the clean run; the seeded run's summary is the '⚠️ Environment is functional' branch (doctor.ts:2140-2152 sets hasWarnings; exit 1 is reserved for error rows at :2158)", + "verify": "echo $? is 0 for both the seeded and the clean run; the seeded run's summary is the '⚠️ Environment is functional' branch (doctor.ts:2250 sets hasWarnings inside the hits branch :2249-2288; exit 1 is reserved for error rows at :2302)", "evidence": "both exit codes + summary lines" }, { - "clause": "decoys stay silent, each for its own documented reason: *.test.ts filtered (doctor.ts:1240), node_modules pruned by the walk (:1130), and the .json file never visited because the walk is .ts-only (:1240) — the .json silence is recorded as a DOCUMENTED BOUNDARY of the scan (retired keys in JSON metadata are out of its reach), never as evidence the metadata is clean", + "clause": "decoys stay silent, each for its own documented reason: *.test.ts filtered (doctor.ts:1316), node_modules pruned by the walk (:1130 — the only anchor revision 2 quoted that did not move), and the .json file never visited because the walk is .ts-only (:1316) — the .json silence is recorded as a DOCUMENTED BOUNDARY of the scan (retired keys in JSON metadata are out of its reach), never as evidence the metadata is clean", "oracle": "log", "verify": "no transcript row names legacy.test.ts, node_modules, or meta.json; the run record states the .ts-only boundary explicitly", "evidence": "the transcript + the staged decoy listing" }, { - "clause": "absent-src honesty: with no src/ directory the scanner returns [] (doctor.ts:1238) and doctor prints the SAME success line a genuinely clean tree gets — so the success line alone is NOT evidence of cleanliness. The run must pair the line with proof src/ existed and was walked; a run record citing the line without that proof is the absence-inference false positive this clause exists to block", + "clause": "absent-src honesty: with no src/ directory the scanner returns [] (doctor.ts:1314) and doctor prints the SAME success line a genuinely clean tree gets — so the success line alone is NOT evidence of cleanliness. The run must pair the line with proof src/ existed and was walked; a run record citing the line without that proof is the absence-inference false positive this clause exists to block", "oracle": "log", "verify": "the no-src run prints '✓ Deprecation scan No deprecated patterns found'; the run record annotates it as scanned-nothing, with `ls src` refusal captured alongside", "evidence": "the no-src transcript + the ls refusal" }, { - "clause": "EXPECTED-FAIL (shippable defect — record actual behavior): the remediation hint printed after hits (doctor.ts:2149) prescribes `objectstack codemod v2-to-v3`, but no codemod command is registered anywhere in packages/cli/src/commands/ — the prescription is a dead end, and content/docs/protocol/backward-compatibility.mdx:134 admits the command is 'not yet available'. The remediation path that DOES exist is `os migrate meta` (cli.migrate-meta-codemod) — but it is not a codemod: it replays the migration chain and PRINTS the mechanical edits for the author to apply by hand. It declares no --write/--fix/in-place flag (exactly from/to/step/out/stored/database-url/apply/yes/force/type/json, commands/migrate/meta.ts:195-239) and writes no source file — both writeFileSync calls are guarded by `if (flags.out)` (:347 and :394-396) and emit a JSON snapshot, and the header at :155-157 declines the AST rewrite as 'unsafe and lossy'. The in-place codemod is commissioned as #9591 (v18, pm:on-hold) and has not been built, so a hint rewritten to name `os migrate meta` must promise a LIST of edits, not an auto-fix. The clause: the hint must name a command os actually registers; today it does not", + "clause": "the remediation hint prescribes NO command at all — it states that no automated codemod ships and routes the operator to the per-finding `→ replacement` instead. #10680 REMOVED the dead `objectstack codemod v2-to-v3` prescription rather than repointing it, and the print site's own comment (doctor.ts:2258-2282) records why a repoint would have been the same defect respelled: `os migrate meta` declines the src/** TypeScript rewrite by design ('unsafe and lossy'), three of the eight patterns (EnhancedObjectKernel and the two deep-import paths) are not metadata at all, and `os lint --fix` is print-only. This clause is the INVERSION of revision 2's expected-fail, and NOT into 'the hint now names a registered command' — that assertion was the wrong SHAPE for head, where the hint deliberately names none", "oracle": "log", - "verify": "the hint line is captured verbatim from the seeded run, and `os codemod --help` errors 'command … not found' (nonzero) from the same binary — the pair is the failure evidence; the clause flips to pass when the hint names a registered command", - "evidence": "the hint line + the command-not-found capture" + "verify": "the closing hint is the blue `ℹ` info line (printInfo, not a `⚠` row) and reads 'Found deprecated pattern(s). No automated codemod ships with the CLI — apply each finding’s replacement by hand (re-run with --verbose to print them).' in the bare run and '… — apply the → replacement shown under each finding above.' under --verbose (doctor.ts:2283-2288); no `os …`/`objectstack …` command name appears anywhere in the hint; and step 6's `os codemod --help` is STILL not-found — the hint's claim that no codemod ships is TRUE, which is what makes prescribing nothing the honest line rather than a second false one", + "evidence": "both hint lines verbatim (bare and --verbose) + the `os codemod --help` not-found capture" } ], "negative": [ "a hit reported from a *.test.ts or node_modules path is a filter regression", "a run with hits exiting nonzero is a gate this surface never had — the scan is advisory by design", "a run record that ticks the clean clause on the success line alone, without proving src/ existed, is the absence-inference false positive", - "the retired-lookup entry reporting only one of its two spellings while both were seeded" + "the retired-lookup entry reporting only one of its two spellings while both were seeded", + "the hint prescribing an unregistered command again — any `os …`/`objectstack …` name resolving to no file under packages/cli/src/commands/ — is the #10680 regression its class-closing pin exists to catch", + "the hint repointed at `os migrate meta`: a REGISTERED command that still cannot do this job, so it would read as a PASS to any check that only asks whether the named command exists. That repoint was considered and deliberately refused", + "the closing hint vanishing along with the count line — removing a false prescription must not remove the finding summary, so a hits run printing rows but no 'Found deprecated pattern(s).' line is a regression, not a cleanup" ], "traps": ["absence-inference", "stale-dist"], "source": [ - "packages/cli/src/commands/doctor.ts (flag :1717; scan block :2136-2153 with the scanDir = /src at :2138 and the hint at :2149; DEPRECATED_PATTERNS :1186-1234; scanDeprecatedPatterns :1236-1265 — absent-dir [] at :1238, .ts-only walk + .test.ts filter :1240; walkDir node_modules prune :1130)", + "packages/cli/src/commands/doctor.ts (flag :1793; scan block :2245-2292 with the scanDir = /src at :2247, the per-hit warning row at :2252, the --verbose `→ replacement` at :2253-2255, the #10680 reasoning comment at :2258-2282, the two-branch hint at :2283-2288 and the clean-run success line at :2290; DEPRECATED_PATTERNS :1262-1310 — retired-lookup pair :1291; scanDeprecatedPatterns :1312-1341 — absent-dir [] at :1314, .ts-only walk + .test.ts filter :1316; walkDir node_modules prune :1130)", + "packages/cli/src/commands/doctor-deprecation-hint-commands.test.ts (#10680's pin — the class-closing sweep that every `os …` hint doctor can PRINT resolves under src/commands/, plus six end-to-end cases over Doctor.run. Added to source at revision 3 because revision 2's knownGaps asserted this surface had no automated coverage of any kind)", "content/docs/protocol/backward-compatibility.mdx:126-134 (the documented workflow, including the :134 admission that the codemod command is not yet available)", "packages/cli/README.md §os doctor (documents -v and --scan-deprecations)", "packages/create-objectstack/src/templates/blank/ (the scaffold whose src/ the seeds land in)", @@ -1042,7 +1047,8 @@ ], "history": [ { "revision": 1, "date": "2026-08-20", "change": "new — scoped scan-functionality sweep (扫描功能), found independently by two hunters and re-verified against source: --scan-deprecations had zero coverage of any kind (no test in the repo references it). The 8-entry pattern table was recounted from doctor.ts:1186-1234; brief line-drift corrected during authoring (absent-dir return is :1238 not :1273, the scanner body ends at :1265 not :1276). The dead `objectstack codemod v2-to-v3` prescription at :2149 is encoded as an expected-fail clause with the docs' own 'not yet available' admission as corroborating source", "ref": "claude/new-session-0pv25p" }, - { "revision": 2, "date": "2026-08-21", "change": "corrected the two descriptors that called `os migrate meta` a codemod — the expected-fail clause's 'The real metadata codemod is os migrate meta' and the source list's 'cli.migrate-meta-codemod (the codemod that actually exists)'. It is not one. Re-derived at this base: the command declares exactly from/to/step/out/stored/database-url/apply/yes/force/type/json (commands/migrate/meta.ts:195-239) with no --write/--fix/in-place; its only file writes are the two `if (flags.out)`-guarded writeFileSync calls at :347 and :394-396, which emit a JSON snapshot; and its header at :155-157 states the command does not silently rewrite TS config source because that AST rewrite is 'unsafe and lossy'. Both descriptors now say what it is — a chain replayer that PRINTS the mechanical edits for hand application — and name #9591 (v18, pm:on-hold) as the commissioned-but-unbuilt in-place codemod, which was added to `source` so the reference resolves. The clause the item turns on is UNCHANGED and still an expected-fail: the hint at doctor.ts:2149 prescribes `objectstack codemod v2-to-v3`, registered nowhere. The clause also now warns that a FIX which merely repoints the hint at `os migrate meta` must describe it as a list, not an auto-fix — otherwise the dead prescription is replaced by a false one. Sibling cli.migrate-meta-codemod was read and deliberately left alone: its revision-3 text (#9733 via PR #10412) already states the print-only contract correctly, and its `-codemod` id is immutable by README lifecycle", "ref": "#10418" } + { "revision": 2, "date": "2026-08-21", "change": "corrected the two descriptors that called `os migrate meta` a codemod — the expected-fail clause's 'The real metadata codemod is os migrate meta' and the source list's 'cli.migrate-meta-codemod (the codemod that actually exists)'. It is not one. Re-derived at this base: the command declares exactly from/to/step/out/stored/database-url/apply/yes/force/type/json (commands/migrate/meta.ts:195-239) with no --write/--fix/in-place; its only file writes are the two `if (flags.out)`-guarded writeFileSync calls at :347 and :394-396, which emit a JSON snapshot; and its header at :155-157 states the command does not silently rewrite TS config source because that AST rewrite is 'unsafe and lossy'. Both descriptors now say what it is — a chain replayer that PRINTS the mechanical edits for hand application — and name #9591 (v18, pm:on-hold) as the commissioned-but-unbuilt in-place codemod, which was added to `source` so the reference resolves. The clause the item turns on is UNCHANGED and still an expected-fail: the hint at doctor.ts:2149 prescribes `objectstack codemod v2-to-v3`, registered nowhere. The clause also now warns that a FIX which merely repoints the hint at `os migrate meta` must describe it as a list, not an auto-fix — otherwise the dead prescription is replaced by a false one. Sibling cli.migrate-meta-codemod was read and deliberately left alone: its revision-3 text (#9733 via PR #10412) already states the print-only contract correctly, and its `-codemod` id is immutable by README lifecycle", "ref": "#10418" }, + { "revision": 3, "date": "2026-08-24", "change": "text refresh only, no capability change: #10680 landed the fix revision 2's expected-fail described, so acceptance[5] inverts — but NOT into 'the hint now names a registered command'. The route taken was prescribe-nothing: at this base the hint is a two-branch printInfo (doctor.ts:2283-2288) reading 'Found deprecated pattern(s). No automated codemod ships with the CLI — apply each finding’s replacement by hand (re-run with --verbose to print them).' bare and '… — apply the → replacement shown under each finding above.' under --verbose, so revision 2's assertion 'the hint must name a command os actually registers' was the wrong SHAPE for head, not a fail that merely flipped. The print site's comment at :2258-2282 records why a repoint at `os migrate meta` was refused, and the clause now cites that comment instead of re-deriving the migrate/meta.ts anchors. Six texts changed beyond the named clause, all re-derived at this base: (a) the title's trailing expected-fail phrase inverted; (b) fixtures.knownGaps — revision 2's 'no test anywhere in the repo references --scan-deprecations' is FALSE at head, #10680 added packages/cli/src/commands/doctor-deprecation-hint-commands.test.ts, and the gap text now states what that pin does and does NOT reach (no decoys, no absent-src probe, two seeded lines rather than eight patterns, in-process Doctor.run rather than the shipped binary — so stale-dist stays this item's to catch), with the test added to source; (c) step 6 repurposed from 'record the command the hint prescribes' into a corroboration probe — `os codemod --help` must STILL be not-found, which is what makes 'No automated codemod ships' true rather than the next false statement; (d) step 3 now demands the closing hint from BOTH runs, since it is not the same sentence; (e) three negatives added for the inverted clause's wrong shapes — an unregistered name returning, a repoint at the registered-but-incapable `os migrate meta`, and the count line vanishing with the prescription; (f) every doctor.ts anchor re-derived by content: #10679 moved the scanner block +76 and #10680's comment pushed the print site to +109, so of the eleven numbers revision 2 quoted only walkDir's node_modules prune at :1130 still held. Verified accurate at head and deliberately LEFT ALONE: content/docs/protocol/backward-compatibility.mdx:126-134 (the :134 'not yet available' admission still reads as quoted — and at head the tool is consistent WITH it), packages/cli/README.md §os doctor :212-215, the blank template's src/objects/note.object.ts, #9591 (still open, target:v18, pm:on-hold), sibling cli.migrate-meta-codemod (still revision 3), the DEPRECATED_PATTERNS count of 8, the four original negatives, both personas and both traps. Out of scope and filed instead: FOLLOW-UPS.md §7b still lists this prescription as unfixed and names `os migrate meta` as 'the real path', the repoint #10680 refused (#11628)", "ref": "#11420" } ] }, {