Skip to content

Commit a98085f

Browse files
Jack Qclaude
andauthored
feat(spec): declare the viewItems channel — a portable home for non-container view artifacts in runtime-assembled manifests (#8564)
* feat(spec): declare the viewItems channel for non-container view artifacts in assembled manifests (#5320) Option B of the 2026-08-12 fork ruling on #5320: runtime-assembled manifests (package export, environment artifacts) get a DECLARED home for the view artifacts the authored stack vocabulary refuses — expanded viewKind items, tenant-authored standalone ViewItems, flattened overlays. - AssembledViewArtifactSchema: the non-container branches of the view metadata vocabulary, built from VIEW_METADATA_MEMBERS minus container (no drift). - partitionAssembledViewArtifacts: producer-side re-aggregation (A's mechanical half) — containers travel in views:, exactly-derivable expanded items fold away, everything else travels in viewItems:. - ObjectStackDefinition.viewItems: declared always-refusing (machine-assembled only) so a hand-authored viewItems fails tsc and parse with the prescription instead of being silently stripped. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012MNV7ZSCjNfA38eDCjsXQL * feat(spec): export the shared container classifier, ADR-0122 Parsed alias, root re-exports, INVALID_METADATA provenance row (#5320) - isViewContainerShaped: ONE classifier for producers (partition) and the consumer (the registration loop's views: tighten) — two classifiers would let an assembler emit an entry the importer refuses. - AssembledViewArtifactParsed (ADR-0122 alias convention). - Root index re-exports the assembled-views vocabulary (the engine imports from the package root). - ERROR_CODE_LEDGER: INVALID_METADATA provenance row under @objectstack/objectql (third emitter — the views: tighten and the viewItems: channel refuse 422). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012MNV7ZSCjNfA38eDCjsXQL * merge origin/main (os-regen artifacts taken from main; regeneration follows) * chore(spec): regenerate api-surface/export-origins after merging origin/main (#5320) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012MNV7ZSCjNfA38eDCjsXQL --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 6b441a8 commit a98085f

11 files changed

Lines changed: 535 additions & 0 deletions

File tree

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
"@objectstack/spec": minor
3+
---
4+
5+
feat(spec): declare the `viewItems:` channel — a portable home for non-container view artifacts in runtime-assembled manifests (#5320, #8070)
6+
7+
Runtime-assembled manifests (`GET /packages/:id/export`, environment artifact
8+
bundles) carry view artifacts the authored stack vocabulary refuses: expanded
9+
`viewKind` items (the ADR-0017 dual-read keeps them registered beside their
10+
container), tenant-authored standalone ViewItems, and flattened overlays.
11+
Measured on #5320, 2 of 3 exported entries in the minimal single-container case
12+
were stack-schema-refused yet metadata-door-legal — the export→import round trip
13+
worked only through the runtime registration loop's undeclared wider acceptance.
14+
15+
Per the 2026-08-12 fork ruling (option B plus A's mechanical half), the bridge
16+
is now declared instead of silent:
17+
18+
- **`AssembledViewArtifactSchema`** (`ui/assembled-views.zod.ts`) — one entry of
19+
an assembled manifest's `viewItems:` collection: the non-container branches of
20+
the `view` metadata vocabulary, built from `VIEW_METADATA_MEMBERS` minus
21+
`container` so the two doors cannot drift. Strictly schema'd; no passthrough.
22+
- **`partitionAssembledViewArtifacts`** — the producer-side re-aggregation
23+
shared by every manifest assembler: containers travel in `views:`; expanded
24+
items a travelling container re-derives exactly are folded away (reported in
25+
`folded`); everything else — standalone items, overlays, items whose stored
26+
body diverged from their container — travels in `viewItems:`.
27+
- **`ObjectStackDefinition.viewItems`** is declared as an always-refusing key:
28+
hand-authoring it in `defineStack` source fails `tsc` (input type `never`) and
29+
parse, with the prescription (author containers in `views:`; author standalone
30+
views through the metadata door). The channel is machine-assembled by design.

packages/spec/api-surface/root.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,14 @@
44
"exports": [
55
"ADMIN_FULL_ACCESS (const)",
66
"ALL_CONVERSIONS (const)",
7+
"ASSEMBLED_VIEW_ITEMS_KEY (const)",
78
"AUDIENCE_ANCHOR_POSITIONS (const)",
89
"Agent (type)",
910
"ApplyConversionsOptions (interface)",
11+
"AssembledViewArtifact (type)",
12+
"AssembledViewArtifactParsed (type)",
13+
"AssembledViewArtifactSchema (const)",
14+
"AssembledViewPartition (interface)",
1015
"AuthoringKeySurface (type)",
1116
"BUILTIN_IDENTITY_METADATA (const)",
1217
"BUILTIN_IDENTITY_NAMES (const)",
@@ -166,6 +171,7 @@
166171
"formatZodIssue (function)",
167172
"isAggregatedViewContainer (function)",
168173
"isKnownPlatformCapability (function)",
174+
"isViewContainerShaped (function)",
169175
"lintUnknownAuthoringKeys (function)",
170176
"lintUnknownKeysAgainstSchema (function)",
171177
"lintUnknownStackKeys (function)",
@@ -175,6 +181,7 @@
175181
"normalizePluginMetadata (function)",
176182
"normalizeStackInput (function)",
177183
"objectStackErrorMap (const)",
184+
"partitionAssembledViewArtifacts (function)",
178185
"safeParsePretty (function)",
179186
"suggestFieldType (function)",
180187
"tmpl (function)"

packages/spec/api-surface/ui.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"ACTION_LOCATIONS (const)",
66
"ACTION_PARAM_BUILTIN_KEYS (const)",
77
"AIChatWindowProps (const)",
8+
"ASSEMBLED_VIEW_ITEMS_KEY (const)",
89
"Action (type)",
910
"ActionAi (type)",
1011
"ActionAiParsed (type)",
@@ -42,6 +43,10 @@
4243
"AppearanceConfigSchema (const)",
4344
"AriaProps (type)",
4445
"AriaPropsSchema (const)",
46+
"AssembledViewArtifact (type)",
47+
"AssembledViewArtifactParsed (type)",
48+
"AssembledViewArtifactSchema (const)",
49+
"AssembledViewPartition (interface)",
4550
"BorderRadius (type)",
4651
"BorderRadiusSchema (const)",
4752
"BreakpointColumnMap (type)",
@@ -409,9 +414,11 @@
409414
"isActionParamValuePresent (function)",
410415
"isAggregatedViewContainer (function)",
411416
"isRecordContextBlockType (function)",
417+
"isViewContainerShaped (function)",
412418
"normalizeFilterOperator (function)",
413419
"normalizeInlineAction (function)",
414420
"pageForm (const)",
421+
"partitionAssembledViewArtifacts (function)",
415422
"reactBlockTagFor (function)",
416423
"reportForm (const)",
417424
"reportSelectionOrder (function)",

packages/spec/export-origins/root.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,14 @@
44
"exports": {
55
"ADMIN_FULL_ACCESS": "src/identity/eval-user.zod.ts#ADMIN_FULL_ACCESS (const)",
66
"ALL_CONVERSIONS": "src/conversions/registry.ts#ALL_CONVERSIONS (const)",
7+
"ASSEMBLED_VIEW_ITEMS_KEY": "src/ui/assembled-views.zod.ts#ASSEMBLED_VIEW_ITEMS_KEY (const)",
78
"AUDIENCE_ANCHOR_POSITIONS": "src/identity/position.zod.ts#AUDIENCE_ANCHOR_POSITIONS (const)",
89
"Agent": "src/ai/agent.zod.ts#Agent (type)",
910
"ApplyConversionsOptions": "src/conversions/apply.ts#ApplyConversionsOptions (interface)",
11+
"AssembledViewArtifact": "src/ui/assembled-views.zod.ts#AssembledViewArtifact (type)",
12+
"AssembledViewArtifactParsed": "src/ui/assembled-views.zod.ts#AssembledViewArtifactParsed (type)",
13+
"AssembledViewArtifactSchema": "src/ui/assembled-views.zod.ts#AssembledViewArtifactSchema (const)",
14+
"AssembledViewPartition": "src/ui/assembled-views.zod.ts#AssembledViewPartition (interface)",
1015
"AuthoringKeySurface": "src/data/authoring-key-lint.ts#AuthoringKeySurface (type)",
1116
"BUILTIN_IDENTITY_METADATA": "src/identity/eval-user.zod.ts#BUILTIN_IDENTITY_METADATA (const)",
1217
"BUILTIN_IDENTITY_NAMES": "src/identity/eval-user.zod.ts#BUILTIN_IDENTITY_NAMES (const)",
@@ -166,6 +171,7 @@
166171
"formatZodIssue": "src/shared/error-map.zod.ts#formatZodIssue (function)",
167172
"isAggregatedViewContainer": "src/ui/view.zod.ts#isAggregatedViewContainer (function)",
168173
"isKnownPlatformCapability": "src/kernel/platform-capabilities.ts#isKnownPlatformCapability (function)",
174+
"isViewContainerShaped": "src/ui/assembled-views.zod.ts#isViewContainerShaped (function)",
169175
"lintUnknownAuthoringKeys": "src/kernel/metadata-authoring-lint.ts#lintUnknownAuthoringKeys (function)",
170176
"lintUnknownKeysAgainstSchema": "src/kernel/metadata-authoring-lint.ts#lintUnknownKeysAgainstSchema (function)",
171177
"lintUnknownStackKeys": "src/kernel/metadata-authoring-lint.ts#lintUnknownStackKeys (function)",
@@ -175,6 +181,7 @@
175181
"normalizePluginMetadata": "src/shared/metadata-collection.zod.ts#normalizePluginMetadata (function)",
176182
"normalizeStackInput": "src/shared/metadata-collection.zod.ts#normalizeStackInput (function)",
177183
"objectStackErrorMap": "src/shared/error-map.zod.ts#objectStackErrorMap (const)",
184+
"partitionAssembledViewArtifacts": "src/ui/assembled-views.zod.ts#partitionAssembledViewArtifacts (function)",
178185
"safeParsePretty": "src/shared/error-map.zod.ts#safeParsePretty (function)",
179186
"suggestFieldType": "src/shared/suggestions.zod.ts#suggestFieldType (function)",
180187
"tmpl": "src/shared/expression.zod.ts#tmpl (function)"

packages/spec/export-origins/ui.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"ACTION_LOCATIONS": "src/ui/action.zod.ts#ACTION_LOCATIONS (const)",
66
"ACTION_PARAM_BUILTIN_KEYS": "src/ui/action-params.zod.ts#ACTION_PARAM_BUILTIN_KEYS (const)",
77
"AIChatWindowProps": "src/ui/component.zod.ts#AIChatWindowProps (const)",
8+
"ASSEMBLED_VIEW_ITEMS_KEY": "src/ui/assembled-views.zod.ts#ASSEMBLED_VIEW_ITEMS_KEY (const)",
89
"Action": "src/ui/action.zod.ts#Action (type)",
910
"ActionAi": "src/ui/action.zod.ts#ActionAi (type)",
1011
"ActionAiParsed": "src/ui/action.zod.ts#ActionAiParsed (type)",
@@ -42,6 +43,10 @@
4243
"AppearanceConfigSchema": "src/ui/view.zod.ts#AppearanceConfigSchema (const)",
4344
"AriaProps": "src/ui/i18n.zod.ts#AriaProps (type)",
4445
"AriaPropsSchema": "src/ui/i18n.zod.ts#AriaPropsSchema (const)",
46+
"AssembledViewArtifact": "src/ui/assembled-views.zod.ts#AssembledViewArtifact (type)",
47+
"AssembledViewArtifactParsed": "src/ui/assembled-views.zod.ts#AssembledViewArtifactParsed (type)",
48+
"AssembledViewArtifactSchema": "src/ui/assembled-views.zod.ts#AssembledViewArtifactSchema (const)",
49+
"AssembledViewPartition": "src/ui/assembled-views.zod.ts#AssembledViewPartition (interface)",
4550
"BorderRadius": "src/ui/theme.zod.ts#BorderRadius (type)",
4651
"BorderRadiusSchema": "src/ui/theme.zod.ts#BorderRadiusSchema (const)",
4752
"BreakpointColumnMap": "src/ui/responsive.zod.ts#BreakpointColumnMap (type)",
@@ -409,9 +414,11 @@
409414
"isActionParamValuePresent": "src/ui/action-params.zod.ts#isActionParamValuePresent (function)",
410415
"isAggregatedViewContainer": "src/ui/view.zod.ts#isAggregatedViewContainer (function)",
411416
"isRecordContextBlockType": "src/ui/react-blocks.ts#isRecordContextBlockType (function)",
417+
"isViewContainerShaped": "src/ui/assembled-views.zod.ts#isViewContainerShaped (function)",
412418
"normalizeFilterOperator": "src/ui/view.zod.ts#normalizeFilterOperator (function)",
413419
"normalizeInlineAction": "src/ui/action.zod.ts#normalizeInlineAction (function)",
414420
"pageForm": "src/ui/page.form.ts#pageForm (const)",
421+
"partitionAssembledViewArtifacts": "src/ui/assembled-views.zod.ts#partitionAssembledViewArtifacts (function)",
415422
"reactBlockTagFor": "src/ui/react-blocks.ts#reactBlockTagFor (function)",
416423
"reportForm": "src/ui/report.form.ts#reportForm (const)",
417424
"reportSelectionOrder": "src/ui/report.zod.ts#reportSelectionOrder (function)",

packages/spec/src/api/error-code-ledger.zod.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,12 @@ export const ERROR_CODE_LEDGER = {
344344
'ERR_BULK_RESULT_MISMATCH',
345345
'ERR_DATASOURCE_UNAVAILABLE',
346346
'ERR_DRIVER_CONNECT',
347+
// [#5320] Third EMITTER of the code (metadata-protocol and plugin-security
348+
// already register it) — the registration loop's `views:` tighten refuses a
349+
// non-container entry, and the `viewItems:` channel refuses an entry the
350+
// assembled vocabulary rejects, both 422/INVALID_METADATA. Provenance, not
351+
// identity, per this file's header.
352+
'INVALID_METADATA',
347353
'ERR_READONLY_FIELD_REJECTED', // strictReadonlyWrites: the write would strip caller-supplied fields, so it was refused (#5126; since #6437 that covers the primary_key strip too — one code, `drops` carries the per-reason breakdown)
348354
'ERR_SUMMARY_RECOMPUTE',
349355
'VALIDATION_FAILED',

packages/spec/src/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,16 @@ export * from './stack.zod';
6868

6969
// DX Helper Functions (re-exported for convenience)
7070
export { defineView, defineForm, defineViewItem, isAggregatedViewContainer, expandViewContainer, expandViewContainerWithDiagnostics } from './ui/view.zod';
71+
// [#5320] The assembled-manifest view channel: the `viewItems:` vocabulary, its
72+
// producer-side partition, and the shared container classifier the registration
73+
// loop's `views:` tighten judges by.
74+
export {
75+
ASSEMBLED_VIEW_ITEMS_KEY,
76+
AssembledViewArtifactSchema,
77+
isViewContainerShaped,
78+
partitionAssembledViewArtifacts,
79+
} from './ui/assembled-views.zod';
80+
export type { AssembledViewArtifact, AssembledViewArtifactParsed, AssembledViewPartition } from './ui/assembled-views.zod';
7181
export type { ExpandedViewItem, ViewKeyCollision, ExpandViewResult } from './ui/view.zod';
7282
export { defineApp } from './ui/app.zod';
7383
export { defineFlow } from './automation/flow.zod';

packages/spec/src/stack.zod.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,37 @@ export const ObjectStackDefinitionSchema = lazySchema(() => z.object({
249249
// LayoutDispatcher, NavigationBuilder or ThemeProvider ever consumed it).
250250
// Author external-user UI with apps/views + positions & permission sets.
251251
views: z.array(ViewSchema).optional().describe('List Views'),
252+
/**
253+
* [#5320] MACHINE-ASSEMBLED ONLY — not an authoring surface.
254+
*
255+
* Runtime-assembled manifests (`GET /packages/:id/export`, environment
256+
* artifact bundles) carry their non-container view artifacts — tenant-
257+
* authored standalone ViewItems, flattened overlays, expanded items a
258+
* travelling container cannot re-derive — under this key, judged by
259+
* `AssembledViewArtifactSchema` (`ui/assembled-views.zod.ts`). Authored
260+
* stack sources never write it: views are authored as `defineView`
261+
* containers in `views:`, and a standalone view is authored through the
262+
* metadata door (Studio / `PUT /api/v1/meta/view`), not in stack source.
263+
*
264+
* Declared here as an always-refusing key — the `retiredKey()` mechanism,
265+
* applied to a key that is machine-only rather than removed — for the same
266+
* reason the tombstones exist (#3855): this schema is not `.strict()`, so an
267+
* UNDECLARED `viewItems:` would be silently stripped and the author would
268+
* learn nothing. Declared-refusing, the mistake fails `tsc` (input type
269+
* `never`) and the parse refusal carries the prescription itself.
270+
*/
271+
viewItems: z.never({
272+
error: () =>
273+
'`viewItems` is the machine-assembled channel for non-container view artifacts in '
274+
+ 'runtime-assembled manifests (package export, environment artifacts) — it is not an '
275+
+ 'authoring surface. Author views as `defineView` containers in `views:`; author a '
276+
+ 'standalone view through the metadata door (Studio / `PUT /api/v1/meta/view`) instead '
277+
+ 'of stack source.',
278+
}).optional().describe(
279+
'[MACHINE-ASSEMBLED] Non-container view artifacts of a runtime-assembled manifest '
280+
+ '(standalone ViewItems, flattened overlays) — written by package export and artifact '
281+
+ 'factories, refused in authored stack sources (#5320).',
282+
),
252283
pages: z.array(PageSchema).optional().describe('Custom Pages'),
253284
dashboards: z.array(DashboardSchema).optional().describe('Dashboards'),
254285
reports: z.array(ReportSchema).optional().describe('Analytics Reports'),
@@ -1674,6 +1705,11 @@ const COMPOSE_KEY_DISPOSITIONS: Record<keyof ObjectStackDefinition, ComposeDispo
16741705
objectExtensions: 'concat',
16751706
apps: 'concat',
16761707
views: 'concat',
1708+
// [#5320] Machine-assembled channel (never authorable — the schema types it
1709+
// `never`, so no authored stack reaches composition carrying it). Assembled
1710+
// manifests are not `composeStacks` inputs today; if two ever were, their
1711+
// non-container view artifacts would concatenate like every other collection.
1712+
viewItems: 'concat',
16771713
pages: 'concat',
16781714
dashboards: 'concat',
16791715
reports: 'concat',

0 commit comments

Comments
 (0)