You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(spec)!: tombstone the seven dead AppSchema authoring keys (#4001 app step, PR A) (#4142)
The 2026-06 AppSchema liveness audit verdicted seven authorable keys DEAD —
never read by any consumer in framework or objectui. Authoring them shipped
config the author believed was in effect; sharing/embed were the dangerous
case: a declared public-access surface no route enforced (ADR-0049 class,
the live path is FormView.sharing). mobileNavigation was a mode picker that
changed nothing (even packages/mobile ignored it).
All seven are retiredKey() tombstones — tsc-level `never` for typed authors,
a parse-time prescription for everyone else. NOT a bare deletion: AppSchema
is not yet .strict(), so deleting the keys would have reintroduced the exact
silent-strip failure mode this campaign eliminates.
Wired through the full ADR-0087 removal machinery (the gen:schema tombstone
gate enforces this): the app-dead-authoring-keys-removed D2 conversion (a
pure lossless delete) joins the protocol-17 chain step, so `os migrate meta`
rewrites authored sources and spec-changes.json / the upgrade guide document
the removal.
Also rewrites the app.test.ts acceptance tests that codified the strip-era
fiction, and swaps two stack.test.ts fixtures that used App.objects as a
map-format filler key.
This clears the enforce-or-remove precondition for PR B (AppSchema +
navigation-union .strict() via z.discriminatedUnion).
Verified: spec 7034 tests + tsc clean; all 12 check gates (including the
migrations/conversions fixture replay); dogfood 73 files / 425 tests;
showcase / crm / todo validate clean.
Copy file name to clipboardExpand all lines: content/docs/references/ui/app.mdx
+7-7Lines changed: 7 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -73,7 +73,7 @@ const result = ActionNavItem.parse(data);
73
73
| :--- | :--- | :--- | :--- |
74
74
|**name**|`string`| ✅ | App unique machine name (lowercase snake_case) |
75
75
|**label**|`string`| ✅ | App display label |
76
-
|**version**|`string`| optional | Appversion |
76
+
|**version**|`any`| optional |[REMOVED]`App.version` was removed in @objectstack/spec 17.0.0 (2026-06 liveness audit — no consumer in framework or objectui). An app is versioned by its owning package: use `manifest.version`. Delete the key.|
@@ -85,13 +85,13 @@ const result = ActionNavItem.parse(data);
85
85
|**contextSelectors**|`{ id: string; label: string; icon?: string; optionsSource: object; … }[]`| optional | App-level scope dropdowns whose value is injected into nav items as `{<id>}` template vars |
86
86
|**homePageId**|`string`| optional | ID of the navigation item to serve as landing page |
87
87
|**requiredPermissions**|`string[]`| optional | Permissions required to access this app |
88
-
|**objects**|`any[]`| optional | Objects belonging to this app |
89
-
|**apis**|`any[]`| optional |Custom APIs belonging to this app |
|**objects**|`any`| optional |[REMOVED]`App.objects` was removed in @objectstack/spec 17.0.0 (2026-06 liveness audit — never read; the spec itself labelled it "config file convenience"). Objects belong to the stack (`defineStack({ objects })`); an app reaches them through its navigation items. Delete the key.|
89
+
|**apis**|`any`| optional |[REMOVED]`App.apis` was removed in @objectstack/spec 17.0.0 (2026-06 liveness audit — never read). Declarative endpoints belong to the stack (`defineStack({ apis })`), not the app shell. Delete the key.|
90
+
|**sharing**|`any`| optional |[REMOVED]`App.sharing` was removed in @objectstack/spec 17.0.0 (2026-06 liveness audit / ADR-0049 enforce-or-remove) — no public-app route ever read it, so it declared sharing that did not exist. Public access is granted per FORM VIEW (`FormView.sharing`, the public-data-collection surface). Delete the key.|
91
+
|**embed**|`any`| optional |[REMOVED]`App.embed` was removed in @objectstack/spec 17.0.0 (2026-06 liveness audit / ADR-0049) — no iframe route ever read it. Embedding is a per-form-view surface (`FormView.sharing`), not an app-level switch. Delete the key.|
92
+
|**mobileNavigation**|`any`| optional |[REMOVED]`App.mobileNavigation` was removed in @objectstack/spec 17.0.0 (2026-06 liveness audit — fully unimplemented; no renderer, including packages/mobile, ever read it). Delete the key; the block returns if/when a real mobile navigation ships.|
93
93
|**defaultAgent**|`string`| optional | Platform agent bound to this app's ambient chat ('ask' is the implicit default; 'build' for authoring surfaces) — ADR-0063 §1 |
94
-
|**aria**|`{ ariaLabel?: string; ariaDescribedBy?: string; role?: string }`| optional | ARIA accessibility attributes for the application|
94
+
|**aria**|`any`| optional |[REMOVED]`App.aria` was removed in @objectstack/spec 17.0.0 (2026-06 liveness audit — no renderer read app-level ARIA attributes). Declare `aria` on the component/widget that renders the DOM node instead. Delete the key.|
Copy file name to clipboardExpand all lines: docs/protocol-upgrade-guide.md
+3Lines changed: 3 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -128,6 +128,8 @@ And it removes the RLS-policy key `priority` (#3896 security audit): promised "c
128
128
129
129
The same close-out retires the four inert tool authoring keys (`category`, `permissions`, `active`, `builtIn`): none is part of AIToolDefinition and no execution path read them. Two were misleading in the dangerous direction — `permissions` promised an invocation gate nothing enforced, and `active: false` read as "withdrawn" while the tool kept reaching the LLM tool set. Lossless deletes; the strict ToolSchema rejects each with its prescription.
130
130
131
+
The AppSchema sheds its seven dead authoring keys (2026-06 liveness audit, #4001 app step): `version` (apps are versioned by manifest.version), `aria`, `objects`/`apis` (the self-described "config file convenience" — nothing read them; the chatbot derives an app's objects from its nav items), `sharing`/`embed` (a declared-but-unenforced public surface — the only live path is FormView.sharing; ADR-0049), and `mobileNavigation` (fully unimplemented). Pure lossless deletes — none ever had a runtime effect; each key is tombstoned with its prescription.
132
+
131
133
ADR-0113 splits the `required` tri-binding: post-17, `required` is ONLY the write-time contract (insert must provide; update may not null out; legacy null rows rest), and the physical NOT NULL is the explicit `storage.notNull`. The `field-required-notnull-explicit` conversion preserves every pre-17 source verbatim-in-meaning by stamping `storage.notNull: true` onto each required field — under the old semantics that column WAS created NOT NULL, so the rewrite writes down what the text already meant. Migration-chain-only (retired from the load path): this is a default flip, not a rename, and a loader that auto-applied it would stamp the constraint onto 17-authored sources that deliberately omit it.
132
134
133
135
On the wire contract it also retires the `/analytics/query` request ENVELOPE (#3878): `AnalyticsQueryRequestSchema` used to describe `{ cube, query: {...}, format }` — the dialect of the retired degraded analytics shim (#3891) that the real engine never understood (an envelope body inferred a column-less cube and died as an SQL syntax error). The canonical request body is now the BARE AnalyticsQuery — `cube` + `measures` at the top level — which is what every real caller already sends; the schema tombstones `query`/`format`, and the dispatcher entry validates bodies and answers 400 with the prescription. No stored metadata carries this shape (it was HTTP-only), so the change is two semantic TODOs for API callers rather than a stack conversion.
@@ -147,6 +149,7 @@ The close-out sweep finishes the enforce-or-remove worklist across the remaining
147
149
|`flow-node-script-config-aliases`|`flow.node.script.config`| script flow-node config keys 'functionName' → 'function', 'input' → 'inputs' (#3796) | live — protocol 17 loader accepts the old shape |
148
150
|`permission-rls-priority-removed`|`permission.rowLevelSecurity.priority`| RLS-policy key 'priority' removed (#3896 audit — policies OR-combine, so the promised conflict-resolution semantics cannot exist; dropping it changes no outcome) | retired — `migrate meta` only |
|`app-dead-authoring-keys-removed`|`app.version / app.aria / app.objects / app.apis / app.sharing / app.embed / app.mobileNavigation`| app keys 'version'/'aria'/'objects'/'apis'/'sharing'/'embed'/'mobileNavigation' removed (2026-06 liveness audit — never read; sharing/embed declared a public surface no route enforced, mobileNavigation was fully unimplemented) | retired — `migrate meta` only |
150
153
|`field-required-notnull-explicit`|`object.fields.*.required / object.fields.*.storage.notNull`| required fields gain explicit 'storage.notNull: true' (ADR-0113 — pre-17 'required' implied the column constraint; post-17 it is only the write contract) | retired — `migrate meta` only |
151
154
|`action-inert-keys-removed`|`action.shortcut / action.bulkEnabled`| action keys 'shortcut'/'bulkEnabled' removed (#3896 close-out — no keydown path dispatches shortcuts; the multi-select toolbar reads the view's bulkActions) | retired — `migrate meta` only |
152
155
|`flow-inert-keys-removed`|`flow.active / flow.template / flow.nodes[].outputSchema / flow.errorHandling.fallbackNodeId`| flow keys 'active'/'template', node 'outputSchema' and errorHandling 'fallbackNodeId' removed (#3896 close-out — active:false never stopped a flow; status is the enforced lifecycle) | retired — `migrate meta` only |
0 commit comments