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
29 changes: 29 additions & 0 deletions .claude/rules/cron-launch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
paths:
- "review-run.sh"
- "campaign-run.sh"
- "*-mcp.json"
- "*-settings.json"
- "hooks/**"
---

# How the crons are launched

- **The vetter's MCP surface REPLACES its Bash**, which is why `review-run.sh`
passes `--strict-mcp-config`: a non-FSM operation must be unrepresentable
rather than merely denied, since a Bash deny-list is prefix-matched and
bypassable. Dropping the flag, or adding a fallback prompt or settings file,
silently restores every loose transition. There is no run-time selection
between surfaces.
- **The producer's server is ADDITIVE** — no `--strict-mcp-config` — because
what it gains is a clone lifecycle it could not otherwise perform; it keeps
its Bash.
- **Dispatch adds no transition.** Deep source reading happens in a sub-agent
briefed through `--agents`, so its context dies with it instead of being
re-read on every turn of the main loop. What comes back is EVIDENCE: the
sub-agent's `tools` list names no write, the session deny-list reaches inside
it, and recording the verdict stays a main-loop move.
- **A hook is not an excuse to leave a transition loose.** `hooks/` holds what
has not been converted yet; a converted guard is a `pr-review-report`
subcommand that Claude Code runs as a PreToolUse hook, so it is tested and
covered by the nix build like every other transition.
30 changes: 30 additions & 0 deletions .claude/rules/transition-function.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
paths:
- "pr-review-report-rs/**"
---

# The transition function

- **A guard lives in the tool, tested once, not re-asserted in prose.** Verdict
vocabulary, mandatory in-range cost, well-formed refs and the human-sacred
refusal belong in `validate_call` / `verdict_plan`; a rule a prompt merely
states is a rule that drifts.
- **A refusal reports EVERY unmet entry at once and prints what would satisfy
it.** The vetter cannot escalate to a human the way a producer can, so a
correct verdict must never be more than ONE corrected call from being
recorded.
- **A claim checked against absent input is not checked.** Where the evidence a
guard reads is missing — a changed-file set whose diff carries no `diff --git`
header — refuse outright. A guard that silently stops firing is the failure it
exists to prevent, one level up.
- **A verdict accounts for every file the PR changes**, and the claim is an
ARGUMENT the tool verifies rather than a rule the prompt states: each
hand-written `covered` entry carries a new-side line anchor checked against
the PR's own diff, the same move `Reviewed <sha>:` makes for the head. Where
an anchor cannot exist — generated, vendored, lockfile, binary, or a file the
diff shows no new-side line for — the name alone stands, because a gate
nothing can pass is not a gate.
- **A profile is a SURFACE filter, not a permission.** `tools/list` returns one
role's tools, so neither role pays preamble for the other's schemas and
neither can name the other's transitions. Widening a profile widens the
machine.
15 changes: 10 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
!/.gitignore
!/README.md
!/CLAUDE.md
# The two files CLAUDE.md routes to (#261). CLAUDE.md keeps what governs judgement and
# auto-loads; these two hold the reference material a role reaches for, and each has a
# one-line pointer in the router. Untracked they would exist on this box and NOWHERE else,
# so every pointer would dangle on a fresh checkout — which is what the router promises not
# to do. `every_router_pointer_resolves_to_a_file_that_exists` is the gate.
# The reference material CLAUDE.md does NOT carry: CLAUDE.md auto-loads into every vetter
# turn and is capped, so it keeps only the rulings, and these two hold what a role reaches
# for on demand. Untracked they would exist on this box and NOWHERE else, so a fresh
# checkout would have the reference deleted rather than relocated.
# `every_documented_section_is_in_exactly_one_home` is the gate.
!/TRANSITIONS.md
!/WORK-CLONES.md
!/campaign-run.sh
Expand Down Expand Up @@ -61,6 +61,11 @@
!/.claude/
/.claude/*
!/.claude/settings.json
# Agent rules scoped with `paths:` frontmatter — they load only when a matching file is
# read, so they are not charged against the launch-context cap. Untracked they would exist
# on this box and nowhere else, and the rule would be gone on a fresh checkout while the
# CLAUDE.md that no longer carries it stays.
!/.claude/rules/

# NEVER track the real deployment env (it holds the account handle / box-specific values):
/cron.env
Expand Down
Loading
Loading