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>
|
||
|---|---|---|
| .. | ||
| githooks | ||
| scripts | ||
| GUARDS.md | ||
| README.md | ||
README.md
Architecture
Status: Current
Owner: <who maintains this>
Last reviewed: <YYYY-MM-DD>
Governs: docs/architecture/**
Review trigger: Any new module, any change to a module boundary or a data shape
What belongs here
How the thing is built, for somebody who has to change it:
- Module boundaries — what each part owns, and what it is not allowed to know about. The boundaries are the architecture; everything else is detail.
- Data shapes — the structures that outlive a single function, especially anything persisted or sent over a wire.
- Reference manuals — the long documents that answer "how does X work" without requiring a full read of X.
- Decisions with consequences — why this database, why this concurrency model, why this dependency. Include the option that was rejected and what it would have cost, because that is the part nobody can reconstruct later.
Documents here
GUARDS.md— how to write a check that actually checks. Read it before adding a structural test or a probe; every rule in it was learned from a guard that had been green over something broken.
What ships in this folder
Working code, not just prose. Copy what a project needs and delete the rest — these are a starting point with the arguments already made, not a framework.
This table is the one copy of that list. docs/TOOLS.md is the
signpost every project is expected to have — it points here rather than
repeating it, and answers the two questions this table does not: which scripts
can stop you, and where to start in a fresh clone.
| Path | What it is |
|---|---|
scripts/release.sh |
version bump, guards, build, verify, push, prune. Refuses to build on a half-run test suite or a malformed public origin. |
scripts/verify.sh |
the repo's own checks, in one command |
scripts/check-env.sh |
which variables are set, which are missing, before anything reads them |
scripts/migrate.sh |
apply and report migrations, including the ones that run outside a transaction |
scripts/backup.sh |
a dump that is verified before it is trusted |
scripts/restore-check.sh |
the other half of backup.sh: restores the newest dump into a scratch database it creates and drops, counts the tables, and times it — the number an incident actually needs. Never accepts a target, because naming one is the mistake --clean punishes. |
scripts/healthcheck.sh |
a liveness tick with the URL written down rather than re-derived each run |
scripts/preflight.sh |
the live-URL checks: headers, TLS, and (with --auth) login rate limiting and account enumeration. Refuses any host but its configured origin — two of its checks generate failed logins and look like an attack in somebody's log. |
scripts/status.sh |
what is deployed, and whether it matches this checkout |
scripts/controls.sh |
which operational controls this project actually has, each row saying how it is known: measured, declared, n/a, or unknown. An unknown is never rendered as absent — "I could not tell" and "it is not there" send people to different places. |
scripts/dev.sh |
bring the local stack up |
scripts/scaffold.sh |
lay out a new project in this shape |
scripts/doc-triggers.py |
which documents a pending change fires, read from their Governs: headers and narrowed by the optional Fires on: — the kinds of change (added, deleted, moved, changed) a document's trigger actually names, so one governing docs/** for existence changes alone does not fire on every edit. The Review trigger on each document names the change that should send somebody back to it; this is the check that asks before the commit rather than after |
scripts/prove-guard.sh |
breaks the thing a guard protects, requires the guard to go red, restores the file from a trap. GUARDS.md §1 written out as a command, including the count — one failing test reported on six lines is not six failures |
scripts/commit-mine.sh |
commits only the paths you name, by pathspec, after the secret scan. For a tree something else is also writing: what anyone else has staged is reported and left exactly as it was |
scripts/doc-claims.sh |
every file a document names must exist, and (--covers) every file that exists is named — the second is the one that catches a list missing rows |
scripts/duplication.py |
code that exists twice, tuned so what it reports is worth reading |
scripts/dead-code.py |
exports nothing imports, and assets nothing renders |
scripts/secrets.sh |
credential shapes in a staged diff, using the project's own patterns where it has them |
scripts/audit-gate.mjs |
high/critical advisories in production dependencies, with the allowlist npm does not have. An entry must say why the advisory cannot reach this app, what would make it reachable, and what retires the entry — three fields, so a waiver stays falsifiable. Exits 2 when nothing was checked. |
scripts/forgejo-issue.py |
file and close issues in the tracker convention, with every rule of it as a check |
scripts/deploy.py |
update the running stack to a published image. Publishing and deploying are separate; this is the second one. The only copy — it existed twice and drifted (#209); the privacyllc-deploy skill's is now a symlink to this file. Identity-free by design: it reads DEPLOY_IMAGE, DEPLOY_STACK_ID, DEPLOY_CONTAINER and DEPLOY_SITE_URL from the environment and refuses to run without them, so each project supplies its own via a wrapper. Never hard-code one here — least of all the site URL, which is frozen into the image at build time. |
scripts/release-notes.mjs |
tags the release and writes its notes, grouped by the commit types the message hook already enforces. Runs after release.sh has published, so a failure here cannot cost an image. Scrubs credential shapes out of commit subjects first — the body goes to a public repository. |
githooks/ |
pre-commit, commit-msg, post-commit — see its README for the one install command |
Every script takes its configuration from the environment and hard-codes nothing
about any particular deployment. check-env.sh is the one to run first.
What does not belong here
- Product intent — that is
docs/planning/PROJECT_PLAN.md - What it should feel like — that is
docs/design/ - What happened while building it — that is a history log, not architecture
A note on drift
Architecture docs go stale faster than any other kind, because code changes under them silently. This is exactly what the Review trigger line is for: name the change that should send somebody back here, and a reader can tell whether the trigger has fired.