Skip to content

Latest commit

 

History

History
303 lines (233 loc) · 17.5 KB

File metadata and controls

303 lines (233 loc) · 17.5 KB

Repo Conventions for Claude Code

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.


Maintainer Status

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}.


Compliance Baseline — EU AI Act 2026 + GDPR (load-bearing)

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 verify exit-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-out spec.telemetry.ping_enabled: false), (b) error telemetry (consent_granted, opt-out env CORVIN_TELEMETRY_OPTIN=false or consent file opted_in:false), (c) healing traces (healing_traces_enabled, opt-out spec.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_htrace backstop 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


Licensing — Apache-2.0 + CLA v3.1 §3 (load-bearing)

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 (Loss-Driven Development) — MANDATORY, ALL SESSIONS (load-bearing)

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


Multi-tenant Axis (ADR-0007)

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().


Project Identity — CorvinOS (hard cut)

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.


Layer Stack Overview

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 is boot_layer / BootLayer and the API is boot_layer_of(), plugins_by_boot_layer(), _declared_boot_layer(), register_global_plugin(..., boot_layer=), audit plugin.boot_layer_rejected, admin field boot_layer / aggregate by_boot_layer. Boot-layer rules — a MECHANISM, today with zero instances above bundled. _GLOBAL_SPECS is empty, register_global_plugin() has no production caller, bootstrap_global() returns [], and nothing loads on compliance/core — so registry.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 only bundled/installed — a privileged claim from tenant.corvin.yaml or registry.yaml is downgraded to installed and audited, never honoured (this one IS live) · origin=community may never claim a privileged boot layer · the compliance boot layer has no off switch (registry.disable() raises PluginDisableRefused) · bootstrap_global() carries no feature flag because it loads the compliance boot layer · only boot_layer=core is replaceable. Guard tests in core/plugins/tests/test_layered_boot.py fail 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


Feature Flags — Ship Dark by Default (load-bearing)

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:

  1. sit behind a named flag in spec.features.<flag_id> of tenant.corvin.yaml;
  2. default to false — off on a fresh install and off after an upgrade (absent key = off; never "on because unset");
  3. be toggleable from the Console Settings → Features panel, no file editing, no restart;
  4. degrade to the pre-feature code path when off — off must be a quiet path, never an error;
  5. 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.

Worker Engine Selection (Settings → Engine)

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 native mode (per-worker context isolation genuinely wins there); everything else stays native.
  • An explicit /delegate from 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.


Testing + Docs Sync (load-bearing)

Before committing changes to adapter.py, daemon.js, or shared/js/:

bash operator/bridges/run-all-tests.sh

Every 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 — Architectural Decision Records

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:

  1. Real design choice was made (chose A over B; constrains future code; genuine alternative existed)
  2. 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


Language

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.