docs_paths.py file-level

at main · View file ↗ · Intel ↗

History
1 files
1 commits
0 hotspots
0 🧊 dead
0 💥 blast risk
sha256:6 fix(ISR): default require_independent_second_reviewer to require Opera… · aaronrene · Sep 2, 2026
1 """Living-doc path joins and ``root_relative_docs`` normalization (§K6.5.2)."""
2
3 from __future__ import annotations
4
5 from pathlib import Path
6
7 from adapters.config import OverseerConfig
8 from adapters.errors import ConfigError
9
10
11 def normalize_docs_root(root_relative_docs: str) -> str:
12 """Return stripped docs-root; ``\".\"`` is the only repo-root sentinel."""
13 return root_relative_docs.strip()
14
15
16 def join_docs_rel(root_relative_docs: str, doc_name: str) -> str:
17 """Join docs root and filename into a repo-relative POSIX path.
18
19 When ``root_relative_docs`` is exactly ``\".\"`` (after strip), return the bare
20 ``doc_name`` (never ``./name`` or ``/name``).
21 """
22 root = normalize_docs_root(root_relative_docs)
23 if not root:
24 raise ConfigError("repo.root_relative_docs must be a non-empty string")
25 if root == ".":
26 return doc_name
27 return f"{root.rstrip('/')}/{doc_name}"
28
29
30 def living_doc_destinations(config: OverseerConfig) -> frozenset[str]:
31 """Return footprint destination paths for configured living docs (all lanes)."""
32 docs_root = config.repo.root_relative_docs
33 docs: set[str] = set()
34 if config.docs.lanes is None:
35 docs.add(join_docs_rel(docs_root, config.docs.handover))
36 docs.add(join_docs_rel(docs_root, config.docs.roadmap))
37 else:
38 for lane in config.docs.lanes.values():
39 docs.add(join_docs_rel(docs_root, lane.handover))
40 docs.add(join_docs_rel(docs_root, lane.roadmap))
41 if config.docs.coordination:
42 docs.add(join_docs_rel(docs_root, config.docs.coordination))
43 return frozenset(docs)
44
45
46 def lane_living_doc_abs(
47 repo_root: Path,
48 config: OverseerConfig,
49 lane_docs: LaneDocsConfig,
50 doc_name: str,
51 ) -> Path:
52 """Absolute path to a lane living doc under ``repo_root``."""
53 return repo_root / join_docs_rel(config.repo.root_relative_docs, doc_name)
54
55
56 def living_doc_abs(repo_root: Path, config: OverseerConfig, doc_name: str) -> Path:
57 """Absolute path to a living doc under ``repo_root``."""
58 return repo_root / join_docs_rel(config.repo.root_relative_docs, doc_name)
59
60
61 def validate_muse_working_dir(repo_root: Path, working_dir: str | None) -> Path | None:
62 """Resolve ``vcs.muse.working_dir`` inside ``repo_root``; raise ``ConfigError`` on escape.
63
64 Returns the absolute Muse cwd, or ``None`` when ``working_dir`` is unset (install root).
65 Escape / absolute-outside-root → ``ConfigError`` (CLI maps to exit ``2``).
66 """
67 if working_dir is None:
68 return None
69 text = working_dir.strip()
70 if not text:
71 raise ConfigError("vcs.muse.working_dir must be a non-empty string or null")
72 candidate = Path(text)
73 if candidate.is_absolute():
74 raise ConfigError("vcs.muse.working_dir must be relative to the install root")
75 if ".." in candidate.parts:
76 raise ConfigError("vcs.muse.working_dir must not contain '..' path segments")
77 root = repo_root.resolve()
78 resolved = (root / candidate).resolve()
79 try:
80 resolved.relative_to(root)
81 except ValueError as exc:
82 raise ConfigError("vcs.muse.working_dir escapes install root") from exc
83 return resolved