ADR 009: Path Sovereignty — Configuration Follows the Target
Status: Active Decider: Architecture Lead Date: 2026-04-12 (v0.7.0 sprint, CEO-052)
Context
find_repo_root() originally searched upward from os.getcwd() — the invoking
shell's current working directory. This worked correctly for the standard case
where the user runs Zenzic from inside the repository they want to analyse.
It failed for any scenario where the caller's working directory differed from the target repository:
# CWD = /home/user/my-tools
# Target = /home/user/another-project/docs
zenzic check all /home/user/another-project/docs
In this case, find_repo_root() would walk upward from /home/user/my-tools,
find that repository's zenzic.toml, and load that repository's
configuration — including its engine, docs_dir, excluded_dirs, and custom
rules. The analysis target was another-project, but the configuration applied
was from my-tools. This is Context Hijacking.
Decision
"The configuration follows the target, not the caller."
When an explicit PATH argument is provided to any filesystem-interacting CLI
command, find_repo_root() is called with search_from=target_path — walking
upward from the target, not the CWD:
# core/scanner.py
def find_repo_root(
search_from: Path | None = None,
fallback_to_cwd: bool = False,
) -> Path:
start = search_from or Path.cwd()
for parent in [start, *start.parents]:
if (parent / ".git").exists() or (parent / "zenzic.toml").exists():
return parent
if fallback_to_cwd:
return Path.cwd()
raise RuntimeError(...)
_apply_target() in cli/_check.py orchestrates the recalibration: after
deriving docs_root from the user-provided target, it calls
find_repo_root(search_from=docs_root) to load the correct zenzic.toml,
then re-derives docs_dir from the target repo's configuration.
The _apply_target() Invariant
When target == repo_root (the user points directly at a repo root, not a
subdirectory), docs_dir is preserved from the config rather than overridden
to ".". This prevents a subtle regression: a user running
zenzic check all /path/to/repo should respect that repo's docs_dir = "docs"
setting, not flatten it to the root.
# _apply_target() — canonical logic
if resolved_target == repo_root:
# Target IS the repo root: honour the config's docs_dir.
docs_root = repo_root / config.docs_dir
else:
# Target is a subdirectory: treat it as the docs root directly.
docs_root = resolved_target
Rationale
1. The Principle of Contextual Integrity
A configuration file belongs to the project it lives in. Loading a foreign
zenzic.toml because of a coincidence of working directory is a configuration
supply chain vulnerability — the analysis is secretly governed by rules the
user did not intend to apply.
2. CI/CD Correctness
In CI pipelines, the working directory is often the runner's home, a workspace
root, or a tool directory — not the documentation repository. Path Sovereignty
ensures that zenzic check all $DOCS_PATH in CI always applies the correct
project-specific rules, regardless of the runner's $PWD.
3. Symmetry with ADR-007
ADR-007 (Sovereign Sandbox) established that the perimeter follows the target. ADR-009 completes the picture: the configuration also follows the target. Together they guarantee that every aspect of an analysis — what is scanned, what rules apply, and what escapes are forbidden — is determined solely by the target repository.
Scope
Path Sovereignty applies to every CLI command that accepts an optional positional
PATH argument (Rule R18 — Total CLI Symmetry):
| Command | PATH semantics |
|---|---|
zenzic check all [PATH] | Sovereign root: find_repo_root(search_from=PATH) |
zenzic score [PATH] | Same |
zenzic diff [PATH] | Same; snapshot path derived from resolved repo_root |
zenzic init [PATH] | Genesis Nomad: PATH is the repo_root directly; created if absent |
zenzic lab, zenzic inspect | No PATH argument — exempt |
Consequences
-
Running Zenzic from any directory now produces identical results to running it
from inside the target repository — no surprises for CI operators.
-
Contributors implementing new CLI commands that accept a
PATHargumentmust call
find_repo_root(search_from=resolved_path)and invoke_apply_target(). This is now a documented invariant in the contribution guide. -
The
fallback_to_cwd=Trueparameter offind_repo_root()is reservedexclusively for the
initcommand (Genesis Fallback — see ADR-003). No other command may use it.