chore(repo): put the template under version control
The basis for every project here was itself unversioned: no .git, no remote,
no history. Changes to it had no diff and no revert, and two of its own guards
could not run at all -- doc-claims.sh and doc-triggers.py both read git
history, so the script written to catch documentation drift could not be run
against the documents that define drift.
This is the tree as it stands, including work that until now existed only as
loose files on disk: WORK_CYCLE.md, TOOLS.md, the Portainer image-line fix in
deploy.py, the status vocabulary corrected to the four words the conformance
checker actually enforces, the Exempt: mechanism documented, and the Forgejo
instance named in README.md.
secrets.sh --tracked reports one candidate, migrate.sh:480. It is the comment
documenting the three Postgres credential shapes that script redacts, with
literal placeholders, and it is left alone deliberately: GUARDS.md section 2
is that a source-grep guard must tell code from the comment about code, and
deleting an explanation to quiet a scanner is the failure it names.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 22:44:26 -05:00
|
|
|
# Tools — where the scripts are, and which ones can stop you
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
Status: Current
|
|
|
|
|
Owner: <who maintains this>
|
|
|
|
|
Last reviewed: <YYYY-MM-DD>
|
|
|
|
|
Governs: docs/architecture/scripts/**, docs/architecture/githooks/**
|
|
|
|
|
Review trigger: Any script added to, removed from or repurposed in
|
|
|
|
|
docs/architecture/scripts/; any change to which of them gates
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
> A signpost, deliberately. Every project that adopts this template has a
|
|
|
|
|
> `docs/TOOLS.md`, so "read `docs/TOOLS.md` first" is an instruction that works
|
|
|
|
|
> without knowing anything about the project — which is the whole reason this
|
|
|
|
|
> file exists at a fixed path.
|
|
|
|
|
|
|
|
|
|
## The list is not here
|
|
|
|
|
|
|
|
|
|
**[`architecture/README.md`](architecture/README.md)** holds the table of what
|
|
|
|
|
ships and what each script is. That is the one copy.
|
|
|
|
|
|
|
|
|
|
A second table here would be two records of one fact, and the other one would
|
|
|
|
|
never hear that a script was renamed — the failure `DOC_TRUST_MAP.md` exists to
|
|
|
|
|
prevent, applied to the tooling instead of the documents. So this file answers
|
|
|
|
|
the questions that table does not, and points at it for everything else.
|
|
|
|
|
|
|
|
|
|
## If this project has fewer scripts than that table lists
|
|
|
|
|
|
|
|
|
|
That is the intended state, not a broken copy. `scaffold.sh` writes the
|
|
|
|
|
documents and deliberately leaves the scripts behind — *"an unconfigured
|
|
|
|
|
`release.sh` landing in every new repository is a loaded gun, not a head
|
|
|
|
|
start"* — so they are taken from the template one at a time, having been read.
|
|
|
|
|
|
|
|
|
|
Which makes the table a menu rather than an inventory here: it says what exists
|
|
|
|
|
to be copied. Once a script is in this project, it is this project's, and its
|
|
|
|
|
row in `architecture/README.md` should say what it does **here** if that has
|
|
|
|
|
drifted from the template's version.
|
|
|
|
|
|
|
|
|
|
## Which ones can stop you
|
|
|
|
|
|
|
|
|
|
Not in a table, because the honest answer lives in each script's own header and
|
|
|
|
|
would go stale here. The rule that matters:
|
|
|
|
|
|
|
|
|
|
**Exit code `2` is never a pass.** These scripts distinguish "the check ran and
|
|
|
|
|
found nothing" from "the check did not run", because those look identical from
|
|
|
|
|
the outside and only one of them is evidence. A CI step or a hook that treats a
|
|
|
|
|
`2` as success has quietly turned the check off. Each script states its codes at
|
|
|
|
|
the top; read them there.
|
|
|
|
|
|
|
|
|
|
The hooks are the other place work gets stopped:
|
|
|
|
|
[`architecture/githooks/`](architecture/githooks/README.md) has the one install
|
|
|
|
|
command and the table of what each hook runs.
|
|
|
|
|
|
feat(guards): gate the claim of being finished, not only the artifact
Every guard in this template fires on an artifact -- `githooks/pre-commit` on a
commit, `audit-gate.mjs` and `preflight.sh` on a release, `verify.sh` when
somebody runs it. `GUARDS.md` §6 is the rule they follow: guards belong before
the artifact exists.
An agent that says "done" and does not commit produces no artifact and trips
none of them. That gap already has two rules pointing at it -- `WORK_CYCLE.md`
opens with *"Done" is not a close*, and the tracker convention refuses a close
under fifteen characters of evidence -- and a rule is a thing a reader can skip.
This is the same rule with an exit code.
`scripts/verify-before-done.sh` is a Claude Code `TaskCompleted` hook. Verified
against Claude Code 2.1.251: the event exists and the binary carries the string
`TaskCompleted hook prevented continuation`.
WIRED TO `verify.sh`, NOT TO THE TEST COMMAND. `npm test` in a repo with no
tests exits 0, so a gate built on it reports green having verified nothing --
`GUARDS.md` §4 and §8's whole subject. `verify.sh` already distinguishes the
three answers, and this treats exit 2 (nothing was verified) as a failure with
its own message rather than a pass. A repository with no checks yet fails this
gate loudly, which is the intended answer.
IT EXITS 2 TO BLOCK, AND THAT IS NOT THE USUAL CONVENTION. Claude Code reads a
`1` from a hook as "the hook broke" and continues, so a gate written the
ordinary way fails OPEN and lets through exactly what it was installed to catch,
looking identical from the outside. `docs/TOOLS.md` now says this beside the
exit-2-is-never-a-pass rule it inverts.
THE LIMITATION IS IN THE HEADER IN CAPITALS, because it decides whether this is
worth installing: it gates Claude Code and nothing else. A Codex session or a
human in the same checkout writes past it. It is a second layer and never the
layer -- `.githooks/pre-commit` is what git runs whoever is driving, and if a
project's real suite is not wired into that hook, wiring it there is worth more
than installing this.
Proved rather than assumed, all four outcomes, per §1: no `verify.sh` -> 2 (fails
closed); pass -> 0 with empty stderr; fail -> 2 naming the code; verified-nothing
-> 2 with the distinct message.
Docs in the same commit, per the triggers `doc-triggers.py` reported:
`architecture/README.md` gains the row its own "Adding one" rule requires,
`TOOLS.md` gains the paragraph under "Which ones can stop you", and
`DOC_TRUST_MAP.md` gains the question it now answers. `doc-claims.sh` re-run:
51 claimed paths across the three, all present.
Prompted by an XDA piece on Claude Code shipping unverified work. The idea is
theirs; the exit-2 contract, the `verify.sh` wiring and the Codex caveat are
what it needed to be true here.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-29 08:42:00 -05:00
|
|
|
**And a third place, which stops something the other two cannot: the claim.**
|
|
|
|
|
`scripts/verify-before-done.sh` is a Claude Code `TaskCompleted` hook that runs
|
|
|
|
|
`verify.sh` and refuses to let a task be reported as finished when it fails.
|
|
|
|
|
Every other gate here fires on an **artifact** — a commit, a build, a tag — so
|
|
|
|
|
an agent that says "done" without committing trips none of them, which is the
|
|
|
|
|
gap `WORK_CYCLE.md` is describing when it says *"Done" is not a close*.
|
|
|
|
|
|
|
|
|
|
Two things to know before installing it, both in the script's header at length:
|
|
|
|
|
|
|
|
|
|
- **It exits `2` to block, and `1` fails open.** Claude Code reads a `1` from a
|
|
|
|
|
hook as "the hook broke" and continues, so a gate written the ordinary way
|
|
|
|
|
lets through precisely what it was installed to catch, and looks identical
|
|
|
|
|
doing it. This is the exit-code rule above with the numbers swapped, and it is
|
|
|
|
|
the one place in this template where that is true.
|
|
|
|
|
- **It gates Claude Code and nothing else.** A Codex session, a human, or any
|
|
|
|
|
other agent in the same checkout writes past it without knowing it is there.
|
|
|
|
|
It is a second layer; `.githooks/pre-commit` is the layer, because git runs
|
|
|
|
|
that whoever is driving. If this project's real suite is not wired into that
|
|
|
|
|
hook, wiring it there is worth more than installing this.
|
|
|
|
|
|
chore(repo): put the template under version control
The basis for every project here was itself unversioned: no .git, no remote,
no history. Changes to it had no diff and no revert, and two of its own guards
could not run at all -- doc-claims.sh and doc-triggers.py both read git
history, so the script written to catch documentation drift could not be run
against the documents that define drift.
This is the tree as it stands, including work that until now existed only as
loose files on disk: WORK_CYCLE.md, TOOLS.md, the Portainer image-line fix in
deploy.py, the status vocabulary corrected to the four words the conformance
checker actually enforces, the Exempt: mechanism documented, and the Forgejo
instance named in README.md.
secrets.sh --tracked reports one candidate, migrate.sh:480. It is the comment
documenting the three Postgres credential shapes that script redacts, with
literal placeholders, and it is left alone deliberately: GUARDS.md section 2
is that a source-grep guard must tell code from the comment about code, and
deleting an explanation to quiet a scanner is the failure it names.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 22:44:26 -05:00
|
|
|
## Where to start in a fresh clone
|
|
|
|
|
|
|
|
|
|
1. `bash docs/architecture/scripts/check-env.sh` — what is configured and what
|
|
|
|
|
is missing, before anything reads it.
|
|
|
|
|
2. `bash docs/architecture/scripts/secrets.sh --tracked` — the one-time audit of
|
|
|
|
|
what is already committed. The staged-diff mode is for the hook; this mode is
|
|
|
|
|
for the day you adopt the template.
|
|
|
|
|
3. [`architecture/GUARDS.md`](architecture/GUARDS.md) — how to write a check that
|
|
|
|
|
can actually fail, before you write one. `scripts/prove-guard.sh` performs its
|
|
|
|
|
first rule.
|
|
|
|
|
|
|
|
|
|
## Adding one
|
|
|
|
|
|
fix(docs): three documents named the template's layout, not the project's
The remaining half of #16 was not a decision. It was three documents being wrong
about their own audience.
A freshly scaffolded project failed doc-claims on four PATH claims:
docs/architecture/scripts (twice), docs/architecture/githooks, and
docs/architecture/scripts/release.sh -- named by DOC_TRUST_MAP.md, TOOLS.md and
WORK_CYCLE.md. I had modelled that as a tension between documents that were
correct and a scaffold that declined to create what they named, and filed it
needing a call from Kaspa between three unattractive options.
The evidence says otherwise. Every script's own header reads "Copy to
`scripts/<name>`", the hooks install to `.githooks/`, and FIVE documents already
use that project-relative form -- OPERATIONS.md, architecture/README.md,
GUARDS.md and parts of TOOLS.md and DOC_TRUST_MAP.md. Only three used
`docs/architecture/...`, which is where the scripts live in THIS repository and
nowhere a project that adopts them will ever look.
So the documents now name the layout their reader will actually have. No tooling
change, no empty directories, and the claims get more accurate rather than
vaguer -- the opposite of the direction I was leaning.
Verified both ways, since a fix that only works in one tree is what produced the
bug: the template stays green at 112 claims, and a freshly scaffolded project
committed and checked exits 0 for the first time, with the bare-filename notes
from f5fd67b reported as information rather than failure.
Worth recording why this was invisible from inside: every path in question
resolves here. The documents were only wrong from a vantage point this
repository does not have, which is why scaffolding into a scratch directory
found it and reading it here never would.
closes #16
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 00:16:48 -05:00
|
|
|
Put it in `scripts/`, give it a header saying what it does and
|
chore(repo): put the template under version control
The basis for every project here was itself unversioned: no .git, no remote,
no history. Changes to it had no diff and no revert, and two of its own guards
could not run at all -- doc-claims.sh and doc-triggers.py both read git
history, so the script written to catch documentation drift could not be run
against the documents that define drift.
This is the tree as it stands, including work that until now existed only as
loose files on disk: WORK_CYCLE.md, TOOLS.md, the Portainer image-line fix in
deploy.py, the status vocabulary corrected to the four words the conformance
checker actually enforces, the Exempt: mechanism documented, and the Forgejo
instance named in README.md.
secrets.sh --tracked reports one candidate, migrate.sh:480. It is the comment
documenting the three Postgres credential shapes that script redacts, with
literal placeholders, and it is left alone deliberately: GUARDS.md section 2
is that a source-grep guard must tell code from the comment about code, and
deleting an explanation to quiet a scanner is the failure it names.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 22:44:26 -05:00
|
|
|
**which incident motivated it**, state its exit codes, and add a row to
|
|
|
|
|
`architecture/README.md`'s table — this file's `Review trigger` fires on exactly
|
|
|
|
|
that.
|
|
|
|
|
|
|
|
|
|
The bar, from the scripts that are already here: **done by hand three times, or
|
|
|
|
|
once with a consequence.** A script written before either of those has no failure
|
|
|
|
|
to describe in its header, which is the part that stops the next person deleting
|
|
|
|
|
it.
|