fix(tools): three documents doc-triggers has never once fired
docs/data/README.md, docs/data/img/README.md and
docs/architecture/githooks/README.md have never fired for anything, since the
first commit. They were not reported as skipped either -- they fell into neither
list, so nothing on screen said they had not been checked.
Each carries a Governs entry that explains itself after the glob:
Governs: docs/data/** -- the assets privacyllc.dev renders for this project
Governs is split on commas only, so that is one entry and the whole string was
used as the glob. It contains a slash, so looks_like_path() called it a path and
the document was classified as path-governing -- which also kept it out of the
"govern a subject rather than paths, judge them yourself" list, the one that
exists so a reader does not conclude everything was checked. Then matches()
tested the file against a glob ending "renders for this project", which is false
and always would be.
Same class as the previous commit and the opposite sign, which makes it worse.
That one fired a document when it should not: a false prompt, costing a glance.
This one silently did not fire when it should, costing a document that goes
quietly stale while the tool reports success. GUARDS.md opens with the sentence
that applies -- a guard that cannot fail is worse than no guard, because it is
trusted.
docs/data/img/README.md governs the branding assets, which is the subject of open
issue #14. Editing them had never once prompted the document that specifies their
names, dimensions and ceilings.
The glob is now extracted from the entry: cut at the first spaced em dash, en
dash or --, then take the tokens on the left that themselves look like paths,
falling back to the entry unchanged if that yields nothing.
Three details are load-bearing:
- The cut requires whitespace both sides. A bare - would halve source-grep and
doc-claims, both of which appear in these headers.
- Tokens come from the left of the gloss, not the whole entry. privacyllc.dev in
the docs/data gloss passes looks_like_path on the extension rule and would
otherwise become a glob firing on a file nobody has.
- Classification still reads the whole entry. Deciding path-or-subject on a token
would move documents between the two lists as a side effect of this fix.
A trailing / on a glob now means the directory and everything under it.
githooks/README.md governs "the .githooks/ a project installs", which extraction
yields as a bare .githooks/, and fnmatch would not match a file inside it.
DOC_TRUST_MAP.md owns the header schema, so it now states the gloss form and that
it is the only one recognised -- a gloss in parentheses or after a colon puts a
document straight back into silence, which is the failure that was invisible here
for the life of the repository.
Verified: both docs/data documents fire on docs/data/img/icon.webp when it is
added, deleted and modified; githooks/README.md fires on
docs/architecture/githooks/pre-commit and on .githooks/pre-commit; privacyllc.dev
matches nothing; the split stays 7 path-governing and 12 subject-governing,
exactly as before. The previous commit's behaviour is unchanged -- a modified
script still fires TOOLS.md and architecture/README.md and not DOC_TRUST_MAP.md.
closes #21
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
81e326829b
commit
2577cb9ed8
|
|
@ -108,6 +108,19 @@ the first sixteen lines of the file, and one carrying `Status` without
|
|||
`Review trigger` is reported as incomplete — that combination looks finished and
|
||||
is not.
|
||||
|
||||
**Governs** is a comma-separated list, and an entry may explain itself after the
|
||||
glob with a spaced dash — `docs/data/** — the assets privacyllc.dev renders for
|
||||
this project`. `scripts/doc-triggers.py` cuts the entry there and reads the globs
|
||||
from the left of it. **Use that form and no other.** A gloss in parentheses, or
|
||||
after a colon, is not recognised: the whole entry becomes the glob, matches
|
||||
nothing, and the document is silently never fired — not reported as skipped
|
||||
either, because it still looks like a path. Three documents here sat in exactly
|
||||
that state from the first commit, `docs/data/img/README.md` among them, so
|
||||
editing the branding assets never once prompted the document that specifies their
|
||||
names and sizes. A trailing `/` on a glob means the directory and everything in
|
||||
it, which is how this file's `.githooks/` entry reaches a project's installed
|
||||
hooks.
|
||||
|
||||
**Fires on** is optional, and only for the case where `Governs:` is much broader
|
||||
than the trigger. `Governs:` says *where* a document is authoritative; the
|
||||
trigger says *which kinds of change* to that place should bring somebody back,
|
||||
|
|
|
|||
|
|
@ -155,6 +155,36 @@ def looks_like_path(glob: str) -> bool:
|
|||
return "/" in glob or "*" in glob or re.search(r"\.\w{1,5}$", glob) is not None
|
||||
|
||||
|
||||
# A `Governs:` entry is split on commas, so an entry that explains itself after
|
||||
# the glob arrives whole: `docs/data/** — the assets privacyllc.dev renders for
|
||||
# this project`. Used as a glob that matches nothing, ever, and because it
|
||||
# contains a slash `looks_like_path` calls it a path — so the document was
|
||||
# neither fired nor listed among the ones no change can fire mechanically. It was
|
||||
# simply absent, which is the one outcome a reader cannot notice.
|
||||
#
|
||||
# Three of the seven path-governing documents here were in that state from the
|
||||
# first commit, `docs/data/img/README.md` among them: editing the branding assets
|
||||
# had never once prompted the document that specifies their names and sizes.
|
||||
GLOSS = re.compile(r"\s+(?:—|–|--)\s+")
|
||||
|
||||
|
||||
def globs_in(entry: str) -> list[str]:
|
||||
"""The globs inside one `Governs:` entry, with any trailing gloss removed.
|
||||
|
||||
The cut requires whitespace on both sides of the dash: `source-grep` and
|
||||
`doc-claims` appear in these headers and a bare `-` would halve them. Tokens
|
||||
are taken from the left of the gloss rather than from the whole entry,
|
||||
because prose on the right can itself look like a path — `privacyllc.dev`
|
||||
passes the extension test and would become a glob that fires on a file
|
||||
nobody has.
|
||||
|
||||
An entry yielding no token falls back to itself, so a shape not foreseen here
|
||||
behaves exactly as it did before.
|
||||
"""
|
||||
head = GLOSS.split(entry, 1)[0]
|
||||
return [tok for tok in head.split() if looks_like_path(tok)] or [entry]
|
||||
|
||||
|
||||
def matches(path: str, glob: str) -> bool:
|
||||
"""Whether `path` is governed by `glob`.
|
||||
|
||||
|
|
@ -167,6 +197,10 @@ def matches(path: str, glob: str) -> bool:
|
|||
return False
|
||||
if glob.endswith("/**"):
|
||||
return path.startswith(glob[:-2]) or path == glob[:-3]
|
||||
if glob.endswith("/"):
|
||||
# A bare directory, as `githooks/README.md` governs ".githooks/". fnmatch
|
||||
# would not match a file inside it.
|
||||
return path.startswith(glob)
|
||||
if "/**/" in glob:
|
||||
head, tail = glob.split("/**/", 1)
|
||||
return path.startswith(head + "/") and fnmatch.fnmatch(path, "*" + tail)
|
||||
|
|
@ -320,8 +354,11 @@ def main() -> int:
|
|||
if not governs:
|
||||
continue
|
||||
|
||||
globs = [g.strip() for g in governs.split(",") if g.strip()]
|
||||
path_globs = [g for g in globs if looks_like_path(g)]
|
||||
entries = [g.strip() for g in governs.split(",") if g.strip()]
|
||||
# Classification reads the whole entry and extraction reads inside it:
|
||||
# deciding "path or subject?" on a token would move documents between the
|
||||
# two lists as a side effect of this fix.
|
||||
path_globs = [g for e in entries if looks_like_path(e) for g in globs_in(e)]
|
||||
if not path_globs:
|
||||
subject_only.append(rel)
|
||||
continue
|
||||
|
|
|
|||
Loading…
Reference in New Issue