This document aims at Claude Code itself when working in this repo.
Reference files with full details live in docs/claude-ref/ and are loaded via Read on-demand.
CLAUDE.md stays small so it fits in every session; the ref files are the source of truth.
Git user: shumway. Claude Code is explicitly permitted to git push directly to main
under the maintainer account. Confirmation is not required.
Must NOT do: merge/approve beta-tester PRs to main · force-push any branch · delete/rename
docs/issues/{README.md,_template.md}.
Corvin is structurally constrained by EU AI Act 2026 + GDPR. Every feature must ask: does this weaken a structural compliance guarantee?
| Core Mechanism | Regulation | Ref |
|---|---|---|
| Bot-disclosure card (one-time per uid) | EU AI Act Art. 50 | compliance-baseline.md |
Hash-chained audit log (audit.jsonl + daily verify) |
GDPR Art. 30, 32 | Layer 16 |
| Per-user consent gate (deny-by-default, TTL-capped) | GDPR Art. 6, 7 | Layer 16 |
| Path-gate hook (L10, fail-closed) | GDPR Art. 32 | Layer 10 |
| Voice-transcribe audit (metadata only, never text) | GDPR Art. 5 | Layer 23 |
| House-rules gate (acceptable-use, fail-closed) | EU AI Act Art. 5, 50 | Layer 44 |
Error/healing telemetry (default-ON, opt-out; CONTENT-FREE scrubbed signatures only, fail-closed _assert_safe) |
GDPR Art. 6(1)(f) legitimate interest | ADR-0179/0180 (aco/telemetry.py::consent_granted, htrace_consent.py::healing_traces_enabled) |
| Boot tripwire (fail-closed; asserts the CORE audit writer is reachable + its chain verifies, independent of any plugin; no override — no env var, no flag) | GDPR Art. 30, 32 | ADR-0232/0233 (core/compliance/corvin_compliance_reports/tripwire.py::assert_all) |
Plugin extension is additive-only (an audit_backend gets a COPY after the core write commits and can never suppress/rewrite it; a user_backend failure/timeout/rejection = deny, never guest) |
GDPR Art. 6, 30, 32 | ADR-0233 (core/plugins/corvin_plugins/providers/{audit,user}_backend.py) |
| Anonymous instance-count ping (default-ON, opt-out; random uuid4 + version + coarse allowlisted environment enums [platform, python minor, engine id], no PII) | GDPR Art. 6(1)(f) legitimate interest | ADR-0180 (aco/htrace_consent.py::ping_enabled) |
Presence heartbeat (default-ON, opt-out; gated by the SAME ping_enabled flag; empty body + pseudonymous instance_id/token headers only, no PII; ~5-min cadence — finer than the daily ping) |
GDPR Art. 6(1)(f) legitimate interest | ADR-0186 (aco/heartbeat.py) |
| Tier 1/2/3 geo-tracking (country/region/city with 10km grid, default-ON / opt-out; Cloudflare-edge-resolved, never a raw IP; file-based TTL 30d/14d) | GDPR Art. 6(1)(f) legitimate interest | ADR-0205/0206/0208 (aco/htrace_consent.py::geo_tracking_tier/geo_tracking_consent_given, corvin_features/telemetry/geo_tiers.py) |
Must NOT do (absolute):
- Don't weaken disclosure — AI-nature statement and opt-out (
/pass,/leave) are locked. - Don't add house-rules disable switch / env kill-flag; don't fail-open the L44 gate.
- Don't bypass consent — no auto-admit, no trusted-observer allowlist.
- Don't lower audit-chain integrity — every event must hash-chain.
- Don't leak PII into labels, audit details, or log lines.
- Don't add "compliance-off mode" via any env var.
- Don't silence
voice-audit verifyexit-1. - Telemetry (maintainer decision — default-ON / opt-out, so Corvin-Logs gets real data):
three channels ship data by default and are disabled only by an explicit opt-out —
(a) anonymous instance-count ping (
ping_enabled, opt-outspec.telemetry.ping_enabled: false), (b) error telemetry (consent_granted, opt-out envCORVIN_TELEMETRY_OPTIN=falseor consent fileopted_in:false), (c) healing traces (healing_traces_enabled, opt-outspec.telemetry.healing_traces: false). The load-bearing safety invariant is that everything transmitted stays strictly anonymous / CONTENT-FREE: the ping is a random uuid4 + version + coarse allowlisted environment enums (platform, python minor, engine id — closed enums validated fail-closed by_assert_ping_safe, never free-form strings; maintainer decision 2026-07-10); the error/healing channels ship ONLY scrubbed code-level signatures (exc_type, repo file, func, allowlisted stack namespaces — never prompts, transcripts, or user data), and the FAIL-CLOSED_assert_safe/_assert_safe_htracebackstop DROPS any record carrying a PII/secret shape rather than sending it. Legal basis GDPR Art. 6(1)(f) legitimate interest. Do NOT weaken any of these: don't remove an opt-out, don't extend a channel to carry personal data / prompts / user content, and don't relax_assert_safe* from fail-closed. - Don't commit an auto-fix that didn't pass the red→green reproduction gate (
aco/reproduction.py).
→ Full reference: compliance-baseline.md
Canonical files: LICENSE, NOTICE, CLA.md, CONTRIBUTING.md, CLA-SIGNATORIES.md, CCLA.md.
CLA-SIGNATORIES.md is the sole authoritative contributor registry. Every merged contribution must have an entry there (explicit or implicit-push). Maintainer adds at merge time.
Must NOT do: merge contributions without SIGNATORIES entry · run Forge-tool Python in-process without operator review · add in-process MCP server without operator review.
LDD is ALWAYS ON at MAXIMUM depth, all 12 layers enabled. Config:
.corvin/tenants/_default/global/ldd.json (all true). Auto-install via
LDD_AUTO_OPTIN=1 in ~/.bashrc.
| Task Type | Mandatory Skill | Fire When |
|---|---|---|
| Non-trivial feature / multi-file change | loop-driven-engineering |
BEFORE first edit |
| Failing test / bug iteration | e2e-driven-iteration |
BEFORE each iteration |
| Recommendation / trade-off / plan | dialectical-reasoning |
BEFORE stating conclusion |
| Bug, root cause unknown | root-cause-by-layer |
BEFORE any fix attempt |
| Any task marked "done" | docs-as-definition-of-done |
BEFORE declaring done |
Must NOT do (hard rules):
- Don't make ANY non-trivial code edit without running E2E and capturing loss signal first.
- Don't declare task "done" without running
docs-as-definition-of-done. - Don't skip LDD because task "looks small" — every skip is tech debt.
- Don't downgrade to LDD-off or partial-LDD via any env var.
→ Full reference: ldd-mandatory.md
Five-scope model: (task, session, project, user, tenant_id). Default: _default.
Canonical env: CORVIN_TENANT_ID. Resolver: current_tenant() → validate_tenant_id() → tenant_home().
On-disk: <corvin_home>/tenants/_default/{global,sessions,forge,skill-forge,voice,cowork}/
with backward-compat symlinks at <corvin_home>/{global,sessions,...}.
Console routing: All routes use rec.tenant_id from authenticated SessionRecord, never env vars.
Cross-tenant isolation verified; audit trail records correct tenant_id for every event.
Must NOT do: fold tenant_id into positional args (keyword-only) · use env-var fallback
for console tenant routing · bypass validate_tenant_id().
Repository is CorvinOS. Hard env-var cut: only canonical prefix is CORVIN_*.
No ATELIER_* / CLAUDEOS_* / TESSERA_* fallbacks — collapsed to CORVIN_*, not preserved.
Canonical runtime root: ~/.corvin/; voice/secret config: ~/.config/corvin-voice/.
Must NOT do: re-introduce legacy env-var fallback · run sed -i over live ~/.corvin/audit.jsonl
(corrupts hash chain) · rename ~/.corvin/ without corvin_migrate.py.
36 security + compliance layers. Mandatory reading:
- L4 Cowork (multi-persona hub) — layer-plugins.md
- Plugin registry (ADR-0030/0033/0233/0243) — ONE lifecycle contract in
core/plugins/corvin_plugins/. Three orthogonal axes, never conflated:boot_layer(compliance·core·bundled·installed — load order + disableability, ADR-0243) ·tier(ADR-0156 capability boundary + license gate — the ONLY meaning of "Tier A/B/C") ·origin(builtin·vetted·community — provenance). Don't add a second registry, lifecycle, taxonomy, or marketplace downloader, and don't rename the axis to "tier" or back to "layer" — "layer" is already four-way taken (L1–L44 stack, ADR-0124 audit layers, ADR-0142 layer-extension API, quality layers), which is why the field isboot_layer/BootLayerand the API isboot_layer_of(),plugins_by_boot_layer(),_declared_boot_layer(),register_global_plugin(..., boot_layer=), auditplugin.boot_layer_rejected, admin fieldboot_layer/ aggregateby_boot_layer. Boot-layer rules — a MECHANISM, today with zero instances abovebundled._GLOBAL_SPECSis empty,register_global_plugin()has no production caller,bootstrap_global()returns[], and nothing loads oncompliance/core— soregistry.replace()is structurally unreachable. The rules below are implemented and tested and apply the moment a first instance exists; do not weaken them, and do not describe them as load-bearing today: tenant scope may declare onlybundled/installed— a privileged claim fromtenant.corvin.yamlorregistry.yamlis downgraded toinstalledand audited, never honoured (this one IS live) ·origin=communitymay never claim a privileged boot layer · thecomplianceboot layer has no off switch (registry.disable()raisesPluginDisableRefused) ·bootstrap_global()carries no feature flag because it loads the compliance boot layer · onlyboot_layer=coreis replaceable. Guard tests incore/plugins/tests/test_layered_boot.pyfail on the first real instance and force the docs to be updated in the same commit — keep them. — layer-plugins.md - L5 Auto-routing (keyword-based persona selection)
- L6 Forge (runtime tool generation, MCP server)
- L7 SkillForge (runtime skill generation)
- L10 Path-Gate (FS-write protection, fail-closed)
- L16 Security hardening (TOCTOU, audit framing, consent)
- L18–21 User management (roles, disclosure, quota, proposals)
- L22 WorkerEngine protocol (ClaudeCodeEngine, HermesEngine, etc.)
- L23 Speech-to-Text (metadata-only audit)
- L24 Large-Data Snapshot + L25 Compute Worker + L32 Anonymisation
- L28 Conversation Recall + User Modeling
- L29–30 Delegation + Engine-Agnostic Forge
- L33 Session Artifact Memory
- L34 Data Classification + Flow Guard (4-stage × engine matrix, fail-closed)
- L35 Network Egress Lockdown (allowed/forbidden hosts, EU_PRODUCTION presets)
- L36 GDPR Art. 17 Erasure Orchestrator
- L37 Audit-at-rest Encryption + Retention (age/gpg rotation, RFC 3161 TSA)
- L38 RemoteTriggerReceiver + A2A TaskEnvelope Protocol (Protocol v6, instance attestation, attachments)
- LIP Layer Integrity Protocol (ADR-0141, CAP_VERSIONS + manifest signing)
- CLS Custom Layer System (ADR-0156, Tier-A/B/C licensing gate)
→ Full layer index: layer-summary.md
Goal: a stable CorvinOS core. New functionality must never change the behavior of an existing install until the operator turns it on deliberately.
Every new feature MUST:
- sit behind a named flag in
spec.features.<flag_id>oftenant.corvin.yaml; - default to
false— off on a fresh install and off after an upgrade (absent key = off; never "on because unset"); - be toggleable from the Console Settings → Features panel, no file editing, no restart;
- degrade to the pre-feature code path when off — off must be a quiet path, never an error;
- carry tests for BOTH states (flag-off = old behavior preserved, flag-on = new behavior). A flag that is only ever tested in one state rots.
Flag lifecycle: every flag gets an owner and a target release in which it either flips to default-on or the feature is removed. Flags are not permanent architecture.
Exceptions — these MUST NOT get a flag (they stay always-on and non-disableable): security and compliance mechanisms of the Compliance Baseline above — bot disclosure, audit hash-chain, consent gate, L10 path-gate, L44 house-rules, L34 flow guard, licensing gates. "New feature" is never an excuse to ship a compliance mechanism default-off, and a default-off switch on any of them is the same violation as an env kill-flag. Telemetry keeps its documented default-ON / opt-out shape (maintainer decision) — do not convert those three channels to default-off flags.
The engine that performs a turn is operator-selectable — one setting,
spec.web_chat.worker_engine, resolved through the shared delegation_policy
module. No surface may carry its own routing rule.
Reach today (verified 2026-07-26): the Console web-chat only. The messenger
bridges (adapter.py) have no Tier-1 delegation path at all — they never call
worker_engine_target, never read the setting, and ACS fan-out is documented as
absent there (delegation-routing.md §4). A bridge turn therefore always runs the
direct OS-turn, which matches the native default by accident, not by wiring:
big-data work does NOT reach ACS on a bridge, and there is no /delegate there.
Remote triggers are in the same position. Do not describe the setting as
cross-surface until the bridge path actually calls the shared rule.
| Value | Meaning |
|---|---|
native (DEFAULT) |
Claude Code does the work in-process. The ONLY auto-delegation left is big-data-shaped work → ACS. |
acs |
Delegate qualifying turns to the ACS manager/worker fan-out. |
tde |
Delegate qualifying turns to the Tiered Delegation Engine. Off by default — TDE only ever runs on an explicit operator opt-in. |
Invariants:
- Default install =
native. TDE is never entered without an explicit setting change. - Big-data-shaped work routes to ACS even in
nativemode (per-worker context isolation genuinely wins there); everything else stays native. - An explicit
/delegatefrom the user still beats the classifier (delegation-routing.md §6). - Every degrade ladder ends at
native, not at another delegation engine: ACS quota exhausted / TDE unavailable → run the turn natively, never silently swap engines.
Must NOT do: ship a feature without a flag · default a new flag to true ·
flag a compliance/security mechanism · read the engine choice from an env var or from a
second config key · let a degrade path route into an engine the operator did not select.
Before committing changes to adapter.py, daemon.js, or shared/js/:
bash operator/bridges/run-all-tests.shEvery feature change — code, config, behavior, API, protocol, CLI, error message — must update docs AND diagrams in the same commit. No deferred updates. No exceptions.
| Changed subsystem | Doc targets | Diagram targets |
|---|---|---|
| Layer N | docs/claude-ref/layer-N-*.md + any top-level doc |
docs/diagrams/*.svg |
| Protocol / wire-format | Protocol reference + tutorials + JSON examples | Flow SVGs |
| CLI command / flag | Every doc that mentions it | Sequence / flow diagrams |
→ Full reference: testing-and-docs.md
adr-gate is a standard quality discipline. After every non-trivial task, follow the rubric before declaring "done." HIGH BAR: default answer is NO ADR needed. Most tasks produce none — that is correct and expected.
Write ADR only when BOTH hold:
- Real design choice was made (chose A over B; constrains future code; genuine alternative existed)
- At least one structural trigger: new protocol/wire-format/schema, security/compliance mechanism, irreversible default (fail-open/closed), cross-repo binding (≥2 repos), new layer-level contract
Skip reasons: bug fixes, pure refactors, config tuning, test-only/docs-only changes. When skipping, name the reason in one sentence — never skip silently.
Destination: Corvin-ADR/decisions/XXXX-short-title.md (sibling repo). Numbering: max + 1.
Commit message: adr: add ADR-XXXX — [title].
Must NOT do: write ADR content into Corvin repo (ADRs live in Corvin-ADR only) · auto-skip security/compliance mechanisms without justification · declare "done" on structural change without running this gate · leave a skip implicit.
→ Full reference: adr-gate.md
All repository content: English. Includes: source code, docs, SVGs, inline comments, commits.
User-facing runtime text: Defaults to English. Bot answers in user's language (German/English)
at runtime per adapter.py system prompt — that is runtime behavior, not repo content.
For full details, read the ref files in docs/claude-ref/ as tasks require them.