mcs-architecture-contract
Overview
This repo (my-claude-setup) is a prompt-driven Claude Code configuration system: markdown files ARE the runtime, there is no build step, and ~/.claude/settings.json / ~/.claude/hooks.json / ~/.claude/skills are symlinks straight into this git working tree ("this machine only" fact, re-verify with ls -la ~/.claude | grep -- '->'). Every load-bearing decision below exists to keep that live-production surface fail-soft: a malformed hook, a missing sibling repo, or an unavailable runtime capability must degrade quietly, never hard-fail a session. Read this before changing commands/_council-engine.md, settings.json, hooks.json, or anything under pipeline/hooks/.
When to use and when NOT to
Use when: reviewing a change that touches the engine/theme boundary, deciding whether a proposed change breaks an invariant, tracing why a symptom exists (state disagreeing across files, a hook that silently no-ops, a routing value that doesn't match another routing value), or scoping work that must not violate pipeline/specs/SKILL-GOVERNANCE-SPEC.md.
Route instead to:
- mcs-run-and-operate: how to actually run
/council,/ship,/looper,/handover; where session artifacts land. - mcs-claude-code-platform: generic Claude Code mechanics (hook event contract, Skill/Workflow tool semantics, settings precedence) that apply beyond this repo.
- mcs-config-and-flags: the full catalog of every configuration axis and its current value.
- mcs-debugging-playbook: live symptom-to-cause triage.
- mcs-failure-archaeology: the historical chronicle of what was already tried and reverted.
System diagram (as of 2026-07-02)
commands/council.md ──┐ commands/ece.md ──┐
(theme: 14 vars) │ (theme: 14 vars) │
▼ ▼
commands/_council-engine.md (1753 lines, theme-agnostic
Phase 0-5 orchestration, 8 modes, 3 cost profiles)
│
┌───────────────┼────────────────────┐
▼ ▼ ▼
agents/council-*.md skills/council/<dept>/ workflow templates
agents/ece-*.md DEPARTMENT.md + SKILL.md council-deliberation
(no model: frontmatter) (progressive loading) .template.js /
council-audit
.template.js
│ (Workflow tool,
▼ args = JSON obj)
session artifacts (.claude/council/sessions/<slug>/):
session.md, design.md, prd.md, acceptance-contract.md,
issues.md, plus .claude/council/index.json (workspace)
and ~/.claude/council/global-registry.json (cross-workspace)
── surrounding the whole stack ──
settings.json (live via ~/.claude symlink: model, permissions, hooks,
mcpServers), hooks.json (PreCompact only), pipeline/hooks/*.py
(pre-commit, mostly ^skills/**/*.md, plus 2 single-file exceptions
for settings.json and model-routing.json; CI runs none of them)
Load-bearing decisions, with WHY
1. Shared engine + themed layer. commands/_council-engine.md (1753 lines, wc -l commands/_council-engine.md) holds all theme-agnostic Phase 0-5 orchestration, mode table, and the 3-tier cost-profile table. commands/council.md (337 lines) and commands/ece.md (266 lines) each supply the same 14 config variables (THEME_ID, THEME_NAME, INTAKE_PROMPT, AGENT_FILE_PREFIX, CONDUCTOR_PERSONA, SESSION_DIR_ROOT, TEAM_PREFIX, GLOBAL_REGISTRY_PATH, INDEX_PATH, CHALLENGE_RULES, DEFAULT_PROFILE, EXTRA_MECHANICS, plus phase labels and roster), ending "Follow all instructions in commands/_council-engine.md, substituting the theme variables defined above" (council.md:337, ece.md:266). WHY: a third theme costs one new command file, zero engine edits.
2. Prompt-driven, no build step. Markdown files are the executable artifact. The only non-markdown runtime pieces are two Workflow-tool JS templates (skills/council/references/workflows/*.template.js) and the OpenRouter MCP server (mcp/openrouter/). WHY: zero compile/deploy latency between edit and effect, which is what makes decision 3 safe to rely on.
3. Symlink deployment model. ~/.claude/settings.json -> repo/settings.json, plus hooks.json, skills, commands, agents (verify: ls -la ~/.claude, this-machine-only fact). WHY: editing this repo IS editing the live global config, no staging, no gate before the next tool call. This is why fail-soft (decision 5) is non-negotiable.
4. Backend degradation chain: workflow -> teams -> sequential. Preflight (commands/_council-engine.md:34-46) detects CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 for teams and Workflow-tool availability (requires Claude Code >= 2.1.154) for workflows, "never hard-exits here" (engine:36). workflow modes degrade workflow -> teams -> sequential (engine:42); teams-preferred phases degrade teams -> workflow/sequential (engine:43). A workflow invocation that fails at runtime is itself treated as a detection signal and triggers the next degradation step (engine:39). The resolved backend is printed once and recorded in session.md's Backend: field.
5. Fail-soft everywhere. Four independently-verified cases of the same contract: OpenRouter consult() never raises, every failure returns {"error", "error_kind", "fallback": "claude"} (mcp/openrouter/openrouter_client.py:69-117); hooks/telemetry-dispatch.sh (shipped 2026-07-02, PR #67/#59) exits 0 silently if the private hook is missing and never exits 2 (on Stop, exit 2 would block Claude from stopping); hooks/acceptance-gate.sh treats a hook-input parse failure as exit 0 allow, not a block (acceptance-gate.sh:20-21); the workflow templates tolerate a JSON-stringified args payload rather than throwing (council-deliberation.template.js:32-35, PR #56/01b6081). WHY: each sits on the hot path of ordinary tool use in a live symlinked config; a hard failure here breaks every subsequent tool call, not just the current one.
6. Progressive skill loading. Coordinator (SKILL.md, target <=800 tokens) routes to Layer 2 specialists (target <=2000 tokens), which reference Layer 3 files (target <=1500 tokens) loaded within a procedure step. Per-file targets are guideline, not hard limits: the only hard limit is the suite context-load ceiling of 5500 tokens (pipeline/config/budgets.json:11; enforced by check_context_load.py, hard tier). The user's own global CLAUDE.md states the ceiling informally as "~5,000 tokens"; that number is stale against enforced 5500, which is the enforcement truth.
7. Session-state layering. Three surfaces, written at different points, never reconciled: session.md (per-session phase/backend/profile), .claude/council/index.json (workspace roster), ~/.claude/council/global-registry.json (cross-workspace, keyed by absolute workspace path). This is why a session run from a different repo can leave usage entries in this repo's registry.json: ~/.claude/skills symlinks here, and the registry is shared across every workspace on the machine.
Invariants and their enforcement status
Six invariants matter most; full evidence and re-verification commands are in references/invariants-enforcement.md.
| Invariant | Status |
|---|---|
Tier-alias-only model references, never pinned claude-* IDs |
prose-only (no validator covers agents/) |
Agents omit model: frontmatter |
true today, unenforced |
| One specialist loaded at a time; no cross-specialist references | enforced-by-hook (structure) / prose-only (loading discipline) |
| Eval cases live outside skill directories | contradicted in practice by 9 council departments; not enforced |
Workflow args passed as a real JSON object, never a string |
prose-instructed; code tolerates a string and degrades gracefully |
Blocking hooks require PreToolUse + exit 2 + stderr |
platform semantics, documented via two repeat incidents, not self-checking |
Known-weak points (as of 2026-07-05)
Full detail and re-verification commands are in references/known-weak-points.md.
- Routing truth on 3 surfaces, third now schema-full and hook-enforced (
settings.jsonmodel, engine cost-profile prose,model-routing.jsonv2.0 withtiers/spawn_sites/egress_policy, guarded by hard hookcheck_model_routing.py): US-005/#63 content is done on branchfeat/63-unified-routing-tablebehind open PR #72; issue #63 staysOPENuntil that PR merges to main. - Session state on 3 unsynced surfaces (
session.md,index.json, global registry):index.jsonshowscontract_issue: nullwhileissues.mdalready lists real issues. - Private-repo coupling: US-001/#59 (merged PR #67) made the 5 telemetry events fail-soft; US-007/#65 (dormant-suite extraction) landed via PR #73, leaving only gitignored symlinks pointing at the private repo.
- Registry telemetry durable for one day only (
dc44611, 2026-07-02): short of the 2-4 week window the design doc requires before pruning. /carefuland/freezeare unwired:checkhooks aren't registered anywhere, and use the wrong (env-var, not stdin-JSON) interface even if they were.- Spec-promised hooks that don't exist:
check_security.py,check_triggers.py, documented in the security/trigger specs, absent frompipeline/hooks/. - Governance validators cover
^skills/almost exclusively; CI runs none of them: two new single-file exceptions (settings-schemaonsettings.json,model-routingonskills/council/model-routing.json) landed via PR #71/#72, butagents/,commands/,pipeline/,hooks/stay outside every glob, andvalidate.ymlnever invokespre-commitor anycheck_*.py.
Gotchas
- WRONG: assuming
council.md/ece.mdduplicate logic. RIGHT: only the 14-variable header duplicates; all behavior lives once in_council-engine.md, so a fix there fixes both themes. - WRONG: treating a workflow-tool failure as a hard stop. RIGHT: it's a detection signal that re-resolves the backend one rung down; check
session.md'sBackend:line before assuming a bug. - WRONG: reading
registry.jsonas a trustworthy usage signal. RIGHT: write-mostly telemetry, one day of durable history as of 2026-07-02; the directory tree plus eachDEPARTMENT.mdis authoritative. - WRONG: assuming a hook that warns and exits 1 blocks anything. RIGHT: only
PreToolUse+exit 2+ stderr blocks. This exact mistake shipped and merged once already (incident605112d). - WRONG: believing
/carefulor/freezeadd protection because the command prints "enabled". RIGHT: theircheckhooks are never registered, and would no-op on a stdin/env mismatch anyway. - WRONG: citing eval-cases-outside-skill-dirs as a compliance guarantee because the spec marks it "Hard". RIGHT: zero hook coverage, and 9 council departments already violate it in the committed tree.
- WRONG: assuming
settings.json, the engine's cost table, andmodel-routing.jsonagree on routing. RIGHT: three different surfaces;model-routing.jsonis now the schema-full, hook-enforced one, but the other two are still separate prose/config sources (US-005/#63 content done, PR #72 open, not yet merged to main). - WRONG: editing
settings.jsonhere "just to test something locally." RIGHT: it's the live global config via symlink, no staging gate; every edit lands on the next tool call.
Provenance and maintenance
Last verified: 2026-07-06, main at 74e34c5 (campaign closeout: PR train #67-#74 all merged, issues #58-#66 closed, contract 26/26 verified).
Re-verification commands:
- Engine/theme line counts:
wc -l commands/_council-engine.md commands/council.md commands/ece.md - Symlink deployment (this-machine-only):
ls -la ~/.claude | grep -- '->' - Suite ceiling hard limit:
grep max_simultaneous_tokens pipeline/config/budgets.json - Current settings.json model value:
grep '"model"' settings.json - US-005/US-007 landing status:
gh issue view 63 --json state,title/gh issue view 65 --json state,title, andgh pr view 72 --json state,titlefor the PR itself (orcat .claude/ship-state.mdfor the local queue snapshot) - All pre-commit hook file globs (confirms the 2 exceptions to
^skills/):grep -n 'files:' .pre-commit-config.yaml - Deep invariant/weak-point re-checks: see
references/invariants-enforcement.mdandreferences/known-weak-points.md