doctor
rigger doctor [--hubs] [--json]Prints the profile in use, the database path, its schema version and how many records of each kind it holds. Before init it says so instead of failing, so doctor is always safe to run first.
$ rigger doctorprofile: line (C:\Users\you\AppData\Local\lacodda\rigger\data\config.toml)database: C:\Users\you\AppData\Local\lacodda\rigger\data\profiles\line\rigger.dbschema: version 27projects: 1versions: 0tasks: 0sessions: 0events: 0mcp: answers - protocol 2025-06-18, 25 tools, 1 promptbackup: today, 3 copies keptWith --json:
{ "database": "C:\\Users\\you\\AppData\\Local\\lacodda\\rigger\\data\\rigger.db", "initialised": true, "schema_version": 4, "counts": { "projects": 1, "retired": 0, "versions": 0, "tasks": 0, "sessions": 0, "events": 0 }, "backup": { "newest_at": "2026-09-15T12:02:53Z", "age_days": 0, "copies": 3, "state": "fresh" }}Before init, only profile, database and initialised (false) are printed.
| Field | Type | Meaning |
|---|---|---|
profile |
string | The name of the profile in use. |
database |
string | The database path. |
initialised |
boolean | Whether the database exists. |
schema_version |
integer | The schema version the database holds. |
counts |
object | How many records of each kind the database holds. |
counts.projects |
integer | Projects, the retired among them. |
counts.retired |
integer | Of those, how many are retired. |
counts.versions |
integer | Versions. |
counts.tasks |
integer | Tasks. |
counts.sessions |
integer | Sessions. |
counts.events |
integer | Events. |
mcp |
object | What the built-in MCP server answered to a self-check. |
mcp.version |
string | The server’s version. |
mcp.protocol |
string | The MCP protocol version it speaks. |
mcp.tools |
integer | How many tools it offers. |
mcp.prompts |
integer | How many prompts it offers. |
backup |
object | How old the newest database copy is. |
backup.newest_at |
string or null | When the newest copy was taken, in RFC 3339 UTC; null when there is none. |
backup.age_days |
integer or null | Whole days since then; null when there is no copy. |
backup.copies |
integer | How many copies are kept. |
backup.state |
string | fresh (under a day), stale (a day or more), old (a week or more) or none. |
hubs |
array or null | Hub files the record cannot vouch for; null unless --hubs is given. |
hubs[] |
object | One such hub file. |
hubs[].project |
string | The project’s name. |
hubs[].file |
string | The file, several joined by , when kept by hand, or - when no hub is known. |
hubs[].state |
string | edited (written in since the record wrote it, or a kept file whose bytes differ), unaccounted (differs, and the record has no note of writing it), by-hand (never handed over), not-held (lives only in the hub), too-large (larger than the record keeps), unrecorded-hub (a hub no project names), no-hub or hub-missing. |
hubs[].why |
string | The reason the file cannot be vouched for, and what to do about it. |
hubs_behind |
array or null | Hub files the record wrote and has moved on from since - nothing in them is lost; null unless --hubs is given. |
hubs_behind[] |
object | One such file. |
hubs_behind[].project |
string | The project’s name. |
hubs_behind[].file |
string | The file, relative to the hub. |
hubs_verdict |
object or null | Whether deleting the hubs would lose anything; null unless --hubs is given. |
hubs_verdict.root |
string or null | The directory the profile keeps its hubs in; null when it names none. |
hubs_verdict.loses_nothing |
boolean | True when the record holds every file of every hub - nothing above at stake, no wish left in a wishes file. |
closed_without_a_tag |
array | Versions the plan closed that no tag confirms. |
closed_without_a_tag[] |
object | One such version. |
closed_without_a_tag[].project |
string | The project’s name. |
closed_without_a_tag[].version |
string | The version. |
one_step_apart |
array | Places where the plan and the tags look one step apart. |
one_step_apart[] |
object | One such place: two neighbouring versions of one project. |
one_step_apart[].project |
string | The project’s name. |
one_step_apart[].closed |
string | The version the plan closed with no tag to show for it. |
one_step_apart[].tagged |
string | Its neighbour: a tag the plan never named a stage for. |
never_synced |
array | Projects sync has never read. |
never_synced[] |
string | A project’s name. |
wishes_left_in_the_hub |
array | Projects with wishes waiting in Хотелки.md. |
wishes_left_in_the_hub[] |
object | One such project. |
wishes_left_in_the_hub[].project |
string | The project’s name. |
wishes_left_in_the_hub[].wishes |
integer | How many wishes wait. |
How old the insurance is
Section titled “How old the insurance is”The record is one file. backup: says when the newest copy of it was taken, and how many are kept:
$ rigger doctor...backup: 3 days ago, 5 copies kept - older than a day; run `rigger backup`A copy from today passes without advice, older than a day is worth a line, and older than a week ("state": "old") means the copy no longer resembles the record. Having none at all says so plainly:
backup: none - one file, no copy of it; run `rigger backup`The age is read from the moment in the copy’s name, not from the file’s own timestamp, which says when it was last moved rather than when its contents were true. See backup.
Where the plan and git disagree
Section titled “Where the plan and git disagree”Once rigger sync has read a project, doctor lists the versions its plan closed that no tag confirms - across every project, without reading git again:
$ rigger doctorprofile: line (C:\Users\you\AppData\Local\lacodda\rigger\data\config.toml)database: C:\Users\you\AppData\Local\lacodda\rigger\data\profiles\line\rigger.dbschema: version 27projects: 1versions: 3tasks: 1sessions: 0events: 1mcp: answers - protocol 2025-06-18, 25 tools, 1 promptbackup: today, 1 copy kept
closed in the plan, no tag in git (1): claimed v0.2.0 a tag would settle it; rigger does not change what you wroteReported, never corrected. A missing tag is not proof a release did not happen - it may simply never have been fetched - and silently reopening a version would erase what you wrote (ADR 0005).
One step apart
Section titled “One step apart”Either half alone is ordinary - a tag not fetched yet, a release older than the plan. Side by side they are the shape of a plan running a step ahead of its tags: a version closed without a tag, right beside a tag the plan never named a stage for. Since v0.27.0 doctor names each such pair:
$ rigger doctor...the plan and the tags look one step apart (1): sample v0.2.0 closed without a tag, beside v0.3.0 tagged without a stage the tags may name the stage before the one the plan gave them; renumber the plan, or tag the stageReported, like the list above it, and never corrected: which of the two is right is a question about the work, and the record cannot answer it. Tagging the closed stage, or renumbering the plan so its stages carry the numbers the tags gave them, settles it. Under --json the pairs are one_step_apart.
A project that has never been synced is named rather than judged: without a sync there is no way to tell a claim from a fact, and doctor says which projects it cannot speak for.
Wishes left in the hub
Section titled “Wishes left in the hub”Хотелки.md stopped being read at every session in v0.20.0: a wish now arrives through rigger wish or the assistant’s tool and lives in the record. Which means a wish written into the file after that is a wish nobody would ever read - so doctor says when one is waiting:
wishes left in Хотелки.md (1): atlas 1 wish the file is no longer read at every session: `rigger import <project> --hub <dir>` takes them inChecked without being asked: it is one small file per project that has a hub. The template, its placeholder and a note saying the wishes were already sorted do not count.
Hubs (--hubs)
Section titled “Hubs (--hubs)”A file of a hub that somebody has written in has stopped being a view of the record, and the next export would overwrite the edit without saying so. --hubs names those files. It is off by default because it reads every hub from disk.
$ rigger doctor --hubs
hubs the record cannot vouch for (1): sample План.md edited since the record wrote it; `rigger import` takes the edit in edited: `rigger import` takes the edit into the record; `rigger export` discards itBoth ways out are named, because either can be the right one: the edit was worth making, or it was not.
Behind is not edited
Section titled “Behind is not edited”A file can differ from the record for two opposite reasons: somebody wrote in it, or the record moved on since it wrote the file - a question asked, a box ticked, a sitting ended. The first is a decision to make; the second loses nothing, and writing the file again is all it needs. Since v0.31.0 the record notes what it last wrote into each file (or read in from it), so the two are told apart:
$ rigger doctor --hubs
hubs: nothing written in by handbehind the record (3 files in 2 hubs): alpha, beta nothing in them is lost: the next session end, or `rigger export`, writes them againReported in one list, the second kind buried the first: of the 66 files an earlier doctor named across one line’s hubs, 64 were only behind, and the two that held an edit were lost among them. A file the record has no note of - written before it kept them - is named apart, as unaccounted, with export --check to see how it differs; after the next export it has one.
A project whose hub the record has never seen is named too, rather than passed over:
gamma - no hub recorded; import or export oneThat is not pedantry. A check that skips what it cannot find prints the same clean line as a check that looked and found nothing wrong - and the record learns where a hub is only when one is imported or exported, so before that it genuinely cannot speak for it.
A hub still kept by hand is named as such, with the files that are:
beta План.md, Изменения.md kept by hand; `rigger export --adopt` hands it overIt is the one thing “every project through rigger” has left to do, so the list says how far along a line is rather than staying quiet about the files it skipped.
Can the hubs go
Section titled “Can the hubs go”The last line of --hubs answers the question the check exists for: would deleting the directory of hubs lose anything. Since v0.33.0 it looks at every file there, not only the files the record writes - the hubs of retired projects, the files that are not texts, and the line’s own files beside the hubs:
$ rigger doctor --hubs
hubs: nothing written in by handonly in the hubs, not in the record (7 files in 2 hubs): sample Исследования/bench/queries.json line brand/README.md, brand/svg/mark.svg, _Исследования/2026-09-01 — Сверка.md and 3 more `rigger import <project>` takes them in; `rigger doctor --hubs --json` names every oneC:\dev\hubs: deleting it would lose what is named aboveOnce each is taken in:
$ rigger doctor --hubs
hubs: every file says what the record saysC:\dev\hubs: the record holds every file - deleting it loses nothing; `rigger export --all --docs` writes it backEach file counts once, by what it is:
| A file | is held when |
|---|---|
| a generated file - plan, changelog, diary, README | it is what the record wrote, or the record has moved on past it (behind loses nothing) |
| a handwritten text | it is a document the record holds, saying the same |
Решения.md |
every entry in it is a decision the record holds, and its preamble is the decisions document |
Хотелки.md |
every wish in it is in the record |
| any other file | the record keeps it, with the same bytes |
What is not held is named: not-held lives only in the hub, too-large is more than the record keeps (64 MB), and unrecorded-hub is a directory with a plan or a changelog that no project names - somebody’s hub, not the line’s text. A project with no hub, or whose hub is already gone, has nothing at stake and does not count. Under --json the answer is hubs_verdict.
What it leaves out
Section titled “What it leaves out”A service project never appears among the projects waiting to be synced, and neither does a folder of work with no repository. There is no repository to read, so either would have sat in that list for ever being advised a command that could not help it.