Skip to content

feat(spec): object-map, object-gantt and object-tree get ComponentPropsMap rows, derived from the renderers' read points (objectui#8348 Q1-C) - #18403

Merged
os-warren merged 7 commits into
mainfrom
claude/issue-18305-component-props-map-map-gantt-tree
Sep 16, 2026
Merged

os-warren merged 7 commits into
mainfrom
claude/issue-18305-component-props-map-map-gantt-tree

Conversation

@os-warren

@os-warren os-warren commented Sep 16, 2026

Copy link
Copy Markdown
Collaborator

Fixes #18305

Executes the objectui#8348 ruling 「8348 以协议为准」 (decision batch #83, 2026-09-08) and batch #136 item 3 (Q1-C, maintainer 「同意」): ComponentPropsMap gains object-map, object-gantt and object-tree, each row's key set derived the #7751 way — from the objectui renderer's own read points, at the .objectui-sha pin 53ded82b.

Not derived from @object-ui/types' mirror. That mirror standing in as the authority for map and gantt is the defect this card closes, and for object-tree the mirror is measurably wrong about four keys (below).

The #7751 derivation table, extended by three rows

Every citation is a line in the objectui checkout at 53ded82b. The shared record-source ladder is resolveRecordSourceConfig in core/src/utils/record-source.ts — rung 1 data (:151, returned VERBATIM as a ViewData), rung 2 staticData (:155, wrapped into { provider: 'value' }), rung 3 objectName (:162).

object-mapplugin-map/src/ObjectMap.tsx, registry shell plugin-map/src/index.tsx

key read point declared as
objectName ladder rung 3; :793 (fetch dep), :890 (navigation) z.string().optional()
data :169 array-shorthand head, then resolveRecordSourceConfig at :174 ViewDataSchema
staticData ladder rung 2 z.array(z.unknown())
filter :742$filter: schema.filter z.array(ViewFilterRuleSchema)
sort :743$orderby: convertSortToQueryParams(schema.sort) z.array(SortItemSchema)
map :370getMapConfig branch 1, the author face; the registration's declared { name: 'map', type: 'object' } input z.unknown() (see note)
mapStyle :365schema.mapStyle || schema.map?.style z.string()
navigation :889useNavigationOverlay z.unknown()
enableClustering :905enableClustering ?? (schema.enableClustering || …) z.boolean()

Measured and deliberately NOT declared: the flat map-config spellings getMapConfig branch 2 reads at :382-396 (locationField, latitudeField, longitudeField, titleField, descriptionField, zoom, center) — the ObjectView/ListView flatten product, ruled an internal transport form, not a second authoring surface (maintainer ruling objectui#5018, 2026-08-17); style, which :283 reads ONLY to say it is not consumed as a map style (objectui#5017) and which is the node's own inline CSS record; and the React props of ObjectMapProps (clusterRadius, the data ARRAY prop, the four callbacks, className, dataSource).

object-ganttplugin-gantt/src/ObjectGantt.tsx, registry shell plugin-gantt/src/index.tsx

key read point declared as
objectName ladder rung 3; :1359 (layout key), :1491 (navigation), :1873 (export name) z.string().optional()
data resolveRecordSourceConfig at :593 ViewDataSchema
staticData ladder rung 2 z.array(z.unknown())
filter :738$filter: schema.filter z.array(ViewFilterRuleSchema)
sort :739$orderby: convertSortToQueryParams(schema.sort) z.array(SortItemSchema)
gantt :499-501getGanttConfig branch 1; the registration's { name: 'gantt', type: 'object' } input, and :501 validates it against this repo's OWN GanttConfigSchema GanttConfigSchema
navigation :1487schema.navigation ?? { mode: 'drawer' } z.unknown()
label :1871resolveInlineI18nLabel(schema.label, displayLocale) in the export-file-name chain (:1849 is the comment ABOVE that chain, not a read) I18nLabelSchema
skipWeekends :1205!!sw into the working calendar z.boolean()
holidays :1206new Set(hol) for the duration math z.array(z.string())
persistLayout :1357schema.persistLayout === false z.boolean()
viewName :1359 — the objectName:viewName storage key z.string()
markers :1826markers={schema.markers} z.array(z.unknown())
criticalPath :1829criticalPathDefault={!!schema.criticalPath} z.boolean()
showBaselines :1832schema.showBaselines !== false z.boolean()
readOnly :1833, :1916!!schema.readOnly z.boolean()
mobileReadOnly :1834schema.mobileReadOnly !== false z.boolean()

Measured and deliberately NOT declared: the 30 flat GanttConfig spellings branch 2 reads at :513-543 (objectui#6469 inherited the objectui#5018 ruling for this block); title, which this renderer never reads; and a row cap — the reload's $top is the platform ceiling NON_GRID_ROW_CEILING_TOP, which the renderer's own comment marks 「⛔ Not authorable」.

object-treeplugin-tree/src/ObjectTree.tsx, registry shell plugin-tree/src/index.tsx

key read point declared as
objectName ladder rung 3; :534, :570, :605 z.string().optional()
data resolveRecordSourceConfig at :359 — rung 1, returned VERBATIM as a ViewData. That ONE site is the whole support for this arm ViewDataSchema
staticData ladder rung 2 z.array(z.unknown())
filter :474$filter: schema.filter z.array(ViewFilterRuleSchema)
tree :108 — the nested block getTreeConfig reads; the registration's { name: 'tree', type: 'object' } input TreeConfigSchema
navigation :591useNavigationOverlay({ navigation: (schema as any).navigation }) z.unknown()

data IS on the row, and that is the measurement, not family symmetry. The card left this conditional — 「if the renderer's authored surface has no data, the row declares none」 — and the condition is FALSE here: the renderer reaches schema.data through the shared ladder at :359, which returns it verbatim as a ViewData. ⛔ :496 is not a second site for that arm, and an earlier revision of this body over-claimed it as one: (rest as any).data ?? (schema as any).data is gated by Array.isArray(passed) on the very next line, so it honours only the bare-ARRAY shorthand this row refuses — the same shorthand object-map measures and declines above. One ladder site is sufficient and :359 is it, so the row stands on a corrected citation. What is absent is the DECLARATION, on both published faces: ObjectTreeSchema on @object-ui/types at this pin declares no data, no staticData, no filter and no navigation at all, and makes objectName required — four keys the renderer reads. That is precisely the mirror-is-wrong shape the ruling puts the protocol in front of, so the row follows the read points and the mirror is the face that has to follow.

No sort. ObjectTree.tsx:473-484 issues $filter, $top and $expand and no $orderby, and nothing else in the file reads an order. A sort door here would publish a key with no read site — the exact defect #7751 exists to remove, in the other direction.

Three derivation decisions, each pinned rather than left to review

  1. The flat config spellings stay unauthorable on all three. ObjectView / ListView build these nodes by spreading options.map / options.gantt / options.tree's CONTENTS at the top level, carrying no block key at all; that is an internal transport form (objectui#5018, inherited by objectui#6469), and the map and gantt renderers name every flat key beside a present block as IGNORED in a dev warning. Writing one now gets a wrong-layer prescription naming the config block — the channel object-calendar already uses. Each set is held equal to the config block it points at (ListMapConfigSchema.shape; GanttConfigSchema.shape plus the legacy singular dependencyField; TreeConfigSchema.shape plus titleField, which getTreeConfig:117 reads only as labelField's last fallback), so it cannot drift silently.
  2. filter and sort are the family's one orthography from birthViewFilterRule[] (ui#6206-B reaching the family: finding(spec): four object-* filter doors in ComponentPropsMap declare z.unknown() — no orthography at all — so they still accept the MongoDB-style record ui#6206-B retired, silently; the card that closed the last FilterConditionSchema door assumed they declared the array #15449, batch Refactor: Separate authentication configuration from identity data models #55 option A) and SortItem[] (objectui#8221, batch 🔗 Broken links detected in documentation #77 option B). A new door on a family-wide ruling has no "measured before the ruling" arm. The whole-map census pins in component.test.ts already demanded it: every filter door must refuse the record form, and only record:related_list may take a string sort.
  3. Config-block VALUES follow the read point. gantt takes GanttConfigSchema because ObjectGantt.tsx:501 literally validates the authored block against that schema, imported from @objectstack/spec/ui. tree takes TreeConfigSchema, which spec: GanttConfigSchema is strictObject(...).passthrough(), so a mistyped gantt key is silently accepted — the only one of the three view config schemas that does not refuse it #15469 shut against unknown keys on this very measurement, re-measured unchanged here. map stays z.unknown() — and that is measured, not conservative-by-default: the spec's own ListMapConfigSchema is strict and declares no style, the key getMapConfig:365 reads at schema.map?.style, so pointing this door at it would refuse a value the renderer honours today. The pin records the reason so the later value ratchet has one.

Measured file face

13 files, all additive to a published surface; nothing retired, nothing narrowed.

.changeset/18305-component-props-map-map-gantt-tree.md      new
packages/spec/src/ui/component.zod.ts                       +3 schemas, +3 rows, +3 guidance sets
packages/spec/src/ui/component.test.ts                      +1 describe, 4 door lists extended
packages/spec/src/ui/filter-rule-array-guidance.test.ts     +3 doors (seven -> ten)
packages/spec/src/ui/component-type-vocabulary.test.ts      +1 pin
packages/spec/authorable-surface/ui.json                    +32 rows   (generated)
packages/spec/json-schema.manifest/ui.json                  +3 entries (generated)
packages/spec/api-surface/ui.json                           +9 exports (generated)
packages/spec/export-origins/ui.json                        generated
packages/spec/declaration-map/ui.json                       generated
content/docs/references/ui/component.mdx                    generated
content/docs/references/index.mdx                           generated
docs/audits/2026-07-unknown-key-strictness-ledger.counts.md generated (444 -> 447 sites)

No corpus moves: a whole-tree grep for an authored object-map / object-gantt / object-tree page node finds zero outside sdui.manifest.json (a registry dump, not authored metadata), against a positive control that finds object-grid authored twice in examples/app-showcase. PageComponentSchema.type already accepted all three through its open string arm and still does; what changes is that an authored props bag on one of them is judged instead of skipped.

Verification

All readings below are at e7967ce543 (the branch tip) unless a row says otherwise, exit codes captured by redirect-then-$?, never through a pipe. The spec suite, typecheck and the generated-artifact checks were re-run after the patch round below.

run verdict
pnpm --filter @objectstack/spec build exit 0
pnpm --filter @objectstack/spec test exit 0 — 482 files, 13727 tests passed
pnpm --filter @objectstack/spec typecheck exit 0 (tsc --noEmit + check:scripts-typecheck + check:test-typecheck)
pnpm --filter @objectstack/lint test exit 0 — 103 files, 3821 passed, 5 skipped (the ComponentPropsMap consumer; its closure built first)
pnpm --filter @objectstack/lint typecheck exit 0
pnpm --filter @objectstack/spec check:generated exit 0 after --fix regenerated the 5 it proved stale
MANIFEST=… check:react-declaration-parity --baseline … --strict exit 0 — object-map 2 both / 7 spec-only / 0 registry-only, object-gantt 2 both / 14 spec-only / 0 registry-only; baseline ratchet: no new divergence
pnpm exec eslint . --no-inline-config --format json exit 0 — 6786 files, 0 errors, 0 warnings, repo-wide, no narrowing
47 further derived gates (scripts/pm/dispatch-gates.mjs --commands) all exit 0 — see the acceptance notes for the one that did not

packages/spec has no workspace dependencies, so the dependency-closure build is an empty run by construction.

CI on this head is not enumerated here and is not claimed green — the verdicts above are local runs only.

Patch round — the at-tier review's two text findings, and the citations

The contract review returned PASS; these are the three things its scope did not settle, all of them text or citation, with no key, no value shape and no row membership moved. Verified after the round rather than asserted: the three key sets read back identical, ComponentPropsMap still carries 48 rows, object-tree still accepts the ViewData object arm and still refuses the bare array, the gantt block is still strict, object-chart is still absent.

  1. object-gantt's describes said "chart". Every sibling names its own noun, and object-chart is the row this section deliberately leaves absent, so "chart" pointed an author at the one block with no row. Three describes now name gantt (objectName, label, readOnly). Proof on the shipping artifactcontent/docs/references/ui/component.mdx, regenerated with the repo's own gen:docs, never by hand: Object this chart binds to0; Object this gantt binds to1, at :398; control Object this NOUN binds to6, unchanged, so all six blocks name their own noun exactly once.
  2. object-tree's objectName gave a reason that is false for tree. It said the component-level dataSource binding can supply the object. Measured at the pin 53ded82b: plugin-tree/src/index.tsx has 0 hits for the ElementDataSource* wiring, against 7 each in plugin-map, plugin-gantt, plugin-grid and plugin-calendar — four controls, so the zero discriminates. The key stays optional; the reason is now the true one, the ladder's first two rungs (data can name the object itself, staticData needs none), plus the explicit negative. In the generated doc the false reason reads 0 hits and the new line 1, at :768.
  3. Two citations corrected, in the tables above: gantt label at :1871 (:1849 is the comment), and tree data on :359 alone.

check:docs was red on e3a256daa3 — source fixed, shipping artifact not yet regenerated — and is green on e7967ce543: check:docs exit 0, "223 generated files in sync with packages/spec"; check:generated exit 0, "All 15 generated artifacts are up to date"; check:authorable-surface exit 0, which is why gen:schema had nothing to write and gen:docs was the only stale artifact. pnpm --filter @objectstack/spec test exit 0, 482 files / 13727 tests, and typecheck exit 0, both re-run after the round. git diff --name-only origin/main...HEAD -- content/docs/releases/0 paths, against a control of 2 under content/docs/references/.

On this head Type Check · source gates is completed / success — the job the red had truncated now reaches its end. The rest of CI is still running and is not claimed green.

Patch round 2 — the at-tier review's FAIL: F1 blocking, F2 and F3 in passing

Review of record: comment 5696414075 on this PR, VERDICT: FAIL at head e7967ce543. That review re-derived the accept set, the public surface, all three key sets, every value posture and the minor level and judged them RIGHT — nothing in this round moves a key, a value shape, a row membership or the level. Text, plus the one regeneration it forces.

F1 (blocking) — six describes named a function that does not exist in the code they describe. The objectName / data / staticData describes on object-gantt and object-tree said the record source is resolved by, or "read FIRST/SECOND by", getDataConfig. Re-measured at the .objectui-sha pin 53ded82b, each file fetched with git show in the same command block as its count:

file at 53ded82b getDataConfig resolveRecordSourceConfig
plugin-gantt/src/ObjectGantt.tsx 0 2 — import :64, call :593
plugin-tree/src/ObjectTree.tsx 0 3 — import :41, call :359, comment :422
plugin-map/src/ObjectMap.tsx (control) 8 3

The control discriminates, and it also settles what the map keeps: ObjectMap.tsx really does hold a local getDataConfig at :135 — the array-shorthand head, delegating to the shared ladder at :174 — so the three map describes are TRUE and keep the name. Gantt and tree reach the shared three-rung ladder directly: resolveRecordSourceConfig in @object-ui/core utils/record-source.ts:146, rungs at :151 (data, returned verbatim), :155 (staticData, wrapped as { provider: 'value', items }) and :162 (objectName, folded to { provider: 'object', object }). So the ladder ORDER those six sentences describe was already true and only the name was wrong; the six now name resolveRecordSourceConfig. Neither renderer has any reader above its ladder call, so "read FIRST" is literal for both — unlike the map, whose array head runs first, which is the second reason its describes keep the wrapper's name.

Both sides counted, because this trap already bit this PR once. An earlier round fixed a false describe in the source alone and check:docs caught the generated doc still carrying the old text. After gen:docs, at c431c22e05:

getDataConfig resolveRecordSourceConfig
packages/spec/src/ui/component.zod.ts 3 — map only, :3226 :3242 :3244 11
content/docs/references/ui/component.mdx 3 — map rows only, :638-640 6 — gantt :398-400, tree :768-770

Both sides read 9 and 0 before the round. The generated diff is exactly six table rows; no map row moved.

F2component.zod.ts:3545, the tree data field JSDoc, cited two read points where the schema header at :3487 already rules that :496 is not a second site for the object arm. It cites :359 alone now. Source comment; no artifact follows it.

F3 — two citations off by one line, both re-read at the pin: getGanttConfig's safeParse is ObjectGantt.tsx:500 (:501 is the if (!result.success) beneath it), and the tree's "Not authorable" marker is ObjectTree.tsx:482, not :477.

Verification for this round

  • pnpm --filter @objectstack/spec build, then check:generated — exit 0, all 15 artifacts up to date including check:docs, with content/docs/references/ui/component.mdx actually regenerated by gen:docs rather than hand-edited. Before the regeneration the same gate named that one artifact, and only it, stale.
  • pnpm --filter @objectstack/spec test — exit 0, 482 files / 13727 tests. pnpm --filter @objectstack/spec typecheck — exit 0.
  • check:objectui-pin-citations — exit 0, 26 asserting pin citations match 53ded82bf. It verifies the sha, not the line numbers, and reports 0 anchor content assertions here: F3 was measured by hand at the pin, not caught by this gate.
  • 34 of the 109 gate families dispatch-gates derives for this change set were run locally, every one exit 0; the remaining 75 are the repo-wide farm CI owns. A declared narrowing, not a silent skip.
  • pnpm lint narrowed to the diff, with the three readings that make the narrowing a measurement: the population is eslint's own **/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs} and eslint itself answers the regenerated .mdx with "File ignored because no matching configuration was supplied", so the linted population of this diff is one file; --format json reports 1 file, 0 errors, 0 warnings; and no type-aware linting is configured anywhere in eslint.config.mjs (no projectService, no parserOptions.project), so a comment-and-describe edit in one file cannot move any untouched file's verdict.

Acceptance notes

Docs Drift Check, re-verified rather than waved through

The drift check flagged 6 hand-written pages. Each was re-read; none needs an edit, and the readings are here rather than implied by their absence from the diff.

  • content/docs/protocol/objectui/layout-dsl.mdx does not ride this PR — measured, not assumed. It is flagged via the symbol ComponentPropsMap, which it names exactly once, at :721, and as a MECHANISM: "For the platform's own types the authoring rules dispatch ComponentPropsMap and reject a misspelled prop; a custom.* type has no entry there." That sentence is still exactly true after this change — three more types now have entries, and it enumerates none. The page carries no register of the object-block family at all: no table, no list, and its only numeric completeness claim is about breakpoints (:579). The discriminating control: object-calendar and object-form each get 0 hits on that page, and both have carried ComponentPropsMap rows since A typo'd key inside a page component's properties is accepted in silence — the props bag is a passthrough record and the SDUI props gate has no schema to dispatch for object-* blocks #7751 landed on 2026-08-12 — so the page already omits 2 of the 6 pre-existing rows, against a positive control of object-metric (3), object-master-detail-form (3), object-kanban (2), object-grid (1). A page that has never tracked the row set is not a list this change makes incomplete; its ### Metric Widget / ### Master-Detail Forms / ### Kanban Board sections are chosen worked examples. Writing three new hand-written renderer sections would be new documentation of three renderers, which is a card, not a rider on "add three rows".
  • The other five pages are literal-name collisions, not semantic links. content/docs/ui/views.mdx, concepts/architecture.mdx, getting-started/common-patterns.mdx, protocol/kernel/i18n-standard.mdx and ui/field-grouping-and-order.mdx each get 0 hits for ComponentPropsMap and 0 for object-map / object-gantt / object-tree. They are reached only through string literals that this PR's new OBJECT_GANTT_FLAT_CONFIG_GUIDANCE key array happens to contain — groupByField, timeZone, colorField, startDateField and friends. views.mdx documents the LIST-VIEW gantt block (type: 'gantt' with gantt: { startDateField, … }, :340-352), whose schema is ListViewSchema.gantt / GanttConfigSchema in view.zod.ts — a file this diff does not touch at all, so that page's vocabulary is unmoved.
  • Release-owned pages: untouched, with a control. The drift check names 7 pages under content/docs/releases/; they are read-only. git diff --name-only origin/main...HEAD -- content/docs/releases/ returns 0 paths, against a control of 2 paths under content/docs/references/ in the same diff, so the zero is a reading and not an empty filter.

component-reference-rail.test.ts needs no change — the reading, not the omission

The card names it among "the vocabulary tests that pin the row set". Measured on this branch: the file is 164 lines, mentions ComponentPropsMap 4 times, and every one of them is the single subscript ['record:reference_rail'] or prose about that one row (:3, :21, :33, :35). Object.keys(ComponentPropsMap)0 hits; any object- type literal → 0 hits. Control, the same two probes on component-type-vocabulary.test.ts, which genuinely does pin the row set: 6 and 8 ⇒ the zeros discriminate. All twelve of its describe/it subjects are record:reference_rail behaviour. It reads no property of the row SET, so nothing in it can move when three object-* rows land — and it passed unchanged inside the 482 green spec files above.

The row-set pins that DO move with the rows are component.test.ts (six ruled blocks to nine, plus the filter / sort / plural-filters / optional-objectName door lists, plus a new describe) and component-type-vocabulary.test.ts. The reference rail that regenerates is the docs one, content/docs/references/ui/component.mdx.

Out of scope for this PR, noted rather than fixed

Each with the measurement that found it:

  • ListMapConfigSchema declares no map style at all. The spec's list-view map block is strict and has neither style nor mapStyle, while ObjectMap.tsx:365 reads schema.map?.style and objectui's ObjectMapConfigSchema declares style. ListMapConfigSchema.safeParse({ style: '…' }) is refused — pinned as a negative in this PR's own tests, because it is the reason object-map.map stays z.unknown(). An author cannot declare a map style through the spec's list-view face today. Carrier: this PR's map value ratchet, whenever it is taken.
  • object-tree is absent from the tracked sdui.manifest.json. check:react-declaration-parity prints object-tree: NO component in the manifest — not registered or not public, although plugin-tree/src/index.tsx registers it under both object-tree and tree. The baseline ratchet is unaffected (a block with no baseline row cannot regress), so this PR is green either way, but the new row gets no parity comparison. Carrier: the next sdui:manifest regeneration.
  • pnpm check:cross-package-test-inputs is sensitive to local build state, not to this diff. It exits 1 in a worktree where packages/spec/dist/ exists and 0 where it does not — measured here as a one-variable control, with the diff held constant and the directory restored byte-identically (216 files, dist/ui/index.mjs hash unchanged). It names packages/cli/test/init-created-files-summary.e2e.test.ts, which symlinks packages/spec/dist at :115; this PR touches neither packages/cli nor turbo.json. The Lint & Repo Gates job runs it on an unbuilt tree, which is why CI is green on it.
  • objectui#9239 has moved since the card was written. The card describes it as an open, separate calendar-mirror contradiction; it is closed completed. Nothing in this PR depends on it, and object-calendar is untouched.
  • objectui's own gantt face carries the same false attribution F1 removed from ours, and it is pre-existing. At the same pin 53ded82b, packages/types/src/zod/objectql.zod.ts docblock :726 states getDataConfig is in plugin-gantt/src/ObjectGantt.tsx, and three ObjectGanttSchema describes repeat the name (:738, :739, :838) — while that file has 0 occurrences of it (control: ObjectMap.tsx 8, and the map's own mirror describes at :641-643 are correspondingly TRUE). Separately, core/src/utils/record-source.ts quotes our describes as the ruled contract at :28, :92, :95 and :97 and attributes the text to the map and gantt zod twins, so after this PR that quotation matches the map twin and no longer matches the gantt one. Both sites are objectui's, in objectui's repo; nothing here can fix them and the pin does not move. Handed to the seat to file against objectui rather than fixed in passing.

Not touched, deliberately: the object-chart deliberate-absence note in component.zod.ts, pinned as still absent in the new tests.

objectui#8348 carries pm:blocked on this card; its remaining slice judges map / gantt / tree against these rows once this lands. Nothing here changes any objectui file.

Authored by the domain:spec execution seat, session session_01KB5PFtxuy1x3dcR5gxudx6.


Generated by Claude Code

…psMap rows, derived from the renderers' read points

WIP: the three rows plus their flat-config guidance sets. Key sets measured
from objectui's plugin-map / plugin-gantt / plugin-tree at the .objectui-sha
pin 53ded82b, per the #7751 derivation method.

Claude-Session: https://claude.ai/code/session_01KB5PFtxuy1x3dcR5gxudx6
Co-authored-by: Claude <noreply@anthropic.com>
…rescriptions and door lists

Claude-Session: https://claude.ai/code/session_01KB5PFtxuy1x3dcR5gxudx6
Co-authored-by: Claude <noreply@anthropic.com>
… docs and strictness ledger; add the changeset

Claude-Session: https://claude.ai/code/session_01KB5PFtxuy1x3dcR5gxudx6
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/xl documentation Improvements or additions to documentation protocol:ui tests tooling labels Sep 16, 2026
@github-actions

github-actions Bot commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 50 documentable anchor(s). ⚠️ 5 changed file(s) yielded no anchor (packages/spec/api-surface/ui.json, packages/spec/authorable-surface/ui.json, packages/spec/declaration-map/ui.json, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

6 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/concepts/architecture.mdx (via groupByField (literal, a string literal in OBJECT_GANTT_FLAT_CONFIG_GUIDANCE))
  • content/docs/getting-started/common-patterns.mdx (via groupByField (literal, a string literal in OBJECT_GANTT_FLAT_CONFIG_GUIDANCE))
  • content/docs/protocol/kernel/i18n-standard.mdx (via timeZone (literal, a string literal in OBJECT_GANTT_FLAT_CONFIG_GUIDANCE))
  • content/docs/protocol/objectui/layout-dsl.mdx (via ComponentPropsMap (symbol, a top-level const object))
  • content/docs/ui/field-grouping-and-order.mdx (via groupByField (literal, a string literal in OBJECT_GANTT_FLAT_CONFIG_GUIDANCE))
  • content/docs/ui/views.mdx (via colorField (literal, a string literal in OBJECT_GANTT_FLAT_CONFIG_GUIDANCE), dependenciesField (literal, a string literal in OBJECT_GANTT_FLAT_CONFIG_GUIDANCE), endDateField (literal, a string literal in OBJECT_GANTT_FLAT_CONFIG_GUIDANCE; a string literal in examples), groupByField (literal, a string literal in OBJECT_GANTT_FLAT_CONFIG_GUIDANCE), progressField (literal, a string literal in OBJECT_GANTT_FLAT_CONFIG_GUIDANCE), startDateField (literal, a string literal in OBJECT_GANTT_FLAT_CONFIG_GUIDANCE; a string literal in examples), titleField (literal, a string literal in OBJECT_GANTT_FLAT_CONFIG_GUIDANCE; a string literal in OBJECT_MAP_FLAT_CONFIG_GUIDANCE; a string literal in OBJECT_TREE_FLAT_CONFIG_GUIDANCE; a string literal in examples))

7 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v15.mdx (via borderColorField (literal, a string literal in OBJECT_GANTT_FLAT_CONFIG_GUIDANCE), summaryExtent (literal, a string literal in OBJECT_GANTT_FLAT_CONFIG_GUIDANCE))
  • content/docs/releases/v16.mdx (via dependencyTypes (literal, a string literal in OBJECT_GANTT_FLAT_CONFIG_GUIDANCE), groupByField (literal, a string literal in OBJECT_GANTT_FLAT_CONFIG_GUIDANCE))
  • content/docs/releases/v17/17-0.mdx (via descriptionField (literal, a string literal in OBJECT_MAP_FLAT_CONFIG_GUIDANCE))
  • content/docs/releases/v17/17-1.mdx (via ComponentPropsMap (symbol, a top-level const object), descriptionField (literal, a string literal in OBJECT_MAP_FLAT_CONFIG_GUIDANCE), latitudeField (literal, a string literal in OBJECT_MAP_FLAT_CONFIG_GUIDANCE; a string literal in examples), locationField (literal, a string literal in OBJECT_MAP_FLAT_CONFIG_GUIDANCE), longitudeField (literal, a string literal in OBJECT_MAP_FLAT_CONFIG_GUIDANCE), titleField (literal, a string literal in OBJECT_GANTT_FLAT_CONFIG_GUIDANCE; a string literal in OBJECT_MAP_FLAT_CONFIG_GUIDANCE; a string literal in OBJECT_TREE_FLAT_CONFIG_GUIDANCE; a string literal in examples), viewMode (literal, a string literal in OBJECT_GANTT_FLAT_CONFIG_GUIDANCE; a string literal in examples))
  • content/docs/releases/v17/17-3.mdx (via ComponentPropsMap (symbol, a top-level const object))
  • content/docs/releases/v17/17-4.mdx (via ComponentPropsMap (symbol, a top-level const object))
  • content/docs/releases/v9.mdx (via groupByField (literal, a string literal in OBJECT_GANTT_FLAT_CONFIG_GUIDANCE))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 5 changed file(s) yielded no anchor (packages/spec/api-surface/ui.json, packages/spec/authorable-surface/ui.json, packages/spec/declaration-map/ui.json, …) — pages documenting those are invisible to this run
  • 2 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 60 of 215 client-bound route-ledger rows — the other 155 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 155: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 100 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 136 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json b6fe311a9ab7bebdfb22631030ac19ebeb87c6a5packageMentionDocs.

Which tree this was computed on

This run read content/docs from d20154227410f6a7b1d37f07ae61e0cce799fa02 — the merge of head c431c22e058f0f74be179cd2388911634d64c67f into base b6fe311a9ab7bebdfb22631030ac19ebeb87c6a5, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin d20154227410f6a7b1d37f07ae61e0cce799fa02 && git checkout d20154227410f6a7b1d37f07ae61e0cce799fa02
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin b6fe311a9ab7bebdfb22631030ac19ebeb87c6a5 c431c22e058f0f74be179cd2388911634d64c67f && git checkout -B drift-repro b6fe311a9ab7bebdfb22631030ac19ebeb87c6a5 && git merge --no-ff c431c22e058f0f74be179cd2388911634d64c67f

node scripts/docs-audit/affected-docs.mjs --json b6fe311a9ab7bebdfb22631030ac19ebeb87c6a5

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs b6fe311a9ab7bebdfb22631030ac19ebeb87c6a5 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: e235bf4b70cb1e91c53721b9951baf13a071a2e2

① Derived judgments

The method was tested against the renderers themselves at the .objectui-sha pin 53ded82b (read with git show 53ded82b:… from the objectui checkout), not against the PR's own table. Every schema.*, (schema as any).* and rest.* read in plugin-map/src/ObjectMap.tsx (1207 lines), plugin-gantt/src/ObjectGantt.tsx (2011), plugin-tree/src/ObjectTree.tsx (744), the three registry index.tsx shells and core/src/utils/record-source.ts was enumerated and reconciled with the declared key sets.

  • Declared keys with no read point: none. object-map 9 of 9 (data :169 then resolveRecordSourceConfig :174, filter :742, sort :743, map :370, mapStyle :365, navigation :889, enableClustering :905, objectName :793 and :890 plus rung 3, staticData rung 2 at record-source.ts:155). object-gantt 17 of 17 (gantt :498-505, the ladder at :593, filter :738, sort :739, skipWeekends :1205, holidays :1206, persistLayout :1357, viewName :1359, navigation :1487, objectName :1359/:1491/:1873, markers :1826, criticalPath :1829, showBaselines :1832, readOnly :1833 and :1916, mobileReadOnly :1834, label :1871). object-tree 6 of 6 (the ladder at :359, filter :474, tree :108, navigation :591, objectName :534/:570/:605, staticData rung 2).
  • Read points with no declared key, each accounted for. The flat config spellings (map 7 at :382-396, gantt 30 at :513-543, tree 5 at :109-119) are read but carried as wrong-layer guidance sets under the objectui#5018 ruling (2026-08-17, 「同意」: the map block is the author face and the flat spelling is an internal form that stays out of the declared schema), inherited by objectui#6469 for gantt. I re-derived the three guidance key lists from the renderer reads: 7 of 7, 30 of 30 including the legacy dependencyField, 5 of 5 including titleField (read only as labelField's last fallback at :117). style at ObjectMap :283 is read only to warn (objectui#5017); filter.map at :216-221 and filter.tree at ObjectTree :108 are legacy stashes read to warn or as a fallback; rest.data (gantt :686 and :794, tree :496) is the React host prop, and the gantt shell forwards no host props at all.
  • Mirror and calendar traces: none. The rows differ from @object-ui/types in key set (ObjectMapSchema also declares the flat locationField and titleField; ObjectTreeSchema at this pin declares only a REQUIRED objectName plus parentField, labelField, fields, defaultExpandedDepth, so the PR's claim that the tree mirror lacks data, staticData, filter and navigation is verified) and in value shapes (filter/sort are the ruled array orthographies; map, navigation and markers are z.unknown() where the mirror binds objectui schemas). Shared describe boilerplate with object-grid and object-calendar is family idiom; no key set matches an existing row, and object-calendar's array-arm data is not reproduced.
  • Claim 1, tree data: verified, the card's conditional is FALSE. ObjectTree.tsx:359 calls resolveRecordSourceConfig(schema), whose rung 1 (record-source.ts:151-153) returns schema.data verbatim, and the tree consumes that config: dataProvider, dataObjectName and dataItems at :438-445, the object fetch at :461 and the value branch at :506. The ViewData row is measured, not invented for symmetry. One correction to how the table cites it is in ③.
  • Claim 2, map: z.unknown(): honest, not a dodge. ListMapConfigSchema (view.zod.ts:1631) is strict with exactly seven field keys and no style; the renderer reads schema.map?.style at :365 and config.style at :378, so binding the door to it would refuse an honoured value. The A typo'd key inside a page component's properties is accepted in silence — the props bag is a passthrough record and the SDUI props gate has no schema to dispatch for object-* blocks #7751 section header already sets this value posture (z.unknown() where the value contract still lives in objectui; object-calendar.calendar is the precedent), the key is what the card buys, and the reason is pinned in the new tests with ListMapConfigSchema.safeParse({ style }) as the negative. The gap it exposes belongs to the list-view face and is correctly filed as out of scope.
  • Generated artifacts match the source. authorable-surface +32 = 9 + 17 + 6 rows exactly; api-surface and export-origins +9 = three symbols per row; declaration-map +6 follows the existing two-entries-per-row pattern (calendar and grid carry the same pair); json-schema.manifest +3; strictness ledger 444 to 447 = the three new strictObject sites; component.mdx gains three sections with nested shapes only where a spec schema is bound (.gantt, .tree, none for map); index.mdx 153 to 156 and 1522 to 1525. packages/spec/json-schema/ is gitignored, so no per-schema JSON is owed to the diff, and the liveness ledger does not key on ComponentPropsMap rows (0 hits for ObjectCalendarProps in liveness/ui.json), so nothing there is stale. The parity gate's "14 spec-only" for gantt reconciles with 17 keys because label is a node-level key the gate reports separately.
  • object-chart note untouched. The diff carries no object-chart line; the note sits at head :2416 and :3689 (base :2416 and :3295, a pure line shift), and the new tests pin the absence.
  • layout-dsl.mdx does not ride, agreed. Re-counted at head: object-calendar 0 and object-form 0 (both rows since A typo'd key inside a page component's properties is accepted in silence — the props bag is a passthrough record and the SDUI props gate has no schema to dispatch for object-* blocks #7751), against object-metric 3, object-master-detail-form 3, object-kanban 2, object-grid 1; ComponentPropsMap 1, the :719-723 callout, a mechanism sentence that stays true. A page that never enumerated the row set is not made incomplete by three more rows.
  • Scope. The merge-base is 8b81ab60d7 and the three-dot diff is exactly the 13 files the PR lists; .objectui-sha is 53ded82b at both base and head; all 46 check runs on this head are success or skipped, zero failures.

② Semver level

minor on @objectstack/spec is the right level. Three new rows and nine new exports (ObjectMapPropsSchema, ObjectMapProps, ObjectMapPropsParsed and the gantt and tree twins) widen a published surface; the PageComponentSchema parse is unchanged (all three types already passed the open string arm), the props gate that dispatches these rows is @objectstack/lint's warning layer (#5068, untouched), and nothing is retired or narrowed. Precedent agrees: the #7751 six-row entry (8f20201) shipped as a Minor Change. Nothing else in the diff widens or narrows beyond the three rows: the only source change outside the three schemas, three guidance sets and three map rows is importing GanttConfigSchema and TreeConfigSchema, both already exported from view.zod. Clause-②: yes is correct and the changeset carries it.

③ Boundary flags

  1. Tree data at :496 is an ARRAY read, not a ViewData read. const passed = (rest as any).data ?? (schema as any).data; if (Array.isArray(passed)) … honours the bare-array shorthand the row refuses, the same shorthand the PR classifies for map as measured and deliberately NOT declared. The ViewData arm rests on :359 plus the consumption at :438-445, :461 and :506, which is sufficient on its own. The table, the schema header and the test comment should say so rather than cite :496 as support for the object arm, and objectui#8348's remaining slice inherits a concrete read to retire under 「以协议为准」: (schema as any).data at :496.
  2. Tree objectName describe gives a reason that is false for this block. "Optional because the component-level dataSource binding can supply the object instead" holds for map and gantt, whose shells wrap ElementDataSourceGate; plugin-tree/src/index.tsx at the pin is a plain wrapper with no gate, so the spec's PageComponentSchema.dataSource binding does not reach the tree. Optionality itself is right (rungs 1 and 2 supply records without an object name), and the published description should give that reason. One-line describe fix; it does not change the accept set.
  3. Citation slip. gantt label is cited at :1849 in the PR table, the schema header and the test comment; :1849 is a comment line, and the read is resolveInlineI18nLabel(schema.label, displayLocale) at :1871. Key and shape are right.

Implemented-by: claude/issue-18305-component-props-map-map-gantt-tree
Reviewed-by: session_01KB5PFtxuy1x3dcR5gxudx6

VERDICT: PASS


Generated by Claude Code

…tree states the real reason its objectName is optional; two citations corrected

Claude-Session: https://claude.ai/code/session_01KB5PFtxuy1x3dcR5gxudx6
Co-authored-by: Claude <noreply@anthropic.com>

Copy link
Copy Markdown
Collaborator Author

Contract review

126/126 at-tier stamps (control: an os-dev round in this same session reads 209/209 claude-opus-5)
Served-tier: CONTRACT_REVIEW_TIER
Head-sha: e7967ce543b038804ae6eff70a309f962ebb80b1

① Derived judgments

Method: every read point re-read from objectui at the pin 53ded82b (the .objectui-sha at both head and origin/main) with git show 53ded82b:PATH; every spec reading from git show e7967ce…:PATH or a detached worktree at that sha; the dev's report and the earlier review comment on the PR were treated as claims and re-derived, not adopted.

Accept-set and public-surface changes, one by one:

  1. Three new ComponentPropsMap rows, 48 rows total, Object.keys lists them, object-chart still undefined (probe; note intact at :2416/:3719). RIGHT.
  2. object-map key set of 9 — each key has a read point (data :169/:174, staticData and objectName via the ladder :155/:162 plus :793/:890, filter :742, sort :743, map :370, mapStyle :365, navigation :889, enableClustering :905). Full enumeration of schema.* reads (17 distinct) = 9 declared + 7 flat + node keys type/style; nothing undeclared. RIGHT.
  3. object-gantt key set of 17 — every key cited lands (gantt :498-505, ladder :593, filter :738, sort :739, skipWeekends :1205, holidays :1206, persistLayout :1357, viewName :1359, navigation :1487, objectName :1359/:1491/:1873, markers :1826, criticalPath :1829, showBaselines :1832, readOnly :1833/:1916, mobileReadOnly :1834, label :1871). Enumeration 47 distinct = 17 declared + 30 flat + type; title 0 reads. gantt: GanttConfigSchema is right — the renderer validates the block against that very schema (:500) and all 19 ganttConfig.* reads sit inside its 29 keys. RIGHT.
  4. object-tree key set of 6 with data IN and sort OUT — :359 is rung 1 returning schema.data verbatim, consumed at :438-445/:461/:506; the fetch :473-485 issues $filter $top $expand, $orderby 0 hits (control $filter 1); :496 is the bare-array read gated by Array.isArray at :497, so the PR's corrected reading is TRUE; the mirror ObjectTreeSchema declares only a required objectName plus four tree keys, so the mirror-is-wrong claim is TRUE. The card's conditional ("if the renderer's authored surface has no data, declare none") is falsified by measurement. RIGHT.
  5. Value postures: data = ViewDataSchema (object arm accepted, bare array refused) RIGHT; filter = ViewFilterRule[] and sort = SortItem[] RIGHT (map :742-743 and gantt :738-739 hand them to the same sinks the grid uses; string sort refused); map = z.unknown() RIGHT and measured, not lazy (ListMapConfigSchema 7 keys, no style; ObjectMapConfigSchema 8 keys incl. style; renderer reads map?.style at :365); tree = TreeConfigSchema RIGHT (4 keys = the 4 nested reads in getTreeConfig); navigation = z.unknown() follows the object-grid precedent at :2601; label = I18nLabelSchema (:1871 resolves an inline locale map); markers = z.array(z.unknown()) (GanttMarker { date, label?, color? }). RIGHT.
  6. Flat-config guidance sets: map 7 = ObjectMap :382-396 = ListMapConfigSchema.shape; gantt 30 = :513-543 = GanttConfigSchema 29 + dependencyField; tree 5 = :109-119 = TreeConfigSchema 4 + titleField (:117). Probe shows the block-naming prescription in each refusal. RIGHT.
  7. Tree objectName describe (no dataSource gate): plugin-tree shell 0 ElementDataSource lines vs 5 each in the map, gantt, grid and calendar shells. TRUE.
  8. Generated artifacts: check:generated exit 0 in my worktree at head — all 15 up to date; authorable-surface +32 = 9+17+6, api-surface and export-origins +9, declaration-map +6, json-schema manifest +3, ledger 444 to 447, component.mdx and index.mdx regenerate identically. RIGHT.
  9. Lint type gate is untouched for these types (validate-component-types.ts :96 skips any type without a reserved colon namespace); the only behavioural change is the props gate now judging an authored props bag on these nodes, which is what the PR states. Corpus: 0 authored nodes of the three types outside spec/manifest/docs vs 7 for object-grid. RIGHT.
  10. Patch-round assertions on the shipped doc: Object this chart binds to 0, Object this gantt binds to 1, all-noun control 6, tree registers no such gate 1, the old tree reason 0. TRUE.

FALSE claims — text only; no key, value shape, row membership or level moves:

  • F1. The objectName, data and staticData describes on object-gantt (component.zod.ts :3379, :3387, :3389) and object-tree (:3542, :3549, :3551) say the record source is resolved by, or "read FIRST/SECOND by", getDataConfig. At the pin ObjectGantt.tsx and ObjectTree.tsx contain 0 occurrences of getDataConfig (control: ObjectMap.tsx 8, which keeps a local wrapper at :135; resolveRecordSourceConfig 2 and 3 in gantt and tree). The name was removed from both files by objectui commit ce2aaefe1 (objectui#7632, one shared ladder). The ladder ORDER is true; the function named does not exist in the code these describes are about, and the six sentences ship into the public reference at component.mdx :398-400 and :768-770 (9 getDataConfig rows at head vs 0 at base; the three map rows are true). MUST CHANGE: in those six describes name the actual read point — the shared record-source ladder resolveRecordSourceConfig (@object-ui/core record-source.ts :151/:155/:162), reached at ObjectGantt.tsx :593 and ObjectTree.tsx :359 — or use a function-agnostic spelling; then gen:docs so component.mdx follows. The map describes may keep getDataConfig.
  • F2. The tree data field JSDoc at component.zod.ts :3545 still reads "Declared from the READ POINTS (:359 and :496)", contradicting the schema header at :3487 (":496 is NOT a second site for the object arm") and the PR's own corrected position. MUST CHANGE: cite :359 alone (source comment only; no regeneration owed).
  • F3. Two off-by-N citations in source JSDoc: gantt ":501 validates" — the safeParse is at :500 (:501 is the if (!result.success)); tree "Not authorable at :477" — the marker is at :482. Fix in passing.

② Semver level

Changeset .changeset/18305-component-props-map-map-gantt-tree.md, level quoted: "@objectstack/spec": minor. Correct. Three new rows and nine new exports (ObjectMapPropsSchema/ObjectMapProps/ObjectMapPropsParsed and the gantt and tree twins) widen a published surface — @objectstack/spec is not private, sits in the fixed group, and its files[] ships dist, json-schema, api-surface and src/**/*.zod.ts. Nothing is retired or narrowed: PageComponentSchema still parses all three types through the open string arm, the lint type gate never refused them, and only the props gate's coverage grows. Precedent 8f20201f1c (#7751, six rows) shipped as minor. The F1–F3 fixes are text and leave the level unchanged.

③ Boundary flags

open_questions is empty. Every flag the dev raised, answered:

  1. "Tree data conditional is FALSE" — confirmed at the pin (:359 rung 1, consumption :438-445/:461/:506); the row correctly declares data. Answered.
  2. "component-reference-rail.test.ts pins no row set" — confirmed: 164 lines, 4 ComponentPropsMap mentions all ['record:reference_rail'], Object.keys(ComponentPropsMap) 0, 'object- literals 0 vs 6 and 4 on the vocabulary test. No edit owed. Answered.
  3. "ListMapConfigSchema declares no style" — confirmed (7 keys; safeParse({ style }) false); out of scope for this card; the z.unknown() posture for map is the right one here. File it against the list-view face. Answered.
  4. "object-tree absent from sdui.manifest.json" — confirmed (57 components, none object-tree; parity prints NO component). The row is derived from sources, so unaffected; carrier is the manifest regeneration. Answered.
  5. "check:cross-package-test-inputs is build-state sensitive" — not re-measured here; not contract-bearing for this diff (no cli or turbo.json touch); Lint & Repo Gates is green on the head. Escalate as a filed, non-blocking defect. Answered by escalation.
  6. "layout-dsl.mdx does not ride" — confirmed: object-calendar 0, object-form 0 vs object-metric 3, master-detail-form 3, kanban 2, grid 1; ComponentPropsMap once at :721 as a mechanism sentence. Answered.
  7. "GanttConfigSchema has no dependencyField alias inside the block" — confirmed by probe (refused inside the block); view.zod.ts is untouched by this diff; out of scope. Answered.
  8. "objectui#9239 closed; object-calendar data is the array arm" — confirmed (calendar data: z.array(z.unknown()) at :2983); not this card. Answered.
  9. "Declared narrowing: 60 gate families unrun" and "CI not green" — answered by measurement: 42 check runs on the head, 38 success, 4 skipped, 0 failure; plus my own worktree runs at head — build 0, check:generated 0, parity 0, test 0 (482 files, 13727 tests).
  10. Clause-②: check-clause2-carriers --pair 18403 exit 0; needs:contract-review read on both the card and the PR from the API. Answered.
  11. "Changeset publishes" — package flags and files[] read above; agree. Answered.
  12. os_dev_md_divergences — process notes, not contract; no action in this review. Noted.
  13. The earlier review's three ③ items (:496 over-claim, tree objectName reason, :1849 citation) — verified fixed at head in the header, the describe and the PR table; the one leftover is the field-level JSDoc in F2.

Implemented-by: claude/issue-18305-component-props-map-map-gantt-tree
Reviewed-by: session_01KB5PFtxuy1x3dcR5gxudx6

VERDICT: FAIL


Generated by Claude Code

…ree describes

The `objectName` / `data` / `staticData` describes on `object-gantt` and
`object-tree` named `getDataConfig` as the reader that resolves the record
source. Measured at the `.objectui-sha` pin `53ded82b`: `ObjectGantt.tsx` and
`ObjectTree.tsx` contain zero occurrences of that name (control: `ObjectMap.tsx`
has 8 — it keeps a local wrapper at :135, so the map describes stay as they are).
Both renderers reach the shared three-rung ladder `resolveRecordSourceConfig`
(`@object-ui/core` `record-source.ts` :151 / :155 / :162) — gantt at :593, tree
at :359. The ladder ORDER those sentences describe was already true; only the
function they named was wrong, and all six ship into the public reference.

Also: the tree `data` JSDoc cited two read points where the schema header
already rules that `:496` is not a second site for the object arm, and two
source citations were off by a line (`safeParse` is at :500, not :501; the
"Not authorable" marker is at :482, not :477).

Claude-Session: https://claude.ai/code/session_01KB5PFtxuy1x3dcR5gxudx6
Co-authored-by: Claude <noreply@anthropic.com>
…rce describes

Six rows follow the corrected describes: `object-gantt` and `object-tree` now
name `resolveRecordSourceConfig`. The three `object-map` rows keep
`getDataConfig` because that renderer really does keep a local wrapper of that
name above the shared ladder.

Claude-Session: https://claude.ai/code/session_01KB5PFtxuy1x3dcR5gxudx6
Co-authored-by: Claude <noreply@anthropic.com>

Copy link
Copy Markdown
Collaborator Author

Contract review

119/119 at-tier stamps (control: an os-dev round in this same session reads 156/156 claude-opus-5)
Served-tier: CONTRACT_REVIEW_TIER
Head-sha: c431c22e058f0f74be179cd2388911634d64c67f

① Derived judgments

Method: every read point re-read from objectui at the pin 53ded82b (the .objectui-sha at both head and origin/main) with git show; every spec reading from git show c431c22e:PATH; the generated reference re-produced by running build and check:generated in a detached worktree at origin/main-merged-with-head (exit 0, 15 of 15 artifacts current, clean status). The prior FAIL record, the dev reports and the PR body were treated as claims and re-derived.

Accept-set and public-surface changes, one by one:

  1. ComponentPropsMap gains object-map, object-gantt, object-tree (48 rows); object-chart remains undefined and its note is untouched (test pins both). RIGHT.
  2. Nine new exports (ObjectMapPropsSchema, ObjectMapProps, ObjectMapPropsParsed and the gantt and tree twins); authorable-surface +32 = 9 + 17 + 6; declaration-map, export-origins, json-schema manifest, strictness ledger 444 to 447 and both reference pages regenerate identically — measured by running the generator, not by reading the diff. RIGHT.
  3. object-map key set of 9 — every key has a read point at the pin (data :169 then ladder :174, staticData and objectName via rungs :155 and :162 plus :793 and :890, filter :742, sort :743, map :370, mapStyle :365, navigation :889, enableClustering :905); the 17 distinct schema.* reads reconcile as 9 declared + 7 flat + the node keys type and style (:283 reads style only to warn). RIGHT.
  4. object-gantt key set of 17 — every citation lands (ladder :593; filter :738; sort :739; gantt :498-505 with the safeParse at :500 against GanttConfigSchema imported from @objectstack/spec/ui at :28; navigation :1487; label :1871; skipWeekends :1205; holidays :1206; persistLayout :1357; viewName :1359; markers :1826; criticalPath :1829; showBaselines :1832; readOnly :1833 and :1916; mobileReadOnly :1834; objectName :1359, :1491, :1873). title reads 0; the row cap is the platform ceiling marked not authorable at :747. RIGHT.
  5. object-tree key set of 6 with data IN and sort OUT — :359 is the ladder call and no reader of schema.data sits above it; the fetch :473-485 issues $filter, $top, $expand and $orderby is 0 (control $filter 1); :496 is the bare-array read gated by Array.isArray at :497, correctly cited only as the negative; the mirror ObjectTreeSchema at the pin declares a REQUIRED objectName plus four tree keys and (via BaseSchemaCore) none of data, staticData, filter, navigation, so the mirror-is-wrong claim is TRUE and the card's conditional is falsified by measurement. RIGHT.
  6. Value postures: data = ViewDataSchema on all three (rung 1 returns the authored value verbatim); filter = ViewFilterRule[], sort = SortItem[] (the ruled orthographies; string sort refused); gantt = GanttConfigSchema (the renderer validates against that very schema, ListViewSchema.gantt binds the same block at view.zod.ts:2116); tree = TreeConfigSchema (view.zod.ts:2121; four keys = the four nested reads); map = z.unknown(), measured not lazy (ListMapConfigSchema 7 keys and no style; objectui ObjectMapConfigSchema 8 keys including style; renderer reads map?.style at :365); navigation = z.unknown() on the object-grid precedent; label = I18nLabelSchema (:1871 resolves an inline locale map); markers = z.array(z.unknown()) (GanttMarker { date, label?, color? }). RIGHT.
  7. Flat-config guidance sets held equal to the blocks they point at: map 7 = ListMapConfigSchema.shape; gantt 30 = GanttConfigSchema 29 + dependencyField (:513-543); tree 5 = TreeConfigSchema 4 + titleField (:117). RIGHT.
  8. Consequence for consumers: PageComponentSchema does not dispatch ComponentPropsMap at parse time (6 mentions, all prose), so the spec's parse accept set is unchanged; @objectstack/lint's validate-component-props gains three rows and emits severity: 'warning' only; validate-component-types never refused these types. Only the warning layer's coverage grows, exactly as the PR states. RIGHT.

Every sentence asserting runtime behaviour, re-read at the pin — all 32 describes on the three new schemas, on BOTH sides (32 of 32 byte-exact in content/docs/references/ui/component.mdx; counts in the evidence above):

  • The six F1 sentences now name resolveRecordSourceConfig; ObjectGantt.tsx and ObjectTree.tsx contain 0 getDataConfig and reach the ladder at :593 and :359 with no earlier reader, so "read FIRST", "read SECOND" and "THIRD" are literal. TRUE, both sides (mdx :398-400, :768-770). The three map sentences keep getDataConfig, which exists at ObjectMap.tsx:135 and delegates to the ladder at :174. TRUE (mdx :638-640).
  • map objectName and gantt objectName "the component-level dataSource binding can supply the object": both shells wrap ElementDataSourceGate (5 lines each). TRUE. tree objectName "registers no such gate": tree shell 0 against those two controls. TRUE.
  • map mapStyle (read before map.style, not the node style, overriding the public demo tiles): :365, :283, DEFAULT_MAP_STYLE = demotiles. TRUE. map enableClustering "clusters only above 100 markers": :905. TRUE. map map "taken WHOLE when present, flat spelling ignored": :370-378 plus warnOnShadowedFlatMapKeys. TRUE.
  • gantt gantt, navigation (default drawer), label (second link after exportFileName, before the object's label), skipWeekends (Saturday/Sunday, scheduling.ts:34), holidays (ISO strings folded into a Set), persistLayout (only explicit false disables; key objectName:viewName), viewName (default 'default'), markers (drawn like the Today marker), criticalPath (toggle stays in the toolbar, GanttView.tsx:535), showBaselines (on unless false), readOnly (every write path plus the drawer lock at :1916), mobileReadOnly (narrow viewports, GanttView.tsx:894). All TRUE. One simplification, not a falsehood: the persistence key's first half falls back to data.object then 'gantt' when objectName is absent (:1359).
  • tree tree ("same block ListViewSchema.tree declares", parentField auto-detects, :533-535) and navigation (row click, :606). TRUE.
  • filter "lowered to the wire $filter" and sort "lowered to $orderby" on every door that carries them. TRUE.

Citations: every :NNN in the schema headers and describes of the three new blocks was opened at the pin and lands on what it claims, including the three corrected in this round (:500 safeParse, :482 marker, :1849 named only as the comment above the chain, :496 cited only as the negative). The residual :499-501 range is a true range (cast, safeParse, failure branch).

Non-blocking imprecisions, source or docs text, none a false attribution and none moving a key, value shape, row or level — the seat may fold them into the next round that touches these lines, with gen:docs for the two that ship:

  • N1 (ships in the mdx, map row :643). map map describe writes { latitudeField, longitudeField, locationField?, … }, marking the pair required. At the pin ObjectMapConfigSchema declares all eight keys optional and the flat branch (:382) accepts locationField alone; the spec's own ListMapConfigSchema describes locationField as the alternative to the pair. Suggested spelling: latitudeField?, longitudeField? or "either locationField or the latitudeField/longitudeField pair". The door is z.unknown(), so nothing is refused by it.
  • N2 (ships in the mdx, three navigation rows). The mode list page | drawer | modal | split | popover | none omits new_window, which the spec's NavigationModeSchema declares and useNavigationOverlay.ts switches on at the pin (:298). Inherited idiom: the object-grid row lists five of the seven (no popover, no new_window), so this PR's list is closer to complete than the precedent, not worse. Suggested fix: add new_window or point at NavigationModeSchema.
  • N3 (source only, component.test.ts, never generated). Two comment citations were not carried when the zod headers were corrected: :3425 still reads label (:1849) — the read is :1871; :3521 still reads "ObjectGantt.tsx:501 validates" — the safeParse is :500. Same class as the prior review's F3.
  • N4 (gate coverage, not text truth). The three new schema headers cite the pin in the HISTORICAL spelling ("measured at the .objectui-sha pin 53ded82b", head :3187, :3339, :3468), while the sibling A typo'd key inside a page component's properties is accepted in silence — the props bag is a passthrough record and the SDUI props gate has no schema to dispatch for object-* blocks #7751 rows use the ASSERTING spelling (.objectui-sha = 53ded82bf, 7 sites). check:objectui-pin-citations therefore counts these roughly 45 line anchors among the "36 historical not checked" and will not flag them on the next pin bump. The gate permits the historical form for "where the measurement was taken", so this is within its vocabulary; recommend the asserting form (or anchor quotes) so the records stay re-checkable, since re-checkability is what they are for.

② Semver level

Changeset .changeset/18305-component-props-map-map-gantt-tree.md, level quoted: "@objectstack/spec": minor. Correct. Three new declared rows and nine new exports widen a published surface; nothing is retired or narrowed: PageComponentSchema still parses all three types through the open string arm and does not dispatch the rows at parse time, the lint type gate never refused them, and the props gate that now judges them emits warnings only. The changeset body carries the Clause-② widening declaration, which by AGENTS.md makes minor the floor; no author-writable key is removed or renamed, so nothing reaches major. Precedent (#7751, six rows) shipped as minor.

③ Boundary flags

open_questions is empty in all three dev reports. Each flag and each out_of_scope_findings entry, re-derived:

  1. Tree data conditional is FALSE — confirmed at the pin (:359 rung 1, consumption :438-445, :461, :506; no earlier reader). The row correctly declares data. Answered.
  2. component-reference-rail.test.ts pins no row set — confirmed: 164 lines, Object.keys(ComponentPropsMap) 0, 'object- literals 0, against 6 and 4 on the vocabulary test. No edit owed. Answered.
  3. ListMapConfigSchema declares neither style nor mapStyle — confirmed (7 keys). Out of scope for this card; the z.unknown() posture for map is the right one here. Escalate to the seat as a filing against the list-view face.
  4. object-tree absent from sdui.manifest.json — confirmed (0 vs 2 and 2 for the map and gantt). Not contract-bearing; the row is derived from sources. Carrier: the manifest regeneration. Answered.
  5. check:cross-package-test-inputs build-state sensitivity — not re-measured here; it touches no path of this diff and Lint & Repo Gates is green at head. Escalate as a filed, non-blocking defect.
  6. layout-dsl.mdx does not ride — confirmed to the number (ComponentPropsMap 1, object-calendar 0, object-form 0, object-metric 3, master-detail 3, kanban 2, grid 1). Answered.
  7. GanttConfigSchema has no dependencyField alias inside the block — confirmed (0 vs dependenciesField 1). view.zod.ts is untouched; out of scope. Answered.
  8. objectui#9239 closed; object-calendar.data is the array arm — confirmed (z.array(z.unknown())). Not this card. Answered.
  9. objectui's own gantt face carries the false getDataConfig attribution — confirmed at the pin: objectql.zod.ts :726, :738, :739, :838 name it for a file with 0 occurrences (the map's :580, :588, :634, :641-643 are TRUE), and record-source.ts :28, :92, :95, :97 quote it as the contract text. objectui's repo; escalate to the seat to file there. Not a defect of this PR.
  10. The gantt header's :499-501 range — verified as a true range; no change owed. Answered.
  11. The PR body carries no line-initial Clause-② (0; control object-gantt 7); the carriers are the card's claim comment (1) and the changeset (1), which is what the carrier gate reads. Consistent. Answered.
  12. "The 75 unrun derived gate families are CI's to run" — judged, not accepted: a declared narrowing is only discharged by CI actually running those families on this head. At head all 46 check runs completed with 0 failures and the six required contexts are success, on a merge ref computed after main's last commit; my own build and check:generated at head-merged-with-main exit 0. The declaration holds because CI's result discharges it, not on its own authority; the posture of declaring the narrowing in the report rather than skipping silently is the right one.
  13. "Not merging origin/main into the 18-commit-behind branch was right for a text-only round" — judged on measurement rather than on the stated reason: the branch is 19 behind at review time; git merge-tree --write-tree of origin/main and head exits 0 with no conflicts; main changed 96 files since the merge-base, 0 of them among the PR's 13 paths; main added no .zod.ts, so the reference index totals (1525) stay right; and check:generated on the merged tree is green with a clean status. So the call was safe. The dev's stated reason (that a merge would widen a text-only round into a regeneration) is not itself a justification — a stale shared artifact after merge would have been the very thing to fix — but the measured grounds above are, and the queue rebuilds against main regardless.
  14. os_dev_md_divergences — process notes, not contract; no action in this review.
  15. Observation for objectui#8348's remaining slice, not a flag on this PR: all three registry shells declare objectName with required: true while the protocol rows make it optional on the ladder's first two rungs; under the ruling the mirror follows the protocol.

Implemented-by: claude/issue-18305-component-props-map-map-gantt-tree
Reviewed-by: session_01KB5PFtxuy1x3dcR5gxudx6

VERDICT: PASS


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Provenance — at-tier PASS at the current head, carriers cleared, pre-check clear

domain:spec execution seat, session session_01KB5PFtxuy1x3dcR5gxudx6, 2026-09-16T12:01Z.

  • Review of record: comment 5697057114 on this PR — ## Contract review, VERDICT: PASS, Head-sha: c431c22e058f0f74be179cd2388911634d64c67f, this PR's current head. ⚠️ This is a fresh review of the new head, ⛔ not a re-check of the earlier FAIL's list: the reviewer was told the prior record (5696414075, FAIL at e7967ce543) is a claim about an older tree, and that its silence on a sentence is not evidence — it had missed six false attributions the first time.
  • Served tier, verified rather than asserted: the reviewer's transcript carries 119 "model" stamps, all claude-fable-5-1, 0 of anything else. Control: an os-dev round in this same session reads 156/156 claude-opus-5.
  • Independence pair: Implemented-by: claude/issue-18305-component-props-map-map-gantt-tree vs Reviewed-by: session_01KB5PFtxuy1x3dcR5gxudx6 — different identities, ⛔ not a self-review. Fed the card, its rulings and this PR only. Adopted verbatim.
  • Both carriers cleared in one stroke: needs:contract-review removed from this PR and from card spec(ui): ComponentPropsMap has no rows for object-map, object-gantt and object-tree — add them from the renderers' read points (#7751 method) so 「以协议为准」 resolves for all five ladder blocks (objectui#8348 Q1, ruled C) #18305, each by the four-step writer with a matching read-back.

Landing pre-check, all three, ⛔ every one re-read AFTER the carrier strip:

① the PASS above names this head. ② --pair 18403 → exit 0. ③ every check green as the latest run per check name — 49 runs over 35 names, 0 not-green. Governed predicate on the script-derived list: 0 of 13 paths ⇒ NOT governed.

⚠️ ③ was not taken from the pre-strip reading, and that mattered: the strip re-triggered Check Changeset, run count went 46 → 49, and it read in_progress for ~90 seconds. The 11:40Z reading (46 runs, all green) was stale the moment the label was written. This is the hazard recorded on #18446.

⚠️ What this landing does NOT settle

The review named four non-blocking imprecisions (N1-N4); two of them ship into the public reference — the object-map map describe marks the lat/lng pair required where every key is optional and locationField alone is accepted, and the navigation mode list omits new_window. They are carried on #18459, filed before this PR was enqueued so the record cannot be read as settling them. ⛔ The seat did not appeal the verdict; it adopted it verbatim and carried what the verdict left open.


Generated by Claude Code

@os-warren
os-warren marked this pull request as ready for review September 16, 2026 12:01
@os-warren
os-warren added this pull request to the merge queue Sep 16, 2026
Merged via the queue into main with commit a3d4c59 Sep 16, 2026
51 checks passed
@os-warren
os-warren deleted the claude/issue-18305-component-props-map-map-gantt-tree branch September 16, 2026 12:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:ui size/xl tests tooling

Projects

None yet

2 participants