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
2 changes: 1 addition & 1 deletion packages/spec/liveness/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -805,7 +805,7 @@ marker where the Notes cell goes, never a guess at what belongs there.
| datasource | seeded 2026-08-01 (#4487) — the **highest dead ratio of any governed type** (20 of 43), and it was ungoverned until now, which is not a coincidence: #4410/#4465/#4481 found six inert keys here by hand, two security-shaped (`schemaMode` left an external DB constructible as `managed` with DDL ungated; `ssl` configured nothing while looking configured). Dead set = `capabilities.*` (all 11 — the engine gates pushdown on the runtime driver's `supports.*` object, a non-overlapping vocabulary), `healthCheck.*` (3 — nothing schedules a datasource probe; the 20 `healthCheck` hits in the repo all belong to the PLUGIN health monitor and other surfaces), `retryPolicy.*` (4 — `retryPolicy` IS enforced on `hook` and `job`, which is what makes this one read alive; the shapes differ), `external.label`, `external.requirePermission`. **`capabilities.readOnly` is the one to know**: it reads as a safety switch, gates nothing, and two shipped prescriptions pointed authors at it until #4487 — `external.allowWrites: false` is the enforced write gate. `config` is a `z.record`, so its per-driver keys sit outside the walk (recorded in the entry's note, not silently skipped) **批 A CLOSED 2026-08-02 (#4583)**: the `capabilities` block — 11 flags, every one dead and authorWarn'd — was REMOVED rather than bridged; pushdown comes from the runtime driver's own `supports.*`, so there was nothing to connect it to. Its rows are deleted (strict-removal route), which is why dead falls 20 → 9. `readOnly` was the reason the audit was worth doing: it read as a safety switch, gated nothing, and had already been MOVED twice toward somewhere it might be enforced (#4410, #4465) — the shipped CRM example called a datasource a read replica on the strength of it while the datasource took writes. Removing it does NOT hand the author a working alternative: `external.allowWrites` only gates FEDERATED datasources, so a managed one has no read-only gate at all (#4584). Remaining 9 = healthCheck ×3 + retryPolicy ×4 + external ×2, batches B/C/D of #4583 **BATCHES B/C/D CLOSED 2026-08-02 — datasource now has ZERO dead properties**, down from the 20 it was seeded with (the highest dead ratio of any governed type). `retryPolicy` ×4 and `healthCheck` ×3 went as whole blocks, `external.label` / `external.requirePermission` as keys. None was bridgeable: each already had a different LIVE mechanism doing the job — the boot policy, the driver handle's on-demand `ping()`/`checkHealth()`, the top-level `label`, and ordinary permission sets + RLS. The `retryPolicy` rejection deliberately refuses to offer a rename: `hook`/`job` retryPolicy ARE enforced but spell the delay `backoffMs`, and that inconsistency is itself the evidence nothing read the datasource one (#4488's sharpest trap) |
| webhook | **not a registered metadata type** — governed via the gate's spec-only schema override (`SPEC_ONLY_SCHEMAS`), not `getMetadataTypeSchema`; folding it onto the registry is the #3490 reassessment. This row once read 0/1/16 ("the ENTIRE authoring surface is dead", #3461) and both halves of that were CLOSED same-quarter: #3489 built the materializer bridge (authored `webhooks:` entries now land as `sys_webhook` dispatcher rows) and #3494 pruned the aspirational props outright — so the surviving surface is fully live. Kept in the table as the worked example that a dead verdict is a worklist entry, not a tombstone: enforce-or-remove resolved this one by ENFORCING |
| app | seeded 2026-08-01 (#4488). Dead 9 = the seven #4142 `retiredKey` tombstones (version/aria/objects/apis/sharing/embed/mobileNavigation — rows stay while the tombstones hold the keys in the walked shape) + `homePageId` (#4667 tombstone — the landing IS the first nav item; root landing follows `isDefault` routing) + `areas.description` (benign, docs-shaped, kept and not warned). RETIRED 17.0.0 (#4509, rows deleted — the selector schema is strict): selector `includeAll` (deliberately DISOBEYED, not merely unread — selectors are mandatory-scope and an "All" row would clear the scope, leaking system metadata through Studio's package filter; STUDIO_APP authored it against a renderer that ignored it) and `placement` (no renderer read it; "topbar" placed nothing). Nav walk covers the union's `object` variant; other variants hand-verified live, and the `actionDef` dispatch gap closed in #4509 **#4651**: the **fail-open area gates** `areas.visible` / `areas.requiredPermissions` — this ledger's most important app finding — are REMOVED, rows DELETED (strict removal; retained rows would report ORPHAN). They were not merely unread: `filterAppForUser` never reads `item.areas` at all and the shell renders every area, so a "hidden" or permission-gated area was served to everyone, while the identically named per-ITEM and per-APP keys ARE enforced. Route B (remove) over route A (enforce): enforcing needs semantics decided first (does filtering an area remove its items everywhere? does the server bind `user` for area CEL?), which the 17.0.0 window could not hold. Boundary unchanged and still recorded on `areas.navigation`: per-item gating inside an area is shell-side only. **#4667**: `homePageId` TOMBSTONED (row stays — retiredKey keeps it in the walked shape) and `areas.order` row DELETED (strict removal); `areas.order` read alive because the per-ITEM `order` really is sorted (NavigationRenderer.tsx:1154) while no renderer ever sorted areas. **The dead nine above still reconcile exactly** — nothing has left or joined that set since #4667; what moved after it are two additions on the other side of the ledger. **#4829** (PR #6942) declares `_unpublished`, `live`: a MACHINE-MANAGED publish gate, never authored, written by the AI additive-materialization path and cleared by `POST /packages/:id/publish-drafts`, and server-enforced in `filterAppForUser`. It is declared on AppSchema rather than omitted because the write path validates against that schema, so the flip itself would otherwise be unwritable — a `live` row for a key no author may set, which is the inverse of every dead row here. **#4848** (PR #7253) gives this type its **first `planned`**: `navigation.runAction`, the contract-first half of the SDUI deep-link promotion. `planned` and not `dead` is load-bearing, the same distinction `api`'s two mapping transforms draw — the reference IS validated at authoring (framework `validateCrossReferences`, the lint's nav `runAction` arm), and it carries `authorWarn` saying the shell does not read the declared slot yet; auto-run still fires only via the transitional `?runAction=` query param. Flip to `live` with a NavigationRenderer pointer when the objectui half lands. |
| book | seeded 2026-08-01 (#4488). ADR-0046 §6 spine; `audience` is ENFORCED and fail-closed (tree 401/403 + per-doc effective-audience union on both list and tree). Dead 2 = BOTH inline `translations` maps (book-level and per-group): no resolver reads them and the bundle translator doesn't cover `book` — the trap is that `doc.translations` two files over works on every read path. Also recorded: the `include: { tag }` rule variant can never match (DocSchema declares no `tags`) **#4667**: both inline translation maps retired — book-level row DELETED (BookSchema is strictObject), group-level row KEPT as a tombstone (BookGroupSchema is a plain z.object with no .strict(), so a bare delete would have zod silently strip it). No resolver read either; the trap was proximity to `doc.translations`, which is live on every doc render path. |
| book | seeded 2026-08-01 (#4488). ADR-0046 §6 spine; `audience` is ENFORCED and fail-closed (tree 401/403 + per-doc effective-audience union on both list and tree). Dead 2 = BOTH inline `translations` maps (book-level and per-group): no resolver reads them and the bundle translator doesn't cover `book` — the trap is that `doc.translations` two files over works on every read path. Also recorded: the `include: { tag }` rule variant is live — `tags` was DECLARED on DocSchema in 17.0.0 (#4509, ADR-0049); see the `doc` row below for the fix's full history. **#4667**: both inline translation maps retired — book-level row DELETED (BookSchema is strictObject), group-level row KEPT as a tombstone (BookGroupSchema is a plain z.object with no .strict(), so a bare delete would have zod silently strip it). No resolver read either; the trap was proximity to `doc.translations`, which is live on every doc render path. |
| doc | seeded 2026-08-01 (#4488). Fully live: the kernel stores `content` unparsed, but the REST read layer localizes (resolveDocLocale), audience-gates, list-strips `content`, and the book resolver consumes name/label/description/order/group — plus the objectui console portal renders it all. The schema's own "docs are inert data" header describes the kernel, not the type. **`tags` DECLARED in 17.0.0 (#4509)** — the enforce half of enforce-or-remove: the book resolver's `include: { tag }` matcher, the REST transport and `ResolverDoc.tags` all already existed, but DocSchema is strict and had no `tags` key, so authoring one was a parse error and the variant could never match. Live on arrival |
| email_template | this row read 8/–/13/– for one day (seeded 2026-08-01, #4488: "every authorable property is dead", the webhook shape on AUTH mail) and #4509 CLOSED it by ENFORCING — the second worked example, after `webhook`, that a dead verdict is a worklist entry rather than a tombstone. `bootstrapDeclaredEmailTemplates` materializes declared items into the `sys_email_template` rows `sendTemplate` reads, sharing `mapTemplateToRow` with the built-in seeder so the two doors cannot drift, and re-materializes on live metadata writes (`email_template` is `allowRuntimeCreate: true`, so boot-only would have left Studio saves inert). Three breaks had to close, not one: the engine never registered `emailTemplates:` into the registry, built-in seeds masqueraded as `managed_by: admin` and outranked declared templates, and nothing materialized. ADR-0054 proof bound on `subject` (`email-template-materialization`) |
| job | seeded 2026-08-01 (#4488). The file-authored path is fully enforced: all three schedule shapes honored by the adapters, `retryPolicy`/`timeout` enforced since #3494 (this is the retryPolicy the datasource ledger warns about confusing with its dead namesake), `enabled: false` skips scheduling. Dead 3 = `id` (authorWarn — `name` is the identity everywhere) + label/description (docs-kept). The type-level gap CLOSED 2026-08-02 (#4509) by closing the door rather than bridging it: `handler` names a function in the compiled bundle's function table, which a runtime writer cannot name, so `allowRuntimeCreate` **and** `allowOrgOverride` are now false and `*.job.ts` / `defineStack({ jobs })` are the supported doors. The kind stays registered — its file loader is genuinely consumed (ADR-0088 admission test) **#4667**: `id` REMOVED (row deleted, strict removal) — nothing read it and its own describe() ("defaults to `name` when omitted") advertised an identity override that never existed; `name` is the scheduling key, the sys_job row key and the JobExecution.jobId stamp, so two jobs differing only in `id` were one job. **#7131** (PR #7425) takes the remaining two: `label` and `description` re-grade `dead` → `live` under the 2026-08-10 maintainer ruling that **designer previews count as consumers** — objectui's `JobPreview` had been reading `d.label` and `d.description` and rendering them as the preview card's title and subtitle the whole time, so the old "no runtime consumer" was a true statement about the *scheduler* and a false one about the system. **This row now has zero dead and the ADR-0033 exemption is still in force**, which is worth saying out loud because it is the first row in this table where those two facts hold together: the keys are still docs-shaped, still deliberately KEPT, still not `authorWarn`'d, and enforce-or-remove still has nothing to chase here. What changed is only that the exemption no longer has to carry the verdict — the measurement does. |
Expand Down
4 changes: 2 additions & 2 deletions packages/spec/liveness/book.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -72,9 +72,9 @@
},
"include": {
"status": "live",
"verifiedAt": "2026-08-01",
"verifiedAt": "2026-08-17",
"evidence": "packages/spec/src/system/book.zod.ts:236, packages/spec/src/system/book.zod.ts:193",
"note": "the derived-membership rule — the heart of the §6.2.1 design. CAVEAT, recorded not hidden: only the GLOB form can match today. The `{ tag }` variant is declared and the resolver implements it (matchesInclude reads doc.tags), but DocSchema declares no `tags` property, so the corpus always carries `tags: undefined` and a tag rule matches nothing. Either add `doc.tags` or retire the variant (enforce-or-remove)."
"note": "the derived-membership rule — the heart of the §6.2.1 design. Both forms are live: the GLOB variant matches on name/group, and the `{ tag }` variant (matchesInclude reads doc.tags) started matching the moment DocSchema DECLARED `tags` in 17.0.0 (#4509, ADR-0049) — see doc.json's `tags` entry for the fix's full history."
},
"package": {
"status": "live",
Expand Down
Loading