Skip to content

fix(rest)!: apply an inline import mapping entry's transform, defaultValue and required - #25540

Draft
objectstack-fleet[bot] wants to merge 3 commits into
mainfrom
claude/issue-25491-import-inline-transform
Draft

objectstack-fleet[bot] wants to merge 3 commits into
mainfrom
claude/issue-25491-import-inline-transform

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Sales · project thread

Fixes #25491

Clause-②: no (narrowing) — the import doors refuse two request shapes they used to run: an inline mapping entry declaring transform: 'date_format' (or a transform name outside the declared set), and an entry marked required: true whose column a row leaves blank. Nothing is widened.

Metadata-protocol: none (a REST import request's handling; nothing a metadata author writes changes)

What changes

ImportRequestSchema.mapping accepts a FieldMappingEntry[] whose entries carry transform, defaultValue and required. prepareImportRequest (packages/rest/src/import-prepare.ts) kept only each entry's column and field and dropped the rest, so the import ran with no transform. Each entry's rules now run on its column after the rows are built and before cell coercion, for every format (csv, json rows, xlsx) and for both doors that parse through this function (the sync import and the async import job):

  • trim / uppercase / lowercase rewrite a string cell; none and lookup copy it (a lookup cell is still resolved to an id by the runner's reference resolution, as the named mapping's lookup is).
  • defaultValue fills a blank cell (absent, empty, whitespace or one of nullValues, judged by core's isBlank, the predicate cell coercion uses).
  • required: true with a cell still blank refuses the request 400 MISSING_REQUIRED_FIELD before any row, on the dry run and the commit alike, naming the column and the rows.
  • date_format, and any name outside the declared set, refuses the request 400 UNSUPPORTED_TRANSFORM naming the transform.

A plain { sourceField, targetField } entry produces no rule and the row is built exactly as before.

Measured before choosing the route

The dispatch asked to reuse what the named-mapping path does. Measured on origin/main dc3e5b9d7b: the named path (applyMappingToRows, packages/core/src/utils/import-mapping.ts) implements a different vocabulary, the mapping artifact's none / constant / map / split / join / lookup (and refuses javascript). trim, uppercase, lowercase and date_format are implemented nowhere in the tree (git grep over packages, tests excluded: the only hit is the spec enum), and no path implements an entry's defaultValue or required. So there was no shared implementation of these to hand the entries to, and this PR adds no second one: the three string transforms live in one table in import-prepare.ts; none and lookup keep the named path's semantics (copy, resolve downstream); the blank test reuses core's isBlank. No packages/spec change and no file outside packages/rest.

date_format is refused rather than applied: FieldMappingEntry carries no format for it, so it has no defined input. A date or datetime target is already parsed as a date by cell coercion, so the remedy is to drop the transform.

Tests

packages/rest/src/import-inline-mapping-entry.test.ts drives the real POST /api/v1/data/:object/import route (real ObjectQL engine, protocol, sqlite :memory:):

  • the card's reproduction: uppercase on the credit-code column passes a ^[0-9A-Z]{18}$ format rule and is stored upper-cased, under writeMode: upsert / matchFields: ['credit_code'];
  • lowercase on an email column: ZHOU@X.EXAMPLE.COM then zhou@x.example.com under matchFields: ['supplier', 'email'] leaves one contact (created 1, then updated 1);
  • trim with trimWhitespace: false; defaultValue for a blank cell and a missing column; required refusal on the dry run and the commit with nothing written; required satisfied by a defaultValue; date_format refused 400 UNSUPPORTED_TRANSFORM;
  • control: a plain { sourceField, targetField } mapping (and transform: 'none') stores the cells untouched.

Ablation (one-shot, through scripts/ablation-replace.mjs, source-resolved suite so no dist leg): flipping the guard if (entryRules.length > 0) { to a less-than-zero comparison (so the rules never run) turned 5 of 8 red (uppercase, lowercase, trim, defaultValue, required); the 3 that stay green are the parse-time date_format refusal, required-with-default and the control, as expected. Restored: blob equals HEAD, git diff HEAD empty.

Local verification (head b5a3141e7f, base dc3e5b9d7b)

  • pnpm --filter @objectstack/rest exec vitest run --maxWorkers=2 src/import-inline-mapping-entry.test.ts: 8/8 passed (lock VERDICT command-exit 0).
  • pnpm --filter @objectstack/rest exec vitest run --maxWorkers=2 (whole package): Test Files 322 passed, Tests 6616 passed / 344 skipped (VERDICT command-exit 0). Source unchanged since; the later commits touch only the changeset.
  • pnpm --filter @objectstack/rest build && pnpm --filter @objectstack/rest typecheck (tsc + check:test-typecheck, which compiles the new test file): VERDICT command-exit 0.
  • Full dogfood battery (owed by the ! title and the (narrowing) arm), on b5a3141e7f: pnpm --filter '@objectstack/dogfood^...' build && pnpm --filter @objectstack/dogfood test under the verify lock: VERDICT command-exit 0, Test Files 290 passed / 3 skipped, Tests 2494 passed / 18 skipped.
  • node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran: 67 derived, 65 run (all exit 0), 2 NOT MEASURED: pnpm check:dual-build-cjs-loads (exit 3, PREREQUISITE NOT MET: needs a whole-workspace build) and pnpm check:type-check-debt (exit 3: its closure build waits on the verify lock, which a sibling's dogfood run held past the wait budget). Both run in CI.
  • pnpm lint: NOT MEASURED repo-wide (the verify lock was held by a sibling's 28-minute run past three wait budgets); narrowed and declared: pnpm exec eslint --no-inline-config over the two changed source files, exit 0. CI's Lint & Repo Gates runs the repo-wide sweep.
  • origin/main moved 11 commits since the base; none touches packages/rest, packages/core/src/utils or the spec's import schema, and git merge-tree against it is clean, so no merge was taken.

Acceptance notes

  • A required miss refuses the whole request rather than failing only the offending rows: the runner (packages/core) has no seam for a pre-failed row, and giving it one is outside this card's file surface. The refusal names every row, so the operator fixes the file once.
  • The import-mapping.ts docblock still describes the inline mapping as a plain rename; it is accurate for the compact record form and for plain entries.

🤖 Generated with Claude Code

https://claude.ai/code/session_01CxBKycaiUpA1yGx1XyoGqX

@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Oct 23, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/api/client-sdk.mdx (via /:object/import (route, bridged from symbol prepareImportRequest — its route source's handler names it))
  • content/docs/api/error-catalog.mdx (via MISSING_REQUIRED_FIELD (literal, a string literal in applyInlineEntryRules))
  • content/docs/api/wire-format.mdx (via /:object/import (route, bridged from symbol prepareImportRequest — its route source's handler names it))
  • content/docs/automation/webhooks.mdx (via MISSING_REQUIRED_FIELD (literal, a string literal in applyInlineEntryRules))
  • content/docs/data-modeling/fields.mdx (via /:object/import (route, bridged from symbol prepareImportRequest — its route source's handler names it))
  • content/docs/data-modeling/import-mappings.mdx (via sourceField (symbol, a field of interface InlineEntryRule), targetField (symbol, a field of interface InlineEntryRule), UNSUPPORTED_TRANSFORM (literal, a string literal in readInlineEntryRule), /:object/import (route, bridged from symbol prepareImportRequest — its route source's handler names it), /:object/import/jobs (route, bridged from symbol prepareImportRequest — its route source's handler names it))
  • content/docs/permissions/system-context.mdx (via /:object/import (route, bridged from symbol prepareImportRequest — its route source's handler names it), /:object/import/jobs (route, bridged from symbol prepareImportRequest — its route source's handler names it))
  • content/docs/protocol/kernel/error-handling.mdx (via MISSING_REQUIRED_FIELD (literal, a string literal in applyInlineEntryRules))
  • content/docs/protocol/objectql/state-machine.mdx (via /:object/import (route, bridged from symbol prepareImportRequest — its route source's handler names it), /:object/import/jobs (route, bridged from symbol prepareImportRequest — its route source's handler names it))

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

  • content/docs/releases/v12.mdx (via UNSUPPORTED_TRANSFORM (literal, a string literal in readInlineEntryRule), /:object/import (route, bridged from symbol prepareImportRequest — its route source's handler names it))
  • content/docs/releases/v17/17-6.mdx (via /:object/import (route, bridged from symbol prepareImportRequest — its route source's handler names it))
  • content/docs/releases/v17/17-7.mdx (via date_format (literal, a string literal in readInlineEntryRule))

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 anchor(s) matched too much of the corpus to be a work list: defaultValue (symbol, 33 pages)
  • 7 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 19 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 74496c64b0637103f87cceec3f37932a48813e65 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 7560ce704adfd5656edf593ae5c510ae4a2565c0 — the merge of head b5a3141e7f0a2efdf94e9ae94b88d935cf96f37a into base 74496c64b0637103f87cceec3f37932a48813e65, 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 7560ce704adfd5656edf593ae5c510ae4a2565c0 && git checkout 7560ce704adfd5656edf593ae5c510ae4a2565c0
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 74496c64b0637103f87cceec3f37932a48813e65 b5a3141e7f0a2efdf94e9ae94b88d935cf96f37a && git checkout -B drift-repro 74496c64b0637103f87cceec3f37932a48813e65 && git merge --no-ff b5a3141e7f0a2efdf94e9ae94b88d935cf96f37a

node scripts/docs-audit/affected-docs.mjs --json 74496c64b0637103f87cceec3f37932a48813e65

⚠️ 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 74496c64b0637103f87cceec3f37932a48813e65 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: b5a3141e7f0a2efdf94e9ae94b88d935cf96f37a
Local-runs: none
Scope: packages/rest/src/import-prepare.ts, packages/rest/src/import-inline-mapping-entry.test.ts + packages/spec/src/api/export.zod.ts, packages/core/src/utils/import-coerce.ts, packages/core/src/utils/import-mapping.ts, packages/core/src/utils/import-runner.ts, packages/rest/src/import-mapping.ts, packages/rest/src/data-action-routes.ts, packages/plugins/plugin-auth/src/admin-import-users.ts, packages/client/src/index.ts, content/docs/data-modeling/import-mappings.mdx, content/docs/references/api/export.mdx, content/docs/references/api/error-code-ledger.mdx, .changeset/25078-csv-text-cell.md

Inputs read: card #25491 (body + 3 comments: triage 6370310580, claim 6371842583, os-dev-report 6374589262), PR #25540 body and file list (3 paths), git diff origin/main...b5a3141e7f (+365/−1), 41 check-runs on the head: 34 success, 7 skipped (path-filtered opt-in and matrix shards: 18.x upgrade harness, Build Docs, Console Pin Gate, Dogfood shard matrix, Dogfood Verify CLI, Packed-tarball smoke, Temporal Conformance), 0 failed. Every derived gate family the diff reaches is green on the head, the Governed Surface Queue Guard included.

① Derived judgments

Accept-set and public-surface changes the diff implies, read in the cited code:

  1. Inline FieldMappingEntry.transform ∈ {trim, uppercase, lowercase} is now applied per string cell — RIGHT. FieldMappingEntrySchema (packages/spec/src/api/export.zod.ts) declares the enum with .describe('Transformation to apply during mapping'); ImportMappingSchema admits the array form on ImportRequestSchema.mapping; on main the parser kept mapping[e.sourceField] = e.targetField and nothing else, so the card's declared-but-dropped defect is real. The dev's "implemented nowhere" measurement holds: git grep for case 'uppercase'|'lowercase'|'trim'|'date_format' across packages (tests and spec excluded) returns nothing, and no consumer of ExportImportTemplate exists outside spec, so there was no shared implementation to reuse and the PR adds exactly one table (INLINE_TRANSFORMS). Applied after the rows are built and the maxRows ceiling, before metaMap/coercion: the right seam, and trimWhitespace (default on) still trims downstream, so trim only matters when the request turns it off — the test pins that case.
  2. none and lookup copy the cell — RIGHT. Matches the named path's case 'none': case 'lookup' in core applyMappingToRows (lookup resolved downstream by metaMap reference resolution). A plain entry or transform: 'none' with no default and not required yields no rule, so the row is built exactly as before — the control test pins the byte-identical rename.
  3. defaultValue fills a blank cell, blank judged by core isBlank(value, nullValues) — RIGHT. isBlank is the predicate import-coerce.ts uses and is exported from core's index (export * from './utils/import-coerce.js'); @objectstack/core is already a workspace:* dependency of rest and already imported by five rest sources. Missing column (no header named sourceField) is handled: row[targetField] is undefined → blank → default written. null as a default writes null (hasDefault is !== undefined), which is the only coherent reading.
  4. required: true with a still-blank cell refuses the whole request 400 MISSING_REQUIRED_FIELD before any row, dry run and commit alike — RIGHT as a contract, with the row-vs-request choice judged under ③. Code and status: MISSING_REQUIRED_FIELD is in the error-code ledger (error-code-ledger.mdx line 212) and the ledger fixes it at 400 with the single controlled_by_parent 422 exception, which this is not. The refusal's row numbers are i + 1 over the built rows; the runner numbers its report const rowNo = i + 1 over the same array (import-runner.ts line 1115), so the refusal and the per-row report agree. Both REST doors answer res.status(prep.status).json({ code, error }) — the same envelope every other prepareImportRequest refusal uses; the test's error?.code ?? code reads it correctly.
  5. date_format (and any name outside the table) refuses 400 UNSUPPORTED_TRANSFORM — RIGHT. Same code, same door, same precedent as the named mapping's declared-but-refused javascript (core applyMappingToRows default arm; documented in import-mappings.mdx lines 129 and 221). FieldMappingEntry carries no format argument, so the transform has no defined input; Prime Directive chore: version packages #10 allows "reject loudly" over "declare and skip". The spec enum is left as is: the same FieldMappingEntrySchema serves ExportImportTemplateSchema in both directions, so trimming date_format there is a wider spec decision, correctly out of this card. The accept-set narrows exactly as declared: two request shapes that ran on main now 400.
  6. Reach of the narrowing — WRONG as stated. The PR body and changeset say the rules run "for both doors that parse through this function (the sync import and the async import job)". prepareImportRequest has THREE non-test callers on main: data-action-routes.ts lines 369 and 483 (the two named) and packages/plugins/plugin-auth/src/admin-import-users.ts line 550, which spreads ...body into the parser so an inline mapping array reaches it unchanged. The repository's own precedent for this parser names all three (.changeset/25078-csv-text-cell.md: "the sync import …, the async import job … and plugin-auth's admin user import (POST /api/v1/auth/admin/import-users), with an inline mapping or a named one"). The behaviour at that third door is the same and correct (it answers fail(prep.status, prep.code, prep.error)), and plugin-auth's own code is untouched so no changeset on it is owed — but a BREAKING entry that ships as the @objectstack/rest CHANGELOG must name every door it narrows, and a released entry can only be corrected by a dedicated docs-only PR afterwards. Fix: name the third door in the changeset body and the PR body's Clause-② sentence.
  7. Documented contract on the inline form — WRONG, left stale. content/docs/data-modeling/import-mappings.mdx (hand-written tree, editable in a code PR) states in its "Inline mapping vs mappingName" table: Transforms | None — rename only for the inline form (line 202), and its "Rejections before any row is read" table (lines 212–223: "whole-request failures — nothing is written, and there is no per-row report") lists UNSUPPORTED_TRANSFORM only for the artifact's javascript and does not list MISSING_REQUIRED_FIELD at all. After this diff the inline form carries transforms, defaults and a required rule, and two new whole-request refusals exist. The page now contradicts the shipped contract in the one place an operator looks. The dev's report said "no file outside packages/rest" and flagged only the import-mapping.ts docblock; this page was not noticed. The references/api/export.mdx rows are auto-generated from the unchanged spec and stay correct. Fix: update the two tables on that page (and, since the package is already being edited, the import-mapping.ts seam docblock "inline = a plain … RENAME" that the dev deferred).
  8. No other surface moves. No packages/spec change (the TypeScript Type Check artifact gates are green), no new export from rest (InlineEntryRule, INLINE_TRANSFORMS, readInlineEntryRule, applyInlineEntryRules are module-private), the compact { column: field } record and the named mappingName path (mutually exclusive via CONFLICTING_MAPPING, so the new block never runs beside applyMappingToRows) are untouched, the client SDK's import(object, request: ImportRequest) types are unchanged. In-repo senders of an inline entry with a transform: none outside spec and tests (git grep over client, cli, console, plugins, docs, examples) — so no in-repo caller is broken by the narrowing.
  9. Tests — RIGHT. Route-level through the real POST /api/v1/data/:object/import handler on sqlite :memory:, eight cases covering the card's reproduction, the email-merge case, trim under trimWhitespace: false, default for blank cell and missing column, required refusal on dry run and commit with zero rows written, required satisfied by default, date_format refusal, and the plain-entry control. The ablation report (guard flipped → 5 of 8 red, the 3 parse-time/control cases green) is consistent with the code paths each case exercises.

② Semver level

.changeset/25491-import-inline-mapping-entry-rules.md: '@objectstack/rest': patch, title fix(rest)!:, **BREAKING** banner with Before / Now / Fix, exactly one adr-0087: not-required (no-migration-prescription) marker, and both declaration lines present line-initial on the changeset AND the PR body: Clause-②: no (narrowing) and Metadata-protocol: none.

Judged against the rule: the kind is none (nothing a metadata author writes changes — a REST request body's handling moves, no spec key, authorable spelling or stored shape), so the level is patch; a non-protocol change that is breaking "ships patch and keeps its BREAKING banner and ADR-0087 disposition" — which this changeset does. (narrowing) is the right arm: two accepted request shapes are now refused, nothing is widened. The only package the diff moves is @objectstack/rest; plugin-auth inherits the behaviour through its workspace dependency with no source change, so no second changeset is owed. Check Changeset, check-adr-0087-registration and the queue guard are green on the head. Level, arm and kind: right. The body's prose defect is ①-6 (reach), not the level.

Clause-②: no (narrowing)

③ Boundary flags

Dev flags (os-dev-report 6374589262, deviations; open_questions is empty):

  • date_format refused 400 UNSUPPORTED_TRANSFORM rather than implemented — ANSWERED, accepted. No format argument exists on the entry; the card's own "Expected" offered a clear refusal as an acceptable outcome; the javascript precedent in the same pipeline is the same shape (declared by spec, refused loudly at the door). The spec enum stays because it also serves export templates; whether to trim or parameterise date_format on FieldMappingEntry is a spec question outside this card — not filed here, since the loud refusal means nothing is faked (Prime Directive chore: version packages #10 corollary satisfied).
  • A required miss refuses the whole request rather than failing the row — ANSWERED, accepted with the reasoning recorded. The reason given (the core runner has no pre-failed-row seam; core is outside the file surface) is true on main: runImport builds results[i] itself from coercion and write outcomes and takes no caller-supplied failures. Whole-request refusal is the shape every other mapping-level verdict in this parser takes (refuseUnknownMappingTargets, CONFLICTING_MAPPING), it names every offending row so one edit fixes the file, it runs on the dry run too so the preview still reports it, and nothing is written. A per-row variant would be a core change under its own card; the dev noted it in Acceptance notes, which is the right carrier.
  • Clause-② moved from no to no (narrowing), ! title, patch + BREAKING — ANSWERED, right (see ②).
  • Repo-wide pnpm lint not run locally, narrowed to the two files — ANSWERED: Lint & Repo Gates and Lint · ESLint are green on the head; the local gap is closed by CI.
  • PR body opens with the dispatch-mandated HTML attribution comment — ANSWERED: a platform marker the dispatch requires; it is not part of the contract under review.
  • Out-of-scope note: import-mapping.ts docblock calls the inline mapping a plain rename — ESCALATED to a fix in this PR together with the docs page it mirrors (①-7): the seam description is in the package this PR already edits, and the hand-written docs page that restates the same rule is the one the hunks falsify.

Not flagged by the dev, found here: ①-6 (the third door the narrowing reaches is unnamed) and ①-7 (the inline-vs-named docs table and the pre-row refusal table are now false). Both are prose on public surfaces (the CHANGELOG-bound changeset and the operator-facing docs page), both are small, and both must be corrected on the head before this narrowing lands.

What lifts the verdict: (a) changeset body and PR Clause-② sentence name POST /api/v1/auth/admin/import-users (plugin-auth) beside the two REST doors; (b) content/docs/data-modeling/import-mappings.mdx — inline column of the comparison table says the entry form carries transform (trim / uppercase / lowercase; none / lookup copy), defaultValue and required, and the pre-row refusal table gains the inline date_format (and undeclared-name) cause under UNSUPPORTED_TRANSFORM plus a MISSING_REQUIRED_FIELD row; (c) the packages/rest/src/import-mapping.ts seam docblock stops calling the inline form a plain rename. No code change is asked for.

Implemented-by: claude/issue-25491-import-inline-transform
Reviewed-by: session_01CxBKycaiUpA1yGx1XyoGqX

VERDICT: FAIL


Generated by Claude Code

This branch has not been deployed

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

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

/import: inline mapping entries' transform (uppercase / lowercase / trim …) is accepted and silently ignored

2 participants