Skip to content

watch preview does not live-update for batch or create edits (ResidentServer.ExecuteBatch never calls NotifyWatch*) #169

Description

@letsgotoplay

Summary

When officecli watch <file> is running, single-command edits (add / set / move / remove) live-update the preview via SSE content patches (word-patch / replace / excel-patch). But batch and create --force edits do not trigger any content patch — the preview stays stale until the watch process is manually restarted.

This breaks any client that uses watch as a live preview while the agent edits the document via batch (the recommended way to apply multi-step edits) or re-creates the file.

Reproduce

BIN=officecli   # v1.0.125, macOS darwin-x64
mkdir -p /tmp/repro && cd /tmp/repro
$BIN create deck.pptx --force --json
$BIN add deck.pptx '/' --type slide --json
$BIN add deck.pptx '/slide[1]' --type shape --prop text="initial" --prop x=1cm --prop y=1cm --json
$BIN watch deck.pptx --port 9560 &
sleep 2

# stream /events in the background
curl -s -N --max-time 5 http://127.0.0.1:9560/events > /tmp/repro/ev.txt &
sleep 1

# (A) single add -> SSE emits a content patch ("replace")  ✅
$BIN add deck.pptx '/slide[1]' --type shape --prop text="via single add" --prop x=1cm --prop y=4cm --json
sleep 2

# (B) batch via stdin -> SSE emits ONLY mark-update/selection-update, NO content patch  ❌
echo '[{"command":"add","parent":"/slide[1]","type":"shape","props":{"text":"via batch","x":"1cm","y":"7cm"}}]' \
  | $BIN batch deck.pptx --json
sleep 2

# (C) create --force overwrite -> same, NO content patch  ❌
$BIN create deck.pptx --force --json
sleep 2

grep -oE '"action":"[^"]+"' /tmp/repro/ev.txt | sort -u

Observed SSE action values: mark-update, replace, selection-update.

  • The replace comes only from the single add in step (A).
  • Steps (B) batch and (C) create --force produce no content action — only mark-update / selection-update. The watched DOM never updates for those writes.

Same result for .xlsx (excel-patch is emitted for single set/add but not for batch) and .docx (word-patch for single add but not for batch).

Expected

batch and create applied to a file that has a running watch should trigger the same SSE content update as the equivalent single commands, so the preview reflects the new content without a manual watch restart.

Root cause

In src/officecli/ResidentServer.cs, ExecuteCommand dispatches each verb and calls a NotifyWatch* helper after the mutation (e.g. NotifyWatchSlideChanged, NotifyWatchRootChanged, NotifyWatchFullRefresh):

  • the add / set / move / remove / etc. cases each call NotifyWatch* after mutating (~lines 954–1006), so the watch SSE-patches the rendered DOM.
  • the batch case (~line 1016) only calls ExecuteBatch(request), and ExecuteBatch (~line 1039) applies items directly via CommandBuilder.ApplyBatchItems(_handler, items, …) without calling any NotifyWatch* helper before returning. So the in-memory document is updated but the watch is never notified → no content SSE event.
  • create --force overwriting a file that is currently watched likewise produces no content notification.

The non-resident (standalone CLI) path goes through CommandBuilder.cs which does call WatchNotifier.NotifyIfWatching(...) (~lines 1503–1563), but when a resident/watch is already holding the file, edits route through ResidentServer and miss the notification.

Suggested fix

Call a watch notification at the end of ResidentServer.ExecuteBatch (after ReconcileGlobalIds / PrintBatchResults) — e.g. NotifyWatchFullRefresh() (or the per-handler variant already used by the other verbs) — and ensure the create --force overwrite path notifies when the file is currently watched.

Environment

  • officecli: 1.0.125 (darwin-x64 bundled runtime)
  • Discovered while integrating officecli watch as a live in-place preview surface (single stable iframe over the watch HTTP server) in an Electron app, where batch/create are the common multi-step write paths.

Happy to send a PR if maintainers confirm the intended fix location.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions