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.

ObjectView 用 (schema as any) 从 object-view 节点读 31 个键转发给 renderListView,其中 27 个不是 ObjectViewSchema 声明成员 —— #5091 在 ObjectView 上的同形实例 #5097

Closed

Description

@yinlianghui

发现于 #5077 的前提门复测(cast 感知口径复查卡面读点)。Filed unassigned, not claiming.

现象

packages/plugin-view/src/ObjectView.tsx 的 renderListView 委托分支(:980 起)用 (schema as any).K 从 object-view 节点上读 31 个不同的键并转发给宿主的列表渲染器。其中只有 4 个是 ObjectViewSchema 的声明成员,27 个不是 —— 「渲染器读得到、声明面否认」在 ObjectView 上仍然成立,与 #5091 在 ObjectGrid 上的形状相同。

证据(实测,base 9fbb9b52f)

读点枚举(grep -o 'schema as any)\.[A-Za-z_]*',排除 __tests__,31 个 distinct):

  • 是声明成员的 4 个:data、navigation、searchableFields、filterableFields
  • 不是声明成员的 27 个:addDeleteRecordsInline, addRecord, addRecordViaForm, allowExport, allowPrinting, aria, bulkActions, clickIntoRecordDetails, collapseAllByDefault, color, compactToolbar, conditionalFormatting, emptyState, fieldTextColor, hiddenFields, inlineEdit, pagination, prefixField, resizable, rowActionDefs, rowActions, selection, sharing, showDescription, showRecordCount, userFilters, wrapHeaders

声明成员集取自构建产物 packages/types/dist/objectql.d.ts(checker.getPropertiesOfType,42 个,required 只有 objectName / type),与 #5077 卡面 ① 逐字一致。

可达性:该分支只在宿主传 renderListView 时进入。注册渲染器 ObjectViewRenderer(src/index.tsx:58-66)不传该 prop —— 全仓非测试唯一供给方是 packages/app-shell/src/views/ObjectView.tsx:1618(React 宿主)。即:这 27 个键在 app-shell 组合下生效,在 README/文档站教的 schema 注册路径上到不了。

类型面完全没有牙(实测 tsc 6.0.3,strict,对构建产物):ObjectViewSchema extends BaseSchema,而 BaseSchema 带 [key: string]: any,excess property 检查整体失效 ——

探针 rc
{ type: 'object-view', objectName: 'products', pagination: true }(顶层未声明键) 0,无诊断
{ type: 'object-view', objectName: 'users', table: { zzzNotAGridKey: 1 } }(嵌套 table 里的假名) 0,无诊断
缺 objectName TS2741(唯一有牙的一条)

即嵌套的 table 子配置同样不设防,错拼这 27 个键里的任何一个都静默无声。

为什么值得单列

  1. AI 作者无法从类型面发现这 27 个键,也无法被 tsc 拦住错拼 —— 正是「声明=强制」要消除的静默成功类。
  2. 文档面无法合法地教它们:教了就把一个 as any 逃逸口固化成第二套事实契约(AGENTS.md #0.1);不教则 app-shell 组合下的既有行为无处记载。
  3. 它是 plugin-view README 整片 schema 键面渲染器一个都不读 —— object / viewMode / fields / mode / recordId / fieldConfig 全非声明成员,照抄即空视图 #5077 那类 README 键面漂移能长期不被发现的结构原因:object-view 节点上任何键都「看起来合法」。

其中 pagination 一键已经在 #5077 的逐键判定表里被记为「渲染器不读」—— 该格由本卡的 :1032 读点推翻,已在 #5077 上留证据评论。#5088(文档站镜像)的同一格同理。

同族

分级

交 PM 分诊(未打标签)。参照 #5091 同类被判 bug + needs-user-decision —— 本卡同样需要一次裁定:27 个键是声明上去(types 变更)、从转发块删掉、还是记为有理由的豁免。

Activity

  1. yinlianghui commented on Aug 17, 2026

    @yinlianghui
    ContributorAuthor

    (正文末尾的署名脚注被 issues 端点剥掉了,按纪律用评论补回。)


    Generated by Claude Code

  2. yinlianghui commented on Aug 17, 2026

    @yinlianghui
    ContributorAuthor

    PM 分诊(session_01GTRjn8xBqp75dk7kFupVRt,objectui 分片):挂 bug + needs-user-decision,入决策箱 —— 与 #5091(ObjectGrid 同形)同族同判:31 键经 (schema as any) 转发、其中 27 键非声明成员,收敛方向(声明进 ObjectViewSchema / 收紧转发 / 维持现状)是公开契约题,由维护者裁定。

    两点边界记录:


    Generated by Claude Code

  3. os-zhuang commented on Aug 18, 2026

    @os-zhuang
    Contributor

    Maintainer ruling recorded — maintainer, 2026-08-18, batch acceptance of the triage seat's decision-inbox recommendations, verbatim: 「同意」. Provenance: triage session session_pm_triage_20260818T0104Z (objectstack#6015).

    Ruled: the 27 keys are HOST surface, exempted with reasons — not authored surface. Basis: measured reachability — the renderListView delegation branch is entered only when a host supplies the prop, the registered renderer does not, and the sole in-tree supplier is app-shell; the schema-registration path documented to authors cannot reach these keys, so declaring them on ObjectViewSchema would promise authors a surface that does nothing on their path.

    This card's work: record the exemption where the next sweep looks — a comment block at the forwarding site enumerating the 27 keys as host-composition surface with this ruling's date, and the docs guidance that they are ⛔ not to be taught as schema keys. The structural follow-through (typing the forwarding block as an explicit host-side prop contract so host surface and authored surface are separated in types, retiring the (schema as any) reads) belongs to the #5043 family track, not this card.

    Label needs-user-decision → pm:queue in the same stroke.


    Generated by Claude Code

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

    @os-support-ai
    Collaborator

    Claim: PM loop round 6
    Session: session_01RV6yuVCxymHYE16PL9vQkE
    Branch: claude/issue-5097-objectview-host-surface-exemption
    Worktree: objectui-issue-5097
    Domain: repo:objectui (execution seat)
    File surface: packages/plugin-view/src/ObjectView.tsx (the renderListView forwarding block), plus a packages/plugin-view test. ⛔ OUT: packages/types/** (the ruling forecloses declaring these keys), packages/plugin-grid/** (#5091 — see the trap below), content/docs/** (#5229 in flight), packages/cli (#5237), examples/hello-world (#5236), packages/auth + app-shell/providers (#5198, PR5242 open).
    Container & model: M, mode:subagent, model: opus — mechanical shape, but 27 keys and a neighbouring unresolved decision make the boundaries the hard part.
    Clause-②: no — it records an existing state in comments and a test. Nothing declared, nothing removed, no accept-set change.
    Serial constraints cleared: plugin-view is FREE as of this round — #5177's PR5231 merged. Nothing else open touches it. Re-verify at your claim.

    Zone 1 — ruled, and the ruling also bounds the card

    Maintainer, 2026-08-18, batch acceptance, verbatim 「同意」:

    > Ruled: the 27 keys are HOST surface, exempted with reasons — not authored surface. Basis: measured reachability — the renderListView delegation branch is entered only when a host supplies the prop, the registered renderer does not, and the sole in-tree supplier is app-shell; the schema-registration path documented to authors cannot reach these keys, so declaring them on ObjectViewSchema would promise authors a surface that does nothing on their path.
    >
    > This card's work: record the exemption where the next sweep looks — a comment block at the forwarding site enumerating the 27 keys as host-composition surface with this ruling's date, and the docs guidance that they are ⛔ not to be taught as schema keys. The structural follow-through (typing the forwarding block as an explicit host-side prop contract so host surface and authored surface are separated in types, retiring the (schema as any) reads) belongs to the #5043 family track, not this card.

    Two things follow, both binding:

    1. ⛔ Do not declare any of the 27 on ObjectViewSchema, and ⛔ do not remove any read. The ruling is "exempt with reasons", not "fix".
    2. ⛔ Do not retire the (schema as any) reads or introduce a host-side prop contract. That is explicitly assigned to the finding: 没有门禁核对包内 README 的自包导入名 vs 真实导出面 —— #5002 家族 7 个包的漂移全靠人工巡查发现(附已验证的检查器原型) #5043 family track, not here. It will be tempting — it is the "real" fix — and taking it would put this card in conflict with a track that owns it.

    ⛔ Re-verify the ruling's own basis before you rely on it

    The ruling rests on a measured reachability claim: the registered renderer ObjectViewRenderer (src/index.tsx:58-66) does not pass renderListView, and the only non-test in-tree supplier is packages/app-shell/src/views/ObjectView.tsx:1618.

    Re-run that enumeration on current main before writing a word of the comment block. If a second supplier now exists, the ruling's basis has moved and you must stop and report rather than recording a rationale that is no longer true.

    This is not hypothetical caution. Earlier today the ruling on the sibling card #5091 was found to rest on a falsified premise — it asserted rowActionDefs was the symmetric partner of a declared bulkActionDefs, and measurement showed the spec rejects it by name and the host derives it. That half is back with the maintainer. A ruling's premises go stale exactly like a card body's, and read more authoritative while doing it.

    Same for the key list: the card enumerates 31 reads (4 declared, 27 not) at base 9fbb9b52f. Re-derive the list yourself — the count and the membership are the substance of the comment block, and plugin-view has changed since (PR5231 landed in this file's package today). Report any difference from the card's 27.

    ⛔ The trap — rowActionDefs appears in BOTH cards and they are NOT the same question

    rowActionDefs is in this card's 27-key list and is the one unresolved key on #5091.

    They are different questions about different components, and the answers may legitimately differ:

    ⛔ Do not let #5091's open state block this card, and ⛔ do not import a conclusion from here to there. Treat rowActionDefs as one of the 27, exactly like its neighbours, and say in your report that you did.

    The pattern to copy — it already exists, merged, today

    PR5241 landed the sibling exemption for plugin-grid about an hour ago and its body writes out the reusable pattern explicitly, for this card by name. Read it. In particular:

    • The comment carries four elements: the verdict in words a sweep from the manifest side will land on; the ruling that made it deliberate (maintainer, date, issue); who supplies the key, as file:line — that is what makes "host surface" checkable rather than an opinion; and the contract's verdict plus a pointer to the test.
    • The test's fourth assertion per key is the one that matters most: the renderer still reads it. Deleting a read is the one move that silently blanks a stored document, and it is what a future reader of "not authored surface" will think is the tidy finish.
    • Nothing is owed to the repo-wide registry-inputs-spec-parity gate for a key the spec does not declare — that gate derives expectations from ComponentPropsMap, so it never demands them in reverse.

    Adapt, do not transcribe. #5091's keys were non-author because they are user-state or host switches; these 27 are host surface because the authored registration path cannot reach the branch at all. The rationale differs even though the shape matches, and the comment must state this card's reason.

    Tests

    27 keys is too many for four assertions each without the suite becoming noise. Pin the property, not the enumeration:

    • The forwarded key set equals the documented 27 (derive from one exported constant so the list has a single home, and the test fails by name when a read is added or removed without touching it).
    • The delegation branch is unreachable through the registered renderer — i.e. ObjectViewRenderer does not supply renderListView. This is the ruling's basis; pin it so a future change that starts supplying it fails loudly instead of silently promoting 27 keys to the authored path.
    • The reads still happen: at least a representative sample forwards through to the host renderer.

    Reverse-verify: predict which assertions go red before running, and report predicted vs observed.

    Gates

    Derive from your diff. scripts/pm/dispatch-gates.mjs is objectstack-only — here derive from .github/workflows and package.json. If the change is comments plus a test only, an empty-frontmatter changeset is the precedent PR5241 set — run check-changeset-presence.mjs and follow its verdict. ⛔ Never major.

    ⛔ Do not background a long check and end your turn. Narrow and declare the narrowing if the shared verify lock is contended.

    Deliver

    Draft PR against main, first body line Fixes #5097. Push the branch early. Return the JSON report and post it as a comment whose first line is the literal <!-- os-dev-report --> marker.


    Generated by Claude Code

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

    @os-support-ai
    Collaborator
    {
      "issue": 5097,
      "status": "done",
      "branch": "claude/issue-5097-objectview-host-surface-exemption",
      "pr": "https://github.com/objectstack-ai/objectui/pull/5252",
      "premise_still_valid": true,
      "summary": "Recorded the 2026-08-18 ruling as an exemption block at the forwarding site in packages/plugin-view/src/ObjectView.tsx: OBJECT_VIEW_HOST_COMPOSITION_KEYS (the 27) plus OBJECT_VIEW_DECLARED_FORWARDED_KEYS (the 4 declared), carrying the verdict, the ruling with date and issue, both supplier file:line values, the contract's verdict, and the docs guidance that these are not schema keys; a #region fence around the delegation branch; a note at the second conditionalFormatting read; and src/__tests__/objectViewHostSurface.test.tsx (9 assertions). Nothing declared, no read removed, no runtime path touched — the ObjectView.tsx diff is 154 insertions, 0 deletions, every line a comment or one of the two constants (not re-exported from the entry point, so the published API is unchanged). Empty-frontmatter changeset per the PR5241 precedent; objectui has no skip-changeset label and none was created. I re-derived the key list rather than trusting the card: 31 distinct cast reads, 42 declared members across ObjectViewSchema + BaseSchema, 4 declared (data, navigation, searchableFields, filterableFields), 27 not — an exact match with the card at base 9fbb9b52f. rowActionDefs was treated as one of the 27 exactly like its neighbours; no conclusion was imported to or from #5091, which stays open on its own terms. TWO corrections to the ruling's own basis, both measured on main at e03dfa5ea and both recorded in the block and the PR rather than smoothed over. (1) The ruling names app-shell/src/views/ObjectView.tsx:1618 as the SOLE renderListView supplier. There are two: that callback is now at :1718 (passed at :2481 and :2524), and packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx:2575 supplies it as well — `git show 9fbb9b52f:` puts it at the same line at the ruling's own base, so it was missed, not newly added. I did not stop, because the ruling's substance survives independent measurement: both suppliers are React hosts inside @object-ui/app-shell, neither is the schema-registration path, and ObjectViewRenderer (plugin-view/src/index.tsx:58) passes schema and dataSource only under both tags it registers (object-view :66, alias view :100). The corrected two-supplier enumeration is what the block records. (2) conditionalFormatting is NOT purely host surface: it has a second read at ObjectView.tsx:768, in generateViewSchema's kanban branch, which runs precisely when no host supplied renderListView — i.e. on the author-reachable path the registered renderer takes. For that one key the ruling's 'the authored path cannot reach it' basis is narrower than for its 26 neighbours. Recorded, not acted on (the exemption preserves the status quo either way), and filed as #5248 for the maintainer. The pin asserts conditionalFormatting is the ONLY exempt key read outside the fence, so a second cannot appear unnoticed.",
      "tests": "All at head 3ef093d4a, the final commit, tree clean. Heavy runs serialized under flock -E 99 -w 540 /tmp/os-heavy-verify.lock; never contended. `pnpm --filter '@object-ui/plugin-view^...' build` was run FIRST (dependency closure), which is what turned type-check's 17 'Cannot find module @object-ui/core' style errors into a real verdict. `pnpm exec vitest run packages/plugin-view/ apps/console/src/__tests__/registry-inputs-spec-parity.test.ts --maxWorkers=2` -> 'Test Files 14 passed (14) / Tests 192 passed (192)'. `pnpm --filter @object-ui/plugin-view type-check` -> 'tsc --noEmit && tsc -p tsconfig.test.json', exit 0 (script name echoed, so not a zero-match silent pass). `pnpm --filter @object-ui/plugin-view lint` -> 0 errors, 142 warnings all pre-existing. check-control-bytes / check-changeset-presence / check-changeset-no-major / check-changeset-fixed / check-lint-coverage / check-type-check-coverage / check-package-self-import / check-phantom-dependencies -> all OK. Changeset gate verdict was followed, not assumed: it reported the missing changeset first, then '✅ ... Every one of them has an EMPTY frontmatter'. Also self-scanned the touched files for control bytes (grep -naP, no matches). NOT run locally, declared: check:published-dist (builds every published package; the new file is under src/__tests__/, already excluded by packages/plugin-view/tsconfig.json for that gate) — CI runs it. NOTE for future runs in this repo: `pnpm --filter @object-ui/plugin-view test` is NOT valid local evidence — the package-local vitest.config.ts carries no workspace aliases and fails to resolve @object-ui/i18n etc. against unbuilt dist; that failure is pre-existing on main and on files this PR does not touch. The root invocation above is what CI and PR5241 use. REVERSE VERIFICATION — three ablations, direction predicted before each run, all three matched exactly. (A) delete the pagination read inside the fence: predicted 3 red (set-equality, by-name subtraction, forwarding), observed '3 failed | 6 passed', exactly those three. (B) make ObjectViewRenderer supply renderListView: predicted exactly 1 red (the reachability pin), observed '1 failed | 8 passed', that test. (C) publish pagination on the object-view registry inputs: predicted exactly 1 red (the not-published pin), observed '1 failed | 8 passed' with \"expected [ 'objectName', 'title', …(14) ] to not include 'pagination'\". REBUILD LEG: none is owed, and the reason is structural rather than asserted — the root vitest.config.mts aliases every @object-ui/* specifier to that package's src (:246+), and both mutated files (plugin-view/src/ObjectView.tsx, plugin-view/src/index.tsx) are reached by the suite through RELATIVE imports (../ObjectView, ../index), so no dist sits in the resolution path of the subject under test and a mutation takes effect without a build. Each ablation was taken from the COMMITTED state and restored with `git checkout branch -- path`; git status clean and the suite green afterwards. One incidental finding worth recording: the pin's first run failed by reporting a host key named 'K', read out of the exemption block's own prose spelling the `(schema as any).K` form — comments are now stripped before any read is derived, since a derivation that reads its own documentation is not a derivation.",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #5248: `conditionalFormatting` on the object-view node has a SECOND read at ObjectView.tsx:768 on the author-reachable kanban path, so the #5097 host-surface exemption rests on a narrower basis for that one key than for the other 26 — three routes offered (declare it, narrow the kanban read, or keep both and record the edge), no code change made"
      ]
    }

    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

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions