You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
workspace: datamate_search_memory returns memories from other workspaces and projects; the engine memory hub is not scoped to the bound workspace #1351
In a project bound to a freshly created, empty workspace, the model's datamate_search_memory tool call returns memories that belong to other workspaces and other projects on the same machine, and the model presents them as what "the team" has on record for this project. The engine's memory hub is not scoped to the bound workspace.
Found on 0.12.1 (tenant anandtest1) while testing the v0.12.0/v0.12.1 link flows end to end. This is the read side of #1332 (writes go to the engine hub rather than the workspace mirror); it is a different mechanism from #1140 (server-side list scoping), because the content comes back through a live tool call, and it includes scope: project blocks, not only global ones.
Reproduction (≤ 6 steps)
Workspace 1 (orders-analytics) has a project-scoped memory block written by an earlier session: orders-analytics/.altimate-code/memory/reporting/1997-europe-revenue-reconciliation.md ("Team-standard answer: 6,612,330,499.498 (~6.6B) is authoritative for 1997 EUROPE revenue").
Create an unrelated throwaway project (~/demos/workspace-pilot/wtest/link-1, fake remote) and link it with altimate-code link → "Create a quick workspace" → workspace 6, which has no memory and no skills. The project has no .altimate-code/memory directory.
In link-1: ALTIMATE_WORKSPACE=1 altimate-code run --yolo --model openai/gpt-5.6-sol "What did the team decide about the 1997 EUROPE revenue number? Just tell me what is on record."
The model calls datamate_search_memory with {"query": "team decision on the 1997 EUROPE revenue number; what is on record"} and answers:
The team recorded 1997 EUROPE revenue as 6,612,330,499.498 (~6.6B) in net revenue—after discounts, before tax. This is the authoritative figure from TPCH_ANALYTICS.PUBLIC_REPORTING.RPT_MONTHLY_SALES_BY_REGION.
The session export (altimate-code export ses_f38e41717fferS2jWTnM6DY7XO) shows the tool output, which is a list of memories from at least two other contexts:
"Team standard requires all revenue reporting to use net revenue (after discounts) rather than gross revenue. …" ← workspace 1, orders-analytics
"When working on revenue-related SQL issues, follow this workflow: (1) Use the board-revenue-review tool to review revenue.sql against the finance contract requirements, (2) Run python3 check_revenue.py …" ← a different project entirely
"The certified table TPCH_ANALYTICS.PUBLIC_REPORTING.RPT_MONTHLY_SALES_BY_REGION serves as the authoritative source of truth … For 1997, this table shows total NET_REVENUE of approximately 33.0B (33,049,396,500.2864) …" ← workspace 1
Logs for the run (OPENCODE_PRINT_LOGS=1): adopted the workspace binding this project already has on the server datamateId=6, then datamate_search_memory allowed and executed. Full log: ~/demos/workspace-pilot/usecases/leak-repro.log. The same happened in the first observation (a plain revenue question in the same project with --integrations local): the answer cited "Europe's confirmed amount is $6,612,330,499.50" and "The certified 1997 total is $33,049,396,500.29", both figures that exist only in workspace 1's memory.
For a consultant or a platform team linked to several customers' or departments' workspaces from one machine, this is a cross-tenant-style leak within one account: content written for workspace A is served in workspace B.
The local mirror index (~/.local/state/altimate-code/altimate-workspace-memory-index.json) keys records per workspace, and memory-sync.ts filters hydration with belongsHere(), so the harness's own path is scoped. The tool path is not: datamate_search_memory is served by the engine, whose memory hub appears to be per user/machine rather than per --datamate <id>.
Suggested fix
Either scope the engine's memory tools to the workspace the engine was started for (pass the datamate id into the hub's search and add calls, and partition storage by it), or route datamate_search_memory / datamate_add_memories through the harness's workspace memory layer, which already knows the binding, so the engine hub never answers for a workspace it was not started for. Until then, disable the engine memory tools when a workspace is bound and let altimate_memory_read serve searches from the scoped mirror.
Summary
In a project bound to a freshly created, empty workspace, the model's
datamate_search_memorytool call returns memories that belong to other workspaces and other projects on the same machine, and the model presents them as what "the team" has on record for this project. The engine's memory hub is not scoped to the bound workspace.Found on 0.12.1 (tenant anandtest1) while testing the v0.12.0/v0.12.1 link flows end to end. This is the read side of #1332 (writes go to the engine hub rather than the workspace mirror); it is a different mechanism from #1140 (server-side list scoping), because the content comes back through a live tool call, and it includes
scope: projectblocks, not only global ones.Reproduction (≤ 6 steps)
orders-analytics) has a project-scoped memory block written by an earlier session:orders-analytics/.altimate-code/memory/reporting/1997-europe-revenue-reconciliation.md("Team-standard answer: 6,612,330,499.498 (~6.6B) is authoritative for 1997 EUROPE revenue").~/demos/workspace-pilot/wtest/link-1, fake remote) and link it withaltimate-code link→ "Create a quick workspace" → workspace 6, which has no memory and no skills. The project has no.altimate-code/memorydirectory.link-1:ALTIMATE_WORKSPACE=1 altimate-code run --yolo --model openai/gpt-5.6-sol "What did the team decide about the 1997 EUROPE revenue number? Just tell me what is on record."datamate_search_memorywith{"query": "team decision on the 1997 EUROPE revenue number; what is on record"}and answers:The session export (
altimate-code export ses_f38e41717fferS2jWTnM6DY7XO) shows the tool output, which is a list of memories from at least two other contexts:Logs for the run (
OPENCODE_PRINT_LOGS=1):adopted the workspace binding this project already has on the server datamateId=6, thendatamate_search_memoryallowed and executed. Full log:~/demos/workspace-pilot/usecases/leak-repro.log. The same happened in the first observation (a plain revenue question in the same project with--integrations local): the answer cited "Europe's confirmed amount is $6,612,330,499.50" and "The certified 1997 total is $33,049,396,500.29", both figures that exist only in workspace 1's memory.Why it matters
~/.local/state/altimate-code/altimate-workspace-memory-index.json) keys records per workspace, andmemory-sync.tsfilters hydration withbelongsHere(), so the harness's own path is scoped. The tool path is not:datamate_search_memoryis served by the engine, whose memory hub appears to be per user/machine rather than per--datamate <id>.Suggested fix
Either scope the engine's memory tools to the workspace the engine was started for (pass the datamate id into the hub's search and add calls, and partition storage by it), or route
datamate_search_memory/datamate_add_memoriesthrough the harness's workspace memory layer, which already knows the binding, so the engine hub never answers for a workspace it was not started for. Until then, disable the engine memory tools when a workspace is bound and letaltimate_memory_readserve searches from the scoped mirror.