Skip to content

memtrace route reports outside_indexed_root for every path, making the OpenCode rail a permanent no-op #82

Description

@hermanngeorge15

Summary

memtrace route returns {"action":"allow_raw","reason":"outside_indexed_root"} for every path I can test, including repositories that are current members of the active workspace and present in ~/.memtrace/watches.json.

Because the OpenCode rail plugin (memtrace-rail.js, written by memtrace install) acts only on action == "suppress_raw_with_result", the rail is a permanent no-op: it spawns a memtrace route subprocess on every bash/grep/glob call and can never influence the outcome, at any MEMTRACE_RAIL setting.

The rest of Memtrace is healthy — the MCP server resolves the same repositories fine — so this looks specific to route's indexed-root resolution, not to indexing or the backend.

This is the OpenCode-side counterpart of the enforcement mechanism requested in #78: the mechanism exists here, but currently cannot fire.

Environment

memtrace 1.1.8
opencode 1.18.21
OS macOS 26.5.2, arm64
MemDB mode local
Workspace dev, 9 members
Runtime memtrace start --workspace dev --headless, listening on 127.0.0.1:3030
memcore-server listening on 127.0.0.1:50051

Reproduction

From inside an indexed repository:

printf '{"host":"open-code","cwd":"%s","tool":"Grep","input":{"pattern":"def fill"},"session_id":"t"}' "$PWD" \
  | MEMTRACE_RAIL=rail memtrace route --json -

Actual:

{"action":"allow_raw","mode":"rail","surface":"outside_root",
 "reason":"outside_indexed_root",
 "message":"Target is outside every indexed root; allowed."}

Expected: the target is inside an indexed root, so route should evaluate the Grep against the graph and, in rail mode, be able to return suppress_raw_with_result.

mode correctly reflects MEMTRACE_RAIL (observe / nudge / rail), so the env var is read. Only the root resolution fails.

The path is genuinely indexed

list_indexed_repositories reports the same repo as a current member of workspace dev:

{"repo_id":"<repo-a>", "repo_path":"/Users/<user>/dev/<repo-a>",
 "branch":"<branch>", "nodes":5947, "edges":14145,
 "last_indexed_at":"2026-09-02T08:05:24.949393000Z"}

~/.memtrace/runtime.json lists it first in workspaceMembers, and ~/.memtrace/watches.json has a matching entry registered the same day:

{"path":"/Users/<user>/dev/<repo-a>", "repo_id":"<repo-a>",
 "branch":"<branch>",
 "registered_at":"2026-09-02T09:05:24.287875+00:00", "origin":"manual"}

So this is not a stale-registry problem — the path is in both registries and on disk. I initially assumed it was, and the data refuted it.

Scope of the failure

Five paths tested, all returning outside_indexed_root:

cwd indexed in watches.json result
<repo-a> (5,947 nodes) yes yes outside_indexed_root
<repo-a>/tools (subdirectory) yes via parent outside_indexed_root
<repo-b> (14,790 nodes) yes outside_indexed_root
<repo-c> (3,096 nodes) yes outside_indexed_root
<repo-d> yes outside_indexed_root

watches.json holds 367 entries and every one of their paths still exists on disk (0 dangling). No path tested, from either registry, resolved as inside an indexed root.

Workspace scoping cannot be supplied

route appears not to accept a workspace selector, so it cannot be pointed at dev explicitly:

  • memtrace route --json --workspace dev - → prints Named workspace dev: 9 member(s) (data dir: …) then fails with unknown flag: dev
  • memtrace route --json --workspace=dev - → resolves the workspace on the same line, then fails with unknown flag: -
  • MEMTRACE_WORKSPACE=dev memtrace route --json - → ignored; still outside_indexed_root
  • memtrace route --help shows the top-level help; it documents no route-specific flags

Note that with --workspace=dev the workspace is resolved — its 9 members and data dir are printed — and the argument parser then rejects the - stdin operand. So workspace lookup itself works; the two just cannot be combined.

Impact

memtrace-rail.js runs on tool.execute.before for bash, grep and glob, and fails open on anything that is not an explicit [Memtrace Rail] throw. With route never returning suppress_raw_with_result:

  • the rail cannot steer OpenCode away from raw grep/bash toward Memtrace, at any mode;
  • every bash/grep/glob call still pays for an execFileSync subprocess spawn (1500 ms timeout) to reach a decision that can never act.

Workaround: remove the plugin from ~/.config/opencode/plugin/. That costs only the already-inert rail and leaves the MCP server and skills fully functional.

Secondary observations

  1. plugin/ and plugins/ are both loaded. memtrace install wrote a byte-identical memtrace-rail.js into both ~/.config/opencode/plugin/ and ~/.config/opencode/plugins/. OpenCode 1.18.21's own help string reads .opencode/plugin/` or `.opencode/plugins/, so the hook registers twice and the subprocess spawns twice per tool call. The installer should pick one directory.

  2. memtrace status is misleading outside a workspace-aware cwd. Run from an indexed repo it reports Store present: no and No reachable owner. Run 'memtrace start' while a healthy runtime is serving that very repo — it resolves ./.memdb rather than the pinned workspace store. memtrace status --workspace dev is correct. Suggest falling back to the pinned workspace, or naming it in the error. (Adjacent to memtrace status prints a UI URL when no dashboard listener is running #64.)

  3. list_watched_paths returns count: 0 while watches.json holds 367 entries. These may intentionally mean different things (registered vs. actively watched), but the naming invites confusion and it is a plausible neighbour of the root-resolution bug above. (Possibly related to 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.)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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