Skip to content

Commit aa3e6d4

Browse files
committed
fix(spec,lint): correct a false clause in the ledger README and narrow a test's claim
Two required fixes from the at-tier contract review of this PR, both re-measured here before editing rather than taken from the record. F1 — `packages/spec/liveness/README.md`'s `field` cell claimed this was the type's FIRST `planned` -> `live` flip. False: `field.valueDomain` reads `live` with `verifiedAt` 2026-09-04 on origin/main, and `fa125f3bfe` (#15316) is the commit that changed its `"status": "planned"` to `"status": "live"` in this very file. Corrected in place, and the correction says what was wrong: a file whose job is to say true things about the ledger had acquired a confident false sentence of exactly the species it exists to catch. F2 — the anti-vacuity guard's comment and test name claimed "same ledger load, one warning and not two" / "not a lint that stopped loading ledgers". Measured, that overstates it: `loadWarnMap` returns an empty map silently for a missing (:77) or unparseable (:81-83) `field.json`, and `object` and `field` are two separate loads (:485-486), so the guard distinguishes a lint that never resolved the ledger DIRECTORY, not one that lost the field ledger. The name and comment are narrowed to that; the pins themselves are unchanged and still fail when the row is reverted. The per-file blind spot is `loadWarnMap`'s and is left to its own card. Claude-Session: https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3 Co-authored-by: Claude <noreply@anthropic.com>
1 parent 7f9df93 commit aa3e6d4

2 files changed

Lines changed: 23 additions & 10 deletions

File tree

‎packages/lint/src/lint-liveness-properties.test.ts‎

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -727,15 +727,28 @@ describe('lintLivenessProperties', () => {
727727

728728
// Anti-vacuity guard for both silence pins above — the shape the dashboard
729729
// and navigation blocks use, and the reason those pins are worth keeping at
730-
// all. `lintLivenessProperties` returns [] when it cannot resolve the
731-
// shipped ledgers, so "no field findings" is also what a lint that had
732-
// stopped reading ledgers returns; and with `relatedListFilter` live the
733-
// FIELD ledger has no warned row left, so nothing inside the field walk can
734-
// tell a working lint from one that never loaded. This authors the flipped
735-
// key and a property that IS still `authorWarn` — `object.externalSharingModel`,
736-
// on the very object carrying the fields — in the SAME call: same process,
737-
// same ledger load, one warning and not two.
738-
it('the related-list silence is a real verdict, not a lint that stopped loading ledgers', () => {
730+
// all. It answers ONE question, and the test name is narrowed to exactly
731+
// that question: could this file's silence be coming from a lint that never
732+
// resolved the ledger DIRECTORY? `lintLivenessProperties` returns [] outright
733+
// when `resolveLivenessDir()` finds nothing, so "no field findings" is also
734+
// what a lint holding no ledgers at all returns; and with `relatedListFilter`
735+
// live the FIELD ledger has no warned row left, so nothing inside the field
736+
// walk can tell those two apart on its own. This authors the flipped key and
737+
// a property that IS still `authorWarn` — `object.externalSharingModel`, on
738+
// the very object carrying the fields — in the SAME call: one warning and
739+
// not two, out of a directory that demonstrably resolved.
740+
//
741+
// ⚠️ What it does NOT cover, measured rather than assumed: a missing or
742+
// unparseable `field.json` ALONE. `loadWarnMap` returns an empty map
743+
// SILENTLY for both — `lint-liveness-properties.ts:77` for the absent file,
744+
// `:81-83` for the parse failure — and `object` and `field` are two separate
745+
// loads (`:485-486`), so the second warning below still arrives from
746+
// `object.json` and this test stays green. With the row live, a lost
747+
// `field.json` is observationally identical to the flip from inside
748+
// `lintLivenessProperties`. That blind spot belongs to `loadWarnMap`'s silent
749+
// returns, is shared by the #6774 and #10068 blocks this one is modelled on,
750+
// and is left to its own card rather than patched in behind a test name.
751+
it('the related-list silence is a real verdict, not a lint that never resolved the ledger directory', () => {
739752
const findings = lintLivenessProperties({
740753
objects: [{
741754
name: 'account',

‎packages/spec/liveness/README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -902,7 +902,7 @@ marker where the Notes cell goes, never a guess at what belongs there.
902902
| Type | Notes |
903903
|---|---|
904904
| object | aspirational tier (versioning/softDelete/search/recordName/keyPrefix) + tags/active/abstract REMOVED (#2377) — tombstoned in UNKNOWN_KEY_GUIDANCE; `enable.trash`/`mru` REMOVED (#2377 close-out) — tombstoned in the now-`.strict()` ObjectCapabilities; `isSystem` + `enable.searchable` CORRECTED to live (#2377 — sharing default-model + global-search opt-out; 2026-06 audit missed both readers); `tenancy.strategy`/`crossTenantAccess` REMOVED post-15.0 (#2763) |
905-
| field | full dead set (vectorConfig/fileAttachmentConfig/dependencies, then referenceFilters/columnName/index) REMOVED (#2377); columnName also dropped the ADR-0062 D7 lint + StorageNameMapping column helpers. **#13043** ends the empty dead column this type had carried since that sweep — the reason the cell said "healthy" until 2026-08-29: `conditionalRequired` is re-classified `live` → `dead` with no key added or removed. It has been a `retiredKey` tombstone since 2026-07-28 (protocol 17, #3855), so the row stays (the `rls.priority` precedent) while the verdict does not. BOTH halves of its evidence were falsified, not just the citation: the `.transform` lowering `conditionalRequired` → `requiredWhen` that the row credited does not exist (field.zod.ts has zero `.transform` calls), and the objectql rule-validator `requiredWhen ?? conditionalRequired` fallback its note leaned on was retired by #3903, which replays the ADR-0087 conversion chain at rehydration instead — so a stored pre-17 row reaches the validator already lowered. The rot was invisible to every citation check (pointer in range, right file, file names the key) and the entry carried no `verifiedAt`, so nothing ever re-asked — the #12516 class, the same shape `action.execute` turned out to have. It was also the ledger's LAST `path:NNN` citation, so retiring it took #13003's line-citation counter to zero **#19187** flips `relatedListFilter` `planned` → `live` 2026-09-20, the fourth member of the related-list family joining its three siblings and this type's first flip of that direction. The row is the clean case the `app.navigation.runAction` (#10068) and `list.map` (#11442) flips established: #8704 seeded it contract-first with `authorWarn` and wrote the flip condition into its own note, objectui#4664 satisfied that condition, and the flip was taken by re-measuring at the `.objectui-sha` pin rather than on objectui main — `deriveRelatedLists` puts the authored value on the derived descriptor and `RecordDetailView` writes it onto the synthesized `record:related_list` node, so the rows and the tab badge answer one composed question. What makes it a DEFECT rather than bookkeeping is the direction a stale `planned` row fails in: its `authorHint` was a sentence `packages/lint` repeated at every compile — 「the auto-derived related list does not apply this filter yet」 — about a key the pinned console applies, so the ledger was steering authors off a working key rather than merely lagging it. It is also the direction no citation check can see: a `planned` row cites nothing, so nothing rots, and only the consumer landing falsifies it. With it, `field` carries NO `authorWarn` row at any depth, which gates the lint's field walk off entirely (`if (fieldWarn.size > 0)`) — recorded because the next warned field row re-opens that walk, and the #11385 field-walk pin in `packages/lint` is narrowed until one does |
905+
| field | full dead set (vectorConfig/fileAttachmentConfig/dependencies, then referenceFilters/columnName/index) REMOVED (#2377); columnName also dropped the ADR-0062 D7 lint + StorageNameMapping column helpers. **#13043** ends the empty dead column this type had carried since that sweep — the reason the cell said "healthy" until 2026-08-29: `conditionalRequired` is re-classified `live` → `dead` with no key added or removed. It has been a `retiredKey` tombstone since 2026-07-28 (protocol 17, #3855), so the row stays (the `rls.priority` precedent) while the verdict does not. BOTH halves of its evidence were falsified, not just the citation: the `.transform` lowering `conditionalRequired` → `requiredWhen` that the row credited does not exist (field.zod.ts has zero `.transform` calls), and the objectql rule-validator `requiredWhen ?? conditionalRequired` fallback its note leaned on was retired by #3903, which replays the ADR-0087 conversion chain at rehydration instead — so a stored pre-17 row reaches the validator already lowered. The rot was invisible to every citation check (pointer in range, right file, file names the key) and the entry carried no `verifiedAt`, so nothing ever re-asked — the #12516 class, the same shape `action.execute` turned out to have. It was also the ledger's LAST `path:NNN` citation, so retiring it took #13003's line-citation counter to zero **#19187** flips `relatedListFilter` `planned` → `live` 2026-09-20, the fourth member of the related-list family joining its three siblings. ⛔ NOT this type's first flip of that direction, which is what an earlier draft of this cell claimed: `valueDomain` went `planned` → `live` in `fa125f3bfe` (#15316) once the record validator's call into `isValueDomainMember` landed, and it stands `live` with `verifiedAt` 2026-09-04. The correction is kept rather than quietly deleted because the false clause was the same species as the row it was describing — a confident sentence in the file whose job is to say true things about the ledger, falsified by one `git log -p` over this file. The row is the clean case the `app.navigation.runAction` (#10068) and `list.map` (#11442) flips established: #8704 seeded it contract-first with `authorWarn` and wrote the flip condition into its own note, objectui#4664 satisfied that condition, and the flip was taken by re-measuring at the `.objectui-sha` pin rather than on objectui main — `deriveRelatedLists` puts the authored value on the derived descriptor and `RecordDetailView` writes it onto the synthesized `record:related_list` node, so the rows and the tab badge answer one composed question. What makes it a DEFECT rather than bookkeeping is the direction a stale `planned` row fails in: its `authorHint` was a sentence `packages/lint` repeated at every compile — 「the auto-derived related list does not apply this filter yet」 — about a key the pinned console applies, so the ledger was steering authors off a working key rather than merely lagging it. It is also the direction no citation check can see: a `planned` row cites nothing, so nothing rots, and only the consumer landing falsifies it. With it, `field` carries NO `authorWarn` row at any depth, which gates the lint's field walk off entirely (`if (fieldWarn.size > 0)`) — recorded because the next warned field row re-opens that walk, and the #11385 field-walk pin in `packages/lint` is narrowed until one does |
906906
| flow | dead count = **5 tombstone entries** + the kept docs field: `active`/`template`/nodes.`outputSchema`/errorHandling.`fallbackNodeId` REMOVED 2026-07-30 (#3896 close-out sweep — `active: false` never stopped a flow, `status` is the enforced lifecycle; faults route via per-node fault edges), plus errorHandling.`retryDelayMs` RENAMED to `backoffMs` 2026-08-04 (#4964). The rename is why the dead column moved while live did not: a rename is a removal on this ledger, so the old spelling is tombstoned (`retiredKey` keeps it in the walked shape) and the new spelling enters as its own `live` row. Read it beside the four above as the one entry here that cost an author nothing — the block was a THIRD encoding of the retry policy #4661 converged, invisible to that pass because it is an anonymous inline block with no exported name, and #4964 spelled its base delay `backoffMs` to match `job.retryPolicy` and a `try_catch` node's `retry`. Remaining dead = `description`, KEPT deliberately: docs-shaped, exempt from enforce-or-remove |
907907
| action | `type:'form'` CORRECTED to live (objectui ActionRunner.executeForm, #2377); dead `timeout` REMOVED (#2377); `disabled` live since objectui#2863; `undoable` CORRECTED to live (#3714); `shortcut` + `bulkEnabled` REMOVED 2026-07-30 (#3896 close-out sweep — no keydown path dispatches shortcuts; the multi-select toolbar reads the view's bulkActions). **#7367** (PR #7430) adds `description` as an authorable key, `live` on arrival — the only row this type has gained since that sweep. **#13036** makes the dead set three: `execute` joins it, re-classified `live` → `dead` 2026-08-29 with no key added or removed. Its `live` verdict rested on a `.transform` lowering `execute` → `target` that protocol 17 (#3855) removed along with the alias; the key has been a `retiredKey` tombstone since 2026-07-28, so the row stays (the `rls.priority` precedent) while the verdict does not. The rot was invisible to every citation check — the pointer was in range, in the right file, and the file names the key — and the entry carried no `verifiedAt`, so nothing ever re-asked |
908908
| hook | model-healthy; label/description dead but KEPT deliberately (2026-07-30 sweep) — docs-shaped annotation fields, exempt from enforce-or-remove |

0 commit comments

Comments
 (0)