Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .claude/rules/knowledge-base.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,26 @@ valid, not correct.
2. **Describe the system, not the people.** Knowledge concentration is a property of the code.
3. **Keep negative results and contradictions.**

## The estate: what a program answers, a page does not

- **A value the estate can compute is a check, not a sentence.** A deployed version, a certificate
expiry, a restart count, a quota, a licence date: write the probe into
`.claude/tools/status.project.sh` and cite the check. A date no system can answer (a vendor's
billing change, a renewal set by hand) goes into `.claude/deadlines.conf` with its lead time and
its owner. `status.sh` runs both at every `/orient`. Measured 2026-08-24 on one installation:
five of seven such sentences were stale or wrong within weeks of being written.
- **A runbook states its capabilities, not its author.** The header field `requires:` names tokens
from `00-orientation/capabilities.md`, one table row per token: a blast marker (🟢 read-only or
non-production, 🟡 changes non-production or reads production data, 🔴 touches production), what
it grants, a read-only proof command, the expected output, and how to obtain it. `preflight.sh -c
<tokens>` proves them before the page is followed; `knowledge-check.sh` refuses a token the page
does not define. Where nobody has written how a capability is obtained, the row says UNRESOLVED
and names who to ask; an invented procedure is worse than an admitted gap.
- **Probes live beside the pages.** `_probes/` holds runnable, read-only scripts that re-measure a
claim; each opens with a `Requires:` line and ends with the traps that cost a run, and
`_probes/README.md` says what each one answers. An investigation cites the probe that produced its
numbers, so the number can be produced again.

## `99-pending.md`

One line, same turn, without asking. Grouped by who can act: only the project team · needs a
Expand Down
8 changes: 7 additions & 1 deletion .claude/rules/standing-orders.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ Every piece of work gets a **tier**, stated in one line the owner can override:

**The hard floor:** auth, authorization, payments, secrets, data migration, a public API or
contract, a security control, cross-module architecture. Any of these is Tier 3 and cannot be
tiered down. Round up when unsure.
tiered down. Round up when unsure. A task whose runbook names, in its `requires:` field, a capability
marked as touching production is Tier 3 by that alone.

After the yes, **build uninterrupted.** Pause only for the stop-list: a Tier-3 area touched
unexpectedly · a new dependency · a schema or migration change · data deletion · anything reaching
Expand Down Expand Up @@ -80,6 +81,11 @@ One more thing is mechanical, and it is a check rather than a hook: `verify.sh`
base whose durable pages lack the required header, whose checked references do not resolve, or whose
README states a count the tree contradicts. It decides structure, never whether a claim is true.

Two more things are checks rather than hooks. `status.sh` computes what the estate is doing right
now from the project's own read-only probes and its declared deadlines, and exits red on anything
RED; `/orient` runs it first and leads with those lines. `preflight.sh` proves, read-only, the
capabilities a page requires; `/work` runs it before the one yes. `verify.sh` proves both can go red.

## Where things live

| Path | What it is |
Expand Down
26 changes: 26 additions & 0 deletions .claude/rules/working-here.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,29 @@ search for the same root cause. Same cause with a different symptom means merge,
a file under `docs/knowledge-base/` left `knowledge-base.md` out of context for a whole turn; the
`Read` tool on the same file brought it in at once. A session that prefers the shell writes
knowledge-base pages without the rule that says how. Open one page with `Read` first. 2026-09-05.

## Reasoning

The traps above are about shells. These are about judgement, and they cost more. All five were
measured on production questions on one installation.

- **The size of a mechanism reported as its effect.** A migration said to "revoke entitlements from
353 subscriptions" read a collection holding zero documents; "86 of 88 messages dropped" counted
52 correct skips and the system's own echoes; a dead-token alarm fired on channels where 1,083
sends had just succeeded. Count the thing the customer has, not the thing the code touches; trace
one item end to end before trusting an aggregate; check whose. 2026-08-02 to 2026-08-08.
- **A green test on an unreachable path.** A fix recorded as proven by an offline replay (12 of 77
cases to 44 of 77) had fired zero times in production: the replay supplied an input the running
system never produces. Ask of any passing suite which line would fail if the claim were false,
and after a deploy look for the success line the fix emits, not the failure line. 2026-08-24.
- **The dangerous zero.** Three zeros in one day meant "my question could not see it": a query that
had never run, a grep for a guard an upstream check made unreachable, and a health query reporting
zero healthy targets on a system that was visibly serving. Before believing a zero, confirm the
same query sees something you are certain exists. A zero that fits your hypothesis deserves more
scrutiny than one that contradicts it. 2026-08-24.
- **An instrument that measures itself.** A counter built to measure a behaviour scanned the very
text it was measuring and always agreed with itself. Validate a new instrument against a case
whose answer you already know before citing it. 2026-08-24.
- **Log volume read as usage.** A service with 26 log lines a day was written down as idle; it
carried all console traffic and logged at Warning. A quiet log is a mechanism; find the callers in
configuration and client code, which is the effect. 2026-09-20.
26 changes: 17 additions & 9 deletions .claude/rules/writing.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ reading effort, so simplify that part, never the terms.

- **Lead with the bottom line.** One or two sentences that answer the question, then why it
matters, then how it works.
- **One idea per sentence.** "Create the API endpoint", not "the implementation of the API endpoint
should be undertaken in order to facilitate the required functionality".
- **One idea per sentence, and most sentences under twenty words.** "Create the API endpoint", not
"the implementation of the API endpoint should be undertaken in order to facilitate the required
functionality". A sentence carrying two "which" clauses splits into two sentences.
- **Plain words first.** A technical term is fine when it is the precise one. Define it in the same
breath the first time it appears: "the drafter, which is the part that writes the database query".
- **Name the real moving parts** and how they connect. An analogy supplements the mechanism; it
Expand All @@ -38,16 +39,17 @@ reading effort, so simplify that part, never the terms.
- **Absolute dates.** 2026-08-27, not "last Tuesday". When a time is needed, write it in UTC.
- **One name per concept.** The project rule lists the project's terms; do not alternate. Where
code and docs disagree, use the code's name and mention the other name once.
- **Numbers:** digits, not words; a unit on every quantity; "about" on every estimate; a base beside
every percentage ("5 of 7", not "71%"); and the date beside any number that can change, in chat
as much as in a page.
- **When the words are the record, quote them.** A decision, a waiver, or an instruction is written
verbatim, in quotation marks, with its date. A paraphrase of a decision is a different decision.

## Questions

Three things always get asked, and none of them are politeness: an instruction with two readings
(ask before acting, in its own message); a decision that is not yours to make, or whose options
have materially different consequences the project cannot settle; anything irreversible, or any
security, data-loss, or money decision. Everything else you work out yourself from the code, the
configuration, the knowledge base, and previous decisions. A question the tools could have answered
wastes a whole turn. When a question is necessary, make it decidable in one reply: one question, the
options named, a recommendation stated.
When to ask is settled in the standing orders (the three habits, the one stop, the stop-list); this
file only says how. A question the tools could have answered wastes a whole turn. When a question is
necessary, make it decidable in one reply: one question, the options named, a recommendation stated.

## Shape

Expand All @@ -67,6 +69,12 @@ options named, a recommendation stated.
items, tables for comparisons, numbered steps for a procedure. One purpose per section.
- **Recommend one option.** "Recommended: X, because" beats a menu of five. Name an alternative only
when it is genuinely close.
- **Bold one phrase per paragraph, or none:** the thing the reader must not miss. When everything is
bold, nothing is.
- **Markers are a fixed set:** the words RED, AMBER, OK and SKIP, and the blast markers 🟢 🟡 🔴 with
the meanings the knowledge-base rule gives them. No decorative emoji, no ticks, no rockets.
- **Never paste more than about ten lines of output.** Quote the one line that matters, say what it
means, and say where the rest is.
- **When the answer includes code:** use the architecture and conventions already in the project,
verify what the code actually does before assuming anything, keep examples focused, and write no
boilerplate nobody asked for.
Expand Down
19 changes: 13 additions & 6 deletions .claude/skills/orient/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,19 @@ when the ref was last fetched. In a workspace, say which branch each clone is on
branch is often not the trunk. Uncommitted work means somebody was mid-task. Say what is
uncommitted and leave it.

## 4 · What is actually deployed, when the project rule names a way to check
## 4 · What the estate is doing, computed

Git does not know what is on the server. When the project rule names a deployed-state check (a
health endpoint that reports the running commit, a deploy tag, a file on the host), run it and
compare: `git log --oneline <deployed-commit>..origin/<trunk>` is what is not yet live. When there
is no such check, say so rather than guessing.
```bash
bash .claude/tools/status.sh
```

It runs the project's own read-only probes (`.claude/tools/status.project.sh`) and the declared
deadlines (`.claude/deadlines.conf`), and exits 1 on anything RED. **Lead the report with the RED
lines, each with its remedy:** a RED that stays inside the session is a RED nobody sees. Git does
not know what is on the server; when a probe reports the deployed commit, `git log --oneline
<deployed-commit>..origin/<trunk>` is what is not yet live. When the project has no probes yet the
tool says so, and you say that rather than guessing. If a value you are about to state could be
read from a system, it belongs in `status.project.sh`, not in the report.

## 5 · Does it still verify

Expand All @@ -74,7 +81,7 @@ Five lines, no headings:
1. which branch, per clone in a workspace, and whether it is ahead of its upstream
2. where the last session got to, with its date
3. what is uncommitted or unfinished
4. what verify reported
4. what status and verify reported, RED first
5. **the one thing most worth doing next**, named. Not a list of five.

Then stop and let the human choose.
Expand Down
3 changes: 3 additions & 0 deletions .claude/skills/work/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ A bottom line of what you are about to do, then the details, in the house writin
- **the branch sentence:** which repository, cut from which branch after a fetch, following the
project rule's branch model; which branch the pull request targets. When the repository does not
follow the model, put the evidence in front of them and ask one question with the options named
- **the capabilities it needs, proven:** when a page the task follows names tokens in its `requires:`
field, `bash .claude/tools/preflight.sh -c <tokens>` has run before this message, and any FAIL or
UNRESOLVED is named here; a token marked as touching production makes the task Tier 3
- the tier line, and the route: Codex design review first, or go, with the default stated. Tier 3
review, Tier 2 offer it, Tier 1 go

Expand Down
127 changes: 127 additions & 0 deletions .claude/tools/estate.test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#!/usr/bin/env bash
# Runs status.sh and preflight.sh against fixture trees and asserts the exit code and the message.
# This is how the two estate tools prove they can go red: a gate that has never failed is not known
# to gate, and a failure with the wrong message is not actionable.
#
# bash .claude/tools/estate.test.sh
#
# Every case builds a real tree and runs the real tool with --root. Nothing here greps the tools'
# source, because a test that reads the script instead of running it passes over a broken script.

unset MSYS_NO_PATHCONV
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ST="$HERE/status.sh"; PF="$HERE/preflight.sh"
[ -f "$ST" ] && [ -f "$PF" ] || { echo "status.sh or preflight.sh not found beside this suite"; exit 1; }
T="$(mktemp -d 2>/dev/null || mktemp -d -t estatetest)"
trap 'rm -rf "$T"' EXIT

fails=0; total=0; got=""
check() { # name expected-exit needle
total=$((total+1))
if [ "$got" != "$2" ]; then
printf ' FAIL %-58s expected exit %s got %s\n' "$1" "$2" "$got"; fails=$((fails+1))
sed 's/^/ /' "$T/out" | head -8; return
fi
if [ -n "$3" ] && ! grep -qF -e "$3" "$T/out"; then
printf ' FAIL %-58s exit %s, no message matching: %s\n' "$1" "$got" "$3"; fails=$((fails+1))
sed 's/^/ /' "$T/out" | head -8; return
fi
printf ' ok %-58s exit %s\n' "$1" "$got"
}
plus_days() { # n -> yyyy-mm-dd, GNU date first, BSD second
date -d "$1 days" +%Y-%m-%d 2>/dev/null || date -v"+$1d" +%Y-%m-%d
}
R="$T/p"
mk() { rm -rf "$R"; mkdir -p "$R/.claude/tools" "$R/docs/knowledge-base/00-orientation"; }
run_st() { got="$(bash "$ST" --root "$R" "$@" >"$T/out" 2>&1; echo $?)"; }
run_pf() { got="$(bash "$PF" --root "$R" "$@" >"$T/out" 2>&1; echo $?)"; }

echo "status.sh: nothing to check"
mk; run_st; check "no probes and no deadlines is not a failure" 0 "no status.project.sh"
mk; run_st; check "it says the estate answers nothing yet" 0 "not checked."

echo "status.sh: declared deadlines"
mk; printf '%s 30 owner :: a thing that is overdue :: some-page.md\n' "$(plus_days -3)" > "$R/.claude/deadlines.conf"
run_st; check "an overdue date is RED and the run fails" 1 "overdue by 3 d"
mk; printf '%s 30 owner :: within the warning window :: some-page.md\n' "$(plus_days 20)" > "$R/.claude/deadlines.conf"
run_st; check "a date inside warn_days is AMBER, run passes" 0 "-- AMBER (1) --"
mk; printf '%s 30 owner :: a red-zone date :: some-page.md\n' "$(plus_days 5)" > "$R/.claude/deadlines.conf"
run_st; check "a date inside a quarter of warn_days is RED" 1 "-- RED (1) --"
mk; printf '%s 30 owner :: far away :: some-page.md\n' "$(plus_days 200)" > "$R/.claude/deadlines.conf"
run_st; check "a far date is OK" 0 "-- OK (1) --"
mk; printf '%s 400 owner :: long runway, still near :: p.md\n' "$(plus_days 25)" > "$R/.claude/deadlines.conf"
run_st; check "the RED zone is capped at 21 days, so 25 days out is AMBER" 0 "-- AMBER (1) --"
mk; printf 'not-a-date 30 owner :: bad line :: p.md\n' > "$R/.claude/deadlines.conf"
run_st; check "a bad date is a RED about the file, not a silent skip" 1 "date is not yyyy-mm-dd"
mk; printf '%s 30 owner no separators at all\n' "$(plus_days 9)" > "$R/.claude/deadlines.conf"
run_st; check "a line without :: is a RED about the file" 1 "no '::' separator"
mk; printf '# only a comment\n\n' > "$R/.claude/deadlines.conf"
run_st; check "a file with no deadline lines is reported, not passed silently" 0 "has no deadline lines"

echo "status.sh: the project's probes"
mk; printf '#!/usr/bin/env bash\nprintf "OK\\tpods\\tprod\\tall 12 running\\t\\n"\n' > "$R/.claude/tools/status.project.sh"
run_st; check "an OK finding passes and is shown" 0 "all 12 running"
mk; printf '#!/usr/bin/env bash\nprintf "RED\\tcerts\\t*.example.test\\texpires in 3 d\\trenew: run the cert job\\n"\n' > "$R/.claude/tools/status.project.sh"
run_st; check "a RED finding fails the run and prints its remedy" 1 "> renew: run the cert job"
mk; printf '#!/usr/bin/env bash\nprintf "RED\\tcerts\\tx\\tno remedy here\\t\\n"\n' > "$R/.claude/tools/status.project.sh"
run_st; check "a RED without a remedy is itself reported" 1 "RED finding without a remedy"
mk; printf '#!/usr/bin/env bash\necho "this is not a finding"\n' > "$R/.claude/tools/status.project.sh"
run_st; check "a malformed line is a RED, never dropped" 1 "malformed finding"
mk; printf '#!/usr/bin/env bash\nprintf "PURPLE\\ta\\tb\\tc\\td\\n"\n' > "$R/.claude/tools/status.project.sh"
run_st; check "an unknown level is malformed" 1 "malformed finding"
mk; printf '#!/usr/bin/env bash\nexit 3\n' > "$R/.claude/tools/status.project.sh"
run_st; check "a probe script that exits non-zero is RED" 1 "exited 3"
mk; printf '#!/usr/bin/env bash\n:\n' > "$R/.claude/tools/status.project.sh"
run_st; check "a probe that prints nothing is reported, not passed" 0 "printed no findings"
mk; printf '#!/usr/bin/env bash\nprintf "SKIP\\tlicence\\tpilot\\tno kubectl context\\t\\n"\n' > "$R/.claude/tools/status.project.sh"
run_st; check "SKIP is allowed without a remedy" 0 "no kubectl context"

echo "status.sh: --check and --canary"
mk; printf '#!/usr/bin/env bash\nif then fi\n' > "$R/.claude/tools/status.project.sh"
run_st --check; check "--check catches a probe script that does not parse" 1 "does not parse"
mk; printf '#!/usr/bin/env bash\nprintf "RED\\ta\\tb\\tc\\td\\n"\n' > "$R/.claude/tools/status.project.sh"
run_st --check; check "--check runs no probe, so a RED probe does not fire" 0 "parses (not run)"
mk; run_st --canary; check "--canary fails and says both injections were seen" 1 "a synthetic RED and a malformed line were both reported"

echo "preflight.sh"
cap="$R/docs/knowledge-base/00-orientation/capabilities.md"
mkcap() {
mk
cat > "$cap" <<'EOF'
---
title: fixture capabilities
status: verified
as_of: 2026-09-22
last_verified: 2026-09-22
verification_method: estate.test.sh
scope: fixture
confidence: High. fixture
known_gaps: none
reverify_when: never
---

| Token | Blast | Grants | Proof (read-only) | Expected | Obtain |
|---|---|---|---|---|---|
| `TOOL-TRUE` | 🟢 | a command that exits 0 | `true` | | install nothing |
| `TOOL-FALSE` | 🟢 | a command that exits 1 | `false` | | ask nobody |
| `TOOL-MATCH` | 🟢 | output matches | `echo hello world` | ^hello | none |
| `TOOL-NOMATCH` | 🟡 | output does not match | `echo hello world` | ^goodbye | the docs |
| `TOOL-PIPE` | 🟢 | a pipe inside the proof | `printf 'a\nb\n' \| wc -l` | 2 | none |
| `GAP-ONE` | 🔴 | nobody knows how | `false` | UNRESOLVED | the platform owner |
EOF
}
mkcap; run_pf --list; check "--list runs nothing and exits 0" 0 "TOOL-FALSE"
mkcap; run_pf -c TOOL-TRUE; check "a passing proof" 0 "PASS TOOL-TRUE"
mkcap; run_pf -c TOOL-FALSE; check "a failing proof fails the run" 1 "FAIL TOOL-FALSE"
mkcap; run_pf -c TOOL-MATCH; check "expected regex matched" 0 "PASS TOOL-MATCH"
mkcap; run_pf -c TOOL-NOMATCH; check "expected regex not matched fails" 1 "output did not match"
mkcap; run_pf -c TOOL-PIPE; check "an escaped pipe inside the proof works" 0 "PASS TOOL-PIPE"
mkcap; run_pf -c GAP-ONE; check "UNRESOLVED is reported and does not fail" 0 "UNRESOLVED"
mkcap; run_pf -c NOPE-X; check "a token the page does not define fails" 1 "not defined"
mkcap; run_pf -c TOOL-TRUE,TOOL-FALSE; check "one failing token fails the set" 1 "1 failed"
mk; run_pf; check "no capabilities page is exit 2, not a pass" 2 "no capabilities page"
mkcap; printf '# nothing here\n' > "$cap"; run_pf; check "a page with no token rows is exit 2" 2 "no token rows"

echo
if [ "$fails" -eq 0 ]; then echo " $total cases, 0 failed"; else echo " $total cases, $fails FAILED"; fi
[ "$fails" -eq 0 ]
Loading
Loading