chore: initial scaffold — workspace, docs, blueprint

Sets up the PiCloud monorepo as a Cargo workspace organised around the
three-service architecture (manager / orchestrator / executor), each
backed by a *-core library crate so the same logic powers both the MVP
all-in-one `picloud` binary and the future split-process cluster mode.

  * crates/shared, executor-core, orchestrator-core, manager-core
    define the library surface and trait seams between the three
    services (`ExecutorClient`, `ScriptResolver`, `ScriptRepository`).
  * crates/picloud is the MVP entrypoint; serves /healthz on 8080
    (override via PICLOUD_BIND).
  * crates/picloud-{manager,orchestrator,executor} are skeleton
    binaries that keep the crate boundaries honest until cluster
    mode is built out in v1.3+.
  * docs/git-workflow.md defines the trunk-based workflow:
    short-lived branches, Conventional Commits, separate hotfix
    flow with mandatory reproduction tests.
  * CLAUDE.md captures the working rules for future Claude sessions.

Workspace passes `cargo fmt`, `cargo clippy -D warnings` (with
pedantic enabled), and `cargo test --workspace`. The all-in-one
binary responds on `/healthz` and `/`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-05-22 23:16:32 +02:00
commit b8b544816d
36 changed files with 5843 additions and 0 deletions

View File

@@ -0,0 +1,61 @@
use std::sync::Arc;
use async_trait::async_trait;
use picloud_executor_core::{Engine, ExecError, ExecRequest, ExecResponse};
/// The seam between the orchestrator and the executor.
///
/// Single-node mode plugs in `LocalExecutorClient`, which calls
/// `executor-core` in-process. Cluster mode plugs in `RemoteExecutorClient`,
/// which forwards over HTTP to an executor node. Everything else in
/// orchestrator-core depends only on this trait.
#[async_trait]
pub trait ExecutorClient: Send + Sync {
async fn execute(&self, source: &str, req: ExecRequest) -> Result<ExecResponse, ExecError>;
}
/// In-process executor — wraps `executor-core::Engine` directly.
pub struct LocalExecutorClient {
engine: Arc<Engine>,
}
impl LocalExecutorClient {
#[must_use]
pub fn new(engine: Arc<Engine>) -> Self {
Self { engine }
}
}
#[async_trait]
impl ExecutorClient for LocalExecutorClient {
async fn execute(&self, source: &str, req: ExecRequest) -> Result<ExecResponse, ExecError> {
self.engine.execute(source, req).await
}
}
/// Remote executor — forwards to a peer executor node over HTTP.
///
/// Skeleton only; fleshed out when cluster mode lands.
pub struct RemoteExecutorClient {
_client: reqwest::Client,
_base_url: String,
}
impl RemoteExecutorClient {
#[must_use]
pub fn new(base_url: impl Into<String>) -> Self {
Self {
_client: reqwest::Client::new(),
_base_url: base_url.into(),
}
}
}
#[async_trait]
impl ExecutorClient for RemoteExecutorClient {
async fn execute(&self, _source: &str, _req: ExecRequest) -> Result<ExecResponse, ExecError> {
Err(ExecError::Runtime(
"RemoteExecutorClient not implemented (cluster mode is v1.3+)".into(),
))
}
}