Skip to content

fix(table): typewriter flash, Run-row completed-skip, dispatch-scope running count#4687

Merged
TheodoreSpeaks merged 4 commits into
stagingfrom
fix/table-running-count
May 21, 2026
Merged

fix(table): typewriter flash, Run-row completed-skip, dispatch-scope running count#4687
TheodoreSpeaks merged 4 commits into
stagingfrom
fix/table-running-count

Conversation

@TheodoreSpeaks
Copy link
Copy Markdown
Collaborator

@TheodoreSpeaks TheodoreSpeaks commented May 21, 2026

Three table run-execution fixes (was split across #4685 + #4687, now combined).

1. Typewriter flash

A cell going Running → value flashed the full text for one frame, then restarted the type-out animation. The reset to empty happened in a useEffect (one frame late) while the render fell back to revealed ?? kind.text. Now the reset runs synchronously during render (the "adjust state when a prop changes" pattern), so the full-text frame never paints; animation still fires only on post-mount changes (SSE completions), not on first mount/scroll-in. Verified live via DOM instrumentation: 40 animations, 0 flashes (was 77/100).

2. Run-row re-running a completed workflow

On a row with one completed group and one cancelled group, "Run row" re-ran the completed one. Two causes, both fixed:

  • bulkClearWorkflowGroupCells (incomplete mode) was row-level — it wiped every targeted group's data + exec on any row that wasn't fully filled, so a completed group got wiped because a sibling group on the row was incomplete. Now per-group: only error/cancelled groups are cleared; completed and in-flight groups are left intact.
  • classifyEligibility now skips completed groups for manual incomplete runs (only "Run all" re-runs completed). Client optimistic stamp mirrors it.

3. "X running" count from dispatch scope (reload matches live)

The badge read the sidecar in-flight count, but the dispatcher only stamps a ~20-cell window at a time — so a 1000-row Run-all showed ~1000 live but ~20 on reload. Now derived from the active dispatches: rows in scope ahead of cursor × |groupIds| (persisted scope + cursor, so reload computes the same number). Exact for Run-all; an upper bound for incomplete/new (counts cells the eligibility filter later skips). applyDispatch re-syncs the badge per window; applyCell keeps runningByRowId live for the gutter; the optimistic on-click seed uses totalCount × groups.

Type of Change

  • Bug fix

Testing

tsc clean; vitest (lib/table + hooks/queries, 202 passing); lint clean. Typewriter verified live in-browser (0 flashes). Counter verified consistent live↔reload on a large Run-all.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

- 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.
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.
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.
@vercel
Copy link
Copy Markdown

vercel Bot commented May 21, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped May 21, 2026 1:54am

Request Review

@cursor
Copy link
Copy Markdown

cursor Bot commented May 21, 2026

PR Summary

Medium Risk
Touches table run/dispatch core paths (React Query run-state bookkeeping plus DB-side bulk-clear logic), so regressions could affect run eligibility or displayed running counts, but changes are localized and additive with fallbacks.

Overview
Fixes several table run UX/state mismatches: the cell value typewriter animation now resets synchronously on text change to prevent a one-frame full-text flash before animating.

Aligns manual mode: 'incomplete' behavior across client and server so completed groups are skipped for Run-row/Run-incomplete, and updates bulkClearWorkflowGroupCells to clear outputs/executions per group (only error/cancelled) rather than wiping entire rows and unintentionally re-running completed siblings.

Makes the "X running" badge derive from active dispatch scope instead of only sidecar in-flight rows: adds countActiveRunCells, updates the dispatches API to use it, changes SSE handling to keep runningByRowId live from cell events while re-syncing runningCellCount by invalidating on dispatch window updates, and adjusts optimistic run seeding to use full run scope (rows × groups, using cached table rowCount when available).

Reviewed by Cursor Bugbot for commit a7c6337. Bugbot is set up for automated code reviews on this repo. Configure here.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 21, 2026

Greptile Summary

This PR fixes the "X running" badge inconsistency between live view and reload by deriving the cell count from the persisted dispatch scope (rows ahead of cursor × |groupIds|) rather than the transient sidecar in-flight count. It also seeds an optimistic full-scope count on click and re-syncs the badge from the server on each dispatch SSE window event.

  • countActiveRunCells (dispatcher.ts): computes remaining work from active dispatch rows; falls back to the sidecar when no dispatch is active.
  • applyDispatch (use-table-event-stream.ts): calls invalidateQueries once per window so the badge steps down exactly as the cursor advances, matching reload.
  • useRunColumn (tables.ts): seeds totalCount × groups as the optimistic delta so the badge is correct immediately on click, before the first window SSE arrives.

Confidence Score: 4/5

The core fix is well-reasoned — anchoring the badge in the persisted dispatch scope means live and reload always compute the same number, and the per-window invalidateQueries approach is clean.

COUNT queries in countActiveRunCells run serially across active dispatches (fine for N=1, suboptimal for concurrent runs), and readTotalRowCount may pick up a filtered totalCount from cache when multiple row queries are cached, causing the optimistic badge to briefly show a filtered-scope count before the first dispatch SSE corrects it.

apps/sim/lib/table/dispatcher.ts and apps/sim/hooks/queries/tables.ts warrant a second look — the former for the serialized COUNT queries, the latter for the readTotalRowCount cache-scan logic.

Important Files Changed

Filename Overview
apps/sim/lib/table/dispatcher.ts New countActiveRunCells derives badge count from dispatch scope (rows ahead × groupIds); sequential COUNT queries per active dispatch — parallelizable edge case.
apps/sim/hooks/queries/tables.ts Optimistic badge now uses full scope (totalCount × groups); new readTotalRowCount helper may return a filtered or stale totalCount from cache.
apps/sim/app/workspace/[workspaceId]/tables/[tableId]/hooks/use-table-event-stream.ts applyCell no longer touches runningCellCount; applyDispatch invalidates server count per window; updateRunningByRow correctly maintains per-row gutter only.
apps/sim/app/api/table/[tableId]/dispatches/route.ts Switches from countRunningCells to countActiveRunCells; passes pre-fetched dispatch rows to avoid double-fetching dispatches table.

Sequence Diagram

sequenceDiagram
    participant U as User
    participant C as Client (useRunColumn)
    participant S as Server (dispatches route)
    participant D as Dispatcher

    U->>C: Click Run all
    C->>C: onMutate bumpRunState(totalCount x groups)
    Note over C: Badge = 1000 optimistic
    C->>S: POST /run-column
    S->>D: "insertDispatch(scope, cursor=-1)"
    S-->>C: dispatchId
    C->>C: onSuccess seed dispatch into overlay

    loop per window 20 rows
        D->>D: dispatcherStep batchEnqueueAndWait
        D->>D: advanceCursor(lastPosition)
        D->>C: SSE dispatch status dispatching cursor
        C->>C: applyDispatch update overlay
        C->>S: invalidateQueries GET /dispatches
        S->>S: countActiveRunCells rows ahead x groups
        S-->>C: runningCellCount N minus window_size
        Note over C: Badge steps down per window
    end

    D->>C: SSE dispatch status complete
    C->>C: applyDispatch remove dispatch from overlay
    C->>S: invalidateQueries GET /dispatches
    S-->>C: runningCellCount 0
    Note over C: Badge = 0
Loading

Reviews (1): Last reviewed commit: "fix(table): X-running count from dispatc..." | Re-trigger Greptile

Comment thread apps/sim/lib/table/dispatcher.ts Outdated
Comment thread apps/sim/hooks/queries/tables.ts Outdated
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 89cf52a. Configure here.

Comment thread apps/sim/hooks/queries/tables.ts
@TheodoreSpeaks TheodoreSpeaks changed the title fix(table): X-running count from dispatch scope (reload matches live) fix(table): typewriter flash, Run-row completed-skip, dispatch-scope running count May 21, 2026
@TheodoreSpeaks TheodoreSpeaks changed the base branch from fix/table-typewriter-runrow to staging May 21, 2026 01:51
…Count

- 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.
@TheodoreSpeaks TheodoreSpeaks merged commit 57b9a2f into staging May 21, 2026
10 checks passed
@waleedlatif1 waleedlatif1 deleted the fix/table-running-count branch May 21, 2026 06:45
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