Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

field-type-coverage.test.ts 的表单半边漏掉了 person-picker 家族(user / owner 不在 FORM_WIDGET_TYPES),该守卫对「别名被删」单向失明 #4855

Closed

Description

@yinlianghui

发现于 #4814 的前置消费者测量(方案 A 退役 owner 别名)。不在该卡范围内,且无论 #4814 最终裁 A 还是 B 都独立存在,故单独立此单。

观察类(observation-class):今天没有用户碰得到 —— 两条别名都还在,守卫没有在保护一个已经破的东西。它记录的是守卫本身的覆盖洞。

事实(实读 origin/main @ 1ef236e18)

packages/fields/src/field-type-coverage.test.ts 是「field-type → renderer 注册表」的回归守卫,文件头自陈它防的正是这个失效模式:

These mappings regressed once already: ~25 specialized field types silently fell back to a plain text input (form) and the raw text cell renderer (read), because the mapping tables were incomplete.

它有两张名单,分别钉两条路径:

  • CELL_RENDERER_TYPES(:48)—— 含 'user' 与 'owner',断言二者解析到专用 cell renderer 而非 TextCellRenderer。
  • FORM_WIDGET_TYPES(:19-32)—— 既不含 user,也不含 owner。

所以表单那一半对 person-picker 家族是空的:packages/fields/src/field-type-alias.ts 里的 user: 'field:user'(:104)与 owner: 'field:owner'(:105)任意一条被删,mapFieldTypeToFormType 会走 :155 的 return typeMap[fieldType] || 'field:text'; 静默兜到文本框,而这个守卫不会红。lookup / master_detail / tree 等其余引用类型都在 FORM_WIDGET_TYPES 里,唯独 person-picker 这一支不在。

为什么是「单向失明」而不是「对称缺失」

同一个守卫的 cell 半边是钉住的。于是删 registerFieldRenderer('user'|'owner', UserCellRenderer) 会红,删对应的表单别名不会红 —— 同一个家族、同一个文件、两条路径,红绿方向相反。#4814 的测量就是在这个不对称上撞出来的:按字面执行退役,cell 那半立刻红、表单那半静默降级,读起来像「测试通过了所以退役是安全的」。

影响

今天为零 —— 两条别名都在,field:user / field:owner 都能解析。这条记录的是守卫的可信度:一份自称防「静默退化成文本框」的名单,恰恰在最容易被当成冗余别名删掉的两个键上没有覆盖(owner 正在 #4814 被提议删除,user 是 #4790 一族刚动过的邻居)。

建议(不预设结论)

把 user 加进 FORM_WIDGET_TYPES 是显然的一半。owner 那一半取决于 #4814 的重裁结果 —— 若 owner 退役,它就不该进这张名单,而该进「已退役键」的反向断言(参照 index.tsx 里 field:capability-multiselect 的 TOMBSTONE 约定)。因此这张单建议排在 #4814 重裁之后,或者先只补 user、把 owner 留给 #4814 收口。

参考

Activity

  1. os-support-ai commented on Aug 18, 2026

    @os-support-ai
    Collaborator

    Triage — first-touch grading (triage seat, session session_01KTguWpd7WNGYTGmSNY31RL): promoted to pm:queue, type Task — and the scope has narrowed since filing. Re-verified on origin/main (current head):

    Remaining scope: add 'user' to FORM_WIDGET_TYPES (one line + whatever the it.each needs, i.e. nothing). Mechanical S; restores the guard to what its own header claims to protect.


    Generated by Claude Code

  2. os-support-ai commented on Aug 18, 2026

    @os-support-ai
    Collaborator

    Claim: PM loop round 12 (refill)
    Session: session_01RV6yuVCxymHYE16PL9vQkE
    Branch: claude/issue-4855-form-widget-types-user
    Worktree: objectui-issue-4855
    Domain: repo:objectui (fields)
    File surface: packages/fields/src/field-type-coverage.test.ts (stop on breach; explain in the report)
    Container & model: S, mode:subagent, model: sonnet
    Clause-②: no — test-only; restores a guard to the coverage its own header claims.
    Serial constraints cleared: packages/fields is free — no open PR and no in-flight claim touches it. Deliberately checked the adjacent one: #4914 (owner surviving as a live field-type across ~13 code faces, including a published Zod enum) is pm:queue, unassigned and not dispatched, so no collision — but it is the card most likely to touch this same package next, and it must not be dispatched concurrently with this one.

    Scope is triage's narrowed version, not the filed one: owner is already resolved (#4814 ruling A′ executed — tombstone at field-type-alias.ts:74-77, retirement pinned the opposite direction by __tests__/owner-retired.test.tsx). ⛔ Do not add owner to FORM_WIDGET_TYPES; that would re-assert a retired key and contradict the tombstone pin. Remaining scope is exactly one key: 'user'.


    Generated by Claude Code

  3. added 2 commits that reference this issue on Aug 18, 2026
    62b2713
    07d895c
  4. os-support-ai commented on Aug 18, 2026

    @os-support-ai
    Collaborator

    os-dev-report

    {
      "issue": 4855,
      "status": "done",
      "branch": "claude/issue-4855-form-widget-types-user",
      "pr": "https://github.com/objectstack-ai/objectui/pull/5277",
      "premise_still_valid": true,
      "summary": "Added 'user' to FORM_WIDGET_TYPES in packages/fields/src/field-type-coverage.test.ts, closing the one-way blindness where CELL_RENDERER_TYPES already pinned 'user' but the form half did not. Verified triage's narrowed scope on origin/main first: owner is already retired (tombstone at field-type-alias.ts:74-81, opposite-direction pin in owner-retired.test.tsx) so it was deliberately NOT added, per the dispatch's hard limit. Reverse verification (mandatory) confirmed the fix: deleting user: 'field:user' from field-type-alias.ts made exactly the new form-half assertion red while the cell half (independently registered via registerFieldRenderer in index.tsx) stayed green, matching a prediction made before running.",
      "tests": "All run from repo root against HEAD 07d895cfd. pnpm exec vitest run packages/fields/src/field-type-coverage.test.ts: 88/88 passed. pnpm exec vitest run packages/fields/ (full package suite, exercises owner-retired.test.tsx): 107 files / 1802 tests passed. Reverse verification: deleted user: 'field:user' from field-type-alias.ts, predicted only the form-half 'user' assertion would go red (cell half resolves via an independent registerFieldRenderer call, not this alias table) -- observed exactly 1 failure (the predicted one), 87 passed, then restored the alias line and confirmed a clean git status/diff. pnpm --filter '@object-ui/fields^...' build (dependency closure, required first -- a stale/missing dist otherwise gives false TS2307 errors unrelated to this change). pnpm --filter @object-ui/fields type-check: clean. pnpm --filter @object-ui/fields lint: 0 errors, 839 pre-existing warnings untouched by this diff. node scripts/check-control-bytes.mjs: OK. node scripts/check-changeset-presence.mjs: required a changeset (test file lives under packages/fields/src/); added .changeset/field-type-coverage-user-form-widget-4855.md with empty frontmatter (test-only, no published behaviour change), confirmed passing.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  5. os-support-ai commented on Aug 18, 2026

    @os-support-ai
    Collaborator

    PR #5277 is up — and its ablation corrects a prediction I put in the dispatch order.

    I wrote: "delete user: 'field:user' from field-type-alias.ts, predict that the form half now goes red (it should — that is the whole fix) alongside the cell half."

    That last clause is wrong. The dev predicted the opposite before running, from reading the code, and observed it:

    x maps "user" to a dedicated form widget (not field:text)
      AssertionError: expected 'field:text' not to be 'field:text'
    Tests  1 failed | 87 passed (88)
    

    Only the form half went red. The cell half stayed green, because getCellRenderer('user') resolves through an independent registration — registerFieldRenderer('user', UserCellRenderer) in index.tsx plus a literal map entry — which never consults field-type-alias.ts's typeMap at all.

    Worth being precise about why I was wrong, because the card had it right and I garbled it: the card says "删 registerFieldRenderer('user'|'owner', UserCellRenderer) 会红,删对应的表单别名不会红" — i.e. the two halves have independent resolution paths, and which mutation you make decides which half reddens. Deleting the alias was only ever going to touch the form half. I collapsed two different mutations into one expected outcome.

    This also sharpens what the card is actually about. The asymmetry is not "one list is missing an entry" — it is that the two halves of one guard resolve through two unrelated mechanisms, so a single list omission makes the guard blind in exactly one direction with no signal. The fix closes that, and the ablation is what proves it rather than merely asserting it.

    The owner boundary was held: left out of FORM_WIDGET_TYPES, tombstone at field-type-alias.ts:74-81 untouched, and the full packages/fields suite (107 files / 1802 tests) run so owner-retired.test.tsx was exercised alongside the change rather than assumed unaffected.


    Generated by Claude Code

  6. added a commit that references this issue on Aug 23, 2026
    6f01aa3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions