revert-redo-fix

Revert + redo: fix-tui-graph shipped wrong scope (constant viewport scroll); revert and redo via generic last_interaction_at primitive

Metadata

Statusdone
Assignedagent-1384
Agent identityf51439356729d112a6c404803d88015d5b44832c6c584c62b96732b63c2b0c7e
Created2026-05-01T18:06:56.347715083+00:00
Started2026-05-01T19:25:25.431689743+00:00
Completed2026-05-01T19:58:09.505607398+00:00
Tagspriority-critical,fix,tui,perf,revert, eval-scheduled
Eval score0.92
└ blocking impact0.95
└ completeness0.95
└ constraint fidelity0.55
└ coordination overhead0.90
└ correctness0.95
└ downstream usability0.85
└ efficiency0.85
└ intent fidelity0.75
└ style adherence0.95

Description

Description

fix-tui-graph (commit 83b3814b3, including 73f2f5c11) shipped the chat-specific 'sort by last-activity' minimal version. User redirected via wg msg send AFTER agent had committed; the redirect to a generic primitive (last_interaction_at field on every task, debounced rendering) was not incorporated.

Result post-landing: every event (state change, chat message, agent heartbeat) triggers a re-sort of the visible task list. Viewport chases whatever just bubbled. The TUI is unusable — 'CONSTANTLY scrolling through things' per user.

User direct quote 2026-05-01: 'wg tui fixing sort has left a COMPLETELY BROKEN STATE the TUI scrolls through things CONSTANTLY it's so fucking wild and weird.'

Step 1: revert the bad ship

git revert 73f2f5c11    # 'fix: sort TUI graph view by last-activity within status groups'
# Plus any merge commit if necessary

Confirm revert is clean (no leftover field references, no dangling code paths). cargo build passes. cargo install. TUI is usable again.

Step 2: redo properly per the user's redirected scope

Per the redirect logged on fix-tui-graph (which the original agent missed):

Generic primitive

  • Add last_interaction_at to every task in graph.jsonl
  • Schema migration: hard add (per skip-back-compat-ceremony memory). Existing tasks default to created_at.
  • Single helper that wraps mutation + timestamp-bump. Every mutation site calls it.

Events that count as interaction

  • State change (open → in-progress → done/failed/etc.)
  • New wg log entry
  • New wg msg send / wg msg read
  • Chat history append (user typed, agent replied)
  • wg edit mutations
  • Worker agent heartbeat / activity (NOTE: this is the dangerous one — heartbeats fire frequently)

Render performance — CRITICAL

The original fix's failure mode was 'sort on every event = re-render on every event = viewport chases'. The redo MUST add:

  • Sort stability: when two tasks tie on last_interaction_at (e.g., both at the most recent second), tie-break by stable ID order so the visible row positions don't flip-flop
  • Render debouncing: the TUI's view-rebuild on graph events is throttled to ~5 events/sec MAX, regardless of underlying mutation rate. Sub-second events accumulate; the next debounced flush applies them all in one sort+render pass.
  • Worker heartbeat exclusion (recommended): heartbeats SHOULD update last_interaction_at semantically, but the TUI render path may ignore heartbeat-only changes and only re-sort on substantive events (state change, log entry, message, edit). Discuss in implementation: easiest is to exclude heartbeats from triggering re-sort.

Viewport behavior

The recent fix-tui-viewport / implement-tui-viewport cycle established a stability policy. The new sort logic must compose with that — the user expects viewport to stay anchored unless they explicitly move it. Sort changes that bubble a task into view should NOT yank the viewport; only explicit user navigation should change focus.

Validation

  • Step 1 (revert) lands cleanly: cargo build + cargo test pass; TUI doesn't constantly scroll; user can verify visibly
  • Step 2 (redo) — failing tests written first
  • last_interaction_at field exists on all tasks; updated by all mutation paths
  • Sort stability test: two tasks with identical last_interaction_at don't flip-flop
  • Render debounce test: simulate 100 events/sec; assert at most ~5 re-renders/sec
  • Heartbeat-noise test: with 8 active agents heartbeating, TUI doesn't re-sort visibly. Only substantive events bubble tasks.
  • Viewport test: when a task bubbles into view due to sort change, viewport doesn't jerk; user navigation still works
  • No regression of fix-tui-viewport stability properties
  • Live smoke: open TUI with 8/8 agents busy + active chat. ASSERT: no constant scrolling, viewport stable, sort visible-but-calm
  • cargo build + cargo test pass
  • Permanent smoke scenario covering the constant-scroll regression
  • cargo install --path . was run before claiming done

Process note

This is the third time today an agent committed before reading a redirect message. The pattern: wg msg send is best-effort with no ack; agents poll inbox at checkpoints; commit can happen between message-arrival and next-checkpoint.

For high-stakes redirects, consider:

  • A wg msg send-blocking variant that pauses the agent's spawn until the message is read (changes the IPC semantics; not free)
  • The corrective verify-task pattern (which DID work for verify-correct-wg-2 — but only because the redirect was honored; same pattern would have caught this miss too)

The cost of this revert+redo is real (2 worktrees, redo work). The lesson is: any redirect of in-flight work should be paired with a corrective verify-task pre-emptively, NOT after observing whether redirect landed.

Depends on

Required by

Messages 1 message

  1. #1user2026-05-01T18:18:39.112100166+00:00delivered
    HEADS UP 2026-05-01: a duplicate revert is now on main. The user needed an immediate fix while you worked on the redo, so I committed a separate revert directly on main as 0c7d23722.
    
    Your branch already has 3c534627a reverting the same content. Both reverts are semantically identical (revert of 83b3814b3).
    
    When merging your branch back to main: git will see two reverts of the same commit. Likely outcomes:
    - 'Already reverted' — git skips your 3c534627a as a no-op (preferred, clean).
    - Conflict on state.rs — resolve by taking the merged-into-main version (mine), then your redo work applies on top of that.
    
    The user's current binary already has my revert (0c7d23722) — they're unblocked. Your job: continue Step 2 (the redo) and merge your branch's redo work on top of the already-reverted main. The revert in your branch can be dropped from the merge if it conflicts, since main already has it.
    
    Concretely: when ready to merge, you can either:
    - `git rebase main` your branch onto current main; rebase will probably squash the duplicate revert; redo work applies on top
    - OR use `git checkout main && git cherry-pick <your-redo-commits>` to apply only the redo, skipping the revert
    
    Either is fine. Don't ship a redo that includes a stale revert that fights main.

Log