Repository navigation
Commit 4ed1ab9
test(cli): pin the union fold by the finding's pedigree, not by its exit code (#19369)
Fixes #18897
Clause-②: no
## The sweep, and what it found
The card's discriminant: **a control is satisfied by an impostor
whenever some mechanism other than the one under test can supply the
asserted value in that fixture.** The method is ablation — delete the
mechanism a control claims to pin, re-run, and read the colour. A
control that stays green was never pinning it.
21 `packages/cli` fixtures were enumerated by the property (*a control
that asserts a survivor or a resolution exists*), not by the three files
the card body tabulates, and every one of them was ablated. Baseline
first: 21 files / 225 tests, all green at the dispatch base
`13d52947d8`.
**One control family was impostor-satisfied, and its impostor is a THIRD
mechanism — neither the echo nor the id tail.**
`test/union-fold-command-parity.test.ts` claimed to pin
`authoringRuleUnionStack`, the fold that makes the author-time rule
table's INPUT non-empty on an option-B (`packages[]`-only) stack. It
could not. Since #18677 (`os validate`) and #18778 (`os lint`) all three
doors ALSO run `runPerPackageAuthoringRules`, which judges each package
body as its own stack — and on this fixture that pass raises the SAME
rule at the SAME path and refuses with the same exit code.
| ablation | `union-fold-command-parity.test.ts` |
|:--|:--|
| `authoringRuleUnionStack` never folds | **6/6 GREEN** |
| `runPerPackageAuthoringRules` yields no findings | **6/6 GREEN** |
| BOTH of the above | **3 RED** (the three refusal cases) |
Either mechanism alone kept every case green. Only deleting both turned
the refusal cases red. The control was correct when it was written
(#17069) and became impostor-satisfied when a sibling pass was added to
the same doors a year of cards later — which is the generalisation this
PR contributes back to the card.
## The repair
The two mechanisms are told apart by the finding's **pedigree**.
`runPerPackageAuthoringRules` prefixes the `where` of every finding it
raises with `package 'ID' — `; the union run renders `where` bare.
Measured on that fixture, with only the fold's presence moving:
```text
fold intact • object "ob_order" · field "ghost": …
fold ablated • package 'com.example.ob' — object "ob_order" · field "ghost": …
```
So each refusal case now asserts that prefix is ABSENT, and a new
**positive pedigree control** asserts the same suite can make it PRESENT
— on a fixture whose only finding really is per-package-only (`core`
declares `ob_account.industry`, `orders` owns the view that displays it:
the falsifier shape PR #18878 landed). Without that pair the negative
assertion would be satisfied by a prefix this suite never produces at
all.
Verified in both directions, from the committed state:
| run | result |
|:--|:--|
| repaired file at HEAD | 9/9 GREEN |
| union fold ablated | **3 RED** — the three refusal cases (was 6/6
green before this change) |
| per-package pass ablated | **3 RED** — the three pedigree controls;
refusal cases correctly stay green |
### One half of the prescribed remedy is not available here, and the
header says so
The card's remedy shape is *a real falsifier AND a pedigree assertion*.
The falsifier half cannot be built for this rule class, and that was
measured rather than assumed: the per-package pass is a SUPERSET of the
union run for reference rules (`utils/artifact-packages.ts`: *"the
per-package run is STRICTER"*), because each body is judged with the
artifact's own `packages[]` handed in as resolution context. A name no
package provides therefore dangles in BOTH views, under every
arrangement of packages. Two candidate union-only fixtures were built
and probed; neither produced a finding the per-package pass could not
also raise. The pedigree is the whole discriminant here, and the
positive control is what makes it falsifiable.
## The full ablation table
Every fixture is reported, including the ones that needed no change — a
sweep that lists only what it changed cannot be checked for coverage.
| fixture | mechanism deleted | red? | verdict |
|:--|:--|:--|:--|
| `test/union-fold-command-parity.test.ts` | `authoringRuleUnionStack`
fold | **NO — 6/6 green** | **IMPOSTOR (third family)** — repaired here
|
| `test/union-fold-command-parity.test.ts` | per-package pass findings |
**NO — 6/6 green** | same control, other mechanism |
| `test/union-fold-command-parity.test.ts` | both of the above | YES — 3
| confirms neither alone is load-bearing |
| `test/validate-per-package-authoring-parity.test.ts` | per-package
pass findings | YES — 1/4 | correct (only the lit control; the parity
pins go vacuous, as the card predicts) |
| `test/validate-per-package-authoring-parity.test.ts` | the fixture's
falsifier view | YES — 1/4 | correct; `expected 0 to be greater than 0`
|
| `test/build-text-face-advisory-count.test.ts` | per-package pass
findings | YES — 3/5 | correct |
| `test/build-text-face-advisory-count.test.ts` | the fixture's
falsifier view | YES — 1/5 | correct |
| `test/lint-per-package-authoring-parity.test.ts` | per-package pass
findings | YES — 2/5 | correct (documented already-correct; verified,
not inherited) |
| `test/lint-per-package-authoring-parity.test.ts` | the fixture's
falsifier view | YES — 1/5 | correct |
| `test/lint-per-package-authoring-seam.test.ts` | per-package pass
findings | YES — 2/6 | correct |
| `test/lint-per-package-authoring-seam.test.ts` | the fixture's
falsifier view | YES — 1/6 | correct |
| `test/validate-per-package-authoring-seam.test.ts` | per-package pass
findings | YES — 2/6 | correct for its narrower claim (it de-duplicates
against an EMPTY union by construction, and its header says so) |
| `test/per-package-dedup-positional-echo.test.ts` | per-package pass
findings | YES — 5/6 | correct by construction |
| `src/utils/collect-docs.package-docs.test.ts` | `docsPackageRefs`
id-TAIL branch | YES — 12/32 | correct |
| `src/utils/collect-docs.package-docs.test.ts` | `docsPackageRefs`
FULL-ID branch | YES — 2/32 | correct — and the 30 staying green is the
#18962 reading reproduced |
| `src/utils/artifact-packages.test.ts` | `packageBodyAsStack`
resolution context | YES — 2/4 | correct; it already carries its own
contextless CONTROL leg |
| `test/lint-handwritten-checks-package-fold.test.ts` |
`authoringRuleUnionStack` fold | YES — 5/7 | correct |
| `src/utils/format.metadata-stats-package-fold.test.ts` |
`authoringRuleUnionStack` fold | YES — 3/8 | correct (the top-level-only
and empty-stack rows rightly stay green) |
| `src/utils/stack-collections.test.ts` | `authoringRuleUnionStack` fold
| YES — 3/16 | correct |
| `src/utils/stack-collections.test.ts` | `resolveStackCollection`
packages leg | YES — 2/16 | correct |
| `test/info-detail-package-fold.test.ts` | `resolveStackCollection`
packages leg | YES — 7/7 | correct |
| `src/utils/nav-contribution-groups.test.ts` | `artifactPackages`
reports no packages | YES — 6/9 | correct |
| `src/utils/nav-contribution-groups.package-id.test.ts` |
`artifactPackages` reports no packages | YES — 4/4 | correct |
| `src/utils/permission-set-name-collisions.test.ts` |
`artifactPackages` reports no packages | YES — 5/10 | correct |
| `test/build-multi-package-artifact.e2e.test.ts` | `packageBodyAsStack`
resolution context | YES — 1/7 | correct |
| `test/build-multi-package-artifact.e2e.test.ts` | compile drops
`packages` from the artifact | YES — 2/7 | correct |
| `test/build-package-docs-attachment.e2e.test.ts` | `docsPackageRefs`
id-TAIL branch | YES — 2/3 | correct |
| `test/build-package-docs-attachment.e2e.test.ts` | `docsPackageRefs`
FULL-ID branch | **NO — 3/3 green** | correct, NOT an impostor: its
fixture directory is an id tail by construction and its header says so.
It never claims the full-id spelling. Recorded as a coverage boundary,
not a defect |
| `test/compile-artifact-packages.e2e.test.ts` | `artifactPackages`
reports no packages | **NO — 4/4 green** | correct, NOT an impostor: it
reads the written artifact, which is not produced through
`artifactPackages` |
| `test/compile-artifact-packages.e2e.test.ts` | compile drops
`packages` from the artifact | YES — 1/4 | correct; this IS its
mechanism |
| `test/validate-build-gate-parity.test.ts` | `authoringRuleUnionStack`
fold body | **NO — green** | correct, NOT an impostor: it is a
source-text WIRING pin with its own positive control (the export must
exist) and fabricated negative controls. Ablating the body leaves the
wiring, which is what it asserts |
| `test/init.test.ts` | not ablated | n/a | EXCLUDED, with evidence: its
`packages:` occurrences are the pnpm-workspace key, and no control in it
asserts a per-package survivor or resolution. It appears in the card's
keyword tally as a false positive |
Every ablation was performed through `scripts/ablation-replace.mjs`, so
each mutation carries its own on-disk proof (anchor count moved, blob
hash changed) and each restore is proven by blob equality against HEAD
plus an empty `git diff HEAD`. No ablation is left on disk.
## Fences observed
- `packages/cli/src/utils/collect-docs.ts` is **not** edited by this PR
— #19248 holds that file. Two ablation legs mutated it transiently
inside this worktree only, each restored with a proven blob match; the
diff touches one file,
`packages/cli/test/union-fold-command-parity.test.ts`.
- The de-duplication key (#18893) and the docs scan depth (#18965) are
out of scope. The key was reverted to its positional form for ONE
measurement — the historical-impostor reconstruction described in the
report comment — and restored.
## Verification
At `e2b7d13d0f`:
- `pnpm --filter @objectstack/cli exec vitest run --project unit` — 220
files / 3114 tests, all pass
- `pnpm --filter @objectstack/cli exec vitest run --project integration`
— 51 files / 430 tests, all pass, run in two halves for the foreground
cap
- `pnpm --filter @objectstack/cli typecheck` — pass
- `node scripts/pm/dispatch-gates.mjs --commands` derived 46 gate
families; all 46 run, all exit 0, reconciled with `--ran` carrying each
exit code: *"46 derived families accounted for — 46 run, 0 NOT-MEASURED
(a DERIVED zero)"*
- `pnpm lint` (repo-wide, `eslint . --no-inline-config`) — exit 0, run
in full rather than narrowed
- `pnpm --filter '@objectstack/cli^...' build` and the full `turbo run
build` over the package farm — both exit 0
`check:dual-build-cjs-loads` and `check:type-check-debt` first answered
`PREREQUISITE NOT MET` (exit 3, which both scripts state is neither a
pass nor a finding) because only the CLI closure had been built. Both
were re-run after the prescribed full build and both exit 0.
## Acceptance notes
Noted, not filed — observations from the sweep that are not reproducible
defects, contract violations or authoring traps:
- `test/build-package-docs-attachment.e2e.test.ts` cannot distinguish
`docsPackageRefs`' full-id branch from its id-tail branch. That is
deliberate in its fixture and stated in its own comments, so it is a
coverage boundary rather than a defect. Next toucher: whoever changes
`docsPackageRefs`' spelling set — the unit pins next door already cover
both branches, and they go red.
- `test/validate-per-package-authoring-seam.test.ts` and the non-vacuity
case of `test/lint-per-package-authoring-seam.test.ts` de-duplicate
against an EMPTY union by construction, so their survivors are not
asserted to be union-invisible. Both headers say so explicitly; the
stronger claim lives in the parity files. Next toucher: none currently
in flight.
- The card's keyword tally lists `test/init.test.ts` as an unexamined
multi-package fixture. It is not one; the match is the pnpm-workspace
`packages:` key.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01QCdUBjM47SxioST9z5Zwdf)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent a88a973 commit 4ed1ab9
1 file changed
Lines changed: 172 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
49 | 49 | | |
50 | 50 | | |
51 | 51 | | |
| 52 | + | |
| 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 | + | |
52 | 101 | | |
53 | 102 | | |
54 | 103 | | |
| |||
69 | 118 | | |
70 | 119 | | |
71 | 120 | | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
72 | 130 | | |
73 | 131 | | |
74 | 132 | | |
| |||
100 | 158 | | |
101 | 159 | | |
102 | 160 | | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
103 | 228 | | |
104 | 229 | | |
105 | 230 | | |
| |||
152 | 277 | | |
153 | 278 | | |
154 | 279 | | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
155 | 297 | | |
156 | 298 | | |
157 | 299 | | |
| |||
171 | 313 | | |
172 | 314 | | |
173 | 315 | | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
174 | 346 | | |
0 commit comments