Uh oh!
There was an error while loading. Please reload this page.
docs(data-modeling): add Import Mappings and Object Extensions guides - #10341
Conversation
…#10213) Two authorable metadata surfaces had no page in Data Modeling. `mapping` is a full metadata kind whose reason for existing is a consumer: `POST /data/:object/import` resolves a registered artifact by `mappingName`. That parameter had zero occurrences in the hand-written docs, so the new page is built around the request itself — the artifact shape, the five transforms the pipeline actually executes, the strict-projection difference from an inline `mapping`, every pre-row rejection code, and the three different things "a row that does not match" means depending on write mode. `objectExtensions` was documented only inside protocol/objectql/schema.mdx, which is written for implementers. The new page covers when to extend rather than define, the seven keys the merge carries, the four it refuses (with the alternative each one needs), priority ordering, field-name collisions, and the own/overlay/extend contributor model including the 2026-08-13 ruling that an extension's scalars yield to a tenant-renamed base. The protocol page is cross-linked, not edited. Both pages are written from the implementation. Two gaps found while verifying are filed rather than documented as working: #10329 (the `lookup` transform's params are read by nothing) and #10330 (`mappingName` is undeclared on ImportRequestSchema, so the typed SDK cannot express it). `.claude/workflows/docs-accuracy-audit.js` is the mechanical output of `node scripts/docs-audit/check-audit-scope.mjs --write`, which is the only remedy for the `check:docs-audit-scope` red that adding a page causes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GawRwpD44VwBDVy3hs77AX
os-elon
commented
Aug 21, 2026
Landed by the PM under the maintainer's explicit instruction — audit record. This is a governed PR ( Provenance — who, what, where. The repository owner, in the PM session driving epic #10206, 2026-08-20. Authorization verbatim: 「你帮我处理合并吧」. Their per-PR verdict on this one, verbatim: 「可合」 — 「两个派发 premise 被实测推翻并如实入文,两个 spec 缺口立卡不粉饰」. PM position, recorded rather than dropped. I stated before acting that an agent-executed governed merge removes the human click the fence treats as the review record, and that the audit would flag it. The maintainer instructed anyway. The decision is theirs; this is the record of it, not a silent reinterpretation of the rule. Landing route. A direct merge is refused by repository rules ( Generated by Claude Code |
Fixes#10213 (sub-issue of #10206)
Two authorable metadata surfaces had no page in Data Modeling. This adds one each, both written from the implementation rather than from the schema alone.
content/docs/data-modeling/import-mappings.mdx(new)mappingis a full metadata kind, and the reason it is a kind is that a consumer landed:POST /api/v1/data/:object/importresolves a registered artifact bymappingName. Grepping the hand-written docs formappingNamereturned zero hits before this PR, so the page is built around that request rather than around the shape.What is on it, and where each claim comes from:
defineMapping()+defineStack({ mappings }), plus the two build-time refusals invalidateCrossReferences(stack.zod.ts:1067): an unknowntargetObject, and ajavascripttransform.allowRuntimeCreate: trueon themappingregistry entry (metadata-plugin.zod.ts:755); both origins are addressed identically becauseresolveNamedMappinggoes throughgetMetaItem, which reads thesys_metadatarows first and the in-memory registry second.TransformTypevalues against whatapplyMappingToRows(import-mapping.ts:115-167) actually executes: five applied,lookupa deliberate pass-through,javascripta 400.mappingNamebody and the real response envelope, taken from the route handler (rest-server.ts:6999-7040).mappingvsmappingName— the strict-projection difference. The artifact path emits only mapped targets; the inline rename passes unmapped columns through. This is the trap the seam comment inimport-mapping.tswas written for.MAPPING_NOT_FOUND,MAPPING_TARGET_MISMATCH,MAPPING_FORMAT_UNSUPPORTED,MAPPING_FORMAT_MISMATCH,UNSUPPORTED_TRANSFORM,CONFLICTING_MAPPING,INVALID_REQUEST,PAYLOAD_TOO_LARGE, with statuses.import-runner.ts:713-740rather than assumed. It is three different outcomes, chosen by write mode and match count: created (upsert),skipped/NO_MATCH(update),failed/AMBIGUOUS_MATCH(more than one match), plusBLANK_MATCH_KEY.content/docs/data-modeling/object-extensions.mdx(new)objectExtensionswas documented only insideprotocol/objectql/schema.mdx, which is written for implementers. The protocol page is cross-linked, not edited (/docs/protocol/objectql/schema#object-extensions).mergeObjectDefinitions(registry.ts:137) actually reads, with each one's behaviour: fields additive with same-name replacement, validations and indexes concatenated, three scalars last-writer-wins.actions,hooks,listViews,fieldGroups, each with the alternative the schema's ownguidanceblock prescribes. These are enforced refusals, not omissions.own/overlay/extend, and the ADR-0029 D9.2 base selection (overlay ?? own) that makes extension fields survive a tenant-customized object.label/pluralLabel/descriptionyield once the base has been authored away from the packaged owner's value (tenantAuthoredScalars,registry.ts:1718). Carried as a Callout because an author cannot discover it from the schema.Verifying the dispatch's three mechanism assumptions
All three were checked before writing, and two came back different from the premise:
import-coercion.tsbounds what the page may promise about transformation — partly. That file holds the vocabulary (boolean tokens, reference types) as constants; the coercion logic ispackages/rest/src/import-coerce.ts, and mapping transforms are a third thing again, inimport-mapping.ts. The page keeps the three separate: the mapping decides which value lands in which field, coercion converts it afterwards from the target object's field metadata.objectExtensionsinteracts with the customization overlay system — yes, but not throughsupportsOverlay. That flag isfalseforobjectand gates onlyassertDeleteAllowed(themetadata-plugin.zod.ts:693warning says so explicitly and calls a "fix" to it a regression). The real interaction is the schema registry's three-kind contributor model, and the page describes that.namespace-prefix.tsgoverns prefixing for fields an extension adds — no.validateObjectNamespacePrefixis applied byvalidateNamespacePrefixtoconfig.objectsonly; it never walksobjectExtensions, and it is about object names, not field names. The page says the platform applies no prefix rule here and recommends prefixing as a convention, which is what the shipped extensions do (plugin-auth'soidc_client_secret, the showcase'sloyalty_tier).Declared deviation from the file surface
.claude/workflows/docs-accuracy-audit.jsis in this diff. It is the mechanical output ofnode scripts/docs-audit/check-audit-scope.mjs --write— the only remedy for thecheck:docs-audit-scopered that adding a page causes, and a file the gate rejects hand-edits to. Two lines, one per new page.That puts
.claude/**in the path surface, so this PR is human-merge-only (#9866, Prime Directive #14). It is deliberately left in draft: not flipped ready, not queued, no auto-merge armed.Findings filed, not documented around
Two gaps surfaced while verifying. Both are
domain:speccalls, so they are filed rather than written up as if they worked — and each has a Callout on the page pointing at it, to be deleted when the issue lands:lookuptransform params (object / fromField / toField / autoCreate) are authorable and read by nothing #10329 — thelookuptransform'sparamskeys (object/fromField/toField/autoCreate) are authorable, alias-routed from eight other spellings, and read by nothing. The liveness ledger records the state as a sub-walk boundary but it has no enforce-or-remove disposition.mappingNameis accepted by both import routes but undeclared on ImportRequestSchema — the typed SDK cannot express it #10330 —mappingNameis accepted by both import routes and undeclared onImportRequestSchema, so the typed SDK cannot express it. Measured: zero hits inpackages/spec/dist/**/*.d.ts, and a literal passed toclient.data.import()fails withTS2353.Gates
Derived from the real diff with
node scripts/pm/dispatch-gates.mjs(no paths passed) after the final commit, and run at3e85784bd. All 15 derived families pluscheck:skill-examplesandcheck:nul-bytes, green:check:doc-anchors✅ check-doc-anchors: 259 internal #fragment link(s) across 401 source file(s) all resolve to a real headingcheck:doc-authoring✓ doc authoring guard: 382 files clean — no bare metadata literals.check:docs-audit-scope✓ docs-accuracy-audit scope is in sync with content/docs/: 182 hand-written doc(s).check:docs-redirectscheck-docs-redirects: OK (apps/docs/redirects.mjs: 92 entries ...)check:published-readme-links✓ check:published-readme-links — 152 outbound link(s) across 60 published markdown file(s)check:role-wordcheck-role-word: OK, no new occurrences of the reserved word.check:cross-package-test-inputsOK: 12 package(s) read outside themselves, all declaredcheck:nul-bytescheck-nul-bytes: OK (scanned 6092 text file(s) ... no raw ASCII control bytes).check:pm-governed-merges✓ check-governed-merges --self-test: 81 assertionscheck:skill-frame-sync✓ check-skill-frame-sync: 4 copies of the decision frame are structurally isomorphiccheck:doc-formula-expressions✓ ... 22 record-scoped formula example(s) across 408 files / 1420 TS blocks judged clean by @objectstack/formula.check:empty-state✓ all classified (1 closed, 2 open, 4 output, 9 scope)check:liveness✓ every governed-type property at the walk's one-level granularity is classified ...check:strictness-ledger✓ strictness ledger: 61 file(s) across 5 triaged director(ies)check:variant-docs✓ variant/doc gate: 18 discriminated union(s) — 8 governed, 10 exemptcheck:skill-examples✅ 212 prose examples type-check against @objectstack/specThe three
{/* os:check */}blocks were confirmed extracted, not silently skipped — the gate log names each one:check:doc-formula-expressionsneeded@objectstack/formulabuilt first — a fresh-worktree dependency-closure miss, not a defect in this diff.No changeset
Docs pages plus a generated scope list; nothing published.
skip-changesetapplied.Generated by Claude Code