Skip to content

refactor(plugin-calendar,plugin-gantt)!: retire the flat fallback read; the calendar and gantt renderers read the block only - #27026

Merged
objectstack-fleet[bot] merged 7 commits into
mainfrom
claude/project-thread-eqo5sw
Oct 28, 2026
Merged

objectstack-fleet[bot] merged 7 commits into
mainfrom
claude/project-thread-eqo5sw

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Yibo Wang · project thread

Part of #25923

Clause-②: no (narrowing)
Metadata-protocol: none
Skills-docs: none — skills/objectstack-ui/rules/list-views.md:290-301 and skills/objectui/guides/page-builder.md:291-305 already teach the block only

维护者速读

  • 改了什么:日历和甘特图组件只从 calendar / gantt 配置块读取字段绑定,不再读取写在节点顶层的平铺字段;规范里两条拒收提示同步改掉了“渲染器仍会读取平铺写法”这半句。
  • 为什么改:平铺写法早已被规范拒收、类型包也不再声明,两个视图生产者都只写配置块;对全部可达元数据的普查没有找到一行平铺存储,留着这条读取只是第二种方言(Add comprehensive test suite for Zod schema validation #12)。
  • 风险与回滚:直接挂载 ObjectGantt / ObjectCalendar 组件并写平铺字段的宿主会看到“需要配置”提示而不是图表;回滚即还原本 PR,无存储数据迁移。
  • 先看:ui/packages/plugin-gantt/src/ObjectGantt.tsx(getGanttConfig 与 warnOnFlatGanttKeys)、ui/packages/plugin-calendar/src/ObjectCalendar.tsx(getCalendarConfig)、packages/spec/src/ui/component.zod.ts(两条拒收提示)。
  • 两分钟验证:cd ui && pnpm exec vitest run packages/plugin-gantt/src/ObjectGantt.blockPrecedence.test.tsx packages/plugin-calendar/src/ObjectCalendar.unconfiguredRefusal-7029.test.tsx,预期全绿:平铺节点被拒并提示配置块,配置块节点正常绘制。
  • 需要你做的:无,席位落地

What changes

  • @object-ui/plugin-calendar: getCalendarConfig reads the calendar block only. A node that writes startDateField / endDateField / titleField / colorField / allDayField flat with no block reaches the "Calendar configuration required" screen, whose hint names the calendar block. The package-local ObjectCalendarFlatFields and ObjectCalendarHandoff types are removed; ObjectCalendarProps['schema'] is the published ObjectCalendarSchema.
  • @object-ui/plugin-gantt: getGanttConfig reads the gantt block only. Flat GanttConfig keys with no block reach "Gantt configuration required", which now names the gantt block. The dev warning warnOnFlatGanttKeys names every flat key the chart did not read, with or without a block beside it. The legacy singular dependencyField is read nowhere. ObjectGanttFlatFields is replaced by ObjectGanttSearchFields, which carries only the search / searchableFields pair that ListView writes at runtime.
  • @objectstack/spec: only the false clause of the object-calendar and object-gantt flat-key refusal texts is rewritten (they said the renderer still reads the flat spelling). The prescription itself, write the keys in the block, is unchanged.
  • The three comment polishes from 6437594464: the dated 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 notes in calendar-flat-color-allday-8466.test.ts merged and its read-census rows inverted; objectql.ts gantt block note; the objectql.zod.ts binding bullet and view-level arms note.
  • Renderer and mirror tests that mounted a flat node now write the block; the tests that pin the refusal keep a flat node on purpose.
  • READMEs of both plugins and the gantt demo moved to the block.

Tests

  • New and inverted pins: a flat calendar node and a flat gantt node are each REFUSED with a hint naming the block (ObjectCalendar.unconfiguredRefusal-7029, ObjectGantt.blockPrecedence), with a CONTROL that the same keys moved into the block draw. ganttHandoff-25923.test.ts (renamed from ganttHandoffFlatFields-25923) pins that the hand-off type carries only the search pair and that a flat startDateField is a compile error. ObjectGantt.dependencyAlias pins the singular alias as not read when written flat and refused by name inside the block.
  • Census before and after (TS AST + JSON + YAML walk over 19,856 files, 14 parse failures, all JSONC tsconfigs): flat calendar nodes 12 to 7, flat gantt nodes 23 to 9; every remaining site is a test that pins the refusal or a type-level mirror pin. Zero sites in examples, apps, the console's shipped metadata, ui/examples, seeds, platform-objects or fixtures, before and after.

Acceptance notes

  • packages/spec/src/kernel/functional-completeness.ts and packages/spec/liveness/view.json are in the amended surface but were not edited: their calendar/gantt rows are dated readings pinned at objectui b9814b334, and no sentence in them becomes false with this change.
  • No reference doc or refusal-text snapshot carries the old wording; the only other copy is the earlier .changeset/25923-calendar-gantt-flat-keys-handoff.md, left as written.
  • @object-ui/types, @object-ui/plugin-list and @object-ui/plugin-view carry a patch entry. Their code changes are only comments and tests (in @object-ui/types), or none at all, but the item-1 changeset compiles into their changelogs too. So the Supersedes paragraph has to land beside it there as well.
  • Both plugin entries are graded patch: Metadata-protocol: none takes patch under ruling A. The BREAKING banner and the ADR-0087 marker stay.
  • The item-1 changeset 25923-calendar-gantt-flat-keys-handoff.md says the renderers still read the flat spelling. This PR does not edit it, because check-empty-changeset refuses a change to a changeset the merge base already carries. This PR's own changeset instead names the sentences it supersedes, in the same release.
  • Unreachable for the census: hotcrm, tenant sys_metadata rows, cloud.
  • Out of this PR, per the order: the FormField index signature, the immutable render-or-refuse follow-up, work item 2, and the list options bag (PR 2 closes the spec door with an ADR-0087 fold; PR 3 deletes the objectui readers).

Verification

ui tests (os-verify-lock)
  • calendar + gantt + consumer set: Test Files 818 passed | 3 skipped (821) · Tests 18599 passed | 211 skipped · VERDICT command-exit 0
  • wider registry-reaching set: Test Files 75 passed (75) · Tests 1693 passed | 12 skipped · VERDICT command-exit 0
  • five nodes moved after the after-census: Test Files 5 passed (5) · Tests 111 passed · exit 0
  • plugin-gantt rerun after the Reflect.get fix: Test Files 101 passed | 1 skipped (102) · Tests 768 passed | 114 skipped · exit 0
type-check and spec tests
  • pnpm --filter @object-ui/types --filter @object-ui/plugin-calendar --filter @object-ui/plugin-gantt --filter @object-ui/plugin-list --filter @object-ui/app-shell run type-check (each runs tsc --noEmit and tsc -p tsconfig.test.json): VERDICT command-exit 0
  • pnpm --filter @objectstack/spec test: Test Files 738 passed (738) · Tests 22130 passed | 1 todo · exit 0
dispatch gates
  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack: 84 commands, each run with its exit code recorded.
  • --ran: 84 derived, 83 run, 1 NOT-MEASURED, 0 UNRUN. All 83 measured exit 0.
  • NOT-MEASURED: pnpm check:dual-build-cjs-loads exit 3 (prerequisite: a full pnpm build; no package it judges is in this diff). CI runs it.
  • node scripts/check-platform-checklist.mjs --changed-from origin/main: OK, exit 0.
review rounds (heads 25a73f4, 0226b9c)
  • Lint UI: ObjectCalendar.tsx imported ObjectCalendarBlockConfig, which only the removed hand-off types used; the import is dropped. pnpm --filter lint over types, plugin-calendar, plugin-gantt, plugin-list, app-shell and console: 0 errors, exit 0.
  • check-changeset-no-major.mjs --base origin/main with this PR body as the event: exit 0 (exit 1 on 6001470, the minor grades). check-adr-0087-registration, check-empty-changeset, check:changeset-fixed, check:changeset-gate-self-tests, check:nul-bytes: exit 0.
  • 0226b9c adds the three packages at patch. The same changeset checks all exit 0 again.
  • plugin-calendar tests Test Files 64 passed (64) · Tests 458 passed | 95 skipped, then plugin-calendar type-check: VERDICT command-exit 0.
dogfood battery

Head 6001470dc, closure pnpm --filter '@objectstack/dogfood^...' build (exit 0), then pnpm --filter @objectstack/dogfood test under the lock: VERDICT command-exit 1 · Test Files 1 failed | 343 passed | 3 skipped (347) · Tests 2 failed | 2782 passed | 20 skipped (2804).

The two failures are both in test/packaged-action-disabled-dispatch-live.dogfood.test.ts (#26572): acceptance[0] read the showcase task's done as true before the disabled action ran, and acceptance[5]/[2] found a row id where it expected none. That file passes alone on the same head (Test Files 1 passed (1), Tests 8 passed (8), exit 0). This diff touches no server, action or showcase package. It is the known flake #26952: the test picks an unordered showcase_task?$top=1 and fails when that seed task is already done, and it is red on main too.


Generated by Claude Code

…d; the calendar and gantt renderers read the block only

getCalendarConfig and getGanttConfig read the `calendar` / `gantt` block and
nothing flat. The package-local ObjectCalendarFlatFields / ObjectCalendarHandoff
types are gone; ObjectGanttFlatFields shrinks to ObjectGanttSearchFields
(the `search` / `searchableFields` pair ListView writes at runtime). A flat
node reaches the configuration-required screen, which names the block, and
the gantt dev warning names every flat key it did not read. The renderer
tests that authored flat nodes move to the block form.

The spec's object-calendar / object-gantt refusals drop the clause that the
renderer still reads the flat spelling, and three comment polishes from the
item-1 review land with it.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGufzevL8Q6rTfGz2XHXGT
…s binding in the calendar block

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGufzevL8Q6rTfGz2XHXGT
…nderer and mirror nodes to the block; read the unread flat keys with Reflect.get

An after-change census found five more test nodes written flat (a gantt
expand test, the console filter-token sweep, a calendar invalidation test
and two @object-ui/types mirror pins). They now write the block. Three
comments that still said the renderer reads the binding flat are rewritten.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGufzevL8Q6rTfGz2XHXGT
@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)), so this run has no opinion about the docs.

What this run could not see

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 6001470dceeae8a1413c05c3943d99d97cedef3e
Local-runs: none
Scope: packages/spec/src/ui/component.zod.ts, .changeset/25923-calendar-gantt-flat-fallback-retired.md, packages/spec/src/ui/component.test.ts, ui/apps/console/src/tests/filterContextTokensSweep-10666.test.tsx, ui/packages/app-shell/src/tests/widget-dom-leak-sweep.test.tsx, ui/packages/plugin-calendar/src/CalendarView.popoverLocale-10747.test.tsx, ui/packages/plugin-calendar/src/ObjectCalendar.invalidationRefetch-10572.test.tsx, ui/packages/plugin-calendar/src/ObjectCalendar.quickCreateHandoff-12291.test.tsx, ui/packages/plugin-calendar/src/ObjectCalendar.refusalRemedy-8170.test.tsx, ui/packages/plugin-calendar/src/ObjectCalendar.tsx, ui/packages/plugin-calendar/src/ObjectCalendar.unconfiguredRefusal-7029.test.tsx, ui/packages/plugin-calendar/src/tests/ObjectCalendar.filterIsNotAConfigSlot-7711.test.tsx, ui/packages/plugin-calendar/src/tests/calendarUnionReads-8651.test.tsx, ui/packages/plugin-calendar/src/tests/objectCalendarConfigMembers-8071.test.tsx, ui/packages/plugin-calendar/src/index.tsx, ui/packages/plugin-calendar/src/object-calendar-renderer.propsContract.test.tsx, ui/packages/plugin-gantt/demo/main.tsx, ui/packages/plugin-gantt/src/ObjectGantt.blockPrecedence.test.tsx, ui/packages/plugin-gantt/src/ObjectGantt.contractEnvelope-6839.test.tsx, ui/packages/plugin-gantt/src/ObjectGantt.dependencyAlias.test.tsx, ui/packages/plugin-gantt/src/ObjectGantt.expandFls-7230.test.tsx, ui/packages/plugin-gantt/src/ObjectGantt.exportFileName.i18n.test.tsx, ui/packages/plugin-gantt/src/ObjectGantt.persistfilters.test.tsx, ui/packages/plugin-gantt/src/ObjectGantt.propertiesBag-10859.test.tsx, ui/packages/plugin-gantt/src/ObjectGantt.quickfilter.i18n.fallback.test.tsx, ui/packages/plugin-gantt/src/ObjectGantt.quickfilter.i18n.test.tsx, ui/packages/plugin-gantt/src/ObjectGantt.quickfilter.test.tsx, ui/packages/plugin-gantt/src/ObjectGantt.referenceArms-6837.test.tsx, ui/packages/plugin-gantt/src/ObjectGantt.test.tsx, ui/packages/plugin-gantt/src/ObjectGantt.tsx, ui/packages/plugin-gantt/src/ObjectGantt.unconfiguredRefusal-7070.test.tsx, ui/packages/plugin-gantt/src/ObjectGantt.viewmode.test.tsx, ui/packages/plugin-gantt/src/tests/ganttHandoff-25923.test.ts, ui/packages/plugin-gantt/src/tests/ganttHandoffFlatFields-25923.test.ts (deleted), ui/packages/types/src/tests/calendar-flat-color-allday-8466.test.ts, ui/packages/types/src/tests/gantt-declared-keys.test.ts, ui/packages/types/src/tests/gantt-dependency-field-deprecated-alias.test.ts, ui/packages/types/src/tests/gantt-flat-config-declared-keys.test.ts, ui/packages/types/src/tests/gantt-search-keys-10250.test.ts, ui/packages/types/src/tests/object-kanban-calendar-navigation-8652.test.ts, ui/packages/types/src/objectql.ts, ui/packages/types/src/zod/objectql.zod.ts + the three prose files ui/packages/plugin-calendar/README.md, ui/packages/plugin-gantt/README.md (read for the retired-wording claim) + cited on origin/main acc9c57d: ui/packages/plugin-list/src/ListView.tsx (case 'calendar' 4205-4266, case 'gantt' 4392-4496), ui/packages/plugin-view/src/ObjectView.tsx (2124-2132, 2328-2345, 2438-2456), ui/packages/app-shell/src/views/InterfaceListPage.tsx (182-207, 456-462), ui/packages/app-shell/src/views/objectViewListRenderer.tsx (190-205), ui/packages/plugin-calendar/src/index.tsx and ui/packages/plugin-gantt/src/index.tsx (entry exports and registry inputs), packages/spec/src/kernel/functional-completeness.ts (246-460), packages/spec/liveness/view.json (props.list.children.calendar.note, .gantt.note), packages/spec/liveness/page.json (row regions), .changeset/25923-calendar-gantt-flat-keys-handoff.md, skills/objectstack-ui/rules/list-views.md (286-304), skills/objectui/guides/page-builder.md (289-307), examples/app-crm/src/views/activity.view.ts, examples/app-showcase/src/ui/views/task.view.ts, content/docs/ui/views.mdx (336-356), packages/qa/dogfood/test/packaged-action-disabled-dispatch-live.dogfood.test.ts and its five siblings that flip showcase_mark_done, .github/workflows/ci.yml (345-362, 2925-2950), scripts/check-changeset-no-major.mjs (895-930, ruling A), .changeset/config.json, ui/AGENTS.md #0.1

Inputs read: card #25923 body and all 21 comments (in particular 6437594464, 6448239783, 6456306486, 6456384663, 6456494843, 6458717005); the census file /mnt/project-files/issue-25923/item3-census-and-premise.md (read only); PR #26411's file list and its contract review 6436950621; PR #27026's body (updated_at 2026-10-28T02:07:26Z), 44-file list (+694 / -829 = 1523 changed lines), its one comment (docs-drift bot 6458721387), zero review comments and zero reviews, and the net diff against main at the head (application/vnd.github.diff, 2573 lines, read in full); the head files named in Scope via GET /contents?ref=6001470d; a read-only git fetch origin main (acc9c57, which is also the PR's base sha, so the net diff reads the same against today's main: git log acc9c57d..origin/main -- <44 paths> is empty) followed by git grep / git show on origin/main; the check-runs on the head, first read 2026-10-28T02:08:39Z and re-read until the readings recorded in ③. record-recognisers --template --paths over the 44 files returns hits = component.zod.ts (the schema face); the Clause-②: no (narrowing) line adds the narrowing face, so the Scope line carries every path of the diff plus the cited files. The dispatch order and the dispatching seat's own conclusions were not read.

① Derived judgments

  • D1 — RIGHT, the flat fallback read is gone in both renderers, read at the head. getCalendarConfig (ObjectCalendar.tsx:322-336) is one line of code, return schema.calendar ? (schema.calendar as ObjectCalendarConfig) : null; the memo depends on [schema.calendar] alone (:655); the only schema.<flat key> spelling left in the file is a comment at :1058 quoting calendar-view. getGanttConfig (ObjectGantt.tsx:633-646) calls warnOnFlatGanttKeys and then reads schema.gantt or returns { config: null, invalidBlock: null }; the 30-key flat pick and the dependenciesField || dependencyField limb are deleted. The complete set of schema.X reads in ObjectGantt.tsx at the head is objectName, sort, searchableFields, search, gantt, filter, readOnly, label, viewName, type, skipWeekends, showBaselines, persistLayout, navigation, mobileReadOnly, markers, holidays, data, criticalPath — no GanttConfig key and no dependencyField; ganttHandoff-25923.test.ts pins three of those absences on the source text and the compile-time absence on ObjectGanttHandoff for every KnownGanttConfigKey.
  • D2 — RIGHT, the hand-off types retire as the claim says, and the search pair stays package-local. ObjectCalendarFlatFields / ObjectCalendarHandoff are deleted (ObjectCalendar.tsx, the -20 hunk at :131) and ObjectCalendarComponentProps['schema'] is the published ObjectCalendarSchema (:151), pinned by assertionSchemaPropIsThePublishedElementSchema in calendarUnionReads-8651. ObjectGanttFlatFields is gone; ObjectGanttSearchFields (ObjectGantt.tsx:313-321) carries exactly search?: string and searchableFields?: string[], ObjectGanttHandoff = ObjectGanttSchema & ObjectGanttSearchFields (:324) types the prop (:349), and the pair is read only on the fetch path (:875-877), which ListView writes at runtime (its case 'gantt' toolbar arm, read in refactor(types,plugin-calendar,plugin-gantt,plugin-list,plugin-view)!: flat object-calendar / object-gantt keys leave the published schemas; producers emit the block #26411's review). Neither published @object-ui/types face declares the pair (objectql.ts:3839-3845, objectql.zod.ts:2972-2976 at the head, comments only in this diff; the pins gantt-search-keys-10250.test.ts are unchanged in substance). The plugin entries export by name only ObjectGanttProps / QuickFilterDef (plugin-gantt index.tsx:20) and ObjectCalendarComponentProps (plugin-calendar index.tsx:23, :32), so the changeset's "neither package's entry exports" the hand-off types is true by name; the pair is reachable as ObjectGanttProps['schema'], which the changeset's plugin-gantt bullet says in those words. The registries' inputs declare calendar / gantt blocks only (plugin-calendar index.tsx:450, plugin-gantt index.tsx:184), so no registry input and no sdui.manifest.json row advertised the flat read, and none needs regenerating.
  • D3 — RIGHT, a flat node is refused with a remedy and renders nowhere silently wrong or empty. Calendar: calendarConfig null reaches the if (!calendarConfig) arm whose text names startDateField and says "It belongs on the view's calendar block" (ObjectCalendar.tsx:1448-1454); pinned by the inverted REFUSES a binding written FLAT on the node case (unconfiguredRefusal-7029:106-124, asserts the refusal text, /calendar block/, and that neither record draws) beside the surviving draw CONTROL (:127-145). Gantt: config null reaches if (!ganttConfig) whose text now ends "in the view's gantt block" (ObjectGantt.tsx:2330), and warnOnFlatGanttKeys (:578-598) names every present FLAT_GANTT_CONFIG_KEYS member via Reflect.get whether or not a block is beside it, once per distinct key set; pinned by blockPrecedence REFUSES a node whose binding is written flat with no gantt block (refusal text, gantt block, no gantt-view, exactly one warning naming the three keys) with its moved-into-block CONTROL. Producers: ListView.tsx:4254-4266 spreads calendar: calendarBlock and :4411-4496 gantt: ganttBlock, no flat calendar/gantt key; plugin-view ObjectView.tsx:2328-2345 and :2438-2456 nest calendar: {…} / gantt: {…}; app-shell InterfaceListPage composes view-level calendar / gantt (:456-462) and objectViewListRenderer only warns on view-level flat keys (:194-205) — both feed ListView, which builds the block; ViewPreview.tsx folds options.KIND onto the view-level KIND, upstream of the block build. The demo host moved to gantt: {…} (demo/main.tsx:367-383). The map and timeline arms still flatten by design and are not this card's node types. A rough census of my own over the 5,994 test files under ui/ outside the PR's 44 paths (a type: 'object-gantt'|'gantt'|'object-calendar'|'calendar' key followed within eight lines at the same indentation by a flat startDateField / endDateField / titleField) finds 0 sites, consistent with the dev's after-census (7 calendar / 9 gantt flat sites, all inside PR files as refusal or type-level pins).
  • D4 — RIGHT, the census is honest and the no-conversion retirement is justified on it. The census file names its tree (origin/main 4b8f8cdc), its method (TS AST + JSON + YAML over 19,856 files, 14 JSONC parse failures) and its counts (flat calendar 12, flat gantt 23, all tests but the demo host; shipped metadata 0), and says in its own words "Not reachable, so not zero: hotcrm metadata, production tenant sys_metadata rows, cloud" — the PR body and the dev report repeat that line; neither counts the unreachable sources as zero. The retirement needs no ADR-0087 entry because no accepted shape moves: the spec refused the flat keys on object-calendar / object-gantt before this card, @object-ui/types stopped declaring them in refactor(types,plugin-calendar,plugin-gantt,plugin-list,plugin-view)!: flat object-calendar / object-gantt keys leave the published schemas; producers emit the block #26411, and both producers write the block — what goes is a consumer's tolerance of an off-spec shape, which is what Prime Directive Add comprehensive test suite for Zod schema validation #12 forbids keeping. A tenant sys_metadata VIEW row is unaffected (its calendar / gantt / options.KIND is read at the view level and the block is built from it); a tenant PAGE row carrying a flat node, which the spec door already refuses, now reaches the "configuration required" screen naming the block and, in dev, the console warning naming each key (D3) — a refusal with a remedy, not a silent drop, and not a renderer-side accommodation.
  • D5 — RIGHT, the renderer tests move to the block honestly, with the same assertions. Fixture moves (same assertions, binding relocated): filterContextTokensSweep-10666, widget-dom-leak-sweep, popoverLocale-10747, invalidationRefetch-10572 (also corrects a pre-existing dateField alias to startDateField inside the block), quickCreateHandoff-12291, refusalRemedy-8170 (both nodes), filterIsNotAConfigSlot-7711 (memo pin now flips calendar.startDateField), objectCalendarConfigMembers-8071, propsContract, contractEnvelope-6839, expandFls-7230, exportFileName.i18n (extra / gantt split), persistfilters, quickfilter ×3, referenceArms-6837, unconfiguredRefusal-7070 (the draw CONTROL moved; the half-axis refusal keeps a flat node on purpose), viewmode, gantt-declared-keys, object-kanban-calendar-navigation-8652. Inverted pins (the fact flipped, assertion kept at the same strength): unconfiguredRefusal-7029 (draw → refuse, D3), blockPrecedence (still reads the flat spelling → REFUSES + CONTROL; does NOT fire on the flatten product → the same shape now asserts exactly one warning; says which face won → says where the keys belong), ObjectGantt.test.tsx (forwards summaryExtent from top-level → not.toBe('self') beside a valid block), dependencyAlias (DEPRECATED singular is still read → not read when flat, deps '' / autoSchedule 'false'; new singular INSIDE the block is refused on gantt-invalid-config naming the key), calendarUnionReads-8651 (accepts the flat vocabulary → @ts-expect-error; every one is still READ → not.toContain; the census CONTROL now asserts calendar is read and the twins are not), 8466 (read rows and memo-deps rows inverted; the allDayField load-bearing behaviour stays pinned in allDayFieldIsHonoured-8026, which this diff does not touch). Removed outright, each with its reason: dependencyAlias aimed at ONE field, identical result and an EMPTY canonical value falls through to the alias (both pinned the || limb that no longer exists; the replacement pins the alias as unread), propertiesBag-10859's FLAT_CONFIG it.each arm (pinned that flattened config keys made the identical call; now refused, pinned in blockPrecedence), and the file ganttHandoffFlatFields-25923.test.ts (replaced by ganttHandoff-25923.test.ts, which carries the surviving rows — the search pair's types, no double home, the prop typed with the hand-off — and inverts the rest; the PR body names the rename). No assertion was weakened: every removed pin pinned the retired behaviour itself.
  • D6 — RIGHT with one polish, the four spec files. component.zod.ts: the two prescription strings lose only the clause "the flat spelling is only a stored-document fallback the renderer still reads" and now read "the renderer reads the binding there only: the flat spelling is not read" (6060-6062, 7407-7410); the remedy "Write this as a key of the calendar / gantt config object instead — …" and the dependencyField → dependenciesField clause are byte-unchanged; name, keys, examples and the strictObject wiring are untouched (the hunks are prose). Both JSDoc blocks (6044-6051, 7373-7386, 7478-7489) are true against D1-D3. component.test.ts:3293-3295 says "Not read by getCalendarConfig since objectstack#25923" — true. kernel/functional-completeness.ts, unedited: its calendar row (:277-304) is "Measured on objectui main at 0cf2d6644 (2026-09-21)" and "last re-read on 2026-10-14", and its sentence "getCalendarConfig returns null with neither a calendar block nor a flat startDateField" stays true (a node with neither still gets null); its gantt row (:305-319) is "Measured on the console this repo ships (read points recorded in view.json, row list.gantt)" and says getGanttConfig "takes its flat branch only when BOTH dates are present and otherwise returns null" — true of the sha it cites and of the built console this PR does not rebuild; the row's verdict, "NO fallback, and no silence", holds a fortiori. liveness/view.json: both notes open "re-read at objectui @b9814b334 (2026-10-14)" and describe that sha. No sentence in either goes false; both will need their next dated re-read to drop the flat clause, which is the ordinary re-measure, not this PR. liveness/page.json row regions carries no calendar/gantt flat sentence. No generated artifact carries the retired wording: git grep on origin/main for "stored-document fallback" and "renderer still reads" outside the PR's paths hits only unrelated keys (staticData), released CHANGELOGs, and the standing changeset .changeset/25923-calendar-gantt-flat-keys-handoff.md (③).
  • D7 — RIGHT, the three comment polishes from 6437594464 are applied: the two consecutive dated 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 notes in calendar-flat-color-allday-8466.test.ts are one paragraph at the head (:32-43), and its read-census rows are inverted (:415-456); objectql.ts:317-320 no longer says "BOTH authoring faces" ("in the gantt: { … } block, the one spelling it reads since objectstack#25923 retired the flattened top-level fallback"); objectql.zod.ts:1877-1885 no longer lists "the flat node face itself" among producer-fed surfaces and explains why the flat node face takes the binding clause now.
  • D8 — WRONG, the changeset's levels. .changeset/25923-calendar-gantt-flat-fallback-retired.md:8-9 grades @object-ui/plugin-calendar and @object-ui/plugin-gantt minor beside Metadata-protocol: none (:16). Ruling A (scripts/check-changeset-no-major.mjs:899-925) and AGENTS.md Post-Task Checklist Implement ObjectStack protocol specification with Zod schemas and TypeScript interfaces #3 decide the level by the kind alone: none → every introduced entry patch; a non-protocol narrowing ships patch and keeps its BREAKING banner and ADR-0087 disposition. The kind none is the right declaration — no spec key, accepted value or published authoring face moves (the spec and @object-ui/types refused the flat keys before this PR); what narrows is a React props type and a drawing outcome, which is not "what a metadata author can write" — so the two minor entries are a none beside a minor, "the half of the ruling with teeth", and the Check Changeset run on this head says exactly that (failure at 02:08:14Z: "metadata-protocol declares none while a changeset entry is graded minor or above … Under none the level is patch"). The remedy is patch on both plugin packages (spec is already patch); the ! title, the BREAKING banner and the marker stay.
  • D9 — RIGHT, the dogfood red is not this diff's doing. packages/qa/dogfood/test/packaged-action-disabled-dispatch-live.dogfood.test.ts boots the showcase server (bootShowcase) and exercises /actions/showcase_task/showcase_mark_done, the activation ledger rows and the MCP run_action tool over HTTP — server, action and metadata-store code, no renderer and no @object-ui/* package. This diff touches ui/ renderers and tests, two spec prose constants and one spec test comment, none of which the booted showcase reads. Five sibling dogfood files flip showcase_mark_done or write showcase_task done (action-activation-door-live, activation-ledger-actor, showcase-record-write-actions, showcase-anonymous-deny-surfaces, object-hook-lifecycle-showcase), which is the shape of the two failures the dev reports (acceptance[0] read done true before the disabled action ran; acceptance[5]/[2] found a row id where none was expected) and consistent with the file passing alone on the same head (8/8). So: battery ordering, pre-existing, not a consequence of this change — and a defect in its own right (③).
  • D10 — RIGHT, CI measures the one NOT-MEASURED derived gate. pnpm check:dual-build-cjs-loads is a step of Build Core in ci.yml (:2945, "Every published require entry point actually loads"), which runs on this head because a packages/spec path is in the diff; its conclusion is in the ③ readings.

② Semver level

  • Changeset .changeset/25923-calendar-gantt-flat-fallback-retired.md (root .changeset/, right for ui/** packages): @object-ui/plugin-calendar: minor, @object-ui/plugin-gantt: minor, @objectstack/spec: patch; Clause-②: no (narrowing); Metadata-protocol: none; one ADR-0087 marker not-required (no-migration-prescription) whose facts hold (no spec key, accepted value, stored row or stored shape moves; the census; the narrowed members are package-local types; the packages publish; no ADR-0087 id covers it); a BREAKING banner with Before / Now / Fix, the fix naming the block and the titleField requirement the flat branch used to floor. The PR body carries the same two declaration lines plus Skills-docs: none with two citations.
  • Level: patch on all three is the honest grade under ruling A, and the two minor entries are refused by the gate (D8). @objectstack/spec at patch is right (prose only). @object-ui/types carries comment and test changes only (objectql.ts and objectql.zod.ts hunks are JSDoc; the six test files are tests) and takes no entry — the gate did not object, and the changeset's level gate is the one reader. No fixed-group lift follows from patch.
  • Kind: none is right. Clause-②: no (narrowing): right — nothing widens, and a published public surface narrows (ObjectCalendarComponentProps['schema'] and ObjectGanttProps['schema'] lose the flat keys as compile errors; a host's flat node stops drawing), so the (narrowing) arm, which the ADR-0087 gate and the queue guard read, is owed and declared on both carriers. Skills-docs: none: right — skills/objectstack-ui/rules/list-views.md:289-304 and skills/objectui/guides/page-builder.md:289-305 teach the gantt block only and the latter already says a flat startDateField on the node is refused by name; git grep object-(gantt|calendar) under skills/ hits only those two spans.

③ Boundary flags

  • FAIL reason D8 — changeset level: .changeset/25923-calendar-gantt-flat-fallback-retired.md:8-9 minor ×2 under Metadata-protocol: none; fix is patch on @object-ui/plugin-calendar and @object-ui/plugin-gantt, nothing else in the file moves. Check Changeset on the head is red on this and only this.
  • Dev acceptance note "functional-completeness.ts and liveness/view.json not edited, no sentence goes false": VERIFIED sentence by sentence (D6). ESCALATED to the seat as a note, not a defect: both carry present-tense descriptions of the flat branch anchored to a sha; the next dated re-read of rows list.calendar / list.gantt should drop the flat clause.
  • Dev acceptance note "the only other copy is .changeset/25923-calendar-gantt-flat-keys-handoff.md, left as written": VERIFIED (the grep in D6). ESCALATED to the seat: that standing, unreleased changeset says "The renderer is unchanged: the calendar block wins, and the flat spelling is read only without it" and will compile into the same release's CHANGELOG as this one; each describes its own PR truthfully and the later entry states the newer state, so no edit is owed by this PR, but the seat may prefer a one-clause amendment there ("retired in a later slice") so an upgrading reader who greps ObjectGanttFlatFields is not told the read survives.
  • Dev acceptance note "@object-ui/types comment and test changes only, no bump": VERIFIED (D5, ②).
  • Dev acceptance note "Unreachable for the census: hotcrm, tenant sys_metadata rows, cloud": VERIFIED as honestly stated in the census file, the PR body and the report (D4).
  • Dev deviation "check:dual-build-cjs-loads NOT-MEASURED, exit 3": ANSWERED — it is a Build Core step and CI measures it on this head (D10).
  • Dev flag "dogfood battery red in packaged-action-disabled-dispatch-live, green alone; observation only, not filed": the attribution is ANSWERED (D9, not this diff's doing). ESCALATED to the seat: an order-dependent red inside the full battery is a reproducible defect under Prime Directive chore: version packages #10 and the main-red convention, and the dev's "carrier: none" leaves it unfiled; the seat should file it or name the card that carries it. Dogfood Regression Gate on this head is success with its three shards path-skipped, so CI did not run the battery for this PR; the dogfood owed by the narrowing arm is the dev's local run, honestly reported.
  • Polish, not a FAIL reason, for the seat's acceptance notes: objectCalendarConfigMembers-8071.test.tsx:172 keeps the title "CONTROL: with no calendar object authored, the flat legacy spelling is read instead" over a body that now authors the calendar object — the assertions are right, the title states the retired fact. Also ObjectCalendar.tsx:1058 quotes schema.allDayField in a comment about calendar-view, harmless but the one flat spelling left in the file.
  • The seat ruling 6456384663 on the options bag (PRs 2 and 3): not in this diff, as the amendment ordered; ListView.tsx still double-reads schema.X || schema.options?.X at the sites cited in Scope, and no file of this PR touches the overlay door. Out of scope here, recorded as read.
  • 6436950621's escalations for this item (census counts rows whose options.gantt carries a GanttConfigSchema-refused value; retire the flat fallback reads): the retirement is DELIVERED (D1-D3); the refused-value count is a work-item-3 census question that belongs to PR 2's stored-row conversion, and the census file does not report it — ESCALATED to the seat to carry into PR 2.
  • Governance: the PR is a draft, head repo = base repo, no auto-merge, 1523 changed lines (under 3,000), and its 44 paths touch no governed surface (Governed Surface Queue Guard = success).
  • Gate readings on the head (UTC, 2026-10-28), first read 02:08:39Z: 39 check-runs, 11 success, 8 skipped, 1 failure (Check Changeset, 02:08:14Z, the D8 finding), 19 in_progress. Re-read 02:14:49Z: 59 check-runs, 20 success, 7 skipped, 1 failure, 31 in_progress. Last read 02:20:51Z, before this post: 59 check-runs — 31 success, 7 skipped, 1 failure (Check Changeset), 20 in_progress. Of the eight required contexts at 02:20:51Z: Lint & Repo Gates success (02:12:29Z), Build Core success (02:16:55Z, so check:dual-build-cjs-loads was measured on this head and passed — D10), Governed Surface Queue Guard success (02:06:29Z), Build & E2E UI success (02:16:21Z); Test Core shards 1, 2, 8 success and 3-7 in_progress; Lint UI, Type Check UI in_progress; no check-run named TypeScript Type Check is present yet (its lanes Type Check · source gates / consumer gates / debt ledger are success and Type Check · workspace in_progress). Also success: Spec property liveness, Plan Test Core shards, Build UI Deps, Check PR Size, Dogfood Regression Gate (shards path-skipped), Flag docs affected by code changes, Auto Label, the nine Lint · lanes, the PR-body guards, filter. Test UI 1, 8, 11, 12 success; 2-7, 9, 10, 13-16 in_progress. An in_progress reading is recorded as in_progress, not as a pass; the verdict below rests on D8, which no later gate reading can change on this head, so this record was posted without waiting for the remaining runs.

Implemented-by: claude/project-thread-eqo5sw
Reviewed-by: session_01WGufzgbJhFoXYsLSe5WSPP

VERDICT: FAIL

…h, name the item-1 sentences it supersedes, drop an unused import

Metadata-protocol none takes patch under ruling A. This PR's changeset now
names the sentences of the item-1 changeset that the retirement makes false,
in the same release, instead of editing that note. One calendar test title
now matches its block-authored body, and ObjectCalendar.tsx no longer
imports ObjectCalendarBlockConfig, which only the removed hand-off types used
(the Lint UI error).

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGufzevL8Q6rTfGz2XHXGT
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 25a73f45607072e268bd4d15b84e2f561b006f6d
Local-runs: none
Scope: .changeset/25923-calendar-gantt-flat-fallback-retired.md, ui/packages/plugin-calendar/src/ObjectCalendar.tsx, ui/packages/plugin-calendar/src/tests/objectCalendarConfigMembers-8071.test.tsx (the r2 increment) + the 41 other paths of the net diff, unchanged since 6001470 and adopted from record 6458913292 (its Scope line lists them, with the cited files) + cited for this round: .changeset/25923-calendar-gantt-flat-keys-handoff.md on origin/main (every sentence, read against the Supersedes paragraph), scripts/check-empty-changeset.mjs (the merge-base rows A/M/D, :159-180), scripts/check-changeset-no-major.mjs (ruling A, :899-925), card #26952 body, the Lint UI annotations on 6001470 (check-run 122589934186)

Inputs read: the PR as it stands (head 25a73f45, updated_at 2026-10-28T02:35:31Z, draft, 44 files, +698 / -831 = 1529 changed lines), its body after the seat's replacement, its two comments (docs-drift bot 6458721387, my r1 record 6458913292), zero reviews; GET /compare/6001470d...25a73f45 as JSON (status ahead, ahead_by 1, behind_by 0, one commit 25a73f45 with the single parent 6001470d, no merge of main, three files: the changeset +4/-2, ObjectCalendar.tsx +1/-1, the 8071 test +1/-1) and as a diff (48 lines, read in full); ObjectCalendar.tsx at the new head; the check-runs on 25a73f45, first read 02:35:45Z and re-read until the readings in ③; origin/main at acc9c57d (the PR's base; unmoved since r1). Round 1's inputs and judgments are adopted for everything the increment does not touch: the compare proves the other 41 paths are byte-identical to the head 6458913292 judged. The coordinator's message and the dev's own claims were read as data and each one verified below.

① Derived judgments

  • D1 — RIGHT, the increment is exactly the three announced paths and nothing else. The compare's file list is the changeset, ObjectCalendar.tsx and the 8071 test; the diff's 48 lines carry three hunks: the two front-matter levels minor → patch and one added paragraph in the changeset; the one import line in ObjectCalendar.tsx (ObjectCalendarBlockConfig dropped from the @object-ui/types type import, :26); the one it( title in the 8071 test (:172). No code path, string, key or accept set moves; r1's D1-D7, D9 and D10 hold on this head unchanged.
  • D2 — RIGHT, r1's FAIL reason D8 is CLOSED. .changeset/25923-calendar-gantt-flat-fallback-retired.md:2-4 now grades @object-ui/plugin-calendar: patch, @object-ui/plugin-gantt: patch, @objectstack/spec: patch beside Metadata-protocol: none and Clause-②: no (narrowing) — the exact shape ruling A prescribes for a non-protocol narrowing (scripts/check-changeset-no-major.mjs:916-919); the ! title, the BREAKING banner with Before / Now / Fix and the single not-required (no-migration-prescription) marker are unchanged. The gate's verdict on this head is in ③.
  • D3 — RIGHT, the Lint UI red on 6001470 is this import, and it is gone. The check-run's one failure annotation is ui/packages/plugin-calendar/src/ObjectCalendar.tsx:26 — 'ObjectCalendarBlockConfig' is defined but never used (the rest are pre-existing no-explicit-any warnings in plugin-timeline and e2e files, not errors). The symbol was used only by the deleted ObjectCalendarFlatFields (Pick<ObjectCalendarBlockConfig, …>); the new head's ObjectCalendar.tsx has zero occurrences of it and the import reads ObjectCalendarSchema, DataSource, CalendarConfig. A missed unused import was an r1 gap of mine (I grepped the file for flat reads, not for the import line); recorded.
  • D4 — RIGHT, the Supersedes paragraph names every item-1 sentence this PR makes false, and makes the compiled notes true where both entries land. The item-1 changeset (on origin/main, unreleased) carries five such sentences: the marker's "the renderers still read the flat spelling a host hands them"; the plugin-calendar bullet "ObjectCalendarFlatFields and ObjectCalendarHandoff type the flat read getCalendarConfig keeps … The renderer is unchanged: the calendar block wins, and the flat spelling is read only without it"; the plugin-gantt bullet "ObjectGanttFlatFields … type the flat read the same way. The renderer is unchanged"; the spec bullet "the flat spelling is only a stored-document fallback the renderer still reads"; and the Now / Fix clauses "A renderer handed a flat node by a host still draws it" and "ObjectCalendarProps['schema'] or ObjectGanttProps['schema'], which carry the flat keys". The new paragraph names each: the renderers "unchanged and still read the flat spelling a host hands them", the two props types "carry the flat keys", the refusals "call the flat spelling a fallback the renderer still reads" — and states the superseding fact for each ("the renderers read the block only, the props types carry no flat binding key, and the refusals no longer say it"). Both changesets compile into the @object-ui/plugin-calendar, @object-ui/plugin-gantt and @objectstack/spec CHANGELOGs of the same release, and both entries name ObjectCalendarFlatFields / ObjectCalendarHandoff / ObjectGanttFlatFields, so a reader who greps a tombstoned symbol lands on both; the filename in the paragraph will not survive compilation, but "the earlier slice of 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" identifies the entry. Not editing the item-1 file is the gate's rule, not a choice: check-empty-changeset.mjs:162-164 refuses an M or D row for any changeset present at the merge base.
  • D5 — RIGHT, the 8071 title now matches its body: "CONTROL: a calendar object with no flat sibling is read as authored" over a fixture that authors calendar: { startDateField: 'other_start', titleField: 'other_title' } and asserts the event's start and title come from those fields; assertions unchanged. r1's polish flag is closed.
  • D6 — RIGHT, the replacement PR body matches the diff. New or changed claims, each verified: "Both plugin entries are graded patch" (D2); "This PR does not edit [the item-1 changeset], because check-empty-changeset refuses a change to a changeset the merge base already carries. This PR's own changeset instead names the sentences it supersedes" (D4); the review round (head 25a73f456) block names the dropped import and the file (D3) and reports local lint / changeset-gate / plugin-calendar runs as the dev's own readings, which CI re-measures (③); the dogfood paragraph now cites dogfood: packaged-action-disabled-dispatch-live picks an unordered showcase_task?$top=1; when that seed task is already done, acceptance[0] fails and acceptance[5] cascades (main push red 21:56Z and 22:08Z) #26952, which is the existing open card for exactly this file: "picks an unordered showcase_task?$top=1; when that seed task is already done, acceptance[0] fails and acceptance[5] cascades", red/green/red/green on main pushes with no code change in between — so r1's escalation that the red was unfiled is closed by citation, and r1's D9 attribution (not this diff's doing) stands with the card's own mechanism. Everything else in the body is byte-for-byte the r1 body, and the file list (44, the deletion counted) matches.

② Semver level

  • Changeset .changeset/25923-calendar-gantt-flat-fallback-retired.md: patch ×3 (@object-ui/plugin-calendar, @object-ui/plugin-gantt, @objectstack/spec); Clause-②: no (narrowing); Metadata-protocol: none; one ADR-0087 marker not-required (no-migration-prescription), facts unchanged and true; BREAKING banner with the FROM → TO fix. The PR body carries the same two declaration lines and Skills-docs: none with its two citations (r1 ② verified them).
  • Level: patch is the grade ruling A demands for none (r1 D8 / ② reasoning adopted); the BREAKING banner and the marker rightly survive at patch. @object-ui/types still takes no entry (comments and tests only).
  • Kind and arm: none and no (narrowing) unchanged and right (r1 ②).

③ Boundary flags

  • r1 FAIL reason D8 (changeset level): CLOSED (D2).
  • r1 polish flags: the 8071 title CLOSED (D5); the standing item-1 changeset ANSWERED by the Supersedes paragraph (D4), with one residue ESCALATED to the seat as a note, not a defect: the item-1 entry also compiles into the @object-ui/types, @object-ui/plugin-list and @object-ui/plugin-view CHANGELOGs, where no entry of this PR sits beside it; its sentence "the keys only the host writes now live on each renderer package's own hand-off type" stays true for the gantt search pair and goes stale for the calendar keys there. Carrying a note into those CHANGELOGs would need an entry on packages this PR does not move, which the gate refuses under none; the seat may accept the residue or amend at release time per the Documentation Guardrails. The ObjectCalendar.tsx:1058 comment quoting schema.allDayField stands as r1 recorded (harmless).
  • r1 escalation "dogfood red is an unfiled defect": CLOSED — it is card dogfood: packaged-action-disabled-dispatch-live picks an unordered showcase_task?$top=1; when that seed task is already done, acceptance[0] fails and acceptance[5] cascades (main push red 21:56Z and 22:08Z) #26952 (D6), and the PR body now says so.
  • r1 escalation for PR 2 (count stored options.gantt rows carrying a GanttConfigSchema-refused value): unchanged, still with the seat.
  • Dev claim "Lint UI fixed by dropping an import only the removed hand-off types used": VERIFIED against the check-run annotation and the head file (D3).
  • Dev claim "check-empty-changeset refuses edits to a changeset the merge base carries": VERIFIED in the script (D4).
  • Dev's local readings in the review round block (lint over six packages exit 0; check-changeset-no-major --base origin/main exit 0 on this head and exit 1 on 6001470; plugin-calendar 64 files / 458 tests, type-check exit 0): read as the dev's own measurements, not re-run; CI's Lint UI, Check Changeset and Test UI on this head are the readings of record (below).
  • Governance unchanged: draft, head repo = base repo, no auto-merge, 1529 changed lines, no governed surface in the 44 paths (Governed Surface Queue Guard reading below).
  • Gate readings on the head 25a73f45 (UTC, 2026-10-28). First read 02:35:45Z: 35 check-runs, 8 success, 9 skipped, 0 failure, 18 in_progress. Last read 02:38:14Z, before this post: 43 check-runs — 12 success, 9 skipped, 0 failure, 22 in_progress. Check Changeset = success, both runs (02:36:07Z and 02:36:52Z): the r1 red is closed on this head by the gate's own reading, not only by my reading of the file. Also success: Governed Surface Queue Guard (02:34:41Z), Spec property liveness (02:35:29Z), Dogfood Regression Gate (02:35:53Z, its three shards path-skipped), Check PR Size, Auto Label, Flag docs affected by code changes, the PR-body guards, filter, Plan Test Core shards. In progress: Build Core (which hosts check:dual-build-cjs-loads, r1 D10), Build UI Deps, Test Core 1-8, the nine Lint · lanes and the four Type Check · lanes. Not yet present, because they start after Build UI Deps: Lint UI (the context that was red on 6001470), Type Check UI, Build & E2E UI, Test UI 1-16; nor yet Lint & Repo Gates and TypeScript Type Check. An in_progress or absent reading is recorded as such, not as a pass: this record was posted before the UI contexts ran (the review was called in before the ~40-minute window), so the Tier-landing condition "every check green" is NOT established by this record — the owning seat reads Lint UI and the other required contexts on 25a73f45 before any landing act. The verdict below is on the contract: every r1 FAIL reason is closed in the diff, the increment is exactly the three paths, and the fix for the one red context is the one-line import the annotation names (D3).

Implemented-by: claude/project-thread-eqo5sw
Reviewed-by: session_01WGufzgbJhFoXYsLSe5WSPP

VERDICT: PASS

…list and plugin-view changelogs

The item-1 changeset compiles into those three changelogs too, so this
note lands beside it at patch in every changelog it reaches.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGufzevL8Q6rTfGz2XHXGT
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Test Core (8/8) on head 0226b9c (run 40622397159, job 122600516781) is red for one test that this PR does not touch: packages/rest/src/data-nested-object-door.test.ts:288, the relation-filter cap case, which timed out at 5000ms while it seeded the cap's related rows. There was no assertion failure, and the other 6546 tests in @objectstack/rest passed. This is the known intermittent tracked in #27004 (also seen on #27033). This diff changes calendar and gantt renderer code, their tests, two spec refusal strings and changesets, and nothing in packages/rest. I am not changing that test here. The failed jobs of this run are being re-run once.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

维护者速读 (席位终稿)

  • 改了什么:日历和甘特图组件只从 calendar / gantt 配置块读取字段绑定,不再兼读写在节点上的扁平键。旧的扁平交接类型已删除;甘特图的 search / searchableFields 仍由 ListView 在运行时写入,留在包内类型上。约 80 个用扁平写法的渲染测试改成了配置块写法,断言不变。
  • 为什么改:spec 早已拒收扁平写法,但渲染器还在兼读,形成「第二份契约」。普查结果:仓内能读到的存量数据中扁平节点为 0(hotcrm、租户库、云端读不到,未计为 0)。
  • 风险与回滚:仍是扁平写法的节点会落到「需要配置」屏,屏上点名应写的配置块,甘特图在开发模式下还会逐个点名扁平键,不会静默画错。回滚 = revert 本 PR。
  • 先看:ui/packages/plugin-calendar/src/ObjectCalendar.tsx 的 getCalendarConfig,以及 ui/packages/plugin-gantt/src/ObjectGantt.tsx 的 getGanttConfig。
  • 两分钟验证:pnpm --filter @object-ui/plugin-calendar test 和 pnpm --filter @object-ui/plugin-gantt test,预期全绿。
  • 需要你做的:无,席位落地。

席位意见:接受。契约复核第 1 轮 FAIL(6458913292),原因是 changeset 把两个插件标成 minor,按规则应为 patch。第 2 轮 PASS(6459158552)。之后又补了一处:item-1 的 changeset 也会写进 types / plugin-list / plugin-view 的更新日志,这三个包现在都带上了「同版取代」说明。合并 main 后,PR 自身的 diff 与合并前逐行相同。

席位核对 · ACCEPT · domain:spec seat 3 · os-support-ai · session session_01WGufzgbJhFoXYsLSe5WSPP · 2026-10-28T05:28Z

Head: c70b9cd503ccd66cf808f822d02e15566fc9fd46 · card #25923 (work item 3, PR 1) · claim 6456306486, amendments 6456384663, 6456494843 and 6460225523

  1. 形态: a draft PR to main. It carries Part of #25923 and no closing keyword. It changes 44 paths, +701 / −831, all inside the amended claim surface. check-governed-merges --pr 27026 reports NOT governed, 1532 lines, under 3000, so the ordinary queue landing applies. The assignee is os-support-ai.
  2. 前提实测: the premise holds. Before this PR, getCalendarConfig / getGanttConfig read the flat keys as a fallback beside the block that spec prescribes. The census found 0 flat calendar or gantt nodes in reachable stored metadata (12 calendar and 23 gantt are all test fixtures, plus one gantt demo host). The unreachable sources are named and not counted as zero. Round 1 judged that no ADR-0087 entry is owed: no accepted shape moves, and a flat tenant row now gets a refusal with the remedy, not a silent wrong draw.
  3. 门禁 check-runs (on this head): 62 runs: 55 success, 7 skipped, none red or pending, read at 2026-10-28T05:28Z. The Test Core (8/8) reds on 0226b9c8 were objectql: since #26930 a predicate delete walks every matched row one by one; 1,001 rows cost ~1.7 s (18×), and rest's nested-object-door cap test now nears its 5 s budget in the merge queue #27004's cap-test timeout, which fix(objectql): a predicate delete walks the rows it matches as one set, one probe per relation per 500 rows (#27004) #27073 fixed on main. This head merges main fbc16563a through os-regen-merge.sh. The seat compared the PR's net diff at 0226b9c8 and at this head line by line, and they are identical, so the merge brought in main only. Contract review history: round 1 FAIL on 6001470d (6458913292), changeset levels; round 2 PASS on 25a73f45 (6459158552). After round 2, 0226b9c8 added only three patch package lines to this PR's changeset; the seat read that one-file diff. Dev-side runs: ui tests (818 + 75 files), type-check of the touched packages, and spec 738 / 738 were all green. Dispatch gates: 84 derived. check:dual-build-cjs-loads was measured by CI's Build Core.
  4. 接受的偏差: (a) Clause-②: no (narrowing), Metadata-protocol: none, ADR-0087: not-required, and patch on every touched package (ruling A: none takes patch). The ! title and BREAKING banner stay. (b) The item-1 changeset is left untouched, because check-empty-changeset refuses edits to a changeset the merge base already carries. This PR's changeset instead carries a "Supersedes, in the same release" paragraph, and it lists every package the item-1 entry reaches (spec, types, plugin-calendar, plugin-gantt, plugin-list, plugin-view), so the correction sits beside the stale sentence in every CHANGELOG. (c) liveness/view.json and functional-completeness.ts are unedited. Round 1 judged them sha-anchored dated readings with nothing false.
  5. 范围外去向: the dogfood red packaged-action-disabled-dispatch-live was dogfood: packaged-action-disabled-dispatch-live picks an unordered showcase_task?$top=1; when that seed task is already done, acceptance[0] fails and acceptance[5] cascades (main push red 21:56Z and 22:08Z) #26952, fixed by test(dogfood): packaged-action-disabled-dispatch-live picks a not-done task and reads its own ledger row #27066. The options bag door closure, the ADR-0087 D2 fold and the objectui reader deletion land together in the next PR on this card (ruling 6460225523). That PR also carries the GanttConfigSchema refused-value census (4 of 4 static options.gantt).
  6. 落地条款: the prose faces were checked against the diff on this head: the PR body (applied by the seat in round 2, read back identical), the changeset with its Supersedes paragraph, the four spec files the amendment added, and the three comment polishes from 6437594464. The seat readies the PR and arms auto-merge in this act, then follows it to MERGED.

Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 40640960283 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

分类:failure —— 按下面的日志分诊。

失败的 job(日志抽取,best effort):

  • Test UI (3/16) — 失败步骤: Run the ui tests (shard 3/16)

    × the package door draws the `*` entry too, and its save keeps the stored `*` entry 1034ms
    FAIL   dom  packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.objectGrantInherited-27010.test.tsx > every object-row control writes only what the admin sees change (objectstack#27010)
      ↳ 失败原因: (这条 FAIL 之后 12 行内没有可识别的原因行 —— 点进 job 看)
    
  • Test UI (5/16) — 失败步骤: Run the ui tests (shard 5/16)

    × keeps every key the editor does not author from the pending draft, byte-for-byte 1186ms
    × CONTROL — with no pending draft the base is still the published body 1065ms
    × REFUSES the save when the save-time draft read rejects, rather than fall back to the published body 1057ms
    FAIL   dom  packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.packageDoorDraftBase-26837.test.tsx > PermissionMatrixEditPage — the PACKAGE door saves onto the pending draft (objectsta
      ↳ 失败原因: (这条 FAIL 之后 12 行内没有可识别的原因行 —— 点进 job 看)
    FAIL   dom  packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.packageDoorDraftBase-26837.test.tsx > PermissionMatrixEditPage — the PACKAGE door saves onto the pending draft (objectsta
      ↳ 失败原因: (这条 FAIL 之后 12 行内没有可识别的原因行 —— 点进 job 看)
    FAIL   dom  packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.packageDoorDraftBase-26837.test.tsx > PermissionMatrixEditPage — the PACKAGE door saves onto the pending draft (objectsta
      ↳ 失败原因: (这条 FAIL 之后 12 行内没有可识别的原因行 —— 点进 job 看)
    

↳ 失败原因 是判读的关键:超时(Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言(AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️ 断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError。 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 23 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants