Files
sim/apps
Theodore Li 57b9a2fb38 fix(table): typewriter flash, Run-row completed-skip, dispatch-scope running count (#4687)
* fix(table): no typewriter flash; Run-row skips completed workflows

- Typewriter: reset the revealed text synchronously during render when the
  value changes (not in an effect), so a cell going from running→value no
  longer flashes the full text for one frame before animating.
- Run row / manual incomplete runs now treat a `completed` group as done even
  if an output column is blank — only "Run all" re-runs completed cells. The
  auto cascade keeps re-filling blank outputs (completedAndFilled). Client
  optimistic stamp mirrors: incomplete skips `completed` cells.

* fix(table): incomplete bulk-clear is per-group, not per-row

bulkClearWorkflowGroupCells in incomplete mode wiped EVERY targeted group's
output data + exec on any row that wasn't fully filled across all targeted
groups. So Run-row on a row with one completed group and one cancelled group
wiped the completed group's outputs + exec too, and the dispatcher re-ran it.

Now incomplete-mode clears per-group: only error/cancelled groups get their
output columns + exec cleared; completed and in-flight groups are left intact
(never-run groups have nothing to clear and run via eligibility). Combined
with the classifyEligibility guard, a completed workflow is never re-run by
Run-row — only Run-all re-runs it.

* fix(table): X-running count from dispatch scope so reload matches live

The "X running" badge read countRunningCells (sidecar in-flight), but the
dispatcher only stamps one ~20-cell window at a time. During a 1000-row
Run-all the client optimistically showed ~1000 while a reload showed ~20 —
the sidecar never holds more than a window.

Derive the count from the active dispatches instead: rows in scope ahead of
the cursor × |groupIds| (exact for Run-all, upper bound for incomplete/new).
Both scope and cursor are persisted, so a reload computes the same number.

- countActiveRunCells (dispatcher.ts): dispatch-scope total, sidecar fallback
  when no dispatch is active. byRowId stays sidecar-based (the client overlay
  renders queued rows ahead of the cursor).
- Live: applyDispatch re-syncs the badge from the server on every dispatch
  event (one per window, after its cells finish + cursor advances), so the
  badge steps down per window and matches reload. applyCell no longer touches
  runningCellCount (still keeps runningByRowId live for the gutter).
- Optimistic on click: useRunColumn seeds the full run scope (totalCount ×
  groups) so the badge is right before the first window lands.

* fix(table): address review — parallel dispatch counts, unfiltered rowCount

- countActiveRunCells: run the per-dispatch COUNT queries + the sidecar count
  in parallel instead of serially (one round-trip per dispatch).
- Optimistic Run-all estimate now reads the table definition's maintained,
  unfiltered rowCount (detail cache) instead of the rows query's filter-scoped
  totalCount — the dispatcher runs every row regardless of the active filter.
2026-05-20 18:55:27 -07:00
..