From 1e5831a53f68f7d58a7dd369b0d1bd9033b0ee26 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 27 Oct 2026 20:55:16 +0000 Subject: [PATCH 1/2] fix(spec): the liveness ledger grades field.settledWhen live The row said no reader existed, while ui/'s isDeadlineSettled has been asked by every surface that draws a date cell since objectui#11815 landed. Flip the row to live with its readers and producers, refresh the dueLike note's stale PARTIAL clause, regenerate the field state counts, and re-state the expression-conformance row as enforced / fail-soft-log. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01WGufzgGEj5Qdn1KEGPUrU8 --- .changeset/26727-settled-when-ledger-live.md | 14 ++++++++++++++ .../test/expression-conformance.ledger.ts | 16 +++++++--------- packages/spec/liveness/README.md | 2 +- packages/spec/liveness/field.json | 13 +++++++------ packages/spec/liveness/state-counts/field.md | 2 +- 5 files changed, 30 insertions(+), 17 deletions(-) create mode 100644 .changeset/26727-settled-when-ledger-live.md diff --git a/.changeset/26727-settled-when-ledger-live.md b/.changeset/26727-settled-when-ledger-live.md new file mode 100644 index 00000000000..8e68f11af4f --- /dev/null +++ b/.changeset/26727-settled-when-ledger-live.md @@ -0,0 +1,14 @@ +--- +'@objectstack/spec': patch +--- + +fix(spec): the liveness ledger grades `field.settledWhen` live + +Clause-②: no +Metadata-protocol: none + +The liveness ledger shipped in `@objectstack/spec` (`liveness/field.json`) graded a deadline field's `settledWhen` as `planned`, with a note saying no reader existed. The console has read it for some time: while the predicate holds for a record, that record's deadline shows no overdue wording and no overdue colour, in the grid, list, gallery, kanban, tree, record detail and approval snapshot. The row is now `live` and names that reader. Nothing about what you can write, or what it does, has changed. + +The `dueLike` row's note is corrected the same way: it said the grid did not carry the key yet, and it does. + +`object.tenancy` already had correct rows (`tenancy.enabled` and `tenancy.tenantField`, both live), so it is unchanged. diff --git a/packages/qa/dogfood/test/expression-conformance.ledger.ts b/packages/qa/dogfood/test/expression-conformance.ledger.ts index ab7ab58828c..c98dc888c5c 100644 --- a/packages/qa/dogfood/test/expression-conformance.ledger.ts +++ b/packages/qa/dogfood/test/expression-conformance.ledger.ts @@ -219,18 +219,16 @@ export const EXPRESSION_SURFACE: ExprSurface[] = [ }, { // #22227 — the deadline's settle predicate (objectui#11815 ruling D). A row - // of its own rather than a fourth `cel-field-rule` cover: that row is - // `enforced` and names the evaluators of its three slots, and neither reads - // this one. The CEL is judged at authoring as the family's is; what is - // missing is the reader, so the honest tier is `unevaluated`. + // of its own rather than a fourth `cel-field-rule` cover: that row names + // the evaluators of its three slots, and this one has its own, the + // console's `isDeadlineSettled`. It was `experimental` / `unevaluated` + // until objectui#11815 landed that reader; re-stated in #26727. id: 'cel-field-deadline-settled', - summary: "a date / datetime deadline's settle predicate (Field.settledWhen) — display only, declared ahead of its renderer", - dialect: 'cel', mode: 'interpret', state: 'experimental', failPolicy: 'unevaluated', + summary: "a date / datetime deadline's settle predicate (Field.settledWhen) — display only", + dialect: 'cel', mode: 'interpret', state: 'enforced', failPolicy: 'fail-soft-log', enforcement: - 'NO EVALUATOR FOUND — no runtime consumer yet, in either repo: nothing in this checkout reads the slot, and objectui @55e90fd has no `settledWhen` read at all (measured by grep). PARSE ONLY at the spec seam: `FieldSchema` refuses it off `date` / `datetime` and without `dueLike: true`. Its build-time reader is lint\'s field-rule pass (`validate-expressions.ts`), which judges the CEL as it judges `visibleWhen` (it parses, `record.` only, the family\'s bound roots) at `os build`, `os validate` and the object save door — a reader of the source, not an evaluator of it', + 'console (ui/) isDeadlineSettled (core/evaluator/deadlineSettled.ts) → evalRowPredicate → @objectstack/formula celEngine (interpret), evaluated per record with the row bound as `record`, by every host that draws a date cell beside a row (grid, list, gallery, kanban, tree, the record detail sections and the approval snapshot); the verdict reaches the date / datetime cell as `deadlineSettled`, and a settled deadline paints no overdue wording and no overdue colour. A predicate that faults is logged once (warnEvalError) and answers NOT settled, so the deadline shows as it does without the key. Display only: nothing on the write path reads it. Judged at authoring by lint\'s field-rule pass (`validate-expressions.ts`) as `visibleWhen` is', covers: ['data/field.zod.ts:FieldSchema.settledWhen'], - // The reader is objectui#11815, Blocked-by #22227. - note: 'EXPERIMENTAL until its one reader lands: objectui\'s date cell, whose host evaluates the predicate per row and hands the cell one boolean. Re-state as `enforced`, naming that evaluator and its fail policy, when it lands. Display only by declaration: nothing on the write path will read it, so this row never carries a write-path refusal.', }, { // Split out of `cel-field-rule` by #15500. Both keys used to be spelled diff --git a/packages/spec/liveness/README.md b/packages/spec/liveness/README.md index 885666da471..2e4a15cde77 100644 --- a/packages/spec/liveness/README.md +++ b/packages/spec/liveness/README.md @@ -976,7 +976,7 @@ marker where the Notes cell goes, never a guess at what belongs there. | Type | Notes | |---|---| | 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). **#19054** REMOVES `tenancy.organizationField` at protocol 18 (ADR-0049 enforce-or-remove) — the STRICT-deletion route, so the row leaves this ledger with the key rather than staying as a tombstone: the `tenancy` block is a `strictObject`, the key is gone from the walked shape, and a surviving row would read as an ORPHAN. It was classified `live` on one real consumer (`resolveRecordOrganizationField`'s limb 0) and one real declaration, both of them ours — the key was authorable by every application and declared, repo-wide, only on `sys_api_key`. ⛔ Not a correction of that `live` verdict: the consumer still reads the same column for the same table, now from `PLATFORM_STAMP_ORGANIZATION_COLUMNS` in `@objectstack/metadata-core`, which is not an authorable surface and therefore has no row here. **#23268** REMOVES `externalSharingModel` at protocol 18 (ADR-0049 enforce-or-remove, ruling of record 6159300530) — the TOMBSTONE route, so its row stays as `dead` (retiredKey keeps the key in the walked shape); it was `planned` against a consumer (#2696) that closed unbuilt, and the authorization kernel's `EXTERNAL` rung is explicitly shared rows only | -| field | full dead set (vectorConfig/fileAttachmentConfig/dependencies, then referenceFilters/columnName/index) REMOVED (#2377); columnName also dropped the ADR-0062 D7 lint + StorageNameMapping column helpers. **Commit e25e8397a** 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 the gate'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 | +| field | full dead set (vectorConfig/fileAttachmentConfig/dependencies, then referenceFilters/columnName/index) REMOVED (#2377); columnName also dropped the ADR-0062 D7 lint + StorageNameMapping column helpers. **Commit e25e8397a** 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 the gate'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. **#26727** flips `settledWhen` `planned` → `live` 2026-10-27 (planned 6 → 5, live 99 → 100): the same stale-`planned` shape, found by a Studio QA run rather than by the gate. Its consumer, objectui#11815's `isDeadlineSettled`, had landed under `ui/` and is asked by every surface that draws a date cell, while the row still said no reader existed in either repo. It carried no `authorWarn`, so nothing had been steering authors off it; the cost was agents reading the ledger and treating a working key as inert | | flow | dead count = **5 tombstone entries**: `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`. `description` is the kept docs field — KEPT deliberately, docs-shaped, exempt from enforce-or-remove — and it left the dead set in #20299: the Studio metadata list's default columns and the metadata quick-find draw it for every flow, which for a display key is the whole of the claimed effect (the #7131 ruling above). Still not authorWarn'd | | 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. **Commit cae2169cf** 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. **#20323** makes the dead set four: `aria` re-classified `live` → `dead` 2026-09-28 and tombstoned (the `rls.priority` precedent again). Its `live` verdict rested on an uncited 「PARTIAL — honored by a few objectui renderers」 note; re-measured at the `.objectui-sha` pin, no surface that renders an action reads an action's `aria`, and each takes the accessible name from the required `label` | | hook | model-healthy; label/description KEPT deliberately (2026-07-30 sweep) — docs-shaped annotation fields, exempt from enforce-or-remove — and **`live` since #20299**: `hook` has no registered preview, but the Studio metadata list's default columns and the metadata quick-find draw both keys for every hook, which for a display key is the whole of the claimed effect (the #7131 ruling above). Still not authorWarn'd | diff --git a/packages/spec/liveness/field.json b/packages/spec/liveness/field.json index 5fa57a461d1..3423ab7e685 100644 --- a/packages/spec/liveness/field.json +++ b/packages/spec/liveness/field.json @@ -143,14 +143,15 @@ "verifiedAt": "2026-10-09", "evidenceScope": "in-repo", "evidence": "ui/packages/fields/src/index.tsx#resolveDueLike (`declared === true` on the cell's field, read by both the date and the datetime cell renderer: the relative overdue wording and the overdue colour once the date has passed); ui/packages/plugin-detail/src/fieldEnrichment.ts#ENRICHED_FIELD_METADATA_KEYS (copies the object-schema field's `dueLike` onto the detail field, so the record detail sections hand it to that cell). The same two reads are present at the `.objectui-sha` pin f0268ad78", - "note": "Deadline semantic, `date` / `datetime` only (DEADLINE_FIELD_TYPES; the superRefine refuses it on every other type). Declared by #22227 as the protocol half of objectui#11815's maintainer ruling D (declared-only overdue). LIVE on the detail surfaces today: an authored TRUE paints the overdue affordance there even where the field name matches no pattern. PARTIAL until objectui#11815 (Blocked-by #22227) lands: objectui's grid copy set does not carry the key yet, and objectui still ALSO infers due-likeness from the field name (`DUE_LIKE_FIELD_NAME`), which that card retires — after it, absent means not a deadline everywhere, as the key's describe states. Display only: nothing on the write path reads it." + "note": "Deadline semantic, `date` / `datetime` only (DEADLINE_FIELD_TYPES; the superRefine refuses it on every other type). Declared by #22227 as the protocol half of objectui#11815's maintainer ruling D (declared-only overdue). LIVE on every surface that draws a date cell since objectui#11815 landed: the grid copies the key onto its column metadata (ui/packages/plugin-grid/src/objectGridGenerateColumns.tsx, all three column paths) as the detail sections do, and the name inference (`DUE_LIKE_FIELD_NAME`) is gone, so absent means not a deadline everywhere, as the key's describe states. CORRECTED 2026-10-27: this note said PARTIAL until that landing, and was not updated when it came. Display only: nothing on the write path reads it." }, "settledWhen": { - "status": "planned", - "verifiedAt": "2026-10-09", - "evidenceScope": "cross-repo", - "evidence": "packages/spec/src/data/field.zod.ts#DEADLINE_FIELD_TYPES (the parse-time door: refused off `date` / `datetime`, and refused without `dueLike: true`); packages/lint/src/validate-expressions.ts#runStackExpressionPasses (the authoring gate: the field-rule pass judges it as it judges `visibleWhen` — it parses, it reads `record.`, only the family's bound roots — at `os build` / `os validate` and the object save door)", - "note": "PLANNED — declared ahead of its one reader by the ruling's own sequencing (#22227, the protocol half of objectui#11815's maintainer ruling D): the host evaluates the predicate per row and hands the date cell one boolean, and that read is objectui#11815, Blocked-by #22227, which lands once the spec publishes. No reader in either repo today — objectui @55e90fd has no `settledWhen` read at all — so until then a settled deadline still paints overdue exactly as it does without the key; nothing is stored or enforced differently. No `authorWarn`: the gap is a display refinement with a named, blocked consumer card, and the authoring half (parse, scope, the `dueLike` pairing) is already enforced. Flip to `live` on the objectui#11815 landing, citing its host helper." + "status": "live", + "verifiedAt": "2026-10-27", + "evidenceScope": "in-repo", + "evidence": "ui/packages/core/src/evaluator/deadlineSettled.ts#isDeadlineSettled (the one host helper: `true` only when the field declares `dueLike: true` and a `settledWhen` that holds for the row, evaluated through `evalRowPredicate` with `record` bound; a predicate that fails to evaluate answers not settled); ui/packages/fields/src/index.tsx#resolveDueLike (the date and datetime cells take the host's verdict as `deadlineSettled`, and a settled deadline paints as a non-deadline: no overdue wording, no overdue colour); ui/packages/plugin-grid/src/objectGridGenerateColumns.tsx#useObjectGridGenerateColumns (the grid asks the helper per row and hands each date cell its answer); ui/packages/app-shell/src/views/approval-decision/RecordSnapshotSummary.tsx#FieldFace (the approval snapshot judges the predicate against the snapshot's own values)", + "producer": "ui/packages/plugin-grid/src/objectGridGenerateColumns.tsx#useObjectGridGenerateColumns copies the object definition's `settledWhen` onto the column's field metadata beside `dueLike` on each of its three column paths; ui/packages/plugin-detail/src/fieldEnrichment.ts#ENRICHED_FIELD_METADATA_KEYS copies it onto a detail-view field; ui/packages/plugin-grid/src/objectGridRowLoad.ts#useObjectGridRowLoad and ui/packages/plugin-list/src/ListView.tsx#ListView fetch the predicate's operands for a shown deadline, so a list that does not show the column the predicate reads still judges the row", + "note": "Deadline settle predicate, `date` / `datetime` with `dueLike: true` only (the parse door refuses it off type or without `dueLike`; lint's field-rule pass judges the CEL at `os build`, `os validate` and the object save door, the way it judges `visibleWhen`). LIVE in the console since objectui#11815 landed: while the predicate holds for a record, that record's deadline shows no overdue wording and no overdue colour. Readers beyond the four in the evidence ask the same helper per row and hand its answer to the same cell: the record detail sections (plugin-detail DetailSection, HeaderHighlight and RelatedList), the kanban card (plugin-kanban ObjectKanban), the gallery and compact list (plugin-list ObjectGallery, ListViewCompact) and the tree (plugin-tree ObjectTree); each surface is pinned by its own `deadlineSettled-11815` test under ui/packages. CORRECTED 2026-10-27 from `planned`: the row was written ahead of that landing and not flipped when it came. Display only: nothing on the write path reads it. No `authorWarn` was ever set, so none is dropped." }, "conditionalFormatting": { "status": "planned", diff --git a/packages/spec/liveness/state-counts/field.md b/packages/spec/liveness/state-counts/field.md index 13d4cfe2aa4..311575be0a5 100644 --- a/packages/spec/liveness/state-counts/field.md +++ b/packages/spec/liveness/state-counts/field.md @@ -12,4 +12,4 @@ committed anywhere: `check:liveness` sums the shards when it reads them. | Type | live | exp | elsewhere | dead | planned | classified | |---|---|---|---|---|---|---| -| `field` | 99 | 0 | 0 | 1 | 6 | 106 | +| `field` | 100 | 0 | 0 | 1 | 5 | 106 | From 39caaea39c7e9f52992ca1266f43ef0713b05fb9 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 27 Oct 2026 21:18:22 +0000 Subject: [PATCH 2/2] test(dogfood): name the settledWhen fault warning by its real route isDeadlineSettled passes no warnOnError, so evalRowPredicate hands the predicate to evalFieldPredicate, and a fault is warned by warnPredicateFailure, not warnEvalError. Spell the chain through evalFieldPredicate in the cel-field-deadline-settled enforcement cell. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01WGufzgGEj5Qdn1KEGPUrU8 --- packages/qa/dogfood/test/expression-conformance.ledger.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/qa/dogfood/test/expression-conformance.ledger.ts b/packages/qa/dogfood/test/expression-conformance.ledger.ts index c98dc888c5c..b628807efe6 100644 --- a/packages/qa/dogfood/test/expression-conformance.ledger.ts +++ b/packages/qa/dogfood/test/expression-conformance.ledger.ts @@ -227,7 +227,7 @@ export const EXPRESSION_SURFACE: ExprSurface[] = [ summary: "a date / datetime deadline's settle predicate (Field.settledWhen) — display only", dialect: 'cel', mode: 'interpret', state: 'enforced', failPolicy: 'fail-soft-log', enforcement: - 'console (ui/) isDeadlineSettled (core/evaluator/deadlineSettled.ts) → evalRowPredicate → @objectstack/formula celEngine (interpret), evaluated per record with the row bound as `record`, by every host that draws a date cell beside a row (grid, list, gallery, kanban, tree, the record detail sections and the approval snapshot); the verdict reaches the date / datetime cell as `deadlineSettled`, and a settled deadline paints no overdue wording and no overdue colour. A predicate that faults is logged once (warnEvalError) and answers NOT settled, so the deadline shows as it does without the key. Display only: nothing on the write path reads it. Judged at authoring by lint\'s field-rule pass (`validate-expressions.ts`) as `visibleWhen` is', + 'console (ui/) isDeadlineSettled (core/evaluator/deadlineSettled.ts) → evalRowPredicate → evalFieldPredicate (core/evaluator/fieldRules.ts) → @objectstack/formula celEngine (interpret), evaluated per record with the row bound as `record`, by every host that draws a date cell beside a row (grid, list, gallery, kanban, tree, the record detail sections and the approval snapshot); the verdict reaches the date / datetime cell as `deadlineSettled`, and a settled deadline paints no overdue wording and no overdue colour. A predicate that faults is logged once per predicate text (warnPredicateFailure, fieldRules.ts: no `warnOnError` is passed, so evalRowPredicate takes the evalFieldPredicate route) and answers NOT settled, so the deadline shows as it does without the key. Display only: nothing on the write path reads it. Judged at authoring by lint\'s field-rule pass (`validate-expressions.ts`) as `visibleWhen` is', covers: ['data/field.zod.ts:FieldSchema.settledWhen'], }, {