# Project-Level Scope Resolution vs Global Scope Preferences in ai-memory: Key Differences Explained

> Understand project-level scope resolution versus global scope preferences in ai-memory. Learn how they enforce data isolation and set installation-wide defaults for your projects.

- Repository: [Fabio Akita/ai-memory](https://github.com/akitaonrails/ai-memory)
- Tags: deep-dive
- Published: 2026-08-20

---

**Project-level scope resolution enforces strict data isolation by binding every database row and wiki page to a specific workspace-project tuple, while global scope preferences provide installation-wide defaults stored in a hidden project namespace that individual projects can override.**

The ai-memory repository implements a dual-layer scoping system to separate data containment from configuration inheritance. Understanding how **project-level scope resolution** and **global scope preferences** interact ensures you maintain secure project boundaries while leveraging convenient default settings across your installation.

## How Project-Level Scope Resolution Works

Project-level scope resolution guarantees that every row in the SQLite store, every wiki page, and every MCP request is bound to a specific **workspace + project** triple. This mechanism isolates data between projects and prevents accidental cross-project leakage.

The core type `ScopeResolver` in [`crates/ai-memory-core/src/routing_snippet.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-core/src/routing_snippet.rs) serves as the only entry point for looking up or creating a `(workspace_id, project_id, path)` tuple. All write-paths call helpers such as `create_explicit_scope` or `resolve_many_existing_scopes`, while read-paths use `lookup_existing_scope` and fail closed if the scope is missing. As documented in **AGENTS.md**, every SQL row carries this three-tuple, and any route touching the store must pass through the resolver, enabling the system to safely run multiple users or independent projects within the same binary.

## Understanding Global Scope Preferences

Global scope preferences provide a mechanism for users or organizations to define persistent settings that apply **across all projects** unless explicitly overridden. These preferences include default auth tokens, LLM providers, and UI settings.

Global preferences are stored as durable wiki pages under a hidden "global" project whose ID remains constant for the entire installation. They are accessed via the memory API (specifically `ai_memory::memory::get_global_pref` or `GlobalPrefs::get`) and automatically merged into the configuration when `Config::load()` runs from [`crates/ai-memory-cli/src/config.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-cli/src/config.rs). Because these preferences live in a dedicated global scope, they are not tied to any workspace or project data, ensuring that changing a global preference affects only default behaviors without compromising project data integrity.

## Critical Differences in Implementation

### Isolation Guarantees vs. Configuration Inheritance

**Project-level scope resolution** enforces mandatory isolation. Every request must supply an explicit scope, and there is **no fallback** to a global scope. If a scope cannot be resolved, the request fails with a 404-style error. This invariant is hard-coded in [`crates/ai-memory-core/src/routing_skills.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-core/src/routing_skills.rs).

**Global scope preferences** operate on an override cascade. The lookup logic first checks the project scope, then falls back to the global map. Individual projects can mask any global preference by storing a page with the same key under their own scope using `Wiki::write_page` from [`crates/ai-memory-wiki/src/wiki.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-wiki/src/wiki.rs).

### Data Storage Locations

- **Project data**: Resides in scoped SQLite tables and wiki namespaces tied to the workspace-project tuple.
- **Global preferences**: Stored in the special "global" project namespace within the wiki system, accessible via `GlobalPrefs` methods.

## Practical Code Examples

### Resolving an Existing Project Scope

Use `ScopeResolver` to validate a project exists before performing operations:

```rust
use ai_memory_core::routing_skills::ScopeResolver;
use ai_memory_store::Scope;

// Returns an error if the scope does not exist
let resolver = ScopeResolver::new(&config)?;
let project_scope: Scope = resolver.lookup_existing_scope("my-workspace", "my-project")?;

```

*Source:* [`crates/ai-memory-core/src/routing_snippet.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-core/src/routing_snippet.rs)

### Creating a New Project Scope

Initialize new project isolation boundaries explicitly:

```rust
let resolver = ScopeResolver::new(&config)?;
let new_scope = resolver.create_explicit_scope("my-workspace", "new-project")?;

```

*Source:* [`crates/ai-memory-core/src/routing_skills.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-core/src/routing_skills.rs)

### Reading Global Preferences

Access installation-wide defaults with fallback values:

```rust
use ai_memory_core::memory::GlobalPrefs;

// Falls back to "openai" if unset
let default_provider = GlobalPrefs::get("default_provider")
    .unwrap_or_else(|| "openai".to_string());

```

*Source:* Implementation based on [`AGENTS.md`](https://github.com/akitaonrails/ai-memory/blob/main/AGENTS.md) specification

### Overriding Global Preferences at Project Level

Mask global settings for specific project requirements:

```rust
// This masks the global setting for this project only
wiki.write_page(
    &project_scope,
    "settings/default_provider",
    "anthropic"
)?;

```

*Source:* [`crates/ai-memory-wiki/src/wiki.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-wiki/src/wiki.rs)

## Summary

- **Project-level scope resolution** enforces strict data isolation through the `ScopeResolver` type in [`routing_snippet.rs`](https://github.com/akitaonrails/ai-memory/blob/main/routing_snippet.rs), requiring explicit workspace-project tuples for all data access with no fallback mechanisms.
- **Global scope preferences** use a hidden "global" project namespace to store default configurations that cascade down to all projects unless overridden.
- The `ScopeResolver` implementation in [`routing_skills.rs`](https://github.com/akitaonrails/ai-memory/blob/main/routing_skills.rs) provides `lookup_existing_scope` and `create_explicit_scope` for project management, while `GlobalPrefs` handles cross-project configuration.
- Configuration loading in [`crates/ai-memory-cli/src/config.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-cli/src/config.rs) merges global preferences automatically, but project-specific wiki pages always take precedence.

## Frequently Asked Questions

### Can global preferences leak sensitive data between projects?

No. Global preferences store only configuration defaults like provider names or UI themes, not project data. The **ScopeResolver** enforces that all actual data rows belong to specific project scopes. When a project queries data, the system exclusively uses the resolved workspace-project tuple, ensuring global configuration values never bypass project isolation boundaries.

### What happens if a project doesn't define a specific preference?

The system performs a cascading lookup: it first checks for a value in the project's wiki namespace, then falls back to the global scope preferences stored in the hidden global project. If neither exists, the application uses hardcoded defaults or returns an error for mandatory configuration fields.

### How do I migrate a setting from global to project-specific scope?

Write a wiki page to the target project scope using `wiki.write_page(&project_scope, "settings/KEY", "VALUE")` as implemented in [`crates/ai-memory-wiki/src/wiki.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-wiki/src/wiki.rs). Once stored, this value automatically masks the global preference for that specific project without affecting others or requiring changes to the global configuration.

### Is ScopeResolver thread-safe for concurrent requests?

Yes. The `ScopeResolver` implementation in [`crates/ai-memory-core/src/routing_snippet.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-core/src/routing_snippet.rs) is designed to handle concurrent lookups across multiple threads. The SQLite store underlying the scope registry uses proper transaction isolation, and the resolver itself maintains no mutable state that would cause race conditions during `lookup_existing_scope` or `resolve_many_existing_scopes` calls.