Skip to content

feat(spec)!: retire the type: 'page' list-view mount and its pageName binding - #17298

Draft
os-bill wants to merge 2 commits into
mainfrom
claude/issue-17063-retire-list-view-page-member
Draft

feat(spec)!: retire the type: 'page' list-view mount and its pageName binding#17298
os-bill wants to merge 2 commits into
mainfrom
claude/issue-17063-retire-list-view-page-member

Conversation

@os-bill

@os-bill os-bill commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

Fixes #17063

Clause-②: no

⚠️ The mechanical reading DISAGREES with that declaration, and I am reporting it rather than moving it — the seat moves a clause-② declaration, never the dev. See Clause ② — measured, three readings below.

This PR is GOVERNED (check-governed-merges --test on every diff path returns exit 3). No seat flips it ready, enqueues it, or arms auto-merge — a human merge is the review record. The dispatch predicted exit 0 / NOT governed; that prediction was false, and the reason is below.


What is retired

A list view could declare type: 'page' and name a published page in pageName, rendering nothing of its own and delegating to the page renderer (#13216 direction 1, spec half PR #13372). Only the spec half was ever built. Measured on objectui at the pinned .objectui-sha 53ded82b and at objectui main 3fbdd4a2d:

  • packages/plugin-list/src/ListView.tsx — the view-type switch shares its default: arm with case 'grid' ("an unrecognized viewType must degrade to a working table"), so 'page' has always drawn an empty grid;
  • @object-ui/core's isListViewVisualization('page') === false (normalize-list-view.test.ts:647), with a residual test named for objectui#8429.

So the three parse refusals that policed the binding policed a mount that never mounted anything. ADR-0049 enforce-or-remove; maintainer ruling 2026-09-09, decision batch #107 item 1, verbatim 「撤」, on 「已发布零消费的能力不因沉没成本获得豁免」.

Route, and why each half took the route it did

retired thing kind route why
pageName authorable KEY retiredKey() tombstone on ListViewSchema + ObjectListViewSchema tsc types it never and the parse raises the prescription instead of a bare unrecognized-key report. strict-object.ts calls a tombstone "strictly stronger than a guidance entry", and this same shape already carries virtualScroll: retiredKey(…).
'page' enum VALUE the type enum's own error map, keyed on issue.input An enum-value narrowing has no tombstone to hang a prescription on — the def survives, one value lighter. The precedent is in this same file: LIST_VIEW_EXPORT_PDF_RETIRED (#8010), and HookBodyCapability / object.managedBy: 'system' before it.

Liveness-ledger disposition follows the route, as the playbook's table demands: the pageName row stays, flipped to status: "dead" with a verifiedAt and a REMOVED note, because retiredKey() keeps the key in the walked shape (the rls.priority precedent). Deleting it would report UNCLASSIFIED.

The four surfaces the dispatch front-loaded

1 — a sys_metadata row already carrying type: 'page' / a pageName. The read path does re-validate: applyConversionsToStoredItem replays the full ADR-0087 chain on every rehydration seam, and the loader skips only 'flow' (packages/metadata/src/loaders/database-loader.ts:824 — the if (singular === 'flow') return payload guard, one line above the call at :825), never view. The new D2 conversion view-page-mount-removed therefore fires, and the row is served canonical. Measured, with a lit control:

IN   {"name":"orders.dash","object":"orders","viewKind":"list","type":"page","pageName":"sales_home","columns":[]}
OUT  {"name":"orders.dash","object":"orders","viewKind":"list","columns":[]}
     2 notices, both view-page-mount-removed (views[0].type, views[0].pageName)
CTRL {"…","type":"grid","columns":["name"]}  ->  unchanged, 0 notices

What an operator sees is a warn, not a 400 and not silence — and there are three emitters, one per rehydration seam, which do not carry the same string. Grep carries a pre-protocol shape:

emitter file:line string
[DatabaseLoader] packages/metadata/src/loaders/database-loader.ts:831 stored view/NAME carries a pre-protocol shape; NOTICE
[ObjectQLPlugin] packages/objectql/src/plugin.ts:2134 stored view/NAME carries a pre-protocol shape; NOTICE
[Protocol] packages/metadata-protocol/src/protocol.ts:4511 the same, plus The row itself is unchanged — re-save it (Studio edit → save, or run "os migrate meta --stored --apply") to persist the canonical shape.

A write through PUT /api/v1/meta/view is where the refusal fires instead: the tombstone and the enum error map both answer at parse, and that door heals the row on the next save.

⚠️ Coverage boundary, stated rather than left to be found: the conversion walks stack.views[] in all three persisted spellings (mapViewPayloads); it does not reach objects[].listViews.*, which no conversion in this registry reaches — the view-export-options-pdf-removed precedent has the identical reach. An object body still carrying a mount is refused at its own door with the prescription rather than converted. Both populations measured at zero (below).

2 — the ADR-0087 disposition. registered view-page-mount-removed, a real D2. not-required (no-migration-prescription) was not available: ADR-0087's own text (docs/adr/0087…md:596) refuses it for a body carrying a migration prescription, and this changeset carries a FROM → TO table because AGENTS.md requires one. A D3 semantic entry was considered and is not what this needed: a D2 is possible here and therefore owed. The conversion strips type rather than rewriting it to 'grid'ListViewSchema.type carries .default('grid'), so deleting the key lands the row on exactly what it already rendered, declared in one place instead of guessed in the registry, and stripKeys-shaped deletion is idempotent by construction. Registered under protocol 18 (RETIRED_KEYS_BY_MAJOR[18], MIGRATIONS_BY_MAJOR[18].conversionIds, step18.rationale extended), matching every in-flight retirement on the 17.x line. check-adr-0087-registration run after the commit — it reads changeset bodies from git, not the working tree:

✓ check-adr-0087-registration: 1 declared-breaking changeset(s), each carrying an ADR-0087 disposition.
    .changeset/retire-list-view-page-mount.md  [BREAKING]  registered view-page-mount-removed (new here: view-page-mount-removed)

3 — the tombstone kit. Per the spec-property-retirement playbook. Both prescriptions carry the pinned os migrate meta --from 17 house sentence, and pageName's is a retiredKey(). Two RETIRED_KEYS_BY_MAJOR[18] entries as new files under entries/retired-keys/ (18.ui__ListView__pageName.ts, 18.ui__ObjectListView__pageName.ts) plus gen:migration-registry; nothing was hand-edited inside the generated markers. The build gate named the two exact key spellings itself before they were registered, which is how they were derived rather than guessed. No RETIRED_DEFS_BY_MAJOR row is owed: no def is deleted.

4 — the three refusal messages. checkListViewPageMount existed only to police this mount, so the check, its three message constants and the export all go with it. They were module-private string constants, not error codes — no ERROR_CODE_LEDGER row exists for any of them (VIEW_PAGE_MOUNT_NEEDS_PAGE_NAME / VIEW_PAGE_NAME_NEEDS_PAGE_TYPE / VIEW_PAGE_MOUNT_HAS_COLUMNS appear nowhere but view.zod.ts), so nothing is owed there. @objectstack/lint's VIEW_PAGE_UNRESOLVED is a lint rule id, not an ADR-0112 code, and leaves with validateViewPageRefs.

What else left with it, and what deliberately did not

  • validateCrossReferences' page branch in stack.zod.ts. The surviving three page references in that function (an app nav item's pageName, a modal action's target at two rungs) and pageNames itself are untouched.
  • @objectstack/lint's validateViewPageRefs / VIEW_PAGE_UNRESOLVED. Its nav twin validateNavTargetRefs is untouched.
  • RuntimeStackContext.pages and the page row of CLOSURE_CONTEXT_KEY_BY_TYPE. validateViewPageRefs was the only member whose runtimeTypes reached stack.pages (every other page-reading rule takes the frozen flow default, and validatePageVisualizationBindings says in its own comment that it is not crossed for exactly this reason). The runtime-gate docblock states the widening rule — "made when a rule that reads the collection actually crosses the wall, never in advance" — so the symmetric retirement is owed, not optional: leaving it would create a carried collection no rule reads, which is the ADR-0049 shape this card exists to remove. A view publish also stops paying a sys_metadata round trip for it.
  • view.form.ts's page section (its one input was pageName) and the four i18n metadata-form bundles it fed — a form input for an unwritable key is the false-compliant UI half of a retirement.
  • Not touched: direction 3 of [feature] Give runtime-published custom pages an end-user entry point: a page view type on objects, or nav registration at publish time #13216 (command-palette page indexing, PR feat(spec,metadata-protocol): a page hit kind on GET /api/v1/search — command palette indexes published pages #14249) and the nav-item type: 'page' / PageNavItem.pageName surface, which is the page mount that has always rendered.

Zero usage, re-measured with a lit control

Query: git grep -n "type: *'VALUE'" over examples/**, packages/platform-objects/**, packages/mcp/**, packages/cloud-connection/**, packages/create-objectstack/**, tests excluded.

value hits verdict
page 30 all NAV items — every one sits in an items: array under navigation, carrying id / label / icon (25 in examples/app-showcase/src/ui/apps/index.ts, plus cloud-connection-ui.ts:69, marketplace-ui.ts:99, connect-ui.ts:72; studio.app.ts:187 is an action params bag). Zero list views.
kanban 4 lit control — the query shape finds authored list-view type values
tree 4 lit control
gallery 1 lit control

A zero whose control was never inspected survives review while saying nothing; each of the four rows above was read, not counted.

Ablation — the old spelling now fails, proven both ways

Each leg: mutate → prove it landed on disk (anchor count + injected-marker count + git hash-object moved) → run → restore → prove the restore by state. The spec unit suite imports ./view.zod as source (no dist hop), so no build leg is owed; the restore is git checkout HEAD -- PATH, never a bare git checkout --.

leg mutation landed result
reinstate 'page' in the type enum anchor 1→0, marker 0→1, hash 34f111fcc3c5d06b 3 pins RED — the three door-parameterised REFUSES type: 'page' cases
reinstate pageName: SnakeCaseIdentifierSchema.optional() anchor 1→0, marker 0→1, hash 34f111fc6447f4e3 6 pins RED
control — unmutated tree 13 passed, 0 failed

Restore, both legs: git hash-object returned 34f111fcef274c7ade5543b195a884fe6abde7c4, byte-equal to the HEAD:packages/spec/src/ui/view.zod.ts blob, and git diff HEAD -- PATH was empty. Not read from an exit code. A trap on EXIT INT TERM with absolute paths carried the crash path.

⚠️ The first attempt at leg 1 was a void reading and is reported as one. Its landing check used grep -cF with a multi-line anchor, which counts lines rather than occurrences; the guard mis-fired, the run was declared VOID and restored without being read. Leg 1 above is the re-run with a single-line anchor. The mutation had in fact landed — that is exactly why a landing check that cannot be trusted has to void the reading rather than be talked past.

Verification

Every exit code captured to disk before any pipe. Scripts under scripts/pm/ were run from an origin/main mirror (git archive origin/main scripts | tar -x, origin/main = a256f189), each byte-verified: check-widening-tells.mjs 949e94fd, check-governed-merges.mjs 081df968, check-clause2-carriers.mjs 60b7562c — all three identical to git rev-parse origin/main:PATH.

run exit
pnpm --filter @objectstack/spec build 0
pnpm --filter @objectstack/spec test (13 185 tests) 0
pnpm --filter @objectstack/spec check:generated (15 artifacts) 0
pnpm --filter @objectstack/lint typecheck && … test (3 688 tests) 0
pnpm --filter @objectstack/metadata-protocol typecheck && … test (2 482) 0
pnpm --filter @objectstack/platform-objects test (561) 0
check:liveness · check:empty-state · check:variant-docs · check:skill-examples · check:skill-refs · check:skill-docs · check:exported-any · check:dual-source-exports · check:duration-unit-keys · check:entry-nameability · check:yaml-examples 0 each
pnpm check:i18n 0 (RED first — 7 drifted bundles — then --write; the diff is 44 pure deletions)
check:doc-authoring · check:nul-bytes · check:cross-package-test-inputs · check:test-source-alias · check:stack-collection-maps · check:quick-reference-counts · check:corpus-claim-drift · check:published-files · check:type-check-coverage · check:i18n-stale-fill · check:objectui-changeset · check:pm-changeset-deadline-census 0 each
check-adr-0087-registration --self-test / --base (post-commit) 0 / 0
check-changeset-no-major --self-test / --base 0 / 0
check-empty-changeset · check-reference-carrier-shape (+self-tests) · check-skills-token-ratchet · check-skill-line-ratchet · check-spec-docblock-symbol-anchors · check-comment-mask-adoption · check-comment-mask-corpus · check-closing-keyword-parity 0 each

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack was re-derived after the i18n commit; it grew six entries (the changeset/i18n/release-rehearsal family), all of which were then run. The remaining families in its 121-line list are declared to CI as a narrowing, not claimed green.

ESLint — a measured narrowing, not a skipped run. ① The population is read from eslint's own config, not guessed: pnpm exec eslint --no-inline-config --format json . enumerates 6 465 files. ② The narrowed run linted 32 files (every surviving .ts/.tsx/.mjs/.mts path in the diff; the three deleted paths cannot be linted), count read from --format json: 0 errors, 0 warnings, exit 0. ③ Invariance: this repo runs one eslint.config.mjs and it never enables type-aware linting for any file — no parserOptions.project, no typed @typescript-eslint rules, stated and positively controlled at eslint.config.mjs:327-335 — so nothing in this diff can move the verdict on a file it does not touch. Measured at 1e937111c, this branch's final commit.

Clause ② — measured, three readings

Run on the finished diff (git diff MERGE_BASE...HEAD), from the origin/main mirror:

probe exit reading
check-widening-tells --declaration no 4 3 × T2 — "a new member of a closed set … the accept set gains a value"
check-widening-tells --declaration yes 0 a yes already routes to contract review, so a tell on top decides nothing
check-clause2-carriers --pair THIS_PR_NUMBER see the report comment on #17063 run once this PR had a number

The three T2 tells, by number and line:

  1. packages/spec/src/migrations/registry.ts:5443+ 'view-page-mount-removed', (the D3 chain step's conversionIds)
  2. packages/spec/src/migrations/registry.ts:12757+ 'ui/ListView:pageName',
  3. packages/spec/src/migrations/registry.ts:12791+ 'ui/ObjectListView:pageName',

⚠️ Correction (seat, 2026-09-10T05:00Z): two of the three are generated rows inside registry.ts's os-generated markers; registry.ts:5443 is NOT — the nearest marker above it is the CLOSING </os-generated semantic:17>, so it is the hand-maintained step18.conversionIds. The argument below is unchanged. Originally written as: all three are generated rows produced by gen:migration-registry from the three new files under src/migrations/entries/. They are the retirement LEDGER growing — the mechanical record that two keys are now unwritable — and every one of them is a consequence of the accept set shrinking. But that argument is the seat's to accept or reject: the dispatch ordered me to measure rather than agree, and to stop and report on disagreement. The declaration in this body stays no, exactly as claimed in comment 5612101866. ⛔ I have not moved it.

Governance — the dispatch's prediction was false

check-governed-merges.mjs --test over all 48 diff paths returns exit 3, GOVERNED: skills/** ×2 hits the register — skills/objectstack-ui/SKILL.md (the retired list-view type row, a surface the retirement playbook requires) and skills/objectstack-ui/references/react-blocks.md (regenerated by gen:react-blocks; the #11705 generated-surface exception did not lift it, because the generator declares no readable output manifest, so it fails closed). The dispatch's pre-flight read packages/spec/** and packages/lint/** only and concluded "⛔ No … ordinary queue landing applies". One hit governs the whole PR. ⛔ Dropping the skills edit to dodge governance is not an option — a published SKILL.md teaching a key that now refuses is the thing being retired.

Changeset

minor for @objectstack/spec, @objectstack/lint, @objectstack/metadata-protocol, with the **BREAKING** banner. Level derived, not inherited: check-changeset-no-major refuses major in the launch window, existing pending changesets already put @objectstack/spec at minor (next release 17.5.0, which the two prescriptions name), and all three packages publish removed surface — @objectstack/lint loses two exports and @objectstack/metadata-protocol narrows a published context type, so neither is a patch and skip-changeset is plainly wrong.

维护者速读(草稿)

改了什么 — 列表视图的 type: 'page' 挂载与它的 pageName 绑定被整体退役:枚举值、键、三条拒绝消息、checkListViewPageMount 导出、defineStack 的页面交叉引用分支、@objectstack/lintvalidateViewPageRefs、以及只为那一条规则而存在的 RuntimeStackContext.pages 实时页面全集。表单区块与四份 i18n 词条随之删除。应用导航项的 type: 'page'另一个键,原样保留。

为什么改 — 2026-09-09 裁决「撤」,依据 ADR-0049 enforce-or-remove 与「已发布零消费的能力不因沉没成本获得豁免」。spec 侧半边落了地,objectui 的渲染半边从未建成:实测 objectui 的 switchdefault:grid 共用分支,所以 page 视图一直画的是空表格,而三条解析拒绝守的是一个从未挂载过任何东西的挂载点。

风险与代价(含回滚) — 已存 sys_metadata 行不会炸:ADR-0087 的 D2 转换在每个再水化接缝上重放,剥掉两个键,行按规范服务;type剥除不是改写,靠 schema 自己的 grid 默认值落到它本来就渲染的样子。运营者看到的是三个接缝各一条 warn(字符串见上表),不是 400。已测编写面用量为零(带亮对照)。⚠️ objects[].listViews.* 不在任何转换的行走范围内 —— 那条路上的旧行在它自己的门上被带处方拒绝,而不是被转换。回滚 = 还原本 PR;墓碑与转换都是新增,不改动任何现存行为。

席位意见 — 达档契约复核返回 PASS WITH FINDINGS,无 must-fix;席位判定可以合。转录保险丝:120 个 harness 打戳的 claude-fable-5-1,零残留,对照词点亮。⚠️ 独立性说实话:复核者是本席位派出的子代理、继承本 session id ⇒ 席位内达档复核,⛔ 不是跨席位独立。⛔ 人工合并即审核记录,但 ⛔ 不替代契约复核 —— 上面那份就是那一道。

两件请你在 diff 里特别看一眼的事。

一、这不是普通的收窄,它是一次真 D2 —— 但覆盖有边界,而边界的代价被测出来了。 存量 view 行会被自动改写(探针:type:'page' 视图 → 2 条 view-page-mount-removed notice;grid 对照 → 0 条;容器视图 → 3 条)。⛔ 但转换只走 stack.views[],够不到 objects[].listViews.*。复核实测了那条边界的实际代价:一条带 page mount 的存量 object 行会原样穿过转换链(0 条 notice),在自己的门被 ObjectSchema 拒绝,却仍然被注册,每个进程打一条 [metadata_spec_invalid] —— 供着,不治;而两处 prescription 都叫作者去跑一个够不到它的 os migrate meta今天两个人群实测都是零生产者,所以不挡落地,但它是 ACCEPT 时要立的卡(view-export-options-pdf-removed 先例有同样的够不到)。

二、clause ② 的机械读数与本 PR 的声明冲突,我裁维持 no 三条 T2 tell 全部落在退役台账的行上 —— 它们存在的原因恰恰是接受集缩小了。改判 yes 会让 clause ② 轴对语义上最明确的收窄报「扩大」,并成为此后每一次退役的先例。工具侧另立 #17300

⚠️ 而我在裁决里把那三条 tell 都说成「生成的行」—— 错了一条。 registry.ts:5443 不在任何 os-generated 区域内(它上面最近的标记是闭合标记 </os-generated semantic:17>),它是手工维护step18.conversionIds。复核逮到,我自己在 head 上重测确认。已在裁决评论和 #17300 上就地更正 —— 因为 #17300 原本提的修法只按生成区域来判,会只覆盖三分之二的 tell,而每一次 D2 退役都会注册一个 conversion。

复核另纠了三处报告口径,都不动字节: runtime 读者的「唯一」少算了一个(validateVisibilityPredicates 也读 stack.pages,但 collectionEntries(undefined) 返回 [],无害,措辞应为「不把引用解析进 stack.pages」);消融计数 3/6/13 实为 4/4/14(方向对、守卫有效,只是记录要写真数);以及 #11705 的生成物例外其实抬得起 react-blocks.md,dev 读到的「fail closed」是未构建闭包导致的环境依赖结果。

受管面归属复核已重跑确认:exit 3,由 skills/objectstack-ui/SKILL.md 命中。 ⛔ 我当初的预检说「走普通队列」是错的 —— 我把检查跑在了卡片点名的路径上,而不是一次退役实际需要碰的路径上,而退役手册本身就要求动已发布的 skills 目录。⛔ 把 skills 拆出去躲开受管面这条路,dev 在我之前就拒了,理由对:一份教着「现在会在 parse 处被拒的键」的 SKILL.md,正是被退役的东西本身。

你要做的 — ① 判定 clause-② 的分歧:机械读数给出三条 T2(全部是退役台账的生成行),语义方向仍是收窄;移动声明是席位的事。② 本 PR 受治理(skills/**),需人工合并。③ ACCEPT 时按规则 3 在 objectui 立跨仓跟进:objectui#8429 的前提被本次退役消灭。


⚠️ Reminder, not an action: objectui#8429 is open and pm:blocked — it is the mount half that was never built, and this retirement kills its premise. ⛔ Nothing in objectui was filed or touched by this PR; that cross-repo follow-up is the seat's at ACCEPT.


Generated by Claude Code

…ame` binding

A list view could declare `type: 'page'` and name a published page in
`pageName`, delegating its whole rendering to the page renderer. Only the
spec half of that was ever built (#13216 direction 1, PR #13372). No
renderer ever routed the member — objectui's list-view switch shares its
`default:` arm with `case 'grid'`, and `isListViewVisualization('page')`
is false — so a page view drew an empty table where the page belonged,
and the three parse refusals policing the binding (`checkListViewPageMount`)
policed a mount that never mounted anything.

ADR-0049 enforce-or-remove; maintainer ruling 2026-09-09, decision batch
 #107 item 1.

- `pageName` becomes a `retiredKey()` tombstone on both list-view doors.
- `'page'` leaves the `type` enum; the enum's own error map carries the
  prescription, keyed on `issue.input` (the `exportOptions` 'pdf'
  precedent — an enum-VALUE narrowing has no tombstone to hang one on).
- `checkListViewPageMount`, its three refusal messages, the
  `validateCrossReferences` page branch, `@objectstack/lint`'s
  `validateViewPageRefs` / `VIEW_PAGE_UNRESOLVED`, and `view.form.ts`'s
  `page` section are removed with the mount.
- `RuntimeStackContext.pages` and the `page` row of
  `CLOSURE_CONTEXT_KEY_BY_TYPE` leave with the only crossed rule that
  read them: a view publish no longer gathers a live page universe.
- ADR-0087: D2 conversion `view-page-mount-removed` (protocol 18) strips
  both keys from stored rows and `os migrate meta --from 17` output;
  `type` is stripped rather than rewritten, since the schema defaults it
  to `grid` — exactly what the row already rendered.

The surviving page mount is the app navigation item
(`PageNavItem.pageName`), untouched.

Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH
Co-authored-by: Claude <noreply@anthropic.com>
…tirement

`view.form.ts`'s `page` section and its `pageName` input are gone (#17063), so
the extracted metadata-form labels drop with them: the `page` section
label/description and the `pageName` label/helpText, across en/es-ES/ja-JP/zh-CN
plus the three source-hash bundles. Pure deletion — merge mode adds and
translates, it does not remove, so this is `check-i18n-bundles.mjs --write`
rewriting the default locale from source and dropping the orphaned keys.

Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/xl documentation Improvements or additions to documentation protocol:ui tests tooling labels Sep 10, 2026
@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 40 documentable anchor(s). ⚠️ 10 changed file(s) yielded no anchor (packages/lint/src/index.ts, packages/lint/src/validate-list-view-field-refs.ts, packages/spec/api-surface/ui.json, …), 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, a path literal in REFERENCE_INTEGRITY_RULES; a path literal in evaluateRuntimeAuthoringGate; a path literal on a changed line))
  • content/docs/concepts/metadata-lifecycle.mdx (via /api/v1/meta/view (route, a path literal in REFERENCE_INTEGRITY_RULES; a path literal in evaluateRuntimeAuthoringGate; a path literal on a changed line))
  • content/docs/kernel/services-checklist.mdx (via /api/v1/meta/view (route, a path literal in REFERENCE_INTEGRITY_RULES; a path literal in evaluateRuntimeAuthoringGate; a path literal on a changed line))
  • content/docs/protocol/objectui/index.mdx (via /api/v1/meta/view (route, a path literal in REFERENCE_INTEGRITY_RULES; a path literal in evaluateRuntimeAuthoringGate; a path literal on a changed line))
  • content/docs/ui/apps.mdx (via pageName (literal, a string literal in apply; a string literal in checkListViewPageMount; a string literal in sections))
  • content/docs/ui/forms.mdx (via /api/v1/meta/view (route, a path literal in REFERENCE_INTEGRITY_RULES; a path literal in evaluateRuntimeAuthoringGate; a path literal on a changed line))

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

  • content/docs/releases/v12.mdx (via ObjectListViewSchema (symbol, a top-level const))
  • content/docs/releases/v17/17-1.mdx (via ListViewSchema (symbol, a top-level const))
  • content/docs/releases/v17/17-3.mdx (via crm_contract (literal, a string literal in fixture))
  • content/docs/releases/v17/17-4.mdx (via checkListViewPageMount (symbol, a top-level function))

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
  • 10 changed file(s) yielded no anchor (packages/lint/src/index.ts, packages/lint/src/validate-list-view-field-refs.ts, packages/spec/api-surface/ui.json, …) — pages documenting those are invisible to this run
  • 9 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 60 of 215 client-bound route-ledger rows — the other 155 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 155: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 100 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 — 135 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 a2509d720ecc77ba34410e0ee62dd5eaacbd4460packageMentionDocs.

Which tree this was computed on

This run read content/docs from 07b767880d2be58bbb7dc50bd7e1d03417b34700 — the merge of head 1e937111c14c19fd4fc28cfe7872e28786d9b18a into base a2509d720ecc77ba34410e0ee62dd5eaacbd4460, 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 07b767880d2be58bbb7dc50bd7e1d03417b34700 && git checkout 07b767880d2be58bbb7dc50bd7e1d03417b34700
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin a2509d720ecc77ba34410e0ee62dd5eaacbd4460 1e937111c14c19fd4fc28cfe7872e28786d9b18a && git checkout -B drift-repro a2509d720ecc77ba34410e0ee62dd5eaacbd4460 && git merge --no-ff 1e937111c14c19fd4fc28cfe7872e28786d9b18a

node scripts/docs-audit/affected-docs.mjs --json a2509d720ecc77ba34410e0ee62dd5eaacbd4460

⚠️ 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 a2509d720ecc77ba34410e0ee62dd5eaacbd4460 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

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

Projects

None yet

2 participants