You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
/import: common spreadsheet cell shapes fail whole rows (dotted / compact dates, option value case, one error per row) #25582
Ruled: 6377844879 · batch #9 item 3a · letter A · 2026-10-24T00:43:45Z (proposals 1, 2, 4; not 3)
Context
Trial import of a simulated messy supplier ledger (hotsrm#10, the kind of sheet a purchasing department keeps in Excel for years). These are the cell shapes that failed rows which a person reads without hesitation. Each failure rejects the whole row, including rows where the bad cell is an optional field.
Cell
Field type
Result
Expected
2011.07.01
date
invalid_date
2011-07-01 (YYYY.MM.DD is the most common Chinese written date form)
20090615
date
invalid_date
2009-06-15 (8-digit compact date, common in ERP exports)
2005/3/8
date
accepted ✓
—
CN
select, option value cn
invalid_option
match option values case-insensitively (labels already are)
中国大陆 / 物流
select (labels 中国 / 物流商)
invalid_option
fine to refuse, but there is no way to declare synonyms on an option or picklist, so every legacy sheet needs a hand clean-up
And one reporting problem: when a row has several bad cells, only the first coercion error is reported (coerceRow collects all errors, the runner keeps errors[0]). The operator fixes one cell, re-runs, finds the next one. Two to three rounds per row in our trial.
Proposal
Accept YYYY.MM.DD and YYYYMMDD in parseDateCell (unambiguous, year first).
Case-insensitive match on option values.
Optional aliases: string[] on select options / picklist options, used by import coercion only. (not in letter A — a new permanent metadata key waits for a second app; ruling 6377844879)
Report all cell errors of a row (e.g. errors: [{ field, code, message }] next to the existing first-error keys).
Temporary measure in hotsrm
The import guide lists a cleaning checklist (date format, option labels, ISO country labels) to run on the sheet before importing.
Re-filed as objectstack-fleet[bot] from #25493, same body and labels. #25493 was written off the fleet relay, under the maintainer's personal account, by a project-thread session through GitHub MCP; pm-dispatch allows content writes only through the relay. #25493's thread (triage, ruling, comments) stays there for reference; this card replaces it in the queue.
Session: session_018fnREjMeK9XQeHDwkJXKMW
Claim: PM loop · domain:cli seat 1 · p2 · 2026-10-24T01:38Z
Session: session_01CxBKycaiUpA1yGx1XyoGqX
Account: os-sales (the seat's linked user; always the card's assignee)
Branch: claude/issue-25582-import-cell-shapes
Worktree: objectstack-issue-25582
Domain: domain:server (this seat's side of the server lane under the coordinator's split: former cli and engine paths)
Seat: domain:cli#1 (seat post #6024; 由Sales账户的项目负责)
File surface (read on origin/maince18e73483; stop on a breach): packages/core/src/utils/import-coerce.ts (parseDateCell, the select / multiselect option-value match), its tests, a route-level pin under packages/rest (test only), one changeset. This slice is ruling 6377844879 letter A proposals 1 (YYYY.MM.DD and YYYYMMDD dates) and 2 (option values matched case-insensitively). PR carries Part of #25582.
Not in this slice: proposal 4 (every cell error of a row). The row report is typed and parsed by ImportRowResultSchema (packages/spec/src/api/export.zod.ts), a z.object that strips an undeclared key, as its own docblock records for warnings. So an errors array needs a spec member first. That is the domain:spec seat's; a note for it follows on this card. The runner half (import-runner.ts ~:1132, which keeps errors[0]) is this seat's once the member exists.
Container & model: S, mode:subagent, model: default tier
Clause-②: no
Basis for the Clause-② line: two cell shapes that were refused (invalid_date, invalid_option) are now accepted; nothing that was accepted is refused. The arm is re-read against AGENTS.md on the measured diff (a widening arm if AGENTS.md names one).
Responsibility: n/a — ruled by the maintainer (6377844879, letter A); re-filed from #25493.
Thread-read: 6377844879
Serial constraints cleared: none of the 12 open PRs touches packages/core/src/utils/import-coerce.ts (file lists read in this act; PR #25540 touches packages/rest/src/import-prepare.ts only).
For the domain:spec seat · from domain:cli seat 1 (seat post #6024; lane read as domain:server) · session session_01CxBKycaiUpA1yGx1XyoGqX · 2026-10-24T01:39Z · ⛔ Not a claim.
Ruling 6377844879, letter A proposal 4: report every cell error of a row, not only the first. That needs one spec member before the server half can ship. ImportRowResultSchema (packages/spec/src/api/export.zod.ts:361) is a z.object, so it strips an undeclared key, as its own docblock records for warnings.
What the server half needs is an optional errors array on the row: one element per failed cell, { field, code, message }. The existing first-error keys field / code / error stay, and keep naming the first element. The core coercion already builds this list: coerceRow returns every error, and import-runner.ts ~:1132 keeps errors[0].
When the member lands, this seat carries the runner half (copy the list onto the failed row, on the dry run and the commit, for the sync route and the async job) on this card. Proposals 1 and 2 ship now from this seat as Part of #25582 (claim above).
{
"issue": 25582,
"status": "done",
"branch": "claude/issue-25582-import-cell-shapes",
"pr": "https://github.com/objectstack-ai/objectstack/pull/25596",
"session": "session_01CxBKycaiUpA1yGx1XyoGqX",
"premise_still_valid": true,
"summary": "Ruling 6377844879 letter A, proposals 1 and 2, in packages/core/src/utils/import-coerce.ts. (1) parseDateCell reads YYYY.MM.DD and YYYYMMDD (four-digit year, two-digit month and day, a real calendar day) for date and datetime cells. (2) matchOption matches option values case-insensitively in a third pass after exact value and label, storing the declared value. A cell naming two case-only-distinct declared values is not resolved. The spec refuses such a pair (FieldSchema: options.1.value must be lowercase); a programmatic registry.registerObject keeps it. Draft PR #25596 says Part of #25582: proposal 4 still waits on a spec member on ImportRowResultSchema, and import-runner.ts and packages/spec are untouched. Newest Claim 6378276682 names this branch. Card assignee and labels are untouched; the PR is assigned to os-sales at pr_create.",
"tests": "Head c630eacca0. `pnpm --filter @objectstack/core test`: 118 files / 2757 tests passed. Rest vitest over import-year-first-day-option-case (new) + import-date-cell-iso-real-day + import-dryrun-parity + import-integration: 4 files / 119 tests passed. `pnpm --filter @objectstack/core typecheck` and `pnpm --filter @objectstack/rest typecheck` passed, test layers included (check:test-typecheck OK for both). All runs through the lock, VERDICT command-exit 0. Ablation, once per proposal, through scripts/ablation-replace.mjs (anchor 1->0, blob 60af90a93041->mutated), then a core rebuild, then ablation-dist-preflight with the marker present in dist/index.js and dist/index.cjs. P1 (readYearFirstDay call removed): core 9 failed / 167 passed; route test 3/3 failed (ledger row ok:0 errors:1). P2 (value pass returns nothing): core 3 failed / 173 passed; route 2 failed / 1 passed. Restore: blob == HEAD 60af90a93041, git diff HEAD empty. Core rebuilt; preflight --absent passed for both markers (14 built files, clean tree).",
"gates": "Local, at final head c630eacca0. dispatch-gates --commands --repo objectstack-ai/objectstack derived 70 commands; all 70 exit 0. `--ran` reconciliation: '✓ dispatch-gates --ran: 70 derived famil(ies) accounted for — 70 run, 0 NOT-MEASURED'. `pnpm lint` through the lock: VERDICT command-exit 0. After merging origin/main: rest closure rebuilt; `pnpm --filter @objectstack/spec check:generated`: '✓ All 14 generated artifacts are up to date'. An earlier pre-merge round at 9c829a236f had check:type-check-debt and check:dual-build-cjs-loads exit 3 (lock queue-timeout / no dist, PREREQUISITE NOT MET). Both were re-run green, and the final-head union above is all 0. CI on PR #25596 at report time: 7 success, 7 skipped, 24 in_progress (Build Core, Check Changeset and Governed Surface Queue Guard among them). NOT MEASURED: the full @objectstack/rest suite locally (only a test file is added there; CI Test Core runs it) and test:repo (no repo-project test is touched).",
"dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
"mcp_calls": "0",
"api_writes": "4 — fleet-write relay: 2 x POST /repos/objectstack-ai/objectstack/dispatches (the pr_create stroke and this comment's stroke). Inside the relay: POST /repos/objectstack-ai/objectstack/pulls (draft #25596), POST /repos/objectstack-ai/objectstack/issues/25596/assignees (os-sales), POST /repos/objectstack-ai/objectstack/issues/25582/comments (this report). That is 3 content writes plus the dispatches. git push (4 pushes of the branch) is not a REST write.",
"line_budget": "S · 4 files · +319/-7 vs merge-base: import-coerce.ts +69/-7, import-coerce.test.ts +105, rest import-year-first-day-option-case.test.ts +130, changeset +15",
"files_changed": [
"packages/core/src/utils/import-coerce.ts",
"packages/core/src/utils/import-coerce.test.ts",
"packages/rest/src/import-year-first-day-option-case.test.ts",
".changeset/25582-import-dotted-compact-day-option-value-case.md"
],
"deviations": [
"Clause-②: the claim said 'no'. Re-read against AGENTS.md Post-Task Checklist step 3: the arms are the closed pair (widening)/(narrowing), and 'no (widening)' is malformed. This diff only widens the accept set (two refused cell shapes now read), so the PR body and the changeset carry 'Clause-②: yes (widening: ...)' with 'Metadata-protocol: none' and a @objectstack/core patch. No ADR-0087 marker: the changeset is not breaking, and check-adr-0087-registration reports '1 non-breaking changeset(s) seen'.",
"datetime: the two new forms are read for datetime as well as date. A datetime gets midnight UTC, the reading a bare year-first 2026/7/15 already gets there. They are not read for time: a time cell holds no day, and its branch keeps the existing year-first-only fallback.",
"The compact YYYYMMDD form is read only from a text cell. A JSON number 20090615 stays refused, keeping the reader's existing rule that a number is never a temporal cell. An xlsx numeric cell reaches the reader as text through xlsxCellToString, so it is read. That path is read from the code, not measured.",
"The dotted form requires two-digit month and day, per the ruling's 不放宽其它形式. 2011.7.1 stays refused and is pinned refused.",
"Ambiguous option value (two declared values differing only by case, a cell matching both by case alone): no match, the pre-change answer. That is invalid_option, or the raw cell kept under createMissingOptions. Exact matches still win.",
"One throwaway measurement test (rest, registerObject with options cn+CN) was created, run and deleted, never committed.",
"Commit trailers: the model-free pair AGENTS.md prescribes (Claude-Session + Co-authored-by: Claude). The harness reminder's model-named Co-Authored-By line was not used. The branch carries one merge commit of origin/main (taken before opening the PR, per §10)."
],
"open_questions": [],
"out_of_scope_findings": [
"carrier: none · noted, not filed — the xlsx template instructions (TEMPLATE_READER_CLAIMS, packages/rest/src/import-template.ts) do not mention the two new date spellings. Every claim it makes stays true. Recorded in the PR's Acceptance notes."
]
}
merged: true, merge commit 9f390945e, merged 2026-10-24T04:32:23Z through the merge queue
origin/main
9f390945e is an ancestor of origin/main, single parent (squash)
queue branch
no gh-readonly-queue/main/* head for PR 25596 remains
State: card stays open — PR #25596 is Part of #25582 (proposals 1 and 2 of ruling 6377844879 letter A); proposal 4 waits on the ImportRowResultSchema spec member (note 6378282334). pm:dispatched stripped in this act.
Note: this PR entered the merge queue at 2026-10-24T03:57Z, before the release pause reached this seat. Disabling auto-merge and converting it to draft at 2026-10-24T03:58Z did not take it out of the queue, and it merged as a draft. The maintainer has been told.
State:pm:queue, so the next proposal under ruling 6377844879 can be dispatched. The grade is unchanged at p2, domain:server, and the assignee stays with the seat's series.
⛔ The 2026-10-24 merge freeze before the 18.1.0 re-cut applies to its next PR.
Activity
objectstack-fleet commentedon Oct 24, 2026
Claim: PM loop ·
domain:cliseat 1 · p2 · 2026-10-24T01:38ZSession:
session_01CxBKycaiUpA1yGx1XyoGqXAccount:
os-sales(the seat's linked user; always the card's assignee)Branch:
claude/issue-25582-import-cell-shapesWorktree:
objectstack-issue-25582Domain:
domain:server(this seat's side of the server lane under the coordinator's split: former cli and engine paths)Seat:
domain:cli#1(seat post #6024; 由Sales账户的项目负责)File surface (read on
origin/maince18e73483; stop on a breach):packages/core/src/utils/import-coerce.ts(parseDateCell, the select / multiselect option-value match), its tests, a route-level pin underpackages/rest(test only), one changeset. This slice is ruling 6377844879 letter A proposals 1 (YYYY.MM.DDandYYYYMMDDdates) and 2 (option values matched case-insensitively). PR carriesPart of #25582.Not in this slice: proposal 4 (every cell error of a row). The row report is typed and parsed by
ImportRowResultSchema(packages/spec/src/api/export.zod.ts), az.objectthat strips an undeclared key, as its own docblock records forwarnings. So anerrorsarray needs a spec member first. That is thedomain:specseat's; a note for it follows on this card. The runner half (import-runner.ts~:1132, which keepserrors[0]) is this seat's once the member exists.Container & model:
S,mode:subagent,model: default tierClause-②: no
Basis for the Clause-② line: two cell shapes that were refused (
invalid_date,invalid_option) are now accepted; nothing that was accepted is refused. The arm is re-read against AGENTS.md on the measured diff (a widening arm if AGENTS.md names one).Responsibility: n/a — ruled by the maintainer (6377844879, letter A); re-filed from #25493.
Thread-read: 6377844879
Serial constraints cleared: none of the 12 open PRs touches
packages/core/src/utils/import-coerce.ts(file lists read in this act; PR #25540 touchespackages/rest/src/import-prepare.tsonly).Generated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
For the
domain:specseat · fromdomain:cliseat 1 (seat post #6024; lane read asdomain:server) · sessionsession_01CxBKycaiUpA1yGx1XyoGqX· 2026-10-24T01:39Z · ⛔ Not a claim.Ruling 6377844879, letter A proposal 4: report every cell error of a row, not only the first. That needs one spec member before the server half can ship.
ImportRowResultSchema(packages/spec/src/api/export.zod.ts:361) is az.object, so it strips an undeclared key, as its own docblock records forwarnings.What the server half needs is an optional
errorsarray on the row: one element per failed cell,{ field, code, message }. The existing first-error keysfield/code/errorstay, and keep naming the first element. The core coercion already builds this list:coerceRowreturns every error, andimport-runner.ts~:1132 keepserrors[0].When the member lands, this seat carries the runner half (copy the list onto the failed row, on the dry run and the commit, for the sync route and the async job) on this card. Proposals 1 and 2 ship now from this seat as
Part of #25582(claim above).Generated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
os-dev-report
{ "issue": 25582, "status": "done", "branch": "claude/issue-25582-import-cell-shapes", "pr": "https://github.com/objectstack-ai/objectstack/pull/25596", "session": "session_01CxBKycaiUpA1yGx1XyoGqX", "premise_still_valid": true, "summary": "Ruling 6377844879 letter A, proposals 1 and 2, in packages/core/src/utils/import-coerce.ts. (1) parseDateCell reads YYYY.MM.DD and YYYYMMDD (four-digit year, two-digit month and day, a real calendar day) for date and datetime cells. (2) matchOption matches option values case-insensitively in a third pass after exact value and label, storing the declared value. A cell naming two case-only-distinct declared values is not resolved. The spec refuses such a pair (FieldSchema: options.1.value must be lowercase); a programmatic registry.registerObject keeps it. Draft PR #25596 says Part of #25582: proposal 4 still waits on a spec member on ImportRowResultSchema, and import-runner.ts and packages/spec are untouched. Newest Claim 6378276682 names this branch. Card assignee and labels are untouched; the PR is assigned to os-sales at pr_create.", "tests": "Head c630eacca0. `pnpm --filter @objectstack/core test`: 118 files / 2757 tests passed. Rest vitest over import-year-first-day-option-case (new) + import-date-cell-iso-real-day + import-dryrun-parity + import-integration: 4 files / 119 tests passed. `pnpm --filter @objectstack/core typecheck` and `pnpm --filter @objectstack/rest typecheck` passed, test layers included (check:test-typecheck OK for both). All runs through the lock, VERDICT command-exit 0. Ablation, once per proposal, through scripts/ablation-replace.mjs (anchor 1->0, blob 60af90a93041->mutated), then a core rebuild, then ablation-dist-preflight with the marker present in dist/index.js and dist/index.cjs. P1 (readYearFirstDay call removed): core 9 failed / 167 passed; route test 3/3 failed (ledger row ok:0 errors:1). P2 (value pass returns nothing): core 3 failed / 173 passed; route 2 failed / 1 passed. Restore: blob == HEAD 60af90a93041, git diff HEAD empty. Core rebuilt; preflight --absent passed for both markers (14 built files, clean tree).", "gates": "Local, at final head c630eacca0. dispatch-gates --commands --repo objectstack-ai/objectstack derived 70 commands; all 70 exit 0. `--ran` reconciliation: '✓ dispatch-gates --ran: 70 derived famil(ies) accounted for — 70 run, 0 NOT-MEASURED'. `pnpm lint` through the lock: VERDICT command-exit 0. After merging origin/main: rest closure rebuilt; `pnpm --filter @objectstack/spec check:generated`: '✓ All 14 generated artifacts are up to date'. An earlier pre-merge round at 9c829a236f had check:type-check-debt and check:dual-build-cjs-loads exit 3 (lock queue-timeout / no dist, PREREQUISITE NOT MET). Both were re-run green, and the final-head union above is all 0. CI on PR #25596 at report time: 7 success, 7 skipped, 24 in_progress (Build Core, Check Changeset and Governed Surface Queue Guard among them). NOT MEASURED: the full @objectstack/rest suite locally (only a test file is added there; CI Test Core runs it) and test:repo (no repo-project test is touched).", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "0", "api_writes": "4 — fleet-write relay: 2 x POST /repos/objectstack-ai/objectstack/dispatches (the pr_create stroke and this comment's stroke). Inside the relay: POST /repos/objectstack-ai/objectstack/pulls (draft #25596), POST /repos/objectstack-ai/objectstack/issues/25596/assignees (os-sales), POST /repos/objectstack-ai/objectstack/issues/25582/comments (this report). That is 3 content writes plus the dispatches. git push (4 pushes of the branch) is not a REST write.", "line_budget": "S · 4 files · +319/-7 vs merge-base: import-coerce.ts +69/-7, import-coerce.test.ts +105, rest import-year-first-day-option-case.test.ts +130, changeset +15", "files_changed": [ "packages/core/src/utils/import-coerce.ts", "packages/core/src/utils/import-coerce.test.ts", "packages/rest/src/import-year-first-day-option-case.test.ts", ".changeset/25582-import-dotted-compact-day-option-value-case.md" ], "deviations": [ "Clause-②: the claim said 'no'. Re-read against AGENTS.md Post-Task Checklist step 3: the arms are the closed pair (widening)/(narrowing), and 'no (widening)' is malformed. This diff only widens the accept set (two refused cell shapes now read), so the PR body and the changeset carry 'Clause-②: yes (widening: ...)' with 'Metadata-protocol: none' and a @objectstack/core patch. No ADR-0087 marker: the changeset is not breaking, and check-adr-0087-registration reports '1 non-breaking changeset(s) seen'.", "datetime: the two new forms are read for datetime as well as date. A datetime gets midnight UTC, the reading a bare year-first 2026/7/15 already gets there. They are not read for time: a time cell holds no day, and its branch keeps the existing year-first-only fallback.", "The compact YYYYMMDD form is read only from a text cell. A JSON number 20090615 stays refused, keeping the reader's existing rule that a number is never a temporal cell. An xlsx numeric cell reaches the reader as text through xlsxCellToString, so it is read. That path is read from the code, not measured.", "The dotted form requires two-digit month and day, per the ruling's 不放宽其它形式. 2011.7.1 stays refused and is pinned refused.", "Ambiguous option value (two declared values differing only by case, a cell matching both by case alone): no match, the pre-change answer. That is invalid_option, or the raw cell kept under createMissingOptions. Exact matches still win.", "One throwaway measurement test (rest, registerObject with options cn+CN) was created, run and deleted, never committed.", "Commit trailers: the model-free pair AGENTS.md prescribes (Claude-Session + Co-authored-by: Claude). The harness reminder's model-named Co-Authored-By line was not used. The branch carries one merge commit of origin/main (taken before opening the PR, per §10)." ], "open_questions": [], "out_of_scope_findings": [ "carrier: none · noted, not filed — the xlsx template instructions (TEMPLATE_READER_CLAIMS, packages/rest/src/import-template.ts) do not mention the two new date spellings. Every claim it makes stays true. Recorded in the PR's Acceptance notes." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
ACCEPT → PR #25596 comment 6379557624 ·
domain:cliseat 1 · sessionsession_01CxBKycaiUpA1yGx1XyoGqX· 2026-10-24T03:56ZGenerated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
Landed ·
domain:cliseat 1 · sessionsession_01CxBKycaiUpA1yGx1XyoGqX· 2026-10-24T04:33Zmerged: true, merge commit9f390945e, merged 2026-10-24T04:32:23Z through the merge queueorigin/main9f390945eis an ancestor oforigin/main, single parent (squash)gh-readonly-queue/main/*head for PR 25596 remainsState: card stays open — PR #25596 is
Part of #25582(proposals 1 and 2 of ruling 6377844879 letter A); proposal 4 waits on theImportRowResultSchemaspec member (note 6378282334).pm:dispatchedstripped in this act.Note: this PR entered the merge queue at 2026-10-24T03:57Z, before the release pause reached this seat. Disabling auto-merge and converting it to draft at 2026-10-24T03:58Z did not take it out of the queue, and it merged as a draft. The maintainer has been told.
Generated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
Half-state closed · triage seat ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-24T05:54Z9f390945e,Part of).pm:dispatchedcame off, but no state went back on, so the card was invisible to dispatch.pm:queue, so the next proposal under ruling6377844879can be dispatched. The grade is unchanged at p2,domain:server, and the assignee stays with the seat's series.Generated by Claude Code