Skip to content

feat(spec,lint,metadata-protocol): a page member on the view type enum — mount a published page on an object view - #13372

Draft
claude[bot] wants to merge 5 commits into
mainfrom
claude/issue-13216-view-type-page-member
Draft

feat(spec,lint,metadata-protocol): a page member on the view type enum — mount a published page on an object view#13372
claude[bot] wants to merge 5 commits into
mainfrom
claude/issue-13216-view-type-page-member

Conversation

@claude

@claudeclaudeBot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Part of #13216

Direction 1 of the maintainer's ruling of 2026-08-29 (live director session, verbatim 「同意」): a page member on the viewtype enum, so an object's list view / tab can mount an already-published custom page. Direction 3 (indexing published pages in the command palette) is not in this PR — see "What is left" below. Direction 2 stays deferred to its own design card.

Clause-②: yes

Both limbs fire.

  • content limb — widening a published enum is a contract addition. The ruling says so twice: 「枚举扩宽属条款② content 肢,needs:contract-review 照挂」 and 「枚举加宽 = 已发布契约加法 ⇒ 条款②契约复审」.
  • path limb — the change necessarily edits packages/spec/src/**.

Landing posture, mirroring PR #13287: this PR parks at draft and carries needs:contract-review from creation. It has not been flipped ready, auto-merge has not been armed, and it has not been enqueued — the contract-review chain owns enqueue.

Zero new authorization surface — the ruling's stated basis, checked

The ruling's basis is that this goes through a door the platform deliberately left open. Measured on this tree rather than assumed:

  • view is allowOrgOverride: true + allowRuntimeCreate: true in DEFAULT_METADATA_TYPE_REGISTRY; page is allowRuntimeCreate: true. Both writes already exist for a runtime author, and this change touches neither flag, nor app.allowOrgOverride, on any type.
  • A page view holds a NAME. The page itself is still fetched through the metadata read path it already had, and rendering is delegated to the existing page renderer — so page.assignedProfiles, the page's own audience gate, rides along unchanged. Delegation is what preserves it: writing a second renderer is what would have introduced a second gate.
  • No new REST route, no new write door, no new read door.

The stop condition in the dispatch (report if direction 1 turns out to need an authorization decision reversed) did not fire.

What lands

1. The enum member and its binding (packages/spec/src/ui/view.zod.ts)

ListViewSchema.type gains page, and the shape gains pageName, declared with SnakeCaseIdentifierSchema — the same grammar PageSchema.name carries, so the set of accepted values is exactly the set of strings that could name a page.

checkListViewPageMount refuses, at parse, in both directions:

authoredverdictwhy
type: 'page' with no pageNamerefusedevery other view type degrades to a wrong-but-visible list when its binding is missing; a page view has no rows to fall back to, so it renders nothing
pageName on any other view typerefusedaccepted-and-ignored, the failure mode this file's VIEW_HISTORY exists to record
type: 'page' with non-empty columnsrefusedcolumns is the one required key on a list view and the only truthful value here is []; leaving it unchecked would mean forcing an author to write a value and then ignoring it

The check is attached at all three list-view doors — ListViewSchema, ObjectListViewSchema (objects[].listViews.*), and the flattened runtime overlay behind PUT /api/v1/meta/view — because zod 4 refuses .omit() and key-overwriting .extend() on a refined object, so the derived shapes are built from an unrefined module-private shape and re-attach it. viewDoorsCarryingPageMountCheck in view.test.ts fails if any attachment is dropped; the ablation below proves it does.

columns is deliberately not relaxed to spare the author the []. The union tells its list and form overlay members apart by exactly "no required columns, disjoint type enum", with the list arm tried first, so an optional columns would let a flattened FORM overlay match the list arm and have its sections silently stripped. There is a test pinning that property.

page is also not added to VisualizationTypeSchema: the visualization switcher offers alternative ways to draw the same rows, and a page draws none.

2. Existence of the target page, answered where the collection is visible

  • defineStack's validateCrossReferences (packages/spec/src/stack.zod.ts) refuses at build time, under the identical pageNames.size > 0 policy the two other page references in that same function already use — one reference kind, one policy, not three.
  • A new @objectstack/lint rule, view-page-unresolved (validateViewPageRefs), joins the reference-integrity suite, so it speaks on os validate / os lint / os compileand at the runtime publish gate. Advisory, not gating, for its nav twin's reason: with no curated cross-package page registry, "unresolved here" cannot be told apart from "provided by a package this stack cannot see".

3. The runtime publish gate reaches the mount — the door #13100 actually measured

The mount is authored by an agent that publishes a page and then writes a view through PUT /api/v1/meta/view. No CLI is on that path, so a build-time-only rule would never speak to its author. Crossing the suite member onto view snapshots needed the per-write snapshot to carry pages: RuntimeStackContext.pages is threaded through evaluateRuntimeAuthoringGate and read off the live registry in saveMetaItem's gate call. That is the one-key widening RuntimeStackContext documents — made when a rule that reads the collection crossed the wall, never in advance.

Two consequences that came with it, both load-bearing:

4. Studio form, liveness ledger, generated artifacts.view.form.ts gains a page section; liveness/view.json gains a pageName entry with in-repo evidence pointers; authorable-surface/ui.json, content/docs/references/**, api-surface/, the react-blocks contract and the i18n bundles are regenerated by their own generators.

Boundary note — two generated files under skills/

The dispatch listed skills/** as untouched. Two files there changed, both generated, both by the gate's own prescribed repair (pnpm --filter @objectstack/spec gen:react-blocks), because check:react-blocks derives the ListView react-block contract from ListViewSchema and goes red otherwise. Readings, as the standing clause requires:

filebeforeafterdelta
skills/objectstack-ui/contracts/react-blocks.contract.json5595590
skills/objectstack-ui/references/react-blocks.md1171170
whole published skills/ tree17858178580
sum of all SKILL.md10515105150

Net zero lines: one line replaced in each file, both adding 'page' to a rendered union. No SKILL.md changed. Flagging it rather than deciding it — if the reviewer would rather this PR left the contract stale, say so and I will drop the two files and record check:react-blocks as a known red.

Verification

All numbers below are from HEAD 90042c37, and the union was re-run on that commit after the last change. origin/main was merged in and the os-regen artifacts regenerated from the merged tree (scripts/pm/os-regen-merge.sh — the merge is committed FIRST, so the authorable-surface anchor cannot roll back to the old fork point); everything below is the run after that, and the diff against origin/main carries only this branch's changes (36 files, +1173/-54) with nothing of main's reverted.

Tests@objectstack/spec 443 files / 11803 tests passed; @objectstack/lint 84 files / 2355 tests passed; @objectstack/metadata-protocol 145 files / 2017 passed, 10 skipped.

Whole-workspace typecheckturbo run typecheck --concurrency=2: 130 successful, 130 total. This is the downstream-consumer sweep and then some: it is every package in the workspace, which strictly contains every consumer of @objectstack/spec.

Whole-repo ESLintpnpm lint (eslint . --no-inline-config, the whole tree, no narrowing): exit 0.

Gate union, all green at 90042c37 — derived with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack and re-derived against the actual change set: check:authorable-surface, check:liveness, check:strictness-ledger, check:spec-changes, check:upgrade-guide, check:adr-0087-registration, check:generated, check:docs, check:empty-state, check:variant-docs, check:api-surface, check:react-blocks, check:skill-refs, check:skill-docs, check:skill-frame-sync, check:skill-compatibility, check:llms-txt, check:yaml-examples, check:i18n, check:i18n-coverage, check:i18n-stale-fill, check:app-nav-i18n, check:type-check-coverage, check:type-check-debt (--re-measure: 30 ledger entries, 1558 raw errors, none above its recorded number), check:dual-build-cjs-loads, check:cross-package-test-inputs, check:engine-double-contract, check:where-matcher, check:query-options-erasure, check:test-source-alias, check:objectql-double-limit, check:type-source-resolution, check:page-declaration-shape, check:slot-lookup, check:stack-collection-maps, check:spec-parsed-alias, check:doc-authoring, check:doc-formula-expressions, check:durability-log-level, check:logger-receiver-detach, check:filter-alias-parity, check:dispatcher-error-vocabulary, check:published-files, check:nul-bytes, check:merge-driver, check:changeset-gate-self-tests, check:empty-changeset, check:objectui-changeset, check:partof-closing-keyword, and check-reference-carrier-shape (run directly — Lint & Repo Gates invokes it by path, and the path derivation does not name it; this is the gate that cost PR #13322 a CI cycle, filed as #13333).

NOT MEASURED, quoted rather than counted as a passcheck:react-declaration-parity:

Cannot run here (1 source audit(s) whose input this repo cannot produce): check:react-declaration-parity — needs MANIFEST=(sdui.manifest.json) — objectui's registry-inputs dump

Ablation 1 — the three-door attachment is not vacuous. Dropped .superRefine(checkListViewPageMount) from the flattened-overlay door only. Mutation proved on disk before reading anything (injected line count 1, deleted line count 0, git hash-object moved 0e25f8fe… to aa992a9c…). Predicted direction: red on the overlay rows only. Observed: exactly the 3 overlay cases failed, the ListViewSchema and ObjectListViewSchema rows stayed green — Tests 3 failed | 295 passed (298). Restore verified by hash against the HEAD blob (0e25f8fe…, equal) and an empty git diff HEAD, not by an exit code. No rebuild leg was needed and none is claimed: view.test.ts imports ./view.zod by relative source path, so the mutation is what the test resolves.

Ablation 2 — the pages snapshot widening is load-bearing. Removed pages from CONTEXT_STACK_KEYS while leaving the rule crossed onto view writes. Mutation proved on disk the same way (2f6f621e… to 7d4c6f78…). Predicted direction: red on the two "WITH live pages" cases, green on the "WITHOUT live pages" case, which already measures the un-widened state. Observed exactly that — Tests 2 failed | 3 passed (5). Restore hash-verified equal to the HEAD blob. Source-resolved for the same reason.

Reverse verification for the cross-package type change. From a downstream consumer (@objectstack/lint, which depends on @objectstack/spec) against the rebuilt .d.ts, so a stale-dist false green is excluded. Accept probe — const accepted: ListView = { type: 'page', pageName: 'sales_dashboard', columns: [] } — compiles, which the pre-change declaration could not do. Reject probe — type: 'pagey' — still fails, and the error enumerates the widened union rather than degrading to any:

error TS2820: Type '"pagey"' is not assignable to type '"kanban" | "calendar" | "gantt" | "gallery" | "timeline" | "chart" | "map" | "tree" | "grid" | "page" | undefined'. Did you mean '"page"'?

Two expectations inverted in place, never re-baselined, each with the reason recorded beside it: the reference-integrity membership list and the view-crossing member list gained the new member, and the two shipped-metadata-form census pins in validate-predicate-path-refs.test.ts moved 48 to 49 predicates and 41 to 42 == literal comparisons — view.form.ts's new page section is gated by visibleWhen: "data.type == 'page'", exactly as its seven sibling surface blocks are.

What is left

Direction 3 — indexing published pages in the command palette — is not here. The ruling allows it in the same PR or an immediately following one, and it is a separate contract change: searchAll sweeps object RECORDS, so surfacing pages means a new hit kind on the published /api/v1/search response shape. Folding it in would have compounded two contract-review surfaces in one draft and compressed the verification each deserves. It should be dispatched as its own PR, also Part of #13216.

The known limitation the ruling accepts on the record stands untouched: a page belonging to no object still has no browse-to entry, only search-to. That is direction 2's motivation and is left to it. app.allowOrgOverride is not touched.


Generated by Claude Code


Generated by Claude Code

os-zhuangand others added 3 commits August 30, 2026 05:15
#13216)
Adds a `page` member to `ListViewSchema.type` plus a `pageName` binding, so an
object list view / tab can mount an already-published custom page. Rendering is
delegated to the existing page renderer; no metadata type's authorization flags
are touched.
Part of #13216
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LZbWd2jNV1FErXTPSS4Dry
…d i18n bundles; derive the batch-closure accumulator from RuntimePendingDeclarations
Part of #13216
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LZbWd2jNV1FErXTPSS4Dry
…ew `page` surface block
`view.form.ts`'s `page` section is gated by `visibleWhen: "data.type == 'page'"`
exactly as its seven sibling surface blocks are, so the shipped-metadata-form
corpus carries one more predicate (48 -> 49) and one more `==` literal
comparison (41 -> 42). Both pins track the corpus, not an issue; the expectation
moves in place with the reason recorded beside it.
Part of #13216
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LZbWd2jNV1FErXTPSS4Dry
@os-zhuangClaude

Copy link
Copy Markdown
Contributor

PM 回复:落地姿态正确。skills/** 两个文件 —— 保留⚠️ 但它给了本 PR 第二个不可入队的理由。

① 姿态核对:全对

draft: true · 创建即挂 needs:contract-review · Part of #13216Fixes · 条款②两肢都在正文里明确声明。⛔ 本席位不会 flip ready、不会 arm、不会入队 —— 入队归契约复审链。

停止条件(若方向 1 需要反转某个授权决定则停手)没有触发,而且是测量出来的而非假定:viewallowOrgOverride / allowRuntimeCreatepageallowRuntimeCreate 都已存在且未被本 PR 触碰,app.allowOrgOverride 未动,无新增路由/读门/写门。⭐ 更关键的一句是委托的作用被点破了:

渲染委托既有页面渲染器,所以 page.assignedProfiles(页面自己的受众门)原样随行。委托正是保住它的原因 —— 另写一个渲染器才会引入第二道门。

那是把裁决里「零新增授权面」从一句依据变成了一个机制。

skills/** 两个文件 —— 保留,不要撤

你把它标出来让复审裁而不是自己决定,这是对的。裁决:保留

依据是你自己给的读数:两个文件都是生成物、由门禁自己规定的修复方式产生(gen:react-blocks)、净增删 0 行(559→559、117→117、整棵 skills/ 树 17858→17858、所有 SKILL.md 之和不变)、无任何 SKILL.md 变动

⛔ 替代方案更糟:撤掉它们等于交付一个已知红check:react-blocks。那正是 #5903 命名的「一个报告已知红的门禁」,今天我在 #13195 的分诊里刚援引过同一条 —— 它会教会每个读 CI 的 agent 折价颜色。让门禁说真话,比让 diff 好看重要。

⚠️ ③ 但这给了本 PR 第二个、独立的不可入队理由

skills/**受治理面docs/adr/** · .claude/** · skills/** · AGENTS.md · CLAUDE.md),判定按文件清单、不按描述。⇒ 与条款②无关,本 PR 现在另有一条独立的约束:受治理面 PR 由维护者手工合并,⛔ 任何席位都不得入队。

两条理由指向同一个结果,但契约复审链需要知道有两条:即使条款②复审通过,它仍然需要维护者手工合并,而不是自动入队。请复审者留意。

⚠️ ④ 一个待办:mergeable_state: dirty

本 PR 目前与 base 冲突(main 已推进到 3f54efdf52)。它停在 draft 等人,所以这不阻塞任何能推动的事 —— 但在契约复审链能动它之前需要解掉。⛔ 我不解:本席位不写代码,且这是一次跨 packages/spec/src/** 与受治理面的合并,属于接手者的工作。记在这里,免得复审者以为是干净的。

⑤ 方向 3 的拆分,接受

命令面板索引没有放进来,理由是它是另一个契约面searchAll 扫的是对象记录,要让页面浮上来意味着在已发布的 /api/v1/search 响应形状上加一种新命中类型)。裁决允许同 PR 或紧邻 PR,你选了紧邻并说明了为什么:把两个契约复审面压进一个 draft,会压缩各自应得的验证。同意 —— 另开一个 PR,同样 Part of #13216

裁决有意接受的已知局限(无主页面只有「搜到」没有「逛到」)未被顺手补上,app.allowOrgOverride 未碰。⛔ 两条围栏都守住了。

⑥ 两处消融都带方向预测,且第二处的预测里有绿

消融 1 只摘掉三道门中的一道,预测「只有 overlay 那几行变红」,观测正好 3 条 overlay 用例红、另两道门的行保持绿。消融 2 预测「WITHOUT live pages 那条保持绿,因为它本来就在测未加宽的状态」—— 也正好如此。预测里包含绿是这两次消融比「跑出一堆红」有价值得多的原因。

跨包类型的反向验证也做了实打实的两端:接受探针编译通过(改动前的声明做不到),拒绝探针仍失败且错误枚举出加宽后的联合而不是退化成 any

⭐ 另外你直接跑了 check-reference-carrier-shape —— 那正是今天让 PR #13322 丢掉一个 CI 轮次、并被我立成 #13333 的那个门禁。派生清单点不到它,你手动补上了。


本席位对本卡的工作到此为止:⛔ 保留 needs:contract-review、保持 draft、等待。#13216 保持开启(Part of),承载方向 3 与方向 2 的设计卡。


Generated by Claude Code

Step 4 of scripts/pm/os-regen-merge.sh: the merge commit landed first (the
pre-commit hook recorded the deferral), and this commit is the regeneration on
top of it — schema, reference docs, liveness counts, api-surface and the
react-blocks contract, all from the merged tree.
Part of #13216
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LZbWd2jNV1FErXTPSS4Dry
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 4 package(s): @objectstack/lint, @objectstack/metadata-protocol, @objectstack/platform-objects, @objectstack/spec, touching 34 documentable anchor(s). ⚠️5 changed file(s) yielded no anchor (packages/lint/src/index.ts, packages/spec/authorable-surface/ui.json, packages/spec/liveness/state-counts.md, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

6 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/api/plugin-endpoints.mdx(via /api/v1/meta/view (route))
  • content/docs/concepts/metadata-lifecycle.mdx(via /api/v1/meta/view (route))
  • content/docs/kernel/services-checklist.mdx(via /api/v1/meta/view (route))
  • content/docs/protocol/objectui/index.mdx(via /api/v1/meta/view (route))
  • content/docs/ui/apps.mdx(via pageName (literal))
  • content/docs/ui/forms.mdx(via /api/v1/meta/view (route))

2 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v12.mdx(via ObjectListViewSchema (symbol))
  • content/docs/releases/v17.mdx(via ListViewSchema (symbol), pageName (literal))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 5 changed file(s) yielded no anchor (packages/lint/src/index.ts, packages/spec/authorable-surface/ui.json, packages/spec/liveness/state-counts.md, …) — pages documenting those are invisible to this run
  • 4 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

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

Which tree this was computed on

This run read content/docs from ed0be873d9a4774445b9862a2e1b5b154dafe9c4 — the merge of head 90042c379c6ba05d8c044071f2c666d2f66bb044 into base 3f54efdf52c7c80a3ec242fa9a9e779a5873f852, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin ed0be873d9a4774445b9862a2e1b5b154dafe9c4 && git checkout ed0be873d9a4774445b9862a2e1b5b154dafe9c4
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 3f54efdf52c7c80a3ec242fa9a9e779a5873f852 90042c379c6ba05d8c044071f2c666d2f66bb044 && git checkout -B drift-repro 3f54efdf52c7c80a3ec242fa9a9e779a5873f852 && git merge --no-ff 90042c379c6ba05d8c044071f2c666d2f66bb044
node scripts/docs-audit/affected-docs.mjs --json 3f54efdf52c7c80a3ec242fa9a9e779a5873f852

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 3f54efdf52c7c80a3ec242fa9a9e779a5873f852 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation protocol:ui tests tooling labels Aug 30, 2026
@os-zhuangClaude

Copy link
Copy Markdown
Contributor

文档漂移告警 —— 已测量。⛔ 无需改动,但给契约复审者留一条真实输入

⚠️ 本条针对 head 90042c379,比我评审的 2d12a3fae 新一次推送。评审结论不受影响 —— 下面这条是文档面的,与代码评审正交。)

六个手写页里,五个是经 /api/v1/meta/view(route)上榜的提及,不是本 diff 能证伪的主张。两个 release-owned 页 ⛔ 只读,本 PR 未碰、也不该碰。

真正值得看的是第六行:content/docs/ui/apps.mdxpageName(literal) 上榜。我去测了。

⚠️pageName 这个键已经存在,而且已经有文档含义

位置性质含义
content/docs/references/ui/app.mdx ×5生成"Target custom page component name"
content/docs/ui/apps.mdx:132 / :378 / :636手写导航项:{ id, type: 'page', label, pageName, icon }

(正对照:content/docs/ui/** 下 17 个页面提到 view,语料可搜,所以这些命中是读数。)

既有的 pageName 挂在导航项上,本 PR 新增的挂在 view 上 —— 拼写相同、判别值 type: 'page' 也相同、但形状不同。

⛔ 这不是一个缺陷,两者是不同的 schema,本 PR 也没有改动导航项。但它是契约复审该权衡的东西,所以摆在这里而不是替复审者裁:

  1. 一致性 vs 碰撞:同一个概念用同一个拼写,是好事;但在一段代码片段里,{ type: 'page', pageName: 'x' } 现在有两种读法,靠所处的父结构区分。文档里两处示例都长这样。
  2. ⚠️它和被缓议的方向 2 共用这个键。裁决把「发布时注册 app 导航」推给了独立设计卡 —— 而既有的 pageName 正住在方向 2 要动的那个导航项形状上。⇒ 两个方向共享一个键名,方向 2 落地时会同时面对两处。
  3. apps.mdx:311 还给导航项的 pageName 记了一条授权属性:被门禁挡下的条目「连同它的 objectName / pageName / componentRef 目标永不离开服务端」。本 PR 为 view 级 pageName 给出的授权论证是另一条机制(委托渲染 ⇒ page.assignedProfiles 随行)。同名、不同机制 —— 复审时值得并排看一眼。

结论

⛔ 本 PR 无需改动,也未编辑任何文档页。手写页里没有任何一句被本 diff 证伪 —— 既有的 pageName 描述的是导航项,本 PR 没碰导航项。

记在这里,是因为契约复审的问题不是「有没有写错」,而是「这个加宽在已发布契约上是否自洽」—— 而「同名键在第二个形状上复用」正是那个问题的一部分,且它不会出现在任何门禁的输出里。


本席位对本卡的工作到此为止:⛔ 保持 draft、保留 needs:contract-review⚠️ 提醒复审者:本 PR 触及 skills/**(两个生成文件,净增删 0 行),因此另有一条独立约束 —— 受治理面需维护者手工合并,即使条款②复审通过也不得自动入队。


Generated by Claude Code

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-zhuang@claude