rust-server-architecture
GitHub指导在OrbitDock Rust服务端进行高质量架构设计、代码审查与重构,强调不变量、类型安全边界及状态转换,确保系统健壮性。
Trigger Scenarios
Install
npx skills add Robdel12/OrbitDock --skill rust-server-architecture -g -y
SKILL.md
Frontmatter
{
"name": "rust-server-architecture",
"description": "Use when writing, reviewing, or refactoring OrbitDock Rust server code to implement features and system designs without shortcuts. Covers strong domain modeling, typed boundaries, single-writer persistence, state transitions, additive migrations, connector\/runtime separation, and when to pull in testing-philosophy for confidence."
}
Rust Server Architecture
Use this skill when the work touches orbitdock-server/ and the quality of the design matters as much as the patch itself.
The goal is not just "make the tests pass." The goal is to make the server own durable truth, make invalid states hard or impossible to represent, and let the compiler force correct updates when the model changes.
Start Here
Before changing code, read only the docs that match the task:
docs/GETTING_STARTED.mdfor setup, build commands, and themake rust-*workflowdocs/ARCHITECTURE.mdfor server-authoritative state, typed-boundary expectations, and client/server guardrailsdocs/OPERATIONS.mdwhen schema, persistence, restore, deployment, or database troubleshooting are involveddocs/data-flow.mdwhen HTTP, WebSocket, session surfaces, or conversation rows are involved
If tests are part of the task, also use testing-philosophy.
Workflow
1. State the invariant first
Write down the user-facing or system-facing truth the server must protect.
Examples:
- "steer rows are not user prompts"
- "only the server owns durable approval state"
- "conversation rows get sequence numbers from one writer"
Do not start from the existing shape of the code if that shape is already suspicious.
2. Find the authority boundary
Decide which layer owns the truth:
domain/for business rules and pure state transitionsruntime/for orchestration, actors, registries, and command flowtransport/for HTTP and WebSocket mappinginfrastructure/for SQLite, filesystem, auth, crypto, and external concernsconnectors/for provider-specific translation
If multiple layers can independently "decide" the same thing, the design is probably wrong.
3. Make invalid states unrepresentable
Prefer:
- enums over booleans when there are meaningful modes
- dedicated structs or enum variants over "same shape, different meaning"
- newtypes over raw
Stringoru64when identity matters - typed params structs over long positional argument lists
Do not reuse a variant just because the payload shape matches.
Bad:
enum ConversationRow {
User(MessageRowContent),
}
with steer encoded somewhere else in the payload or inferred by helpers.
Better:
enum ConversationRow {
User(UserPromptRow),
Steer(SteerPromptRow),
}
If changing the meaning should force the compiler to revisit every match arm, it needs its own type.
4. Prefer explicit transitions over scattered conditionals
When behavior depends on state, centralize it in a transition function, reducer, or actor command path.
Prefer a small number of obvious transition points over many helper methods that each tweak one field.
If a fix requires "remember to call this helper everywhere," stop and redesign.
For OrbitDock session memory, the build should enforce this boundary:
- keep
SessionCoreStatefields private - expose reads through snapshots/accessors
- expose writes through domain methods, actor commands, or transition inputs only
- never let transport, HTTP handlers, connectors, or registry code assign business fields directly
- derive affordance fields from primary state instead of storing mutable duplicates
If someone tries to mutate session memory outside that boundary, it should fail at compile time. Do not replace that compiler failure with a helper in the wrong layer.
5. Keep persistence and protocol honest
Existing migration files are immutable history. Do not edit them, comments included; add a new migration instead because refinery validates file checksums.
When durable truth changes:
- update the domain model
- update persistence read/write paths
- update restore and hydration logic
- update protocol types if clients need the field
- verify the client renders server truth instead of reconstructing it
Do not hide a missing persisted concept behind client inference or transcript scanning.
6. Preserve the single-writer path
For conversation rows and other sequence-owned state:
- do not create side-path writes
- do not persist before sequence assignment
- do not let helpers bypass the actor or transition layer
If the design seems to need a second writer, the design almost always needs to be reworked instead.
7. Design for tests, then write the right tests
Use testing-philosophy.
For Rust server work, the usual split is:
- unit tests for pure domain functions and state transitions
- integration tests for persistence, protocol mapping, and component boundaries
- workflow tests for user-visible behavior across runtime paths
Do not mock your own domain model to compensate for a tangled design. Untangle the design.
8. Let tooling push the design upward
- use
make rust-checkfor fast compile feedback - use
make rust-check-workspacewhen shared crates or workspace wiring changed - use
make rust-testfor behavior changes - use
make rust-ciwhen the change is broad or risky
Do not silence Clippy design feedback with #[allow(clippy::...)] unless explicitly approved.
OrbitDock-Specific Non-Negotiables
- The Rust server owns durable state. The client should not derive business truth by replaying history.
- Keep REST for request/response mutations and reads. Use WebSocket for subscriptions, streaming, and broadcasts.
- Keep typed protocol boundaries. Do not replace real schemas with bags of fields.
- SQLite ownership stays in the Rust server.
- Conversation rows must stay on the single-writer persistence path.
- Actor-owned in-memory session state is a projection, not a second source of truth.
- WebSocket transport must not normalize or repair business state; snapshots and deltas come from the actor/domain boundary.
- Mutable maps, locks, and caches are resource ownership tools only. Do not use them as hidden business-state stores.
Smells That Mean “Refactor, Don’t Patch”
Read references/design-smells.md when the change feels deceptively small or "just one more helper" seems tempting.
Common smell:
- the old code keeps compiling after a semantic change because two concepts still share one variant or one payload type
- a transport or HTTP layer "fixes" a field before sending it instead of moving the derivation into the domain projection
- a new public field or setter appears on session state to get a quick UI fix through
That is not safety. That is hidden coupling.
Review Standard
When reviewing or implementing, ask:
- What invariant is the server protecting?
- Which layer is authoritative for that invariant?
- Could the compiler catch a mistaken callsite after this change?
- Did we introduce a second source of truth?
- Are tests proving user-visible outcomes and durable behavior?
If the answer to question 3 is "no" and the concept matters, the typing is probably still too weak.
Version History
- 6926bc3 Current 2026-07-25 08:33


