Skip to content

feat(files): publish entry changes made outside the app - #8

Merged
Francois3d merged 1 commit into
localfrom
agent/files-external-entry-changes
Aug 29, 2026
Merged

Francois3d merged 1 commit into
localfrom
agent/files-external-entry-changes

Conversation

@Francois3d

Copy link
Copy Markdown
Owner

The Files tree only learned about changes the app itself made. A touch in a
terminal, a file added in Finder, or a build writing output stayed invisible
until the panel remounted or its 30s staleness window lapsed. The server-side
data was already live — the index picks up external creates and deletes through
its own filesystem watcher in ~100ms — but FileFinder exposes no change
callback, so the server never learned the index had moved and #1's signal was
never published.

A subscribed workspace now polls the one thing that is cheap to read: the index
entry count, mixedSearch("", { pageSize: 1 }).totalMatched, about 0.5ms
against this repo's 17k entries. When it moves, the existing coarse signal goes
out on the channel #1 built. One fibre per normalised workspace root, refcounted
to the subscriptions through an RcMap, so the last unsubscribe stops it. No
new filesystem watcher, no rescan, no second channel, and no contract, auth, or
client change. A read that fails logs a warning and is retried 30s later rather
than leaving the workspace unwatched for the rest of the subscription.

This narrows the gap rather than closing it. A rename, or a balanced add and
delete inside one interval, leaves the count where it was and publishes nothing;
those still wait out the client's 30s window, and in-app renames still go
through refresh. Content-only edits publish nothing by design — the tree does
not render contents, and subscribeProjectFileChanges already covers open
files.

Verification

Focused tests (vp test run apps/server/src/workspace/, 72 passed), lint, and
tsgo for t3, contracts, and client-runtime. The two new
WorkspaceEntries tests drive a TestClock, so they assert the silence and the
signal without a sleep or a timeout; both were mutation-checked — removing the
RcMap.get or the retry fails the matching test.

Verified in the web client against a real index, with the Files panel open on
one tab and never remounted, counting the entry-change pushes on the socket:

  • external touch appears in the tree, external rm removes it — one push each
  • idle repo for 31.7s: zero pushes
  • 30 files written under node_modules/, a 200KB cache blob, 5 commits and a
    git gc: zero pushes
  • the next real touch after those quiet windows pushed exactly one, proving
    the instrumentation was live throughout

No before/after images: nothing about the panel's appearance changes, only when
it stops being stale.

Closes #4

Model: Claude Opus 5, harness: Claude Code

The Files tree only learned about changes the app itself made. The index
already picks up external creates and deletes through its own filesystem
watcher within ~100ms, but it exposes no change callback, so the server
never published the signal the client is already listening for.

A subscribed workspace now polls the one thing that is cheap to read -
the index entry count, `mixedSearch("", { pageSize: 1 }).totalMatched`,
about 0.5ms - once a second, and publishes the existing coarse signal
when it moves. One fibre per workspace root, refcounted to the
subscriptions, so the last unsubscribe stops it; no new filesystem
watcher, no rescan, no second channel. A read that fails is retried
after 30 seconds rather than leaving the workspace unwatched.

This narrows the gap rather than closing it: a rename, or a balanced
add and delete inside one interval, leaves the count where it was and
still waits out the client's 30s staleness window. Content-only edits
publish nothing by design - the tree does not render contents, and open
files have their own subscription.

Closes #4

Model: Claude Opus 5, harness: Claude Code
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L labels Aug 29, 2026
@github-actions

Copy link
Copy Markdown

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ℹ️ No successful main baseline artifact is available yet. This run establishes the initial measurement.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire — 13.3 KiB — 15.1 KiB ✅
Codex Thread snapshot wire — 6.9 KiB — 7.3 KiB ✅
Codex Live turn WebSocket wire — 6.4 KiB — 7.8 KiB ✅
Codex Live turn WebSocket decoded — 55.6 KiB — 66.4 KiB ✅
Codex Live turn messages — 11 — 21 ✅
Claude Total thread wire — 13.3 KiB — 15.1 KiB ✅
Claude Thread snapshot wire — 6.9 KiB — 7.3 KiB ✅
Claude Live turn WebSocket wire — 6.4 KiB — 7.8 KiB ✅
Claude Live turn WebSocket decoded — 56.4 KiB — 66.4 KiB ✅
Claude Live turn messages — 10 — 21 ✅

Baseline: unavailable · PR result: 82229f2 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 109.4 KiB
  • Claude decoded thread snapshot: 110.1 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@Francois3d
Francois3d merged commit 3f6a67e into local Aug 29, 2026
16 checks passed
@Francois3d
Francois3d deleted the agent/files-external-entry-changes branch August 29, 2026 09:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Files tree does not update for changes made outside the app

1 participant