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>
8.4 KiB
Git Workflow
PiCloud uses trunk-based development: a single long-lived branch (main) that is always deployable, with short-lived branches for changes that need review.
This document defines how branches, commits, reviews, and releases work — and how bug fixes flow through the system.
1. Branches
| Branch | Purpose | Lifetime |
|---|---|---|
main |
The trunk. Always green, always deployable. | Permanent |
feat/<topic> |
New feature or non-trivial change. | ≤ 2 days |
fix/<topic> |
Bug fix (non-critical). | ≤ 1 day |
hotfix/<topic> |
Critical bug fix that needs to ship now. | Hours |
chore/<topic> |
Refactor, deps update, CI tweak, docs. | ≤ 1 day |
release/vX.Y |
Long-lived release branch for back-porting hotfixes to a shipped version. | Per minor version, once we tag releases |
Rules:
- Branch directly off
main. Never branch off another feature branch. - Keep branches short. If a branch lives longer than 2 days, you're doing too much in one go — split it.
- Branch names use
kebab-caseafter the prefix:feat/script-crud-endpoints,fix/executor-timeout-leak. - Delete the branch after merge.
2. Commits
Format: Conventional Commits.
<type>(<scope>): <subject>
<body — optional, wrapped at 72 chars>
<footer — optional: BREAKING CHANGE, issue refs>
Types: feat, fix, chore, refactor, docs, test, perf, build, ci.
Scopes (current crates / areas): executor-core, orchestrator-core, manager-core, shared, picloud, dashboard, caddy, compose, ci, docs.
Examples:
feat(executor-core): add operation budget enforcement
fix(orchestrator-core): release Postgres pool guard before dispatch
chore(ci): pin Rust toolchain to 1.92.0
Rules:
- One logical change per commit. If you can't describe it in one line, split it.
- Subject line: imperative, ≤ 72 chars, no trailing period.
- Body explains why, not what — the diff already shows what.
- No
WIP,fixup, oroopscommits onmain. Squash or rewrite before merging.
3. The Feature Loop
┌─────────────────────┐
│ Pull latest main │
└──────────┬──────────┘
│
┌──────────▼──────────┐
│ Branch: feat/<name> │
└──────────┬──────────┘
│
┌──────────▼──────────┐
│ Commit small steps │◄────┐
│ Run tests locally │ │
└──────────┬──────────┘ │
│ │
┌──────────▼──────────┐ │
│ Open PR to main │─────┘ (iterate on feedback)
│ CI must be green │
└──────────┬──────────┘
│
┌──────────▼──────────┐
│ Squash-merge to main│
│ Delete branch │
└─────────────────────┘
Before opening a PR:
git fetch origin
git rebase origin/main # keep history linear
cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --workspace
PR requirements:
- CI green (fmt, clippy, tests, build).
- At least one approving review for non-trivial changes. Solo dev exception: self-review the diff explicitly before merging.
- PR description names why and links to any issue.
- Squash-merge by default → one commit per PR on
main.
4. Bug-fix Workflow
Bugs are classified by severity:
Non-critical bug — normal flow
- Branch
fix/<topic>frommain. - Write a failing test that reproduces the bug.
- Fix it. The test now passes.
- PR → review → squash-merge.
Critical / production hotfix
┌──────────────────┐
│ Bug reported │
└────────┬─────────┘
│
┌───────────▼───────────┐
│ Reproduce locally │ ← required before any fix
│ Write failing test │
└───────────┬───────────┘
│
┌───────────▼───────────┐
│ Branch hotfix/<name> │
│ from main │
└───────────┬───────────┘
│
┌───────────▼───────────┐
│ Minimal fix only │ ← no refactors, no unrelated changes
│ Test passes │
└───────────┬───────────┘
│
┌───────────▼───────────┐
│ Fast-track review │
│ Merge to main │
└───────────┬───────────┘
│
┌───────────▼───────────┐
│ Tag patch release │ (vX.Y.Z+1)
│ Deploy │
└───────────┬───────────┘
│
┌───────────▼───────────┐
│ Cherry-pick to │ ← only if old releases are
│ release/vX.Y if active│ still in use
└───────────────────────┘
Critical hotfix rules:
- A hotfix fixes one bug. Nothing else. No "while I'm here" cleanups.
- The reproduction test stays in the suite forever — it's our guarantee the bug doesn't return.
- If the fix can't be minimal (e.g. requires an architectural change), ship a mitigation as the hotfix (feature flag off, rate limit, revert), then schedule the real fix as a normal feature.
5. Keeping main Green
Trunk-based only works if main is always green. Mechanisms:
- CI gate: PRs cannot merge unless CI passes (
fmt,clippy -D warnings,cargo test --workspace, dashboard build). - Feature flags: Code for incomplete features lives on
mainbehind a flag, off by default. We finish features in small mergeable slices, not in long-lived branches. - Reverts are cheap: If something slips through and breaks
main, revert first, debug after.git revert <merge-sha>and ship. - No force-push to
main. Ever.
6. Releases
Until we ship publicly we deploy off main continuously. Once we have external users:
- Tag releases on
main:v0.1.0,v0.1.1,v0.2.0(SemVer). - Patch releases (
v0.1.1) come from hotfixes; bump the patch number on each fix. - Minor releases (
v0.2.0) come from accumulated features onmain. - For each minor version supported in the wild, create
release/v0.1and cherry-pick patches there. Otherwise don't bother — single trunk is simpler.
Tagging:
git tag -a v0.1.0 -m "v0.1.0 — MVP"
git push origin v0.1.0
7. What We Do Not Do
- No GitFlow. No
develop, noreleasebranches by default, nonext. They add merge overhead without buying anything at our scale. - No long-lived feature branches. If a feature is too big to land in 2 days, split it. Use a feature flag.
- No merge commits on
mainfrom feature branches. Squash-merge keeps history linear and bisectable. - No commits straight to
main— even for typo fixes. Go through a PR (it can take 30 seconds to review and merge). - No
--no-verifyto bypass hooks. If a hook fails, fix the underlying issue.