WORK_CYCLE.md covered only the end of the cycle. A cycle has two ends, and
the same six questions kept having to be asked out loud on every piece of
work. They are now mandatory in every plan, each justified by something this
household has actually paid for:
unified code eight copies of secrets.sh once existed here and five of
seven could not detect the most common secret shape --
INCLUDING THE TEMPLATE, so every project scaffolded from
it inherited a blind scanner
error handling the recurring fault is the silent pass, not the crash
logging an append-only log is unbounded by construction
blind spots named ones get fixed
landmine fixes the trap found while passing is cheapest to fix while
passing
hardcode little derive it, or justify the constant
GUARDS.md was five sections behind the project that has been learning; 11,
12 and 13 are backported, genericised to match the template's style. 13 is
the narrow form of the sixth rule, and WORK_CYCLE now cites it -- so
backporting it is what makes that citation true rather than a broken
reference.
Also adds the three files an agent reads BEFORE it reads docs/:
CLAUDE.md short, and it POINTS at DOC_TRUST_MAP rather than
repeating it -- a second copy of the map is the
failure that map exists to prevent. Carries the
exit-code table, the commit gates, and two standing
instructions: flag what looks wrong even when it is
not what you were asked about, and never print a
credential -- not from a file, not from a command's
output, not from a config subtree "with the secrets
filtered out", because that filter has failed before
by matching key NAMES while the secret sat inside an
object whose name was innocent
.claudeignore excludes artifacts and NEVER docs/. The Command
Center reads this repository's documents at a commit;
a generic ignore file that sweeps "documentation" or
"data" starves both the agent and the reconcile, and
everything still runs, just blind
.claude/settings.json deny rules in the double-slash absolute form. A
tilde-style rule looks right in review and silently
matches nothing. It closes the Read TOOL only -- a
shell reads a file a hundred ways -- so it catches
the accidental read, not the determined one
scaffold.sh gains a ROOT array for the three, kept apart from DOCS so the
H1-plus-status-block check stays meaningful rather than being loosened into
a warning that is always wrong (GUARDS.md 5).
Verified: scaffold --dry-run into a scratch repo creates 22 files including
all three, with no HEADERLESS warning; doc-claims passes with 124 claimed
paths, all present.
Does not touch docs/architecture/scripts/secrets.sh, which carries someone
else's uncommitted improvement.
|
||
|---|---|---|
| .. | ||
| 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 the Governs: headers of everything under docs/ and of the documents at the repository root, 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/verify-before-done.sh |
a Claude Code TaskCompleted hook: runs verify.sh and blocks the claim of being finished when it fails. One step earlier than GUARDS.md §6 — every other guard here fires on an artifact, and an agent that says "done" without committing produces none. Wired to verify.sh rather than the test command because npm test on a repo with no tests exits 0, and exit 2 (verified nothing) is treated as a failure, not a pass. Exits 2 to block: a hook exiting 1 fails open. Gates Claude Code only — a Codex session in the same checkout writes past it, so .githooks/pre-commit remains the gate that catches every writer |
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.