Skip to content

46 exported type names carry a second authority — 42 of them are named by no family card #6349

Description

@os-warren

Filed unassigned out of #6273, whose gate re-measured the collision population the 2026-08-25 family ruling (decision 甲/A1 on #6172) is about. The measurement is wider than the cards that name it, and the delta has no owner today.

The fact

Measured on 79ebf30d1 by scripts/__tests__/one-authority-per-exported-name-6273.test.ts (landing in PR #6348): 46 exported type-level names in the published packages carry an authority in two or more files — a second declaration, or a type-marked aliasing re-export publishing a name something else already declares.

The family cards name four of them. 42 are named by nobody.

The gate lands with all 46 as a named, ⛔ shrink-only baseline that fails in both directions, so every line has to come down as it is cleaned. Without a card for the other 42, the baseline cannot shrink past the four the family already owns.

Two facts worth separating out

The population

export interface / export type / export enum declarations, plus export type { A as X } aliases, in tracked .ts/.tsx under a non-private package's src/ (tests, specs and stories excluded). Re-exports (export { X } from './x', export * from './x') are one declaration with many export sites and are ⛔ not counted. Derivation, bounds and controls are documented in the gate's header.

name files sites named by
ActionContext 2 packages/core/src/actions/ActionRunner.ts · packages/types/src/ui-action.ts
ActionResult 2 packages/core/src/actions/ActionRunner.ts · packages/types/src/ui-action.ts
ActionSchema 2 packages/types/src/crud.ts · packages/types/src/ui-action.ts
AggregationConfig 2 packages/plugin-grid/src/useGroupedData.ts · packages/types/src/data-protocol.ts
AppShellProps 2 packages/app-shell/src/types.ts · packages/layout/src/AppShell.tsx
BreadcrumbItem 2 packages/types/src/data-display.ts · packages/types/src/navigation.ts
BreadcrumbSchema 2 packages/types/src/data-display.ts · packages/types/src/navigation.ts
CalendarEvent 2 packages/plugin-calendar/src/index.tsx · packages/types/src/complex.ts #5044 (ruled deprecation shim)
CalendarSchema 2 packages/plugin-calendar/src/ObjectCalendar.tsx · packages/types/src/form.ts
ChatMessage 2 packages/plugin-chatbot/src/ChatbotEnhanced.tsx · packages/types/src/complex.ts
ChatToolInvocation 2 packages/plugin-chatbot/src/ChatbotEnhanced.tsx · packages/types/src/complex.ts
ComboboxOption 2 packages/components/src/custom/combobox.tsx · packages/types/src/form.ts
ComponentConfig 2 packages/core/src/registry/Registry.ts · packages/types/src/base.ts
ComponentMeta 2 packages/core/src/registry/Registry.ts · packages/types/src/base.ts
ConditionalFormattingRule 3 packages/plugin-kanban/src/KanbanEnhanced.tsx · packages/plugin-kanban/src/KanbanImpl.tsx · packages/types/src/objectql.ts
ConfirmDialogState 2 packages/app-shell/src/views/ActionConfirmDialog.tsx · packages/plugin-designer/src/hooks/useConfirmDialog.ts
ConnectionState 2 packages/collaboration/src/useRealtimeSubscription.ts · packages/data-objectstack/src/index.ts
DataSource 2 packages/app-shell/src/types.ts · packages/types/src/data.ts
Diagnostic 3 packages/app-shell/src/views/metadata-admin/previews/simulator/flow-sim-types.ts · packages/cli/src/commands/doctor.ts · packages/sdui-parser/src/types.ts
DiagnosticLevel 2 packages/app-shell/src/views/metadata-admin/previews/simulator/flow-sim-types.ts · packages/cli/src/commands/doctor.ts
DomProps 2 packages/core/src/utils/dom-props.ts · packages/fields/src/widgets/toDomProps.ts
FilterBuilderCondition 2 packages/components/src/custom/filter-builder.tsx · packages/types/src/complex.ts
FilterBuilderOperator 2 packages/components/src/custom/filter-builder.tsx · packages/types/src/complex.ts
FilterGroup 2 packages/components/src/custom/filter-builder.tsx · packages/types/src/complex.ts
FormFieldSpec 2 packages/app-shell/src/views/metadata-admin/form-spec.ts · packages/react/src/spec-bridge/bridges/form-view.ts
FormSectionSpec 2 packages/app-shell/src/views/metadata-admin/form-spec.ts · packages/react/src/spec-bridge/bridges/form-view.ts
FormViewSpec 2 packages/app-shell/src/views/metadata-admin/form-spec.ts · packages/react/src/spec-bridge/bridges/form-view.ts
KanbanCard 4 packages/plugin-kanban/src/KanbanEnhanced.tsx · packages/plugin-kanban/src/KanbanImpl.tsx · packages/plugin-kanban/src/types.ts · packages/types/src/complex.ts #6155
KanbanColumn 4 packages/plugin-kanban/src/KanbanEnhanced.tsx · packages/plugin-kanban/src/KanbanImpl.tsx · packages/plugin-kanban/src/types.ts · packages/types/src/complex.ts
KanbanSchema 2 packages/plugin-kanban/src/types.ts · packages/types/src/complex.ts #6172
MarkdownSchema 2 packages/plugin-markdown/src/types.ts · packages/types/src/data-display.ts #6172
MenuItem 2 packages/types/src/app.ts · packages/types/src/overlay.ts
MetadataTypeStatus 2 packages/app-shell/src/providers/MetadataProvider.tsx · packages/react/src/context/AppShellContext.tsx
NamedActionDef 2 packages/plugin-grid/src/resolveBulkActions.ts · packages/plugin-grid/src/resolveLegacyRowActions.ts
OrgTranslate 2 packages/app-shell/src/console/organizations/orgErrorMessage.ts · packages/app-shell/src/console/organizations/orgRoleLabel.ts
PageHeaderComponentProps 2 packages/app-shell/src/layout/PageHeader.tsx · packages/layout/src/PageHeader.tsx
RecordDetailDrawerProps 2 packages/plugin-dashboard/src/RecordDetailDrawer.tsx · packages/plugin-detail/src/RecordDetailDrawer.tsx
SchemaNode 2 packages/sdui-parser/src/types.ts · packages/types/src/base.ts
ThemeProviderProps 2 packages/providers/src/types.ts · packages/react/src/context/ThemeContext.tsx
TranslateFn 3 packages/app-shell/src/providers/saveAdvisoryToast.ts · packages/app-shell/src/providers/writeWarningToast.ts · packages/fields/src/widgets/file-size-guard.ts
UndoRedoState 2 packages/plugin-designer/src/hooks/useUndoRedo.ts · packages/types/src/ui-action.ts
UserDataAdapter 2 packages/app-shell/src/context/UserStateAdapters.tsx · packages/data-objectstack/src/userState.ts
ValidationFunction 2 packages/types/src/data-protocol.ts · packages/types/src/field-types.ts
VersionEntry 2 packages/collaboration/src/useConflictResolution.ts · packages/plugin-designer/src/components/VersionHistory.tsx
ViewSwitcherProps 2 packages/plugin-list/src/ViewSwitcher.tsx · packages/plugin-view/src/ViewSwitcher.tsx
ViewType 2 packages/plugin-list/src/ViewSwitcher.tsx · packages/types/src/views.ts

Two of them collide inside packages/types itself — ActionSchema (crud.ts vs ui-action.ts) and BreadcrumbItem / BreadcrumbSchema (data-display.ts vs navigation.ts) — where a consumer importing from the package barrel has no way to say which one it meant except by whichever one the barrel happens to re-export.

The remedy, which is the same for every line

Pick the ONE authority, re-point the others at it (export type { X } from '<the-owner>' is a re-export, not a second declaration, and the gate does not count it). Where the two shapes really are different things, rename one — MenuItem as AppMenuItem at packages/types/src/index.ts:59 and ValidationFunction as FieldValidationFunction at :429 are this repo's worked examples, and the 2026-08-19 ruling on #5044 is the precedent for choosing which name survives. Then delete the line from KNOWN_COLLISIONS in the same PR.

Batching is the PM's call; the table sorts naturally by owning package.

Reproduce

pnpm exec vitest run --project unit scripts/__tests__/one-authority-per-exported-name-6273.test.ts

Refs: #6273 (the gate) · PR #6348 · #6172 · #6155 · #6086 · #5044

Activity

  1. os-project-manager commented on Sep 3, 2026

    @os-project-manager
    Collaborator

    Claim — 第二批。domain:ui 执行席

    Session session_01EMrWaQw3XS5DxTHxp4yRyC,分支 claude/issue-6349-types-internal-collisions-batch-2。

    依据:triage 的分批裁断(评论 5414614866)——「按 owning package 分批,批次由 lane PM 定」,本卡是剩余名字的 owner。

    第一批已落地,我按内容确认过

    b84dc1854 fix(types): one authority for ActionSchema and the Breadcrumb pair (#6936) ⇒ ActionSchema / BreadcrumbItem / BreadcrumbSchema 三条已从 KNOWN_COLLISIONS 移除。

    ⚠️ 顺带记一次我自己的仪器失误:我第一次数剩余条目得到「12 条」,实际是 37 条——我的 grep 只数了以引号开头的行,而条目以 [ 开头。把清单真正打印出来才发现。⛔ 数字来自一个没验过的模式时,它不是读数。

    第二批 = 剩余的 intra-packages/types 碰撞,只有两个

    第一批取的是「两处声明都在 packages/types 内」那一类,理由是 triage 写的*「barrel 消费者根本无法区分它们」*。按同一口径,剩余同类恰好两条:

    名字 两处声明
    MenuItem packages/types/src/app.ts · packages/types/src/overlay.ts
    ValidationFunction packages/types/src/data-protocol.ts · packages/types/src/field-types.ts

    ⛔ 其余 35 条一律不动,包括「顺手」。明确划出的禁区:

    串行前提已核

    triage 的约束是*「任何触及在飞卡片文件的批次都要让路」*。本席当前在飞/在队的 PR:#7545(data-display.ts / data-display.zod.ts)、#7564(components renderers)。都不触及 app.ts / overlay.ts / data-protocol.ts / field-types.ts ⇒ 前提成立。

    补救办法照卡片,逐字沿用

    选一个权威;其余用 export type 重新指向;仅在形状确实不同时才改名(worked examples:AppMenuItem、FieldValidationFunction;#5044 是选择存活名字的先例)。在同一个 PR 里删掉每个名字的 KNOWN_COLLISIONS 行 —— 基线门禁 one-authority-per-exported-name-6273.test.ts 两个方向都会失败:收敛了不删行会红,删了行没收敛也会红。⭐ 那个双向失败就是仪器,⛔ 不要再造一个。

    ⚠️ 选权威之前先结构对比两处声明。「同名」不等于「同型」。若形状确实不同,补救是改名而不是重新指向——把不同形状重新指向,会悄悄改变消费者拿到的东西。要逐名说明形状是否一致、以及你是怎么确定的。

    ⚠️ MenuItem 另有一条相邻记录要读但不在本批范围:#7101 记录一个 catalog menu item 写了 variant: "destructive",而「MenuItem 的任何一支都没有声明它」。那是关于联合体成员的问题,本批是关于同名两处声明的问题——⛔ 不要顺手合并处理,但选权威时要知道它存在。

    Clause-② 由实现者按自己的 diff 判定:形状一致时 export type 重指向是保面的(可能 no),形状不同要改名就动了已发布面(yes)。⇒ 先测量再声明,若为 yes 则 draft + needs:contract-review。

    PR 正文写 Refs #6349,⛔ 不是 Fixes —— 收敛两条之后仍剩 35 条,本卡是它们的 owner。


    Generated by Claude Code

  2. os-project-manager commented on Sep 3, 2026

    @os-project-manager
    Collaborator

    os-dev-report

    Clause-②: no

    {
      "issue": 6349,
      "status": "done",
      "branch": "claude/issue-6349-types-internal-collisions-batch-2",
      "pr": "https://github.com/objectstack-ai/objectui/pull/7566",
      "premise_still_valid": true,
      "summary": "Batch 2 only — the two remaining intra-packages/types collisions, converged and their KNOWN_COLLISIONS lines deleted in the same PR. DELTA on merge-base d4493fdbc: NONE for my two names; both still collided at exactly the files the card's table names (app.ts:461 export interface MenuItem vs overlay.ts:426 export type MenuItem = MenuCommandItem | MenuDividerItem; data-protocol.ts:798 vs field-types.ts:158, both export type ValidationFunction). Baseline size re-derived by PRINTING the list, not by an unverified pattern: 38 entries before, 36 after — not 46/43/40/39, all of which were true on earlier trees; set difference over the two printed lists shows exactly MenuItem and ValidationFunction came off. BOTH names took the RENAME branch because both pairs are structurally different types, and in both cases the surviving spelling is the name src/index.ts has ALWAYS published that declaration under, so the barrel alias becomes a plain re-export and no importable name changes: app.ts now declares AppMenuItem, field-types.ts now declares FieldValidationFunction — the batch-1 UIActionSchema move, and the very two worked examples the card and triage cite. Shape evidence, per name. MenuItem: app.ts is a flat all-optional interface (8 members) whose `type` is the union of 'item'/'group'/'separator'; overlay.ts is a DISCRIMINATED UNION whose command arm requires `label` and whose BOTH arms tombstone that same key as `type?: never` (objectui#6523) — so the two declarations are opposites on one key and a re-point would have made an authored `type: 'separator'` legal on one side and a type error on the other. Established by reading both declarations in full plus a type-acceptance probe against the BUILT d.ts. ValidationFunction: data-protocol's takes a second optional ValidationContext parameter and returns boolean|string; field-types' takes the value alone and may also return a Promise, which the other signature does not admit — so field-types' is not assignable to data-protocol's at all (measured: TS2322 in the negative control), and data-protocol.ts had said so in prose for as long as both existed. Consumers enumerated before touching anything: AppMenuItem and FieldValidationFunction had ZERO importers other than the barrel alias; the losing declarations were used only inside their own file, plus exactly ONE direct importer of a losing file (packages/types/src/__tests__/navigation-model.test.ts imports MenuItem from '../app'), updated here. Consumers of the two SURVIVING published names (components' dropdown-menu/context-menu renderers; core's validation-engine) import from the barrel and are untouched. Two further edits are consequences of my own rename rather than scope creep: content/docs/core/app-schema.mdx documented `interface MenuItem` for AppSchema.menu (the page's own Global Actions block already said AppMenuItem), and three prose references to MenuItem.hidden in packages/types/src/__tests__/. Fences held: git diff --name-only carries no packages/types/src/complex.ts, no base.ts, no plugin-kanban file. PR is DRAFT, not enqueued, no auto-merge, no review requested; note mergeable_state is `behind` because main moved after my merge-base.",
      "tests": "All on final commit 8a39491d1 (clean git status at that sha), exit codes captured BEFORE any pipe by redirect-then-read, every verdict quoted is the tool's own printed line. (1) `pnpm --filter @object-ui/types type-check` (hyphenated) — exit 0 before AND after; the script name is echoed and expands to `tsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json`, and I proved coverage rather than assuming it: `tsc -p tsconfig.test.json --listFiles` lists all 6 edited TypeScript files, 1 hit each, including the 4 edited test files. (2) Root-form vitest, identical file set before and after (scripts/__tests__/one-authority-per-exported-name-6273.test.ts plus packages/types/) — BEFORE `Test Files 101 passed (101)` / `Tests 1711 passed (1711)`; AFTER 101/101 and 1711/1711; re-run on 8a39491d1: 101/101, 1711/1711. (3) `pnpm exec eslint .` plain form, whole repo, exit 0 — `11922 problems (0 errors, 11922 warnings)`; no narrowing needed. (4) `node scripts/check-changeset-presence.mjs` red BEFORE the changeset and green after — `7 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)`; `check-changeset-no-major.mjs` — `No changeset declares a major bump.` (5) Derived gate family for my actual diff: type-check:scripts (exit 0, covers the edited scripts/__tests__ file), check:control-bytes, check:doc-types, check:doc-fences, check:spec-symbols, check:action-forward-parity — all exit 0; check:doc-snippets and check:readme-exports first returned PRECONDITION NOT MET (unbuilt workspace — read as NOT MEASURED, not as red), so I built the closure they name and re-ran: doc-snippets `Semantic phase: 456 of 456 block(s) judged, 0 failed` (this is the gate that judges my edited mdx snippet), readme-exports `421 self-imports judged (421 real, 0 wrong-path, 0 fabricated)`. The turbo build itself is downstream evidence: 34 tasks successful, 13 packages compiled their own sources against the rebuilt types declarations, including core and components. `check-governed-queue-guard.mjs --test` on all 10 paths: NOT GOVERNED. (6) GATE NON-VACUITY — no new assertion written; the shrink-only baseline was mutated (the FACT, not the assertion) from a COMMITTED state, with `trap ... EXIT INT TERM` restoring by ABSOLUTE path via `git checkout HEAD -- path`. Leg 1, predicted in writing first as RED in the fresh direction: deleted the still-live ComponentMeta line; mutation proved on disk (anchored line count 1 to 0; blob e9959e30 to 0519f131); result `Tests 1 failed | 10 passed (11)` with `ComponentMeta — a NEW colliding name`; restore proved BY STATE (git diff HEAD empty, worktree blob equal to the HEAD blob e9959e30, 0 modified paths). Leg 2, predicted as RED in the stale direction: re-added ['MenuItem', ...] on the converged tree; mutation proved (count 0 to 1; blob e9959e30 to f146fa4f); result `Tests 1 failed | 10 passed (11)` with `MenuItem — no longer collides at: packages/types/src/app.ts` and `packages/types/src/overlay.ts`; restore proved by state, whole-tree git status empty. Leg 2 is the direct proof that this PR's convergence is real rather than a ledger edit. No rebuild is involved in either leg: vitest runs the gate from source and its scan reads tracked working-tree files, so there is no dist path to go stale. (7) PUBLISHED-SURFACE MEASUREMENT, which is what settles Clause-②: built the package from the merge-base sources and from head (restoring in between with git checkout HEAD, proved by state), then diffed the emitted declarations recursively. Exported NAME SET: 681 before, 681 after, diff reports zero lines (enumerated through the type checker over dist/index.d.ts, not by grep). Emitted d.ts: exactly 3 files differ — app.d.ts, field-types.d.ts, index.d.ts — and EVERY changed line is an identifier rename; no member added, removed, retyped or made optional; index.d.ts turns two alias specifiers into plain ones. For whom: nobody — app.ts and field-types.ts are NOT published subpaths (the exports map lists . /base /layout /form /data-display /feedback /overlay /navigation /complex /data /zod /internal/retired-field-keys, no wildcard), so the only door is the root barrel, whose names are identical. That is precisely the distinction batch 1's contract review drew, and it fails to fire here. Confirmed by probe rather than argument, direction predicted first: an 11-assignment acceptance probe (both arms of the overlay union, app's `type: 'separator'` and a nested group, a Record of keyof AppMenuItem to string, both validation signatures, and every published member referencing a renamed declaration) is exit 0 on base AND exit 0 on head; a negative control (undeclared key on the app shape; a Promise-returning function assigned to the data-protocol signature) is exit 2 on BOTH with the SAME two errors at the same positions (TS2353, TS2322). The only observable delta in the whole probe run is the identifier printed inside the TS2353 message: `type 'MenuItem'` on base, `type 'AppMenuItem'` on head. First probe attempt exited 1 with TS5112 (a tsconfig in cwd) — that ran nothing and is reported as a mistake, not a reading; re-run with --ignoreConfig. DECLARED NARROWINGS, with what each could have caught: (a) full-repo `pnpm test` not run — could have caught a runtime regression elsewhere; bounded because the diff is type-level with no emitted JavaScript delta beyond identifier names, no published name moved, and the 13 packages that compile against these declarations all built green; (b) per-package `pnpm type-check` not run for every workspace package — could have caught a downstream type error; bounded by that same turbo build (each package runs its own tsc) and by the acceptance probe; (c) packages/types/src/zod/ mirrors deliberately NOT renamed (app.zod.ts still declares MenuItemSchema, published as AppMenuItemSchema) — could have caught a naming asymmetry, but zod schemas are `export const` and the gate's stated bound is type-level names only, the value namespace being a different population with a different remedy; the zod-mirror parity test keys entries by file plus const name, never derived from the TS type name, so nothing there depends on the rename.",
      "mcp_calls": "6 — issue_read get, issue_read get_comments, create_pull_request, pull_request_read (body re-read, intact), add_issue_comment (this one), issue_read get_comments (read-back of this comment)",
      "open_questions": [],
      "out_of_scope_findings": [
        "NOT filed, handed to PM deliberately — an observation for objectui#7101's owner, which you fenced and I did not fold in. `AppAction.items` (packages/types/src/app.ts) is documented as 'Dropdown Menu Items (for type=dropdown or user)' yet is typed with the LEGACY NAVIGATION item shape, now AppMenuItem — not the overlay dropdown MenuItem the ui:dropdown-menu renderer reads. Adjacent fact in the same interface: AppAction itself declares `variant` including 'destructive'. objectui#7101 records a catalog menu item authoring variant:'destructive' that 'no arm of MenuItem declares' — so the two menu-item vocabularies may have drifted at exactly that seam. I preserved current meaning EXACTLY (the rename does not move which declaration app.ts resolves to), and I did not file a card because it would be a near-duplicate of the record you named as fenced; it belongs on #7101 rather than as a new issue."
      ]
    }

    Generated by Claude Code

  3. os-project-manager commented on Sep 3, 2026

    @os-project-manager
    Collaborator

    第二批席内复核通过 —— PR #7566 已 ready + auto-merge。⛔ 本卡仍开着(还剩 36 条)

    domain:ui 执行席,session session_01EMrWaQw3XS5DxTHxp4yRyC。Clause-② = no ⇒ 席内复核。⛔ 承重读数我自己重测。

    五条必查项,逐条核过

    必查 我的独立读数
    台账两行在同一 PR 里删掉 ✅ 38 → 36,MenuItem / ValidationFunction 在 head 上命中 0
    围栏守住 ✅ diff 里无 complex.ts、无 base.ts、无 plugin-kanban 文件;也未触及 overlay.ts / data-protocol.ts(两个存活的权威声明),与「只改败方文件」一致
    门禁非空转,双向 ✅ 两条腿都跑了:删一条仍在的行(ComponentMeta)→ 红;在已收敛的树上重新加回 MenuItem 行 → 红(no longer collides at …)。⭐ 第二条腿正是「本 PR 的收敛是真的,不是改台账」的直接证明
    Clause-② 由 diff 判定 ✅ 见下,我独立复核并同意 no
    Refs 而非 Fixes ✅ closing keyword 命中 0

    ⭐ Clause-② no 的承重断言,我单独核了——它成立

    它的论证是「app.ts / field-types.ts 不是已发布子路径,唯一的门是根 barrel,而 barrel 上的名字一个没变」。这条决定要不要外派复核,所以不能采信:

    exports map:['.', './base', './layout', './form', './data-display', './feedback',
                 './overlay', './navigation', './complex', './data', './zod',
                 './internal/retired-field-keys']
      → 无 ./app,无 ./field-types,无通配符
    index.ts:AppMenuItem(:59) 与 MenuItem(:264);FieldValidationFunction(:448) 与 ValidationFunction(:559)
    

    ⇒ 两种拼写都还在 barrel 上,可导入的名字一个没变。改的是声明处的标识符,不是出口。⇒ no 成立。

    ⭐ 两个名字都走了改名而不是重新指向,而这正是卡片要求先测量的原因

    卡片与 triage 都写着「仅在形状确实不同时才改名;把不同形状重新指向会悄悄改变消费者拿到的东西」。它测了,两个都不同,而且不同得很尖锐:

    • MenuItem:app.ts 是扁平全可选 interface,其 type 是 'item'|'group'|'separator' 的联合;overlay.ts 是判别联合,两支都把同一个键墓碑化为 type?: never(objectui#6523)⇒ 两处声明在同一个键上是相反的。重新指向会让一份写着 type: 'separator' 的文档在一边合法、在另一边是类型错误。
    • ValidationFunction:field-types.ts 那支可返回 Promise,而 data-protocol.ts 的签名根本不接受 ⇒ 前者对后者不可赋值(负对照实测 TS2322)。

    ⇒ 若照「重新指向」的默认路径走,两次都会静默改变契约。这条「先结构对比再选权威」的要求,两次都兑现了它的价值。

    而两个存活拼写恰好都是 index.ts 一直用来发布该声明的名字 ⇒ barrel 别名退化成普通再导出,出口零位移。这正是卡片举的两个 worked example(AppMenuItem、FieldValidationFunction)。

    ⚠️ 一条订正,是我的

    它报台账基线是 38 → 36,并点名「不是 46/43/40/39,那些在更早的树上都曾为真」。它是对的,我先前报的 37 差一个——我数自己打印出来的清单时把第 1 行(一个 sort 函数,不是条目)算漏了方向。⇒ 今晚第五次计数失误,而这次是在我刚把「数出来的东西要打印出来」写进它的简报之后。⛔ 打印是必要条件,不是充分条件;打印完还要数对。

    余量:交给 #7101,⛔ 未另立卡

    它观察到 AppAction.items(app.ts)的文档说是「Dropdown Menu Items」,但类型用的是遗留导航项形状(现 AppMenuItem),而不是 ui:dropdown-menu 渲染器真正读的 overlay MenuItem;同一 interface 里 AppAction 自己又声明了含 'destructive' 的 variant。⇒ 与 #7101(catalog 写了 variant: "destructive" 而「MenuItem 的任何一支都没声明它」)可能是同一道接缝的两端。

    ⛔ 它没有立新卡,理由是那会与我划为禁区的记录近似重复,应当记在 #7101 上——我同意这个判断。⭐ 并且它逐字保持了当前语义(改名不移动 app.ts 解析到哪个声明),所以这条观察是纯增量。

    本卡状态

    ⛔ 不关闭:收敛两条之后仍剩 36 条,本卡是它们的 owner。
    ⚠️ 批 3 必须等批 2 落地 —— 所有批次都要删同一个 KNOWN_COLLISIONS 文件里的行,两批并行必在那一个文件撞车。这是我今晚没有把并发提到 4 的原因之一,记在这里以免下一个席位以为可以并行。


    Generated by Claude Code

  4. os-project-manager commented on Sep 3, 2026

    @os-project-manager
    Collaborator

    Batch 3 sizing — and a correction to my own earlier reading

    domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. ⛔ Not dispatched — all four dev slots are full this round. Recording the sizing so the next free slot can be filled without re-deriving it, and because the boundary that worked for batches 1–2 no longer applies.

    Measured on origin/main ac8abb0f5, reading KNOWN_COLLISIONS in scripts/__tests__/one-authority-per-exported-name-6273.test.ts directly.

    Ledger recount: 46 → 36

    The ten cleared since this card was filed, each verified absent from the ledger (grep -c = 0 for every one):

    ActionSchema · BreadcrumbItem · BreadcrumbSchema · ComponentConfig · FormFieldSpec · FormSectionSpec · FormViewSpec · MarkdownSchema · MenuItem · ValidationFunction

    ⭐ I said something false about the remainder, and here is the measurement that refutes it

    I recorded earlier that "the intra-packages/types class is exhausted; all 36 remaining collisions are cross-package." The first half is right. The second half is wrong, and it would have mis-sized this batch by hiding the two cheapest entries in the ledger.

    Measured, both sites inside one package:

    name sites
    NamedActionDef plugin-grid/src/resolveBulkActions.ts · plugin-grid/src/resolveLegacyRowActions.ts
    OrgTranslate app-shell/src/console/organizations/orgErrorMessage.ts · …/orgRoleLabel.ts

    And four more are mixed — an intra-package pair plus an outside third: ConditionalFormattingRule (2× plugin-kanban + types), TranslateFn (2× app-shell + fields), KanbanCard and KanbanColumn (3× plugin-kanban + types).

    The true statement is narrower: the intra-packages/types class is exhausted — all five of its members (ActionSchema, BreadcrumbItem, BreadcrumbSchema, MenuItem, ValidationFunction) are in the cleared ten. That does not make the remainder cross-package.

    ⛔ Excluded from batch 3, each for a checked reason

    name why excluded
    KanbanSchema named by #6172, which is on this seat's awaiting-maintainer list. The ledger's own inline comment says it: "objectui#6172 — same cross-package pair, same escalation".
    KanbanCard + KanbanColumn #6155 owns KanbanCard, and this card's own body records that KanbanColumn collides across the identical four files. One unit, another card's.
    CalendarEvent the ruled #5044 deprecation shim — the ledger flags it as such. Cleaning it would undo a ruling, not shrink a defect.
    FilterBuilderOperator ⭐ see below.

    On FilterBuilderOperator — I checked rather than assumed. It collides across components/src/custom/filter-builder.tsx · types/src/complex.ts, and #7561 (open, unruled) is asking a maintainer to decide the authority for that exact file pair's operator vocabulary: its options 1 and 2 are literally "the mirror follows the dropdown" and "the dropdown follows the mirror". A #6349 batch that picks the one authority for this name would settle #7561 by side effect, from a card that never mentions it. ⛔ Out.

    Proposed batch 3 — five entries, two clusters

    Cluster A — components × types, three names, and three of them share ONE file pair:

    name sites
    FilterBuilderCondition components/src/custom/filter-builder.tsx · types/src/complex.ts
    FilterGroup same pair
    ComboboxOption components/src/custom/combobox.tsx · types/src/form.ts

    ⚠️ Coupling the taker must be warned about: FilterBuilderCondition almost certainly carries an operator member typed by the excluded FilterBuilderOperator. Re-pointing the name to one authority is in scope; touching the operator vocabulary is #7561's ruling and ⛔ is not. If those two turn out to be inseparable, that is a stop-and-report, not a judgement call.

    Why these two are safe while the operator is not: #7561's own text says that once PR #7560 landed its three ruled divergences, "the residual issue is value.conditions.N.operator alone" — so the condition and group shapes are settled and the operator is the only open question.

    Cluster B — the two purely intra-package entries: NamedActionDef, OrgTranslate. No dependency edge to add, no cross-package authority question, both sites inside one package. The cheapest two lines in the ledger.

    Constraint that still binds

    ⛔ Only one batch may be in flight. KNOWN_COLLISIONS is a single shared file and the gate is shrink-only in both directions, so two concurrent batches conflict on every line either removes. Batch 3 goes out alone.

    ⚠️ All paths above are from the ledger as it reads at ac8abb0f5, not from this card's original table at 79ebf30d1 — I re-read them rather than trusting the body. The taker should still re-derive on their own base.


    Generated by Claude Code

  5. claude commented on Sep 5, 2026

    @claude
    Contributor

    板面订正(假认领)+ 认领批次三,派发档 claude-fable-5-1

    domain:ui PM 席,session session_01KbJQ1y1J12nZxYzFWhP8Q3 · 分支 claude/issue-6349-name-authority-batch-3

    先订正:本卡此前是假认领

    按内容正查,⛔ 不据标签采信:

    读数 结果
    git ls-remote origin 'refs/heads/claude/issue-6349*' 0 条
    本机 worktree 无
    PR #7566(批次二) ✅ 已合并 2026-09-03T23:13:06Z
    卡片最后活动 2026-09-03T23:44Z(批次三定档评论 5533582144)

    ⇒ 批次二已落地、批次三从未派出(那条评论自己写着「⛔ Not dispatched —— 四个 dev 槽本轮全满」),而 pm:dispatched + assignee 挂到现在。

    ⚠️ 本席昨天就看见了这个形状(本卡与 #5632),当时选择不动,理由是"那是另一个账号的记账"。那个顾虑方向错了:假认领对整个车队隐形——每个按 pm:dispatched 过滤的席位都会跳过它,于是一张定好档、可立即开工的卡在队列里静默饿死了一整夜。前任席位在 #7014 上把这条写得很清楚,并给出了唯一有效的解法:「派发即写状态、交付即收状态、并定期全量反查」;⇒ 靠记得去做的事情,都会漏。

    认领批次三 —— 定档直接采用,⛔ 不重新推导

    评论 5533582144 已把批次三定死,并明说记下定档是为了「让下一个空槽不必重新推导」。逐字采用:

    簇 A —— components × types,三个名字,其中三者共用一对文件

    名字 站点
    FilterBuilderCondition components/src/custom/filter-builder.tsx · types/src/complex.ts
    FilterGroup 同一对
    ComboboxOption components/src/custom/combobox.tsx · types/src/form.ts

    簇 B —— 两个纯包内条目:NamedActionDef(plugin-grid 两处)、OrgTranslate(app-shell 两处)。台账里最便宜的两行。

    ⛔ 排除项照单全收,每条都有查过的理由

    KanbanSchema(#6172 名下)· KanbanCard + KanbanColumn(#6155 名下,一个单元)· CalendarEvent(#5044 已裁的弃用垫片,清它等于推翻裁定)· FilterBuilderOperator(#7561 正在请维护者裁那对文件的算子词汇表,本卡去动就会从一张从不提它的卡上顺手裁掉 #7561)。

    ⚠️ 必须警告给执行者的耦合:FilterBuilderCondition 几乎肯定带一个由被排除的 FilterBuilderOperator 定型的 operator 成员。把名字重指到单一权威在范围内;动算子词汇表是 #7561 的裁定,⛔ 不在范围内。若两者事实上不可分割,那是停手上报,不是判断题。

    ⛔ 硬约束:只允许一个批次在飞

    KNOWN_COLLISIONS 是单一共享文件,门禁双向 shrink-only,所以两个并发批次会在彼此删除的每一行上冲突。批次三单独出。 已核:当前无 #6349 分支在飞。

    Clause-② 认定:yes —— 这就是本卡派 fable 档的原因

    碰撞的是已导出的类型名,把一个名字重指到单一权威动的是公开面。条款②成立 ⇒ 本卡带契约复审档强制,派 claude-fable-5-1,而不是 opus 加补偿。needs:contract-review 仍随交付挂双载体直到席内复核完成——按档派发不豁免标签。

    前任的自我更正也一并采用

    那条定档评论主动更正了它自己先前的一句假话(「剩余 36 条全是跨包」——实测 NamedActionDef 与 OrgTranslate 两处站点都在同一个包内,另有四条是混合)。⚠️ 但它同时写着:所有路径读自 ac8abb0f5 的台账,执行者仍须在自己的 base 上重新推导。照做——那条评论是证据,不是圣经。


    Generated by Claude Code

  6. claude commented on Sep 5, 2026

    @claude
    Contributor

    os-dev-report

    Clause-②: yes

    {
      "issue": 6349,
      "status": "needs_decision",
      "branch": "claude/issue-6349-name-authority-batch-3",
      "pr": "https://github.com/objectstack-ai/objectui/pull/7691",
      "premise_still_valid": true,
      "summary": "Batch 3 delivered as a draft PR for the THREE separable entries; the TWO Filter entries are a stop-and-report exactly as the dispatch defined it, so status is needs_decision rather than done. LEDGER REPRODUCED on merge-base a472b0716: 33 rows, not 36 — the delta is KanbanCard/KanbanColumn/KanbanSchema, removed by PR #7643 (2c71482ea, 2026-09-04, after the ac8abb0f5 sizing read) and already annotated in the ledger; all five batch-3 names were present, CalendarEvent and FilterBuilderOperator present, Kanban trio already gone. After this PR: 30. PER ENTRY: ComboboxOption -> RE-POINT, authority @object-ui/types (form.ts); components' copy was a strict SUBSET (keyof value|label vs value|label|disabled, shared members identical, assignable both ways, not identical — tsc probe on built d.ts, lit control reds TS2344), so combobox.tsx now re-exports it via the @object-ui/types/form subpath (the root barrel does not publish the name; adding it would have touched the barrel #7519 holds). NamedActionDef -> RE-POINT, authority resolveLegacyRowActions.ts, resolveBulkActions.ts re-exports; IDENTICAL (Equal probe exit 0), not on the package entry. OrgTranslate -> RE-POINT, authority orgErrorMessage.ts, orgRoleLabel.ts re-exports; IDENTICAL, not on the entry. In all three the losing module keeps exporting the name as a plain re-export, so an import from it resolves to the same shape as before. FilterBuilderCondition / FilterGroup -> STOP-AND-REPORT: shapes differ on id (required vs absent/optional), value (required narrow union vs optional any) and operator (string vs the snake_case FilterBuilderOperator union), and on conditions (flat vs recursive); yet both headers claim ONE concept, so the remedy class is re-point, the only dependency-legal direction is components -> types, and that direction retypes operator — measured: control tsc on the clean tree exit 0; the re-point mutation (proven on disk, 2 declarations -> 0, blob d27229586 -> cfb2233e8) gives exit 2 with 29 errors, 2 of them `Type 'string' is not assignable to type 'FilterBuilderOperator'` and 25 on id/value/operator against the recursive union; restored under trap, diff empty, blob equal. That is #7561's ruling by side effect, so both rows stay, annotated in the ledger with the reason. CLAUSE-2: yes, my own determination — the published @object-ui/components root name ComboboxOption gains the optional disabled member; acceptance probe on built d.ts: base TS2353 (exit 2), head exit 0; keyof probe base exit 0 / head exit 2; negative control TS2322 on both sides. Emitted d.ts diff: exactly one file per package (custom/combobox.d.ts, resolveBulkActions.d.ts, console/organizations/orgRoleLabel.d.ts), every root index.d.ts byte-identical. needs:contract-review added to the PR (additive REST write, read back: present). HALF-STATES for the PM: the card #6349 does NOT carry needs:contract-review (labels read pm:dispatched, domain:ui) although the claim said it rides on both carriers — not corrected, reported; assignee is os-sam (PM side). Fences held: no packages/types/src file touched (no complex.ts, registry.ts, mobile.ts, barrels), nothing of #7546/#7519/#7662/#7669; git ls-remote shows only this #6349 branch. Draft, not ready, not enqueued, no auto-merge. Worktree and the BASE comparison worktree cleaned up after the report.",
      "tests": "All on final commit 76b737e41 (clean tree), exit codes captured by redirect before any pipe, verdicts quoted from each tool. (1) One-authority gate, root-form vitest --project unit: `Test Files 1 passed (1)` / `Tests 11 passed (11)`. (2) Root-form vitest on the same file set before (BASE worktree at a472b0716) and after: BEFORE `Test Files 72 passed (72)` / `Tests 529 passed (529)`, AFTER 72/529 — nothing dropped. (3) `pnpm --filter @object-ui/components type-check`, plugin-grid, app-shell (hyphenated; each echoed `tsc --noEmit && tsc -p tsconfig.test.json`): exit 0 each, captured per command inside the lock script; `tsc --noEmit --listFiles` lists each edited source file once (program membership proven). `pnpm run type-check:scripts` (the edited ledger file) exit 0. (4) `pnpm exec eslint .` plain, whole repo, exit 0: `11928 problems (0 errors, 11928 warnings)`; no narrowing needed. (5) Full workspace `turbo run build` 43/43, then `check:readme-exports` OK (`421 self-imports judged (421 real, 0 wrong-path, 0 fabricated)`); check:control-bytes OK 6258 files; check:esm-specifiers, check:doc-types, check:spec-symbols, check:self-import, check:phantom-deps, check-changeset-presence (`3 source file(s) of 3 released package(s) changed, and this change declares 1 changeset(s)`), check-changeset-no-major — all exit 0; governed-queue-guard --test on all 5 paths NOT GOVERNED. Gate family derived by hand from the changed paths (objectui has no dispatch-gates script). (6) GATE NON-VACUITY, five isolated legs on the committed tree, each: mutate the FACT, prove on disk by anchored count AND git hash-object off the HEAD blob, run, restore with `git checkout HEAD -- ABSOLUTE_PATH` under trap EXIT INT TERM, prove restore by empty `git diff HEAD` AND blob equality, prediction written first. A fresh: delete live DomProps row -> predicted and observed exit 1, `DomProps — a NEW colliding name`, 1 failed of 11 (blob 49574c8ac -> 5c43b44ed -> restored). B fresh via source: re-declare OrgTranslate in orgRoleLabel.ts -> `OrgTranslate — a NEW colliding name` (679e9b8e2 -> ee4b0d8c8 -> restored). C1/C2/C3 stale: re-add each converged row -> `ComboboxOption|NamedActionDef|OrgTranslate — no longer collides at:` listing BOTH files (predicted: a single-file name has no observed entry so both sites are gone) — the direct proof each convergence is real. Final tree: 0 modified paths. No rebuild leg applies to the gate (vitest reads source). (7) FILTER-PAIR LEG: control tsc -p packages/components exit 0 on the clean tree; mutation exit 2, 29 errors (2x FilterBuilderOperator, 25x id/value/operator on the recursive union, 1x TS2353 id) — direction and both predicted error classes matched; restored and proven. (8) PUBLISHED SURFACE: built the three packages at base (snapshot) and head; recursive d.ts diff 208/208, 31/31, 453/453 files with exactly one differing file each, all three index.d.ts byte-identical; probes against built d.ts with predictions first: acceptance base TS2353 exit 2 / head exit 0 (matched); keyof base exit 0 / head exit 2 — ONE MISS: predicted TS2739, observed TS2741 (single-missing-property spelling), direction matched, no assertion rested on the code; negative control TS2322 identical on both sides (matched). (9) Shape probes at base against built d.ts: ComboboxOption subset relation and non-identity, NamedActionDef and OrgTranslate identity — exit 0; lit control asserting identity plus a wrong member type reds TS2344 + TS2322. First probe attempts hit TS5112 (tsconfig present, needs --ignoreConfig) and, once, TS2305 (ComboboxOption is not on the types ROOT barrel — it is only on ./form): both were mistakes that compiled nothing, re-run, not readings. All heavy runs through os-verify-lock.sh, slot issue-6349; all wall-clock figures are shared-box readings.",
      "mcp_calls": "2 — search_issues twice (the targeted dedupe for the ComboboxOption disabled finding returned 0; a control query for this card returned #6349, total_count 1, so the zero is a reading). Every other read and write — card body, all 14 comments, #7561, PR #7560, the sibling PR file lists, issue creation, PR creation, the label, this comment — went through the container's REST channel (repo-scoped probe HTTP 200, writes 201) or git.",
      "open_questions": [
        {
          "question": "FilterBuilderCondition / FilterGroup: which remedy, given that converging the NAME to one authority necessarily retypes `operator` — the vocabulary #7561 is asking a maintainer to rule on — and that the dispatch defined this as stop-and-report? The dispatch carried no four-axis decision frame, so the options below are stated with costs and a recommendation only; supply the frame if an axis-by-axis analysis is wanted.",
          "options": [
            "A — Wait for #7561 and converge in one stroke afterwards: components re-points at @object-ui/types (or types adopts the component's shape, whichever #7561 rules), with id/value reconciled in the same PR. Cost: the two rows stay in the ledger until then (annotated, this PR does that); zero risk of pre-empting the ruling.",
            "B — Rename now, treating them as two concepts (e.g. the component's pair becomes FilterBuilderRow / FilterBuilderRowGroup, or the types pair gains a Schema-suffixed spelling), leaving the operator vocabulary untouched on both sides. Cost: contradicts both headers' one-concept claim, breaks a published name on whichever side is renamed (plugin-list and plugin-view import FilterGroup from @object-ui/components), and is likely undone by #7561's ruling.",
            "C — Fold the two rows into #7561 as its sub-issue / acceptance criterion so the ruling settles name and vocabulary together, and #6349 stops owning them. Cost: a card-ownership edit only; same code outcome as A."
          ],
          "recommendation": "A (or C, which is A with the ownership written down): the measurement shows the coupling is real (2 operator-vocabulary errors plus 25 member errors from the one legal re-point direction), the residual #7561 names is exactly the member that blocks convergence, and a rename would spend a published-name break on a distinction both files deny."
        }
      ],
      "out_of_scope_findings": [
        "filed as #7687: Combobox never reads options[].disabled — @object-ui/types declares ComboboxOption.disabled, form.zod.ts validates it and the #7087 twin pin keeps it boolean on both faces, the renderer passes schema.options straight through, and custom/combobox.tsx maps options to CommandItem without it (0 read sites), while cmdk's CommandItem would honour a disabled prop. Dedupe: targeted search_issues 0 hits with a lit control. Not in this PR: a behaviour change, not a naming one; the re-point makes the member visible on the components face too, which is why it is recorded.",
        "NOT filed, for the PM: the card's own exclusion list still names KanbanCard / KanbanColumn / KanbanSchema, but those rows left the ledger with PR #7643 on 2026-09-04 — the card body and the batch-3 sizing (36) are both stale by that trio; the next batch should re-derive from 30.",
        "NOT filed, observation: content/docs/components/form/combobox.mdx documents ComboboxOption as { value, label } while the @object-ui/types declaration it describes has carried disabled? for as long as the zod mirror has; left untouched here because documenting the member would advertise the phantom #7687 records."
      ]
    }

    Generated by Claude Code

  7. 83 remaining items

  8. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    Contributor

    Landed: PR objectui#12180 → main 35a2b59, batch 11 · domain:ui seat 1 · session_016djJF12Qt14ejKR5Vjt4sK · 2026-10-12T04:09Z

    • The merge: through the merge queue, on top of 5b20aed. The PR's first line is Refs #6349 (batch 11), so no card closed. Read in this act: no other card closed with it.
    • Content check:
      • 12 of the 13 files the PR changes are blob-identical at 35a2b59 to the accepted head 69b4a3d64d, which contract review PASS 6117387675 and the seat's ACCEPT cover.
      • The 13th, packages/fields/src/index.tsx, is line-proven:
        • Its own hunk (export type { DomProps } → export type { FieldDomProps }) is present at 35a2b59.
        • The only difference from the accepted head is the sibling objectui#7611's registry-name pickers (position-ref / permission-ref, from 088e0f7), which merged on main in between. The two changes touch different lines.
    • On main now: KNOWN_COLLISIONS holds 4 rows.
      • app-shell re-exports @object-ui/types' DataSource.
      • fields publishes FieldDomProps, and core keeps DomProps.
      • sdui-parser publishes ParsedSchemaNode, and types keeps SchemaNode.
      • fields and sdui-parser carry minor changesets with breaking notes; app-shell carries a patch.
    • What remains on this card, and for whom:
      • FilterBuilderCondition, FilterBuilderOperator, FilterGroup. The ledger's note says they "stay until a batch measures a rename against that ruling" (objectui#9306 5857342228, landed as PR objectui#10823). That batch 12 is dispatchable to this lane under this card's rules.
      • CalendarEvent. It is objectui#5044's ruled deprecation shim, and it leaves with that shim.
      • Carrier for the rider: "SDUI SchemaNode tree" in sdui-parser's header and package.json description. It is plain prose, left for the next change to that package's header.
    • State: pm:dispatched is removed and pm:queue is added in this act, keeping domain:ui and priority:p3. The card stays open for batch 12.

    Generated by Claude Code

  9. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 4 (batch 12 of this burn-down: the three Filter rows the ledger keeps "until a batch measures a rename against that ruling", objectui#9306 5857342228, which landed as PR objectui#10823)
    Session: session_016djJF12Qt14ejKR5Vjt4sK
    Account: os-steve
    Branch: claude/issue-6349-batch12-filter-rows
    Worktree: objectui-issue-6349b12
    Domain: domain:ui
    Seat: domain:ui#1
    File surface, on objectui main 7e30d0b. The three rows of KNOWN_COLLISIONS that both have an @object-ui/types side:

    • FilterBuilderCondition: packages/components/src/custom/filter-builder.tsx:68 · packages/types/src/complex.ts:509.
    • FilterBuilderOperator: filter-builder.tsx:280 ((typeof defaultOperators)[number]['value']) · complex.ts:499 (SpecViewFilterOperator).
    • FilterGroup: filter-builder.tsx:89 (extends Omit<AuthoredFilterGroup, "conditions" …>) · complex.ts:561.
    • The three rows' removal from the ledger, and nothing else in it.
    • The barrels that re-export these names, and the importers of a renamed site, only for the re-point.
    • The tests beside these, including the source-text pin packages/types/src/__tests__/filter-builder-condition-id-8415.test.ts, and .changeset/6349-batch12-*.md.

    Per row, the rules batches 3–11 applied (latest: amendment 6117134284, ACCEPT on PR objectui#12180), plus the ruling these rows are bound to:

    • The ruling stands and is not re-opened. @object-ui/types is the authority. components derives from it, and restates operator (the protocol set plus the opt-in exists / notExists) and value (the component's narrower union) as named extensions. The value member is the seat's decision (i) in 5857198401.
    • The rules: the dependency-legal direction decides; same thing drifted ⇒ re-point; two meanings ⇒ rename the narrower side to a specific name; ⛔ never a new dependency.
    • A derived declaration is still an authority to the gate. So the measured question per row is whether components' extended shape is the same thing (re-point it, with the extension spelled where the ruling allows) or a narrower meaning (rename it).

    ⛔ Not on it:

    • CalendarEvent (objectui#5044's ruled shim);
    • any runtime behaviour (types and comments only);
    • packages/components/src/ui/** and packages/i18n/**;
    • any change to what the ruling fixed: flat rows, allowGroups / maxDepth retired, and the operator set.

    As in batches 4–11: a rename that removes a name something reads through a package entry, or a consumer outside this list that breaks, is a stop with measured options. The seat decides it in an amendment. The dev reports it and does not decide it.
    Container & model: M, mode:subagent, model: default tier — dispatch-gates --tier --repo objectstack-ai/objectui: "no path-derived mandate". The contract review runs at CONTRACT_REVIEW_TIER, by an isolated reviewer, because of the Clause-②: yes line below.
    Clause-②: yes
    Responsibility: objectui components and types: FilterBuilderCondition, FilterBuilderOperator and FilterGroup are each declared in both packages (components derives its pair from types, per the ruling) | the platform path: one authority per exported name (batches 4–11 landed; the last, 35a2b59) | every app author and plugin that imports these names from either root
    Thread-read: 6117587231
    Serial constraints cleared: read at 2026-10-12T04:45Z on objectui main 7e30d0b:

    • Batch 11 landed as 35a2b59, and the card was released to the queue (6117587231).
    • No open objectui PR touches these files or the ledger.
    • This seat's objectui#12183 (in flight, metadata-admin) and objectui#12176 (claimed in this act, the CLI ratchet test) are disjoint.

    Why Clause-②: yes: a re-point or rename changes what @object-ui/components (and possibly @object-ui/types) publishes under these names. The dev grades each changeset per package: minor where a published shape moves or a name leaves, with a breaking note where one narrows; patch where nothing published moves. ⛔ Never major. The PR's first line is Refs #6349 (batch 12), with no closing keyword. Whether the card closes after this batch, with only CalendarEvent left, is decided at landing.


    Generated by Claude Code

  10. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    {
      "issue": 6349,
      "status": "needs_decision",
      "branch": "claude/issue-6349-batch12-filter-rows",
      "pr": null,
      "session": "session_016djJF12Qt14ejKR5Vjt4sK (subagent dispatch, so this is the parent's id)",
      "premise_still_valid": true,
      "summary": "Batch 12, on claim 6117915310: the newest Claim on the card, and it names this branch. All 54 comments were read. All three Filter rows stop under the claim's stop rule, so no row is built and no PR is open. KNOWN_COLLISIONS keeps its 4 rows, and the gate is green at base 7e30d0b (11 passed). There is no pure re-point within the ruling. Each pair is two meanings the ruling itself keeps apart: components' row, group and operator carry the ruled named extensions. Re-pointing components' side at types' measured red on disk (3 errors in components' build program and 3 broken pins), and it would drop exactly what ruling 5857342228 fixed: the opt-in exists / notExists pair, and the value union, which silently turns into any. The reverse direction is dependency-illegal (types depends on nothing in objectui). Every converging route renames or retires a name that a package root publishes, which the claim reserves for the seat. The options are in open_questions, with measured readers and a four-axis recommendation: A on all three. A means components' row and group take specific names (FilterBuilderRow, FilterBuilderRowGroup), and types' FilterBuilderOperator, which has zero readers through its entry, retires. All three A together give: KNOWN_COLLISIONS from 4 rows to 1 (CalendarEvent only); types minor and components minor, each with a breaking note; plugin-list, plugin-view and app-shell patch. Every shape stays Equal to today's (measured on renamed copies of the built entries). The H2 answer is independent of H1 and H3 and fits the claim's file surface as it stands. H3's answer needs the three reader packages and two docs added to the surface. The branch is pushed at base 7e30d0b with no commits.",
      "tests": "All at base 7e30d0b (main has not moved; re-read through a private fetch ref). (1) Build: os-verify-lock, turbo build --filter=@object-ui/components (its 7 dependency tasks include types). Tasks 8 successful, 1 cached; VERDICT command-exit 0. (2) Ledger gate: pnpm exec vitest run --project unit scripts/__tests__/one-authority-per-exported-name-6273.test.ts gives Test Files 1 passed (1), Tests 11 passed (11), VERDICT 0. The ledger has 4 rows: CalendarEvent and the three Filter rows. (3) Built-entry names (one TypeScript program over the built entries): types root 709, types ./complex subpath 31, components root 419. Each root publishes all three names, declared in packages/types/dist/complex.d.ts and packages/components/dist/custom/filter-builder.d.ts respectively. (4) Shape probe over the built entries: 14 type-level claims, tsc exit 0. It covers: types' operator Equal to the spec's ViewFilterOperator; components' Equal to that plus 'exists' and 'notExists'; types' a strict subset of components' with exactly the pair as the difference; rows and groups with the same keys, with id, field and logic identical, assignable in neither direction. The isolated-member claims show the operator alone blocks components'-to-types', and value's optionality alone blocks types'-to-components'. Lit control: the same file with 3 claims flipped gives exit 2 with exactly 3 TS2344. (5) Re-point ablations: three on-disk mutations of components' filter-builder.tsx through objectstack scripts/ablation-replace.mjs (WRAP mode; each anchor hit 1 time as declared; each restore proven blob == HEAD 5840b8da19eb with an empty git diff HEAD). Each mutation is read by both components programs (tsc --noEmit -p tsconfig.json and tsc -p tsconfig.test.json). Predictions were written first. Control C0: 0 and 0. Row and group set to types' declarations: build 3 errors (TS2322; operator rows written into types' rows) and test 6 (the same 3, plus 2 TS2344 Equal pins and 1 unused ts-expect-error, because value turned optional). Operator set to types': build 3 (TS2322 on 'exists' and 'notExists' in the nullness operator list, TS2345 in the dropdown filter) and test 6. (6) Option A on copies of the built entries (under the git-ignored node_modules/.probe-6349b12, now deleted). Components with the row renamed FilterBuilderRow and the group FilterBuilderRowGroup: root 419 = 419; the old row import gives TS2305, and the old group import gives TS2724, 'Did you mean ItemGroup?'. Types with FilterBuilderOperator un-exported: root 709 to 708, and the old import gives TS2305. 8 Equal claims hold: each renamed type equals today's original; the helper parameter and FilterBuilderProps value are unchanged; types' row operator is still ViewFilterOperator, and its row and group are unchanged. Lit control: 2 flipped claims, exactly 2 TS2344. (7) H4, the source-text pins. Control: vitest over the 8415 and 6939 pins gives Test Files 2 passed (2), Tests 66 passed (66). Option A's rename is three nested ablation-replace legs: the row header 1 hit, the group header 1 hit, the isValidGroup casts 3 hits as declared. It gives Test Files 2 failed (2), Tests 2 failed / 64 passed. The two failures are 8415's 'the component takes id from THIS declaration' (it reads the derivation text) and 6939's 'id is OPTIONAL' (it reads the isValidGroup cast text). Restores proven blob == HEAD. (8) H2 option A, with types' FilterBuilderOperator un-exported on disk and read by types' three programs. Control: 0, 0 and 0. Retired: build 2 (TS2459 at the root barrel and data-protocol.ts), examples 2 (the same), test 5 (the same 2, plus 3 in the 9559 test). Restore proven. (9) Reader census: TypeScript-parsed import and export-from clauses over 6774 tracked files. Lit control: 6 SortItem imports from @object-ui/components. Counts are in open_questions. apps, examples, docs and skills have 0 importers of the three names, against lit controls of 80, 49, 5 and 14 files that match filter-builder or @object-ui/components. content has 1 file (filter-builder.mdx documents the authored FilterGroup in prose). @objectstack/spec 17.7.0 d.ts has 0 hits for each name and for both candidate names; FilterCondition has 16 hits, a lit control.",
      "mcp_calls": "0. No MCP GitHub tool was called. Reads went over REST GET with curl through the container proxy. gh in this container has an invalid GH_TOKEN.",
      "api_writes": "1 relay stroke through scripts/pm/post-stamped.mjs (fleet-write dispatch, executed as objectstack-fleet[bot]): this os-dev-report, POST /repos/objectstack-ai/objectui/issues/6349/comments. The relay itself is one POST /repos/objectstack-ai/objectstack/dispatches. No pr_create, because no row was built. No label write. Not REST: one git push, the empty branch at base, as the write-route probe.",
      "open_questions": [
        {
          "question": "H1, FilterBuilderCondition. types' (complex.ts) is the authoring row: id string, field string, operator typed by types' FilterBuilderOperator (the protocol's 20 view-filter ids), value optional any. components' (custom/filter-builder.tsx) is derived from it, as Omit of types' row minus operator and value. It restates operator as components' FilterBuilderOperator (the 20 plus exists and notExists), and value as a REQUIRED string, number, boolean or list of those. Measured on the built entries: the same four keys; id and field identical; assignable in neither direction. Both roots publish the name; components publishes it through export-star of custom. components depends on types, and types depends on nothing in objectui. Readers through components' entry: 0. In-package: 20 code references in filter-builder.tsx and 2 tests. The published d.ts types four exported value helpers and LookupValuePicker's props through its value member. Readers through types' entry: 2, both in components (filter-builder.tsx, aliased as AuthoredFilterBuilderCondition, and the 9306 name-authority test). A re-point under types' name cannot carry the ruled extensions without a second declaration. Which route?",
          "options": [
            "A: rename components' declaration to FilterBuilderRow. Its doc comment already opens with 'One row of the FilterBuilder UI', and batch 3's report proposed the same spelling. Measured on a renamed copy of the built entry: root 419 = 419, the old import gives TS2305, and the renamed type is Equal to today's, so flat rows, the operator set and the value union do not move. Edits: the 20 references in filter-builder.tsx; 2 components tests; the 8415 source-text pin (measured: exactly 1 of its tests reads the derivation text and goes red); the prose that names components' FilterBuilderCondition in types' complex.ts and complex.zod.ts; and the ledger row. components minor, with a breaking note; types patch (test and comments only).",
            "B: keep the declaration module-local and stop publishing it. Root 419 to 418. The four exported value helpers (reshapeFilterValue, retypeFilterValue, narrowFilterValueToOptions, isFilterValueComplete) and LookupValuePicker are typed on its value member, so the published parameter type is left with no importable name.",
            "C (refused): re-point at types' row. Measured on disk: 3 errors in components' build program and 3 more broken pins in its test program, one of them an unused ts-expect-error because value turned optional. value would also become any, which switches off the four helpers' typing with zero errors. Ruling 5857342228 fixed both the operator set and the value union.",
            "D (refused): rename types' declaration. The ruling makes it the single name authority for the row; it is the TypeScript twin of FilterBuilderConditionSchema, and components reads it through the entry."
          ],
          "recommendation": "A, on the four axes. Real business need: both meanings are live. types' is the authoring row the zod mirror enforces; components' is the row the shipped builder edits and its helpers type. components' name has 0 readers through its entry. Long-term fitness: after A, the name FilterBuilderCondition means one thing, types' authoring row, which is what 'single name authority' says. components keeps deriving from it under a name of its own, as batch 11 did for FieldDomProps. Keeping AI from writing it wrong: today an auto-import of FilterBuilderCondition hands over either a row with an optional, any-typed value or a row with a required, narrowed value, depending on which root it picks. After A, an old import fails loudly (TS2305) instead of type-checking against the other shape. Startup scope: no new capability; an immediate rename with a breaking note, no alias window and no new gate."
        },
        {
          "question": "H3, FilterGroup. types' is the authoring group: id optional, logic 'and' or 'or', conditions as types' flat rows. components' is Omit of types' group minus conditions, and restates conditions as components' rows. Measured on the built entries: id and logic identical, assignable in neither direction (the rows decide it). It follows H1, but it is its own ledger row: renaming only the row leaves this one colliding. Readers through components' entry: 7 files in 3 packages. 3 of them are shipped source: plugin-list ListView.tsx (7 references; its published ListViewProps.onFilterChange and initialFilters name it); app-shell views/userFilterUrlState.ts (5 references; not published from the app-shell root); plugin-view config/view-config-utils.ts (2 references; its published toFilterGroup returns it). The other 4 are tests: 3 in plugin-list and 1 in app-shell. In-package: 12 references in filter-builder.tsx (FilterBuilderProps value and onChange, isValidGroup, EMPTY_GROUP, the state) and 2 tests. Readers through types' entry: 2, both in components. Which route?",
          "options": [
            "A: rename components' declaration to FilterBuilderRowGroup, batch 3's report's spelling for the pair. Measured on a renamed copy of the built entry: root 419 = 419 and the shape is Equal. The old import gives TS2724 with a misleading suggestion, 'Did you mean ItemGroup?'. ItemGroup is an unrelated React component in custom/item.tsx, and using it as a type fails again, so the error stays loud. Edits: filter-builder.tsx; the 3 shipped readers and 4 tests in plugin-list, app-shell and plugin-view; 2 components tests; the 6939 source-text pin (measured: exactly 1 of its tests reads the isValidGroup cast text and goes red); the prose that names components' FilterGroup in types' complex.ts, packages/components/docs/FilterBuilder.md and plugin-view's README line; and the ledger row. Changesets: components minor, with a breaking note. plugin-list, plugin-view and app-shell patch: their shapes are unchanged, only the referenced name moves. types patch. The three reader packages and the two docs need adding to the claim's file surface.",
            "B: stop publishing it. FilterBuilderProps' value and onChange keep a type with no importable name, and the 3 shipped readers would have to spell it as NonNullable of FilterBuilderProps' value.",
            "C (refused): re-point at types' group. This is the same mutation as H1's C (the rows inside become types' rows): 3 build errors plus 3 broken pins. The builder's onChange would be typed as emitting rows it cannot emit.",
            "D (refused): rename types' declaration. It is the ruled name authority and the type of FilterBuilderSchema.value on the authoring face. content/docs filter-builder.mdx documents it by that name, and the 6939 test reads that doc block.",
            "E (refused): make types' FilterGroup generic over its row and re-export it from components. It still needs H1's distinct row name, it changes a published types shape, and it silently changes what components' FilterGroup means for an external reader, because the default row is types' row."
          ],
          "recommendation": "A, on the four axes, with batch 3's objection answered. Batch 3 refused a rename because it 'would spend a published-name break ... on a distinction both file headers explicitly deny'. Since objectui#9306, both headers state that distinction (the named extensions), so the break now buys one name per meaning. Real business need: 3 shipped readers, all re-pointed in the same PR; an external reader gets a loud TS2724. Long-term fitness: FilterGroup means the authored group only. Keeping AI from writing it wrong: today, an auto-import of types' FilterGroup used as FilterBuilder's value fails with a remote TS2322 on value requiredness (the card's own failure mode). After A, it is one name and one shape. Startup scope: an immediate rename with a breaking note, no alias window and no new gate."
        },
        {
          "question": "H2, FilterBuilderOperator. types' is SpecViewFilterOperator, the spec's ViewFilterOperator by reference: the protocol's 20 ids. components' is derived from its dropdown list (the value of each defaultOperators entry) and pinned EQUAL to ViewFilterOperator plus 'exists' and 'notExists'. Measured on the built entries: types' is a strict SUBSET of components', and the difference is exactly the opt-in pair, so the two are not equal. These are two ruled meanings: the operator an authored condition may carry (the zod mirror refuses the pair), and the operator the builder can draw. Ruling 5852274286 keeps the pair opt-in and names components' FilterBuilderOperator as 'exported by name'. Readers through components' entry: 1 (plugin-view view-config-utils.ts). In-package: 13 references and 2 tests. Readers through types' entry: 0. In-package: the root barrel; data-protocol.ts, which imports it as BaseFilterOperator for AdvancedFilterOperator; and the 9559 test. Which route?",
          "options": [
            "A: retire types' FilterBuilderOperator. types' row types operator with SpecViewFilterOperator, which complex.ts already imports, and the row carries the doc comment. data-protocol.ts imports the spec's ViewFilterOperator as BaseFilterOperator. The barrel drops the name. The 9559 test pins FilterBuilderCondition's operator member instead. Measured on disk: types' build and examples programs give 2 errors each (the barrel and data-protocol.ts), and its test program gives 5 (those 2, plus 3 in the 9559 test), with nothing outside that list. Measured on a retired copy of the built entry: root 709 to 708, the old import gives TS2305, and FilterBuilderCondition and FilterGroup stay Equal to today's. types minor, with a breaking note: import ViewFilterOperator from @objectstack/spec/ui instead. components is untouched. It fits the claim's file surface as it stands.",
            "B: rename components' declaration, for example to FilterBuilderDropdownOperator. This changes the name ruling 5852274286 says components exports, and it breaks plugin-view's import through the entry. It also leaves FilterBuilderOperator meaning the 20, under a name that says FilterBuilder, while the builder draws 22.",
            "C (refused): move the 22-id union down into types under the name. types would publish the opt-in pair as a vocabulary beside the spec, and its TypeScript face would disagree with its zod mirror (20). Ruling 5852274286 refused putting the pair into types (its option C).",
            "D (refused): re-point components' declaration at types'. Measured on disk: 3 errors in components' build program ('exists' and 'notExists' rejected in the nullness operator list, and in the dropdown filter) and 3 broken pins in its test program. It drops the opt-in pair the ruling keeps."
          ],
          "recommendation": "A, on the four axes. Real business need: types' name has 0 readers through its entry, while components' is the live vocabulary of the shipped dropdown and is read by plugin-view. Long-term fitness: the spec's declaration comes before any implementation. The protocol set already has a canonical name, the spec's ViewFilterOperator, and types restating it under a builder-flavoured name is a second name for the spec's type. A references the spec by its own name and leaves FilterBuilderOperator with the one meaning the ruling gave it. Keeping AI from writing it wrong: today, the same import name gives 20 or 22 ids depending on the root. An AI that types an authored condition with components' name gets 'exists' through tsc, and only the mirror refuses it, later. After A, the authoring row refuses it at compile time, and an old import fails loudly. Startup scope: a zero-reader name retires immediately, with a breaking note, no alias window and no new gate."
        }
      ],
      "out_of_scope_findings": [
        "carrier: this card's H1 round (option A edits these lines anyway) · noted, not filed. packages/types/src/zod/complex.zod.ts says 'The component's own exported FilterBuilderCondition declares it string' about the row id. Since objectui#9306, the component no longer declares id; it derives it from types. Prose drift only.",
        "carrier: this card's H3 round (option A edits the same section) · noted, not filed. packages/components/docs/FilterBuilder.md is not in components' files[], so it is unpublished. It documents FilterGroup.id as required and lists the camelCase operator aliases objectui#9306 took out of the dropdown (notEquals, isEmpty, and so on). The spec's alias table still folds them on read, so nothing is refused. Prose drift only."
      ],
      "gates": [
        {
          "command": "os-verify-lock -c 'pnpm exec turbo run build --filter=@object-ui/components --concurrency=2' (base 7e30d0b)",
          "exit": 0,
          "verdict": "Tasks 8 successful, 8 total; Cached 1; VERDICT command-exit 0"
        },
        {
          "command": "os-verify-lock -c pnpm exec vitest run --project unit scripts/__tests__/one-authority-per-exported-name-6273.test.ts (base 7e30d0b)",
          "exit": 0,
          "verdict": "Test Files 1 passed (1), Tests 11 passed (11); 4 ledger rows"
        },
        {
          "command": "tsc over the shape probe on the built entries (and its 3-claim flipped control)",
          "exit": 0,
          "verdict": "0 diagnostics; control exit 2, exactly 3 TS2344"
        },
        {
          "command": "os-verify-lock -c ablate.sh (components tsc, both programs, under three ablation-replace legs)",
          "exit": 0,
          "verdict": "C0 0/0; row+group re-point 3/6; operator re-point 3/6; every restore blob == HEAD; VERDICT command-exit 0"
        },
        {
          "command": "tsc over the option-A copies of the built entries (old imports, Equal claims, flipped control)",
          "exit": 2,
          "verdict": "expected red: TS2305 (row), TS2724 (group), TS2305 (types operator); the Equal file has 0 errors; control exactly 2 TS2344"
        },
        {
          "command": "os-verify-lock -c pins.sh (vitest over the 8415 and 6939 pins, clean and with option A renamed)",
          "exit": 0,
          "verdict": "control 66 passed; renamed 2 failed / 64 passed (one per pin); restores proven; VERDICT command-exit 0"
        },
        {
          "command": "os-verify-lock -c types-retire.sh (types tsc, three programs, types FilterBuilderOperator un-exported)",
          "exit": 0,
          "verdict": "control 0/0/0; retired 2/2/5, all on the barrel, data-protocol.ts and the 9559 test; restore proven; VERDICT command-exit 0"
        },
        {
          "command": "pnpm check:readme-exports, check:phantom-deps, check:doc-snippets, check:changeset-claims, check:pending-changeset-literals, check:control-bytes, check:new-line-citations; node scripts/check-changeset-presence.mjs and check-changeset-no-major.mjs",
          "exit": null,
          "verdict": "NOT MEASURED, reason: empty diff. No row was built, so there is no change for them to judge"
        },
        {
          "command": "grep -naP over the control-byte class, run on this report's body file",
          "exit": 1,
          "verdict": "no hits"
        }
      ],
      "deviations": [
        "No PR. Every row is a stop with measured options, which is the route Zone 3 gives when every row stops.",
        "The build was the components closure only: components plus its dependency tasks, which include types. It was not the dispatch's types-dependents filter. With no row built, nothing downstream needed building. Option A's effects on readers were measured on copies of the built entries and through an import census instead.",
        "The first H2 retirement probe measured nothing. ablation-replace refused it, because the replacement text was a substring of the anchor, and restored the file. It was re-run with a non-substring replacement and is not counted.",
        "Emitted-JS identity and head built-entry counts: NOT MEASURED, reason: no head (no row built). Option A projections for the root name counts were measured on copies of the built entries.",
        "The claim's line addresses for the six sites (filter-builder.tsx 68, 89 and 280; complex.ts 499, 509 and 561) were re-read at 7e30d0b and hold. This report cites by symbol instead (objectui AGENTS.md #11).",
        "The worktree /home/user/objectui-issue-6349b12 is kept for the follow-up round, with node_modules and the built dist of the components closure. It has no edits and no commits. The probe copies under node_modules and a private fetch ref were removed.",
        "Reads used REST GET over curl, because gh in this container has an invalid token. No MCP tool was used."
      ],
      "files_changed": [],
      "line_budget": "not applicable (no skills/** surface; empty diff)"
    }

    Generated by Claude Code

  11. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    Contributor

    Claim amendment: batch 12's three stopped rows, A on each · domain:ui seat 1 · session_016djJF12Qt14ejKR5Vjt4sK · 2026-10-12T05:11Z

    This amends batch 12's claim 6117915310. The dev stopped on every row, as the claim requires (report 6118141319). No PR was opened.

    Answer: A on all three, decided by the seat under the rules this card applies, and read against the ruling the rows are bound to. The maintainer's veto window is open.

    The ruling reads this way: objectui#9306 5852274286 makes "the @object-ui/types declaration … the single name authority for the builder's condition and group". It honours ruling 5651809976 item 4, "converge objectui#6349's two parked name-authority rows on the types declaration", and it names components' FilterBuilderOperator (the protocol set plus exists / notExists) as "exported by name".

    • Today one name covers two shapes, measured assignable in neither direction. That is the collision the ruling meant to converge.
    • A gives the name to the types declaration alone. components keeps deriving from it under names of its own, carrying the ruled extensions unchanged.
    • Batch 3's objection is met: it refused a rename "on a distinction both file headers explicitly deny". Since objectui#9306, both headers state that distinction.

    FilterBuilderCondition → A: components' declaration is renamed FilterBuilderRow.

    • It has 0 readers through components' entry. A re-point measured red (3 build errors, 3 broken pins), and it would turn value into any.
    • The renamed type is Equal to today's, measured on a renamed copy of the built entry, and the root keeps 419 names.
    • @object-ui/components minor, with a breaking note: old import → TS2305, replaced by FilterBuilderRow.

    FilterGroup → A: components' declaration is renamed FilterBuilderRowGroup.

    • Its 3 shipped readers are re-pointed in the same PR:
      • plugin-list ListView.tsx, whose published ListViewProps name it;
      • plugin-view view-config-utils.ts, whose published toFilterGroup returns it;
      • app-shell views/userFilterUrlState.ts.
    • Their 4 tests are re-pointed too.
    • Shapes are Equal, and the root keeps 419 names.
    • @object-ui/components minor, with a breaking note. The old import gives TS2724 with a misleading "Did you mean ItemGroup?", so the note names the replacement explicitly.
    • plugin-list, plugin-view and app-shell are patch: their shapes do not move; only the referenced name does.

    FilterBuilderOperator → A: types' declaration retires.

    • It has 0 readers through types' entry. It is the spec's ViewFilterOperator under a builder-flavoured name.
    • types' row types operator with SpecViewFilterOperator, which complex.ts already imports. data-protocol.ts imports the spec's ViewFilterOperator. The 9559 test pins the row's operator member instead.
    • types' root goes from 709 names to 708, and FilterBuilderCondition and FilterGroup stay Equal to today's.
    • @object-ui/types minor, with a breaking note: import ViewFilterOperator from @objectstack/spec/ui instead.
    • components' FilterBuilderOperator keeps its name, as the ruling says.

    After A the ledger keeps 1 row: CalendarEvent, objectui#5044's ruled shim.

    File surface, added:

    • Readers: packages/plugin-list/src/ListView.tsx and its 3 tests; packages/plugin-view/src/config/view-config-utils.ts and its README line; packages/app-shell/src/views/userFilterUrlState.ts and its test.
    • types: packages/types/src/data-protocol.ts, the barrel line, and the 9559 test.
    • Source-text pins: 8415 and 6939. Each has exactly one test that reads the renamed text, and those tests move with it.
    • Prose that names the renamed declarations: in complex.ts and complex.zod.ts, and in packages/components/docs/FilterBuilder.md. That page is unpublished; fix its FilterGroup.id "required" line and the retired camelCase operator aliases while editing the section (the dev's two out-of-scope findings, carried here).
    • Changesets: .changeset/6349-batch12-*.md, one per graded package.

    Clause-②: yes is unchanged. A contract review at CONTRACT_REVIEW_TIER is owed on the final head before the queue. The PR's first line stays Refs #6349 (batch 12). Whether the card closes with only CalendarEvent left is decided at landing.


    Generated by Claude Code

  12. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    {
      "issue": 6349,
      "round": 2,
      "addendum_to": "6118141319",
      "status": "done",
      "branch": "claude/issue-6349-batch12-filter-rows",
      "pr": "https://github.com/objectstack-ai/objectui/pull/12189",
      "session": "session_016djJF12Qt14ejKR5Vjt4sK (subagent dispatch, so this is the parent's id)",
      "premise_still_valid": true,
      "summary": "Round 2 of batch 12, on the claim 6117915310 as amended in 6118165522 (A on all three rows), built in one draft PR, objectui#12189 (head 9330f85, assignee os-steve). All three rows converge, so KNOWN_COLLISIONS goes from 4 rows to 1: CalendarEvent, objectui#5044's ruled shim. FilterBuilderCondition: components' derived row is renamed FilterBuilderRow. FilterGroup: components' derived group is renamed FilterBuilderRowGroup, and its 3 shipped readers re-point (plugin-list ListView.tsx, plugin-view config/view-config-utils.ts, app-shell views/userFilterUrlState.ts), with their 4 tests. FilterBuilderOperator: types' declaration retires. The authored row types operator with the spec's ViewFilterOperator, data-protocol.ts imports that type as BaseFilterOperator, and the barrel drops the name; components' FilterBuilderOperator keeps its name. Every shape stays Equal, and the emitted JS is identical once comments are removed. The 8415 and 6939 source-text pins, the 9559 test, the prose in complex.ts and complex.zod.ts, packages/components/docs/FilterBuilder.md (including the 'required' group id and the camelCase aliases) and plugin-view's README line move with the rename. Changesets: components minor and types minor, each with a breaking note; plugin-list, plugin-view and app-shell patch. main moved to d928c83 during the round (objectui#12183, disjoint) and was merged in, not rebased; every gate was re-run on the merged head.",
      "tests": "Final head 9330f85 (two commits on base 7e30d0b, plus a merge of main d928c83). Base dist came from a separate detached worktree at 7e30d0b, since removed. (1) Build, through os-verify-lock. At base: plugin-list and plugin-view closures, 17 tasks, and app-shell closure, 29 tasks, all successful. At head: the same closures, then a rebuild on the merged head, 29 tasks; then the doc-snippet build filter, 35 tasks; VERDICT command-exit 0 each time. (2) Built-entry names (one TypeScript program per side), base to head: types 709 to 708; components 419 = 419; plugin-list 19 = 19; plugin-view 39 = 39; app-shell 259 = 259. At head, components publishes FilterBuilderRow, FilterBuilderRowGroup and FilterBuilderOperator, and neither old name; types publishes FilterBuilderCondition and FilterGroup, and not FilterBuilderOperator. (3) Equal shapes, base built entry against head built entry, one claim per exported name. components: 419 claims, with the two renames mapped, 0 errors. types: 716 claims, 715 hold; the 1 that does not is INFLIGHT_GET_REGISTRY_KEY, a unique symbol, which is never identical across two declarations, and its declaration file is byte-identical. plugin-list, plugin-view and app-shell: each changed .d.ts is identical to base, with comments removed, once the one renamed identifier is substituted; the control without the substitution differs. (4) Emitted JS, base dist against head dist. components: 0 of 2 JS files differ (1 file differs in all, custom/filter-builder.d.ts). plugin-list 0 of 2. plugin-view 0 of 2. types: 2 of 74 differ (zod/complex.zod.js and zod/index.zod.js), both identical with comments removed (TypeScript printer); the other .d.ts differences are union-member reordering in declaration emit. app-shell: views/userFilterUrlState.js is identical with comments removed, and the other 5 differing JS files are exactly the sources objectui#12183 changed. The types, components, plugin-list and plugin-view dist at the final head is byte-identical to the 306db5f head build. (5) Ablation legs, run twice (at 306db5f and again on the final head with the same readings), through objectstack scripts/ablation-replace.mjs: each anchor hit 1 time as declared, and each restore was proven blob == HEAD with an empty git diff HEAD, ending at 0 status lines. Control: gate Tests 11 passed. Row put back: FilterBuilderCondition, FilterGroup and FilterBuilderOperator each give gate 1 failed / 10 passed, naming the row with 'no longer collides at'. Name declared again: components' FilterBuilderCondition gives gate 1 failed ('a NEW colliding name'), the 8415 pin 1 failed / 11 passed, and components' test program TS2578 on the old-row pin. components' FilterGroup gives gate 1 failed, and TS2578 on the old-group pin. types' FilterBuilderOperator gives gate 1 failed. (6) Type-check through os-verify-lock, each package's own script (both or all three programs echoed): types, components, plugin-list, plugin-view and app-shell, exit 0 each; --listFilesOnly confirms the test programs include every touched test. type-check:scripts exit 0. eslint over the 19 touched TypeScript files: 0 errors, and 269 warnings, the same as base on the same files. (7) Targeted vitest through the lock, 59 files beside the touched sites: the ledger test; every filter-builder test in components, types and examples/schema-catalog; plugin-list's convertFilterGroupToAST, ListView.propsResolution and offered-operator parity tests; app-shell's userFilterUrlState, viewFilterFold and protocol-ids census tests; plugin-view's view-config-utils readers; and the tests that read complex.ts or data-protocol.ts. Test Files 59 passed (59), Tests 1468 passed (1468).",
      "mcp_calls": "0. No MCP GitHub tool was called. Reads went over REST GET with curl through the container proxy; gh has an invalid token here.",
      "api_writes": "Round 2: 2 relay strokes, each one POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]. (1) pr_create through scripts/pm/fleet-write/dispatch.mjs: POST /repos/objectstack-ai/objectui/pulls (draft), and its assignee leg POST /repos/objectstack-ai/objectui/issues/12189/assignees. Read back: 8164 of 8164 bytes identical; assignees os-steve; draft true. (2) This round-2 os-dev-report: POST /repos/objectstack-ai/objectui/issues/6349/comments, through post-stamped.mjs. Round 1 had 1 stroke, report 6118141319. No label write. Not REST: 3 git pushes this round (306db5f, 2fdbacb, then the merge 9330f85), plus round 1's empty-branch push.",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: 承接者:无 · noted, not filed (an instrument observation, in this report only). A cross-worktree Equal probe over two built copies of the same package version is silently deduplicated by TypeScript: the second copy that a node_modules import reaches is redirected to the first. Here it made plugin-list's and plugin-view's head declarations resolve @object-ui/components to the base copy. It was caught because the renamed export was missing; those three packages were judged by a normalised .d.ts comparison with a control instead."
      ],
      "gates": [
        {
          "command": "os-verify-lock -c turbo run build --filter=@object-ui/app-shell --filter=@object-ui/plugin-list --filter=@object-ui/plugin-view --concurrency=2 (final head 9330f85)",
          "exit": 0,
          "verdict": "Tasks 29 successful, 29 total; VERDICT command-exit 0"
        },
        {
          "command": "os-verify-lock -c turbo run build $(node scripts/check-doc-snippet-types.mjs --build-filter) --concurrency=2",
          "exit": 0,
          "verdict": "Tasks 35 successful, 35 total; VERDICT command-exit 0"
        },
        {
          "command": "os-verify-lock -c typecheck.sh: pnpm --filter @object-ui/{types,components,plugin-list,plugin-view,app-shell} type-check (final head)",
          "exit": 0,
          "verdict": "each exit 0, 0 errors; VERDICT command-exit 0"
        },
        {
          "command": "os-verify-lock -c pnpm exec vitest run --maxWorkers=2 (59 targeted files, ledger test included) (final head)",
          "exit": 0,
          "verdict": "Test Files 59 passed (59), Tests 1468 passed (1468)"
        },
        {
          "command": "os-verify-lock -c legs/run.sh (six ablation legs plus control) (final head)",
          "exit": 0,
          "verdict": "control 11 passed; all six legs red as predicted; every restore blob == HEAD; 0 status lines after"
        },
        {
          "command": "pnpm check:readme-exports",
          "exit": 0,
          "verdict": "OK, 591 of 591 self-imports judged real (0 wrong-path, 0 fabricated)"
        },
        {
          "command": "pnpm check:phantom-deps",
          "exit": 0,
          "verdict": "Every in-scope import is declared by the package that publishes it."
        },
        {
          "command": "pnpm check:doc-snippets",
          "exit": 0,
          "verdict": "Semantic phase: 789 of 789 block(s) judged, 0 failed."
        },
        {
          "command": "pnpm check:control-bytes",
          "exit": 0,
          "verdict": "OK (scanned 8450 tracked text file(s); skipped 85 binary)"
        },
        {
          "command": "pnpm check:new-line-citations",
          "exit": 0,
          "verdict": "VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0"
        },
        {
          "command": "node scripts/check-changeset-presence.mjs",
          "exit": 0,
          "verdict": "18 source file(s) of 5 released package(s) changed, and this change declares 5 changeset(s)"
        },
        {
          "command": "node scripts/check-changeset-no-major.mjs",
          "exit": 0,
          "verdict": "No changeset declares a `major` bump."
        },
        {
          "command": "pnpm check:changeset-claims",
          "exit": 0,
          "verdict": "No pending changeset names a file this change touches."
        },
        {
          "command": "pnpm check:pending-changeset-literals",
          "exit": 0,
          "verdict": "No test source names a pending changeset."
        },
        {
          "command": "node scripts/check-changeset-fixed.mjs",
          "exit": 0,
          "verdict": "All workspace packages are in the changeset fixed group."
        },
        {
          "command": "pnpm type-check:scripts",
          "exit": 0,
          "verdict": "tsc -p tsconfig.scripts.json, exit 0"
        },
        {
          "command": "pnpm exec eslint (19 touched .ts/.tsx files)",
          "exit": 0,
          "verdict": "0 errors, 269 warnings (base on the same files: 269)"
        },
        {
          "command": "node scripts/check-governed-queue-guard.mjs --test (the 26 changed paths)",
          "exit": 0,
          "verdict": "NOT GOVERNED"
        },
        {
          "command": "GET /repos/objectstack-ai/objectui/commits/9330f85c4/check-runs (one read, no polling)",
          "exit": null,
          "verdict": "in_progress: 42 runs, 13 success, 3 skipped, 26 in progress, 0 failed at the time of reading"
        }
      ],
      "deviations": [
        "main moved to d928c83 during the round (objectui#12183, app-shell metadata-admin only; 0 files shared with this branch). It was merged into the branch (commit 9330f85), not rebased, and the whole gate union above was re-run on the merged head.",
        "A second commit (2fdbacb) prefixes the three new type-only pins with an underscore (_OldRowName, _OldGroupName, _RetiredOperatorName). Their first spelling added 3 no-unused-vars warnings against base; with the underscore the warning count equals base.",
        "Edits beyond the amendment's letter, all inside its listed files: the 8415 pin's comment pointed at filter-builder-name-authority-9306.test.ts, but the file is .tsx, so the path is corrected. complex.ts's FilterField.operators doc named the retired FilterBuilderOperator, so its history line is reworded. The share-filter-sort-spec-parity test's spec-collision table now pins FilterBuilderRow (one of the amendment's 2 components tests). The name-authority and 9559 tests gained pins that the old names are not published.",
        "The first readme-exports and doc-snippets runs were PREREQUISITE NOT MET (six untouched packages unbuilt). They were re-run after the gates' own build filter and are not counted.",
        "Equal-claim instrument bounds, stated rather than hidden. The unique symbol INFLIGHT_GET_REGISTRY_KEY cannot be identical across two declarations. TypeScript deduplicates same-version package copies across worktrees, so plugin-list, plugin-view and app-shell were judged by a normalised .d.ts comparison with a control. In the components probe, both sides resolve @object-ui/types to one copy; the types probe proves that copy's row and group Equal from base to head.",
        "Commit trailers use the model-free pair (Claude-Session plus Co-authored-by: Claude) per objectui AGENTS.md, and the PR footer uses the os-dev session-URL form, instead of the harness-suggested lines.",
        "Cleanup: both worktrees (/home/user/objectui-issue-6349b12 and the temporary base worktree) were removed after the PR opened, each from a clean status, and the private fetch ref was deleted. The branch head 9330f85 is on the remote.",
        "CI was not awaited: the report is delivered at the end of local verification, and the check-runs reading above is in_progress."
      ],
      "files_changed": [
        ".changeset/6349-batch12-app-shell.md",
        ".changeset/6349-batch12-components.md",
        ".changeset/6349-batch12-plugin-list.md",
        ".changeset/6349-batch12-plugin-view.md",
        ".changeset/6349-batch12-types.md",
        "packages/app-shell/src/views/userFilterUrlState.listState-11860.test.ts",
        "packages/app-shell/src/views/userFilterUrlState.ts",
        "packages/components/docs/FilterBuilder.md",
        "packages/components/src/__tests__/filter-builder-empty-null-pair-11810.test.tsx",
        "packages/components/src/__tests__/filter-builder-name-authority-9306.test.tsx",
        "packages/components/src/__tests__/share-filter-sort-spec-parity.test.ts",
        "packages/components/src/custom/filter-builder.tsx",
        "packages/plugin-list/src/ListView.tsx",
        "packages/plugin-list/src/__tests__/ListView.propsResolution.test.ts",
        "packages/plugin-list/src/__tests__/convertFilterGroupToAST.canonicalSpelling.test.ts",
        "packages/plugin-list/src/__tests__/convertFilterGroupToAST.test.ts",
        "packages/plugin-view/README.md",
        "packages/plugin-view/src/config/view-config-utils.ts",
        "packages/types/src/__tests__/filter-builder-condition-id-8415.test.ts",
        "packages/types/src/__tests__/filter-builder-mirror-6939.test.ts",
        "packages/types/src/__tests__/filter-operator-protocol-set-9559.test.ts",
        "packages/types/src/complex.ts",
        "packages/types/src/data-protocol.ts",
        "packages/types/src/index.ts",
        "packages/types/src/zod/complex.zod.ts",
        "scripts/__tests__/one-authority-per-exported-name-6273.test.ts"
      ],
      "line_budget": "not applicable (no skills/** surface)"
    }

    Generated by Claude Code

  13. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    Contributor

    ACCEPT: PR objectui#12189 (head 9330f85c4d), batch 12 · domain:ui seat 1 · session_016djJF12Qt14ejKR5Vjt4sK · 2026-10-12T06:06Z

    Reviewed against GitHub and the diff, not the reports (os-dev-report round 1 6118141319, round 2 6118611797). Claim 6117915310, amended in 6118165522.

    Contract review: PASS, PR objectui#12189 comment 6118724397. The seat checked the record's lines:

    • Served-tier: CONTRACT_REVIEW_TIER;
    • Head-sha equal to the head;
    • Local-runs: none;
    • Implemented-by and Reviewed-by signed;
    • VERDICT: PASS;
    • no model identifier.

    Verdict

    • Shape:
      • Draft on main. The first line is Refs #6349 (batch 12), with no closing keyword, and Clause-②: yes sits at a line start.
      • The title names all three rows, and the body cites the amendment.
      • Assignee os-steve.
    • Scope: 26 files, +337/−208, exactly the claim's surface plus the amendment's. check-governed-merges --pr objectstack-ai/objectui#12189: NOT governed, 545 changed lines.
      • The branch merged main d928c83 (objectui#12183, which shares no files with it) rather than rebasing, and every gate was re-run on the merged head.
      • It now reads behind because main moved again (objectui#12176). That is not an enqueue veto.
    • What converged: KNOWN_COLLISIONS goes from 4 rows to 1, leaving CalendarEvent, objectui#5044's ruled shim.
      • FilterBuilderCondition: components' derived row is now FilterBuilderRow. The derivation from types' row, with operator and value as the ruled named extensions, is unchanged, and the shape is Equal. @object-ui/types alone publishes the name, as ruling 5852274286 makes it "the single name authority".
      • FilterGroup: components' derived group is now FilterBuilderRowGroup. Its 3 shipped readers re-point with their published shapes unchanged: plugin-list ListViewProps, plugin-view toFilterGroup, and app-shell's URL state, which is not published.
      • FilterBuilderOperator: types' zero-reader declaration retires. The authored row and data-protocol.ts bind the spec's ViewFilterOperator; @objectstack/spec is already a types dependency. components' FilterBuilderOperator keeps its name, as the ruling says.
    • Evidence:
      • Built-entry names: types 709 → 708; components 419 = 419; plugin-list 19 = 19; plugin-view 39 = 39; app-shell 259 = 259.
      • Equal-shape claims hold across the published names. The one exception is a unique symbol that cannot be identical across two declarations; its declaration file is byte-identical.
      • Emitted JS is identical with comments removed.
      • Six ablation legs ("row put back" and "name declared again" per row) went red as predicted. Each restore was blob-equal to HEAD.
      • Targeted vitest: 59 files, 1468 tests.
    • Changesets: @object-ui/components minor and @object-ui/types minor, each with a breaking note naming the replacement. The components note names it explicitly, because TS2724 suggests ItemGroup. @object-ui/plugin-list, @object-ui/plugin-view and @object-ui/app-shell are patch. No major.
    • Gates on this head (read at this record): 35 success, 3 skipped by design, 4 in progress (Test shards 1, 2, 4 and Spec Main Shape Gate), 0 failure. The seat enqueues only once all of them read success.

    Out-of-scope findings

    • One instrument observation (the dev's): TypeScript deduplicates same-version package copies across worktrees, so a cross-worktree Equal probe can read the base copy. The dev caught it and judged those packages by a normalised .d.ts comparison with a control instead. It is in the report only; nothing to file.

    Landing: ready and auto-merge through the merge queue once every check on 9330f85 is success or skipped by design.

    The card after this batch (decided at landing): CalendarEvent is the only row left, and it is objectui#5044's ruled deprecation shim. It leaves when that shim retires, which is objectui#5044's subject, not a batch of this card. So the card's own population, "the 42 named by no family card", is done. The landing note closes it as completed, pointing the last row at objectui#5044.


    Generated by Claude Code

  14. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    Contributor

    Landed: PR objectui#12189 → main 26b0bd2, batch 12 · domain:ui seat 1 · session_016djJF12Qt14ejKR5Vjt4sK · 2026-10-12T06:34Z

    • The merge: through the merge queue, on top of 0f31c3a (objectui#12186, which shares no files with it). The PR's first line is Refs #6349 (batch 12), so it closed no card. Read in this act: nothing else closed with it.
    • Content check: all 26 files the PR changes are blob-identical at 26b0bd2 to the accepted head 9330f85c4d, which contract review PASS 6118724397 and the seat's ACCEPT 6118744136 cover.
    • On main now: KNOWN_COLLISIONS holds 1 row, CalendarEvent (counted at 26b0bd2).
      • @object-ui/types alone publishes FilterBuilderCondition and FilterGroup. components' derived shapes are FilterBuilderRow and FilterBuilderRowGroup, and plugin-list, plugin-view and app-shell read them.
      • types' zero-reader FilterBuilderOperator is retired. The authored row binds the spec's ViewFilterOperator, and components' FilterBuilderOperator keeps its name.
      • components and types carry minor changesets with breaking notes. plugin-list, plugin-view and app-shell carry patch.
    • A correction to the ACCEPT: it said the CalendarEvent row "leaves when that shim retires, which is objectui#5044's subject". That is wrong. objectui#5044 closed as completed on 2026-08-23. Its ruling (option A) keeps the @deprecated alias in packages/plugin-calendar/src/index.tsx as a working export, and it rejected removing the export (option B) as breaking with an external cost that cannot be measured. So no card owns the alias's removal, and none is owed. The row is the ledger's one ruled exception, and it changes only if the maintainer rules again on objectui#5044's subject.
    • The card is done: its population was "the 42 named by no family card", and the ledger now holds nothing but that ruled exception. It closes in this act as completed.
    • One prose rider, not filed: sdui-parser's file header and package.json description still say "SDUI SchemaNode tree" (batch 11's note). It is description text, not an authority, so the gate does not read it. It can go with the next change to that package.
    • State: pm:dispatched is removed in this act. domain:ui and priority:p3 stay.

    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p3

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions