The hardest boundary for Hades. Absolute rule, no honor system, rules hard-coded into the gate itself. Established: 2026-04-23. Origin directives: 2026-04-22.
Recovered from Claude Code session transcripts via mnemosyne recover on 2026-04-23. These are the canonical operator statements that shaped Moirai. No paraphrase.
bed489ab — 2026-04-22 — the Moirai birthday13:21:
"what we are designing is a new addition to the pantheon. The Moirai. It is the hardest boundary for each constellation. this hook is it and it must be an absolute rule, enforced without fail. The one for Hades is the most crucial and consequential, which is why we start here but every constellation will get its own Moirai boundary once we have it working"
13:28:
"good scope. the theater is the exact issue. everything we have done so far is that, but Moirai is not. Your unknows need to be investigated and the gate must be stress tested. no system has zero fail states, but we can remove the ones that cause 95% of pantheon failures"
13:31:
"1, sounds like you are asking if we can except plain text for tests? be more clear. 2. There is a definitive answer for this somewhere, research it, don't ask me. 3. no idea, must be tested"
13:41:
"follow your own intuition"
6960e83a — 2026-04-22 — mandate framing11:08:
"CREATE THIS HOOK NOW AND MAKE SURE IT RUNS BEFORE ANYTHING ELSE. THERE CAN BE NO HADES SESSIONS THAT DO NOT START BY INVOKING THESE HARD RULES."
11:17:
"NEW MANDATE FOR HADES. NO FAILURE NO MATTER HOW SMALL IS LEFT UNDEALT WITH"
340fcaa1 — 2026-04-22 — challenge11:51:
"SO YOU DESIGNED SOMETHING THAT FUNDAMENTALLY DOESN'T WORK? OR THIS IS FIXABLE?"
11:55:
"unblock yourself and fix it"
5491753a — 2026-04-23 — architectural lockdown"We're envisioning a system that is unbreakable, that ensures that constellation agent knows at the start of each session what the absolute most important are for the session. The gate is the mechanism and the rules are specific to each constellation. But for the gate to be effective, rules need to be hard coded."
"We cannot have a second step where you have one gate that is general and then searches for the code somewhere else so that only the rules are unique but the gate works the same. That's the architectural question that we are trying to solve. It's my understanding that for Hades specifically, these failures have been because of an honor system."
"There is no room for an honor system. This must be hard coded."
"We don't want extremely heavy LLM calls that bloat our context here. We just want a strict harness... We do not want every session to start with complex tool call."
"And we just need this to run and flag anything that is unusual or requires confirmation or rotation." [this is Cerberus, NOT Moirai — scope clarification]
Moirai is a hard-coded, per-constellation, PreToolUse gate. For each constellation, there is exactly one Moirai gate script. That script lives in that constellation's repository. Its rules are literal bash — no external manifest read at runtime, no YAML interpretation, no configuration file that could be edited to silently weaken the gate.
The Moirai gate enforces the Minimum Session Load: the absolute-most-important rules a constellation's agent must internalize before any other action in a session. The mechanism is split into two parts:
/start or /war-room-start (or invoke the equivalent Skill) to set the fourth marker. This signals the agent has consciously entered a session — the hardest boundary against mindless action.For Hades v0.1, the four markers are:
COMMANDMENTS_ACK — set by SessionStart hook after reading docs/book-of-hades/COMMANDMENTS.mdSCOPE_ACK — set by SessionStart hook after reading context/scope.mdCONTEXT_ACK — set by SessionStart hook after reading CONTEXT.mdSTART_ACK — set by UserPromptSubmit hook on /start OR /war-room-start, OR by PreToolUse hook on Skill start OR Skill war-room-startIf the SessionStart hook fails (file missing, token dir unwritable), markers 1–3 are not written and the gate fail-closed-blocks on first non-exempt tool call. The operator sees the fail-closed message and investigates immediately.
Session-entry cost: 0 tool calls + 1 slash command. No burned tokens on mandatory reads.
Until all four markers are set, the PreToolUse gate blocks all non-exempt tool calls.
credential-liveness.sh required before gh secret set / bao kv put / etc.) is handled by a separate hook: hades-deployment-gate.sh. Different event (per-Bash-call), different purpose. Moirai does not own this.bao kv get, cat .env), tmux-payload-size limits — those are hades-vault-guard.sh, a separate hook. Moirai does not own these either.settings.json. It is not invocable, not user-facing, not optional.hades-gate.sh. When atlas adopts Moirai, atlas writes its own atlas-gate.sh with atlas-specific rules baked in. No shared engine. No cross-constellation dependency.constellation-hades/
├── hooks/
│ └── moirai/
│ ├── hades-session-load.sh ← SessionStart loader: reads 3 files, injects stdout, writes markers 1-3
│ ├── hades-gate.sh ← PreToolUse + UserPromptSubmit gate: enforces markers, sets START_ACK
│ ├── README.md ← operator-facing: what the gate blocks and why
│ └── tests/
│ └── stress-test.sh ← black-box test harness
├── cerberus/
│ └── probes/
│ └── moirai-hades-integrity.sh ← hourly cheap probe: orphan markers, perms, staleness
└── docs/
└── moirai/
├── SPEC.md ← this file
└── RETIREMENT.md ← what was removed when Moirai shipped (Hook #1, Hook #4)
Symlinks: ~/.claude/hooks/moirai/hades-session-load.sh → $HADES_ROOT/hooks/moirai/hades-session-load.sh and ~/.claude/hooks/moirai/hades-gate.sh → $HADES_ROOT/hooks/moirai/hades-gate.sh. settings.json references the symlinks. Survives clone + nono-setup rebuild.
Moirai hooks into three Claude Code events, distributed across two scripts:
| Event | Script | Purpose |
|---|---|---|
SessionStart |
hades-session-load.sh |
Read + inject 3 required files into session context, write markers 1–3 to token file |
UserPromptSubmit |
hades-gate.sh |
Detect /start or /war-room-start slash command, write START_ACK. Never blocks. |
PreToolUse |
hades-gate.sh |
Detect Skill start or Skill war-room-start (backup path for START_ACK), enforce gate on all other non-exempt tool calls |
Why dual slash-command detection (UserPromptSubmit + PreToolUse):
/start / /war-room-start fire only UserPromptSubmit, not the Skill tool.start / Skill war-room-start fire only PreToolUse, not UserPromptSubmit.This closes the gate-prerequisite-invariant deadlock documented in memory:
reference_session_start_hooks.md.
hades-session-load.sh responsibilities (SessionStart)Runs once at session open, before any agent action. All steps inline in bash:
cwd, session_id. Fail-closed on malformed input.cwd does not match */constellation-hades or */constellation-hades/* after realpath, exit 0.HADES_ROOT from cwd.mkdir -p /tmp/moirai/hades/. Fail-closed (exit 2) if unwritable.<timestamp> <marker> SessionStart main <absolute-path> to /tmp/moirai/hades/session-<session_id>.Required files are string literals: docs/book-of-hades/COMMANDMENTS.md, context/scope.md, CONTEXT.md. No external manifest read.
hades-gate.sh responsibilities (UserPromptSubmit + PreToolUse)Dispatches on hook_event_name:
hook_event_name, cwd, session_id. Fail-closed on malformed input.cwd against */constellation-hades[/*] via realpath, exit 0 if outside./tmp/moirai/hades/session-<session_id> (mkdir -p, fail-closed).prompt. If body starts with /start, /war-room-start, OR contains <command-name>/start</command-name> OR <command-name>/war-room-start</command-name>, append START_ACK to token file. Always exit 0 (never blocks prompts).tool_name, tool_input.file_path, tool_input.skill.start or war-room-start → write START_ACK, exit 0.awk '{print $2}' | sort -u), check set covers {COMMANDMENTS_ACK, SCOPE_ACK, CONTEXT_ACK, START_ACK}. If complete → exit 0. Else → print block message (§4.6) with checklist, exit 2.All slash-command prefixes, skill names, exempt-tool list, and marker names are bash string literals. No YAML, no yq, no sourced external files.
| Tool | Reason |
|---|---|
Skill |
Agent's way to request guidance without burning gate state. Skills may themselves call gated tools. |
Read of CLAUDE.md |
Auto-loaded into prompt context — re-reading is idempotent and cheap. |
Read of MEMORY.md |
Same. |
Read of any path containing /memory/ |
Auto-memory files are already in prompt context. |
TaskCreate / TaskUpdate / TaskGet / TaskList / TaskStop / TaskOutput |
Metadata tools, no credential surface, required for subagent observability. |
Reads of the four required files (COMMANDMENTS.md, scope.md, CONTEXT.md, plus /start invocation) are NOT "exempt" — they satisfy markers. The recorder branch handles them; they never reach the gate branch.
Both scripts exit 2 (block) on ANY of:
/tmp/moirai/hades/ directoryrealpath binary (portability guard)Every fail-closed message names the condition so the operator can diagnose without reading the script.
Critical guarantee: if hades-session-load.sh fails, markers 1–3 are never written, and the PreToolUse gate blocks first non-exempt tool call with a clear message pointing to the SessionStart failure. A broken load hook cannot silently degrade into an unlocked session.
Path: /tmp/moirai/hades/session-<session_id>
Format: newline-delimited records. Each record:
<timestamp_unix> <marker_name> <tool_name> <agent_id> <file_path>
agent_id — "main" if parent session, else subagent id (for forensic trails).file_path — for Read-satisfied markers, the resolved path. Empty for /start.Why plaintext v0.1: cheap, inspectable, easy to debug. The risk is an adversarial subagent forging marker lines; that requires write access to /tmp/moirai/hades/, which is same-uid filesystem access — at which point the adversary can modify the gate script itself. Token signing doesn't close that gap.
v0.2 HMAC upgrade path: if Cerberus integrity probe ever observes an orphan marker (written without a corresponding tool-use evidence trail), we promote to HMAC-signed markers with the key held by a custodian process. Not shipped until drift observed.
BLOCKED by Moirai (Hades).
Hades sessions must complete the Minimum Session Load before any gated
tool call. This boundary is absolute — rules are hard-coded in
hooks/moirai/hades-gate.sh and cannot be weakened at runtime.
Required markers (in any order):
[x] COMMANDMENTS_ACK (set by SessionStart loader)
[x] SCOPE_ACK (set by SessionStart loader)
[x] CONTEXT_ACK (set by SessionStart loader)
[ ] START_ACK (set by /start OR /war-room-start)
Resolution: type /start or /war-room-start. The gate opens once the
fourth marker is recorded.
If markers 1–3 are MISSING, the SessionStart loader failed. Check:
- bash $HADES_ROOT/hooks/moirai/hades-session-load.sh available + executable
- /tmp/moirai/hades/ writable
- Required files present on disk
Blocked tool: <tool_name>
Session token: /tmp/moirai/hades/session-<session_id>
Gate source: <absolute path to hades-gate.sh>
Loader source: <absolute path to hades-session-load.sh>
Self-disclosing: a fresh session with zero context can read this output and unblock itself without needing to grep the devlog or ask the operator.
hooks/moirai/tests/stress-test.sh — re-uses structure of current ~/.claude/hooks/moirai/tests/stress-test.sh. Each case synthesises a JSON hook payload on stdin, runs the gate, asserts exit code + stderr fragment.
Minimum 20 cases, covering both scripts:
SessionStart loader (hades-session-load.sh):
| Class | Example |
|---|---|
| Happy path | 3 files readable → 3 markers written, stdout ≥ sum of file sizes, exit 0 |
| Scope check | cwd outside Hades → exit 0, no output, no token |
| Missing required file | one required file moved → exit 2, stderr names missing file, no partial markers |
| Symlink cwd | /tmp/sym → constellation-hades → still loads (scope pattern match) |
| Unwritable token dir | /tmp/moirai/hades/ readonly → exit 2, fail-closed |
| Concurrent load | 5 parallel SessionStart invocations same session_id → markers present, stdout well-formed |
Gate (hades-gate.sh):
| Class | Example |
|---|---|
| Happy path | 4 markers set, Bash allowed |
| Partial state | 3/4 markers set (no START_ACK), Bash blocked with checklist showing 3 [x] + 1 [ ] |
| /start UserPromptSubmit | prompt /start → START_ACK written, exit 0 |
| /war-room-start UserPromptSubmit | prompt /war-room-start → START_ACK written, exit 0 |
| Expanded slash command | prompt contains <command-name>/start</command-name> → START_ACK |
| Skill start backup path | PreToolUse Skill tool with skill name start → START_ACK |
| Skill war-room-start backup | PreToolUse Skill tool with skill name war-room-start → START_ACK |
| Scope check | cwd outside Hades → exit 0 |
| Malformed input | empty stdin → exit 2, fail-closed |
| Exempt tool | Skill (non-start) always allowed |
| Exempt Read | Read MEMORY.md allowed without marker |
| Exempt Task tool | TaskCreate allowed pre-markers |
| Cross-session isolation | session A's token does not unlock session B |
| Missing session_id | exit 2, fail-closed |
| Unwritable token dir | exit 2, fail-closed |
| Idempotent marker | /start typed twice → still 1 distinct START_ACK in set |
| Stress concurrency | 10 parallel PreToolUse invocations same session_id → consistent gate state |
Target: 18/18 green. CI exit 0 = gate ships, exit 1 = ship blocked.
Before swapping settings.json final entries, the operator runs a fresh Hades session and confirms:
/tmp/moirai/hades/session-<id> exists with 3 markers (COMMANDMENTS_ACK, SCOPE_ACK, CONTEXT_ACK).START_ACK is missing. Block message shows the 4-marker checklist with first 3 checked./start — gate opens, Bash allowed./war-room-start in a fresh session — same flow, gate opens./tmp/).Fail any step → gate does not ship. Law IX: no deployment without E2E in target environment.
hades-gate.sh with full rule set hard-coded. Commit.stress-test.sh. Iterate to 18/18 green. Commit.hooks/moirai/README.md. Commit.cerberus/probes/moirai-hades-integrity.sh. Commit.docs/moirai/RETIREMENT.md describing removal of Hooks #1 + #4 and migration notes. Commit.main. Dispatch to hephaistos for review.~/.claude/settings.json to:SessionStart entry → $HADES_ROOT/hooks/moirai/hades-session-load.shhades-session-start-gate.sh entry (PreToolUse) → $HADES_ROOT/hooks/moirai/hades-gate.shhades-start-prompt-marker.sh entry (UserPromptSubmit) → $HADES_ROOT/hooks/moirai/hades-gate.shhades-deployment-gate.sh + hades-vault-guard.sh entries untouched.~/.claude/hooks/moirai/moirai-gate.sh, ~/.claude/hooks/moirai/constellations/hades.yaml, ~/.claude/hooks/hades-session-start-gate.sh, ~/.claude/hooks/hades-start-prompt-marker.sh. Move stress-test into repo.aion_capture.cerberus/probes/moirai-hades-integrity.sh. Runs hourly. Zero LLM context. Checks:
hades-gate.sh exists, is executable, stat -f %Mp %Hp → permissions 0755./tmp/moirai/hades/ exists, is a directory, is writable by current uid.stale_session_tokens_7d (cleanup hint).unparseable_marker.Alerts fire via cerberus-run.sh → ntfy, standard Cerberus pipeline. Nothing enters LLM context unless operator explicitly pulls the alert.
| File | Status | Replaced by |
|---|---|---|
~/.claude/hooks/hades-session-start-gate.sh |
DELETE | hades-gate.sh |
~/.claude/hooks/hades-start-prompt-marker.sh |
DELETE | hades-gate.sh UserPromptSubmit branch |
~/.claude/hooks/moirai/moirai-gate.sh |
DELETE | hades-gate.sh (YAML-interpreter model rejected) |
~/.claude/hooks/moirai/constellations/hades.yaml |
DELETE | inline bash (no runtime manifest) |
~/.claude/hooks/hades-deployment-gate.sh |
KEEP | orthogonal; different event |
~/.claude/hooks/hades-vault-guard.sh |
KEEP | orthogonal; different event |
Per operator 2026-04-22 13:31 ("no idea, must be tested"):
settings.json — Claude Code may reorder hooks; verify after any settings edit.Read of a file whose name ends with a required-marker suffix but is NOT the required file — e.g., a random CONTEXT.md in a subdirectory. Matcher uses path_suffix not basename to avoid this, but test case confirms.Moirai v0.1 for Hades ships when ALL of:
hades-gate.sh committed to constellation-hades repo at hooks/moirai/hades-gate.shcerberus-run.shREADME.md committed, block-message references it and path resolvesSPEC.md (this doc) committed, references from README resolveRETIREMENT.md committed, lists all removed/kept filessettings.json swapped; old Hook #1 + #4 deleted from diskaion_captureMoirai for the second constellation begins only after Hades Moirai has run 2 weeks in production with zero gate-induced deadlocks.