Repository navigation
fix(objectql,rest)!: serve an object an extension renames without the base's plural (#30391) - #30459
Conversation
…red on main) Claude-Session: https://claude.ai/code/session_01Utp6CYaDMYnWxbm95qon7G Co-authored-by: Claude <noreply@anthropic.com>
…old plural Claude-Session: https://claude.ai/code/session_01Utp6CYaDMYnWxbm95qon7G Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Utp6CYaDMYnWxbm95qon7G Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Utp6CYaDMYnWxbm95qon7G Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Utp6CYaDMYnWxbm95qon7G Co-authored-by: Claude <noreply@anthropic.com>
…dger Claude-Session: https://claude.ai/code/session_01Utp6CYaDMYnWxbm95qon7G Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 2 package(s): 14 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 34 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin df74fd4b6d29abe0778921b5577ebf62762bcde6 && git checkout df74fd4b6d29abe0778921b5577ebf62762bcde6
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin c58008187fb14259cae52461aff9971a1b4b15e9 a1107d3c65641be8cbc9650ff48eb8466a776741 && git checkout -B drift-repro c58008187fb14259cae52461aff9971a1b4b15e9 && git merge --no-ff a1107d3c65641be8cbc9650ff48eb8466a776741
node scripts/docs-audit/affected-docs.mjs --json c58008187fb14259cae52461aff9971a1b4b15e9
|
|
domain:server#5 · os-bill · session_01Utp6CYVV6fcAQrNTDQRvPb Contract reviewServed-tier: Inputs read: card #30391 (body, triage 6655169453, claim 6656144660, os-dev-report 6657882269), PR #30459 body and file list, the net diff against Check-runs on the head as they stand: every completed run is ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgmentDoes the narrowing take a declared capability away at the consumer, or make the spec's own refusal real? Read against
Trying to break it:
No must-fix. Should-fix: items 5, 6 and 7 of ③ (showcase extension plural; Skills sentence plus ADR-0029 D9.2a note; Implemented-by: VERDICT: PASS |
|
domain:server#5 · os-bill · session_01Utp6CYVV6fcAQrNTDQRvPb 席位核对 · ACCEPT — held for the maintainer's word (BREAKING)Head
|
|
domain:server#5 · os-bill · session_01Utp6CYVV6fcAQrNTDQRvPb 维护者速读
席位意见:同意落地。这次收窄改的是一个 spec 并未承诺的返回值,没有拿掉任何已声明的能力。达档合同复核 PASS(6658126879),钉住 head 席位核对Head
|
Requested by Bill · project thread
domain:server#5 · os-bill · session_01Utp6CYVV6fcAQrNTDQRvPb
维护者速读
.custom配对包里的object_extension,或代码包的objectExtensions)只改了label、没写pluralLabel时,/meta/object不再返回基础包的复数名,也不再返回基础包对旧复数的译文;对象按“没声明复数”的样子返回,列表标题就显示新名字。配对包自己保存了该复数的译文时,返回配对包的译文。showcase_account(列表标题从 “Accounts” 变成 “Account (Success Overlay)”)。没有数据迁移,回滚即撤本 PR。packages/objectql/src/registry.ts的mergeObjectDefinitions(标量覆盖那一段),再看packages/rest/src/meta-item-read-gate.ts的withoutStaleObjectPlural和它在translateMetaList/translateMetaDocument里的两处调用。pnpm --filter @objectstack/rest exec vitest run src/meta-object-extension-plural-label.test.ts,预期 11 条全绿;修复前的代码上有 4 条是红的。Fixes #30391
Clause-②: no (narrowing) —
/meta/object(item and list reads, both transports) and every registry read of an object (SchemaRegistry.getObject, the extension fold) stop servingpluralLabelfor an object an extension renames without a plural of its own, where they served the base's plural (and, in a translated locale, the base's translation of it). The shipped showcase'sshowcase_accountis one. Nothing an author writes is refused, and no exported symbol moves.Metadata-protocol: semantic
Skills-docs: follow-up #30466 —
skills/objectstack-data/SKILL.md"Object Extension Model" says an extension "can add but never remove"; a renaminglabelnow takes the base'spluralLabeloff. The sentence wants that exception (skills/**is outside this change's surface).What changes
pluralLabelis the plural oflabel. The seat's route (served side, under ruling record 6631250894's label order): an extension that changeslabelwithoutpluralLabelserves what an object with nopluralLabelserves, the pair's own translation first. The new pin measures that reference on the same boot (crm_note, which declares no plural): it is served with nopluralLabelkey inenand inzh-CN, and the Console then titles a list by the label (useNavTargetLabel'sobjectPluralLabel: served plural, else served label).Two halves, and each one alone leaves a locale red (Ablations below):
mergeObjectDefinitions,@objectstack/objectql). When an extension'slabelis applied (it does not yield to a tenant-authored label, ADR-0029 D9.2a), differs from the running label, and the extension sets nopluralLabel, the runningpluralLabelis deleted. A plural the tenant authored stays (the same D9.2a yield). The fold stays idempotent over an already-folded base: the second pass yields on the label and leaves the plural absent.getPackagedObjectBaseis untouched and keeps the owner's plural, which the translators compare against./metaread (withoutStaleObjectPlural,@objectstack/rest). The spec'stranslateObjectanswers a missingpluralLabelwith the catalog'sobjects.NAME.pluralLabel, and that entry is the base package's translation of the plural the extension left behind (theencatalog repeats the source text, soenis affected too). Where the served object carries no plural, the packaged base declares one, and the served label diverged from the packaged label, the catalog's plural is replaced by the owner package's.custompair's own translation ofobjects.NAME.pluralLabel(getPairTranslationBundle(_packageId of the packaged base), read once per base per request, translated throughtranslateMetadataDocumentover the pair's bundle alone), or dropped when the pair has none. Every other object is returned unchanged, and the pair is read only when the rule applies. It runs on bothtranslateMetaListandtranslateMetaDocument, which both transports use.No spec change: the catalog lookup stays the spec's; the server decides what it serves. No ADR text changes: ADR-0146 D10 says an object is extended, never replaced; the base's declaration is untouched and only the served fold changes.
Tests
packages/rest/src/meta-object-extension-plural-label.test.ts(new, realSchemaRegistry+ realObjectStackProtocolImplementation+RestServer, i18n service over a deployment catalog with the pair's row merged in, both reads,enandzh-CN): the reference (crm_note, no plural); a label-only rename serves what the reference serves; the pair'szh-CNplural is served andengets none; a pair row translating onlylabeldoes not let the base's plural back; controls: label + plural served as set, plural only, neither (base plural and任务kept), repeated label, an untouched object. 4 red on main09720e2c(en and zh-CN rename, pair, non-pair), 11/11 after.packages/objectql/src/registry-extension-plural-label.test.ts(new, the fold): rename drops the plural; packaged base keepsTasksand_packageId; idempotent refold; plural set, plural only, fields only, repeated label; tenant-authored plural stays; tenant-authored label makes the rename yield; two extenders in priority order both ways. 11/11.f71f7bdc(after mergingorigin/main; the two later commits touch only the changeset and the engine-double ledger):@objectstack/objectql551 files, 10983 passed;@objectstack/rest387 files, 7346 passed / 332 skipped;typecheckfor both exit 0 (includingcheck:test-typecheck); eslint on the four changed source/test files exit 0.Ablations
Through
scripts/ablation-replace.mjs, each restored to blob == HEAD withgit diff HEADempty.delete merged.pluralLabelguarded off (rebuilt;ablation-dist-preflight.mjsexit 0, then--absentexit 0 after the restore rebuild)withoutStaleObjectPluralreturns the translator's answer unchanged (rest tests importsrc)encatalog repeats the source plural)Acceptance notes
labelreads no pair translation (PR fix(metadata-protocol,rest): a .custom pair's declared override is labelled in the ruled order on every /meta read #30353 left objects out ofpairBundle, seat ruling: an object is extended, never overridden). The plural now reads the pair's translation per this card's route, so a pair that translates both strings gets its plural translated and its label served as written. Carrier: seat (domain:server#5).pluralLabelis untouched (the tenant authored that body, and The i18n catalog is resolved ahead of the document, so a tenant's own Studio rename never reaches either/meta/objectread — and neither do extension scalar overrides #8284's pin keeps its plural translated). Carrier: seat, not filed.form-routes.ts(public form views) translates the form's object schema throughtranslateMetadataDocumenton its own path, outside this change's surface; for such an object that schema can still carry the catalog's plural. Not measured. Carrier: seat.Verification
Derived gates, dogfood, lint — final head a1107d3
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackatf71f7bdc: 91 commands; the ledger commit added 8 (99). Each run with its exit code captured before any pipe. 94 exit 0 on the first pass. Five did not:check-adr-0087-registration --base origin/mainexit 1: aFROM → TOblock contradictednot-required (no-migration-prescription). Nothing has to be rewritten, so the block became a sentence saying so; re-run exit 0.check:engine-double-contractexit 1: the new pin'sfindOnedouble was not in the ledger;node scripts/check-engine-double-contract.mjs --writeadded one row (scripts/engine-double-contract.pinned.json, +5); re-run exit 0.check-plugin-teardown-shape --self-test,check:skill-examples,check:dual-build-cjs-loadsexit 3 (prerequisite): exit 0 after fetching the pinned commit and a whole-workspace build.dispatch-gates --ranata1107d3c6: 99 derived, 99 run, 0 NOT-MEASURED, 0 UNRUN, exit 0.pnpm lint(eslint . --no-inline-config) ata1107d3c6: exit 0.Clause-②carries the narrowing arm),pnpm --filter @objectstack/dogfood testunderos-verify-lock.shatf71f7bdc(same code as the final head):VERDICT command-exit 0, Test Files 419 passed / 3 skipped (422), Tests 3492 passed / 36 skipped (3528), 0 failures.Docs
content/docs/data-modeling/object-extensions.mdx: the merge table'slabelrow now says a new label without a plural takes the target's plural off.content/docs/ui/translations.mdx: the object paragraph of "An edit beats the packaged catalog" now says such an object is served with no plural unless the owning package's.custompair translates it.releases/**untouched.Generated by Claude Code