Memory Scopes
Session, project, workspace, and global scopes — how memories are organized and expire.
Scopes
Every memory has a scope that determines its visibility and default lifetime:
| Scope | What it's for | Default TTL |
|---|---|---|
session | Single coding session — what you just did, decisions made, blockers hit | 90 days |
project | Per-repo knowledge — API quirks, architecture decisions, team conventions | Forever |
workspace | Shared across related repos — org-wide standards, client-specific patterns | Forever |
global | Cross-project patterns — personal preferences, tool configs, workflow habits | Forever |
Project keying
Projects are identified by git remote URL. This means the same project is recognized regardless of where the repo is checked out — different machines, different paths, same project.
When storing a memory with scope: "project", the project field should be the git remote (e.g. git@github.com:Saturate/HUSK.git). The Claude Code plugin handles this automatically.
Workspaces
Workspaces group related projects so memories can be shared across repos. For example, a "client-a" workspace might contain all repos for that client — memories scoped to workspace are visible from any project in the group.
- Create and manage workspaces in the admin UI under Workspaces
- Assign projects (git remotes) to workspaces
- Auto-detection can infer the workspace from the git remote's org name (e.g.
github.com/my-org/*→ workspace "my-org"), toggleable per user - Search walks up the hierarchy: session → project → workspace → global
TTL and expiry
Memories expire based on their scope's default TTL, unless overridden:
- Custom TTL: Pass
ttl(in seconds) when storing to override the default - Explicit forever: Pass
ttl: nullto store a memory that never expires regardless of scope - Admin max: The
HUSK_TTL_MAXconfig caps all TTLs (even "forever") to a ceiling value
Expired memories are filtered out at query time — they're still in the database but won't appear in search results.
Duplicate detection
Before storing a new memory, HUSK checks for semantically similar existing memories from the same user. If a match is found above the dedup threshold (default: 0.92 similarity), the existing memory is returned instead of creating a duplicate.
You can:
- Force store: Pass
force: trueto skip dedup and store anyway - Replace: Pass
replace: "memory-id"to overwrite an existing memory's content - Adjust threshold: Set
HUSK_DEDUP_THRESHOLD(0.5–1.0) to make dedup more or less aggressive