Skip to content
This repository was archived by the owner on Sep 4, 2026. It is now read-only.

feat(chat): render pi-kit lane cards inside a pickforge-lanes MCP row - #395

Merged
ElbertePlinio merged 1 commit into
mainfrom
feat-362-lanes-in-chat
Jul 27, 2026
Merged

feat(chat): render pi-kit lane cards inside a pickforge-lanes MCP row#395
ElbertePlinio merged 1 commit into
mainfrom
feat-362-lanes-in-chat

Conversation

@ElbertePlinio

Copy link
Copy Markdown
Member

#362 PR 3 — the last slice of that issue.

When an agent dispatched lanes and waited, the chat showed nothing about which lanes existed or how they were doing, while the Settings panel had exactly that information a screen away.

One card, not two

RunCard is extracted out of PiKitLanesPanel into PiKitRunCard.tsx and rendered by both surfaces. The issue was explicit about this — "avoid a second divergent lane card" — and it is right: a copy drifts from the original the moment either changes.

The extraction is behaviour-neutral: the Settings panel's own tests pass untouched, which is the issue's acceptance criterion for it.

Correlation is a pure function

pikitRunRef reads the run id out of the row's argument summary, which the parser already attaches (#387):

tool where the run id is
lanes_wait / lanes_status the run argument, present immediately
lanes_spawn only in the result — so an in-flight spawn resolves to nothing rather than guessing

It matches run-<stamp> rather than any token, so a stray word in a result is not read as a run id. Pure, so the correlation is testable without a store, a timer, or a chat — 10 tests.

Live in flight, frozen after

The open decision, resolved:

  • live while the call is in flight — that is the whole reason to watch a lanes_wait
  • frozen once it finishes, so a replayed row does not quietly rewrite itself from a run that has moved on, or blank out because the run was pruned

A finished run whose status is gone says so plainly (No lane status retained for run-…) rather than rendering an empty box; an in-flight one with no status yet says it is waiting.

Polling is scoped to a mounted, open row — Disclosure (#385) keeps closed bodies out of the DOM, so a transcript full of old lanes rows costs nothing — and startPiKitLanesPolling is idempotent, so it neither doubles the interval nor stops the Settings panel's poll on cleanup.

Abandon is inert in a transcript

readOnly hides both abandon affordances. Acting on a run belongs in the panel that owns it; a destructive action two clicks from a replayed message is a trap.

Tests

16 new. Verified load-bearing — removing the live gate fails "never polls once the call has finished"; ignoring readOnly fails "offers no Abandon control in a transcript".

  • correlation: lanes_wait argument, lanes_spawn result, in-flight spawn resolves to null, other servers ignored, no detail, and an ordinary word is not a run id
  • lifecycle: polls in flight, never polls when frozen, renders the Settings card's own selectors, no Abandon, and both empty-state messages

Validation

  • bun run test:unit — 119 files, 1640 passed
  • bunx tsc --noEmit clean; bun run lint clean
  • Settings panel + store tests pass unchanged (15), proving the extraction is neutral

Not done

No VRT scenario for a lanes_wait row with lanes — the mock emits no pickforge-lanes call, so that needs a fixture. Flagging rather than skipping silently.

Closes #362

#362 PR 3, the last slice of that issue. When an agent dispatched lanes and
waited, the chat showed nothing about which lanes existed or how they were
doing, while the Settings panel had exactly that information a screen away.

**One card, not two.** `RunCard` is extracted out of `PiKitLanesPanel` into
`PiKitRunCard.tsx` and rendered by both surfaces. The issue was explicit about
this: a second lane card would drift from the first the moment either changed.
The extraction is behaviour-neutral — the panel's own tests pass untouched.

**Correlation is a pure function.** `pikitRunRef` reads the run id out of the
row's argument summary, which the parser already attaches: `lanes_wait` and
`lanes_status` take a `run` argument, and `lanes_spawn` names its run only in
the result. So an in-flight spawn correctly resolves to nothing rather than
guessing. Matching `run-<stamp>` rather than any token keeps a stray word in a
result from being read as a run id.

**Live in flight, frozen after** (decided 2026-07-27). Polling while the call
runs is what makes `lanes_wait` worth watching; freezing afterwards keeps a
replayed row from quietly rewriting itself from a run that has since moved on
or been pruned. A finished run whose status is gone says so plainly instead of
rendering an empty box.

Polling is scoped to a mounted, open row — `Disclosure` keeps closed bodies out
of the DOM, so a transcript full of old lanes rows costs nothing — and
`startPiKitLanesPolling` is idempotent, so it will not double the interval or
stop the Settings panel's own poll.

**Abandon is inert in a transcript.** Acting on a run belongs in the panel that
owns it; a destructive action two clicks from a replayed message is a trap.

Refs #362
@ElbertePlinio
ElbertePlinio merged commit f4d023b into main Jul 27, 2026
6 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MCP tool rows in chat are dead ends: no args, no result, no completion — and pickforge-lanes should show lane cards

1 participant