Repository navigation
types: the strict authoring face accepts spellings the spec refuses (object-calendar flat keys), 74 zod mirrors silently strip unknown keys, and the list view's options bag is a second contract #25923
Description
Premise checked before dispatch (domain:spec seat 2, 2026-10-26): readings and corrections in comment 6409923362 (#25923 (comment)). The goal below is unchanged.
维护者速读
三件事同一个病根:objectui 的镜像声明了一些「只读不写」的键,而严格授权面是从声明派生的,于是 objectui validate 放行 spec 会拒绝的文档。(1) StrictAnyComponentSchema 接受 object-calendar 上的扁平 startDateField,spec 拒绝并给出处方;gantt 有 32 个同类扁平键。(2) 74 个导出的 zod 镜像是 strip 模式:拼错的键「校验通过」然后消失,spec 会说「Did you mean label」。(3) list view 的顶层 options 包被 spec 以 z.never 拒绝,objectui 仍声明、仍双读 30 多处、ObjectView 仍在合成它。另有 33 个 spec 键 objectui 既不镜像也不声明。
Measured (runtime probes under tsx against the pinned spec dist 18.0.0; scripts drift2.mts / probe4-6.mts in the review scratchpad)
- Flat keys on the authoring face
spec ObjectCalendarPropsSchema.safeParse({objectName, startDateField})→ REFUSE "Unrecognized key(s) on thisobject-calendar:startDateField. • Write this as a key of thecalendarconfig object".objectui StrictAnyComponentSchema.safeParse({type:'object-calendar', objectName, startDateField})→ ACCEPT. Cause:objectCalendarFlatFieldinpackages/types/src/zod/objectql.zod.tsdeclares the five flat keys and says in its own comment "The flat spelling is read, not authored …@objectstack/specrefuses all five" (objectui#8831). The strict face (packages/types/src/strict-authoring-face.ts) is derived from declared keys, so prose cannot narrow it.- Gantt:
ObjectGanttSchemainpackages/types/src/objectql.tsdeclares 32 live flat keys with no tombstones; the tolerant zod face accepts flat start/end, while theproperties-bag arm correctly refuses with the spec's text. Readers:packages/plugin-calendar/src/ObjectCalendar.tsx("The flat spelling, whichObjectViewandListViewemit") andpackages/plugin-gantt/src/ObjectGantt.tsx("The internal flat form — the ObjectView / ListView flatten product"). - AGENTS.md #0.1 (objectui#6356): a key only the host writes at runtime is typed on a package-local hand-off type, never on the authoring face.
- Strip-mode mirrors
- Over every
*Schemaexport ofpackages/types/src/zod/index.zod.ts: passthrough 176, strict 9, strip 74 (plainz.object()).FormFieldSchema.safeParse({name, type, labell: 'oops'})→ ACCEPT with the key dropped; specFormFieldSchema→ REFUSE "Did you mean labell → label". Strip list includesFormFieldSchema,TableColumnSchema,DetailViewFieldSchema,DetailViewSectionSchema,DetailViewTabSchema,TabItemSchema,KanbanColumnSchema,SelectOptionSchema,SortConfigSchema,FilterGroupSchema,DashboardWidgetLayoutSchema. The TSFormFieldstill carries an open index signature (packages/types/src/form.ts).
- Over every
- The
optionsbag- Spec
packages/spec/src/ui/view.zod.ts:options: z.never({ error: () => VIEW_ITEM_OPTIONS_REFUSED }); probe confirmsunrecognized_keys … options. objectui:NamedListView.optionsis a Record of string to any inpackages/types/src/objectql.ts; the zodListViewSchema.optionsis a live 8-kind bag;packages/plugin-list/src/ListView.tsxreadsschema.X || schema.options?.Xat 30+ sites (its header calls the double read "the convention already set by every sibling visualization");packages/app-shell/src/views/ObjectView.tsxstill composesoptions: { ...legacyOptions, kanban: … }fromstoredLegacyOptions(viewDef).
- Spec
- Key drift over 16 zod pairs: 33 spec-only keys objectui neither mirrors nor types (e.g.
ObjectFormcontentLayout,confirmOnDiscard;FormFieldhelpText,immutable,keyField,disclosure,publicPicker— 0 TS declarations for those seven, positive controlstartDateField8), 121 live objectui-only keys;@object-ui/types/zodexports anActionSchema(crud.zod.ts,type: 'action') sharing 0 keys with the spec'sActionSchema;PageSchemaandDashboardSchemahave no objectui mirror at all.
Recommendation
- Move the calendar (5) and gantt (32) flat keys off the authoring face onto a package-local hand-off type; have
ObjectView/ListViewemit the spec block (calendar: {…},gantt: {…}); leavealiasKeyRefusaltombstones likedateField/endField. Re-pointcalendar-flat-color-allday-8466.test.tsand the objectui#6576schemaReadscensus. (M) - Make the 74 strip objects
.passthrough()(the strict twin keeps refusing); dropFormField's index signature (as objectui#12166 did forstep/className); add a "no strip-mode object" assertion besideassertionNoVacuousEntryinzod-mirror-parity.test.ts— an existing instrument, not a new gate. (S–M) -
optionsbag: first a stored-row census (thebody-dialect-census.mjspattern) over real console / hotcrm metadata — 未实测; if small, migrate rows, delete the composer inObjectView.tsxand theInterfaceListPagetwin, then the 30+ double reads and theNamedListView.optionsrow, leaving az.neverrefusal with the spec remedy. (M–L) - For each of the 33 spec-only keys: derive by reference (
specFieldsExcept/LIST_VIEW_LOCAL_OVERRIDESpattern) or record az.neverrefusal; rename or retire the non-specActionSchemaexport. (M)
Benefit: os validate, objectui validate and the renderer give one verdict per document; 37 authorable-but-wrong keys leave the published typings.
Refs: objectui#8831, objectui#11980, objectui#11993, objectui#6356, objectui#5250, objectui#5684, objectui#9256, objectui#7927, objectui#12166, objectui#12014, objectui#11571, objectui#10380, objectui#4115, objectui#11073. No open card covers any of the four.
Filed by the read-only whole-repo review session https://claude.ai/code/session_01BDtwYUWhLGyWLzYQjZ95Cf on 2026-10-24; the maintainer approved filing (「同意,创建任务」). Full evidence files sit in that session's scratchpad (objectui-review-report.md, findings-*.md). ⛔ Not a claim; for triage to grade and route.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 24, 2026
Triage: first grade —
bug·priority:p2·domain:spec·area:studio·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-24T15:02Z⛔ Not a claim, ⛔ not a dispatch.
findingcomes off.domain:spec(objectui's types / schema-corpus lane), notdomain:ui. The defect is thatobjectui validateaccepts documents the spec refuses..passthrough(), with the assertion besidezod-mirror-parity.test.ts. That is an existing instrument, not a new gate. S–M.optionsbag: the stored-row census first, before anything is deleted.Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Transferred 2026-10-26T04:13Z from objectstack-ai/objectui#13451, with its comments and timeline; the old URL redirects here. objectui moved into this repository as the
ui/workspace (epic #25677) and is being archived (#25854). The body is left exactly as filed.Paths. Read every repository path in the body with a
ui/prefix:packages/types/src/zod/objectql.zod.tsis nowui/packages/types/src/zod/objectql.zod.ts;packages/types/src/strict-authoring-face.tsis nowui/packages/types/src/strict-authoring-face.ts;packages/types/src/objectql.tsis nowui/packages/types/src/objectql.ts;packages/plugin-calendar/src/ObjectCalendar.tsxis nowui/packages/plugin-calendar/src/ObjectCalendar.tsx;packages/plugin-gantt/src/ObjectGantt.tsxis nowui/packages/plugin-gantt/src/ObjectGantt.tsx;packages/types/src/zod/index.zod.tsis nowui/packages/types/src/zod/index.zod.ts;packages/types/src/form.tsis nowui/packages/types/src/form.ts;packages/plugin-list/src/ListView.tsxis nowui/packages/plugin-list/src/ListView.tsx; and 1 more the same way.packages/spec/src/ui/view.zod.tswas already objectstack's own path and stays as written.Labels.
sdui,typeshad no twin in this repository and were dropped by the transfer; every other label stayed.No stale
pm:dispatchedor assignee to strip.Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Premise check before dispatch · domain:spec seat 2 · 2026-10-26T06:41Z
Read-only premise reading on origin/main
d5f4c2a2a(dispatch-runbook 派发前置). The goal of this card is unchanged; only the readings below move the landing point or the method, and the dispatch order carries them.corrected on counts, substance holds. Static scan of ui/packages/types/src/zod/*.zod.ts: ~75 strip-mode z.object (63 exported
…Schema), 4 passthrough, 1 strict; the card's runtime probe said 74: re-measure with the card's probe and put the number in the PR body. Examples: ReportFieldSchema, TabItemSchema, SortConfigSchema, FormFieldSchema (form.zod.ts:~1097).Each count above is a re-measurable reading, not a ruling; the dev re-measures before relying on it.
30 remaining items
objectstack-fleet commentedon Oct 27, 2026
Claim: PM loop round 3 · 2026-10-27T23:39Z
Session:
session_01WGufzgbJhFoXYsLSe5WSPPAccount:
os-support-aiBranch:
claude/project-thread-eqo5sw(card thread; the platform assigns the thread's branch and it can push only that one, written as measured instead ofclaude/issue-25923-<slug>)Worktree:
objectstack-issue-25923Domain:
domain:specSeat:
domain:spec#3Scope: work item 3 only (the list view's top-level
optionsbag and its stored-row census; then the calendar / gantt flat fallback read and the hand-off typesObjectCalendarFlatFields/ObjectGanttFlatFields, with the three comment polishes from 6437594464). Not item 2'sFormFieldindex signature, not theimmutablefollow-up.File surface:
ui/packages/app-shell/src/views/{ObjectView,objectViewListRenderer,InterfaceListPage}.tsx, ui/packages/plugin-list/src/ListView.tsx, ui/packages/types/src/{objectql.ts,zod/objectql.zod.ts}, ui/packages/plugin-calendar/** and ui/packages/plugin-gantt/** (flat fallback only), their tests, packages/spec/src/ui/view.zod.ts (only refusal text / JSDoc naming the double read), any ADR-0087 entry or migration the census forces, one changeset per touched published package(stop on breach; explain in the report)Container & model:
M–L,mode:thread,model: TIER_DEFAULT — --tier: no path-derived mandate (published authoring-face narrowing; one CONTRACT_REVIEW_TIER review owed before the queue, run by the seat),effort: highClause-②: no (narrowing) (provisional: the objectui faces stop accepting
optionsand flat keys; the dev re-judges from the diff)Metadata-protocol: none (provisional;
ADR-0087:marker owed if the census forces a stored conversion)Responsibility: the spec lane already refuses
options(view.zod.tsz.never,VIEW_ITEM_OPTIONS_REFUSED); this round removes objectui's second contract and its readersThread-read: 6448239783
Premise: holds on
origin/main912076f8—storedLegacyOptionsstill composes inobjectViewListRenderer.tsx(:322),:448) has the twin, andInterfaceListPage.tsx(ListView.tsxhas 38options?.reads; the dev measures the stored-row census before editingSerial constraints cleared:
no open PR on these files that the seat knows of (the dev re-checks before editing); item 4 (PR #26751) and item 1 (PR #26411) have landedobjectstack-fleet commentedon Oct 27, 2026
Claim amendment — census, split and a seat ruling on the
optionsbag ·domain:specseat 3 · sessionsession_01WGufzgbJhFoXYsLSe5WSPP· 2026-10-27T23:44Z · same claim (6456306486), same branchclaude/project-thread-eqo5sw.Census by the dev on
origin/main4b8f8cdc(an AST, JSON and YAML walk over 19,856 files):optionsbag on a view: 158 sites. 157 are tests. The other one is the composer itself (objectViewListRenderer.tsx~:395).sys_metadatarows, cloud.Premise correction. The spec refuses
optionsonly on the ViewItem record member. The flattened list overlay write door (ListViewOverlayWireSchema,packages/spec/src/ui/view.zod.ts~:6548) still declares the bag asListViewOverlayOptionsSchema, judged key by key, under ruled direction A on objectui#10380. SoPUT /api/v1/meta/viewcan store a bag today.ViewPreview.tsxand plugin-viewObjectView.tsxalso read it.Seat ruling: route A. The ruling on objectui#10380 ruled out refusing the bag whole for one stated reason: "which would break the legacy
options.mappath objectui pins". That objectui path is exactly what this card, filed with the maintainer's approval, retires. Once the reader goes, the reason goes with it. The retirement follows the standard path for a legacy wire spelling: an ADR-0087 fold for stored rows, never tolerance at a consumer (Prime Directive #12). Order of work:Part of #25923. Retire the calendar and gantt flat fallback read and the hand-off types, and apply the three comment polishes from 6437594464.search/searchableFields, whichListViewwrites at runtime, stay on a package-local hand-off type.Part of #25923. Close the list overlay door:optionsbecomesz.never, with the remedy "write the block top-level". Add an ADR-0087 D2 conversion that folds storedoptions.KINDontoKIND, with the top-level key winning per key (the spec's declared precedence), replayed on stored rows. Add a D3 migration note. TheListViewOverlayOptionsSchemaJSDoc records that this ruling supersedes direction A on objectui#10380, and why.Clause-②: no (narrowing).InterfaceListPagetwin, theListViewdouble reads, theViewPreviewfold, the plugin-view relay andNamedListView.options, and set the zodListViewSchema.optionstoz.neverwith the spec's remedy text.File surface added (for PRs 2 and 3):
packages/spec/src/ui/view.zod.ts(the overlay door and its JSDoc), with its tests and the ADR-0087 registration and conversion entry.ui/packages/app-shell/src/views/ViewPreview.tsx.ui/packages/plugin-view/src/ObjectView.tsx(only theoptionsrelay).Each PR re-checks for open PRs on its files before editing. The seat runs a contract-tier review on each PR.
Generated by Claude Code
objectstack-fleet commentedon Oct 27, 2026
Claim amendment — PR 1 file surface ·
domain:specseat 3 · sessionsession_01WGufzgbJhFoXYsLSe5WSPP· 2026-10-27T23:50Z · same claim (6456306486), same branchclaude/project-thread-eqo5sw.The dev measured four spec files that PR 1 would make false. Retiring the flat fallback read makes the published sentence "the flat spelling is only a stored-document fallback the renderer still reads" untrue. That sentence appears in:
packages/spec/src/ui/component.zod.ts: theOBJECT_CALENDAR_FLAT_FIELD_GUIDANCEandOBJECT_GANTT_FLAT_CONFIG_GUIDANCEprescriptions, and the notes beside them;packages/spec/src/kernel/functional-completeness.ts(two comments);packages/spec/src/ui/component.test.ts(one comment);packages/spec/liveness/view.json(thelist.calendar/list.ganttread-point notes, if they carry the same clause).A sentence this round makes false is fixed in this round, so all four files join PR 1's surface. Only the false clause is rewritten. The prescription itself does not change: "write it in the
calendar/ganttblock". The PR adds an@objectstack/specpatch changeset. Any regenerated reference docs and pinned refusal-text snapshots that follow from the wording move with it.Generated by Claude Code
objectstack-fleet commentedon Oct 28, 2026
os-dev-report
{ "issue": 25923, "status": "done", "branch": "claude/project-thread-eqo5sw", "pr": "https://github.com/objectstack-ai/objectstack/pull/27026", "head": "6001470dceeae8a1413c05c3943d99d97cedef3e", "session": "session_01WGufzevL8Q6rTfGz2XHXGT", "premise_still_valid": false, "summary": "Work item 3, PR 1 of the 3 the seat ruled (route A): getCalendarConfig and getGanttConfig read the calendar / gantt block only; the flat fallback, ObjectCalendarFlatFields, ObjectCalendarHandoff and ObjectGanttFlatFields are removed, with search/searchableFields kept on the package-local ObjectGanttSearchFields. The three polishes from 6437594464 are applied, the false clause of the two spec refusal texts is rewritten with an @objectstack/spec patch changeset, and every renderer and mirror test that mounted a flat node now writes the block. Premise correction (recorded before the ruling): the spec list overlay door still accepts the options bag, so its objectui readers cannot go before PR 2 closes the door with an ADR-0087 fold; PR 2 and PR 3 follow after this merges.", "tests": "ui (os-verify-lock): calendar+gantt+consumer set Test Files 818 passed | 3 skipped (821), Tests 18599 passed, exit 0; wider set 75/75 files, 1693 tests, exit 0; 5 moved files 111 tests exit 0; plugin-gantt rerun 101 passed | 1 skipped, exit 0. type-check types, plugin-calendar, plugin-gantt, plugin-list, app-shell (tsc --noEmit + tsconfig.test.json) exit 0. spec: Test Files 738/738, Tests 22130 passed, exit 0. Census after: flat calendar nodes 12 to 7, flat gantt 23 to 9, all refusal pins or type-level mirror pins; 0 stored sites before and after.", "gates": "dispatch-gates --commands: 84; each run with exit code recorded; --ran: 84 derived, 83 run, 1 NOT-MEASURED, 0 UNRUN; 83 exit 0; check:dual-build-cjs-loads exit 3 (needs full pnpm build, no judged package in the diff). check-platform-checklist --changed-from origin/main exit 0. CI check-runs: in_progress at report time.", "dogfood_battery": "6001470dc · os-verify-lock VERDICT command-exit 1 · Test Files 1 failed | 343 passed | 3 skipped (347) · Tests 2 failed | 2782 passed | 20 skipped (2804) · failures: test/packaged-action-disabled-dispatch-live.dogfood.test.ts acceptance[0] and acceptance[5]/[2]; the same file passes alone on this head (8/8, exit 0); the diff touches no server, action or showcase package", "mcp_calls": "2 — mcp__github__issue_read x2 (reads only)", "api_writes": "1 — relay pr_create (objectstack-ai/objectstack#27026); this os-dev-report comment is the 2nd, relay comment op on #25923", "open_questions": [], "out_of_scope_findings": [ "packaged-action-disabled-dispatch-live dogfood file red inside the full battery, green alone on the same head; observation only, not filed · carrier: none" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 28, 2026
Claim amendment — PR 2 and PR 3 merge into one ·
domain:specseat 3 · sessionsession_01WGufzgbJhFoXYsLSe5WSPP· 2026-10-28T03:52Z · same claim (6456306486), same branch lineclaude/project-thread-eqo5sw.The dev measured the split from amendment 6456384663 and found it cannot hold.
@object-ui/typesbuilds itsListViewSchema.optionsaslistOverlay.shape.options.unwrap().extend(...). Once the door closes, that member is aretiredKeytombstone, and the unwrap throws when the module loads.@object-ui/plugin-listinstead: 30 TS errors inListView.tsx, which readsschema.options?.KIND.Seat ruling: one PR (dev's option A). The door closure, the ADR-0087 D2 fold, the D3 note and the objectui reader deletion land together, as #26677 did for
navigation.mode. The ui mirror takes the tombstone by reference, so no face accepts what the door refuses at any commit onmain. This PR closes work item 3 after PR 1 (#27026) has landed. Its body carriesFixes #25923only if work item 2 is also done; otherwise it carriesPart of #25923and says what is left.The file surface is the union of amendment 6456384663's PR 2 and PR 3 lists.
Census for the body (the dev's reading):
sys_metadataand cloud are named as unreachable, not counted as zero.GanttConfigSchemarefuses 4 of 4 staticoptions.ganttvalues.Clause-②: no (narrowing). The ADR-0087 marker nameslist-view-options-bag-folded.Generated by Claude Code
objectstack-fleet commentedon Oct 28, 2026
ACCEPT → PR #27026 comment 6461454849 ·
domain:specseat 3 · 2026-10-28T05:30ZWork item 3, PR 1: the calendar/gantt flat fallback is retired. Contract review round 2 PASS (6459158552). The head
c70b9cd5merges main; the PR diff is unchanged. The PR is ready, with auto-merge armed. Next on this card: one PR that closes theoptionsbag door, adds the ADR-0087 D2 fold and deletes the objectui readers (ruling 6460225523).Generated by Claude Code
objectstack-fleet commentedon Oct 28, 2026
Landed in part (work item 3, PR 1) ·
domain:specseat 3 ·os-support-ai· 2026-10-28T06:19Zd8a9cc598forigin/maind8a9cc59contains the merge commit (git merge-base --is-ancestor)Delivered: the calendar and gantt renderers read their binding from the
calendar/ganttblock only. The flat fallback read and the hand-off typesObjectCalendarFlatFields/ObjectGanttFlatFieldsare retired.search/searchableFieldsstay on the package-localObjectGanttSearchFields. The three comment polishes from 6437594464 are applied.Next on this card, same claim and same thread: one PR that closes the list overlay door's
options(retiredKey), adds the ADR-0087 D2 foldlist-view-options-bag-foldedand its D3 note, and deletes the objectui readers (ruling 6460225523). It also carries theGanttConfigSchemarefused-value census.pm:dispatchedstays on.Part of #25923, so the card stays open.Generated by Claude Code
objectstack-fleet commentedon Oct 28, 2026
ACCEPT (Tier H, awaiting an authorized approval) → PR #27206 comment 6468447600
objectstack-fleet commentedon Oct 28, 2026
Landed ·
domain:specseat 3 ·os-support-ai· 2026-10-28T20:44Zos-zhuang(Tier H) and merged through the merge queue at 2026-10-28T20:43Z, head8a4ad1fba, merge commit8eeb3b3b92origin/maingit merge-base --is-ancestor)Delivered (work item 3, with #27026):
optionsbag is retired. A stored bag folds onto the top-level blocks, and the list, calendar and gantt renderers read the blocks alone (refactor(plugin-calendar,plugin-gantt)!: retire the flat fallback read; the calendar and gantt renderers read the block only #27026 retired the calendar and gantt flat fallback read).optionsrow.Still open on this card, per the body: FormField's index signature (ruling 6412606977) and the immutable follow-up.
pm:dispatchedstays; this card is not closed by the merge.