Skip to content

spec(api): declare mappingName on ImportRequestSchema with the mutual-exclusion refine (#10330) - #11316

Merged
os-sam merged 1 commit into
mainfrom
claude/issue-10330-import-mapping-name
Aug 23, 2026
Merged

spec(api): declare mappingName on ImportRequestSchema with the mutual-exclusion refine (#10330)#11316
os-sam merged 1 commit into
mainfrom
claude/issue-10330-import-mapping-name

Conversation

@os-sam

Copy link
Copy Markdown
Collaborator

Fixes#10330

What

Declares mappingName on ImportRequestSchema (packages/spec/src/api/export.zod.ts), next to mapping, with a .describe() naming the artifact resolution and the mutual exclusion. CreateImportJobRequestSchema is the same object, so the async route's declaration lands in the same edit. Deletes the SDK warning callout from content/docs/data-modeling/import-mappings.mdx (the warning pointed at #10330; re-located after PR #11280's edits to the same page). Regenerated surfaces: content/docs/references/api/export.mdx and packages/spec/authorable-surface/api.json gained the key under both published names. Changeset: patch on @objectstack/spec (a published contract gains an expressible key).

Per the triage determination (issue comment, reusing the maintainer's #11006 option-B sibling ruling, 2026-08-22): enforcement exists, declaration was missing — declare it. Both import routes read body.mappingName via prepareImportRequest (packages/rest/src/import-prepare.ts), which is untouched.

The refine question — argued FOR, and added

The triage left "does the exclusion also become a schema refine?" to this PR. Measured, then added:

  • Cost is zero in this repo's Zod 4 (zod ^4.4.3): refinements live inside schemas, so .refine() returns the same ZodObjectImportRequestSchema.shape access (pinned by the existing runAutomations describe test at export.test.ts) still works, the CreateImportJobRequestSchema aliasing is untouched, and z.toJSONSchema still converts (verified empirically; the custom check is simply not represented in the JSON Schema). The OpenAPI generator's component list does not include this schema, so gen:openapi is unaffected. No existing caller builds partial bodies through the schema: the only parse call sites (spec's own tests and rest's import-run-automations-agreement.test.ts) parse complete bodies, and all pass unchanged.
  • Benefit is the authoring-time refusal: anyone building the body through the published schema gets the conflict rejected at parse, instead of discovering it as a runtime 400. Consumer-side leniency is where generated mistakes hide; the schema now says what the wire enforces.
  • Both layers stay pinned: the schema-level refusal (message and path pinned in export.test.ts) AND the route-level 400 CONFLICTING_MAPPING (already pinned in packages/rest/src/import-integration.test.ts, re-run green). The route parses the raw body itself, so its check never depended on callers using this schema — it stays, unchanged.

Zero-migration fact

ImportRequestSchema is a plain z.object — it strips unknown keys rather than rejecting them, so before this change mappingName was silently stripped by schema-validating callers and the change is purely additive for every existing caller. The refine only fires on a body carrying bothmappingName and an inline mapping — a body the wire has always refused with 400 CONFLICTING_MAPPING, so no previously-working schema-built body changes outcome.

Tests

  • packages/spec/src/api/export.test.ts, new describe (7 tests): parse-through with the value surviving on both schema names; the issue's TS2353 repro inverted into a compile-time pin (typed ImportRequest literal + expectTypeOf; the file is compiled by check:test-typecheck and is not in the debt ledger); conflicting pair refused at parse with message and path pinned, on both names; each side alone still accepted; the describe prose pinned to mention the exclusion and CONFLICTING_MAPPING.
  • Reverse verification (schema): reverted export.zod.ts to the merge-base, confirmed on disk (grep count 0), re-ran the file — 5 of 7 new tests red (the compile-time literal is runtime-neutral and the each-side-alone guard holds on base, as expected); restored, 64/64 green.
  • Reverse verification (cross-package, rebuilt d.ts): after pnpm --filter @objectstack/spec build, a temporary probe in packages/client type-checked a literal with mappingName (accepted) beside a typo key — tsc reported exactly one TS2561 on the typo, itself suggesting mappingName, proving the client compiles against the rebuilt declarations. Probe deleted; client tsc green again.

Verification (all at 816391575, the final commit)

  • pnpm --filter @objectstack/spec build → verdict command-exit 0; grep mappingName packages/spec/dist/*.d.ts now matches (the issue's measured zero, inverted).
  • pnpm --filter @objectstack/spec check:generated → after gen:docs regen, command-exit 0.
  • Spec full test suite: Test Files 418 passed / Tests 11125 passed. Spec typecheck: check:test-typecheck: OK.
  • Rest: import-integration.test.ts + import-run-automations-agreement.test.tsTest Files 2 passed / Tests 37 passed; tsc --noEmit clean. Client: typecheck green (downstream consumers of the spec change; rest and client are the two packages that read this schema).
  • node scripts/pm/dispatch-gates.mjs (no paths; change set derived from merge base, 6 paths = this diff) → all 32 path-derived families run green, including the convention-triggered test-file family (check:type-check-debt --re-measure: 33 entries, none above ceiling; check:engine-double-contract, check:where-matcher, check:query-options-erasure, check:cross-package-test-inputs all green) and check:nul-bytes.

Clause-② yes (accept-set change) — needs:contract-review expected.


Generated by Claude Code

…-exclusion refine (#10330)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RadETjNRLALFLhFA3xehZP
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 2 documentable anchor(s).

1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/data-modeling/import-mappings.mdx(via mappingName (literal))

1 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v12.mdx(via mappingName (literal))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/spec/authorable-surface/api.json) — pages documenting those are invisible to this run
  • the SDK route bridge reached 45 of 221 client-bound route-ledger rows — the other 176 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run: node scripts/docs-audit/affected-docs.mjs --bridge-coverage

Coarse fallback — 126 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json f24c90df3d57d53785f288b1329ae98ce06649cepackageMentionDocs.

Which tree this was computed on

This run read content/docs from d90fb5b889acd5cd9f95f96021ca21515f743005 — the merge of head 81639157557afe4f27228cd2074464e8eff5e163 into base f24c90df3d57d53785f288b1329ae98ce06649ce, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin d90fb5b889acd5cd9f95f96021ca21515f743005 && git checkout d90fb5b889acd5cd9f95f96021ca21515f743005
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin f24c90df3d57d53785f288b1329ae98ce06649ce 81639157557afe4f27228cd2074464e8eff5e163 && git checkout -B drift-repro f24c90df3d57d53785f288b1329ae98ce06649ce && git merge --no-ff 81639157557afe4f27228cd2074464e8eff5e163
node scripts/docs-audit/affected-docs.mjs --json f24c90df3d57d53785f288b1329ae98ce06649ce

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs f24c90df3d57d53785f288b1329ae98ce06649ce → pass the list as
args.docs, on the commit named under Which tree this was computed on.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

spec(api): mappingName is accepted by both import routes but undeclared on ImportRequestSchema — the typed SDK cannot express it

2 participants

@os-sam@claude