spec: ImportRowResultSchema declares an optional errors array (every failed cell of a row), so /import can report more than the first error (#25582 proposal 4) #26425
Description
Filing gate: ③ maintainer-directed. The spec half of ruling 6377844879 (batch #9 item 3a, letter A, maintainer 2026-10-24T00:43:45Z) on #25582, proposal 4. Filed by the domain:server seat 1 under cross-repo-coordination.md rule 3, because the server lane holds no packages/spec (lanes/server.md red line). ⛔ Not a claim.
Business reason: an /import row with several bad cells reports only the first. The user fixes one cell, re-imports, and hits the next, once per cell (#25582: hotsrm's spreadsheet imports).
Reader: the domain:spec lane, packages/spec/src/api/export.zod.ts. The server half (import-runner.ts) rides #25582, which carries Blocked-by: this card.
Dedupe (open AND closed): "ImportRowResultSchema errors array every cell error of a row" → only #25582 and its closed predecessor #25493; no spec card.
What to declare
ImportRowResultSchema (packages/spec/src/api/export.zod.ts:401) is a z.object, so it strips an undeclared key, as its own docblock records for warnings. Declare one optional member:
errors: an array, one element per failed cell,{ field, code, message }. Present only on a failed row that has at least one cell error.- The existing first-error keys
field,codeanderrorstay, and keep naming the first element, so every current reader is unchanged. - Reuse an existing issue element shape if one fits (for example
ValidateDataIssueSchema, whichwarningsalready uses) rather than a new one. The spec seat measures which.
Producer: the core coercion already builds this list. coerceRow returns every error, and import-runner.ts (about :1132) keeps errors[0].
Out of scope
- Proposal 3 (
aliaseson options) is not ruled (letter A excludes it). - The runner change: /import: common spreadsheet cell shapes fail whole rows (dotted / compact dates, option value case, one error per row) #25582's server half, after this lands.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 27, 2026
Claim: PM loop round 1 · 2026-10-27T03:45Z
Session:
session_01WGufzfhobWEgUUrjz4rrbhAccount:
os-support-aiBranch:
claude/project-thread-fb0xrv(card threadsession_01WGufzfC9dmHR7kTAVNHv56. The platform assigns the thread's branch and the thread can push only that one, so the branch is written as measured instead ofclaude/issue-26425-<slug>.)Worktree:
objectstack-issue-26425Domain:
domain:specSeat:
domain:spec#1File surface:
packages/spec/src/api/export.zod.ts(ImportRowResultSchema, one optionalerrorsmember), one spec test underpackages/spec, one changeset, and the artifactscheck:generatedregenerates. ⛔ Nopackages/core/src/utils/import-runner.ts(that is #25582's server half). Stop on a breach and explain it in the report.Container & model:
S,mode:thread,model: TIER_DEFAULT, default effortClause-②: yes (widening)
Metadata-protocol: none
Responsibility: n/a. This is a ruled protocol addition, not a defect card.
Thread-read: none
Premise: holds (read at
origin/main:ImportRowResultSchemaatpackages/spec/src/api/export.zod.ts:401is az.objectwithfield/code/errorand noerrorsmember, andValidateDataIssueSchemaexists atprotocol.zod.ts:2456)Serial constraints cleared: yes. No open PR or claim names
export.zod.ts. #25582's server half is blocked behind this card and does not touch spec.Ruling-ref: #25582 ruling 6377844879 (batch #9 item 3a, letter A), proposal 4. Proposal 3 (
aliases) is excluded.objectstack-fleet commentedon Oct 27, 2026
os-dev-report
{
"issue": 26425,
"status": "done",
"branch": "claude/project-thread-fb0xrv",
"pr": "#26454",
"session": "session_01WGufzfC9dmHR7kTAVNHv56",
"premise_still_valid": true,
"summary": "ImportRowResultSchema (packages/spec/src/api/export.zod.ts) declares one optional member, errors: z.array(ValidateDataIssueSchema), one element per failed cell of a failed row, present only on a failed row with at least one cell error; field/code/error stay and name element 0 (describes and docblock say so). Element shape reused, not new: its required members are exactly the ruling's { field, code, message }, coerceRow's FieldCoerceError fits it unmapped, and the row's warnings plus the validate verdict's row errors already use it; the only cost is the advisory-only optional rule/severity, documented as never present on a cell error. Spec only, import-runner.ts untouched. Premise held at origin/main 75afeff: the schema had no errors key and import-runner.ts:1221 keeps errors[0].",
"tests": "head 2c2ade7. pnpm --filter @objectstack/spec test (lock): Test Files 727 passed (727), Tests 22016 passed | 1 todo, VERDICT command-exit 0. pnpm --filter @objectstack/spec typecheck: VERDICT command-exit 0, check:test-typecheck OK. New describe in src/api/export.test.ts, 7 tests (parse keeps errors in order; field/code/error agree with element 0; optional; element toBe ValidateDataIssueSchema and equals the warnings element; element without message refused at errors.0.message; ImportResponseSchema and ImportJobResultsSchema keep it; expectTypeOf ValidateDataIssue). Ablation via scripts/ablation-replace.mjs (errors: renamed ablatedErrors:, anchor 1 to 0, blob ee8a1e9b6078 to 69e10a948a46): Tests 5 failed | 52 passed (57), the optional-key test stayed green as expected; restored, blob == HEAD, git diff HEAD empty. Test imports ./export.zod from src, so no dist leg.",
"gates": "local, head 2c2ade7: check:generated first named content/docs/references/** stale, gen:docs, then 'All 14 generated artifacts are up to date' (exit 0). dispatch-gates --commands derived 106; all run with exit codes recorded; --ran: '106 derived famil(ies) accounted for, 99 run, 7 NOT-MEASURED (7 DERIVED from a recorded exit 3)'. NOT MEASURED, prerequisite refusals unrelated to packages/spec: lint check:doc-formula-expressions and check:doc-security-posture (formula/lint not built), spec check:skill-examples (client-react dist not built), check:docs-transcript-drift (lint not built), check:dual-build-cjs-loads and check:lean-entry-closure (other dists not built), check-plugin-teardown-shape --self-test (shallow checkout). Local-only check:dev-prereqs exit 1: 37 workspace packages (mostly ui/) not built, local state not this diff; spec dist stamp fresh. CI on PR at report time: No other open PR may claim the same issue = success; the rest in_progress (Lint lanes, Type Check lanes, Check Changeset, Governed Surface Queue Guard, Check PR Size, claim-branch check).",
"dogfood_battery": "n/a: no ! in the PR title, Clause-② arm is (widening)",
"mcp_calls": "2: issue_read x2 (get, get_comments on #26425); no MCP write",
"api_writes": "2 relay strokes via scripts/pm/fleet-write/dispatch.mjs: (1) pr_create POST /repos/objectstack-ai/objectstack/pulls + POST issues/26454/assignees, read-back identical 8453 bytes, assignee os-support-ai; (2) this report: comment POST issues/26425/comments, plus pr_request_reviewers POST pulls/26454/requested_reviewers (os-support-ai, per the project's PR-attribution instruction). Plus git push x3 to the branch.",
"open_questions": [
{
"question": "On a dry-run row the validate verdict rejects, import-runner.ts (about :1293) sets error to composeValidationMessage(verdict.errors), every finding joined by '; ', not element 0's message. If #25582's server half also fills errors on those rows, error would not name element 0. Which does #25582 do?",
"options": [
"A: #25582 fills errors only from coerceRow's cell errors (:1221), where error already equals element 0's message; verdict rows keep today's composed error and no errors key",
"B: #25582 fills errors on verdict rows too and keeps the composed error there; a follow-up spec PR adds one describe sentence that error may compose every element on such a row",
"C: #25582 fills errors on verdict rows and sets error to element 0's message there"
],
"recommendation": "A for #25582's first cut, because it is exactly the ruling's cell-error scope and needs no spec follow-up; B later only if verdict rows prove to need it. Carrier: #25582, not this PR."
}
],
"out_of_scope_findings": [
"carrier: #25582 · the composed-error divergence above, noted in the PR Acceptance notes, not filed"
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 27, 2026
Claim amendment ·
domain:specseat 1 (#6017) · sessionsession_01WGufzfhobWEgUUrjz4rrbh· 2026-10-27T04:29ZSupersedes the
Metadata-protocolline of claim 6433227708. Everything else in it stands.Metadata-protocol: added
Clause-②: yes (widening)
Why: the PR moves
packages/spec/authorable-surface/api.json(api/ImportRowResult:errors), andcheck-changeset-no-majorrefusesnonewhen the baseline moves (none-baseline-moved). The claim'snonewas the seat's error, and the dev copied it. The changeset grade becomes'@objectstack/spec': minor, as with thewarningswidening of the same schema. Contract review FAIL: 6433766607.objectstack-fleet commentedon Oct 27, 2026
Landed ·
domain:specseat 1 (#6017) · sessionsession_01WGufzfhobWEgUUrjz4rrbh· 2026-10-27T05:36ZPR #26454 merged at
43714cae(merged 2026-10-27T05:35Z).43714caeis an ancestor oforigin/main. On every path the PR touched, the merge tree matches the accepted headeff10df5byte for byte.ImportRowResultSchemanow declares an optionalerrorslist: every cell error of a failed row, in theValidateDataIssueSchemashape. It is filled from the import's cell coercion, andfield/code/errorkeep naming element 0.@objectstack/specgrade: minor,Metadata-protocol: added. The server half is #25582. It fillserrorsfromcoerceRow's cell errors only (option A); a fill from verdict rows or refused writes would be a spec change. The card closed throughFixes #26425, and this seat removespm:dispatched.