|
| 1 | +--- |
| 2 | +"@objectstack/driver-sql": patch |
| 3 | +--- |
| 4 | + |
| 5 | +test(driver-sql): attribute each `legacyUniqueReplacements` guard to exactly one case (#8557) |
| 6 | + |
| 7 | +**`patch`, and deliberately not `none`.** This adds no runtime code and changes |
| 8 | +no behaviour — every assertion is green on `main` before the change. The bump is |
| 9 | +the floor rather than a skipped changeset because the file it protects is |
| 10 | +release-relevant: what lands is the pin that makes a future single-guard |
| 11 | +deletion visible, and the release notes for the version that first carries it |
| 12 | +are the place a maintainer looks to learn the pin exists. A `minor` would claim |
| 13 | +a capability; `none` would leave the protection undocumented at the only moment |
| 14 | +anyone reads for it. |
| 15 | + |
| 16 | +The declared-index replacement arm's guards were **individually unpinned**: |
| 17 | +measured on #8468, deleting the ADR-0120 S6 name-identity guard, or admitting a |
| 18 | +declared bare `unique: true` through the scope filter, left the entire suite |
| 19 | +green — including the two tests whose names say they cover exactly those cases. |
| 20 | +The protection was real but collective, so no test attributed it to a line, and |
| 21 | +a refactor could remove any single guard and be told nothing. |
| 22 | + |
| 23 | +`schema-drift.legacy-unique-guard-attribution.test.ts` adds that attribution. |
| 24 | +The existing object-level suites are untouched — they are broader than any one |
| 25 | +guard, which is why they could not do this job. |
| 26 | + |
| 27 | +- **Nine guards are individually attributable.** One input per guard, |
| 28 | + constructed so only that guard can reject it, each paired with a **twin** — |
| 29 | + the same input with the single property that guard reads changed, which must |
| 30 | + produce exactly one replacement. The twin is the reachability witness: without |
| 31 | + it a case would still pass while some earlier guard swallowed the input, which |
| 32 | + is the failure mode being fixed, one level up. Measured: deleting any one of |
| 33 | + the nine turns **exactly one** test red, and its name says which line went. |
| 34 | +- **Five guards cannot be attributed at all**, because another guard rejects a |
| 35 | + superset of their inputs — deleting one is behaviour-preserving for every |
| 36 | + possible argument, so a test claiming to pin it would be lying. For those, |
| 37 | + what is pinned is the **fact the domination rests on**, so the day it breaks |
| 38 | + and the guard becomes load-bearing alone, something goes red. |
| 39 | + |
| 40 | +Behind the dominated S6 guard are the hand-written organization composites on |
| 41 | +`sys_team`, `sys_business_unit` and `sys_member` — three shipped platform |
| 42 | +objects on a spelling valid indefinitely. Those composites are now pinned |
| 43 | +directly, in both the shipped bare-`true` spelling and the respelled |
| 44 | +`'organization'` form. |
| 45 | + |
| 46 | +The bare-spelling case is the test-side half of a pair whose first half already |
| 47 | +shipped: #8463 (PR #8512) put the same divergence into prose on |
| 48 | +`isOrganizationScopedUnique`'s JSDoc, in this same file, with no test attributing |
| 49 | +it. Routing the declared branch through the field predicate remains the rejected |
| 50 | +option 1 of #8323 (maintainer ruling 2026-08-13), and is now refused by a test |
| 51 | +rather than only by a comment. |
0 commit comments