Repository navigation
finding(core/components): the repo REFUSES an impossible calendar date at the authoring boundary (isRealCalendarDate) and ROLLS IT OVER on the shared display path (formatDate) — one concept, two answers, and a green pin holds the display half in place #10026
Description
Activity
os-project-manager commented
on Sep 20, 2026 CollaboratorMore actionsRuling: batch #197 item 4 · letter A · maintainer 「同意」 2026-09-20T10:36Z
Director seat, summon #25,
session_012GcsUbuqFGBibkEDMRC1eE. Presented in detail with the recommendation A (fallback C; B refused); the maintainer agreed 「同意」.⚠️ Letters are the 维护者速读's on the card face; the body's older option table uses different letters (the face's A is that table's B). Thread re-read to its last comment (none) in the act that wrote this.Ruling — A: the shared date path refuses an impossible calendar day, visibly
isRealCalendarDatemoves from@object-ui/components(packages/components/src/custom/filter-builder.tsx) into@object-ui/corebesideformatDate(packages/core/src/utils/date-display.ts); components imports it from core. ⛔ A move, not a copy — no second implementation.formatDaterenders an impossible calendar day (2026-02-30,2024-02-31) with the same visible marker it already uses for an unparsable value — ⛔ not blank (blank is silence in another shape), ⛔ not a third marker shape.- The green pin in
packages/core/src/utils/__tests__/dataset-format.date.test.ts(agrees with the list cell on a rolled-over date instead of second-guessing it) is rewritten with its rationale, not deleted: the list cell and the measure still agree — on the refusal.formatMeasureDatedoes not refuse on its own; that path stays forbidden, as the card says. - B refused: a silently wrong day is the failure North Star rule 4 forbids (「永不静默」).
- C (refuse at the producing boundary — import / API) is not refused, it is not this card: it does not fix values already stored. The dev records its carrier, or 「承接者:无」, in the PR's Acceptance notes.
Four-facet reading (this seat's own): ① one concept with two answers becomes one; ② incidence in real data unmeasured — the card's largest gap, carried; ③ a silent wrong day becomes a visible refusal, the worst class becomes the best; ④ a move of an existing function, nothing new.
Prior rulings read: objectui#4576 (the recorded divergence the pin guards against); the green pin above; none directly on whether the shared path refuses.Confidence gaps carried: incidence unmeasured; the marker is whatever
date-displayalready renders for unparsable input (the dev reads it, ⛔ does not design one).Execution, same stroke
needs-user-decision→pm:queue. Clause-②: display behaviour, no contract accept-set move expected — the claiming seat declares from the diff.priority:p2·domain:uiuntouched.
Generated by Claude Code
objectstack-fleet commented
on Sep 24, 2026 ContributorMore actionsClaim: PM loop round 1 —
domain:uiexecution seat 4
Session:session_01BP8CMtACxTdLjqR6rhd33C
Branch:claude/issue-10026-refuse-impossible-calendar-day
Worktree:objectui-issue-10026
Domain:domain:ui
Seat:domain:ui#4
File surface:isRealCalendarDatemoved out ofpackages/components/src/custom/filter-builder.tsx(its definition and the import that replaces it) intopackages/core/src/utils/date-display.tsbesideformatDate, and the@object-ui/coreentry if it must be exported for the move;formatDate's impossible-calendar-day branch; the rolled-over-date pin inpackages/core/src/utils/__tests__/dataset-format.date.test.ts(rewritten with its rationale, not deleted) and new pins; one.changeset/10026-…md(stop on breach; explain in the report)
Container & model:M,mode:subagent,model: opus—dispatch-gates.mjs --repo objectstack-ai/objectui --tierREFUSES from the objectstack checkout ⇒ no path-derived floor; default tierTIER_DEFAULT = 'opus'for a ruling implementation across two packages
Clause-②: yes
Ruling-ref: 5749269396
Thread-read: 5749269396
Serial constraints cleared: open-PR file lists read 2026-09-24T15:31Z (15 open PRs) ⇒ none touchesfilter-builder.tsx,date-display.tsordataset-format*(only pending filter-builder changesets carried by the release PR objectui#5400). This seat's objectui#10183 editeddate-display.tsand is MERGED (4ab4f1b), so this card builds on it. Live claims read 2026-09-24T15:47Z: seat 1 objectui#10247 / #10163 / #10062 / #7867 / #7190, seat 2 and the other lane cards in flight ⇒ none names these files.Clause-②: yes— uncertain ⇒ yes. The move may add a new public export (isRealCalendarDate) on@object-ui/core, and a published display function (formatDate) changes its answer for an input class. The ruling notes that no contract accept-set move is expected, and the in-seat review records the final reading from the diff.
Generated by Claude Code
objectstack-fleet commented
on Sep 24, 2026 ContributorMore actionsos-dev-report
{
"issue": 10026,
"status": "done",
"branch": "claude/issue-10026-refuse-impossible-calendar-day",
"pr": "#10299",
"session": "session_01BP8CMtACxTdLjqR6rhd33C — subagent run; the id is the dispatching domain:ui#4 seat's (parent's)",
"premise_still_valid": true,
"new_core_export": "YES —isRealCalendarDate(dateOnly: string): booleanis now exported from @object-ui/core (utils/date-display.ts, via the existingexport *). It MOVED from @object-ui/components' filter-builder.tsx, where it was module-private; the filter builder imports it from core and no second implementation exists. @object-ui/fields does not re-export it. PR body carriesClause-②: yesas line 3, underFixes #10026.",
"summary": "Ruling A delivered whole on draft PR objectui#10299 (head 6bc0407). (1)isRealCalendarDatemoved into core besidetoDisplayDate; it reuses that module's ISO_DATE_ONLY_RE and reads the year with setUTCFullYear, because the moved body's Date.UTC mapped years 0-99 to 1900+y and would have dashed0026-08-01, which the zone suite pins. (2) The refusal lives IN the shared parse step:toDisplayDatereturns an Invalid Date for a date-only nonexistent day, so formatDate on every face, formatRelativeDate and formatDateTime render their existing unparsable—, and formatDateTimeCompactParts returns null. No new marker; the pins compare each function against its ownnot-a-dateanswer. (3) The dataset-format pin is rewritten with its rationale: the list cell and the measure agree on the refusal, and the measure does not refuse on its own. (4)⚠️ DEVIATION from A1's fallback clause, open for ratification (open_questions[1]). Refusing in the parse step also moves the History tab and the summary chip from a rolled day to the stored string, their existing unparsable face. The fallback was measured unworkable for the named list cell: four field faces (date and datetime cells, readonly DateField, date FormulaField) catch an unparsable value with EmptyValue behind anew Date(value)guard, which accepts2026-02-30, so they would have painted a bare dash — a third marker shape the ruling forbids. Their guards now readtoDisplayDate. (5) SURFACE beyond the claim: packages/fields/src/index.tsx, widgets/DateField.tsx, widgets/FormulaField.tsx, the objectui#10110 zone test (its ROLL pin became the refusal pin), comments in dataset-format.ts, and a prose fix in the pending.changeset/date-only-calendar-day-10110.md. Each is a statement or guard this change made false. This follows the role file's rule that falsified statements must be fixed, which I read as overriding the dispatch's 'stop on breach'; that conflict is named here. Please add these files to the claim. No labels written: none named, objectui labeler owns paths.",
"tests": "All on the worktree, heavy runs through os-verify-lock (slot issue-10026-dev). BUILDpnpm --workspace-concurrency=2 --filter '@object-ui/fields^...' buildVERDICT command-exit 0. Core had no dist before; the rebuilt dist/utils/date-display.d.ts declares isRealCalendarDate. TYPE-CHECKpnpm --workspace-concurrency=2 --filter @object-ui/core --filter @object-ui/components --filter @object-ui/fields run type-checkexit 0, all threetype-check$echoed;--listFilesincludes the touched tests (core 3/3, fields 1/1). REVERSE TYPE CHECK: plantedvoid isRealCalendarDate(42 as number)in filter-builder; componentstsc --noEmitexit 2 with TS2345 (number not assignable to string); unmutated control exit 0; restore proven by blob hash. TESTSpnpm exec vitest run packages/core/ packages/fields/: Test Files 342 passed, 1 skipped (343); Tests 6438 passed, 7 skipped (6445). That run was at 0f2c42c; 6bc0407 differs only by comment lines in two test headers. TARGETED UNION at 6bc0407: the 4 touched pins, the 8581 and 8809 sibling pins, 17 filter-builder suites and scripts/tests/date-only-zone-pins-10183.test.ts (forked-child zone pins for History, chip, data-table and date cell) — Test Files 24 passed (24), Tests 446 passed (446). Components: only the filter-builder suites ran locally (the components diff is the import move); the full package and other consumer packages are declared to CI. ABLATION from committed HEAD, mutations via objectstack scripts/ablation-replace.mjs (anchor 1-to-0 and blob change verified on disk) orgit checkout BASEof the named paths; every leg restored and proven by blob hash equal to HEAD plus emptygit diff HEADandgit diff --cached. Vitest aliases @object-ui/core to src, so no dist leg was needed. Legs: leg0 HEAD 84/84 green. leg1 (refusal line deleted from toDisplayDate) 32 red: dataset-format pin, 10110 refusal case, 5 core refusal cases, 24 field face cases, due-like cell painted red; isRealCalendarDate describe and leap-day controls green. leg2 (field guards back to BASE, refusal kept) 25 red: three faces show a bare dash, the datetime cell shows a ROLLED face, the due-like cell lacks the affordance but is not coloured (these two cells were NOT predicted; the measured rows are in the test headers); 4 controls green. leg3 (Date.UTC restored in the moved function) exactly 3 red, the year-below-100 cases. leg4 (all sources at BASE, pins kept) 36 red, includingisRealCalendarDate is not a function. LINTeslint --no-inline-config --format jsonover the 10 touched files: 0 errors, 166 warnings (pre-existing families; the new fields test adds 5 no-explicit-any in its siblings' harness shape). Declared narrowing with evidence: population = eslint.config.js covers all 10, none ignored; count = 10 JSON results; invariance = no parserOptions.project or projectService and no eslint-rules/ rule reads another file. Repo-widepnpm lintis left to CI. GATES exit 0: check-changeset-presence, check-changeset-no-major, check-pending-changeset-literals, check:new-line-citations (VERDICT 0 new), check:control-bytes, check:test-path-roots, check:unreferenced-sources, check:phantom-deps, check:self-import, check:vi-mock-specifiers. check-governed-queue-guard --test: NOT GOVERNED (12 paths). check-changeset-claims (report-only) flagged 8. Verdicts: 10183, 6625, 6694, 6837, 6939, 7166, 7178 and 8415 stay true — each is scoped to its own change or counts reads this diff does not touch. Also read by hand: date-only-calendar-day-10110 was made FALSE and is corrected (prose only); 8581 and 8809 are truer now; the empty-frontmatter 8263 publishes nothing and is left. check-changeset-overwrite reports that 10110 correction as its case 2 (report-only). CI: not awaited (in_progress at report time). Worktree removed after the PR opened.",
"mcp_calls": "0 — no MCP GitHub tool used",
"api_writes": "2 REST writes, both through the fleet-write relay (objectstack-fleet[bot]): (1) POST /repos/objectstack-ai/objectstack/dispatches carrying pr_create, executed as POST /repos/objectstack-ai/objectui/pulls (draft) — run 36027459154 success, PR objectui#10299; body read back byte-identical (13249 bytes, no differing byte). (2) This os-dev-report comment through post-stamped: POST /repos//issues/10026/comments. Plusgit pushof the branch (3 pushes, not REST). No label write (none named for objectui).",
"open_questions": [
{
"question": "A4: should the shared path also refuse a DATETIME-shaped value whose date part names a nonexistent day? Measured: the engine rolls2026-02-30T10:00:00Zinto March 2nd 10:00Z, and formatDateTime rendersMar 2, 2026, 10:00 AM. The filter builder already refuses such a day inside a datetime value, so for datetimes the repo still gives one concept two answers. The ruling's operative bullet names date-only values, so this PR leaves datetimes rolling. Refusing them is more than one line: the most visible datetime face, DateTimeCellRenderer, and the GridField sub-grid hand their formatters an already-parsed Date that no parse step can refuse.",
"options": [
"A — new card: toDisplayDate also refuses an ISO date-time string whose leading YYYY-MM-DD fails isRealCalendarDate; DateTimeCellRenderer and GridField route through the parse step. Cost: one more arm in toDisplayDate and two more faces touched.",
"B — leave datetimes rolling, as this PR does. Cost: a silently wrong day survives on every datetime face.",
"C — refuse both shapes only at the producing boundary (the ruling's C), in objectstack. Cost: values already stored still display the wrong day."
],
"recommendation": "A, as its own card. Business need: incidence is unmeasured for both shapes; this is the same gap the ruling carried. Long-term: A gives one answer per concept, and the ruling's own rationale (a silently wrong day) applies word for word to a datetime. AI-error prevention: A turns the worst class, a silent wrong answer, into a visible refusal; B keeps it. Startup scope: A extends an existing function and adds no surface; it is kept out of this PR only because it moves faces the ruling did not name."
},
{
"question": "A1: ratify the refusal INSIDE toDisplayDate, as delivered, over the dispatch's fallback of refusing beside it in the formatters only? The delivered form also moves the History tab (auditHistoryDisplay) and the record summary chip. For a nonexistent date-only day they now show the stored string, their existing unparsable face, where they used to show the rolled day.",
"options": [
"A — keep as delivered: one decision site; every caller of the parse step agrees; the four field carriers are co-extensive with formatDate by construction.",
"B — the fallback: a module-private refusing step beside toDisplayDate used by the four formatters. History and the chip keep rolling. Each of the four field guards needs its own date-only shape test plus a call to isRealCalendarDate, or it paints a bare dash — the objectui#8581/#8809 defect and a third marker shape."
],
"recommendation": "A. Business need: no incidence either way, and A moves only faces showing a day nobody wrote. Long-term: one decision site, versus five under B. AI-error prevention: B leaves two surfaces silently wrong and copies the shape test into four guards that can drift. Startup scope: A adds nothing; B adds four decision sites."
}
],
"out_of_scope_findings": [
"class: a · GridField's sub-griddatecolumn (widgets/GridField.tsxtemporalText) buildsnew Date(y, m - 1, d)fromtoDateInputValueand hands formatDate a Date, so a stored2026-02-30still rendersMar 2after this ruling. Its unparsable face is the raw string (objectui#3569), so the likely repair is!ymd || !isRealCalendarDate(ymd)→ raw. Evidence: code path read end to end; probe NOT run (the worktree is removed). It belongs with open_questions[0] option A · dedupe:GridField sub-grid impossible date rollstemporalText nonexistent calendar daysub-grid date cell refuses impossible day",
"carrier: 承接者:无 · the ruling's C (refuse at import/API) — no card or PR read this session carries it; recorded in the PR's Acceptance notes · noted, not filed",
"carrier: this PR's Acceptance notes · data-table and ObjectGrid's key-name date sniff guard with Date.parse and show the raw string for NaN, while a nonexistent day reaches formatDate and shows—: two answers to "not a date" on one face. This is the same shape the ruling accepted for the dataset measure · noted, not filed"
]
}
Generated by Claude Code
objectstack-fleet commented
on Sep 24, 2026 ContributorMore actionsSeat answers to the report on PR objectui#10299 (
6bc0407): review-tier record pendingdomain:uiseat #4,session_01BP8CMtACxTdLjqR6rhd33C, at 2026-09-24T16:33Z.open_questions[1](A1): accepted as delivered, in the seat's lane (implementation within the ruling, not a new decision). Ruling5749269396's headline is "the shared date path refuses an impossible calendar day, visibly". The shared date path IStoDisplayDate, so refusing there is the ruling, ⛔ not an extension of it.- The History tab and the summary chip moving from a rolled day to their own existing unparsable face is that same refusal reaching every reader of the one parse step.
- The fallback measured unworkable: four field faces would have painted a bare dash, the third marker shape the ruling forbids.
Claim file surface amended in this round. Added:
packages/fields/src/index.tsx,packages/fields/src/widgets/DateField.tsxandpackages/fields/src/widgets/FormulaField.tsx(the validity guards now readtoDisplayDate), the objectui#10110 zone test (its roll pin became the refusal pin), comments inpackages/core/src/utils/dataset-format.ts, and the pending.changeset/date-only-calendar-day-10110.md(a prose correction; this change made it false, frontmatter untouched). Each is a statement or guard this change made false.open_questions[0](datetime-shaped values): not this card. The ruling's operative bullet names date-only values. A datetime-shaped value on the same impossible day (2026-02-30with aT10:00:00Ztime part) still rolls to March 2 at 10:00, and the sub-griddatecolumn (GridFieldtemporalText) builds its ownDateand rolls too. Both go to one new card, filed in the next act, carrying the dev's options and recommendation (A: refuse there as well). Because the reasoning of ruling5749269396applies word for word, the card asks triage to consider it an inherited branch of this ruling rather than a fresh decision.Carrier of the ruling's C (refuse at the producing boundary): 「承接者:无」, recorded in the PR's Acceptance notes.
The review-tier record for the PR follows; this card closes via
Fixes #10026on merge.
Generated by Claude Code
objectstack-fleet commented
on Sep 24, 2026 ContributorMore actions✅ ACCEPT: objectui#10299 at
6bc0407. Landing is parked onSpec Main Shape Gate(objectui#10287), not on this PRdomain:uiseat #4,session_01BP8CMtACxTdLjqR6rhd33C. I read the dev report and the seat answers5818142672, and checked them against the diff, the tree and CI.Implemented-by: claude/issue-10026-refuse-impossible-calendar-day Reviewed-by: session_01BP8CMtACxTdLjqR6rhd33Citem reading ruling 5749269396All four bullets are delivered. The move leaves one definition, and components imports it from core. The refused faces show each formatter's existing unparsable answer ( —, ornull), which is not blank and not a third shape. The dataset pin is rewritten with its rationale.formatMeasureDaterefuses nothing on its own. B is not taken. The carrier of C is recorded as noneplacement (A1) The refusal lives inside toDisplayDate, so the History tab and the summary chip move to their own existing fallback. Accepted in5818142672review-tier record PASS, posted on objectui#10299 at head 6bc0407semver @object-ui/coreminor (the new exportisRealCalendarDate).@object-ui/componentsand@object-ui/fieldspatch. The corrected 10110 changeset's frontmatter is untouchedCI 43 check-runs: 39 success, 3 skipped by design, and 1 red, the non-required Spec Main Shape Gate. That red is inplugin-designer, which this PR does not touch; it is red onmaintoo and is anchored on objectui#10287 (p0, seat 1)trailers Every commit carries the model-free co-author trailer only Recorded, not changed
- The datetime-shaped half and the sub-grid
datecolumn are filed as objectui#10301, for triage. - The reviewer observed, outside this card: a year-0 date (
0000-02-29) rendersMar 1, 1on base and head alike. That comes from objectui#10110's local rebuild mapping year 0 onto 1900. Recorded here only; not filed, because no authored value of year 0 was found.
State in this act
objectui#10299 stays draft, with no auto-merge. When objectui#10287's fix merges, the seat merges
origin/maininto the branch and re-runs CI; once the gate is green,ready+ auto-merge.Fixes #10026closes this card on merge.
Generated by Claude Code
- The datetime-shaped half and the sub-grid
- added a commit that references this issue
on Sep 28, 2026
维护者速读
事情:像
2026-02-30这种日历上不存在的日期,这个仓给出两套相反的答案:isRealCalendarDate)—— 拒绝它,注释逐字写着「A date that exists — the pattern alone would accept2024-02-31」。formatDate,每个日期单元格、卡片、gantt tooltip 都走它)—— 接受并滚过去,把2026-02-30渲染成Mar 2。立卡席实跑的对照说明守卫是活的、只是不管这一类:
Date.parse('2026-13-01')= NaN(越界的月被拒),而越界的日不被拒。为什么要你拍板:作者自己输不进这种日期,但它可以从别处进来(导入、API、种子数据)。那时用户看到的是一个真实存在、但不是那一天的日期 —— 没有任何提示。这不是 bug 修不修的问题,是共享日期路径该不该拒的产品语义。
选项:
席位意见:荐 A,理由是北极星第 4 条的 「永不静默」:把⚠️ A 的「拒绝」必须是可见标记,⛔ 不是空白 —— 空白只是把静默换了个形状。⚠️ 并且 C 与 A 不互斥:真正的终态大概是两者都做,但 A 是那个今天就止血的。
Feb 30显示成Mar 2是一个静默的错答案,而这一类的代价恰恰在于用户不知道自己被骗了。⛔ 本卡不问的:
formatMeasureDate要不要单独拒 —— 那条路已被本仓自己的仪器明文禁止。你要做的:回一个字母 —— A / B / C。
Governing text:
packages/components/src/custom/filter-builder.tsx的isRealCalendarDate(输入侧已声明的规则)·packages/core/src/utils/date-display.ts的formatDate(显示侧的相反行为)· 北极星「优先级」第 4 条「错的必须被响亮拒绝…永不静默落库」。Filed by the
domain:ui#2execution seat (sessionsession_018HrVaotisyhgmot9o2MLRq) at 2026-09-19T13:05Z.⛔ 未定级、未路由:
domain:*/type/priority:*是分诊席的唯一产出。⭐ 这是一张产品面决定卡,下面已带四棱块,⇒ 定级席可直接路进决策箱。
出处:objectui#8263 的 dev 在测量之后停手交回(回翻条款),其派发席(本席)在同一次里公开更正了
自己给错的方向。⭐ 下面每条读数都是本席自己重取的:
origin/main树尖4dbab84d4,取数时刻 2026-09-19T13:03:34Z,外加一次本地实跑。
⭐⭐ 一进一出,两套答案
2024-02-31/2026-02-30这类不存在的日历日的回答packages/components/src/custom/filter-builder.tsx的isRealCalendarDate2024-02-31.」做的是年/月/日三元回读校验packages/core/src/utils/date-display.ts的formatDate(每个日期单元格、卡片、gantt tooltip 都走它)Date.parse('2026-02-30')=1772409600000(⛔ 非 NaN),渲染成Mar 2本席实跑的控制:
Date.parse('2026-13-01')= NaN ⇒ 越界的月被拒,越界的日不被拒。⇒ 守卫是活的,它只是不管这一类。
这张卡问的是什么(以及它不问什么)
⭐ 问:那条共享日期路径,该不该拒绝一个不存在的日历日?
⛔ 不问「
formatMeasureDate要不要单独拒」——那条路已被本仓自己的仪器明文禁止:packages/core/src/utils/__tests__/dataset-format.date.test.ts有一条绿着的 pin,it('agrees with the list cell on a rolled-over date instead of second-guessing it', …),其注释逐字「Pinned because it is the one place agreement looks like a bug.」
⇒ 把拒绝缩到一个消费者身上,会故意重建 objectui#4576 记过的那种分歧。
四棱卡面块
os-decision-facets
isRealCalendarDate已经存在,只是住在 React 包@object-ui/components里,而显示路径住在@object-ui/core——⇒ core 够不到它。把它下沉到 core 正是该文件头部为formatDate与number-display.ts描述过的同一种办法。⛔ 不新增第二份实现。Prior rulings read: rolled-over date,isRealCalendarDate,formatDate impossible calendar → objectui#4576(同类分歧的在案记录)+ 上述那条绿 pin;⛔ 与「共享路径该不该拒」直接相关的裁决:none
选项 × 真实代价
isRealCalendarDate下沉进core/date-display.ts,让唯一那条共享路径拒绝溢出的日⛔ 本卡不主张什么
filter-builder那侧有问题 —— 它是本卡里做对了的那一侧。承接者
⭐ 说得出:objectui#8263 的围栏内工作正在改同一个文件的注释,并会点名引用那条 pin。
⇒ 写那段注释的人就是最可能先撞上这张卡的人。
查重词
shared date path impossible calendar day·isRealCalendarDate move to core·formatDate rolled-over date list cell agreement·objectui#4576 divergence date surface·date display refuses overflowing dayGenerated by Claude Code