fix(platform-objects): carry the tree-reference rule into the es-ES, ja-JP and zh-CN reference help text - #16858
Merged
os-musk merged 2 commits intoSep 8, 2026
Conversation
…ja-JP and zh-CN reference help text
The English source for `object.fields.reference` gained a normative sentence on
2026-09-05: a `tree` field's `reference` is optional and, when present, must
name the declaring object — a link to a different object is a `lookup`. The
rule is enforced at parse time.
The three translated locales still served the pre-2026-09-05 text, and were
wrong in both directions at once: they dropped the tree rule, and they asserted
a purpose the source no longer states ("for lookup / master_detail") that the
tree case contradicts. Both halves move in all three locales.
Only the three `helpText` leaf values change. The `label` siblings, the key set
and the generated structure are untouched, and the `*.source-hashes.generated`
tables correctly keep no record for a leaf that is a real translation.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ADLdAs2pVcH17h9tZKWMBg
…ference-helptext-three-locales
Contributor
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)), so this run has no opinion about the docs. What this run could not see
Coarse fallback — 2 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
os-musk
marked this pull request as ready for review
September 8, 2026 11:51
os-musk
enabled auto-merge
September 8, 2026 11:51
os-musk
deleted the
claude/issue-16820-reference-helptext-three-locales
branch
September 8, 2026 12:17
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #16820
One leaf, three locales:
metadataForms.object.fields.fields.reference.helpTextines-ES,ja-JPandzh-CN. Nothing else moves.All readings below are pinned to this PR's head,
dcdb32971f.Clause-②: no
What was wrong, and why it is a bug
The English source for this row gained a normative sentence on 2026-09-05. The three translated locales still served the pre-2026-09-05 text, and were wrong in both directions at once — they dropped the
treerule, and they asserted a purpose the source no longer states.en(source copy, untouched)Target object name. For a tree field it is optional and, if given, must be this object (a tree is a hierarchy within its own object — link a different object with a lookup)es-ESObjeto de destino (para lookup/master_detail)Nombre del objeto de destino. En un campo tree es opcional y, si se indica, debe ser este mismo objeto (un árbol es una jerarquía dentro de su propio objeto — para enlazar con otro objeto, usa un lookup)ja-JP対象オブジェクト(lookup/master_detail 用)対象オブジェクト名。tree フィールドでは省略可能で、指定する場合はこのオブジェクト自身でなければなりません(ツリーは同一オブジェクト内の階層です — 別のオブジェクトへリンクする場合は lookup を使います)zh-CN目标对象(用于 lookup / master_detail)目标对象名称。tree 字段可省略;若填写,必须是本对象自身(树是同一对象内部的层级 — 链接到其他对象请用 lookup)The dropped half is the user-facing side of a constraint enforced at parse time, so a Spanish, Japanese or Chinese author was told a purpose that excludes
tree, got no hint, and met the rule as a parse failure.packages/spec/src/data/object.zod.tsis where the refusal lives:The fossils, read before rewriting anything
1. The current English source, established by reading the tree
The leaf is generated from
packages/spec/src/data/object.form.ts:230, whose own comment states the intent:The commit that put it there:
2. The instrument is working as designed — this PR does not touch it
source-hash.ts's own file header documents the coverage hole, in the section "Why the generated tables can be BACKFILLED with no history":Measured on this tree, with firing controls, the three tables hold no record for this leaf — which is correct and stays correct after this PR, because the new values are real translations rather than source copies:
The two positive controls are the non-zero counts; the negative control (
metadataForms.object.fields.ZZZNOPE) returned 0 in all three. No hash table is edited by this PR.3. Hand-editing the leaf value is the sanctioned path, and the tree says so
Every one of these bundles carries this docblock,
es-ES.metadata-forms.generated.tsincluded:That is how the hand edit is proven sanctioned rather than assumed: the generator's own header prescribes "Edit translations in place", restricts the prohibition to "the structure — only the leaf string values", and states that re-running the generator would not perform this repair, because "a present-but-stale string is not a gap, so it is left as-is". Re-running
os i18n extract --mergeis therefore the one thing that could not have produced this diff. Only leaf string values change here;pnpm check:i18nindependently confirms the structure is intact.Verification
The leaf moved, both halves, in all three locales
Values after the change, extracted by anchoring on the leaf's dotted key rather than by line number:
The absence check on the removed claim is leaf-scoped, because
master_detaillegitimately survives elsewhere in each file:File-level,
master_detailgoes 3 occurrences to 2 in each of the three files. The two survivors (lines 1245 and 1275) are thedatasetform's own rows, which are genuinely aboutlookup/master_detailrelationships and are out of scope here.Before the change, the same greps fired the other way — 3 hits per file, the leaf among them — which is the positive control proving these checks can detect the claim at all.
It reaches consumers
@objectstack/platform-objects@17.3.0is published (privateabsent,publishConfig.access: public,files: ["dist", ...]). Reading the built bundle, after decoding the\uXXXXescapes the build emits:A raw byte grep of
dist/returns zero for every non-ASCII string, in both directions, because the bundler escapes them — that reading is void, and only the decoded comparison above counts.Package suite
Both under
scripts/pm/os-verify-lock.sh, which reportedVERDICT command-exit 0.Gates
Derived with
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackat headdcdb32971f, all 50 run, then reconciled. The reconciliation line, verbatim:That line accounts for coverage, not verdicts. The verdicts, stated separately: 50 of 50 exited 0 at head
dcdb32971f.On the first pass two gates exited
3(PREREQUISITE NOT MET). Exit 3 is NOT MEASURED and never a pass, so each named closure was built and the gate re-run to a real verdict:check:i18nis the load-bearing one here: it is what proves the hand edit left the generated structure and key set in sync with the source.Every exit code was captured before any pipe (
cmd > log 2>&1; EXIT=$?), never through| heador| tail.Lint
The repo-wide
eslint . --no-inline-configscan belongs to CI. The narrowed local run is a measurement, with all three pieces of evidence:isPathIgnoredovergit ls-files): 6358 lintable files, 0 ignored — so the population is 6358, not a guess.--format json: 3 files, 0 errors, 0 warnings.eslint.config.mjsitself: "this repo runs oneeslint.config.mjs, which never enables type-aware linting (noparserOptions.project, no typed@typescript-eslintrules) for ANY file, test or not" — so this diff cannot move the verdict on any file it does not touch.Bytes
pnpm check:nul-bytesgreen, plus a direct scan of the edited files for control characters: no matches.Changeset
patch, on@objectstack/platform-objects— route 1 of the WHICH LEVEL block in.github/workflows/pr-automation.yml, decided on the dist measurement above rather than on size: the strings do reach a published package'sdist, so this releases something andskip-changesetwould be wrong. The level ispatchand notminorbecause no public surface widens — no exported symbol is added and no key is added to a published payload; three leaf string values change.AGENTS.mdlines 1017-1020 set the floor: "A bug fix in a released package takes apatchchangeset — never none, and ⛔ neverskip-changeset".This matches triage's grading, 「按 i18n 修正处理级别即可,⛔ 不必按破坏性变更发」.
Scope, and the four exclusions
labelsiblings are untouched:Reference/Referencia/「参照」/「引用对象」 all stand exactly as before.dashboard.sections.layoutis stale against its source #14587 is not reopened. That classification was correct against the source as it then stood; what moved afterwards was the source. That card is untouched here.source-hash.ts's own header and is working as designed. No hash table is edited.Neighbouring cards, named for context only and none of them addressed by this PR: #16227 was bounded to the nine leaves it enumerates and this leaf is in neither that count nor its two borderline cases; #16165 concerns other leaves of the same class and does not reach this one; #15979 is the source edit that created the gap, and it remains as it is.
Notes for review
AGENTS.md:1033-1036. On this tree the floor sentence is at lines 1017-1020 and the landing rule at 1012-1016; the text is unchanged, only the line numbers differ. Quoted above from the file rather than from the order.metadataForms.object.fields.fields.reference.helpText. In the bundle source that is the dotted string key"fields.reference"nested underobject.fields, which is also exactly how the*.source-hashes.generated.tstables spell their keys. A first attempt to resolve the path with an identifier-only walker returned "PATH NOT FOUND" for all four locales; since all four failing together is an instrument failure rather than a reading, it was discarded and replaced with the key-anchored extractor used throughout above.🤖 Generated with Claude Code
https://claude.ai/code/session_01ADLdAs2pVcH17h9tZKWMBg
Generated by Claude Code