# Maintenance

> Check Hansard's health, refresh the right part of the archive, recover lost harness files, reclaim space, and upgrade safely.

Most problems need only one part of the archive refreshed. Check health first,
then pick the smallest command that fixes what is stale.

- App: **Settings → Imports**
- CLI: `hansard doctor`

## Check health in the app

1. Open **Settings → Imports**. Check that **Background import** is running,
   whether **Pause search updates** is on, the **Selection** mode and the
   agents it follows, and the **Look-back window**.

2. Select **Incomplete runs** to see runs that stopped before saving any
   messages. Hansard keeps them out of **Sessions**, search, and Stats.

3. If a conversation seems missing, open **Sessions**, select **Filter &
   sort**, select **Reset**, and then set **Source** and **Time** one at a
   time.

4. Open **Settings → About** to confirm the running version and whether an
   update is available.

**Settings → Imports → Maintenance** runs the common repairs without a
terminal:

| Row                                   | What it does                                                                        | CLI equivalent                         |
| ------------------------------------- | ----------------------------------------------------------------------------------- | -------------------------------------- |
| **Refresh archive data**              | Re-derives titles, tool usage, and views after an update.                           | `hansard rebuild --since all`          |
| **Re-read with current importers**    | Re-imports conversations that older importers archived.                             | `hansard rebuild --stale-parsers`      |
| **Rebuild search index**              | Rebuilds search from the archive.                                                   | `hansard index rebuild`                |
| **Restore Claude Code files**         | Puts back session files Claude Code deleted, from Hansard's copies, after a review. | `hansard repair claude-code-backups`   |
| **Recover Antigravity conversations** | Reads older conversations through the running Antigravity app.                      | `hansard import antigravity --recover` |
| **Recover Windsurf conversations**    | Reads trajectories through the running Windsurf app.                                | `hansard import windsurf --recover`    |
| **Recover Cursor models and usage**   | Reads each request's model and tokens from your Cursor account.                     | `hansard import cursor --recover`      |
| **Run diagnostics**                   | Checks sources, storage, and setup.                                                 | `hansard doctor`                       |

**Refresh after updates** controls automatic archive refresh. The watcher
updates older conversation data, reparses supported preserved originals, and
converts eligible storage formats while imports are idle and the computer has
AC power. It preserves newer files and refuses parses with fewer messages.
Turn it off with `hansard config set archive.autoRefresh false`.
`hansard status` reports its progress. See [Background work](https://www.hansard.dev/reference/background-work/)
for scheduling, resource controls, and restart behavior.

**Import older history** on the same page reads each agent's history again, as
far back as you choose. When the watcher runs a different copy of Hansard than
the app, **Importer version** names it; see
[Several copies of Hansard](https://www.hansard.dev/reference/maintenance/#several-copies-of-hansard). Full reimports, raw
recovery, and update recovery run from the CLI.

## Check health from the CLI

```sh
hansard status
hansard doctor
hansard index status
hansard watcher status
hansard import status
```

`status` shows the active paths, watcher, conversation counts, search index,
sync, and recent imports. `doctor` checks required tools, sources, the search
index, login startup, and sync, and lists commands for anything it flags. It
also reports harness history that has disappeared since Hansard archived it,
the slowest steps of recent watcher passes under **watcher ticks**, and, under
**agent hooks**, hooks that failed or passed their 3.5-second limit in the last
seven days.

`doctor` also counts conversations four ways: the archive's conversation files,
the session list, the session records Stats reads, and search. The four numbers
match on a healthy archive. When they differ, `doctor` names the repair, usually
`hansard index rebuild`. A conversation imported in the last few minutes shows
as not searchable yet until the watcher's next index pass.

Run `hansard doctor --deep` for an archive-wide check of parser versions,
missing sources, and import health; it also reads every search document. Add
`--json` to `status` or `doctor` for machine-readable output.

### Search index repairs and disk space

The four conversation counts include received sessions, subagents, SDK jobs,
and machine probes. A cloud chat copy counts as searchable when another copy
of that conversation is indexed. Diagnostics compare stored rows with their
metadata files. A newer store or index calls for a Hansard update instead of
a rebuild. An unreadable metadata file is reported without a repair command.

Resume paused indexing with `hansard index resume` before rebuilding.
A rebuild can continue past a conversation with damaged stored events. It
keeps that conversation's metadata and the passages read before the failure,
and reports the conversation in the rebuild, index status, and watcher log.
If it would skip more than 10 conversations, or 2% when larger, it fails and
keeps the existing index. A permission error also stops replacement.

Ordinary watcher passes replace only changed conversation passages and remove
deleted ones. A title edit replaces only its summary passage and shared
record. A passage-format update refreshes affected passages while search keeps
working. `hansard index rebuild` replaces the whole index, or refreshes all
passages in place when other processes keep it open, and reports that choice.

A separate rebuild needs free space above the storage floor:

- At least 512 MiB.
- About 1.25 times the live index size when the current version built it.
- About twice the live index size after an index-version change.

A build that exceeds its allowance retries with twice the allowance, up to
16 GiB. Updating an index in place needs about its live size free above the
floor for the write log. Search continues using the existing index during a
failure or storage refusal. Watcher retries start after a minute and double
up to 30 minutes. A storage refusal can retry sooner when enough space returns.

## Choose the right refresh

| Symptom                                                                        | Command                                    | Reads harness history?                         |
| ------------------------------------------------------------------------------ | ------------------------------------------ | ---------------------------------------------- |
| The app shows an outdated layout, title, or tool summary                       | `hansard rebuild`                          | No                                             |
| Search or lists miss conversations that exist                                  | `hansard index rebuild`                    | No                                             |
| `doctor --deep` reports an older parser version                                | `hansard rebuild --stale-parsers`          | Yes, for each source an older release imported |
| Conversations from one source are missing or wrong                             | `hansard import --source <id> --since all` | Yes                                            |
| The harness's history is gone but Hansard kept the originals                   | `hansard rebuild --from-raw`               | No, reads preserved originals                  |
| A project moved to a different repository                                      | `hansard repair repo-keys`                 | No                                             |
| [Release notes](https://www.hansard.dev/releases/) require a complete reimport | `hansard update`                           | Yes, all configured sources                    |

Each release starts a new parser version, so right after an upgrade
`doctor --deep` reports every source, and `hansard rebuild --stale-parsers` and
**Re-read with current importers** re-read every local source. Prefer the
narrower commands the [release notes](https://www.hansard.dev/releases/) name.

`hansard rebuild` accepts `--provider`, `--since`, and `--force`. `--from-raw`
and `--stale-parsers` also accept `--source-type` and `--session`:

```sh
hansard rebuild --provider cursor --since 90d
hansard rebuild --force --since all
hansard rebuild --from-raw --source-type claude-web-export
```

Archive format 2 is the default. It keeps each conversation's Markdown
gzip-compressed, the events of large conversations as event records, and their
views only as compressed pages, and updates a growing conversation's
transcript in place. A configuration in which an earlier release saved format
1 reads as format 2. Conversations archived before keep their files until
`--format 2` converts them; `--format 1` writes them back as plain
`conversation.md`, `events.jsonl`, and `view.json`. A record that no longer
matches its transcript stays, with its pages, unless
`HANSARD_ACCEPT_OLDER_DERIVED=1` is set, since the events derived in its place
would be redacted with the rebuilding process's inputs rather than the
import's.

`hansard rebuild --since all` compresses the Markdown that earlier versions
kept plain, typically to about 35–40% of its original size. Automatic refresh
also converts eligible conversations after an update. Otherwise, each
conversation's Markdown is compressed when it is next written or read. Commands that
hand out the Markdown's path, such as `hansard show --path`, name a plain copy
in Hansard's cache, which cache cleanup removes after 30 days unused. Exports
and uploads still carry a plain `conversation.md`.

```sh
hansard rebuild --format 2 --since all
```

The [`archive.format`](https://www.hansard.dev/reference/configuration/) setting, not this flag, decides what
later imports write. Earlier Hansard releases cannot read all of it, so
imports keep writing format 1, and `--format 2` refuses, while a copy of
Hansard that may open the archive lacks the format 2 readers. Update every
copy on the computer, the desktop app and an npm installation alike, first.
`hansard config set archive.format 2` names those copies. To keep format 1, run
`hansard config set archive.format 1`. Before you move back to a release that
cannot read format 2, run `hansard rebuild --format 1 --since all` and
`hansard config set archive.format 1`.

Under archive format 2, `rebuild` first cuts a partial last line that an
interrupted update left in a selected conversation's transcript or reveal
cache file. `hansard doctor` reports such lines and cuts them when no import is
running.

`rebuild` starts from the conversation files already in the archive, so it
cannot apply a new parser. A stale search index, in turn, never needs a
reimport. Preview `--from-raw` and `--stale-parsers` with `--dry-run`.
`--from-raw` supports sources preserved one file per session, browser captures
such as ChatGPT, Claude.ai, Perplexity, and Devin, Claude.ai account exports,
and the Devin CLI, OpenCode, and MiMo Code databases, which it rebuilds as a
whole snapshot. Other shared databases and multi-session exports report that
they are unsupported. The watcher's
[refresh after updates](https://www.hansard.dev/reference/background-work/#refresh-after-updates) leaves
Devin CLI, OpenCode, and MiMo Code snapshots as they are, so
`hansard rebuild --from-raw` is the way to refresh them.

`hansard rebuild` leaves a conversation whose transcript needs more memory than
the process has as it is, so one oversized conversation does not end the run.
It counts such conversations under **Left for a larger heap** and prints the
`NODE_OPTIONS=--max-old-space-size` value that refreshes them.

Returning a rebuilt SQLite snapshot to ordinary live imports currently needs
a full archive refresh. Preview `hansard update --dry-run` first. Its
`--sources` option selects the reimport inputs and does not limit archive
cleanup. See [Run a full refresh](https://www.hansard.dev/reference/maintenance/#run-a-full-refresh) before proceeding.

`hansard repair repo-keys` finds sessions whose folder now resolves to a
different repository, for example after a remote was renamed. It prints the
import commands that re-file them.

## Several copies of Hansard

The desktop app, the npm CLI, and a source checkout can share one archive, and
each imports with its own importers. The watcher runs whichever copy started
it; when that copy differs from the app's, `hansard status` and
`hansard doctor` name it under **watcher copy**, **Settings → Imports →
Importer version** names it, and **Use this version** moves the watcher and login
startup to the app's copy.

An import never rewrites a conversation that a newer importer archived,
because an older importer can drop attachments, models, or tool details the
newer one extracts. New messages in such a conversation wait for a copy with
the newer importer. `hansard status`, `hansard doctor`, and the
**Importer version** row count them. `HANSARD_ACCEPT_OLDER_PARSERS=1` allows a
deliberate downgrade.

Files Hansard derives from a conversation follow a similar rule when a copy
reads a conversation or runs `hansard rebuild`. Neither rewrites the events,
views, conversation Markdown, tool usage, or inferred titles that a newer
version wrote; the copy shows its own reading of events and views in memory
instead, and `hansard rebuild` skips those files and counts the conversations
it kept. `HANSARD_ACCEPT_OLDER_DERIVED=1` allows a deliberate downgrade. An
import rewrites all of a conversation's derived files, so only the importer
rule above keeps an older copy from replacing them.

An older copy can read a newer session or search index when that index's
reader version allows it. It never updates or replaces the newer index.
When a memory index is newer, it reads memory records directly. Running
processes notice these version changes without restarting.

## Restart the watcher

```sh
hansard watcher restart
hansard watcher logs --follow
```

Restart after changing storage. The watcher applies a changed source list
within 30 seconds and moves to a new version of Hansard on its own; a restart
makes either take effect now. To back
up harness memories outside the watcher's 15-minute schedule, run
`hansard memory backup`. Unchanged memories do not create new snapshots.

## Save a conversation before a repair

```sh
hansard export <session-id> --out ./session-export
```

This copies the conversation files and attachments. Add `--include-raw` to
also copy the original harness files. Those are not redacted, so store the
export somewhere as private as the archive.

A raw export includes the chunk dependencies needed to verify preserved source
bytes independently. Hansard reserves space on the destination volume. An
interrupted export can leave completed files. Use `--force` to retry into the
same directory after reviewing its contents. Exported files remain in storage
accounting until they are removed and usage is measured again.

## Recover missing harness files

When `hansard doctor` reports that a harness deleted its original files,
Hansard can put its preserved copies back so the harness and the importer can
read them again:

```sh
hansard restore-sources claude --dry-run
hansard restore-sources claude
hansard import --source claude --since all
```

`restore-sources` writes only files that are missing from their original
paths. It never overwrites a file and never restores shared databases. For
ChatGPT and Claude.ai, it lists the newest preserved exports and the commands
that import them again.

Claude Code has a dedicated repair with an explicit confirmation:

```sh
hansard repair claude-code-backups --dry-run
hansard repair claude-code-backups --yes
hansard import --source claude --since all
```

## Reclaim disk space

```sh
hansard prune archive-objects --dry-run
hansard prune sync-outbox --dry-run
```

With Personal Sync and a verified storage copy, `hansard storage offload` also
frees space by removing original files and attachments older than 30 days from
this computer. See
[Offload old files to the backup](https://www.hansard.dev/sync/personal-sync/#offload-old-files-to-the-backup).

`archive-objects` deletes stored originals, attachments, and artifacts that no
session references anymore, after a one-day grace period. For older shared
snapshots, it keeps the newest copy and one per day for `--keep-days` days
(default 7). Anything it cannot verify is kept.

`sync-outbox` deletes local upload copies that have already reached their sync
destination, and the records of uploads that a newer snapshot replaced, that
were abandoned, or whose object the archive no longer keeps. It also releases
the local copy of the latest upload once every configured Personal Sync
destination holds its exact files, after a 15-minute grace. It then returns
the space they used to the disk. Remote backups do not change.

> **Update every copy of Hansard first:** The release waits until every copy of Hansard installed on the computer
> supports it. After it has run, a copy from an earlier release that you
> install or open on the same computer cannot open the upload queue, so its
> Personal Sync captures and uploads stop until you update it.

The watcher schedules archive cleanup separately from successful imports,
normally every 15 minutes. It waits while source changes or queued imports
are ready. Lock contention or a failed sweep retries after one minute.
The last successful sweep time remains visible after a failure.

Run cleanup by hand to reclaim space now, or to include received copies
and older shared snapshots. Remove `--dry-run` to delete after a confirmation,
or replace it with `--yes` to delete at once.
`hansard prune archive-objects --dry-run --json` explains anything that blocks
a cleanup.

Object cleanup preserves references from verified raw manifests even when an
interrupted first write left no session metadata. Corrupt manifests or invalid
metadata protect the affected object namespace. Unrelated unreachable objects
can still be collected.

Outbox cleanup keeps pending uploads and unfinished recovery data. Current
inventory entries, pinned uploads, and required release-history records keep
their receipts and chunk lists. Other cached receipts have a limited spare
allowance. Dropping one can require a later upload again, but does not remove
remote backup history. Logical byte reductions can exceed reclaimed disk space
because hardlinks and clones share physical storage.

To compress the original harness files already in the archive immediately,
run the raw compaction pass. Automatic refresh runs the same pass after updates
when **Refresh after updates** is on:

```sh
hansard rebuild --compact-raw --dry-run --json
hansard rebuild --compact-raw
```

The preview lists the eligible files and their sizes without estimating the
savings. The pass stores each eligible file losslessly in verified 1 MiB
chunks and reparses nothing. It needs free space for both forms while it
converts, and archive maintenance removes the replaced files after their grace
period. It waits while a copy of Hansard or a Personal Sync device cannot read
chunked files. Once a compacted conversation uploads, the backup requires
every device to read chunked files from then on. Later imports keep the
storage mode that [`archive.rawStorage`](https://www.hansard.dev/reference/configuration/) sets.

Files below 64 KiB remain whole. Each replacement must save at least 5%.
The pass refuses a session above 512 MiB of raw evidence or 128 raw records.
Received copies, immutable generations, source cohorts, and recovery directories
remain unchanged. The preview reports logical bytes, not estimated compression.

```sh
hansard prune reveal-cache --dry-run
```

`reveal-cache` replaces the whole unredacted copy of each conversation that
earlier versions kept for `hansard show --unredacted` with what Hansard keeps
now: nothing for a conversation redaction left unchanged, whose archived
transcript already holds every message as it was, and the original form of
the messages redaction changed for the rest. It removes a copy only after the
conversation reads back exactly as the copy held it. It also removes the files
of conversations the archive no longer holds, which an earlier version of
Hansard leaves behind when it deletes a conversation. Background imports do
not run it; each conversation's next import replaces its own copy. Remove
`--dry-run` to apply it.

### Compress classification history

Classification retains an immutable revision of every result. New revisions
are compressed. To inspect and compress older text revisions:

```sh
hansard classify compact
hansard classify compact --apply
```

The command checks that each compressed revision decodes to the original text.
Add `--rewrite` to return freed space to the filesystem when sufficient
space remains. Automatic maintenance compresses revisions for at most
20 seconds per pass. Once none remain, it releases free space when that space
is at least one eighth of the database.

### Clean received history

```sh
hansard prune received-history --dry-run
hansard prune received-history --yes
```

Cleanup preserves the active snapshot, eight prior completed snapshots, indexed
generations, and active readers. It removes only files and manifest nodes that
no retained inventory reaches. Pending restores, unverified or legacy
inventories, and an unreadable newer session index defer cleanup.

This changes local received copies, not remote history. Automatic maintenance
also releases blob-store links for versions that no retained snapshot uses.
Bytes become reclaimable after both snapshot and blob-store links are gone.

### Review snapshot history

**Settings → Imports → Snapshot history** lists saved checkpoints. Open one
to preview a verified text file, compare two observations, or pin it. Capture
details name the source coverage and observation times. Binary, missing, or
oversized files explain why a text preview is unavailable.

Historical checkpoint expiry is off by default. With
`storage.historyRetention.enabled` on, pressure recovery can expire eligible
checkpoints only after garbage cleanup and an exact admission check show that
a pending write still cannot fit.

| Policy key under `storage.historyRetention.policy` | Default                                                                 |
| -------------------------------------------------- | ----------------------------------------------------------------------- |
| `allForMs`                                         | Keep every observed checkpoint for one day.                             |
| `hourlyForMs`                                      | Keep hourly checkpoints through seven days.                             |
| `retainForMs`                                      | Keep daily checkpoints through 30 days.                                 |
| `maxBytes`                                         | Allow 10 GiB beyond current evidence. Pins count toward this allowance. |

Current evidence, readers, and pending uploads remain protected. A write without
an exact size estimate defers history expiry. This setting does not turn on
automatic checkpoint capture.

### Storage limits

Hansard reserves space before it copies original files and upload payloads,
and leaves 2 GiB free on the archive's disk by default. Change the floor, or
set optional allowances for the storage Hansard accounts for:

```sh
hansard config set storage.limits.minFreeBytes 2147483648
hansard config get storage.limits
```

`storage.limits.totalBytes` and `storage.limits.totalObjects` default to
`null`, which means no allowance. The `evidence`, `history`, `staging`, and
`pendingUploads` entries under `storage.limits.categories` each accept `bytes`
and `objects`. An allowance needs a complete usage scan; while a scan is
incomplete, writes it covers wait. The limits cover original files, session
files, memory snapshots, catalogs, upload payloads, exports, received
downloads, and caches, but not every archive writer, so they are not a quota
for the whole archive. `hansard status --json` reports the recorded usage and
the current free space.

Files of at least 1 MiB can use copy-on-write clones on supported APFS, Btrfs,
or XFS volumes. A clone still reserves and counts its full logical size.
Frozen uploads can hardlink immutable archive objects instead of copying them.
Interrupted writes remain charged until cleanup establishes their actual usage.

Outbox and receive databases each have a 512 MiB image limit and a 1 GiB
write-ahead-log limit. Normal transactions allow at most 8 MiB of database
growth and 8 MiB of bound input. Received manifest transactions allow 32 MiB
to hold a complete dependency record. Database checkpoints reserve space
separately and defer at the free-space floor.

SQLite can hold a dirty image and statement journal in memory, approaching
1 GiB together at the database limit, before query results and input buffers.
`hansard personal-sync status` and `hansard doctor storage` report backup
capacity warnings at half of a limit and again at four fifths. A full outbox
stops capture and uploads. Each backup stream also stops at one million files
or a 64 MiB file list.

## Upgrade Hansard

```sh
npm install -g hansard@latest
hansard watcher restart
```

The restart moves the watcher to the new version now; otherwise it moves on its
own once nothing it started is running. Reopen the app or browser.
The Mac app updates itself when it was installed from the signed installer; see
[Update the app](https://www.hansard.dev/getting-started/download/#update-the-app). Automatic
installation with `hansard app install` requires a configured desktop release
manifest.

Reinstall the npm package after changing the Node.js major version so its
SQLite native module matches the runtime. On macOS, Hansard uses Homebrew's
stable Node link when available, so routine Homebrew upgrades do not leave
integrations pointing at a removed version directory. `hansard doctor` checks
the viewer and watcher runtimes and their SQLite module.

After an update, the app shows **New in Hansard** once, as a short sequence of
the release's highlights and the setup steps that apply to this computer, and
the first interactive command prints a short note. The app runs the steps you
chose only at **Finish**, and a step that fails shows its reason beside
**Run again**. `hansard whats-new` shows the release
again and runs the steps you choose; **Settings → About** reopens it in the
app. Turn off the dialog and note with
`hansard config set updates.whatsNew false` or `HANSARD_NO_WHATS_NEW=1`.

A normal upgrade needs no rebuild. When a release changes how a source is
read, its [release notes](https://www.hansard.dev/releases/) name the reimport or `rebuild`
command to run.

Hansard checks the npm registry for new releases at most once a day, from
interactive CLI commands and **Settings → About**. Turn this off with
`hansard config set updates.checkOnStartup false`.

## Run a full refresh

`hansard update` clears the local conversation archive, search indexes, import
records, caches, and local sync records, then reimports your configured
sources. It never deletes remote copies or the harnesses' own history.

> **Check your sources first:** A conversation whose harness history is gone can come back only from
> preserved originals. Run `hansard doctor` and restore missing files before
> you clear the archive.

1. Run `hansard doctor` and confirm the configured sources still have their
   history.

2. Preview what the update removes and keeps:

   ```sh
   hansard update --dry-run --since all
   ```

3. Restore any harness files that exist only in Hansard's preserved copies.

4. Run the update:

   ```sh
   hansard update --yes --since all
   ```

5. Check the result with `hansard doctor` and `hansard status`.

`update` keeps your configuration, redaction rules, memories, skill history,
imported evaluations, Deep Recall threads, classifications, web-export
archives and account labels, conversations whose sources have disappeared, and
recall integrations. `--sources a,b,c` limits the reimport, `--no-index` skips
the search rebuild, and `--no-restart` leaves the watcher stopped.

### Recover from an interrupted update

An update that stops partway keeps a verified backup of the archive it
replaced. List the preserved operations, preview one, then restore it:

```sh
hansard update --list-recoveries
hansard update --recover <operation-id> --dry-run
hansard update --recover <operation-id> --yes
```

`--recover` restores the preserved archive without reading harness history. It
replaces the files the interrupted update wrote, and the backup stays available
if the restore fails.

## Delete, reset, or uninstall

| Command                         | Effect                                                                                                                                                                                                                                                                                                                                               |
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `hansard delete <session-id>`   | Removes Hansard's copy of one conversation, after any import that is running finishes. The harness's history is untouched. With Personal Sync, delete confirms the conversation's removal, and automatic maintenance removes it from the backup a day later. `hansard personal-sync restore-sessions` brings it back only when `--session` names it. |
| `hansard reset --dry-run`       | Lists what a reset removes: configuration, state, logs, indexes, caches, managed account profiles and router, and the archive.                                                                                                                                                                                                                       |
| `hansard reset --yes`           | Removes all of that for a fresh setup. Harness history outside the Hansard home and recall integration files stay.                                                                                                                                                                                                                                   |
| `hansard uninstall --keep-data` | Stops the watcher and managed router and removes the login item. Keeps the archive, configuration, and managed profiles.                                                                                                                                                                                                                             |
| `hansard uninstall`             | Also removes local Hansard data after confirmation. Lists recall and MCP files for you to remove.                                                                                                                                                                                                                                                    |

Managed account profiles live in the Hansard home and hold harness sign-ins and
history. Move them elsewhere before a reset or uninstall if you want to keep
them.

Prefer an export, a narrow rebuild, or a single-source reimport before a reset.
