Repository navigation
Add default props to all components to prevent collapse in designer - #4
Merged
Merged
Conversation
Co-authored-by: huangyiirene <7665279+huangyiirene@users.noreply.github.com>
Co-authored-by: huangyiirene <7665279+huangyiirene@users.noreply.github.com>
Co-authored-by: huangyiirene <7665279+huangyiirene@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Add initial state to all components
Add default props to all components to prevent collapse in designer
Jan 13, 2026
huangyiirene
requested review from
Copilot
and removed request for
huangyiirene
January 13, 2026 14:22
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds defaultProps to component registrations to prevent components from collapsing when dragged into the designer. Components now appear with meaningful initial dimensions, content, and configurations.
Changes:
- Added
defaultPropswith initial dimensions, labels, placeholders, and sample content to 40+ component registrations - Added
defaultChildrento container components that require nested elements - Added comprehensive test coverage for default props validation
Reviewed changes
Copilot reviewed 42 out of 42 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| packages/renderer/src/renderers/overlay/tooltip.tsx | Added default trigger button, content text, delay, and side positioning |
| packages/renderer/src/renderers/overlay/sheet.tsx | Added default title, description, trigger button, and sample content |
| packages/renderer/src/renderers/overlay/popover.tsx | Added default trigger button, content, alignment, and side positioning |
| packages/renderer/src/renderers/overlay/hover-card.tsx | Added default trigger button, hover content, and side positioning |
| packages/renderer/src/renderers/overlay/dropdown-menu.tsx | Added default trigger button and sample menu items with separator |
| packages/renderer/src/renderers/overlay/drawer.tsx | Added default title, description, trigger button, and content |
| packages/renderer/src/renderers/overlay/dialog.tsx | Added default title, description, modal flag, trigger button, and content |
| packages/renderer/src/renderers/overlay/context-menu.tsx | Added default menu items with separator and trigger text |
| packages/renderer/src/renderers/overlay/alert-dialog.tsx | Added default title, description, action/cancel text, and trigger button |
| packages/renderer/src/renderers/navigation/sidebar.tsx | Added default props and children structure for sidebar components |
| packages/renderer/src/renderers/navigation/header-bar.tsx | Added default breadcrumb structure |
| packages/renderer/src/renderers/layout/tabs.tsx | Added default tab items with values and content |
| packages/renderer/src/renderers/layout/card.tsx | Added default title, description, and width class |
| packages/renderer/src/renderers/form/toggle.tsx | Added default label, variant, and size for toggle components |
| packages/renderer/src/renderers/form/textarea.tsx | Added default label, placeholder, and ID |
| packages/renderer/src/renderers/form/switch.tsx | Added default label and ID |
| packages/renderer/src/renderers/form/slider.tsx | Added default value, range, and width |
| packages/renderer/src/renderers/form/select.tsx | Added default label, placeholder, and sample options |
| packages/renderer/src/renderers/form/radio-group.tsx | Added default ID and sample radio items |
| packages/renderer/src/renderers/form/input.tsx | Added default label, placeholder, type, and ID |
| packages/renderer/src/renderers/form/input-otp.tsx | Added default max length |
| packages/renderer/src/renderers/form/checkbox.tsx | Added default label and ID |
| packages/renderer/src/renderers/form/calendar.tsx | Added default mode and border styling |
| packages/renderer/src/renderers/form/button.tsx | Added default label, variant, and size |
| packages/renderer/src/renderers/feedback/toaster.tsx | Added default provider type |
| packages/renderer/src/renderers/feedback/skeleton.tsx | Added default width, height, and styling |
| packages/renderer/src/renderers/feedback/progress.tsx | Added default value and width |
| packages/renderer/src/renderers/disclosure/collapsible.tsx | Added default trigger button, content, and width |
| packages/renderer/src/renderers/disclosure/accordion.tsx | Added default accordion items with content |
| packages/renderer/src/renderers/data-display/badge.tsx | Added default label and variant |
| packages/renderer/src/renderers/data-display/avatar.tsx | Added default fallback initials and alt text |
| packages/renderer/src/renderers/data-display/alert.tsx | Added default title, description, and variant |
| packages/renderer/src/renderers/complex/table.tsx | Added default caption, columns, and sample data rows |
| packages/renderer/src/renderers/complex/scroll-area.tsx | Added default dimensions, orientation, and sample content |
| packages/renderer/src/renderers/complex/resizable.tsx | Added default direction, dimensions, and sample panels |
| packages/renderer/src/renderers/complex/carousel.tsx | Added default orientation, arrows, and sample slides |
| packages/renderer/src/renderers/basic/text.tsx | Added default text content |
| packages/renderer/src/renderers/basic/span.tsx | Added default padding and child text |
| packages/renderer/src/renderers/basic/separator.tsx | Added default orientation and margin |
| packages/renderer/src/renderers/basic/div.tsx | Added default padding, border, and minimum height |
| packages/renderer/src/tests/registry.test.tsx | Added tests for defaultProps and defaultChildren support |
| packages/renderer/src/tests/defaultProps.test.tsx | Added comprehensive test suite validating all component defaults |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Contributor
|
✅ All checks passed!
|
huangyiirene
marked this pull request as ready for review
January 13, 2026 14:26
17 tasks done
Copilot AI
added a commit
that referenced
this pull request
Jan 24, 2026
- Fix handleExportCSV to guard on gridRef.current?.api (issue #1) - Add dedicated onContextMenuAction callback instead of overloading onCellClicked (issue #2) - Remove icon property from customItems to prevent HTML injection (issue #3) - Remove validation claim from README - only basic AG Grid editing (issue #4) - Add test assertions for all new inputs (editable, exportConfig, etc.) (issue #5) - Fix onExport type to only support 'csv' format (issue #6) - Remove unused ColumnConfig properties (autoSize, groupable) (issue #9) - Type schema props with proper interfaces instead of 'any' (issue #10) - Update export description to only mention CSV (issue #11) - Add AG Grid Community vs Enterprise section to docs (issue #8) - Update README and docs with new callback and clarifications All tests pass (8/8), lint clean (0 errors) Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
7 tasks done
21 tasks
This was referenced Feb 23, 2026
Copilot AI
added a commit
that referenced
this pull request
Feb 28, 2026
…y chip (#7) - ConfigRow: add label maxWidth (45%), text truncation with title tooltip for both label and value - Toolbar section: add summary chip showing "X of Y enabled" at top of expanded section - Add toolbarEnabledCount i18n key to all 10 locales - Update view-config-schema tests for new _toolbarSummary field Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
This was referenced Feb 28, 2026
os-zhuang
added a commit
that referenced
this pull request
Jun 29, 2026
… AI slot (#2060) * feat(console): Studio WYSIWYG design surface dev harness (ADR-0080 slice-1) Additive /dev/studio-design route + DevStudioDesign four-zone shell reusing getMetadataPreview/getMetadataInspector + SchemaRenderer. Touches no existing surface. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor(app-shell): promote Studio design surface + open-core AI slot (ADR-0080) Move the design surface out of the apps/console dev harness into @object-ui/app-shell as StudioDesignSurface. The left AI copilot becomes an injected aiSlot prop — OSS renders three zones (nav/canvas/inspector); the cloud edition injects AI. Repoint /dev/studio-design at the promoted component. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(app-shell): wire Studio design surface to real metadata (ADR-0080 #3/#4) Replace the fixture with the shared MetadataClient: the single-App nav lists real pages, clicking loads that surface into the canvas (real PagePreview + live data), and the toolbar saves drafts / publishes through the real pipeline. Route moves inside ProtectedRoute for the connected client + data adapter. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor(app-shell): route Studio as /studio/:packageId/{data,automations,interfaces} Three pillars are now real routes scoped to the package being designed. The shell renders the package + pillar route links; Interfaces drives the real MetadataClient (nav/canvas/inspector + draft/publish), Data/Automations are scaffolded placeholders pending the App-nav + object/flow surface reuse map. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(app-shell): real 3-pillar Studio — App-nav Interfaces + Data + Automations Interfaces: load the real App by package id and render its navigation tree (groups + typed leaves page/object/dashboard/report); clicking loads the bound surface (PagePreview/DashboardPreview/object-grid). Data: object list + record grid (object-grid) + field table. Automations: flow list + FlowPreview graph. All reuse existing renderers — no new editor code. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(app-shell): Data pillar = primary full-height record grid (Airtable parity) Drop the redundant field table — the record grid's columns ARE the fields. Data mode now reads like a spreadsheet (objects on the left, the records directly viewable), matching Airtable's Data tab. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(app-shell): object surface = object-view (our list-view model, not raw grid) Per design direction, the Data pillar (and Interfaces object items) render an object via object-view — its named list views + ViewSwitcher + record actions, falling back to a grid — instead of the raw object-grid. Matches our view model. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(app-shell): scope Data/Automations lists to the current package (#9) Pass packageId into DataPillar/AutomationsPillar and use client.list(type, { packageId }) (=> ?package=) so each pillar lists only the current package's objects/flows, not all env metadata. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(app-shell): render flow preview via createElement (satisfy react-hooks/static-components) AutomationsPillar assigned `const Preview = getMetadataPreview(...)` and rendered it as JSX, which react-hooks/static-components flags as a component created during render. Render via React.createElement instead, and derive the type from the selected flow (current.type) to mirror InterfacesPillar. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This was referenced Oct 1, 2026
os-sales
pushed a commit
that referenced
this pull request
Oct 1, 2026
…fusal (objectui#10872 batch 10, patch round 1) 10289's migration example writes `label` and `actionType` in `properties` beside `params`, the bag spelling AGENTS.md #4 teaches. 7867's example is that entry's historical reading, so it is kept and gets a dated note: later in this release a flat `actionType` and a node-level `params` object on `action:button` are refused by name toward their bag members. Frontmatter of both files byte-identical. Claude-Session: https://claude.ai/code/session_01JG2jy8a9su7ia4Hx7zxv42 Co-authored-by: Claude <noreply@anthropic.com>
This was referenced Oct 2, 2026
os-project-manager
pushed a commit
that referenced
this pull request
Oct 4, 2026
…he contract; §4 drops `props` Describes what shipped with objectui#8347: `BaseSchema` declares no index signature, a refused key is declared by reference or fixed at the producer, never cast past, and the fresh-literal bound is stated with the pin that measures it. §4's abridged listing drops `props`, which `BaseSchema` does not declare, and #4's `events` sentence names the face that still keeps one. Claude-Session: https://claude.ai/code/session_01CPvhwGcirXqBGEdPSb72TZ Co-authored-by: Claude <noreply@anthropic.com>
This was referenced Oct 4, 2026
Closed
akarma-synetal
pushed a commit
to akarma-synetal/objectui
that referenced
this pull request
Oct 7, 2026
…roperties` bag (objectui#11183) (objectstack-ai#11317) Fixes objectstack-ai#11183 Clause-②: no Every taught `action:*` node under `AGENTS.md`, `skills/objectui/**` and `content/docs/guide/**` now writes the block's props in the spec's `properties` bag, as the `@objectstack/spec` `ComponentPropsMap` row declares them, instead of flat on the node. Triage's ruling on the objectui#10872 fork, carried by the card: **A, the `properties` bag is the contract.** The spec's `PageComponentSchema` is `.strict()` (ADR-0089 D3a) and refuses a node-level `actionType` / `target` as mis-layered; objectui's `StrictAnyComponentSchema` refuses the same two keys. The runtime reads both spellings through the `SchemaRenderer` `properties` hoist, so only documents and their pins change — ⛔ no runtime or validator code in this diff. **Order: this PR lands BEFORE PR objectui#11069** (objectui#5250, `objectui validate` onto the strict face). objectui#5250 carries `Blocked-by: objectstack-ai#11183`; PR objectui#11069 touches `content/docs/guide/layout.md` and `schema-rendering.md` in other sections than the ones edited here (its `layout.md` hunk is the `label` / `text` paragraph under the page-buttons passage; its `schema-rendering.md` hunk is the three validation doors), so it merges `main` after this lands with no textual overlap. Governed surface: `AGENTS.md`, `skills/objectui/SKILL.md`, `skills/objectui/guides/page-builder.md`, `skills/objectui/rules/protocol.md` (`node scripts/check-governed-queue-guard.mjs --test` answers exit 3, GOVERNED). This PR stays **draft** until an APPROVED review by `os-zhuang` or `hotlong`; the claiming seat lands it. Cross-lane surface (`content/docs/**`, `packages/**` pins), declared on the claim comment of the card. ## 维护者速读(草稿) **改了什么。** 文档与测试,零运行时代码。我们教给作者(和 AI)的 `action:button` 示例,把 `actionType` / `target` 这类执行器键从节点顶层挪进了 `properties` 包里 —— 共 11 个节点、10 个文件(受管面 4 个:`AGENTS.md` 第 objectstack-ai#4 条、`skills/objectui/SKILL.md`、`guides/page-builder.md`、`rules/protocol.md`;非受管 6 个:`content/docs/guide/` 下 4 篇指南、`content/docs/plugins/plugin-detail.mdx`、随同的两条 pin 测试)。同一家族的两处 rider 一并处理:plugin-detail 指南里的 `record:activity` 示例(`limit` / `showCompleted` 进包),以及 page-builder 技能里的 `object-form` 表单示例(objectui#11291 落地后 `objectui validate` 已按名拒绝扁平写法)。另附一个空 frontmatter 的 changeset(声明不发版)。 **为什么改。** spec 的 `PageComponentSchema` 是 `.strict()` 的(ADR-0089 D3a),节点顶层出现 `actionType` / `target` 会被当作错层键直接拒绝;objectui 自己的严格校验面 `StrictAnyComponentSchema` 同样拒绝这两个键。PR objectui#11069 正要把 `objectui validate` 切到这个严格面,届时受管规程文件里的标准示例就会变成校验器拒收的节点,而 agent 会照抄示例。裁决是「教的内容向 spec 靠」,不是反过来给 objectui 开一条 spec 拒绝的扁平通道。实测:12 个被改示例在改前全部被两个严格面拒绝,改后在宽容面、严格面、spec 三面全部通过(见下方 census)。 **风险与代价(含回滚)。** 运行时无变化:`SchemaRenderer` 把 `properties` 整包提升到节点上再交给渲染器,两种拼写渲染结果相同;`guide-layout-page-buttons-7926` 这条真实渲染 pin 读的就是 layout 指南里被改的那段 JSON,改后 Edit / Delete 两个按钮照常画出。代价是示例各长了 2–5 行。回滚:revert 本 PR 即可,没有任何生成物、锁文件或发布物需要同步;但回滚后 objectui#11069 不能先落地,否则规程示例会被校验器拒收。 **席位意见。**(留空) **你要做的。** 作为受管面 PR,需要你(`os-zhuang` / `hotlong`)在本 PR 上留一条 APPROVED review;之后由认领席翻 ready 并入合并队列,⛔ 本 PR 不自行 ready、不自行入队。请顺手确认 `AGENTS.md` objectstack-ai#4 的新措辞(「props 住在 `properties` 包里,⛔ 不写在节点顶层」)是你希望 agent 读到的规则。 ## Census (the gate the card asks for) Instrument: for every `"type": "action:button|icon|group|menu"` node under `content/docs/**`, `skills/**`, `AGENTS.md`, `README.md` (`grep -rnE '"type":\s*"action:(button|icon|group|menu)"' content/docs skills AGENTS.md README.md`), the keys written at the node's own level — `actionType`, `target`, `endpoint`, `method`, `name`, `icon`, `variant`, `params`, `label` — before the next node or the closing brace. | reading | action nodes | nodes with a flat row key | |---|---:|---:| | base `caa0cd392` (objectui `origin/main` at cut) | 11 | 11 | | this branch `f580661` | 11 | 0 | Per site (base → branch), by heading, all in one commit each: | file | site | moved into `properties` | |---|---|---| | `AGENTS.md` | objectstack-ai#4 example (one-line node) | `label`, `actionType`, `target` | | `skills/objectui/SKILL.md` | "4. The action system" | `label`, `actionType`, `target` | | `skills/objectui/rules/protocol.md` | "Rule: Actions Are Node Types, Not An Event Bag" (the node example only; the 「Readers only.」 paragraph is objectstack-ai#11009's and is untouched) | `label`, `actionType`, `target` | | `skills/objectui/guides/page-builder.md` | "5. Use action data" | `label`, `actionType`, `target` | | `skills/objectui/guides/page-builder.md` | "Form plugin example" (`object-form` rider) | `objectName`, `mode`, `fields` | | `content/docs/guide/quick-start.md` | "Add Actions" | `label`, `actionType`, `target` | | `content/docs/guide/record-edit-modes.md` | "Triggering the routes from JSON", three nodes | `label`, `icon`, `actionType`, and the already-bagged `params` stays in the same bag | | `content/docs/guide/schema-rendering.md` | "Reference actions in schemas" | `name`, `label`, `actionType`, `endpoint`, `method` | | `content/docs/guide/layout.md` | "Detail Page with Actions", two nodes | `name`, `label`, `variant`, `icon`, `actionType` | | `content/docs/plugins/plugin-detail.mdx` | "The `record:activity` block" (`record:activity` rider) | `limit`, `showCompleted` | Each edited fence judged on three faces (a probe run from `packages/types` against the source `zod/index.zod.ts` and the installed `@objectstack/spec` 17.5.0): at base, every one of the 12 is refused by `StrictAnyComponentSchema` and by the spec's `PageComponentSchema` with exactly the flat keys named above (the `object-form` one is refused on the tolerant face too, by name, since objectui#11291); on this branch all 12 pass tolerant, strict and spec. The prose beside each fence moved with it: the sentence "the node's own keys carry that executor's arguments" now reads "the row's other keys" / "the bag's other keys", and each site says why the flat spelling is refused. `skills/**` line readings (the governed-surface reading, lines; no token meter exists for this surface — `check:skill-eval-tokens` meters eval `must_contain` tokens, not file length): `SKILL.md` 137 → 142, `guides/page-builder.md` 346 → 353, `rules/protocol.md` 364 → 370; bundle `skills/objectui/**/*.md` 4782 → 4800; all `SKILL.md` files 137 → 142. The whole increase is the bag's two wrapper lines per fence plus the one-sentence refusal note; nothing was re-wrapped. ## Pins moved - `packages/types/src/__tests__/held-public-block-arms-10872.test.ts` (reads the quick-start's "Add Actions" fence): the taught node now validates AS WRITTEN on both faces and on the spec's `PageComponentSchema`; the pre-move flat spelling is DERIVED from the taught node (bag hoisted flat) and pinned as refused by exactly `actionType` and `target` on both strict faces while the tolerant face still keeps it — so the reading of the refusal stays live and moves with the page. - `packages/cli/src/__tests__/registered-types-validate-ratchet-10859.test.ts` (same fence, through `objectui validate`): lit control that the extracted node is in the bag spelling; the row still passes the tolerant face `validate` runs today and will pass the strict one PR objectui#11069 wires. ⛔ Its header and `refusedAtType` helper — the lines PR objectui#11069 edits — are untouched here. - Reverse leg, run with the pins committed: `git checkout BASE -- content/docs/guide/quick-start.md` ⇒ 4 red (`reads the taught node … bag spelling`, `strict face: the taught node AS WRITTEN … validates`, `the spec's own PageComponentSchema accepts the taught node as written`, the ratchet's `validates a page with the taught action:button node`), 60 green; `git checkout HEAD -- …` restored, `git diff HEAD` empty, `git hash-object` of the page equals its HEAD blob `ca70f902fc5c…`. ## Gates run locally (head `f580661`; exit codes read before any pipe) Derived from `.github/workflows/*.yml` on `origin/main` (41 files, the same count the working tree lists): my diff touches `**/*.md`, `content/**`, `.changeset/**` AND two `packages/*/src/__tests__/*.test.ts`, so the `Decide whether this change needs a full run` step in `lint.yml` / `ci.yml` answers `should_run=true`, every always-on doc gate fires, and `changeset-guard.yml` fires on the `.changeset/**` path. | gate (workflow) | command | exit | |---|---|---:| | Skills Paths | `node scripts/check-skills-paths.mjs` | 0 | | Skill Examples | `node scripts/check-skill-examples.mjs --self-test` · `node scripts/check-skill-examples.mjs` (both after the scoped build its `--build-filter` names, 30 tasks; before the build both answer exit 2 = could not run, not a verdict) | 0 (60 cases) · 0 | | Skill Eval Tokens | `node scripts/check-skill-eval-tokens.mjs --self-test` · `node scripts/check-skill-eval-tokens.mjs` | 0 · 0 | | Line Citation Gate | `node scripts/check-new-cross-file-line-citations.mjs` | 0 (0 new citations) | | Control Bytes | `node scripts/check-control-bytes.mjs` | 0 | | Doc Fence Languages · Doc Example Ids · Doc Component Types | the three `node scripts/check-doc-*.mjs` | 0 · 0 · 0 | | Doc Snippet Types | `node scripts/check-doc-snippet-types.mjs` (after the full build `pnpm turbo run build --filter='!@object-ui/site'`, 43 tasks) | 0 | | Changeset Presence · Changeset Guard | `node scripts/check-changeset-presence.mjs` · `node scripts/check-changeset-no-major.mjs` · `check-changeset-claims` · `check-pending-changeset-literals` | 0 · 0 · 0 · 0 | | README Exports | `node scripts/check-readme-exports.mjs` (after the full build) | 0 | | Docs Links · Docs Route Eager Closure · Action Ref Convention · Shell Escape Residue · Inert vi.mock Specifiers · Pre-Install Import Graph | each `node scripts/check-*.mjs` | 0 each | | `node scripts/check-comment-mask-corpus.mjs` · `node scripts/markdown-test-inputs.mjs --audit` | | 0 · 0 (107 candidate test files adjudicated, 78 entries present) | | Governed Surface Guard | `node scripts/check-governed-queue-guard.mjs --test AGENTS.md skills/objectui/SKILL.md skills/objectui/guides/page-builder.md skills/objectui/rules/protocol.md` | 3 = GOVERNED, by design | | Lint (`lint.yml` roster) | `pnpm lint --concurrency=2` (47 tasks, 0 errors) · `node scripts/check-vi-mock-override-shape.mjs` · `node scripts/check-test-path-roots.mjs` · `pnpm --filter '@object-ui/cli...' build` · `pnpm check` | 0 · 0 · 0 · 0 · 0 | | CI (`ci.yml`) — the affected tests | `pnpm exec vitest run` on the two moved pins + `guide-layout-page-buttons-7926` (3 files, 71 tests) · the other eight ledgered readers of the edited docs (8 files, 87 tests) · the root `scripts/` suite (177 files, 5371 tests, 2 skipped) | 0 · 0 · 0 | | type-check of what I edited | `tsc -p packages/types/tsconfig.test.json` · `tsc --noEmit` in `packages/cli` · `eslint` on the two test files | 0 · 0 · 0 | Not run locally, declared to CI: the full `pnpm test` shards (`ci.yml`), Live E2E, Spec Main Shape Gate (it builds `@objectstack/spec` from objectstack `main` on the runner), Bundle Analysis. Heavy steps ran under `/home/user/objectstack/scripts/pm/os-verify-lock.sh` (three locked runs: the scoped build 234 s held, the scripts suite + lint, the full build + two gates after a 452 s queue wait). ## Acceptance notes - **`packages/plugin-detail/README.md` — the `items` half of the `record:activity` rider is NOT moved, by measurement.** `ComponentPropsMap['record:activity']` on the installed spec has no `items` key (its keys: `aria`, `enableMentions`, `enableReactions`, `enableThreading`, `filterMode`, `limit`, `showCommentInput`, `showCompleted`, `showFilterToggle`, `showSubscriptionToggle`, `types`, `unifiedTimeline`), and both objectui faces refuse `properties.items` by name, so the bag spelling would turn a working example into a refused one. The README's example is TSX code composing a `DetailViewSchema` whose Activity tab hands the block a host-supplied feed — `record-activity.tsx` reads `schema.items` as the first of its three feed sources — the same runtime-slot shape as `record:history`'s `entries`. Left as is; reported in the dev report for the `domain:ui` lane to rule on whether `items` wants a named RUNTIME SLOT refusal on the authoring faces. - **Out of the roots, same family: `packages/app-shell/README.md`** teaches two flat `action:button` nodes (`navigate_create` / `navigate_edit`, under "The handler name goes in `actionType`"), the twin of the `record-edit-modes.md` passage moved here. Outside the claimed surface; in the dev report as a finding, not edited. - **Different family, noted: `content/docs/api/schema-reference.md` "ActionSchema"** teaches `"type": "action"` nodes with `actionType: "ajax"` and an `api` key — a vocabulary `schema-rendering.md` itself says no action renderer forwards. Not an `action:*` public block; not edited. - Zone 2 item 4 of the dispatch assumed `check:skill-eval-tokens` meters skill-file length; it meters eval `must_contain` tokens against the bundle (its header's "THE ORACLE" section). No length ceiling gate exists for `skills/**` in this tree; the line readings above are reported in its place. - objectui#11198 (`guides/app-composition.md` nav label) does not ride this PR; that file is untouched. Session: `session_01KTZmMfzVzjNvyaLyQ8mHvg` (PM loop round 11, claim comment 5923347428). --- _Generated by [Claude Code](https://claude.ai/code/session_01KTZmMfzVzjNvyaLyQ8mHvg)_ --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Warren Buffett <warren@objectstack.ai>
akarma-synetal
pushed a commit
to akarma-synetal/objectui
that referenced
this pull request
Oct 7, 2026
…locks, two spec-row blocks and page documents, by reference; SchemaRenderer's schema prop accepts them (objectui#11364) (objectstack-ai#11385) Refs objectstack-ai#11364 Clause-②: yes Why yes: new published TS types are added to `@object-ui/types`, and `@object-ui/react`'s `SchemaRendererProps.schema` accepts them, so the published TS surface widens. Parent: objectui#8347. This is its preparation; the `BaseSchema` index-signature removal itself is not in this PR, and nothing here edits `BaseSchema`. The `props` alias question stays open for the maintainer (see "Open questions"), which is why this PR refers to the card rather than closing it. Dispatched run, session `https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT`. Draft, left for the seat. ## What changes - **`@object-ui/types`**: `packages/types/src/authoring-nodes.ts`, exported from the main entry. Each type is derived by reference; no member is restated. - `PublicBlockNode`: one member per arm of `PublicBlockComponentSchema` (27 arms), each the arm's own `z.input` read off its SHAPE with the object config closed. `PublicBlockNodeOf` picks one member by its `type` literal (for example `'element:text'`). - `ElementTextInputNode` and `ElementRecordPickerNode`: no zod arm exists for these two, so each is built the way an arm is built: the zod `BaseSchema` shape extended with the `type` literal, the node envelope (`NODE_ENVELOPE`: `responsiveStyles`, the spec's `ResponsiveStylesSchema`), `properties` as the spec's `ComponentPropsMap` row, and the `body` / `children` tombstones every arm carries, each by reference. (Round 2: the round-1 type left out the envelope and the tombstones; see below.) The record picker also declares the node-level `dataSource` (the spec's `ElementDataSourceSchema`) its renderer reads, and leaves the bag's `object` optional for the spec props gate's `dataSource` waiver, the same waiver the `element:number` arm mirrors. - `PageDocumentNode`: the spec's `PageSchema` input with `type` (the page kind) required, the document `PageView` hands to `SchemaRenderer` (objectui#9642). - `AuthoringNode`: their union. - **`@object-ui/react`**: `SchemaRendererProps.schema` is now `BaseSchema | AuthoringNode | string | null | undefined`. No index signature and no `Record` on the prop. `toRenderableSchema` returns `SchemaRendererProps['schema']` by reference, so the bridge's return type widens with the prop in this release (the `.d.ts` text of `schema-input` does not change, so the H5 diff cannot show it). - **4 component test files**: the three `page:tabs` literals move `items` into `properties` (where `PageTabsProps` declares it); the `action:button` literals move `label` / `actionType` into the bag (objectui#11183's landed shape) and the helper takes the arm's node type beside `BaseSchema`; the `element:text_input` literal handed to `renderComponent` is typed as `ElementTextInputNode`. CASE A in `action-params-templates-7867` keeps its node-level `params` object: that spelling is what it proves is not read (objectui#10289). `SchemaRenderer` hoists the bag onto the node, so every renderer reads the same values; the suites below are green. - **Pins**: `packages/types/src/__tests__/authoring-nodes-11364.test.ts` (new) and assertion 1 of `packages/react/src/__tests__/SchemaRenderer.propsResolution.test.ts` (the prop's exact union). - **Docs**: `packages/types/README.md`, `packages/react/README.md`, `content/docs/guide/schema-rendering.md`. - **Changesets**: `.changeset/11364-public-block-ts-faces.md` (types minor, react minor). Three pending entries this release makes false get a dated, append-only note, each with its frontmatter byte-identical (md5 of the frontmatter lines unchanged): `9256-public-blocks-content-channels.md` ("This package has no TypeScript declaration of these nodes"), `8908-bridge-falsy-primitives.md` ("The return type is unchanged", round 2) and `schema-input-bridge-permanent-4622.md` (the prop's union spelled as it was, round 2). ## The pin `authoring-nodes-11364.test.ts` runs in every `type-check` today, before objectui#8347 lands. It builds `PostRemovalSchema`: the prop's union with `BaseSchema`'s index signatures stripped at the type level. Against it: - `{ type: 'element:text', properties: { content: 'Hello' } }` compiles; - `properties: { contnet: 'Hello' }` is refused, as is a node-level `contnet` and a `properties` bag on a `button` node. It also checks that no arm is lost by the derivation (`PublicBlockNode['type']` equals the union's own), and that no member carries an index signature. A control proves that check can fire: the bare `z.input` of the same arms does carry one. Every `@ts-expect-error` there is consumed (an unused one is TS2578). ## Round 2 (contract record `5933841900`, FAIL on `29acb0107`) Head `6af8e9da3`. `origin/main` was merged again (`3ae919307`, no conflicts, no rebase). - **A, the spec-row nodes.** `SpecRowNode` is now the type-level twin of an arm's `BaseSchema.extend(...)`: `Omit` of the zod `BaseSchema` shape by the own keys, then the `type` literal, `typeof NODE_ENVELOPE`, `properties`, and `body` / `children` as `retirementTombstone`'s return type, all type-only imports. The spec's own `PageComponentSchema` refuses a node-level `children` on `element:text_input` and `element:record_picker` (`unrecognized_keys`), as it does on `element:text`. Measured three ways: - before the fix (type-level P1 simulation): through the union, `responsiveStyles` compiled on `element:text` and was TS2353 on both spec-row nodes, and a node-level `children` was refused on `element:text` but compiled on `element:text_input`; on the node types, both spec-row nodes accepted `children` and refused `responsiveStyles`; - after the fix, under the REAL P1 mutation, through the actual `SchemaRenderer` `schema` prop (a throwaway probe compiled against the built `react` dist and the mutated `types` dist): `responsiveStyles` compiles on `element:text`, `element:text_input` and `element:record_picker`, and a node-level `children` is refused (TS2353) on all three, exactly as on `element:text`. The census still reads 10, and the restore is proven; - the pin's reverse check: with the fix ablated through `ablation-replace.mjs` (restore to `HEAD` proven), the new pin case turns red as predicted: TS2353 on the two styled spec-row literals and TS2578 on the three spec-row `children` directives, with the `element:text` control unchanged. - **B, the bridge.** The `11364` entry now says the bridge's return widens with the prop; `8908` gets the dated note. `schema-input-bridge-permanent-4622.md` spells the prop's old union too, so it gets the same note by the same rule. - **C.** The `11364` entry no longer says nothing stops compiling "because `BaseSchema` still carries its index signature". It says every value the old union accepted is still accepted, that a key absent from a closed member is TS2339 on a union whatever the signature allows, and that no caller here reads a key off the prop's type or the bridge's return (the `Type Check` job re-derives that). - **Round-2 gates** (at `7638daa9e`; `6af8e9da3` adds only a changeset `.md`): `@object-ui/components^...` closure build exit 0; `type-check` types / react / components exit 0, 0 errors, scripts echoed; the pin and `SchemaRenderer.propsResolution` under vitest 2 files, 8 tests passed; `check-changeset-presence`, `check-changeset-no-major`, `check:changeset-claims`, `check:pending-changeset-literals`, `check:control-bytes`, `check:new-line-citations`, `check:spec-symbols`, `check:phantom-deps`, `check:unreferenced-sources` all exit 0 (re-run at `6af8e9da3`). P1 census 10, src 0. ## Measurements (round 1, at `29acb0107` unless noted) **H1, the population** (objectui#8347's P1 mutation, `BaseSchema`'s `[key: string]: any` deleted alone through `ablation-replace.mjs`, types rebuilt each leg, `@object-ui/components`' test program): | head | test-program errors | src-program errors | |---|--:|--:| | `b5b0afea5` (base) | 30 | 0 | | `29acb0107` (this PR) | 10 | 0 | The 30 at base, by reading each site: 13 public blocks (3 `element:text`, 7 `element:text_input`, 3 `element:record_picker`), 3 `page:tabs`, 5 `action:button`, 2 page documents (`type: 'home'`), 4 `props` alias, 1 `content` probe, 2 test-registered probe type. The card's "14" public blocks reads 13 here; the 30 includes the `content` probe. The 10 left at head, each a literal no referenced type can or should accept: - 4 `props` alias (`alias-precedence-cross-channel`): waiting on the `props` question. - 1 `content` on `label` (`form-renderers`): the retired-spelling probe. - 2 `properties: 'not-a-bag'` on a test-registered probe type (`degenerate-config-bag`). - 1 node-level `params` object on `action:button` CASE A: the objectui#10289 probe. - 2 `visibility` on `element:text` (`page-variables`): the spec's deprecated alias, which objectui#10872 kept off the TS face; `SchemaRenderer` reads it only defensively for raw metadata. TS2353 hid this second key behind `properties` at base. All 10 compile today; they are objectui#8347's flips. Every leg's restore is proven: `ok restored: blob == HEAD (ce2bc9e) and git diff HEAD is empty`, dist index-signature count back to 2. The mutation is never committed. **H2, arm or row per block.** | node | zod arm | spec row | type here | |---|---|---|---| | `element:text`, `page:tabs`, `action:button`, and the other 24 public blocks | yes | yes | `PublicBlockNode` member | | `element:text_input` | none (the validator refuses it at `type`) | `ElementTextInputPropsSchema` | `ElementTextInputNode` | | `element:record_picker` | none (refused at `type`) | `ElementRecordPickerPropsSchema` | `ElementRecordPickerNode` | | page kind (`home`, `app`, `record`, `utility`) | none (`PageNodeSchema` is `type: 'page'`; refused at `type`) | `PageSchema` | `PageDocumentNode` | The PM's prediction (a one-line `z.input` of each arm) is falsified in one respect: the zod `BaseSchema` is `.passthrough()`, so the bare `z.input` of an arm has `keyof` = `string` (`[k: string]: unknown`), which would put the removed index signature back. The derivation reads the arm's shape with the config closed instead. The spec rows of `element:image`, `element:metadata_viewer`, `element:filter` and `element:form` are not typed here: none appears in the census. **H3, the prop.** Under P1, `SpecPageComponent` is refused by exactly one shared key: `visibleWhen` (`BaseSchema` says `string`, the spec says `string` or an Expression envelope). Its other keys are spec-only and do not block a non-fresh value. So no union of referenced types makes it assignable while keeping the pin: - adding the spec's `PageComponent` to the union makes it assignable, but `properties: { contnet }` on `element:text` then compiles (measured); - widening `BaseSchema.visibleWhen` to `ExpressionWire` (type-level simulation) makes it assignable AND keeps the misspelling refused, but that edits `BaseSchema`, which is excluded here. That is the fork the dispatch asked to be reported, and it is in "Open questions". Each new node type IS assignable to the prop, and the union is pinned exactly. **H4, the spec's page row.** `PageSchema.type` is `PageTypeSchema` (`record`, `app`, `home`, `list`, `utility`; default `record`); `kind` is the override mode (`full`, `slotted`, `html`, `react`, `jsx`). `page:tabs` `items` is a member of the bag (`PageTabsProps`), not of the node; each item requires `label` and `children`. **H5, the published surface** (base `b5b0afea5` built in a comparison worktree, against this branch): - `@object-ui/types` `dist`: one new file, `authoring-nodes.d.ts` (references only: `z.input` of the arms and spec rows), and one new `export type` line in `index.d.ts`. 13 `zod/*.zod.d.ts` files differ only by the print order of inferred members; each has a token multiset equal to base's (whitespace, the union bar and trailing punctuation normalised). - `@object-ui/react` `dist`: `SchemaRenderer.d.ts` gains the `AuthoringNode` import and the widened prop, and an existing inferred `render()` return type prints its union members in a different order. ## Open questions (in the report, with the four-axis frame) 1. **The `props` alias on the TS authoring face.** Measured producers: objectui's 1720 non-test TS/TSX files author no node `props` (control `properties: {` reads 28); tracked JSON has 0 `"props": {` files (control 10); objectstack `examples/**` TS at local `95fed33a2` has 0 (control 52); the published skill teaches node-level `props` as WRONG. The only authors are the 4 `alias-precedence` probes. Undecided here, per the dispatch. 2. **`SpecPageComponent` assignability** (H3): widen `BaseSchema.visibleWhen` to the spec's union (objectui#8347's file), or add `PageComponent` to the prop and lose the bag check, or leave it unassignable. ## Acceptance notes - In the prop's union `BaseSchema` (`type: string`) still accepts any node structurally, so these types judge each node's own keys and values (bag members, `kind`, `items`) but do not make a member's required keys required. A page document without `label` still compiles against the prop. Requiredness stays the validator's. - `SchemaNode` is not widened. Measured: with the signature present, a reader of a widened `SchemaNode` that reads an undeclared key (`columns`) turns TS2339 on the closed member, where today it compiles through the index signature. A literal nested in a `children` slot therefore still needs its own typing after objectui#8347. - The TS face now types three node kinds that `AnyComponentSchema` still refuses at `type` (`element:text_input`, `element:record_picker`, page documents). That refusal is the population `registered-types-validate-ratchet-10859` counts, not a judgement that the nodes are invalid. - Finding, not carded: the spec's `PageComponentSchema` normalises `visibleWhen` into a `{ dialect: 'cel', source }` envelope at parse, the renderer evaluates it, and `AnyComponentSchema` refuses the envelope (`invalid_type` at `visibleWhen`). Measured through the built validator. Same root as open question 2. - Not carded: AGENTS.md objectstack-ai#4 still teaches the flat `action:button` executor spelling that objectui#11183 moved into the bag. That file is governed; objectui#11183's lane carries it. ## Gates (round 1, head `29acb0107`, unless noted) - Build: `pnpm --filter '@object-ui/components^...' build` exit 0 (at `11eaa8ae5`, after the merge); `@object-ui/types` and `@object-ui/react` builds exit 0 at `29acb0107`. - `type-check` (script names echoed, `tsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json` for types, `tsc --noEmit && tsc -p tsconfig.test.json` for react and components): types 0, react 0, components 0 errors, all exit 0. - vitest: `packages/types/` 313 files, 8000 tests passed; `packages/react/` 106 files, 1389 tests passed (both at `29acb0107`, each part's exit read on its own: 0 and 0). `packages/components/` in three shards: 1/3 117 files (1136 passed, 7 skipped) and 2/3 117 files (1363 passed, 10 skipped) at `11eaa8ae5`; 3/3 115 passed and 1 skipped (988 passed, 7 skipped) at `29acb0107`. The only change between those heads is a type-alias default in `authoring-nodes.ts`, a file with only type declarations. - `check:doc-fences`, `check:doc-types`, `check:new-line-citations` (0 new), `check:control-bytes`, `check:changeset-claims`, `check:pending-changeset-literals`, `check:spec-symbols`, `check:test-path-roots`, `check:phantom-deps`, `check:self-import`, `check:unreferenced-sources`, `check:vi-mock-specifiers`, `check:doc-example-ids`, `check:component-surface-parity`, `check:esm-specifiers`, `check-changeset-presence` (9 source files, 3 packages, 1 changeset), `check-changeset-no-major`: all exit 0. - `check-governed-queue-guard --test` on the 14 paths: NOT GOVERNED. - Lint, narrowed: the 9 touched `.ts`/`.tsx` files (population: `eslint.config.js` `**/*.{ts,tsx}`), `--format json`: head 1 error / 19 warnings; the same 7 pre-existing files at base read 1 error / 19 warnings. The one error is a `no-restricted-imports` on `FormField` in `packages/types/src/index.ts`, present at base. Invariance: `eslint.config.js` sets no `parserOptions.project` / `projectService` (type-aware linting off), so this diff cannot move a verdict on an untouched file. Full `pnpm lint` is CI's. - NOT MEASURED: `check:doc-snippets`, reason: its prerequisite is a 34-package build. Narrowed instead: the two fences this PR adds compile `--strict` against the built `types/dist/authoring-nodes.d.ts` and `react/dist/SchemaRenderer.d.ts` (exit 0, resolution read from `--listFiles`), and a control with `contnet` in the guide fence fails with TS2561. - `check:readme-exports`: exit 1 is its "could not be judged" state (431 self-imports in unbuilt packages); `--list` reads both names this PR's README imports (`ElementTextInputNode`, `PublicBlockNodeOf`) as `real`. `origin/main` was merged in (`edfcf5a5e`, no conflicts, no rebase), and every reading above after the merge line was retaken on the merged tree. --- _Generated by [Claude Code](https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal
pushed a commit
to akarma-synetal/objectui
that referenced
this pull request
Oct 7, 2026
…d by name, toward its `properties` bag (objectui#10872, batch 10) (objectstack-ai#11391) Part of objectstack-ai#10872 Clause-②: no Batch 10 of objectui#10872: the flat-props refusal half. A public block's prop written directly on the node, instead of in its `properties` bag, is now refused by name on both faces of `@object-ui/types/zod`, at its own path, with a message naming `properties.KEY`. The card keeps `record:line_items` (no spec row yet) and the six batch-4 arms' `children` narrowing. Dispatched run, session `https://claude.ai/code/session_01JG2jy8a9su7ia4Hx7zxv42`, claim `5933064114` (`domain:ui` seat 2). Draft, left for the seat. ## The ruling this executes Triage's answer A on objectui#10872 (comment `5906817482`): "The spec's strict page-component schema refuses flat `actionType` / `target` as mis-layered (ADR-0089 D3a)", and "B is not taken. A flat channel in objectui's arms is a second dialect: `objectui validate` would accept what `os validate` refuses." Its precondition, the taught docs moving to the bag, landed with objectui#11183 (completed 2026-10-01). Two teaching sites that move missed are moved here (below). ## Zone 2 readings (measured before the change, objectui `2e060ba`, `@objectstack/spec` 17.5.0) 1. **Both faces accepted flat row keys.** Population: every arm of `PublicBlockComponentSchema` and `ObjectQLPublicBlockComponentSchema` whose `properties` is a spec row: 34 arms. Four of them already refused their flat spelling by name, each with its own map (`object-form`, `object-map`, `object-gantt`: objectui#10859; `object-chart`: objectui#11276). The other 30 (27 public blocks plus `object-metric`, `object-master-detail-form`, `object-timeline`) are this batch. The "29" in the release note was dated: `object-timeline` joined later. Probe: for every row key that is not a node-level key of the spec's page component, `{ type, KEY: v }` for seven values, read per face for an issue at `[KEY]`. - Tolerant face (`safeValidateSchema`, what `objectui validate` runs): 26 arms accepted at least one flat row key; 223 keys passed unjudged. - Strict face (`StrictAnyComponentSchema`): every key `BaseSchema` does not declare was refused, but only as an unnamed `unrecognized_keys`. 14 keys passed outright, judged by `BaseSchema`'s own type, because the row member shares a name with a base key: `visible` (`record:alert`, the four `action:` blocks), `disabled` (`element:button`, `action:button`, `action:icon`), `name` and `description` (`action:button`, `action:icon`), `description` (`object-metric`), `data` (`object-timeline`). - After this change: 0 and 0, on all 34 arms. - The spec's `PageComponentSchema` refuses every one of those keys on the node (`unrecognized_keys`), and accepts `label`. 2. **The pull.** Instrument: the committed scanner `scripts/body-dialect-census.mjs` (`scanNodes`, `keepFencedCodeOnly`, `bucketOf`) under an uncommitted driver. FLAT = a node-level key that is a member of the type's spec row, is not a node-level key of the spec's page component, and is not already refused by name toward `properties`.⚠️ Batch 7's dated reading counted only keys the arm leaves undeclared, so it could not see the base-declared row keys (`visible`, `disabled`, `name`, `description`, `data`); this reading counts them. - objectstack `main` `454bbb6` (depth-1 scratch clone; the shared checkout was not fetched or edited): 9627 files, 675 nodes of the 30 types (572 carry `properties`), **0 flat nodes in every bucket, tests included**. Lit control on the same corpus and mechanism: node-level `responsiveStyles` on 17 example-app nodes of these types, batch 8's reading. - objectui `2e060ba`: 9729 files, 667 nodes of the 30 types. Non-test flat writers: 5 teaching nodes and 8 composed in code. - Teaching: `packages/app-shell/README.md` (two `action:button` route examples, flat `actionType`), `content/docs/guide/slotted-pages.md` (one `record:related_list` example, flat `objectName` / `relationshipField` / `columns` / `actions`), and two pending changesets of other cards (`.changeset/10289-action-params-properties.md`, `.changeset/7867-action-params-templates.md`, flat `actionType`). The first two are moved to the bag in this PR; the changesets are not (Acceptance notes). - Composed in code, never judged by a zod face: `MasterDetailForm` hosts in `plugin-form` (`ObjectForm`, `ModalForm`, `DrawerForm`) and the console's `DevMasterDetail` page (`object-master-detail-form`), `DashboardRenderer` / `DashboardGridLayout` (`object-metric`), `action-bar` (`action:menu`), `ListView` (`object-timeline`). `SchemaRenderer` runs `@object-ui/core`'s structural `validateSchema`, not a zod face, and keeps reading both spellings. - After this PR: the two pending changesets are the only non-test teaching writers left. 3. **The precedent fits, generalised.** objectui#11328 (`object-chart`) refuses each bag member written flat with `aliasKeyRefusal(KEY, 'properties.KEY', …)`, one map per arm, as `object-form` / `object-map` / `object-gantt` do. This batch makes that ONE helper, `flatPropRefusals(type, row)` in `public-blocks.zod.ts`, spread into the 26 public-block arms whose row declares a member and into the three ObjectQL arms. It also carries the two exceptions those maps had per arm: a row key the spec's page component declares on the node (`label`, `aria`, `type`) is left alone, as `object-gantt` leaves `label`; and a row member the spec itself retires keeps the row's own retirement when written flat, as `object-chart`'s three retired spellings do (`page:header`'s `icon`, `page:card`'s `actions`, `record:details`' `layout`). 4. **The html tier is not reached.** No non-test path feeds `sdui-parser` output into a zod face. `objectui validate` reads JSON or YAML (`validate.ts`), and `objectui check` globs `**/*.{json,yaml,yml}`. `SchemaRenderer` runs the structural validator, and `packages/sdui-parser` imports no zod face. Seven test files import both tiers; each judges each tier on its own spelling, and all seven are in the sweep below, green. No `packages/sdui-parser` file is touched. 5. **PR objectui#11069.** Its ratchet counts `invalid_union` at `['type']`; no arm joins or leaves, so the count does not move (the `packages/cli` suite, ratchet included, is green). One thing it should account for when it un-parks: on the strict face these flat keys now report `invalid_type` at the key, with the by-name message, instead of `unrecognized_keys`, so its `findUndeclaredKeys` (which reads `unrecognized_keys`) will not list them on its undeclared-key line. The zod message names each one. ## What changed - `packages/types/src/zod/public-blocks.zod.ts`: `flatPropRefusals` and its message composer; `PAGE_COMPONENT_NODE_KEYS`, the spec page component's node-level keys (transcribed; the pin re-derives them from the installed spec's own shape); one spread line in each of the 26 arms, placed BEFORE the arm's own members, so an arm's own refusal of a row key keeps its message (`record:alert`'s `body`, the `action:` controls' `onSuccess`, the content-channel tombstones). Module and `record:alert` docblocks updated. - `packages/types/src/zod/objectql.zod.ts`: the same spread in `object-metric`, `object-master-detail-form` and `object-timeline`; their docblocks, and the `object-form` sentence "Unlike the blocks above", which this change made false. - `packages/types/src/zod/README.md`: two lines. - Nothing at render time moves, and no renderer is touched. The helper is internal: not re-exported from `@object-ui/types/zod`, and the package has no deep `./zod/*` export. ## Producer side (bounded in-place fix, declared) The two teaching sites objectui#11183's move missed now write the bag: the `record:related_list` actions example in `content/docs/guide/slotted-pages.md`, and the two `action:button` route examples in `packages/app-shell/README.md`. Each migrated snippet parses on both faces and on the spec's page component, and its flat spelling is refused by name (probe, not committed). The four conditions hold: the same defect class as objectui#11183 (taught flat props on a public block); a mechanical move whose shape AGENTS.md objectstack-ai#4 pins; no open PR touches either file; docs only, so no new gate surface. Neither file is governed. ## Pins - `packages/types/src/__tests__/flat-props-refusal-10872.test.ts` (new): the population read off the live unions, closed over the 30 covered arms plus the four earlier ones; every owed row key refused on both faces for five values, at its own path, with `invalid_type` and a message naming `properties.KEY` (retired members excepted); per arm a RED row (its known-good bag written flat is refused by exactly the owed keys, each named, with no `unrecognized_keys` left) and a GREEN control (the same bag in `properties` parses on both faces); the spec's page component refusing the same flat keys; what is left alone (node-level row keys, retired members by reference, the arms' own refusals); the earlier four arms holding the same invariant; nested nodes in a node-level slot and in a bag child list refused at their own paths. - `packages/types/src/__tests__/held-public-block-arms-10872.test.ts`: the batch-4 CONTROL on the taught node's flat spelling asserted the old state (strict face: an unnamed `unrecognized_keys`; tolerant face: success). It now asserts both faces refuse exactly `actionType` and `target` by name, and keeps the spec reading in its own row. - Ablation, implementation committed first, through objectstack `scripts/ablation-replace.mjs` in WRAP mode plus a shell trap restoring by absolute path. Anchor: the helper's key filter, replaced by a filter that keeps no key (marker `ABLATION-10872-B10`), so no arm gets a generated refusal and every explicit member stays. On disk: anchor 1 to 0, marker 0 to 1, blob `42a567fcff9e` to `a6e95cc943d1`. Result: `Tests 258 failed | 145 passed (403)`, red as predicted. The red rows: 223 per-key rows (the base probe's 223), 26 per-arm RED rows (all but `element:divider` and the three thin `page:` containers, whose only owed key is overridden), 3 retired-member rows, 4 nested rows, 2 batch-4 CONTROL rows. All GREEN controls stayed green. Restore: blob after restore == HEAD blob `42a567fcff9e`, `git diff HEAD` empty. The target and both pin files are blob-identical between the ablation commit `2cc5b0e` and the head. - Reverse type check through the rebuilt dist: a scratch file in `packages/cli` (resolving `@object-ui/types/zod` to `packages/types/dist/zod/index.zod.d.ts`) assigning `{ type: 'action:button', actionType: 'url' }` to the arm's input type got exactly one error, `TS2322: Type 'string' is not assignable to type 'undefined'`, on that line. The bag spelling compiled. The file was deleted, and the type-check is clean again. A first attempt imported `zod`, which `packages/cli` does not depend on, and failed on TS2307 instead: it measured nothing and is not counted. ## Gates (head `401d406`, after merging `main` at `8673402a3f`, which carries objectui#11384, objectui#11385, objectui#11386 and objectui#11387) - `pnpm --filter @object-ui/types build`: dist completeness 1 package complete. `pnpm --filter @object-ui/types type-check`: exit 0 (its test program lists both pin files). One lock hold with these two and the next two joined by `&&`: VERDICT command-exit 0. - `pnpm exec vitest run packages/types/`: `Test Files 316 passed (316)`, `Tests 8371 passed (8371)`. `pnpm exec vitest run packages/cli/`: `24 passed (24)`, `338 passed (338)`. - `pnpm --filter @object-ui/cli type-check`: exit 0, after `pnpm --filter '@object-ui/cli^...' build` (9 of 47 projects; the rebuilt `public-blocks.zod.d.ts` carries the helper). `packages/cli` is the one package whose source reads these arms' types. - Consumer sweep, 168 test files outside `packages/types` (re-derived after the last merge): every test naming a face, an arm, `@object-ui/types/zod`, the edited markdown or a changeset reader, plus the cli suite and the arm-list consumers the batch-1 ruling names. `81 passed (81)` / `1847 passed` and `87 passed (87)` / `2126 passed`. The same matrix was green at `da80d90` before the last merge (166 files, 3955 tests). - Exit 0: `check:spec-symbols`, `check:component-surface-parity` (report-only; it skips `zod/`), `check:new-line-citations` (0 new citations), `check:control-bytes`, `check-changeset-presence` (1 changeset declared), `changeset:check`, `check:handler-key-reads` (140 arms, 56 judged), `check:changeset-claims` (report-only; 23 pending changesets naming a touched file, each read; the four this change falsifies get dated notes, below), `check:pending-changeset-literals`, `check:test-path-roots`, `markdown-test-inputs.mjs --audit` (all adjudicated), `check:doc-types`, `check-doc-links`. - NOT MEASURED: `check:doc-snippets` and `check:doc-examples`, both exit 2, PRECONDITION NOT MET (34 packages unbuilt). This diff changes no `ts` / `tsx` fence and no `@example`, and no doc fence references a public-block arm's type. CI builds and runs both. Also NOT MEASURED: `check:readme-exports` (needs every package's dist); the README edits add no fence or import. - eslint, declared narrowing: the 4 changed TS files, `--no-inline-config --format json`: 4 files, 0 errors, 1 warning, a pre-existing `no-explicit-any` in `objectql.zod.ts` outside this diff's hunks. `--print-config` shows no `project` / `projectService`, so linting is not type-aware and no untouched file's verdict can move. Repo-wide `pnpm lint` is CI's. - `check-governed-queue-guard --test` over the 12 paths: NOT GOVERNED. ## Changesets - `.changeset/10872-flat-props-refusal.md`: `'@object-ui/types': minor`, the objectui#11328 and batch-5/6 precedent. The arms are unreleased (`@object-ui/types` 17.6.0 on npm has no public-block arm: 0 hits for `PublicBlockComponentSchema` in its dist, against 4 for `AnyComponentSchema`), so across the release the accept set for these types only widens. - Dated notes, frontmatter byte-identical (md5 of the first four lines unchanged): `10872-public-block-zod-arms.md`, `10872-held-public-block-arms.md`, `10859-object-timeline-zod-arm.md`, `10859-pivot-object-block-zod-arms.md`. Each stated that a flat key passes the tolerant face unjudged, or that a base-declared row key is judged by the base. - `Clause-②: no` is the claim's line. The criterion is whether the card widens an accept set or the public surface, and this one only refuses. The precedent batches 5 and 6 and objectui#11328 declared their narrowings `yes`; that difference is recorded for the seat rather than chosen silently. ## objectui#11385 (landed as `304f611379` during this batch; merged into this branch at `401d406`) No shared file. Its `PublicBlockNode` is each arm's own `z.input` read off the arm's shape, so the TS authoring types and these zod faces describe the same bag by construction. Its pins run on the merged tree and pass: `authoring-nodes-11364.test.ts` is in the `types` test program (type-check exit 0) and in the `types` vitest run. Its two `@ts-expect-error` rows on flat `items` and flat `actionType` stay needed. A type probe against this branch (scratch, deleted; a deliberately false control proved the file was in the program) found that no arm's closed input carries an index signature. Flat `actionType`, `visible` and `title` type as `undefined`, `label` stays a node key, and the bag spelling compiles. Its `SchemaRenderer` hunk is type-only (the `schema` prop union), so the hoist this batch relies on is unchanged.⚠️ **One disagreement, recorded rather than repaired (its files are not this batch's).** The landed `authoring-nodes.ts` builds `ElementTextInputNode` and `ElementRecordPickerNode`, the two spec-row blocks with no zod arm, "the way every arm is built": `BaseSchema.extend({ type, ...NODE_ENVELOPE, properties, body, children })`. After this batch every arm also spreads `flatPropRefusals`, so those two TS types are the one place the bag shape differs. A base-declared row key written flat type-checks there: `placeholder`, `disabled` and `description` on `element:text_input`, and `placeholder` on `element:record_picker` (probe against the installed rows). Their other row keys written flat are refused, but only as excess properties, not by name. No validator disagrees today: `AnyComponentSchema` refuses both types at `type`. When those two get zod arms, the arms should spread `flatPropRefusals`, and the TS types can then derive from the arms. ## Acceptance notes - Two pending changesets of other cards teach the flat `actionType` on `action:button` in their examples: `.changeset/10289-action-params-properties.md` (its Migration block) and `.changeset/7867-action-params-templates.md`. Their claims are about the runtime and stay true, but the examples will freeze into the CHANGELOG at release teaching a spelling `objectui validate` now refuses. Not edited here; carrier: the seat (a dated note before the release, objectui#11183's family). - `object-form`, `object-map`, `object-gantt` and `object-chart` keep their own per-arm maps and messages; folding them into `flatPropRefusals` would change their messages, on cards held out of this round. Carrier: objectui#10859 / objectui#11276. - `AGENTS.md` objectstack-ai#4 and `skills/objectui/rules/protocol.md` say objectui's strict face refuses the flat `actionType` / `target`; both faces now do. The sentence stays true, so no governed file is touched. - The eight in-code composers above still build flat nodes. They are the node as the renderer reads it after the hoist, not authored documents, and no zod face judges them. --- _Generated by [Claude Code](https://claude.ai/code/session_01JG2jy8a9su7ia4Hx7zxv42)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal
pushed a commit
to akarma-synetal/objectui
that referenced
this pull request
Oct 7, 2026
… literal refuses an undeclared key; the compile-fail pin, both directions (objectui#8347) (objectstack-ai#11607) Fixes objectstack-ai#8347 Clause-②: yes (narrowing) `BaseSchema` no longer declares `[key: string]: any`. A node literal annotated with its node type now refuses a key that no declaration names, a misspelled key included, and the correct spelling still compiles. This executes the objectui#7927 ruling ("the TypeScript face is an executable contract"), with the compile-fail pin that ruling made non-negotiable. `ComponentRendererProps` keeps its own signature, and a row in the pin notices if it is ever removed. The zod faces and every runtime path are unchanged, except `visibleWhen` (below). Governed: this diff touches `AGENTS.md` and two published-skill files (`skills/objectui/guides/architecture.md`, `skills/objectui/rules/protocol.md`), so the PR stays a draft until an authorized approval is on it (`node scripts/check-governed-queue-guard.mjs --test` answers GOVERNED for each of them). Session: `https://claude.ai/code/session_01CPvhwGcirXqBGEdPSb72TZ` (PM loop round 5, claim `5977679107`, amended for patch round 3 by `5979679543`). ## The maintainer's one open choice: the `props` alias on the TypeScript face This PR writes **option A**: `props` is not declared on the TypeScript authoring face. The renderer still reads it at runtime (the maintainer's 2026-08-18 ruling binds the runtime and leaves the TS face open). So the two `badge` literals in `components`' `alias-precedence-cross-channel.test.tsx` that author `props` now carry an `@ts-expect-error` naming this choice, and `AGENTS.md` §4's abridged listing drops the `props` member that `BaseSchema` never declared. - **A (written here):** not declared. The dev measurement on objectui#11364 found no non-test TypeScript, tracked JSON or objectstack example that authors a node `props`, and the published skill teaches it as wrong. - **B:** `props?: unknown` on `BaseSchema`. `AuthoringNode` derives from the zod `BaseSchema` shape, so B also needs a zod mirror row and a parity entry. - **C:** declared on `AuthoringNode` only. To flip it, remove the two directives and add the member. Nothing else in this PR depends on the choice. ## What changed, class by class (one commit each) 1. **The two src sites.** `plugin-kanban`: `ObjectKanban` reads the gate-written `sort` (the binding's `dataSource.sort`) through `GateBoundKanbanSchema`, a read type private to the file; `ObjectKanbanSchema` still declares no `sort` (objectui#8174). `plugin-timeline`: `TimelineRenderSchema`, the private hand-off type, declares the `onItemClick` slot `ObjectTimeline` composes; `TimelineSchema` still declares none. Both compile with the signature present or absent. 2. **The mechanical rows.** Two schema-to-record test reads convert through `unknown`; the gantt and map `propertiesBag-10859` fixtures keep `operator` a literal and type the bag node as its block node. 3. **`BaseSchema.visibleWhen`, Q6 = B.** The TS face is the spec's `EvaluatedExpressionInput`, by reference. The zod twin is the same accept set (a string, or the spec's own `EvaluatedExpressionSchema` envelope) with `EvaluatedExpressionInputSchema`'s verdict and without its string transform. 4. **The removal, and everything that can only land with it.** The compile-fail pin; the adjudicated probe and ceiling flips; the two `props` flips; the valueless envelope `metric-card` pinned refused on the TS face (Q3 = A); `envelopeStray` and its `complex.ts` note deleted per the constant's own removal condition; the two `object-view-slot-key-lists` tripwires flipped; `partial-schema-collapse-pin.test.ts` deleted per its header, with `PartialSchema`'s docblock describing the alias as it now works; five mirror-parity ledger entries (below); and the prose in those files that described the signature as live. 5. **Docs.** The 13 listed doc blocks plus the `objectos-integration.mdx` page block (now `regions`, and since round 4 rendered under `SchemaRendererProvider`); the published passages that described the signature as live; the plugin-calendar README note; the plugin-dashboard README widget-key passage; the `examples/dashboard.ts` comment; a dated note on the pending `11564` changeset, whose "until objectui#8347 removes it" sentence this release makes false. 6. **`AGENTS.md`.** §5 #0.1 gains the rule as shipped: the TS authoring face is part of the contract, a refused key is declared by reference or fixed at the producer and never cast past, and the fresh-literal bound is named with the pin that measures it. §4 drops `props`; objectstack-ai#4's `events` sentence names the face that still keeps one. 7. **The changeset.** `minor` for `@object-ui/types` with the breaking note; `minor` for `plugin-timeline` (`TimelineRenderSchema`, the `schema` prop type of the exported `TimelineRenderer`, gains one optional member); `patch` for `plugin-kanban`. 8. **Patch round 3, after the merge of `main` at `b508ac50`.** The two published-skill sections (H3 below); the five restored deletion guards and the reworded test strings (Acceptance notes); `main` merged by a merge commit, with the type-check of every package `main` moved since the merge base green afterwards. 9. **Patch round 4, doc and comment bytes only.** The integration guide's App block installs the adapter through `SchemaRendererProvider dataSource` (the renderer reads it from `SchemaRendererContext`; a `dataSource` prop on `SchemaRenderer` reaches only the root component), and its system-objects comment names only the provider. The parity ledger's `complex.zod.ts#DashboardWidgetSchema` `component` entry says the mirror slot's `BaseSchema` fallback stays by objectui#8344's decision, with no open card retiring it, instead of reserving it for this card. ## The compile-fail pin, both directions `packages/types/src/__tests__/base-schema-closed-face-8347.test.ts`, read by `tsc -p tsconfig.test.json`: - objectui#7927's P5 on a FRESH literal: `titleFieldd` on a `CalendarViewSchema` literal is an `@ts-expect-error`, and `titleField` compiles. The same on `BaseSchema` itself (`classNmae` against `className`), on a node nested in a slot (`labell` on a `button` child), and on `PartialSchema`. - P5 as objectstack-ai#7927 ran it, on today's plugin-calendar README block, stays green with or without the signature (measured on this card's census): that block is a type alias plus two assignments of declared variables, and tsc never excess-checks those. So the pin uses fresh literals, and the bound itself is pinned as a directive-free row, so nobody reads the file as claiming more. - Ablation, run on the committed head through `ablation-replace.mjs` (anchor hit 1 to 0, on-disk blob moved, trap-restored to blob equal to HEAD, `git diff HEAD` empty): with `[key: string]: any` put back on `BaseSchema`, the types test program exits 2 with 52 errors, and every refusal row of the pin turns red (TS2578 on the four misspelling rows, TS2344 on the two no-index rows). At HEAD the same program exits 0. ## The measurements the dispatch named **H1, the re-census at BASE `2e818d0b5`, before any kept edit.** Instrument: every package built (turbo, forced: 40 of 40, 0 cached); the six `BaseSchema` lines deleted and nothing else; types rebuilt (dist keeps one signature, `ComponentRendererProps`'); then every tsc program the workspace type-check scripts name, one job each (90 jobs, apps/site's generators included, plus the four root programs). | | programs | red | unique errors | src | tests | |---|---|---|---|---|---| | BASE, signature removed alone | 90 | 13 | 81 in 44 files | 3 | 78 | | BASE control, signature present (the same 13) | 13 | 0 | 0 | | | | final head `e0a9af67`, removal kept | 90 | 0 | 0 | 0 | 0 | The 81 fall exactly into the predicted classes: src 3 (`ObjectKanban` `sort` twice, timeline `onItemClick`); tests 67 adjudicated probe and ceiling flips (types 49; charts `xAxisKy` twice and `objectNme`; dashboard `bogusKey` twice and `zzUndeclared7353` four times; grid `bogusKeyRefusedNowhere` and two `Expect` wrappers; list `visibleWhn` twice; map `enableClusterng`; tree `zzplTreeAbsentControl8655`, `objectNaem`; components `content` on `label`), 2 `props` flips, 3 mechanical rows (two schema-to-record casts, one mirror-parity ledger mismatch), and the 6 gantt and map fixture literals. No site needed a contract decision. `check:doc-snippets` under the removal at BASE: 14 of 777 blocks failed, the 13 listed plus the `objectos-integration.mdx` page block (BASE control: 0 failed); doc-examples, doc-types and skill-examples stayed green. **The parity ledger.** The mismatch was five pairs whose member is typed with an `interface` (`AppSchemaRendererNodeSchema.schema`; the `reports` pairs' `report`, `sections`, `chart`) against a `.passthrough()` mirror whose `z.input` carries a string index signature. TypeScript grants an implicit index signature to anonymous and mapped types and never to an interface. Measured: the declared type is refused, and a one-level anonymous copy of it fits the same mirror input. They are ledgered as an artifact of the assignability test, with that reading in each entry's docblock, and the header's derived figures moved with them. **H2, the objectui#11573 headroom pin.** `AnyComponentSchema`'s declaration on the compiler's counter: 12260 before, 12974 after (the `visibleWhen` twin), limit 900000. Read through a trap-deleted copy of the pin that writes its reading. **H3, skill fences.** `check:skill-examples` exits 0 under the removal at BASE and on every head since. Patch round 3 edits the two `skills/**` files whose prose the removal made false: the architecture guide's "JSON Protocol" section and the protocol rule's "Component Schema Structure" section said the published `BaseSchema` declares its members plus an index signature, and that `props` / `events` / `properties` reach a node through it. Both now state the two verdicts a key no member declares gets, one per face (a compile error in a fresh typed literal on the TypeScript face, with the fresh-literal bound said as AGENTS.md §5 #0.1 says it; kept and judged by the named reader on the tolerant zod face), and their teaching fences drop the three keys `BaseSchema` never declared and end on the same no-index-signature line as AGENTS.md §4. `props` keeps one sentence stating option A, so a flip to B or C is a one-sentence edit. Neither fence is a marked example; `check:skill-examples`, `check:skill-eval-tokens` and `check:skills-paths` stay green. **H4.** The flex rows' header and `examples/dashboard.ts` no longer say "until objectui#8347"; the flex test gains the row that removal promised (a `text` entry misspelled inside the list is refused, and the well-formed list's `text` entry compiles). **H5, `visibleWhen`.** `visible-when-spec-input-8347.test.ts`: the TS member is `Equal` to the spec's `EvaluatedExpressionInput`; on 13 probe values the mirror's verdict equals `EvaluatedExpressionInputSchema`'s and `PageComponentSchema.visibleWhen`'s, read in the same run (the probe set holds both verdicts); a blank string is refused with the spec's own sentence; a string parses to the same string, while the spec rewrites it (the control); an envelope parses to itself; and the envelope a spec parse writes is accepted by both component faces, a dialect-less one refused.⚠️ This is a narrowing as well as a widening on the tolerant face: a blank `visibleWhen` string used to parse and is refused now, as the spec refuses it. ## Gates, on head `e0a9af67` The dev's local readings on that head. The gate verdict on the current head is its own check-runs. - type-check, every program (the H1 instrument): 90 of 90 exit 0, 0 errors. - `pnpm --filter @object-ui/types build`: exit 0. `pnpm --filter @object-ui/types test`: exit 0, `Test Files 353 passed (353)`, `Tests 9419 passed (9419)`. - vitest over the edited plugin packages (kanban, timeline, gantt, map, charts, dashboard, grid, list, tree), on the same file contents: exit 0, `Test Files 819 passed | 1 skipped (820)`. `components`: see the report. - `check:doc-snippets` `777 of 777 block(s) judged, 0 failed.`; `check:doc-examples` `35 compile, 87 fail, 87 of those declared`; `check:doc-types`, `check:skill-examples`, `check:readme-exports`, `check:spec-symbols`, `check:changeset-claims`, `check:pending-changeset-literals`, `check:control-bytes`, `changeset:check`, `type-check:coverage`: exit 0. `check:new-line-citations`: `0 new citation(s)`. - `check-governed-queue-guard.mjs --test` over the changed paths: GOVERNED (`AGENTS.md`; since patch round 3 also the two `skills/**` files). ## Overlap PR objectstack-ai#11069 (draft) edits `content/docs/guide/architecture.md`'s paragraph on a button's text key. This PR edits the same file's Type Safety block (the button literal writes `label`). Region-disjoint; whichever lands second merges `main`. ## Acceptance notes (read, not filed) - `ObjectChartBlock`'s pre-gate `schema` prop is `BaseSchema` (ruling `5810232641`). A fresh literal at that prop is now excess-checked against `BaseSchema` itself, so a correctly spelled `objectName` is refused there as much as the `objectNme` probe; in-tree callers pass typed nodes. The flipped probe's comment says so. - plugin-grid's `RemoveIndexSignature` in `ObjectGrid.tsx` strips nothing now, as its own test predicted; retiring it is cleanup left out of this PR. - `object-view-slot-key-lists`' `form` `Pick` list may move back to `Omit`, per its header's removal condition; not done here. - `PartialSchema` has no consumer outside its declaration (a tracked-files `git grep` for the name, with `SchemaByType` as the positive control). The enforce-or-remove card the deleted pin's header asks for is filed: objectui#11608 (`Blocked-by: objectstack-ai#8347`). - Source comments, test comments, docs and the published skill that described the signature as live, or said objectui#8347 removes it, are repaired in this PR (patch rounds 2 and 3). The enumerating commands are `git grep -n -i "index signature"`, `git grep -n -F '[key: string]: any'` and `git grep -n 'objectui#8347 remove'` over `packages/`, `apps/`, `examples/`, `content/docs/` and `skills/`, CHANGELOGs and release notes excluded as append-only. What they still find is true at the head: other types' own signatures (`ComponentRendererProps`, `AuthUser`, `FormField`, a widget's own props type, the field-widget `data-` attribute signature, `QueryParams`' statements), the tolerant zod face's `.passthrough()`, and past-tense history. - The deletion guards the removal silenced are restored (patch round 3). While the signature stood, an `@ts-expect-error` over a tombstone or a typed member also failed (TS2578) when its member was deleted; since the removal the undeclared key satisfies the directive. `object-kanban-card-title-9606`, `object-grid-default-sort-tombstone-5861`, `object-grid-show-filters-retired-11068`, `object-grid-declared-keys-11068` and `gantt-flat-config-declared-keys` each gain a type-level row that is a compile error when its member is deleted and pins the declared or tombstone type; each was shown red by a trap-restored deletion of its member and green at HEAD (readings on the card). Three test titles and one assertion message that described the signature as live are reworded, assertion logic untouched. ## 维护者速读(草稿) **改了什么**:`@object-ui/types` 的 `BaseSchema` 去掉了 `[key: string]: any`。从此用 TypeScript 写节点时,拼错或未声明的键会直接编译报错,正确拼写照常通过;新增的钉子测试两个方向都钉住,并用"把签名加回去、钉子变红"的消融证明它真能失败。顺带:`visibleWhen` 改为直接引用 spec 的输入类型(字符串或 `{ dialect, source }` 信封);看板与时间轴两处运行期键改为在各自包内私有类型上声明;一批文档示例与说明改成在新约束下成立;AGENTS.md §5 #0.1 写明"TS 编写面也是契约"。已发布技能包(`skills/objectui`)里两处讲 `BaseSchema` 形状的教学文案同步改写——它们原先说该类型带索引签名、`props`/`events` 由签名承接,去掉签名后已不成立;因此本 PR 的受管面除 AGENTS.md 外还含 `skills/**`,同一份批准覆盖两者。 **为什么改**:这是 objectui#7927 的裁决(TS 面是可执行契约)的落地,也是本卡的全部范围。此前签名让任何拼写都能编译,AI 批量写错元数据时被静默放过。 **风险与代价(含回滚)**:对 TS 使用者是破坏性收窄(按仓规以 `minor` 发布并在 changeset 写明);运行期与 zod 宽容面不变,唯一例外是 `visibleWhen` 现在拒绝空字符串、接受 spec 的信封。已知边界:TS 只对"新鲜对象字面量"做多余键检查,经变量转手的值不会再查,已在 AGENTS.md 与钉子里写明。回滚:还原本 PR 即可,无数据迁移。 **席位意见**: **你要做的**:1)裁决 `props` 别名在 TS 面上的去留:本 PR 按方案 A(不声明)写,B 是在 `BaseSchema` 上声明 `props?: unknown`,C 是只在 `AuthoringNode` 上声明;改选只需删两条 `@ts-expect-error` 并加成员。2)批准后由认领席落地(受管面,草稿态等你的批准)。 --- _Generated by [Claude Code](https://claude.ai/code/session_01CPvhwGcirXqBGEdPSb72TZ)_ --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: os-zhuang <jack@objectstack.ai>
This was referenced Oct 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Components collapsed into invisible heaps when dragged into the designer due to missing initial dimensions and content.
Changes
Added
defaultPropsto 40+ component registrations across:Example before/after:
The Canvas component already spreads
defaultPropswhen creating nodes (lines 67-71), so no designer changes needed.Tests added to verify all components have defaults and maintain proper structure.
Original prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.