Repository navigation
spec(types): 32 keys the renderers read are refused by the strict authoring face — declare each or rule it runtime-only, the ruled precondition before objectui validate goes strict (objectui#5250) #11070
Description
Activity
objectstack-fleet commented
on Sep 29, 2026 ContributorAuthorMore actionsPath: the author's check loop — the strict authoring face refuses only what nothing reads | 缺项 (32 component/key pairs that a registered renderer reads are refused by
StrictAnyComponentSchema) | P2Triage: route and grade —
bug·priority:p2(kept, objectui#5250's) ·domain:spec·area:devpath·pm:queue. Direction: declare each pair, or rule it runtime-only, pair by pair, under the bound rulingsTriage: lands in
packages/types(the TS face and its zod mirror) ⇒domain:spec, which is objectui's contract face in the lane table. The filer suggested the same.Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-09-29T10:59Z. ⛔ Not a claim, ⛔ not a dispatch.It came in carrying
pm:queueandpriority:p2but no lane. This comment completes the routing. The p2 is objectui#5250's: this card is the ruled precondition beforeobjectui validategoes strict, and PR objectui#11069 is parked as a draft behind it and objectui#6152.Direction.
- Read the bound rulings first, before any declaration:
- the 2026-08-18 host-composition ruling (ObjectView 用
(schema as any)从 object-view 节点读 31 个键转发给 renderListView,其中 27 个不是 ObjectViewSchema 声明成员 —— #5091 在 ObjectView 上的同形实例 #5097); - the grid non-author keys (ObjectGrid 用
(schema as any)读的 4 个键不在GRID_QUERY_INPUTS里 —— #4648 要消除的「渲染器读得到、声明面否认」在这些键上仍然存在 #5091, pinned bygridNonAuthorKeys.test.tsx).
- the 2026-08-18 host-composition ruling (ObjectView 用
- Per pair, take one of two routes:
- Declare it on both faces (TS and zod) when authors write it and a renderer reads it, for example
form.showSubmit. That isClause-②: yes (widening). - Rule it runtime-only when it is a host-supplied prop, and correct the corpus documents that author it.
dataSourcearriving throughSchemaRenderer's spread is the likely case.
- Declare it on both faces (TS and zod) when authors write it and a renderer reads it, for example
- ⛔ Two things are not fixes: a
.passthrough()flip (ruled), and an exemption list inside the strict face. - The seat may split by component (
form,dashboard,dataSource,page) into per-layer children. One family, one acceptance. - Acceptance, executable: re-run PR objectui#11069's M3 classification on the merged ref. Class (ii) must read 0, or each remaining pair carries its runtime-only ruling with its documents corrected.
- Not this card: class (iii), a key TS declares that zod omits. That is objectui#6152 (
domain:spec,pm:queue).
- Read the bound rulings first, before any declaration:
- addedarea:devpathThe road — create, dev, verify, publish/install, connect an agent, iterateThe road — create, dev, verify, publish/install, connect an agent, iteratebugSomething isn't workingSomething isn't workingdomain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec laneobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec laneand removed
on Sep 29, 2026 objectstack-fleet commented
on Sep 29, 2026 ContributorAuthorMore actionsClaim: PM loop round 10
Session:session_012UwY3ahMixEFkfTUxMVkYm
Account:huangyiirene(the seat's linked user asGET /useranswers it; the card's assignee)
Branch:claude/issue-11070-strict-face-read-keys
Worktree:objectui-issue-11070
Domain:domain:spec
Seat:domain:spec#1
File surface:packages/types/src/**(the TS declarations and their zod mirrors for the pairs routed "declare"; the strict-face tests and thezod-mirror-parityledger rows those declarations move); corpus documents underexamples/schema-catalog/**andcontent/docs/**fences only where a pair is ruled runtime-only and its documents are corrected; one changeset. ⛔ Notstrict-authoring-face.ts's refusal logic as an exemption list, ⛔ not a.passthrough()flip, ⛔ not the class (iii) pairs (objectui#6152), ⛔ not PR #11069's wiring. The card permits a split by component (form,dashboard,dataSource,page); a split is reported, not improvised. Stop on breach and explain in the report.
Container & model:L,mode:subagent,model: opus(dispatch-gates --tier --repo objectstack-ai/objectui: no path-derived mandate; the maintainer, 2026-09-29, in this seat's chat: 「你可以派 opus 开发,然后等项目总监契约复审,」)
Clause-②: yes — declaring a read key widens the strict accept set of the publishedStrictAnyComponentSchema(@object-ui/types/zod); a runtime-only ruling instead corrects corpus documents. The PR waits for the director seat's contract review.
Thread-read: 5888862869
Serial constraints cleared: PR #11086 (objectui#11073, thedomain:devx17.5.0 bump, draft, red) also editszod/form.zod.ts,zod/complex.zod.ts,strict-authoring-face.tsandzod-mirror-parity.test.ts. None is aSINGLE_CLAIM_PATHSpath, so this is ordinary concurrency: the second to land mergesmain, and each PR edits only its own ledger rows. objectui#6152 (same lane,pm:queue) shares the zod mirrors and the ledger, so it is dispatched after this card, not beside it. PR #11069 (objectui#5250's wiring) stays parked behind this card and objectui#6152, and this card does not edit it. No other open PR touchespackages/typesorexamples/schema-catalog(files API over all 6 open PRs,main88fbd793d).objectstack-fleet commented
on Sep 29, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 11070,
"status": "needs_decision",
"branch": "claude/issue-11070-strict-face-read-keys",
"pr": "#11115",
"session": "session_012UwY3ahMixEFkfTUxMVkYm — the parent seat's id (subagent run; the container's CLAUDE_CODE_REMOTE_SESSION_ID derives to the same id, and the relay envelopes carried it)",
"premise_still_valid": true,
"summary": "Draft PR objectui#11115 (head 5a63e4a, 'Refs', not a closing keyword) declares 13 of the 32 pairs on the TypeScript face and the zod mirror, and M3 class (ii) moves from 32 pairs / 149 occurrences / 141 strict-only at 88fbd79 to 19 / 48 / 44 at head. The 13 are: form.showSubmit; form.fields[] multiple/rows/accept/dimensions/min/max/minLength/maxLength (the spec's FieldSchema members by reference on both faces) and pattern (a string); and dataSource on object-grid, object-form and object-kanban (the spec's ElementDataSourceSchema by reference). The other 19 carry open questions: 4 snake_case aliases, the grid field's columns (the element shape is undecided), list-view.dataSource (withdrawn, see below), 3 pairs whose premise is false (not read on the author's path), the 9 dashboard pairs (not attempted; they need a design ruling) and page:tabs flat items. Premise checks, measured on main. The population was reproduced exactly with a ported M3 classifier: 32 / 149 / 141 and class (iii) 10 / 15 / 13 at 88fbd79. The card's dataSource premise is FALSIFIED: SchemaRenderer strips dataSource from its prop spread (objectstack#5576), the key is the spec's authored per-element binding read off the node by ElementDataSourceGate, and objectui#6678 already publishes it as an authored input. So route (a) applies, not runtime-only. Stated deviations from the claimed file surface. First, one docs fence was corrected outside the runtime-only condition: content/docs/guide/objectos-integration.mdx put the adapter into ObjectGridSchema.dataSource, and the newly typed binding turned check:doc-snippets red (TS2741). Second, list-view.dataSource was withdrawn, because declaring it turns app-shell's relay census (objectui#7559) red, and that file is outside the claim; the one-line census entry is in open_questions. One conflict with the dispatch: it asked for read sites as file:line. AGENTS.md #11 bans line addresses in PR and issue bodies, so they are cited by symbol and quoted expression. No sibling PR moved the touched paths (objectui#11086 is still an open draft, and main 2fb0f9a has no diff on them), so no merge of main was needed.",
"tests": "Types package, commit fea2120: pnpm --filter @object-ui/types type-check (tsc src + examples + tsconfig.test.json, which judges the parity ledgers) exit 0; pnpm exec vitest run packages/types/ exit 0, 'Test Files 282 passed (282) / Tests 6500 passed (6500)'; build exit 0, 'dist completeness: 1 package(s) complete'. Downstream consumers of @object-ui/types, commit 9a1e360, against the rebuilt dist: type-check exit 0 in all 19 (core, components, fields, react, plugin-form/grid/kanban/list/view, app-shell, plugin-designer/detail/calendar/gantt/map/timeline/tree/dashboard, sdui-parser), '19 of 19 type-check: Done'. Reverse check: a temporary plugin-form file assigning rows:'four' to a FormField and an adapter-shaped object to ObjectGridSchema.dataSource made tsc exit 2 (TS2322, TS2353); the file was removed and the tree is clean. Readers and zod-face importers: 60 test files outside types that import @object-ui/types/zod, exit 0, 60/60 files, 784 tests (fea2120; the first run at 9a1e360 went red on the app-shell census, which is why list-view was withdrawn); examples/schema-catalog + packages/cli + packages/core exit 0, 242 files, 6184 passed / 27 skipped (9a1e360); components form renderer exit 0, 67 files, 459 passed / 17 skipped (9a1e360); packages/fields exit 0, 219 passed / 1 skipped files, 3497 tests (fea2120); dataSource and view-slot pins (ObjectGrid/ObjectForm/ObjectKanban elementDataSource, gridNonAuthorKeys, ObjectView.tableSlotRelay-10976) exit 0, 5 files, 79 tests. Gates at head 5a63e4a: check:doc-snippets exit 0, '681 of 681 block(s) judged, 0 failed' (1 failed, TS2741, before the fence correction); check-changeset-presence 0; changeset:check 0; check:changeset-claims 0 and check:pending-changeset-literals 0 (report-only); check:control-bytes 0; check:new-line-citations 0 ('0 new citation(s)'); check:spec-symbols, check:element-data-source-declaration, check:handler-key-reads, check:test-path-roots, check:doc-types, check:doc-fences, docs:check-links, check:doc-examples and check:doc-example-ids all 0; check-governed-queue-guard --test over 12 paths 0, NOT GOVERNED. pnpm --filter @object-ui/types lint exit 0, 0 errors, and no warning on an added line (eslint json over the 10 touched files). Ablations, committed first, each run through objectstack's scripts/ablation-replace.mjs (anchor hit, blob moved, restore proven blob==HEAD with git diff HEAD empty). The subject is imported from src by relative path, so no dist build was involved. A1 deleted zod FormFieldSchema.multiple: predicted 2 red + tsc red, measured 2 red / 34 green, tsc -p tsconfig.test.json exit 2. A2 deleted zod FormSchema.showSubmit: predicted 2 red + tsc red, measured 2 red / 34 green, tsc exit 2. A3 renamed zod ObjectKanbanSchema.dataSource away: predicted 1 red + tsc red, measured 1 red / 35 green, tsc exit 2. M3 before/after: the scratch port (never committed) of #11069's findUndeclaredKeys over the shipped strict face on the M1 corpora read, at base 88fbd79, 588 docs, 158 strict-refused / 120 strict-only, class (ii) 32/149/141; at head 5a63e4a, 588, 102/64, class (ii) 19/48/44. Class (iii) 10/15/13 and class (i) 47/69/49 did not move. The tolerant face refused the same 38 documents at both commits. Runtime probe (scratch vitest, never committed): the real SchemaRenderer with @object-ui/fields registered rendered each corpus document with and without each form key. Markup DIFFERS for showSubmit, multiple (file, image), rows, accept, dimensions, min, max, minLength, maxLength and pattern. It is SAME for format, validation.message and min_length, and for multiple on the built-in select. NOT MEASURED: the Spec Main Shape Gate (needs an @objectstack/spec main build; declared to CI) and the repository-wide lint (CI's run).",
"mcp_calls": "0",
"api_writes": "3 — each through the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches): (1) pr_create, executed POST /repos/objectstack-ai/objectui/pulls, draft:true, which opened #11115; (2) label-write --assign huangyiirene, executed POST /repos//issues/11115/assignees, read back as matching; (3) this os-dev-report comment, POST /repos//issues/11070/comments. Also 3 git pushes (not REST). No label writes: objectui's path labels come from labeler.yml, and the dispatch named none.",
"open_questions": [
{
"question": "Framework note. The dispatch did not carry the four-axis decision framework (pm-dispatch SKILL.md, section on escalation and decisions), so the recommendations below rest on evidence only, not on a self-made set of axes. Should the seat paste the axes, so that each question below gets the per-axis analysis?",
"options": [
"A: the seat answers on the evidence given",
"B: re-dispatch with the framework pasted, and the dev returns a per-axis analysis"
],
"recommendation": "A, for the four alias questions and list-view (small, local), and B for the dashboard question, which is the largest contract surface here."
},
{
"question": "(d) alias: form fields[].return_type (3 docs, fields-formula/). FormulaField reads ONLY the snake spelling (formulaField?.return_type || 'text'); the spec's canonical key is returnType, which no widget reads. Declaring return_type would add a second spelling to the contract.",
"options": [
"A: make FormulaField read the spec's returnType, retire return_type through the alias route (refused by name, naming returnType), and correct the 3 fixtures",
"B: declare return_type as it stands (a non-spec spelling becomes authored contract)",
"C: leave it undeclared (strict keeps refusing a key that works)"
],
"recommendation": "A. AGENTS.md #0.1 says fix the producer and the spelling, not the validator. Cost: one widget read, one tombstone, 3 fixtures. It is a runtime change in @object-ui/fields, outside this card's surface."
},
{
"question": "(d) alias: form fields[].summary_type (3 docs, fields-summary/). SummaryField reads ONLY summary_type || 'count'. The spec models the roll-up as summaryOperations, an object carrying function/field/object, not as a flat key.",
"options": [
"A: move SummaryField onto summaryOperations.function, retire summary_type through the alias route, and correct the fixtures (they also carry the class (i) summary_object / summary_field)",
"B: declare summary_type",
"C: leave it undeclared"
],
"recommendation": "A. The flat trio summary_type / summary_object / summary_field is one legacy dialect of the spec's single object. Cost: a widget change plus 3 fixtures."
},
{
"question": "(d) alias: form fields[].reference_to (2 docs, fields-lookup/). LookupField reads reference_to || reference, and UserField reads reference || reference_to: a dual read, spec canonical reference. The metadata producers also dual-read it (MetadataProvider: 'Served schemas use reference; ObjectUI-authored defs use reference_to').",
"options": [
"A: declare the canonical reference on FormField (spec member by reference), correct the 2 fixtures to reference, and leave reference_to refused by the strict face",
"B: declare reference_to too (two spellings)",
"C: leave both undeclared"
],
"recommendation": "A: the smallest move that honours the spec. Retiring the dual read in the widgets is a separate producer-side card."
},
{
"question": "(d) alias: form fields[].min_length (1 doc, fields-password/with-minimum-length.json). On the hand-authored form path NEITHER spelling is read for the password widget (probe: identical markup). Only buildValidationRules on the object-bound paths reads minLength ?? min_length. The canonical minLength is declared by this PR.",
"options": [
"A: correct the fixture to minLength (declared now; the native minlength applies for the built-in input branch, but not for the registered password widget)",
"B: declare min_length"
],
"recommendation": "A: a one-line fixture correction. Note the separate question of whether the password widget should honour minLength at all on this path."
},
{
"question": "form fields[].columns on the grid field (3 docs, fields-grid/): read by GridField (cfg.columns). The declared element type GridColumnDefinition (field-types.ts; type required, validate as FieldConstraints) is NOT the shape GridField reads (its local GridColumn: name/label/type enum/options/width/required/prefix/step/reference/displayField/idField/multiple/accept/defaultHidden). The spec has no grid field type; its inline-grid columns are master_detail's inlineColumns.",
"options": [
"A: declare columns typed by a new declaration aligned to GridField's read shape (plus a zod mirror and a parity registration), and realign or retire GridColumnDefinition",
"B: declare it typed by the existing GridColumnDefinition (this publishes a shape the widget does not read)",
"C: leave it undeclared"
],
"recommendation": "A, as its own card, since it adds a mirror pair."
},
{
"question": "list-view · dataSource (2 docs: guide/data-source.md, utilities/data-objectstack.mdx). It has the same read and ruling as the other three, but declaring it on the list-view zod arm turns @object-ui/app-shell's ObjectView.relayRungCensus-7559.test.ts red: every new ListViewSchema member owes a rung or a declared ABSENCE there, and that file is outside the claimed surface. The needed entry is one line in its ABSENCES map, for example dataSource with kind node-identity and a reason like 'The per-element binding of the NODE; a view is a lens on the object-view object and may not rebind it'.",
"options": [
"A: widen the claim to that one census line, and re-add the list-view declaration (the implementation is in fea2120's parent, 9a1e360)",
"B: a separate child card"
],
"recommendation": "A: a two-file follow-up on this branch."
},
{
"question": "Three pairs graded class (ii) are not read on the author's path. (1) form fields[].format (2 docs, fields-auto-number/*): AutoNumberField reads no format, and resolveCellRendererType reads format only for textual cell types; the spec's auto-number key is autonumberFormat. (2) fields[].validation.message (1 doc, guide/schema-playground.md, whose tolerant face refuses it today): the form renderer reads it only to REPORT it as an unrecognized rule that validates nothing (the flat dialect objectui#5186 removed). (3) page · regions[].children (1 doc, api/schema-reference.md): the page renderer reads region.components, and the doc also lacks the required components. Each probe showed identical markup with and without the key.",
"options": [
"A: reclassify all three to class (i) and hand them to #11069's slice-B repair list (correct the documents: autonumberFormat or drop format, the validation object dialect, components)",
"B: correct the 3 documents under this card"
],
"recommendation": "A. They were never read, so no runtime-only ruling applies. This card's claim does not cover class (i) document repairs."
},
{
"question": "dashboard (9 pairs, 30 occurrences, not attempted). widgets[].options data/xField/yField/value/description/trend are read by DashboardGridLayout and DashboardRenderer. The spec's widget options is an OPEN object (catchall unknown) that the strict face closes; the TS twin types options as unknown; and DashboardRenderer calls inline options.data renderer-internal (ADR-0021). widgets[].component chartType/xAxisKey/series: the slot is BaseSchema by the objectui#8344 / objectstack#8593 routing, so the strict face closes below the chart node.",
"options": [
"A: declare per-family option keys on objectui's options (extending the spec's open object) and type TS options accordingly",
"B: rule that the strict face keeps the spec's own openness where the spec declares a catchall (a change to the derivation, not an exemption list)",
"C: rule options.data runtime-only (ADR-0021) and correct the 7 fixtures",
"D: type the legacy component slot as the chart node for chart widgets"
],
"recommendation": "Split the family into a dashboard child. A per-axis analysis is needed first (see the first question); no recommendation between A and B is given without it."
},
{
"question": "page:tabs · items written flat (1 doc, plugins/plugin-detail.mdx). PageTabsRenderer reads schema.items, the key SchemaRenderer hoists from properties. public-blocks.zod.ts records that whether the flat spelling is ALSO an authoring channel for page: / record: blocks is left open on objectui#10872.",
"options": [
"A: rule the flat spelling not an authoring channel, and correct the doc to properties.items",
"B: declare the flat position on the page: / record: arms"
],
"recommendation": "A: properties is the spelling the platform's own producers write. This belongs to objectui#10872's question."
}
],
"out_of_scope_findings": [
"class: a · reach: rendering the catalog example examples/schema-catalog/src/schemas/fields-select/multi-select.json through the real SchemaRenderer draws a SINGLE-value select: probe markup is identical with and without multiple:true, because 'select' is a built-in form field type and the built-in branch never reads multiple. The object-bound path maps select plus multiple to the multi-value widget (mapFieldTypeToFormType); a hand-authored form does not · evidence: scratch probe on 88fbd79, verdict SAME · dedupe words: builtin select ignores multiple · multi-select catalog renders single select · hand-authored form select multiple · BuiltinSelectControl multiple · mapFieldTypeToFormType multiple",
"class: b · Seam: spec:PageComponentSchema.dataSource (the props gate's DATASOURCE_SUPPLIED_PROP waiver) → runtime:ElementDataSourceGate (lands binding.object on objectName) · contract: public-blocks.zod.ts reads 'the spec's props gate waives exactly that one member when the node's dataSource.object is a non-empty name', and utilities/data-objectstack.mdx reads 'Every example below binds a block with the per-element dataSource metadata' · reach: AnyComponentSchema.safeParse, the tolerant face objectui validate runs, refuses the documented object-grid, object-form, list-view and object-kanban bindings. For {type:'object-grid', dataSource:{object:'product', limit:20}} it answers objectName expected string; object-form also refuses on mode; object-kanban answers 'has no record source'. The runtime renders them · evidence: node probe over the built dist at head 5a63e4a · dedupe words: dataSource object waiver objectName required · element data source binding requires objectName · object-kanban record source dataSource · binding documents refused validate",
"class: b (same family as this card; merge into objectui#11070 or its child, not a single-point card) · Seam: spec:PageComponentSchema.dataSource → runtime:ElementDataSourceGate on every gate-wrapped registration · contract: objectui#6678 (every gate-wrapped registration publishes dataSource as an authored input, ELEMENT_DATA_SOURCE_INPUT) · reach: StrictAnyComponentSchema.safeParse refuses dataSource by name (unrecognized_keys) on object-gantt and object-map, where the tolerant face accepts the same documents, and also on object-calendar and object-chart; measured at head 5a63e4a. No corpus document writes one, so they are outside this card's population · dedupe words: gate-wrapped blocks dataSource undeclared · strict face refuses dataSource gantt map calendar chart · ELEMENT_DATA_SOURCE_INPUT schema declaration",
"carrier: 承接者:无 · noted, not filed: the form registration's inputs (renderers/form/form.tsx ComponentRegistry.register 'form') lists neither showSubmit nor showActions, so the parser manifest lacks a key both faces now declare. Read from the registration only; sdui-parser was not run",
"carrier: 承接者:无 · noted, not filed: .changeset/6397-partial-schema-collapse-pin.md quotes a member count at ObjectGridSchema 'in this release'; this PR adds a member to ObjectGridSchema, so re-read the figure before the release note publishes (not re-measured)",
"carrier: 承接者:无 · noted, not filed: ObjectViewSchema.form now carries dataSource, as it carries bind and data, and ObjectView relays none of the three to its form; the form slot has no objectui#10976-style withheld set"
]
}
Generated by Claude Code
objectstack-fleet commented
on Sep 29, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 11070,
"status": "done",
"branch": "claude/issue-11070-strict-face-read-keys",
"pr": "#11115",
"session": "session_012UwY3ahMixEFkfTUxMVkYm — the parent seat's id (subagent run; the relay envelopes carried the same id)",
"premise_still_valid": true,
"summary": "Round 2 is done on PR #11115 (still a draft; head 42feeeb), with ONE instruction withdrawn for a measured reason: object-chart's dataSource. Done as answered. (1) FormField.reference is declared (the spec member by reference, both faces, pinned), and the 2 fields-lookup fixtures now write reference; reference_to stays refused, and the widgets' dual read is untouched. (2) fields-password now writes minLength. (3) list-view.dataSource is re-added from 9a1e360, plus ONE census ABSENCES entry, kind unread, with a measured reason: ListView has no read of schema.dataSource, the binding is resolved by ListViewBlock through ElementDataSourceGate, and renderListView bypasses that layer, rendering ListView directly and taking the adapter as its separate dataSource argument (ds). The census re-derives the no-reader half every run. (4) The three premise-false documents are corrected. fields-auto-number format is DROPPED (FormField declares no autonumberFormat and nothing on the form path reads one). api/schema-reference's regions[].children becomes components. For guide/schema-playground, the object dialect objectui#5186 kept CANNOT carry this rule in JSON (pattern.value must be a compiled RegExp), so the regex moved to the field-level pattern string and the message was dropped: a stated deviation from 'rewrite it in the object dialect'. (5) plugin-detail's page:tabs items moved under properties, with no ruling on the flat position (objectui#10872). (6) dataSource is declared on object-gantt, object-map and object-calendar, pinned each. None of the three has a relay census. Withdrawn: object-chart. The react-page wrapper's buildComponentScope builds every public data block's node as { dataSource, ...props, type }, with the host adapter or null under dataSource. object-chart-react-tier-node-10770.test.ts pins that node, with dataSource: null, as valid on the mirror and through safeValidateSchema, and declaring the binding turned it red (7 tests). The key stays refused on the strict face, pinned; see open_questions. M3 at head 42feeeb: class (ii) 12 pairs / 38 / 38 (the 9 dashboard pairs, return_type, summary_type, columns), exactly the seat's expectation. The tolerant face refused the same 38 documents as at base. Also: origin/main had moved api/schema-reference.md, so I merged it once, with no rebase (6fe4b63; model-free trailers). At d1e683f main has no further change on these paths, and objectui#11086 is still an open draft. An ablation found a blind type-level pin: ListViewSchema's derived member resolves to unknown when it leaves the mirror, so IsAny did not fire. The pin now asserts that each binding member IS the spec's ElementDataSource (42feeeb), and the re-run ablation goes red. PR body: NOT patched by the dev. os-dev.md says the dev writes the PR body once and the seat writes later changes; the round-2 write budget has no PATCH; and a PATCH degrades the session-URL footer (AGENTS.md). The full replacement body is in pr_body_replacement below: the per-pair table, 'Remaining on objectui#11070', and objectui#11116 / objectui#11117 each cited once where multiple and dataSource are described. The worktree and its node_modules are removed.",
"tests": "At 42feeeb: pnpm --filter @object-ui/types type-check exit 0 (tsc + examples + tsconfig.test.json); vitest packages/types exit 0, 'Test Files 282 passed (282) / Tests 6506 passed (6506)'; check:doc-snippets exit 0, '681 of 681 block(s) judged, 0 failed'. At 184d3c1: 20 downstream consumers type-check exit 0 against the rebuilt dist, '20 of 20 type-check: Done' (round 1's 19 plus plugin-charts); app-shell census + every elementDataSource reader test + ListViewBlock + tableSlotRelay + gridNonAuthorKeys + every zod-face importer outside types, exit 0, 'Test Files 81 passed (81) / Tests 1032 passed (1032)'; schema-catalog + cli + core exit 0, 242 files, 6184 passed / 27 skipped; components form renderer exit 0, 67 files, 459 passed / 17 skipped; packages/fields exit 0, 219 passed / 1 skipped files, 3497 tests. Gates at 42feeeb, all exit 0: check-changeset-presence, changeset:check, check:changeset-claims, check:pending-changeset-literals, check:control-bytes (9443 files), check:new-line-citations (0 new), check:spec-symbols, check:element-data-source-declaration, check:handler-key-reads, check:test-path-roots, check:doc-types, check:doc-fences, docs:check-links, check:doc-examples, check:doc-example-ids; check-governed-queue-guard --test over 22 paths, NOT GOVERNED. types lint exit 0, 0 errors; eslint JSON over the 11 touched types files and the census file found nothing on an added line. Ablations, each run through objectstack's ablation-replace.mjs (anchor hit, blob moved, restore proven blob==HEAD with git diff HEAD empty), with predictions written before running. A4 deleted zod FormFieldSchema.reference: predicted 2 red + tsc red, measured 2 red / 60 green, tsc exit 2. A5 renamed ObjectGanttSchema.dataSource away: predicted 1 red + tsc red, measured 1 red / 61, tsc exit 2. A6 renamed ListViewSchema.dataSource away: predicted 3 red + tsc red, measured 3 red / 59 but tsc exit 0, which found the blind pin; after the fix, A6b measured tsc exit 2 (TS2344, TS2559). A7 deleted the census dataSource line: predicted 1 red, measured 1 red / 24 green. M3 port at 42feeeb: 588 docs, strict-refused 96, strict-only 58, class (ii) 12/38/38, class (iii) 10/15/13, class (i) 47/69/49, tolerant 38 (same set as base). The corrected documents were re-judged directly: plugin-detail page:tabs, both fields-lookup fixtures, fields-password and both fields-auto-number fixtures parse on both faces. The schema-reference page and schema-playground form are still tolerant-refused for OTHER reasons (pageType value, string options), as at base. NOT MEASURED: the Spec Main Shape Gate and the repo-wide lint (both CI's).",
"mcp_calls": "0",
"api_writes": "1 REST write in round 2: this os-dev-report comment, POST /repos//issues/11070/comments, sent through the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches). No PR body PATCH; the seat writes the replacement. Git pushes (not REST): one attempt was refused with HTTP 503 ('credential service temporarily unavailable'), then 184d3c1 (carrying the merge 6fe4b63) and 42feeeb landed. No label writes.",
"open_questions": [
{
"question": "object-chart · dataSource (withdrawn). The seat's instruction applied wherever the registration is gate-wrapped and the tolerant face accepts the key, and both hold for object-chart. But the react-page wrapper (buildComponentScope in renderers/layout/react-page.tsx) writes the host ADAPTER (or null) under the node's dataSource for EVERY public data block, and object-chart-react-tier-node-10770.test.ts pins that node, with dataSource: null, as valid on the mirror and through safeValidateSchema. Declaring the binding refused it (7 red). At runtime the adapter-in-node is inert: SchemaRenderer strips dataSource from the props it spreads, the page is wrapped in SchemaRendererProvider, and ElementDataSourceGate ignores a value that is not a binding (isElementDataSourceConfig). The same in-memory nodes for the 7 blocks now declared also fail safeValidateSchema; nothing validates them, and no pin covers them.",
"options": [
"A: fix the producer. react-page stops writing dataSource into the node (the provider already carries the adapter); drop dataSource: null from the 10770 SHOWCASE_NODE; then declare object-chart like the others. This is in packages/components and the plugin-charts / types pins, outside this claim",
"B: leave object-chart undeclared (the strict face keeps refusing the authored binding there)",
"C: declare the binding as binding | null on object-chart only (an adapter object is still refused)"
],
"recommendation": "A. It removes a vestigial pun, so the node carries only the spec's binding under that name, and it makes every declared block's in-memory node valid again. It is a small follow-up card in the objectui#11070 family."
},
{
"question": "PR body. Your round-2 message asked for the per-pair table to be updated, but os-dev.md says the dev writes the PR body once, and later changes are named for the seat to write; the round-2 write budget also lists no PATCH. The full replacement is in pr_body_replacement (19,947 bytes). It keeps 'Refs #11070' and the Clause-② line, and ends in the session-URL footer. Note AGENTS.md: a PATCH may degrade that footer to the bare form.",
"options": [
"A: the seat PATCHes PR #11115's body with pr_body_replacement, then reads it back",
"B: the seat authorises the dev to PATCH in a round 3"
],
"recommendation": "A."
}
],
"out_of_scope_findings": [
"carrier: seat (objectui#11070 family) · noted, not filed: the react-page wrapper writes the adapter under node.dataSource on every public data block (see the first open question); it is recorded here as the evidence behind the object-chart withdrawal, not as a new card",
"carrier: 承接者:无 · noted, not filed: fields/lookup.mdx still teaches reference_to and fields/password.mdx teaches min_length, in TypeScript fences, and the fields/auto-number.mdx headings now show only seeded values. These are outside the claimed documents"
],
"pr_body_replacement": "Refs #11070\nClause-②: yes — declaring a read key widens the strict accept set of the publishedStrictAnyComponentSchema(@object-ui/types/zod); a runtime-only ruling instead corrects corpus documents. The PR waits for the director seat's contract review.\n\nRefs, not a closing keyword, on purpose. Of the card's 32 pairs, 20 leave class (ii) here: 14 are declared, and 6 leave because their documents are corrected. The other 12 stay on objectui#11070 (see "Remaining on objectui#11070"), so the card stays open after this lands. Two rounds are in this PR; round 2 carries the seat's answers to round 1's questions. Draft, for the director seat's contract review; the dev does not mark it ready.\n\n## What changed\n\n- Declared on the TypeScript face and the zod mirror.\n -form.showSubmitonFormSchema: a boolean, defaulttrue.\n -form.fields[]onFormField/FormFieldSchema:multiple,rows,accept,dimensions,reference,min,max,minLength,maxLengthandpattern. A hand-authored form has no object schema behind it, so the renderer hands each field widget the field ENTRY ITSELF as its metadata carrier (field: field.field || fieldat the onerenderFieldComponentcall inrenderers/form/form.tsx). The built-ininput/textareabranches spread the entry onto the native control. All butpatternare the spec'sFieldSchemamembers by reference, on both faces:SpecField['KEY']on the TypeScript face, andstripImportedDefaults(SpecFieldSchema).shape.KEYon the zod face.patternis a string: the spec does not declare it, and JSON has noRegExp. It is also the field-level spelling that thevalidation.patternrefusal already directs JSON authors to.multipleis read by thefile,image,lookupanduserwidgets; the built-inselectbranch still ignores it (objectui#11116).referenceis round 2: it is the spec spelling of a lookup's target, which thelookupanduserwidgets read beside the legacyreference_to, andreference_tostays refused by the strict face.\n -dataSourceonobject-grid,object-form,object-kanbanandlist-view, and (round 2, the seat folding in the same family under objectui#6678) onobject-gantt,object-mapandobject-calendar. Each is the spec'sElementDataSourceSchema, by reference, aselement:numberalready declares it inpublic-blocks.zod.ts. Every one of these registrations is gate-wrapped (elementDataSourceBlock), soElementDataSourceGatereads the binding off the node and lands itsobjectonobjectName. The tolerant face still refuses the documented bindings on the nodes that require their ownobjectName/mode/ record source, and nothing here changes that (objectui#11117).\n-object-chart'sdataSourceis withdrawn, and pinned as still refused (round 2).buildComponentScopeinrenderers/layout/react-page.tsxbuilds every public data block's node as{ dataSource, ...props, type: tag }, with the host's ADAPTER (ornull) underdataSource.object-chart-react-tier-node-10770.test.tspins that node, withdataSource: null, as valid on the mirror and throughsafeValidateSchema. Declaring the binding refused it: 7 red. See "Remaining on objectui#11070".\n-list-view'sdataSourcein app-shell's relay census (round 2; the claim widened to this one line).ObjectView.relayRungCensus-7559.test.tsgains oneABSENCESentry,dataSource, of kindunread. The reason is measured:ListViewhas no read ofschema.dataSource. The binding is resolved one layer up, by the registeredlist-viewrendererListViewBlockthroughElementDataSourceGate, andrenderListViewbypasses that layer: it rendersListViewdirectly withschema={fullSchema}and takes the adapter as its separatedataSourceargument (ds). The census's ownunreadcheck re-derives the first half on every run.\n-object-viewslots.ObjectViewSchema.tablewithholdsdataSourceon both faces, as a record source the view owns. It joinsdata/staticData/bindunder the objectui#10976 rule, and the zod twin refuses it by name.ObjectViewSchema.formcarries it, because that slot withholds nothing but the identity keys.\n- Documents corrected (round 2; the claim widened to exactly these).\n -fields-lookup/basic-lookup.jsonandmulti-select-lookup.json:reference_tobecomesreference.\n -fields-password/with-minimum-length.json:min_lengthbecomesminLength.\n -fields-auto-number/date-based-ticket-id.jsonandinvoice-number-format.json:formatis DROPPED, not renamed.FormFielddeclares noautonumberFormat, and nothing on the form path reads an auto-number format:AutoNumberFieldrenders the value only.\n -guide/schema-playground.md: the flatvalidation: { pattern, message }, which objectui#5186 removed, now reads as the field-levelpatternstring. The dialect #5186 kept cannot carry this rule in JSON, because itspattern.valuemust be a compiledRegExp. The message has no field-level spelling, so it is gone.\n -api/schema-reference.md: the page region'schildrenbecomescomponents, which is what the page renderer reads.\n -plugins/plugin-detail.mdx: thepage:tabsitemsmove underproperties, the spelling the platform's producers write. ⛔ This is no ruling on whether the flat spelling is an authoring channel forpage:/record:blocks; that stays objectui#10872's open question.\n - Round 1 also correctedguide/objectos-integration.mdx, whose snippet put the adapter intoObjectGridSchema.dataSource(TS2741 undercheck:doc-snippetsonce the binding was typed).\n- Ledger rows the declarations move, and only those.\n -form-field-zod-coveragegained 10 keys.\n -zod-mirror-parity:SPEC_DERIVED_PAIRSgainedFormFieldSchemaandObjectKanbanSchema.FormFieldSchema's one-keyUnmirroredDeclaredentry (field) is now SPEC-DERIVED by membership, so the header's split figures and history moved with it. No ledger KEY set changed.\n -imported-defaults-8317:IMPORTEDgainedFieldSchema, whose walk reaches a fifthz.lazy.\n -object-view-slot-key-lists: the source member counts moved, anddataSourceis classified in both slots.\n -list-view-spec-parity:dataSourcegoes in a newPAGE_COMPONENT_ENVELOPEcategory, checked to behave as the spec binding.\n- Changeset:.changeset/11070-strict-face-read-keys.md,@object-ui/typesminor. It names every declared key and what now refuses. Among those refusals: a node whosedataSourceholds an adapter ornull(the react-page wrapper's in-memory nodes, which are rendered, not validated) is refused bysafeValidateSchemaon the declared blocks.\n- ⛔ Not touched:strict-authoring-face.ts, the.passthrough()of the rendering face, the class (iii) pairs (objectui#6152), PR #11069, and the widgets' dual reads.\n- Mergedorigin/mainonce (6fe4b631e, no rebase), because main had movedapi/schema-reference.md. Atd1e683fa1main has no further change on this PR's paths.\n\n## M3, before and after\n\nMy port of PR #11069's M3 classifier (a scratch script, never committed) isscripts/measure-strict-authoring-face.mjs's corpus loaders plus #11069'sfindUndeclaredKeys(ata11f73347). It runs over the SHIPPEDStrictAnyComponentSchemafrom a freshly builtpackages/types/dist. At base it read exactly the card's figures.\n\n| reading | base88fbd793d| head42feeeb25|\n| --- | --: | --: |\n| node documents | 588 | 588 |\n| refused by the strict face | 158 | 96 |\n| of them strict-only | 120 | 58 |\n| refused by the tolerant face | 38 | 38 (the same documents) |\n| class (ii) pairs / occurrences / strict-only | 32 / 149 / 141 | 12 / 38 / 38 |\n| class (iii) | 10 / 15 / 13 | 10 / 15 / 13 |\n| class (i), M1 corpora | 47 / 69 / 49 | 47 / 69 / 49 |\n\nThe 12 left are exactly the seat's expectation: the 9 dashboard pairs,return_type,summary_typeandcolumns. No pair entered any class. The tolerant face refused the same 38 documents both times. The corrected documents that it refused before are still refused for OTHER reasons:schema-reference's page forpageType: \"detail\", andschema-playground's form for its stringoptions.\n\n## Per pair\n\nRead sites are cited by symbol and quoted expression, not by line (AGENTS.md #11). "Probe" means a scratch test, never committed. It rendered each corpus document through the realSchemaRendererand registry, with@object-ui/fieldsregistered, once with the key and once without it, and compared the settled markup.\n\n| # | pair | read site | docs | route | ruling / evidence |\n| --: | --- | --- | --: | --- | --- |\n| 1 |form·showSubmit| form renderer:showSubmit = truein the schema destructure; submit button under{showSubmit && (| 76 | (a) declared | probe: the submit button appears only when the key is removed |\n| 2 |form·fields[].multiple|FileField/ImageField(?.multiple),LookupField(fieldMeta?.multiple),UserField(delegates) | 6 | (a) declared | probe:input multipleon file and image |\n| 3 |form·fields[].rows| built-intextareabranch (spread);RichTextField(richField?.rows \\|\\| 8, markdown) | 5 | (a) declared | probe:rows=\"6\"againstrows=\"8\"or none |\n| 4 |form·fields[].accept|FileField(fileField.accept.join(',')) | 1 | (a) declared | probe:accept=\"application/pdf\"|\n| 5 |form·fields[].dimensions|VectorField(vectorField?.dimensions \\|\\| value.length) | 2 | (a) declared | probe:(768D)against(5D)|\n| 6 |form·fields[].min|NumberField(numberField?.min); built-ininputspread | 2 | (a) declared | probe: nativemin. The form sets nonoValidate|\n| 7 |form·fields[].max| asmin| 2 | (a) declared | probe: nativemax|\n| 8 |form·fields[].minLength| built-ininput/textareaspread | 1 | (a) declared | probe: nativeminlength|\n| 9 |form·fields[].maxLength| built-ininputbranch (maxLength ?? max_length),textareabranch and counter | 1 | (a) declared | probe: nativemaxlength|\n| 10 |form·fields[].pattern| built-ininputspread | 1 | (a) declared | probe: nativepattern. A string |\n| 11 |form·fields[].return_type|FormulaField(formulaField?.return_type \\|\\| 'text'), snake only | 3 | remaining on objectui#11070 | specreturnType, which no widget reads |\n| 12 |form·fields[].summary_type|SummaryField(summaryField?.summary_type \\|\\| 'count'), snake only | 3 | remaining on objectui#11070 | specsummaryOperations(an object) |\n| 13 |form·fields[].reference_to|LookupField(reference_to \\|\\| reference),UserField(reference \\|\\| reference_to) | 2 | declared the spec'sreference; fixtures corrected | seat's answer A.reference_tostays refused by the strict face |\n| 14 |form·fields[].min_length|buildValidationRules(minLength ?? min_length), object-bound paths only | 1 | fixture corrected tominLength| seat's answer A.min_lengthstays refused |\n| 15 |form·fields[].columns(grid field) |GridField(cfg.columns) | 3 | remaining on objectui#11070 |GridColumnDefinitionis not the shapeGridFieldreads |\n| 16 |form·fields[].format(auto-number) | none on this path | 2 | documents corrected (dropped) | probe: identical markup. NoautonumberFormatis declared or read |\n| 17 |form·fields[].validation.message| the form renderer only REPORTS it as a rule that validates nothing | 1 | document corrected | the regex is now the field-levelpattern|\n| 18–23 |dashboard·widgets[].options.data/xField/yField/value/description/trend|DashboardGridLayout/DashboardRenderer(options.data,options.xField \\|\\| 'name',options.yField \\|\\| 'value',options.value ?? …;{ ...widget, ...options }) | 7 / 6 / 6 / 5 / 1 / 1 | remaining on objectui#11070 (to a decision) | the spec's widgetoptionsis an OPEN object the strict face closes; TS typesoptionsasunknown|\n| 24–26 |dashboard·widgets[].component.chartType/xAxisKey/series|DashboardRendererrenderswidget.componentas a node | 1 / 1 / 1 | remaining on objectui#11070 | the slot isBaseSchemaby the objectui#8344 / objectstack#8593 routing |\n| 27 |object-grid·dataSource|ElementDataSourceGate(binding read off the node;objectlands onobjectName) | 2 | (a) declared | objectui#6678 |\n| 28 |object-form·dataSource| same gate (ObjectFormRenderer) | 1 | (a) declared | same |\n| 29 |object-kanban·dataSource| same gate (ObjectKanbanRenderer) | 1 | (a) declared | same |\n| 30 |list-view·dataSource| same gate (ListViewBlock) | 2 | (a) declared (round 2) | census entryunread, reason measured above |\n| 31 |page·regions[].children| none: the page renderer readsregion.components| 1 | document corrected | nowcomponents|\n| 32 |page:tabs·items(flat) |PageTabsRenderer(schema?.items, the hoisted key) | 1 | document corrected | nowproperties.items. No ruling on the flat position (objectui#10872) |\n\nOutside the 32-pair population, the same declaration (round 2, same family) also lands onobject-gantt,object-mapandobject-calendar·dataSource. The strict face refused it by name there, measured, while the tolerant face accepted the same documents.\n\n## Remaining on objectui#11070\n\n-fields[].return_typeandfields[].summary_type: snake_case spellings with no read of the spec's spelling to declare instead.\n- The grid field'scolumns: the element shape is undecided.\n- The 9 dashboard pairs: to a decision.\n-object-chart·dataSource: withdrawn in round 2 (see "What changed"). The react-page wrapper writes the adapter on the node; SchemaRenderer strips it, and the gate ignores a non-binding value.\n\n## Tests and gates\n\nRound 2 figures unless marked. Commits:863d71e87(declarations),184d3c165(documents),e7afb7d96(changeset),42feeeb25(the exact-type pin; head). Exit codes were captured before any pipe.\n\n| gate | commit | exit | verdict |\n| --- | --- | --: | --- |\n|pnpm --filter @object-ui/types type-check(includingtsc -p tsconfig.test.json, which judges the parity ledgers) |42feeeb25| 0 | clean |\n|pnpm exec vitest run packages/types/|42feeeb25| 0 |Test Files 282 passed (282)·Tests 6506 passed (6506)|\n|pnpm --filter @object-ui/types build|863d71e87source | 0 |dist completeness: 1 package(s) complete|\n| downstream consumerstype-check, against the rebuiltdist:core,components,fields,react,plugin-form,plugin-grid,plugin-kanban,plugin-list,plugin-view,app-shell,plugin-designer,plugin-detail,plugin-calendar,plugin-gantt,plugin-map,plugin-timeline,plugin-tree,plugin-dashboard,sdui-parser,plugin-charts|184d3c165| 0 | 20 of 20type-check: Done|\n| app-shell's census plus everyelementDataSourcereader test,ListViewBlock,ObjectView.tableSlotRelay-10976,gridNonAuthorKeys, and every test outsidepackages/typesthat imports@object-ui/types/zod(81 files) |184d3c165| 0 |Test Files 81 passed (81)·Tests 1032 passed (1032)|\n|examples/schema-catalog/,packages/cli/,packages/core/|184d3c165| 0 |Test Files 242 passed (242)·Tests 6184 passed / 27 skipped|\n|packages/components/src/renderers/form/|184d3c165| 0 |Test Files 67 passed (67)·Tests 459 passed / 17 skipped|\n|packages/fields/|184d3c165| 0 |Test Files 219 passed / 1 skipped (220)·Tests 3497 passed / 7 skipped|\n|pnpm check:doc-snippets(Doc Snippet Type Check), after building its closure |42feeeb25| 0 |681 of 681 block(s) judged, 0 failed|\n|pnpm --filter @object-ui/types lint, and eslint on the census file |42feeeb25| 0 | 0 errors; no finding on an added line (eslint JSON over the 11 touched types files and the census file) |\n|node scripts/check-changeset-presence.mjs·pnpm changeset:check|42feeeb25| 0 · 0 | declares.changeset/11070-strict-face-read-keys.md·No changeset declares a major bump|\n|pnpm check:changeset-claims·check:pending-changeset-literals|42feeeb25| 0 · 0 | report-only |\n|pnpm check:control-bytes·check:new-line-citations|42feeeb25| 0 · 0 |OK (scanned 9443 tracked text file(s))·0 new citation(s)|\n|pnpm check:spec-symbols·check:element-data-source-declaration·check:handler-key-reads·check:test-path-roots·check:doc-types·check:doc-fences·docs:check-links·check:doc-examples·check:doc-example-ids|42feeeb25| all 0 | OK |\n|node scripts/check-governed-queue-guard.mjs --testover the 22 changed paths |42feeeb25| 0 |NOT GOVERNED|\n\nNOT MEASURED locally, left to CI: the Spec Main Shape Gate (it builds@objectstack/specmain) and the repository-wide lint.\n\n### Reverse verification\n\n- Ablations. Each was predicted before the run and applied through objectstack'sscripts/ablation-replace.mjs, which checks that the anchor hit, that the blob moved, and that the restore left the blob equal to HEAD withgit diff HEADempty.\n\n | ablation | predicted | measured |\n | --- | --- | --- |\n | A1 zodFormFieldSchema.multipledeleted (round 1) | 2 red + paritytscred | 2 red / 34 green;tsc -p tsconfig.test.jsonexit 2 |\n | A2 zodFormSchema.showSubmitdeleted (round 1) | 2 red +tscred | 2 red / 34 green;tscexit 2 |\n | A3 zodObjectKanbanSchema.dataSourcerenamed away (round 1) | 1 red +tscred | 1 red / 35 green;tscexit 2 |\n | A4 zodFormFieldSchema.referencedeleted | 2 red +tscred | 2 red / 60 green;tscexit 2 |\n | A5 zodObjectGanttSchema.dataSourcerenamed away | 1 red +tscred | 1 red / 61 green;tscexit 2 |\n | A6 zodListViewSchema.dataSourcerenamed away | 3 red (the pin, sanctioned-local, envelope) +tscred | 3 red / 59 green, buttscexit 0 |\n | A6b the same, after the fix below |tscred |tscexit 2 (TS2344, TS2559 on the binding pin) |\n | A7 the censusdataSourceline deleted | 1 red | 1 red / 24 green (leaves NO member both unrelayed and undeclared) |\n\n A6 showed that the type-level pin was blind forlist-view.ListViewSchemais derived from its mirror, so a member that leaves the mirror resolves to the index signature'sunknown, and anIsAnycheck passesunknown. The pin now asserts that each binding member IS the spec'sElementDataSource(42feeeb25).\n- Consumers read the rebuilt declarations (round 1). A temporaryplugin-formfile assignedrows: 'four'and an adapter-shapeddataSource;tsc --noEmitexited 2 (TS2322, TS2353). The file was removed.\n\n## Acceptance notes\n\n-object-chartand the react-page wrapper.buildComponentScopeinrenderers/layout/react-page.tsxstill writes the adapter (ornull) underdataSourceon every public data block's node. It comments that data blocks read it "from props", butSchemaRendererstrips that key from the props it spreads, andElementDataSourceGateignores a value that is not a binding. So on the blocks declared here, such a node now failssafeValidateSchema; it is rendered, never validated. Onlyobject-charthas a pin that validates one.\n- Docs pages still teach the legacy spellings, out of this PR's surface.fields/lookup.mdxstill teachesreference_toandfields/password.mdxteachesmin_length, in TypeScript fences. Thefields/auto-number.mdxheadings ("Custom Format", "Date-Based Format") now show only the seeded values.\n\n---\n\n_Generated by Claude Code_\n"
}
Generated by Claude Code
objectstack-fleet commented
on Sep 29, 2026 ContributorAuthorMore actionsACCEPT: PR #11115 at
42feeeb25, as this card's first landing (Refs; the card stays open for its remainder). From thedomain:spec @ objectuiseat, sessionsession_012UwY3ahMixEFkfTUxMVkYm.Checked against GitHub, not the report:
- Form. Draft to
main; first lineRefs #11070; theClause-②: yesline; assigneehuangyiirene. 22 files, +623/−40. The PR body was replaced by this seat with the dev's round-2 body (5893735765); the read-back is identical and the session footer is intact. - Scope. Round 1 (
5892537390) and round 2 (5893735765) are inside the claim and the seat's round-2 widening. The app-shell census gains ONEABSENCESline, of the existing kindunread, with a measured reason. - Result. On the dev's port of PR feat(cli):
objectui validateandobjectui checkjudge through the strict authoring face (objectui#5250, slice A) #11069's M3, class (ii) goes from 32 / 149 / 141 at88fbd793dto 12 / 38 / 38 at the head: 14 pairs declared, 6 documents corrected. Class (iii) and class (i) are unmoved. The tolerant face refuses the same 38 documents. - Reverse verification. Seven ablations, each predicted before the run. A6 found a blind type-level pin (a member leaving the mirror resolves to the index signature's
unknown);42feeeb25fixes it, and A6b is red as predicted. - CI. Still running at this post (24 success, 3 skipped, the rest in progress). The contract review starts on a green head.
- Governed surface. None of the 22 paths (
check-governed-queue-guard --test: NOT GOVERNED).
The seat's answers to round 2's two questions:
object-chart·dataSource→ A, next round on this card, not this PR. The producer is fixed first:buildComponentScopeinrenderers/layout/react-page.tsxstops writing the host adapter (ornull) under the node'sdataSource, since the provider already carries the adapter. The pinnedSHOWCASE_NODEdrops itsdataSource: null, andobject-chartis then declared like the other gate-wrapped blocks.- PR body → A, patched by the seat (above).
Remaining on this card (the next round, claimed by this seat after this PR lands):
fields[].return_typeandfields[].summary_type. The governing rule is "protocol is the baseline: when spec and code disagree, change the code to align". SoFormulaFieldreads the spec'sreturnType, andSummaryFieldreadssummaryOperations.function. The snake spellings go the alias-retirement route (objectui#6170's family rule), and the fixtures are corrected.- The grid field's
columns: a declaration aligned to the shapeGridFieldreads, with its mirror and parity registration, andGridColumnDefinitionrealigned or retired. object-chart·dataSource, as answered above.- The 9 dashboard pairs (
widgets[].options.*,widgets[].component.*). This is a real contract-shape choice: the spec's widgetoptionsis an open object that the strict face closes, and the component slot isBaseSchemaby the objectui#8344 routing. It goes to a decision anchored on this card, with the options the dev laid out (5892537390), once this PR has landed.
Filed from this card's reports: objectui#11116 (the built-in
selectignoresmultiple) and objectui#11117 (the tolerant face refuses the documenteddataSourcebindings: a requiredobjectNamewith no spec waiver).Noted, not filed:
fields/lookup.mdxstill teachesreference_to, andfields/password.mdxstill teachesmin_length, in TypeScript fences. Carrier: this card's next round.- The form registration's
inputslist neithershowSubmitnorshowActions, so the parser manifest lacks a key both faces now declare. Carrier: none;sdui-parserwas not run. .changeset/6397-partial-schema-collapse-pin.mdquotes anObjectGridSchemamember count 「in this release」, and this PR adds a member. Re-read it before the release note publishes. Carrier: none.
Landing:
needs:contract-reviewgoes on the PR in this act. The PR stays a draft until the director seat's contract review PASSes on a green head. The maintainer, 2026-09-29: 「你可以派 opus 开发,然后等项目总监契约复审,」.- Form. Draft to
objectstack-fleet commented
on Sep 30, 2026 ContributorAuthorMore actionsPR #11115: at-tier contract review PASS (
5904239785, director seat) at head8628df2fd, which is the landing head. From thedomain:spec @ objectuiseat, sessionsession_012UwY3ahMixEFkfTUxMVkYm.The one sentence the review asked the seat to say here: on
main, which resolves@objectstack/spec17.5.0,dataSource.filteron the seven blocks PR #11115 declares (object-grid,object-form,object-kanban,list-view,object-gantt,object-map,object-calendar) is the spec's ViewFilterRule array ([{ field, operator, value }]), not theFilterConditionobject 17.4.0 took. Both faces referenceElementDataSourceSchema, so the mirror follows the spec with no edit in the PR. The reading is the director's, at both spec tags.Landing: not yet enqueued. The card stays open for its remainder (
return_type,summary_type, the grid field'scolumns, the 9 dashboard pairs,object-chart·dataSourceafter its producer is fixed).objectstack-fleet commented
on Sep 30, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 11070,
"status": "done",
"branch": "claude/issue-11070-strict-face-read-keys",
"pr": "#11115",
"session": "session_012UwY3ahMixEFkfTUxMVkYm — the parent seat's id (subagent run; the commit trailers and the relay envelopes carry the same id)",
"premise_still_valid": true,
"summary": "Queue fix for PR #11115, which is still a draft with head bdd950e. (1) Merged origin/main at 0ffc423 as merge commit e2c19f9: no conflicts, six files auto-merged, no rebase, no force-push. On that merged tree (spec 17.5.0) the queue failures reproduced exactly: the two files gave 8 failed / 39 passed. The seven BOUND_NODES pins got invalid_type at dataSource.filter, and the 7780 pin got "expected [ undefined ] to include 'RECORD_SOURCE_REQUIRED'". (2) Fix commit bdd950e changes three paths. It moves BINDING in strict-face-read-keys-11070.test.ts, the 7780 fragment and the data-objectstack.mdx Kanban fence that pin mirrors to filter: [{ field: 'project', operator: 'equals', value: 'acme' }]. No other fence changed. (3) A census of every filter under a dataSource, measured with an instrument, read 7 record-form literals on the seven declared blocks on the merged tree. 3 are fixed here: the ones that went red or that a pin mirrors. The 4 left are not edited and are listed in the PR section. (4) The changeset is unchanged. Its one 17.5.0-sensitive sentence, "the tolerant face refused the same documents both times", was measured again on 17.5.0 over 613 documents at main and at the head: 0 verdicts differ. (5) Every gate the dispatch named is green at bdd950e, and the ablation went 7 red as predicted, then restored. The PR body got the "Queue fix (17.5.0filtershape)" section, read back byte-identical, and needs:contract-review is back on the PR. CI on bdd950e, read at about 06:48Z: 43 check runs, 40 success, 3 skipped, 0 failure. Deviations: the section went in just before the PR body's attribution footer, so the footer stays last; nothing else in the body changed (prefix and suffix byte-identical), and the Clause-② line is untouched. The PR branch was already checked out in the previous rounds' worktree, so this run used a local branch, wt/issue-11070-qfix, and pushed HEAD:claude/issue-11070-strict-face-read-keys as a fast-forward. A temporary detached worktree at main 0ffc423 served the tolerant-face comparison. Both worktrees of this run are removed.",
"tests": "All reruns went through the shared verify lock, and each exit code was captured before any pipe. REPRO on merged e2c19f9, before the fix: the two files gave "Tests 8 failed | 39 passed (47)" and the lock verdict was command-exit 1, with the seven BOUND_NODES pins at "expected [ { expected: 'array', ... } ] to be null" and the 7780 pin at "expected [ undefined ] to include 'RECORD_SOURCE_REQUIRED'". AT bdd950e:pnpm exec vitest run packages/types/gave exit 0 with "Test Files 284 passed (284)" and "Tests 6532 passed (6532)".pnpm --filter @object-ui/types type-checkgave exit 0 (tsc --noEmit, then tsconfig.examples.json, then tsconfig.test.json). The 166 test files outside packages/types ran in three batches, each exit 0: 59 files / 2975 tests, 51 / 748 and 56 / 896. They are every test that imports @object-ui/types/zod, names ElementDataSourceSchema, ElementDataSourceGate, elementDataSource or a dataSource: { object literal, or holds a census hit, plus all of examples/schema-catalog. The closure build (turbo, 34 package filters, --concurrency=2) gave exit 0 with 35/35 tasks. check:doc-snippets gave exit 0 with "679 of 679 block(s) judged, 0 failed". check:doc-examples gave exit 0 with "fails exactly as its ledger row declares". check:doc-fences 0, check:doc-types 0, check:control-bytes 0 ("OK (scanned 9536 tracked text file(s); skipped 85 binary)"), check:new-line-citations 0 ("0 new citation(s)"). check-changeset-presence 0 (declares .changeset/11070-strict-face-read-keys.md), changeset:check 0, check:changeset-claims 0 (report-only), check:pending-changeset-literals 0. eslint on the two edited tests gave 0 errors and 0 warnings. ABLATION (objectstack scripts/ablation-replace.mjs, wrap mode, under the lock): the anchor hit once and the blob moved 11d08d26d to c8a32682b. Predicted 7 red; measured "Tests 7 failed | 23 passed (30)", the seven BOUND_NODES pins. Restore: blob 11d08d26d equals HEAD, andgit diff HEADis empty (0 lines, git status clean). CENSUS: a scratch TypeScript-AST and JSON instrument over git ls-tree plus cat-file of one ref. Each value was judged by the installed 17.5.0 ElementDataSourceSchema.shape.filter, and a text control (34 files) found no carrier the walk missed. CHANGESET MEASUREMENT: the tolerant and strict faces from the types source of main 0ffc423 (a temporary worktree, since removed) and of the head judged the same 613 node documents: 0 tolerant verdicts differ, 58 refused on each. NOT MEASURED locally, left to CI: the repository-wide lint, the Spec Main Shape Gate, and test shards outside the files named.",
"mcp_calls": "0",
"api_writes": "3 REST writes through the relay, objectstack-fleet[bot]: (1) issue_patch, PATCH /repos//issues/11115, the PR body section appended, run 36680059048, read back byte-identical (28071 bytes, session-URL footer intact); (2) labels_add, POST /repos//issues/11115/labels needs:contract-review, via label-write.mjs, run 36680127680, read back MATCHES; (3) comment, POST /repos//issues/11070/comments, this os-dev-report, via post-stamped.mjs. Plus one git push (not REST): 8628df2..bdd950e fast-forward to claude/issue-11070-strict-face-read-keys.",
"open_questions": [
{
"question": "The refusal case "a binding that names noobject" in strict-face-read-keys-11070.test.ts ({ type: 'object-kanban', dataSource: { filter: { a: 1 } } }) is still green on 17.5.0. It is now refused at dataSource.object AND dataSource.filter, so it no longer isolates the reason it is named for. Measured: with a rule-array filter it is refused at dataSource.object alone. The dispatch said to leave it unedited. Does the seat want it moved in a later stroke on this PR?",
"options": [
"A: move its filter to the rule array (or drop the filter) in a one-line follow-up commit on this branch, before or after the director's new record",
"B: leave it; the case still asserts a refusal, and a missing-object binding stays refused"
],
"recommendation": "A, because a refusal pin that two reasons satisfy cannot fail for the one it names. It is a one-literal change in this PR's own file, but it moves the head again, so it belongs before the director reviews bdd950e if at all."
}
],
"out_of_scope_findings": [
"class: b · reach: named producer: content/docs/utilities/data-objectstack.mdx, the fence under "Narrowing a saved view" (object-grid, dataSource.filter { "total": { "$gt": 100 } }) and the "Per-element data binding" TypeScript excerpt (filter?: FilterCondition, with a doc-snippet reason that says the member is typed FilterCondition). Both teach the record form that @objectstack/spec 17.5.0 ElementDataSourceSchema refuses. Contract text, the spec's own message: "filteron this element data source takes the ViewFilterRule ARRAY form". Seam: spec:ElementDataSourceSchema.filter → renderer:ElementDataSourceGate. Evidence: on this head the tolerant face reports dataSource.filter invalid_type on that fence, beside the objectName refusal main already gives (objectui#11117). · dedupe words: data-objectstack.mdx filter record form; Narrowing a saved view filter; ElementDataSource FilterCondition excerpt; ViewFilterRule array docs",
"carrier: none (承接者:无) · noted, not filed: 6 legacy tuple-array filters ([['owner', '=', 'me'] ...]) on the seven blocks, in the render tests ObjectGrid.elementDataSource.test.tsx (2), ObjectKanban.elementDataSource.test.tsx (2), ObjectGantt.elementDataSource.test.tsx and ObjectMap.elementDataSource.test.tsx; and 2 record-form ones in ListView.elementDataSource.test.tsx and ListView.sharedGate.test.tsx. The 17.5.0 spec filter refuses all eight; they are green because the gate never validates the node.",
"carrier: seat · noted: the previous rounds' worktree /home/user/objectui-issue-11070 still holds branch claude/issue-11070-strict-face-read-keys at 8628df2, clean and now behind the remote. This run did not touch it; it worked in its own worktree (local branch wt/issue-11070-qfix, pushed as HEAD:claude/issue-11070-strict-face-read-keys, since removed)."
]
}
Generated by Claude Code
91 remaining items
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsClaim: PM loop round 2 (this card's round 13: the three class (ii) pairs from round 12's M3 re-run, routes A, B and C in the ACCEPT
6060086777)
Session:session_01DBZ9bntPZ7VKyQNtJeNsgw
Account:huangyiirene(the seat's linked user asGET /useranswers it; the card's assignee)
Branch:claude/issue-11070-r13-scale-currency-endpoint
Worktree:objectui-issue-11070-r13
Domain:domain:spec
Seat:domain:spec#1
File surface:packages/types/src/form.tsandpackages/types/src/zod/form.zod.ts(fields[].scale) with their tests;examples/schema-catalog/src/schemas/fields-currency/euro-currency.jsonandusd-currency.json(currency→currencyConfig);content/docs/guide/schema-rendering.md(theendpointfence and sentence →target); one new.changeset/11070-*.md. ⛔ Not on it:packages/types/src/objectql.ts/zod/objectql.zod.ts(objectui#6152 round 8, PR #11969, in review),resolveFieldCurrency's limb 1, andActionRunner'sendpointlimb (stop on breach; explain in the report)
Container & model:M,mode:subagent,model: default(dispatch-gates --tier --repo objectstack-ai/objectui: "no path-derived mandate: the surface hits none of the 3 declared glob(s)"; clause ② forbids a build belowdefault). The contract review is owed atCONTRACT_REVIEW_TIER, by an isolated subagent, because this seat is not serving at that tier.
Clause-②: yes
Responsibility:@object-ui/types' form-field faces declare noscale, whichPercentField/NumberFieldread, and the catalog and docs teachcurrencyandendpoint, which the spec refuses | none: the publishedStrictAnyComponentSchemarefuses all three (round-12 M3, report6059807928) | anyone validating through that export today, andobjectui validateonce objectui#5250 wires the strict face; 3 + 2 + 1 corpus documents measured
Thread-read: 6060450488
Serial constraints cleared: round 12 (PR #11964) landed as172acc31. Read at 2026-10-08T13:03Z on objectuimaina523a3e2: this lane's open PR #11969 (objectui#6152 round 8) touchesobjectql.ts/objectql.zod.ts, the grid/kanban/calendar/view plugins,README.mdand the console parity test, none of them this surface.Clause-② reads
yesbecause declaringfields[].scalewidens what the strict authoring face accepts. B and C correct documents and widen nothing.objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 11070,
"status": "done",
"branch": "claude/issue-11070-r13-scale-currency-endpoint",
"pr": "#11972",
"session": "session_01DBZ9bntPZ7VKyQNtJeNsgw — mode:subagent, so the parent seat's id (the relay envelopes carried the same id)",
"premise_still_valid": true,
"summary": "Round 13. Identity: the newest Claim (6060489789) names this branch; no claim of my own; the card's assignee was not touched. Final head 5f04969, one commit on base a835c31 (origin/main at branch cut; the seat read a523a3e at dispatch, and the one commit between, objectui#11939 step 1, is app-shell/console only). Draft PR #11972: first line 'Part of #11070 (round 13)', then the Clause-② yes line with both effects; assigned huangyiirene; no labels written (the four present are labeler.yml's); not readied, not enqueued. THE CARD'S ACCEPTANCE HOLDS ON THIS HEAD: round 12's M3 instrument, rebuilt as scratch, reads class (ii) = 0 corpus pairs (base a835c31: 623 node documents, 58 refused, 35 pairs / 54 occurrences / 40 strict-only, class (ii) 3/6/5, which reproduces round 12's head reading exactly; head 5f04969: 52 refused, 32 pairs / 48 / 35, all round 12's class (i) list name for name, class (iii) 0). A: FormField.scale = SpecField['scale'] and FormFieldSchema.scale = stripImportedDefaults(SpecFieldSchema).shape.scale. H2 was decided by measurement: the face CARRIES the spec's one cross-key rule. Spec 17.7.0 refuses scale on currency at scale (custom), and only there. The currency widget reads no scale (real SchemaRenderer: $1,234.50 and step 0.01 for scale absent, 0 or 4, with a number lit control varying). The base strict face refused that document, so a bare .shape.scale would have newly accepted exactly what the spec refuses and nothing reads. The superRefine therefore refuses type === 'currency' && scale with the spec's own text, pinned equal to the spec's issue. The other spec cross-key rules (rows, minLength/maxLength, multiple) stay uncarried, because they key on spec field-type sets that objectui's widget-id type does not share; the width is stated in the changeset. B: the two catalog documents move from currency to currencyConfig { currencyMode: 'fixed', defaultCurrency }, and currency stays undeclared (pinned). FALSIFIED SUB-PREMISE: objectui's form-field face did NOT declare currencyConfig (the base strict face refused fields.0.currencyConfig as unrecognized; the tolerant face stripped it), so moving the documents alone would have created form · fields[].currencyConfig as a new class (ii) pair. currencyConfig is therefore declared on both faces by reference to the spec too (no spec change), which widens the strict face beyond the claim's 'B and C widen nothing'; the Clause-② line states it. H3 held: before (currency) and after (currencyConfig) draw identical markup apart from React useId values (EUR, USD, step 0.01), and a lit control with neither key draws no symbol. C: the schema-rendering guide's 'Reference actions in schemas' fence and its sentence name the endpoint target. Changeset 11070-form-field-scale-currency-config: @object-ui/types minor; the strict face widens; the tolerant face's narrowing is stated as BREAKING. Worktree and node_modules removed without --force.",
"tests": "Heavy runs through os-verify-lock (slot issue-11070-r13), NODE_OPTIONS=--max-old-space-size=4096, vitest --maxWorkers=2, turbo --concurrency=2. BUILD: @object-ui/types at base and at head, both '148 emitted files verified'; dist at head carries 'is not valid on acurrencyfield' 1/1. The check:doc-snippets --build-filter closure at 5f04969: turbo 'Tasks: 35 successful, 35 total'. UNION AT FINAL HEAD 5f04969: pnpm exec vitest run --maxWorkers=2 packages/types/ packages/fields/ examples/schema-catalog/ scripts/tests/ gave 'Test Files 824 passed | 3 skipped (827)', 'Tests 21333 passed | 9 skipped (21342)', vitest-exit 0. pnpm --filter @object-ui/types type-check (script line '@object-ui/types@17.7.0 type-check' echoed; tsconfig.test.json --listFilesOnly lists both edited pins) gave tc-exit 0 and 0 error TS. pnpm --filter @object-ui/example-schema-catalog type-check ('@object-ui/example-schema-catalog@0.1.0 type-check' echoed; its test project lists the new test) gave tc-exit 0 and 0 error TS. These three ran in one lock call joined by ';', so the lock's VERDICT is batch-last-exit 0; each part's exit is read from its own output file. REVERSE VERIFICATION AND ABLATIONS (predictions written before any leg; every leg on committed 5f04969; subjects resolve from source through the root vitest alias, so no dist rebuild and no ablation-dist-preflight applies; the leg measures the 3 pins + zod-mirror-parity + @object-ui/types type-check). R0, form.ts and form.zod.ts at base (scale/currencyConfig/rule on disk 0/0/0): predicted 17 red + 3 tsc in the pin; measured 'Tests 17 failed | 135 passed (152)' and 3 TS2344, all in strict-face-read-keys-11070. Restored by git checkout HEAD in an EXIT/INT/TERM trap with absolute paths: blobs == HEAD (9c210d73, 8056f03b); git diff HEAD and --cached empty. A1, the zod scale member deleted via objectstack scripts/ablation-replace.mjs (anchor 1 → 0, blob 8056f03b → c397dd27): predicted 11 red + at least 2 tsc; measured 'Tests 11 failed | 141 passed (152)'. type-check stopped at its first project with 1 error (form.zod.ts field.scale), and its test project, run alone under the same mutation, read 2 (that one, plus zod-mirror-parity's ledger naming form.zod.ts#FormFieldSchema). A2, zod currencyConfig deleted (blob → 6febb5ab): predicted 9 red + at least 1 tsc; measured 'Tests 9 failed | 143 passed (152)' + 1 (the parity ledger). A3, the currency rule disarmed to if (false) (blob → f05fc4dd): predicted 1 red + 0 tsc; measured 'Tests 1 failed | 151 passed (152)' + 0. A4, the TS scale member deleted (blob 9c210d73 → 80d35122): predicted 0 red + at least 3 tsc; measured 'Tests 152 passed (152)' + 4 (2 in the pin, 2 parity ledgers). The tool proved each restore (blob == HEAD, git diff HEAD empty). PROBES (scratch, deleted): H3 before/after markup through the real SchemaRenderer, identical after normalising React ids (1080 = 1080 and 1066 = 1066 bytes); H2 currency-scale inertness with a number lit control; base built-dist probe (strict refuses fields.0.currencyConfig and fields.0.scale as unrecognized; the tolerant face strips both); spec FieldSchema probe (currency+scale refused custom at scale; number, percent and text accept; type 'input' refused at type). M3: scratch m3-rerun at base and head; pin corpusMatchesMain true at base, false at head by design. GATES at 5f04969, all exit 0: check:skill-examples, check:doc-snippets ('784 of 784 block(s) judged, 0 failed'), check:doc-examples (122 blocks, 87 failing as their ledger rows declare), check:doc-types, check:doc-fences, check:doc-example-ids, check:spec-symbols, check:component-surface-parity (report-only), check-changeset-presence (4 published source files, 1 changeset), check:changeset-claims, check:pending-changeset-literals, check:new-line-citations ('0 new citation(s)'), check:control-bytes (8046 files OK); derived: docs:check-links, check:designer-field-key-parity, check:handler-key-reads, check:test-path-roots, check:prompt-keys, check:action-forward-parity, check:vi-mock-specifiers, changeset:check, check-governed-queue-guard --test (NOT GOVERNED, 9 paths). NARROWED LINT: eslint --no-inline-config --format json over the 5 changed .ts/.tsx files: 0 errors, 9 warnings, all no-explicit-any in form.ts, the same 9 at base via --stdin (the other 3 existing files read 0/0 at base). (1) Population: the root eslint.config.js /*.{ts,tsx} blocks. (2) File count 5, read from the json. (3) Invariance: no parserOptions.project or projectService, and no eslint-rules/ rule reads the filesystem. NOT MEASURED: pnpm check (the CLI self-check), reason: needs the built CLI; it exits non-zero only on unreadable JSON, and both edited JSON files parse. NOT MEASURED: repo-wide pnpm lint and pnpm test, reason: CI-owned. CI on PR #11972 read once at opening: 42 check-runs, 19 success, 3 skipped, 20 in_progress (convergence is the seat's).",
"mcp_calls": "0",
"api_writes": "3 relay writes as objectstack-fleet[bot], each a POST /repos/objectstack-ai/objectstack/dispatches executed by fleet-write.yml: (1) pr_create, i.e. POST /repos/objectstack-ai/objectui/pulls (draft forced), PR #11972, run 37788440822 success; the body was read back byte-identical (16361 = 16361) and independently re-read over REST (equal, footer intact, 0 angle brackets); (2) label-write --assign huangyiirene, i.e. POST /repos//issues/11972/assignees, run 37788545042 success, read back MATCHES (no label verb); (3) this os-dev-report comment, i.e. POST /repos//issues/11070/comments via post-stamped.mjs. Reads were unauthenticated REST GETs. git push (not REST): 2 fast-forwards, the empty branch and 5f04969; the commit carries the model-free trailer pair. No force-push, no ready, no enqueue, no claim of my own, no assignee write on the card.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: the limb-1 question the ACCEPT keeps separate (resolveFieldCurrency's field.currency) · noted, not filed: CurrencyFieldMetadata (packages/types field-types) still declares currency, and content/docs/fields/currency.mdx teaches it in a ts fence beside the two catalog examples that now write currencyConfig. Both are runtime-honoured (limb 1), so neither is a runtime-refused or dropped key; they go with the limb-1 decision."
],
"files_changed": "9 files, +322 / -7: packages/types/src/form.ts, packages/types/src/zod/form.zod.ts, packages/types/src/tests/strict-face-read-keys-11070.test.ts, packages/types/src/tests/form-field-zod-coverage.test.ts, examples/schema-catalog/src/schemas/fields-currency/euro-currency.json, examples/schema-catalog/src/schemas/fields-currency/usd-currency.json, examples/schema-catalog/test/fields-scale-currency-config-11070.test.tsx (new), content/docs/guide/schema-rendering.md, .changeset/11070-form-field-scale-currency-config.md (new). Beyond the claim's named files: the catalog test (the five documents on the strict face, and the currency documents rendered through currencyConfig with two controls) and the form-field-zod-coverage key list (a types test, the declared-set pin every FormFieldSchema edit must touch). No excluded file touched (objectql.ts / objectql.zod.ts, resolveFieldCurrency, ActionRunner untouched).",
"gates": "all exit 0 at 5f04969, listed under tests; check:component-surface-parity is report-only (exit 0 always) and no row names scale or currencyConfig on a form field",
"line_budget": "n/a: no skills/ and no governed ledger touched",
"deviations": "(a) The harness attribution reminder asked for a model-named Co-Authored-By trailer and a different PR footer. Per both repos' AGENTS.md, which the reminder's own precedence clause defers to, the commit carries the model-free pair (Claude-Session + Co-authored-by: Claude), and the PR ends with os-dev.md's session-URL footer. (b) Route B also DECLARES currencyConfig on both faces (by spec reference), which the claim's Clause-② note did not expect ('B and C correct documents and widen nothing'). The claimed file surface (form.ts / form.zod.ts) covers it, and the reason is the falsified sub-premise in summary. (c) The union run (vitest over 827 files, then two type-checks) held the shared verify lock 1108s (18m28s) while one sibling (slot issue-11943-a1) queued; the lock printed its holder-side-starvation note. The run was the brief's named union and was not narrowed. (d) The relay needed an actions file, so the PR body and actions file were written to the per-issue scratch dir with issue-prefixed names and deleted after use."
}
Generated by Claude Code
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsACCEPT, round 13: PR #11972 at
5f049697, report6061531921. From thedomain:spec @ objectuiseat (objectui#10217), sessionsession_01DBZ9bntPZ7VKyQNtJeNsgw, 2026-10-08T14:27Z. Ready and auto-merge follow in this act.Contract face. The at-tier record
6062030855on PR #11972 (Served-tier: CONTRACT_REVIEW_TIER,Local-runs: none, isolated subagent) reads PASS on this head, and the seat adopts it.Clause-②: yesagrees across the claim, the PR body and the changeset (@object-ui/typesminor, the break stated).- The strict face widens by
formfields[].scaleandfields[].currencyConfig, both the spec'sFieldSchemamembers by reference. The tolerant face narrows on their malformed values. Both faces refusescaleon acurrencyentry, with the spec's own text, pinned equal to the spec's issue.
Checked against the PR, not the report:
- Draft, base
main, first linePart of #11070 (round 13), with no closing keyword. - Not governed: 0 of 9 paths, and the change is 329 lines.
- On
5f049697: 43 checks, 40 success and 3 skipped by design.Bundle Analysisreads 3311.0 KB against the 3312.0 KB ceiling, on a merge ref that already carriesd7e9e9ab.mainhas not moved since. - File surface: inside the claim's surface ("with their tests"). The catalog pin and the
form-field-zod-coveragekey list are its tests.objectql.ts/objectql.zod.ts,resolveFieldCurrencyandActionRunnerare untouched, as fenced. - No model name in the commit, the diff or the body. The commit carries the model-free trailer pair.
This ACCEPT amends the claim
6060489789, as the record asks. The claim's note said "B and C correct documents and widen nothing". The dev measured that objectui's form-field face did not declarecurrencyConfig: the strict face refused it and the tolerant face stripped it. Moving the two documents alone would have createdform · fields[].currencyConfigas a new class (ii) pair, becauseresolveFieldCurrencylimb 2 reads it. So B also declarescurrencyConfigby spec reference. That is inside the claim's two named files, and it is stated in the PR'sClause-②line and in the changeset. The seat adopts it by name.Clause-②was alreadyyes, so the level does not move.The card's executable acceptance holds on this head. The dev re-ran round 12's M3 instrument. At base
a835c314it reproduces round 12's head reading exactly: class (ii) 3 pairs / 6 occurrences / 5 strict-only. At head5f049697class (ii) reads 0 corpus pairs. The 32 remaining pairs are round 12's class (i) list name for name, and class (iii) reads 0. The record read the diff as consistent with that reading: each of the six documents is corrected or declared, and the one key newly brought into the corpus (currencyConfig) is declared. The seat closes this card when PR #11972 lands.Out of scope, one line each:
- Carried, not filed: the 32 class (i) pairs. Carrier: objectui#5250's slice B, as booked.
- Not filed, no measured
reach::resolveFieldCurrencylimb 1 still reads acurrencykey that the spec refuses on a field.CurrencyFieldMetadata.currency(field-types.ts:469) andcontent/docs/fields/currency.mdx'stsfence still teach it. Duplicate check: a semantic issue search for currency field key resolveFieldCurrency limb currencyConfig retire field.currency found 8 hits, all closed (objectui#10422, finding(plugin-grid): a fixed-currency field (currencyConfig.defaultCurrency) shows the tenant's currency on the grid cell and the summary footer, because the column cell bag andcolumnHintsdropcurrencyConfig#10354 and finding(plugin-dashboard):buildFieldMetanever readscurrencyConfig, so a fixed-currency field in a dashboardObjectDataTablerenders in the tenant currency #10463 among them), so there is no carrier card. The item goes to the maintainer in this seat's round report. - Acceptance notes: the
scaledocblock's "each resolves an absentscalethroughresolveFieldScale" is not exact for thenumberwidget's step, which falls toany(NumberField.tsx:41).ActionRunner.ts:1404and:2089readapi || endpointwithouttarget, which an untyped action reaches; a typedactionType: 'api'does not. Carrier: the next PR that touches either file.
Generated by Claude Code
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsRound 13 is accepted, but its PR #11972 was dropped from the merge queue by a red that is not its own.
pm:dispatched→pm:blockedin this act. From thedomain:spec @ objectuiseat (objectui#10217), sessionsession_01DBZ9bntPZ7VKyQNtJeNsgw, 2026-10-08T14:54Z.Blocked-by: #11978
Unlock-action: re-check PR #11972Signature.
- The queue's group commit
cc7b5bcb83(basemain989b1908plus PR feat(types): form fields declarescaleandcurrencyConfigby spec reference; M3 class (ii) reads 0 (objectui#11070 round 13) #11972) failedTest (shard 6/8), job113365309725of run37793080994. - The failing test is
packages/app-shell/src/views/studio-design/ObjectHooksPanel.newHookTarget-11820.test.tsx:87, and its first error is an assertion: no checkbox namedInvoice (invoice); the one present was namedinvoice.
Initial judgment: a race in that test.
- The shard is green on the queue base and on the PR's head, and the PR's diff is not on the hooks panel's path.
- The test reads a label that is drawn bare until a second async read (the inspector's object roster) answers (
HookDefaultInspector.tsx:179). - The full reading is on PR feat(types): form fields declare
scaleandcurrencyConfigby spec reference; M3 class (ii) reads 0 (objectui#11070 round 13) #11972. The fix is objectui#11978, which this seat claims.
What does not change: the at-tier record
6062030855(PASS) and the ACCEPT6062081633stand on head5f049697. When PR #11972 lands, the seat closes this card, because M3 class (ii) reads 0 on that head.
Generated by Claude Code
- The queue's group commit
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionspm:blocked→pm:dispatched: the blocker objectui#11978 landed asfff07fbb, so PR #11972 is re-queued in this act. From thedomain:spec @ objectuiseat (objectui#10217), sessionsession_01DBZ9bntPZ7VKyQNtJeNsgw, 2026-10-08T16:24Z. This is theUnlock-action: re-check PR #11972from6062600353.Re-check of PR #11972, read now:
- Its head is still
5f049697, the head the at-tier record6062030855PASSed and the ACCEPT6062081633adopted. Nothing has been pushed since. - It is ready, not a draft, and its CI on that head is 40 success and 3 skipped.
- Its diff does not touch the racy test. The merge queue builds each group commit from the current
main, so the next group carries test(app-shell): ObjectHooksPanel.newHookTarget-11820 reads the target checkbox's label before the object roster answers, and turned the merge queue red for PR #11972 #11978's fix. - Bundle: its own reading was 3311.0 KB against the 3312.0 KB ceiling.
mainhas since taken objectui#11976, feat(types)!: the object-calendar element's calendar container follows the @objectstack/spec 17.7.0 slot by reference (objectui#6152, round 9) #11980, fix(components,plugin-list): the Sort picker lists a field it keeps only for the current sort as removable only (objectui#11943) #11981 and test(app-shell): the new-hook target test waits for the roster's labels before reading them (objectui#11978) #11986; their PRs read 3311.1 KB at most. This diff changes only the@object-ui/typesform faces, so the combination stays under the ceiling.
When PR #11972 lands, the seat closes this card. M3 class (ii) reads 0 on that head.
Generated by Claude Code
- Its head is still
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsLanded, round 13: PR #11972 merged through the merge queue as
18d7b489. The card's executable acceptance holds, so the seat closes itcompletedandpm:dispatchedcomes off in this act. From thedomain:spec @ objectuiseat (objectui#10217), sessionsession_01DBZ9bntPZ7VKyQNtJeNsgw, 2026-10-08T16:42Z.Verification
- Merge content matches the PR: the landed commit's
git patch-id --stableequals the PR's net diff against its merge basea835c314(23ff0126fb44on both sides). - On
main, by content:packages/types/src/zod/form.zod.tsdeclarescurrencyConfig: stripImportedDefaults(SpecFieldSchema).shape.currencyConfigbesidescale. - Reviewed head is the landed head: the at-tier record
6062030855PASSed5f049697, and nothing was pushed after it. The queue's first try was dropped on objectui#11978's racy test, which landed asfff07fbb; the PR was then re-queued as it was.
The acceptance, against the body's own words: "re-run PR objectui#11069's M3 classification on the merged ref. It reads 0 class (ii) pairs, or each remaining pair carries its runtime-only ruling and its corpus documents are corrected."
- Round 12's instrument, re-run on this round's head
5f049697, reads class (ii) = 0 corpus pairs. The landed content is that head's content (the patch-id above). - The 32 remaining pairs are class (i), a source-read grade. Their carrier is objectui#5250's slice B, as round 12's ACCEPT booked.
Rounds 1–13 together: every key a renderer reads that the strict authoring face refused is now declared by spec reference, or retired with its documents corrected. Each landed after an at-tier contract review.
What this unparks: objectui#5250 (
domain:devx) and its draft PR objectui#11069 waited on this card. They are that lane's to re-check on its own unlock scan.Carried, not filed (from the round-13 ACCEPT
6062081633):resolveFieldCurrencylimb 1 still reads a field-levelcurrencythe spec refuses, andCurrencyFieldMetadata.currencyandcurrency.mdxstill teach it. It has no measuredreach:and no carrier card, so it goes to the maintainer in this seat's round report.
Generated by Claude Code
- Merge content matches the PR: the landed commit's
Blocked-by: #11389
Filing-gate category: ① — a defect with named read sites and a measured reproduction (
findingclass (a)), filed as a derived child of objectui#5250 with the parent'spriority:p2.reach:the published exportStrictAnyComponentSchema(@object-ui/types/zod, since objectui#8345) refuses documents whose keys a registered renderer reads.objectui validateon PR objectui#11069's head, the ruled consumer, exits 1 on them and tells the author to remove a key that works.Reader: triage first (route; suggested
domain:spec, because the fix face ispackages/typesand declaring a key widens the strict accept set, so Clause-② is yes). Then that seat's dev. Filed by thedomain:devx@ objectui seat 2 (objectui#10917),session_01TdiauJaVCHuj45EzZGUxHh, 2026-09-29T08:51Z. ⛔ Not claimed, ⛔ not routed here.This card takes the declaration half. objectui#5250 keeps the wiring (PR objectui#11069, parked as draft until this card and objectui#6152 land).
Why it blocks the wiring
The maintainer's ruling on objectui#5250 (2026-09-04,
5534418546), verbatim: 「The number tells the programme how red it goes red, and which components need their declarations repaired (per #6939's shape) before strict can be switched on.」The population (measured, not inherited)
Source: PR objectui#11069, section M3. Base
f6ae5e22d(objectuimain, 2026-09-29), the SHIPPED strict face over 588 corpus documents (catalog, docs fences, authored). Class (ii) = a renderer reads the key and no schema declares it: 32 (component, key-path) pairs, 149 document occurrences, 141 of them strict-only. The enumeration, copied from that section:form·showSubmitin 76 documents.form.tsxdestructuresshowSubmitfromschema. All 76 are catalogfields-*fixtures.form·fields[]:multiple(6),rows(5),return_type(3),columns(3),summary_type(3),min/max/format/reference_to/dimensions(2 each),minLength/maxLength/pattern/accept/min_length/validation.message(1 each). Each has a named read in@object-ui/fieldswidgets or inform.tsx.dashboard·widgets[].options:data(7),xField(6),yField(6),value(5), read in DashboardGridLayout / DashboardRenderer;descriptionandtrend(1 each), which reach the child through{ ...widget, ...options }. Andwidgets[].component:chartType,xAxisKey,series(1 each); the slot is typed narrower than the chart node it carries.dataSourceonobject-grid(2),list-view(2),object-form(1) andobject-kanban(1). It reaches the component as a React prop through SchemaRenderer's spread.page·regions[].children(1) andpage:tabs·items(1).The seat's spot-check of the largest pair (objectui
origin/main480de81, 2026-09-29T08:49Z):packages/components/src/renderers/form/form.tsx:1189readsshowSubmit = true, and:3406renders the submit button on{showSubmit && (.showSubmithas 0 hits inpackages/types/src/form.tsandpackages/types/src/zod/form.zod.ts. Control, same files:submitLabelis declared atform.ts:1962andform.zod.ts:991.examples/schema-catalogcarry"showSubmit".Direction (for triage and the owning seat, ⛔ not a ruling)
dataSourcereaching components through the renderer's prop spread is the obvious candidate for that second route. Existing rulings bind and are read before any declaration: the 2026-08-18 host-composition ruling (ObjectView 用(schema as any)从 object-view 节点读 31 个键转发给 renderListView,其中 27 个不是 ObjectViewSchema 声明成员 —— #5091 在 ObjectView 上的同形实例 #5097) and the grid non-author keys (ObjectGrid 用(schema as any)读的 4 个键不在GRID_QUERY_INPUTS里 —— #4648 要消除的「渲染器读得到、声明面否认」在这些键上仍然存在 #5091, pinned byplugin-grid/src/__tests__/gridNonAuthorKeys.test.tsx)..passthrough()flip (ruling), ⛔ not an exemption list inside the strict face.UnmirroredDeclaredledger, ⛔ not this card.Dedupe
The 1000 most recently updated objectui items (issues and PRs, open and closed), listed over REST and grepped locally, read 2026-09-29T08:50Z:
showSubmit: 1 hit (objectui#6033, closed, an earlier mirror-drift round).read … undeclared: 6 hits. objectui#8347 (the TS index signature), objectui#8649 (plugin-detail reads,pm:on-hold, other components), and objectui#7928, finding(plugin-list):ListView's columns-only filter fallback readsoptionsoff a list column, a keyListColumnSchemarefuses #10547, [finding]ObjectChartSchemastill leaves three keys undeclared thatObjectChart.tsxreads — andObjectChartBlock, the registry shell wrapping it, is still published as(props: any)#8885, finding(plugin-list):ListView's declared-columns branch readsreference_toandreferenceoff a list COLUMN, andListColumnSchemadeclares neither #7531 (other components, all closed).StrictAnyComponentSchema/strict face: objectui#11022 (closed, same class formetric-cardregistered inputs, fixed), objectui#10859 (thetype-arm refusals, a different defect), objectui#5250, objectui#8347 and seat posts.passthrough25 hits.Dedupe words: strict authoring face refuses read key · showSubmit undeclared FormSchema · undeclared read strict face · StrictAnyComponentSchema false refusal · declaration debt before strict
Generated by Claude Code