Documents / Decisions / adr-20eec6e2e2ca
Per-project store discovery + docir init
Why a project-local .docir store is discovered the way git finds .git.
Context¶
docir resolved its store from DOCIR_HOME or a single global ~/.docir default
(ADR context in config/settings.py). That is a fine model for personal,
cross-project notes, but it has no per-repo story: running docir inside a project
wrote that project's docs into the global store, and the agent guide's own
"commit the docs" instruction was misleading because nothing landed in the repo.
For the common ask — "restructure this repo's existing docs into docir" — an
agent (or human) had no way to keep the docs with the code short of exporting
DOCIR_HOME in every shell.
Decision¶
Add a project-local store discovered the way git finds .git, plus a
command to create one:
- Discovery (
config/settings.discover_project_home): when neither an explicit--homenorDOCIR_HOMEis set,Settings.resolvewalks up from the working directory for a.docir/directory and uses the first one found; otherwise it falls back to the global~/.docir. New home precedence, highest first:--home→DOCIR_HOME→ discovered project.docir→~/.docir. docir init [DIR] [--profiles ...] [--force]: createsDIR/.docir, writes adocs-schema.yaml(the bundled default, or with the chosen profiles), writes a.gitignorefor the derived index + daemon runtime, ensures the directory layout, and runs migrations — the same startup path every command uses, so an initialized store is immediately valid. Existing files are preserved unless--force; an unknown profile is aSchemaError(exit 3).
Placement: the initialization logic is a bootstrap operation and lives in the
composition root (entry_points/composition.initialize_store), the one place
already allowed to touch every layer; the CLI command is a thin wrapper that runs
it in-process (no daemon/dispatcher, like agent and version). It reuses the
documents module's DEFAULT_SCHEMA_YAML / PROFILE_NAMES (newly exported from
documents.api, with the paired CONTRACT.md update) rather than reaching into
documents.infra.
Consequences¶
- Easier:
docir initscopes a repo's docs to the repo; commands run anywhere in the tree find the store automatically. The commit story is now honest —.docir/docs/+docs-schema.yamlare committed, the index is gitignored. This is what makes agent-driven doc migration land in the right place. - Backward compatible: with no project
.docirand noDOCIR_HOME, resolution still yields~/.docirexactly as before; every existing test setsDOCIR_HOME, so discovery never fires in the suite. - Cost:
Settings.resolvenow reads the CWD (aPath.cwd()walk) when nothing else pins the home. This is pinned behind the "no explicit home / no env" branch, so it is inert whenever a home is given. - Note: the global
~/.docirremains a valid store and is itself just a.docirdirectory, so discovery and the default coincide when a repo sits directly under~with no store of its own — the same resolved path either way. - Scoped out: no migration of an existing global store into a project store, and no multi-store federation/search across stores — one resolved store per invocation, as before.