Thank you for your interest in contributing to CloudBlocks! This guide covers everything you need to get started.
- Code of Conduct
- Getting Started
- Development Setup
- Project Structure
- Making Changes
- Coding Standards
- Documentation
- Pull Request Process
- Architecture Decision Records
- Getting Help
This project follows the Contributor Covenant Code of Conduct. By participating, you agree to uphold a welcoming, inclusive environment.
- Node.js >= 20.0.0
- pnpm >= 9.0.0 (enforced — npm/yarn will be rejected)
- Python >= 3.10 (for backend)
- Git
# Clone the repository
git clone https://github.com/yeongseon/cloudblocks.git
cd cloudblocks
# Install dependencies
pnpm install
# Start the frontend dev server
cd apps/web && pnpm devOpen http://localhost:5173 to verify the app is running.
- Milestone = official planning container (GitHub milestone). Use this term for all new planning work.
- Phase = legacy label from early development. Keep it only when referencing completed historical work for traceability.
# Development server with hot reload
pnpm dev
# Type check (strict mode — must pass before submitting)
cd apps/web && npx tsc -b
# Build
cd apps/web && pnpm build
# Lint
pnpm lintcd apps/api
pip install -e ".[dev]"
uvicorn app.main:app --reload --port 8000Auth implementation notes (current):
- Session auth is cookie-based (
cb_session, httpOnly), not JWT-based - OAuth state uses encrypted httpOnly
cb_oauthcookie - Frontend/backend auth calls must use credentialed requests (
credentials: 'include')
Common tasks are available via make:
make help # Show all available commands
make install # Install all dependencies (frontend + backend)
make dev # Start both frontend and backend
make build # Build frontend
make lint # Run linters
make test # Run backend testscloudblocks/
├── apps/
│ ├── web/ # React frontend (FSD architecture)
│ └── api/ # Python FastAPI backend
├── packages/ # Shared packages (scaffolded)
├── docs/ # Project documentation
│ ├── concept/ # PRD, Architecture, Roadmap
│ ├── model/ # Domain model, schema specs
│ ├── engine/ # Generator, rules, templates, provider
│ ├── design/ # Visual specs, security, release gates
│ ├── api/ # API specification
│ ├── guides/ # Tutorials, deployment
│ └── adr/ # Architecture Decision Records
├── examples/ # Example architecture READMEs
├── infra/ # Deployment scaffolds
└── scripts/ # Dev, build, deploy scripts
The frontend follows Feature-Sliced Design (FSD):
apps/web/src/
├── app/ # App shell, providers, routing
├── shared/ # Types, utils, storage (used everywhere)
│ ├── types/ # Domain types — CANONICAL source for Milestone 1
│ └── utils/ # ID generation, storage operations
├── entities/ # Domain entities (store, blocks, connections)
│ └── validation/ # Validation engine (placement + connection rules)
├── features/ # Feature modules
└── widgets/ # Composite UI widgets (toolbar, palette, panels, scene)
Key rule: Dependencies flow downward only: widgets → features → entities → shared. Never import upward.
CloudBlocks uses two planning paths:
- Roadmap implementation work:
Milestone -> Epic -> Sub-issue -> Branch -> PR - Small fixes, documentation, and maintenance:
Issue -> Branch -> PR
Current roadmap work is tracked under open milestones. Each milestone contains one or more Epic issues that group related sub-issues.
Apply labels before starting work.
epic— required for Epic tracking issues onlysize/S— Small: 1–2 files, < 1 hoursize/M— Medium: 3–5 files, 1–3 hourssize/L— Large: 6–10 files, 3–8 hourssize/XL— Extra Large: 10+ files, 8+ hours
Every non-Epic issue should have exactly one size/* label. Assign it when the issue is created.
Use exactly one type label for every non-Epic issue:
enhancement— feature workbug— defect fixestesting— test coverage, regression tests, test infrastructuredocumentation— docs-only changes
Use one or more domain labels for implementation work:
frontendbackendsecurityauthinfrastructureuxdesign-systemdomain-modelcloud-provider
Documentation issues may omit domain labels when the change is cross-cutting. If the docs clearly belong to one area, add the matching domain label.
| Issue kind | Required labels |
|---|---|
| Epic | epic + 1 or more domain labels |
| Sub-issue (feature) | enhancement + 1 or more domain labels |
| Sub-issue (bug) | bug + 1 or more domain labels |
| Sub-issue (testing) | testing + 1 or more domain labels |
| Documentation | documentation + optional domain label(s) |
| Small fix / maintenance | bug or enhancement + 1 or more domain labels |
Use the full workflow for any feature that spans multiple PRs, multiple domains, or a named roadmap milestone.
-
Create or reuse a milestone
- Create a milestone when the work is a named roadmap milestone or release, spans multiple Epics, or needs shared tracking across multiple implementation issues.
- Milestone names use the format
Milestone N - Name. - Reuse an existing open milestone when the work clearly belongs to it.
-
Create an Epic issue
- Use the title format
[Epic] <feature area>. - Assign the Epic to the milestone.
- Apply the
epiclabel plus one or more domain labels. - Do not apply a type label to an Epic.
- The Epic body must use this structure:
## Overview ## Problems Solved | Problem | Why it matters | | ------- | -------------- | | ... | ... | ## Architecture ## Sub-Issues - [ ] #123 - [ ] #124 ## Dependencies ## Constraints ## Branch Name `feature/<short-description>`
- Use the title format
-
Create sub-issues from the Epic
- Break the Epic into focused implementation units that fit in a single branch and a single PR.
- A good sub-issue usually covers one entity, one route, one UI slice, one provider integration, one migration, or one test suite.
- Assign each sub-issue to the same milestone and link it from the Epic checklist.
- Apply exactly one type label and one or more domain labels.
- Do not apply the
epiclabel to sub-issues.
-
Sync
mainbefore starting work- Always update local
mainbefore creating or refreshing a branch:
git checkout main git pull --ff-only origin main
- Always update local
-
Create a branch from
mainfeature/description— new featuresfix/description— bug fixesdocs/description— documentation changes
-
Implement and open a PR
- Work on one sub-issue per branch.
- Open one PR per branch.
- Reference the issue in the PR description with
Closes #<issue-number>. - Keep each PR focused on one logical change.
-
Merge requirements
- CI must pass before merge.
- Merge via squash-and-merge to
main.
Small bug fixes, documentation updates, and maintenance tasks that do not belong to a milestone may use the simpler Issue -> Branch -> PR flow.
-
Create or find an issue
- Check existing issues first.
- Every PR must reference an issue. Open an issue before starting work.
-
Apply labels
- Use exactly one type label.
- Add one or more domain labels for implementation work.
- Documentation issues may omit domain labels when the scope is cross-cutting.
-
Sync
main- Always update local
mainfirst:
git checkout main git pull --ff-only origin main
- Always update local
-
Create a branch
feature/description— new featuresfix/description— bug fixesdocs/description— documentation changes
-
Implement, verify, and open a PR
- Keep changes focused: one logical change per PR.
- Reference the issue in the PR description with
Closes #<issue-number>. - CI must pass before merge.
- Merge via squash-and-merge to
main.
main branch has the following protections:
- Required status checks must pass before merge
- Force pushes are not allowed
- Branch deletion is not allowed
- Admin enforcement enabled (no bypassing)
pnpm build
pnpm lint- Strict mode is enforced (
strict: true,noUnusedLocals,noUnusedParameters) verbatimModuleSyntax: true— useimport typefor type-only importserasableSyntaxOnly: true— noenum, use union types instead- Never use
as any,@ts-ignore, or@ts-expect-error - Never use empty catch blocks (
catch(e) {})
- The rendering layer uses SVG sprites with CSS transforms for 2.5D isometric projection
- The rendering layer is projection only — the editing model is 2D coordinates with containment hierarchy
- State management uses Zustand (not Redux, not Context)
- Use canonical field names from
apps/web/src/shared/types/index.ts:category(nottype) for blocksplacementId(notplateId) for block placementfrom/toendpoint IDs (notsource/target) for connectionsarchitecture(singular, notarchitectures[]) in workspace
- Connection direction follows the initiator model:
internet → gateway → compute → database/storage - Database and Storage are receiver-only — they never initiate connections
Write clear, descriptive commit messages:
Add drag-and-drop block repositioning # Feature
Fix connection validation for gateway # Bug fix
Update DOMAIN_MODEL.md with connection rules # Documentation
Refactor container block rendering to use shared utils # Refactoring
- New feature → Update relevant docs (PRD, ARCHITECTURE, TUTORIALS)
- Changed behavior → Update DOMAIN_MODEL.md and affected docs
- New field/type → Update both
index.ts(source of truth) and DOMAIN_MODEL.md
- Milestone 1 documents must match code exactly — verify against canonical source files
- Future features must be labeled with version markers (
> **Milestone 6+**: ...) - Field names must match
DOMAIN_MODEL.md/index.ts— never invent your own - ROADMAP.md is canonical for version timelines
- DOMAIN_MODEL.md is canonical for model specification
- generator.md is canonical for code generation pipeline
When docs mix implemented behavior and future design, use these rules:
- Implementation docs must match code
- Verify claims against canonical source files before merging.
- Future concepts must be explicitly labeled
- Use version markers for non-implemented features (for example:
> **Milestone 6+**: ...).
- Use version markers for non-implemented features (for example:
- ROADMAP is the canonical timeline
- Milestone/phase status and "when does X ship?" are defined only in
docs/concept/ROADMAP.md.
- Milestone/phase status and "when does X ship?" are defined only in
- One concept, one canonical owner
DOMAIN_MODEL.mdowns domain model semantics.generator.mdowns generation pipeline semantics.ROADMAP.mdowns timeline semantics.
- Canonical source wins on conflicts
- If two docs disagree, update the non-canonical doc to match the canonical source.
Status labels and meanings must stay consistent with the Document Ownership table in docs/README.md.
| Status | Meaning | Handling rule |
|---|---|---|
| Canonical | Source of truth for active behavior | Update in place when behavior changes; other docs must align to it |
| Canonical (v2.0 Target) | Accepted target spec not yet fully implemented | Keep as forward-looking target; do not rewrite as implemented behavior |
| Supporting | Reference material that explains canonical docs | Keep concise; update links and examples to match canonical docs |
| Historical | Past decisions/specs kept for traceability | Do not evolve behavior here; keep read-only historical context |
| Superseded | Replaced by a newer canonical source | Do not continue active edits; add/keep clear pointer to replacement |
| Accepted (ADR) | Active architectural decision in effect | Keep immutable; create a new ADR if the decision changes |
Decision rules for already-merged documentation:
- Update in place when the doc is Canonical and behavior has changed.
- Demote to Supporting when a doc duplicates canonical content and mainly adds explanation.
- Archive as Historical/Superseded when content reflects past behavior or is replaced by a newer source.
- Merge duplicates when two active docs cover the same canonical concept; keep one owner and convert the other to Supporting or Superseded.
All code changes that affect documented behavior MUST include corresponding documentation updates in the same PR. If no docs change is needed, the PR description must explicitly state why.
- Field names match canonical types (
placementIdnotplateId,categorynottype) - Connection rules follow initiator model (database/storage are receiver-only)
- Connection types are
dataflow,http,internal,data,async -
Workspace.architectureis singular (notarchitectures[]) in Milestone 1 context - Future features have explicit version labels
- Cross-references use correct paths (docs are in subdirectories)
- TypeScript strict mode passes (
tsc -bwith zero errors) - Production build succeeds (
vite build) - Linting passes (
eslint) - No type suppression (
as any,@ts-ignore, etc.) - Changes are focused — one logical change per PR
## Summary
Brief description of what this PR does and why.
## Changes
- List of specific changes
## Verification
- [ ] `tsc -b` passes
- [ ] `vite build` succeeds
- [ ] Linting clean
- [ ] Docs updated for any behavior changes (REQUIRED for code changes)- Submit PR against
main - Automated checks must pass
- Follow current branch protection requirements for approvals (maintainer review is recommended)
- Address review feedback
- Squash and merge
Major architectural decisions are documented in docs/adr/. Each ADR captures the context, decision, and consequences of a significant technical choice.
- Choosing a framework, library, or tool
- Defining a system boundary or integration pattern
- Establishing a convention that affects multiple files
- Making a trade-off between competing approaches
See existing ADRs in docs/adr/ for the template. Each ADR follows:
- Title — Short description of the decision
- Status — Accepted, Superseded, or Deprecated
- Context — Why this decision was needed
- Decision — What was decided
- Consequences — Trade-offs and implications
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Documentation: Start with README.md, then explore
docs/
By contributing to CloudBlocks, you agree that your contributions will be licensed under the Apache License 2.0.