Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .changeset/20299-display-annotations-ledger.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
"@objectstack/spec": patch
---

Liveness ledger: `flow.description`, `hook.label` and `hook.description` are now `live`, not `dead`. Studio already shows all three to a human. Ledger data, one README cell per type and one gate test fixture only. ⛔ No schema, parse, `.describe()` or accept-set change.

The ledgers ship inside this package (`files[]` includes `liveness`), and `@objectstack/lint` reads them to decide which authored keys draw an advisory warning. None of the three rows sets `authorWarn`, so the set of warnings does not change.

- **What shows them.** These are display keys, so under the ledger's "Designer previews count as consumers" ruling, being shown to a human is the whole of their claimed effect. Neither `flow` nor `hook` registers its own list columns in the Studio metadata admin, so the Studio metadata list page falls back to its default columns: name, `label` and `description`. The Studio metadata quick-find indexes and shows every item's `label` and `description` too. Each row cites that reader at the `.objectui-sha` pin `dd3f7e1be`.
- **Where the values come from.** Each row names its producer: the Studio route that mounts the list page, the metadata client's `GET /api/v1/meta/:type` read, and this repo's shared list answer (`createMetaListAnswer`), which adds no projection for either type that would drop the keys. A booted read of the showcase app confirms it: `GET /api/v1/meta/flow` served 30 flows and `GET /api/v1/meta/hook` served 4 hooks, each with its authored `label` and `description` and the showcase's project-scoped package id.
- **Still kept, still not warned.** The re-grade reverses no ADR-0033 decision. All three rows stay docs-shaped annotation, deliberately kept and exempt from enforce-or-remove. Each row keeps the note it carried while `dead`, as history.
- The regenerated liveness counts are the `liveness/state-counts/flow.md` and `liveness/state-counts/hook.md` shards. `flow` has 35 live and 5 dead (was 34 and 6). `hook` has 21 live and 1 dead (was 19 and 3). The README's `flow` and `hook` Notes cells no longer list these keys as dead. The liveness gate test that borrowed `flow.description` as its sample `dead` row now uses the `flow.active` tombstone, which the gate holds at `dead`.
4 changes: 2 additions & 2 deletions packages/spec/liveness/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -908,9 +908,9 @@ marker where the Notes cell goes, never a guess at what belongs there.
|---|---|
| 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 |
| 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 |
| 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 |
| 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. **#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. **#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 dead but KEPT deliberately (2026-07-30 sweep) — docs-shaped annotation fields, exempt from enforce-or-remove |
| 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 |
| permission | CRUD/FLS/RLS live; dead `contextVariables` REMOVED (ADR-0105 D11 — RLS resolves only the `current_user.*` built-ins plus runtime-staged `rlsMembership` sets). 2026-07-30 security-subset re-verification (all 33 entries `verifiedAt`-stamped): `rowLevelSecurity.enabled` was live-with-wrong-evidence and UNREAD — a disabled policy kept contributing its OR-branch grant; ENFORCED same day in rls-compiler (`getApplicablePolicies`), the `positions` ADR-0049 resolution repeated. `rowLevelSecurity.priority` CORRECTED to dead+authorWarn — semantically void under OR-combination (no conflict exists to order), a REMOVE candidate. `rls.label`/`description`/`tags` CORRECTED to dead (benign display, no consumer in either repo). `tabPermissions` was UNDERSTATED ("only hidden read" → the rank merge reads all four values; me-apps dogfood test exercises it). `allowExport` re-verified TRUE end-to-end (server-side 403 gate, not just the /me projection). `objects.allowRestore`/`allowPurge` REMOVED 2026-08-26 (#12497, ADR-0049 — the `restore`/`purge` ops never existed; the 2026-07-30 'live' verdict cited only the evaluator pre-mapping, retired in the same batch; `retiredKey` tombstones, keys return with M2 per the #1883 ruling). `rowLevelSecurity.tags` REMOVED 2026-09-27 (#20321, ADR-0049 — graded RETIRE by the maintainer's criterion: no mainstream platform tags a row-level policy; a `retiredKey` tombstone, so the row stays `dead` beside `priority`'s) |
| position | (role's ADR-0090 successor) fully live; all 4 `verifiedAt`-stamped 2026-07-30 |
| agent | dead `tenantId` + `planning.strategy`/`allowReplan` REMOVED (#2377); autonomy tier experimental; `knowledge` REMOVED 2026-07-30 (#3896 close-out sweep — declaring sources never scoped retrieval; AIKnowledgeSchema removed with it, the topics→sources rename absorbed pre-release); **#18304** re-classifies `tools` `live` -> `dead` with no key added or removed — the row asserted `live` on a key `agent.zod.ts` had tombstoned in protocol 17 (#3894), and it sat that way from the 2026-06 audit because its citation was EXEMPT from resolution rather than resolved (`packages/services/service-ai/...` matched `FOREIGN_PATH_PREFIXES`; the `cloud` realm marker that replaced it in #13309 is equally unresolvable, so no gate could ever fail on it). The load-bearing evidence is local and re-measurable — the `retiredKey` tombstone plus the `agent-tools-to-skills` strip cover authored and stored input respectively, so nothing can carry a value for any consumer to read; the cloud zero-consumer census (cloud @cb8ee7ff, #13272, 2026-09-15) is attributed, not re-taken. `live-elsewhere` is refused for want of a foreign enforcer, not left undeclared |
Expand Down
9 changes: 6 additions & 3 deletions packages/spec/liveness/flow.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@
"note": "display."
},
"description": {
"status": "dead",
"evidence": "no reader either layer",
"note": "KEPT deliberately (2026-07-30 sweep): docs-shaped, not capability-shaped — an annotation field documents intent for the next reader (per ADR-0033, often a model) even without a runtime consumer. Exempt from enforce-or-remove; do not re-litigate. PREVIEW LOOKUP RECORDED 2026-08-10 (#7427): 'no reader either layer' is a cross-repo absence claim, so the previews ruling (#7131; README, 'Designer previews count as consumers') was applied to it mechanically. At objectui @e9ab52f9 FlowPreview IS registered (previews/index.ts:58) and reachable (ResourceEditPage.tsx:949), and it does NOT read the flow description — the only `description` token in the file is a TypeScript interface member at FlowPreview.tsx:64, not a draft read; the preview keys off `d.name`, the node graph and the edges. The claim survives the look; the verdict is unchanged and this note re-litigates nothing."
"status": "live",
"verifiedAt": "2026-09-29",
"evidenceScope": "cross-repo",
"evidence": "objectui @dd3f7e1be: packages/app-shell/src/views/metadata-admin/ResourceListPage.tsx#defaultColumns (the Studio metadata list's default `description` column: `flow` registers no `listColumns`, so `DefaultMetadataList` takes these defaults and draws each row's `description` through `defaultCell`); objectui @dd3f7e1be: packages/app-shell/src/views/metadata-admin/QuickFind.tsx#MetadataQuickFind (the Studio metadata quick-find indexes every item's `description` off the same list read and draws it under the item's name)",
"producer": "objectui @dd3f7e1be: packages/app-shell/src/console/AppContent.tsx#AppContent (mounts `MetadataResourceListPage` on the Studio route `metadata/:type`, which the metadata directory tile and the quick-find both navigate to); objectui @dd3f7e1be: packages/app-shell/src/views/metadata-admin/ResourceListPage.tsx#MetadataResourceListPage (renders a registered custom `ListPage` and otherwise `DefaultMetadataList`); objectui @dd3f7e1be: packages/app-shell/src/views/metadata-admin/anchors.ts#registerBuiltinAnchors (the only `flow` registration, which sets no `ListPage` and no `listColumns`); objectui @dd3f7e1be: packages/data-objectstack/src/metadata-client.ts#MetadataClient (`list('flow')` reads `GET /api/v1/meta/flow` and takes its `items`); framework: packages/rest/src/meta-item-read-gate.ts#createMetaListAnswer (the one list answer both transports serve: after the per-caller gate it runs no projection for `flow` except the translation step, so the authored `description` reaches the body)",
"note": "RE-GRADED dead → live 2026-09-29 (#20299) under the #7131 previews ruling (README, 'Designer previews count as consumers'): for a display key, being shown to a human is the whole of the claimed effect. The lane's call on #20299 (claim 5880838693) counts a Studio list column and the metadata quick-find as that showing. The superseded evidence, 'no reader either layer', was answered by the preview lookup below, which looked at previews only. READER, read at the `.objectui-sha` pin dd3f7e1be: `MetadataResourceListPage` falls through to `DefaultMetadataList` because no registration gives `flow` a `ListPage`, and that page takes `config.listColumns ?? defaultColumns(...)`, whose columns are the primary key, `label` and `description`. Each cited string counts the same at objectui main 5d689c3. PRODUCER, measured booted rather than read: a throwaway `@objectstack/verify` boot of the real examples/app-showcase composition, signed in as the dev admin, answered `GET /api/v1/meta/flow` 200 with a top-level `items` array of 30 flows, and all 30 carried their authored `label` and `description` with `_packageId: com.example.showcase`. `GET /api/v1/meta/package` listed that package with `scope: project`, which is what the list page's package scope admits. UNCHANGED by the re-grade: still docs-shaped annotation, deliberately KEPT (2026-07-30 sweep, ADR-0033) and not authorWarn'd. `live` here does not mean the automation engine acquired a use for it. HISTORY, kept as history — the note this row carried while `dead`: KEPT deliberately (2026-07-30 sweep): docs-shaped, not capability-shaped — an annotation field documents intent for the next reader (per ADR-0033, often a model) even without a runtime consumer. Exempt from enforce-or-remove; do not re-litigate. PREVIEW LOOKUP RECORDED 2026-08-10 (#7427): 'no reader either layer' is a cross-repo absence claim, so the previews ruling (#7131; README, 'Designer previews count as consumers') was applied to it mechanically. At objectui @e9ab52f9 FlowPreview IS registered (previews/index.ts:58) and reachable (ResourceEditPage.tsx:949), and it does NOT read the flow description — the only `description` token in the file is a TypeScript interface member at FlowPreview.tsx:64, not a draft read; the preview keys off `d.name`, the node graph and the edges. The claim survives the look; the verdict is unchanged and this note re-litigates nothing."
},
"version": {
"status": "live",
Expand Down
Loading
Loading