Describe the bug
BackfillWorkspaceSlugs (internal/indexer/workspace_resolve.go) stamps WorkspaceID/ProjectID from config "whenever those fields are empty" — written to close the upgrade gap where old snapshots had "". It has no path for a field that is non-empty but stale.
A repo tracked on its own gets workspace_id = <repo name>. When it later joins a workspace the declaration moves — gortex workspace list shows the new one — but existing nodes keep the old key; only newly written nodes get the new one. filterTextMatchesByResolvedScope (tools_search_text.go) then fail-closes, dropping any match it cannot attribute to an in-scope node, so every query against that repo returns count: 0 with no error.
To Reproduce
gortex track <repo> on its own and let it index — it self-keys workspace_id = <repo name>.
gortex workspace set-all shared --global --yes, then restart the daemon.
- Any
search text for a string you know is in that repo returns count: 0. grep finds it.
- Confirm the cause:
select repo_prefix, workspace_id, count(*) from nodes group by 1,2;
-- repo_prefix = <repo>, workspace_id = <repo> -- never 'shared'
Expected behavior
Joining a workspace re-stamps existing nodes, or the query reports that it could not attribute them. A repo silently answering zero while workspace list shows it correctly configured is indistinguishable from "this string isn't in the codebase".
Environment:
- OS: macOS 26.5.1 (25F80)
- Go version: 1.26.5
- Gortex version: v0.63.7+0988412
Additional context
(Repo names anonymized.) On a five-repo store the split tracks exactly when each repo was tracked relative to the workspace:
repo-ts | repo-ts | 818190 <- stale, returns 0
repo-ts | shared | 91850
repo-cs | repo-cs | 540733 <- stale, returns 0
repo-cs | shared | 43
repo-go | shared | 537315 ok, tracked after the workspace existed
repo-infra | shared | 104203 ok
repo-agent | shared | 66574 ok
The three clean repos return hits; the two stale ones return zero. The store is intact throughout — symbol_fts joins 100% to nodes, and the graph holds nodes under the exact stamped path the trigram match carries, so this is purely the scope filter. UPDATE nodes SET workspace_id='shared', project_id='shared' over the two stale repos restored exact grep parity immediately.
One secondary fix worth taking regardless: the scope_note says "0 results within the active scope (repo:X) — widen with repo:"*"", which points at scope narrowing rather than the attribution failure that actually happened — and repo:"*" is inert for both text and symbols. project:<workspace> as a top-level arg does work, and is the fastest way to confirm this diagnosis.
Related from the other direction: #663 derives the workspace id from the folder name and collides across branches. Same root cause — the id comes from a mutable name and is never reconciled — opposite symptom.
Describe the bug
BackfillWorkspaceSlugs(internal/indexer/workspace_resolve.go) stampsWorkspaceID/ProjectIDfrom config "whenever those fields are empty" — written to close the upgrade gap where old snapshots had"". It has no path for a field that is non-empty but stale.A repo tracked on its own gets
workspace_id = <repo name>. When it later joins a workspace the declaration moves —gortex workspace listshows the new one — but existing nodes keep the old key; only newly written nodes get the new one.filterTextMatchesByResolvedScope(tools_search_text.go) then fail-closes, dropping any match it cannot attribute to an in-scope node, so every query against that repo returnscount: 0with no error.To Reproduce
gortex track <repo>on its own and let it index — it self-keysworkspace_id = <repo name>.gortex workspace set-all shared --global --yes, then restart the daemon.search textfor a string you know is in that repo returnscount: 0.grepfinds it.Expected behavior
Joining a workspace re-stamps existing nodes, or the query reports that it could not attribute them. A repo silently answering zero while
workspace listshows it correctly configured is indistinguishable from "this string isn't in the codebase".Environment:
Additional context
(Repo names anonymized.) On a five-repo store the split tracks exactly when each repo was tracked relative to the workspace:
The three clean repos return hits; the two stale ones return zero. The store is intact throughout —
symbol_ftsjoins 100% tonodes, and the graph holds nodes under the exact stamped path the trigram match carries, so this is purely the scope filter.UPDATE nodes SET workspace_id='shared', project_id='shared'over the two stale repos restored exactgrepparity immediately.One secondary fix worth taking regardless: the
scope_notesays "0 results within the active scope (repo:X) — widen withrepo:"*"", which points at scope narrowing rather than the attribution failure that actually happened — andrepo:"*"is inert for bothtextandsymbols.project:<workspace>as a top-level arg does work, and is the fastest way to confirm this diagnosis.Related from the other direction: #663 derives the workspace id from the folder name and collides across branches. Same root cause — the id comes from a mutable name and is never reconciled — opposite symptom.