The vault_read fix changes what the injected workspace means: it is the
global root wired once at boot, not a per-tenant root, since one tool
instance serves every tenant. These tests seeded a non-master tenant but
wrote fixtures straight to the injected root, so they only passed while
reads ignored the tenant layer.
Write fixtures to config.TenantWorkspace(base, tenantID, slug) and carry
the slug in context, matching a real run. tenantSlug is now the single
source of truth shared with seedTenantAgent, so on-disk layout in a test
cannot drift from the seed.
* fix(vault): prevent vault_read id-namespace collision
vault_search was leaking KG/episodic entity ids into result sets even when
narrow `types` were requested, and callers then passed those ids to
vault_read which returned a generic "document not found". The cause was
threefold:
1. `types` filter was only applied to the vault fan-out; KG and episodic
ran unconditionally. Now gated by shouldFanout(types, key).
2. vault_search output lacked a per-source tool hint. Each result now ends
with " → use <tool>" naming the correct follow-up (vault_read,
knowledge_graph_search, or memory_search).
3. vault_read miss returned "document not found" without checking whether
the id belonged to a foreign namespace. It now probes KG then episodic
and returns a namespace-specific redirect error. Stores are injected
via SetKGStore/SetEpisodicStore, nil-safe, tenant-scoped.
Adds red→green characterization tests plus an end-to-end integration
scenario seeding a vault doc + KG entity with identical basenames.
* test(agent): bump none-mode prompt size budget to 3100
vault_read wiring (#948) added ~95 chars to read_file tool summary,
pushing none-mode prompt from <3000 to 3075 chars. Bump budget to
3100 (~775 tokens) to match the intentional addition.
* test(integration): ensure data_migrations table exists in reset helper
The reset helper runs before RunPendingHooks, but RunPendingHooks is
what normally creates data_migrations. On a fresh CI database the
DELETE fails with 'relation does not exist'. Create the table
defensively so reset works regardless of execution order.
* refactor(vault): per-source id fields + wire episodic into search
Align vault_search output fields with downstream tool input params:
doc_id (vault_read), entity_id (knowledge_graph_search), episodic_id
(memory_expand). Prevents LLMs from pattern-matching a generic `id:`
and misrouting a foreign-namespace uuid into vault_read. Fallback
redirect in vault_read now quotes id + names the correct param so
the LLM can self-correct in one turn.
Also wire stores.Episodic into VaultSearchService (stale comment
claimed pending-impl; PGEpisodicStore has existed and been in use
since v3). Unifies search fan-out with vault_read namespace probe.
---------
Co-authored-by: viettranx <viettranx@gmail.com>