You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Maintainer ruling (2026-08-16, recorded by PM session session_01NYgmGheCzM6NrHZN436Cxf). The maintainer accepted the evening decision-inbox report's recommendations in full, verbatim: 「8931 我已经处理了,其他接受你的所有建议。」 For this card:
Ruled: Option A — retire the owner alias, preconditioned on a zero-consumer measurement.
Precondition (first step of the dispatch): search all four repos + examples/catalog for field:owner / 'owner' widget usage. Zero hits ⇒ proceed with A (drop the field-type-alias.ts mapping, the field:owner registration, and the USER_WIDget_TYPES membership in plugin-grid; cell renderer disposition per measurement). Any real consumer ⇒ fork report back to the decision box — do not migrate silently.
Rationale on record: owner is not a spec FieldType, adds nothing over user (same widget), and is reachable only via hand-written SDUI with broken plumbing (dependsOnLabels has no fallback — the objectstack#5407 hole still open here).
Either way: the grid claim in packages/fields/src/widgets/types.tsdataSource TSDoc (zero consumers) is corrected in the same batch.
First-touch grading completes: label flipped needs-user-decision → pm:queue in the same stroke.
按裁定评论 5307574997 的前置条件执行第一步扫描。结果:owner 不是零消费者。命中 3 处代码 + 4 处文档/示例消费面,其中一处是裁定与本单正文都未列出的第三个代码消费者。按「Any real consumer ⇒ fork report back to the decision box — do not migrate silently」,本次不实施退役、不开 PR,把命中清单交回决策箱。
content/docs/core/report-schema.mdx:99 —— 报表列 type 联合含 | 'user' | 'owner'。
顺带一提:fields-form-hosted.test.tsx 的文件头自陈这批 fields-* 例子是「the docs site's field demos and a few-shot retrieval source for AI authors, so what they show is what gets copied」—— 即第 4、5 条不只是文档,还是 AI 作者的抄写样本。
Maintainer ruling (2026-08-17, recorded by PM session session_01DbRmJD3iPhXjKr6vhd4Qkv). The maintainer reviewed the re-framed options after the fork report (comment 5308145625) and the four-card analysis presented in PM chat, and approved its recommendations, verbatim: 「同意」.
Ruled: Option A′ — complete retirement of the owner widget alias, with a loud tombstone.
This supersedes the 2026-08-16 Option A ruling (whose zero-consumer premise the fork report falsified) and decides against the dev/PM preference for B recorded above. Deciding rationale on record: the spec's closed FieldType (48 members) has no owner; the word carries zero behavioral delta over user (same widget); and this card's own measurement history — three code faces already drifted three ways on this one word — is the standing evidence that every extra synonym is another drift channel. A smaller closed vocabulary with loud refusal beats teaching AI authors two spellings for one thing; the same idiom survives verbatim as { type: 'user', name: 'owner' }.
Scope, all one batch:
All three code consumers retire together: the field-type-alias.ts mapping, the index.tsxfield:owner registration, plugin-grid's USER_WIDGET_TYPES membership — plus the third face packages/app-shell/src/utils/paramToField.ts:94, same batch (the two param faces move together, never split).
Loud tombstone: the retired spelling gets an explicit refusal/warning per the field:capability-multiselect TOMBSTONE convention — ⛔ the silent field:text fallback must not be the retirement's failure mode. This is also the answer to the unscanned cloud surface: any unknown consumer fails loudly instead of silently degrading to a text box.
Docs/catalog contract shrink: record-owner-read-only.json migrates to type: 'user' or is removed together with user.mdx's "Owner Field" section (implementer judges the dedup against single-user-selection and states the choice in the PR); the three public doc type unions (user.mdx, filter-builder.mdx, report-schema.mdx) drop 'owner'.
Cell renderer: disposed consistently with the retirement — field:owner/owner registrations removed and field-type-coverage.test.ts expectations updated in the same PR.
The grid TSDoc zero-consumer correction (packages/fields/src/widgets/types.ts:166) rides the same batch, per the standing half of the prior ruling. Downstream: #4815 stays Blocked-by this card until it closes; #4855's premise gets re-read after this lands.
Also clearing the stale assignee left from the pre-fork claim (that claim ended with the fork report; comment 5308167330 flipped the label but left the assignee — half-state repair). Label flipped needs-user-decision → pm:queue in the same stroke.
Claim: PM loop round 2
Session: session_01DbRmJD3iPhXjKr6vhd4Qkv
Branch: claude/issue-4814-retire-owner-tombstone
Worktree: objectui-issue-4814
Domain: repo:objectui
File surface: packages/fields/src/field-type-alias.ts + packages/fields/src/index.tsx + packages/fields/src/field-type-coverage.test.ts + packages/fields/src/widgets/types.ts (grid TSDoc line) + packages/plugin-grid/src/components/bulkParamToField.ts + packages/app-shell/src/utils/paramToField.ts + examples/schema-catalog (the fields-user/record-owner-read-only example + its index entries) + content/docs/fields/user.mdx / content/docs/components/complex/filter-builder.mdx / content/docs/core/report-schema.mdx + tests (stop on breach; explain in the report)
Container & model: M, mode:subagent, model: opus
Serial constraints cleared: the #4648 same-package serial (plugin-grid registration surface) is resolved — PR #4901 MERGED 03:05Z, branch from post-merge main. In-flight checked this round: #4636 (lockfile + package.json ranges only), #4886 (sibling seat, app-shell/views/metadata-admin/ResourceEditPage.tsx), #4003 / PR #4892 (examples/schema-catalog — its 100-file list grepped for fields-user / record-owner-read-only: zero overlap), #4884 / PR #4888 (plugin-gantt) — all disjoint from this surface. Prior claim on this card (branch claude/issue-4814-retire-owner-alias, session session_01GTRjn8xBqp75dk7kFupVRt) ended with the fork report at comment 5308145625 — zero code, zero PR, zero worktree per the PM disposition; this is a fresh dispatch under the superseding A′ ruling, new branch name to avoid any stale-ref ambiguity.
{
"issue": 4814,
"status": "done",
"branch": "claude/issue-4814-retire-owner-tombstone",
"pr": "https://github.com/objectstack-ai/objectui/pull/4915",
"premise_still_valid": true,
"summary": "Implemented ruling A' in full: the `owner` field-type alias, its `field:owner` widget-map entry, its cell-renderer registrations, and both param faces (plugin-grid `bulkParamToField` + app-shell `paramToField`) are retired together; the catalog example migrates to `type: 'user'` and the three public doc type unions drop `'owner'`; the zero-consumer `grid` claim in the `dataSource` TSDoc is corrected (independently re-verified: zero `dataSource` occurrences in GridField.tsx). The tombstone is LOUD as ruled: because `form.tsx`'s `renderFieldComponent` resolves both `type: 'owner'` and `widget: 'field:owner'` through the same `ComponentRegistry.get('field:owner')` lookup, one registered tombstone widget catches both spellings with a visible refusal, plus a console prescription deduped once per spelling. Two caveats the PM should weigh. (1) The A' premise stands and is strengthened, but the ruling's SCOPE enumeration is materially incomplete: re-sweeping for `owner` in the field-type namespace (not the field NAME) finds ~13 further faces across 8 packages, including a published runtime Zod enum in `@object-ui/types` (`reports.zod.ts:49`) that still ACCEPTS `type: 'owner'`, and two published TS unions that are the code twins of the very doc unions this PR shrinks — so docs and code now disagree in the opposite direction. All were present at the fork report's own base commit `1ef236e18`, i.e. pre-existing faces the earlier sweep did not reach, NOT new consumers (checked with `git show 1ef236e18:<path>`), so this is not the dispatch's new-consumer stop-and-report trigger. Filed as #4914 rather than silently widening the batch into 8 unclaimed packages. (2) One face beyond the ruled list IS included because it turned red here and its own drift guard prescribed the fix: `app-shell/src/utils/paramValueShape.ts` declared a value shape for `owner`, and its guard 'no stale contract entries pointing at removed widget types' failed the moment `owner` left FORM_FIELD_TYPES. Also correcting the fork report on one point: it concluded the form side has NO gate that reddens on the alias removal. Removing the `fieldWidgetMap` ENTRY does redden two — `widget-aria-invalid-registry-e2e` and `widget-dom-leak-e2e`, both of which pin a local widget inventory against FORM_FIELD_TYPES. Only the alias-table half was ungated. File surface stayed within the claim (all extra files are tests, covered by the claim's '+ tests'), except paramValueShape.ts as described.",
"tests": "Union run at HEAD=59b30a100 (= final commit = pushed remote head; PR head sha confirmed 59b30a100 after a second push). Repo-root path-filter vitest only; heavy runs under `flock -E 99 -w 540 /tmp/os-heavy-verify.lock`. Dependency closures built BEFORE judging — the first typecheck pass failed only on missing dist/*.d.ts (`Cannot find module '@object-ui/fields'`) and went green after `--filter '@object-ui/plugin-grid^...' --filter '@object-ui/app-shell^...' build`. RESULTS: packages/fields/ -> 'Test Files 100 passed (100) / Tests 1689 passed (1689)'; examples/schema-catalog/ -> 'Test Files 9 passed (9) / Tests 1589 passed (1589)' (fields-form-hosted green under the migrated example); app-shell/src/utils + plugin-grid param tests -> 'Test Files 23 passed (23) / Tests 377 passed (377)'. type-check (hyphenated script name, echoed in output so no zero-match trap) for @object-ui/fields, @object-ui/plugin-grid, @object-ui/app-shell -> all 'Done'. Gates: check:doc-types (143 mdx, 632 code blocks, 558 type literals) OK; check:control-bytes (4389 tracked text files) OK; docs:check-links (13 scan roots) OK; changeset:check OK. Also grepped for references to the renamed 'Owner Field' heading anchor: zero. REVERSE VERIFICATION, direction predicted first — predicted the ablation (removing the retired-type branch + tombstone registration, i.e. the naive 'just delete the alias' execution) would redden the new pin ONLY on the loudness assertions while pre-existing gates stayed green. Observed exactly that: 5 failures, all loudness — \"expected 'field:text' not to be 'field:text'\", \"expected 'text' not to be 'text'\", \"expected '' to contain '`owner`'\", the dedupe-count assertion, and \"expected undefined to be [Function RetiredFieldTombstone]\" — while field-type-coverage.test.ts stayed GREEN in the same run ('Test Files 1 failed | 1 passed (2)'), reproducing the fork report's central claim that no existing gate catches a silent retirement. Ablation restored byte-identically from the commit via `git checkout <branch> -- <paths>` (empty `git diff HEAD`). No dist-ablation concern: these suites import from source, not dist.",
"open_questions": [
{
"question": "The A' ruling scoped 'all three code consumers' + the fourth param face, but the measured field-type consumer surface is ~15 faces across 8 packages. The three published contract twins in particular (`@object-ui/types` reports.zod.ts runtime enum, reports.ts, field-types.ts) now bless a spelling the renderer refuses. Should those ride a fast follow-up, or be re-adjudicated together with the drift-channel argument that decided A'?",
"options": [
"A. Fast-follow #4914 items 1-3 only (the published TS/Zod twins) as a small dedicated card — closes the docs/code contradiction this PR introduces, leaves the 12 dead predicate branches for later cleanup",
"B. Fast-follow all of #4914 in one PR — mechanically similar work, one review, but 8 packages of collision surface at once",
"C. Leave as filed and let normal triage schedule it — the tombstone already makes any surviving `owner` fail loudly, so nothing is silently broken meanwhile"
],
"recommendation": "A, because it is the only half that is an active contradiction rather than dead code: `reports.zod.ts` is a PUBLISHED runtime validator that accepts `type: 'owner'` while `@object-ui/fields` now refuses it, and `field-types.ts` still offers `owner` in published .d.ts autocomplete — so an author is told 'legal' by the types package and 'retired' by the renderer, which is the declared-but-unenforced shape ADR-0049 targets and exactly the drift the A' ruling was chartered to end. Items 4-15 are inert by comparison (branches that can no longer match) and can wait for ordinary triage. Note the types/spec question overlaps #2231, so A may want to be scoped as a sub-issue of that rather than standalone."
}
],
"out_of_scope_findings": [
"filed as #4914: `owner` survives as a live field-type in ~13 more code faces across 8 packages after this retirement — including a published runtime Zod enum (`packages/types/src/zod/reports.zod.ts:49`) that still accepts it and two published TS unions (`reports.ts:105`, `field-types.ts:584`) that are the code twins of the doc unions this PR shrinks; plus 8 dead predicate branches (filter-builder, InlineFieldInput, buildDefaultPageSchema, view-config-utils, ObjectDataTable, recordFields, UserFilters, FieldEditWidget) and 4 test fixtures. All verified present at the fork report's base commit 1ef236e18, so pre-existing rather than new."
]
}
Scope (from changed files, 17): all four ruled pieces present and verified in the diff — the alias/table retirement with the TOMBSTONE table, the field:owner tombstone registration (skipFallback, never claims the bare name), both param faces in lockstep with mirror-comments, cell path degrading loudly, catalog example migrated, three doc unions shrunk, user.mdx rewritten to state the honest model, grid TSDoc corrected, coverage/e2e inventories updated with one-place-per-fact comments. Real patch changeset on the three released packages.
Bounded out-of-surface edit accepted: paramValueShape.ts — its own drift guard ("no stale contract entries pointing at removed widget types") turned red the moment owner left FORM_FIELD_TYPES; mechanical, prescribed by the gate, declared in the report and PR body.
Catalog disposition accepted: migrate rather than delete — measured right: the example's unique coverage is readonly: true, not the retired spelling.
Fork-report correction noted with approval: the form side does have two gates that redden on the map-entry removal (aria-invalid / dom-leak e2e inventories); only the alias-table half was ungated. The new owner-retired.test.tsx pins LOUDNESS (refusal shape), not absence — the ablation run reproduced the fork report's central claim (coverage test green under the naive deletion) as the predicted asymmetry.
Patch round on PR #4915 — CI Test shard 4/4 fixed. New head abbc8173e.
Same claim, same branch, no restart. The failure was real and caused by this PR — a fifth stale-inventory face, same class as the four already fixed, in a package I never ran.
What it was, and why the snapshot edit is not "forcing"
packages/plugin-detail/src/__tests__/inlineEditTypeCoverage.test.tsx — "the four buckets, as landed".
Read before touching, as asked. The snapshot is derived from ALL_TYPES, defined at the top of that file as FORM_FIELD_TYPES ∪ spec FieldType.options. owner left both halves: this PR removed it from FORM_FIELD_TYPES, and it was never a member of the spec enum — which is this card's own founding premise. So owner does not move between buckets, it leaves the universe the guard quantifies over. - "owner" from routed is the complete delta, and the edit records a derived reality rather than overriding one.
Two spots, not the one reported:
:234 (now :243) — the routed array in the snapshot.
:77 — the universe rationale comment, which still named owner among "the form-only keys the spec enum misses". That sentence became false in this PR and would have misinformed the next reader about why the union exists. Refreshed with the same one-place-per-fact mirror style used in field-type-coverage.test.ts and the two e2e inventories, pointing at owner-retired.test.tsx and objectui#4814.
The thing you asked me to say rather than force
INLINE_ROUTED_FIELD_TYPES (packages/plugin-detail/src/InlineFieldInput.tsx:107) still contains 'owner'. The snapshot does not derive from it, so I left it alone and said so in a comment at the snapshot instead.
But sharpening what I filed earlier: this member is not merely dead code. I had classified InlineFieldInput under "8 dead predicate branches" in #4914. That classification is too generous. Because the set is consulted at runtime against whatever type a stored field actually carries, a record whose field is still typed owner would get inline-edit routing to the user picker, while the record form now shows the tombstone refusal. That is a live inconsistency between two edit surfaces for the same field, not an unreachable branch. Same for InlineFieldInput.tsx:290's editType === 'user' || editType === 'owner'.
It is still out of this card's ruled scope and outside the claimed file surface, so I have not changed it — but #4914 item 5 should be read as more urgent than the "inert" framing in my main report, and it strengthens the case for option A in that report's open_questions.
Readings at abbc8173e
packages/plugin-detail/src/__tests__/inlineEditTypeCoverage.test.tsx
Test Files 1 passed (1) Tests 66 passed (66)
packages/plugin-detail/ (full package, as instructed)
Test Files 81 passed (81) Tests 787 passed (787)
check:control-bytes 4389 tracked text files OK
The targeted file passing is itself the proof it resolved the newFORM_FIELD_TYPES: had it read a stale build still containing owner, ALL_TYPES would have re-included it and the edited snapshot would have failed in the opposite direction.
No changeset impact — tests-only delta on a PR whose changeset is already declared. Prior union at 59b30a100 is unaffected by a test-file edit in a package outside it; the scanning gate was re-run at the new head as shown.
Activity
os-zhuang commentedon Aug 16, 2026
Maintainer ruling (2026-08-16, recorded by PM session
session_01NYgmGheCzM6NrHZN436Cxf). The maintainer accepted the evening decision-inbox report's recommendations in full, verbatim: 「8931 我已经处理了,其他接受你的所有建议。」 For this card:Ruled: Option A — retire the
owneralias, preconditioned on a zero-consumer measurement.field:owner/'owner'widget usage. Zero hits ⇒ proceed with A (drop thefield-type-alias.tsmapping, thefield:ownerregistration, and theUSER_WIDget_TYPESmembership in plugin-grid; cell renderer disposition per measurement). Any real consumer ⇒ fork report back to the decision box — do not migrate silently.owneris not a specFieldType, adds nothing overuser(same widget), and is reachable only via hand-written SDUI with broken plumbing (dependsOnLabelshas no fallback — the objectstack#5407 hole still open here).gridclaim inpackages/fields/src/widgets/types.tsdataSourceTSDoc (zero consumers) is corrected in the same batch.First-touch grading completes: label flipped
needs-user-decision→pm:queuein the same stroke.Generated by Claude Code
yinlianghui commentedon Aug 16, 2026
【认领】objectui 分片 PM(session_01GTRjn8xBqp75dk7kFupVRt)批次 12 派发,os-dev 实施。分支:
claude/issue-4814-retire-owner-alias。按 2026-08-16 维护者裁定(评论 5307574997)执行:方案 A 退役
owner别名,前置零消费者测量 —— 第一步扫可达仓 + examples/catalog 的field:owner/'owner'widget 用法(零命中须带邻近词反查;任何真消费者 ⇒ 停手回报决策箱,⛔ 静默迁移);零消费者 ⇒ 删field-type-alias.ts映射、index.tsx的field:owner注册、plugin-gridUSER_WIDGET_TYPES成员(cell renderer 按测量定夺)。types.tsdataSourceTSDoc 的grid零消费者声明同批更正。文件面:
packages/fields/src/field-type-alias.ts、packages/fields/src/index.tsx、packages/plugin-grid/src/components/bulkParamToField.ts、packages/fields/src/widgets/types.ts+ 测试。与在飞 #4788(⛔ widgets 零改动)、#4824(TextAreaField/RichTextField)无文件交集。Generated by Claude Code
yinlianghui commentedon Aug 16, 2026
测量结论:前置零消费者条件 不成立 —— 停手,未迁移,无 PR
按裁定评论 5307574997 的前置条件执行第一步扫描。结果:
owner不是零消费者。命中 3 处代码 + 4 处文档/示例消费面,其中一处是裁定与本单正文都未列出的第三个代码消费者。按「Any real consumer ⇒ fork report back to the decision box — do not migrate silently」,本次不实施退役、不开 PR,把命中清单交回决策箱。扫描面(诚实记录)
objectstack-ai/objectuiorigin/main@1ef236e18examples/schema-catalog、content/docs、apps、scripts)objectstack-ai/objectstackorigin/main@d491625c1examples/app-showcase、content/docs、packages/spec)objectstack-ai/cloudadd_repo返回 "you don't have access to objectstack-ai/cloud")。裁定要求的「四仓」我只够到两仓,这一面是空白,不是绿。扫描器反查对照(证明零命中处确实是零、而不是扫描器没够到):
field:user在 objectui 命中 3 文件;type: 'user'在 objectstack 命中 8 处以上(含examples/app-showcase/src/automation/flows/*.flow.ts)—— 即扫描确实到达了两仓的 examples / docs / metadata 面。真消费者命中(widget 键 / 字段类型命名空间)
代码 3 处,全部在活路径上:
packages/app-shell/src/utils/paramToField.ts:94——if (LOOKUP_WIDGET_TYPES.has(type) || type === 'user' || type === 'owner')。本单正文与裁定都没列这一处;它是 plugin-gridbulkParamToField的孪生面(后者注释自陈 "mirrors app-shell'sparamToField")。只删裁定点名的三处,会把这一支留成悬空分支。packages/plugin-grid/src/components/bulkParamToField.ts:43——USER_WIDGET_TYPES = new Set(['user', 'owner'])(裁定已点名)。packages/fields/src/index.tsx:2190——registerFieldRenderer('owner', UserCellRenderer)。它被现有门钉住:packages/fields/src/field-type-coverage.test.ts:48的CELL_RENDERER_TYPES显式含'owner',断言「resolves "owner" to a dedicated cell renderer (not TextCellRenderer)」。删它 = 现有测试直接变红。示例 / 文档 4 处,用户可见,其中一条是被真实渲染的活例子:
examples/schema-catalog/src/schemas/fields-user/record-owner-read-only.json:9——"type": "owner"。它不是死文件:在目录索引里注册(examples/schema-catalog/src/index.ts:379的 import、:3730的条目),并被文档站渲染 ——content/docs/fields/user.mdx:18是< SchemaExample id="fields-user/record-owner-read-only" / >(此处<后特意加了空格,避开 GitHub 正文的 HTML 清洗;原文无空格)。它还被examples/schema-catalog/test/fields-form-hosted.test.tsx经真SchemaRenderer+ 真表单渲染器实跑。content/docs/fields/user.mdx:24—— 公开类型联合type: 'user' | 'owner';;:93-94的 "Permission Patterns" 可复制片段{ type: 'owner', name: 'owner', ... };正文「How it works」把user/owner并列描述为sys_user的特化 lookup。content/docs/components/complex/filter-builder.mdx:27——FilterField.type联合含| 'user' | 'owner'。content/docs/core/report-schema.mdx:99—— 报表列type联合含| 'user' | 'owner'。顺带一提:
fields-form-hosted.test.tsx的文件头自陈这批fields-*例子是「the docs site's field demos and a few-shot retrieval source for AI authors, so what they show is what gets copied」—— 即第 4、5 条不只是文档,还是 AI 作者的抄写样本。关键新事实:方案 A 按字面执行是静默降级,现有门只红一半
这一条决定了它不是「删三行」:
mapFieldTypeToFormType的未知类型兜底是packages/fields/src/field-type-alias.ts:155的return typeMap[fieldType] || 'field:text';,resolveFormWidgetType的兜底是packages/fields/src/index.tsx:2486的return fieldWidgetMap[mapped] ? mapped : 'text';—— 两者都是静默的,不告警不报错。而field-type-coverage.test.ts的FORM_WIDGET_TYPES名单里没有owner(也没有user),所以删表单别名不会让它红。fields-form-hosted.test.tsx依然绿 —— 它只断言form元素存在、[data-field="record_owner"]存在、label 文本 "Owner" 落在该字段的 chrome 里,一个 TextField 全部满足。文档站会展示一个错的示例,而没有任何门会报。这也顺带回答了派发里「cell renderer 按测量定夺」:它不但被
field-type-coverage.test.ts:48钉住,还有文档(第 7 条)把owner当作报表/表格列类型宣传,即数据侧owner列确实会流到单元格路径。读数支持保留 cell renderer。objectstack 的 4 处
type: 'owner'不是消费者(不同命名空间)packages/spec/src/conversions/registry.ts:354,365、packages/spec/src/security/sharing.test.ts:200、content/docs/permissions/permissions-matrix.mdx:184、content/docs/protocol/objectql/security.mdx:387—— 全部是 sharing rule 的type判别式(sharingRules[].type),与字段 widget 类型无关。附带一提,那个 sharing 的owner规则形状自己也已经退役(sharing.test.ts:194"should reject the retired owner-based rule shape")。同理,objectui 里role === 'owner'(组织成员角色)与"owner"作字段名(dashboard / grid / report 示例)也都不算消费者,已逐条排除。交回决策箱的问题
方案 A 的前提已被证伪,「A 还是 B」需重裁,且无论走哪条都多出一个迁移决定:
record-owner-read-only.json必须同批迁到type: 'user'—— 但那样它就和同目录的single-user-selection重复、失去存在理由,可能该连同user.mdx的 "Owner Field" 小节整条删除;三处公开类型联合也要去掉'owner'。这是用户可见的文档契约收缩,不该由实施 agent 自行定夺。field:text静默兜底意味着任何存量手写type: 'owner'都会无声退化成文本框。要不要同批给「已退役 widget 名」加显式告警或拒绝(参照field:capability-multiselect的 TOMBSTONE 约定)?否则退役对线上手写 SDUI 是无声破坏 —— 而无声正是 ADR-0049 要消灭的那类失效。paramToField.ts:94这个第三消费者要不要纳入同一张单?它与 plugin-grid 那处必须同进同退,否则两个 param 面再次漂移 —— 正是bulkParamToField的注释想防的。gridTSDoc 那一条(裁定说「无论 A/B 都改」)已独立复核,零消费者属实:
packages/fields/src/widgets/types.ts:166-167的dataSourceTSDoc 写着 "(lookup / user / object-ref / recipient-picker / grid)",而packages/fields/src/widgets/GridField.tsx里 grep 不到dataSource(零命中)。这一条与owner的 A/B 完全解耦,且只是注释。因为本单主体已停手,我没有单独为它开 PR(免得一个只改注释的 PR 挂在一张处置未定的单上),建议决策箱要么并入重裁后的批次,要么单独派一张小单 —— 请指示。Generated by Claude Code
yinlianghui commentedon Aug 16, 2026
【PM 处置 · 测量证伪前提,转回决策箱】objectui 分片 PM(session_01GTRjn8xBqp75dk7kFupVRt):
pm:queue→needs-user-decision。dev 按 2026-08-16 裁定(评论 5307574997)的停手条款执行正确 —— 零消费者前置不成立,零代码零 PR 零 worktree,完整命中清单见 dev 评论 5308145625。测量要点:①代码消费者 3 处,含裁定与正文都未列出的第三面
packages/app-shell/src/utils/paramToField.ts:94(plugin-gridbulkParamToField的孪生,注释自陈互为镜像);②文档/示例消费者 4 处,其中record-owner-read-only.json被content/docs/fields/user.mdx真实渲染、被 hosted 测试实跑,且该测试文件自陈是 AI 作者的 few-shot 检索源;③方案 A 按字面执行是静默降级:表单别名走field:text兜底,删后无任何门变红、catalog 测试空绿(dev 已做红绿不对称预判:cell renderer 有钉、表单别名无钉);④objectstack 仓 owner-as-widget 真零(带field:user邻近词反查);⑤ cloud 仓未扫(凭据无权限),该面是空白不是绿。重裁一问(dev 建议 B,PM 附议):
type: 'owner'无声得到文本框 —— A 不带显式失败等于亲手新增一类肉眼才能发现的元数据错误);widgetHintOnly: true,同批修掉dependsOnLabels无兜底(objectstack#5407 同源洞)。enforce-or-remove 的 enforce 侧同样合规,且此处 enforce 明显更便宜 —— owner 不是「无人用的顺手别名」,是 4 处公开契约在宣传的词汇。附属两件随主裁:paramToField.ts:94 无论 A′/B 都与 plugin-grid 同单同进退(拆单必产生两面判断相反的时间窗);
types.tsgrid TSDoc 更正(零消费者已独立复核属实)并入重裁后批次。关联:#4815(Blocked-by 本单,继续持有)、#4855(dev 顺手立的 FORM_WIDGET_TYPES 守卫缺口 finding,排重裁后)。Generated by Claude Code
os-steve commentedon Aug 17, 2026
Maintainer ruling (2026-08-17, recorded by PM session
session_01DbRmJD3iPhXjKr6vhd4Qkv). The maintainer reviewed the re-framed options after the fork report (comment 5308145625) and the four-card analysis presented in PM chat, and approved its recommendations, verbatim: 「同意」.Ruled: Option A′ — complete retirement of the
ownerwidget alias, with a loud tombstone.This supersedes the 2026-08-16 Option A ruling (whose zero-consumer premise the fork report falsified) and decides against the dev/PM preference for B recorded above. Deciding rationale on record: the spec's closed
FieldType(48 members) has noowner; the word carries zero behavioral delta overuser(same widget); and this card's own measurement history — three code faces already drifted three ways on this one word — is the standing evidence that every extra synonym is another drift channel. A smaller closed vocabulary with loud refusal beats teaching AI authors two spellings for one thing; the same idiom survives verbatim as{ type: 'user', name: 'owner' }.Scope, all one batch:
field-type-alias.tsmapping, theindex.tsxfield:ownerregistration, plugin-grid'sUSER_WIDGET_TYPESmembership — plus the third facepackages/app-shell/src/utils/paramToField.ts:94, same batch (the two param faces move together, never split).field:capability-multiselectTOMBSTONE convention — ⛔ the silentfield:textfallback must not be the retirement's failure mode. This is also the answer to the unscanned cloud surface: any unknown consumer fails loudly instead of silently degrading to a text box.record-owner-read-only.jsonmigrates totype: 'user'or is removed together withuser.mdx's "Owner Field" section (implementer judges the dedup againstsingle-user-selectionand states the choice in the PR); the three public doc type unions (user.mdx,filter-builder.mdx,report-schema.mdx) drop'owner'.field:owner/ownerregistrations removed andfield-type-coverage.test.tsexpectations updated in the same PR.The
gridTSDoc zero-consumer correction (packages/fields/src/widgets/types.ts:166) rides the same batch, per the standing half of the prior ruling. Downstream: #4815 stays Blocked-by this card until it closes; #4855's premise gets re-read after this lands.Also clearing the stale assignee left from the pre-fork claim (that claim ended with the fork report; comment 5308167330 flipped the label but left the assignee — half-state repair). Label flipped
needs-user-decision→pm:queuein the same stroke.Generated by Claude Code
os-steve commentedon Aug 17, 2026
Claim: PM loop round 2
Session:
session_01DbRmJD3iPhXjKr6vhd4QkvBranch:
claude/issue-4814-retire-owner-tombstoneWorktree:
objectui-issue-4814Domain:
repo:objectuiFile surface:
packages/fields/src/field-type-alias.ts+packages/fields/src/index.tsx+packages/fields/src/field-type-coverage.test.ts+packages/fields/src/widgets/types.ts(grid TSDoc line) +packages/plugin-grid/src/components/bulkParamToField.ts+packages/app-shell/src/utils/paramToField.ts+examples/schema-catalog(thefields-user/record-owner-read-onlyexample + its index entries) +content/docs/fields/user.mdx/content/docs/components/complex/filter-builder.mdx/content/docs/core/report-schema.mdx+ tests (stop on breach; explain in the report)Container & model: M,
mode:subagent,model: opusSerial constraints cleared: the #4648 same-package serial (plugin-grid registration surface) is resolved — PR #4901 MERGED 03:05Z, branch from post-merge main. In-flight checked this round: #4636 (lockfile + package.json ranges only), #4886 (sibling seat,
app-shell/views/metadata-admin/ResourceEditPage.tsx), #4003 / PR #4892 (examples/schema-catalog — its 100-file list grepped forfields-user/record-owner-read-only: zero overlap), #4884 / PR #4888 (plugin-gantt) — all disjoint from this surface. Prior claim on this card (branchclaude/issue-4814-retire-owner-alias, sessionsession_01GTRjn8xBqp75dk7kFupVRt) ended with the fork report at comment 5308145625 — zero code, zero PR, zero worktree per the PM disposition; this is a fresh dispatch under the superseding A′ ruling, new branch name to avoid any stale-ref ambiguity.Generated by Claude Code
fix(fields): retire the `owner` field-type alias with a loud tombstone (
fix(app-shell): drop the stale `owner` param value-shape entry (#4814)
os-steve commentedon Aug 17, 2026
{ "issue": 4814, "status": "done", "branch": "claude/issue-4814-retire-owner-tombstone", "pr": "https://github.com/objectstack-ai/objectui/pull/4915", "premise_still_valid": true, "summary": "Implemented ruling A' in full: the `owner` field-type alias, its `field:owner` widget-map entry, its cell-renderer registrations, and both param faces (plugin-grid `bulkParamToField` + app-shell `paramToField`) are retired together; the catalog example migrates to `type: 'user'` and the three public doc type unions drop `'owner'`; the zero-consumer `grid` claim in the `dataSource` TSDoc is corrected (independently re-verified: zero `dataSource` occurrences in GridField.tsx). The tombstone is LOUD as ruled: because `form.tsx`'s `renderFieldComponent` resolves both `type: 'owner'` and `widget: 'field:owner'` through the same `ComponentRegistry.get('field:owner')` lookup, one registered tombstone widget catches both spellings with a visible refusal, plus a console prescription deduped once per spelling. Two caveats the PM should weigh. (1) The A' premise stands and is strengthened, but the ruling's SCOPE enumeration is materially incomplete: re-sweeping for `owner` in the field-type namespace (not the field NAME) finds ~13 further faces across 8 packages, including a published runtime Zod enum in `@object-ui/types` (`reports.zod.ts:49`) that still ACCEPTS `type: 'owner'`, and two published TS unions that are the code twins of the very doc unions this PR shrinks — so docs and code now disagree in the opposite direction. All were present at the fork report's own base commit `1ef236e18`, i.e. pre-existing faces the earlier sweep did not reach, NOT new consumers (checked with `git show 1ef236e18:<path>`), so this is not the dispatch's new-consumer stop-and-report trigger. Filed as #4914 rather than silently widening the batch into 8 unclaimed packages. (2) One face beyond the ruled list IS included because it turned red here and its own drift guard prescribed the fix: `app-shell/src/utils/paramValueShape.ts` declared a value shape for `owner`, and its guard 'no stale contract entries pointing at removed widget types' failed the moment `owner` left FORM_FIELD_TYPES. Also correcting the fork report on one point: it concluded the form side has NO gate that reddens on the alias removal. Removing the `fieldWidgetMap` ENTRY does redden two — `widget-aria-invalid-registry-e2e` and `widget-dom-leak-e2e`, both of which pin a local widget inventory against FORM_FIELD_TYPES. Only the alias-table half was ungated. File surface stayed within the claim (all extra files are tests, covered by the claim's '+ tests'), except paramValueShape.ts as described.", "tests": "Union run at HEAD=59b30a100 (= final commit = pushed remote head; PR head sha confirmed 59b30a100 after a second push). Repo-root path-filter vitest only; heavy runs under `flock -E 99 -w 540 /tmp/os-heavy-verify.lock`. Dependency closures built BEFORE judging — the first typecheck pass failed only on missing dist/*.d.ts (`Cannot find module '@object-ui/fields'`) and went green after `--filter '@object-ui/plugin-grid^...' --filter '@object-ui/app-shell^...' build`. RESULTS: packages/fields/ -> 'Test Files 100 passed (100) / Tests 1689 passed (1689)'; examples/schema-catalog/ -> 'Test Files 9 passed (9) / Tests 1589 passed (1589)' (fields-form-hosted green under the migrated example); app-shell/src/utils + plugin-grid param tests -> 'Test Files 23 passed (23) / Tests 377 passed (377)'. type-check (hyphenated script name, echoed in output so no zero-match trap) for @object-ui/fields, @object-ui/plugin-grid, @object-ui/app-shell -> all 'Done'. Gates: check:doc-types (143 mdx, 632 code blocks, 558 type literals) OK; check:control-bytes (4389 tracked text files) OK; docs:check-links (13 scan roots) OK; changeset:check OK. Also grepped for references to the renamed 'Owner Field' heading anchor: zero. REVERSE VERIFICATION, direction predicted first — predicted the ablation (removing the retired-type branch + tombstone registration, i.e. the naive 'just delete the alias' execution) would redden the new pin ONLY on the loudness assertions while pre-existing gates stayed green. Observed exactly that: 5 failures, all loudness — \"expected 'field:text' not to be 'field:text'\", \"expected 'text' not to be 'text'\", \"expected '' to contain '`owner`'\", the dedupe-count assertion, and \"expected undefined to be [Function RetiredFieldTombstone]\" — while field-type-coverage.test.ts stayed GREEN in the same run ('Test Files 1 failed | 1 passed (2)'), reproducing the fork report's central claim that no existing gate catches a silent retirement. Ablation restored byte-identically from the commit via `git checkout <branch> -- <paths>` (empty `git diff HEAD`). No dist-ablation concern: these suites import from source, not dist.", "open_questions": [ { "question": "The A' ruling scoped 'all three code consumers' + the fourth param face, but the measured field-type consumer surface is ~15 faces across 8 packages. The three published contract twins in particular (`@object-ui/types` reports.zod.ts runtime enum, reports.ts, field-types.ts) now bless a spelling the renderer refuses. Should those ride a fast follow-up, or be re-adjudicated together with the drift-channel argument that decided A'?", "options": [ "A. Fast-follow #4914 items 1-3 only (the published TS/Zod twins) as a small dedicated card — closes the docs/code contradiction this PR introduces, leaves the 12 dead predicate branches for later cleanup", "B. Fast-follow all of #4914 in one PR — mechanically similar work, one review, but 8 packages of collision surface at once", "C. Leave as filed and let normal triage schedule it — the tombstone already makes any surviving `owner` fail loudly, so nothing is silently broken meanwhile" ], "recommendation": "A, because it is the only half that is an active contradiction rather than dead code: `reports.zod.ts` is a PUBLISHED runtime validator that accepts `type: 'owner'` while `@object-ui/fields` now refuses it, and `field-types.ts` still offers `owner` in published .d.ts autocomplete — so an author is told 'legal' by the types package and 'retired' by the renderer, which is the declared-but-unenforced shape ADR-0049 targets and exactly the drift the A' ruling was chartered to end. Items 4-15 are inert by comparison (branches that can no longer match) and can wait for ordinary triage. Note the types/spec question overlaps #2231, so A may want to be scoped as a sub-issue of that rather than standalone." } ], "out_of_scope_findings": [ "filed as #4914: `owner` survives as a live field-type in ~13 more code faces across 8 packages after this retirement — including a published runtime Zod enum (`packages/types/src/zod/reports.zod.ts:49`) that still accepts it and two published TS unions (`reports.ts:105`, `field-types.ts:584`) that are the code twins of the doc unions this PR shrinks; plus 8 dead predicate branches (filter-builder, InlineFieldInput, buildDefaultPageSchema, view-config-utils, ObjectDataTable, recordFields, UserFilters, FieldEditWidget) and 4 test fixtures. All verified present at the fork report's base commit 1ef236e18, so pre-existing rather than new." ] }Generated by Claude Code
Generated by Claude Code
os-steve commentedon Aug 17, 2026
ACCEPT — PR #4915 (reviewer of record: PM session
session_01DbRmJD3iPhXjKr6vhd4Qkv; readings taken on GitHub at head59b30a10, not from the report).main, first lineFixes #4814— correct: the ruled A′ scope completes with this merge; the residual faces areownersurvives as a live field-type in ~13 more code faces after #4814's retirement — including a published Zod enum that still accepts it #4914's, filed rather than silently widened into 8 unclaimed packages.field:ownertombstone registration (skipFallback, never claims the bare name), both param faces in lockstep with mirror-comments, cell path degrading loudly, catalog example migrated, three doc unions shrunk,user.mdxrewritten to state the honest model,gridTSDoc corrected, coverage/e2e inventories updated with one-place-per-fact comments. Real patch changeset on the three released packages.paramValueShape.ts— its own drift guard ("no stale contract entries pointing at removed widget types") turned red the momentownerleftFORM_FIELD_TYPES; mechanical, prescribed by the gate, declared in the report and PR body.readonly: true, not the retired spelling.owner-retired.test.tsxpins LOUDNESS (refusal shape), not absence — the ablation run reproduced the fork report's central claim (coverage test green under the naive deletion) as the predicted asymmetry.@object-ui/typesreports.zod.ts:49runtime enum,reports.ts:105,field-types.ts:584): they are an active contradiction (types package says legal, renderer refuses — the declared≠enforced shape A′ was chartered to end), while the ~12 dead predicate branches are inert behind the tombstone and stay recorded onownersurvives as a live field-type in ~13 more code faces after #4814's retirement — including a published Zod enum that still accepts it #4914 for ordinary triage. Disposition recorded onownersurvives as a live field-type in ~13 more code faces after #4814's retirement — including a published Zod enum that still accepts it #4914; the fast-follow respects the Unify hand-written @object-ui/types zod with @objectstack/spec/ui (ListViewSchema drift) #2231 overlap noted in the report.Ready-flip + auto-merge after the six gate jobs report
successat this head — a pointed check is armed.Generated by Claude Code
test(plugin-detail): drop `owner` from the inline-edit bucket snapshot (
os-steve commentedon Aug 17, 2026
Patch round on PR #4915 — CI Test shard 4/4 fixed. New head
abbc8173e.Same claim, same branch, no restart. The failure was real and caused by this PR — a fifth stale-inventory face, same class as the four already fixed, in a package I never ran.
What it was, and why the snapshot edit is not "forcing"
packages/plugin-detail/src/__tests__/inlineEditTypeCoverage.test.tsx— "the four buckets, as landed".Read before touching, as asked. The snapshot is derived from
ALL_TYPES, defined at the top of that file asFORM_FIELD_TYPES ∪ spec FieldType.options.ownerleft both halves: this PR removed it fromFORM_FIELD_TYPES, and it was never a member of the spec enum — which is this card's own founding premise. Soownerdoes not move between buckets, it leaves the universe the guard quantifies over.- "owner"fromroutedis the complete delta, and the edit records a derived reality rather than overriding one.Two spots, not the one reported:
:234(now:243) — theroutedarray in the snapshot.:77— the universe rationale comment, which still namedowneramong "the form-only keys the spec enum misses". That sentence became false in this PR and would have misinformed the next reader about why the union exists. Refreshed with the same one-place-per-fact mirror style used infield-type-coverage.test.tsand the two e2e inventories, pointing atowner-retired.test.tsxand objectui#4814.The thing you asked me to say rather than force
INLINE_ROUTED_FIELD_TYPES(packages/plugin-detail/src/InlineFieldInput.tsx:107) still contains'owner'. The snapshot does not derive from it, so I left it alone and said so in a comment at the snapshot instead.But sharpening what I filed earlier: this member is not merely dead code. I had classified
InlineFieldInputunder "8 dead predicate branches" in #4914. That classification is too generous. Because the set is consulted at runtime against whatever type a stored field actually carries, a record whose field is still typedownerwould get inline-edit routing to the user picker, while the record form now shows the tombstone refusal. That is a live inconsistency between two edit surfaces for the same field, not an unreachable branch. Same forInlineFieldInput.tsx:290'seditType === 'user' || editType === 'owner'.It is still out of this card's ruled scope and outside the claimed file surface, so I have not changed it — but #4914 item 5 should be read as more urgent than the "inert" framing in my main report, and it strengthens the case for option A in that report's
open_questions.Readings at
abbc8173eThe targeted file passing is itself the proof it resolved the new
FORM_FIELD_TYPES: had it read a stale build still containingowner,ALL_TYPESwould have re-included it and the edited snapshot would have failed in the opposite direction.No changeset impact — tests-only delta on a PR whose changeset is already declared. Prior union at
59b30a100is unaffected by a test-file edit in a package outside it; the scanning gate was re-run at the new head as shown.Generated by Claude Code
Generated by Claude Code
fix(types,plugin-detail): stop publishing the retired `owner` field t…
fix(fields): retire the `owner` field-type alias with a loud tombstone (
fix(types,plugin-detail): stop publishing the retired `owner` field t…