Skip to content

Commit cc16979

Browse files
committed
fix(spec): one sub-table per variant for a property that opens more than one object shape (#12316)
The measured remainder of #11601: 28 property rows repo-wide open two or more object shapes, and #12309 refused all of them because a heading had no single shape to name. The accessor grammar it introduced extends to cover them — a selector segment spliced in where the union sits, `[type='sidebar']` where the union has a discriminant and `[option 2]` where it does not. `nestedShapeOf` now reads its single-shape answer out of a general `nestedShapesOf` walk, so the two cannot drift apart about what one shape level means, and the section renderer loops over every shape instead of the one. Regenerated tree: 15 files, +1397 / -0 lines. All 1469 single-shape headings are byte-identical. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E5LFCYBJ3q2s6yW6oMLxwy
1 parent 7bd6447 commit cc16979

19 files changed

Lines changed: 2018 additions & 51 deletions
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
---
2+
"@objectstack/spec": patch
3+
---
4+
5+
fix(spec): reference pages carry every variant's `.describe()` text when a property opens more than one object shape (#12316)
6+
7+
The designed-and-measured remainder of #11601. #12309 gave a property that
8+
opens exactly ONE nested object shape a `### Nested Shape:` table, and refused
9+
a property whose type is a union of TWO OR MORE object shapes — there was no
10+
single "the shape of this property" for a heading to name, and naming one
11+
would have meant a variant index, i.e. a second addressing notation. Those rows
12+
kept the collapsed signature cell, and the cell has no description column, so
13+
their nested `.describe()` text stayed exactly as unreachable as #11601 found
14+
it.
15+
16+
**The population, re-measured on `origin/main@7bd6447`.** 28 of the 8604
17+
rendered property rows open two or more shapes, and **all 28** carry describe
18+
text on at least one variant: `ui/App.navigation`,
19+
`ui/NavigationArea.navigation` and `ui/NavigationContribution.items` with nine
20+
variants each, `data/ConditionalValidation.then` / `.otherwise` and
21+
`system/CRDTMergeResult.state` with five, `system/ChangeSet.operations` /
22+
`.rollback` with seven, down to four two-variant rows. The census is unchanged
23+
from the one #11601 recorded when it deferred them.
24+
25+
**What is rendered now.** One sub-table per variant, each under the same
26+
`### Nested Shape:` heading in the same position — no new grammar, and no new
27+
heading level, so the single-h1 invariant and the module-header numbering are
28+
untouched. The accessor gains a SELECTOR segment, spliced in where the union
29+
sits in the wrapper stack rather than appended to the finished path, so
30+
`StateMachine.on[string][option 3][number]` reads left to right as *the record
31+
value, its third option, an element of it* — the reading `[string][number]`
32+
already had.
33+
34+
Two spellings, in preference order:
35+
36+
- **`[type='sidebar']`** where the union has a discriminant — a key every
37+
shape-bearing variant pins to a *different* literal. It states what the
38+
author writes to select that variant, in the same `formatLiteral` spelling
39+
the Type cell prints two lines above, and it is stable: reordering the union
40+
or adding a tenth variant moves no existing heading and breaks no existing
41+
anchor. 22 of the 28 rows have one.
42+
- **`[option 2]`** for the six that do not — deliberately the word the union
43+
branch has printed under `### Union Options` since long before this, rather
44+
than a bare `[2]`, which in a stack of `[number]`/`[string]` segments would
45+
read as a tuple index into the property's own type. It counts position in the
46+
union *including* arms that open no shape, so the number is checkable against
47+
the `string | { … } | { … }[]` cell the table sits under.
48+
49+
The distinctness half of the discriminant test is load-bearing: a union whose
50+
arms pin the same `const` has no discriminant by this rule, because answering
51+
one would emit two identical headings — two identical anchors on one page, the
52+
defect the `Schema.key` qualification exists to prevent.
53+
54+
Every bound #11601 set still holds. Depth stays at `SHAPE_DEPTH_LIMIT = 1` —
55+
what was lifted is the multi-shape *refusal* at level 1, not the budget, so a
56+
shape nested inside a variant is exactly as unreachable as it was inside a lone
57+
shape. "Only where there is text to publish" is now decided **per variant**,
58+
which is the same rule one level finer: `ui/FormView.submitBehavior` opens four
59+
shapes and one carries prose, so it gets one table, not four. And a variant
60+
table still relocates no vocabulary — it is a third position for those keys.
61+
62+
A property opening exactly one shape gains no selector segment, because the
63+
stamp is conditioned on the union's own yield and not on the row's: all 1469
64+
single-shape headings #11601 published are byte-identical. The regenerated tree
65+
is **purely additive** — 15 files, **+1397 / -0** lines, no reordering.

‎content/docs/references/ai/conversation.mdx‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,42 @@ const result = CodeContentSchema.parse(data);
104104
| **embedding** | `number[]` | optional | Vector embedding for semantic search |
105105
| **metadata** | `Record<string, any>` | optional | |
106106

107+
### Nested Shape: `ConversationMessage.content[number][type='text']`
108+
109+
| Property | Type | Required | Description |
110+
| :--- | :--- | :--- | :--- |
111+
| **type** | `'text'` | ✅ | |
112+
| **text** | `string` | ✅ | Text content |
113+
| **metadata** | `Record<string, any>` | optional | |
114+
115+
### Nested Shape: `ConversationMessage.content[number][type='image']`
116+
117+
| Property | Type | Required | Description |
118+
| :--- | :--- | :--- | :--- |
119+
| **type** | `'image'` | ✅ | |
120+
| **imageUrl** | `string` | ✅ | Image URL |
121+
| **detail** | `Enum<'low' \| 'high' \| 'auto'>` | optional (default: `"auto"`) | |
122+
| **metadata** | `Record<string, any>` | optional | |
123+
124+
### Nested Shape: `ConversationMessage.content[number][type='file']`
125+
126+
| Property | Type | Required | Description |
127+
| :--- | :--- | :--- | :--- |
128+
| **type** | `'file'` | ✅ | |
129+
| **fileUrl** | `string` | ✅ | File attachment URL |
130+
| **mimeType** | `string` | ✅ | MIME type |
131+
| **fileName** | `string` | optional | |
132+
| **metadata** | `Record<string, any>` | optional | |
133+
134+
### Nested Shape: `ConversationMessage.content[number][type='code']`
135+
136+
| Property | Type | Required | Description |
137+
| :--- | :--- | :--- | :--- |
138+
| **type** | `'code'` | ✅ | |
139+
| **text** | `string` | ✅ | Code snippet |
140+
| **language** | `string` | optional (default: `"text"`) | |
141+
| **metadata** | `Record<string, any>` | optional | |
142+
107143
### Nested Shape: `ConversationMessage.functionCall`
108144

109145
| Property | Type | Required | Description |

‎content/docs/references/ai/knowledge-source.mdx‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,24 @@ const result = FileKnowledgeSourceSchema.parse(data);
8787
| **refresh** | `{ onRecordChange?: boolean; cron?: string }` | optional (default: `{}`) | |
8888
| **aiExposed** | `boolean` | optional (default: `true`) | |
8989

90+
### Nested Shape: `KnowledgeSource.source[kind='object']`
91+
92+
| Property | Type | Required | Description |
93+
| :--- | :--- | :--- | :--- |
94+
| **kind** | `'object'` | ✅ | |
95+
| **object** | `string` | ✅ | Short object name to index |
96+
| **contentFields** | `string[]` | ✅ | Fields contributing to document content |
97+
| **metadataFields** | `string[]` | optional (default: `[]`) | |
98+
| **where** | `Record<string, any>` | optional | |
99+
100+
### Nested Shape: `KnowledgeSource.source[kind='file']`
101+
102+
| Property | Type | Required | Description |
103+
| :--- | :--- | :--- | :--- |
104+
| **kind** | `'file'` | ✅ | |
105+
| **prefix** | `string` | ✅ | Storage prefix |
106+
| **mimeTypes** | `string[]` | optional (default: `[]`) | |
107+
90108
### Nested Shape: `KnowledgeSource.embedding`
91109

92110
| Property | Type | Required | Description |

‎content/docs/references/automation/state-machine.mdx‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,24 @@ Type: `string`
159159
| **states** | `Record<string, { type: Enum<'atomic' \| 'compound' \| 'parallel' \| 'final' \| 'history'>; entry?: (string \| object)[]; exit?: (string \| object)[]; on?: Record<string, string \| object \| object[]>; … }>` | optional | |
160160
| **meta** | `{ label?: string; description?: string; color?: string; aiInstructions?: string }` | optional | |
161161

162+
### Nested Shape: `StateMachine.on[string][option 2]`
163+
164+
| Property | Type | Required | Description |
165+
| :--- | :--- | :--- | :--- |
166+
| **target** | `string` | optional | Target State ID |
167+
| **cond** | `string \| { type: string; params?: Record<string, any> }` | optional | Condition (Guard) required to take this path |
168+
| **actions** | `(string \| { type: string; params?: Record<string, any> })[]` | optional | Actions to execute during transition |
169+
| **description** | `string` | optional | Human readable description of this rule |
170+
171+
### Nested Shape: `StateMachine.on[string][option 3][number]`
172+
173+
| Property | Type | Required | Description |
174+
| :--- | :--- | :--- | :--- |
175+
| **target** | `string` | optional | Target State ID |
176+
| **cond** | `string \| { type: string; params?: Record<string, any> }` | optional | Condition (Guard) required to take this path |
177+
| **actions** | `(string \| { type: string; params?: Record<string, any> })[]` | optional | Actions to execute during transition |
178+
| **description** | `string` | optional | Human readable description of this rule |
179+
162180

163181
---
164182

@@ -177,6 +195,24 @@ Type: `string`
177195
| **states** | `Record<string, [StateNode](#statenode)>` | optional | |
178196
| **meta** | `{ label?: string; description?: string; color?: string; aiInstructions?: string }` | optional | |
179197

198+
### Nested Shape: `StateNode.on[string][option 2]`
199+
200+
| Property | Type | Required | Description |
201+
| :--- | :--- | :--- | :--- |
202+
| **target** | `string` | optional | Target State ID |
203+
| **cond** | `string \| { type: string; params?: Record<string, any> }` | optional | Condition (Guard) required to take this path |
204+
| **actions** | `(string \| { type: string; params?: Record<string, any> })[]` | optional | Actions to execute during transition |
205+
| **description** | `string` | optional | Human readable description of this rule |
206+
207+
### Nested Shape: `StateNode.on[string][option 3][number]`
208+
209+
| Property | Type | Required | Description |
210+
| :--- | :--- | :--- | :--- |
211+
| **target** | `string` | optional | Target State ID |
212+
| **cond** | `string \| { type: string; params?: Record<string, any> }` | optional | Condition (Guard) required to take this path |
213+
| **actions** | `(string \| { type: string; params?: Record<string, any> })[]` | optional | Actions to execute during transition |
214+
| **description** | `string` | optional | Human readable description of this rule |
215+
180216
### Nested Shape: `StateNode.always[number]`
181217

182218
| Property | Type | Required | Description |

0 commit comments

Comments
 (0)