engram doctor runs read-only operational diagnostics against the local SQLite store. It detects, explains, and suggests safe next steps; the base diagnostic command does not repair data, apply migrations, delete rows, or mutate sync cursors.
engram doctor
engram doctor --json
engram doctor --project engram
engram doctor --check sync_mutation_required_fields
engram doctor repair --project sias-app --check session_project_directory_mismatch --plan
engram doctor repair --project sias-app --check session_project_directory_mismatch --dry-run
engram doctor repair --project sias-app --check session_project_directory_mismatch --applyFlags:
--jsonprints the stable diagnostic envelope for agents.--project PROJECTscopes checks to a normalized project name.--check CODEruns one registered check and fails loudly for unknown codes.doctor repairsupports exactlyinvalid_session_identity,manual_session_name_project_mismatch,orphaned_observation_session,session_project_directory_mismatch,sync_mutation_required_fields, andsync_target_closed_space. It requires--project,--check, and exactly one mode:--plan,--dry-run, or--apply.sync_mutation_required_fieldsmay omit--projectand the mode; an omitted mode defaults to--dry-run. Its optional project scopes title repair, supersession, quarantine, and source-title repair. The diagnostic-only checks areambiguous_active_runtime_sessions,sqlite_lock_contention, andunowned_session_project; a rejected repair names the correspondingengram doctor --check <code>continuation.
Agents can call mem_doctor with the same contract as engram doctor --json:
{
"project": "engram",
"check": "sqlite_lock_contention"
}Both fields are optional. When project is omitted, MCP uses the existing read-tool project detection. Unknown explicit projects return the standard structured unknown_project error.
The CLI --json and MCP tool return:
{
"status": "ok|warning|blocked|error",
"project": "engram",
"summary": { "total": 4, "ok": 4, "warnings": 0, "blocked": 0, "errors": 0 },
"checks": [
{
"check_id": "sqlite_lock_contention",
"result": "ok|warning|blocked|error",
"severity": "info|warning|blocking|error",
"reason_code": "stable_reason_code",
"evidence": {},
"safe_next_step": "No action required.",
"requires_confirmation": false
}
]
}session_project_directory_mismatch— warns whensessions.projectdisagrees with the project inferred from trusted repository evidence for the session directory. A known exactmanual-save-{project}target takes precedence over directory inference, so it does not produce this competing finding. Unknown manual suffixes and non-manual sessions retain normal trusted-directory behavior. The MVP trustsgit_remoteandgit_rootonly; it ignores basename fallback, ambiguous workspaces, missing directories, and child-repo auto-promotion to avoid noisy false positives.manual_session_name_project_mismatch— warns when a knownmanual-save-{suffix}session name disagrees with its persisted project. The suffix must normalize to a project already evidenced by a local session; a name alone never establishesproject_ownedownership.ambiguous_active_runtime_sessions— warns once per project when two or more active runtime candidates match the same directory. Evidence contains the active-candidate count, involved directories, and session IDs. It uses the same lease-aware selection as omitted-session resolution: valid unexpired local leases take precedence in their own directory, expired or malformed nonblank leases are excluded, and the legacy seven-day effective-activity window applies only when that directory has no live lease. Multiple live leases remain ambiguous. Doctor is diagnostic-only: it never selects, ends, or modifies sessions. End only confirmed stale IDs withmem_session_end; otherwise keep explicit runtime attribution withsession_idon writes.sync_mutation_required_fields— blocks when a pendingsync_mutations.payloadis missing required fields. On a device that uses cloud sync (at least one project enrolled), it also blocks when pending cloud mutations belong to a project that is not enrolled; the finding identifies the project and backlog count, so enroll intended projects withengram cloud enroll <project>or review enrollment before retrying. A local-only install with no enrolled project never reports that finding: any pending non-enrolled row there is legacy or otherwise pre-existing backlog, because new unenrolled local writes are not journaled.orphaned_observation_session— warns when active or soft-deleted observations reference a missing session. Findings are grouped by the stored observation project and session ID. After reviewing a plan,doctor repaircan create an immediately-ended, local-only, project-owned placeholder for a group with complete evidence; it preserves observations and never emits sync state. Apply revalidates the current observations inside the same transaction and derives the placeholder's start time and observation count from them, so a stale plan or a concurrent change cannot persist outdated placeholder metadata; a planned orphan that resolves before apply reportsnoopwith zero applied rows.unowned_session_project— warns for each session with an unclassified or invalid ownership mode, including blank persisted projects and contradictory legacy manual-save identities. Doctor never guesses a rescue. Useengram projects rescue-ownership --project <name> --session <id>only after review; its apply path creates a SQLite backup that can be restored for rollback. The listing is deliberately unscoped.- Session modes are
sharedandproject_owned. Runtime and HTTP-created sessions default toshared; deterministic CLI and MCP manual-save sessions areproject_owned. Shared sync can use old peers. Project-owned sync requires a mode-capable manifest (version 2); returning to an older manifest after project-owned sessions exist is unsupported and fails loudly. sqlite_lock_contention— warns on conservative SQLite contention signals; returns an error if lock state cannot be evaluated.
Plain engram doctor remains diagnostic-only. Findings that imply data movement set requires_confirmation=true so agents know a human must review evidence before repair.
engram doctor repair is intentionally narrow and local-first: local SQLite remains the source of truth. Project reclassification supports:
session_project_directory_mismatch, using trustedgit_remoteorgit_rootevidence from doctor findings.manual_session_name_project_mismatch, only for exactmanual-save-{known_project}sessions. The known manual target takes precedence over trusted directory evidence; unknown suffixes remain unrepaired.
Title restoration supports sync_mutation_required_fields only when a pending observation upsert has a blank title as its sole missing field and the matching local titleless observation has non-empty content. Run engram doctor repair --check sync_mutation_required_fields --dry-run first (add --project <project> to scope it); cloud-upgrade tooling instead requires configured cloud sync. The repair derives a sanitized, bounded title from local content and updates observations.title and sync_mutations.payload in place; all other invalid mutations remain quarantined on --apply.
The same repair also supersedes a pending local upsert when a local session/observation delete tombstone or prompt tombstone proves the entity was deleted while its project was unenrolled. superseded is auditable local evidence, not a cloud acknowledgement: it is excluded from transport and allows re-enrollment backfill to reconstruct the current local delete state. Superseded evidence missing its reason, evidence, or timestamp remains blocking until manually repaired; complete terminal quarantined and superseded rows remain informational without keeping doctor in warning or blocked status.
Repair never deletes or deduplicates rows, never edits sync cursors, never acknowledges undelivered mutations, and never writes cloud state. --plan and --dry-run are non-mutating. --apply creates a SQLite backup under <ENGRAM_DATA_DIR>/backups/ before a project reclassification transaction updates only:
sessions.projectsessions.ownership_mode(project_ownedfor a session namedmanual-save-{target_project}, otherwiseshared)observations.projectuser_prompts.project
Title restoration does not create a SQLite backup.
ambiguous_active_runtime_sessions, sqlite_lock_contention, and unowned_session_project are diagnostic-only and are not supported by engram doctor repair. SQLite lock contention has no repair.
All repair modes print stable JSON to stdout:
For sync_mutation_required_fields, repairs lists title-only observation upserts that can be restored in place; actions continues to list residual rows quarantined on --apply; superseded lists obsolete local upserts retired by durable local delete evidence.
{
"project": "sias-app",
"check": "session_project_directory_mismatch",
"mode": "plan|dry_run|apply",
"status": "planned|dry_run|applied|noop",
"actions": [
{
"session_id": "session-id",
"from_project": "sias-app",
"to_project": "engram",
"reason_code": "session_project_directory_mismatch",
"evidence_source": "git_remote"
}
],
"skipped": [],
"counts": {
"sessions_planned": 1,
"observations_planned": 2,
"prompts_planned": 1,
"sessions_applied": 0,
"observations_applied": 0,
"prompts_applied": 0
},
"backup_path": ""
}On --apply, backup_path contains the backup database path and *_applied counts report the rows updated.
Never experiment on production ~/.engram/engram.db. Use a SQLite backup clone or a temporary ENGRAM_DATA_DIR:
mkdir -p /tmp/engram-repair-clone
sqlite3 ~/.engram/engram.db ".backup '/tmp/engram-repair-clone/engram.db'"
ENGRAM_DATA_DIR=/tmp/engram-repair-clone engram doctor --json --project sias-app --check session_project_directory_mismatch
ENGRAM_DATA_DIR=/tmp/engram-repair-clone engram doctor repair --project sias-app --check session_project_directory_mismatch --plan
ENGRAM_DATA_DIR=/tmp/engram-repair-clone engram doctor repair --project sias-app --check session_project_directory_mismatch --dry-run
ENGRAM_DATA_DIR=/tmp/engram-repair-clone engram doctor repair --project sias-app --check session_project_directory_mismatch --applyAfter a project reclassification apply, verify each planned session's project and ownership_mode classification, the related observation and prompt projects, and that backup_path exists. If the repair is wrong, stop Engram processes and restore the backup_path database file manually.