Repository navigation
Commit 45b90b6
fix(cli): evaluate
Fixes #17821
`os lint`'s `naming/namespace-prefix` advisory read ONE flattened array
per collection key — `lint.ts:428`, `const items: any[] =
Array.isArray(stack[key]) ? stack[key] : [];` — with no package
boundary, while its own comment at `lint.ts:362` asserted "We only see
one package's config here". On a composed multi-package project, which
is exactly the shape `composeStacks(…, { manifest: 'preserve' })` emits,
that array carries every package's items. So two packages that each
legitimately declare `home` were reported as one package declaring it
twice, prescribing a rename of a name that was already correct — with
the OTHER package's namespace as the suggested prefix — under a closing
sentence saying distinct packages may reuse a name freely.
## What this does
ADR-0130 D4/D5 already ruled that artifacts register per package, and
`compile.ts` step 3b-ii runs the author-time rule table once per package
through `artifactPackages` + `packageBodyAsStack`. This ports that ruled
shape into the entry that missed it; it does not invent a new one.
Those two functions move verbatim to
`packages/cli/src/utils/artifact-packages.ts`, and `lintConfig` calls
the same pair. The alternative to a shared module was importing one
oclif command from another — which would pull `compile.ts`' whole module
graph, the lowerer and the docs sweep included, into every `os lint`
invocation — or a second copy of the package-id rule, free to drift from
the one the runtime registers under.
⛔ Not a narrowing, an opt-out, or a skip. `compile.ts:118`'s fence —
"This is NOT 'skip the site per package' — that would silence the gate"
— binds here too. Every finding the advisory exists to raise is still
raised, on the package that owns it. What changed is that a name is
judged against its OWN package's declarations instead of against the
union of every package's. The defect was missing attribution, never the
signal.
Fewer than two packages keeps today's read verbatim: a stack with no
`packages[]` is one package by definition, and with exactly one the
flattened top level carries that package's collections and nothing else,
in its own order. With two or more, the flattened top level mixes
packages, so the path is written whole
(`packages[1].manifest.apps[1].name`) and resolves in BOTH ADR-0130 D4
shapes — a bare `apps[1]` would, on the additive shape, point at a
different item than the one being reported.
## Measured — both directions, both stack shapes
Through the real binary (`node packages/cli/bin/run.js lint`), `os lint`
exit **0** on every row. Before: `origin/main` `84e6b05b6`. After: this
branch at `7ccfdd189`. Count is `naming/namespace-prefix` warnings.
| fixture | stack shape | before | after | |
|:--|:--|--:|--:|:--|
| two packages each declaring `home` (the card's repro) | additive | 1 |
0 | false positive, removed |
| two packages each declaring `home` (the card's repro) |
`packages[]`-only | 1 | 0 | false positive, removed |
| two `home` apps inside ONE package | additive | 1 | 1 | true positive,
kept |
| two `home` apps inside ONE package | `packages[]`-only | 1 | 1 | true
positive, kept |
| one `log_call` action per package, same `objectName` | additive | 1 |
0 | false positive, removed |
| two `log_call` actions on one `objectName`, one package | additive | 1
| 1 | true positive, kept |
⛔ The true-positive rows are not decoration: a run of the false-positive
side alone is satisfied by an advisory that was simply switched off, and
that is the outcome the fence above names.
The prescription is fixed in the same motion. The surviving true
positive used to read `rename one, e.g. "alpha_portal"` — `alpha` being
the namespace of the package that does not own the reported item — and
now reads `"beta_portal"`, because `ns` is read off the same package
stack the items came from.
Both shapes are live today: #17528 landed (`8305ad6df`) and folds an
option-B stack before the hand-written checks run, so the card's "after
#17528" column is the present tense and repairing only the additive
shape would have been half a fix.
## Ablations
Both legs mutate `packages/cli/src/commands/lint.ts` on disk, prove the
bytes changed (`git hash-object` before vs after, and an anchor
occurrence count), run the suites, then restore with `git checkout HEAD
-- …` and prove the restore (`git diff HEAD` empty, blob hash equal to
`HEAD:packages/cli/src/commands/lint.ts`). The mutated module is
imported by the tests through a relative specifier into `src/`, so no
`dist/` is on this consumption path.
1. **Revert the per-package dispatch** (`packageEntries.length > 1`
becomes `> 999`, i.e. always the union leg): 12 of the 15 new pins go
red, and BOTH pre-existing files — `lint-namespace-prefix.test.ts` and
`lint-handwritten-checks-package-fold.test.ts` — stay green. That green
is why the new file had to exist: the old pins are all single-package
and cannot see this axis at all.
2. **Silence the advisory** (early `return issues;`): every
TRUE-POSITIVE row goes red while every "THE CARD'S REPRO" row stays
GREEN. That is the signature of the outcome the fence forbids, and the
pair is what detects it.
## Verification
- `pnpm --filter '@objectstack/cli^...' build` — exit 0.
- `pnpm --filter @objectstack/cli typecheck` — exit 0 (includes
`check:test-typecheck`).
- `pnpm --filter @objectstack/cli exec vitest run --project unit` —
**205 files / 2950 tests passed**. The `integration` tier is declared to
CI: the diff touches no spawn entry, no `test/helpers/serve-process.ts`,
and no driver/kernel boot path.
- `pnpm lint` (repo-wide, `eslint . --no-inline-config`) — exit 0, 96s.
Not narrowed.
- Gate families derived with `node scripts/pm/dispatch-gates.mjs` from
this tree: **62 derived, 62 run, 0 NOT MEASURED, 0 UNRUN**, every one
recording exit 0 (reconciled with `--ran`).
- os lint's duplicate-name advisory per package (#17952)1 parent 38472ce commit 45b90b6
5 files changed
Lines changed: 591 additions & 201 deletions
File tree
- .changeset
- packages/cli
- src
- commands
- utils
- test
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
| 19 | + | |
19 | 20 | | |
20 | 21 | | |
21 | 22 | | |
| |||
50 | 51 | | |
51 | 52 | | |
52 | 53 | | |
53 | | - | |
54 | | - | |
55 | | - | |
56 | | - | |
57 | | - | |
58 | | - | |
59 | | - | |
60 | | - | |
61 | | - | |
62 | | - | |
63 | | - | |
64 | | - | |
65 | | - | |
66 | | - | |
67 | | - | |
68 | | - | |
69 | | - | |
70 | | - | |
71 | | - | |
72 | | - | |
73 | | - | |
74 | | - | |
75 | | - | |
76 | | - | |
77 | | - | |
78 | | - | |
79 | | - | |
80 | | - | |
81 | | - | |
82 | | - | |
83 | | - | |
84 | | - | |
85 | | - | |
86 | | - | |
87 | | - | |
88 | | - | |
89 | | - | |
90 | | - | |
91 | | - | |
92 | | - | |
93 | | - | |
94 | | - | |
95 | | - | |
96 | | - | |
97 | | - | |
98 | | - | |
99 | | - | |
100 | | - | |
101 | | - | |
102 | | - | |
103 | | - | |
104 | | - | |
105 | | - | |
106 | | - | |
107 | | - | |
108 | | - | |
109 | | - | |
110 | | - | |
111 | | - | |
112 | | - | |
113 | | - | |
114 | | - | |
115 | | - | |
116 | | - | |
117 | | - | |
118 | | - | |
119 | | - | |
120 | | - | |
121 | | - | |
122 | | - | |
123 | | - | |
124 | | - | |
125 | | - | |
126 | | - | |
127 | | - | |
128 | | - | |
129 | | - | |
130 | | - | |
131 | | - | |
132 | | - | |
133 | | - | |
134 | | - | |
135 | 54 | | |
136 | 55 | | |
137 | 56 | | |
| |||
0 commit comments