Repository navigation
Commit 9d29223
fix(spec): the ImportRequest.matchFields describe names the multi-value or structured match-key refusal (#27658)
<!-- claude-projects-attribution: {"github_login":"marchtian"} -->
_Requested by **田騏** · [project
thread](https://claude.ai/code/project/chan_01VmuUhXU9SSUzqJYyXSmYSi?thread=cmsg_01VmuUhXU9SSUzqJYyXSmYSi8fwG9uuvQnfYYbTRahdmZw)_
Fixes #27583
Clause-②: no
Metadata-protocol: none
## 维护者速读
- **改了什么**:`ImportRequestSchema.matchFields` 的 describe 补了一句:update /
upsert 的匹配键若指向多值或结构化字段(`multiple: true` 的
select/radio/lookup/user/file/image、multiselect/checkboxes/tags、json/composite/repeater/record/location/address/vector),导入入口在读任何一行之前以
`400 INVALID_REQUEST` 拒绝并点名字段;绕过入口直接进 import runner 的调用方(mapping 的
connector pull)每行以 `VALIDATION_ERROR` 失败。同句写明命名 mapping 的 `upsertKey`
作键时同样判定,补救是用单值字段做键。API 参考页随之重生成,附 patch changeset。
- **为什么改**:#27562 落地后服务端已经这样拒绝,但 JSON schema 与 API 参考里的 describe
只字未提,集成方或 AI 作者按 describe 选 matchFields 只能在被拒时才知道。
- **风险与回滚**:纯文本,不改 schema、不改接受集、不改服务端回答;回滚即 revert 本 PR。
- **先看**:`packages/spec/src/api/export.zod.ts`(describe
新增的那一句);`.changeset/27583-import-match-fields-describe-multi-value-key.md`;`content/docs/references/api/export.mdx`(生成件,两行)。
- **两分钟验证**:`pnpm --filter @objectstack/spec build && pnpm --filter
@objectstack/spec check:docs`,预期 `232 generated files in sync`;或打开
`content/docs/references/api/export.mdx` 第 56 行,应看到 `400
INVALID_REQUEST` 与 `VALIDATION_ERROR` 两处。
- **需要你做的**:无,席位落地。
## What changes
One sentence appended to the `matchFields` describe of
`ImportRequestSchema` (`packages/spec/src/api/export.zod.ts`), written
to the code the base already runs:
- In `update` / `upsert`, a key naming a field that holds a list or a
structured value is refused with `400 INVALID_REQUEST` before any row is
matched or written, naming each such field. The request's `matchFields`
and the named mapping's `upsertKey` it falls back to are judged the same
way (`packages/rest/src/import-prepare.ts`, `prepareImportRequest`, via
`multiValueMatchKeyRefusal`).
- Which fields: the population the spec's own arbiters define —
`isMultiValueField` (a `multiple: true` select, radio, lookup, user,
file or image field; a `multiselect`, `checkboxes` or `tags` field) and
`STRUCTURED_JSON_TYPES` (`json`, `composite`, `repeater`, `record`,
`location`, `address`, `vector`),
`packages/spec/src/data/field-value.zod.ts`.
- The remedy: match on single-value fields.
- A caller that hands rows to the import runner without the door (a
mapping's connector pull) gets every row failed with `VALIDATION_ERROR`
and the same message (`packages/core/src/utils/import-runner.ts`,
`runImport`).
`content/docs/references/api/export.mdx` is regenerated by `pnpm
--filter @objectstack/spec gen:docs` (two `matchFields` rows: the sync
import request and `CreateImportJobRequestSchema`, which is the same
object). Changeset: patch on `@objectstack/spec`, text only.
## Why
Measured on `178b5238`: the describe (L295–L311) said nothing about the
refusal; the server path reads the request's `matchFields` by hand in
`prepareImportRequest` (no Zod parse of `ImportRequestSchema` on that
path, so the describe documents the published contract and enforces
nothing), and the refusal landed in PR #27562 (commit `f557d90f`, an
ancestor of the base). The published JSON schema and the generated API
reference carry the describe, so a caller reading them was not told
about a 400 the server answers.
`content/docs/data-modeling/import-mappings.mdx` already states both
halves (L263, L282); the describe now agrees with it.
## Tests
No behaviour changes, so no new test. Run on the affected package:
On the final head `e40c42dd`, each under `scripts/pm/os-verify-lock.sh`
(verdict = the lock's `VERDICT command-exit` line):
- `pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2
src/api/export.test.ts`: Test Files 1 passed (1), Tests 57 passed (57);
command-exit 0.
- `pnpm --filter @objectstack/spec typecheck`: command-exit 0 (1m24s).
- `pnpm --filter @objectstack/spec test`: Test Files 752 passed (752),
Tests 22343 passed | 1 todo (22344); command-exit 0 (4m24s).
No ablation: the diff changes no tested behaviour (describe text, a
changeset, a regenerated reference).
## Acceptance notes
- `ImportValidationConfigSchema.deduplication.matchFields` (same file,
L115) is left as it is: `git grep deduplication` over
`packages/rest/src`, `packages/core/src`, `packages/plugins`,
`packages/services` finds no reader, so it is not the key the door
judges. A dormant describe, not a finding (noted, not filed; `carrier:
none`).
- `ui/packages/types/src/data.ts` L1129 carries a one-line TS docblock
mirror of the describe's first sentence (`matchFields?: string[]`,
already behind the key-set union). A TypeScript type comment on the UI
side, not a metadata surface and not this card's file; noted, not filed
(`carrier: none`).
- An `insert` import matches nothing and is not judged; the sentence
scopes itself to `update` / `upsert` as the card asked.
## Verification
Local gates on `e40c42dd`, exit codes captured to a file before any
pipe. `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derived 97 families from the three changed
files; every one was run.
- 94 exit 0, among them spec `check:docs` (`✅ 232 generated files in
sync with packages/spec`), `check:api-surface`,
`check:authorable-surface`, `check:export-origins`, `check:llms-txt`,
`check:variant-docs`, `check:yaml-examples`, `check:spec-parsed-alias`,
`check:spec-docblock-symbol-anchors`, `check:doc-authoring`,
`check:nul-bytes`, `check:issue-citations`,
`check:closed-ceremony-text`, `check:published-files`,
`check:merge-driver`, `check:pm-prior-rulings`,
`check-adr-0087-registration --base origin/main`, `check-empty-changeset
--base origin/main`, `check-changeset-no-major --base origin/main`,
`check:changeset-gate-self-tests`, `check-adr-links`,
`check-doc-route-spelling --advisory`, the docs-audit scripts and the
`--self-test` batteries.
- 3 NOT MEASURED, prerequisite not met (exit 3: they import every
published package's built `dist/`): spec `check:skill-examples`, `pnpm
check:dual-build-cjs-loads`, `pnpm check:lean-entry-closure`. CI's `Lint
& Repo Gates` runs them.
- `dispatch-gates --ran`: "97 derived, 94 run, 3 NOT-MEASURED, 0 UNRUN".
- `pnpm --filter @objectstack/spec check:generated` after the build on
the merged head: 1 of 14 stale (`check:docs`), regenerated with
`gen:docs` (two rows), then all 14 up to date (exit 0).
- The full battery (`pnpm lint`, Test Core shards, the UI jobs) is CI's
and was not re-run locally.
- `origin/main` (`efa2915e`) merged once before opening, as `4be9eb37`
via `scripts/pm/os-regen-merge.sh`; `packages/spec` and
`content/docs/references` had not moved on main since the base, so no
generated-file conflict.
- Independent full review at `CONTRACT_REVIEW_TIER` (an isolated
subagent, not the drafter): PASS on `29c63f99` with no must-fix and no
should-fix; two of its optional notes applied in `d19be4c9`; delta
review PASS on `e40c42dd`. Both records are posted as a comment on this
PR.
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
- [x] 2. Every name I added uses the casing for its kind. (No name
added.)
- [x] 3. No formatter ran; `pnpm lint` is CI's.
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset.
- [x] 6. `packages/spec` touched: its generated artifacts are
regenerated and committed.
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English (the 维护者速读 block is the
repo's own Chinese reader section).
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
---
_Generated by [Claude
Code](https://claude.ai/code/session_017ihL2cAD3GsST8mQKFsbJZ)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 524befb commit 9d29223
3 files changed
Lines changed: 22 additions & 3 deletions
File tree
- .changeset
- content/docs/references/api
- packages/spec/src/api
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
53 | 53 | | |
54 | 54 | | |
55 | 55 | | |
56 | | - | |
| 56 | + | |
57 | 57 | | |
58 | 58 | | |
59 | 59 | | |
| |||
311 | 311 | | |
312 | 312 | | |
313 | 313 | | |
314 | | - | |
| 314 | + | |
315 | 315 | | |
316 | 316 | | |
317 | 317 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
307 | 307 | | |
308 | 308 | | |
309 | 309 | | |
310 | | - | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
311 | 320 | | |
312 | 321 | | |
313 | 322 | | |
| |||
0 commit comments