---
title: Audit and publish the corpus
description: Every memhtml doctor finding and its fix, what --fix will and will not repair, and how memhtml publish resolves a conflict in a generated file.
---

```bash
memhtml doctor          # thirteen findings; ten of them decide healthy
memhtml doctor --fix    # repairs the two that need no judgement call
memhtml publish         # regenerate index.html listings and sitemap.xml, and commit
```

A healthy store answers with every finding list empty:

```json
{
  "apiVersion": "1",
  "type": "doctor.report",
  "data": {
    "root": "/home/you/memhtml",
    "healthy": true,
    "dangling": [],
    "orphanAccessRows": [],
    "inboxDepth": 0,
    "inboxCrowded": false,
    "inboxTaskDepth": 0,
    "inboxTasksCrowded": false,
    "overdueTasks": [],
    "staleBlockers": [],
    "untypedEntities": [],
    "untypedEntityTotal": 0,
    "stuckSleepRuns": [],
    "warnings": [],
    "unparseable": [],
    "indexFresh": true,
    "indexHeadSha": "4e232759bfad745b0445ecd83cc9883c30a0c426",
    "headSha": "4e232759bfad745b0445ecd83cc9883c30a0c426",
    "embedModelMatches": true,
    "storedEmbedModel": "cohere.embed-v4:0@1024",
    "configuredEmbedModel": "cohere.embed-v4:0@1024",
    "vectorCoverage": 1,
    "vectorCoverageFloor": 0.95,
    "vectorCoverageLow": false,
    "chunks": 9402,
    "embeddings": 9402,
    "vectorCoverageRemedy": null,
    "dirty": []
  }
}
```

Every list is present and possibly empty, so a parser never has to branch on a missing key. `healthy` is true when every check comes back clean.

## The findings

| Finding                    | Meaning                                                                                                                                                                                                                                                                                                                                                                    | Fix                                                                                                                                                                         |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `dangling`                 | A `<link>` points at a path the tree does not hold.                                                                                                                                                                                                                                                                                                                        | `--fix` rewrites it to the archive path, or drops it when the target is gone.                                                                                               |
| `orphanAccessRows`         | A `state.access` row whose path left the tree.                                                                                                                                                                                                                                                                                                                             | `--fix` prunes it.                                                                                                                                                          |
| `inboxCrowded`             | Over 20 active memories in `areas/inbox/`, which means the placement rules stopped matching what agents write.                                                                                                                                                                                                                                                             | Re-place them, or revisit the rules.                                                                                                                                        |
| `inboxTasksCrowded`        | Over 10 open tasks in `areas/inbox/tasks/`: work with no project.                                                                                                                                                                                                                                                                                                          | Drain it. A task inbox is a queue, and this one is filling up.                                                                                                              |
| `overdueTasks`             | An open task whose `memhtml-due` has passed.                                                                                                                                                                                                                                                                                                                               | Doctor is the only surface that reads `due_at`, because search excludes a task by default and every sleep phase skips it.                                                   |
| `staleBlockers`            | A `blocks` edge whose blocker is archived or absent.                                                                                                                                                                                                                                                                                                                       | Decide whether the blocked task is ready. Each file on its own is valid, and only the pair is wrong.                                                                        |
| `untypedEntities`          | A `memhtml-entity` meta written as a bare name, so it indexes under the `unknown` type.                                                                                                                                                                                                                                                                                    | Name a type in the producer. `unknown:checkout-api` is not reachable by `--entity service:checkout-api`.                                                                    |
| `stuckSleepRuns`           | A `sleep_runs` row still `running` whose branch is gone or whose start is over 20 hours old: a killed run.                                                                                                                                                                                                                                                                 | `memhtml sleep run` reaps it at start, and `memhtml sleep run --dry-run` does too. Doctor never writes that table.                                                          |
| `warnings`                 | An element outside the closed vocabulary. The file still indexes.                                                                                                                                                                                                                                                                                                          | Author's intent, and doctor will not guess at it.                                                                                                                           |
| `unparseable`              | A file the parser refuses. It is absent from the index.                                                                                                                                                                                                                                                                                                                    | Read the violations with `memhtml read <path>`, fix the file, then `memhtml index rebuild`.                                                                                 |
| `indexFresh: false`        | The index describes an older commit.                                                                                                                                                                                                                                                                                                                                       | `memhtml index update --embed`.                                                                                                                                             |
| `embedModelMatches: false` | The stored vectors came from a different embedding model than the configured one.                                                                                                                                                                                                                                                                                          | Delete the database and rebuild: see [rebuild the index](/learn/operations/rebuild-the-index/).                                                                             |
| `vectorCoverageLow: true`  | Under `vectorCoverageFloor` (default 0.95, `MEMHTML_VECTOR_COVERAGE_FLOOR`) of the chunks carry a vector in the configured space while the plane is in use: some vector exists, or an embedder is configured. Search drops the vector arm and reports `degraded: true` with `vectorCoverage`. A store with zero vectors and no embedder is lexical-only and stays healthy. | `vectorCoverageRemedy` names it: `memhtml index embed` to backfill, or `memhtml index rebuild --embed`. Set `MEMHTML_EMBED=off` if the store is meant to stay lexical-only. |

The two inbox thresholds are `INBOX_WARN_DEPTH` 20 and `INBOX_TASK_WARN_DEPTH` 10 (`apps/cli/src/doctor.ts:69`, `apps/cli/src/doctor.ts:78`).

`stuckSleepRuns` counts toward `healthy` for the same reason `orphanAccessRows` does: a row that says `running` about a process that is gone is a false statement in the ledger. Each entry names the `runId`, its `branch`, its `startedAt`, and `branchExists`, which is `false` when the branch was deleted (the row is stuck on that alone) and `true` when the row is stuck on age alone and `memhtml sleep resume <run-id>` could still finish it. The age bound is `SLEEP_RUN_STALE_AFTER_MS`, 20 hours (`packages/sleep/src/contract.ts`): a bound on how long one run can still be executing, several times a full run's length and four hours short of a day so a once-a-day caller always reaps the previous run on the next start. A row that is young and whose branch exists is a run executing right now and is not listed.

`overdueTasks`, `staleBlockers` and `untypedEntities` are the three findings excluded from `healthy`. The first two describe work that has fallen behind rather than a corpus that is wrong, and folding them in would make `healthy: false` the normal state. `untypedEntities` is excluded because `unknown` is a supported storage type: a bare name costs reachability, not correctness, and gating on it would turn a hand-authored corpus red for writing its metas the way the format allows. `untypedEntities` is capped at `UNTYPED_ENTITY_SAMPLE` 20 with the distinct count reported beside it as `untypedEntityTotal`, so a truncated sample cannot be read as the whole set.

Search excludes a `task` memory by default and every sleep phase skips it, so `memhtml doctor` is the only command that reports a task past its due date. If you use tasks, put this command on your cron.

## What `--fix` repairs, and why only two

`--fix` rewrites `dangling` hrefs and prunes `orphanAccessRows`. Everything else needs a decision doctor is not entitled to make: a crowded inbox might be a bad day or bad placement rules, an element outside the vocabulary might be exactly what the author meant, and an unparseable file needs a human to read the violations.

`--fix` reuses the repair the sleep integrity phase already implements, which splices the changed bytes in place instead of writing the file out again through the serializer. The reason goes past avoiding a second implementation: a repair routed through the serializer would move the content hash of every file it touched, which would then read as a body change in the next `memhtml sleep review` and would re-embed the file for nothing.

Under `--fix` the report gains a `repaired` object: hrefs `rewritten` (pointed at the target's archive path), hrefs `dropped` (the target has no file anywhere), `failedWrites`, `prunedAccessRows`, and the `commitSha` the href repairs landed in — `null` when nothing was rewritten (`apps/cli/src/doctor.ts:156`).

A repair counts only when its bytes reached disk. `failedWrites` lists the source paths whose repaired bytes could not be written, and those findings are still open: they stay in `dangling`, they are absent from `rewritten` and `dropped`, and they are never staged, so a re-run retries them. Both halves of that matter — staging an unchanged file would put the pre-repair bytes into a commit whose subject claims they were repaired, and counting it would report a finding as settled while it is still open. A non-empty `failedWrites` is the one part of a `--fix` report that needs a second look, usually at permissions on the paths it names.

## Publish

```bash
memhtml publish
```

```json
{
  "apiVersion": "1",
  "type": "publish.report",
  "data": {
    "root": "/home/you/memhtml",
    "artifacts": 5,
    "written": 5,
    "paths": [
      "index.html",
      "resources/index.html",
      "resources/infra/index.html",
      "resources/runbook/index.html",
      "sitemap.xml"
    ],
    "commitSha": "f1242b5e861ce09efb7fc1dfafd00f95b446215d"
  }
}
```

`memhtml publish` regenerates the per-directory `index.html` listings and `sitemap.xml` and commits them. It is deterministic to the byte (`apps/cli/src/publish.ts:10`), so two runs over an unchanged corpus write nothing and commit nothing, reporting `written: 0` and `commitSha: null`. That is what makes it safe on the cron.

Those artifacts are what make the store browsable: open `$MEMHTML_ROOT/index.html` in a browser and walk the corpus with no tooling at all.

## Publish is also the conflict resolution

`.gitattributes` marks `index.html` and `sitemap.xml` with `merge=ours`, and that attribute needs the per-clone `merge.ours.driver` config that [`memhtml init`](/learn/operations/initialize-a-store/) sets. If a merge has already written conflict markers into a generated file, leave the file itself alone.

Finish the merge however git needs you to, then run `memhtml publish` and let it overwrite the artifact. The generator is the authority for a derived file, so an edit you make by hand only produces a version the next publish discards.