docir — design documents Graph

Documents / Runbooks / run-f4a756206fe0

Upgrade docir in a project

What to run after a new docir release: the package, the derived index, and the generated files — the ones self upgrade refreshes and the ones it does not.

run-f4a756206fe0runbookactive#agents#cli#release
View as Markdown◉ View in graph

docir ships its schema, its agent instructions and its site templates inside the package. A release can therefore change what a store enforces and what an agent reads without a single file in your repository changing — there is nothing in git diff to review. Some of that is applied for you on the next command; the rest is this runbook.

Run it once per store. A machine has as many stores as it has .docir/ directories plus the global ~/.docir, and each carries its own index, its own daemon and its own schema baseline.

What happens without you

Two pieces of derived state carry on as if nothing changed, and neither announces itself:

  • The daemon. It loaded docir once and lives on, so after an upgrade it keeps answering from the old code — and a stale answer is indistinguishable from a correct one. The pid file records a CodeStamp (__version__ plus the newest mtime across the package sources), and a client that does not match stops and respawns it.
  • Embedding vectors. Each row records the model that produced it; a foreign model_id reads as dirty rather than as a vector to compare against, so a changed model recomputes on the next write instead of raising a dimension mismatch. Force it with docir embed --flush, or let the full docir reindex below do it — it re-saves every document and queues every one, and the drain recomputes each vector whose inputs moved, which under a changed model is all of them (issue-77dd42e3a03a).

What you have to run

bash
docir self upgrade        # install the new docir, then resync this store

That is the whole procedure where docir owns its environment (a uv tool, a pipx install, a virtualenv): it runs the installer, re-executes as the build it just installed, and then does the steps below in order, reporting each. Where docir does not own its environment — a checkout, a project whose lockfile pins it, an ephemeral uvx run — it says so and does the rest anyway; upgrade the package where it is pinned. docir self status says which case you are in.

The steps are still worth understanding, because when only one of them is what you need, that one is still a command.

bash
docir reindex             # once per store
docir agent update        # then commit the refreshed instruction files
docir check               # last, so it describes the state you are left in

docir reindex — the only mandatory step

It is the only writer of the two things the index records about the code that built it — the schema baseline and the docir version — so until it runs, check reports neither schema-drift nor stale-index-build: absent means unknown, not unchanged. The two answer different questions, which is why both exist: the baseline compares schemas and stays silent for a release that changes how documents are read rather than what they must contain (chunked embeddings rewrote every vector without touching a type or a cadence). It also raises the id counter to what is on disk, which is what a fresh clone needs — the index is gitignored, so a clone has no index and every read answers nothing until it is built. Opening a store whose index is empty rebuilds it before anything is dispatched (Opening a store with no index builds one, deferring the vectors), and where that state still arises check reports empty-index as an error (CI's document-integrity gate ran over an empty index).

There is deliberately no docir accept-schema verb. reindex is already the "make the derived state agree with the sources" command, and a separate acknowledgement would be a ritual whose only effect is to silence a report.

docir agent update — the files nothing tracks for you

.claude/skills/docir/SKILL.md and the docir block in AGENTS.md are generated from a template inside the package and stamped <!-- docir:vX -->. They are committed files, so refreshing them is a commit, and nothing detects that they are behind: check covers the corpus, not the generated instructions. docir 0.11.0 shipped with its own skill file still claiming v0.10.0.

The store's own .gitignore is generated the same way, and self upgrade tops it up in the same pass — there is no separate command. It is written once, by the init that created the store, so every entry docir has added since reached only stores created afterwards; feedback/ is the one that cost something, since in a store predating it the first upstream draft an agent writes shows up untracked, and that draft is the one thing in the store nobody has reviewed for redaction (The store's generated .gitignore misses release-check.json and is never refreshed). Entries are appended, not rewritten over: your own lines in that file are yours, and the report names what was added so the next move is a git status.

docir check — new warnings are expected

missing-required, unknown-relation-kind, unknown-type and schema-drift can all appear on a corpus that was clean yesterday. Every one is a warning, so --strict stays green and CI does not go red on the release that moved a rule: the documents are untouched and it is the rule that moved. Deal with them as documents (docir update <id> ...) or as schema (docs-schema.yaml), then reindex to re-baseline.

DOCIR_SCHEMA_NOTICE=1 prints the drift on stderr after every command, for the change nobody will run check to discover.

If it applies to you

  • docir init --force regenerates the store's .gitignore outright, which is more than the upgrade's top-up does: it also drops any line you added. Reach for it when you want the file back exactly as the package generates it. A docs-schema.yaml you have edited is preserved and reported, not replaced — --force-schema is what replaces it.
  • docir build --out <dir> — the site templates ship in the package, so a published corpus is only as new as its last build.
  • MCP clients hold a long-lived server process: restart the client so it re-execs the new binary. One spawned as uvx docir mcp serve resolves from uv's cache, so name the version (uvx docir@0.11.0 mcp serve) when it matters. docir self upgrade will not touch a uvx environment — there is nothing there to upgrade, since it is resolved per run and thrown away.
  • CI installs docir on its own; pin the version there and bump it in the same commit, or the gate runs a different docir from the one you tested with.

Knowing an upgrade is due

docir self status answers it without a network call: it reports the newest release as last checked, and an absent latest means nobody has checked, not "up to date". --refresh asks PyPI — docir's only network call, skipped when the answer is already from today.

A store created by docir init carries update_check: true in its committed config.yaml, which turns it into a background fact: the daemon refreshes the answer daily and a command mentions a newer release on stderr — over MCP, in the server's instructions at the handshake. Creating the store is the act that opts in, so a documentation tool still never phones home unasked, and the answer is one the whole team inherits rather than one each person exports (adr-bea870d0b666). DOCIR_UPDATE_CHECK=0 turns it off for one shell, =1 on; a run with CI set never checks.

It says a release once per day per store, not once per command, and it says nothing at all to an installation that cannot act on it — a checkout, or a docir pinned by a project's lockfile, which is upgraded in that project. Those are the two reasons a notice repeating until somebody acts stops being read, answered rather than avoided.

Act on it between tasks. docir self upgrade replaces the running process, respawns the daemon and rebuilds the index; a task that continues across that runs its remaining steps on a build nothing verified.

docir check covers the other direction, the one that needs no network at all: stale-index-build says the index was built by a docir that is no longer installed.

Verify

bash
docir daemon status       # reports the build being served
docir query --limit 1     # a non-empty answer means the index is populated
docir check --strict      # exit 0

Teammates who have not upgraded

The store is committed, so a teammate who has not upgraded keeps reading it — but their first command refuses. The index you migrated sits at a revision their build does not ship, and every command opens the index.

The message names both revisions. A plain docir reindex is not the way out: it opens the index too, and refuses identically. They delete the file and rebuild, once.

bash
rm .docir/index.db* && docir reindex     # on their machine

Nothing under docs/ is touched. The index is derived and gitignored, so this costs a rebuild and no content.

And their writes erode the store quietly

An older build writes back the frontmatter keys it knows and drops the rest, so a document it edits loses whatever the newer release added — code_baseline: today, and whatever comes next. Nothing errors; the document simply stops being watched.

Measured on a three-document store: one update --set-owner from the older build took the baselines from three to two.

docir check --fix refiles what was dropped and names each document it touched, so the repair is one command and reviewable in the diff.

The cost falls on whoever upgraded, because they are the only one who can see it. Upgrading together avoids both halves; where that is not practical, run --fix after a teammate on an older build has written.

To amend: Re-verify: