fleet-constitution¶
One architectural source of truth (~/.claude/CLAUDE.md), generated into every CLI's native
config format, drift-gated in CI, and checked for consistency across all fleet machines.
FLT-82 · council verdict 2026-07-17
(llm-council/reports/COUNCIL-LOG.md).
Why this exists¶
The fleet runs multiple agent CLIs — claude, codex, gemini/agy, grok, local Ollama — across 4 PCs.
Only Claude Code reads CLAUDE.md; the other CLIs never see the architectural rules. An LLM council
evaluated adopting GitHub Spec Kit to solve this and rejected it: Spec Kit's constitution is
context injection, fed to the model exactly like CLAUDE.md already is — no hook, no enforcement,
and it would add a second (then third) hand-editable source of truth, making drift worse. The one
real thing on offer was cross-CLI reach, and reach is distribution, not a new pipeline.
So we build only the distribution:
- One master.
~/.claude/CLAUDE.mdis the single writable source. Everything else is generated. - Content hash = identity. Same master → same SHA-256 → same output on every box. The hash is stamped into every generated file's header.
- Two levels of check.
verifyproves one box's exports are current (intra-machine).fleet-verifyproves the boxes agree with each other (inter-machine) — the drift no single box can see.
Commands¶
fleet-constitution generate [--master PATH] [--out DIR]
fleet-constitution verify [--master PATH] [--out DIR] # exit 1 on any drift
fleet-constitution report [--master PATH] [--out DIR] # this box's JSON self-report
fleet-constitution install [--master PATH] [--home DIR] [--dry-run] # inject managed block into each CLI's real config
fleet-constitution fleet-verify [--reports DIR] # aggregate reports; exit 1 if fleet disagrees
--master defaults to ~/.claude/CLAUDE.md; --out to ./generated; --home to the user profile.
install — put the rules into each CLI's real config¶
Injects a delimited, hash-stamped managed block into the instruction file each CLI actually loads, without touching the rest of the file. Targets and their gotchas were fixed by the FLT-86 council (the CLI file-owners answering for their own configs):
| CLI | Real target | Note |
|---|---|---|
| codex | ~/.codex/AGENTS.md |
install warns if the file exceeds Codex's 32768-byte project_doc_max_bytes budget |
| gemini | ~/.gemini/GEMINI.md |
injected into the CLI's <user_rules> |
| grok | ~/.grok/AGENTS.md |
not GROK.md — Grok proved (grok inspect) GROK.md is never loaded |
Claude reads the master directly and Ollama has no instruction file (per-request prompt), so neither is an install target.
The block:
<!-- BEGIN fleet-constitution sha256:<hash> -->
…constitution…
<!-- END fleet-constitution -->
.bak is written before the first mutation; the write is atomic
(temp + rename). A BEGIN with a missing END aborts rather than guessing the boundary. Use
--dry-run to preview per-target actions with no writes.
Verify statuses¶
| Status | Meaning | Exit |
|---|---|---|
Ok |
On-disk file matches a fresh generation | 0 |
Missing |
Expected generated file absent | 1 |
Stale |
Generated from a different/older master (header hash ≠ current) | 1 |
HandEdited |
Header claims the current master, but the body was edited | 1 |
Targets¶
Generated under --out, placed at each CLI's real config location by the deploy step:
| CLI | File |
|---|---|
| (canon) | fleet-constitution.md |
| codex | codex/AGENTS.md |
| gemini | gemini/GEMINI.md |
| grok | grok/GROK.md |
| ollama | ollama/system-prompt.txt |
Claude is intentionally absent — it reads the master directly, so exporting to it would recreate the second-master problem.
Cross-machine consistency¶
scripts/fleet-verify.ps1 fans report out to every PC and aggregates. It fails loud if any box is
stale, or if the master hash or exporter version diverges across machines. Run it detached and poll
(usage-governor: no turn-by-turn grinding). In production the fan-out goes through the fleet-dispatch
queue rather than raw Invoke-Command, so spend stays governed.
When the master changes: commit CLAUDE.md → fleet-dispatch pushes it and reruns generate on
every box → fleet-verify stays red until all boxes report the same hash. A partial rollout is a
failure state, not a warning.
Build¶
dotnet build FleetConstitution.slnx
dotnet test tests/FleetConstitution.Tests/FleetConstitution.Tests.csproj
Layout: src/FleetConstitution.Core (pure, testable — hashing, header stamp, exporter, verifier,
fleet aggregator), src/FleetConstitution.Cli (verbs + I/O), tests/FleetConstitution.Tests.
CI (.github/workflows/ci.yml, home-ci self-hosted runners): build -warnaserror, vulnerable-package
gate, tests + ≥80% line coverage on Core, and a drift gate that verifies the committed
samples/generated/ outputs against samples/CLAUDE.sample.md without regenerating.