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] TimelineSchema.items has no declared element type, and there are two of them #6356
Triage (North Star full-board re-grade, 2026-09-19): No checklist item covers the timeline element shape. Kept on the definition layer 「看数据的视图」 plus North Star priority 4: items is declared any[], which is declaration-shaped but enforces nothing, while the renderer actually reads two distinct element shapes discriminated by variant — so an AI authoring timeline items meets no refusal. Maintainer-confirmed.
Found while working #6170 (PR #6352); deliberately left out of that card's scope. Observational — filed for triage, not as a defect claim.
The fact
PR #6352 declared items on TimelineSchema for the first time, as any[]. That is faithful to what the renderer does (items.map((item: any) => …) at every site, and items as Array<any> at two), but it means the element is still undeclared.
The renderer reads two different element shapes, discriminated by variant:
variant
element shape read
vertical / horizontal
time, title, description, variant, icon, color, content, className, meta, group
content/docs/plugins/plugin-timeline.mdx documents both as named shapes ("Timeline Item (Vertical/Horizontal)", "Gantt Item"), so the vocabulary is already written down — just nowhere a compiler or validator can read it.
Two of those keys are not in the docs at all: meta (inline chips beside the title) and group (sticky bucket headers). Both are produced by ObjectTimeline and consumed by TimelineRenderer, so they are a real internal contract with no declaration on either side.
Why it was left out
Declaring the element precisely is a genuine narrowing of a published type, not an additive declaration: items accepts anything today. It needs its own measurement — which keys are authored in the wild, whether meta/group are authorable or renderer-internal, and whether the two shapes should be one union or a discriminated pair keyed off variant. #6170's ruling did not cover it and PR #6352 declined to guess.
Blocked-by: #6170 — the alignment has to land first, since this refines a key that card introduced.
Reproduce
sed -n '250,300p' packages/plugin-timeline/src/renderer.tsx # feed item reads
sed -n '447,470p' packages/plugin-timeline/src/renderer.tsx # gantt row reads
sed -n '80,110p' content/docs/plugins/plugin-timeline.mdx # the two documented shapes
Unblock: pm:blocked → pm:queue. The prerequisite this card names is on main; it never waited on #6170's stage 2
Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) · session_01W89enF2dYV7K4N2Fbfj33f · 2026-09-27T06:40Z. ⛔ Not a claim, ⛔ not a dispatch. Read: this card (no comments), #6170's body and thread, #6354 (closed duplicate), and PR #6352.
Double-check ②: no merged PR references this card.
Execution notes. They are unchanged from the body: it asks for its own measurement first.
Which element keys are authored in the wild.
Whether meta / group are authorable or renderer-internal.
One union, or a discriminated pair keyed off variant.
The change narrows a published type ⇒ Clause-②: yes. It edits TimelineSchema in packages/types, the same file as #6170's stage 2, so if a claim on #6170 is in flight, the dispatching seat's in-flight check sequences the two. ⛔ This is not a wait on #6170.
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-6356-timeline-item-element
Worktree: objectui-issue-6356
Domain: domain:ui
Seat: domain:ui#1
File surface: the TimelineSchema declaration region in packages/types/src/data-display.ts and its zod mirror region in packages/types/src/zod/data-display.zod.ts (the element type of items), any packages/plugin-timeline/src read the narrowing breaks, content/docs/plugins/plugin-timeline.mdx if its two documented shapes change, tests beside them, one .changeset/6356-…md. Measurement first: ⛔ no narrowing before the in-the-wild authoring reading exists (stop on breach; explain in the report)
Container & model: M, mode:subagent, model: opus (TIER_DEFAULT, objectstack scripts/pm/dispatch-gates.mjs:12534 at 0d3ec47137; --tier refuses objectui paths)
Clause-②: yes
Thread-read: 5853468236
Serial constraints cleared: read 2026-09-27T15:09Z at objectui origin/mainc907a9c87. Draft PR objectui#10714 (objectui#9590, seat #2) edits data-display.ts and data-display.zod.ts in the ListSchema / ListItem region, not TimelineSchema ⇒ region-disjoint; merge main after it lands. Draft PR objectui#8941 edits packages/types/src/objectql.ts, outside this surface. objectui#6170 (stage 2, same file) is pm:blocked and unclaimed ⇒ nothing to sequence against. Not in pm:epic objectui#6349's subtree (GET …/6356/parent answers 404).
Why Clause-②: yes: triage 5853468236 — declaring the element narrows a published type, and a new exported element type grows the public face. A contract review is owed before enqueue.
objectui#6356 — measurement first, then a stop: needs_decision
domain:ui seat #1 dispatch, dev run session_01DuWo5bdP9SdVebamn99GGk (mode:subagent). Read at objectui bff63cc12 (branch base) and objectstack 0d3ec471. No PR: nothing was narrowed, per the claim's stop line. Three choices remain that change what an author may write, so they go to the seat as open_questions. The measurement below is the deliverable. Generics and placeholders are spelled in words on purpose, because GitHub strips tag-shaped text from bodies.
What the card says, checked against main
The card says "items is declared any[]". That is stale. objectui#7164 and objectui#7365 have since declared both levels, on both halves, as OBJECTS whose keys stay open (.passthrough() in the mirror, a string index signature in the TS type). The element's own keys are still undeclared, so the card's core still holds: { title, date } type-checks and parses green (Table 3).
The card says there are "two element shapes". There are really three read sets. horizontal drops five keys that vertical reads (Table 2), so the card's feed row in its table is the vertical read set, not a shared one.
The card says meta / group come from ObjectTimeline and are read by TimelineRenderer. Confirmed, and they are read by the vertical branch only.
New fact. The strict authoring face (objectui#8345, deriveStrictAuthoringSchema) refuses every legitimate timeline element today, including the schema-catalog's own feed items and gantt rows and bars (Table 3). The element and the bar declare no keys, so the face closes them to nothing. The objectui#5250 ruling says objectui validate and the doc-snippet gates will run strict. Declaring the element is therefore a prerequisite for wiring that face without false refusals.
Zone 2 assumption 1 (file locations) holds. Assumption 2 holds too: PR objectui#10714's 18 file patches mention Timeline zero times in data-display.ts and data-display.zod.ts, so the two changes touch separate regions.
Instrument
Static walk. A TypeScript-AST walk plus a JSON walk over git ls-files, on a clean worktree (so every file read is the HEAD blob). In objectui it covered 8785 files with 0 parse failures. Fenced code blocks in .md and .mdx files were parsed too.
What counts as a timeline node. An object literal whose type is timeline, plugin-timeline:timeline, object-timeline or view:timeline. For registration examples and render props, the walk also counts an object whose variant is vertical, horizontal or gantt and that carries items.
Lit control. A synthetic two-file repo authors meta, group, color, startDate, endDate, _data and date, once in an apps/ TS file and once in a skills/ JSON fence. The walk reported every one of those keys.
Render probes. A throwaway vitest file rendered the real TimelineRenderer. It was deleted uncommitted, and the tree was verified clean with git status --porcelain = 0.
Strict-face probe.tsx ran against packages/types/src.
Not re-derivable from this comment alone. The scripts lived in the session scratchpad. The follow-up implementation should re-take Table 1 with a committed instrument if it wants a gate.
Table 1 — element keys authored in each corpus
The figure after each key is the number of elements that carry it.
time 11, title 11, variant 10, description 9, icon 4, className 1, content 1
3 / 9
label 3, items 3 / 9 each
registration defaultProps plus examples (plugin-timeline:timeline)
10
time 10, title 10, description 10, variant 10, icon 4
3 / 5
label 3, items 3 / 5 each
apps/ (298 files), skills/ (27 files)
0
none
0
none
objectstack, whole tree (9418 files; 21 timeline-typed nodes: 2 list views, 1 component-manifest entry in sdui.manifest.json, 18 in tests)
0 outside tests
none
0
none
test fixtures (15 files; they show which tests a narrowing would reach, not what authors write)
15
title 15, time 13, description 6, variant 3, date 2
7 / 7
label 7, items 7 / title, startDate, endDate: 7
Authored totals (the four authoring corpora combined):
Feed: 39 elements. time 39, title 39, variant 38, description 36, icon 19, className 1, content 1.
Feed keys never authored: color, meta, group, startDate and endDate all score 0.
Gantt: 11 rows, all carrying label and items. 22 bars, all carrying title, startDate, endDate and variant.
The seven feed keys agree everywhere. The authored feed vocabulary is exactly the seven keys the docs page documents. The registration's inputs description lists six of them and omits className.
Authored variant values on elements and bars are default 6, success 25, info 16, warning 12, danger 3: exactly the documented five. The marker also paints todo, in-progress and done (zero authored); the bar paints only the five. Lit control: the same grep hits the node-level values vertical 11, horizontal 4, gantt 5.
Test fixtures.ObjectTimeline.test.tsx authors { title, date } twice, typed schema: any. date is the TimelineEvent vocabulary and nothing reads it off an item, so the two tests assert the title only. This is the card's trap, live in-repo.
Table 2 — what each feed branch reads, measured in render
The probe rendered one item carrying every candidate key, with dateFormat: 'iso'.
key
vertical
horizontal
time, title, description, icon, className
drawn
drawn
color (marker style)
drawn
dropped
meta (chips)
drawn
dropped
group (sticky bucket header)
drawn
dropped
startDate (as the only date), endDate
drawn
dropped
date
dropped
dropped
Not re-probed.variant and content are read by both branches in source (the TimelineMarker variant and renderChildren(item.content)).
Gantt read set. The gantt branch reads row label / items and bar title / startDate / endDate / variant. The existing gantt suites pin that, so it was not re-probed.
The only producer of the five composed-only keys is ObjectTimeline's effectiveItems memo, which composes { title, time, startDate, endDate, description, variant, color, group, meta, _data } per record. Its click handler reads _data. ListView and ObjectView hand it config and never items.
Table 3 — what each door answers today
input
tolerant mirror (safeValidateSchema, what objectui validate runs)
strict face (objectui#8345, not wired yet)
render
catalog feed item
accept
refuse: unrecognized_keys at items.0 [time, title, description, variant, icon]
draws
catalog gantt row and bar
accept
refuse: items.0 [label] and items.0.items.0 [title, startDate, endDate, variant]
draws
{ title, date }
accept
refuse (both keys, the same way as any key)
title only; date silently dropped
a feed item under variant: 'gantt'
accept
refuse (every element key)
empty unlabelled row, no diagnostic
a gantt row under vertical / horizontal
accept
refuse (every element key)
empty item, no diagnostic
The two cross-shape rows are the evidence for the second question below. A union at the element level accepts both of them by construction, because each is a valid arm.
Reproduce the cross-shape reading
// vitest file under packages/plugin-timeline/src/__tests__/, run from the repo rootimportReactfrom'react';import{render}from'@testing-library/react';import{TimelineRenderer}from'../renderer';import{TimelineSchema}from'@object-ui/types/zod';constfeed={time: '2024-01-15',title: 'FEED_TITLE'};constrow={label: 'ROW_LABEL',items: [{title: 'BAR_TITLE',startDate: '2024-01-01',endDate: '2024-02-01'}]};for(constschemaof[{type: 'timeline',variant: 'gantt',items: [feed]},{type: 'timeline',variant: 'vertical',items: [row]},]){constok=TimelineSchema.safeParse(schema).success;// true, bothconsthtml=render(React.createElement(TimelineRenderer,{ schema })).container.innerHTML;// neither FEED_TITLE nor ROW_LABEL nor BAR_TITLE, and no role="alert"}
The same shapes under their own variant are the lit control: FEED_TITLE draws under vertical, and ROW_LABEL plus BAR_TITLE draw under gantt.
{
"issue": 6356,
"status": "needs_decision",
"branch": "claude/issue-6356-timeline-item-element",
"pr": null,
"session": "session_01DuWo5bdP9SdVebamn99GGk (dev subagent; the seat's session id)",
"premise_still_valid": true,
"summary": "Measured first and stopped before narrowing anything. The core premise holds on bff63cc12: the element's own keys are undeclared, and { title, date } passes both TS and the tolerant mirror. Three sub-claims moved. items is no longer any[] (objectui#7164 and #7365 declared row and bar as open objects). There are three read sets, not two: horizontal drops color, meta, group, startDate and endDate, measured in render. And the strict authoring face (objectui#8345) refuses every legitimate timeline element today. Authored vocabulary across four corpora is exactly the documented seven feed keys (39 feed elements) plus label/items rows (11) and title/startDate/endDate/variant bars (22). Zero color, meta, group, startDate or endDate were authored anywhere, zero in apps/ and skills/, and zero in objectstack. Cross-shape items (a feed item under gantt, a gantt row under vertical or horizontal) are accepted and render empty with no diagnostic. Three choices change what an author may write (open_questions), so this returns needs_decision, with one recommendation per question.",
"tests": "No gate owed: zero-diff branch (no commits; origin branch == base bff63cc12). Measurements: (1) static walk over git ls-files: objectui 8785 files, 0 parse failures; objectstack 9418 files, 2 JSONC tsconfig parse failures (irrelevant). Lit control: a synthetic repo authoring meta, group, color, startDate, endDate, _data and date, all reported. (2) Render probe through bash os-verify-lock.sh -c 'pnpm exec vitest run packages/plugin-timeline/src/__tests__/zz-probe-6356.test.tsx', three runs, each 'Test Files 1 passed (1)' and 'os-verify-lock: VERDICT command-exit 0'. The probe file was deleted uncommitted; git status --porcelain = 0 before the worktree was removed. (3) Strict-face probe with tsx on packages/types/src (deriveStrictAuthoringSchema(TimelineSchema)), exit 0, readings in Table 3. (4) zod 4.4.3 check for question 2 option C: a superRefine'd object keeps type object, .shape and discriminatedUnion membership, but .extend() that OVERWRITES a key throws 'Cannot overwrite keys on object schemas containing refinements. Use .safeExtend() instead.'",
"gates": [
"none run: zero-diff branch, no files changed; measurement probes only (render probe VERDICT command-exit 0, x3)"
],
"line_budget": "n/a: no skills/** or other governed file touched (no file touched at all)",
"deviations": [
"No PR opened, so neither pr_create nor the PR-assignee label-write was spent. Reason: needs_decision, and the claim's stop line forbids narrowing before a ruling.",
"A temporary probe test file was created in the worktree to take the render readings, then deleted uncommitted; the tree was verified clean before git worktree remove.",
"The pushed branch claude/issue-6356-timeline-item-element sits at base bff63cc12 with no commits. It remains on origin as the claim's landing marker (this session cannot delete remote refs).",
"The report fields gates, line_budget, deviations and files_changed were added to the role-file template at the PM's request.",
"The measurement table lives in this comment rather than in a PR body, because there is no PR."
],
"files_changed": [],
"mcp_calls": "0",
"api_writes": "1: POST /repos/objectstack-ai/objectui/issues/6356/comments (this report, via objectstack scripts/pm/post-stamped.mjs). Reads, not writes: REST GET issue 6356 and its comments, and GET pulls/10714/files.",
"open_questions": [
{
"question": "Q1. Are meta and group, and the other three keys that only the composed path produces on a feed item (color, startDate, endDate), authorable element keys or renderer-internal?",
"options": [
"A. Renderer-internal. The authored feed element declares only the documented seven (time, title, description, variant, icon, content, className). The composed-only five plus _data get an internal type inside plugin-timeline for the ObjectTimeline-to-TimelineRenderer handoff. The renderer keeps reading them, so it stays more lenient than validate (the house posture), and the strict face refuses them when authored. Cost: someone who authors meta or group on a vertical timeline today gets chips and headers, and would be refused once the strict face is wired. Measured authored instances: zero.",
"B. Authorable, vertical only. This needs the element discriminated three ways (vertical, horizontal, gantt), because horizontal drops all five in render. Cost: five keys declared with zero authored pull, and a three-arm discrimination.",
"C. Authorable on one shared feed element. Not viable: it declares keys horizontal silently drops (Table 2), which is a declared capability the runtime does not honour."
],
"recommendation": "A. Axis 1 (real need): zero authored across four authoring corpora, apps/, skills/ and objectstack, and the sole producer is ObjectTimeline's own composition. Axis 2 (long term): an internal handoff is typed where it lives, and the author face becomes the documented face, which the docs, the registration and every authored corpus already agree on. Axis 3 (AI errors): fewer declared keys means fewer authorings that horizontal silently drops, and the strict face refuses the rest. Axis 4 (no expansion): no pull, so no declaration. This is the same posture objectui#10222 (letter A) took for the chip-field list that produces meta."
},
{
"question": "Q2. Should the element be one union, or a pair discriminated by the node's variant? And if a pair, in which form?",
"options": [
"A. Element union (feed item or gantt row); TimelineSchema stays an interface and a ZodObject. Cheapest. It refuses unknown keys (TS literals get excess-key errors against the closed arms, and the strict face closes them) but NOT cross-shape items, which Table 3 shows are silent today.",
"B. Pair as a split node: TS TimelineSchema becomes a union type alias (feed node or gantt node) and the zod mirror becomes a union. It refuses cross-shape in TS and in validate. Cost: the published zod TimelineSchema stops being a ZodObject, so .shape and .extend are gone. Four in-repo test files use them, and the parity ratchet pairs every mirror by reading its own .shape. The node-level tombstones (timeScale, body, children) and the three deprecated keys must ride on both arms.",
"C. Pair as a refinement: the TS element union from A, and the zod mirror stays a ZodObject with a node-level superRefine that judges each element against the arm variant selects (absent means vertical). This is the house pattern for cross-key rules (calendarSelectionFitsMode, requireRecordSource). It refuses cross-shape at objectui validate only, because TS cannot see the parent key. Costs: .extend key-overwrite throws on a refined object in zod 4.4.3, so the two tests that do TimelineSchema.extend with an items override move to .safeExtend. On the tolerant, passthrough face, cross-shape is refusable only through at least one REQUIRED key per arm. Measured: every authored feed element carries title and time, every row carries label and items, every bar carries title, startDate and endDate. But row items is ruled optional (objectui#6750, objectui#7164), so label is the gantt arm's only discriminator, and making label (and feed title) required is new semantics."
],
"recommendation": "C, with feed title and gantt label required, and the element and bar variant declared as the documented five (all authored values fall inside it; the composed path keeps record values through its internal type). Axis 3: it refuses both unknown keys and cross-shape at the JSON door AI authors through, and TS literals still get excess-key refusal. Axis 2: TimelineSchema stays an interface and a ZodObject, so the parity ratchet, the tombstones and DataDisplaySchema's discriminated union are untouched; B's restructuring buys only TS-level cross-shape refusal. Axis 4: no key beyond the measured ones, and the two required keys are present in 100% of authored elements. Axis 1: gantt is authored in all four authoring corpora, and the cross-shape failure is measured silent."
},
{
"question": "Q3. How should this be sequenced with @objectstack/spec? The spec's object-timeline element declares items as z.array(z.unknown()) ('the author owns the item shape'), and its items reach the same TimelineRenderer.",
"options": [
"A. objectui first, on its own presentational node timeline. Then an objectstack follow-up takes the element vocabulary for object-timeline.items, the way the spec already defers to objectui for the objectui-owned mapping contract (z.unknown(), 'its contract still lives in objectui').",
"B. Spec first; objectui mirrors after.",
"C. objectui only; the spec door stays open."
],
"recommendation": "A. Axis 2: the vocabulary's implementation, docs and only producer all live in objectui, and the spec's stated posture for an objectui-owned contract is to follow objectui. The base principle 'spec declaration over implementation' points toward B, so this is a real conflict, surfaced here rather than silently resolved. Axis 3: C leaves os validate open for the same element that objectui validate would refuse. Axis 4: A adds no gate and no staged window."
}
],
"out_of_scope_findings": [
"carrier: objectui#6170 (stage 2, events/orientation/position) · packages/types/examples/data-display-examples.json authors a timeline with events (zero read points), not items · noted, not filed",
"carrier: the implementation PR for this card after the ruling · ObjectTimeline.test.tsx authors { title, date } items (date is silently dropped; typed schema: any, so a narrowing will not redden them); the implementation should re-spell them time · noted, not filed",
"carrier: none (承接者:无) · packages/components/src/renderers/complex/TIMELINE.md lists three gantt time scales where six exist · documentation drift, Acceptance-notes class, not filed"
]
}
Measurement delivered ⇒ two contract-shape questions to the box · domain:ui seat #1 · 2026-09-27T15:55Z
session_01DuWo5bdP9SdVebamn99GGk (os-steve). The card is measurement-first. The dev measured, wrote nothing (the claim's stop line), and returned three questions (report 5857222802, which holds the tables). The seat re-read the one reading everything rests on: on objectui origin/main, TimelineSchema.items is z.array(TimelineRowSchema) (packages/types/src/zod/data-display.zod.ts:1135), not the body's any[]. The element is an object with its keys still open. ⇒ The card's core stands, and its body's framing is stale.
Release: session_01DuWo5bdP9SdVebamn99GGk · cause: measurement done, two open contract-shape choices · destination: needs-user-decision. The claim 5857054090 is spent. The branch claude/issue-6356-timeline-item-element holds no commits beyond base. The next dispatch re-claims.
Governing text: this card's triage block (「items … enforces nothing … an AI authoring timeline items meets no refusal」, maintainer-confirmed); triage 5853468236 (the three execution questions, Clause-②: yes); objectui#10222 letter A (the same posture for the chip-field list that produces meta); the pm-dispatch base principle 「spec 声明 > 实现 > 文档面」.
One-line problem
An author (human or AI) can put any keys on a timeline item, or put a gantt row on a feed timeline. Both are accepted, and the wrong parts just do not draw. In addition, the strict authoring check that objectui validate is due to run would refuse every legitimate timeline element today, because the element declares no keys at all (measured, report Table 3).
Q1 — meta, group, color, startDate, endDate on a feed item: authorable, or renderer-internal?
what it means
cost, measured
A
Renderer-internal. The authored feed element declares the documented seven (time, title, description, variant, icon, content, className). The five composed-only keys get an internal type inside plugin-timeline for the ObjectTimeline → renderer handoff.
Zero authored instances across four authoring corpora, apps/, skills/ and objectstack. Anyone who authors meta today would be refused once the strict face is wired.
B
Authorable, on vertical only. Needs a three-way element split (vertical / horizontal / gantt), because horizontal drops all five.
Five keys declared with zero pull, plus a three-arm discrimination.
C
Authorable on one shared feed element.
Declares keys that horizontal silently drops (report Table 2) ⇒ not viable.
Q2 — one union, or a pair discriminated by the node's variant?
what it means
cost, measured
A
Element union (feed item or gantt row); TimelineSchema stays an interface and a ZodObject.
Refuses unknown keys, but NOT a gantt row on a feed timeline (silent today, Table 3).
B
Split node: TS TimelineSchema becomes a union alias and the zod mirror a union.
Refuses cross-shape in TS and in validate. The published zod TimelineSchema stops being a ZodObject (.shape / .extend gone; 4 test files and the parity ratchet use them); the tombstones ride on both arms.
C
TS element union as in A, plus a node-level superRefine in the zod mirror that judges each element against the arm variant selects (absent ⇒ vertical). This is the house pattern for cross-key rules (calendarSelectionFitsMode, requireRecordSource).
Refuses unknown keys AND cross-shape at the JSON door AI authors use. TS literals still get excess-key errors; cross-shape is caught at validate, not at compile. Two tests move from .extend to .safeExtend.
① 项目长远合理性:作者面只承诺文档写明、作者在用的形状;内部拼装的键在它所在的包里定型。已发布的 TimelineSchema 保持对象形状,一致性棘轮、墓碑、DataDisplaySchema 的判别联合都不动,所以 Q2 取 C。参照主流平台:这类组件的条目 schema 是闭合的,内部渲染字段不外露。
② 实际业务拉动:四个作者语料里,那五个组合键的作者写入实测为零;甘特在四个语料里都有人写,而串用失败在今天是静默的。
③ 防 AI 犯错:Q1-A 少声明五个键,就少五种会被横向布局静默丢掉的写法;Q2-C 在 JSON 入口同时拒绝未知键和串用,AI 走的正是这道门。
④ 创业阶段不扩散:没有拉动就不声明;没有新增门禁;不设分阶段窗口。
os-decision-facets
① 项目长远合理性:Q1-A + Q2-C 缩小契约增生——作者面收成文档写明的形状,已发布 schema 的对象形状不变;Q1-B 与 Q2-B 各扩大一块面。
② 实际业务拉动:组合键作者写入为零;甘特四个语料都有,串用今天静默失败。
③ 防 AI 犯错:闭合的条目 schema 加入口处按 variant 判别,未知键与串用都响亮拒绝。
④ 创业阶段不扩散:零拉动的键不声明,不加门禁,无过渡窗口。
Prior rulings read: timeline items,meta group,discriminated variant → 1 hit; objectui#10222 ruling 5824012155 letter A (retire the undeclared metaFields read — the same posture for the list that produces meta); thread: triage 5853468236
推荐:Q1 取 A,Q2 取 C(feed 的 title 与 gantt 的 label 设为必填,两者在已写元素中出现率均为 100%)。回退:Q2 取 A(先拦未知键,串用另立卡)。
自检:只看①选 Q1-A / Q2-C;②③④ 是否翻转:否。
置信缺口:租户存储里是否有人写了 meta / group 等键,本席看不见;若有,接上严格校验后这些记录会被拒(今天它们在横向布局下本就不画)。
Q3 — sequencing with @objectstack/spec — decided by this seat, ⛔ not escalated
Order of technical work is a non-escalation class. Decision: objectui first, then the spec follows (the dev's A). The vocabulary's implementation, docs and only producer all live in objectui, and the spec's own object-timeline.items is z.array(z.unknown()) with 「the author owns the item shape」. On landing, this seat files the objectstack follow-up (Blocked-by: this card's PR), which lets the spec take the element vocabulary for object-timeline.items. ⚠️ This runs against 「spec 声明 > 实现」 read literally, and it is recorded here so the ruling can overturn it in the same stroke.
After the ruling
Re-dispatch with Clause-②: yes, surface = the TimelineSchema region of packages/types/src/data-display.ts and its zod mirror region, packages/plugin-timeline/src (the internal handoff type, and re-spelling ObjectTimeline.test.tsx's { title, date } items), content/docs/plugins/plugin-timeline.mdx, tests, and a minor changeset for @object-ui/types. The measurement tables in 5857222802 are the dev's starting data and are not re-derived.
Ruling: maintainer · Q1 letter A 「A 内部键(推荐)」 · Q2 letter C 「C 联合+校验判别(推荐)」 · 2026-09-27T16:01Z
Recorded by the domain:ui seat #1 (session_01DuWo5bdP9SdVebamn99GGk, os-steve). ⛔ Not this seat's adjudication: the maintainer answered the two box questions of 5857426543 in this session's chat. The quoted labels are the options chosen, verbatim. Freshness: the thread was re-read in this act, and 5857426543 is the newest comment.
Ruled.
Q1 = A. The authored feed element declares the documented seven: time, title, description, variant, icon, content, className. meta, group, color, startDate, endDate (and _data) are renderer-internal. They get an internal handoff type inside plugin-timeline for ObjectTimeline → TimelineRenderer, and the strict face refuses them when authored.
Q2 = C. The TS element is a union (feed item or gantt row). The zod TimelineSchema stays a ZodObject, with a node-level superRefine that judges each element against the arm variant selects (absent ⇒ vertical). Feed title and gantt label are required, and the element / bar variant is declared as the documented five. The two .extend call sites move to .safeExtend.
Q3 (sequencing) was decided in-seat in 5857426543 and stands: objectui first; the objectstack follow-up for object-timeline.items is filed on landing.
State note. The box's Release: line never reached the labels: the flip to needs-user-decision was refused by the write-pace budget, and the ruling arrived before it could be retried. ⇒ The card stayed pm:dispatched / os-steve throughout. The release is recorded as spent in the next act, which is a fresh Claim:.
domain:ui seat #1 · ruling record · 2026-09-27T16:01Z
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-6356-timeline-item-element
Worktree: objectui-issue-6356
Domain: domain:ui
Seat: domain:ui#1
File surface: the TimelineSchema / timeline element region of packages/types/src/data-display.ts and of packages/types/src/zod/data-display.zod.ts (element union, node-level superRefine, required title / label, element and bar variant); packages/plugin-timeline/src (the internal handoff type for the composed-only keys; ObjectTimeline.test.tsx's { title, date } items re-spelled time); the two .extend → .safeExtend test call sites; content/docs/plugins/plugin-timeline.mdx where its documented shapes change; tests beside them; one minor changeset for @object-ui/types (plus @object-ui/plugin-timeline if its published source changes) (stop on breach; explain in the report)
Container & model: M, mode:subagent, model: opus (TIER_DEFAULT, objectstack scripts/pm/dispatch-gates.mjs:12534 at 0d3ec47137; --tier refuses objectui paths)
Clause-②: yes
Thread-read: 5857468846
Serial constraints cleared: read 2026-09-27T16:02Z. Draft PR objectui#10714 (objectui#9590) edits data-display.ts / data-display.zod.ts in the ListSchema / ListItem region only (its patches name Timeline zero times, per report 5857222802) ⇒ region-disjoint; merge main after it lands. No other open PR or in-flight claim in this lane names these files. objectui#6170 (stage 2, same file) is pm:blocked and unclaimed.
Re-claim under the maintainer's ruling 5857468846. Why Clause-②: yes: a published @object-ui/types element type and its authoring mirror narrow (unknown element keys and cross-shape elements become refused).
{
"issue": 6356,
"status": "done",
"branch": "claude/issue-6356-timeline-item-element",
"pr": "https://github.com/objectstack-ai/objectui/pull/10822",
"session": "session_01DuWo5bdP9SdVebamn99GGk — dev subagent (mode:subagent); the seat's session id, as the harness-stamped Claude-Session line carries it",
"premise_still_valid": true,
"summary": "Implemented the maintainer ruling 5857468846 (Q1 = A, Q2 = C) on objectui base c58b8e4a0, final head 42ef5763f, draft PR #10822. TS: TimelineSchema.items is now an array of TimelineFeedItem (the seven documented keys, title required) or TimelineGanttItem (label required, bars TimelineGanttItemBar with all-optional keys), item and bar variant = the documented five (TimelineItemVariant); arms are closed, so { title, date } is TS2353. zod: TimelineSchema stays a ZodObject and ends in .superRefine(timelineItemsFitVariant), which judges each element against the arm variant selects (absent => vertical): required key present, no key of the other arm; cross-shape is refused at the public validate door (safeValidateSchema answers custom @ items.0.title / items.0.label / items.0.items for a gantt row on a vertical timeline). The node SHAPE declares both arms' keys (optional) rather than an element-level z.union, because a union was measured to fold objectui#7164/#7365's pinned paths into one invalid_union at items.0; the strict face therefore names meta/group/color/startDate/endDate/_data/date by name (unrecognized_keys at items.0) and now accepts the schema-catalog documents it refused before. The composed-only keys are typed only in plugin-timeline/src/renderHandoff.ts (not exported), which is TimelineRenderer's prop type. Zone 2: all four assumptions hold as measured (details in PR body); superRefine leaves ZodObject class, .shape, DataDisplaySchema dispatch, the parity ratchet and the timeScale/body/children tombstones intact; .extend over an existing key throws, both control sites moved to .safeExtend. PR objectui#10714 was still an open draft at merge time, region-disjoint. ObjectTimeline.test.tsx items re-spelled time and typed, so the compiler now refuses date there. Choices beyond the ruling's letter are listed for the Clause-② contract review in the PR body (registered mirror pairs 159 -> 162, four new TS exports and three zod exports, requiredness kept to title/label only, bar dates = string | finite number | Date per objectui#6781, bar declared as a type alias).",
"tests": "All heavy runs via os-verify-lock.sh, exit codes by redirect-then-status, on head 42ef5763f unless noted. (1) pnpm --filter @object-ui/types type-check -> exit 0 (tsc + tsconfig.examples + tsconfig.test, the last judges the parity ratchet and the new @ts-expect-error pins). (2) pnpm --filter @object-ui/types build -> exit 0; dist/data-display.d.ts carries TimelineFeedItem. (3) pnpm --filter @object-ui/plugin-timeline type-check -> exit 0 (reads @object-ui/types from that rebuilt dist; its closure was built first with turbo run build --filter=@object-ui/plugin-timeline... --concurrency=2, 11/11 tasks). (4) pnpm exec vitest run packages/types/ packages/plugin-timeline/ --maxWorkers=2 -> 'Test Files 280 passed (280)', 'Tests 5722 passed (5722)', exit 0. (5) REVERSE CHECK across the package boundary via objectstack scripts/ablation-replace.mjs (wrap mode): planting date on the typed item in ObjectTimeline.test.tsx -> plugin-timeline tsc -p tsconfig.test.json: 'error TS2353: Object literal may only specify known properties, and 'date' does not exist in type 'TimelineFeedItem | TimelineGanttItem'', child exit 2; mutation landed (anchor 1 -> 0, blob a6f953ca63f1 -> af4a03fb097c); restored blob == HEAD a6f953ca63f1, git diff HEAD empty. (6) ABLATION via ablation-replace.mjs: '}).superRefine(timelineItemsFitVariant);' -> '});' in data-display.zod.ts (anchor 1 -> 0, blob ee4326c02190 -> 02f2aa66b150) -> vitest on timeline-item-element-6356.test.ts: 'Failed Tests 10', exactly the refinement-dependent pins (cross-shape x5, required keys, strict-clone cross-shape, DataDisplaySchema dispatch, safeExtend keeps refinement, TS cross-shape validate leg); the strict by-name and vocabulary pins stayed green as expected; child exit 1; restored blob == HEAD ee4326c02190, git diff HEAD empty. Direction observed = the usual one (turns red). The pin file imports ../zod/data-display.zod.js relatively, so no dist/ is on the ablation's resolution path. (7) check:doc-snippets after its declared --build-filter closure (turbo build exit 0) -> 'Semantic phase: 676 of 676 block(s) judged, 0 failed.' exit 0; check:doc-examples exit 0. (8) eslint (root config, as each package's lint script runs it) over the 12 changed .ts/.tsx files, --format json -> 12 files, 0 errors, 80 warnings (warnings are not ratcheted; 2 are the new z.ZodType-any-any annotation in the 7365 control); population = the 12 files of git diff --name-only c58b8e4a0 HEAD filtered to ts/tsx, count read from the JSON output; the config enables no type-aware linting, so this diff cannot move any untouched file's verdict. Final tree after all legs: git status --porcelain = 0 lines.",
"gates": [
"pnpm --filter @object-ui/types type-check -> (inside verify2 under the lock) exit 0; lock line 'os-verify-lock: VERDICT command-exit 0 · held the lock 147s (2m27s) · waited 241s (4m01s)' @ 42ef5763f",
"pnpm --filter @object-ui/types build -> exit 0 @ 42ef5763f",
"pnpm --filter @object-ui/plugin-timeline type-check -> exit 0 @ 42ef5763f",
"pnpm exec vitest run packages/types/ packages/plugin-timeline/ --maxWorkers=2 -> 'Test Files 280 passed (280)' / 'Tests 5722 passed (5722)' -> exit 0 @ 42ef5763f",
"turbo run build --filter=@object-ui/plugin-timeline... --concurrency=2 -> 'Tasks: 11 successful, 11 total' -> exit 0 (verify round 1, 4a07d05d5; src unchanged since except one test annotation)",
"pnpm check:doc-snippets (after turbo build over its --build-filter, exit 0) -> 'Semantic phase: 676 of 676 block(s) judged, 0 failed.' -> exit 0; lock line 'os-verify-lock: VERDICT command-exit 0 · held the lock 218s (3m38s) · waited 47s' @ 42ef5763f",
"pnpm check:doc-examples -> 'Every covered `@example` compiles, or fails exactly as its ledger row declares.' -> exit 0 @ 42ef5763f",
"pnpm check:control-bytes -> '✅ check-control-bytes: OK (scanned 8995 tracked text file(s); skipped 85 binary).' -> exit 0 @ 42ef5763f",
"pnpm check:new-line-citations -> 'VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0' -> exit 0 @ 42ef5763f",
"pnpm check:test-path-roots -> '✅ check-test-path-roots: OK' -> exit 0 @ 42ef5763f",
"pnpm check:handler-key-reads -> 'OK 105 arm(s), 211 registration(s) ...' -> exit 0 @ 42ef5763f",
"pnpm check:doc-fences -> '✅ check:doc-fences — every TypeScript block in 227 document(s) is fenced ...' -> exit 0 @ 42ef5763f",
"pnpm check:doc-types -> '✅ Every documented component type is registered.' -> exit 0 @ 42ef5763f",
"pnpm check:doc-example-ids -> '✅ 414 real reference(s) all resolve in the catalog registry.' -> exit 0 @ 42ef5763f",
"pnpm check:doc-example-readers -> 'OK 79 documented symbol(s) ...' -> exit 0 (@ 42ef5763f tree, run once)",
"node scripts/check-changeset-presence.mjs -> '✅ 12 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s): .changeset/6356-timeline-item-element.md.' -> exit 0 @ 42ef5763f",
"node scripts/check-changeset-no-major.mjs -> '✅ No changeset declares a `major` bump.' -> exit 0 @ 42ef5763f",
"pnpm check:changeset-claims -> report-only; went-false requests on the pending objectui#7164 / objectui#7365 changesets answered in this PR's changeset text; self-contradiction reading '✅ Every package declared ... is either not negated' -> exit 0 @ 42ef5763f",
"pnpm check:component-surface-parity -> 'component-surface-parity: REPORT-ONLY (objectui#4631) -- exits 0 with findings.' -> exit 0; its one timeline row (plugin-timeline:timeline.scale) exists on base, only its line moved",
"node scripts/check-governed-queue-guard.mjs --test (14 changed paths) -> '✅ NOT GOVERNED — 14 path(s) checked against 5 governed surface(s); none matched.' -> exit 0",
"eslint --format json over the 12 changed ts/tsx files -> 12 files, 0 errors -> exit 0 @ 42ef5763f",
"CI on PR #10822 head 42ef5763f -> in_progress at report time (42 check runs: 22 success, 3 skipped, 17 in progress); not waited on, per the role file"
],
"line_budget": "n/a — no skills/** or other governed file touched (governed-queue-guard --test: NOT GOVERNED, 14 paths)",
"deviations": [
"Zone 3 route said 'closed objects in both faces'. The TS arms are closed; the zod arms stay .passthrough() on the TOLERANT face and are closed only by the strict authoring face. Reason: the ruling says 'the strict face refuses them when authored' and the retirement posture names the strict face; closing the tolerant face would also refuse undeclared keys at objectui validate today, which no ruling ordered.",
"The zod element in the node SHAPE is not a z.union of the two arms but an object declaring both arms' keys (optional), with the static type asserted to the TS union (the NODE's accept set, enforced by the refinement). Measured reason: an element-level union folds objectui#7164 / #7365's path-pinned refusals into one invalid_union at items.0 and names an unknown key twice on the strict face. Documented in the zod docblock; same assertion spelling as nodeUnion in base.zod.ts. Flagged for the Clause-② contract review.",
"Registered TimelineFeedItemSchema / TimelineGanttItemSchema / TimelineGanttItemBarSchema as exported mirror pairs (zod-mirror-parity.test.ts MIRRORS + Declared, EXPECTED_MIRROR_PAIRS 159 -> 162) and put them on the ./zod barrel; added TimelineFeedItem / TimelineGanttItem / TimelineGanttItemBar / TimelineItemVariant to the root TS barrel. Not named by the brief; it is what keeps the element keys under the parity ratchet despite the static assertion. Public-surface growth, flagged for the contract review.",
"Brief's gate list named type-check for all dependents of @object-ui/types; declared narrowing: only @object-ui/types and @object-ui/plugin-timeline were type-checked locally. Evidence: TimelineSchema is imported only by plugin-timeline (git grep over the repo), and the only other TS type: 'timeline' literals are list-view types (six hits in apps/console, core, plugin-view tests). CI runs the full set.",
"The four commits (6929275cc, 955a457d5, 4a07d05d5, 42ef5763f) carry the harness's model-named Co-Authored-By trailer, which the role file says to report and not imitate (trailers model-free). Reported, not rewritten: history is not amended and objectui forbids force-push.",
"Two lock acquisitions returned exit 99 (queue-timeout) behind another seat's 30+ minute locked vitest sweep (holder pid 31779, issue-10594 verify.sh, alive and CPU-busy); the slot objectui-6356-dev kept my place and the later runs acquired. Heavy runs were launched in the background and waited on in the foreground with tail --pid, because lock wait plus run exceeded one foreground call.",
"Pending changesets for objectui#7164 and objectui#7365 (same release) state that row and bar keys stay undeclared and feed timelines are untouched; not edited (they are other changes' records). This PR's changeset names what it supersedes instead.",
"TimelineGanttItemBar is a type alias, not an interface: the parity ratchet read the gantt pair's items as narrower-than-declared only because an interface has no implicit index signature against the passthrough bar's input type."
],
"files_changed": [
".changeset/6356-timeline-item-element.md",
"content/docs/plugins/plugin-timeline.mdx",
"packages/plugin-timeline/src/ObjectTimeline.test.tsx",
"packages/plugin-timeline/src/ObjectTimeline.tsx",
"packages/plugin-timeline/src/renderHandoff.ts",
"packages/plugin-timeline/src/renderer.tsx",
"packages/types/src/__tests__/timeline-item-element-6356.test.ts",
"packages/types/src/__tests__/timeline-items-bar-shape-7365.test.ts",
"packages/types/src/__tests__/timeline-items-row-shape-7164.test.ts",
"packages/types/src/__tests__/zod-mirror-parity.test.ts",
"packages/types/src/data-display.ts",
"packages/types/src/index.ts",
"packages/types/src/zod/data-display.zod.ts",
"packages/types/src/zod/index.zod.ts"
],
"mcp_calls": "0 — no MCP GitHub tool called",
"api_writes": "3 relay writes as objectstack-fleet[bot]: POST /repos/objectstack-ai/objectui/pulls (draft PR #10822, via with-fleet.sh --via dispatch, run 36336820064); POST /repos/objectstack-ai/objectui/issues/10822/assignees (os-steve, via label-write.mjs, run 36336860100, read back MATCHES); POST /repos/objectstack-ai/objectui/issues/6356/comments (this report, via post-stamped.mjs). Plus 3 git push to the branch (not REST). Zero label writes. Reads only otherwise: REST GET issue 6356 and its comments, pulls/10714 (twice), pulls?head=, pulls/10822, commits/42ef5763f/check-runs.",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: public door — safeValidateSchema (what objectui validate runs) refuses the packages/plugin-timeline/README.md 'Schema-Driven Usage' document exactly as written (invalid_union @ type) and accepts it respelled type: 'timeline' · evidence: the README spells the node component: 'timeline', and README.md is in the package's published files list · dedupe words: plugin-timeline README component, Schema-Driven Usage component timeline, README component instead of type",
"carrier: 承接者:无 · packages/components/src/renderers/complex/TIMELINE.md lists three gantt time scales where six exist (carried from the first dev's report) · noted in PR Acceptance notes, not filed"
]
}
session_01DuWo5bdP9SdVebamn99GGk (os-steve). Same claim 5857481990, same branch; the patch round went to the original dev.
Contract review 5858250761 (head 42ef5763f, Served-tier: CONTRACT_REVIEW_TIER) is FAIL on one defect. Everything else was judged conforming to ruling 5857468846: a 50-row probe on head and base found the TS face and the tolerant zod face pure narrowings; the strict face refuses the composed keys by name; the refinement holds for every variant; and the new exports are necessary for the ratchet.
Defect:.changeset/6356-timeline-item-element.md says of @object-ui/plugin-timeline 「Anything that type-checked against it before still does.」 That is false. TimelineRenderer is public and its schema prop narrowed, so an element literal carrying an undeclared key (for example date) was accepted at base and is TS2353 at head. That sentence publishes verbatim into the CHANGELOG. Fix: spell the break; the bump stays minor.
Same edit: 「a bar still accepts keys it does not declare」 is true of the tolerant face only, so it gets qualified.
Not this PR's defects, noted: safeValidateSchema still accepts { title, date } on the tolerant face until objectui#5250 wires the strict face; the README component: 'timeline' refusal is filed as objectui#10824.
{
"issue": 6356,
"status": "done",
"branch": "claude/issue-6356-timeline-item-element",
"pr": "https://github.com/objectstack-ai/objectui/pull/10822",
"head": "11725ece913208630276727e5d4a1bc9914702af",
"round": "PATCH ROUND (REWORK, round 1) — contract review 5858250761 (VERDICT FAIL on one changeset sentence), same branch, same claim 5857481990",
"session": "session_01DuWo5bdP9SdVebamn99GGk — dev subagent (mode:subagent); the seat's session id",
"premise_still_valid": true,
"summary": "Fixed the one defect contract review 5858250761 found, in .changeset/6356-timeline-item-element.md only; no code change. (1) The @object-ui/plugin-timeline paragraph no longer says 'Anything that type-checked against it before still does.' It now states the break. The paragraph is labelled breaking at compile time, shipped as a minor. It says TimelineRenderer is a public export and its schema prop is now the renderer's input type: the two declared item shapes plus the composed keys color / group / meta / startDate / endDate / _data, with any string as a composed item's variant. An object literal passed to the prop whose item carries a key neither shape declares is now TS2353, with the example schema={{ type: 'timeline', items: [{ title: 'T', date: 'x' }] }} and the fix (a feed item's date is time). It lists what still type-checks: any value typed TimelineSchema, a non-literal items array (e.g. string-keyed records in a variable), empty-object items, and the composed keys. (2) The supersession paragraph now scopes 'a bar still accepts keys it does not declare' to the tolerant TimelineSchema and says the strict authoring schema refuses them by name. The minor bump on both packages is unchanged. Every claim in the new paragraph was measured with a tsc probe on this head before it was committed (see tests). origin/main 9a5f99880 was merged first with a clean text merge, no conflicts. Main had touched @object-ui/types (field-types.ts, form.ts, index.ts, select-option.ts, objectql.zod.ts, one new test), so the types type-check and tests were re-run and are green. The PR body does not repeat the false sentence; its only TimelineRenderer mention is accurate, so no body replacement is needed. The body's heading 'Verification — final head 42ef5763f' now names the pre-merge head. The seat may retitle it; this round's readings are in the gates field.",
"tests": "(1) CLAIM PROBE, BORN-FALSE guard, run under os-verify-lock on the head sources. A scratch tsc project (strict, bundler resolution, @object-ui/types path-mapped to head packages/types/src/data-display.ts) declares prop(schema: TimelineRenderSchema), the type TimelineRenderer's schema prop is declared with. Under @ts-expect-error: a fresh literal with an item { title, date } and a gantt bar { title, progress }. Plain lines that must compile: a value typed TimelineSchema; items as a Record-of-string-to-unknown array variable; items [{}]; a feed item carrying variant 'todo', color, group, meta chips, startDate, endDate and _data; a gantt bar with startDate 0 and endDate new Date(). Also a base-control line: the base element type restated verbatim accepts { title, date }. Result: tsc exit 0, so both directives fired (an unfired one is TS2578) and every compile case compiled. Positive control: the same file with the two directives removed gives tsc exit 2 with exactly two errors, 'TS2353 ... 'date' does not exist in ty…' and 'TS2353 ... 'progress' does not exist', and nothing else. The first probe attempt excluded @types/react and gave exit 2 on three 'Cannot find namespace React' errors inside data-display.ts with zero errors in the probe file; I re-ran with React types for the clean reading above and did not count the first attempt as a result. (2) pnpm --filter @object-ui/types type-check (tsc + examples + test program) -> exit 0, zero error TS lines. (3) pnpm exec vitest run packages/types/ packages/plugin-timeline/ --maxWorkers=2 -> 'Test Files 281 passed (281)', 'Tests 5729 passed (5729)', exit 0. Both ran under os-verify-lock, 'VERDICT command-exit 0 · held the lock 133s (2m13s) · waited 80s (1m20s)', on head 11725ece9 with git status clean before and after.",
"gates": [
"node scripts/check-changeset-presence.mjs -> '✅ 12 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s): .changeset/6356-timeline-item-element.md.' -> exit 0 @ 11725ece9",
"node scripts/check-changeset-no-major.mjs -> '✅ No changeset declares a `major` bump.' -> exit 0 @ 11725ece9",
"pnpm check:changeset-claims -> born-false PASS rows; '✅ Every package declared across those 1 body(ies) is either not negated'; report-only went-false list unchanged in kind (32 pending changesets name files this change touches) -> exit 0 @ 11725ece9",
"pnpm check:pending-changeset-literals -> '✅ No test source names a pending changeset.' -> exit 0 @ 11725ece9",
"pnpm check:control-bytes -> '✅ check-control-bytes: OK (scanned 9020 tracked text file(s); skipped 85 binary).' -> exit 0 @ 11725ece9; plus grep -naP control-byte self-scan of the changeset: 0 lines",
"pnpm --filter @object-ui/types type-check -> exit 0 (under lock: 'os-verify-lock: VERDICT command-exit 0 · held the lock 133s (2m13s) · waited 80s (1m20s)') @ 11725ece9",
"pnpm exec vitest run packages/types/ packages/plugin-timeline/ --maxWorkers=2 -> 'Test Files 281 passed (281)' / 'Tests 5729 passed (5729)' -> exit 0 @ 11725ece9",
"tsc claim probe (scratch project over head sources) -> main exit 0; directive-removed control exit 2 with exactly the two expected TS2353 errors (under lock, 'VERDICT batch-last-exit 0', parts' exits recorded separately: main-exit=0, control-exit=2)",
"NOT MEASURED this round: @object-ui/plugin-timeline type-check, reason: the merge did not touch plugin-timeline and this round changed no code; its build closure was not rebuilt in the fresh worktree. Last green reading: exit 0 @ 42ef5763f (round 1). CI on 11725ece9: not read, not waited on."
],
"line_budget": "n/a — no skills/** or other governed file touched",
"deviations": [
"The merge commit 2854a0c32 is a --no-ff merge of the pinned sha 9a5f99880, not a fast-forward, because the branch had its own commits. It carries the harness's model-named Co-Authored-By trailer, as do the fix commit 11725ece9 and the four round-1 commits. Reported, not rewritten (no force-push).",
"The first tsc probe attempt excluded @types/react, so it ran with errors outside the probe file (exit 2). It was re-run with React types and the clean run is the one reported; the first attempt is not counted as a reading.",
"I edited only the two sentences the review named. The PR body was not edited, as instructed. Suggested follow-up for the seat, optional and not a defect: the body's 'Verification — final head `42ef5763f`' heading now names the pre-merge head (the new head adds merge 2854a0c32 and changeset-only commit 11725ece9), and the body's 'What changes for an author' table has no row for the TimelineRenderer prop. If the seat wants one, its exact text is: '| a fresh literal passed to `TimelineRenderer`'s `schema` prop whose item carries an undeclared key (e.g. `date`) | compiles | TS2353 — the prop is the renderer's handoff type; values typed `TimelineSchema` and the composed keys still type-check |'."
],
"files_changed": [
".changeset/6356-timeline-item-element.md"
],
"mcp_calls": "0 — no MCP GitHub tool called",
"api_writes": "1 relay write as objectstack-fleet[bot]: POST /repos/objectstack-ai/objectui/issues/6356/comments (this report, via post-stamped.mjs). Plus 1 git push (42ef5763f..11725ece9, fast-forward, not REST). Zero label writes, zero PR-body writes. Reads: REST GET issues/comments/5858250761 (the review), issues/6356/comments, pulls/10822.",
"open_questions": [],
"out_of_scope_findings": []
}
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; body opens Fixes #6356, the only closing keyword; Clause-②: yes at line start.
Scope: 14 files, +979 / −124, inside the surface the ruling's re-dispatch named: the TimelineSchema region of packages/types/src/data-display.ts and its zod mirror (closed TimelineFeedItem / TimelineGanttItem arms, title / label required, the node-level superRefine that judges each element against the arm variant selects), packages/plugin-timeline/src (the internal handoff type in renderHandoff.ts, the renderer prop, ObjectTimeline.test.tsx re-spelled off the authored date), the docs page, pins re-decided (7164, 7365, mirror parity), not deleted, a new pin for this card, and one changeset (@object-ui/types minor, @object-ui/plugin-timeline minor).
Ruling fit (5857468846, Q1 = A, Q2 = C): the authored feed element declares the documented seven; the five composed keys live on the renderer-internal handoff type, which is not exported by name; the published TimelineSchema stays an interface and a ZodObject.
Governed-surface predicate on the 14 paths: NOT governed (AGENTS.md lit control: exit 3). Checks on head 11725ece9: 43 check-runs, 40 success, 3 skipped, 0 failure. mergeable_state clean at this read.
Contract review: record 5858250761 (head 42ef5763f) FAILED on one changeset sentence, a compile break at TimelineRenderer's prop the text denied; patch round, dev report 5858397783. Delta record 5858491252 — Served-tier: CONTRACT_REVIEW_TIER, head 11725ece913208630276727e5d4a1bc9914702af (the current head), independence pair present, PASS. Every sentence of the rewritten paragraphs measured true; the merge of origin/main touched none of the PR's timeline surface, and no round-1 judgment moves.
Deviation 1 (the zod arms stay .passthrough() on the tolerant face; unknown keys are refused by name only on the strict face) was judged to conform to the ruling's letter. ⇒ safeValidateSchema still accepts { title, date } today; the by-name refusal lands at objectui validate when objectui#5250 wires the strict face. The changeset says so.
Out-of-scope findings, one line each
packages/plugin-timeline/README.md spells the node component: 'timeline', which validate refuses — already filed as objectui#10824.
The renderer prop is slightly wider than the prose's summary (a handoff feed item may omit title, and time is unknown); relative to the base it is still a narrowing, and the renderHandoff.ts docblock states it. No change.
Tenant-stored timeline items carrying meta / group keys: NOT MEASURED by anyone; the box's confidence gap says so.
Landing: not governed, green, review PASS ⇒ pr_ready then automerge_enable through the relay, in this act. On merge the seat files the objectstack follow-up for object-timeline.items (Q3: objectui first, then the spec), Blocked-by: none, and objectui#6170's serial constraint on this PR clears.
objectui#6170 was held behind this PR, and that serial constraint is now clear. Its next dispatch re-reads the card on the merged tree, because Q1 = A (the five composed keys are renderer-internal) may already settle part of it.
Activity
objectstack-fleet commentedon Sep 27, 2026
Unblock:
pm:blocked→pm:queue. The prerequisite this card names is onmain; it never waited on #6170's stage 2Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01W89enF2dYV7K4N2Fbfj33f· 2026-09-27T06:40Z. ⛔ Not a claim, ⛔ not a dispatch. Read: this card (no comments), #6170's body and thread, #6354 (closedduplicate), and PR #6352.Blocked-by: #6170— the alignment has to land first, since this refines a key that card introduced」. The key isitems, introduced by PR fix(types): align TimelineSchema with the keys the timeline renderer reads #6352 (finding(plugin-timeline): thetimelinerenderer reads seven keysTimelineSchemadoes not declare, and declares three it never reads #6170 stage 1), which merged on 2026-08-25T15:36Z. That prerequisite has held for a month.timelinerenderer reads seven keysTimelineSchemadoes not declare, and declares three it never reads #6170, which is stillpm:blockedfor stage 2: the tombstoning ofevents/orientation/position, a different set of keys. That stage's carrier, Release-batch retirement (next@object-ui/typesrelease): three declared-but-unhonoured surfaces retire together as ADR-0049 tombstones — the North Star's 「不一键一卡」 batch #10060, now answers 404 (pointer on finding(plugin-timeline): thetimelinerenderer reads seven keysTimelineSchemadoes not declare, and declares three it never reads #6170, same round). Nothing in stage 2 touchesitems.Execution notes. They are unchanged from the body: it asks for its own measurement first.
meta/groupare authorable or renderer-internal.variant.The change narrows a published type ⇒
Clause-②: yes. It editsTimelineSchemainpackages/types, the same file as #6170's stage 2, so if a claim on #6170 is in flight, the dispatching seat's in-flight check sequences the two. ⛔ This is not a wait on #6170.objectstack-fleet commentedon Sep 27, 2026
Claim: PM loop round 1
Session:
session_01DuWo5bdP9SdVebamn99GGkAccount:
os-steve(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-6356-timeline-item-elementWorktree:
objectui-issue-6356Domain:
domain:uiSeat:
domain:ui#1File surface: the
TimelineSchemadeclaration region inpackages/types/src/data-display.tsand its zod mirror region inpackages/types/src/zod/data-display.zod.ts(the element type ofitems), anypackages/plugin-timeline/srcread the narrowing breaks,content/docs/plugins/plugin-timeline.mdxif its two documented shapes change, tests beside them, one.changeset/6356-…md. Measurement first: ⛔ no narrowing before the in-the-wild authoring reading exists (stop on breach; explain in the report)Container & model:
M,mode:subagent,model: opus(TIER_DEFAULT, objectstackscripts/pm/dispatch-gates.mjs:12534at0d3ec47137;--tierrefuses objectui paths)Clause-②: yes
Thread-read: 5853468236
Serial constraints cleared: read 2026-09-27T15:09Z at objectui
origin/mainc907a9c87. Draft PR objectui#10714 (objectui#9590, seat #2) editsdata-display.tsanddata-display.zod.tsin theListSchema/ListItemregion, notTimelineSchema⇒ region-disjoint; mergemainafter it lands. Draft PR objectui#8941 editspackages/types/src/objectql.ts, outside this surface. objectui#6170 (stage 2, same file) ispm:blockedand unclaimed ⇒ nothing to sequence against. Not inpm:epicobjectui#6349's subtree (GET …/6356/parentanswers 404).Why
Clause-②: yes: triage5853468236— declaring the element narrows a published type, and a new exported element type grows the public face. A contract review is owed before enqueue.domain:uiseat #1 · claim · 2026-09-27T15:09ZGenerated by Claude Code
objectstack-fleet commentedon Sep 27, 2026
os-dev-report
objectui#6356 — measurement first, then a stop:
needs_decisiondomain:uiseat #1 dispatch, dev runsession_01DuWo5bdP9SdVebamn99GGk(mode:subagent). Read at objectuibff63cc12(branch base) and objectstack0d3ec471. No PR: nothing was narrowed, per the claim's stop line. Three choices remain that change what an author may write, so they go to the seat asopen_questions. The measurement below is the deliverable. Generics and placeholders are spelled in words on purpose, because GitHub strips tag-shaped text from bodies.What the card says, checked against
mainitemsis declaredany[]". That is stale. objectui#7164 and objectui#7365 have since declared both levels, on both halves, as OBJECTS whose keys stay open (.passthrough()in the mirror, a string index signature in the TS type). The element's own keys are still undeclared, so the card's core still holds:{ title, date }type-checks and parses green (Table 3).horizontaldrops five keys thatverticalreads (Table 2), so the card's feed row in its table is the vertical read set, not a shared one.meta/groupcome fromObjectTimelineand are read byTimelineRenderer. Confirmed, and they are read by the vertical branch only.deriveStrictAuthoringSchema) refuses every legitimate timeline element today, including the schema-catalog's own feed items and gantt rows and bars (Table 3). The element and the bar declare no keys, so the face closes them to nothing. The objectui#5250 ruling saysobjectui validateand the doc-snippet gates will run strict. Declaring the element is therefore a prerequisite for wiring that face without false refusals.Timelinezero times indata-display.tsanddata-display.zod.ts, so the two changes touch separate regions.Instrument
git ls-files, on a clean worktree (so every file read is the HEAD blob). In objectui it covered 8785 files with 0 parse failures. Fenced code blocks in.mdand.mdxfiles were parsed too.typeistimeline,plugin-timeline:timeline,object-timelineorview:timeline. For registration examples and render props, the walk also counts an object whosevariantis vertical, horizontal or gantt and that carriesitems.meta,group,color,startDate,endDate,_dataanddate, once in anapps/TS file and once in askills/JSON fence. The walk reported every one of those keys.TimelineRenderer. It was deleted uncommitted, and the tree was verified clean withgit status --porcelain= 0.tsxran againstpackages/types/src.Table 1 — element keys authored in each corpus
The figure after each key is the number of elements that carry it.
plugin-timeline.mdx(1)TIMELINE.md, plugin-timelineREADME.md(2)defaultPropsplusexamples(plugin-timeline:timeline)apps/(298 files),skills/(27 files)sdui.manifest.json, 18 in tests)color,meta,group,startDateandendDateall score 0.labelanditems. 22 bars, all carryingtitle,startDate,endDateandvariant.inputsdescription lists six of them and omitsclassName.variantvalues on elements and bars are default 6, success 25, info 16, warning 12, danger 3: exactly the documented five. The marker also paintstodo,in-progressanddone(zero authored); the bar paints only the five. Lit control: the same grep hits the node-level values vertical 11, horizontal 4, gantt 5.ObjectTimeline.test.tsxauthors{ title, date }twice, typedschema: any.dateis theTimelineEventvocabulary and nothing reads it off an item, so the two tests assert the title only. This is the card's trap, live in-repo.Table 2 — what each feed branch reads, measured in render
The probe rendered one item carrying every candidate key, with
dateFormat: 'iso'.variantandcontentare read by both branches in source (theTimelineMarkervariant andrenderChildren(item.content)).label/itemsand bartitle/startDate/endDate/variant. The existing gantt suites pin that, so it was not re-probed.ObjectTimeline'seffectiveItemsmemo, which composes{ title, time, startDate, endDate, description, variant, color, group, meta, _data }per record. Its click handler reads_data.ListViewandObjectViewhand it config and never items.Table 3 — what each door answers today
safeValidateSchema, whatobjectui validateruns)unrecognized_keysat items.0 [time, title, description, variant, icon]{ title, date }variant: 'gantt'vertical/horizontalThe two cross-shape rows are the evidence for the second question below. A union at the element level accepts both of them by construction, because each is a valid arm.
Reproduce the cross-shape reading
The same shapes under their own variant are the lit control:
FEED_TITLEdraws under vertical, andROW_LABELplusBAR_TITLEdraw under gantt.{ "issue": 6356, "status": "needs_decision", "branch": "claude/issue-6356-timeline-item-element", "pr": null, "session": "session_01DuWo5bdP9SdVebamn99GGk (dev subagent; the seat's session id)", "premise_still_valid": true, "summary": "Measured first and stopped before narrowing anything. The core premise holds on bff63cc12: the element's own keys are undeclared, and { title, date } passes both TS and the tolerant mirror. Three sub-claims moved. items is no longer any[] (objectui#7164 and #7365 declared row and bar as open objects). There are three read sets, not two: horizontal drops color, meta, group, startDate and endDate, measured in render. And the strict authoring face (objectui#8345) refuses every legitimate timeline element today. Authored vocabulary across four corpora is exactly the documented seven feed keys (39 feed elements) plus label/items rows (11) and title/startDate/endDate/variant bars (22). Zero color, meta, group, startDate or endDate were authored anywhere, zero in apps/ and skills/, and zero in objectstack. Cross-shape items (a feed item under gantt, a gantt row under vertical or horizontal) are accepted and render empty with no diagnostic. Three choices change what an author may write (open_questions), so this returns needs_decision, with one recommendation per question.", "tests": "No gate owed: zero-diff branch (no commits; origin branch == base bff63cc12). Measurements: (1) static walk over git ls-files: objectui 8785 files, 0 parse failures; objectstack 9418 files, 2 JSONC tsconfig parse failures (irrelevant). Lit control: a synthetic repo authoring meta, group, color, startDate, endDate, _data and date, all reported. (2) Render probe through bash os-verify-lock.sh -c 'pnpm exec vitest run packages/plugin-timeline/src/__tests__/zz-probe-6356.test.tsx', three runs, each 'Test Files 1 passed (1)' and 'os-verify-lock: VERDICT command-exit 0'. The probe file was deleted uncommitted; git status --porcelain = 0 before the worktree was removed. (3) Strict-face probe with tsx on packages/types/src (deriveStrictAuthoringSchema(TimelineSchema)), exit 0, readings in Table 3. (4) zod 4.4.3 check for question 2 option C: a superRefine'd object keeps type object, .shape and discriminatedUnion membership, but .extend() that OVERWRITES a key throws 'Cannot overwrite keys on object schemas containing refinements. Use .safeExtend() instead.'", "gates": [ "none run: zero-diff branch, no files changed; measurement probes only (render probe VERDICT command-exit 0, x3)" ], "line_budget": "n/a: no skills/** or other governed file touched (no file touched at all)", "deviations": [ "No PR opened, so neither pr_create nor the PR-assignee label-write was spent. Reason: needs_decision, and the claim's stop line forbids narrowing before a ruling.", "A temporary probe test file was created in the worktree to take the render readings, then deleted uncommitted; the tree was verified clean before git worktree remove.", "The pushed branch claude/issue-6356-timeline-item-element sits at base bff63cc12 with no commits. It remains on origin as the claim's landing marker (this session cannot delete remote refs).", "The report fields gates, line_budget, deviations and files_changed were added to the role-file template at the PM's request.", "The measurement table lives in this comment rather than in a PR body, because there is no PR." ], "files_changed": [], "mcp_calls": "0", "api_writes": "1: POST /repos/objectstack-ai/objectui/issues/6356/comments (this report, via objectstack scripts/pm/post-stamped.mjs). Reads, not writes: REST GET issue 6356 and its comments, and GET pulls/10714/files.", "open_questions": [ { "question": "Q1. Are meta and group, and the other three keys that only the composed path produces on a feed item (color, startDate, endDate), authorable element keys or renderer-internal?", "options": [ "A. Renderer-internal. The authored feed element declares only the documented seven (time, title, description, variant, icon, content, className). The composed-only five plus _data get an internal type inside plugin-timeline for the ObjectTimeline-to-TimelineRenderer handoff. The renderer keeps reading them, so it stays more lenient than validate (the house posture), and the strict face refuses them when authored. Cost: someone who authors meta or group on a vertical timeline today gets chips and headers, and would be refused once the strict face is wired. Measured authored instances: zero.", "B. Authorable, vertical only. This needs the element discriminated three ways (vertical, horizontal, gantt), because horizontal drops all five in render. Cost: five keys declared with zero authored pull, and a three-arm discrimination.", "C. Authorable on one shared feed element. Not viable: it declares keys horizontal silently drops (Table 2), which is a declared capability the runtime does not honour." ], "recommendation": "A. Axis 1 (real need): zero authored across four authoring corpora, apps/, skills/ and objectstack, and the sole producer is ObjectTimeline's own composition. Axis 2 (long term): an internal handoff is typed where it lives, and the author face becomes the documented face, which the docs, the registration and every authored corpus already agree on. Axis 3 (AI errors): fewer declared keys means fewer authorings that horizontal silently drops, and the strict face refuses the rest. Axis 4 (no expansion): no pull, so no declaration. This is the same posture objectui#10222 (letter A) took for the chip-field list that produces meta." }, { "question": "Q2. Should the element be one union, or a pair discriminated by the node's variant? And if a pair, in which form?", "options": [ "A. Element union (feed item or gantt row); TimelineSchema stays an interface and a ZodObject. Cheapest. It refuses unknown keys (TS literals get excess-key errors against the closed arms, and the strict face closes them) but NOT cross-shape items, which Table 3 shows are silent today.", "B. Pair as a split node: TS TimelineSchema becomes a union type alias (feed node or gantt node) and the zod mirror becomes a union. It refuses cross-shape in TS and in validate. Cost: the published zod TimelineSchema stops being a ZodObject, so .shape and .extend are gone. Four in-repo test files use them, and the parity ratchet pairs every mirror by reading its own .shape. The node-level tombstones (timeScale, body, children) and the three deprecated keys must ride on both arms.", "C. Pair as a refinement: the TS element union from A, and the zod mirror stays a ZodObject with a node-level superRefine that judges each element against the arm variant selects (absent means vertical). This is the house pattern for cross-key rules (calendarSelectionFitsMode, requireRecordSource). It refuses cross-shape at objectui validate only, because TS cannot see the parent key. Costs: .extend key-overwrite throws on a refined object in zod 4.4.3, so the two tests that do TimelineSchema.extend with an items override move to .safeExtend. On the tolerant, passthrough face, cross-shape is refusable only through at least one REQUIRED key per arm. Measured: every authored feed element carries title and time, every row carries label and items, every bar carries title, startDate and endDate. But row items is ruled optional (objectui#6750, objectui#7164), so label is the gantt arm's only discriminator, and making label (and feed title) required is new semantics." ], "recommendation": "C, with feed title and gantt label required, and the element and bar variant declared as the documented five (all authored values fall inside it; the composed path keeps record values through its internal type). Axis 3: it refuses both unknown keys and cross-shape at the JSON door AI authors through, and TS literals still get excess-key refusal. Axis 2: TimelineSchema stays an interface and a ZodObject, so the parity ratchet, the tombstones and DataDisplaySchema's discriminated union are untouched; B's restructuring buys only TS-level cross-shape refusal. Axis 4: no key beyond the measured ones, and the two required keys are present in 100% of authored elements. Axis 1: gantt is authored in all four authoring corpora, and the cross-shape failure is measured silent." }, { "question": "Q3. How should this be sequenced with @objectstack/spec? The spec's object-timeline element declares items as z.array(z.unknown()) ('the author owns the item shape'), and its items reach the same TimelineRenderer.", "options": [ "A. objectui first, on its own presentational node timeline. Then an objectstack follow-up takes the element vocabulary for object-timeline.items, the way the spec already defers to objectui for the objectui-owned mapping contract (z.unknown(), 'its contract still lives in objectui').", "B. Spec first; objectui mirrors after.", "C. objectui only; the spec door stays open." ], "recommendation": "A. Axis 2: the vocabulary's implementation, docs and only producer all live in objectui, and the spec's stated posture for an objectui-owned contract is to follow objectui. The base principle 'spec declaration over implementation' points toward B, so this is a real conflict, surfaced here rather than silently resolved. Axis 3: C leaves os validate open for the same element that objectui validate would refuse. Axis 4: A adds no gate and no staged window." } ], "out_of_scope_findings": [ "carrier: objectui#6170 (stage 2, events/orientation/position) · packages/types/examples/data-display-examples.json authors a timeline with events (zero read points), not items · noted, not filed", "carrier: the implementation PR for this card after the ruling · ObjectTimeline.test.tsx authors { title, date } items (date is silently dropped; typed schema: any, so a narrowing will not redden them); the implementation should re-spell them time · noted, not filed", "carrier: none (承接者:无) · packages/components/src/renderers/complex/TIMELINE.md lists three gantt time scales where six exist · documentation drift, Acceptance-notes class, not filed" ] }objectstack-fleet commentedon Sep 27, 2026
Measurement delivered ⇒ two contract-shape questions to the box ·
domain:uiseat #1 · 2026-09-27T15:55Zsession_01DuWo5bdP9SdVebamn99GGk(os-steve). The card is measurement-first. The dev measured, wrote nothing (the claim's stop line), and returned three questions (report5857222802, which holds the tables). The seat re-read the one reading everything rests on: on objectuiorigin/main,TimelineSchema.itemsisz.array(TimelineRowSchema)(packages/types/src/zod/data-display.zod.ts:1135), not the body'sany[]. The element is an object with its keys still open. ⇒ The card's core stands, and its body's framing is stale.Release:
session_01DuWo5bdP9SdVebamn99GGk· cause: measurement done, two open contract-shape choices · destination:needs-user-decision. The claim5857054090is spent. The branchclaude/issue-6356-timeline-item-elementholds no commits beyond base. The next dispatch re-claims.Governing text: this card's triage block (「
items… enforces nothing … an AI authoring timeline items meets no refusal」, maintainer-confirmed); triage5853468236(the three execution questions,Clause-②: yes); objectui#10222 letter A (the same posture for the chip-field list that producesmeta); the pm-dispatch base principle 「spec 声明 > 实现 > 文档面」.One-line problem
An author (human or AI) can put any keys on a timeline item, or put a gantt row on a feed timeline. Both are accepted, and the wrong parts just do not draw. In addition, the strict authoring check that
objectui validateis due to run would refuse every legitimate timeline element today, because the element declares no keys at all (measured, report Table 3).Q1 —
meta,group,color,startDate,endDateon a feed item: authorable, or renderer-internal?time, title, description, variant, icon, content, className). The five composed-only keys get an internal type insideplugin-timelinefor theObjectTimeline→ renderer handoff.apps/,skills/and objectstack. Anyone who authorsmetatoday would be refused once the strict face is wired.Q2 — one union, or a pair discriminated by the node's
variant?TimelineSchemastays an interface and aZodObject.TimelineSchemabecomes a union alias and the zod mirror a union.TimelineSchemastops being aZodObject(.shape/.extendgone; 4 test files and the parity ratchet use them); the tombstones ride on both arms.superRefinein the zod mirror that judges each element against the armvariantselects (absent ⇒ vertical). This is the house pattern for cross-key rules (calendarSelectionFitsMode,requireRecordSource)..extendto.safeExtend.业务含义直译
四轴论证(业务立场)
TimelineSchema保持对象形状,一致性棘轮、墓碑、DataDisplaySchema的判别联合都不动,所以 Q2 取 C。参照主流平台:这类组件的条目 schema 是闭合的,内部渲染字段不外露。推荐:Q1 取 A,Q2 取 C(feed 的
title与 gantt 的label设为必填,两者在已写元素中出现率均为 100%)。回退:Q2 取 A(先拦未知键,串用另立卡)。自检:只看①选 Q1-A / Q2-C;②③④ 是否翻转:否。
置信缺口:租户存储里是否有人写了
meta/group等键,本席看不见;若有,接上严格校验后这些记录会被拒(今天它们在横向布局下本就不画)。Q3 — sequencing with
@objectstack/spec— decided by this seat, ⛔ not escalatedOrder of technical work is a non-escalation class. Decision: objectui first, then the spec follows (the dev's A). The vocabulary's implementation, docs and only producer all live in objectui, and the spec's own⚠️ This runs against 「spec 声明 > 实现」 read literally, and it is recorded here so the ruling can overturn it in the same stroke.
object-timeline.itemsisz.array(z.unknown())with 「the author owns the item shape」. On landing, this seat files the objectstack follow-up (Blocked-by:this card's PR), which lets the spec take the element vocabulary forobject-timeline.items.After the ruling
Re-dispatch with
Clause-②: yes, surface = theTimelineSchemaregion ofpackages/types/src/data-display.tsand its zod mirror region,packages/plugin-timeline/src(the internal handoff type, and re-spellingObjectTimeline.test.tsx's{ title, date }items),content/docs/plugins/plugin-timeline.mdx, tests, and aminorchangeset for@object-ui/types. The measurement tables in5857222802are the dev's starting data and are not re-derived.domain:uiseat #1 · box · 2026-09-27T15:55ZGenerated by Claude Code
objectstack-fleet commentedon Sep 27, 2026
Ruling: maintainer · Q1 letter A 「A 内部键(推荐)」 · Q2 letter C 「C 联合+校验判别(推荐)」 · 2026-09-27T16:01Z
Recorded by the
domain:uiseat #1 (session_01DuWo5bdP9SdVebamn99GGk,os-steve). ⛔ Not this seat's adjudication: the maintainer answered the two box questions of5857426543in this session's chat. The quoted labels are the options chosen, verbatim. Freshness: the thread was re-read in this act, and5857426543is the newest comment.Ruled.
time, title, description, variant, icon, content, className.meta,group,color,startDate,endDate(and_data) are renderer-internal. They get an internal handoff type insideplugin-timelineforObjectTimeline→TimelineRenderer, and the strict face refuses them when authored.TimelineSchemastays aZodObject, with a node-levelsuperRefinethat judges each element against the armvariantselects (absent ⇒ vertical). Feedtitleand ganttlabelare required, and the element / barvariantis declared as the documented five. The two.extendcall sites move to.safeExtend.5857426543and stands: objectui first; the objectstack follow-up forobject-timeline.itemsis filed on landing.State note. The box's
Release:line never reached the labels: the flip toneeds-user-decisionwas refused by the write-pace budget, and the ruling arrived before it could be retried. ⇒ The card stayedpm:dispatched/os-stevethroughout. The release is recorded as spent in the next act, which is a freshClaim:.domain:uiseat #1 · ruling record · 2026-09-27T16:01ZGenerated by Claude Code
objectstack-fleet commentedon Sep 27, 2026
Claim: PM loop round 1
Session:
session_01DuWo5bdP9SdVebamn99GGkAccount:
os-steve(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-6356-timeline-item-elementWorktree:
objectui-issue-6356Domain:
domain:uiSeat:
domain:ui#1File surface: the
TimelineSchema/ timeline element region ofpackages/types/src/data-display.tsand ofpackages/types/src/zod/data-display.zod.ts(element union, node-levelsuperRefine, requiredtitle/label, element and barvariant);packages/plugin-timeline/src(the internal handoff type for the composed-only keys;ObjectTimeline.test.tsx's{ title, date }items re-spelledtime); the two.extend→.safeExtendtest call sites;content/docs/plugins/plugin-timeline.mdxwhere its documented shapes change; tests beside them; oneminorchangeset for@object-ui/types(plus@object-ui/plugin-timelineif its published source changes) (stop on breach; explain in the report)Container & model:
M,mode:subagent,model: opus(TIER_DEFAULT, objectstackscripts/pm/dispatch-gates.mjs:12534at0d3ec47137;--tierrefuses objectui paths)Clause-②: yes
Thread-read: 5857468846
Serial constraints cleared: read 2026-09-27T16:02Z. Draft PR objectui#10714 (objectui#9590) edits
data-display.ts/data-display.zod.tsin theListSchema/ListItemregion only (its patches nameTimelinezero times, per report5857222802) ⇒ region-disjoint; mergemainafter it lands. No other open PR or in-flight claim in this lane names these files. objectui#6170 (stage 2, same file) ispm:blockedand unclaimed.Re-claim under the maintainer's ruling
5857468846. WhyClause-②: yes: a published@object-ui/typeselement type and its authoring mirror narrow (unknown element keys and cross-shape elements become refused).domain:uiseat #1 · claim · 2026-09-27T16:02ZGenerated by Claude Code
objectstack-fleet commentedon Sep 27, 2026
os-dev-report
{ "issue": 6356, "status": "done", "branch": "claude/issue-6356-timeline-item-element", "pr": "https://github.com/objectstack-ai/objectui/pull/10822", "session": "session_01DuWo5bdP9SdVebamn99GGk — dev subagent (mode:subagent); the seat's session id, as the harness-stamped Claude-Session line carries it", "premise_still_valid": true, "summary": "Implemented the maintainer ruling 5857468846 (Q1 = A, Q2 = C) on objectui base c58b8e4a0, final head 42ef5763f, draft PR #10822. TS: TimelineSchema.items is now an array of TimelineFeedItem (the seven documented keys, title required) or TimelineGanttItem (label required, bars TimelineGanttItemBar with all-optional keys), item and bar variant = the documented five (TimelineItemVariant); arms are closed, so { title, date } is TS2353. zod: TimelineSchema stays a ZodObject and ends in .superRefine(timelineItemsFitVariant), which judges each element against the arm variant selects (absent => vertical): required key present, no key of the other arm; cross-shape is refused at the public validate door (safeValidateSchema answers custom @ items.0.title / items.0.label / items.0.items for a gantt row on a vertical timeline). The node SHAPE declares both arms' keys (optional) rather than an element-level z.union, because a union was measured to fold objectui#7164/#7365's pinned paths into one invalid_union at items.0; the strict face therefore names meta/group/color/startDate/endDate/_data/date by name (unrecognized_keys at items.0) and now accepts the schema-catalog documents it refused before. The composed-only keys are typed only in plugin-timeline/src/renderHandoff.ts (not exported), which is TimelineRenderer's prop type. Zone 2: all four assumptions hold as measured (details in PR body); superRefine leaves ZodObject class, .shape, DataDisplaySchema dispatch, the parity ratchet and the timeScale/body/children tombstones intact; .extend over an existing key throws, both control sites moved to .safeExtend. PR objectui#10714 was still an open draft at merge time, region-disjoint. ObjectTimeline.test.tsx items re-spelled time and typed, so the compiler now refuses date there. Choices beyond the ruling's letter are listed for the Clause-② contract review in the PR body (registered mirror pairs 159 -> 162, four new TS exports and three zod exports, requiredness kept to title/label only, bar dates = string | finite number | Date per objectui#6781, bar declared as a type alias).", "tests": "All heavy runs via os-verify-lock.sh, exit codes by redirect-then-status, on head 42ef5763f unless noted. (1) pnpm --filter @object-ui/types type-check -> exit 0 (tsc + tsconfig.examples + tsconfig.test, the last judges the parity ratchet and the new @ts-expect-error pins). (2) pnpm --filter @object-ui/types build -> exit 0; dist/data-display.d.ts carries TimelineFeedItem. (3) pnpm --filter @object-ui/plugin-timeline type-check -> exit 0 (reads @object-ui/types from that rebuilt dist; its closure was built first with turbo run build --filter=@object-ui/plugin-timeline... --concurrency=2, 11/11 tasks). (4) pnpm exec vitest run packages/types/ packages/plugin-timeline/ --maxWorkers=2 -> 'Test Files 280 passed (280)', 'Tests 5722 passed (5722)', exit 0. (5) REVERSE CHECK across the package boundary via objectstack scripts/ablation-replace.mjs (wrap mode): planting date on the typed item in ObjectTimeline.test.tsx -> plugin-timeline tsc -p tsconfig.test.json: 'error TS2353: Object literal may only specify known properties, and 'date' does not exist in type 'TimelineFeedItem | TimelineGanttItem'', child exit 2; mutation landed (anchor 1 -> 0, blob a6f953ca63f1 -> af4a03fb097c); restored blob == HEAD a6f953ca63f1, git diff HEAD empty. (6) ABLATION via ablation-replace.mjs: '}).superRefine(timelineItemsFitVariant);' -> '});' in data-display.zod.ts (anchor 1 -> 0, blob ee4326c02190 -> 02f2aa66b150) -> vitest on timeline-item-element-6356.test.ts: 'Failed Tests 10', exactly the refinement-dependent pins (cross-shape x5, required keys, strict-clone cross-shape, DataDisplaySchema dispatch, safeExtend keeps refinement, TS cross-shape validate leg); the strict by-name and vocabulary pins stayed green as expected; child exit 1; restored blob == HEAD ee4326c02190, git diff HEAD empty. Direction observed = the usual one (turns red). The pin file imports ../zod/data-display.zod.js relatively, so no dist/ is on the ablation's resolution path. (7) check:doc-snippets after its declared --build-filter closure (turbo build exit 0) -> 'Semantic phase: 676 of 676 block(s) judged, 0 failed.' exit 0; check:doc-examples exit 0. (8) eslint (root config, as each package's lint script runs it) over the 12 changed .ts/.tsx files, --format json -> 12 files, 0 errors, 80 warnings (warnings are not ratcheted; 2 are the new z.ZodType-any-any annotation in the 7365 control); population = the 12 files of git diff --name-only c58b8e4a0 HEAD filtered to ts/tsx, count read from the JSON output; the config enables no type-aware linting, so this diff cannot move any untouched file's verdict. Final tree after all legs: git status --porcelain = 0 lines.", "gates": [ "pnpm --filter @object-ui/types type-check -> (inside verify2 under the lock) exit 0; lock line 'os-verify-lock: VERDICT command-exit 0 · held the lock 147s (2m27s) · waited 241s (4m01s)' @ 42ef5763f", "pnpm --filter @object-ui/types build -> exit 0 @ 42ef5763f", "pnpm --filter @object-ui/plugin-timeline type-check -> exit 0 @ 42ef5763f", "pnpm exec vitest run packages/types/ packages/plugin-timeline/ --maxWorkers=2 -> 'Test Files 280 passed (280)' / 'Tests 5722 passed (5722)' -> exit 0 @ 42ef5763f", "turbo run build --filter=@object-ui/plugin-timeline... --concurrency=2 -> 'Tasks: 11 successful, 11 total' -> exit 0 (verify round 1, 4a07d05d5; src unchanged since except one test annotation)", "pnpm check:doc-snippets (after turbo build over its --build-filter, exit 0) -> 'Semantic phase: 676 of 676 block(s) judged, 0 failed.' -> exit 0; lock line 'os-verify-lock: VERDICT command-exit 0 · held the lock 218s (3m38s) · waited 47s' @ 42ef5763f", "pnpm check:doc-examples -> 'Every covered `@example` compiles, or fails exactly as its ledger row declares.' -> exit 0 @ 42ef5763f", "pnpm check:control-bytes -> '✅ check-control-bytes: OK (scanned 8995 tracked text file(s); skipped 85 binary).' -> exit 0 @ 42ef5763f", "pnpm check:new-line-citations -> 'VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0' -> exit 0 @ 42ef5763f", "pnpm check:test-path-roots -> '✅ check-test-path-roots: OK' -> exit 0 @ 42ef5763f", "pnpm check:handler-key-reads -> 'OK 105 arm(s), 211 registration(s) ...' -> exit 0 @ 42ef5763f", "pnpm check:doc-fences -> '✅ check:doc-fences — every TypeScript block in 227 document(s) is fenced ...' -> exit 0 @ 42ef5763f", "pnpm check:doc-types -> '✅ Every documented component type is registered.' -> exit 0 @ 42ef5763f", "pnpm check:doc-example-ids -> '✅ 414 real reference(s) all resolve in the catalog registry.' -> exit 0 @ 42ef5763f", "pnpm check:doc-example-readers -> 'OK 79 documented symbol(s) ...' -> exit 0 (@ 42ef5763f tree, run once)", "node scripts/check-changeset-presence.mjs -> '✅ 12 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s): .changeset/6356-timeline-item-element.md.' -> exit 0 @ 42ef5763f", "node scripts/check-changeset-no-major.mjs -> '✅ No changeset declares a `major` bump.' -> exit 0 @ 42ef5763f", "pnpm check:changeset-claims -> report-only; went-false requests on the pending objectui#7164 / objectui#7365 changesets answered in this PR's changeset text; self-contradiction reading '✅ Every package declared ... is either not negated' -> exit 0 @ 42ef5763f", "pnpm check:component-surface-parity -> 'component-surface-parity: REPORT-ONLY (objectui#4631) -- exits 0 with findings.' -> exit 0; its one timeline row (plugin-timeline:timeline.scale) exists on base, only its line moved", "node scripts/check-governed-queue-guard.mjs --test (14 changed paths) -> '✅ NOT GOVERNED — 14 path(s) checked against 5 governed surface(s); none matched.' -> exit 0", "eslint --format json over the 12 changed ts/tsx files -> 12 files, 0 errors -> exit 0 @ 42ef5763f", "CI on PR #10822 head 42ef5763f -> in_progress at report time (42 check runs: 22 success, 3 skipped, 17 in progress); not waited on, per the role file" ], "line_budget": "n/a — no skills/** or other governed file touched (governed-queue-guard --test: NOT GOVERNED, 14 paths)", "deviations": [ "Zone 3 route said 'closed objects in both faces'. The TS arms are closed; the zod arms stay .passthrough() on the TOLERANT face and are closed only by the strict authoring face. Reason: the ruling says 'the strict face refuses them when authored' and the retirement posture names the strict face; closing the tolerant face would also refuse undeclared keys at objectui validate today, which no ruling ordered.", "The zod element in the node SHAPE is not a z.union of the two arms but an object declaring both arms' keys (optional), with the static type asserted to the TS union (the NODE's accept set, enforced by the refinement). Measured reason: an element-level union folds objectui#7164 / #7365's path-pinned refusals into one invalid_union at items.0 and names an unknown key twice on the strict face. Documented in the zod docblock; same assertion spelling as nodeUnion in base.zod.ts. Flagged for the Clause-② contract review.", "Registered TimelineFeedItemSchema / TimelineGanttItemSchema / TimelineGanttItemBarSchema as exported mirror pairs (zod-mirror-parity.test.ts MIRRORS + Declared, EXPECTED_MIRROR_PAIRS 159 -> 162) and put them on the ./zod barrel; added TimelineFeedItem / TimelineGanttItem / TimelineGanttItemBar / TimelineItemVariant to the root TS barrel. Not named by the brief; it is what keeps the element keys under the parity ratchet despite the static assertion. Public-surface growth, flagged for the contract review.", "Brief's gate list named type-check for all dependents of @object-ui/types; declared narrowing: only @object-ui/types and @object-ui/plugin-timeline were type-checked locally. Evidence: TimelineSchema is imported only by plugin-timeline (git grep over the repo), and the only other TS type: 'timeline' literals are list-view types (six hits in apps/console, core, plugin-view tests). CI runs the full set.", "The four commits (6929275cc, 955a457d5, 4a07d05d5, 42ef5763f) carry the harness's model-named Co-Authored-By trailer, which the role file says to report and not imitate (trailers model-free). Reported, not rewritten: history is not amended and objectui forbids force-push.", "Two lock acquisitions returned exit 99 (queue-timeout) behind another seat's 30+ minute locked vitest sweep (holder pid 31779, issue-10594 verify.sh, alive and CPU-busy); the slot objectui-6356-dev kept my place and the later runs acquired. Heavy runs were launched in the background and waited on in the foreground with tail --pid, because lock wait plus run exceeded one foreground call.", "Pending changesets for objectui#7164 and objectui#7365 (same release) state that row and bar keys stay undeclared and feed timelines are untouched; not edited (they are other changes' records). This PR's changeset names what it supersedes instead.", "TimelineGanttItemBar is a type alias, not an interface: the parity ratchet read the gantt pair's items as narrower-than-declared only because an interface has no implicit index signature against the passthrough bar's input type." ], "files_changed": [ ".changeset/6356-timeline-item-element.md", "content/docs/plugins/plugin-timeline.mdx", "packages/plugin-timeline/src/ObjectTimeline.test.tsx", "packages/plugin-timeline/src/ObjectTimeline.tsx", "packages/plugin-timeline/src/renderHandoff.ts", "packages/plugin-timeline/src/renderer.tsx", "packages/types/src/__tests__/timeline-item-element-6356.test.ts", "packages/types/src/__tests__/timeline-items-bar-shape-7365.test.ts", "packages/types/src/__tests__/timeline-items-row-shape-7164.test.ts", "packages/types/src/__tests__/zod-mirror-parity.test.ts", "packages/types/src/data-display.ts", "packages/types/src/index.ts", "packages/types/src/zod/data-display.zod.ts", "packages/types/src/zod/index.zod.ts" ], "mcp_calls": "0 — no MCP GitHub tool called", "api_writes": "3 relay writes as objectstack-fleet[bot]: POST /repos/objectstack-ai/objectui/pulls (draft PR #10822, via with-fleet.sh --via dispatch, run 36336820064); POST /repos/objectstack-ai/objectui/issues/10822/assignees (os-steve, via label-write.mjs, run 36336860100, read back MATCHES); POST /repos/objectstack-ai/objectui/issues/6356/comments (this report, via post-stamped.mjs). Plus 3 git push to the branch (not REST). Zero label writes. Reads only otherwise: REST GET issue 6356 and its comments, pulls/10714 (twice), pulls?head=, pulls/10822, commits/42ef5763f/check-runs.", "open_questions": [], "out_of_scope_findings": [ "class: a · reach: public door — safeValidateSchema (what objectui validate runs) refuses the packages/plugin-timeline/README.md 'Schema-Driven Usage' document exactly as written (invalid_union @ type) and accepts it respelled type: 'timeline' · evidence: the README spells the node component: 'timeline', and README.md is in the package's published files list · dedupe words: plugin-timeline README component, Schema-Driven Usage component timeline, README component instead of type", "carrier: 承接者:无 · packages/components/src/renderers/complex/TIMELINE.md lists three gantt time scales where six exist (carried from the first dev's report) · noted in PR Acceptance notes, not filed" ] }Generated by Claude Code
objectstack-fleet commentedon Sep 27, 2026
REWORK (round 1) — PR objectui#10822 ·
domain:uiseat #1 · 2026-09-27T17:47Zsession_01DuWo5bdP9SdVebamn99GGk(os-steve). Same claim5857481990, same branch; the patch round went to the original dev.Contract review
5858250761(head42ef5763f,Served-tier: CONTRACT_REVIEW_TIER) is FAIL on one defect. Everything else was judged conforming to ruling5857468846: a 50-row probe on head and base found the TS face and the tolerant zod face pure narrowings; the strict face refuses the composed keys by name; the refinement holds for everyvariant; and the new exports are necessary for the ratchet..changeset/6356-timeline-item-element.mdsays of@object-ui/plugin-timeline「Anything that type-checked against it before still does.」 That is false.TimelineRendereris public and itsschemaprop narrowed, so an element literal carrying an undeclared key (for exampledate) was accepted at base and isTS2353at head. That sentence publishes verbatim into the CHANGELOG. Fix: spell the break; the bump staysminor.Not this PR's defects, noted:
safeValidateSchemastill accepts{ title, date }on the tolerant face until objectui#5250 wires the strict face; the READMEcomponent: 'timeline'refusal is filed as objectui#10824.domain:uiseat #1 · REWORK · 2026-09-27T17:47ZGenerated by Claude Code
objectstack-fleet commentedon Sep 27, 2026
os-dev-report
{ "issue": 6356, "status": "done", "branch": "claude/issue-6356-timeline-item-element", "pr": "https://github.com/objectstack-ai/objectui/pull/10822", "head": "11725ece913208630276727e5d4a1bc9914702af", "round": "PATCH ROUND (REWORK, round 1) — contract review 5858250761 (VERDICT FAIL on one changeset sentence), same branch, same claim 5857481990", "session": "session_01DuWo5bdP9SdVebamn99GGk — dev subagent (mode:subagent); the seat's session id", "premise_still_valid": true, "summary": "Fixed the one defect contract review 5858250761 found, in .changeset/6356-timeline-item-element.md only; no code change. (1) The @object-ui/plugin-timeline paragraph no longer says 'Anything that type-checked against it before still does.' It now states the break. The paragraph is labelled breaking at compile time, shipped as a minor. It says TimelineRenderer is a public export and its schema prop is now the renderer's input type: the two declared item shapes plus the composed keys color / group / meta / startDate / endDate / _data, with any string as a composed item's variant. An object literal passed to the prop whose item carries a key neither shape declares is now TS2353, with the example schema={{ type: 'timeline', items: [{ title: 'T', date: 'x' }] }} and the fix (a feed item's date is time). It lists what still type-checks: any value typed TimelineSchema, a non-literal items array (e.g. string-keyed records in a variable), empty-object items, and the composed keys. (2) The supersession paragraph now scopes 'a bar still accepts keys it does not declare' to the tolerant TimelineSchema and says the strict authoring schema refuses them by name. The minor bump on both packages is unchanged. Every claim in the new paragraph was measured with a tsc probe on this head before it was committed (see tests). origin/main 9a5f99880 was merged first with a clean text merge, no conflicts. Main had touched @object-ui/types (field-types.ts, form.ts, index.ts, select-option.ts, objectql.zod.ts, one new test), so the types type-check and tests were re-run and are green. The PR body does not repeat the false sentence; its only TimelineRenderer mention is accurate, so no body replacement is needed. The body's heading 'Verification — final head 42ef5763f' now names the pre-merge head. The seat may retitle it; this round's readings are in the gates field.", "tests": "(1) CLAIM PROBE, BORN-FALSE guard, run under os-verify-lock on the head sources. A scratch tsc project (strict, bundler resolution, @object-ui/types path-mapped to head packages/types/src/data-display.ts) declares prop(schema: TimelineRenderSchema), the type TimelineRenderer's schema prop is declared with. Under @ts-expect-error: a fresh literal with an item { title, date } and a gantt bar { title, progress }. Plain lines that must compile: a value typed TimelineSchema; items as a Record-of-string-to-unknown array variable; items [{}]; a feed item carrying variant 'todo', color, group, meta chips, startDate, endDate and _data; a gantt bar with startDate 0 and endDate new Date(). Also a base-control line: the base element type restated verbatim accepts { title, date }. Result: tsc exit 0, so both directives fired (an unfired one is TS2578) and every compile case compiled. Positive control: the same file with the two directives removed gives tsc exit 2 with exactly two errors, 'TS2353 ... 'date' does not exist in ty…' and 'TS2353 ... 'progress' does not exist', and nothing else. The first probe attempt excluded @types/react and gave exit 2 on three 'Cannot find namespace React' errors inside data-display.ts with zero errors in the probe file; I re-ran with React types for the clean reading above and did not count the first attempt as a result. (2) pnpm --filter @object-ui/types type-check (tsc + examples + test program) -> exit 0, zero error TS lines. (3) pnpm exec vitest run packages/types/ packages/plugin-timeline/ --maxWorkers=2 -> 'Test Files 281 passed (281)', 'Tests 5729 passed (5729)', exit 0. Both ran under os-verify-lock, 'VERDICT command-exit 0 · held the lock 133s (2m13s) · waited 80s (1m20s)', on head 11725ece9 with git status clean before and after.", "gates": [ "node scripts/check-changeset-presence.mjs -> '✅ 12 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s): .changeset/6356-timeline-item-element.md.' -> exit 0 @ 11725ece9", "node scripts/check-changeset-no-major.mjs -> '✅ No changeset declares a `major` bump.' -> exit 0 @ 11725ece9", "pnpm check:changeset-claims -> born-false PASS rows; '✅ Every package declared across those 1 body(ies) is either not negated'; report-only went-false list unchanged in kind (32 pending changesets name files this change touches) -> exit 0 @ 11725ece9", "pnpm check:pending-changeset-literals -> '✅ No test source names a pending changeset.' -> exit 0 @ 11725ece9", "pnpm check:control-bytes -> '✅ check-control-bytes: OK (scanned 9020 tracked text file(s); skipped 85 binary).' -> exit 0 @ 11725ece9; plus grep -naP control-byte self-scan of the changeset: 0 lines", "pnpm --filter @object-ui/types type-check -> exit 0 (under lock: 'os-verify-lock: VERDICT command-exit 0 · held the lock 133s (2m13s) · waited 80s (1m20s)') @ 11725ece9", "pnpm exec vitest run packages/types/ packages/plugin-timeline/ --maxWorkers=2 -> 'Test Files 281 passed (281)' / 'Tests 5729 passed (5729)' -> exit 0 @ 11725ece9", "tsc claim probe (scratch project over head sources) -> main exit 0; directive-removed control exit 2 with exactly the two expected TS2353 errors (under lock, 'VERDICT batch-last-exit 0', parts' exits recorded separately: main-exit=0, control-exit=2)", "NOT MEASURED this round: @object-ui/plugin-timeline type-check, reason: the merge did not touch plugin-timeline and this round changed no code; its build closure was not rebuilt in the fresh worktree. Last green reading: exit 0 @ 42ef5763f (round 1). CI on 11725ece9: not read, not waited on." ], "line_budget": "n/a — no skills/** or other governed file touched", "deviations": [ "The merge commit 2854a0c32 is a --no-ff merge of the pinned sha 9a5f99880, not a fast-forward, because the branch had its own commits. It carries the harness's model-named Co-Authored-By trailer, as do the fix commit 11725ece9 and the four round-1 commits. Reported, not rewritten (no force-push).", "The first tsc probe attempt excluded @types/react, so it ran with errors outside the probe file (exit 2). It was re-run with React types and the clean run is the one reported; the first attempt is not counted as a reading.", "I edited only the two sentences the review named. The PR body was not edited, as instructed. Suggested follow-up for the seat, optional and not a defect: the body's 'Verification — final head `42ef5763f`' heading now names the pre-merge head (the new head adds merge 2854a0c32 and changeset-only commit 11725ece9), and the body's 'What changes for an author' table has no row for the TimelineRenderer prop. If the seat wants one, its exact text is: '| a fresh literal passed to `TimelineRenderer`'s `schema` prop whose item carries an undeclared key (e.g. `date`) | compiles | TS2353 — the prop is the renderer's handoff type; values typed `TimelineSchema` and the composed keys still type-check |'." ], "files_changed": [ ".changeset/6356-timeline-item-element.md" ], "mcp_calls": "0 — no MCP GitHub tool called", "api_writes": "1 relay write as objectstack-fleet[bot]: POST /repos/objectstack-ai/objectui/issues/6356/comments (this report, via post-stamped.mjs). Plus 1 git push (42ef5763f..11725ece9, fast-forward, not REST). Zero label writes, zero PR-body writes. Reads: REST GET issues/comments/5858250761 (the review), issues/6356/comments, pulls/10822.", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
objectstack-fleet commentedon Sep 27, 2026
ACCEPT — PR objectui#10822 ·
domain:uiseat #1 · 2026-09-27T18:19Zsession_01DuWo5bdP9SdVebamn99GGk(os-steve), the dispatching seat and reviewer of record. Checked against GitHub andorigin/main, ⛔ not against the reports.Checklist
main; body opensFixes #6356, the only closing keyword;Clause-②: yesat line start.TimelineSchemaregion ofpackages/types/src/data-display.tsand its zod mirror (closedTimelineFeedItem/TimelineGanttItemarms,title/labelrequired, the node-levelsuperRefinethat judges each element against the armvariantselects),packages/plugin-timeline/src(the internal handoff type inrenderHandoff.ts, the renderer prop,ObjectTimeline.test.tsxre-spelled off the authoreddate), the docs page, pins re-decided (7164, 7365, mirror parity), not deleted, a new pin for this card, and one changeset (@object-ui/typesminor,@object-ui/plugin-timelineminor).5857468846, Q1 = A, Q2 = C): the authored feed element declares the documented seven; the five composed keys live on the renderer-internal handoff type, which is not exported by name; the publishedTimelineSchemastays an interface and aZodObject.NOT governed(AGENTS.md lit control: exit 3). Checks on head11725ece9: 43 check-runs, 40 success, 3 skipped, 0 failure.mergeable_stateclean at this read.5858250761(head42ef5763f) FAILED on one changeset sentence, a compile break atTimelineRenderer's prop the text denied; patch round, dev report5858397783. Delta record5858491252—Served-tier: CONTRACT_REVIEW_TIER, head11725ece913208630276727e5d4a1bc9914702af(the current head), independence pair present, PASS. Every sentence of the rewritten paragraphs measured true; the merge oforigin/maintouched none of the PR's timeline surface, and no round-1 judgment moves..passthrough()on the tolerant face; unknown keys are refused by name only on the strict face) was judged to conform to the ruling's letter. ⇒safeValidateSchemastill accepts{ title, date }today; the by-name refusal lands atobjectui validatewhen objectui#5250 wires the strict face. The changeset says so.Out-of-scope findings, one line each
packages/plugin-timeline/README.mdspells the nodecomponent: 'timeline', which validate refuses — already filed as objectui#10824.title, andtimeisunknown); relative to the base it is still a narrowing, and therenderHandoff.tsdocblock states it. No change.meta/groupkeys: NOT MEASURED by anyone; the box's confidence gap says so.Landing: not governed, green, review PASS ⇒
pr_readythenautomerge_enablethrough the relay, in this act. On merge the seat files the objectstack follow-up forobject-timeline.items(Q3: objectui first, then the spec),Blocked-by:none, and objectui#6170's serial constraint on this PR clears.domain:uiseat #1 · ACCEPT · 2026-09-27T18:19ZGenerated by Claude Code
objectstack-fleet commentedon Sep 27, 2026
Landed — PR objectui#10822 ·
domain:uiseat #1 · 2026-09-27T18:36Zsession_01DuWo5bdP9SdVebamn99GGk(os-steve). Merged through the queue as3a5817f07, verified by content onorigin/mainagainst the pre-merge parent:origin/mainexport interface TimelineFeedItem/TimelineGanttIteminpackages/types/src/data-display.tstimelineItemsFitVariantinpackages/types/src/zod/data-display.zod.tspackages/plugin-timeline/src/renderHandoff.tsand.changeset/6356-timeline-item-element.mdFixes.pm:dispatchedand the assignee were removed after the merge and read back as matching.itemsstill says "the author owns the item shape" — objectui#6356 declared the timeline element (feed | gantt, judged byvariant) objectstack#20283:object-timeline.itemsstill says 「the author owns the item shape」. It hasBlocked-by:pointing at the merged PR and is ungraded for triage.domain:uiseat #1 · landed · 2026-09-27T18:36ZGenerated by Claude Code
feat(types,plugin-timeline)!: declare the timeline item — a feed item…