Repository navigation
fix(core): an import row with several bad cells reports every one in errors (#25582) - #26543
Conversation
…main) Claude-Session: https://claude.ai/code/session_01FEkQ5otdGFpkLtVKZRmYpa Co-authored-by: Claude <noreply@anthropic.com>
…errors The runner copies coerceRow's cell errors onto the failed row as ImportRowResultSchema.errors, beside the first-error keys, which keep naming element 0. A validate-verdict row and a refused write carry none. Claude-Session: https://claude.ai/code/session_01FEkQ5otdGFpkLtVKZRmYpa Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 6 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 3 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 29 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 5b1734541c7c8db836203a6406f8ced01ff1fdec && git checkout 5b1734541c7c8db836203a6406f8ced01ff1fdec
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 185512ccaf6394364a0da1af55cd8f1bfae37869 7a406058637b388f1fc1dfc13d7b50069570f497 && git checkout -B drift-repro 185512ccaf6394364a0da1af55cd8f1bfae37869 && git merge --no-ff 7a406058637b388f1fc1dfc13d7b50069570f497
node scripts/docs-audit/affected-docs.mjs --json 185512ccaf6394364a0da1af55cd8f1bfae37869
|
Contract reviewServed-tier: Inputs read: card #25582 (body and all 11 comments, the ruling line ① Derived judgmentsDiff:
Check-runs on ② Semver levelChangeset Clause-②: ③ Boundary flagsDev report
Implemented-by: VERDICT: PASS |
席位核对 · ACCEPT ·
|
| item | reading |
|---|---|
| Shape | Draft, base main, Fixes #25582. 4 files: packages/core/src/utils/import-runner.ts, a core test, a REST route test, and a patch changeset for @objectstack/core. Clause-②: yes (widening …) and Metadata-protocol: none appear in both the body and the changeset. |
| Fix | Proposal 4 of ruling 6377844879 letter A, under the spec seat's fill rule 6434667735. The one coerceRow failure site copies every cell error into errors as { field, code, message }, in column order. The first-error keys stay as element 0. The site sits before the dry-run/commit split, so sync, async, dry run and commit all carry it. Validate-verdict rows, refused writes and match-key rows carry no errors, and tests pin that. |
| Contract | Review 6436235757: the widening matches the landed spec member (#26454); patch is the right level. Noted, not wrong: plugin-auth's identity import copies runImport rows verbatim (admin-import-users.ts:777), so that endpoint reports errors too, under the same contract. |
| Evidence | CI on head 7a40605863: 39 succeeded, 8 expected skips, none failed or pending; mergeable clean. Red first on main (core 3 red, route 4 red), and the ablation turns all 7 red. Derived gates: 61 of 61 exit 0, with --ran reconciled. |
| Card | This lands the last ruled proposal (3 is unruled), so the card closes with Fixes. |
| Landing | 「按照 pm-dispatch 规则复核过的都授权」 (Leehom, 2026-10-25T11:35Z): ready and auto-merge through the relay, once every check on this head is green or an expected skip. |
Generated by Claude Code
Requested by Leehom · project thread
Fixes #25582
This closes the card: maintainer ruling
6377844879letter A ruled proposals 1, 2 and 4, and not 3. Proposals 1 and 2 landed in #25596; this PR is proposal 4, so every ruled proposal has then landed. Proposal 3 (optionaliases) is not ruled and nothing here implements it.Clause-②: yes (widening: a failed import row whose cells did not coerce gains the optional
errorslist,ImportRowResultSchema.errors; every existing key keeps its value)Metadata-protocol: none
维护者速读
errors列表,按列序列出每个坏单元格(field、code、message);原来的field/code/error不变,仍指第一个。import-runner.ts那一处赋值。packages/core/src/utils/import-runner.ts(行报告赋值处与ImportRowResult.errors注释),packages/rest/src/import-row-cell-errors.test.ts(四条路径加两种不带errors的失败)。pnpm --filter @objectstack/rest exec vitest run src/import-row-cell-errors.test.ts,预期 6 个用例全过;用例里一行同时填坏日期、坏数字、坏选项,结果的errors按列序列出这三项。What changes
runImport(packages/core/src/utils/import-runner.ts) copiescoerceRow's cell errors onto the failed row aserrors, one{ field, code, message }element per failed cell, in column order. The first-error keysfield,codeanderrorare unchanged and are element 0, asImportRowResultSchema.errors(landed in feat(spec): ImportRowResultSchema declares errors, every cell error of a failed row #26454) states.POST /api/v1/data/:object/importand on the async job'sGET /api/v1/data/import/jobs/:jobId/results(the job storessummary.resultsand the results route returns the items verbatim).6434667735): a validate-verdict row (erroriscomposeValidationMessage(...)), a refused write (toFailedResult/adoptDoorVerdict), and the duplicate / ambiguous / blank match-key rows.ImportRowResult(@objectstack/core) gains the optionalerrors?: ValidateDataIssue[], documented with that rule.@objectstack/corepatch.Why
An operator importing a sheet with several bad cells in a row fixed one cell, re-ran, and only then saw the next. The trial in the card took two to three rounds per row.
Tests
packages/core/src/utils/import-runner-cell-errors.test.ts(new, 5 cases, against anImportProtocolLikedouble): three bad cells around a good one report all three in column order on the dry run and on the commit (the commit writes nothing), element 0 equals the first-error keys, every element has exactlyfield/code/message; one bad cell gives a one-element list; a validate-verdict refusal and a refused write carry noerrors.packages/rest/src/import-row-cell-errors.test.ts(new, 6 cases, realObjectQL+ protocol + SQLite withsys_import_job): the same row through the sync route and the async job, each on the dry run and the commit; and the absence pin for a dry-run validate verdict and a commit refused write (a missingrequiredfield), sync and async.main(45de8c6cba), tests committed alone as46ca67cf: core 3 failed / 2 passed, route 4 failed / 2 passed. The four route reds read the first cell only (field: opened,code: invalid_date) with noerrorskey. The two absence pins pass onmaintoo, as they should.Acceptance notes
sys_metadataand siblings) for the protocol to hand the route the object's field metadata. Without it every cell passes through uncoerced and the engine's validator answers instead, so a row with bad cells reports a joined validation message and no cell errors. The new route test registers them, asimport-year-first-day-option-case.test.tsdoes. That is test setup, not a product path.aliaseson options) stays unruled; closing this card does not decide it.Verification
7a40605863(after mergingorigin/main; the merge touched no import file).pnpm --filter @objectstack/core test: 123 files / 2793 tests passed;pnpm --filter @objectstack/core typecheckpassed (test layer included). Measured ata975cbab, before the merge.pnpm --filter @objectstack/rest test: 325 files, 6489 passed / 326 skipped;pnpm --filter @objectstack/rest typecheckpassed. Measured ata975cbab.7a40605863: the two new files plusimport-year-first-day-option-case,import-dryrun-parity,import-row-schema-drift-20701andimport-job-integration: 6 files / 49 tests passed.scripts/ablation-replace.mjs: theerrors:fill line deleted (anchor 1 to 0, blob4983d0e39a26to923db228c535), core rebuilt,ablation-dist-preflight --absentpassed (marker absent from all 14 built files). Core test 3 failed / 2 passed, route test 4 failed / 2 passed. Restored: blob equals HEAD,git diff HEADempty; rebuilt, preflight found the marker back indist/and the tree clean.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 61 commands at7a40605863; all 61 exit 0. Two first ran exit 3 for a missing prerequisite (check-plugin-teardown-shape --self-teston a shallow clone;check:dual-build-cjs-loadsbefore a full build) and were re-run green aftergit fetch --unshallowand the full buildcheck:type-check-debtperforms.--ran: "61 derived famil(ies) accounted for — 61 run, 0 NOT-MEASURED".pnpm exec eslint --no-inline-config packages/core packages/rest: exit 0.test:repo(no repo-project test touched; CI Test Core runs it) and the full dogfood battery (no!in the title and no narrowing arm).Human Minimum
packages/specholds no runtime logic. (Nopackages/specchange.)pnpm lintpasses (scoped to the touched packages).main, one pull request.packages/spectouched: n/a.content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.Generated by Claude Code