Repository navigation
Commit 1eea507
feat(devx): gate CLI command-id string literals outside the CLI package (#12225)
* feat(devx): gate CLI command-id literals outside the CLI package (#12016)
A CLI command id spelled as a string literal outside `packages/cli` has
nothing tying it to the command file it names. Rename the command, update
the CLI and the docs but not the engine, and `driver-sql`'s drift warning
keeps printing a route that no longer exists — a stale hint inside an
otherwise-correct message, which no suite reads as wrong: the driver's own
pin asserts the message CONTAINS the constant, and the constant still
matches itself.
The card offered the gate on the argument that the constant "is unlikely to
stay the only such string" — a claim about a population nobody had counted.
Counted first: 274 command-id literals across 98 files outside the CLI
package, 14 of them in `schema-drift.ts` itself. The same warning message
that interpolates the named constant also spells "os migrate apply" inline,
as a bare literal with no constant and no pin at all.
The gate derives ids from declared data (`oclif.bin` plus every `bin` key;
`src/commands/**` paths), never a table, and resolves topic directories as
well as command files. Its self-test pins the #12016 rename itself: the same
literal that resolves before the rename resolves to nothing after it. That
case is what caught a plain longest-prefix rule quietly rescuing
`os migrate multi-value-columns` as `os migrate` — blind to exactly the
failure the gate exists for.
Two declared ledgers, both self-retiring: three self-test fixtures, and one
real violation the first live run found in `packages/spec` (#12223).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_af22b339-91b5-5814-8b9b-2453fc5b3f68
* fix(devx): exclude the command-id gate's own source from its own population
A checker cannot be its own evidence. Every negative fixture in this file —
`os migrate nonexistent-command`, `os demo`, `os nope nope` — is an
unresolvable id ON PURPOSE, because that is what a self-test for this gate
is made of. Scanning them made the gate red in proportion to how well it was
tested, with "delete the tests" as the only way back to green.
Found the honest way rather than reasoned out: `populationFiles` reads
`git ls-files`, so the gate ran green while its own source was still
UNTRACKED and reded on the first run after it was committed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_af22b339-91b5-5814-8b9b-2453fc5b3f68
* fix(devx): declare the command-id gate's scripts/ subtree so a dispatch can name it
`dispatch-gates` derives WHICH gates a card must run by matching path literals
in each gate's source against the card's changed files, and `hintCovers`
REFUSES a bare single-segment literal as too generic — a measured refusal
(+139084 fabricated pairs) that stays. `populationFiles` spelled its admission
predicate as `f.startsWith('scripts/')`, which collapses to `scripts`, so this
gate would have "landed already invisible": never named for a card touching its
own population, scoring the same quiet green for every card in the tree.
Remedy (a), not (b), and the difference is what the gate READS. `'scripts/'`
here is an ADMISSION predicate, not an exclusion or a fixture path: every
tracked source file under that root is in the population with no further test,
and seven of them already carry findings. So the subtree spelling is a true
statement about the scan, and declaring it is the `ROOT_DIR_WATCH_HINTS` idiom
(`check-role-word.mjs`, `check-examples-live-imports.mjs`), not a fabricated
lead.
The roots are hoisted to POPULATION_ROOTS so the predicate and the declaration
cannot drift, and four self-test cases pin both directions: every
separator-less root is declared as `<root>/**`, nothing is declared that the
gate does not walk whole, the declaration is a source LITERAL (a computed
`${r}/**` builds no hint at all), and the population really reaches across the
root. `packages/**` is deliberately NOT declared — this gate opens each
package's `src` subtree and its package.json, not the root entire.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_af22b339-91b5-5814-8b9b-2453fc5b3f68
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 18c432e commit 1eea507
3 files changed
Lines changed: 600 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1604 | 1604 | | |
1605 | 1605 | | |
1606 | 1606 | | |
| 1607 | + | |
| 1608 | + | |
| 1609 | + | |
| 1610 | + | |
| 1611 | + | |
| 1612 | + | |
| 1613 | + | |
| 1614 | + | |
| 1615 | + | |
| 1616 | + | |
| 1617 | + | |
| 1618 | + | |
1607 | 1619 | | |
1608 | 1620 | | |
1609 | 1621 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
73 | 73 | | |
74 | 74 | | |
75 | 75 | | |
| 76 | + | |
76 | 77 | | |
77 | 78 | | |
78 | 79 | | |
| |||
0 commit comments