Repository navigation
feat(core): import reads dotted and compact year-first days and matches option values in any case - #25596
Conversation
…hes option values case-insensitively Part of the ruling's letter A (proposals 1 and 2). Claude-Session: https://claude.ai/code/session_01CxBKycaiUpA1yGx1XyoGqX Co-authored-by: Claude <noreply@anthropic.com>
…alue through the import route Claude-Session: https://claude.ai/code/session_01CxBKycaiUpA1yGx1XyoGqX Co-authored-by: Claude <noreply@anthropic.com>
…value case Claude-Session: https://claude.ai/code/session_01CxBKycaiUpA1yGx1XyoGqX Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CxBKycaiUpA1yGx1XyoGqX Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift Check5 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 — 28 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 88bc2fef2425bc0cc51a46b74aac2ef9af1212e6 && git checkout 88bc2fef2425bc0cc51a46b74aac2ef9af1212e6
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin e073455929a60b67c48da3f91324261e57aaa26b c630eacca0a73660a2d582895d95ffd2321e320d && git checkout -B drift-repro e073455929a60b67c48da3f91324261e57aaa26b && git merge --no-ff c630eacca0a73660a2d582895d95ffd2321e320d
node scripts/docs-audit/affected-docs.mjs --json e073455929a60b67c48da3f91324261e57aaa26b |
席位核对ACCEPT ·
Generated by Claude Code |
This reverts commit 74fa3cb. The 18.1.0 publish was refused at that version commit: its full battery is red (Dogfood Regression Gate, the showcase D3/D4 proof pinning the pre-#25549 refusal sentence), and the release gate reads only runs at or before the version commit, so no re-run on it can go green. Reverting restores the 265 changesets it consumed, including the five that landed after the Version Packages PR was generated (#25591, #25592, #25596 and two patches), so the regenerated Version Packages PR versions 18.1.0 again from a tree whose battery can be read green, with a changelog that names everything it ships. Nothing was published: npm still has 18.0.0 and no 18.1.0 tag exists. Claude-Session: https://claude.ai/code/session_014C7q5Hm34g4EPdirar9SzN Co-authored-by: Claude <noreply@anthropic.com>
Requested by Sales · project thread
Part of #25582
Clause-②: yes (widening: two date-cell spellings and an option value in another case, each refused before with
invalid_date/invalid_option, are now read; nothing read before is refused or stored differently)Metadata-protocol: none
What
Ruling 6377844879 (on #25493, letter A), proposals 1 and 2, in
packages/core/src/utils/import-coerce.ts:parseDateCellreadsYYYY.MM.DDandYYYYMMDD. A four-digit year, a two-digit month and day, nothing else in the cell.2011.07.01is stored as2011-07-01,20090615as2009-06-15. The day must exist (namesRealCalendarDay, the same arithmetic as the other shapes):20090230and2009.02.30stayinvalid_date. No other form is widened:2011.7.1,2011.07.01 9:00,11.07.01,01.07.2011,2011.07-01,200961,2009615,200906150and20090615 9:00stay refused.dateand adatetime(midnight UTC, the reading a bare2026/7/15already gets on adatetime), never for atime: both spellings name a day and no clock. Thetimebranch keeps its year-first-only fallback unchanged.20090615stays refused. The existing reader treats numbers as never temporal (outsideYearsdocblock: an Excel serial, epoch ms). An xlsx cell holding that number reaches the reader as its text (xlsxCellToStringinimport-prepare.ts), so it is read. That path is read from the code, not measured.numberortextcolumn is unaffected (20090615→ the number20090615/ the text'20090615'). This is pinned.matchOptionnow has three passes: exact value, then label case-insensitively (as before), then value case-insensitively. It stores the declared value, never the cell's spelling:CNstorescn. The value pass runs last, so a cell that matched before stores what it stored (pinned: a cell that is one option's label and, by case, another option's value keeps the label match). Single select,multiple: trueselect and multiselect parts all go through this one function.FieldSchema.safeParseon[{value:'cn'},{value:'CN'}]fails atoptions.1.value("System identifier must be lowercase…"). A programmaticengine.registry.registerObjectaccepts and keeps the pair unparsed. Measured with a throwaway test against@objectstack/objectql, then deleted. So the pass refuses to guess. A cell naming two such values by case alone (Cn) gets the no-match answer it got before (invalid_option, or kept undercreateMissingOptions). An exactcn/CNstill matches exactly. The same value declared twice counts as one value.#25582 remains open: proposal 4 (every cell error of a row) is not in this PR. It needs an
errorsmember onImportRowResultSchemafirst (thedomain:specseat's; the note is on the card), andimport-runner.tsstill keepserrors[0]. Proposal 3 (aliases) was not ruled in.Tests
packages/core/src/utils/import-coerce.test.ts: a[#25582]block covering the new forms ondateanddatetime, the invalid-calendar controls, the not-widened controls,timerefusal, number refusal, the 8-digit number/text control, and the row-levelinvalid_date.matchOptiongets value-case match storing the canonical value, label-ahead-of-value, and the ambiguous pair.coerceRowgets select, multiselect andmultiple: trueselect parts stored canonical, withkeptOptionValuesempty and an unmatched part named.packages/rest/src/import-year-first-day-option-case.test.ts(new): through the realPOST /api/v1/data/:object/importoverSqlDriver(better-sqlite3), a CSV row2011.07.01,20090615,CNimports and stores2011-07-01/2009-06-15/cn. The JSON dry run of the same row is clean and persists nothing.20090230andDEare still that row'sinvalid_date/invalid_option.Ablation, once per proposal. Each leg went through
scripts/ablation-replace.mjs(anchor hit 1 → 0, blob moved). Then@objectstack/corewas rebuilt, because the rest test reads core'sdist/, andablation-dist-preflight.mjssaw the marker indist/index.jsanddist/index.cjs.readYearFirstDaycall removed): core 9 failed / 167 passed of 176. Route 3 failed of 3 (ok: 0, errors: 1on the ledger row).60af90a93041),git diff HEADempty. Core rebuilt, and--absentpassed for both markers on all 14 built files with a clean tree.Gates
All readings below were taken at head
c630eacca0(after mergingorigin/main), unless a line names another commit.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 70 commands. All 70 ran and exited 0.--rangives:✓ dispatch-gates --ran: 70 derived famil(ies) accounted for — 70 run, 0 NOT-MEASURED.pnpm lint, through the verify lock:VERDICT command-exit 0.pnpm --filter @objectstack/core test: 118 files / 2757 tests passed.pnpm --filter @objectstack/core typecheckandpnpm --filter @objectstack/rest typecheckpassed (each package's test-layercheck:test-typecheck: OK). Both through the lock,VERDICT command-exit 0.pnpm --filter @objectstack/rest exec vitest runover the new file plusimport-date-cell-iso-real-day,import-dryrun-parityandimport-integration: 4 files / 119 tests passed.@objectstack/restclosure was rebuilt, andpnpm --filter @objectstack/spec check:generatedgives✓ All 14 generated artifacts are up to date.@objectstack/restsuite (only a test file is added there; CITest Coreruns it) andtest:repo(no repo-project test is touched).Acceptance notes
TEMPLATE_READER_CLAIMSinpackages/rest/src/import-template.ts) lists example date spellings and does not mention the two new ones. Every claim it makes is still true (none of its refused examples is now read). Adding them is outside this slice's file surface. carrier: none.2011.7.1(dotted, one-digit month/day) stays refused, per the ruling's 「不放宽其它形式」. Widening it would be a new decision.Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_01CxBKycaiUpA1yGx1XyoGqX