Skip to content

A graph of what is running - #414

Merged
VeryComplexAndLongName merged 2 commits into
mainfrom
a-graph-of-what-is-running
Sep 12, 2026
Merged

A graph of what is running#414
VeryComplexAndLongName merged 2 commits into
mainfrom
a-graph-of-what-is-running

Conversation

@VeryComplexAndLongName

Copy link
Copy Markdown
Owner

A Pipeline tab: every active change in the order it declares, what is running right now and whose run it is, and what can be started alongside what.

Sequential work is not a separate thing to draw — a chain of changes each blocked by the previous is a graph one lane wide, and independent changes are lanes side by side. One drawing covers both.

The Pipeline tab

The three decisions, and why (ADR 0025)

The picture is derived in core, coordinates and all. layoutChanges returns each node's position and size and each edge's path, in abstract units. Nothing in the view is measured: one unit is one rem for both the cards and the SVG, so they line up without either being asked where the other ended up.

The alternative was to lay the nodes out with CSS and measure them — getBoundingClientRect per node, a ResizeObserver, a re-run after every layout pass and after fonts settle. That was the only part of this view holding state derived from a moment of rendering, the only part not checkable without a browser, and the only part with a failure mode that looks fine: an edge positioned from stale geometry is a line pointing at nothing. Edge routing is now checked by the same unit tests as the ordering.

A rem and not an em, for two reasons that both bite: a custom property holds a token rather than a computed length, so 1em would resolve against the font size of whichever element used it; and the shell fixes body at 14px, so an em would not follow the reader's browser font setting at all.

A collision is not an edge. blocked_by is an order and an arrow means exactly that. Two changes that would meet in one spec file have no precedence between them — either may go first — and a line would assert a sequence the repository does not contain. A reader would believe it, because it would look like every other line in the drawing. So a collision is stated in words on the card it affects, in the same wording openspec-ui-cli ready already uses.

No graph library. What one would contribute is the part this puts in core; what it would cost is the part that matters. A card is a <button>, already focusable and already named, which a canvas or an SVG-only drawing would each need given by hand — and this shell is held to WCAG AA by a browser suite that runs axe on every screen. The SVG carries only edges and is aria-hidden: what a line draws is already on the card it points from.

Also

  • A cycle of declared blockers is named rather than placed, and so is anything waiting on one. A cycle has no depth, and there is no column one past nowhere; a picture that quietly put one somewhere would be a wrong answer that looks like a right one.
  • Below 720px the picture becomes headed lanes. Four columns of cards do not fit a phone in any implementation, and shrinking until it is technically present and practically unreadable is the worse answer. Nothing is lost — that wording is what the edges illustrate.
  • ChangeReadiness gains the blockers it declares (kept to those still active); the report carried them only for blocked changes, so the picture would have lost the relations of a running one.
  • The report's shape and the words a collision is described in moved to a browser-safe leaf, so both surfaces describe one fact identically.
  • The bridge operation for the VS Code embed was dropped deliberately, and recorded as dropped rather than quietly skipped: the embed shows one tab (ALLOWED_TABS_VSCODE_EMBED), so it would have been an operation nothing could call.

Verification

npm run verify unpiped with everything staged: typecheck, lint and test all passed — cli 107, core 1092, extension 327, server 83, webui 389; 1998 in all.

The whole browser suite, 16 of 16, including this tab's axe run at WCAG AA and a phone-width check that the page body never scrolls sideways. The two harness screenshots regenerated with it: the tab strip they show now carries this tab, so leaving them would have documented a screen that had stopped existing.

One task stays open — 6.5, delegated to claude-cli: with two changes actually running in their own working directories, check that each card names the right author and the right state. The unit tests drive the view with a written report; only real runs show that the report a live workspace produces is the one the picture draws.

🤖 Generated with Claude Code

ADR 0025 and the change documents for a graph of what is running, plus
the layout it is drawn from.

The layout is a pure function of the readiness report and lives in core,
not in the view: the terminal and the shell must place changes
identically, and a second implementation in the browser would be free to
drift invisibly, both pictures continuing to look plausible.

A column is a change's depth in the declared blocked_by order. Within a
column, changes are ordered by name and not by state: a node that moved
because a run started would read as the plan having changed.

A collision is not an edge. Two changes that would meet in one spec file
have no precedence between them, and a line would assert a sequence the
repository does not contain, which a reader would believe because it
would look like every other line in the drawing.

A cycle of declared blockers is named rather than placed, and so is
anything waiting on one. A cycle has no depth, and there is no column
one past nowhere; a picture that quietly put one somewhere would be a
wrong answer that looks like a right one.

ChangeReadiness gains the blockers it declares, kept to those still
active. The report carried them only for blocked changes, so the picture
would have lost the relations of a running one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A Pipeline tab: every active change in the order it declares, what is
running right now and whose run it is, and what can be started alongside
what. Sequential work is not a separate thing to draw — a chain of
changes each blocked by the previous is a graph one lane wide.

The picture is derived, coordinates and all, by layoutChanges in core.
Nothing in the view is measured: one unit is one rem for both the cards
and the SVG, so they line up without either being asked where the other
ended up. The alternative held the only state derived from a moment of
rendering, needed a browser to check, and failed in a way that looks
fine — an edge positioned from stale geometry is a line pointing at
nothing. Edge routing is now checked by the same unit tests as the
ordering.

A declared blocker is drawn as a relation. A collision is not: two
changes that would meet in one spec file have no precedence between
them, and a line would assert a sequence the repository does not
contain, which a reader would believe because it would look like every
other line. A collision is stated in words on the card it affects, in
the same wording openspec-ui-cli ready uses.

A cycle of declared blockers is named rather than placed, and so is
anything waiting on one.

Below 720px the picture becomes headed lanes. Four columns of cards do
not fit a phone in any implementation, and the wording on a card is what
the edges illustrate — which is already what the picture offers a screen
reader, the SVG being aria-hidden.

The bridge operation for the VS Code embed was dropped deliberately: the
embed shows one tab, so it would have been an operation nothing could
call.

Verified: npm run verify unpiped with everything staged — cli 107, core
1092, extension 327, server 83, webui 389. The whole browser suite, 16
of 16, including this tab's axe run at WCAG AA.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@VeryComplexAndLongName
VeryComplexAndLongName merged commit e411f9a into main Sep 12, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant