docs(cli): name the reader of each _views tail per side, and reconcile objectui's inert-key pin - #17721
Conversation
…r tail The `pushViewEntries` docblock attributed all three tails it emits to one browser-side reader: "the convention the runtime resolver reads (`viewLabel` / `viewDescription` / `viewEmptyState` in @object-ui/i18n)". Measured against `origin/main` and the objectui checkout, the three tails have three different readers and the sentence was wrong about two of them: - `label` is read on BOTH sides — `resolveViewLabel` (`packages/spec/src/system/i18n-resolver.ts:403`) via `translateView` (:907), registered as `view:` in `METADATA_DOCUMENT_TRANSLATORS` (:991) from which `TRANSLATABLE_METADATA_TYPES` is derived (:1008) and read by `packages/rest/src/rest-server.ts:363`; and `useObjectLabel().viewLabel` client-side. - `description` is read SERVER-SIDE ONLY, by `resolveViewDescription` (:424, called at :908). objectui#7219 removed the `useObjectLabel().viewDescription()` member, and the objectui source says so in as many words at `packages/i18n/src/useObjectLabel.ts:485`. The old prose therefore pointed the reader at a symbol that no longer exists, for a key that is still resolved and still reaches the screen — the inversion #15180 read as "declared, accepted, read by nothing". - `emptyState` is read CLIENT-SIDE ONLY. `emptyState` has zero occurrences in `i18n-resolver.ts` (positive control in the same file: `description`, 90 occurrences) and `resolveViewEmptyState` has zero occurrences tree-wide (positive control: `resolveViewLabel`, present), while `useObjectLabel().viewEmptyState` is live at `useObjectLabel.ts:512`. The second clause was re-checked rather than carried over and is TRUE: the platform bundle does carry `sys_user._views.all_users.label` (`packages/platform-objects/src/apps/translations/en.objects.generated.ts:124` `_views` → `:128` `all_users` → `label: "All Users"`). Across all nine shipped `en.objects.generated.ts` bundles, `_views` blocks carry `label` leaves and no `description` leaf, so naming `.label` there is exact. `viewLabel` and `viewEmptyState` are NOT retired: objectui kept the `viewSuffixes` helper all three shared and dropped only the `'description'` tail. The added warning says that, so the next reader does not over-apply objectui#7219 and delete two current citations. `pushViewEmptyState`'s own docblock (:453) names `viewEmptyState` in @object-ui/i18n and is left untouched — on this measurement it is accurate. Claude-Session: https://claude.ai/code/session_01TSf4DV7ziu4V5j73e46b7c Co-authored-by: Claude <noreply@anthropic.com>
…tail docblock Measured rather than assumed: `@objectstack/cli` builds with plain `tsc -p tsconfig.build.json`, so JSDoc survives into `dist/utils/i18n-extract.js`, which is inside the package's `files[]`. A comment-only diff therefore moves published bytes here, and `skip-changeset` would be wrong — the false sentence is text an upgrading agent can grep in the published tarball. Positive control on the same probe: the docblock's `sys_user._views.all_users.label` phrase is likewise present in the built `dist/utils/i18n-extract.js`, and absent from the emitted `.d.ts`. Claude-Session: https://claude.ai/code/session_01TSf4DV7ziu4V5j73e46b7c Co-authored-by: Claude <noreply@anthropic.com>
… `_views.<view>.description` Successor pass on the R73 draft. The draft's reader topology re-derived and CONFIRMED; one omission corrected and the changeset reversed. Corrected. The draft said "⛔ The absence of a browser-side helper is not evidence the key is inert" and stopped there. objectui does not merely lack a helper — it PINS this key as "an inert catalog entry" (`packages/i18n/src/__tests__/useObjectLabel-view.test.tsx`, a whole `describe`), and measured across all nine shipped `en.objects.generated.ts` bundles, `_views` blocks author `label` (104) and `emptyState` (9) leaves and ZERO `description` leaves. A reader who greps objectui hits "inert catalog entry" and deletes this emission — the same defect class as the sentence this card is about, aimed the other way. The block now names both facts, scopes them to the browser path objectui#7219 removed, and says neither reaches the server reader. The reach claim is now cited rather than asserted: the overlay lands on the served view document's `description`, which objectui relays (`packages/app-shell/src/views/ObjectView.tsx` `description: viewDef.description ?? listSchema.description`) and renders (`packages/plugin-list/src/ListView.tsx`, `pickLocalized(schema.description, displayLocale)`). Changeset REVERSED to `skip-changeset`. The draft argued `patch` because the JSDoc is emitted into `dist/utils/i18n-extract.js` inside `files[]`. That premise is true — re-measured — but it shows bytes moving, not a release. Measured with a positive control, the docblock reaches NO `.d.ts` (an exported symbol's JSDoc in the same file does), and `pushViewEntries` is not exported, so nothing a consumer can call or type against moves. House precedent is directly on point and went the other way: a comment-only edit to `packages/objectql/src/engine.ts` and `validation/rule-validator.ts` — a released package that also ships `dist` — took `skip-changeset` and no changeset file. Left standing deliberately, both re-measured as ACCURATE: the `pushViewEmptyState` docblock naming `viewEmptyState` in @object-ui/i18n (live), and the `formViews` comment naming `viewLabel` (live) — which `packages/lint` quotes verbatim, so editing it would dangle that citation. Claude-Session: https://claude.ai/code/session_01TSf4DV7ziu4V5j73e46b7c Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift Check
What this run could not see
Coarse fallback — 23 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
Review of record — ACCEPTHead reviewed: Reviewed-by:
Gates and CI on this head
⭐ The dev falsified this card's title, and this seat verified it at the sourceRecorded in full at
⇒ objectui says it in its own words. The card has been retitled so the false premise does not become the citation surface when it closes; ⛔ the body was not rewritten — 存量不追溯改写, and edit history is the archive.
⭐ The correction the dev made to the dead run's draft is sharper than the original defectThe draft had the reader topology right and it was kept. What was added is the half that makes the docblock safe to read: objectui does not merely lack a helper — it pins the key as "an inert catalog entry", and no bundle in this repo authors a ⇒ left unstated, the next reader greps objectui, reads "inert", and deletes the emission — this card's own defect class with the polarity flipped. The docblock now names both facts, scopes them to the browser path objectui#7219 removed, and cites the reach claim ( What this seat measured that the review would otherwise have overclaimedThe docs-drift bot reported "1 changed file yielded no anchor … this is not a clean bill of health", so its blind spot was live. Checked by hand: ⇒ no docs page carries the falsehood. Four pages do state the Accepted as reported
VerdictACCEPT. One file, comment bytes only, provably so; the card's own premise falsified with evidence and verified here at the source; the omission that would have inverted the defect closed; the out-of-fence site read, judged and left alone. Landing armed by this seat. Generated by Claude Code |
Fixes #17546
Clause-②: no— declared on the card. No exported symbol, no accepted-input set, no published behaviour moves.Recovery dispatch. The first dev pushed
72b23c09and died with the container without opening a PR, so no CI had ever run on it and no reading was on record. Every claim below was re-derived here; nothing was inherited as measured.The card says
viewLabel/viewDescription/viewEmptyStateare "symbols objectui#7219 retired". Measured: onlyviewDescriptionwas. The other two are current, and naming them is not residue.objectui, on its
origin/main(fetched; the repo is reachable from this container, so this half is measured, not unmeasured):useObjectLabel().viewLabelpackages/i18n/src/useObjectLabel.ts:481useObjectLabel().viewDescriptionpackages/i18n/src/useObjectLabel.ts:485useObjectLabel().viewEmptyStatepackages/i18n/src/useObjectLabel.ts:512The shared helper survived too —
viewSuffixes(useObjectLabel.ts:289) is still called byviewLabel(:482) andviewEmptyState(:519,:522), and objectui's own comment at:501states it: "viewSuffixesis NOT retired with it:viewLabelabove andviewEmptyStatebelow share that helper and keep resolving. Only the'description'tail passed to it is gone."⇒ the deliverable is not "swap a retired name for the server resolver". It is "name three different readers correctly", because the three tails have three different reader topologies.
The reader topology, per tail
Spec side,
packages/spec/src/system/i18n-resolver.ts(read only — see the red line below):resolveViewLabel(:403) readsobjects.OBJECT._views.KEY.labelresolveViewDescription(:424) readsobjects.OBJECT._views.KEY.descriptiontranslateView(:907,:908), registered asview:inMETADATA_DOCUMENT_TRANSLATORS(:991), from whichTRANSLATABLE_METADATA_TYPESis derived (:1008)@objectstack/restreads that derived set at the metadata boundary:isTranslatableMetaType(packages/rest/src/rest-server.ts:363) gatestranslateMetaItem(:3686) /translateMetaItems(:3952), both of which calltranslateMetadataDocument(:3733,:3977)No empty-state reader exists on the spec side. Case-insensitive
empty.?stateini18n-resolver.tsonorigin/main: exit 1, zero hits — with two same-file, same-query-shape positive controls firing,description(89 lines) and the camelCasepluralLabel(8 lines).resolveViewEmptyStatehas zero occurrences tree-wide under one pattern that simultaneously foundresolveViewLabelandresolveViewDescription, so the control is built into the query.labelresolveViewLabeluseObjectLabel().viewLabeldescriptionresolveViewDescriptionemptyStateuseObjectLabel().viewEmptyState⭐ The fact the draft missed, and the reason this commit exists
The draft ended the
descriptionbullet with "⛔ The absence of a browser-side helper is not evidence the key is inert" — true, but it stops one step short of the thing that will bite the next reader.objectui does not merely lack a helper. It pins this key as inert:
packages/i18n/src/__tests__/useObjectLabel-view.test.tsx:144is a wholedescribe('_views.VIEW.descriptionis an inert catalog entry (objectui#7219)'), and the ruling comment says a bundle entry written under it "reached no screen". And in this repo, no bundle authors the leaf at all — parsed across all nine shippeden.objects.generated.ts,_viewsblocks carrylabel(104 leaves) andemptyState(9 leaves) and zerodescriptionleaves. The zero carries its control: the same parser found the other two leaf kinds in the same blocks.A reader who greps objectui, hits "inert catalog entry", and deletes this emission would be repeating this card's own defect class with the polarity flipped. So the docblock now names both facts, scopes them to the browser path objectui#7219 removed, and says neither reaches the server reader.
The "reaches the screen" claim is now cited rather than asserted — the server overlay lands on the served view document's
description, which objectui relays and renders:packages/app-shell/src/views/ObjectView.tsx:2401—description: viewDef.description ?? listSchema.descriptionpackages/plugin-list/src/ListView.tsx:3404—pickLocalized(schema.description, displayLocale)The second original clause is TRUE — re-checked, not carried over
The card's site made two claims. The first was false; ⛔ repairing one and leaving the other standing is this card's defect class, so the second was re-measured: the shipped platform bundle does carry
sys_user._views.all_users.label—packages/platform-objects/src/apps/translations/en.objects.generated.ts:124_views→:128all_users→label: "All Users".@objectstack/platform-objectsis published (not private,17.4.0,files: [dist, README.md, CHANGELOG.md]). The clause survives unchanged.Changeset:
skip-changeset— the draft'spatchis REVERSEDThe draft argued
patchon the grounds that@objectstack/clibuilds with plaintsc, so the JSDoc is emitted intodist/utils/i18n-extract.jsinsidefiles[]. That premise is correct and was re-measured — the build script istsc -p tsconfig.build.jsonand the docblock text is present in the built.js. But it shows bytes moving, not a release, and three things decide it the other way:pushViewEntriesis not exported (i18n-extract.ts:392, a plainfunction)..d.ts— the consumer-visible type surface — while an exported symbol's JSDoc in the same file does.pushViewEntriesinpackages/cli/dist/utils/i18n-extract.d.ts: zero hits, exit 1; controlcollectExpectedEntriesin the same file: 3 hits, exit 0. So no consumer can call it, type against it, or observe it.packages/objectql/src/engine.tsandpackages/objectql/src/validation/rule-validator.ts, a released package that also shipsdist— carriesskip-changesetand no changeset file.On the draft's reading, no comment edit in any
src/file could ever take the label, which would empty the category.github/workflows/lint.yml:3104names as "the textbookskip-changesetcase — such a PR releases nothing". The changeset file is deleted in this PR.⛔
packages/specwas read, never writtenpackages/spec/src/system/i18n-resolver.tsis the thing the new prose describes. 「凡触packages/spec一律转domain:spec座位,不论谁需要它」 — reading it is required, and not one byte was written into it.git diff --name-only origin/main...HEADis exactly one file,packages/cli/src/utils/i18n-extract.ts.Judgement owed on
packages/lint/src/validate-translation-references.ts:381— ⛔ not editedVerdict: leave it. Its load-bearing clause is true and its names are live, so there is no stale-prose defect here — only an imprecision that is not worth another seat's landing.
The comment argues why the default form view gets
addSectionsbut no_viewsname: "the composer does give the default form a runtime identity (OBJECT.form), but_views.*is a LIST-view convention —viewLabel/viewDescriptionresolve view tabs, and the i18n walker emits no_viewsentry for any form view". Its conclusion is sound and unaffected by objectui#7219, and unlike the CLI site it never says "in@object-ui/i18n", so it is not pointing a reader at a removed member of a named package.viewLabelis live on both sides. Two imprecisions remain:viewDescriptionnames no client-side resolver any more (server-side the symbol isresolveViewDescription), and "resolve view tabs" conflates_viewswith_tabs, which isresolveTabLabel/translateInterfaceTabsterritory. Both are cosmetic — no behaviour, no gate and no reader's conclusion turns on either. ⇒ this is anoted, not filedobservation, not a defect;packages/lint's spec-facing surface routes todomain:spec, so if the seat disagrees it is that seat's edit, not mine.i18n-extract.tsverbatim — "form views have no counterpart in theviewLabel/_views.*resolver convention". That sentence is a thirdviewLabelsite inside my own authorised file (i18n-extract.ts:1242), and it is accurate, so editing it would have dangled the lint file's citation for no gain. It is deliberately untouched.Verification
Measured at
5fce933b, the final commit. Every exit code captured before any pipe (cmd > log 2>&1; EXIT=$?).node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(the script derives its own change set; no hand-written file list), then reconciled with--rancarrying a recorded exit code per command:✓ 48 derived famil(ies) accounted for — 46 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3). Re-derived after a freshgit fetch origin main: byte-identical to the union that was run.pnpm check:dual-build-cjs-loads :: exit 3— "PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/ … ⛔ This is NOT a pass: nothing was measured." (12 packages outside this card's closure are unbuilt; its own--self-testpassed, 93 cases.)pnpm check:i18n-coverage :: exit 3— "Nothing was compared: 12 config(s) did lint, but a partial round cannot judge the ratchet … this result says NOTHING about whether any declared label went untranslated." (examples/app-showcasecannot load its config without a repo-wide build.) Both need a whole-repopnpm build, which is CI'sBuild Corejob. The two i18n gates that do not need it —pnpm check:i18nandpnpm check:i18n-walk-parity— are green, and they are the ones that would move if the emitted key set had changed.pnpm lint— exit 0, full repo-wide run, not narrowed. This lane's known blind spot:dispatch-gates.mjsdoes not name it, so the lane always adds it. It completed in the foreground, so no narrowing had to be argued.pnpm --filter '@objectstack/cli^...' build --concurrency=2— exit 0, thenpnpm --filter @objectstack/cli build— exit 0 (gates readingdist/needed the rebuilt package; it is also what makes the.d.tsmeasurement above authoritative rather than stale).pnpm --filter @objectstack/cli typecheck— exit 0, includingcheck:test-typecheck("@objectstack/cli's test layer compiles under packages/cli/tsconfig.test.json").pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2— exit 0:Test Files 196 passed (196),Tests 2813 passed (2813).unittier is owed locally: the diff touches no integration-layer file, nobin/entry and no spawn helper. Theintegrationtier is declared to CI, which runs both.scripts/pm/os-verify-lock.shwith a stableOS_VERIFY_LOCK_SLOT, sharing the box with the dev on [finding]packageBodyAsStack's ruled half has no in-package pin — its firing direction is held only by an examples/ fixture, and the dev's own ten-line fix was left unauthorised #17169. Wall-clock figures under that lock are shared-box readings and are not quoted as timings.Acceptance notes
packages/lint/src/validate-translation-references.ts:381still spellsviewDescriptionand saysviewLabel/viewDescription"resolve view tabs". Cosmetic only; the clause it supports is true (judged in full above). Successor:domain:spec, which owns that file's spec-facing surface. ⛔ Not filed as a card: it is an imprecision in a comment whose conclusion holds, not a defect in any of the three filing classes.i18n-extract.ts:1242andi18n-extract.ts:451were re-measured and are accurate, so no repair was owed at either. Recorded because "the card named one site" is not the same as "the file has one site", and the next reader of this file should not have to re-derive that.content/docs/releases/, nopackages/spec, nopackages/lint, no label flip on any other actor's PR.🤖 Generated with Claude Code
https://claude.ai/code/session_01TSf4DV7ziu4V5j73e46b7c
Generated by Claude Code