Skip to content

workspace: datamate_search_memory returns memories from other workspaces and projects; the engine memory hub is not scoped to the bound workspace #1351

Description

@anandgupta42

Summary

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)

  1. 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").
  2. 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.
  3. 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."
  4. 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.

Why it matters

  • Workspace isolation is the contract feat(workspace): state the linked workspace every turn, and mean the Altimate one #1330 states to the model every turn ("this workspace and never another"). Here a brand-new workspace inherits another workspace's decisions on its first question, and the model attributes them to "the team".
  • 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.

Activity

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

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions