Uh oh!
There was an error while loading. Please reload this page.
feat(spec): retire the component-translation submitLabel copy key (#10926, ADR-0049) - #11438
Conversation
…0926, ADR-0049) Option A per the maintainer ruling on #10926 (2026-08-22): drop the key, the submit alias and the PAGE_COMPONENT_COPY_KEYS slot; strict-delete route with guidance tombstones (the face is strictObject); D2 conversion translation-component-submit-label-removed wired into the step-18 chain; pins flipped, not deleted. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T9cDbY2NBiVJWYx3BpWfH2
📓 Docs Drift Check12 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. ✅ What this run could not see
Coarse fallback — 126 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 aafee4d99731a02b8c0546023d5e6ead89f9d2b7 && git checkout aafee4d99731a02b8c0546023d5e6ead89f9d2b7
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin d0e3a885b890e89d6d47ea5c70ae15b0a4a5d139 04ea21ec55a0de0194199db9de456017ea66f291 && git checkout -B drift-repro d0e3a885b890e89d6d47ea5c70ae15b0a4a5d139 && git merge --no-ff 04ea21ec55a0de0194199db9de456017ea66f291
node scripts/docs-audit/affected-docs.mjs --json d0e3a885b890e89d6d47ea5c70ae15b0a4a5d139 |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#10926
Implements the maintainer ruling recorded on the card (2026-08-22, 「接受所有」 — Option A): retire
submitLabelfrom the component-translation face. Theelement:formretirement referenced throughout is the already-landed #9249 work and is not touched here; #11250 (dist-freshness message misattribution, hit during verification) is already tracked and remains open.Ruling preconditions, measured first
translationface is conversion-covered —mapCollection(stack, 'translations', …)with thetranslation-validation-messages-removedprecedent (protocol 17), and stored rows replay the chain viaapplyConversionsToStoredItem. So the ADR-0087 conversion is owed, and written.submitLabeloutsidepackages/specitself. The only non-spec hit isservice-automation'sconfig-unknown-keys.test.ts, which uses the string as an arbitrary unknown key on a screen-node config — a different surface, unaffected. The conversion is therefore protective for customer-stored bundles, with no in-repo rewrites needed.packages/cli/src/utils/i18n-extract.tsimportsPAGE_COMPONENT_COPY_KEYSfrom@objectstack/spec/system(one constant, both sides) — extractor and resolver narrow together; no CLI code change needed.Retirement route: strict-delete + guidance (not a tombstone)
The face is a
strictObject— per the retirement playbook's fork, the strict route applies: the key leaves the shape and aguidanceentry carries the prescription (retiredKey()is the non-strict route). This face's own precedent isvalidationMessages(#4667): strict-delete + guidance + conversion, and — deliberately — noRETIRED_KEYS_BY_MAJORentry: gate (b) registers keys that are tombstoned in the walked authorable surface, and this key has no authorable-surface row (the per-component copy shape is an anonymous nested record value; onlysystem/TranslationData:pagesis walked). Same disposition here.The four sites
translation.zod.ts— measured-table row replaced with the retirement note;submit: 'submitLabel'alias dropped;submitLabelkey deleted;guidancetombstones added for bothsubmitLabel(with the houseos migrate meta --from 17sentence) and thesubmitalias spelling (no migrate sentence on purpose —submitnever parsed, so the tool lists no edit for it).i18n-resolver.ts—PAGE_COMPONENT_COPY_KEYSdrops the slot (six → five).translation-component-submit-label-removed(protocol 18,retiredFromLoadPath: true) walking both authored shapes — locale-keyed bundle entries and bare item/data entries (the shape storedtranslationrows replay through); fixture covers both doors,expectedNotices: 2. Wired into the step-18 chain (conversionIds+ rationale sentence). No overlap with parked fix(spec): reject an authored deleteBehavior 'set_null' on a master_detail at parse time; log the engine coercion loudly #11406 (that PR carries a semantic entry + regeneratedmigrations/registry.tsgenerated regions; this lands in the hand-edited CONVERSIONS registry + hand-editedconversionIds).translation.test.tsnow refusessubmitLabelasserting the prescription text, and refusessubmitasserting the alias story; the derived schema↔list agreement pin self-adjusts.i18n-resolver.test.tskeeps the bespokehotcrm:quick_formnode and pins the NEGATIVE — an off-spec bundle entry carrying the retired key is ignored, not overlaid (the resolver is schema-independent by design; stored rows predating the retirement reach it via the raw sync path).Changeset:
minor(launch-window convention, matching theelement:formprecedent) with the adr-0087registered translation-component-submit-label-removedmarker, FROM → TO mapping and the one-line fix.Ratchet visibility — zero deltas, and that is the expected reading
All four ratchets (
api-surface/,authorable-surface/,json-schema.manifest/,api-surface-signatures) are byte-identical, verified bycheck:generated(14/14 artifacts current) at04ea21ec55after a full spec rebuild. Route-consistent, not suspicious: the key lives on an anonymous nested record-value shape no walker names as a def (repo-wide grep: the onlysubmitLabelin any generated artifact isui/ElementFormPropsfrom #9249), andspec-changes.json/ the upgrade guide project released majors only (protocolVersion: 17.0.0—element-form-removed, protocol 18, is equally absent). The conversion reaches consumers when 18 is cut.Verification (all at
04ea21ec55, spec dist rebuilt first)pnpm --filter @objectstack/spec test: 419 files / 11151 tests passed (includes flipped pins, conversion fixture replay + chain replay, alias-integrity, migrate-sentence pin).pnpm --filter @objectstack/spec typecheck: green (tsc + scripts + test-typecheck ledger OK).pnpm --filter @objectstack/spec check:generated: "All 14 generated artifacts are up to date."node scripts/pm/dispatch-gates.mjs, answer asserted for this repo at04ea21ec55): all 22 path-derived families PASS (changeset gates incl.check-adr-0087-registrationandcheck-changeset-no-major, cross-package-test-inputs, merge-driver, spec-parsed-alias, liveness/empty-state/strictness-ledger/variant-docs, skill-examples/refs/docs, ci-filter-parity, plugin-teardown, affected-docs, doc-formula-expressions) plus convention-triggered ones (query-options-erasure, type-check-coverage, engine-double-contract, where-matcher, nul-bytes). One environment-shaped local red, declared:check-dev-prereqsfails only on 11 unrelated workspace packages having no local dist (workspace-build completeness; this diff cannot move it, CI builds first).pnpm --filter @objectstack/cli typecheckgreen after building the CLI closure; the nine CLI i18n test files re-run explicitly: 9 files / 106 tests passed.check:i18n: "check-i18n-bundles: OK (9 package(s))". Dogfoodexpression-conformance: 3 passed.packages/cli/srcassigning the literal'submitLabel'toPageComponentCopyKeywas confirmed on disk (grep count 1), then failed the CLI's tsc exactly as required —error TS2322: Type '"submitLabel"' is not assignable to type '"title" | "description" | "label" | "placeholder" | "emptyText"'— then removed; the same tsc is green without it. Observed direction: red, as predicted.serve-node-env-production-default.e2e.test.tsfails on any worktree where@objectstack/cliitself is unbuilt ("command serve not found"); afterpnpm --filter @objectstack/cli buildit passes (3/3). Not related to this diff.Clause-② note:
needs:contract-reviewis hung card-side; this PR stays draft pending contract review per the dispatch contract.Generated by Claude Code