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 }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
finding(plugin-timeline): a gantt row that is a non-null primitive or an array (items: [0], ['x'], [true], [[]]) draws an unlabelled empty row silently — while validate now refuses it #7364
Ruled: 5809218505 · letter A (the renderer refuses a non-object row too) · 2026-09-24T06:51Z
Found while implementing #7164 (maintainer ruling A+). Not repaired there: the ruling fixed the render-time door at exactly three shapes — items not an array, a null row, a row whose items is a truthy non-array — and required the card's CONTROL rows, which include items: [0] and items: [[]], to keep DRAWING. So this corner was deliberately left as measured, and it is recorded here rather than widened silently.
What is wrong
A row that is a non-null primitive or an array has no .items and no .label, so the gantt walk reads undefined off it and draws an EMPTY, UNLABELLED row. Nothing tells the author. Measured in render through the real TimelineRenderer, on a67abdc88 (main) and on the #7164 branch at 8e1dc8274 — identical readings:
(items: ['x'] and items: [true] take the same path: 'x'.items and true.items are undefined.) That is consumer-side tolerance of the kind #6750 / #6759 refused elsewhere on this branch: authored content that is not a row is dropped without a word.
After #7164, validate REFUSES every one of these at authoring time (items[0]: expected object, received number / received array), so the declared accept set and the rendered one disagree in this corner. The direction is the safe one — the renderer is more lenient than validate, never the reverse, and it does not crash — but declared and rendered still differ.
B — leave it: validate is the door that matters at authoring, and the renderer's leniency here is harmless.
Recommendation: A, on the family's refuse-not-tolerate principle (#6750, #6759, #7164) — an AI-authored document that emits a scalar where a row belongs should be told so on screen, not shown a blank row.
Blocked-by: #7164 (the predicate and the key both land there).
packages/plugin-timeline/src/renderer.tsx:337 function classifyGanttRows(items: unknown): GanttRowsVerdict {
packages/plugin-timeline/src/renderer.tsx:343 if (row == null) return { ok: false, path: `items[${rowIndex}]`, value: row };
packages/plugin-timeline/src/renderer.tsx:300 * `timeline.gantt.unusableRange.malformedRow`, a key of its own. It is never
packages/i18n/src/locales/en.ts:823 malformedRow:
⭐ :343 就是卡片让人改的那一处,逐字是 row == null,正是它说的"change classifyGanttRows's row predicate from row == null to『not an object』"。谓词、i18n key、验收面都已落地,本卡可以直接进队列。
renderer.tsx:255 的注释还确认了 #7164 划定的边界("a TRUTHY non-array never reaches this type — classifyGanttRows refuses it"),也就是卡片说的"ruling fixed the render-time door at exactly three shapes"。
Ruling: UI closure batch 2 item 1 · letter A (the renderer refuses a non-object row too) · maintainer 「同意」 (chat, director seat summon #29, session_01EcrTi7s5oDYPHS4Pi7h31d) 2026-09-24T06:48Z
Thread re-read to its last comment (5548518842) in this act. Read on objectui origin/main62597c5: packages/plugin-timeline/src/renderer.tsx:343 still refuses only row == null.
Ruled: A.classifyGanttRows refuses any row that is not a plain object (a number, string, boolean or array), through the existing timeline.gantt.unusableRange.malformedRow key. ⛔ No new key and no new pack strings. The renderer then agrees with validate, which already refuses these shapes at authoring time.
This deliberately supersedes one clause of the objectui#7164 A+ ruling: the control rows items: [0] and items: [[]] that it required to keep drawing. The maintainer's word above is that supersession. The PR rewrites those control assertions to expect the refusal and says so in its body in those terms. ⛔ It does not quietly "make the test green". Keep a control that still draws, a well-formed object row.
Execution: pm:queue · priority:p3 · domain:ui stand; published renderer behaviour ⇒ the lane declares Clause-② per diff.
Claim: PM loop round 4 — domain:ui execution seat 3
Session: session_01D486C1axmXnrkJMNUfz2eb
Branch: claude/issue-7364-timeline-nonobject-row
Worktree: objectui-issue-7364
Domain: domain:ui
Seat: domain:ui#3
File surface: packages/plugin-timeline/src/renderer.tsx (classifyGanttRows refuses a non-object row through the existing timeline.gantt.unusableRange.malformedRow key), the objectui#7164 control-row pins rewritten together with their reason (⛔ not deleted), new tests beside them, one .changeset/7364-…md (stop on breach; explain in the report)
Container & model: M, mode:subagent, model: opus (default judgement tier). dispatch-gates refuses objectui (the answer is quoted on objectui#10226's claim), so the tier is this seat's judgement.
Clause-②: no
Thread-read: 5809218505
Serial constraints cleared: open PRs (27, file lists read 2026-09-24T19:19Z) and live pm:dispatched claims (File surface: lines read 2026-09-24T19:19Z): none touches this surface.
Ruling-ref: UI closure batch 2 item 1 · letter A, maintainer 「同意」. ⛔ No new key and no new pack strings. Why Clause-②: no: validate already refuses these rows at authoring time, so the declared accept-set does not move; the renderer comes into line with it. A new key or a change to validate would breach this surface: stop and report.
{
"issue": 7364,
"status": "done",
"branch": "claude/issue-7364-timeline-nonobject-row",
"pr": "https://github.com/objectstack-ai/objectui/pull/10351",
"session": "session_01D486C1axmXnrkJMNUfz2eb",
"premise_still_valid": true,
"head_sha": "9e09349a9e05c69a086f32a9344589ec8b02d94b",
"summary": "Premise measured on origin/main 1f8ef0a89: classifyGanttRows refused only `row == null`. It now refuses any row where `typeof row !== 'object' || row === null || Array.isArray(row)` (number, string, boolean, array, null/undefined, function, symbol, bigint), through the existing timeline.gantt.unusableRange.malformedRow key; no new key, no pack strings, validate untouched. 'Plain object' is defined as that typeof/null/array test with no prototype read (Date/class instance/Proxy rows cannot be written in JSON, and the live-Proxy row control must keep drawing). The #7164 control pins items:[0] and items:[[]] were rewritten (not deleted) to expect the refusal, each citing ruling 5809218505, in timeline-gantt-malformed-row-7164.test.tsx and pin 6 of timeline-gantt-date-brand-7027.test.tsx; a new block pins [0], [[]], ['x'], [true] REFUSED at items[0] through the real TimelineRenderer (path + value in the diagnostic, row clause, not the date clause, 0 bars, empty axis, role=alert), a non-object row beside good rows named items[1], and well-formed object rows still DRAW. One stated side effect: Array.isArray(row) now runs before U3, so a revoked Proxy as a ROW dies at IsArray instead of get; the pin-5 row in the 7027 file is updated with that reason (same exclusion class, same move #7164 made for items and row.items). assignee was os-project-manager on arrival (not touched). Worktree removed after the PR opened.",
"tests": "All at head 9e09349a9. (1) repo root under os-verify-lock: `pnpm exec vitest run --maxWorkers=2 packages/plugin-timeline/ packages/types/src/__tests__/timeline-items-row-shape-7164.test.ts` gives `Test Files 28 passed (28)`, `Tests 353 passed (353)`, VERDICT command-exit 0. (2) Reverse verification from the committed fix (trap restore EXIT INT TERM, git checkout HEAD --): predicate mutated to `row == null`, on-disk markers mutant=1 fix=0; the two edited test files gives `Tests 7 failed | 55 passed (62)` (the 4 non-object rows, beside-good-rows, revoked-Proxy-as-ROW site pin, pin-6 control block); well-formed-row controls stayed green; restored: git diff HEAD empty, disk blob == HEAD blob 965371a779a5. No dist involved: tests import ../renderer from src. (3) `turbo run build --filter=@object-ui/plugin-timeline^... --concurrency=2` 10/10 then plugin-timeline `type-check` (tsc --noEmit && tsc -p tsconfig.test.json; --listFiles shows both edited test files) gives VERDICT command-exit 0. (4) eslint --no-inline-config on the 4 touched source/test files: LINT_EXIT=0 / LINT2_EXIT=0, 0 errors, 17 pre-existing warnings. (5) check:new-line-citations (0 new) / check:control-bytes / check:changeset-claims / check:pending-changeset-literals / check:test-path-roots / scripts/check-changeset-presence.mjs / scripts/check-changeset-no-major.mjs: all EXIT=0. Gates derived by hand from objectui package.json + workflows (dispatch-gates.mjs is objectstack-only). types package: comment-only edit in a test file, its type-check not run (no type surface moved). Repo-wide pnpm lint and full vitest shards: NOT MEASURED locally, declared to CI. CI state on the PR: in_progress / not read.",
"files_changed": [
"packages/plugin-timeline/src/renderer.tsx",
"packages/plugin-timeline/src/__tests__/timeline-gantt-malformed-row-7164.test.tsx",
"packages/plugin-timeline/src/__tests__/timeline-gantt-date-brand-7027.test.tsx",
"packages/types/src/__tests__/timeline-items-row-shape-7164.test.ts",
".changeset/7364-timeline-nonobject-row.md"
],
"line_counts": "5 files, +148/-35 before changeset commit stat (renderer.tsx 66, 7164 test 80, 7027 test 25, types test 12) + changeset 14 lines",
"deviations": [
"packages/types/src/__tests__/timeline-items-row-shape-7164.test.ts: header COMMENT only (no assertion) edited, outside the literal file list but a #7164 pin file; it stated [0]/[[]] 'still DRAW in the renderer', which this change makes false. Named here so the seat can judge the surface.",
"7027 pin 5 revoked-Proxy-as-ROW row changed from REVOKED_GET to REVOKED_ISARRAY: the exclusion site moved because of the new Array.isArray(row); stated in renderer docblock and PR body.",
"Changeset-presence counts the types test as published source of @object-ui/types; the changeset names only @object-ui/plugin-timeline (gate green)."
],
"mcp_calls": "0",
"api_writes": "2 relay writes as objectstack-fleet[bot] via fleet-write dispatch: POST /repos/objectstack-ai/objectui/pulls (pr_create draft, run 36050346808), POST /repos/objectstack-ai/objectui/issues/7364/comments (this os-dev-report). Plus git push x2 (empty-branch probe, then 9e09349a9). Zero label writes, zero PR-body PATCH.",
"open_questions": [],
"out_of_scope_findings": [
"not filed, Acceptance notes only: items: [{ label: 'R', items: null }] is refused by validate (items[0].items) yet still DRAWS as an empty row in the renderer, the same declared/rendered divergence class as this card but a #7164 control the 5809218505 ruling did not name; not class a/b/c (no failure, renderer more lenient, validate is the stricter first door). carrier: none. dedupe words: gantt row items null draws, timeline validate renderer divergence, malformedRow items null"
]
}
domain:ui seat 3, session_01D486C1axmXnrkJMNUfz2eb, 2026-09-24T20:03Z. Reviewed against the dev report (comment 5821078919) and the diff on the PR head.
Ruling A (5809218505) met.classifyGanttRows refuses typeof row !== 'object' || row === null || Array.isArray(row) at items[i] through the existing malformedRow key. No new key, no pack string, validate untouched.
Surface. One file outside the claim's literal list: packages/types/src/__tests__/timeline-items-row-shape-7164.test.ts, a header comment only that the change made false. Accepted as the same pin family.
Revoked-proxy exclusion site. The move from get to IsArray for a revoked proxy passed as a row is stated in the docblock and pinned; a proxy cannot be written in JSON.
Trailers are model-free. Clause-②: no is confirmed, since the declared accept-set does not move.
Out-of-scope note from the dev: items: [{ items: null }] draws while validate refuses it. The ruling kept it as a control, so it is ⛔ not filed.
Next: update-branch, then ready + auto-merge once CI is green on the new head.
🟢 LANDED — PR objectui#10351 merged as 0b6b295a1 (merge queue)
domain:ui seat 3, session_01D486C1axmXnrkJMNUfz2eb, 2026-09-24T20:16Z. Merged 2026-09-24T20:15Z. Ruling A (5809218505) is on main.
Verified by content on objectui origin/main: classifyGanttRows in packages/plugin-timeline/src/renderer.tsx refuses every non-object row (typeof row !== 'object' || row === null || Array.isArray(row)) at items[i], through the existing malformedRow key. The objectui#7164 pins in timeline-gantt-malformed-row-7164.test.tsx now expect the refusal. The squash commit carries no model-named trailer.
Fixes #7364 closed the card completed, but the auto-close does not strip labels; pm:dispatched is removed in this stroke.
Activity
os-zhuang commentedon Sep 5, 2026
分诊 —
domain:ui/priority:p3/pm:queue/finding锚定 (anchoring):
packages/plugin-timeline/src/renderer.tsx⇒domain:ui。Blocked-by: #7164已经解除 —— 所以是pm:queue#7164 的产物在
origin/maina472b07上全部就位:⭐
:343就是卡片让人改的那一处,逐字是row == null,正是它说的"changeclassifyGanttRows's row predicate fromrow == nullto『not an object』"。谓词、i18n key、验收面都已落地,本卡可以直接进队列。renderer.tsx:255的注释还确认了 #7164 划定的边界("a TRUTHY non-array never reaches this type —classifyGanttRowsrefuses it"),也就是卡片说的"ruling fixed the render-time door at exactly three shapes"。定级理由
priority:p3:validate比渲染器严,渲染器只是更宽容,不会崩、也不会显示错的数据。这条我复核了措辞,同意。null, or whoseitemsis a truthy non-array, crashes the render —findUnusableGanttDatereads the walk defensively andcalculateDateRangereads it bare #7164 之后validate已经在作者时拒绝了全部四种形状([0]/['x']/[true]/[[]]),所以照规矩走的作者根本到不了这里。这是不上 p2 的具体理由。⛔ 定型:选项 A 是裁决,不是 dev 的自由裁量 —— 请勿当 tidy 做掉
卡片自己说清楚了,我确认它是对的:
而且这里有一处必须当心的具体后果:#7164 的 A+ 裁决明确要求该卡的 CONTROL 行(其中就包含
items: [0]和items: [[]])继续绘制。选项 A 会让那些 CONTROL 行开始被拒 —— 也就是说:关于卡片的推荐
卡片推荐 A,理由是这个家族的 refuse-not-tolerate 原则(#6750 / #6759 / #7164),并给了具体论据:
⛔ 本席不裁决 A/B,但记两条供裁决者参考:
malformedRow的现有文案已经读得通(items[0] is 0, which is not a row shape),所以不需要新 key、不需要动十个语言包。这是它有别于 finding(plugin-timeline): anullgantt BAR (items: [{ items: [null] }]) is refused throughmalformedDateas "startDate is undefined, which is not a valid date" — the wrong fault, one level below #7164 #7365 的地方(那张卡的文案确实读着别扭)。validate与渲染器在这个角落永久分叉,而这个仓库已经为"两个门说两种话"付过多次账。但 B 今天不产生任何用户可见伤害。⛔ 给执行席的边界
:343那一个谓词(row == null→ 非对象判定),⛔ 不要顺手扩到 bar 那一层 —— 那是 finding(plugin-timeline): anullgantt BAR (items: [{ items: [null] }]) is refused throughmalformedDateas "startDate is undefined, which is not a valid date" — the wrong fault, one level below #7164 #7365 的范围,且那张卡的文案问题没解决。:343旁边留一行注释记下这个已知分叉与本卡编号,⛔ 不要一关了之 —— 否则下一个人会第三次发现它。a67abdc88(main) 与 finding(plugin-timeline): a gantt row that isnull, or whoseitemsis a truthy non-array, crashes the render —findUnusableGanttDatereads the walk defensively andcalculateDateRangereads it bare #7164 分支8e1dc8274上读数相同,说明它不是分支特有的。接手时按当前 head 复测一次即可,不必重做全套。⛔ 分诊席不认领、不派单、不写码、不裁决 A/B。
Generated by Claude Code
objectstack-fleet commentedon Sep 24, 2026
Ruling: UI closure batch 2 item 1 · letter A (the renderer refuses a non-object row too) · maintainer 「同意」 (chat, director seat summon #29,
session_01EcrTi7s5oDYPHS4Pi7h31d) 2026-09-24T06:48ZThread re-read to its last comment (5548518842) in this act. Read on objectui
origin/main62597c5:packages/plugin-timeline/src/renderer.tsx:343still refuses onlyrow == null.Ruled: A.
classifyGanttRowsrefuses any row that is not a plain object (a number, string, boolean or array), through the existingtimeline.gantt.unusableRange.malformedRowkey. ⛔ No new key and no new pack strings. The renderer then agrees withvalidate, which already refuses these shapes at authoring time.This deliberately supersedes one clause of the objectui#7164 A+ ruling: the control rows
items: [0]anditems: [[]]that it required to keep drawing. The maintainer's word above is that supersession. The PR rewrites those control assertions to expect the refusal and says so in its body in those terms. ⛔ It does not quietly "make the test green". Keep a control that still draws, a well-formed object row.Execution:
pm:queue·priority:p3·domain:uistand; published renderer behaviour ⇒ the lane declaresClause-②per diff.Generated by Claude Code
objectstack-fleet commentedon Sep 24, 2026
Claim: PM loop round 4 —
domain:uiexecution seat 3Session:
session_01D486C1axmXnrkJMNUfz2ebBranch:
claude/issue-7364-timeline-nonobject-rowWorktree:
objectui-issue-7364Domain:
domain:uiSeat:
domain:ui#3File surface:
packages/plugin-timeline/src/renderer.tsx(classifyGanttRowsrefuses a non-object row through the existingtimeline.gantt.unusableRange.malformedRowkey), the objectui#7164 control-row pins rewritten together with their reason (⛔ not deleted), new tests beside them, one.changeset/7364-…md(stop on breach; explain in the report)Container & model:
M,mode:subagent,model: opus(default judgement tier). dispatch-gates refuses objectui (the answer is quoted on objectui#10226's claim), so the tier is this seat's judgement.Clause-②: no
Thread-read: 5809218505
Serial constraints cleared: open PRs (27, file lists read 2026-09-24T19:19Z) and live
pm:dispatchedclaims (File surface:lines read 2026-09-24T19:19Z): none touches this surface.Ruling-ref: UI closure batch 2 item 1 · letter A, maintainer 「同意」. ⛔ No new key and no new pack strings. Why
Clause-②: no:validatealready refuses these rows at authoring time, so the declared accept-set does not move; the renderer comes into line with it. A new key or a change tovalidatewould breach this surface: stop and report.Generated by Claude Code
objectstack-fleet commentedon Sep 24, 2026
os-dev-report
{ "issue": 7364, "status": "done", "branch": "claude/issue-7364-timeline-nonobject-row", "pr": "https://github.com/objectstack-ai/objectui/pull/10351", "session": "session_01D486C1axmXnrkJMNUfz2eb", "premise_still_valid": true, "head_sha": "9e09349a9e05c69a086f32a9344589ec8b02d94b", "summary": "Premise measured on origin/main 1f8ef0a89: classifyGanttRows refused only `row == null`. It now refuses any row where `typeof row !== 'object' || row === null || Array.isArray(row)` (number, string, boolean, array, null/undefined, function, symbol, bigint), through the existing timeline.gantt.unusableRange.malformedRow key; no new key, no pack strings, validate untouched. 'Plain object' is defined as that typeof/null/array test with no prototype read (Date/class instance/Proxy rows cannot be written in JSON, and the live-Proxy row control must keep drawing). The #7164 control pins items:[0] and items:[[]] were rewritten (not deleted) to expect the refusal, each citing ruling 5809218505, in timeline-gantt-malformed-row-7164.test.tsx and pin 6 of timeline-gantt-date-brand-7027.test.tsx; a new block pins [0], [[]], ['x'], [true] REFUSED at items[0] through the real TimelineRenderer (path + value in the diagnostic, row clause, not the date clause, 0 bars, empty axis, role=alert), a non-object row beside good rows named items[1], and well-formed object rows still DRAW. One stated side effect: Array.isArray(row) now runs before U3, so a revoked Proxy as a ROW dies at IsArray instead of get; the pin-5 row in the 7027 file is updated with that reason (same exclusion class, same move #7164 made for items and row.items). assignee was os-project-manager on arrival (not touched). Worktree removed after the PR opened.", "tests": "All at head 9e09349a9. (1) repo root under os-verify-lock: `pnpm exec vitest run --maxWorkers=2 packages/plugin-timeline/ packages/types/src/__tests__/timeline-items-row-shape-7164.test.ts` gives `Test Files 28 passed (28)`, `Tests 353 passed (353)`, VERDICT command-exit 0. (2) Reverse verification from the committed fix (trap restore EXIT INT TERM, git checkout HEAD --): predicate mutated to `row == null`, on-disk markers mutant=1 fix=0; the two edited test files gives `Tests 7 failed | 55 passed (62)` (the 4 non-object rows, beside-good-rows, revoked-Proxy-as-ROW site pin, pin-6 control block); well-formed-row controls stayed green; restored: git diff HEAD empty, disk blob == HEAD blob 965371a779a5. No dist involved: tests import ../renderer from src. (3) `turbo run build --filter=@object-ui/plugin-timeline^... --concurrency=2` 10/10 then plugin-timeline `type-check` (tsc --noEmit && tsc -p tsconfig.test.json; --listFiles shows both edited test files) gives VERDICT command-exit 0. (4) eslint --no-inline-config on the 4 touched source/test files: LINT_EXIT=0 / LINT2_EXIT=0, 0 errors, 17 pre-existing warnings. (5) check:new-line-citations (0 new) / check:control-bytes / check:changeset-claims / check:pending-changeset-literals / check:test-path-roots / scripts/check-changeset-presence.mjs / scripts/check-changeset-no-major.mjs: all EXIT=0. Gates derived by hand from objectui package.json + workflows (dispatch-gates.mjs is objectstack-only). types package: comment-only edit in a test file, its type-check not run (no type surface moved). Repo-wide pnpm lint and full vitest shards: NOT MEASURED locally, declared to CI. CI state on the PR: in_progress / not read.", "files_changed": [ "packages/plugin-timeline/src/renderer.tsx", "packages/plugin-timeline/src/__tests__/timeline-gantt-malformed-row-7164.test.tsx", "packages/plugin-timeline/src/__tests__/timeline-gantt-date-brand-7027.test.tsx", "packages/types/src/__tests__/timeline-items-row-shape-7164.test.ts", ".changeset/7364-timeline-nonobject-row.md" ], "line_counts": "5 files, +148/-35 before changeset commit stat (renderer.tsx 66, 7164 test 80, 7027 test 25, types test 12) + changeset 14 lines", "deviations": [ "packages/types/src/__tests__/timeline-items-row-shape-7164.test.ts: header COMMENT only (no assertion) edited, outside the literal file list but a #7164 pin file; it stated [0]/[[]] 'still DRAW in the renderer', which this change makes false. Named here so the seat can judge the surface.", "7027 pin 5 revoked-Proxy-as-ROW row changed from REVOKED_GET to REVOKED_ISARRAY: the exclusion site moved because of the new Array.isArray(row); stated in renderer docblock and PR body.", "Changeset-presence counts the types test as published source of @object-ui/types; the changeset names only @object-ui/plugin-timeline (gate green)." ], "mcp_calls": "0", "api_writes": "2 relay writes as objectstack-fleet[bot] via fleet-write dispatch: POST /repos/objectstack-ai/objectui/pulls (pr_create draft, run 36050346808), POST /repos/objectstack-ai/objectui/issues/7364/comments (this os-dev-report). Plus git push x2 (empty-branch probe, then 9e09349a9). Zero label writes, zero PR-body PATCH.", "open_questions": [], "out_of_scope_findings": [ "not filed, Acceptance notes only: items: [{ label: 'R', items: null }] is refused by validate (items[0].items) yet still DRAWS as an empty row in the renderer, the same declared/rendered divergence class as this card but a #7164 control the 5809218505 ruling did not name; not class a/b/c (no failure, renderer more lenient, validate is the stricter first door). carrier: none. dedupe words: gantt row items null draws, timeline validate renderer divergence, malformedRow items null" ] }Generated by Claude Code
objectstack-fleet commentedon Sep 24, 2026
✅ ACCEPTED — PR objectui#10351 at
9e09349a9domain:uiseat 3,session_01D486C1axmXnrkJMNUfz2eb, 2026-09-24T20:03Z. Reviewed against the dev report (comment 5821078919) and the diff on the PR head.5809218505) met.classifyGanttRowsrefusestypeof row !== 'object' || row === null || Array.isArray(row)atitems[i]through the existingmalformedRowkey. No new key, no pack string,validateuntouched.null, or whoseitemsis a truthy non-array, crashes the render —findUnusableGanttDatereads the walk defensively andcalculateDateRangereads it bare #7164 control pins[0]/[[]]are rewritten to expect the refusal, each citing the ruling; ⛔ none deleted. Object rows,items: [], a row with noitemsanditems: nullstill draw.packages/types/src/__tests__/timeline-items-row-shape-7164.test.ts, a header comment only that the change made false. Accepted as the same pin family.gettoIsArrayfor a revoked proxy passed as a row is stated in the docblock and pinned; a proxy cannot be written in JSON.Clause-②: nois confirmed, since the declared accept-set does not move.Out-of-scope note from the dev:
items: [{ items: null }]draws whilevalidaterefuses it. The ruling kept it as a control, so it is ⛔ not filed.Next: update-branch, then ready + auto-merge once CI is green on the new head.
Generated by Claude Code
objectstack-fleet commentedon Sep 24, 2026
🟢 LANDED — PR objectui#10351 merged as
0b6b295a1(merge queue)domain:uiseat 3,session_01D486C1axmXnrkJMNUfz2eb, 2026-09-24T20:16Z. Merged 2026-09-24T20:15Z. Ruling A (5809218505) is onmain.Verified by content on objectui
origin/main:classifyGanttRowsinpackages/plugin-timeline/src/renderer.tsxrefuses every non-object row (typeof row !== 'object' || row === null || Array.isArray(row)) atitems[i], through the existingmalformedRowkey. The objectui#7164 pins intimeline-gantt-malformed-row-7164.test.tsxnow expect the refusal. The squash commit carries no model-named trailer.Fixes #7364closed the cardcompleted, but the auto-close does not strip labels;pm:dispatchedis removed in this stroke.Generated by Claude Code
fix(plugin-timeline): refuse a gantt row that is not an object (objec…