From 28a3bbd37fec20e0e5865f6655148370eae0a89f Mon Sep 17 00:00:00 2001 From: MechaCat02 Date: Tue, 2 Jun 2026 07:17:29 +0200 Subject: [PATCH] =?UTF-8?q?docs(claude-md):=20clarify=20three-service=20bo?= =?UTF-8?q?undary=20=E2=80=94=20types=20vs=20behavior?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "don't reach across *-core crates" rule was being read as prohibiting any cross-crate import, but the load-bearing intent is to keep *behavior* decoupled (so cluster-mode can swap implementations behind traits in shared). Importing transport DTOs across crates is fine — ExecRequest/ExecResponse/ExecError live in executor-core because that's where they're produced, and the v1.1.1 dispatcher in manager-core legitimately consumes them. Bright line: structs/enums/type-aliases crossing is fine; traits, functions, and service handles crossing is not. Surfaced during the v1.1.1 audit (see REVIEW.md §4). --- CLAUDE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index 1624147..a1aacbb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -100,7 +100,7 @@ docs/ ## Working Rules -- **Honor the three-service boundary.** Don't reach across `*-core` crates. If `orchestrator-core` needs something from `manager-core`, define a trait in `shared` and inject the impl. +- **Honor the three-service boundary.** Don't reach across `*-core` crates *for behavior*. If `orchestrator-core` needs to invoke logic from `manager-core`, define a trait in `shared` and inject the impl — keep implementations decoupled. **Transport DTOs are not behavior**: types like `ExecRequest` / `ExecResponse` / `ExecError` represent values produced or consumed across the wire, and depending on the originating crate's type definitions is fine. The bright line is "don't call across crates," not "don't import types." When in doubt: if the imported item is a `struct`/`enum`/`type alias` with no methods (or only data-shape methods), it's a DTO and crossing is fine; if it's a trait, function, or service, define the abstraction in `shared` and inject. - **`executor-core` has no Postgres dependency.** Data-plane services (kv, docs, users — v1.1+) come in via injected `ServiceProvider` traits. - **Database writes only from `manager-core`.** `orchestrator-core` reads scripts (cached); `executor-core` doesn't touch the DB. - **Stateful SDK services use the handle pattern + `SdkCallCx`.** Collection-scoped surfaces look like `kv::collection("x").get(k)`, not `kv::get("x", k)`. Every service trait method takes `&SdkCallCx` and **MUST** derive `app_id` from `cx.app_id` — never trust a script-passed `app_id`. That is the cross-app isolation boundary. See [docs/sdk-shape.md](docs/sdk-shape.md).