# Usage and operations reference

See the [README](../README.md) for a project overview and installation.
This reference covers detailed behavior and optional controls; normal use is automatic.

- [Installation and updates](#installation-and-updates)
- [Automatic learning and recall](#what-happens-automatically)
- [Evidence, decay and ranking](#evidence-decay-and-self-ranking)
- [Storage and privacy](#local-storage-and-provenance)
- [Commands](#commands)
- [Migration](#migration-from-01)
- [Recovery and troubleshooting](#recovery-and-troubleshooting)
- [Provider fallback, error policies and budgets](recovery.md)

## Installation and updates

Use Pi 0.85+ with a configured model. The runtime can be standalone Pi/Bun or npm
Pi on Node 22.19+. Git installation also requires Git and npm on PATH. Pi supplies
its own APIs and TypeBox; the extension uses built-in SQLite.

Choose **one** source:

```bash
# npm package
pi install npm:pi-memory-evolution

# Git default branch (main), without a historical tag pin
pi install https://github.com/btnalit/pi-memory-evolution

# Local checkout: direct reference, no copy
pi install /absolute/path/to/pi-memory-evolution
```

Run `/reload` and `/memory status` inside Pi. There is no separate installer,
post-install script, owner approval, or additional API key to configure. For local
development, install dev dependencies with `npm ci --ignore-scripts` in the checkout.

Before upgrading, read [migration and backups](#migration-from-01). Update the same
source you installed:

```bash
pi update npm:pi-memory-evolution
# Or, for a Git install:
pi update https://github.com/btnalit/pi-memory-evolution
# For a local checkout, update it yourself:
cd /absolute/path/to/pi-memory-evolution
git switch main
git pull --ff-only origin main
```

Plain `pi update` updates Pi itself, not this package. `PI_OFFLINE=1` also suppresses
explicit package updates; unset it or use `PI_OFFLINE=0` when updating. Package
subcommands do not accept the general `--offline` flag.

Pinned npm versions stay pinned. The old `v0.1.0` Git tag is historical; install the
unpinned Git source to follow the default branch instead. Use `pi list` and remove
the old source before switching between npm/Git/local forms, which Pi treats as
different package identities. `pi remove <source>` does not erase memory state.
Restart or `/reload` all sessions using the extension after changing versions.

### Recovering from duplicate installation sources

Pi treats npm, Git and local paths as different package identities. Installing the
same source twice is idempotent, but keeping both a dev checkout and an npm copy can
register `memory_recall` twice and prevent Pi from starting. Updating either copy
does not remove the other. This is not a failure of a single-source upgrade.

Use terminal commands, not commands inside the broken Pi session:

```bash
pi list
# Keep npm; remove the local source shown by pi list:
pi remove /absolute/path/to/pi-memory-evolution
# Or keep the checkout; remove npm instead:
# pi remove npm:pi-memory-evolution
```

Choose only the removal matching your intended source. If installed project-locally,
run `pi list --approve` and `pi remove <source> -l --approve` from that trusted project.
A manually configured `extensions` entry is not a package entry: remove that explicit
entry from the appropriate settings file as well. Then restart Pi and run
`/memory status`. Package CLI operations work without loading the conflicting
extensions, and removal leaves memory data intact. Do not delete the database or
rename the tool to hide the conflict; an old installation would still run its hooks.

## What happens automatically

- A successful `session_compact` saves a sanitized source and extracts up to 16
  facts, preferences, decisions or project-state claims using recognizable headings.
- Explicit user statements containing cues such as `remember`, `prefer`, `记住`,
  `偏好`, `纠正`, `不对`, `以后`, or `不要` also trigger learning, without waiting for
  another compaction. The English counterparts of `以后`/`不要` count the same way, and
  all of them only at the *start of a sentence*: `from now on`, `going forward`,
  `in the future` (or `in future`), `always`, `never`, `don't` or `do not`. Position is
  what separates a standing rule from a question about one — `Always run the tests.` and
  `In the future, always run the tests.` state a rule, while `Do you always run the
  tests?`, `Don't you think we should refactor?` and `Will this work in the future?` stay
  questions and are not learned. Both `'` and `’` are accepted. Two further narrowings keep
  ordinary sentences out: a text ending in `?` or `？` is a question whatever word it opened
  with (`Never seen this error before, what is it?`), and a bare imperative that limits itself
  to the step at hand — `for now`, `for this`, `this time`, `just` or `yet` after the cue, as in
  `Don't worry about the tests for now, just make it compile.` — is an instruction, not a
  rule. The explicit openers are durable by their own words, so `From now on, just run the
  unit tests.` is still learned.
  A directive quoted from a pasted log or document is indistinguishable from your own
  by position alone, so it is learned; this matches the existing `以后`/`不要` behavior. Natural declarations such as `我比较在意的三大功能…`,
  `我们的核心需求是…` or `Our priorities are…` also trigger learning, even if followed
  by a question asking for feedback. This is bounded intent recognition, not universal
  understanding. Quotes, ordinary recall questions and one-off commands are not requirements.
  Assistant/tool text never becomes a user memory instruction.
- Work requests (commit/push/fix/test/review, etc.) can update existing project states
  from linked tool results. Important commit/push/test results are retained ahead of
  late routine inspection, not simply the last 8 tools. Normal completion and interrupted
  assistant responses are distinguished: a completed tool operation is usable evidence
  even if the final reply failed, but it does **not** prove the entire task completed.
  Internal `memory_recall` results and observations referencing this extension's own
  state directory are excluded from this evidence path.
- Update nomination is separate from answering a query: explicit operation resources and
  project names nominate up to 8 active, unpinned states in the same capture origin,
  prioritizing pending states without a per-path top-2 or answer-deduplication gate.
  Expired states can receive new evidence; forgotten/conflicted states cannot. The model
  can only replace nominated project states, never create preferences from tool results.
  A mixed requirement/work turn can create **two separate serialized sources/calls**,
  preserving statement versus tool authority instead of silently discarding the work.
- Each processing attempt makes at most one background model call, using up to 32
  recently updated active memories from that source's capture origin **that the source
  actually mentions** — the host filters the rest out, so a source cannot replace a
  record it never talks about. The filter runs **before** the cap, so an older record the
  source is squarely about is no longer hidden behind newer unrelated ones; reach is still
  bounded by the 32 most recent records that qualify. This is a
  conservative automatic-replacement safeguard, **not a recall restriction**.
  It defaults to **the current Pi session model and Pi's own provider/auth resolution**.
  With no session override, this is Pi's configured default. Quota/rate limits or repeated
  failures may switch to an available model from another configured provider, not a sibling
  sharing the default provider's quota. The foreground model is never changed. See
  [fallback privacy, error handling and budgets](recovery.md); no extra credentials are stored.
- Valid additions/replacements commit immediately, with provenance and before/after
  history. Inferred memories remain labeled `provisional`, but are recallable without
  approval. Pinned memories cannot be automatically replaced.
- Replayed source events are idempotent. Calls have a **120-second deadline** and send
  **the active model's own output limit** as the cap, never a smaller one: a ceiling is
  consumed by the model's reasoning before it writes an answer, so an invented one can
  leave a thinking model with nothing to say. Cost is governed by the routing policy
  instead. They are
  cancelled on session shutdown/reload. Structured summary claims survive model failure.
  User-cue prose has no local-extraction fallback: its sanitized source is saved, but
  learning its claims requires a successful model attempt.
- Session start checks persisted work across **all origins**. While Pi remains running,
  a local recovery timer checks every **15 seconds** (after the previous check/call ends),
  gradually draining eligible pending/failed work one source per check (at most one immediate
  backup per check). Selection favors least recently checked work, then retry time and oldest
  capture, so unavailable routes do not indefinitely block other sources.
- Recovery is **error-specific**, with persisted source backoff, separate provider/model
  cooldowns, at most one output correction, and bounded cross-provider fallback. Generic
  backoff is 1 minute, 5 minutes, 15 minutes, then 1 hour, plus up to 20% jitter. Defaults
  cap each source at **4 reserved calls / 2 models / 300 seconds**, and all models and
  processes share **20 calls per rolling hour**. Three output failures or five generic
  failures also pause work; a write refused on the store's own authority (pinned, cross-origin,
  or a record already newer than the source) pauses immediately, while a model that breaks the
  output contract is corrected and retried like any other invalid output. `/memory status`
  explains routes, budgets and actual outcomes. `/memory evolve` overrides source limits
  for one attempt, never shared ceilings or settled jobs. [Full policy/configuration](recovery.md).
- A job lease lasts **150 seconds** (120-second deadline plus 30-second grace). The timer
  detects expired running jobs and schedules them with the same bounded backoff. Shutdown/
  reload cancellation returns work to pending without consuming the failure budget.
  Timers stop at shutdown and do not keep a print-mode process alive. Recovery resumes
  next time Pi runs; this is not a standalone daemon.
- Recall searches **the whole memory database**, including previous sessions, other
  directories and existing legacy claims. No project-directory startup or manual adoption
  is needed. Matching uses exact literals, word segmentation and weighted topic coverage
  locally, without a model call. Paths/filenames do not earn extra votes for their component
  words; for example, a repository named `pi-memory-evolution` is not itself evidence
  about cross-session memory.
- Natural questions such as `X相关记忆你还记得吗？` or `What do you remember about X?`
  separate the recall request from its actual subject. This applies across topics, not
  through per-device exceptions. Technical questions about memory/recall remain topics.
  Asking what Pi remembers does not itself trigger a paid learning call.
- Vague follow-ups use bounded recent **user** messages from the active branch, including
  retained compaction tails. Multi-hop refinements such as `SQLite 数据库 → 端口呢？ →
  认证呢？ → 继续` retain the subject and require the current attribute; prior-topic-only
  matches cannot satisfy the new question. Explicit new, unknown and reset topics stop
  old-topic inheritance. Assistant/tool/injected text never supplies the topic.
  The active-context scan covers up to 4096 entries/messages, returning at most six
  user texts; repeated topic-less continuations share a slot so long tool-heavy work
  does not immediately lose its subject. Bounds and compaction still limit recall.
  A fresh session saying only `继续` injects nothing; naming a topic enables cross-session
  recall regardless of its original directory.
- Recall works from an ordinary task prompt, not only from a question aimed at the memory:
  a record qualifies either when it accounts for enough of what was asked **or** when the
  prompt names that record's topic — an exact path/filename, or a known concept or one of the
  record's own aliases that is rare in the store (an alias such as `server` that many records
  share names nothing) — and engages it more than once. Neither measure shrinks as the request
  gets longer, nor as the stored claim gets longer or gains aliases, so describing a task in
  full no longer suppresses the background it is about, and a claim that explains itself is
  no harder to recall than a one-liner. A prompt that engages nothing stored still injects nothing.
  The live prompt itself may run up to 65,536 UTF-8 bytes before feature extraction — far past a
  single conversational turn — so a task naming a stored record after a long pasted log, diff or
  spec is still recalled, provided any paths in the paste sit on stack-frame, diff or fenced lines
  or carry a line reference (next point); a path on an ordinary line of an unfenced paste reads as
  typed and stays required. Only bounded **replayed** history from earlier turns keeps the smaller
  2,048-byte-per-turn budget.
- Query coverage, topic naming, evidence-based document frequency, field weights, mild length
  normalization and a relative cutoff reject weak secondary matches. Unseen query words
  no longer receive the highest rarity weight. An exact path or filename you type must match,
  including case; `/srv/Atlas` and `/srv/atlas` are distinct. A path that arrived inside pasted
  material — a stack-frame line (`at …location:line:col`; a line that merely opens with the word
  `at` is the ask), a diff (a hunk ends where its `@@` header says), fenced code, or
  a `file:line:col` reference — still counts in favour of a record that names it but is not
  required of every record, so a trace pasted ahead of the ask does not block recall of the
  background the ask is about; a path that also appears in the ask itself is typed, and stays
  required. A quoted
  question in a replay/incident note is weaker than evidence answering it. Redundancy
  filtering cannot let a project-state note hide a preference of the same origin.
  Source IDs/cwd have no authority bonus. After relevance gates, host-assigned evidence,
  type-specific freshness and explicit feedback order eligible matches; pin/date break remaining ties.
  Common words such as `没有` or `现在` cannot trigger recall.
  At most three claims fit within **2048 UTF-8 bytes**, with origin/source labels and
  non-truncatable trust guidance. Fewer matches means fewer claims, not padding with recent
  records. Identical content from different origins retains separate provenance.
  Unpinned project-state claims still age out after seven days; pinning exempts age, not truth checks.
  Short named-attribute queries require the subject and attribute, rather than substituting
  another subject/attribute when the correct memory is missing or suppressed.
- Common Chinese/English concepts are normalized locally for existing records. Evolution
  can add up to 8 validated bilingual `searchTerms` per claim, extending matching without
  changing its factual text or adding a recall-time translation call. Aliases alone do
  not refresh evidence dates. This is bounded bilingual support, not universal translation.

A model call may incur the usual charges of your active provider. These background
calls are not assistant turns and their usage is not added to Pi's session token totals.
There is no additional call on ordinary recall. Eligible work turns may now incur one
additional background call each; no related tracked state or no tool observation means
no progress call. A mixed statement/work turn may additionally incur a separate learning
call; interrupted turns with usable observations may also learn through automatic recovery.
Automatic retries/fallback share a default total of four reserved calls per source;
they do not get separate budgets per model. Each request uses Pi's authentication for the
selected provider. Cross-provider data sharing and optional estimated-cost limits are
explained in [recovery configuration](recovery.md). Model mistakes
remain possible; tool observations and model-generated aliases are not proof of truth. Use
history, correction, pinning and undo rather than treating generated claims as verified facts.

## Evidence, decay and self-ranking

- New claims carry **host-assigned** evidence: `summary`, `user_statement`,
  `tool_observation` or `manual_correction`, plus extraction method, source ID and date.
  Model output cannot supply confidence/verification/feedback fields. Old records without
  this metadata remain `unknown`; migration does not guess their source or certify them.
- Source appropriateness matters: a user's stated preference is stronger evidence of that
  preference than a summary; a linked tool observation supports a project state, not a
  user preference or independent proof of success. A weaker model-proposed replacement
  is withheld and its new variant quarantined, with history. Current user corrections
  and new tool-backed project progress can still update automatically; pins remain protected.
- Freshness decreases smoothly by type: project state fastest, then facts, decisions,
  preferences. Every kind retains a nonzero floor, and past its own horizon becomes
  **dormant** — no longer injected automatically, but still stored, still returned by the
  `memory_recall` tool and by `/memory search`, and still offered to learning, so later evidence
  revives or retires it without you doing anything. Dormancy governs what is pushed into a
  session unprompted; it never makes a record unfindable for someone looking for it.
  Project states keep the seven-day cap; no upgrade revives old states. Read/search/injection,
  pinning, alias enrichment, feedback and conflict resolution never move the evidence date,
  and nothing does except a replacement. The separate decay clock is moved only by a later
  source that mentions a record and either leaves it standing or reaffirms it outright — never
  for project states, whose seven-day cap nothing resets.
- Ranking keeps **relevance, evidence, freshness and feedback separate**. Quality cannot
  rescue an unrelated/weak lexical match. `useful` is not `accurate`; neither is independent
  verification. Repeated retrieval or repeated positive feedback earns no cumulative boost.
  `/memory show` and `/memory explain` expose the factors. Injected claims include evidence
  labels and an aging warning, not a fictitious probability of truth.

Optional `/memory feedback <id> useful|unhelpful|accurate|incorrect` records a precise user
verdict without a model call. `unhelpful` modestly lowers utility, not factual credibility.
`incorrect` quarantines the claim and same-origin exact duplicates, retires known pending
repeats, and is undoable. Use `correct` for new content or `resolve` to restore a disputed
claim; conflict/resolution does not rejuvenate old evidence. Feedback with a timestamp
older than the current content's evidence date is ignored. Receipts prevent replay after restart/undo; identical repeated verdicts do not
accumulate weight. Feedback receipts, like history, are not securely erased by forget.
Whole user messages `记忆 <24-hex-id> 有用。` / `memory <24-hex-id> incorrect` also work;
quotes, questions, assistant/tool text and vague “wrong” do not identify a feedback target.
Ordinary natural-language corrections continue through automatic model evolution.

The model also gets a **read-only `memory_recall` tool** for missing background discovered
mid-task. It takes an explicit topic and returns up to three relevant claims / 2048 UTF-8
bytes, using the same lifecycle and quality gates. It does not modify memory, persist a
query in the memory DB, or make an additional retrieval-model call (normal agent tool
turns still incur normal usage and appear in the Pi transcript). Automatic per-user-turn
injection remains the default; the model chooses whether a second lookup is needed.
Explicit tool allowlists must include `memory_recall`; the extension never overrides them.

These are bounded evidence policies, not learned semantic verification, independent-source
corroboration, or a universally accurate self-evolving ranker. See
[core quality design and validation](core-quality.md) for formulas and remaining gaps.

## Local storage and provenance

State lives under Pi's public agent directory (`getAgentDir()`). By default:

```text
~/.pi/agent/agent-suite/memory-evolution/
├── memory.sqlite       # memories, sources, suppression hashes, jobs and history
├── memory.sqlite-wal   # SQLite-managed when open
└── memory.sqlite-shm
```

If `PI_CODING_AGENT_DIR` is set, replace `~/.pi/agent` with that directory. Using a
different agent directory means a different database. Changing cwd does **not** hide
memories in that database or create a recall boundary.

SQLite transactions/WAL protect concurrent processes and interrupted commits.
Model calls run outside transactions; stale responses cannot overwrite intervening
changes. Raw summaries are evidence only, **never a separate recall fallback**, so
forgetting a derived claim cannot expose it again through its parent summary.

The stored `scope` field records capture origin (canonical cwd, not an inferred project
identity). It is retained for provenance and conservative write protection, not eligibility
for recall. An origin can cover multiple projects; facts must retain explicit subject names
where available. Explicit origin identifiers can help a named-context query at low weight;
current cwd, `legacy` labels and source IDs do not boost a claim's authority or relevance.

Global recall does **not** mean global rewriting: automatic replacement candidates remain
within the source origin, and the model must identify the same subject/fact, not just a
matching port or path. Cross-origin variants are not automatically merged or overwritten.
Exact-ID manual corrections/forget work from any session and affect the selected record
and exact duplicates within its origin, **not identical text from unrelated origins**.
Suppression hashes likewise remain origin-qualified. Arbitrary paraphrases or semantic
identity across origins cannot be resolved reliably by a local hash. These safeguards
may leave ambiguous variants for inspection rather than guessing which one to retire.
Forget is logical suppression, not secure erasure of history or the original Pi transcript.

Sensitive lines/blocks are suppressed before capture, edits, model submission and
recall. This covers common token/password/JSON/Chinese/Bearer/private-key formats,
including quoted multiline values, indented YAML blocks and control-character cleanup,
but not every possible secret. Do not rely on a regex as a complete DLP system. Sanitized
sources and selected existing memories go to the active Pi model provider or an allowed
already-configured fallback provider. Fallback is enabled by default; restrict the model
allowlist or disable it in [recovery configuration](recovery.md) to constrain data sharing.

## Commands

These are optional direct controls, **not approval gates**:

```text
/memory list [page]                  # all origins, 20 non-forgotten records per page
/memory list all [page]              # alias for list
/memory list here [page]             # optional current-origin view
/memory list legacy [page]           # optional unknown-origin view
/memory show <id>                     # exact ID, any scope/status; includes provenance
/memory search <query>                # up to 10 recallable matches across all origins
/memory explain                       # last automatic recall snapshot, including injection count
/memory explain <query>               # preview retrieval reasons for an explicit query (3-claim cap)
/memory learning                     # last capture/nomination reasons + recent transaction outcomes
/memory status                       # integrity + retries + processed-versus-changed outcomes
/memory history                      # last 10 events across all origins
/memory evolve                       # optional one-off retry, overriding delay/failure limit
/memory evolve <source-id>            # retry one named source without clearing other failure counts
/memory import [directory]            # explicit, repeat-safe legacy JSONL import; never replays a completed one
/memory archive-legacy                # copy inactive legacy plan files to a private unique archive; originals kept
/memory undo <event-id>               # reverse actual changes, if not modified since
/memory feedback <id> <verdict>       # useful | unhelpful | accurate | incorrect
/memory correct <id> <replacement>    # literal replacement, 4–800 characters
/memory forget <id>
/memory pin <id>
/memory unpin <id>
/memory conflict <id> <other-id>       # suppress both
/memory resolve <id>                  # restore this conflicted side; other stays suppressed
/memory adopt <id>                    # optionally label a legacy claim with this directory
```

Pages start at 1, sorted by update time descending, then ID. For example,
`/memory list legacy 2` reaches the next 20 imports. `all` is retained as an alias;
`here` and `legacy` are optional inspection filters, never recall settings. There is no
full export command. Concurrent updates can move records between
pages. Lists can show conflicted or stale project-state records that search/recall
excludes. Status counts include all scopes and tombstones and validate source jobs and
history as well as memory records. Long content previews are capped at 1,440 bytes in
lists/search or 8,000 bytes in `show`, with an ellipsis when truncated.
Commands that take exact IDs can address records outside the current cwd. `search` and
`explain <query>` use only their explicit query, whereas automatic recall can resolve
follow-ups from recent user context. `explain` without arguments shows the last automatic
snapshot: normalized focus/context features, eligible/excluded counts, scores, query coverage,
up to 10 candidate IDs and rejection/selection reasons, plus actual injected count/bytes.
It retains at most 8,000 bytes (+ truncation marker) in memory, not a database/session log;
no memory bodies or provider errors are included. It resets on reload and is not proof
of what the model subsequently understood. Empty/no-match turns replace the old snapshot. Pin protects against automatic replacement/age expiry,
not manual edits, and does not force an unrelated record into every prompt. Pin/unpin
and adoption, feedback and conflict resolution preserve the stored evidence date; undo restores the prior date. These
bookkeeping actions do not restart the seven-day project-state recall window. Alias-only
model enrichment also preserves that date. A new, explicitly incorporated progress
observation can refresh it, even if the observed state is still unchanged/pending.
Manual correction clears old search aliases rather than attaching them to new content.

Undo reverses claim changes only when the affected records have not changed since;
it is not a database rollback. Suppression hashes remain, and jobs are not reopened.
Suppression retires pending sources known to repeat that fact, not just its first parent.
This skips the entire pending model pass for those sources; unrelated local claims remain,
but unlearned prose may need to be restated in a new source. Job state `done` also includes
these retired sources; history identifies actual model transactions.
There is no `/memory confirm`, `/evolution approve`, or owner-approval step in 0.2.

## Migration from 0.1

On first database use, valid `memories.jsonl` and `memory-actions.jsonl` are imported
once in a transaction. **Original files are not modified or deleted.** Corrupt or
unreadable ledgers stop migration rather than silently ignoring forget/correct actions.

Old records without origin metadata retain the label `legacy`. They participate in
relevance-based recall without adoption, with their unknown origin visible. Use
`/memory list legacy [page]` for inspection; `/memory adopt <id>` is an optional metadata
annotation, not a prerequisite for recall. Structured claims are extracted from eligible
legacy summaries, respecting existing lifecycle actions. Free-form raw summaries
remain available in the original JSONL but are not injected as claims. New imports
preserve unchanged children of corrected summaries and carry superseded-content hashes
when corrected legacy claims are adopted into a project.

**Upgrading from earlier 0.2 development builds:** stop Pi and back up the state directory
first, then update/reload all Pi processes sharing it. Schema markers **2–6 upgrade
transactionally to 7**, adding missing recovery/feedback fields, source call/time/model
budgets, provider cooldowns and usage receipts. Known v6 route waits are separated from
source failure backoff; retained call history seeds counters without inventing missing calls. Existing records, IDs, histories and
evidence dates remain unchanged. Missing evidence stays unknown, with no fabricated backfill.
Existing failures below the limit become automatically eligible; their old error cause/time
remain labeled unknown rather than invented. No copying, manual marker reset or JSONL
re-import is needed. Older builds reject schema 7; rollback requires a matching backup,
not editing the marker. Existing records are immediately eligible for global relevance-based recall.
Legacy claims previously excluded by cwd filtering become eligible too. This shares
relevant stored claims with the active Pi session/provider, not raw session archives.

Empty/missing ledgers do not mark an import completed. After supplying files, use
`/memory import [directory]`. Provably empty v6 snapshots can reopen safely; a zero claim
count alone never authorizes replay of a real consumed ledger. See [migration details](recovery.md).

These migration fixes do not replay an already completed real import or retroactively erase
previously stored sensitive data. If an older import already lost revision information,
use the preserved ledgers/backup to review and correct affected records; do not reset the
migration marker or replace a populated database blindly.

Old signals, agenda, thresholds, proposals, journals and execution plans are historical
files only. This version neither processes nor deletes them. Back up the entire state
directory with Pi stopped before changing versions. Returning to 0.1 reads the old
JSONL, not changes made in the new database.

## Recovery and troubleshooting

- **No `/memory` command:** check `pi list`, then `/reload` (or restart Pi). Only one
  source of this extension should be installed. Processes marked by a nonempty
  `PI_SUBAGENT_AGENT_ID` deliberately do not load it.
- **No recalled memories:** check the active agent directory and `/memory search <topic>`.
  Name the topic if the current session has no recent user context; cwd is not a recall
  gate. `/memory explain` shows the last automatic decision; `explain <query>` previews
  an explicit query without invoking a model or updating claims. Forgotten/conflicted records and unpinned project state older than seven days
  are not recalled. Matching remains lexical, not a guarantee of semantic or cross-language
  equivalence; vague references and languages/terms outside the concept map or learned
  aliases can still be missed.
- **Old progress still shown:** `/memory learning` distinguishes no work observation,
  no update targets, already captured and newly captured sources, with retained/omitted
  observation counts and nomination reasons. `/memory status` shows actual changed-record
  counts from recent model transactions: `done` means processed/retired, **not learned**.
  Age/weight does not prove a task finished. A new eligible
  work observation or compaction can update tracked progress; upgrading alone does not
  invent completion or replay old tool transcripts. Inspect `/memory show <id>` and
  history, or correct the record explicitly when you know the current state.
- **Pending/failed jobs:** normally no command is needed: leave Pi running and recovery
  automatically processes due work. `/memory status` distinguishes scheduled retries from
  paused sources and shows the next eligible time (the next poll may be up to 15 seconds
  later when no local work is queued). After a crash, the 150-second lease must expire first.
  Inspect source call/time/output limits as well as failure count. Status distinguishes
  `auth`, `quota`, `rate_limit`, `context_limit`, `request`, `safety`, transport, output and
  stale-write failures. Provider cooldowns are separate from source retry times; switching
  providers cannot bypass the shared budget. `/memory evolve` makes one explicit source
  attempt without resetting counters or overriding shared ceilings. See [error policy](recovery.md).
  A bad source remains saved/visible rather than being deleted or labeled successful.
- **Persistent storage/import errors:** stop all Pi processes using that agent directory
  and back up the **entire** state directory, including any SQLite sidecars and legacy
  ledgers. Check file permissions and restore a known-good matching backup if needed.
  Do not delete `memory-actions.jsonl` to bypass an import failure: that can discard
  forget/correct history. Failed imports can retry after repairing the original ledgers;
  successful imports are never replayed merely because the old JSONL changes.
- **Disable the extension:** `pi remove <installed-source>`, then `/reload`. Its local
  database remains on disk. Restore/replace state only with all processes using it
  stopped; do not mix one backup's database with another's WAL/SHM files.

Diagnostics intentionally do not echo provider error bodies, which may contain secrets.
`SQLite ok (schema 7)` checks database structure/record validity, not the truth of model claims.
