Repository navigation
Add flowition-cockpit: a Claude Code plugin to watch and steer runs - #7
Conversation
A status line and a /flo pane that bring the viewer's essentials into Claude Code (terminal and the desktop Code tab): - Run cards: launched here, live, and recent grouped by day, with back-to-back runs of one workflow folded; filters (live, needs attention, completed) and search; a New run form that starts a workflow from ~/.flowition/workflows. - A run view: tiles, a progress bar, answerable ask() questions, the result as Markdown, and Agents, Timeline, Phases, Log and Structure tabs. - Live agent threads (transcripts read incrementally by byte offset) with a composer to steer the agent. - Controls, each only from a press in the pane: answer, steer, cancel (confirmed), resume (run --resume --detach, the shared handoff), delete (rm, to the trash), Tell Claude when done, Ask Claude, Open in viewer. - Auto-attach to runs the session launches through Bash or the MCP tools. It reads only through the CLI and the run directory's append-only files and acts only through the CLI; no viewer is needed. On the desktop, controls are drawn as Client faces, since the native plugin Button takes a first click as focus alone. The repository root becomes a plugin marketplace (.claude-plugin/ marketplace.json): /plugin install flowition-cockpit --marketplace ben-vargas/flowition. The npm package is unchanged.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 76cc047746
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…n end Addresses the two Codex review findings on #7. - The timeline (Timeline, Phases, Log, Structure) was built from one grep of events.jsonl through $.process.run, whose stdout is capped at 4 MiB: a large run's read could be cut short and then cached against the file's full size, so a finished run stayed incomplete for good. events.jsonl is now read from where the last read stopped, in chunks of at most 2 MiB (well under the cap, and a truncated read is never folded), through its last complete line, and folded into the timeline already held (foldTimeline). A large run catches up over a few polls with a progress note; a live run costs only its growth. Verified on the largest local run (1.2 MB, 33 lanes): 64 KiB chunks fold to exactly the one-shot result. - A reload clears the cached run details but keeps the "Tell Claude when done" arming; a run that finished before the first post-reload poll was first seen already ended, so hasEnded() never fired and Claude was never told. shouldWake() now also wakes an armed run whose first look finds it ended (an armed run can only be in that state once it has finished). Tests: incremental fold equals one-shot fold at every cut point; the wake rule's cases. 27 pass.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6be33925a3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…s mid-render Addresses the second round of Codex review findings on #7. - New run listed only the newest 20 workflow files, so older ones could not be launched from the pane. The form now has a filter (by project/name) once there are more than 10, and a Show more that adds 20 at a time. - The desktop's face handlers were cleared outright once the registry held more than 500 keys, which could happen in the middle of a render and leave faces that render had already drawn with no handler until the next redraw. Each handler is now stamped with the render that registered it, and the registry is pruned in that render's finally block, keeping the last two renders' handlers, so nothing a render draws loses its handler. Tests: 25 extra workflows exercise the filter and Show more on both surfaces; controls still answer their first press after repeated redraws.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3ab7cd2948
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
- A status over $.process.run's 4 MiB cap (a huge result) made every poll
throw, so the run's end never toasted or woke Claude. readStatus() parses
the output whole, or, when it is cut or unreadable, reads the run's state
off its head ({"runId":…,"state":… comes first) and keeps the rest of the
last good poll, with a note that the result is too large for the pane.
- Cost now sums every attempt: each attempt's final agent event carries that
attempt's usage, which the timeline fold adds up per lane (status --json
folds only the last). The selected run's timeline is kept on every tab
for this. On three real runs, a resumed one among them, the totals match
the journal sums the viewer uses to the cent.
- The attach-the-next-run fallback is armed only by a Bash launch Claude Code
backgrounded, never by a failed launch or an MCP error.
- An ended run's unanswered ask() is shown as abandoned, with no Answer
field, and no longer counts as waiting in the list, its filter or subtitle.
- Runs launched in the session stay attached for the whole session (the cap
of 20 dropped the oldest, live or not, and with it their toasts).
30 plugin tests pass.
|
@codex review |
…t-plugin # Conflicts: # CHANGELOG.md
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: aff37ec903
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…-result Addresses the fourth round of Codex review findings on #7. - A single events.jsonl record longer than a read chunk was skipped by advancing a fixed byte count, so the next read began mid-line, possibly mid-character, and re-measuring decoded text (U+FFFD) let the offset drift: the cache could be rejected and the file refolded every poll, or records skipped or folded twice. The reader (now catchUpTimeline in lib.ts, the process runner injected) only ever starts a read on a line boundary and folds through its last newline. A line longer than a chunk has its byte length measured raw (awk under LC_ALL=C) and is read whole up to 3.5 MiB, or stepped over exactly with a note in the Log tab beyond that. - deriveRunState reports a malformed result.json as `corrupt-result`; the pane matched only `corrupt`. It now colors it as an error and counts it as needing attention. Tests: a byte-accurate fake runner checks odd chunk sizes that split multibyte characters fold to exactly the one-shot result, an oversized line read whole or stepped over exactly, and a half-written last line left for the next poll. Against the real tail/head/awk on macOS, 257-byte chunks over a 61 KB file of multibyte text consume every byte and match the one-shot fold. 34 plugin tests pass.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6205b6ca72
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ways reconciled Addresses the fifth round of Codex review findings on #7. - Output: while a resumed run is live, the engine's spentOutputTokens is the new attempt's counter, so preferring it dropped the tile below the run's lifetime total. Each agent's tokens are now its finished attempts' (from events.jsonl) plus the live attempt's so far, and the tile sums those. - Wake: an armed run whose cached detail was already terminal (armed while the completing poll was in flight) was never polled again, so it stayed armed forever. Armed runs are now polled until a poll disarms them, and an armed run wakes whenever a poll finds it no longer live. - Phase: status --json folds every attempt's phase events, so a resumed two-phase run read "Phase 4". The tile now shows the phase the run last entered, from the timeline (which keeps one entry per phase index). 35 plugin tests pass.
- F1: a cached terminal detail was never re-polled and overwrote the fresh listing, so a run resumed elsewhere kept showing its old state. Runs whose cached detail the listing contradicts are now polled again, and only a detail read in the current poll may correct the listing. - F2: a drag that left a desktop control before release still pressed it (captured moves arrive from outside without a leave). Faces now cancel on a captured move outside the region and require the release inside, with a cell of slack for the desktop's edge mapping. - F3: a failed transcript or timeline read jumped past the wake step. Wakes and toasts now go out first, and each optional read is isolated. - F4: a runs listing cut at the 4 MiB cap (or failing) threw before any status poll. The listing keeps its complete rows (newest first), a failure keeps the last list and is reported, and watched runs are still polled. - F7: renders were counted across surfaces, so terminal redraws pruned the desktop's face handlers. Handlers and their pruning are keyed per surface. - F5: fan-outs nested in different pipeline stages merged into one container; the stage is now part of a container's identity. - F6: a resumed agent's tokens counted its last attempt twice until new progress arrived; the live attempt's count is added only once its output postdates the attempt's start. hooks/review.test.tsx holds integration regressions for F2, F3, F4 and F7 (each fails on the previous commit), and lib.test.ts unit tests for F1, F4, F5 and F6. 43 plugin tests pass.
- F1: a resume that ended in the same state as the cached detail (failed, then failed again) was never re-polled. A cached terminal detail is now read again when its events.jsonl has grown since the last read (a resume appends to it) or when a minute has passed. - F2: the drag-off check allowed a cell of slack, so a release at x = -1 still fired a confirmation. Faces now follow ClientPointerEvent exactly: a negative cell, or one at or past the laid-out size, is outside. - F8: one run's status command rejecting (denied, unstartable, timed out) aborted the whole poll. Each run's status is now isolated. - F9: picking another workflow kept the previous workflow's args, unseen behind the new, empty field. A different pick now starts from empty args. - F10: tool results were paired by a global id map, so a resumed attempt reusing a synthesized id (pi's t1-tool1) took an earlier call's output. pairToolResults pairs in transcript order within an attempt. hooks/review.test.tsx gains six round-2 regressions, F1 through the real poll timer (a started session on the mocked clock); each fails on the previous commit. 49 plugin tests pass.
- F2: a face fired on any release inside it, even with no press there first (a stray up after another control's press). A face now presses only on a left release that ends its own left press inside it, and the press is consumed, so a repeated release fires nothing. - F6: when the final status was too large to read, the cached worker stayed "running" and its live count was added on top of the events' final usage. A live attempt's tokens are added only while the run is live and the lane's attempt is unfinished and has produced output since it started. - F11: an older timeline (events.jsonl reads failing) replaced newer status spend with a smaller total. A finished attempt status reports past anything the lane has read is added from status, and the run view says when the events could not be read. - F12: on a run no longer live, work still marked running/queued is shown as interrupted (cards, phases, timeline, structure), and a phase holding it is interrupted, not running. - F13: a wake prompt refused (dropped, or the call rejected) was lost; it is now re-armed and tried at the next poll, up to three times, then the person is told. A run disarmed while the completing status was in flight is not woken: the wake list is re-read just before submitting. Seven round-3 regressions added to hooks/review.test.tsx (six fail on the previous commit; the disarm-in-flight one guards existing behavior). 56 plugin tests pass, and the reviewer's own round-3 reproduction file passes in full against this commit.
- Spend: an unread attempt is matched to its terminal event (the lane's last paid terminal), not timestamps alone; cached replays add nothing; a resumed attempt's live output counts once the events have not shown it start. - Wake: only a final run state (completed, failed, interrupted, stale, corrupt, corrupt-result) wakes Claude; an unknown detached launch stays armed and polled. - Run list: a history over the 4 MiB read is filtered through node to every unfinished run plus the newest 2,000, and the list says what it leaves out. - Agent thread header shows abandoned work as interrupted, with lifetime spend.
- Spend: a finished attempt counts its final usage (usage: null spends nothing), not the progress counter status keeps from an earlier attempt. - Run list: the filtered long-history listing is written to a private temp file and read back in chunks, so no number of unfinished runs is cut off; what the pane keeps is bounded for $.state's 4 MiB (live and this session's runs first), and the list says when anything is left out. - Oversized status: re-read without the result's value so workers and totals show on first open; with no whole status, the agents come from the run's events, and an unknown set shows as unknown rather than 'No agents yet'.
- A detail not read whole (a placeholder, or a status too large with no slim re-read) is marked partial, and its workers are reconciled with the run's events: new agents appear, states follow the lanes, spend comes from the lanes alone. - The detail cache is bounded for $.state (the run on screen and watched runs first), text fields are clipped at parse, and a refused state write no longer holds back toasts or completion wakes. - Drawing budgets: the agent thread draws its newest events within a budget and says what it leaves out; Agents, Timeline/Phases/Structure and Log draw within a budget, so a run's controls always draw; long errors and labels are clipped. - An ended run keeps the output its abandoned (interrupted) agent was known to make. - The long-history filter keeps this session's runs however old, and a listing not read whole keeps their last known rows.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ff013c3252
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The viewer's control bridge treats completed as resumable (RESUMABLE_STATES): resuming a completed run replays every finished agent from the journal. The cockpit now offers that as Replay… with its own confirmation copy, through the same run --resume --detach.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c70c326a58
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
… tab - A resumed run asks an unanswered question again under the same qid: a run coming back from an end has asked nothing yet, so its questions toast again. - The Phases tab draws each phase's rows within the tab's text budget and says how many it leaves out, so the run's controls always draw.
- Attaching a run resets its notice baseline to a fresh placeholder: a question or end a poll saw while the run was not watched is still announced once. - A crashed attempt's last known output (its progress) stays spent when a resume starts a new attempt; final usage still replaces it when an attempt ends. - Resume falls back to the journal's metadata for the workflow file when the run's first event names none (a run that failed before it started); only the file is read. - A tool call left without a result by an earlier attempt shows as interrupted, not running, while a later attempt runs.
- Attachment generations: every attach (launch, resume, re-attach) takes a new generation, and a poll that began before a run's generation neither writes its detail nor raises its notices or wake, so a resume's fresh baseline holds. - Each backgrounded launch is tracked on its own and matched to its own new run, so two launches before the next listing both attach. - The run id comes from the CLI's own lines in the order written (detached launch, event lines, the run line, a --json envelope), so a workflow result printed after them with a runId field is never taken for the run. - An agent a resumed run has not taken up again (not among the engine's live agents, or its attempt began before the run's latest start) shows interrupted, with no Steer or Cancel.
- A refresh takes its attach generation when it begins, before its listing, so a run attached during the listing keeps its baseline too. - A backgrounded launch records the runs already listed and its workflow file, and takes only a run created after it began, not listed before it, preferring its file. - Every run a launch's output names is attached (a command may launch several), at most as many as the command launches, from the CLI's own lines only. - Timeline, Phases and Structure lanes of abandoned work (an ended run's, or an earlier attempt's a resume has not taken up) show interrupted and stop at their last sign of life.
- Svg and Input are chosen by the surface's declared element table, not by a name being present (the engine fills every table with stand-ins): the terminal now draws its text status badges and timeline bars instead of invisible Svg. - A launch's already-listed runs are captured before the command runs, so a poll that lists the launched run while Bash is still running cannot exclude it. - Every resume a command names is attached (compound commands), and each new run whose id the output lacks gets its own pending record. - A queued lane's waiting span honors its end, so abandoned queued work stops too.
- A backgrounded launch that names its workflow file takes only a run of that file; an unrelated new run never consumes it (a file named through the shell takes any). - Backgrounding is read from Bash's own record (backgroundTaskId: explicit, timeout, Ctrl+B, message delivery) instead of one presentation message. - Run ids are read from Bash's whole stdout as well as the model's text, and a --json envelope cut short (a large result Bash persists) still names its run from its head. - Auto-attaching another run leaves the previous run's agent thread, steering and pending confirmations, as select() does.
- attach() takes its generation before anything it awaits, and a poll judges late attachments afresh wherever it decides: inside its cache update (retries included) and at each notice and wake, so a resume during a commit keeps its baseline. - Launch commands are read as shell words (quotes, escapes, operators) with the CLI's own option grammar: option-first commands and quoted paths name their workflow; a flowition word that is not the command (echo …) is no launch; a shell-expanded file matches any new run. - Every new-run launch Bash or the shell (`… &`) backgrounded keeps a discovery record; runs the output named are attached already and skipped, and unmatched records expire.
- A run whose attach() is still writing its baseline is treated as attached late by any poll that begins meanwhile, so the poll cannot overwrite that baseline. - A backgrounded command's records are reconciled with the runs its output named: once listed, each named run takes back the record of its own workflow file, and until then the command's records match nothing, so no record is left to take another session's run of the same workflow. - Launch commands read like the shell: backslash-newline continuations vanish, and node/bun/npx/nohup wrappers (with their options) are looked through; launch detection and launch reading are one parser.
- Launch commands are read from the shell's structure: simple commands within
and-or lists, pipelines, subshells and compound commands (if/while/for/{ }); '&'
backgrounds its whole and-or list, groups and pipelines included; a heredoc's body
is data, not commands; a redirection's '&' (2>&1, &>) is no background.
- A named run reconciles with its command's records only once listed with its
workflow (not the pre-journal '?'), and first accounts for a foreground launch of
its own workflow before taking back a backgrounded one's record.
- A resume read off the command line attaches only on evidence it ran: the run turns live having not been, or its events grow after the command (a branch that never ran, or a refused resume, attaches nothing). - Every run the output's own CLI lines name is attached, however many (a loop runs more launches than the command text shows). - Named runs reconcile exact workflow matches before shell-expanded wildcards. - A reached phase with no agents reads as current while the run goes on (waiting on an answer), done once a later phase is reached or the run completes.
- A resume attaches on evidence of a new attempt only: the run turning live having not been, or a run started/resumed event written after the command; an attempt already running that keeps logging progress is no evidence. - After a foreground run's `run <id>: <status>` line its result follows: a JSON line carrying only a status, or a run line the CLI did not set off with a blank line, is result text and names no run; loops still name each run (blank-line framing, event lines, detached envelopes). - Phase progression is the current attempt's: a later phase reached only by an earlier attempt is not counted as passed.
- A reload keeps each watched run's notice baseline (its state and question identities, read from any cached shape) while still dropping the display caches, so a run that ended or asked during the reload is announced once. - A foreground --json run's whole outcome line (exactly runId, status, result|error) names its run even after another run's result; other JSON in a result still does not. - A resume's 'was it live' baseline is read fresh (status) just before the command; a state that cannot be read is no evidence. - A new attempt clears the current phase, so the previous attempt's phase is not shown as current before the new one enters any.
- A status JSON line inside a foreground run's result region is doubtful: the next --json launch's outcome (with or without a result) or the result's own text, which the bytes cannot tell apart. It attaches only on evidence: listed as a run created since the command began. A report naming an existing run attaches nothing. - With the run's events read, the Phase tile shows the current attempt's phase only, none before the attempt enters one (not the CLI's accumulated phase history).
- A doubtful id (status JSON in a foreground result region) attaches only as one of the command's own launches: a new run not listed before the command, created since, of a workflow among the command's new-run launches that the runs its output named do not already account for. A single report workflow has no unaccounted launch, so nothing its result says attaches. - Accepting a doubtful id consumes the background-discovery record kept for its launch. - A failing exit (a workflow that fails exits 1 after its outcome) no longer discards doubtful ids or pending resumes; they are judged on their own evidence.
A backgrounded launch keeps its discovery record even when the enclosing Bash call exits unsuccessfully (`… & wait` on a workflow that fails, a later command in the list failing); the record still takes only a new run of its own workflow.
The new-run form reads the session's folder each time it opens and pins it to that form: the form names it, and Start passes it as --cwd, so a full-permission run never runs somewhere other than the folder the confirmation names.
An answer field is keyed by its run and its question event, not the qid alone: a draft typed for one run's q0 can never stay in, and be submitted through, another run's q0 field after the pane switches runs (auto-attach, navigation) or the question is asked again.
The new-run form's args field is bound to the form's args (value), the args Start and Enter launch with: after switching workflows and back, or reopening the form, the field shows exactly what will run, never a draft the surface kept that the launch would drop.
A launch from the new-run form that fails (invalid JSON, or the CLI refusing) draws the args field afresh under a new key, showing the args the form still holds: a surface that cleared the field on submit shows them again, and a retry launches exactly what is shown.
|
@codex review |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
…andling; consolidate tests Auto-attach now reads the run ids the CLI prints (stopping at a foreground run's result), attaches the runs a command resumes, and gives each backgrounded launch the next new run of its workflow. The shell-structure parser, doubtful-id bookkeeping, evidence-checked resumes and loop/compound accounting are gone; a run it does not pick up still shows under Live. Oversized output gets one guard each: a run listing cut at the stdout cap shows its newest rows with a note, a status too large to read keeps its state, the details cache keeps watched runs (as state and questions when too large), and the timeline drops its fan-out paths when needed. The node filter programs and temp-file listing are gone. Resume/Replay now starts once per press; the pane shows Resuming... meanwhile. Tests: the 22 per-round files become attach, notices, display and controls on one shared kit; tests for removed machinery are dropped. 244 tests pass.
…d padding too The desktop draws a face's border and padding around the cells it counts (a chip is one row), so a click on a chip's lower half reads row 1 and was rejected as outside. A down delivered with nothing captured is now always on the face; a captured move or release is off it only past one cell beyond the counted region.
What
plugins/flowition-cockpit, a Claude Code plugin (terminal and desktop Code tab) that brings the viewer's essentials into Claude Code, and a root.claude-plugin/marketplace.jsonso this repository is its marketplace:flo · N running · M questions waiting./flopane, run list: cards for runs launched in this session, live runs, and recent runs grouped by day (back-to-back runs of one workflow fold into one card). Filters (All / Live / Needs attention / Completed), search, and a New run form for~/.flowition/workflows/.ask()questions, the result rendered as Markdown, and Agents / Timeline / Phases / Log / Structure tabs.run --resume --detach, the shared handoff marker), delete (rm, to the trash, confirmed), Tell Claude when done, Ask Claude, Open in viewer.How
Reads only through the CLI (
runs/status --json) and the run directory's append-only files (events.jsonl,agents/<n>.jsonl, read incrementally by byte offset), and acts only through the CLI. No viewer process is needed; Open in viewer reuses a live one's URL viaosascriptstdin, so the token never lands in a process argument.Desktop note: the pane's controls are drawn as
Clientregions, because the desktop's native pluginButtontakes a first click as focus alone. A click trace confirmed that; the plugin README records it.Scope and checks
plugins/and.claude-plugin/, plus a README section, an Unreleased CHANGELOG entry and.gitignorelines for what Claude Code generates beside a loaded plugin.src/and the npm package are unchanged (filesdoesn't listplugins/).claude plugin validatepasses for the plugin and the marketplace.claude plugin test plugins/flowition-cockpit: 25 tests, each UI test run on both the terminal and desktop surfaces. The root suite passes (548).