Skip to content

finding(plugin-timeline): the gantt date path has FIVE non-total operations UPSTREAM of the speller — the property reads that fetch the date crash before spellGanttDateValue is ever entered #7153

Description

@os-warren

Found while implementing #7036. Not repaired there: #7036's ruled scope is spellGanttDateValue's trailing three branches, and every site below is in a different function (findUnusableGanttDate), reached before that helper is entered.

What is wrong

#7036's headline is that spellGanttDateValue's Array.isArray is "the last non-total operation on the gantt date path". Measured, that is false. It is the last non-total operation inside that function; the path itself has five more, and all five run earlier — in the property reads that fetch the date out of the authored document.

findUnusableGanttDate walks the rows with plain member access:

const rowItems = (items[rowIndex]?.items || []) as any[];
const value = rowItems[itemIndex]?.[key];

?. guards null / undefined. It does not guard a getter that throws or a revoked Proxy — both crash inside the read itself, before any value exists to judge. calculateDateRange repeats the same two reads in its flatMap.

Measurement

In-render on dd35800af, through the real TimelineRenderer (not a replica), same run that produced #7036's readings:

input outcome
items[0].items[0] with a throwing endDate getter THREW Error: endDate getter throws
items[0].items[0] is a revoked Proxy THREW TypeError: Cannot perform 'get' on a proxy that has been revoked
items[0] is a revoked Proxy THREW same
items[0].items is a revoked Proxy THREW same
items[0] with a throwing items getter THREW Error: items getter throws
CONTROL — an ordinary row/item drew 1 bar

The control is live and non-zero on the same instrument, so the five THREW rows are readings rather than a broken harness.

Severity, honestly

p3, the same class and the same argument as #7036. Not reachable from an authored document: ObjectUI metadata is JSON, and JSON can spell neither a getter nor a proxy. Re-swept on dd35800af — zero Proxy.revocable in the repo and zero Object.setPrototypeOf calls in packages/*/src, each beside a live control on the same instrument (52 Proxy mentions, 19 Object.assign calls). The three non-comment __proto__ hits are denylists that block it.

So this is filed for the claim it corrects, not for the crash it describes. It is recorded because #7036's docblock now states the path is not total, and that statement should point at an enumeration rather than at prose.

Suggested scope if picked up

⚠️ Read this with #7036's warning first. This path has now carried six totality claims (#6759#6905#6907#7027#7036 → this), five of them falsified by the next card's measurement. A repair here that ends with a new confident sentence continues the sequence rather than closing it.

Two defensible branches, the same either/or #7027 and #7036 were filed under:

The second is the cheaper and probably the better one, but it is an adjudication and this card does not make it. If a row is added it belongs in the existing pinned adversarial set (packages/plugin-timeline/src/__tests__/timeline-gantt-date-brand-7027.test.tsx), not in a new file.

Refs: #7036 (the speller's own exclusion, where these five were measured) · #7027 (the brand gate) · #6907 / PR #7026 (the spelling rule) · #6781 / PR #6905 (the type rule)

Metadata

Metadata

Assignees

Labels

domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatfindingpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions