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
The helper bug first, because this campaign put it there. `skill` closed last
batch while still carrying `retiredKey` tombstones, and `strictObject` built its
"did you mean" candidates from the whole shape — tombstones included. So a
`triggerPhrase` typo was answered with "Did you mean `triggerPhrases`?", a key
that had been REMOVED, and the author who complied got a second rejection
telling them to delete what they had just been told to write.
Third occurrence of a shape the ledger already records twice, and the first one
in a SHARED helper, where it would have reached every conversion after it. Both
helpers were correct alone; only the combination was wrong. Fixed structurally
rather than by special-casing tombstones: never suggest a key the schema cannot
accept. They stay complementary — `retiredKey` is stronger than `guidance`,
since typing the key as `never` also fails `tsc` through a variable, where
excess-property checking would not fire.
`agent` had two security-shaped removals with no tombstone. `visibility` and
`tenantId` were deleted as unenforced security properties — correctly — but
without a prescription, because the shape was `.strip` and there was no
rejection to hang one on. An author who wrote `visibility: 'private'` believed
the agent was hidden; it was listed to everyone, and always had been. The
`skill.permissions` class again. Both now name what actually gates an agent.
`route` on a page was a fiction the platform's own suite carried. `stack.test.ts`
authored `route: '/landing'` for years; `PageSchema` never declared it, and a
page is routed by its `name` — which in the map format under test IS the map
key, which the test asserted six lines below the key contradicting it. Fifth
test found codifying a strip-era fiction, and the likeliest to be reinvented.
`mapping` and `page` gain their ADR-0010 envelope. The undeclared-envelope debt
list is down to two, from eight.
The headline number is now derived instead of tallied — it had already drifted
by one, in a campaign whose recurring finding is that hand-maintained coverage
measurements go stale. `STILL_STRIP` carries a reverse pin like the envelope
list; verified it goes red in both directions before being trusted.
Registered types closed: 21 of 25. Still open: action, dashboard, field, view.
The warning layer's covered roots drop 6 → 3, verified as a hand-off rather than
a hole: `agent.zzz`, `page.zzz` and nested `page.regions[0].zzz` are each now
rejected by the parse. A broken walk and a graduation shrink that count
identically, so the check is pinned next to the number.
Verified: 284 files / 7239 tests, tsc clean, 8 generated artifacts current,
all spec gates green, and the CRM/Todo/showcase/platform-objects builds pass.
Claude-Session: https://claude.ai/code/session_01WnqGjQFQMqd5k81LYV8SCY
Co-authored-by: Claude <noreply@anthropic.com>
`mapping`, `agent` and `page` reject unknown keys — and `strictObject` stops suggesting keys that were removed.
6
+
7
+
**A bug this campaign introduced into its own helper, fixed first.**`skill` closed in the last batch while still carrying `retiredKey` tombstones, and `strictObject` built its "did you mean" candidates from the whole shape — tombstones included. So a `triggerPhrase` typo was answered with *"Did you mean `triggerPhrases`?"*, a key that had been **removed**. An author who complied landed on the tombstone and got a second rejection telling them to delete what they had just been told to write.
8
+
9
+
Third occurrence of a shape the ledger already records twice — this campaign's fix pointing the way into the failure it exists to kill — and the first one in a *shared* helper, where it would have reached every conversion after it. Fixed structurally: **never suggest a key the schema cannot accept.** Candidates that accept `never` are dropped, so the rule holds without knowing why a key is unwritable. The two helpers stay complementary; `retiredKey` is *stronger* than a `guidance` entry, because typing the key as `never` also fails `tsc` when the config arrives through a variable, where excess-property checking would not fire.
10
+
11
+
**`agent` had two security-shaped removals with no tombstone.**`visibility` and `tenantId` were deleted as unenforced security properties — correctly, since neither did anything — but deleted without a prescription, because the shape was `.strip` and there was no rejection to attach one to. An author who wrote `visibility: 'private'` believed the agent was hidden. It was listed to everyone, and always had been. This is the `skill.permissions` class: a key that reads as a security control, is not one, and says nothing when you write it. Closing the shape created the channel, so both now name what actually gates an agent (`access` / `permissions`, enforced at the chat route since #1884).
12
+
13
+
**`route` on a page was a fiction the platform's own test suite carried.**`stack.test.ts` authored `route: '/landing'` for years. `PageSchema` has never declared it — a page is routed by its `name`, which in the map format under test IS the map key, which the test asserted six lines below the key contradicting it. Fifth test found codifying a strip-era fiction as intent, and the most likely to be reinvented, since `route` is the first key anyone reaches for on a page. Tombstoned with `path` and `url`.
14
+
15
+
Also tombstoned from each file's own comments, now that there is somewhere to put them: `agent.memory.shortTerm` (declared a working-memory window nothing consumed — ADR-0013 D3), `page.recordReview` and `page.blankLayout` (page types with no renderer, removed in framework#2265), and wrong-layer pointers for the page keys that read like real controls — `interfaceConfig.visualization` (the display mode is chosen from `appearance.allowedVisualizations`, and is not a page type), `guardrails.allowedTopics` (there is no allow-list, only `blockedTopics`).
16
+
17
+
`mapping` and `page` also gain their ADR-0010 protection envelope, which their loaders stamp and their schemas could not hold. **The undeclared-envelope debt list is down to two** (`action`, `field`), from eight.
18
+
19
+
Registered types closed at the top level: **21 of 25**. Still open: `action`, `dashboard`, `field`, `view`.
20
+
21
+
That count is now derived and pinned rather than tallied by hand — it had already drifted by one, in a campaign whose recurring lesson is that hand-maintained measurements of coverage go stale. `metadata-type-schemas.test.ts` walks each registered schema for its top-level catchall and carries the open list as a reverse pin, so closing a type fails the test until the list shrinks, exactly like the envelope debt list next to it.
22
+
23
+
The unknown-key warning layer's covered roots drop from 6 to 3 — verified as a hand-off rather than a hole: `agent.zzz`, `page.zzz` and the nested `page.regions[0].zzz` are each now rejected by the parse. A broken walk and a successful graduation shrink that count identically, so the check is pinned in the test alongside the number.
24
+
25
+
Authoring impact: a key none of these shapes declares is now rejected instead of silently discarded — it was already being ignored, so no working metadata changes.
|**source**|`string`| optional | Page source text. For kind==='html' (alias 'jsx') it is constrained JSX/HTML+Tailwind compiled to the tree by @objectstack/sdui-parser at save time (parse, never execute). For kind==='react' it is real React/JSX executed at render by @object-ui/react-runtime (trusted tier). Authoritative over `regions` in both. |
91
91
|**requires**|`string[]`| optional | Plugin namespaces the JSX source references (validated at save and load) |
0 commit comments