Skip to content

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.

Terminal window
$ rigger doctor
profile: line (C:\Users\you\AppData\Local\lacodda\rigger\data\config.toml)
database: C:\Users\you\AppData\Local\lacodda\rigger\data\profiles\line\rigger.db
schema: version 27
projects: 1
versions: 0
tasks: 0
sessions: 0
events: 0
mcp: answers - protocol 2025-06-18, 25 tools, 1 prompt
backup: today, 3 copies kept

With --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.

The record is one file. backup: says when the newest copy of it was taken, and how many are kept:

Terminal window
$ 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:

Terminal window
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.

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:

Terminal window
$ rigger doctor
profile: line (C:\Users\you\AppData\Local\lacodda\rigger\data\config.toml)
database: C:\Users\you\AppData\Local\lacodda\rigger\data\profiles\line\rigger.db
schema: version 27
projects: 1
versions: 3
tasks: 1
sessions: 0
events: 1
mcp: answers - protocol 2025-06-18, 25 tools, 1 prompt
backup: 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 wrote

Reported, 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).

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:

Terminal window
$ 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 stage

Reported, 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.

Хотелки.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:

Terminal window
wishes left in Хотелки.md (1):
atlas 1 wish
the file is no longer read at every session: `rigger import <project> --hub <dir>` takes them in

Checked 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.

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.

Terminal window
$ 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 it

Both ways out are named, because either can be the right one: the edit was worth making, or it was not.

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:

Terminal window
$ rigger doctor --hubs
hubs: nothing written in by hand
behind the record (3 files in 2 hubs): alpha, beta
nothing in them is lost: the next session end, or `rigger export`, writes them again

Reported 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:

Terminal window
gamma - no hub recorded; import or export one

That 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:

Terminal window
beta План.md, Изменения.md kept by hand; `rigger export --adopt` hands it over

It 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.

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:

Terminal window
$ rigger doctor --hubs
hubs: nothing written in by hand
only 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 one
C:\dev\hubs: deleting it would lose what is named above

Once each is taken in:

Terminal window
$ rigger doctor --hubs
hubs: every file says what the record says
C:\dev\hubs: the record holds every file - deleting it loses nothing; `rigger export --all --docs` writes it back

Each 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.

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.

  • init - create the database doctor reports on.
  • sync - what fills the mismatch section.