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)
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'sArray.isArrayis "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.findUnusableGanttDatewalks the rows with plain member access:?.guardsnull/undefined. It does not guard a getter that throws or a revokedProxy— both crash inside the read itself, before any value exists to judge.calculateDateRangerepeats the same two reads in itsflatMap.Measurement
In-render on
dd35800af, through the realTimelineRenderer(not a replica), same run that produced #7036's readings:items[0].items[0]with a throwingendDategetterError: endDate getter throwsitems[0].items[0]is a revokedProxyTypeError: Cannot perform 'get' on a proxy that has been revokeditems[0]is a revokedProxyitems[0].itemsis a revokedProxyitems[0]with a throwingitemsgetterError: items getter throwsThe 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— zeroProxy.revocablein the repo and zeroObject.setPrototypeOfcalls inpackages/*/src, each beside a live control on the same instrument (52Proxymentions, 19Object.assigncalls). 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
Two defensible branches, the same either/or #7027 and #7036 were filed under:
tryaround the row/item walk infindUnusableGanttDate(and the matchingflatMapincalculateDateRange), refusing the row through the existingtimeline.gantt.unusableRange.malformedDatediagnostic rather than dying. Note this is a wider change than it looks: it touches the accept set's plumbing, which is finding(plugin-timeline): non-string gantt dates that coerce to the epoch (0,false,[]) still draw a 649-column 1970 axis with no diagnostic #6781's ruling.spellGanttDateValue'sArray.isArrayis the last non-total operation on the gantt date path — a revokedProxycrashes it #7036 chose for its own site, on the argument that acatcharound a read whose failure carries no information is substitution rather than a read, and that substitution is the consumer-side tolerance finding(plugin-timeline): a gantt timeline with an EMPTY literalitemsarray throws —calculateDateRangereduces an empty list #6750 and finding(plugin-timeline): two more unusable gantt date ranges — a malformed date still throws, and an inverted author-pinned range silently draws a negative-width bar on no axis #6759 both refused.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)