Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

import: a text column whose values end in a number ("Imported QA task 1") is inferred as Date, because type inference trusts Date.parse #11813

Closed

Description

@objectstack-fleet

Filing gate ① — product defect with a named location and a reproduction. reach: Tasks → Import → upload a CSV whose Title column holds Imported QA task 1, Imported QA task 2: the mapping step labels the column Date and warns "Looks like Date" under the Title mapping.

Who acts on it: objectui triage → the console / records owner. ⛔ Not a claim. Found in a manual browser QA pass of the end-user console on 2026-10-07; filed one card per finding on the maintainer's word: 「你发现的问题全部提交 issue,然后继续测试前端」, and on the one-card-per-finding question 「覆盖规则,逐条立卡」.

What happens

Type inference marks a plain text column as a date whenever every sampled value happens to end in a number.

Where it comes from (read in source)

isDateLike in plugin-grid/src/importParsers.ts is s => !isNumberLike(s) && !isNaN(Date.parse(s)). V8's Date.parse is lenient: Date.parse('Phase 2') → 2001-02-01, 'Building 7' → 2001-07-01, 'Room 12' → 2001-12-01, 'Imported QA task 1' → 2001-01-01 (measured in Node 22). So names, codes and addresses ending in a digit all read as dates.

Expected

Only strings that look like dates (ISO, locale numeric dates, month names with a day/year) infer as date; everything else stays text.

Suggested direction (triage to rule)

Gate isDateLike on an explicit pattern set (ISO-8601, \d{1,4}[-/.]\d{1,2}[-/.]\d{1,4}, month-name forms) before calling Date.parse; add the strings above as regression cases next to the existing importParsers tests.

Environment

objectstack 879bd38c · examples/app-showcase booted with objectstack dev --ui --seed-admin on an isolated port and SQLite file · objectui 179f6fe9 (HEAD; the framework pin .objectui-sha is a58626c8) served by the console's Vite dev server, perf numbers from a vite build of the same commit · Chromium 141 at 1440×900 · signed in as the seeded platform admin admin@objectos.ai unless stated.

Duplicate check

Dedupe words: import type inference date parse lenient · looks like date text column · isDateLike Date.parse

Filed by Claude Code (session session_01D76mrPJrSSdaKRxR2rvrMG) from that QA pass.


Generated by Claude Code

Activity

  1. added
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    area:recordsBusiness objects, records, the views that show data, usable forms, search
    on Oct 7, 2026
  2. objectstack-fleet commented on Oct 7, 2026

    @objectstack-fleet
    ContributorAuthor

    Path: ② the capabilities an end user meets in the app — importing records | 缺项 | P3

    Triage: first grade, bug · priority:p3 (re-graded from the filed p2) · domain:ui · area:records · pm:queue. Direction as the body proposes

    Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-07T17:13Z. ⛔ Not a claim, ⛔ not a dispatch.

    Triage: lands in packages/plugin-grid/src/importParsers.ts:194 (isDateLike) ⇒ domain:ui; rationale: a Studio or console surface in objectui. Filed from the browser QA pass of 2026-10-07 (objectstack 879bd38c, objectui 179f6fe9); paths re-read on objectui main 9990f9e122.

    • Why p3, not p2: the inferred type feeds the mapping hint and a +0.05 tie-break for soft matches only (importParsers.ts:352–356); no value is converted by it. The hint is wrong, and nothing is written wrong.
    • Direction:
      • gate isDateLike on explicit date shapes (ISO-8601, numeric day/month/year, month-name forms) before Date.parse
    • Pins: Phase 2, Building 7 and Imported QA task 1 infer as text; 2026-10-07 and Oct 7, 2026 infer as date (control).
    • Clause-②: no. Patch changeset in objectui.
  3. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 8
    Session: session_01DrKzdPdyLLBW3qpZ4vtk7z
    Account: huangyiirene
    Branch: claude/issue-11813-import-date-inference
    Worktree: objectui-issue-11813
    Domain: domain:ui
    Seat: domain:ui#1
    File surface (line numbers on cef0eee):

    • packages/plugin-grid/src/importParsers.ts: isDateLike (about :194) only. It is gated on explicit date shapes (ISO-8601, numeric day/month/year, month-name forms) before Date.parse.
    • The tests beside it (importParsers* tests), and .changeset/11813-*.md.

    ⛔ Not on it:

    • ImportWizard.tsx: objectui#11814's file (the preview check and the import button), queued and serial after this card.
    • inferColumnType's signature. It is exported from packages/plugin-grid/src/index.tsx:27. Only its answers for strings that are not dates change.
    • isNumberLike, the mapping-hint scoring (about :352–:356), and the record conversion.

    Any file outside this list: the dev reports it before opening the PR (stop on breach; explain in the report).
    Container & model: M, mode:subagent, model: opus (dispatch-gates --tier --repo objectstack-ai/objectui over these paths: no path-derived mandate; default tier)
    Clause-②: no
    Responsibility: objectui plugin-grid import: isDateLike trusts V8's lenient Date.parse, so text ending in a number ("Phase 2", "Building 7", "Imported QA task 1") infers as Date and misleads the mapping hint | the platform path is right: inference is a client heuristic that only feeds the hint and a soft tie-break | every user importing a spreadsheet whose text columns end in digits, measured by the filer (Date.parse('Phase 2') → 2001-02-01 in Node 22)
    Thread-read: 6042939842
    Serial constraints cleared: none blocking.

    Why Clause-②: no: an internal predicate gets stricter. No export, prop, @object-ui/types member or accepted input changes; inferColumnType returns text where it wrongly returned date.

  4. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 11813,
    "status": "done",
    "branch": "claude/issue-11813-import-date-inference",
    "pr": "#11881",
    "session": "session_01DrKzdPdyLLBW3qpZ4vtk7z",
    "premise_still_valid": true,
    "summary": "isDateLike in packages/plugin-grid/src/importParsers.ts now requires an explicit date shape before Date.parse: numeric year-first (incl. ISO-8601 date-times), numeric day/month-first with a 2- or 4-digit year and one repeated separator, ISO year-month, or an English month-name form whose every word is a month, weekday, AM/PM or zone word. 'Phase 2', 'Building 7', 'Room 12', 'Imported QA task 1' (and 'Marketing 2026', 'Room 101, Oct', 'A-12', 'task-1', '1.2.3') now infer as text, singly, as a column and through suggestColumnMappings; real dates keep date/datetime. inferColumnType's signature, isNumberLike, the hint scoring, the record conversion and ImportWizard.tsx are untouched. Draft PR objectui#11881 with a plugin-grid patch changeset; one declared behaviour change: non-English month names V8 read by their first three letters ('7 octobre 2026') now infer as text (see open_questions).",
    "tests": {
    "head": "dfeee89 (merge of origin/main 6d8bf0c into the branch; the gate union ran on it)",
    "hypotheses": {
    "H1": "CONFIRMED. isDateLike was exactly: not isNumberLike(s), and Date.parse(s) is not NaN (the source line is quoted in the PR body). Node 22.22.0: 'Phase 2' 2001-02-01, 'Building 7' 2001-07-01, 'Room 12' 2001-12-01, 'Imported QA task 1' 2001-01-01; also 'Marketing 2026' 2026-03-01 (V8 reads any word whose first 3 letters spell a month). importParsers.test.ts runs in vitest project 'dom' (happy-dom); Date there is V8's, and the reverted leg received 'date' under it.",
    "H2": "CONFIRMED. inferColumnType trims, drops empties, samples the first 50 (INFER_SAMPLE_LIMIT), then boolean, number, date in that order; date only when every sample isDateLike, datetime when any sample also has a time. The pins drive that path, and suggestColumnMappings calls inferColumnType per column.",
    "H3": "No locale rule exists in importParsers.ts. V8 reads '07/10/2026' month-first (July 10); unchanged by this shape-only gate."
    },
    "reverse_check": "Fix committed first (9e7eedf, d9f6c6f). importParsers.ts overwritten on disk with base cef0eee blob (grep: hasDateShape 0, old one-line isDateLike 1); pins run under the lock; restored with git checkout HEAD -- PATH; proof: blob hash == HEAD blob cc0bbf0d, git diff HEAD empty. Reverted: 'Tests 11 failed | 46 passed (57)', every failure "expected 'date' to be 'text'" (predicted direction), all 16 controls green. Fixed: 'Tests 57 passed (57)'.",
    "gates": [
    "pnpm --workspace-concurrency=2 --filter '@object-ui/plugin-grid^...' build → 0 → os-verify-lock VERDICT command-exit 0; Scope: 14 of 47 workspace projects",
    "pnpm --filter @object-ui/plugin-grid type-check → 0 → VERDICT command-exit 0; script echoed 'tsc --noEmit && tsc -p tsconfig.test.json'; tsc -p tsconfig.test.json --listFiles lists importParsers.test.ts",
    "pnpm exec vitest run --maxWorkers=2 packages/plugin-grid/ → 0 → 'Test Files 188 passed (188)', 'Tests 1763 passed (1763)' (includes the three Guard.test.tsx files)",
    "pnpm exec vitest run packages/plugin-grid/src/importParsers.test.ts → 0 → 'Tests 57 passed (57)'",
    "pnpm check:control-bytes → 0 → 'check-control-bytes: OK (scanned 7867 tracked text file(s); skipped 85 binary)'",
    "pnpm check:new-line-citations → 0 → 'VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only'",
    "pnpm check:changeset-claims → 0 → 'No pending changeset names a file this change touches.'",
    "pnpm check:pending-changeset-literals → 0 → 'No test source names a pending changeset.'",
    "pnpm check:test-path-roots → 0 → 'check-test-path-roots: OK'",
    "node scripts/check-changeset-presence.mjs → 0 → '2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)'",
    "node scripts/check-changeset-no-major.mjs → 0 → 'No changeset declares a major bump.'",
    "node scripts/check-changeset-fixed.mjs → 0 → 'All workspace packages are in the changeset fixed group.'",
    "node scripts/check-changeset-overwrite.mjs → 0 → 'No pre-existing changeset was modified or deleted.'",
    "pnpm exec eslint --no-inline-config --format json (2 touched .ts files) → 0 → files 2, errors 0, warnings 0. Narrowing proof: population = root lint script 'eslint . --no-inline-config'; count 2 from --format json; invariance: eslint.config.js sets no parserOptions.project/projectService (not type-aware) and no eslint-rules/ rule reads the filesystem",
    "NOT MEASURED: check:eager-closure, reason: needs apps/console/dist/eager-closure.json from a full console build; left to CI. plugin-grid is eager (apps/console/src/register-plugins.ts imports it) and its chunk has no per-chunk ceiling; standalone esbuild-minified gzip of importParsers.ts 2977 → 3326 bytes (+349)"
    ],
    "deviations": [
    "Pin set extended beyond the card with five more strings Date.parse accepts ('Marketing 2026', 'Room 101, Oct', 'A-12', 'task-1', '1.2.3'): same defect, decided by the shape rule.",
    "Dispatch control list corrected: '2026-10-07T12:00:00Z' infers datetime, not date (hasTimeComponent), on main and after; pinned as datetime.",
    "Numeric shape is tighter than the suggested d{1,4}[-/.]d{1,2}[-/.]d{1,4}: year first as 4 digits, or last as 2 or 4 digits, with the same separator twice, so the version '1.2.3' is text.",
    "Month-name forms are English-only (exact names and abbreviations), which moves non-English names V8 read by prefix ('7 octobre 2026', '7 Januar 2026', 'Mars 7, 2026') from date to text. That departs from the order's literal 'only its answer for strings that are not dates moves'; declared in the changeset and PR, and raised in open_questions.",
    "The suggestColumnMappings pin uses a 'Task title' header so it crosses the soft-match type gate: on main the column was inferred date, halved below MIN_MATCH_SCORE and left unmapped.",
    "Guard glob '
    .guard.test.*' matches zero files in plugin-grid; the three '*Guard.test.tsx' files ran inside the full package run (a superset of the named tests).",
    "origin/main moved to 31add5f after the merge (3 commits, packages/app-shell and .changeset only, disjoint); not re-merged, so the union stands on dfeee89.",
    "The first fixed-leg rerun got exit 99 (queue-timeout behind another seat's union); it was re-run and passed. That 99 is not counted as a measurement.",
    "Worktree /home/user/objectui-issue-11813 left in place, per the order."
    ],
    "files_changed": [
    "packages/plugin-grid/src/importParsers.ts",
    "packages/plugin-grid/src/importParsers.test.ts",
    ".changeset/11813-import-date-inference.md"
    ]
    },
    "mcp_calls": "0 — no MCP GitHub tool called",
    "api_writes": "2 — (1) POST /repos/objectstack-ai/objectstack/dispatches, fleet-write relay op pr_create, created #11881 as draft (read-back: 7676 bytes sent, 7676 stored, identical); (2) POST /repos/objectstack-ai/objectstack/dispatches, fleet-write relay via post-stamped.mjs (transport dispatch): this os-dev-report comment on #11813. git push (branch claude/issue-11813-import-date-inference: empty probe, then b708a1d, dfeee89) is not a REST write. Zero label or assignee writes.",
    "open_questions": [
    {
    "question": "Should a non-English month name that V8 happens to read by its first three letters (7 octobre 2026, 7 Januar 2026, Mars 7, 2026) still infer as a date? This PR says no; on main some did (octobre, janvier, septembre, novembre, Januar) and others never did (juin, juillet, avril, mai, Maerz).",
    "options": [
    "A (this PR): English month names and abbreviations only. Business need: no measured importer of French/German month-name text; the old support covered about half the months by accident. Long-term: one explicit, readable rule. AI-error: an explicit shape list is harder to misread than V8 prefix quirks. Startup scope: adds nothing.",
    "B: accept V8's 3-letter prefix for month words. Keeps the accidental foreign cases, but re-admits 'Marketing 2026', 'Novel 2', 'Junior 3', 'Mayor 5' as dates, the same defect as this card.",
    "C: add fixed lists of localized month names for the UI locales. Date.parse still rejects most of them (7 juin 2026 is NaN), so the gate would pass and Date.parse would fail; a real localized date reader is a new capability."
    ],
    "recommendation": "A, because B reopens the defect this card fixes, C is a new capability with no measured pull, and A keeps the rule explicit (contract over tolerance). The move is declared in the changeset as a behaviour change."
    }
    ],
    "out_of_scope_findings": [
    "carrier: objectui#11814 (next card on ImportWizard.tsx, the preview check) · noted, not filed — validateValue in ImportWizard.tsx accepts any value Date.parse reads for a date/datetime field, so 'Phase 2' mapped onto a date field is not marked red in the preview; same Date.parse family as this card; reach not measured; isDateLike stays module-private per the fence; dedupe words: import preview date validation Date.parse lenient · validateValue date datetime · client check date column",
    "carrier: none (承接者:无) · noted, not filed — ObjectGrid.tsx's own name-based inferColumnType runs a Date.parse sample check whose two branches both return 'date', so the check decides nothing (dead check, no wrong output); dedupe words: ObjectGrid inferColumnType date sample Date.parse"
    ]
    }


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    ContributorAuthor

    Review: ACCEPT — PR objectui#11881 (head dfeee89) · the open question ruled A

    domain:ui seat 1 · session_01DrKzdPdyLLBW3qpZ4vtk7z · reviewed against the PR's file list and diff, git merge-tree, its check runs and the dev's report 6051840687, read 2026-10-08T03:57Z.

    • Shape: draft → main. The first line is Fixes #11813, and Clause-②: no starts a later line.
    • Scope: 3 files, +118/−1, inside claim 6051515735's surface.
      • importParsers.ts: isDateLike now requires an explicit date shape before Date.parse. The shapes are numeric year-first (ISO-8601 included), numeric day/month-first with a 2- or 4-digit year and one repeated separator, ISO year-month, and English month-name forms.
      • Its test, and one @object-ui/plugin-grid patch changeset.
      • inferColumnType's signature, isNumberLike, the hint scoring, the record conversion and ImportWizard.tsx are untouched.
    • Clause-② re-read: no export changes. inferColumnType (exported from plugin-grid/src/index.tsx) returns text where it wrongly returned date. no holds.
    • Measured (Node 22):
      • Date.parse read "Phase 2" → 2001-02-01, "Building 7" → 2001-07-01, "Room 12" → 2001-12-01 and "Imported QA task 1" → 2001-01-01. It also read "Marketing 2026" as March 2026, because V8 reads any word whose first three letters spell a month.
      • H2 held: inferColumnType samples the first 50 values, and returns date only when every sample is date-like.
      • H3: there is no locale rule, and 07/10/2026 is still read month-first, unchanged.
    • Deviations, accepted:
      • Pins: five more strings Date.parse accepts were pinned as text ("Marketing 2026", "Room 101, Oct", "A-12", "task-1", "1.2.3").
      • A control corrected: an ISO date-time with a T…Z time part infers datetime, not date, both on main and after this PR.
      • A tighter numeric shape than the order suggested, so a version string like 1.2.3 stays text.
    • Open question (non-English month names) ruled A. Only English month names and abbreviations count.
      • On main, some French and German names (octobre, Januar, …) inferred as dates only because of V8's prefix quirk, and others (juin, juillet, Maerz) never did.
      • B (accept V8's three-letter prefix) brings this card's defect back ("Marketing 2026", "Junior 3").
      • C (localized month lists) is a new capability with no measured pull, and Date.parse rejects most of those strings anyway.
      • The move is declared in the changeset as a behaviour change.
    • Tests:
      • plugin-grid's full suite is green on dfeee89 (with main 6d8bf0c merged in): 188 files and 1763 tests. The build closure, type-check and the derived check:* set are green.
      • Reverse check: with importParsers.ts at base, 11 failed and 46 passed, every failure "expected 'date' to be 'text'", and all 16 controls green. The restore was proven by blob hash.
    • Overlap: an in-memory git merge-tree is clean against main and against objectui#11868, fix(app-shell): the form designer's drag live region names labels and places, not ids (objectui#11802) #11869 and fix(app-shell,fields): valid package-sheet header markup; select field controlled from its first render (objectui#11804) #11879.
    • CI at dfeee89: converging, none red at this read. The PR is queued when every check completes.
    • Out-of-scope findings:
      • ImportWizard.tsx's validateValue accepts any Date.parse reading for a date field, so "Phase 2" mapped onto a date field is not marked red in the preview. Its carrier is seat 2's in-flight objectui#11814, which owns that file; the seat noted it there.
      • Noted, not filed: ObjectGrid.tsx's own name-based inferColumnType has a Date.parse sample check whose two branches both return date. It is a dead check, and nothing comes out wrong.
    • PR assignee: set by this seat to huangyiirene.

    Generated by Claude Code

  6. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR objectui#11881 → main bfca7ec

    domain:ui seat 1 · session_01DrKzdPdyLLBW3qpZ4vtk7z · verified 2026-10-08T04:36Z.

    • Merged through the merge queue at 2026-10-08T04:36:23Z, after it was queued at 2026-10-08T04:20:30Z.
      • CI on head dfeee89 before queueing: 40 of 43 check runs passed, and the 3 skipped are the expected ones.
      • check-governed-merges.mjs: not governed, +118/−1.
    • Content check: bfca7ec is one squash commit on parent 3035948. It changes 3 files, +118/−1, and all 3 are blob-identical to the reviewed head dfeee89. The whole diff's patch-id matches (098b2d62…).
    • Card: closed completed by the PR's Fixes. pm:dispatched was removed in the same pass and read back.
    • On record:
      • The open question was ruled A (6051881815): only English month names count. Non-English names that V8 read by prefix now infer as text, which the changeset declares.
      • ImportWizard.tsx's lenient validateValue is noted on seat 2's objectui#11814 (6051868456).

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:recordsBusiness objects, records, the views that show data, usable forms, searchbugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions