docs(claude-md): clarify three-service boundary — types vs behavior
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).
This commit is contained in:
@@ -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).
|
||||
|
||||
Reference in New Issue
Block a user