HUSK

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:

ScopeWhat it's forDefault TTL
sessionSingle coding session — what you just did, decisions made, blockers hit90 days
projectPer-repo knowledge — API quirks, architecture decisions, team conventionsForever
workspaceShared across related repos — org-wide standards, client-specific patternsForever
globalCross-project patterns — personal preferences, tool configs, workflow habitsForever

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: null to store a memory that never expires regardless of scope
  • Admin max: The HUSK_TTL_MAX config 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: true to 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

On this page