fix(tools): doc-triggers found the repository by depth and left it

ROOT was `Path(__file__).resolve().parents[3]`, which is correct only while the
script sits at its template home, docs/architecture/scripts/. TOOLS.md tells an
adopting project to take scripts one at a time into its own scripts/, and from
<project>/scripts/doc-triggers.py that expression resolves to the *parent of the
project* -- outside the repository entirely.

The failure is silent and reads as a pass. main() opens with a `not DOCS.is_dir()`
guard that prints and returns 0, so an adopting project got exit 0 and one line
naming a directory two levels above the code it was asked about. The check that
enforces "update the triggered documents in the same commit as the code" had
quietly stopped running, in exactly the projects that took the template's advice.

That is the failure GUARDS.md opens with -- a guard that cannot fail is worse
than no guard, because it is trusted -- landed on the tool that polices the
documents. It could not be caught by running it here, because here parents[3] is
right; it takes a copy at the documented location to see it.

The root is now found rather than assumed: walk up from __file__ for a directory
holding both docs/ and .git, then either alone, then `git rev-parse
--show-toplevel`, then give up to the script's own parent. Both directories are
required before docs/ alone so that a repository vendoring a docs/ in some
subdirectory does not anchor on it.

Reproduced in a scratch repository with the script at scripts/, a document
governing src/**, and src/a.py staged: before, "no docs/ directory at <tmp>/docs";
after, the document and its trigger. The in-place layout is unchanged and still
fires DOC_TRUST_MAP.md, TOOLS.md and architecture/README.md.

Exit status is still always 0. This is a prompt, not a gate, so the fix belongs
in root resolution and not in the exit code.

closes #17

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
null 2026-08-17 23:39:13 -05:00
parent abf7315aa0
commit 876f09f488
1 changed files with 39 additions and 1 deletions

View File

@ -49,7 +49,45 @@ import re
import subprocess
import sys
ROOT = pathlib.Path(__file__).resolve().parents[3]
def _find_root() -> pathlib.Path:
"""The repository root, found rather than assumed.
This was `parents[3]`, which is correct only while the script sits at
`docs/architecture/scripts/` its home in the template. The moment a
project copies it to `scripts/`, as the template's own adoption
instructions say to, `parents[3]` climbs out of the repository entirely: in
a checkout at `~/Projects/thing/scripts/`, it resolves to `~/`, and the
script reports "no docs/ directory" about a directory two levels above the
project it was run in.
So: walk up from the script looking for a directory that has both `docs/`
and `.git`, then fall back to either alone, then to git's own answer.
"""
here = pathlib.Path(__file__).resolve()
for parent in here.parents:
if (parent / "docs").is_dir() and (parent / ".git").exists():
return parent
for parent in here.parents:
if (parent / "docs").is_dir():
return parent
result = subprocess.run(
["git", "rev-parse", "--show-toplevel"],
cwd=here.parent,
capture_output=True,
text=True,
check=False,
)
if result.returncode == 0 and result.stdout.strip():
return pathlib.Path(result.stdout.strip())
return here.parents[1]
ROOT = _find_root()
DOCS = ROOT / "docs"
# The status header is a fenced block immediately after the H1, and a long value