Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
## Context

The user explicitly asked for both timeline phases to be designed
together with a shared data layer (delivered in Change 1) and for a
logarithmic time axis "from the start," without specifying which
direction. The plan for this change flagged the log-scale direction as
the one piece worth sanity-checking against real data before writing
it into a spec, rather than locking in a formula blind.

## Goals / Non-Goals

**Goals:**
- Several changes' activity is comparable side by side on one shared
axis, with a log scale chosen for a concrete, verified reason (not
"log scale sounds sophisticated").
- No new charting library or canvas/SVG rendering — plain CSS
positioning (`left: X%`), per the explicit request to avoid heavy
chart infrastructure.
- Reuses Change 1's data layer and Change 2's `ChangeTimeline` type
completely unchanged.

**Non-Goals:**
- Not building collision/overlap avoidance for near-simultaneous points
within a lane (e.g. nudging overlapping dots apart). The log-scale
fix already addresses the primary practical case (a dense
squash-merge cluster) by spreading it out — see the empirical check
below. Minor residual overlap for genuinely simultaneous points is
accepted for this first version rather than adding a general
collision-layout algorithm.
- Not adding a manual date-range text input to the VS Code extension's
Command Palette flow — no native date picker exists in VS Code's own
prompt UI, and typed ISO-date entry is worse UX than deriving a
sensible default from the data itself (see Decisions).

## Decisions

### Log-scale direction: from the range start, confirmed empirically

Before writing any component code, real timestamps from this
repository's own archived changes (spanning 2026-08-26, 09:04 through
20:03, including a dense 8-change cluster between 09:04 and 11:03 —
this repository's own squash-merge workflow, exactly the case earlier
discussion in this session flagged as the common outcome) were plotted
under three candidate scales in a throwaway HTML/Playwright sketch:
linear, `log1p(elapsed since range start)`, and `log1p(remaining time
until range end)`. The "from start" direction visibly spread the dense
morning cluster into distinguishable points; "from end" compressed the
same cluster *tighter* than even the linear baseline, defeating the
purpose. `logPosition` (`timeline-scale.ts`) implements the "from
start" direction:

```
raw = (log1p(elapsed) / log1p(span)) * 100, clamped to [0, 100]
```

### Extension global command derives its date range from the data, not user input

`computeDefaultRange` (`commands.ts`) takes the earliest/latest
determinable date across every selected change's created/task/archived
dates. This avoids a manual ISO-date-entry prompt (VS Code's
`showInputBox` has no date-picker affordance) while still producing a
sensible, data-driven range. The standalone app's web UI *does* offer
real `<input type="date">` pickers (a browser-native control VS Code's
prompt API doesn't have), so this asymmetry is a genuine capability
difference between the two hosts, not an inconsistency to paper over.

### `archivedDate` is anchored to end-of-day, not midnight

Found during this change's own verification (a real-browser smoke test
against real archived-change data): `archivedDate` is a plain calendar
date parsed from the archive folder name, with no time-of-day
information. Anchoring it to midnight (`T00:00:00.000Z`) made it
plot — and sort into the default range — *before* that same day's
actual `createdDate`/task timestamps, even though archiving is
chronologically the *last* thing that happens to a change. Anchored to
end-of-day (`T23:59:59.999Z`) instead, in both
`MultiChangeTimelineView.tsx`'s point-building and `commands.ts`'s
`computeDefaultRange`.

### Timeline CSS was missing entirely — added as part of this change

Also found during verification: neither this change's nor Change 2's
timeline components had any matching CSS in `shell-ui.ts`. For Change
2's single-change view this was a cosmetic gap (default HTML element
styling made it readable regardless). For this change's positioned-dots
layout it was a functional one — `left: X%` inline styles do nothing
without `position: relative` on the track and `position: absolute` on
each point, so the axis did not visually exist at all beforehand. Added
`.openspec-timeline-*` and `.openspec-multi-timeline-*` rules to
`shellThemeCss`, matching the existing CSS-variable palette
(`--surface`, `--surface-2`, `--line`, `--ink`, `--muted`, `--primary`,
`--danger`).

## Risks / Trade-offs

- **[Risk]** A smoke test against the bundled `dist/timeline.js` in a
bare browser page (not a real VS Code webview) showed the new track/
point CSS resolving to transparent colors, because `vscodeThemeCss`'s
`:root` rules (which come after `shellThemeCss` in the concatenated
stylesheet and win) reference `--vscode-*` custom properties that
only exist inside a real VS Code webview, not a plain browser page.
→ **Mitigation**: confirmed via computed styles that `position`/
`left`/`transform` (the actual functional positioning) resolved
correctly regardless; the missing color is a known, pre-existing
limitation of this lightweight smoke-test technique (already true for
`AiPanel`'s own styling, not something introduced by this change),
not a defect inside a real VS Code window, where `--vscode-*`
variables are genuinely defined by the host.
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
## Why

Third and final change building the "change timeline" feature (see
`2026-08-26-add-change-timeline-data-layer` and
`2026-08-26-add-change-timeline-view`). This adds Phase 2: a global
"compare changes" view showing several changes in parallel lanes on a
shared, log-scale time axis with a date-range picker — confirmed
explicitly by the user, including the log-scale requirement, and
verified empirically against this repository's own real archived-change
timestamps before implementation (see design.md, "Log-scale direction").

## What Changes

- Add `packages/webui/src/timeline-scale.ts` (`logPosition`): a
log1p-from-range-start position function, 0-100, clamped. Validated
against real data: this direction spreads a dense cluster of
near-simultaneous changes (this project's own squash-merge workflow
produces exactly this) into something readable; the opposite
direction (log from the range end) made the same cluster *more*
overlapped than a plain linear scale.
- Add `packages/webui/src/components/MultiChangeTimelineView.tsx`: one
lane per selected change, plotting created/task/archived points along
the shared axis. Reuses Change 1's `getChangeTimelines` batch
function and Change 2's `ChangeTimeline` type unchanged — no changes
to the data layer.
- Standalone app: a "Single change" / "Compare changes" mode toggle
within the existing Timeline tab (not a new tab) — a date-range
picker plus a multi-select change list, calling `loadChangeTimelines`
via real REST.
- VS Code extension: a new global command,
`openspec-ui.showAllChangesTimeline` (Command Palette only, no tree
item — matching the `openspec-ui.openspecView` precedent), with a
multi-select `showQuickPick` over active and archived changes. The
date range is derived automatically from the selected changes' own
data (earliest/latest determinable date) rather than asking the user
to type ISO dates, since VS Code's prompt UI has no native date
picker.
- `TimelineWebviewPanel` gains `showMulti(...)`, reusing the same
not-a-singleton, embed-the-data-in-initial-HTML approach as `show()`
(Change 2); `timeline-entry.tsx` renders whichever of
`window.__OPENSPEC_UI_TIMELINE__` / `__OPENSPEC_UI_MULTI_TIMELINE__`
is present.
- Add CSS for both this change's and Change 2's timeline classes to
`packages/webui/src/shell-ui.ts` — a real gap found during this
change's own verification: the positioned-dots-on-an-axis layout is
the actual point of the feature, and it did not render as a
positioned axis at all without `position: relative`/`absolute` rules,
which had never been added.
- Fix: an archived change's `archivedDate` (a plain calendar date, no
time-of-day) was anchored to midnight when computing plot positions
and default ranges, which could sort it *before* that same day's real
created/task timestamps — since archiving is chronologically last,
it is now anchored to end-of-day instead.

## Capabilities

### Modified Capabilities

- `vscode-extension`: adds a Requirement for the global comparison
command.
- `standalone-app`: adds a Requirement for the compare-changes mode.

## Impact

- `packages/webui/src/timeline-scale.ts` (new)
- `packages/webui/src/components/MultiChangeTimelineView.tsx` (new)
- `packages/webui/src/standalone-entry.tsx`
- `packages/webui/src/timeline-entry.tsx`
- `packages/webui/src/shell-ui.ts`
- `packages/extension/src/webview/timeline-panel.ts`
- `packages/extension/src/commands.ts`, `package.json`
- `.changeset/*.md` (new changeset file)
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
## ADDED Requirements

### Requirement: The Timeline tab offers a compare-changes mode with a date-range picker

The system SHALL offer, within the existing Timeline tab, a mode that
lets the user pick a date range and select multiple active and/or
archived changes, then shows them as parallel lanes on a shared,
log-scaled time axis.

#### Scenario: User picks a date range and multiple changes

- **WHEN** the user selects a start date, an end date, and multiple
changes, then loads the comparison
- **THEN** the tab shows one lane per selected change within that range

#### Scenario: User has not selected a range or any changes

- **WHEN** the user attempts to load a comparison without a complete
date range or without selecting any change
- **THEN** the system reports what is missing rather than loading an
empty or partial comparison
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
## ADDED Requirements

### Requirement: A global command compares several changes on a shared timeline

The system SHALL offer a Command Palette command, not tied to any
single tree item, that lets the user select multiple active and/or
archived changes and shows them as parallel lanes on a shared,
log-scaled time axis derived from the selected changes' own data. The
axis SHALL use a logarithmic scale from the range start, chosen because
it spreads a dense cluster of near-simultaneous changes into readable
detail rather than compressing it further.

#### Scenario: User selects changes across active and archived

- **WHEN** the user invokes "Show Change Comparison Timeline" and
selects both active and archived changes
- **THEN** a webview opens showing one lane per selected change, points
positioned by their best-effort dates

#### Scenario: User selects no changes

- **WHEN** the user cancels the selection without picking any change
- **THEN** no webview opens

#### Scenario: The comparison computation fails

- **WHEN** fetching the selected changes' timelines throws
- **THEN** the extension shows an error message and does not open a
webview
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
## 1. Log-scale util

- [x] 1.1 Before writing any component, validate the log-scale
direction against real data: a throwaway HTML/Playwright sketch using
this repository's own real archived-change timestamps (a dense
8-change cluster plus sparser later changes), comparing linear,
"log from range start," and "log from range end." Confirmed "from
start" spreads the dense cluster into readable points; "from end"
compressed it tighter than linear.
- [x] 1.2 Add `packages/webui/src/timeline-scale.ts` (`logPosition`)
implementing the confirmed direction, clamped to `[0, 100]`.
- [x] 1.3 Add `timeline-scale.test.ts`.

## 2. Webui: multi-change component

- [x] 2.1 Add `packages/webui/src/components/MultiChangeTimelineView.tsx`:
props `{ timelines, rangeStart, rangeEnd }`, one lane per timeline,
created/task/archived points plotted via `logPosition`. Archived
points anchored to end-of-day (`T23:59:59.999Z`), not midnight (see
design.md).
- [x] 2.2 Add `MultiChangeTimelineView.test.tsx`.
- [x] 2.3 Add `.openspec-timeline-*`/`.openspec-multi-timeline-*` CSS to
`packages/webui/src/shell-ui.ts` — found missing entirely during
verification; required for the positioned-axis layout to render at
all, not just cosmetic (see design.md).

## 3. Standalone app: compare-changes mode

- [x] 3.1 Add a "Single change" / "Compare changes" mode toggle within
the existing Timeline tab in `standalone-entry.tsx`: date-range
`<input type="date">` pair, a multi-select change list, and
`loadChangeTimelines` via real REST.

## 4. Extension: global comparison command

- [x] 4.1 Add `pickChangesForTimeline` (multi-select `showQuickPick`
over active and archived changes) and `computeDefaultRange`
(data-derived date range — no manual date entry, see design.md) to
`commands.ts`.
- [x] 4.2 Register `openspec-ui.showAllChangesTimeline` (Command
Palette only, no tree item, matching `openspec-ui.openspecView`'s
precedent), calling `getChangeTimelines` directly and
`timelinePanel.showMulti(...)`.
- [x] 4.3 Add `showMulti(...)` to `TimelineWebviewPanel`, reusing the
same not-a-singleton, embed-in-initial-HTML approach as `show()`.
- [x] 4.4 Extend `timeline-entry.tsx` to render whichever of
`window.__OPENSPEC_UI_TIMELINE__` / `__OPENSPEC_UI_MULTI_TIMELINE__`
is present.
- [x] 4.5 Add `contributes.commands` entry in `package.json`.
- [x] 4.6 Add tests to `commands.test.ts`: picks across active and
archived, computes the expected default range, calls `showMulti`;
does nothing when no changes are picked.

## 5. Verification

- [x] 5.1 `npm run typecheck` and `npm run lint` (including
`lint:english`) pass workspace-wide.
- [x] 5.2 `npm run test` passes workspace-wide, including all new test
files.
- [x] 5.3 Rebuild the VSIX (`npm run package --workspace
openspec-ui-vscode`) and confirm it packages without error.
- [x] 5.4 Live smoke test: same pre-existing, environment-specific
`@vscode/test-electron` failure reproduces on this machine
(documented since `2026-08-26-signal-run-completion`, unrelated to
this change). As a substitute, verified the built `dist/timeline.js`
bundle in a real Chromium browser (Playwright) loading real
`getChangeTimelines` data for 7 of this repository's own archived
changes: correct lane count, correct point count, zero console
errors, and — after finding and fixing the missing CSS — confirmed
via computed styles that `position`/`left`/`transform` resolve
correctly on each plotted point.
- [x] 5.5 Propose a changeset (`npx changeset`) for `openspec-ui-vscode`
and `@openspec-ui/webui` (both minor: new capability, no breaking
change) instead of hand-editing `version`/`CHANGELOG.md`; apply it
via `npx changeset version`.
- [x] 5.6 Run `openspec change validate --strict add-multi-change-timeline-view`.
20 changes: 20 additions & 0 deletions openspec/specs/standalone-app/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,3 +223,23 @@ with pending or undated tasks shown distinctly rather than omitted.
- **THEN** it is shown without a date rather than omitted or given a
misleading date

### Requirement: The Timeline tab offers a compare-changes mode with a date-range picker

The system SHALL offer, within the existing Timeline tab, a mode that
lets the user pick a date range and select multiple active and/or
archived changes, then shows them as parallel lanes on a shared,
log-scaled time axis.

#### Scenario: User picks a date range and multiple changes

- **WHEN** the user selects a start date, an end date, and multiple
changes, then loads the comparison
- **THEN** the tab shows one lane per selected change within that range

#### Scenario: User has not selected a range or any changes

- **WHEN** the user attempts to load a comparison without a complete
date range or without selecting any change
- **THEN** the system reports what is missing rather than loading an
empty or partial comparison

28 changes: 28 additions & 0 deletions openspec/specs/vscode-extension/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -341,3 +341,31 @@ SHALL each open in their own tab, not replace one another.
- **THEN** the extension shows an error message and does not open a
webview

### Requirement: A global command compares several changes on a shared timeline

The system SHALL offer a Command Palette command, not tied to any
single tree item, that lets the user select multiple active and/or
archived changes and shows them as parallel lanes on a shared,
log-scaled time axis derived from the selected changes' own data. The
axis SHALL use a logarithmic scale from the range start, chosen because
it spreads a dense cluster of near-simultaneous changes into readable
detail rather than compressing it further.

#### Scenario: User selects changes across active and archived

- **WHEN** the user invokes "Show Change Comparison Timeline" and
selects both active and archived changes
- **THEN** a webview opens showing one lane per selected change, points
positioned by their best-effort dates

#### Scenario: User selects no changes

- **WHEN** the user cancels the selection without picking any change
- **THEN** no webview opens

#### Scenario: The comparison computation fails

- **WHEN** fetching the selected changes' timelines throws
- **THEN** the extension shows an error message and does not open a
webview

17 changes: 17 additions & 0 deletions packages/extension/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
# Changelog

## 0.24.0

### Minor Changes

- Add a "compare changes" timeline: a new global command
(`openspec-ui.showAllChangesTimeline`) and a standalone Timeline-tab
mode that show several changes as parallel lanes on a shared,
log-scaled time axis (verified against real archived-change data
before choosing the log-scale direction). Also adds the CSS the
single-change timeline view needed but was missing, and fixes archived
dates plotting before same-day created/task timestamps.

### Patch Changes

- Updated dependencies
- @openspec-ui/webui@1.13.0

## 0.23.0

### Minor Changes
Expand Down
Loading
Loading