Skip to content

Commit fa29803

Browse files
docs(spec): state what fieldGroups[].collapsed maps to on its own (#19699)
Part of #19311 Clause-②: no Why that reading: one `.describe()` string on an already-deprecated alias. No key is added, removed or re-typed, no accept set moves, the normalizer is untouched, and the public export set is byte-identical. The grade stays `patch` exactly as measured below. ## What the card asked, and what re-measuring found #19311 is a verification request: its own body says the reading is INHERITED, was not seat-verified, and asks whether it still holds on today's `origin/main` with a lit control. Both halves were re-measured here rather than inherited. ### Half 1 — the spec side Instrument: `git grep -nE` over `packages/spec/src/**/*.zod.ts`, plus `ObjectSchema.safeParse` run against the package built from this branch. Radius: this repository at `eea7ccc3ec` (the branch point) for the greps; the built `packages/spec/dist` for the parse. Dark control: a nonsense token of the same shape (`collapsibul`) over the same glob returns 0 hits, so the instrument discriminates. The describes **do** already name `collapsible`, and both keys are deprecated in favour of the ADR-0085 `collapse` enum — so the card's literal claim ("the describe does not mention `collapsible`") is stale. What is **not** stale is the gap underneath it: nothing in the published text says what `collapsed` maps to **on its own**. Measured, nine inputs on one field group: | authored on the group | `collapse` after parse | | :--- | :--- | | `collapsed: true` | `'collapsed'` | | `collapsed: false` | `'none'` | | `collapsible: true` | `'expanded'` | | `collapsible: false` | `'none'` | | `collapsed: true` + `collapsible: false` | `'collapsed'` — `collapsed` outranks | | `collapsed: true` + `collapsible: true` | `'collapsed'` | | `defaultExpanded: false` | `'collapsed'` | | neither key (lit control) | `'none'` | | an explicit `collapse` (lit control) | wins; the aliases are not read | The last two rows are the run's own controls: the default arm fires when nothing is authored, and the alias arm is skipped when `collapse` is present — so the other seven rows are the normalizer's doing and not an artefact. `collapse: 'collapsed'` is described one screen above as *collapsible, starts closed*. So on this surface `collapsed: true` alone already implies collapsible — the behaviour was never the gap; the **text** was. The mapping lives only in the docblock of a non-exported helper, which nothing publishes, while the sibling alias `defaultExpanded` spelled its own mapping out in its describe and these two did not. ### Half 2 — the consumer side Two radii, measured separately. **This repository.** `git grep -nE "\.collapsed\b|\.collapsible\b"` over `packages/*/src/**`, excluding `packages/spec/src`, tests and generated files → **0 hits**. Lit control on the same instrument and the same glob: `\.visibleWhen\b` returns hits in five or more files. Nothing here reads the raw pair; rendering is the sibling repo's. **The pinned sibling.** Read at `.objectui-sha` = `87af769e9a3ee28ace099fdd653d3ebd79fe82e2`, which `git merge-base --is-ancestor` confirms (exit 0, self-proving) is an ancestor of the objectui checkout on this box. Dark control on a nonsense token of the same shape over the same blob: 0 hits. - `packages/plugin-form/src/ObjectForm.tsx:1560` — `const isCollapsible = Boolean(section.collapsible) || Boolean(section.collapsed);` — the maintainer ruling of 2026-09-18 (letter A: `collapsed` implies `collapsible`, read from the declaration) is **applied**, and the pin carries it. - `packages/plugin-form/src/DrawerForm.tsx:609` and `:677` — both arms pass `collapsible: section.collapsible` alone. The file names objectui#9780 in a comment and declares the residual deliberately fenced. So the downstream half is **partly repaired and partly still open**, and the open part is another repo's and another seat's. Nothing about it is touched here. ## The change One describe, on the declared file surface and nothing else: ```text before: [DEPRECATED → collapse] Boolean pair with `collapsible`; use the `collapse` enum. after: [DEPRECATED → collapse] true → 'collapsed' (collapsible, starts closed) on its own — it needs no `collapsible` and outranks `collapsible: false`; false → 'none', or 'expanded' beside `collapsible: true`. Use the `collapse` enum. ``` `content/docs/references/data/object.mdx` is regenerated from it by the repo's own generator, never by hand. ### Reverse verification — the gate can see this text Run in order on this branch, exit codes captured before any pipe: 1. after the edit and a spec rebuild, `pnpm --filter @objectstack/spec check:docs` → **exit 1**, naming `content/docs/references/data/object.mdx` as out of date. That is the lit control: the gate reads this describe. 2. `gen:schema && gen:docs` → the two published rows change and nothing else does (the shape is nested under two parents, hence two identical row edits). 3. `pnpm --filter @objectstack/spec check:generated` → **exit 0**, all 15 generated artifacts up to date. Step 1 is what makes step 3 a measurement rather than a silence. ## Verification Measured at `1fae1ecc40`, the last commit on this branch. | run | exit | | :--- | :--- | | the 101 commands from `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` | 0 — each captured before any pipe; six first returned **3** (PREREQUISITE NOT MET, never read as a pass), were cleared by building `@objectstack/lint...`, `@objectstack/client-react`, `@objectstack/objectql` and then the whole workspace, and re-ran at 0 | | `dispatch-gates.mjs --ran` reconciliation | 0 — 101 derived families accounted for, 101 run, 0 not-measured | | the six roster families whose roster sits under a directory this diff touches | 0 each — run explicitly rather than read as silent | | `pnpm --filter @objectstack/spec test` | 0 — 514 files, 14991 passed, 1 todo | | `pnpm --filter @objectstack/spec typecheck` | 0 — includes `check:test-typecheck`; `tsc --listFiles` shows the main program carries 0 `*.test.ts`, which is why that second gate is the one covering them | | `pnpm --filter @objectstack/spec check:generated` | 0 | | `pnpm lint` (`eslint . --no-inline-config`, whole repo, not narrowed) | 0 — 1m46s | ## Changeset — measured, not pattern-matched `patch` on `@objectstack/spec`. Decided by grepping the new describe text through each entry of this package's own `files[]` after a real build: - **positive** — the new text is present in 20 files under `dist`, 11 under `json-schema`, and in `src/data/object.zod.ts`, which `files[]` ships verbatim as `src/**/*.zod.ts`. It publishes three ways over. - **negative, with a lit leg** — the same grep finds `are GENERATED — do not hand-edit them` in `packages/spec/scripts/lib/generated-output.ts` and in **0** files across every `files[]` path. So the instrument separates shipped from unshipped, and the positive above is not an artefact of grepping everything. `skip-changeset` would therefore be wrong here. ## Hand-written docs corpus — key-name probe The Docs Drift Check bot listed nothing on this head and says in its own words that an empty list is **not a clean bill of health**: an emitter-only diff shares no identifier with a page that states the rule by its inputs. So the probe was dropped from the sentence to the narrowest token any page restating this rule must still carry. Radius: the whole hand-written `content/docs` corpus — 216 tracked files, generated `content/docs/references/**` excluded by pathspec. Instrument: `git grep -nw` per token. | token | hit lines | | :--- | :--- | | `collapsed` | 17 | | `collapsible` | 16 | | `collapse` | 24 | | `fieldGroups` | 21 | | **unique lines, deduplicated** | **72, across 31 pages** | | dark control `collapsable` (near-miss spelling) | 0 — exit 1 | | lit control `defaultExpanded` | 1 — exit 0 | Not a double zero, so the instrument discriminates. **Reading: nothing is falsified.** No hand-written page states the `fieldGroups[].collapsible` / `collapsed` mapping at all, so none of them can contradict a describe that now states it. The one page that documents the enum — `content/docs/data-modeling/schema-design.mdx:316` — already spells `'collapsed'` as *collapsible and starts closed*, which is exactly what the corrected describe now says. It names only `defaultExpanded` as the deprecated alias and omits this pair: an omission, not a contradiction, and not edited here. Every single-hit page (18 of them) uses "collapse" in the ordinary English sense of folding two things into one, not as a metadata key. `content/docs/releases/**` contributed 14 of the 72 lines across five pages. Release-owned and read-only: none of them states the mapping rule, all are feature-note prose, and none was touched. **Three hits are about the OTHER surface** — the form-view `sections[]` pair (`FormSectionSchema`, `packages/spec/src/ui/view.zod.ts`), which this PR does not touch and which has no alias mapping behind it. They are reported, not edited, and routing them is the dispatching seat's: - `content/docs/protocol/objectui/concept.mdx:588-596` — a YAML example captioned *"Advanced section (collapsed by default)"* that authors `collapsed: true` on a form section with **no** `collapsible` beside it. At the pinned `.objectui-sha`, the two `DrawerForm` arms honour that literally. - `content/docs/ui/views.mdx:416-417` — the section key table reads `collapsible` = "Can section be collapsed", `collapsed` = "Initially collapsed", silent about the dependency the 2026-09-18 ruling settled. - `content/docs/protocol/objectui/layout-dsl.mdx:354-355`, `:422`, `:529`, `:835-836` — the same pair documented four more times without the dependency. The authored example at `:354-355` writes both keys together, so it is safe as written. **Limit of this radius, stated:** the probe sees a page that NAMES one of the four tokens. It does **not** see a page that describes collapse behaviour in prose with no key name at all. Nothing here claims more than the token can carry. ## Acceptance notes Noted, deliberately not ridden along: - **`collapsible`'s describe carries the mirror-image silence.** `collapsible: true` alone parses to `collapse: 'expanded'` and its describe says only `` Boolean pair with `collapsed` ``. Same family, same correction shape; left out because the card names `collapsed` and the dispatch said the describe and nothing else. - **The `@deprecated` TSDoc lines above both keys** still read "pair with X", the framing that produced the card. They are IDE text, not published-page text, so they are a separate, smaller surface. A measured finding on a file outside this PR's declared surface is reported to the dispatching seat rather than filed from here. --- _Generated by [Claude Code](https://claude.ai/code)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 7a25a3e commit fa29803

3 files changed

Lines changed: 25 additions & 3 deletions

File tree

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
"@objectstack/spec": patch
3+
---
4+
5+
`ObjectFieldGroup.collapsed` — the deprecated alias's `describe()` now states what the key maps to **on its own**, so the published reference page no longer leaves an author to guess whether `collapsed: true` also needs `collapsible` beside it (#19311).
6+
7+
`collapse` (ADR-0085) replaced the `collapsible` / `collapsed` boolean pair, and `ObjectSchema.parse` still folds the old pair onto it. That mapping has always been total — a group authored `collapsed: true` and nothing else parses to `collapse: 'collapsed'`, which the enum's own describe spells out as *collapsible, starts closed* — but the alias's describe said only `` Boolean pair with `collapsible`; use the `collapse` enum. ``, and that sentence is what `content/docs/references/data/object.mdx` publishes. The sibling `defaultExpanded` already spelled its mapping out (`true → 'expanded', false → 'collapsed'`); these two aliases did not.
8+
9+
Measured against the built package, `ObjectSchema.safeParse` on one field group:
10+
11+
| authored on the group | `collapse` after parse |
12+
| :--- | :--- |
13+
| `collapsed: true` | `'collapsed'` |
14+
| `collapsed: false` | `'none'` |
15+
| `collapsible: true` | `'expanded'` |
16+
| `collapsible: false` | `'none'` |
17+
| `collapsed: true` + `collapsible: false` | `'collapsed'` — `collapsed` outranks |
18+
| an explicit `collapse` | wins; the aliases are not read |
19+
20+
- **Text only.** No key is added, removed or re-typed, no accept set moves and the normalizer is untouched: the nine probe inputs above parse to the same nine results before and after.
21+
- **`collapsible`'s own describe is left unchanged** and still carries the mirror-image silence about what `collapsible: true` alone means (`'expanded'`). It is reported rather than ridden along on a card that names `collapsed`.
22+
- **This is the object-level `fieldGroups` pair only.** The form-view `sections[].collapsible` / `sections[].collapsed` pair is a different, non-deprecated surface with no alias mapping behind it, and nothing here touches it.

‎content/docs/references/data/object.mdx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ const result = ApiMethod.parse(data);
311311
| **collapse** | `Enum<'none' \| 'expanded' \| 'collapsed'>` | optional (default: `"none"`) | [ADR-0085] Section collapse behaviour: 'none' (always open, no toggle), 'expanded' (collapsible, starts open), 'collapsed' (collapsible, starts closed). |
312312
| **defaultExpanded** | `boolean` | optional | [DEPRECATED → collapse] true → 'expanded', false → 'collapsed'. |
313313
| **collapsible** | `boolean` | optional | [DEPRECATED → collapse] Boolean pair with `collapsed`; use the `collapse` enum. |
314-
| **collapsed** | `boolean` | optional | [DEPRECATED → collapse] Boolean pair with `collapsible`; use the `collapse` enum. |
314+
| **collapsed** | `boolean` | optional | [DEPRECATED → collapse] true → 'collapsed' (collapsible, starts closed) on its own — it needs no `collapsible` and outranks `collapsible: false`; false → 'none', or 'expanded' beside `collapsible: true`. Use the `collapse` enum. |
315315

316316
### Nested Shape: `Object.tenancy`
317317

@@ -667,7 +667,7 @@ External datasource binding (ADR-0015)
667667
| **collapse** | `Enum<'none' \| 'expanded' \| 'collapsed'>` | optional (default: `"none"`) | [ADR-0085] Section collapse behaviour: 'none' (always open, no toggle), 'expanded' (collapsible, starts open), 'collapsed' (collapsible, starts closed). |
668668
| **defaultExpanded** | `boolean` | optional | [DEPRECATED → collapse] true → 'expanded', false → 'collapsed'. |
669669
| **collapsible** | `boolean` | optional | [DEPRECATED → collapse] Boolean pair with `collapsed`; use the `collapse` enum. |
670-
| **collapsed** | `boolean` | optional | [DEPRECATED → collapse] Boolean pair with `collapsible`; use the `collapse` enum. |
670+
| **collapsed** | `boolean` | optional | [DEPRECATED → collapse] true → 'collapsed' (collapsible, starts closed) on its own — it needs no `collapsible` and outranks `collapsible: false`; false → 'none', or 'expanded' beside `collapsible: true`. Use the `collapse` enum. |
671671

672672

673673
---

‎packages/spec/src/data/object.zod.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1217,7 +1217,7 @@ export const ObjectFieldGroupSchema = lazySchema(() => strictObject({
12171217
/** @deprecated [ADR-0085 → `collapse`] UI-dialect alias (pair with `collapsed`); mapped onto `collapse` at parse. */
12181218
collapsible: z.boolean().optional().describe("[DEPRECATED → collapse] Boolean pair with `collapsed`; use the `collapse` enum."),
12191219
/** @deprecated [ADR-0085 → `collapse`] UI-dialect alias (pair with `collapsible`); mapped onto `collapse` at parse. */
1220-
collapsed: z.boolean().optional().describe("[DEPRECATED → collapse] Boolean pair with `collapsible`; use the `collapse` enum."),
1220+
collapsed: z.boolean().optional().describe("[DEPRECATED → collapse] true → 'collapsed' (collapsible, starts closed) on its own — it needs no `collapsible` and outranks `collapsible: false`; false → 'none', or 'expanded' beside `collapsible: true`. Use the `collapse` enum."),
12211221
}));
12221222

12231223
export type ObjectFieldGroup = z.input<typeof ObjectFieldGroupSchema>;

0 commit comments

Comments
 (0)