How the ai-memory Scope Resolution System Handles Missing Workspaces and Projects

The ai-memory scope resolution system enforces a strict "fail-closed, create-on-write" policy that returns specific errors for missing workspaces and projects during read operations while allowing silent creation during authorized write operations.

The akitaonrails/ai-memory repository implements a robust scope resolution mechanism in crates/ai-memory-store/src/scope.rs that governs how the runtime resolves workspace and project pairs for every store request. The system distinguishes strictly between read-only lookups and write-capable creation paths, ensuring predictable behavior when resources are absent.

Explicit Scope Lookup Failure Modes

When a request provides explicit workspace and project names, the lookup_existing_scope function (lines 81-95) enforces strict validation. The resolution flow first calls lookup_existing_workspace (lines 200-214) to verify the workspace exists, then attempts to locate the project within that workspace context.

If the workspace name cannot be found, the system returns ScopeResolutionError::WorkspaceNotFound. If the workspace exists but the specified project is absent, it returns ScopeResolutionError::ProjectNotFoundInWorkspace. These errors propagate to the HTTP layer, which translates them into standard 404 responses according to the implementation in crates/ai-memory-web/src/routes/api.rs.

Partial Input Rejection

The system rejects incomplete scope specifications early in the resolution pipeline. When only one of the two required names (workspace or project) is supplied, the resolve_read_args function (lines 58-70) immediately returns ScopeResolutionError::WorkspaceProjectPairRequired.

This prevents ambiguous half-filled scopes from being interpreted inconsistently. The validation ensures that downstream code always operates on complete workspace-project pairs or explicit error states, never attempting to infer missing components.

Project-Only Resolution with Fallback Logic

For requests specifying only a project name without an explicit workspace, the resolve_current_or_project function (lines 73-100) implements a tiered fallback strategy. The resolver first checks the active-project map associated with the requesting actor to identify the current workspace context.

If the project exists in the actor's active workspace, resolution succeeds. If not, the system falls back to the default workspace configured at startup. Only if the project cannot be found in either location does the system return ScopeResolutionError::ProjectNotFoundInActiveOrDefault.

Create-on-Write Policy

Write operations follow fundamentally different rules from reads. Only code paths possessing a WriterHandle may invoke create_explicit_scope, located at lines 16-33 of crates/ai-memory-store/src/scope.rs.

This function calls writer.get_or_create_workspace and writer.get_or_create_project, which silently insert missing workspaces and projects into the store. This "create-on-write" capability is restricted to ensure that read-only paths never modify the store structure, while write paths can bootstrap new scopes dynamically. Attempts to create scopes without a valid writer handle result in a WriterRequired error.

Global Preferences Scope Handling

The system treats the reserved global scope differently from user-defined workspaces. The lookup_global_scope helper function (lines 35-44) returns Ok(None) when the global workspace or project does not exist.

Rather than treating this absence as an error, the system interprets it as "nothing to union in" during preference resolution. This allows the global scope to remain optional while still supporting union queries across scopes.

Implementation Architecture

The ScopeResolver type bundles four critical components: a ReaderPool, an optional WriterHandle, the active-project map for actor contexts, and default workspace/project identifiers. Its public methods enforce the three core guarantees of the system:

  • Read-only paths never create missing scopes. All lookup operations require pre-existing resources.
  • Write-on-create paths must hold an explicit writer handle. Without WriterHandle authorization, creation attempts fail immediately.
  • Partial input is rejected early. The WorkspaceProjectPairRequired error prevents ambiguous resolution states.

Higher-level APIs including MCP tools and HTTP admin routes delegate to these helpers. For example, the web API's resolve_scopes function calls resolve_many_existing_scopes for search queries, which uses lookup_existing_scope for each name pair, enforcing the no-creation rule across batch operations.

Code Examples

The following examples demonstrate the resolution behaviors in practice:

// Resolve a read request with only a project name
let resolved = resolver
    .resolve_current_or_project(Some("my-project"), &actor_key)
    .await?;

If "my-project" does not exist in the actor's active workspace or the default workspace, this returns ScopeResolutionError::ProjectNotFoundInActiveOrDefault.

// Create missing workspace/project on write
let scope = ai_memory_store::create_explicit_scope(
    &writer_handle,
    "new-workspace",
    "new-project",
).await?;

The create_explicit_scope function automatically inserts the workspace and project if absent, requiring valid write permissions.

// Resolve explicit scopes for search (no creation allowed)
let names = vec![
    ScopeName::new("ws-1", "proj-a"),
    ScopeName::new("ws-2", "proj-b"),
];
let scopes = ai_memory_store::resolve_many_existing_scopes(&reader_pool, &names, 10).await?;

Missing workspaces or projects in this batch operation result in WorkspaceNotFound or ProjectNotFoundInWorkspace errors, causing the entire request to fail with a 404 status.

Summary

  • Explicit lookups validate both workspace and project existence in lookup_existing_scope, returning specific ScopeResolutionError variants for missing resources.
  • Partial inputs are rejected immediately with WorkspaceProjectPairRequired to prevent ambiguous scope interpretation.
  • Project-only queries check the actor's active project map first, then fall back to the default workspace before failing with ProjectNotFoundInActiveOrDefault.
  • Write operations with a WriterHandle can create missing scopes via create_explicit_scope, while read operations strictly fail closed.
  • Global scopes return Ok(None) when absent rather than errors, supporting optional preference union operations.

Frequently Asked Questions

What error does ai-memory return when a workspace does not exist?

When resolving explicit scope names, the system returns ScopeResolutionError::WorkspaceNotFound if lookup_existing_workspace cannot locate the specified workspace. This error typically propagates to the HTTP layer as a 404 response, indicating the client requested a non-existent workspace.

Can read operations create missing projects or workspaces in ai-memory?

No. Read-only code paths strictly enforce a "fail-closed" policy via functions like lookup_existing_scope and resolve_many_existing_scopes. Only write operations possessing a valid WriterHandle can create missing resources through the create_explicit_scope function.

How does ai-memory handle requests with only a project name but no workspace specified?

The resolve_current_or_project function implements a two-tier fallback: it first checks the requesting actor's active-project map, then falls back to the default workspace configured at startup. If the project exists in neither location, it returns ScopeResolutionError::ProjectNotFoundInActiveOrDefault.

What happens if I provide only a workspace name without a project name?

The system rejects incomplete scope specifications early in the resolution pipeline. The resolve_read_args function returns ScopeResolutionError::WorkspaceProjectPairRequired when only one of the two required identifiers is supplied, preventing partial scope resolution.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →