|
| 1 | +--- |
| 2 | +'@objectstack/cli': patch |
| 3 | +--- |
| 4 | + |
| 5 | +The per-package author-time de-duplication key ignores the top-level collection index, so a package-local finding no longer survives as an echo of the union finding it duplicates |
| 6 | + |
| 7 | +`runPerPackageAuthoringRules` runs the author-time rule table once per |
| 8 | +`packages[]` entry and drops anything the union run already reported. Its key |
| 9 | +was `rule` + `where` + `path` + `message`, and `path` is **positional**: a |
| 10 | +package body re-bases every collection from 0, while the flattened union numbers |
| 11 | +that same entry wherever `authoringRuleUnionStack` placed it. |
| 12 | +`objects[0].fields.industry` and `objects[1].fields.industry` are ONE finding |
| 13 | +under two spellings, so the `Set` never matched them and the echo survived the |
| 14 | +filter that exists to remove it. |
| 15 | + |
| 16 | +Measured on `origin/main` a43b9d0654 over the repo's own two-package fixture |
| 17 | +`examples/app-multi-package`, at every door, before and after: |
| 18 | + |
| 19 | +| | before | after | |
| 20 | +|---|---|---| |
| 21 | +| `os build --json` | warnings 4, exit 0 | warnings 3, exit 0 | |
| 22 | +| `os validate --json` | warnings 4, exit 0 | warnings 3, exit 0 | |
| 23 | +| `os lint --json` | total 4, failing 0, exit 0 | total 3, failing 0, exit 0 | |
| 24 | +| `os lint --json --strict` | total 4, failing 4, exit 1 | total 3, failing 3, exit 1 | |
| 25 | + |
| 26 | +The one warning that stops being reported is `field-no-consumers` on |
| 27 | +`crm_account.industry` re-reported at the package-local index — the union run's |
| 28 | +own finding, printed a second time. Its twin is still reported, which is why no |
| 29 | +verdict moves. |
| 30 | + |
| 31 | +**No input's verdict changes, and that is structural rather than a property of |
| 32 | +this fixture.** Every finding the de-duplication drops has, by construction, a |
| 33 | +finding carrying the same key already in the reported set: the seed is the union |
| 34 | +run's findings, which every door reports, and it grows only with per-package |
| 35 | +findings that themselves survived. So a door's refusal cannot flip — `os build` |
| 36 | +already exits 1 on a union error before this pass runs, and `os lint --strict` |
| 37 | +fails on `errors + warnings`, a count that could only reach zero if the twin |
| 38 | +went unreported too. |
| 39 | + |
| 40 | +Only the **top-level** index is neutralised. Nested positions (`.indexes[1]`, |
| 41 | +`.columns[0]`) address the author's own document and read identically in both |
| 42 | +views, so they stay in the key and keep discriminating. A finding's own `path` |
| 43 | +is never modified — every door still prints the location it always printed. |
| 44 | + |
| 45 | +What this does **not** buy: the key becomes position-insensitive, not |
| 46 | +collision-proof. Two entries that render the same `where` still share a key, |
| 47 | +exactly as they already did whenever their indices happened to match. Measured |
| 48 | +over every example stack in this repo that parses today (`app-multi-package`'s |
| 49 | +built artifact, `app-crm`, `app-showcase`, `app-todo`), 45 registry rules |
| 50 | +produced 103 findings and 103 distinct neutralised keys — zero collisions. |
| 51 | + |
| 52 | +Also corrected: the sentence "what survives the filter is exactly the set the |
| 53 | +union could not see", which was false for as long as the key was positional and |
| 54 | +had been copied from `compile.ts` into the `os validate` and `os lint` doors as |
| 55 | +each was wired. It is now stated at the bound the pass can actually hold, in |
| 56 | +every file that carried it. |
| 57 | + |
| 58 | +Clause-②: no |
0 commit comments