Summary
PR #666 merged (5db96bb) with two Major L1 findings from the last adversarial review still open. In-canvas queue edit is effectively unusable, and a stale edit latch can ghost the band and block promote after a session hydrate.
Source review: PR #666 comment @ 5a910c7 (CONCERNS). 5a910c7 claimed to fix both; it did not.
Major 1 — blur-save closes ✎ on the first editor frame
queue_band.paint (native/harness/src/ui/queue_band.zig ~93–105) treats “TE not focused this frame” as blur and calls saveEdit → closeEdit.
beginEdit does not focusWidget.
- Frame 1 of ✎:
editing was false at paintRow, so no textEntry is created; queue_edit_textentry_id stays stale or null.
- Frame 2: TE is created, id stored, then
focused == null or focused != te_id is almost always true (focus is still the ✎ button). Editor closes.
ui.zig n < prev_msg clears queue_editing_index / buf but not queue_edit_textentry_id. After New/Clear the next ✎ compares against a dead widget id and save-closes in the same frame as beginEdit.
focused == null is treated as blur (unknown ≠ blur).
Break: Busy → enqueue ≥1 → click ✎ → field flashes at most one frame → preview row returns. Cannot edit. After New/Clear, even the flash is gone.
Major 2 — latch drop is still n < prev_msg, not queue SoT
inv_clear_messages already submit_queue.clears. The UI latch is only dropped on ring shrink (ui.zig ~379).
Rail / restore hydrate is hydrateMessages → clearMessages + push in one batch (lib/harnessBridge.ts). Next frame n is the new session length. If that length ≥ last_msg_count, the branch never runs.
FIFO is empty; queue_editing_index stays Some. desiredHeight still treats n==0 && editing as an 80 px ghost band (“0 queued”). Trigger A (ui.zig ~135) still refuses tryPromoteQueued.
New/Clear happens to work (one system line → n < prev). Session switch to a longer transcript does not.
Break: Session A (3 rows) → Busy, enqueue, ✎ (or leftover latch) → switch to session B (20 rows) → ghost band; next Busy follow-ups never promote until an unmarked Escape.
Suggested fix (not locked — for a later plan)
- Blur-save:
focusWidget on the open frame; ignore blur until the TE id has been seen focused at least once; treat focused == null as unknown, not blur; clear queue_edit_textentry_id on every latch drop.
- Latch vs SoT: if
queuedCount()==0, drop queue_editing_index / te id / draft. Do not proxy through ring n < prev_msg.
Non-goals
Refs
Summary
PR #666 merged (
5db96bb) with two Major L1 findings from the last adversarial review still open. In-canvas queue edit is effectively unusable, and a stale edit latch can ghost the band and block promote after a session hydrate.Source review: PR #666 comment @
5a910c7(CONCERNS).5a910c7claimed to fix both; it did not.Major 1 — blur-save closes ✎ on the first editor frame
queue_band.paint(native/harness/src/ui/queue_band.zig~93–105) treats “TE not focused this frame” as blur and callssaveEdit→closeEdit.beginEditdoes notfocusWidget.editingwas false atpaintRow, so notextEntryis created;queue_edit_textentry_idstays stale or null.focused == null or focused != te_idis almost always true (focus is still the ✎ button). Editor closes.ui.zign < prev_msgclearsqueue_editing_index/ buf but notqueue_edit_textentry_id. After New/Clear the next ✎ compares against a dead widget id and save-closes in the same frame asbeginEdit.focused == nullis treated as blur (unknown ≠ blur).Break: Busy → enqueue ≥1 → click ✎ → field flashes at most one frame → preview row returns. Cannot edit. After New/Clear, even the flash is gone.
Major 2 — latch drop is still
n < prev_msg, not queue SoTinv_clear_messagesalreadysubmit_queue.clears. The UI latch is only dropped on ring shrink (ui.zig~379).Rail / restore hydrate is
hydrateMessages→clearMessages+ push in one batch (lib/harnessBridge.ts). Next framenis the new session length. If that length ≥last_msg_count, the branch never runs.FIFO is empty;
queue_editing_indexstaysSome.desiredHeightstill treatsn==0 && editingas an 80 px ghost band (“0 queued”). Trigger A (ui.zig~135) still refusestryPromoteQueued.New/Clear happens to work (one system line →
n < prev). Session switch to a longer transcript does not.Break: Session A (3 rows) → Busy, enqueue, ✎ (or leftover latch) → switch to session B (20 rows) → ghost band; next Busy follow-ups never promote until an unmarked Escape.
Suggested fix (not locked — for a later plan)
focusWidgeton the open frame; ignore blur until the TE id has been seen focused at least once; treatfocused == nullas unknown, not blur; clearqueue_edit_textentry_idon every latch drop.queuedCount()==0, dropqueue_editing_index/ te id / draft. Do not proxy through ringn < prev_msg.Non-goals
· full/ UTF-8 preview (those shipped in feat(harness): queue follow-up prompts while Busy #666).#536canAutoContinuedrain.Refs