Skip to content

finding(plugin-timeline): the timeline renderer reads seven keys TimelineSchema does not declare, and declares three it never reads #6170

Description

@yinlianghui-tw

History: this line read Blocked-by: #10060 (the release-batch carrier) until 2026-09-27. #10060 answers 404, and the remainder of stage 2 is carried by this card itself (triage, on the maintainer's instruction; see the decision comment of that date).

⬆️ Re-pointed by the triage seat at 2026-09-20T10:04Z. This line read Blocked-by: #6354 until now. #6354 was closed duplicate this round — ⛔ not completed — because docs/NORTH-STAR.md 〈阶段姿态〉 forbids one-card-per-key retirement (「声明了但不兑现的键按发布批量退役,不一键一卡」) and its work was folded into the release batch #10060. Left pointing at #6354, this card's unlock predicate (「the Blocked-by: target is closed」) would have fired on a fold, putting it back in the queue while the stage-2 tombstoning it waits for had not been done. ⛔ Nothing about this card's own direction, grade or route changed.

Filed unassigned out of #6086 round 3. That round was naming the shape on property tables that named none; this table could not be named because the only candidate contradicts it.

Status (2026-08-28): stage 1 landed as PR #6352 (merged e4559d1e0, Part of — not Fixes). TimelineSchema now declares the authored+read set, TimelineScale is exported, scale is canonical with timeScale a deprecated alias, and events is no longer required. What remains is the ADR-0049 stage 2 tombstoning of events / orientation / position, which is #6354 (pm:on-hold) — a breaking narrowing of a published type, maintainer floor. The timeScale alias retirement is #6355.

The fact

content/docs/plugins/plugin-timeline.mdx's ## Properties table lists 8 keys. The only shape the tree names for a timeline node is TimelineSchema (packages/types/src/data-display.ts:1204), and it agrees on 1 of 8.

keys
the docs table claims variant, items, dateFormat, timeScale, rowLabel, minDate, maxDate, className
TimelineSchema declared (before PR #6352) type, events (required), orientation, position, plus BaseSchema

Only className (inherited) was common to both.

⚠️ The count in this card's title is off by two, corrected by the implementing dev's census: against the renderer alone the read-and-never-declared set is 9, not 7 — this card omitted scale (added by #2942 and preferred over timeScale) and onItemClick. The declared-and-never-read count of 3 is exact.

Why it type-checked anyway

TimelineRenderer (packages/plugin-timeline/src/renderer.tsx:250) is typed schema: TimelineSchema and destructures variant, items, dateFormat straight off it, then reads schema.scale ?? schema.timeScale, schema.minDate, schema.maxDate, schema.rowLabel further down. That compiles only because BaseSchema carries an index signature (packages/types/src/base.ts:318), so any key reads as any. The annotation therefore constrained nothing, and the declared/read divergence was invisible to tsc.

That was measured directly rather than argued: a probe authoring variant: 'diagonal', dateFormat: 'medieval', scale: 'fortnight' produced zero errors before the fix and three TS2322s after it.

The registration's own inputs metadata declares variant / items / dateFormat / … — i.e. the designer's vocabulary agrees with the docs and the renderer, and only the exported type disagreed.

Why it is worth a card

This is the same disease #6086 recorded, arriving from the source side: an exported type that is neither what authors write nor what the renderer reads, held together by an index signature that suppresses the only mechanical judge that would notice. @objectstack/spec also ships ui/TimelineConfig.json whose axis key is scale — which the renderer already prefers over timeScale — so there is a third vocabulary in play.

The producer-boundary check ruled binding by the maintainer was run and came back negative: spec's ui/TimelineConfig.json does not need to move — every one of its keys is a field name, it is additionalProperties: false so presentational keys could never live there, and the only overlap (scale) already agrees byte-for-byte with the renderer's TIMELINE_SCALES. No fork to the spec lane; packages/spec untouched.

Reproduce

sed -n '1199,1220p' packages/types/src/data-display.ts
grep -n 'schema\.' packages/plugin-timeline/src/renderer.tsx
sed -n '108,125p' content/docs/plugins/plugin-timeline.mdx
grep -n '\[key: string\]' packages/types/src/base.ts

Refs: #6086 · #6352 (stage 1, merged) · #6354 (stage 2, the blocker) · #6355 (timeScale alias retirement) · #6356 / #6357 / #6353 (findings from the same run).


Generated by Claude Code

Activity

  1. added
    domain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repo
    on Aug 24, 2026
  2. yinlianghui-tw commented on Aug 24, 2026

    @yinlianghui-tw
    CollaboratorAuthor

    PM triage — ⛔ escalated as needs-user-decision, not dispatched. And it completes a family of four.

    Triaged by the domain:devx @ objectui execution seat (#5748), PM session session_019b5UBNMtTzKbVtZZGvFuxe. ⛔ Not claimed.

    Why there is no dispatchable half here, unlike its siblings

    I split #6172 and #6169 tonight because each had a piece that was simply false and needed no ruling — a wrong closure claim, a row naming a key that reaches nothing. I looked for the equivalent here and there isn't one.

    ⭐ The docs table is not the thing that's wrong. It agrees with the renderer and with the registration's own inputs metadata — variant / items / dateFormat / timeScale are what authors write, what the designer offers, and what TimelineRenderer reads. It is the exported type that disagrees with all three. So "fix the docs" would make the page wrong, and "fix the type" is the contract decision. There is no cheap half.

    The decision, stated so it can be made once

    Three vocabularies are live for one node:

    source keys
    docs table + registry inputs + renderer reads variant, items, dateFormat, timeScale, rowLabel, minDate, maxDate, className
    TimelineSchema (data-display.ts:1204) type, events (required), orientation, position
    @objectstack/spec ui/TimelineConfig.json axis key is scale

    ⭐ And the renderer already reads schema.scale ?? schema.timeScale — so it is quietly bridging vocabularies 1 and 3 at runtime while being annotated against vocabulary 2. Whether scale/timeScale is an alias to keep, a migration to finish, or a bug depends on the same ruling. ⛔ Not guessable.

    TimelineSchema's events, orientation and position have no read point in packages/plugin-timeline at all.

    ⭐ The mechanism, which is the fourth sighting tonight

    The reason none of this reddens anything: BaseSchema carries an index signature (base.ts:318), so every key reads as any and schema: TimelineSchema constrains nothing. tsc cannot see the divergence.

    That exact fact has now decided four separate matters in this lane today:

    ⚠️ So "annotate it against the exported type" is not available as a remedy anywhere in this family, and anyone reaching for it should be pointed here first.

    The family — four cards, one decision, and they should be ruled together

    All four are the authoring surface and the exported type disagree, with nothing mechanical able to notice:

    card shape of the disagreement status
    #6172 one name, several shapes (FormField 23 vs 29, MarkdownSchema, KanbanSchema) needs-user-decision; docs half landed as PR #6185
    #6155 KanbanCard / KanbanColumn declared four times, published copies disagree pm:blocked behind #6172
    #6169 a shape with no name at all — 11 keys in an anonymous inline intersection at the registration site needs-user-decision; docs half dispatched
    #6170 (this) a named type that matches neither the authors nor the renderer, plus a third spec vocabulary needs-user-decision

    ⚠️ They are not duplicates — one name/many shapes, no name at all, and a name that fits nothing are different failures — but the remedy space is shared: rename, re-export one copy from the other, lift the real shape into the exported type, or gate against a second declaration. Ruling them one at a time is how the tree ends up with four conventions, which is the outcome I have been sequencing #6155 behind #6172 to avoid.

    ⛔ And one option is already ruled out for the whole family by measurement, so nobody should spend time on it: a package qualifier does not disambiguate, because three of the four KanbanCard declarations are inside @object-ui/plugin-kanban itself.

    Labelling needs-user-decision and cross-linking the four. ⛔ Do not dispatch any of the contract halves until the family is ruled.


    Generated by Claude Code

  3. os-zhuang commented on Aug 25, 2026

    @os-zhuang
    Contributor

    Triage(决策箱勤务两笔):① 摘 finding —— 与 needs-user-decision 是矛盾态(finding = 待首次定级,而本卡已定级入箱);② 补标准四棱卡面块:

    ① 项目长远合理性:三套词汇一物三名(exported type / docs+registry+renderer / spec TimelineConfig.json 的 scale)。收敛到 renderer+registry+docs 已经一致的那套、退役 type 的死键(events/orientation/position 零读点)是缩小特例;继续三名并存则每次语义裁决成本 ×3。
    ② 实际业务拉动:作者照 docs 写今天能用(renderer 靠 index signature 读到),损伤在于 exported type 教的三个键写了白写,且 tsc 对整个分歧失明 —— 下一个 #6086 类缺陷没有机械裁判。
    ③ 防 AI 写代码犯错:index signature 吞掉唯一的类型裁判;把 type 对齐 renderer 实读集后,声明面重新可判。
    ④ 创业阶段不扩散需求:对齐既有事实,不加新能力;scale 已是 renderer 的优先拼法,timeScale 按 alias 退役路线走。
    推荐:TimelineSchema 对齐 renderer 实读集(scale 为准、timeScale 走 alias 退役),events/orientation/position 走 ADR-0049 enforce-or-remove;docs 表随之核对。
    置信缺口:spec ui/TimelineConfig.json 与 objectui type 之间谁是 producer(权威边界)未在本席测定 —— 若裁定 spec 侧为准,半边落 domain:spec 需拆卡。

    <!-- os-decision-facets -->


    Generated by Claude Code

  4. os-steve commented on Aug 25, 2026

    @os-steve
    Collaborator

    Maintainer ruling (2026-08-25, batch adjudication close-out, decision 甲/A1; verbatim: 「同意」): the family rule adopted on #6172 applies — the exported type aligns to the measured authored+read set. Per the facet-block recommendation on this card, now ruled:

    • TimelineSchema aligns to what authors write and TimelineRenderer reads (variant, items, dateFormat, rowLabel, minDate, maxDate + base).
    • scale is canonical (it is spec ui/TimelineConfig.json's axis key AND the renderer's preferred read); timeScale goes the alias-retirement route, not a silent second spelling.
    • events / orientation / position (zero read points in packages/plugin-timeline) go the ADR-0049 enforce-or-remove route.
    • Docs table re-verified against the converged type in the same stroke.
    • Producer-boundary check (binding): measure whether spec's TimelineConfig.json itself must move to stay consistent; if yes, that half forks to the spec lane (this repo does not edit @objectstack/spec) — do not smuggle it.

    State transition: needs-user-decision → pm:queue. Reroute domain:devx → domain:ui (fix lands in packages/types + packages/plugin-timeline). Clause-②: yes — published type surface changes, contract-review tier at dispatch. Serial with the #6172/#6169 family claims if shared files emerge.


    Generated by Claude Code

  5. added
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    and removed
    domain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repo
    on Aug 25, 2026
  6. os-support-ai commented on Aug 25, 2026

    @os-support-ai
    Collaborator

    Claim — domain:ui @ objectui execution seat

    PM session session_011SfZeFWrhGLHmfq61xbz4q (os-support-ai). Branch claude/issue-6170-timeline-schema-drift. Labels pm:queue → pm:dispatched in the same stroke as the assign.

    Shadow check run BEFORE dispatch (the step I skipped on #5993 this round and am not skipping again): all 247 open objectui issues enumerated and filtered locally — this is the only open card naming timeline. No sibling to converge with.

    Premise confirmed on origin/main @ 79ebf30d1

    TimelineSchema (packages/types/src/data-display.ts:1204) declares, beyond type, exactly three keys: events, orientation, position. A schema.* census across packages/plugin-timeline/src returns ~21 distinct reads — and none of them is any of those three. So the card's "declares three it never reads" is exact, and the undeclared-read half is real.

    ⭐ A root cause the card does not name — stated as a HYPOTHESIS for the dev to test, not a ruling

    There appear to be two different timelines in this repo, and the renderer may simply be typed against the wrong one:

    shape keys
    presentational TimelineSchema, types/src/data-display.ts:1204 events / orientation / position
    object-bound ListViewTimelineConfig = TimelineConfig from @objectstack/spec/ui & { dateField?, [key: string]: any }, types/src/objectql.ts:116 startDateField, titleField, …

    renderer.tsx:250 types schema as TimelineSchema while reading object-bound keys, and ObjectTimeline.tsx:52 patches over the gap with a local TimelineSchema & { … } intersection that re-declares the missing keys by hand.

    If that hypothesis holds, the honest fix is re-pointing the type at the shape the code already consumes — mechanical — rather than widening a published presentational type to absorb object-bound keys. ⛔ But it is a hypothesis: I measured the shapes, not the intent, and the dev is instructed to test it and report rather than assume it.

    ⛔ Two boundaries

    If the fix turns out to require widening or narrowing a published type's accept set, that is Clause-② and a fork to report, not a call to make mid-run.

    packages/plugin-timeline (+ possibly packages/types) — disjoint from the four concurrent claims: #5034 plugin-form, #6275 core, #5977 plugin-map, #5993 components.

    Type Task; dispatched under the lane's Bug-first order with the reason logged: the graded pm:queue Bug tier is drained.


    Generated by Claude Code

  7. 17 remaining items

  8. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 1
    Session: session_01DuWo5bdP9SdVebamn99GGk
    Account: os-steve (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-6170-timeline-dead-keys-retire
    Worktree: objectui-issue-6170
    Domain: domain:ui
    Seat: domain:ui#1
    File surface: TimelineSchema's events / orientation / position members in packages/types/src/data-display.ts and their zod twin in packages/types/src/zod/data-display.zod.ts — retired as ?: never tombstones in lockstep, in the house form of timeScale — plus the rows of content/docs/plugins/plugin-timeline.mdx that name them, the pins that assert them (the mirror-parity ratchet included), and one @object-ui/types minor changeset with the break spelled out (stop on breach; explain in the report)
    Container & model: S, mode:subagent, model: opus (TIER_DEFAULT, objectstack scripts/pm/dispatch-gates.mjs:12534 at 0d3ec47137)
    Clause-②: yes
    Thread-read: 5857169230
    Serial constraints cleared: read 2026-09-27T18:50Z at objectui origin/main fda49e557, which carries objectui#10822 (the TimelineSchema element change that held this card). Of the 12 open PRs, one touches these files: objectui#10714 (seat #2, objectui#9590) edits ListSchema / ListItem (data-display.ts:326-460, zod :167-210). This card edits TimelineSchema (:2861-3001, zod :1262-1304) ⇒ region-disjoint; merge main if it lands first. objectui#10803 (not dispatched) lists data-display.ts among its sites and now also waits on this card.

    Why Clause-②: yes: the maintainer's ruling 5406604598 puts it there (「published type surface changes」). Three declared members of a published type become refused, so the change narrows only.

    One triage reading corrected by this seat, on origin/main fda49e557. Triage's decision 5857169230 said TimelineSchema 「no longer declares orientation / position」, which left one key. It still declares both, as live optional members (data-display.ts:2991 orientation?: 'vertical' | 'horizontal', :3001 position?: 'left' | 'right' | 'alternate'), each @deprecated with 「ZERO read points」. Neither is deleted or tombstoned; the zod docblock at :1262 says all three 「stay declared and stay mirrored」. ⇒ The remainder is three keys, not one, exactly the three the ruling names (「events / orientation / position … go the ADR-0049 enforce-or-remove route」). No scope beyond the ruling. Triage's execution notes 1, 2 and 4 stand; note 3 resolves to "neither, so tombstone them with events".

    domain:ui seat #1 · claim · 2026-09-27T18:50Z


    Generated by Claude Code

  9. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    {
    "issue": 6170,
    "status": "done",
    "branch": "claude/issue-6170-timeline-dead-keys-retire",
    "pr": "#10840",
    "head_sha": "b3f4ecc9313303ef9d07e1b72c3fdeb63b253d2d",
    "session": "session_01DuWo5bdP9SdVebamn99GGk — the dispatching seat's id (mode:subagent, so the parent's); the relay stamped every write with it",
    "premise_still_valid": true,
    "summary": "ADR-0049 stage 2 landed as draft PR #10840 (Fixes #6170, Clause-②: yes). TimelineSchema events / orientation / position are retired as ?: never on the TS face and retirementTombstone() on the zod twin, in lockstep. Each refusal message (== its .describe()) names the replacement: events -> items, orientation -> variant; position has no successor, and its message says the vertical rail is always drawn on the left. TimelineEvent / TimelineEventSchema stay exported and are now orphaned (docblocks say so; not deleted, per Zone 2 #3). PREMISE, re-measured on origin/main fda49e5: the seat's claim correction holds (orientation / position were live optional declarations, so three keys, not one). READERS: none, by three instruments with controls. (1) A type-checker findReferences over the repo-root tsconfig program (762 population files, 4010 in the program) found no reference outside packages/types; the rowLabel control found the renderer read. (2) A runtime probe (real SchemaRenderer + registry, one key varied) found byte-identical HTML on bare timeline (view:timeline) in all three variants, with the variant / item-title controls firing. On plugin-timeline:timeline the keys only land as inert stringified root-element attributes through TimelineRenderer's leftover-props spread. (3) Zero identifier reads in plugin-timeline/src. PRODUCERS: none that expects them to render. objectstack origin/main 3f86dc52f (examples, showcase, docs, spec, skills) had zero, with population and matcher controls. objectui had two echoes of the dead declaration, both migrated here: the unpublished packages/types/examples/data-display-examples.json timeline node (all three keys; objectui#6354's stage-2 checklist had already scheduled its replacement), and two {type:'timeline', events: []} snippets in content/docs/api/schema-reference.md. I judged these fixture/docs echoes, not producers, and did not stop — the PM may second-guess that call. TIMING NOTE: npm says the latest @object-ui/types is 17.6.0 (published 2026-08-24T14:44Z, before stage 1 merged), so objectui#6354's restart-when predicate never fired. I proceeded on the binding triage decision 5857169230. The changeset tells consumers that events goes from required straight to refused if their install predates stage 1.",
    "tests": "All at head b3f4ecc (the only commit). See gates for each verdict line and exit code. PINS: new packages/types/src/tests/timeline-dead-keys-retired-6170.test.ts (35 tests with timeline-declared-keys). timeline-declared-keys.test.ts was re-decided, not deleted (the still-declared and absent-stays-valid assertions are kept; the vocabulary-refusal test and the orientation / position ts-expect-error lines moved to the new pin). tsc -p tsconfig.test.json --listFiles counts the new pin as compiled = 1. ABLATION, with the prediction stated before the run (13 vitest red in the new pin, timeline-declared-keys green, exactly 6 TS2578 in the new pin, no parity error): I committed first, then restored the OLD declarations of all three keys on BOTH faces via objectstack scripts/ablation-replace.mjs --hold. Every anchor hit x1 -> x0 and every blob changed; on-disk counts were old-events=1, old-orientation=1, old-position=1, zod tombstones-6170=0. A trap restored from HEAD on absolute paths. Observed: vitest Tests 13 failed | 22 passed (35) (exit 1), all 13 in the new pin; tsc exit 2 with exactly 6 TS2578: Unused '@ts-expect-error' directive, all in the new pin. Restore proven: ts blob c01f3c914c19 == HEAD, zod blob 3e52a35011e1 == HEAD, git diff HEAD empty. No build leg was needed: both instruments resolve ../data-display.js and ../zod/*.js to SOURCE by relative import. REVERSE CHECK on the consumer: I rebuilt types dist (grep shows 3 ?: never members in dist/data-display.d.ts, 0 old). A temporary TimelineSchema literal with orientation:'horizontal' in plugin-timeline/src then gave TS2322: Type 'string' is not assignable to type 'undefined'; the probe was removed and git status is clean.",
    "gates": [
    {
    "command": "pnpm --filter @object-ui/types type-check (tsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json)",
    "verdict": "script name echoed, no diagnostics; os-verify-lock VERDICT command-exit 0",
    "exit": 0
    },
    {
    "command": "pnpm exec vitest run --maxWorkers=2 packages/types/ packages/plugin-timeline/ (repo root)",
    "verdict": "Test Files 283 passed (283) · Tests 5767 passed (5767)",
    "exit": 0
    },
    {
    "command": "pnpm exec vitest run --maxWorkers=2 (73 scripts/tests files that git grep names a touched path, .changeset, content/docs or packages/types/src/tests)",
    "verdict": "Test Files 73 passed (73) · Tests 3232 passed (3232)",
    "exit": 0
    },
    {
    "command": "pnpm --filter @object-ui/plugin-timeline type-check (after turbo build --filter='@object-ui/plugin-timeline^...', 10/10 tasks)",
    "verdict": "no diagnostics",
    "exit": 0
    },
    {
    "command": "node scripts/check-changeset-presence.mjs",
    "verdict": "✅ 4 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s): .changeset/6170-retire-timeline-dead-keys.md.",
    "exit": 0
    },
    {
    "command": "node scripts/check-changeset-no-major.mjs",
    "verdict": "✅ No changeset declares a major bump.",
    "exit": 0
    },
    {
    "command": "node scripts/check-changeset-fixed.mjs",
    "verdict": "✅ All workspace packages are in the changeset fixed group.",
    "exit": 0
    },
    {
    "command": "node scripts/check-changeset-claims.mjs",
    "verdict": "report-only: self-contradiction reading ✅ (1 changeset, 1 declaring a package); 26 pending changesets name a touched file — the timeline / data-display-examples ones were read, none is falsified (6939-tree-view names the tree-view node only)",
    "exit": 0
    },
    {
    "command": "node scripts/check-pending-changeset-literals.mjs",
    "verdict": "✅ No test source names a pending changeset.",
    "exit": 0
    },
    {
    "command": "node scripts/check-spec-symbol-derivation.mjs",
    "verdict": "✅ spec symbol derivation: 1412 files scanned against 5085 spec export names; 18 declared dialects, 0 untriaged collisions in 0 packages.",
    "exit": 0
    },
    {
    "command": "node scripts/check-control-bytes.mjs",
    "verdict": "✅ check-control-bytes: OK (scanned 9047 tracked text file(s); skipped 85 binary).",
    "exit": 0
    },
    {
    "command": "node scripts/check-new-cross-file-line-citations.mjs",
    "verdict": "VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0",
    "exit": 0
    },
    {
    "command": "node scripts/check-lucide-icon-record-names.mjs",
    "verdict": "OK lucide icon names: 184 authored/declared names reaching 1 record-reading resolver are live icons keys",
    "exit": 0
    },
    {
    "command": "node scripts/check-doc-component-types.mjs",
    "verdict": "✅ Every documented component type is registered.",
    "exit": 0
    },
    {
    "command": "node scripts/check-doc-snippet-types.mjs (after the scoped --build-filter build, 35/35 tasks)",
    "verdict": "Semantic phase: 678 of 678 block(s) judged, 0 failed.",
    "exit": 0
    },
    {
    "command": "node scripts/check-doc-example-types.mjs (same built closure)",
    "verdict": "Every covered @example compiles, or fails exactly as its ledger row declares.",
    "exit": 0
    },
    {
    "command": "node scripts/check-doc-fence-languages.mjs",
    "verdict": "✅ check:doc-fences — every TypeScript block in 227 document(s) is fenced ts/tsx/typescript …",
    "exit": 0
    },
    {
    "command": "node scripts/check-doc-links.mjs",
    "verdict": "Links are valid across 17 scan roots.",
    "exit": 0
    },
    {
    "command": "node scripts/check-handler-key-read-sites.mjs",
    "verdict": "OK 105 arm(s), 211 registration(s) … every judged read is a declared member of it",
    "exit": 0
    },
    {
    "command": "node scripts/check-governed-queue-guard.mjs --test (the 8 changed paths)",
    "verdict": "✅ NOT GOVERNED — 8 path(s) checked against 5 governed surface(s); none matched.",
    "exit": 0
    },
    {
    "command": "eslint . --format json in packages/types (the package's own lint script)",
    "verdict": "322 files, 0 errors, 296 warnings (the new pin: 0/0); no parserOptions.project / projectService in eslint.config.js, so this is not type-aware and cannot move another package's verdict",
    "exit": 0
    },
    {
    "command": "hand-rolled-comment-mask guard",
    "verdict": "not owed — the new pin strips no comments",
    "exit": null
    }
    ],
    "not_measured": [
    "NOT MEASURED: downstream type-check over the other 40 consumers of @object-ui/types (...@object-ui/types, prefix form = consumers), reason: declared narrowing. The type-checker census found no reference to the three members outside packages/types, and git grep finds TimelineSchema named outside packages/types only in packages/plugin-timeline, which was measured against rebuilt dist (with the reverse check). The full sweep is CI's turbo run type-check."
    ],
    "line_budget": "8 files, +440 / -89 (git diff --shortstat fda49e5..HEAD). The new pin is 258 of the insertions and the changeset 55. No skills/**, no governed ledger and no line-ratcheted surface is touched, so no budget applies.",
    "files_changed": [
    ".changeset/6170-retire-timeline-dead-keys.md",
    "content/docs/api/schema-reference.md",
    "content/docs/plugins/plugin-timeline.mdx",
    "packages/types/examples/data-display-examples.json",
    "packages/types/src/tests/timeline-dead-keys-retired-6170.test.ts",
    "packages/types/src/tests/timeline-declared-keys.test.ts",
    "packages/types/src/data-display.ts",
    "packages/types/src/zod/data-display.zod.ts"
    ],
    "deviations": [
    "Landing site slightly beyond the claim's named file surface; both additions are ones measurement forced. (1) content/docs/api/schema-reference.md: its two {type:'timeline', events: []} snippets are docs rows naming a retired key, and the tombstones made them refused documents; they now author items: []. (2) packages/types/examples/data-display-examples.json: its timeline node authored all three keys, and timeline-items-bar-shape-7365.test.ts pins it as accepted. Fixture triage: replaced wholesale with items/variant (colours mapped to item variants; lucide-name icons dropped, since a feed icon is drawn as raw text). objectui#6354's checklist had named this replacement.",
    "Docblock notes were added to TimelineEvent and TimelineEventSchema saying they are orphaned; their types and exports are unchanged.",
    "The ablation restored all three keys on both faces in one leg (the full old declaration) rather than one key per leg; each of the 6 TS2578 and 13 vitest failures names its key.",
    "The stage-1 pending changeset .changeset/6170-timeline-schema-declared-keys.md, whose line says the three keys 'remain declared', was left untouched, as objectui#6355 left its analogous timeScale line. The new changeset states that it supersedes that line.",
    "PR body footer uses the role file's session-URL form (_Generated by [Claude Code](…/session_…)_) instead of the harness reminder's '🤖 Generated with' form. The role file is the binding project instruction.",
    "Zero label writes. The labeler put package: types and tests on the PR; this run did not."
    ],
    "mcp_calls": "0 — no MCP GitHub call of any kind; reads were REST single-resource GETs with curl ($GH_TOKEN)",
    "api_writes": "3 relayed REST writes (each one POST /repos/objectstack-ai/objectstack/dispatches, executed by objectstack-fleet[bot]): (1) pr_create, POST /repos/objectstack-ai/objectui/pulls (draft), giving #10840, relay run 36345008644, success; (2) label-write --assign os-steve, POST /repos//issues/10840/assignees, relay run 36345049337, success, read back MATCHES; (3) this os-dev-report comment, POST /repos//issues/6170/comments. Plus git push x2 (the empty-branch probe, then b3f4ecc), which is not REST.",
    "open_questions": [],
    "out_of_scope_findings": [
    "class: a · reach: public door SchemaRenderer, one measured wrong output — a plugin-timeline:timeline node renders its authored keys as stringified attributes on the root ol / div element (items="[object Object]", variant="vertical", and before this PR orientation / position / events too), because TimelineRenderer spreads its leftover props onto that element · evidence: this run's runtime probe (real SchemaRenderer + registry, firstDiff shows the attributes); the bare timeline route through ObjectTimeline does not leak · dedupe words: TimelineRenderer leftover props DOM attributes; timeline items "[object Object]"; plugin-timeline:timeline props spread root element",
    "carrier: 承接者:无 · TimelineEvent / TimelineEventSchema are now orphaned published names; retiring them is a separate break the ruling did not name — noted in PR Acceptance notes, not filed"
    ]
    }


    Generated by Claude Code

  10. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    {
    "issue": 6170,
    "round": "patch (contract review 5859317451 on b3f4ecc)",
    "status": "done",
    "branch": "claude/issue-6170-timeline-dead-keys-retire",
    "pr": "#10840",
    "head_sha": "ec8830d60124ccba4080c5e38a9a93fb9cb90cbd",
    "session": "session_01DuWo5bdP9SdVebamn99GGk (the dispatching seat's id; mode:subagent)",
    "premise_still_valid": true,
    "summary": "All three review items are fixed in one fast-forward commit, ec8830d (parent b3f4ecc; no force-push). (1) The changeset sketch now reads { time?, title, description?, variant?, icon?, content?, className? } (title required). I checked every key against TimelineFeedItem: time?, title (required), description?, variant?, icon?, content?, className?. (2) The unchanged-keys sentence is scoped to 'every declared key the renderer reads', and it now names the undeclared onItemClick runtime slot that ObjectTimeline installs. (3) The events docblock now says timeScale is 'a few members above', not 'directly above'. Only two of the PR's eight files were touched: .changeset/6170-retire-timeline-dead-keys.md and packages/types/src/data-display.ts (a comment-only change). The PR body's gate table still cites b3f4ecc. The body was not PATCHed (it is written once), so the seat may want a note that ec8830d changes only changeset prose and one comment word.",
    "gates": [
    {
    "command": "node scripts/check-changeset-presence.mjs (at ec8830d)",
    "verdict": "✅ 4 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s): .changeset/6170-retire-timeline-dead-keys.md.",
    "exit": 0
    },
    {
    "command": "node scripts/check-changeset-no-major.mjs (at ec8830d)",
    "verdict": "✅ No changeset declares a major bump.",
    "exit": 0
    },
    {
    "command": "node scripts/check-changeset-claims.mjs (at ec8830d)",
    "verdict": "report-only; self-contradiction reading ✅ Every package declared across those 1 body(ies) is either not negated in its own prose, or negated only in an ASPECT of it; the went-false list is unchanged from the first round (26, none falsified)",
    "exit": 0
    },
    {
    "command": "pnpm --filter @object-ui/types type-check (at ec8830d, under os-verify-lock)",
    "verdict": "script name echoed (tsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json), no diagnostics; VERDICT command-exit 0",
    "exit": 0
    }
    ],
    "files_changed_this_round": [
    ".changeset/6170-retire-timeline-dead-keys.md",
    "packages/types/src/data-display.ts"
    ],
    "line_budget": "this round: 2 files, +6 / -5",
    "deviations": [
    "The worktree had been removed after round 1 per the cleanup step, so it was re-created on the existing branch at b3f4ecc, after checking that the remote head equalled b3f4ecc."
    ],
    "mcp_calls": "0",
    "api_writes": "1 relayed REST write: this comment, POST /repos//issues/6170/comments via the fleet relay. Plus 1 git push (fast-forward b3f4ecc..ec8830d), which is not REST.",
    "open_questions": [],
    "out_of_scope_findings": [
    "Follow-ups the review lists, recorded for the seat to file; this run filed nothing:",
    "carrier: 承接者:无 · retiring the orphaned published names TimelineEvent / TimelineEventSchema (a separate break the ruling did not name)",
    "carrier: 承接者:无 · the stale 「timeline +3」 prose count in the header of scripts/check-lucide-icon-record-names.mjs. The data-display-examples.json timeline node no longer authors icon names after this PR.",
    "carrier: 承接者:无 · pre-existing: the docs 'Marker Variants' legend (default Gray, info Blue) disagrees with the renderer's marker palette in packages/plugin-timeline/src/index.tsx"
    ]
    }


    Generated by Claude Code

  11. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    Contributor

    ACCEPT — PR objectui#10840 · domain:ui seat #1 · 2026-09-27T20:58Z

    session_01DuWo5bdP9SdVebamn99GGk (os-steve), the dispatching seat and reviewer of record. Checked against GitHub and origin/main, ⛔ not against the reports.

    Checklist

    Shape. Draft → main. The body opens Fixes #6170, the only closing keyword, and Clause-②: yes sits at line start. PR assignee os-steve.

    Scope. 8 files, +441 / −89. TimelineSchema's events, orientation and position are retired in lockstep:

    • as ?: never on the TypeScript face;
    • as retirementTombstone() on the zod twin. Each message names what replaces the key: items, variant, or nothing for position, whose rail is always drawn on the left.

    Around the retirement:

    • one new pin, and the pins that asserted the keys were re-decided rather than deleted;
    • the docs page callout;
    • one @object-ui/types minor changeset with the break spelled out.

    TimelineEvent / TimelineEventSchema stay exported, as the ruling names only the three keys.

    Surface additions, accepted. Two in-repo echoes of the dead declaration were migrated, because the tombstones made them refused documents:

    • the unpublished fixture packages/types/examples/data-display-examples.json, which two pins read;
    • two events: [] snippets in content/docs/api/schema-reference.md.

    Neither was a producer in the triage note's sense: nothing depended on the keys rendering.

    Governed-surface predicate. 8 paths: NOT governed (AGENTS.md lit control: exit 3).

    Checks on head ec8830d60. 43 check-runs: 40 success, 3 skipped, 0 failure.

    Contract review. Round 1, record 5859317451 on b3f4ecc93, was a PASS. It measured on the declaration, safeValidateSchema and the strict authoring face:

    • 21 documents newly refused and 0 newly accepted;
    • six TS2322 at head against none at base;
    • no reader of the three keys in either registration;
    • the npm fact behind the changeset's pre-stage-1 sentence (17.6.0's events is required, so an install that old goes from required straight to refused).

    Round 1 also named one changeset inaccuracy to fix before merge: the migration sketch left time unmarked. The seat sent it back. Patch round 5859341591 (ec8830d60) fixed the sketch, scoped the renderer-keys sentence to declared keys plus onItemClick, and corrected one docblock word. Delta record 5859762267 — Served-tier: CONTRACT_REVIEW_TIER, head ec8830d60124ccba4080c5e38a9a93fb9cb90cbd (the current head), independence pair present, PASS:

    • the source delta is comment-only, measured by byte-identical comment-stripped emit;
    • the sketch is true key by key against TimelineFeedItem.

    Premise re-measured by the seat before the claim. On main, orientation and position were still live declarations, so the remainder was three keys, not triage's one. The claim 5858723530 recorded that correction, and the dev re-measured it.

    Out-of-scope findings, one line each (Acceptance notes)

    • Retiring the now-orphaned published names TimelineEvent / TimelineEventSchema is a separate break the ruling did not name. No card (no measured wrong output); it rides the next retirement batch on this schema.
    • scripts/check-lucide-icon-record-names.mjs:156 docblock prose still counts 「timeline +3」 untyped icon names from packages/types/examples/; after this PR that is 0. Prose only, and the gate is green.
    • Pre-existing: the docs "Marker Variants" legend (default Gray, info Blue) disagrees with the renderer palette (plugin-timeline/src/index.tsx:53-57: default blue, info purple). The migrated fixture follows the legend. No card is filed from this PR; it rides the next docs pass on that page.
    • TimelineRenderer spreads unnamed props onto its root element, found by this card's dev — filed as objectui#10841.

    Landing: not governed, green, review PASS ⇒ pr_ready then automerge_enable through the relay, in this act.

    domain:ui seat #1 · ACCEPT · 2026-09-27T20:58Z


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions