Decisions: Timeline
Append as you go, newest last. Record choices someone could reasonably question later, not every edit.
The full event log is a recipe, not a component
- Decision: components are
TimelineItem(row) andTimeline(thin list that ownsolsemantics, spacing and the rail). The composition with an agentic region (start divider, agentic rows, Chain of Thought, live ThinkingState, end divider) is a story and Usage section, not a component. - Why: where the agent starts and stops, the divider wording and which rows are agentic are screen decisions; a component would need props for them and break for a log with two runs or none. A component can be added later (
TimelineGroup); removing one is harder. - Port impact: none beyond the two components. The recipe is documentation the engineer can copy.
Names
- Decision:
TimelineandTimelineItem, chosen overActivityLog/ActivityItemandStep. - Why: pairs with
LabeledDivideras the marker; "Step" implies a sequence. - Port impact: a rename touches
AgentStepandChainOfThought, both taggedready-for-dev.
Carried over from AgentStep (now TimelineItem)
These decisions were made for AgentStep and still apply to the row:
- Its own primitive, not a part of
ChainOfThought. A single tool call or progress line can sit inline without a list, so the row is element-agnostic (adiv) andChainOfThoughtwraps it in an<li>. Not merged withThinkingState: that one owns the shimmer, pulse and timer; this one owns status, secondary text and detail. statusis optional:done(check),active(monochrome Spinner in the secondary status color, not the AI gradient),pending(empty circle),error(critical diamond exclamation). WithoutstatusoriconNamethere is no icon, which is the tool-log look. Status text is visually hidden for screen readers and comes fromlabelStrings(translatable).- Configurable icon:
iconNamereplaces the status icon (for examplewrench). Theactivespinner still wins so work in progress stays visible; the status color and screen reader text still followstatus. WithiconNameand nostatusthe icon shows in the secondary color. - Tool calls are steps with
detail, not another component. A step withdetailbecomes adetailstoggle with a chevron (angle down, flipping to up when open; it was a right chevron turning down until 2026-10-21). - Secondary text is a slot;
<code>inside it takes the code font. - Uses the local
SpinnerandIconwrapper; the port swaps in the real ones. Storybook:Proposals/Timeline Item, taggednew-pattern. - Open: custom icon on a done step. The check is replaced, so completion shows only through color and hidden text. Keep a small check beside the custom icon?
- Diff counts use
pds-timeline-item__added/__removedspans in the secondary text slot (success and critical foreground). Reasoning text is a variant of Chain of Thought, not a step type. - Expandable step affordance: the row is
inline-flex(only as wide as its content) with the chevron right after the text, like the Chain of Thought summary, and the chevron darkens on hover. It was pushed to the far edge of the list, which read as decoration, not part of the click target. Chain of Thought's summary chevron got the same hover cue. - Detail text inherits the step's size (and line height); paragraphs inside
detailhave their PDS default size and margins reset, andcodeuses the code font. Before, a<p>in the detail rendered larger than its label. childrenslot under the step (2026-10-15). Always-visible content (a log block, file tags, a table) rendered below the row, indented to line up with the label when the step has an icon. Reference: Zappy investigating a failed build, where each step carries its own terminal output and file chips. ExtendsTimelineIteminstead of adding a component, because the row, status and icon are the same.detailstays for content that starts collapsed. The slot ischildren(notcontent, which clashes with the native HTMLcontentattribute). Chain of Thought gets it for free since its rows areTimelineItem.- Open: should content collapse when the step finishes? Today the consumer decides by rendering or not rendering it.
- Open: larger, bolder step style and the Chain of Thought header with a metadata line ("Investigation started…") from the same reference are not built.
- File identifiers under a step are PDS
Tags,tagColor='slate'(the greyest Tag color;stoneis beige),size='xs'. Tag, not Badge: these are objects, not status, and Tag supportslinkContent,isRemovableandtruncationLength. Chosen by Erika 2026-10-15.
AgentStep is renamed to TimelineItem (2026-10-16)
- Decision: the row is now
TimelineIteminproposals/Timeline/. New:time(andtimeDateTime),isAgentic.AgentStep/is removed; Chain of Thought renders aTimelinefor its body. - Why: an agent run is one region of a larger event log, so the row should be generic and the agent flavor a modifier (
isAgentic, the same boolean as Button). Designer chose rename over a thin wrapper. - Port impact: classes renamed
pds-agent-step→pds-timeline-item, variable--agent-step-*→--timeline-item-*. TheHANDOFF.mdfiles of AgentStep and Chain of Thought need rewriting; AgentStep'sready-for-devtag is gone with it.
Rail is optional
- Decision:
TimelinetakeshasRail(defaultfalse). Chain of Thought rendersTimelinewithout it and keeps its own left rule; an event log turns it on. The rail joins only neighbouringTimelineItems, so a divider or Chain of Thought between them breaks it. - Why: designer's answer. The rail is drawn by each item's entry (
::after), because it has to run through the gap between items. - Port impact: uses the
lhCSS unit.
Semantic type classes instead of font primitives (2026-10-19)
- Decision:
Timelinetakes a newsizeprop (xsdefault,s) and putspds-copy-<size>on the list; items inherit it, so--timeline-item-font-sizeis gone. ATimelineItemoutside a Timeline inherits the surrounding text size (its stories wrap it inpds-copy-xs). The item's time and secondary text carrypds-copy-xs. - Why: The designer asked for semantic tokens over primitives where they exist; the semantic layer for type is the
pds-copy-*classes. The classes are!important, so they only go on elements whose own font properties nothing else overrides. A child that sets its own weight (the medium labels in Chain of Thought) still wins, because the!importantonly beats rules on the same element, not inheritance. Computed sizes match before the change (summary 14px, rows 12px ats; divider 14px / 12px). - Port impact: adds the
sizeprop toTimeline; drops--timeline-item-font-size.
TimelineItem takes an optional size (2026-10-19)
- Decision:
TimelineItemgetssize?: 'xs' | 's', which putspds-copy-<size>on the item. No default: unset, the item inherits its text size, so items in aTimeline(or Chain of Thought) still follow the list'ssize. - Why: items are used standalone (the Chat Panel header rows, the event log recipe), where the only way to size them was to wrap them in a
pds-copy-*class; forgetting it rendered them at the page's body size. A prop is easier to find than the wrapper. The designer agreed. - Port impact: adds the
sizeprop and its type toTimelineItemafter the handoff was written;HANDOFF.mdis updated. The item's ownsizewins over the list's.
Open questions
- Tone: per-item
isAgenticonly, or also aTimelineGrouplater (designer). - Is
ThinkingStatea plain child ofTimeline, or doesTimelineItemget an active look?
One chevron for every disclosure (2026-10-21)
- Decision: the expandable step's chevron is now an angle-down that flips to up when open, the same as the Chain of Thought summary and the PDS Panel Row's expand button. It was a right chevron that turned down. The change is the icon (
angleDown) and the open rotation (180 degrees instead of 90). - Why: the Chain of Thought summary and its steps pointed different ways when closed (down and right). Both reveal content in place, so up and down reads as show or hide below; a right chevron suggests going somewhere else.
- Port impact: none beyond the icon name and the rotation.