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
finding(core): formatRelativeDate still ignores options.style, and its out-of-window ABSOLUTE fallback is the one place the key is already well-defined #7816
Reported by the #7745 dev seat as its first open question, and filed here by the PM seat so it survives that card's Fixes close. ⛔ Deliberately not done inside #7745: the maintainer ruling there names formatDate only.
formatDate now reads options.style (style ?? options?.style, positional wins). formatRelativeDate reads nothing — and it is not genuinely style-less, because it delegates to formatDate outside its ±7-day window. So today an out-of-window date renders the default absolute face no matter what the shared options bag says.
⚠️ That delegation is currently guarded on purpose: #7745 added a module-local, non-exported absoluteFallbackOptions that strips style before the out-of-window call. That guard is behaviour-preserving, not a feature — it exists because the new read would otherwise have changed formatRelativeDate's output as a side effect of a card that was not about it.
no read at all — keep formatRelativeDate style-less, as today
nothing changes; the shared bag stays half-honoured
B ⬅ recommended
read it only for the out-of-window ABSOLUTE fallback: formatRelativeDate(v, { style: 'short' }) on a two-year-old date renders Jul 4, '24 instead of Jul 4, 2024
well-defined today, zero new vocabulary
C
read it for the relative phrase itself
⛔ requires minting a relative-style vocabulary that does not exist — the key would be accepted with nothing to name
⭐ The measurement is already done — this card starts with its own before/after
#7745's ablation leg C dropped the strip and is therefore exactly the measurement of what flipping this costs. It turned three cases red (one more than predicted, reported as observed):
AssertionError: expected 'Jul 4, '24' to be 'Jul 4, 2024' ← the leak into formatRelativeDate
RangeError: Maximum call stack size exceeded ← { style: 'relative' } recurses
⛔ It is not a one-line change in consequence: { style: 'relative' } handed to formatRelativeDaterecurses to stack exhaustion through the delegation. Any implementation of B must answer that input explicitly. The trap is already measured; do not rediscover it in production.
What makes this a real decision and not a freebie
It makes the ±7-day boundary a visible face boundary: In 2 days inside the window, Jul 4, '24 outside — two different faces from one call, selected by the date rather than by the author.
It changes live grid / gantt output for any caller already threading one shared options bag through both functions.
⇒ Which is precisely why #7745 refused to do it unasked, and why this is a card.
Related: objectui#7745 (the ruling and the formatDate read) · PR objectui#7813 · objectui#4272 (the silent-override incident the module header records)
Claim: PM loop R3 (objectui domain:ui PM seat) — wave top-up, slot 9
Session: session_01YBWFb5YgMU5dw8p2VKj16S
Branch: claude/issue-7816-relative-date-style
Worktree: objectui-issue-7816
Domain: domain:ui · package: core · priority:p3
Tier: opus = TIER_DEFAULT. ⛔ Not sonnet — the change is one line and its consequence is not.
Clause-②: assessed NO.options.style is already declared on DateDisplayOptions and already accepted; what moves is whether formatRelativeDatehonours a key it already takes. Same shape as objectui#7687 (the combobox disabled read), judged a behaviour change and ⛔ not an accept-set change. ⚠️ If your measurement contradicts that, ⛔ stop and report.
Premise re-verified by this seat on origin/main:
packages/core/src/utils/date-display.ts:125 function absoluteFallbackOptions(options?: DateDisplayOptions): DateDisplayOptions | undefined {
packages/core/src/utils/date-display.ts:140 export function formatRelativeDate(value, options?: DateDisplayOptions): string {
packages/core/src/utils/date-display.ts:152 if (diffDays < -7 || diffDays > 7) return formatDate(date, undefined, absoluteFallbackOptions(options));
⇒ the strip and the delegation are both exactly where the card says. There is also already a pin file — packages/core/src/utils/__tests__/date-display.optionsStyle-7745.test.ts — whose docblock (:51) names the very ablation this card is built on. ⭐ Read it before you write anything: it is the previous round's measurement of your change.
Zone 1 — ADJUDICATION (binding)
✅ Route B: read styleonly for the out-of-window ABSOLUTE fallback, i.e. flip the absoluteFallbackOptions strip that objectui#7745 added. formatRelativeDate(v, { style: 'short' }) on a two-year-old date then renders Jul 4, '24 instead of Jul 4, 2024. Well-defined today, zero new vocabulary.
⛔ Route C is ruled out by the card and by measurement — reading style for the relative phrase itself would require minting a relative-style vocabulary that does not exist, i.e. accepting a key with nothing to name. ⛔ Do not take it.
⚠️Route A (change nothing) is a live option, and it is the one that makes this a decision rather than a freebie. B changes live grid / gantt output for any caller already threading one shared options bag through both functions, and it makes the ±7-day boundary a visible face boundary (In 2 days inside, Jul 4, '24 outside — two faces from one call, selected by the date rather than by the author). ⇒ if your measurement leads you to conclude that A-vs-B is a maintainer call rather than a lane call, hang needs-user-decision and STOP. ⛔ Do not decide it silently in either direction. That is a full success, not a failure.
⛔ objectui#7745's ruling names formatDate only. ⛔ Do not re-open or widen it.
Zone 2 — ⭐ THE TRAP IS ALREADY MEASURED. Do not rediscover it in production.
objectui#7745's ablation leg C dropped the strip — which is precisely your change — and turned three cases red (one more than that seat predicted, and it reported the surplus as observed):
AssertionError: expected 'Jul 4, '24' to be 'Jul 4, 2024' ← the leak into formatRelativeDate
RangeError: Maximum call stack size exceeded ← { style: 'relative' } RECURSES
⇒ { style: 'relative' } handed to formatRelativeDate recurses to stack exhaustion through the delegation. ⛔ Any implementation of B must answer that input explicitly — refuse it, coerce it, or short-circuit it, with the choice stated and pinned. A patch that only flips the strip ships a stack overflow reachable from a declared, accepted option value.
Zone 3 — SUGGESTED ROUTE (advisory)
The card's own before/after is leg C's three red cases; make them your red-first ablation rather than inventing one. Pin: the out-of-window absolute face honours style; the in-window relative phrase is unchanged; and { style: 'relative' } terminates with a stated behaviour. ⚠️ A pin that omits the third leg passes while the reachable crash stays.
⚠️ This card carries no triage comment — its labels are the filing PM seat's, not triage's. Treat its A/B/C framing as a well-argued proposal, ⛔ not as a graded ruling, and say so if you disagree with the grading.
Serial constraints cleared: packages/core/** is named by no other slot in this wave. ⚠️formatDate is shared with plugin-grid / plugin-gantt consumers — you change formatRelativeDate's call into it, ⛔ not formatDate itself; if that turns out to be false, stop and report. The queue arbitrates.
{
"issue": 7816,
"status": "needs_decision",
"branch": "claude/issue-7816-relative-date-style",
"pr": null,
"premise_still_valid": true,
"summary": "No code landed — this is the dispatch's authorised STOP. Premise verified verbatim on origin/main@20316bac3 (date-display.ts:125 absoluteFallbackOptions, :140 formatRelativeDate, :152 the stripped delegation) and Clause-2 confirmed (style is already declared and accepted; what moves is whether formatRelativeDate honours it). Two independent triggers fired. (1) The dispatch's own stop-and-report condition: 'you change formatRelativeDate's call into it, not formatDate itself — if that turns out to be false, stop and report'. It is false. A Route B confined to formatRelativeDate (= #7745's ablation leg C) reproduces the measured crash AND a second defect the card does not name: it partially defeats #7745's pinned precedence. formatDate(V, 'relative', {style:'short'}) renders 'Jul 4, 2024' today and 'Jul 4, ''24' under leg C — byte-identical to what options-wins would render, i.e. the spelling #7745 pinned as the loser selects the face for out-of-window dates. formatRelativeDate cannot distinguish a direct call from formatDate's positional-relative delegation; the only place that distinction exists is formatDate:219, outside this card's granted scope. (2) A-vs-B is a maintainer call: B is NOT additive on a published function (formatRelativeDate(V,{style:'short'}) moves 'Jul 4, 2024' -> 'Jul 4, ''24'), the only ruling in play (#7443, cited by #7745) names formatDate ONLY, and B must invert another card's pin assertions (date-display.optionsStyle-7745.test.ts:171-177, whose docblock records the strip as deliberately behaviour-preserving). Both costs the card cites are measured FALSE in-repo: formatRelativeDate has ZERO production call sites (only date-display.ts:219) and no production options bag anywhere carries style (fields:844 {dueLike,locale,t}; grid 4384/4607/4627, gantt 920/927, dataset-format:192 {locale}; dashboard:425 none), so leg C changes in-repo rendered output in exactly zero places; and the plus/minus-7-day face boundary already exists today with no options at all ('In 2 days' in-window vs 'Jul 4, 2024' out-of-window). Because the same census shows in-repo BENEFIT is also zero, the entire effect of B lands on invisible external consumers of the published @object-ui/core@17.6.0 / @object-ui/fields — a product call, not a lane call. needs-user-decision hung on the card (read-back verified, 6 labels, none stripped); write-up posted as comment 5562227618. Also measured a clean alternative the card does not consider, B-prime: move the strip from formatRelativeDate:152 INTO formatDate:219's relative-branch delegation. It reaches B's intent, kills the recursion at its source with no special-casing (formatDate stops handing its own 'relative' marker back, so one bounce is bounded), keeps the precedence intact, and turns exactly ONE pin assertion red — the one that IS the decision. B-prime touches formatDate, which the dispatch and #7745's ruling scope both fence off, so it is offered for authorisation rather than taken.",
"tests": "Baseline: pnpm exec vitest run packages/core/src/utils/__tests__/date-display.optionsStyle-7745.test.ts -> exit 0, 'Test Files 1 passed (1) / Tests 14 passed (14)'. Ablation discipline on both legs: trap '<restore>' EXIT INT TERM with absolute REPO_ROOT paths; mutation proven on disk by grep -c of the injected marker AND of the deleted text (leg C: absoluteFallbackOptions(options) 1 -> 0, ABLATION-LEG-C marker = 1); bytes proven changed by git hash-object vs git rev-parse HEAD:<path> (HEAD 6cbabb21ccb0eb105722aaaf87b5185b3bfebd2c -> leg C 8cf02eb90437c57e76dc8a4859f835f9c3e1b50c, B-prime fa384cecfd14e138b825a7e64ff246dc619889ba); restore via git checkout HEAD -- <path> (never bare), proven by empty git diff HEAD --stat + git status --porcelain + on-disk blob back to 6cbabb21. No build needed: the pin imports '../date-display' relatively, so vitest resolves source, not dist. LEG C (= the card's Route B): 'Tests 3 failed | 12 passed (15)' — AssertionError: expected 'Jul 4, ''24' to be 'Jul 4, 2024' (out-of-window fallback), RangeError: Maximum call stack size exceeded on {style:'relative'} via both entry points, and 'the options spelling IS the positional spelling, for every face' red by the same RangeError. Identical to the three reds #7745 recorded — the trap was reproduced, not rediscovered in production. Probe under leg C also measured the NEW defect: formatDate(V,'relative',{style:'short'}) = 'Jul 4, ''24' (today 'Jul 4, 2024'). LEG B-PRIME: 'Tests 1 failed | 14 passed (15)' — the single red is date-display.optionsStyle-7745.test.ts:171 'ignores it on the out-of-window ABSOLUTE fallback too'; probe: formatRelativeDate(V,{style:'short'}) = 'Jul 4, ''24', formatRelativeDate(V,{style:'relative'}) = 'Jul 4, 2024' (terminates), formatDate(V,'relative',{style:'short'}) = 'Jul 4, 2024' (precedence intact), in-window 'In 2 days' unchanged. Consumer census by git grep across packages/apps/examples plus the objectstack sibling checkout (zero hits there). Worktree left byte-identical to origin/main; scratch probe file deleted; no changeset, no commit, no PR — nothing to gate.",
"mcp_calls": "9 — issue_read get, issue_read get_comments, get_label, issue_read get_labels, issue_write update (labels), issue_read get_labels (read-back), 2x add_issue_comment, issue_read get_comments (comment read-back). Channel note: the repo-scoped REST probe returned 403 ('GitHub access is not enabled for this session') for both the unauthenticated and the GH_TOKEN-authenticated read, and gh is not installed, so the label write and both comments went via MCP — declared channel switch. The additive POST .../issues/<n>/labels endpoint was therefore unavailable; the whole-set write was read back and diffed against union(read set, target): 6 labels, none stripped.",
"open_questions": [
{
"question": "A or a change at all? Route B is NOT additive on a published function (formatRelativeDate(V, {style:'short'}) renders 'Jul 4, 2024' today and 'Jul 4, ''24' after), and the only maintainer ruling in play (#7443, cited by #7745) names formatDate only. Measurement makes the trade different from the card's: in-repo cost is zero (no production bag carries style; formatRelativeDate has no production callers) and in-repo benefit is zero for the same reason, so the entire effect is on invisible external consumers of @object-ui/core@17.6.0.",
"options": [
"A — keep the strip, formatRelativeDate stays style-less. Cost: the shared bag stays half-honoured for external callers; the documented no-op in DateDisplayOptions.style stays true. Zero risk, zero churn.",
"B — the card's route, confined to formatRelativeDate. Cost: measured, ships a RangeError reachable from a declared accepted option value AND partially defeats #7745's precedence pin. Should not be adopted as written.",
"B-prime — move the strip into formatDate:219's relative-branch delegation. Cost: touches formatDate (outside #7745's ruling scope and this card's granted scope) and inverts one #7745 pin assertion. Benefit: reaches B's intent, terminates {style:'relative'} with the default absolute face (unchanged from today) without any special-casing, and preserves the precedence."
],
"recommendation": "A until the maintainer says otherwise, and if a change is wanted then B-prime, never B as written. Reason: B's only audience is external, so the value cannot be measured from this seat, while its cost is measurable and includes a reachable stack overflow plus a silent weakening of a ruling pinned three cards ago. B-prime is the only shape that delivers the intent with no collateral, and it isolates the decision to one assertion — but it edits formatDate, which is exactly what this card was fenced against, so it needs explicit authorisation rather than a lane decision."
},
{
"question": "If a change is authorised: does the maintainer authorise editing another card's pin? Both B and B-prime must invert date-display.optionsStyle-7745.test.ts:171-177 ('ignores it on the out-of-window ABSOLUTE fallback too') and rewrite the docblock at :51-54 that records the strip as deliberately behaviour-preserving.",
"options": [
"Yes — the pin is #7745's ruling made mechanical; superseding it is exactly what this card decides, and the inverted assertion becomes this card's own pin.",
"No — then A is the only route, since every route to B changes that assertion's verdict."
],
"recommendation": "Yes, but only bundled with the same decision as question 1 — the assertion and the behaviour are one fact, so they must move in one PR or neither."
},
{
"question": "Process note, flagged rather than worked around: the dispatch carried no four-axis decision frame ({decision_frame}), and the standard clause forbids me inventing one. The analysis above is therefore presented along the axes the card and #7745 already use (behaviour preservation / precedence integrity / consumer reach / vocabulary), not a graded escalation frame.",
"options": [
"PM supplies the decision frame and this seat re-renders the escalation analysis along its axes.",
"The maintainer answers question 1 directly from the measurements above, and no frame is needed."
],
"recommendation": "The second — the measurements collapse the decision to one assertion and one authorisation, so a graded frame would add ceremony without adding information. If PM's protocol requires the frame regardless, send it and this seat will re-render without re-measuring."
}
],
"out_of_scope_findings": []
}
Dispatch closed as an authorised STOP — pm:dispatched stripped, needs-user-decision stands. Decision box below.
domain:ui @ objectui PM seat, session_01YBWFb5YgMU5dw8p2VKj16S. The dev returned no code, which is the outcome the dispatch named as a full success. State fixed in this stroke: pm:dispatched removed (it and needs-user-decision are mutually exclusive and both were present — the dev could only union-write, so clearing the pm-state was mine), assignee cleared.
⭐ The dispatch's own stop-and-report condition fired, and my fence was the thing that turned out false. I wrote: 「you change formatRelativeDate's call into it, ⛔ not formatDate itself — if that turns out to be false, stop and report.」 It is false, and the reason is structural rather than incidental.
Verified independently by this seat before writing this box
claim
reading on origin/main
formatRelativeDate has zero production call sites
✅ the only call anywhere is date-display.ts:219; every other hit is a docblock, a comment, or packages/fields/src/index.tsx's re-export (which does not call it)
the delegation is formatDate's own
✅ :218-219 — if (effectiveStyle === 'relative') { return formatRelativeDate(date, options); } — the whole options bag, style included, is handed back
no production options bag carries style
✅ every style: hit in fields / plugin-grid / plugin-gantt is under __tests__/
⭐ And #7745's own pin comment states this card's finding in advance:
This is the edge formatDate's new read would have leaked through: beyond ±7 days formatRelativeDate delegates to formatDate, so without the strip the key would take effect HERE and nowhere else.
⇒ 「here and nowhere else」 is precisely the dev's measurement — in-repo effect zero, and the only audience external.
Decision box
① The question
Should formatRelativeDate honour options.style on its out-of-window absolute fallback — and if so, by which route?
② Options and costs
shape
cost
A ⬅ recommended pending a ruling
Keep the strip. formatRelativeDate stays style-less.
The shared bag stays half-honoured for external callers only. The documented no-op on DateDisplayOptions.style stays true. Zero risk, zero churn.
B
The card's route, confined to formatRelativeDate (= #7745's ablation leg C).
⛔ Should not be adopted as written. Measured: ships a RangeError: Maximum call stack size exceeded reachable from a declared, accepted option value, and partially defeats #7745's precedence pin (below).
B′
⭐ Not in the card. Move the strip out offormatRelativeDate:152 and intoformatDate:219's relative-branch delegation.
Reaches B's intent; kills the recursion at its source with no special-casing (formatDate stops handing its own 'relative' marker back, so one bounce is bounded); preserves the precedence; turns exactly one pin assertion red — the one that is the decision. ⚠️ But it edits formatDate, which both this dispatch and #7745's ruling scope fence off.
③ Measured facts
B's second defect, which the card does not name.formatDate(V, 'relative', {style:'short'}) renders Jul 4, 2024 today and "Jul 4, '24" under B — byte-identical to what options-wins would render. ⇒ the options spelling that objectui#7745 pinned as the loser would select the face for out-of-window dates. formatRelativeDatecannot distinguish a direct call from formatDate's positional-relative delegation; the only place that distinction exists is formatDate:219. ⭐ So B does not merely add a behaviour — it silently weakens a ruling pinned three cards ago.
B′'s ablation: Tests 1 failed | 14 passed — the single red is :171「ignores it on the out-of-window ABSOLUTE fallback too」, i.e. exactly the assertion under decision. Probes: formatRelativeDate(V,{style:'short'}) → "Jul 4, '24"; formatRelativeDate(V,{style:'relative'}) → Jul 4, 2024 (terminates); formatDate(V,'relative',{style:'short'}) → Jul 4, 2024 (precedence intact); in-window In 2 days unchanged.
⚠️ Both costs the card cites are measured FALSE in-repo. The card warns B 「changes live grid / gantt output for any caller already threading one shared options bag」 — no production bag carries style, and formatRelativeDate has no production callers, so B changes in-repo rendered output in exactly zero places. And the ±7-day 「visible face boundary」 the card worries about already exists today, with no options at all (In 2 days in-window vs Jul 4, 2024 out).
⭐ But the in-repo BENEFIT is zero for the same reason. ⇒ the entire effect of any change lands on invisible external consumers of published @object-ui/core@17.6.0 / @object-ui/fields. That is what makes this a product call and not a lane call: the cost is measurable from here and the value is not.
A is stable and honest — the strip is documented as deliberately behaviour-preserving and the style no-op is stated on the declaration. ⇒ nothing rots if this is never answered. The card stays as a recorded asymmetry: a shared options bag one of its two functions ignores, for external callers, with the reason written down.
⚠️ The one thing that should not happen is B landing as written by a later seat reading only the card. It looks like a one-line flip; it is a reachable stack overflow plus a silent weakening of #7745. ⛔ That is why this box exists rather than a dispatch.
Two sub-questions the maintainer also owns
If a change is authorised, is editing another card's pin authorised? Both B and B′ must invert date-display.optionsStyle-7745.test.ts:171-177 and rewrite the :51-54 docblock recording the strip as behaviour-preserving. ⭐ The assertion and the behaviour are one fact — they move in one PR or neither.
The dev flagged, correctly, that my dispatch supplied no decision frame and that the standing clause forbids it inventing one. That is the PM's job, not the dev's — this box is it, rendered in the house four-face shape from the dev's measurements. ⭐ It was right to flag the gap rather than paper over it with an invented frame; a graded escalation frame would have added ceremony without information here, because the measurements collapse the decision to one assertion and one authorisation.
Ruling recorded — option A (director seat, decision batch #72, 2026-09-07)
Maintainer reply, verbatim: 「7749 已加入队列,其他同意」 — this card was presented with A recommended and is covered by 「其他同意」.
Ruling: formatRelativeDate stays style-less. The strip #7745 added at date-display.ts:152 stays; the documented no-op on DateDisplayOptions.style for this function stays true. Reasons, as presented: no production call site in this repo threads style through either function, so in-repo cost and benefit are both zero; the only audience for a change is external consumers of published @object-ui/core, and at this stage of the project a non-additive change to a published function for an unmeasurable audience is not worth taking.
Two things recorded so a later seat does not re-open this from the card text alone:
If the maintainer ever wants the shared bag honoured on the fallback, the only shape is B′ (move the strip into formatDate:219's relative-branch delegation), and it comes with explicit authorisation to invert date-display.optionsStyle-7745.test.ts:171-177. That would be a new card, not this one.
The one item from this round that lands regardless of the ruling is filed as #8262: the out-of-window precedence assertion #7745's pin file lacks, so the strip cannot be flipped silently.
Closing not planned. Thanks to the dev seat for stopping on the dispatch's own condition instead of shipping the one-line flip.
Activity
os-justin commentedon Sep 6, 2026
Claim: PM loop R3 (objectui⚠️ If your measurement contradicts that, ⛔ stop and report.
domain:uiPM seat) — wave top-up, slot 9Session:
session_01YBWFb5YgMU5dw8p2VKj16SBranch:
claude/issue-7816-relative-date-styleWorktree:
objectui-issue-7816Domain:
domain:ui·package: core·priority:p3Tier:
opus=TIER_DEFAULT. ⛔ Notsonnet— the change is one line and its consequence is not.Clause-②: assessed NO.
options.styleis already declared onDateDisplayOptionsand already accepted; what moves is whetherformatRelativeDatehonours a key it already takes. Same shape as objectui#7687 (the comboboxdisabledread), judged a behaviour change and ⛔ not an accept-set change.Premise re-verified by this seat on
origin/main:⇒ the strip and the delegation are both exactly where the card says. There is also already a pin file —
packages/core/src/utils/__tests__/date-display.optionsStyle-7745.test.ts— whose docblock (:51) names the very ablation this card is built on. ⭐ Read it before you write anything: it is the previous round's measurement of your change.Zone 1 — ADJUDICATION (binding)
styleonly for the out-of-window ABSOLUTE fallback, i.e. flip theabsoluteFallbackOptionsstrip that objectui#7745 added.formatRelativeDate(v, { style: 'short' })on a two-year-old date then rendersJul 4, '24instead ofJul 4, 2024. Well-defined today, zero new vocabulary.stylefor the relative phrase itself would require minting a relative-style vocabulary that does not exist, i.e. accepting a key with nothing to name. ⛔ Do not take it.In 2 daysinside,Jul 4, '24outside — two faces from one call, selected by the date rather than by the author). ⇒ if your measurement leads you to conclude that A-vs-B is a maintainer call rather than a lane call, hangneeds-user-decisionand STOP. ⛔ Do not decide it silently in either direction. That is a full success, not a failure.formatDateonly. ⛔ Do not re-open or widen it.Zone 2 — ⭐ THE TRAP IS ALREADY MEASURED. Do not rediscover it in production.
objectui#7745's ablation leg C dropped the strip — which is precisely your change — and turned three cases red (one more than that seat predicted, and it reported the surplus as observed):
⇒
{ style: 'relative' }handed toformatRelativeDaterecurses to stack exhaustion through the delegation. ⛔ Any implementation of B must answer that input explicitly — refuse it, coerce it, or short-circuit it, with the choice stated and pinned. A patch that only flips the strip ships a stack overflow reachable from a declared, accepted option value.Zone 3 — SUGGESTED ROUTE (advisory)
The card's own before/after is leg C's three red cases; make them your red-first ablation rather than inventing one. Pin: the out-of-window absolute face honours⚠️ A pin that omits the third leg passes while the reachable crash stays.
style; the in-window relative phrase is unchanged; and{ style: 'relative' }terminates with a stated behaviour.Serial constraints cleared:⚠️
packages/core/**is named by no other slot in this wave.formatDateis shared withplugin-grid/plugin-ganttconsumers — you changeformatRelativeDate's call into it, ⛔ notformatDateitself; if that turns out to be false, stop and report. The queue arbitrates.Generated by Claude Code
os-justin commentedon Sep 6, 2026
STOP — A-vs-B 判定为维护者决策,已挂
needs-user-decision,本轮不落码(无 PR)派发词给了明确的停车条件,本轮测量把它触发了两次。所有读数在
origin/main@20316bac3上取,pin 文件packages/core/src/utils/__tests__/date-display.optionsStyle-7745.test.ts基线 14/14 绿。1. 派发词自己的 stop-and-report 条件成立
被证伪了。 只改
formatRelativeDate(即卡片写的 Route B = #7745 的 ablation leg C)拿不到 B 的意图,还附带两个缺陷;要拿到 B 的意图且不破坏 #7745 的裁决,必须同时改formatDate第 219 行的委托。2. 三条腿的实测(变异 → 证明落盘 → 跑 →
git checkout HEAD --还原 → blob 哈希比对)基线(今天,
origin/main):Leg C = 卡片的 Route B(去掉
formatRelativeDate里的 strip)—— pin 文件3 failed | 12 passed,与 #7745 记录的三红逐字一致:最后一行是本轮的新发现:Route B 会在窗口外部分推翻 #7745 的 precedence 裁决。 #7745 钉的是「positional 赢,
options.style只在 positional 槽为undefined时被读」。B 之下,作者明明写了 positional'relative',窗口外渲染出来的却正是 bag 里那个本该输掉的'short'的脸 —— 输出与 options-wins 逐字节相同。formatRelativeDate无法区分「作者直接拿这个 bag 调我」和「formatDate的 positional-relative 分支拿作者的 bag 调我」,两者到达时参数完全一样;唯一能区分的位置是formatDate第 219 行。所以这个缺陷在本卡授予的范围内无法修。Leg B′(把 strip 从
formatRelativeDate:152挪进formatDate:219的委托)—— pin 文件1 failed | 14 passed:B′ 把递归在源头关掉:递归之所以存在,是因为
formatDate把自己的'relative'标记原样递回被调方;formatDate侧先剥,一次弹跳就够,深度有界。于是唯一转红的断言恰好就是这张卡要裁的那一条 —— 决策面被收敛到一行。3. 卡片列的两条代价,实测都不成立
代价一「B 会改变任何已经把一个共享 options bag 穿过两个函数的调用方的 grid / gantt 输出」——本仓为零。
formatRelativeDate生产调用点 0 个(仅date-display.ts:219自己那次);喂进这两个函数的生产 bag 全仓如下,没有一个带style:全仓带
style的 bag 只出现在 #7745 的 pin 测试里;姊妹仓objectstack检出内formatRelativeDate/DateDisplayOptions零命中。代价二「它让 ±7 天边界变成可见的脸边界」—— 那条边界今天就在。 不传任何 options:窗口内
"In 2 days",窗口外"Jul 4, 2024"—— 一次调用两张脸、由日期而不是作者选定,是formatRelativeDate的固有形状。B 改的是窗口外那张绝对脸换成哪一张,不是有没有边界。⇒ 两条代价一条为零、一条不归 B。但同一份census 也说明:B 在本仓的收益同样为零(没有调用方在传
style)。本仓成本 0、收益 0,全部效果落在@object-ui/core@17.6.0/@object-ui/fields的外部消费者身上 —— 本席位看不见他们。4. 为什么这是维护者的决策而不是车道内的决策
formatRelativeDate(V, {style:'short'})今天渲染Jul 4, 2024,之后渲染Jul 4, '24。finding(core):DateDisplayOptions.styleis read byformatDateTimealone — onformatDateit is inert AND collides with a positional parameter of the same name #7745 恰恰是靠 additive 这一条被接受的(「今天渲染出一张脸的调用,之后渲染同一张脸」)。datetimehas two display conventions:DateTimeCellRendererinlines its own and never callsformatDateTime#7443,finding(core):DateDisplayOptions.styleis read byformatDateTimealone — onformatDateit is inert AND collides with a positional parameter of the same name #7745 引的那条)只点名formatDate。没有任何裁决授权在formatRelativeDate上做非 additive 的改动。date-display.optionsStyle-7745.test.ts:171-177),而那个文件的 docblock(:51)把这条 strip 记为「刻意的行为保持」。formatDate本身,同时越出 finding(core):DateDisplayOptions.styleis read byformatDateTimealone — onformatDateit is inert AND collides with a positional parameter of the same name #7745 的裁决范围与本卡授予的范围。5. 需要维护者裁的三问
formatDate:219的委托;今天行为不变,精度上它同时修掉递归与 precedence 泄漏)。⛔ 卡片原样的 Route B 不该被采纳 —— 它把一个可达的 stack overflow 和一次 precedence 反转一起发出去。DateDisplayOptions.styleis read byformatDateTimealone — onformatDateit is inert AND collides with a positional parameter of the same name #7745 的 pin 吗? B/B′ 都要反转:171-177那条断言并重写其 docblock。顺带一条与本决策同生共死、暂不单独立卡的观察:#7745 的 precedence pin 只覆盖了窗口内的 sharpest pair(
:109-119用的是inTwoDays()),窗口外的同一对formatDate(V, 'relative', {style:'short'})没有断言 —— 正因如此 leg C 的 precedence 泄漏在 #7745 那轮是静默的。无论裁决走哪条路,补上这条断言都应随之落地。分支
claude/issue-7816-relative-date-style已推空分支(仅认领落地标记,零 commit),worktree 已拆除。等裁决。Generated by Claude Code
os-justin commentedon Sep 6, 2026
os-dev-report
{ "issue": 7816, "status": "needs_decision", "branch": "claude/issue-7816-relative-date-style", "pr": null, "premise_still_valid": true, "summary": "No code landed — this is the dispatch's authorised STOP. Premise verified verbatim on origin/main@20316bac3 (date-display.ts:125 absoluteFallbackOptions, :140 formatRelativeDate, :152 the stripped delegation) and Clause-2 confirmed (style is already declared and accepted; what moves is whether formatRelativeDate honours it). Two independent triggers fired. (1) The dispatch's own stop-and-report condition: 'you change formatRelativeDate's call into it, not formatDate itself — if that turns out to be false, stop and report'. It is false. A Route B confined to formatRelativeDate (= #7745's ablation leg C) reproduces the measured crash AND a second defect the card does not name: it partially defeats #7745's pinned precedence. formatDate(V, 'relative', {style:'short'}) renders 'Jul 4, 2024' today and 'Jul 4, ''24' under leg C — byte-identical to what options-wins would render, i.e. the spelling #7745 pinned as the loser selects the face for out-of-window dates. formatRelativeDate cannot distinguish a direct call from formatDate's positional-relative delegation; the only place that distinction exists is formatDate:219, outside this card's granted scope. (2) A-vs-B is a maintainer call: B is NOT additive on a published function (formatRelativeDate(V,{style:'short'}) moves 'Jul 4, 2024' -> 'Jul 4, ''24'), the only ruling in play (#7443, cited by #7745) names formatDate ONLY, and B must invert another card's pin assertions (date-display.optionsStyle-7745.test.ts:171-177, whose docblock records the strip as deliberately behaviour-preserving). Both costs the card cites are measured FALSE in-repo: formatRelativeDate has ZERO production call sites (only date-display.ts:219) and no production options bag anywhere carries style (fields:844 {dueLike,locale,t}; grid 4384/4607/4627, gantt 920/927, dataset-format:192 {locale}; dashboard:425 none), so leg C changes in-repo rendered output in exactly zero places; and the plus/minus-7-day face boundary already exists today with no options at all ('In 2 days' in-window vs 'Jul 4, 2024' out-of-window). Because the same census shows in-repo BENEFIT is also zero, the entire effect of B lands on invisible external consumers of the published @object-ui/core@17.6.0 / @object-ui/fields — a product call, not a lane call. needs-user-decision hung on the card (read-back verified, 6 labels, none stripped); write-up posted as comment 5562227618. Also measured a clean alternative the card does not consider, B-prime: move the strip from formatRelativeDate:152 INTO formatDate:219's relative-branch delegation. It reaches B's intent, kills the recursion at its source with no special-casing (formatDate stops handing its own 'relative' marker back, so one bounce is bounded), keeps the precedence intact, and turns exactly ONE pin assertion red — the one that IS the decision. B-prime touches formatDate, which the dispatch and #7745's ruling scope both fence off, so it is offered for authorisation rather than taken.", "tests": "Baseline: pnpm exec vitest run packages/core/src/utils/__tests__/date-display.optionsStyle-7745.test.ts -> exit 0, 'Test Files 1 passed (1) / Tests 14 passed (14)'. Ablation discipline on both legs: trap '<restore>' EXIT INT TERM with absolute REPO_ROOT paths; mutation proven on disk by grep -c of the injected marker AND of the deleted text (leg C: absoluteFallbackOptions(options) 1 -> 0, ABLATION-LEG-C marker = 1); bytes proven changed by git hash-object vs git rev-parse HEAD:<path> (HEAD 6cbabb21ccb0eb105722aaaf87b5185b3bfebd2c -> leg C 8cf02eb90437c57e76dc8a4859f835f9c3e1b50c, B-prime fa384cecfd14e138b825a7e64ff246dc619889ba); restore via git checkout HEAD -- <path> (never bare), proven by empty git diff HEAD --stat + git status --porcelain + on-disk blob back to 6cbabb21. No build needed: the pin imports '../date-display' relatively, so vitest resolves source, not dist. LEG C (= the card's Route B): 'Tests 3 failed | 12 passed (15)' — AssertionError: expected 'Jul 4, ''24' to be 'Jul 4, 2024' (out-of-window fallback), RangeError: Maximum call stack size exceeded on {style:'relative'} via both entry points, and 'the options spelling IS the positional spelling, for every face' red by the same RangeError. Identical to the three reds #7745 recorded — the trap was reproduced, not rediscovered in production. Probe under leg C also measured the NEW defect: formatDate(V,'relative',{style:'short'}) = 'Jul 4, ''24' (today 'Jul 4, 2024'). LEG B-PRIME: 'Tests 1 failed | 14 passed (15)' — the single red is date-display.optionsStyle-7745.test.ts:171 'ignores it on the out-of-window ABSOLUTE fallback too'; probe: formatRelativeDate(V,{style:'short'}) = 'Jul 4, ''24', formatRelativeDate(V,{style:'relative'}) = 'Jul 4, 2024' (terminates), formatDate(V,'relative',{style:'short'}) = 'Jul 4, 2024' (precedence intact), in-window 'In 2 days' unchanged. Consumer census by git grep across packages/apps/examples plus the objectstack sibling checkout (zero hits there). Worktree left byte-identical to origin/main; scratch probe file deleted; no changeset, no commit, no PR — nothing to gate.", "mcp_calls": "9 — issue_read get, issue_read get_comments, get_label, issue_read get_labels, issue_write update (labels), issue_read get_labels (read-back), 2x add_issue_comment, issue_read get_comments (comment read-back). Channel note: the repo-scoped REST probe returned 403 ('GitHub access is not enabled for this session') for both the unauthenticated and the GH_TOKEN-authenticated read, and gh is not installed, so the label write and both comments went via MCP — declared channel switch. The additive POST .../issues/<n>/labels endpoint was therefore unavailable; the whole-set write was read back and diffed against union(read set, target): 6 labels, none stripped.", "open_questions": [ { "question": "A or a change at all? Route B is NOT additive on a published function (formatRelativeDate(V, {style:'short'}) renders 'Jul 4, 2024' today and 'Jul 4, ''24' after), and the only maintainer ruling in play (#7443, cited by #7745) names formatDate only. Measurement makes the trade different from the card's: in-repo cost is zero (no production bag carries style; formatRelativeDate has no production callers) and in-repo benefit is zero for the same reason, so the entire effect is on invisible external consumers of @object-ui/core@17.6.0.", "options": [ "A — keep the strip, formatRelativeDate stays style-less. Cost: the shared bag stays half-honoured for external callers; the documented no-op in DateDisplayOptions.style stays true. Zero risk, zero churn.", "B — the card's route, confined to formatRelativeDate. Cost: measured, ships a RangeError reachable from a declared accepted option value AND partially defeats #7745's precedence pin. Should not be adopted as written.", "B-prime — move the strip into formatDate:219's relative-branch delegation. Cost: touches formatDate (outside #7745's ruling scope and this card's granted scope) and inverts one #7745 pin assertion. Benefit: reaches B's intent, terminates {style:'relative'} with the default absolute face (unchanged from today) without any special-casing, and preserves the precedence." ], "recommendation": "A until the maintainer says otherwise, and if a change is wanted then B-prime, never B as written. Reason: B's only audience is external, so the value cannot be measured from this seat, while its cost is measurable and includes a reachable stack overflow plus a silent weakening of a ruling pinned three cards ago. B-prime is the only shape that delivers the intent with no collateral, and it isolates the decision to one assertion — but it edits formatDate, which is exactly what this card was fenced against, so it needs explicit authorisation rather than a lane decision." }, { "question": "If a change is authorised: does the maintainer authorise editing another card's pin? Both B and B-prime must invert date-display.optionsStyle-7745.test.ts:171-177 ('ignores it on the out-of-window ABSOLUTE fallback too') and rewrite the docblock at :51-54 that records the strip as deliberately behaviour-preserving.", "options": [ "Yes — the pin is #7745's ruling made mechanical; superseding it is exactly what this card decides, and the inverted assertion becomes this card's own pin.", "No — then A is the only route, since every route to B changes that assertion's verdict." ], "recommendation": "Yes, but only bundled with the same decision as question 1 — the assertion and the behaviour are one fact, so they must move in one PR or neither." }, { "question": "Process note, flagged rather than worked around: the dispatch carried no four-axis decision frame ({decision_frame}), and the standard clause forbids me inventing one. The analysis above is therefore presented along the axes the card and #7745 already use (behaviour preservation / precedence integrity / consumer reach / vocabulary), not a graded escalation frame.", "options": [ "PM supplies the decision frame and this seat re-renders the escalation analysis along its axes.", "The maintainer answers question 1 directly from the measurements above, and no frame is needed." ], "recommendation": "The second — the measurements collapse the decision to one assertion and one authorisation, so a graded frame would add ceremony without adding information. If PM's protocol requires the frame regardless, send it and this seat will re-render without re-measuring." } ], "out_of_scope_findings": [] }Generated by Claude Code
os-justin commentedon Sep 6, 2026
Dispatch closed as an authorised STOP —
pm:dispatchedstripped,needs-user-decisionstands. Decision box below.domain:ui@ objectui PM seat,session_01YBWFb5YgMU5dw8p2VKj16S. The dev returned no code, which is the outcome the dispatch named as a full success. State fixed in this stroke:pm:dispatchedremoved (it andneeds-user-decisionare mutually exclusive and both were present — the dev could only union-write, so clearing the pm-state was mine), assignee cleared.⭐ The dispatch's own stop-and-report condition fired, and my fence was the thing that turned out false. I wrote: 「you change
formatRelativeDate's call into it, ⛔ notformatDateitself — if that turns out to be false, stop and report.」 It is false, and the reason is structural rather than incidental.Verified independently by this seat before writing this box
origin/mainformatRelativeDatehas zero production call sitesdate-display.ts:219; every other hit is a docblock, a comment, orpackages/fields/src/index.tsx's re-export (which does not call it)formatDate's own:218-219—if (effectiveStyle === 'relative') { return formatRelativeDate(date, options); }— the whole options bag,styleincluded, is handed backstylestyle:hit infields/plugin-grid/plugin-ganttis under__tests__/date-display.optionsStyle-7745.test.ts:171-177⭐ And #7745's own pin comment states this card's finding in advance:
⇒ 「here and nowhere else」 is precisely the dev's measurement — in-repo effect zero, and the only audience external.
Decision box
① The question
Should
formatRelativeDatehonouroptions.styleon its out-of-window absolute fallback — and if so, by which route?② Options and costs
formatRelativeDatestays style-less.DateDisplayOptions.stylestays true. Zero risk, zero churn.formatRelativeDate(= #7745's ablation leg C).RangeError: Maximum call stack size exceededreachable from a declared, accepted option value, and partially defeats #7745's precedence pin (below).formatRelativeDate:152and intoformatDate:219's relative-branch delegation.formatDatestops handing its own'relative'marker back, so one bounce is bounded); preserves the precedence; turns exactly one pin assertion red — the one that is the decision.formatDate, which both this dispatch and #7745's ruling scope fence off.③ Measured facts
formatDate(V, 'relative', {style:'short'})rendersJul 4, 2024today and"Jul 4, '24"under B — byte-identical to what options-wins would render. ⇒ the options spelling that objectui#7745 pinned as the loser would select the face for out-of-window dates.formatRelativeDatecannot distinguish a direct call fromformatDate's positional-relative delegation; the only place that distinction exists isformatDate:219. ⭐ So B does not merely add a behaviour — it silently weakens a ruling pinned three cards ago.Tests 3 failed | 12 passed— the out-of-window leak, plusRangeErroron{style:'relative'}through both entry points. Identical to the three reds finding(core):DateDisplayOptions.styleis read byformatDateTimealone — onformatDateit is inert AND collides with a positional parameter of the same name #7745 recorded.Tests 1 failed | 14 passed— the single red is:171「ignores it on the out-of-window ABSOLUTE fallback too」, i.e. exactly the assertion under decision. Probes:formatRelativeDate(V,{style:'short'})→"Jul 4, '24";formatRelativeDate(V,{style:'relative'})→Jul 4, 2024(terminates);formatDate(V,'relative',{style:'short'})→Jul 4, 2024(precedence intact); in-windowIn 2 daysunchanged.style, andformatRelativeDatehas no production callers, so B changes in-repo rendered output in exactly zero places. And the ±7-day 「visible face boundary」 the card worries about already exists today, with no options at all (In 2 daysin-window vsJul 4, 2024out).@object-ui/core@17.6.0/@object-ui/fields. That is what makes this a product call and not a lane call: the cost is measurable from here and the value is not.formatRelativeDate(V,{style:'short'})movesJul 4, 2024→"Jul 4, '24". The only ruling in play (objectui#7443, cited by finding(core):DateDisplayOptions.styleis read byformatDateTimealone — onformatDateit is inert AND collides with a positional parameter of the same name #7745) namesformatDateonly.④ Consequence of not deciding
A is stable and honest — the strip is documented as deliberately behaviour-preserving and the
styleno-op is stated on the declaration. ⇒ nothing rots if this is never answered. The card stays as a recorded asymmetry: a shared options bag one of its two functions ignores, for external callers, with the reason written down.Two sub-questions the maintainer also owns
date-display.optionsStyle-7745.test.ts:171-177and rewrite the:51-54docblock recording the strip as behaviour-preserving. ⭐ The assertion and the behaviour are one fact — they move in one PR or neither.formatDate, outside both finding(core):DateDisplayOptions.styleis read byformatDateTimealone — onformatDateit is inert AND collides with a positional parameter of the same name #7745's ruling scope and this card's granted scope. It is offered for authorisation, ⛔ not taken.The dev flagged, correctly, that my dispatch supplied no decision frame and that the standing clause forbids it inventing one. That is the PM's job, not the dev's — this box is it, rendered in the house four-face shape from the dev's measurements. ⭐ It was right to flag the gap rather than paper over it with an invented frame; a graded escalation frame would have added ceremony without information here, because the measurements collapse the decision to one assertion and one authorisation.
Generated by Claude Code
os-zhuang commentedon Sep 7, 2026
Ruling recorded — option A (director seat, decision batch #72, 2026-09-07)
Maintainer reply, verbatim: 「7749 已加入队列,其他同意」 — this card was presented with A recommended and is covered by 「其他同意」.
Ruling:
formatRelativeDatestays style-less. The strip #7745 added atdate-display.ts:152stays; the documented no-op onDateDisplayOptions.stylefor this function stays true. Reasons, as presented: no production call site in this repo threadsstylethrough either function, so in-repo cost and benefit are both zero; the only audience for a change is external consumers of published@object-ui/core, and at this stage of the project a non-additive change to a published function for an unmeasurable audience is not worth taking.Two things recorded so a later seat does not re-open this from the card text alone:
RangeErrorreachable from a declared, accepted option value ({ style: 'relative' }recurses through the delegation) and silently weakens finding(core):DateDisplayOptions.styleis read byformatDateTimealone — onformatDateit is inert AND collides with a positional parameter of the same name #7745's precedence pin on the out-of-window path. Both were measured, not predicted.formatDate:219's relative-branch delegation), and it comes with explicit authorisation to invertdate-display.optionsStyle-7745.test.ts:171-177. That would be a new card, not this one.The one item from this round that lands regardless of the ruling is filed as #8262: the out-of-window precedence assertion #7745's pin file lacks, so the strip cannot be flipped silently.
Closing not planned. Thanks to the dev seat for stopping on the dispatch's own condition instead of shipping the one-line flip.
Generated by Claude Code