docir — design documents Graph

Documents / Issues / issue-e5a0cb196607

A miss on an unbuilt index reports the document as nonexistent

update and get answer 'no document with id' in any checkout whose derived index was never built, so the message denies a document that is on disk.

issue-e5a0cb196607issueresolved#cli#integrity#material
View as Markdown◉ View in graph

Class: misleading · Severity: material Flow: arch-0368cc754c15 (the write path) · Step: update's index lookup Frequency: every read or write naming an existing id in a checkout whose index has not been built

Finding

DocumentService.update and _require answer a lookup that misses with no document with id '<id>', whatever the reason for the miss. Two reasons produce it and they are undone by opposite actions: the document is gone from the corpus, or the index answering for it holds nothing. The second is an ordinary state, not a corruption — the index is derived and gitignored, so a fresh clone and every new git worktree start without one, and nothing on the read or write path builds it.

What happens today

REPORTED as GitHub issue #7 against 0.17.0, REPRODUCED on 0.21.0:

text
git worktree add ../wt && cd ../wt
docir update <existing-id> --append-section "Notes" --body-file f.txt
  -> error: no document with id '<existing-id>'   (exit 4)

docir get fails identically. The file is on disk the whole time and docir reindex makes both succeed. The daemon does not rescue it: its watcher reindexes what changes, so an untouched checkout stays in this state indefinitely.

Impact

The message states a fact about the corpus that is false, so it sends the reader to the wrong place: a human looks for a deletion that never happened, and an agent — which cannot look at docs/ and see the file sitting there — concludes the document is gone. The reporter's automated workflow gave up a document amendment to it.

check --strict and doctor --strict have named this condition since 0.20.0 (empty-index, an error, docir reindex as the fix — issue-87410666c867, adr-909734bced92). Neither runs on the path that hits it, so the diagnosis exists in the store's health report and not where somebody meets the symptom.

Proposed default

Say which of the two happened, at the point of the miss. The comparison is already shared by check and doctor as index_is_empty(documents=, documents_on_disk=); a third reader on the not-found path costs two counts on the error path and nothing on a lookup that resolves. Keep the exception type and exit code — the caller's handling should not change, only where the message sends them — and keep the no document with id '<id>' prefix, since the batch read reports it per reference.

Rebuilding automatically was deferred at first, on a cost estimate that turned out to be wrong: a rebuild inside an arbitrary command looked corpus-sized. Measured, ~69s of docir reindex's 70s on this corpus is the embedding drain, and the rebuild alone is ~0.9s against a 0.98s baseline get. So opening a store that has files and no index now rebuilds it with the vectors deferred (adr-e53c813d2f13), and the message is the backstop for what that cannot reach — an index emptied under a running process, or a store opened by an older build.

Actors affected

  • AI coding agent
  • repository maintainer
  • automated documentation workflow

Evidence

  • src/docir/modules/documents/application/services/document_service.py (update, _require)
  • src/docir/modules/documents/application/services/maintenance_service.py (index_is_empty)
  • https://github.com/l0kifs/docir/issues/7

Resolution

FIXED, in two parts. Both raise sites go through DocumentService._not_found, which consults index_is_empty and appends the diagnosis and docir reindex when the index holds nothing while docs/ holds files. Same DocumentNotFoundError, same exit code 4, same message prefix. Verified by injecting the bug: with the helper returning the bare message, the empty-index guards fail while the populated-store guard still passes. Pinned by TestMissingIdNamesAnEmptyIndex.

Then the state itself: build_container rebuilds an index it finds empty beside files, so the message is no longer what a fresh clone or worktree meets first (adr-e53c813d2f13, pinned by TestOpeningAStoreWithNoIndexBuildsOne). no-index became a warning in the same change — it now describes a condition the run reporting it has already undone.

To amend: Re-verify: