Skip to content

watch_directory registers but ingests nothing: 0 episodes in 17h across 42 non-merge commits and a controlled probe save (1.1.1, Windows) #71

Description

@hrabbach

watch_directory registers but ingests nothing: 0 episodes across 15h, 42 non-merge commits, and a controlled probe save

Version: 1.1.1 (1.1.2 available, not installed)
OS: Windows 11 Pro 10.0.26200 (host TZ GMT Standard Time, UTC+01:00 — all times below are UTC)
Backend: memdb
Daemon: headless, started 2026-08-12T18:21:52Z, uptime ~17h at time of testing

Summary

A watch is registered for the correct repo and the correct branch, two seconds after the daemon
started
, the daemon is alive and responsive, every other MCP call is healthy — and the watcher has
produced zero episodes in the ~17 hours since. This is not the enumeration hang of #70; all
aggregate calls are currently instant (see "Secondary observation" below). It is specifically the
live-ingest path.

daemon start:       2026-08-12T18:21:52Z
watch registered:   2026-08-12T18:21:54Z   (+2s — created at startup, not a stale persisted entry)
latest episode:     2026-08-12T11:11:44Z   (7h BEFORE the daemon started)

Because the watch is created fresh at daemon startup, there is no restart-persistence confound:
this is not a stale registry entry whose OS-level subscription was lost. It is a freshly created
watch that never ingests.

Expected vs actual

Per the watch_directory tool description, two event classes should produce episodes:

  1. Source file saves → a working_tree episode, "within ~1 second of saving"
  2. Git ref changes (commits, pulls, rebases) → a git_commit episode

Observed: neither occurs. No episode of either type has been created since the watch started.

Registered watch (from list_watched_paths)

{
  "repo_id":    "my-repo",
  "branch":     "feature/my-branch",
  "path":       "\\\\?\\C:\\Users\\USER\\Coding\\my-repo",
  "origin":     "manual",
  "started_at": "2026-08-12T18:21:54.661928500Z"
}

12 watches total, 12 distinct repo_ids, all \\?\-prefixed, no plain-path twins — i.e. the
path-duplication issue discussed in #70 is not present here.

Evidence

get_repository_stats — the two branch scopes disagree exactly as you'd expect if ingest were dead

branch: "master" (the indexed branch):

{"episode_count":1257, "last_episode_time":"2026-08-12T11:11:44.811304+00:00",
 "last_episode_type":"working_tree", "last_indexed":"2026-08-12T11:13:03.886170900Z",
 "indexing_incomplete":false, "total_nodes":41834, "total_edges":44852,
 "community_count":438, "symbol_node_count":9079}

branch: "feature/my-branch" (the watched branch):

{"episode_count":0, "last_episode_time":null, "last_episode_id":null, "last_episode_type":null,
 "last_indexed":"2026-08-12T21:21:43.717955700+00:00",
 "indexing_incomplete":true, "total_nodes":1, "total_edges":0,
 "symbol_node_count":0, "nodes_by_kind":{"Repository":1}}

Note the branch scope exists and was created at 21:21:43Z (roughly when the branch was checked
out) — so branch detection works — but it contains only the Repository node. indexing_incomplete: true correctly flags it, which is good behaviour and made the diagnosis possible.

Git activity the watcher should have seen

Since started_at (2026-08-12T18:21:54Z), on the watched path:

  • 49 commits across all refs
  • 42 of those are non-merge commits on the checked-out branch

The 42 matters: watch_directory's docs say "Git operation guards prevent noisy intermediate
episodes during rebases and merges", so suppressed merge commits would be by design. These 42 are
ordinary commits and are not covered by that guard.

Controlled probe — a plain save, not a merge, not a worktree

To rule out git-operation guards entirely, I wrote a new source file directly in the watched tree:

// web/js/_memtrace_watch_probe.js
export function memtraceWatchProbeFunction239() {
  return 'probe';
}
  • Path is inside the watched root, is not .gitignored, is a supported language (JavaScript)
  • Written at 10:56:59Z; waited 25 seconds (25× the documented ~1s latency)

Result:

find_symbol("memtraceWatchProbeFunction239", repo_id="my-repo")  → 0 matches
branch scope: episode_count still 0, last_episode_time still null,
              last_indexed still 2026-08-12T21:21:43Z (did not advance by even 1s)

The probe file was removed afterwards.

Caveat, stated honestly: I waited 25 s, not 25 min, so an extremely slow indexer is not formally
excluded. Against that: last_indexed did not advance at all, which I'd expect to move first.

What I ruled out

Hypothesis Status
Enumeration hang (#70) wedged the daemon Ruled out — all calls instant, see below
\\?\ path duplication Ruled out — 12 watches, 12 distinct repo_ids, 0 plain-path twins
Merge-commit guards suppressing episodes Insufficient — 42 non-merge commits also produced nothing
Work happening in ignored worktrees Insufficient — true (.claude/ is gitignored, so agent worktrees are invisible), but the probe save was in the main checkout
Wrong branch scope Ruled out — the watch is registered on exactly the checked-out branch
Daemon dead / unresponsive Ruled outmemcore-server at 58% of one core, all MCP calls responsive
Stale watch entry persisted from a previous daemon run Ruled out — watch registered 2 s after daemon start; created fresh, not inherited

Secondary observation — #70's enumeration hang does NOT reproduce on this daemon

Worth recording because it contradicts earlier reports on this same version:

  • list_indexed_repositories returned instantly, with the full list — on 1.1.1, the version
    previously observed hanging >120 s (and aborting at 300 s on 1.1.0 with zero log output)
  • get_repository_stats(my-repo, master) then returned instantly — and critically, this was
    after the enumerate call, on the same daemon. So the "one enumerate call permanently poisons the
    aggregate path until restart" model does not reproduce
  • No version change was involved (still 1.1.1). Daemon uptime ~15 h, started fresh

Suggestion: any future "still broken on version X" report for #70 should record daemon uptime and
whether a restart preceded the test
, since the failure appears state-dependent rather than
version-determined.

Additional note: no daemon log

There is no memcore-server.log at <repo>/.memdb/ (the directory contains only
.memtrace-store-scope.json) nor under ~/.memtrace/. ~/.memtrace/rail-telemetry.jsonl is being
written actively (records of shape
{action, mode, surface, reason, message, intent, mapped_tool}) but records tool-routing decisions,
not watcher or ingest activity. So there is no server-side log to attach for the watcher path.

Questions

  1. Is there a way to inspect watcher health at runtime — a counter of received FS events vs episodes
    created — to tell "no events received" apart from "events received, ingest failing"? That is the
    single fact needed to localise this, and it is currently unobservable from outside.
  2. Should list_watched_paths expose last_event_at / episodes_created per watch? A watch that is
    registered but ingesting nothing is currently indistinguishable from a healthy idle one — which
    is why this went unnoticed for 17 hours.
  3. On Windows, are \\?\-prefixed extended-length paths fully supported by the FS-notification
    layer? All 12 watches here are stored \\?\-prefixed. The registration clearly accepts them; it is
    less obvious that the underlying watcher subscribes successfully with that prefix. This is a guess,
    flagged as such — I have no direct evidence for it, only that it is the most conspicuous difference
    between this setup and a POSIX one.
  4. Is there a server-side log for the watcher path? None was found (see above), so there is nothing
    to attach showing whether FS events arrive at all.

A hypothesis I raised and then disproved, recorded so nobody re-derives it: that the watch registry
is persisted across restarts while the OS-level watcher is not re-established. It is false here — the
watch is created 2 s after daemon start, not inherited from a previous run. (The apparent 1-hour gap
that suggested otherwise was a timezone artifact: PowerShell's Get-Process ... StartTime returns
local time, while list_watched_paths.started_at is UTC, and this host is UTC+01:00.
Converting both to UTC collapses the gap to 2 seconds. Worth watching for in any Windows report.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions