Skip to content

fix(spec): retire page.assignedProfiles and answer profiles: with the permission-set route - #17835

Draft
os-bill wants to merge 6 commits into
mainfrom
claude/issue-16929-assignedprofiles-removal
Draft

fix(spec): retire page.assignedProfiles and answer profiles: with the permission-set route#17835
os-bill wants to merge 6 commits into
mainfrom
claude/issue-16929-assignedprofiles-removal

Conversation

@os-bill

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

Copy link
Copy Markdown
Collaborator

Fixes #16929

Executes Ruling A — director seat, decision batch #121 item 2, comment 5644017943 (2026-09-12), carrying the maintainer's 「同意」. Nothing here re-opens a question that ruling settled; alternatives B / C / E are not revisited.

Clause-②: no — this is a removal / narrowing. Nothing is widened, so no needs:contract-review.

PR #17401's landed half (the two guidance prescriptions stopping naming the key) stands and is not redone: both prescriptions on origin/main already omit it, and this branch leaves their text alone.


The six ruled items, one by one

# ruled done
1 page.zod.ts: assignedProfiles removed; profiles / assignedTo become refusals naming the permission-set route; the two guidance strings rewritten ✅ — with one route correction, below
2 page.form.ts helpText and its four locale bundles removed
3 the three false records corrected in the same PR ✅ — with one relocation, below
4 major changeset + an ADR-0087 semantic migration entry; key stripped on migrate meta --stored with a structured TODO ✅ substance; ⚠️ the major grade is refused by a standing repo-wide gate — see One ruled item the tree refuses
5 PR #17401's landed half stands ✅ untouched
6 Clause-②: no ✅ declared above in the fixed spelling

Item 1 — route correction: a retiredKey() tombstone, not a bare shape deletion

The retirement playbook offers two routes and keys the choice on whether the schema is strict: retiredKey() for a non-strict schema, delete-plus-guidance for a strict one. PageSchema is a strictObject, so the first attempt took the strict route — and the build refused it:

❌ 1 authorable key(s) disappeared from the contract:
     - ui/Page:assignedProfiles

scripts/build-schemas.ts check (a) is fatal for any key that leaves an emitting def, strictness notwithstanding, and check (c) then ratchets a baseline deletion against the merge base on one of three proofs — aged-out tombstone, def unreachable from the metadata-type roots, or whole def gone. ui/Page is reachable from the page root and keeps emitting, so none holds. The route the tree actually permits here is the tombstone, which is also what the sibling view.pageName retirement took two days ago.

This is not a softening of the ruling. The key is unwritable: tsc types it never, and a value reaching a parse raises the prescription. It simply stays in the walked shape, which is why its liveness row stays (as dead) and why the authorable-surface baseline marks it [RETIRED] instead of losing the line.

Item 3 — one of the three false records is not where the ruling says it is

Every path:line was re-derived by sentence rather than trusted. Two of the three resolved as written; the second did not.

ruled location what is actually there
packages/spec/liveness/page.jsonlive citing a non-existent objectui bridge found, corrected
packages/spec/liveness/view.json:125 — the "page audience gate" justification not there any more. git grep over that file finds zero hits for audience, assignedProfiles or page audience (lit control: pageName reads 3 lines in the same file; dark control 0). The #17063 pageName retirement rewrote that row on 2026-09-10 and the justification left with it. The same assertion is live at packages/spec/src/api/protocol.zod.ts (SearchAllPageHitSchema's TSDoc) — that is the one corrected here
packages/metadata-protocol/src/protocol.ts — "enforced at page render" found, corrected

So the count is still three, and all three assertions are gone; one of them lives at a different address than the ruling recorded.

Before → after, and what makes the new text true.

  1. packages/spec/liveness/page.json — was status: "live", note: "profile-scoped page audience; objectui bridges it (react/src/spec-bridge/bridges/page.ts) to PageLayout.assignedProfiles." Now status: "dead" with a verifiedAt and a note recording that the cited path does not exist in objectui (nor does any spec-bridge directory), while two sibling objectui citations in the same file resolve. True because the key is now a tombstone and the ledger's own route table says a tombstoned key keeps its row with a dead verdict.
  2. packages/spec/src/api/protocol.zod.ts — was "where the page's own audience gate (assignedProfiles) applies unchanged". Now states that a page has no audience gate of its own, that the key which read as one was removed precisely because nothing enforced it, and that what protects a page is the permission sets on the data it shows. True because the key no longer exists and never had a reader.
  3. packages/metadata-protocol/src/protocol.ts — was "is enforced where it is enforced now, at page render". Now states the opposite and keeps the delegation posture the sweep rests on, which never depended on the key. True by the cross-repo measurement the card and triage both took.

⚠️ packages/metadata-protocol/** is domain:engine's lane. It is here only because ruling item 3 puts all three records in one PR, and exactly one sentence is touched.


Measurement

The removal is real, and it reaches the built artifact

Probed against the built packages/spec/dist/ui/index.mjs before and after, same script both times.

Before (origin/main content, built):

A assignedProfiles :: ACCEPTED :: keys=assignedProfiles,isDefault,kind,label,name,regions,template,type :: value=["admin"]
B profiles(alias)  :: REFUSED  :: code=unrecognized_keys :: path=[] :: Unrecognized key(s) on this page: `profiles`. Did you mean `profiles` → `assignedProfiles`? …
C assignedTo       :: REFUSED  :: code=unrecognized_keys :: path=[] :: Unrecognized key(s) on this page: `assignedTo`. Did you mean `assignedTo` → `assignedProfiles`? …
D LIT-CONTROL icon :: ACCEPTED
E DARK-CONTROL     :: REFUSED  :: code=unrecognized_keys

After (this branch, rebuilt):

A assignedProfiles :: REFUSED :: code=invalid_type :: path=["assignedProfiles"] ::
   `page.assignedProfiles` was removed in @objectstack/spec 18 (ADR-0090 D2, ADR-0049 enforce-or-remove) — it was
   named for the Profile concept ADR-0090 D2 deleted, and it gated nothing: no renderer, route or metadata read door
   ever read the key, so a page that "assigned profiles" stayed open to every caller who could reach it. Delete the
   key. Page audience is the permission set's: gate the DATA the page shows with the object's permission sets, and
   bind those sets to people through positions (`sys_position_permission_set`) — those are the checks the runtime
   actually runs. Run `os migrate meta --from 17` to list the mechanical edits for existing sources; apply them by hand.

B profiles(alias)  :: REFUSED :: code=unrecognized_keys :: path=[] :: Unrecognized key(s) on this page: `profiles`.
   • `profiles` / `assignedTo` are not page keys (ADR-0090 D2: no Profile concept). Page audience is the permission
     set's — gate the DATA the page shows with the object's permission sets, and bind those sets to people through
     positions (`sys_position_permission_set`), never on the page itself. …

C assignedTo       :: REFUSED :: same sentence
D LIT-CONTROL icon :: ACCEPTED  (unchanged)
E DARK-CONTROL     :: REFUSED :: code=unrecognized_keys  (unchanged — the generic report, no rename offered)

That is the actual refusal text, not a claim that one exists. Note the refusal moved channel: unrecognized_keys at the page → invalid_type located at ["assignedProfiles"], which is what a z.never() tombstone produces.

The alias refusals point somewhere true — read, not inherited

A previous round on this card asserted that an alias table runs only from the unrecognized_keys path. I re-read the source rather than inherit it. packages/spec/src/shared/strict-object.ts's own docblock puts it in terms — "aliases … is consulted BEFORE the distance fallback" inside strictUnknownKeyError — and shared/alias-integrity.test.ts states the mechanism as the premise of the gate it implements: "an alias only ever runs from the unrecognized_keys path, so a key the shape declares can never reach it."

The before-probe is the direct evidence: rows B and C above are REFUSED with code=unrecognized_keys before any change. So profiles and assignedTo were never in the accept set, the alias only decorated the rejection, and deleting or repointing those entries narrows nothing — same code, same path, different text. My own reading agrees with the earlier round's.

A second consequence made the entries impossible to keep: alias-integrity.test.ts asserts that an alias's target is a key the shape accepts. Once the key is a tombstone, profiles: 'assignedProfiles' would point at a key the schema cannot accept — the ledger's finding-7 shape. They had to become guidance.

The migration entry actually fires

Driven over a stored page carrying the key, against the built artifact:

=== 1. `migrate meta` chain replay (authored source, 17 -> 18) ===
stack after :: {"pages":[{"name":"deal_desk","label":"Deal Desk","isDefault":false}]}
key still present? false
applied :: [{"toMajor":18,"conversionId":"page-assigned-profiles-removed","surface":"page.assignedProfiles",
             "from":"assignedProfiles","to":"(removed)","path":"pages[0].assignedProfiles"}]
structured TODO present? true
  TODO.toMajor = 18
  TODO.surface = `page.assignedProfiles` — the per-page audience list (REMOVED)

=== 2. stored-row rehydration (the `migrate meta --stored` seam) ===
stored row after :: {"name":"deal_desk","label":"Deal Desk","isDefault":false}
key still present? false

=== 3. CONTROLS ===
LIT : a page WITHOUT the key is untouched :: {"name":"team_home","label":"Team Home","isDefault":true}
DARK: a fabricated key is NOT stripped     :: {"name":"p","label":"P","zzqqNeverDeclared":1}

The key goes on both seams and the structured TODO appears. The strip is deliberately paired with a D3 semantic entry rather than left to read as "handled": which permission set a given profile name corresponds to is a judgement no walker can derive.

Ablation — the three new pins, RED before and GREEN after

One mutation leg restores both halves of the fix (the live key, and the two alias entries in place of the guidance ones).

HEAD blob for packages/spec/src/ui/page.zod.ts = 388c9ee934df36bed7145e3bc46c50175826fb2c

leg 0  GREEN baseline                     Tests  83 passed (83)

leg 1  on-disk proof of the mutation
         tombstone line          1 -> 0
         live-key line           0 -> 1
         guidance entries        2 -> 0
         blob hash  388c9ee9… -> 966b7ef6…        (changed, so the write landed)
       RED
         × refuses `assignedProfiles` at the tombstone and prescribes the permission-set route
         × answers `profiles:` with the permission-set route, not the retired key
         × answers `assignedTo:` with the same sentence as `profiles:`
         Tests  3 failed | 80 passed (83)        ← exactly the three new pins, nothing else

leg 2  restore (git checkout HEAD -- <abs path>; trap on EXIT INT TERM)
         restored blob hash = 388c9ee934df36bed7145e3bc46c50175826fb2c  == HEAD blob
         git diff HEAD      :: ''   (empty)
         git status --porcelain :: ''   (empty — index AND tree)
       GREEN                                Tests  83 passed (83)

Restore is proven by the blob hash against the HEAD blob and by an empty git diff HEAD + empty git status --porcelain, never by an exit code. No rebuild leg is needed and none is claimed: page.test.ts imports ./page.zod — a relative source path inside the same package — so this ablation never resolves through dist.

⚠️ One reading in that run is NOT MEASURED: the alias line occurrence count printed empty because the grep -c was mis-quoted inside a double-quoted $(...). The mutation is nonetheless established by the two counts that did fire and by the blob-hash change; a clean re-grep on the shipped file reads 0 alias lines (lit control aliases: { = 6, dark control = 0).

Changeset — it reaches a published dist, with both controls

Built first, then measured (npm pack --dry-run --json before a build reads dist as empty — that trap is avoided).

  • Positive control — the new prescription is in 18 packed dist/ files and in dist/index.d.ts (3 hits), so a consumer's tsc and runtime both see it. src/ui/page.zod.ts is itself in the packed list.
  • Negative controlsrc/ui/page.test.ts and scripts/build-schemas.ts are NOT in the packed list (2012 files packed; 216 dist/, 201 src/, 38 liveness/).
  • ⚠️ A first negative control failed and is reported rather than swapped out quietly: a source-only // comment in page.zod.ts reads 18 hits in dist/. packages/spec's tsup build does not strip comments, so for this package a comment inside a shipped module is published text. Useful, and worth knowing before writing one.
  • packages/metadata-protocol correctly carries no changeset entry: its files is ["dist","README.md","CHANGELOG.md"], the corrected sentence is an inline body comment, and it reads 0 files in that package's dist (lit control CLOSURE_CONTEXT_KEY_BY_TYPE = 2, so the instrument fired).

⚠️ One ruled item the tree refuses as written: the major grade

Ruling item 4 fixes the changeset at major. The changeset in this PR is major, as ruled. A standing repo-wide gate refuses it:

$ node scripts/check-changeset-no-major.mjs --base origin/main        EXIT=1
⛔ This PR introduces changeset(s) that declare a `major` bump.
   .changeset/16929-page-assigned-profiles-removed.md
     - @objectstack/spec: major
Every publishable package is in the Changesets `fixed` (lockstep) group, so a single
`major` promotes the ENTIRE monorepo to a new major version. During the launch window
ship breaking changes as `minor` instead …
If a whole-stack major release is genuinely intended, add the `allow-major` label to
the PR to skip this check.

So the ruling can be executed literally — the gate names its own escape — but the escape asserts "a whole-stack major release is genuinely intended", and that is a release-shaping claim well beyond this card:

  • every publishable package moves (~70, in lockstep) from 17.x to 18.0.0;
  • scripts/sync-protocol-version.mjs keys PROTOCOL_VERSION off the spec package major, so the bump also flips the handshake to 18 and activates the 24 other toMajor: 18 conversions already waiting in the registry.

⛔ I have therefore not applied allow-major, and I have not silently regraded the changeset to minor. The grade stands as ruled and the gate stands red, with its reading recorded here. The remedy is one line and it belongs to the seat or the maintainer:

  • keep major ⇒ add the allow-major label, and this PR is the one that cuts 18.0.0; or
  • regrade to minor ⇒ the launch-window convention, which the gate's own prose says is carried instead by the BREAKING banner and the ADR-0087 disposition — both of which this changeset already has. It is also what the sibling view-page-mount retirement (an identical-shape breaking removal, two days ago) did.

The hot registry file — what was taken, and against which tip

packages/spec/src/migrations/registry.ts is contended by #17792, #17638 and #17635. No entry number was taken, because there are none. The contended regions of that file are generated: src/migrations/entries/ holds one file per entry, filename derived from the id, no index, concatenated by gen:migration-registry and sorted by id (entries/README.md is the authority). This PR adds two such files and never edits between the markers:

  • entries/retired-keys/18.ui__Page__assignedProfiles.tsRETIRED_KEYS_BY_MAJOR[18]
  • entries/semantic/18.page-assigned-profiles-audience-to-permission-set.tsMIGRATIONS_BY_MAJOR[18].semantic

Two hand-edited lines remain, both appends at the tail and neither renumbering anything: step18.conversionIds gains 'page-assigned-profiles-removed' after 'view-page-mount-removed', and step18.rationale gains a paragraph. ⛔ No other PR's entry is renumbered or reordered.

The merge, and what it actually collided with. origin/main was merged immediately before opening this PR: merge commit 8774a8c115, parents 3a1be112ff (this branch) + c1078a5591 (origin/main at that moment). The collision was real but it was not a number — it was step18.rationale, where #17260's landed object-kanban.quickAdd retirement and this card had each appended a paragraph to the same prose field. Resolved semantically, both intents kept, main's paragraph first:

Everything in Verification below was re-run on the merged tree, at 626ca34833.


⭐ T1 of #17618 — known false positive, recorded in advance

This PR declares Clause-②: no, which is the only condition under which #17618's T1 leg fires, and T1 reads a re-declared key line as a new key (three false positives to date, most recently #17796 on a .describe() change). This diff re-declares assignedProfiles on its existing key line and rewrites alias/guidance entries on existing lines, so it is squarely in T1's blast radius. If T1 reds on a line this PR did not add, that is the false positive — the reading goes here, and ⛔ no is not flipped to yes to clear a gate: the declared value is the criterion, not the diff shape.


Verification

Exit codes captured before any pipe. Heavy runs went through scripts/pm/os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=issue-16929; the verdict quoted is the wrapper's own VERDICT line, or a per-part echo "$?" marker where parts were sequenced.

All of the following at 626ca34833, the merged head.

run result
pnpm --filter @objectstack/spec build VERDICT command-exit 0
pnpm --filter @objectstack/spec check:generated all 15 artifacts up to date on the merged tree (an earlier pre-merge run found 2 stale — content/docs/references/** and liveness/state-counts.md — regenerated with --fix)
spec test + typecheck + platform-objects test + metadata-protocol typecheck, joined with && so one verdict covers all four VERDICT command-exit 0 — spec: Test Files 471 passed (471), Tests 13375 passed (13375); platform-objects: 39 files, 561 tests
npx eslint . --no-inline-config --format json — the full repo union, no narrowing claimed ESLINT_EXIT=06647 files received per --format json, 0 errors, 0 warnings
node scripts/pm/dispatch-gates.mjs --ran … --repo objectstack-ai/objectstack EXIT=0114 derived, 112 run, 2 NOT-MEASURED, 0 UNRUN
node scripts/check-i18n-bundles.mjs --write first run EXIT=3 = PREREQUISITE NOT MET = NOT MEASURED (the built CLI closure was absent); the closure was built (VERDICT command-exit 0, 57 tasks) and the re-run is EXIT=0, all nine bundles regenerated
pnpm check:nul-bytes EXIT=0 — 8463 files scanned, no raw control bytes. Own sweep over the 20 changed paths with grep -naP finds none; lit control on an injected byte fires
node scripts/check-changeset-no-major.mjs --base origin/main EXIT=1deliberate, see above

The two NOT-MEASURED families both exited 3 = PREREQUISITE NOT MET, which is neither a pass nor a finding, and neither is counted green here:

  • pnpm check:dual-build-cjs-loads — reads built output for ten packages this worktree never built (@objectstack/studio, client-react, four connectors, …);
  • pnpm check:type-check-debt — wants turbo run build --filter='./packages/*' --filter='./packages/*/*' first, and its own text says ⛔ no ledger number may be raised on a run that measured nothing.

CI's Build Core supplies both. Two families that first read red on a stale build were re-run after rebuilding and are green: check:skill-examples (exit 0, 258 prose examples across 3 surfaces — it had refused on an unbuilt packages/client-react/dist, which was then built) and check:react-declaration-parity, run as CI runs it with MANIFEST="$PWD/sdui.manifest.json" … --baseline react-declaration-parity.baseline.json --strict (exit 0, "no new DECLARATION divergence vs accepted baseline") — its own refusal text says a complete local run is available from the checked-in root manifest and ⛔ must not be reported as NOT MEASURED.

One earlier reading is withdrawn rather than quietly dropped: pnpm check:query-options-erasure first read exit 2. That run happened while src/migrations/registry.ts briefly held a merge-resolution syntax error, and the gate parses that very file. Re-run on the fixed tree it is exit 0"ratchet holds: 67 unswept non-test site(s) in 17 file(s), none new".

Regenerated artifacts, each reviewed rather than waved through:

  • authorable-surface/ui.json"ui/Page:assignedProfiles""ui/Page:assignedProfiles [RETIRED]"
  • liveness/state-counts.mdpage 23 live → 22 live + 1 dead (total 24 unchanged); repo total 850→849 live, 93→94 dead
  • content/docs/references/ui/page.mdx — the row's type becomes never and carries the [REMOVED] prescription
  • the four *.metadata-forms.generated.ts bundles lose the assignedProfiles block (zh-CN 「指定配置文件 / 此页面对哪些 Profile 可用」, ja-JP「割り当てプロファイル」, es-ES "Perfiles asignados", en)

Commit messages were swept for a card relation and a model identifier, each stem counted separately: fix / fixes / fixed / close / closes / closed / resolve / resolves / resolved / part of / refs and #+digits all read 0; Opus / Sonnet / Haiku read 0. Lit control Claude reads 8, so the sweep reaches. The only claude- / anthropic hits are the mandated trailer pair.

验收备注

  • docs/adr/0010-nl-to-flow-authoring.md mentions assignedProfiles in a 2026-era open question. Untouched on purpose: docs/adr/** is a governed surface, and one path hit would make this whole diff governed and unmergeable by the queue. Noted, not filed.
  • docs/audits/2026-06-pageschema-property-liveness.md and docs/audits/2026-06-security-identity-property-liveness.md both name the key. Untouched: they are dated audit records, and editing them would falsify the record rather than correct it.
  • .changeset/page-guidance-stops-prescribing-assignedprofiles.md (PR fix(spec): PageSchema's rejection guidance stops prescribing assignedProfiles as a page gate #17401's, still pending) states "assignedProfiles remains an authorable key with its declaration untouched". Accurate about that PR; superseded by this one inside the same unreleased window. This changeset says so rather than editing another PR's.

Authored by the domain:spec execution seat's os-dev round, session session_01MkQhmuuJAVDjmeWNixwDDH, on branch claude/issue-16929-assignedprofiles-removal at 626ca34833.


Generated by Claude Code

…ur locale bundles

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

# Conflicts:
#	packages/spec/src/migrations/registry.ts
Discharges the os-regen deferral the merge commit recorded. Restores the
`ui/ObjectKanbanProps:quickAdd [RETIRED]` baseline marker the textual merge
dropped, and fixes the rationale concatenation where both sides appended a
paragraph to step18.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation protocol:ui tests tooling labels Sep 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/permissions/delegated-administration.mdx (via sales_manager (literal, a string literal in fixture))
  • content/docs/permissions/index.mdx (via sales_manager (literal, a string literal in fixture))
  • content/docs/permissions/permission-metadata.mdx (via sales_manager (literal, a string literal in fixture))
  • content/docs/permissions/permissions-matrix.mdx (via sales_manager (literal, a string literal in fixture))
  • content/docs/permissions/positions.mdx (via sales_manager (literal, a string literal in fixture))
  • content/docs/permissions/sharing-rules.mdx (via sales_manager (literal, a string literal in fixture))
  • content/docs/protocol/objectui/layout-dsl.mdx (via PageSchema (symbol, a top-level const), sales_manager (literal, a string literal in fixture))
  • content/docs/ui/pages.mdx (via sales_manager (literal, a string literal in fixture))

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

  • content/docs/releases/v16.mdx (via sales_manager (literal, a string literal in fixture))
  • content/docs/releases/v17/17-1.mdx (via searchAll (symbol, a method of class ObjectStackProtocolImplementation))

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
  • 6 changed file(s) yielded no anchor (packages/spec/authorable-surface/ui.json, packages/spec/liveness/README.md, packages/spec/liveness/page.json, …) — pages documenting those are invisible to this run
  • 10 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 — 137 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 0cd841a166453f6f4e4524587969f0c2137be3abpackageMentionDocs.

Which tree this was computed on

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

node scripts/docs-audit/affected-docs.mjs --json 0cd841a166453f6f4e4524587969f0c2137be3ab

⚠️ 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 0cd841a166453f6f4e4524587969f0c2137be3ab → 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

Labels

documentation Improvements or additions to documentation protocol:ui size/m tests tooling

Projects

None yet

2 participants