memtrace-skills 1.2.6 → 1.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -60,7 +60,7 @@ function cursorWorkspaceBoundaryWarnings(ctx) {
60
60
  if (fs.existsSync(path.join(ctx.cwd, '.memtrace-workspace')))
61
61
  return [];
62
62
  return [
63
- `Cursor install target ${ctx.cwd} contains multiple sibling git repos but is not a blessed Memtrace workspace. For separate MemDBs, install/open each repo root. To intentionally share one MemDB, run 'memtrace start --bless-workspace' from ${ctx.cwd}, then verify with 'memtrace workspace status ${ctx.cwd}'.`,
63
+ `Cursor install target ${ctx.cwd} contains multiple sibling git repos but is not a blessed Memtrace workspace. For separate MemDBs, install/open each repo root. To intentionally share one MemDB, declare the members in a workspace manifest and run 'memtrace start --workspace-file <manifest>', or use the legacy Folder Group flow: 'memtrace start --bless-workspace' from ${ctx.cwd}, then verify with 'memtrace workspace status ${ctx.cwd}'. Once a store's membership is declared, a Memtrace MCP session can never widen it — adding a repo means editing the manifest and restarting that store's daemon with 'memtrace start --workspace-file <manifest>'.`,
64
64
  ];
65
65
  }
66
66
  export function registerCursorMcpAt(mcpFile, binary) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memtrace-skills",
3
- "version": "1.2.6",
3
+ "version": "1.2.7",
4
4
  "description": "Memtrace skills for AI coding agents — codebase exploration, temporal evolution, impact analysis, and more.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -31,20 +31,60 @@ arrays as arrays. No quoting numbers, no `"true"` for booleans.
31
31
 
32
32
  | Param | Type | Meaning |
33
33
  |---|---|---|
34
- | `repo_id` | string | Repository identifier from `list_indexed_repositories` (usually the repo folder name, e.g. `"memtrace"`) |
34
+ | `repo_id` | string | Repository identifier from `list_indexed_repositories` (usually the repo folder name, e.g. `"memtrace"`). That list covers this session's store only — see "A `repo_id` this session's store does not hold" below |
35
35
  | `branch` *or* `branch_name` | string | Git branch. Default `"main"` across every tool. Both spellings occur historically — use whichever the specific tool's schema says |
36
36
  | `limit` | integer | Cap on returned results. Always a JSON number, never a string |
37
37
  | `depth` | integer | Graph traversal hops. 1–5 is reasonable; >8 explodes on wide graphs |
38
38
  | `target` / `symbol` | string | Symbol **name** for graph tools — `get_impact`/`analyze_relationships` use `target`; `get_symbol_context` uses `symbol` |
39
39
  | `from` / `to` / `since` / `until` | string | e.g. `"90d ago"`, `"2026-04-17T13:00:00Z"`, `"yesterday"` — relative strings work for temporal tools. **Never `days` on `get_evolution`.** |
40
40
 
41
+
42
+ ## A `repo_id` this session's store does not hold
43
+
44
+ Ten tools check the requested `repo_id` against this store's declared members
45
+ before they answer: `find_code`, `find_symbol`, `get_codebase_briefing`,
46
+ `find_central_symbols`, `find_dependency_path`, `find_bridge_symbols`,
47
+ `list_processes`, `get_process_flow`, `list_communities` and
48
+ `get_repository_stats`. On those, a wrong `repo_id` is never a success-shaped
49
+ zero.
50
+
51
+ - If a live runtime's store declares the repository, the read is forwarded to
52
+ it and the answer carries `_meta.answered_by: "store_owner_daemon"`,
53
+ `_meta.owner_pid`, `_meta.owner_http` and `_meta.store`.
54
+ - Otherwise the call is refused: `repo_in_store: false`,
55
+ `error_code: "repo_not_in_store"`, `count: 0`, and a `diagnostic` naming the
56
+ store that answered, its `members`, `reason` and `repo_lives_in` when a
57
+ runtime declares the repository elsewhere. Nothing was searched. A `reason`
58
+ of `ambiguous_repo_id` is the opposite case: the id matches more than one
59
+ repository in this store, so pass the exact id.
60
+
61
+ The remaining `repo_id` tools do not check membership: the relationship tools
62
+ (`get_symbol_context`, `get_impact`, `analyze_relationships`), the temporal
63
+ tools (`get_evolution`, `get_timeline`, `get_changes_since`), the topology
64
+ tools (`get_api_topology`, `find_api_endpoints`, `find_api_calls`,
65
+ `get_service_diagram`) and `get_source_window`. They read this session's store
66
+ whatever id you pass, set no `repo_in_store` key, and return an ordinary empty
67
+ result for a repository this store does not hold. A zero from one of them is
68
+ not an absence — confirm the repository is a member with one of the ten above
69
+ first.
70
+
71
+ `repo_in_store: true` means membership was proven. `repo_in_store: null` means
72
+ the session discovered no workspace, so membership was assumed rather than
73
+ proven.
74
+
75
+ Tools that change a store, a runtime, or anything outside the process
76
+ (`index_directory`, `delete_repository`, `watch_directory`, `link_*`,
77
+ `cleanup_*`, `replay_history`, the mutating `fleet_*` calls, and the rest) are
78
+ never forwarded. Run them from the session attached to the store that holds
79
+ the repository.
80
+
41
81
  ## Search & discovery
42
82
 
43
83
  ### `find_code`
44
84
  | Field | Type | Required | Default | Notes |
45
85
  |---|---|---|---|---|
46
86
  | `query` | string | yes | — | Natural-language text |
47
- | `repo_id` | string | no | all repos | Scope to one repo |
87
+ | `repo_id` | string | no | this store's members | Scope to one repo. Omitted, an unambiguous session resolves to its own repository and a multi-repo store fans out across its discovered members |
48
88
  | `file_path` | string | no | — | Path substring filter |
49
89
  | `limit` | integer | no | `20` | Max 100 |
50
90
  | `as_of` | string | no | now | Time-travel search |
@@ -58,7 +98,7 @@ No `kind` param — use `find_symbol(kind=...)` instead.
58
98
  | `name` | string | yes | — | Exact or partial identifier |
59
99
  | `fuzzy` | boolean | no | `false` | Exact-match today; field kept for API parity |
60
100
  | `edit_distance` | integer | no | `2` | Max 2. Only used when `fuzzy: true` |
61
- | `repo_id` | string | no | all repos | |
101
+ | `repo_id` | string | no | inferred | One repository is inferred only when that is unambiguous; otherwise the call returns `repo_scope_required` |
62
102
  | `kind` | string | no | — | Same enum as `find_code` |
63
103
  | `file_path` | string | no | — | |
64
104
  | `limit` | integer | no | `10` | Capped at `50` |
@@ -15,6 +15,11 @@ Call `list_indexed_repositories` first. If the repo is already indexed, skip to
15
15
 
16
16
  Otherwise, call `index_directory` with the project path, then poll `check_job_status` until completion.
17
17
 
18
+ `list_indexed_repositories` covers this session's store only. If a later call
19
+ answers `error_code: "repo_not_in_store"`, the repository is indexed in the
20
+ store named in `diagnostic.repo_lives_in` — route the question there instead
21
+ of indexing a second copy here.
22
+
18
23
  **Success criteria:** Repo appears in `list_indexed_repositories` with non-zero node/edge counts.
19
24
 
20
25
  ### 2. Get the lay of the land
@@ -116,6 +121,18 @@ The deliverable is the 7-part overview above. Skeleton (one headline per part):
116
121
  6. Recent Activity — 31 episodes in 30d; hottest file per `top_changed_files`
117
122
  7. Technical Debt — top-10 complex functions, highest complexity first
118
123
 
124
+ ## A `busy` answer is a wait, not an absence
125
+
126
+ Heavy graph work runs one repository-sized fold at a time, because holding
127
+ two in memory is what the bound protects against. When the lane is already
128
+ held, a caller is not queued indefinitely: after a bounded wait it gets a
129
+ successful answer carrying `"busy": true`, `"lane": "graph_materialization"`,
130
+ `"retryable": true`, and a `holder` block naming the operation that holds the
131
+ lane and how long it has held it. Report it as a wait, retry once the holder
132
+ is likely done, or narrow the call (one `repo_id`, a smaller `limit`, a
133
+ shorter window) so it does not need the lane. Do not read it as an empty
134
+ graph and do not fall back to file search.
135
+
119
136
  ## Common Mistakes
120
137
 
121
138
  | Mistake | Reality |
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: memtrace-first
3
- description: "Route code discovery, debugging, flow tracing, how-code-works questions, and pre-edit rationale checks in indexed source-code repos to Memtrace graph plus Cortex decision tools. Use first before searching/reading code, and before editing, refactoring, deleting, or re-picking an approach that may have a recorded decision, ban, convention, or contract. Do not use Grep, Glob, rg, find, or manual file browsing for code discovery when Memtrace is indexed. Zero results are not permission to grep; diagnose/reindex with Memtrace."
3
+ description: "Route code discovery, debugging, flow tracing, how-code-works questions, and pre-edit rationale checks in indexed source-code repos to Memtrace graph plus Cortex decision tools. Use first before searching/reading code, and before editing, refactoring, deleting, or re-picking an approach that may have a recorded decision, ban, convention, or contract. Do not use Grep, Glob, rg, find, or manual file browsing for code discovery when Memtrace is indexed. Zero results are not permission to grep; diagnose/reindex with Memtrace. A `repo_not_in_store` refusal means a different store holds the repository: route to that store, never grep and never re-index here."
4
4
  ---
5
5
 
6
6
  # Memtrace First
@@ -37,19 +37,93 @@ Memtrace's hybrid search = **BM25 over symbol metadata** (name, signature, file_
37
37
 
38
38
  The semantic side means **string literals, error messages, magic constants, log strings, and any text inside an indexed symbol's body are findable through `find_code`**. The body got embedded; the embedding catches it. You do NOT need `Grep` to hunt for `STRIPE_KEY_FOO_BAR` if it lives inside a function in your indexed codebase.
39
39
 
40
- ## Zero results are not a grep license
40
+ ## Read the envelope before you diagnose anything
41
+
42
+ An empty-looking answer has three different causes and they need different
43
+ responses: the tool refused because another store holds the repository, the
44
+ tool never checked membership at all, or this store genuinely holds nothing
45
+ for the query. Before diagnosing, check which tool you called and what
46
+ `repo_in_store` and `error_code` say.
47
+
48
+ ### Only some tools check membership
49
+
50
+ Ten tools check the requested `repo_id` against this store's declared members
51
+ before they answer, and they are the only ones that set the top-level
52
+ `repo_in_store` key: `find_code`, `find_symbol`, `get_codebase_briefing`,
53
+ `find_central_symbols`, `find_dependency_path`, `find_bridge_symbols`,
54
+ `list_processes`, `get_process_flow`, `list_communities` and
55
+ `get_repository_stats`.
56
+
57
+ Every other tool that takes a `repo_id` — `get_symbol_context`, `get_impact`,
58
+ `analyze_relationships`, `get_evolution`, `get_timeline`, `get_source_window`,
59
+ `get_api_topology` and the rest — reads this session's store whatever id you
60
+ pass. Their answers carry no `repo_in_store` key at all, so a zero from one of
61
+ them is not evidence about membership and not evidence that the repository is
62
+ unindexed. When membership is in doubt, establish it with one of the ten above
63
+ before you conclude anything. `repo_in_store: null` is a third case:
64
+ membership was assumed rather than proven, because the session discovered no
65
+ workspace.
66
+
67
+ ### `repo_not_in_store` — wrong store, not a missing index
68
+
69
+ One of the ten, asked for a `repo_id` this session's store does not hold,
70
+ answers `repo_in_store: false` with `error_code: "repo_not_in_store"`. It
71
+ searched nothing. The `diagnostic` block names the store that answered, its
72
+ `members`, the `reason`, and — when a live runtime declares the repository —
73
+ `repo_lives_in` with that store, its path, `owner_pid`, `ui_port`,
74
+ `control_port` and `endpoint`.
75
+
76
+ 1. Read `diagnostic.reason` first. `ambiguous_repo_id` is not a routing
77
+ problem: the id matches more than one repository in *this* store, and the
78
+ message says to pass the exact id. Do that and stop here. Everything below
79
+ applies to the `not_in_store` reason.
80
+ 2. Do not fall back to file search. The refusal says so itself, in `_note`:
81
+ "Wrong store for this repo_id; do not fall back to filesystem search, ask
82
+ the session attached to the store named in diagnostic.repo_lives_in."
83
+ 3. Do not call `index_directory`. The repository is already indexed in the
84
+ store `repo_lives_in` names. Indexing it here would build a second copy in
85
+ the wrong store — writes are never routed to another store, so the call
86
+ lands locally.
87
+ 4. Tell the user which store holds it, and route the question to the session
88
+ or daemon attached to that store. A session cannot ask an owner to add a
89
+ repository: changing the members of a store that already has them means
90
+ running `memtrace start` for that store, with `--workspace-file <manifest>`
91
+ or `--workspace <name>` when a manifest or a Named Workspace owns its
92
+ scope.
93
+ 5. If `repo_lives_in` is absent, no live Memtrace runtime declares the
94
+ repository. Running `memtrace start` in its own workspace is the fix.
95
+
96
+ A refusal is not the only outcome. When the runtime whose store declares the
97
+ repository is live and publishes a reachable control port, the same read is
98
+ forwarded to it and you get the real answer, marked
99
+ `_meta.answered_by: "store_owner_daemon"` alongside `_meta.owner_pid`,
100
+ `_meta.owner_http` and `_meta.store`. That answer is authoritative; say which
101
+ store produced it.
102
+
103
+ A forwarded read can also come back as a bounded `busy` answer with
104
+ `"lane": "graph_materialization"` and `retryable: true`, naming what holds
105
+ the graph lane and for how long. That is a wait, not an absence — retry, or
106
+ narrow the call.
41
107
 
42
- If Memtrace returns 0 results, or repository stats look incomplete, do **not**
43
- infer that a source subdirectory is outside the index. Diagnose through
44
- Memtrace:
108
+ ## Zero results are not a grep license
45
109
 
46
- 1. Call `list_indexed_repositories` and identify the repo root/repo_id.
47
- 2. If the path is under that indexed repo root, keep using Memtrace.
48
- 3. Retry with broader `find_code` terms and, when available, `file_path` filters
110
+ A genuine zero — `count: 0` from a tool that reported `repo_in_store: true` —
111
+ is a coverage question. Do **not** infer that a source subdirectory is outside
112
+ the index. Diagnose through Memtrace:
113
+
114
+ 1. Call `list_indexed_repositories` and identify the repo root/repo_id. It
115
+ lists this session's store only, so a repository missing from it may still
116
+ be indexed and live in another store.
117
+ 2. If the zero came from a tool that sets no `repo_in_store` key, re-ask about
118
+ the same repository through `find_code` or `find_symbol` before reading
119
+ anything into it. Those check membership; the first tool did not.
120
+ 3. If the path is under that indexed repo root, keep using Memtrace.
121
+ 4. Retry with broader `find_code` terms and, when available, `file_path` filters
49
122
  such as `ui/`, `memtrace-ui/`, `src/`, or the framework directory.
50
- 4. If the language/path still appears missing, run `index_directory` on the repo
51
- root with `incremental: true` (or ask before `clear_existing: true`).
52
- 5. Report the indexing coverage problem instead of silently switching to grep.
123
+ 5. If the language/path still appears missing, run `index_directory` on the repo
124
+ root with `incremental: true` (or ask before `clear_existing: true`). Do
125
+ this only after steps 1 and 2 show the repository is a member of this store.
126
+ 6. Report the indexing coverage problem instead of silently switching to grep.
53
127
 
54
128
  ### Workspace Boundary Check
55
129
 
@@ -61,9 +135,22 @@ answer from stale repos.
61
135
 
62
136
  - For separate repos: use the actual git repo root as the `index_directory`
63
137
  path, or ask the user to open/run the agent from that repo root.
64
- - For an intentional shared workspace: the user should bless it explicitly with
65
- `memtrace start --bless-workspace`, then verify it with
138
+ - For an intentional shared workspace: a portable manifest is the durable
139
+ form — `memtrace start --workspace-file <manifest>` declares the members and
140
+ records the manifest as the owner of the store's membership. The Folder
141
+ Group flow still works and the CLI now calls it legacy: the user blesses the
142
+ parent with `memtrace start --bless-workspace`, then verifies it with
66
143
  `memtrace workspace status <path>`; the workspace marker should be present.
144
+ - A store whose membership is already owned by a manifest or a Named Workspace
145
+ cannot be widened by a plain `memtrace start` from a parent folder. That
146
+ start is refused with "a folder-walked start cannot widen it. Run
147
+ `<command>`", and the refusal names the exact command to run instead. Run
148
+ it. Do not route around the refusal by indexing elsewhere. A folder-sourced
149
+ store has no owning definition, so that refusal does not apply to it. A
150
+ `memtrace mcp` session can never widen an existing store under any flag,
151
+ including `--workspace-file`: adding a repository is always a `memtrace
152
+ start` for that store, carrying the manifest or the workspace name when one
153
+ owns its members.
67
154
  - If `list_indexed_repositories` returns empty or its metadata says the MCP
68
155
  child resolved a data dir from cwd because no workspace marker/git root was
69
156
  found, surface the workspace mismatch. Do not "fix" it by indexing the broad
@@ -79,8 +166,11 @@ These are the ONLY cases where file tools beat memtrace:
79
166
 
80
167
  - **Files outside every indexed repo root.** Confirm this with
81
168
  `list_indexed_repositories`; 0 search results or missing language stats do not
82
- prove it. Vendored deps, system headers, and excluded dirs
83
- (`.git`, `node_modules`, `target`, `dist`) are examples Memtrace cannot see.
169
+ prove it, and neither does a `repo_not_in_store` refusal — that list covers
170
+ this session's store only, and the refusal's `diagnostic.repo_lives_in`
171
+ names the store that does hold the repository. Vendored deps, system
172
+ headers, and excluded dirs (`.git`, `node_modules`, `target`, `dist`) are
173
+ examples Memtrace cannot see.
84
174
  - **Non-source artifacts.** `.env`, `package.json`, build scripts, top-level `README.md`, raw config files. Memtrace indexes parseable code, not configuration text.
85
175
  - **Pure file-inventory questions.** "How many `*.test.ts` files exist", "list every Markdown file in `docs/`". You're asking for a file count, not a symbol search.
86
176
  - **Reading at a known path outside Memtrace.** For configs, docs, or non-source artifacts that Memtrace cannot index, file `Read` is fine. For source-code spans returned by Memtrace, read the precise line range (your harness's `Read` with offset/limit, or `get_source_window` if your harness lacks bounded reads). Do not whole-file Read when you have a span.
@@ -101,6 +191,8 @@ For everything else inside the indexed repo, memtrace is the right tool.
101
191
  | "Should I change/delete/refactor `foo`?" | `find_symbol`/`get_symbol_context` → `recall_decision("foo / subsystem / approach")`; if a symbol id is available, `why_is_this_here` + `governing_contracts`; then `get_impact`. |
102
192
  | "Can I use/switch to pattern or library X?" | `recall_decision("X")` FIRST; bans and conventions are decisions. Verify a matching decision with `verify_intent(decision_id)` before relying on it. |
103
193
  | "What changed in `auth.ts` last week?" | `get_evolution(repo_id, from="7d ago", mode="recent", file_path="auth.ts")`. |
194
+ | The answer carries `error_code: "repo_not_in_store"` | Read `diagnostic.reason`. On `ambiguous_repo_id`, pass the exact id. Otherwise read `diagnostic.repo_lives_in` and route the question to the session attached to that store. Never grep, never `index_directory`. |
195
+ | A `repo_id` tool answered zero with no `repo_in_store` key | It never checked membership. Re-ask through `find_code` or `find_symbol` before treating the zero as an absence. |
104
196
  | "List all `*.test.ts` files." | `Glob` (file inventory, not symbol search). |
105
197
  | "Find this string in my `.env`." | `Grep` (non-source artifact). |
106
198
  | "I'm about to edit `foo` — show me its source." | Bounded `Read(file_path, offset=start_line, limit=end_line-start_line+8)`, or `get_source_window` if your harness lacks bounded reads. Never whole-file. |
@@ -203,7 +295,9 @@ You are violating this skill if you think:
203
295
  | "I'll just switch to library/pattern X" | `recall_decision("X")` first — you may be reintroducing a banned approach. |
204
296
  | "It's just a quick search" | Grep has no understanding of call graphs, communities, or time |
205
297
  | "I don't know if it's indexed" | Check with `list_indexed_repositories` first — takes 1 second |
206
- | "Memtrace returned 0 results" | Broaden the Memtrace query, check repo_id/path coverage, then reindex if needed |
298
+ | "Memtrace returned 0 results" | Check `repo_in_store` first. If it is true, broaden the query, check repo_id/path coverage, then reindex if needed |
299
+ | "The zero had no `repo_in_store`, so the repo is not indexed" | Only ten tools set that key. A zero from the others says nothing about membership — re-ask through `find_code` or `find_symbol` |
300
+ | "It said `repo_not_in_store`, so I'll index it here" | The repository is already indexed in the store `diagnostic.repo_lives_in` names. Indexing here makes a second copy in the wrong store |
207
301
  | "Stats only show Rust, but I need `ui/` or `memtrace-ui/`" | That is a coverage diagnostic. Reindex the repo root; do not grep source code. |
208
302
  | "The user didn't say to use Memtrace" | User asked about the code. Repo is indexed. Use Memtrace. |
209
303
  | "This is a simple question" | Simple questions benefit most — one `find_symbol` vs 20 file reads |
@@ -253,5 +347,7 @@ When this skill applies, it overrides default file-search behavior. Use the spec
253
347
 
254
348
  - The answer is grounded in Memtrace graph results (search hit → `get_symbol_context` / `get_impact`), not file-tool discovery.
255
349
  - Grep/Glob/Read appear only via the documented narrow exceptions (non-source artifacts, paths outside every indexed root, file inventory, bounded span reads).
256
- - Zero-result queries were diagnosed (`list_indexed_repositories` → broaden → reindex), not bypassed to grep.
350
+ - A `repo_not_in_store` refusal was routed on `diagnostic.repo_lives_in`, not answered by grep and not answered by indexing the repository into this store.
351
+ - Genuine zero-result queries (`repo_in_store: true`) were diagnosed (`list_indexed_repositories` → broaden → reindex), not bypassed to grep.
352
+ - A zero from a tool that sets no `repo_in_store` key was re-checked through a membership-checking tool before it was called an absence.
257
353
  - Any source read was bounded to the span Memtrace returned.
@@ -73,6 +73,18 @@ Params are **`source`** and **`target`** (symbol names) — not `from`/`to`.
73
73
 
74
74
  `find_dependency_path` returns the ordered symbol chain from `source` to `target`; `list_communities` returns module partitions with their member symbols.
75
75
 
76
+ ## A `busy` answer is a wait, not an absence
77
+
78
+ Heavy graph work runs one repository-sized fold at a time, because holding
79
+ two in memory is what the bound protects against. When the lane is already
80
+ held, a caller is not queued indefinitely: after a bounded wait it gets a
81
+ successful answer carrying `"busy": true`, `"lane": "graph_materialization"`,
82
+ `"retryable": true`, and a `holder` block naming the operation that holds the
83
+ lane and how long it has held it. Report it as a wait, retry once the holder
84
+ is likely done, or narrow the call (one `repo_id`, a smaller `limit`, a
85
+ shorter window) so it does not need the lane. Do not read it as an empty
86
+ graph and do not fall back to file search.
87
+
76
88
  ## Common Mistakes
77
89
 
78
90
  | Mistake | Reality |
@@ -33,6 +33,13 @@ root (for example `ui/`, `memtrace-ui/`, `web/`, `frontend/`, or `src/`), treat
33
33
  that as a stale/partial index. Do not use grep as a workaround. Run incremental
34
34
  indexing on the repo root, then retry the Memtrace query.
35
35
 
36
+ If a search instead came back with `error_code: "repo_not_in_store"`, stop.
37
+ That repository is not missing from the index; it is indexed in a different
38
+ store, named in `diagnostic.repo_lives_in`. `list_indexed_repositories` lists
39
+ this session's store only, so its absence there proves nothing. Indexing it
40
+ here would build a second copy in the wrong store. Report the store that holds
41
+ it and route the question to the session or daemon attached to that store.
42
+
36
43
  ### 2. Index the directory
37
44
 
38
45
  Use the `index_directory` MCP tool:
@@ -44,10 +51,20 @@ Use the `index_directory` MCP tool:
44
51
  If the selected path is just a folder containing multiple independent git repos,
45
52
  do not index that parent folder unless the user explicitly wants a shared
46
53
  workspace. For separate repos, index each repo root separately. For intentional
47
- sharing, ask the user to bless the parent explicitly with
48
- `memtrace start --bless-workspace` first, then verify the boundary with
54
+ sharing, the durable form is a portable manifest started with
55
+ `memtrace start --workspace-file <manifest>`; the legacy Folder Group flow is
56
+ `memtrace start --bless-workspace` from the parent, verified with
49
57
  `memtrace workspace status <path>`.
50
58
 
59
+ A store whose membership is already owned by a manifest or a Named Workspace
60
+ cannot be widened from an MCP session, nor by a plain `memtrace start` from a
61
+ parent folder. Adding a repository means restarting that store's daemon with
62
+ the definition that owns it — `memtrace start --workspace-file <manifest>` or
63
+ `memtrace start --workspace <name>`, editing the manifest first when it is a
64
+ manifest. If a start is refused with "a folder-walked start cannot widen it",
65
+ run the command the refusal names. No MCP session can widen an existing store
66
+ under any flag, whatever declared it.
67
+
51
68
  **Success criteria:** You receive a `job_id` immediately.
52
69
 
53
70
  ### 3. Poll for completion
@@ -22,7 +22,7 @@ Find code using hybrid BM25 + semantic search (RRF). Primary discovery tool —
22
22
  | Param | Required | Default | Notes |
23
23
  |---|---|---|---|
24
24
  | `query` | yes | — | Natural language or symbol text |
25
- | `repo_id` | no | all repos | |
25
+ | `repo_id` | no | this store's members | Omitted, an unambiguous session resolves to its own repository; a multi-repo store fans out across its discovered members |
26
26
  | `limit` | no | 20 | Max 100 |
27
27
  | `file_path` | no | — | Path/directory substring filter |
28
28
  | `as_of` | no | now | ISO-8601 time-travel |
@@ -39,7 +39,7 @@ Find code using hybrid BM25 + semantic search (RRF). Primary discovery tool —
39
39
  | Param | Required | Default | Notes |
40
40
  |---|---|---|---|
41
41
  | `name` | yes | — | Symbol name to search |
42
- | `repo_id` | no | all repos | |
42
+ | `repo_id` | no | inferred | One repository is inferred only when that is unambiguous; otherwise the call returns `repo_scope_required` |
43
43
  | `fuzzy` | no | false | API field exists; currently exact-match in backend |
44
44
  | `edit_distance` | no | 2 | Only when fuzzy enabled |
45
45
  | `kind` | no | — | `Function`, `Class`, `Method`, etc. |
@@ -58,7 +58,7 @@ Full parameter spec for every Memtrace tool: `references/mcp-parameters.md` (bun
58
58
 
59
59
  - Exact name → `find_symbol`
60
60
  - Behaviour description → `find_code`
61
- - All repos → omit `repo_id`
61
+ - Every member of this store → omit `repo_id` on `find_code`. `find_symbol` needs one repository and infers it only when that is unambiguous.
62
62
 
63
63
  ### 2. Execute search
64
64
 
@@ -98,6 +98,23 @@ One `find_code` / `find_symbol` result entry:
98
98
 
99
99
  `score` (and `id`) appear only with `include_diagnostics: true`.
100
100
 
101
+ ## A `repo_id` this store does not hold
102
+
103
+ `find_code` and `find_symbol` check membership before they search. If the
104
+ `repo_id` you passed is not a member of this session's store, the call is
105
+ handed to the live runtime whose store declares it, and the answer comes back
106
+ marked `_meta.answered_by: "store_owner_daemon"` with `_meta.owner_pid`,
107
+ `_meta.owner_http` and `_meta.store`. Use that answer and say which store
108
+ produced it.
109
+
110
+ If no live runtime holds the repository, or its owner cannot answer, you get
111
+ a refusal instead: `repo_in_store: false`, `error_code: "repo_not_in_store"`,
112
+ `count: 0`, and a `diagnostic` naming this store, its `members` and — when it
113
+ is known — `repo_lives_in`. Nothing was searched. Do not read that as an
114
+ empty index, do not switch to grep, and do not index the repository into this
115
+ store. Route the question to the session attached to the store the diagnostic
116
+ names.
117
+
101
118
  ## Common Mistakes
102
119
 
103
120
  | Mistake | Reality |
@@ -105,4 +122,5 @@ One `find_code` / `find_symbol` result entry:
105
122
  | `find_code(kind=...)` | **`kind` only on `find_symbol`** |
106
123
  | Passing symbol `id` to graph tools | Use **`name`** as `symbol` / `target` |
107
124
  | Assuming `fuzzy: true` always works | Backend is exact-match today — try spelling variants |
108
- | Skipping `list_indexed_repositories` | Verify repo is indexed first |
125
+ | Skipping `list_indexed_repositories` | Verify repo is indexed first — it lists this session's store only |
126
+ | Reading `repo_not_in_store` as an empty index | Nothing was searched; the repository lives in the store `diagnostic.repo_lives_in` names |
@@ -85,6 +85,18 @@ Params are **`source`** and **`target`** (symbol names) — not `from`/`to`.
85
85
 
86
86
  `find_dependency_path` returns the ordered symbol chain from `source` to `target`; `list_communities` returns module partitions with their member symbols.
87
87
 
88
+ ## A `busy` answer is a wait, not an absence
89
+
90
+ Heavy graph work runs one repository-sized fold at a time, because holding
91
+ two in memory is what the bound protects against. When the lane is already
92
+ held, a caller is not queued indefinitely: after a bounded wait it gets a
93
+ successful answer carrying `"busy": true`, `"lane": "graph_materialization"`,
94
+ `"retryable": true`, and a `holder` block naming the operation that holds the
95
+ lane and how long it has held it. Report it as a wait, retry once the holder
96
+ is likely done, or narrow the call (one `repo_id`, a smaller `limit`, a
97
+ shorter window) so it does not need the lane. Do not read it as an empty
98
+ graph and do not fall back to file search.
99
+
88
100
  ## Common Mistakes
89
101
 
90
102
  | Mistake | Reality |
@@ -43,6 +43,13 @@ root (for example `ui/`, `memtrace-ui/`, `web/`, `frontend/`, or `src/`), treat
43
43
  that as a stale/partial index. Do not use grep as a workaround. Run incremental
44
44
  indexing on the repo root, then retry the Memtrace query.
45
45
 
46
+ If a search instead came back with `error_code: "repo_not_in_store"`, stop.
47
+ That repository is not missing from the index; it is indexed in a different
48
+ store, named in `diagnostic.repo_lives_in`. `list_indexed_repositories` lists
49
+ this session's store only, so its absence there proves nothing. Indexing it
50
+ here would build a second copy in the wrong store. Report the store that holds
51
+ it and route the question to the session or daemon attached to that store.
52
+
46
53
  ### 2. Index the directory
47
54
 
48
55
  Use the `index_directory` MCP tool:
@@ -54,10 +61,20 @@ Use the `index_directory` MCP tool:
54
61
  If the selected path is just a folder containing multiple independent git repos,
55
62
  do not index that parent folder unless the user explicitly wants a shared
56
63
  workspace. For separate repos, index each repo root separately. For intentional
57
- sharing, ask the user to bless the parent explicitly with
58
- `memtrace start --bless-workspace` first, then verify the boundary with
64
+ sharing, the durable form is a portable manifest started with
65
+ `memtrace start --workspace-file <manifest>`; the legacy Folder Group flow is
66
+ `memtrace start --bless-workspace` from the parent, verified with
59
67
  `memtrace workspace status <path>`.
60
68
 
69
+ A store whose membership is already owned by a manifest or a Named Workspace
70
+ cannot be widened from an MCP session, nor by a plain `memtrace start` from a
71
+ parent folder. Adding a repository means restarting that store's daemon with
72
+ the definition that owns it — `memtrace start --workspace-file <manifest>` or
73
+ `memtrace start --workspace <name>`, editing the manifest first when it is a
74
+ manifest. If a start is refused with "a folder-walked start cannot widen it",
75
+ run the command the refusal names. No MCP session can widen an existing store
76
+ under any flag, whatever declared it.
77
+
61
78
  **Success criteria:** You receive a `job_id` immediately.
62
79
 
63
80
  ### 3. Poll for completion
@@ -32,7 +32,7 @@ Find code using hybrid BM25 + semantic search (RRF). Primary discovery tool —
32
32
  | Param | Required | Default | Notes |
33
33
  |---|---|---|---|
34
34
  | `query` | yes | — | Natural language or symbol text |
35
- | `repo_id` | no | all repos | |
35
+ | `repo_id` | no | this store's members | Omitted, an unambiguous session resolves to its own repository; a multi-repo store fans out across its discovered members |
36
36
  | `limit` | no | 20 | Max 100 |
37
37
  | `file_path` | no | — | Path/directory substring filter |
38
38
  | `as_of` | no | now | ISO-8601 time-travel |
@@ -49,7 +49,7 @@ Find code using hybrid BM25 + semantic search (RRF). Primary discovery tool —
49
49
  | Param | Required | Default | Notes |
50
50
  |---|---|---|---|
51
51
  | `name` | yes | — | Symbol name to search |
52
- | `repo_id` | no | all repos | |
52
+ | `repo_id` | no | inferred | One repository is inferred only when that is unambiguous; otherwise the call returns `repo_scope_required` |
53
53
  | `fuzzy` | no | false | API field exists; currently exact-match in backend |
54
54
  | `edit_distance` | no | 2 | Only when fuzzy enabled |
55
55
  | `kind` | no | — | `Function`, `Class`, `Method`, etc. |
@@ -68,7 +68,7 @@ Full parameter spec for every Memtrace tool: `references/mcp-parameters.md` (bun
68
68
 
69
69
  - Exact name → `find_symbol`
70
70
  - Behaviour description → `find_code`
71
- - All repos → omit `repo_id`
71
+ - Every member of this store → omit `repo_id` on `find_code`. `find_symbol` needs one repository and infers it only when that is unambiguous.
72
72
 
73
73
  ### 2. Execute search
74
74
 
@@ -108,6 +108,23 @@ One `find_code` / `find_symbol` result entry:
108
108
 
109
109
  `score` (and `id`) appear only with `include_diagnostics: true`.
110
110
 
111
+ ## A `repo_id` this store does not hold
112
+
113
+ `find_code` and `find_symbol` check membership before they search. If the
114
+ `repo_id` you passed is not a member of this session's store, the call is
115
+ handed to the live runtime whose store declares it, and the answer comes back
116
+ marked `_meta.answered_by: "store_owner_daemon"` with `_meta.owner_pid`,
117
+ `_meta.owner_http` and `_meta.store`. Use that answer and say which store
118
+ produced it.
119
+
120
+ If no live runtime holds the repository, or its owner cannot answer, you get
121
+ a refusal instead: `repo_in_store: false`, `error_code: "repo_not_in_store"`,
122
+ `count: 0`, and a `diagnostic` naming this store, its `members` and — when it
123
+ is known — `repo_lives_in`. Nothing was searched. Do not read that as an
124
+ empty index, do not switch to grep, and do not index the repository into this
125
+ store. Route the question to the session attached to the store the diagnostic
126
+ names.
127
+
111
128
  ## Common Mistakes
112
129
 
113
130
  | Mistake | Reality |
@@ -115,4 +132,5 @@ One `find_code` / `find_symbol` result entry:
115
132
  | `find_code(kind=...)` | **`kind` only on `find_symbol`** |
116
133
  | Passing symbol `id` to graph tools | Use **`name`** as `symbol` / `target` |
117
134
  | Assuming `fuzzy: true` always works | Backend is exact-match today — try spelling variants |
118
- | Skipping `list_indexed_repositories` | Verify repo is indexed first |
135
+ | Skipping `list_indexed_repositories` | Verify repo is indexed first — it lists this session's store only |
136
+ | Reading `repo_not_in_store` as an empty index | Nothing was searched; the repository lives in the store `diagnostic.repo_lives_in` names |
@@ -33,6 +33,11 @@ Call `list_indexed_repositories` first. If the repo is already indexed, skip to
33
33
 
34
34
  Otherwise, call `index_directory` with the project path, then poll `check_job_status` until completion.
35
35
 
36
+ `list_indexed_repositories` covers this session's store only. If a later call
37
+ answers `error_code: "repo_not_in_store"`, the repository is indexed in the
38
+ store named in `diagnostic.repo_lives_in` — route the question there instead
39
+ of indexing a second copy here.
40
+
36
41
  **Success criteria:** Repo appears in `list_indexed_repositories` with non-zero node/edge counts.
37
42
 
38
43
  ### 2. Get the lay of the land
@@ -134,6 +139,18 @@ The deliverable is the 7-part overview above. Skeleton (one headline per part):
134
139
  6. Recent Activity — 31 episodes in 30d; hottest file per `top_changed_files`
135
140
  7. Technical Debt — top-10 complex functions, highest complexity first
136
141
 
142
+ ## A `busy` answer is a wait, not an absence
143
+
144
+ Heavy graph work runs one repository-sized fold at a time, because holding
145
+ two in memory is what the bound protects against. When the lane is already
146
+ held, a caller is not queued indefinitely: after a bounded wait it gets a
147
+ successful answer carrying `"busy": true`, `"lane": "graph_materialization"`,
148
+ `"retryable": true`, and a `holder` block naming the operation that holds the
149
+ lane and how long it has held it. Report it as a wait, retry once the holder
150
+ is likely done, or narrow the call (one `repo_id`, a smaller `limit`, a
151
+ shorter window) so it does not need the lane. Do not read it as an empty
152
+ graph and do not fall back to file search.
153
+
137
154
  ## Common Mistakes
138
155
 
139
156
  | Mistake | Reality |
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: memtrace-first
3
- description: "Route code discovery, debugging, flow tracing, how-code-works questions, and pre-edit rationale checks in indexed source-code repos to Memtrace graph plus Cortex decision tools. Use first before searching/reading code, and before editing, refactoring, deleting, or re-picking an approach that may have a recorded decision, ban, convention, or contract. Do not use Grep, Glob, rg, find, or manual file browsing for code discovery when Memtrace is indexed. Zero results are not permission to grep; diagnose/reindex with Memtrace."
3
+ description: "Route code discovery, debugging, flow tracing, how-code-works questions, and pre-edit rationale checks in indexed source-code repos to Memtrace graph plus Cortex decision tools. Use first before searching/reading code, and before editing, refactoring, deleting, or re-picking an approach that may have a recorded decision, ban, convention, or contract. Do not use Grep, Glob, rg, find, or manual file browsing for code discovery when Memtrace is indexed. Zero results are not permission to grep; diagnose/reindex with Memtrace. A `repo_not_in_store` refusal means a different store holds the repository: route to that store, never grep and never re-index here."
4
4
  allowed-tools:
5
5
  - mcp__memtrace__list_indexed_repositories
6
6
  - mcp__memtrace__index_directory
@@ -70,19 +70,93 @@ Memtrace's hybrid search = **BM25 over symbol metadata** (name, signature, file_
70
70
 
71
71
  The semantic side means **string literals, error messages, magic constants, log strings, and any text inside an indexed symbol's body are findable through `find_code`**. The body got embedded; the embedding catches it. You do NOT need `Grep` to hunt for `STRIPE_KEY_FOO_BAR` if it lives inside a function in your indexed codebase.
72
72
 
73
- ## Zero results are not a grep license
73
+ ## Read the envelope before you diagnose anything
74
+
75
+ An empty-looking answer has three different causes and they need different
76
+ responses: the tool refused because another store holds the repository, the
77
+ tool never checked membership at all, or this store genuinely holds nothing
78
+ for the query. Before diagnosing, check which tool you called and what
79
+ `repo_in_store` and `error_code` say.
80
+
81
+ ### Only some tools check membership
82
+
83
+ Ten tools check the requested `repo_id` against this store's declared members
84
+ before they answer, and they are the only ones that set the top-level
85
+ `repo_in_store` key: `find_code`, `find_symbol`, `get_codebase_briefing`,
86
+ `find_central_symbols`, `find_dependency_path`, `find_bridge_symbols`,
87
+ `list_processes`, `get_process_flow`, `list_communities` and
88
+ `get_repository_stats`.
89
+
90
+ Every other tool that takes a `repo_id` — `get_symbol_context`, `get_impact`,
91
+ `analyze_relationships`, `get_evolution`, `get_timeline`, `get_source_window`,
92
+ `get_api_topology` and the rest — reads this session's store whatever id you
93
+ pass. Their answers carry no `repo_in_store` key at all, so a zero from one of
94
+ them is not evidence about membership and not evidence that the repository is
95
+ unindexed. When membership is in doubt, establish it with one of the ten above
96
+ before you conclude anything. `repo_in_store: null` is a third case:
97
+ membership was assumed rather than proven, because the session discovered no
98
+ workspace.
99
+
100
+ ### `repo_not_in_store` — wrong store, not a missing index
101
+
102
+ One of the ten, asked for a `repo_id` this session's store does not hold,
103
+ answers `repo_in_store: false` with `error_code: "repo_not_in_store"`. It
104
+ searched nothing. The `diagnostic` block names the store that answered, its
105
+ `members`, the `reason`, and — when a live runtime declares the repository —
106
+ `repo_lives_in` with that store, its path, `owner_pid`, `ui_port`,
107
+ `control_port` and `endpoint`.
108
+
109
+ 1. Read `diagnostic.reason` first. `ambiguous_repo_id` is not a routing
110
+ problem: the id matches more than one repository in *this* store, and the
111
+ message says to pass the exact id. Do that and stop here. Everything below
112
+ applies to the `not_in_store` reason.
113
+ 2. Do not fall back to file search. The refusal says so itself, in `_note`:
114
+ "Wrong store for this repo_id; do not fall back to filesystem search, ask
115
+ the session attached to the store named in diagnostic.repo_lives_in."
116
+ 3. Do not call `index_directory`. The repository is already indexed in the
117
+ store `repo_lives_in` names. Indexing it here would build a second copy in
118
+ the wrong store — writes are never routed to another store, so the call
119
+ lands locally.
120
+ 4. Tell the user which store holds it, and route the question to the session
121
+ or daemon attached to that store. A session cannot ask an owner to add a
122
+ repository: changing the members of a store that already has them means
123
+ running `memtrace start` for that store, with `--workspace-file <manifest>`
124
+ or `--workspace <name>` when a manifest or a Named Workspace owns its
125
+ scope.
126
+ 5. If `repo_lives_in` is absent, no live Memtrace runtime declares the
127
+ repository. Running `memtrace start` in its own workspace is the fix.
128
+
129
+ A refusal is not the only outcome. When the runtime whose store declares the
130
+ repository is live and publishes a reachable control port, the same read is
131
+ forwarded to it and you get the real answer, marked
132
+ `_meta.answered_by: "store_owner_daemon"` alongside `_meta.owner_pid`,
133
+ `_meta.owner_http` and `_meta.store`. That answer is authoritative; say which
134
+ store produced it.
135
+
136
+ A forwarded read can also come back as a bounded `busy` answer with
137
+ `"lane": "graph_materialization"` and `retryable: true`, naming what holds
138
+ the graph lane and for how long. That is a wait, not an absence — retry, or
139
+ narrow the call.
74
140
 
75
- If Memtrace returns 0 results, or repository stats look incomplete, do **not**
76
- infer that a source subdirectory is outside the index. Diagnose through
77
- Memtrace:
141
+ ## Zero results are not a grep license
78
142
 
79
- 1. Call `list_indexed_repositories` and identify the repo root/repo_id.
80
- 2. If the path is under that indexed repo root, keep using Memtrace.
81
- 3. Retry with broader `find_code` terms and, when available, `file_path` filters
143
+ A genuine zero — `count: 0` from a tool that reported `repo_in_store: true` —
144
+ is a coverage question. Do **not** infer that a source subdirectory is outside
145
+ the index. Diagnose through Memtrace:
146
+
147
+ 1. Call `list_indexed_repositories` and identify the repo root/repo_id. It
148
+ lists this session's store only, so a repository missing from it may still
149
+ be indexed and live in another store.
150
+ 2. If the zero came from a tool that sets no `repo_in_store` key, re-ask about
151
+ the same repository through `find_code` or `find_symbol` before reading
152
+ anything into it. Those check membership; the first tool did not.
153
+ 3. If the path is under that indexed repo root, keep using Memtrace.
154
+ 4. Retry with broader `find_code` terms and, when available, `file_path` filters
82
155
  such as `ui/`, `memtrace-ui/`, `src/`, or the framework directory.
83
- 4. If the language/path still appears missing, run `index_directory` on the repo
84
- root with `incremental: true` (or ask before `clear_existing: true`).
85
- 5. Report the indexing coverage problem instead of silently switching to grep.
156
+ 5. If the language/path still appears missing, run `index_directory` on the repo
157
+ root with `incremental: true` (or ask before `clear_existing: true`). Do
158
+ this only after steps 1 and 2 show the repository is a member of this store.
159
+ 6. Report the indexing coverage problem instead of silently switching to grep.
86
160
 
87
161
  ### Workspace Boundary Check
88
162
 
@@ -94,9 +168,22 @@ answer from stale repos.
94
168
 
95
169
  - For separate repos: use the actual git repo root as the `index_directory`
96
170
  path, or ask the user to open/run the agent from that repo root.
97
- - For an intentional shared workspace: the user should bless it explicitly with
98
- `memtrace start --bless-workspace`, then verify it with
171
+ - For an intentional shared workspace: a portable manifest is the durable
172
+ form — `memtrace start --workspace-file <manifest>` declares the members and
173
+ records the manifest as the owner of the store's membership. The Folder
174
+ Group flow still works and the CLI now calls it legacy: the user blesses the
175
+ parent with `memtrace start --bless-workspace`, then verifies it with
99
176
  `memtrace workspace status <path>`; the workspace marker should be present.
177
+ - A store whose membership is already owned by a manifest or a Named Workspace
178
+ cannot be widened by a plain `memtrace start` from a parent folder. That
179
+ start is refused with "a folder-walked start cannot widen it. Run
180
+ `<command>`", and the refusal names the exact command to run instead. Run
181
+ it. Do not route around the refusal by indexing elsewhere. A folder-sourced
182
+ store has no owning definition, so that refusal does not apply to it. A
183
+ `memtrace mcp` session can never widen an existing store under any flag,
184
+ including `--workspace-file`: adding a repository is always a `memtrace
185
+ start` for that store, carrying the manifest or the workspace name when one
186
+ owns its members.
100
187
  - If `list_indexed_repositories` returns empty or its metadata says the MCP
101
188
  child resolved a data dir from cwd because no workspace marker/git root was
102
189
  found, surface the workspace mismatch. Do not "fix" it by indexing the broad
@@ -112,8 +199,11 @@ These are the ONLY cases where file tools beat memtrace:
112
199
 
113
200
  - **Files outside every indexed repo root.** Confirm this with
114
201
  `list_indexed_repositories`; 0 search results or missing language stats do not
115
- prove it. Vendored deps, system headers, and excluded dirs
116
- (`.git`, `node_modules`, `target`, `dist`) are examples Memtrace cannot see.
202
+ prove it, and neither does a `repo_not_in_store` refusal — that list covers
203
+ this session's store only, and the refusal's `diagnostic.repo_lives_in`
204
+ names the store that does hold the repository. Vendored deps, system
205
+ headers, and excluded dirs (`.git`, `node_modules`, `target`, `dist`) are
206
+ examples Memtrace cannot see.
117
207
  - **Non-source artifacts.** `.env`, `package.json`, build scripts, top-level `README.md`, raw config files. Memtrace indexes parseable code, not configuration text.
118
208
  - **Pure file-inventory questions.** "How many `*.test.ts` files exist", "list every Markdown file in `docs/`". You're asking for a file count, not a symbol search.
119
209
  - **Reading at a known path outside Memtrace.** For configs, docs, or non-source artifacts that Memtrace cannot index, file `Read` is fine. For source-code spans returned by Memtrace, read the precise line range (your harness's `Read` with offset/limit, or `get_source_window` if your harness lacks bounded reads). Do not whole-file Read when you have a span.
@@ -134,6 +224,8 @@ For everything else inside the indexed repo, memtrace is the right tool.
134
224
  | "Should I change/delete/refactor `foo`?" | `find_symbol`/`get_symbol_context` → `recall_decision("foo / subsystem / approach")`; if a symbol id is available, `why_is_this_here` + `governing_contracts`; then `get_impact`. |
135
225
  | "Can I use/switch to pattern or library X?" | `recall_decision("X")` FIRST; bans and conventions are decisions. Verify a matching decision with `verify_intent(decision_id)` before relying on it. |
136
226
  | "What changed in `auth.ts` last week?" | `get_evolution(repo_id, from="7d ago", mode="recent", file_path="auth.ts")`. |
227
+ | The answer carries `error_code: "repo_not_in_store"` | Read `diagnostic.reason`. On `ambiguous_repo_id`, pass the exact id. Otherwise read `diagnostic.repo_lives_in` and route the question to the session attached to that store. Never grep, never `index_directory`. |
228
+ | A `repo_id` tool answered zero with no `repo_in_store` key | It never checked membership. Re-ask through `find_code` or `find_symbol` before treating the zero as an absence. |
137
229
  | "List all `*.test.ts` files." | `Glob` (file inventory, not symbol search). |
138
230
  | "Find this string in my `.env`." | `Grep` (non-source artifact). |
139
231
  | "I'm about to edit `foo` — show me its source." | Bounded `Read(file_path, offset=start_line, limit=end_line-start_line+8)`, or `get_source_window` if your harness lacks bounded reads. Never whole-file. |
@@ -236,7 +328,9 @@ You are violating this skill if you think:
236
328
  | "I'll just switch to library/pattern X" | `recall_decision("X")` first — you may be reintroducing a banned approach. |
237
329
  | "It's just a quick search" | Grep has no understanding of call graphs, communities, or time |
238
330
  | "I don't know if it's indexed" | Check with `list_indexed_repositories` first — takes 1 second |
239
- | "Memtrace returned 0 results" | Broaden the Memtrace query, check repo_id/path coverage, then reindex if needed |
331
+ | "Memtrace returned 0 results" | Check `repo_in_store` first. If it is true, broaden the query, check repo_id/path coverage, then reindex if needed |
332
+ | "The zero had no `repo_in_store`, so the repo is not indexed" | Only ten tools set that key. A zero from the others says nothing about membership — re-ask through `find_code` or `find_symbol` |
333
+ | "It said `repo_not_in_store`, so I'll index it here" | The repository is already indexed in the store `diagnostic.repo_lives_in` names. Indexing here makes a second copy in the wrong store |
240
334
  | "Stats only show Rust, but I need `ui/` or `memtrace-ui/`" | That is a coverage diagnostic. Reindex the repo root; do not grep source code. |
241
335
  | "The user didn't say to use Memtrace" | User asked about the code. Repo is indexed. Use Memtrace. |
242
336
  | "This is a simple question" | Simple questions benefit most — one `find_symbol` vs 20 file reads |
@@ -286,5 +380,7 @@ When this skill applies, it overrides default file-search behavior. Use the spec
286
380
 
287
381
  - The answer is grounded in Memtrace graph results (search hit → `get_symbol_context` / `get_impact`), not file-tool discovery.
288
382
  - Grep/Glob/Read appear only via the documented narrow exceptions (non-source artifacts, paths outside every indexed root, file inventory, bounded span reads).
289
- - Zero-result queries were diagnosed (`list_indexed_repositories` → broaden → reindex), not bypassed to grep.
383
+ - A `repo_not_in_store` refusal was routed on `diagnostic.repo_lives_in`, not answered by grep and not answered by indexing the repository into this store.
384
+ - Genuine zero-result queries (`repo_in_store: true`) were diagnosed (`list_indexed_repositories` → broaden → reindex), not bypassed to grep.
385
+ - A zero from a tool that sets no `repo_in_store` key was re-checked through a membership-checking tool before it was called an absence.
290
386
  - Any source read was bounded to the span Memtrace returned.