Repository navigation
fix(spec): the ImportRequest.matchFields describe names the multi-value or structured match-key refusal - #27658
Conversation
…ue or structured match-key refusal An update / upsert import whose match key names a field that holds a list or a structured value is refused at the import door with 400 INVALID_REQUEST, and the import runner fails every row with VALIDATION_ERROR for a caller that reaches it without the door; the describe said nothing about either. It now names the refusal, the fields it covers (the spec's isMultiValueField and STRUCTURED_JSON_TYPES population), the remedy and the runner's answer. Text only. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2cAD3GsST8mQKFsbJZ
…ields describe Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2cAD3GsST8mQKFsbJZ
…un and names the same refusal for the runner Two review notes applied: the door refuses on a dry run as on the import, and the runner answers the same refusal (its message prefix names the key source, so "the same message" was loose). Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2cAD3GsST8mQKFsbJZ
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2cAD3GsST8mQKFsbJZ
|
domain:spec#4 · marchtian · session_017ihL2cAD3GsST8mQKFsbJZ Independent full review (project rule: a spec change gets a complete review at
|
📓 Docs Drift Check1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 167 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin aba7f85191098a655986c3fda924ca7743a31ce2 && git checkout aba7f85191098a655986c3fda924ca7743a31ce2
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin b761968d9df2a2a72bbbdb96b4f67ca78a58739f e40c42dda02beeebde47928540baf6c71e09aae9 && git checkout -B drift-repro b761968d9df2a2a72bbbdb96b4f67ca78a58739f && git merge --no-ff e40c42dda02beeebde47928540baf6c71e09aae9
node scripts/docs-audit/affected-docs.mjs --json b761968d9df2a2a72bbbdb96b4f67ca78a58739f |
|
domain:spec#4 · marchtian · session_017ihL2fi9zfJhUTubyJYb6V 席位核对 · ACCEPT · card #27583 · 2026-10-29T16:48Z
|
|
domain:spec#4 · marchtian · session_017ihL2fi9zfJhUTubyJYb6V Contract reviewServed-tier: ① Derived judgments
② Semver levelPatch on ③ Boundary flags
④ Design judgment
Implemented-by: Rendered by an isolated at-tier subagent (not the drafter) at VERDICT: PASS |
Requested by 田騏 · project thread
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。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
matchFieldsdescribe ofImportRequestSchema(packages/spec/src/api/export.zod.ts), written to the code the base already runs:update/upsert, a key naming a field that holds a list or a structured value is refused with400 INVALID_REQUESTbefore any row is matched or written, naming each such field. The request'smatchFieldsand the named mapping'supsertKeyit falls back to are judged the same way (packages/rest/src/import-prepare.ts,prepareImportRequest, viamultiValueMatchKeyRefusal).isMultiValueField(amultiple: trueselect, radio, lookup, user, file or image field; amultiselect,checkboxesortagsfield) andSTRUCTURED_JSON_TYPES(json,composite,repeater,record,location,address,vector),packages/spec/src/data/field-value.zod.ts.VALIDATION_ERRORand the same message (packages/core/src/utils/import-runner.ts,runImport).content/docs/references/api/export.mdxis regenerated bypnpm --filter @objectstack/spec gen:docs(twomatchFieldsrows: the sync import request andCreateImportJobRequestSchema, 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'smatchFieldsby hand inprepareImportRequest(no Zod parse ofImportRequestSchemaon that path, so the describe documents the published contract and enforces nothing), and the refusal landed in PR #27562 (commitf557d90f, 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.mdxalready 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 underscripts/pm/os-verify-lock.sh(verdict = the lock'sVERDICT command-exitline):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 deduplicationoverpackages/rest/src,packages/core/src,packages/plugins,packages/servicesfinds 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.tsL1129 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).insertimport matches nothing and is not judged; the sentence scopes itself toupdate/upsertas 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/objectstackderived 97 families from the three changed files; every one was run.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-testbatteries.dist/): speccheck:skill-examples,pnpm check:dual-build-cjs-loads,pnpm check:lean-entry-closure. CI'sLint & Repo Gatesruns them.dispatch-gates --ran: "97 derived, 94 run, 3 NOT-MEASURED, 0 UNRUN".pnpm --filter @objectstack/spec check:generatedafter the build on the merged head: 1 of 14 stale (check:docs), regenerated withgen:docs(two rows), then all 14 up to date (exit 0).pnpm lint, Test Core shards, the UI jobs) is CI's and was not re-run locally.origin/main(efa2915e) merged once before opening, as4be9eb37viascripts/pm/os-regen-merge.sh;packages/specandcontent/docs/referenceshad not moved on main since the base, so no generated-file conflict.CONTRACT_REVIEW_TIER(an isolated subagent, not the drafter): PASS on29c63f99with no must-fix and no should-fix; two of its optional notes applied ind19be4c9; delta review PASS one40c42dd. Both records are posted as a comment on this PR.Human Minimum
packages/specholds no runtime logic.pnpm lintis CI's.main, one pull request.packages/spectouched: its generated artifacts are regenerated and committed.content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.Generated by Claude Code