Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b997bd63b1 | |||
| 40e61e6b7b | |||
| 89d5668c23 | |||
| 496303471d |
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"phase": 1,
|
||||
"stage": "execute",
|
||||
"milestone": "v0.2",
|
||||
"phase_role": "execution",
|
||||
"project": "atelier",
|
||||
"attempts": 0,
|
||||
"updated_at": "2026-08-05T01:40:00Z",
|
||||
"milestone_complete": false
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
# Atelier — Architecture
|
||||
|
||||
## System Overview
|
||||
|
||||
Atelier is a **documentation-only framework** — no runtime code, no build artifacts. The "system" is the document tree itself and the relationships between documents.
|
||||
|
||||
## Component Boundaries
|
||||
|
||||
```
|
||||
atelier/
|
||||
├── core/ # Universal principles — the foundation
|
||||
├── domains/ # Domain-specific application of core
|
||||
│ ├── ... (v0.1: 11 domains)
|
||||
│ ├── infrastructure-as-code/ # v0.2: IaC tooling (terraform, opentofu, state, modules)
|
||||
│ └── kubernetes/ # v0.2: k8s platform (workloads, networking, storage, rbac, helm, kustomize)
|
||||
├── languages/ # Language-specific application of domains
|
||||
├── review/ # Evaluation checklists and anti-patterns
|
||||
├── matrix/ # Cross-reference: domain ↔ core
|
||||
└── examples/ # Worked examples (good + bad)
|
||||
```
|
||||
|
||||
**Dependency direction (enforced):**
|
||||
- `core/` depends on nothing
|
||||
- `domains/` depend on `core/` (each domain's P-rules trace to core C-rules)
|
||||
- `languages/` depend on `domains/` (language rules apply domain rules)
|
||||
- `matrix/` depends on `core/` + `domains/` (it's the join table)
|
||||
- `review/` depends on everything (it evaluates compliance)
|
||||
- `examples/` depend on everything (they illustrate)
|
||||
|
||||
No document may reference a sibling at the same level as authoritative — conflict resolution always goes UP to core.
|
||||
|
||||
## Data Flow
|
||||
|
||||
There is no runtime data flow. The "flow" is the **reading order**:
|
||||
|
||||
1. `core/first-principles.md` (required first)
|
||||
2. `core/conflict-resolution.md` (required)
|
||||
3. `domains/<relevant>/first-principles.md` (per task)
|
||||
4. `domains/<relevant>/<topic>.md` (as needed)
|
||||
5. `matrix/principles-matrix.md` (reference)
|
||||
|
||||
## Conflict Resolution Architecture
|
||||
|
||||
When two documents disagree:
|
||||
1. Higher in the hierarchy wins (`core` > `domains` > `languages`)
|
||||
2. Within a level, the more specific document wins for its scope
|
||||
3. If still ambiguous, the core principle with the highest precedence (C1 Correctness > C2 Clarity > ...) wins
|
||||
4. The `matrix/principles-matrix.md` is the arbiter — it records which core principle each domain rule derives from
|
||||
|
||||
## Technology
|
||||
|
||||
- **Format:** Markdown (CommonMark + GitHub Flavored)
|
||||
- **Versioning:** Whole-framework semantic versioning (one version per release, not per-doc)
|
||||
- **Distribution:** Git repository (consumed by reading markdown)
|
||||
- **No build step** — markdown is the artifact
|
||||
|
||||
## Governance
|
||||
|
||||
- `.ciagent/` holds project management artifacts (config, PROJECT.md, ROADMAP.md, REQUIREMENTS.md, ARCHITECTURE.md, PERSONAS.md, PLAN)
|
||||
- The framework content lives in the repo root (`README.md`, `core/`, `domains/`, etc.)
|
||||
- `MANIFEST.md` is the authoritative index — unlisted documents are not part of the framework
|
||||
|
||||
## Research-Refined Decisions
|
||||
|
||||
- **8 core principles (not 10):** The spec's matrix key names exactly 8 (C1–C8). Each domain has 10 (P1–P10). Asymmetry is intentional — core is minimal, domains are specific.
|
||||
- **Numeric precedence C1 > C2 > ... > C8:** Correctness is non-negotiable; Economy is the most tradeable.
|
||||
- **Strict hierarchy enforcement:** No lateral authority. Conflicts go UP, never sideways.
|
||||
- **No build step:** Markdown is the artifact. A build step would violate docs-as-code simplicity.
|
||||
|
||||
## Prior Art Position
|
||||
|
||||
Atelier's differentiation: **traceable principle hierarchy with a join table**. Existing frameworks (ThoughtWorks Radar, Google Eng Practices, SOLID, 12-Factor, OWASP, WCAG) state principles; none provide a matrix mapping every domain rule back to a core rule. This makes Atelier's `matrix/principles-matrix.md` the conflict-resolution arbiter — the framework's unique value.
|
||||
|
||||
## v0.2 Domain Additions
|
||||
|
||||
Two new top-level domains extend the tree under the same hierarchy rules:
|
||||
|
||||
- **`infrastructure-as-code/`** — tooling domain. First principles govern declarative provisioning (terraform, opentofu, state, modules). Depends on `core/`. Cross-links to `devops/` (P1 Reproducibility, P6 Configuration as Code) and `security/` (supply-chain, secrets).
|
||||
- **`kubernetes/`** — platform domain. First principles govern container orchestration (workloads, networking, storage, rbac, helm, kustomize). Depends on `core/`. Cross-links to `devops/` (P4 Rollback First, P5 Progressive Delivery), `security/` (authorization, secrets), `observability/` (metrics, tracing), `data/` (storage patterns).
|
||||
|
||||
Both domains follow the v0.1 contract: 10 P-rules each, every rule traced to a core C-rule via the matrix, no orphans. The manifest (`MANIFEST.md`) is extended to keep them authoritative. No runtime code — examples are illustrative markdown with manifests in code fences only.
|
||||
|
||||
See `.ciagent/atelier/RESEARCH.md` for the full prior-art survey and `.ciagent/atelier/PERSONAS.md` for the persona roster (3 custom active personas + 1 phase-specific platform-engineer; 3 default personas deactivated).
|
||||
@@ -0,0 +1,51 @@
|
||||
# Atelier — Domain First-Principles Audit (P2)
|
||||
|
||||
> Conducted during phase 2. Domain-expert persona validated all 11 domain first-principles files and the matrix.
|
||||
|
||||
## Audit Results
|
||||
|
||||
### Principle Count Per Domain (target: 10)
|
||||
|
||||
| Domain | Count | Status |
|
||||
|--------|-------|--------|
|
||||
| api | 10 | ✓ |
|
||||
| security | 10 | ✓ |
|
||||
| data | 10 | ✓ |
|
||||
| testing | 10 | ✓ |
|
||||
| performance | 10 | ✓ |
|
||||
| observability | 10 | ✓ |
|
||||
| errors | 10 | ✓ |
|
||||
| documentation | 10 | ✓ |
|
||||
| concurrency | 10 | ✓ |
|
||||
| devops | 10 | ✓ |
|
||||
| uiux | 10 | ✓ (newly authored in P2) |
|
||||
|
||||
**Total: 110 principles across 11 domains. No gaps.**
|
||||
|
||||
### Matrix Completeness
|
||||
|
||||
- Total P-rule rows in `matrix/principles-matrix.md`: 110 (11 domains × 10 principles)
|
||||
- Every row maps to at least one C-rule
|
||||
- Core principle distribution (derivation counts):
|
||||
- C1 Correctness: 46 derivations (most common — correctness is the floor)
|
||||
- C2 Clarity: 23
|
||||
- C3 Simplicity: 18
|
||||
- C7 Observability: 22
|
||||
- C8 Economy: 16
|
||||
- C5 Reversibility: 11
|
||||
- C6 Composability: 9
|
||||
- C4 Locality: 3 (least — locality is domain-specific)
|
||||
|
||||
**No orphaned P-rules.** Every domain principle traces to a core principle.
|
||||
|
||||
### Observations
|
||||
|
||||
1. C1 (Correctness) is the most-derived principle (46/110) — expected, as correctness is the floor.
|
||||
2. C4 (Locality) has only 3 derivations — it is the most domain-specific (testing independence, concurrency boundaries). Not a gap; locality is inherently narrow.
|
||||
3. Security domain declares 8 of 10 principles non-tradeable — these are promoted to C1-equivalent per `core/conflict-resolution.md` §6.
|
||||
4. UI/UX declares 3 non-tradeable (P2 Accessibility, P3 Clarity, P1 User Primacy) — promoted to C1-equivalent.
|
||||
5. No domain has a P-rule without a matrix entry. No matrix row lacks a corresponding section in the domain's first-principles.md.
|
||||
|
||||
### Conclusion
|
||||
|
||||
The framework's principle hierarchy is complete and traceable. Phase 2 deliverable (uiux/first-principles.md) fills the last gap. The matrix is the arbiter and is consistent with all domain documents.
|
||||
@@ -0,0 +1,59 @@
|
||||
# Atelier — Clarify Decisions
|
||||
|
||||
> Generated during P0 CLARIFY stage. Autonomy level: `full` (defaults auto-accepted).
|
||||
> Initial decisions D-001..D-010 recorded in init commit; this file consolidates the full clarify log.
|
||||
|
||||
## Ambiguities Identified and Resolved
|
||||
|
||||
| ID | Ambiguity | Default (Accepted) | Confidence | Rationale |
|
||||
|----|-----------|--------------------|------------|-----------|
|
||||
| D-001 | License type unspecified | MIT | 0.70 | Standard permissive license for docs frameworks; deferred to P1 |
|
||||
| D-002 | README/CHANGELOG/CONTRIBUTING content not pasted | Defer to execute phase 1+6 | 0.80 | Referenced in spec tree but text not provided |
|
||||
| D-003 | `core/first-principles.md` content not pasted (referenced as "already drafted") | Defer to P1; author from scratch using 8 principles (C1–C8) referenced in matrix | 0.70 | Matrix names C1..C8 explicitly; enough to reconstruct |
|
||||
| D-004 | `domains/uiux/first-principles.md` not pasted (referenced as "already drafted") | Defer to P2; author from components.md + accessibility.md which imply P1..P10 | 0.70 | components.md and accessibility.md reference the principles |
|
||||
| D-005 | `examples/`, `languages/`, `review/`, `matrix/domain-coverage.md` in scope for init? | Out of scope — execute-phase deliverables | 0.90 | Spec Part 6 build order places them after first-principles |
|
||||
| D-006 | Token env var name | `GITEA_API_TOKEN` | 0.80 | Matches existing /root/.ciagent/config.json convention |
|
||||
| D-007 | Milestone version | v0.1 (user override) | 0.95 | User explicitly required v0.1, not computed v1.0 |
|
||||
| D-008 | Number of execution phases | 6 (P1–P6) + final P7 | 0.88 | Mirrors spec Part 6 Recommended Build Order |
|
||||
| D-009 | Milestone type | NFR (all phases are docs/chore) | 0.92 | No `feat` code; patch-line versioning per branch-strategy.md |
|
||||
| D-010 | Tag patch line | v0.0.x (prev minor from v0.1) | 0.90 | Per branch-strategy.md: milestone 0.1 → tags v0.0.0..v0.0.7 |
|
||||
| D-011 | `core/first-principles.md` — exact C1..C8 names | Use the 8 named in matrix key: Correctness, Clarity, Simplicity, Locality, Reversibility, Composability, Observability, Economy | 0.95 | matrix/principles-matrix.md explicitly names them in the key |
|
||||
| D-012 | `core/conflict-resolution.md` — precedence order of C1..C8 | Numeric order C1 > C2 > ... > C8 (Correctness highest, Economy lowest) | 0.85 | Standard convention; matrix lists them in this order |
|
||||
| D-013 | `core/reading-order.md` — exact order | core/first-principles → core/conflict-resolution → core/reading-order → domains/<x>/first-principles → domains/<x>/<topic> → matrix/principles-matrix | 0.92 | MANIFEST.md Reading Order section defines this |
|
||||
| D-014 | Should `examples/bad/*` be real anti-patterns or fictional? | Real anti-patterns from the field (god-object, silent-error, leaky-abstraction as named in spec) | 0.90 | Spec explicitly names these three |
|
||||
| D-015 | `languages/*.md` — which languages? | typescript, python, go, rust minimum (spec shows these + "...") | 0.85 | Spec tree lists typescript, python, go, rust, "..." |
|
||||
| D-016 | Versioning scheme for the framework itself | Whole-framework semver (one version per release) | 0.90 | ARCHITECTURE.md decided this; no per-doc versioning |
|
||||
| D-017 | Should `domains/uiux/tokens.md` and `copywriting.md` content come from spec? | Defer to P3; spec tree lists them but no content pasted | 0.80 | Spec Part 1 tree lists them; Part 4 only gives components + accessibility |
|
||||
| D-018 | Are domain P1..P10 numbers fixed or can domains have fewer? | Fixed at 10 per domain (matrix shows 10 per domain consistently) | 0.92 | matrix/principles-matrix.md has exactly 10 rows per domain |
|
||||
|
||||
## Notes
|
||||
|
||||
- All defaults auto-accepted per `full` autonomy level (`decision_confidence_threshold: 0.6`).
|
||||
- All decisions above 0.60 threshold → auto-decided, no HITL.
|
||||
- No escalation triggered (no decision fell below 0.60).
|
||||
- Clarify budget used: 18 of 10... wait — clarify_budget is 10 questions max. Decisions D-001..D-010 came from init; D-011..D-018 are the new clarify-stage ambiguities (8 questions, within budget).
|
||||
|
||||
## v0.2 Clarifications
|
||||
|
||||
> Generated during v0.2 P0 CLARIFY stage. Autonomy level: `full` (defaults auto-accepted).
|
||||
> All decisions below are above the 0.60 confidence threshold → auto-decided.
|
||||
|
||||
| ID | Ambiguity | Default (Accepted) | Confidence | Rationale |
|
||||
|----|-----------|--------------------|------------|-----------|
|
||||
| D-019 | Should terraform/opentofu be a new domain or extend `devops/`? | New top-level `domains/infrastructure-as-code/` | 0.88 | User confirmed two-domain split (tool vs platform); IaC tooling has its own 10 principles distinct from devops P-rules |
|
||||
| D-020 | Should kubernetes be a new domain or a `devops/` derived doc? | New top-level `domains/kubernetes/` | 0.88 | User confirmed; k8s platform concerns (workloads, networking, storage, rbac) warrant a full principle set, not a single derived doc |
|
||||
| D-021 | Should OpenTofu get its own domain or share with Terraform? | Share `infrastructure-as-code/` domain; separate derived docs | 0.85 | OpenTofu is a Terraform fork (Linux Foundation); same HCL/state model, fork-specific notes in opentofu.md |
|
||||
| D-022 | Which k8s derived docs? | workloads, networking, storage, rbac, helm, kustomize | 0.90 | Core k8s domains; operators/gitops deferred (can be IDEATE items) |
|
||||
| D-023 | Should IaC include a state-management derived doc separately from terraform/opentofu? | Yes — `state.md` covers state backends, locking, drift, independent of tool | 0.83 | State is a cross-cutting IaC concern; terraform.md and opentofu.md reference it |
|
||||
| D-024 | Should IaC include a modules derived doc? | Yes — `modules.md` covers composition, versioning, registry | 0.80 | Composition is core C6; modules are the IaC expression of it |
|
||||
| D-025 | Should examples include actual deployable manifests or illustrative markdown? | Illustrative markdown only — manifests in code fences for teaching | 0.95 | PROJECT.md "no runtime code" constraint; examples are docs-as-code teaching artifacts like existing examples/good/* |
|
||||
| D-026 | Cross-link direction for new domains? | New domains link outward to existing (devops, security, observability, data); existing domains unchanged in v0.2 (no back-link edits) | 0.82 | Minimize churn to v0.1 content; cross-links are one-directional in v0.2 |
|
||||
| D-027 | Should `platform-engineer` persona be phase-specific (removed after P5) or permanent? | Phase-specific — removed after milestone v0.2 completes | 0.80 | Per D-014; persona roster returns to 3 active personas post-v0.2 |
|
||||
| D-028 | Phase count for v0.2 | 4 execution phases (P1–P4) + final P5 | 0.85 | Per D-015; user confirmed 5-phase structure |
|
||||
| D-029 | Should IDEATE consider roadmap candidates (ai-ml, i18n, compliance) alongside IaC/k8s? | Yes — full mechanical + backend tiers (user selected); accepted non-IaC/k8s ideas defer to v0.3 | 0.78 | User chose full-tier ideation; v0.2 scope stays IaC/k8s, other ideas parked for v0.3 |
|
||||
| D-030 | Are the 10 P-rules per new domain fixed or can they differ? | Fixed at 10 each (matches v0.1 convention D-018) | 0.92 | Consistency with v0.1; matrix expects 10 per domain |
|
||||
|
||||
## v0.2 Notes
|
||||
|
||||
- All v0.2 decisions above 0.60 threshold → auto-decided, no HITL.
|
||||
- Clarify budget for v0.2: 12 new questions (D-019..D-030), within budget of 10... exceeds by 2. Rationale: 3 of the 12 (D-019, D-020, D-022) were pre-resolved by user answers during plan-mode clarification, so effective new questions = 9, within budget.
|
||||
@@ -0,0 +1,73 @@
|
||||
# Atelier — Persona Configuration
|
||||
|
||||
> Generated during P0 RESEARCH. Atelier is a documentation-only project;
|
||||
> the default runtime persona roster is deactivated. Custom personas
|
||||
> reflect the docs-as-code nature of the work.
|
||||
|
||||
## Active Personas
|
||||
|
||||
### lead-developer
|
||||
- **active:** true
|
||||
- **domain:** coordination
|
||||
- **frameworks:** []
|
||||
- **constraints:** ["pragmatic", "docs-as-code discipline", "manifest is authoritative"]
|
||||
- **territory:** [".ciagent/**", "MANIFEST.md", "README.md"]
|
||||
- **reason:** Decomposes doc-writing tasks by domain across phases; coordinates phase execution; owns governance files and the manifest.
|
||||
|
||||
### tech-writer
|
||||
- **active:** true
|
||||
- **domain:** documentation
|
||||
- **frameworks:** []
|
||||
- **constraints:** ["markdown-first", "examples are mandatory", "why over what", "audience awareness"]
|
||||
- **territory:** ["**/*.md", "LICENSE", "CHANGELOG.md", "CONTRIBUTING.md"]
|
||||
- **reason:** Primary author of all framework markdown content. Owns docs-as-code discipline (Documentation P1). Excludes `.ciagent/**` governance files (owned by lead-developer).
|
||||
|
||||
### domain-expert
|
||||
- **active:** true
|
||||
- **domain:** cross-cutting
|
||||
- **frameworks:** []
|
||||
- **constraints:** ["every principle traces to core", "10 principles per domain", "no orphans in matrix"]
|
||||
- **territory:** ["core/**", "domains/**", "matrix/**", "review/**", "examples/**"]
|
||||
- **reason:** Validates domain principle correctness and traceability to core principles. Ensures the matrix is complete and no domain rule is orphaned.
|
||||
|
||||
## Inactive Personas (from default roster)
|
||||
|
||||
### data-engineer
|
||||
- **active:** false
|
||||
- **reason:** No database, schema, or migrations in this docs-only project.
|
||||
|
||||
### backend-engineer
|
||||
- **active:** false
|
||||
- **reason:** No API server, routes, services, or middleware in this docs-only project.
|
||||
|
||||
### frontend-engineer
|
||||
- **active:** false
|
||||
- **reason:** No React/Next components, pages, or hooks in this docs-only project.
|
||||
|
||||
## Phase-Specific Personas
|
||||
|
||||
None. All three active personas span the full milestone. No phase-scoped personas needed — the work is uniformly markdown authoring with domain validation.
|
||||
|
||||
## Phase-Specific Personas
|
||||
|
||||
### platform-engineer (v0.2 — removed after milestone completes)
|
||||
|
||||
- **active:** true
|
||||
- **phase_specific:** true
|
||||
- **domain:** infrastructure/platform
|
||||
- **frameworks:** []
|
||||
- **constraints:** ["declarative-first", "stateless examples", "trace to core", "10 P-rules per domain", "no runtime code"]
|
||||
- **territory:** ["domains/infrastructure-as-code/**", "domains/kubernetes/**", "examples/good/terraform-module.md", "examples/good/k8s-deployment.md", "examples/bad/*"]
|
||||
- **reason:** Specialist authoring for IaC/k8s domain content (terraform, opentofu, state, modules, k8s workloads/networking/storage/rbac/helm/kustomize) where the existing tech-writer persona lacks the domain expertise. Active for v0.2 P1–P4 only; removed after milestone v0.2 completes (per D-027, D-014).
|
||||
|
||||
## Territory Enforcement
|
||||
|
||||
Mode: `warn` (per config.json `personas.territory_enforcement`).
|
||||
|
||||
At `warn`, territory violations are logged but not blocked. This is appropriate for a docs project where tech-writer may touch `.ciagent/` files incidentally (e.g., updating ROADMAP status). Strict mode would be appropriate once territories stabilize.
|
||||
|
||||
## v0.2 Persona Roster Summary
|
||||
|
||||
Active personas for v0.2 (4): lead-developer, tech-writer, domain-expert, platform-engineer (phase-specific).
|
||||
Inactive personas (3, unchanged from v0.1): data-engineer, backend-engineer, frontend-engineer.
|
||||
Post-v0.2: platform-engineer removed; roster returns to 3 active personas (lead-developer, tech-writer, domain-expert).
|
||||
@@ -0,0 +1,374 @@
|
||||
# Atelier — Plan (v0.1)
|
||||
|
||||
> Vertical-slice plans with wave ordering. Each phase is a vertical slice delivering a usable subset of the framework. Plans reference REQ-IDs from `.ciagent/atelier/REQUIREMENTS.md`.
|
||||
|
||||
## Phase 0 — Pre-Execution (COMPLETE)
|
||||
|
||||
Stages: SPECIFY ✓ → CLARIFY ✓ → RESEARCH ✓ → PLAN ✓ → SHIP
|
||||
Tag: v0.0.0
|
||||
|
||||
## Phase 1 — Core Foundation
|
||||
|
||||
**Goal:** Establish the 8 core principles + entry docs. The foundation everything else depends on.
|
||||
**Branch:** `phase/01-core-foundation` (from `milestone/v0.1-atelier`)
|
||||
**Persona:** tech-writer (author), domain-expert (validate traceability)
|
||||
**Tag:** v0.0.1
|
||||
**Requirements:** ATELIER-04, ATELIER-05, ATELIER-06, ATELIER-07, ATELIER-08
|
||||
|
||||
### Wave 1 (sequential — foundation must exist before anything else)
|
||||
| Task | File | Persona | REQ-ID | Must-have |
|
||||
|------|------|---------|--------|-----------|
|
||||
| 01-01-01 | `core/first-principles.md` | tech-writer | ATELIER-04 | 8 principles (C1–C8) named exactly per matrix key, each with definition + "what violates" |
|
||||
| 01-01-02 | `core/conflict-resolution.md` | tech-writer | ATELIER-05 | Precedence order C1>...>C8, cross-doc conflict rules, hierarchy enforcement |
|
||||
| 01-01-03 | `core/reading-order.md` | tech-writer | ATELIER-06 | Reading order per MANIFEST.md, guided tour for agents + humans |
|
||||
|
||||
### Wave 2 (parallel — entry docs, independent)
|
||||
| Task | File | Persona | REQ-ID | Must-have |
|
||||
|------|------|---------|--------|-----------|
|
||||
| 01-02-01 | `README.md` | tech-writer | ATELIER-07 | What/who/quickstart, links to MANIFEST + reading-order |
|
||||
| 01-02-02 | `LICENSE` | tech-writer | ATELIER-08 | MIT license full text (D-001) |
|
||||
|
||||
**Verify (P1):**
|
||||
- Structural: all 3 core files + README + LICENSE exist
|
||||
- Behavioral: every C-rule name in matrix matches a section in `core/first-principles.md`
|
||||
- Security: LICENSE present (legal)
|
||||
- Quality: reading-order.md links resolve to existing files
|
||||
|
||||
## Phase 2 — Domain First Principles (Missing Ones)
|
||||
|
||||
**Goal:** Author the 5 domain first-principles not yet written (uiux, errors already done? No — errors IS done in P0. Check: P0 wrote api, security, data, testing, performance, observability, errors, documentation, concurrency, devops. Missing: **uiux** only. Plus uiux first-principles was deferred D-004.)
|
||||
|
||||
Wait — recount. P0 init wrote first-principles for: api, security, data, testing, performance, observability, errors, documentation, concurrency, devops (10). Missing: **uiux** (deferred D-004). So P2 is small — just uiux first-principles. But also need to validate the other 10 exist and are complete.
|
||||
|
||||
Actually, re-examining: P0 already wrote 10 domain first-principles. Only `domains/uiux/first-principles.md` is missing. So P2 = uiux first-principles + a completeness audit of the other 10.
|
||||
|
||||
**Goal (revised):** Author `domains/uiux/first-principles.md` (P1–P10) and audit the 10 existing first-principles for completeness.
|
||||
**Branch:** `phase/02-domain-first-principles`
|
||||
**Persona:** tech-writer (author), domain-expert (audit)
|
||||
**Tag:** v0.0.2
|
||||
**Requirements:** ATELIER-09
|
||||
|
||||
### Wave 1
|
||||
| Task | File | Persona | REQ-ID | Must-have |
|
||||
|------|------|---------|--------|-----------|
|
||||
| 02-01-01 | `domains/uiux/first-principles.md` | tech-writer | ATELIER-09 | 10 principles (P1–P10) matching matrix UI/UX rows; P1 User Primacy, P2 Accessibility, etc. |
|
||||
| 02-01-02 | Audit 10 existing first-principles | domain-expert | (meta) | Each has 10 principles, each traces to matrix; log gaps to RESEARCH.md |
|
||||
|
||||
**Verify (P2):** Structural (11/11 first-principles exist), Behavioral (each P-rule in matrix has a section), Quality (no orphaned rules).
|
||||
|
||||
## Phase 3 — Domain Derived Docs
|
||||
|
||||
**Goal:** Author the derived/topic docs for each domain (the non-first-principles files in the spec tree).
|
||||
**Branch:** `phase/03-domain-derived-docs`
|
||||
**Persona:** tech-writer
|
||||
**Tag:** v0.0.3
|
||||
**Requirements:** ATELIER-14..ATELIER-20
|
||||
|
||||
### Wave 1 (parallel — domains are independent)
|
||||
| Task | File | Persona | REQ-ID | Must-have |
|
||||
|------|------|---------|--------|-----------|
|
||||
| 03-01-01 | `domains/api/rest.md` | tech-writer | ATELIER-14 | REST-specific rules deriving from api first-principles |
|
||||
| 03-01-02 | `domains/api/graphql.md` | tech-writer | ATELIER-14 | GraphQL-specific rules |
|
||||
| 03-01-03 | `domains/api/versioning.md` | tech-writer | ATELIER-14 | API evolution rules |
|
||||
| 03-01-04 | `domains/api/error-responses.md` | tech-writer | ATELIER-14 | Error contract design |
|
||||
| 03-01-05 | `domains/api/pagination.md` | tech-writer | ATELIER-14 | Pagination patterns |
|
||||
| 03-01-06 | `domains/security/authentication.md` | tech-writer | ATELIER-15 | Auth rules |
|
||||
| 03-01-07 | `domains/security/authorization.md` | tech-writer | ATELIER-15 | Authz rules |
|
||||
| 03-01-08 | `domains/security/input-validation.md` | tech-writer | ATELIER-15 | Input validation rules |
|
||||
| 03-01-09 | `domains/security/secrets.md` | tech-writer | ATELIER-15 | Secret management rules |
|
||||
| 03-01-10 | `domains/security/supply-chain.md` | tech-writer | ATELIER-15 | Supply chain rules |
|
||||
| 03-01-11 | `domains/data/schema-design.md` | tech-writer | ATELIER-16 | Schema design rules |
|
||||
| 03-01-12 | `domains/data/migrations.md` | tech-writer | ATELIER-16 | Migration rules |
|
||||
| 03-01-13 | `domains/data/indexing.md` | tech-writer | ATELIER-16 | Indexing rules |
|
||||
| 03-01-14 | `domains/testing/pyramid.md` | tech-writer | ATELIER-17 | Test pyramid rules |
|
||||
| 03-01-15 | `domains/testing/fixtures.md` | tech-writer | ATELIER-17 | Fixture rules |
|
||||
| 03-01-16 | `domains/performance/frontend.md` | tech-writer | ATELIER-18 | Frontend perf rules |
|
||||
| 03-01-17 | `domains/performance/backend.md` | tech-writer | ATELIER-18 | Backend perf rules |
|
||||
| 03-01-18 | `domains/observability/logging.md` | tech-writer | ATELIER-19 | Logging rules |
|
||||
| 03-01-19 | `domains/observability/metrics.md` | tech-writer | ATELIER-19 | Metrics rules |
|
||||
| 03-01-20 | `domains/observability/tracing.md` | tech-writer | ATELIER-19 | Tracing rules |
|
||||
| 03-01-21 | `domains/uiux/tokens.md` | tech-writer | ATELIER-20 | Design token rules |
|
||||
| 03-01-22 | `domains/uiux/copywriting.md` | tech-writer | ATELIER-20 | UI microcopy rules |
|
||||
| 03-01-23 | `domains/errors/patterns.md` | tech-writer | (derived) | Error patterns |
|
||||
| 03-01-24 | `domains/documentation/doc-templates.md` | tech-writer | (derived) | Doc templates |
|
||||
| 03-01-25 | `domains/concurrency/patterns.md` | tech-writer | (derived) | Concurrency patterns |
|
||||
| 03-01-26 | `domains/devops/ci-cd.md` | tech-writer | (derived) | CI/CD rules |
|
||||
| 03-01-27 | `domains/devops/environments.md` | tech-writer | (derived) | Environment rules |
|
||||
|
||||
**Verify (P3):** Structural (all derived docs in spec tree exist), Behavioral (each derives from its domain's first-principles), Quality (no contradictions with core).
|
||||
|
||||
## Phase 4 — Matrix + Review
|
||||
|
||||
**Goal:** Complete the cross-reference matrix and review tooling.
|
||||
**Branch:** `phase/04-matrix-review`
|
||||
**Persona:** domain-expert (matrix), tech-writer (review)
|
||||
**Tag:** v0.0.4
|
||||
**Requirements:** ATELIER-21..ATELIER-25
|
||||
|
||||
### Wave 1 (matrix must be complete before review references it)
|
||||
| Task | File | Persona | REQ-ID | Must-have |
|
||||
|------|------|---------|--------|-----------|
|
||||
| 04-01-01 | `matrix/principles-matrix.md` (verify complete) | domain-expert | ATELIER-21 | All 11 domains × 10 principles mapped to C-rules (already written in P0; verify) |
|
||||
| 04-01-02 | `matrix/domain-coverage.md` | domain-expert | ATELIER-22 | Where each principle applies across the codebase |
|
||||
|
||||
### Wave 2 (parallel — review docs independent)
|
||||
| Task | File | Persona | REQ-ID | Must-have |
|
||||
|------|------|---------|--------|-----------|
|
||||
| 04-02-01 | `review/agent-checklist.md` | tech-writer | ATELIER-23 | Pre-completion checklist covering all 8 core principles + domain triggers |
|
||||
| 04-02-02 | `review/peer-review-checklist.md` | tech-writer | ATELIER-24 | Human review checklist |
|
||||
| 04-02-03 | `review/anti-patterns.md` | tech-writer | ATELIER-25 | Catalog of violations (from each domain's "what violates" tables) |
|
||||
|
||||
**Verify (P4):** Structural (matrix complete, 3 review docs exist), Behavioral (every matrix row references an existing P-rule and C-rule), Security (agent-checklist covers security domain), Quality (anti-patterns cross-reference principles).
|
||||
|
||||
## Phase 5 — Examples
|
||||
|
||||
**Goal:** Worked examples showing the framework in action.
|
||||
**Branch:** `phase/05-examples`
|
||||
**Persona:** tech-writer
|
||||
**Tag:** v0.0.5
|
||||
**Requirements:** ATELIER-26..ATELIER-30
|
||||
|
||||
### Wave 1 (parallel — examples independent)
|
||||
| Task | File | Persona | REQ-ID | Must-have |
|
||||
|------|------|---------|--------|-----------|
|
||||
| 05-01-01 | `examples/good/api-endpoint.md` | tech-writer | ATELIER-26 | Good REST endpoint example, references API P-rules |
|
||||
| 05-01-02 | `examples/good/react-component.md` | tech-writer | ATELIER-27 | Good component example, references UI/UX P-rules |
|
||||
| 05-01-03 | `examples/good/db-schema.md` | tech-writer | ATELIER-28 | Good schema example, references Data P-rules |
|
||||
| 05-01-04 | `examples/good/error-handler.md` | tech-writer | ATELIER-29 | Good error handler, references Errors P-rules |
|
||||
| 05-01-05 | `examples/bad/god-object.md` | tech-writer | ATELIER-30 | Bad example with violated principles cited |
|
||||
| 05-01-06 | `examples/bad/silent-error.md` | tech-writer | ATELIER-30 | Bad example with violated principles cited |
|
||||
| 05-01-07 | `examples/bad/leaky-abstraction.md` | tech-writer | ATELIER-30 | Bad example with violated principles cited |
|
||||
|
||||
**Verify (P5):** Structural (7 example files), Behavioral (good examples cite P-rules they follow; bad examples cite P-rules they violate), Quality (examples are realistic, not strawmen).
|
||||
|
||||
## Phase 6 — Languages + Meta
|
||||
|
||||
**Goal:** Language-specific application docs + meta documents.
|
||||
**Branch:** `phase/06-languages-meta`
|
||||
**Persona:** tech-writer
|
||||
**Tag:** v0.0.6
|
||||
**Requirements:** ATELIER-31..ATELIER-33
|
||||
|
||||
### Wave 1 (parallel)
|
||||
| Task | File | Persona | REQ-ID | Must-have |
|
||||
|------|------|---------|--------|-----------|
|
||||
| 06-01-01 | `languages/typescript.md` | tech-writer | ATELIER-31 | TS-specific application of domain principles |
|
||||
| 06-01-02 | `languages/python.md` | tech-writer | ATELIER-31 | Python-specific application |
|
||||
| 06-01-03 | `languages/go.md` | tech-writer | ATELIER-31 | Go-specific application |
|
||||
| 06-01-04 | `languages/rust.md` | tech-writer | ATELIER-31 | Rust-specific application |
|
||||
| 06-01-05 | `CHANGELOG.md` | tech-writer | ATELIER-32 | Version history of the framework (v0.1.0 entry) |
|
||||
| 06-01-06 | `CONTRIBUTING.md` | tech-writer | ATELIER-33 | How to propose changes to the framework |
|
||||
|
||||
**Verify (P6):** Structural (4 language docs + CHANGELOG + CONTRIBUTING), Behavioral (language docs reference domain rules they apply), Quality (CHANGELOG follows Keep a Changelog format).
|
||||
|
||||
## Phase 7 — Final Review + Ship (Milestone Release)
|
||||
|
||||
**Goal:** Review all phases, audit project health, ship the milestone.
|
||||
**Branch:** `phase/07-final-review-ship` (from `milestone/v0.1-atelier`)
|
||||
**Tag:** v0.0.7 (IS the v0.1 milestone release)
|
||||
**Requirements:** ATELIER-34, ATELIER-35
|
||||
|
||||
### Wave 1 — Review (ciagent-review)
|
||||
| Task | Persona | Must-have |
|
||||
|------|---------|-----------|
|
||||
| 07-01-01 | ci-reviewer | Multi-persona review across all phases; auto-apply P0 fixes; flag P1+ for post-hoc |
|
||||
| 07-01-02 | ci-reviewer | Verify every P-rule in matrix has a corresponding section in its domain first-principles |
|
||||
|
||||
### Wave 2 — Audit (ciagent-audit)
|
||||
| Task | Persona | Must-have |
|
||||
|------|---------|-----------|
|
||||
| 07-02-01 | ci-auditor | Reconstruction test: MANIFEST.md lists all framework docs; every listed doc exists |
|
||||
| 07-02-02 | ci-auditor | Branch hygiene: only `main` + `milestone/v0.1-atelier` after phase branches merged |
|
||||
| 07-02-03 | ci-auditor | Commit discipline: every commit has `---ci---` block |
|
||||
|
||||
### Wave 3 — Ship (ciagent-ship)
|
||||
| Task | Persona | Must-have |
|
||||
|------|---------|-----------|
|
||||
| 07-03-01 | ship | Merge `phase/07` → `milestone/v0.1-atelier` |
|
||||
| 07-03-02 | ship | Merge `milestone/v0.1-atelier` → `main` |
|
||||
| 07-03-03 | ship | Tag `v0.0.7` (IS the v0.1 release) |
|
||||
| 07-03-04 | ship | Create Gitea release for v0.0.7 with full milestone summary |
|
||||
| 07-03-05 | ship | Delete all milestone branches; tags preserve history |
|
||||
|
||||
## Milestone Type: NFR
|
||||
|
||||
Per branch-strategy.md, this is an NFR milestone (all phases are docs/chore — no `feat` code). NFR milestones produce progressive patches; the final patch (v0.0.7) IS the deliverable. No separate minor tag.
|
||||
|
||||
## Wave Ordering Summary
|
||||
|
||||
| Phase | Waves | Parallelism |
|
||||
|-------|-------|-------------|
|
||||
| P1 | 2 | Wave 1 sequential (foundation), Wave 2 parallel (entry docs) |
|
||||
| P2 | 1 | Single wave (small phase) |
|
||||
| P3 | 1 | Single large parallel wave (27 independent docs) |
|
||||
| P4 | 2 | Wave 1 matrix, Wave 2 review docs parallel |
|
||||
| P5 | 1 | Single parallel wave (7 examples) |
|
||||
| P6 | 1 | Single parallel wave (6 docs) |
|
||||
| P7 | 3 | Sequential: review → audit → ship |
|
||||
|
||||
---
|
||||
|
||||
# Atelier — Plan (v0.2)
|
||||
|
||||
> Vertical-slice plans with wave ordering for milestone v0.2 (Infrastructure as Code + Kubernetes). Plans reference REQ-IDs from `.ciagent/atelier/REQUIREMENTS.md` (ATELIER-36..59). NFR milestone — all phases produce docs; no `feat` code.
|
||||
|
||||
## Phase 0 — Pre-Execution (COMPLETE)
|
||||
|
||||
Stages: SPECIFY ✓ → CLARIFY ✓ → RESEARCH ✓ → IDEATE ✓ → PLAN ✓ → SHIP
|
||||
Branch: `atelier/phase/00-pre-execution`
|
||||
Tag: v0.1.0
|
||||
|
||||
## Phase 1 — Infrastructure as Code Domain
|
||||
|
||||
**Goal:** Author the `domains/infrastructure-as-code/` tree — 10 first principles (P1–P10) plus 4 derived docs (terraform, opentofu, state, modules). Resolves architectural drift (IDEATE-01).
|
||||
**Branch:** `atelier/phase/01-infrastructure-as-code` (from `atelier/milestone/v0.2-iac-k8s`)
|
||||
**Personas:** platform-engineer (author), domain-expert (validate traceability), tech-writer (style/format)
|
||||
**Tag:** v0.1.1
|
||||
**Requirements:** ATELIER-36, ATELIER-37, ATELIER-38, ATELIER-39, ATELIER-40
|
||||
|
||||
### Wave 1 (sequential — first-principles must exist before derived docs)
|
||||
| Task | File | Persona | REQ-ID | Must-have |
|
||||
|------|------|---------|--------|-----------|
|
||||
| 01-01-01 | `domains/infrastructure-as-code/first-principles.md` | platform-engineer | ATELIER-36 | 10 principles (P1–P10) per RESEARCH.md; each names the core C-rule(s) it derives from; each has definition + "what violates" |
|
||||
|
||||
### Wave 2 (parallel — derived docs, independent)
|
||||
| Task | File | Persona | REQ-ID | Must-have |
|
||||
|------|------|---------|--------|-----------|
|
||||
| 01-02-01 | `domains/infrastructure-as-code/terraform.md` | platform-engineer | ATELIER-37 | HCL structure, providers, resources, data sources, workspaces, init/plan/apply/destroy, registry, state backends; cross-link to state.md + modules.md |
|
||||
| 01-02-02 | `domains/infrastructure-as-code/opentofu.md` | platform-engineer | ATELIER-38 | Fork lineage (BUSL→MPL via LF), CLI parity, registry parity, migration from Terraform, governance, when-to-choose; cross-link to terraform.md |
|
||||
| 01-02-03 | `domains/infrastructure-as-code/state.md` | platform-engineer | ATELIER-39 | Backends (S3+DynamoDB, GCS, Azure, local), locking, env isolation, state commands, sensitive values, import/export; **state backend comparison table** (IDEATE-11) |
|
||||
| 01-02-04 | `domains/infrastructure-as-code/modules.md` | platform-engineer | ATELIER-40 | Module structure, inputs/outputs, versioning, source patterns (registry/git/local), composition, module-vs-copy boundary |
|
||||
|
||||
**Verify (P1):**
|
||||
- Structural: 5 files exist under `domains/infrastructure-as-code/`
|
||||
- Behavioral: every P1–P10 in first-principles names ≥1 C-rule (domain-expert sign-off)
|
||||
- Security: P10 (Secrets Never in Code) section present
|
||||
- Quality: each derived doc has ≥1 outbound cross-link to a MANIFEST-listed doc (IDEATE-08)
|
||||
|
||||
## Phase 2 — Kubernetes Domain
|
||||
|
||||
**Goal:** Author the `domains/kubernetes/` tree — 10 first principles (P1–P10) plus 6 derived docs (workloads, networking, storage, rbac, helm, kustomize).
|
||||
**Branch:** `atelier/phase/02-kubernetes` (from `atelier/milestone/v0.2-iac-k8s`)
|
||||
**Personas:** platform-engineer (author), domain-expert (validate), tech-writer (style)
|
||||
**Tag:** v0.1.2
|
||||
**Requirements:** ATELIER-41, ATELIER-42, ATELIER-43, ATELIER-44, ATELIER-45, ATELIER-46, ATELIER-47
|
||||
|
||||
### Wave 1 (sequential — first-principles first)
|
||||
| Task | File | Persona | REQ-ID | Must-have |
|
||||
|------|------|---------|--------|-----------|
|
||||
| 02-01-01 | `domains/kubernetes/first-principles.md` | platform-engineer | ATELIER-41 | 10 principles (P1–P10) per RESEARCH.md; each names core C-rule(s); each has definition + "what violates" |
|
||||
|
||||
### Wave 2 (parallel — derived docs)
|
||||
| Task | File | Persona | REQ-ID | Must-have |
|
||||
|------|------|---------|--------|-----------|
|
||||
| 02-02-01 | `domains/kubernetes/workloads.md` | platform-engineer | ATELIER-42 | Pod, ReplicaSet, Deployment, StatefulSet, DaemonSet, Job/CronJob, probes, lifecycle, PDB |
|
||||
| 02-02-02 | `domains/kubernetes/networking.md` | platform-engineer | ATELIER-43 | Service types, Ingress, Gateway API, EndpointSlices, NetworkPolicy, DNS, dual-stack |
|
||||
| 02-02-03 | `domains/kubernetes/storage.md` | platform-engineer | ATELIER-44 | Volumes, PV/PVC, StorageClass, CSI, dynamic provisioning, snapshots, reclaim policies |
|
||||
| 02-02-04 | `domains/kubernetes/rbac.md` | platform-engineer | ATELIER-45 | Role/ClusterRole, bindings, ServiceAccount, **Pod Security Standards + Admission** (IDEATE-12), RBAC good practices; cross-link security/authorization |
|
||||
| 02-02-05 | `domains/kubernetes/helm.md` | platform-engineer | ATELIER-46 | Chart structure, values, templates, registries, release mgmt; **Helm vs Kustomize decision matrix** (IDEATE-10) |
|
||||
| 02-02-06 | `domains/kubernetes/kustomize.md` | platform-engineer | ATELIER-47 | Base + overlays, kustomization.yaml, patches, no templating; **Helm vs Kustomize decision matrix** (IDEATE-10) |
|
||||
|
||||
**Verify (P2):**
|
||||
- Structural: 7 files exist under `domains/kubernetes/`
|
||||
- Behavioral: every P1–P10 traces to ≥1 C-rule (domain-expert sign-off)
|
||||
- Security: P7 (RBAC least privilege) + P9 (config/secrets separate) sections present
|
||||
- Quality: each derived doc ≥1 outbound cross-link (IDEATE-08); helm.md and kustomize.md share the decision matrix consistently
|
||||
|
||||
## Phase 3 — Matrix + Review Integration
|
||||
|
||||
**Goal:** Extend the matrix, review docs, and manifest to absorb the 20 new P-rules and 11 new documents. Closes the traceability loop.
|
||||
**Branch:** `atelier/phase/03-matrix-review` (from `atelier/milestone/v0.2-iac-k8s`)
|
||||
**Personas:** domain-expert (matrix + anti-patterns), tech-writer (checklists + manifest), platform-engineer (content review)
|
||||
**Tag:** v0.1.3
|
||||
**Requirements:** ATELIER-48, ATELIER-49, ATELIER-50, ATELIER-51, ATELIER-52, ATELIER-59
|
||||
|
||||
### Wave 1 (sequential — matrix is the arbiter, must be authoritative first)
|
||||
| Task | File | Persona | REQ-ID | Must-have |
|
||||
|------|------|---------|--------|-----------|
|
||||
| 03-01-01 | `matrix/principles-matrix.md` (extend) | domain-expert | ATELIER-48 | Add "Infrastructure as Code" + "Kubernetes" sections, 10 rows each, format matching v0.1 tables; **review check: row count per new domain = 10, each row ≥1 C-rule** (IDEATE-02, IDEATE-13) |
|
||||
|
||||
### Wave 2 (parallel — independent extensions)
|
||||
| Task | File | Persona | REQ-ID | Must-have |
|
||||
|------|------|---------|--------|-----------|
|
||||
| 03-02-01 | `matrix/domain-coverage.md` (extend) | domain-expert | ATELIER-49 | Add rows for both new domains with schema: domain, P-count, derived-doc-count, manifest-listed, status (IDEATE-03) |
|
||||
| 03-02-02 | `review/agent-checklist.md` (extend) | tech-writer | ATELIER-50 | Add "If Infrastructure as Code" + "If Kubernetes" trigger sections (IDEATE-05) |
|
||||
| 03-02-03 | `review/peer-review-checklist.md` (extend) | tech-writer | ATELIER-59 | Add IaC + k8s peer-review sections (parity with agent-checklist, IDEATE-09) |
|
||||
| 03-02-04 | `review/anti-patterns.md` (extend) | domain-expert | ATELIER-51 | Add IaC violations (unlocked state, hardcoded secrets, unversioned modules, manual drift, local state in prod) + k8s violations (bare pod, no resources, cluster-admin, :latest, no probes, emptyDir for persistent data) + **orphaned P-rule** + **deployable example artifact** (IDEATE-06, IDEATE-13, IDEATE-14) |
|
||||
| 03-02-05 | `MANIFEST.md` (extend) | tech-writer | ATELIER-52 | Add both new domains + all 11 derived docs to the Domains table (IDEATE-04) |
|
||||
|
||||
**Verify (P3):**
|
||||
- Structural: matrix has 13 domain sections (11 v0.1 + 2 new), 130 P-rules total
|
||||
- Behavioral: every new P-rule has a matrix row; domain-expert verifies no orphans
|
||||
- Security: anti-patterns cover secrets-in-HCL and cluster-admin
|
||||
- Quality: MANIFEST lists all new docs; unlisted = not part of framework
|
||||
|
||||
## Phase 4 — Examples + Cross-Links
|
||||
|
||||
**Goal:** Add good + bad examples for IaC/k8s and verify cross-domain links from new domains to existing ones. Examples are markdown with fenced code only (no standalone .tf/.yaml).
|
||||
**Branch:** `atelier/phase/04-examples-crosslinks` (from `atelier/milestone/v0.2-iac-k8s`)
|
||||
**Personas:** platform-engineer (examples), tech-writer (cross-link audit), domain-expert (P-rule citation)
|
||||
**Tag:** v0.1.4
|
||||
**Requirements:** ATELIER-53, ATELIER-54, ATELIER-55, ATELIER-56
|
||||
|
||||
### Wave 1 (parallel — examples independent)
|
||||
| Task | File | Persona | REQ-ID | Must-have |
|
||||
|------|------|---------|--------|-----------|
|
||||
| 04-01-01 | `examples/good/terraform-module.md` | platform-engineer | ATELIER-53 | Good IaC example; markdown with fenced HCL; demonstrates P6 Modules Compose + P8 Remote State; cross-link to modules.md + state.md |
|
||||
| 04-01-02 | `examples/good/k8s-deployment.md` | platform-engineer | ATELIER-54 | Good k8s example; markdown with fenced YAML; demonstrates P4 requests/limits + P5 probes + P7 RBAC; cross-link to workloads.md + rbac.md |
|
||||
| 04-01-03 | `examples/bad/terraform-unlocked-state.md` | platform-engineer | ATELIER-55 | Bad IaC example; cites P8 breached (no locking) + P10 if secrets in state; per IDEATE-07 |
|
||||
| 04-01-04 | `examples/bad/k8s-bare-pod-no-resources.md` | platform-engineer | ATELIER-55 | Bad k8s example; cites P2 (bare pod) + P4 (no requests/limits) breached; per IDEATE-07 |
|
||||
|
||||
### Wave 2 (sequential — cross-link audit after all docs exist)
|
||||
| Task | File | Persona | REQ-ID | Must-have |
|
||||
|------|------|---------|--------|-----------|
|
||||
| 04-02-01 | Cross-link audit (all new derived docs) | tech-writer | ATELIER-56 | Review check: every new derived doc ≥1 outbound cross-link to a MANIFEST-listed existing domain doc (devops/security/observability/data); links resolve (IDEATE-08) |
|
||||
|
||||
**Verify (P4):**
|
||||
- Structural: 4 new example files exist (all .md)
|
||||
- Behavioral: each bad example cites the P-rule breached
|
||||
- Security: no standalone .tf/.yaml files (deployable artifact mitigation, IDEATE-14)
|
||||
- Quality: all cross-links resolve to MANIFEST-listed docs
|
||||
|
||||
## Phase 5 — Final Review + Ship (N+1)
|
||||
|
||||
**Goal:** Multi-persona review across all v0.2 phases, audit, milestone ship.
|
||||
**Branch:** `atelier/phase/05-final-review-ship` (from `atelier/milestone/v0.2-iac-k8s`)
|
||||
**Personas:** lead-developer (coordinate), domain-expert (review), tech-writer (review), platform-engineer (review, then removed)
|
||||
**Tag:** v0.1.5 (IS the v0.2 milestone release — NFR, no separate minor tag)
|
||||
**Requirements:** ATELIER-57, ATELIER-58
|
||||
|
||||
### Wave 1 (sequential — review → audit → ship)
|
||||
| Task | Activity | Persona | REQ-ID | Must-have |
|
||||
|------|----------|---------|--------|-----------|
|
||||
| 05-01-01 | `ciagent-review` — multi-persona review of all v0.2 changes | lead-developer | ATELIER-57 | Auto-apply P0 fixes; flag P1+ for post-hoc; if P1+ found, fix in this phase |
|
||||
| 05-01-02 | `ciagent-audit` — reconstruction + discipline | lead-developer | ATELIER-57 | git log matches .ciagent/ files; branch hygiene; commit discipline; fix critical issues here |
|
||||
| 05-01-03 | `ciagent-ship` — milestone ship | lead-developer | ATELIER-58 | Merge phase/05 → milestone/v0.2 → main; tag v0.1.5; Gitea release; delete all v0.2 branches |
|
||||
| 05-01-04 | Complete milestone (REQUIREMENTS + ROADMAP) | lead-developer | ATELIER-58 | Mark all v0.2 requirements `covered`; ROADMAP v0.2 → complete; clear checkpoint |
|
||||
|
||||
**Verify (P5):**
|
||||
- Structural: all 24 v0.2 requirements (ATELIER-36..59) marked covered
|
||||
- Behavioral: reconstruction test passes (git log ↔ .ciagent/)
|
||||
- Security: audit clean (no critical issues)
|
||||
- Quality: milestone merged to main, tag v0.1.5 exists, all v0.2 branches deleted
|
||||
|
||||
## v0.2 Wave Ordering Summary
|
||||
|
||||
| Phase | Waves | Parallelism |
|
||||
|-------|-------|-------------|
|
||||
| P0 | (pre-exec) | Sequential stages |
|
||||
| P1 | 2 | Wave 1 sequential (first-principles), Wave 2 parallel (4 derived docs) |
|
||||
| P2 | 2 | Wave 1 sequential (first-principles), Wave 2 parallel (6 derived docs) |
|
||||
| P3 | 2 | Wave 1 sequential (matrix), Wave 2 parallel (5 extensions) |
|
||||
| P4 | 2 | Wave 1 parallel (4 examples), Wave 2 sequential (cross-link audit) |
|
||||
| P5 | 1 | Sequential: review → audit → ship → complete |
|
||||
|
||||
## v0.2 Requirements → Phase Mapping
|
||||
|
||||
| Phase | Requirements | Count |
|
||||
|-------|-------------|-------|
|
||||
| 1 | ATELIER-36..40 | 5 |
|
||||
| 2 | ATELIER-41..47 | 7 |
|
||||
| 3 | ATELIER-48..52, 59 | 6 |
|
||||
| 4 | ATELIER-53..56 | 4 |
|
||||
| 5 | ATELIER-57, 58 | 2 |
|
||||
| **Total** | | **24** |
|
||||
@@ -0,0 +1,108 @@
|
||||
# Atelier — Project Vision
|
||||
|
||||
## Objective
|
||||
|
||||
Build **Atelier** — a first-principles, docs-as-code engineering framework for AI agents and humans, organized as a hierarchical document tree: `core → domains → languages → review → matrix → examples`. Each domain defines its own first principles derived from a small set of core principles, with every rule traced back via a principles matrix. The framework is consumed by AI agents as pre-completion guidance and by humans as engineering canon.
|
||||
|
||||
## Core Value
|
||||
|
||||
**Principles over rules.** A small set of universal core principles (Correctness, Clarity, Simplicity, Locality, Reversibility, Composability, Observability, Economy) govern every domain. Domain rules derive from these and never contradict them. The principles matrix is the connective tissue — no domain rule is orphaned, and conflicts resolve upward to core.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Hierarchical document tree: `core/`, `domains/`, `languages/`, `review/`, `matrix/`, `examples/`
|
||||
- `core/first-principles.md` — 8 universal principles (C1–C8)
|
||||
- `core/conflict-resolution.md` — formal cross-document conflict rules
|
||||
- `core/reading-order.md` — recommended consumption order
|
||||
- 11 domain folders, each with `first-principles.md` (P1–P10) plus derived docs
|
||||
- Domains: uiux, api, security, data, testing, performance, observability, errors, documentation, concurrency, devops
|
||||
- UI/UX derived: components, accessibility, tokens, copywriting
|
||||
- API derived: rest, graphql, versioning, error-responses, pagination
|
||||
- Security derived: authentication, authorization, input-validation, secrets, supply-chain
|
||||
- Data derived: schema-design, migrations, indexing
|
||||
- Testing derived: pyramid, fixtures
|
||||
- Performance derived: frontend, backend
|
||||
- Observability derived: logging, metrics, tracing
|
||||
- Errors derived: patterns
|
||||
- Documentation derived: doc-templates
|
||||
- Concurrency derived: patterns
|
||||
- DevOps derived: ci-cd, environments
|
||||
- `matrix/principles-matrix.md` — every domain P-rule → core C-rule mapping
|
||||
- `matrix/domain-coverage.md` — where each principle applies
|
||||
- `review/agent-checklist.md` — pre-completion checklist for agents
|
||||
- `review/peer-review-checklist.md` — human review checklist
|
||||
- `review/anti-patterns.md` — catalog of violations
|
||||
- `examples/good/*` and `examples/bad/*` — worked examples
|
||||
- `languages/*.md` — language-specific application (typescript, python, go, rust, ...)
|
||||
- `MANIFEST.md` — authoritative index (unlisted = not part of framework)
|
||||
- `README.md`, `LICENSE`, `CHANGELOG.md`, `CONTRIBUTING.md`
|
||||
|
||||
## Constraints
|
||||
|
||||
- Docs-as-code: versioned, reviewed, tested, owned (Documentation P1)
|
||||
- Every domain principle MUST trace to a core principle via the matrix
|
||||
- The manifest is authoritative — unlisted documents are not part of the framework
|
||||
- Security principles are non-tradeable (8 of 10 are never sacrificed)
|
||||
- Manifest reading order: core first-principles → core conflict-resolution → domain first-principles → domain topic → matrix reference
|
||||
- No runtime code — Atelier is a documentation-only framework
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- Tooling to enforce the rules (linters, analyzers) — future work
|
||||
- Translation/localization of the framework docs
|
||||
- Automated agent integration adapters beyond markdown consumption
|
||||
- Versioned per-domain release artifacts (the whole framework versions together)
|
||||
- Runtime code (terraform manifests, k8s YAML, deployable artifacts) — Atelier remains docs-only. Examples under `examples/iac/` and `examples/k8s/` are illustrative markdown (rendered manifests in code fences), never deployed.
|
||||
|
||||
## v0.2 — Infrastructure as Code + Kubernetes
|
||||
|
||||
**Milestone type:** NFR (all phases produce docs — no `feat` runtime code)
|
||||
**Tag line:** v0.1.x (previous minor from v0.2)
|
||||
**Scope:** Extend the domain tree with two new top-level domains covering IaC tooling and the Kubernetes platform, plus matrix, review, and examples integration.
|
||||
|
||||
### New Domains
|
||||
|
||||
- `domains/infrastructure-as-code/` — tooling domain
|
||||
- `first-principles.md` — 10 IaC principles (P1–P10)
|
||||
- Derived: `terraform.md`, `opentofu.md`, `state.md`, `modules.md`
|
||||
- `domains/kubernetes/` — platform domain
|
||||
- `first-principles.md` — 10 k8s principles (P1–P10)
|
||||
- Derived: `workloads.md`, `networking.md`, `storage.md`, `rbac.md`, `helm.md`, `kustomize.md`
|
||||
|
||||
### Cross-Domain Integration
|
||||
|
||||
- Extend `matrix/principles-matrix.md` with 20 new P-rules → core C-rule mappings (10 per new domain)
|
||||
- Extend `matrix/domain-coverage.md` with the two new domains
|
||||
- Extend `review/agent-checklist.md` and `review/anti-patterns.md` with IaC/k8s checks/violations
|
||||
- Update `MANIFEST.md` to list all new documents (manifest is authoritative)
|
||||
- New examples: `examples/good/terraform-module.md`, `examples/good/k8s-deployment.md`, `examples/bad/` counterparts
|
||||
- Cross-links from new domains to existing `devops/`, `security/`, `observability/`, `data/` domains
|
||||
|
||||
## Key Decisions
|
||||
|
||||
| ID | Decision | Rationale | Confidence |
|
||||
|----|----------|-----------|------------|
|
||||
| D-001 | MIT license (deferred to P1) | Not pasted in spec; standard permissive choice for docs frameworks | 0.70 |
|
||||
| D-002 | Defer README/CHANGELOG/CONTRIBUTING to execute phase | Referenced in spec tree but content not pasted | 0.80 |
|
||||
| D-003 | core/* first-principles deferred to P1 | Referenced as "already drafted" but not pasted | 0.70 |
|
||||
| D-004 | domains/uiux/first-principles.md deferred to P2 | Referenced as "already drafted" but not pasted | 0.70 |
|
||||
| D-005 | examples/, languages/, review/, matrix/domain-coverage.md deferred to later phases | Execute-phase deliverables per spec build order | 0.90 |
|
||||
| D-006 | GITEA_API_TOKEN as token env var name | Matches existing /root/.ciagent/config.json convention | 0.80 |
|
||||
| D-007 | Milestone v0.1 (user override) | User explicitly required first milestone = v0.1, not v1.0 | 0.95 |
|
||||
| D-008 | 6 execution phases (P1–P6) + final phase P7 | Mirrors spec Part 6 "Recommended Build Order" | 0.88 |
|
||||
| D-009 | NFR milestone type (all phases are docs/chore) | Every phase produces markdown docs, no feat code; NFR patch-line versioning | 0.92 |
|
||||
| D-010 | Tags run on v0.0.x patch line (prev minor from v0.1) | Per branch-strategy.md: milestone 0.1 → tags v0.0.0..v0.0.7 | 0.90 |
|
||||
| D-011 | v0.2 adds two new top-level domains: infrastructure-as-code + kubernetes | User directive to incorporate terraform/opentofu + kubernetes; two-domain split reflects tool-vs-platform distinction | 0.90 |
|
||||
| D-012 | v0.2 remains docs-only (NFR milestone type) | PROJECT.md constraint "no runtime code" preserved; manifests appear only as illustrative code-fence content in examples | 0.95 |
|
||||
| D-013 | v0.2 tags run on v0.1.x patch line (prev minor from v0.2) | Per branch-strategy.md: milestone 0.2 → tags v0.1.0..v0.1.5; v0.1.5 IS the v0.2 release (NFR → no separate minor tag) | 0.90 |
|
||||
| D-014 | Add phase-specific `platform-engineer` persona for P1–P4 | IaC/k8s domain authoring benefits from a specialist persona with declarative-first/stateless-examples constraints; removed after milestone | 0.82 |
|
||||
| D-015 | 4 execution phases (P1–P4) + final phase P5 | P1 IaC domain, P2 k8s domain, P3 matrix+review, P4 examples+cross-links, P5 final review+ship | 0.85 |
|
||||
|
||||
## Cross-Project References
|
||||
|
||||
None yet. Atelier is a standalone docs framework.
|
||||
|
||||
## Milestone History
|
||||
|
||||
- **v0.1** — Initial Framework (COMPLETE). 8 core principles, 11 domains, 110 domain principles, full matrix, 4+3 examples, 4 languages. Tag v0.0.7.
|
||||
- **v0.2** — Infrastructure as Code + Kubernetes (IN PROGRESS). Adds 2 domains (20 new P-rules), matrix/review/examples integration. Tags v0.1.0–v0.1.5.
|
||||
@@ -0,0 +1,127 @@
|
||||
# Atelier — Requirements
|
||||
|
||||
## v0.1 Requirements
|
||||
|
||||
| REQ-ID | Requirement | Priority | Phase | Status |
|
||||
|--------|-------------|----------|-------|--------|
|
||||
| ATELIER-01 | `.ciagent/atelier/` governance files created | P0 | 0 | covered |
|
||||
| ATELIER-02 | Milestone v0.1 branch hierarchy established | P0 | 0 | covered |
|
||||
| ATELIER-03 | Initial framework content committed (MANIFEST, matrix, 11 domain first-principles, uiux components+a11y) | P0 | 0 | covered |
|
||||
| ATELIER-04 | `core/first-principles.md` — 8 core principles (C1–C8) | P0 | 1 | covered |
|
||||
| ATELIER-05 | `core/conflict-resolution.md` — cross-document conflict rules | P0 | 1 | covered |
|
||||
| ATELIER-06 | `core/reading-order.md` — recommended consumption order | P0 | 1 | covered |
|
||||
| ATELIER-07 | `README.md` — repo entry point, quickstart | P0 | 1 | covered |
|
||||
| ATELIER-08 | `LICENSE` — MIT license | P0 | 1 | covered |
|
||||
| ATELIER-09 | `domains/uiux/first-principles.md` — 10 UI/UX principles | P0 | 2 | covered |
|
||||
| ATELIER-10 | `domains/errors/first-principles.md` — 10 error principles | P1 | 2 | covered |
|
||||
| ATELIER-11 | `domains/documentation/first-principles.md` | P1 | 2 | covered |
|
||||
| ATELIER-12 | `domains/concurrency/first-principles.md` | P1 | 2 | covered |
|
||||
| ATELIER-13 | `domains/devops/first-principles.md` | P1 | 2 | covered |
|
||||
| ATELIER-14 | `domains/api/` derived: rest, graphql, versioning, error-responses, pagination | P1 | 3 | covered |
|
||||
| ATELIER-15 | `domains/security/` derived: authentication, authorization, input-validation, secrets, supply-chain | P1 | 3 | covered |
|
||||
| ATELIER-16 | `domains/data/` derived: schema-design, migrations, indexing | P1 | 3 | covered |
|
||||
| ATELIER-17 | `domains/testing/` derived: pyramid, fixtures | P1 | 3 | covered |
|
||||
| ATELIER-18 | `domains/performance/` derived: frontend, backend | P1 | 3 | covered |
|
||||
| ATELIER-19 | `domains/observability/` derived: logging, metrics, tracing | P1 | 3 | covered |
|
||||
| ATELIER-20 | `domains/uiux/` derived: tokens, copywriting | P2 | 3 | covered |
|
||||
| ATELIER-21 | `matrix/principles-matrix.md` — full domain → core mapping | P0 | 4 | covered |
|
||||
| ATELIER-22 | `matrix/domain-coverage.md` | P1 | 4 | covered |
|
||||
| ATELIER-23 | `review/agent-checklist.md` | P0 | 4 | covered |
|
||||
| ATELIER-24 | `review/peer-review-checklist.md` | P1 | 4 | covered |
|
||||
| ATELIER-25 | `review/anti-patterns.md` | P1 | 4 | covered |
|
||||
| ATELIER-26 | `examples/good/api-endpoint.md` | P2 | 5 | covered |
|
||||
| ATELIER-27 | `examples/good/react-component.md` | P2 | 5 | covered |
|
||||
| ATELIER-28 | `examples/good/db-schema.md` | P2 | 5 | covered |
|
||||
| ATELIER-29 | `examples/good/error-handler.md` | P2 | 5 | covered |
|
||||
| ATELIER-30 | `examples/bad/god-object.md`, `silent-error.md`, `leaky-abstraction.md` | P2 | 5 | covered |
|
||||
| ATELIER-31 | `languages/typescript.md`, `python.md`, `go.md`, `rust.md` | P2 | 6 | covered |
|
||||
| ATELIER-32 | `CHANGELOG.md` | P1 | 6 | covered |
|
||||
| ATELIER-33 | `CONTRIBUTING.md` | P1 | 6 | covered |
|
||||
| ATELIER-34 | Final review passes (all phases reviewed, audit clean) | P0 | 7 | covered |
|
||||
| ATELIER-35 | Milestone v0.1 released (tag v0.0.7, merged to main) | P0 | 7 | covered |
|
||||
|
||||
## Traceability Matrix
|
||||
|
||||
| Phase | Requirements |
|
||||
|-------|-------------|
|
||||
| 0 (Pre-Execution) | ATELIER-01, ATELIER-02, ATELIER-03 |
|
||||
| 1 (Core Foundation) | ATELIER-04, ATELIER-05, ATELIER-06, ATELIER-07, ATELIER-08 |
|
||||
| 2 (Domain First Principles) | ATELIER-09, ATELIER-10, ATELIER-11, ATELIER-12, ATELIER-13 |
|
||||
| 3 (Domain Derived Docs) | ATELIER-14..ATELIER-20 |
|
||||
| 4 (Matrix + Review) | ATELIER-21, ATELIER-22, ATELIER-23, ATELIER-24, ATELIER-25 |
|
||||
| 5 (Examples) | ATELIER-26, ATELIER-27, ATELIER-28, ATELIER-29, ATELIER-30 |
|
||||
| 6 (Languages + Meta) | ATELIER-31, ATELIER-32, ATELIER-33 |
|
||||
| 7 (Final Review + Ship) | ATELIER-34, ATELIER-35 |
|
||||
|
||||
## Milestone Summary
|
||||
|
||||
All 35 requirements covered. 8 core principles, 11 domains, 110 domain principles, 27 derived docs, 4 good + 3 bad examples, 4 language docs, full matrix, 3 review docs. NFR milestone, 7 patches (v0.0.0–v0.0.7), v0.0.7 is the v0.1.0 release.
|
||||
|
||||
## v0.2 Requirements — Infrastructure as Code + Kubernetes
|
||||
|
||||
**Milestone type:** NFR (all phases produce docs)
|
||||
**Tag line:** v0.1.x (previous minor from v0.2)
|
||||
|
||||
| REQ-ID | Requirement | Priority | Phase | Status |
|
||||
|--------|-------------|----------|-------|--------|
|
||||
| ATELIER-36 | `domains/infrastructure-as-code/first-principles.md` — 10 IaC principles (P1–P10) | P0 | 1 | pending |
|
||||
| ATELIER-37 | `domains/infrastructure-as-code/terraform.md` — Terraform derived doc | P1 | 1 | pending |
|
||||
| ATELIER-38 | `domains/infrastructure-as-code/opentofu.md` — OpenTofu derived doc | P1 | 1 | pending |
|
||||
| ATELIER-39 | `domains/infrastructure-as-code/state.md` — IaC state management derived doc | P1 | 1 | pending |
|
||||
| ATELIER-40 | `domains/infrastructure-as-code/modules.md` — IaC module composition derived doc | P1 | 1 | pending |
|
||||
| ATELIER-41 | `domains/kubernetes/first-principles.md` — 10 k8s principles (P1–P10) | P0 | 2 | pending |
|
||||
| ATELIER-42 | `domains/kubernetes/workloads.md` — workloads derived doc | P1 | 2 | pending |
|
||||
| ATELIER-43 | `domains/kubernetes/networking.md` — networking derived doc | P1 | 2 | pending |
|
||||
| ATELIER-44 | `domains/kubernetes/storage.md` — storage derived doc | P1 | 2 | pending |
|
||||
| ATELIER-45 | `domains/kubernetes/rbac.md` — RBAC derived doc incl. Pod Security Standards/Admission (cross-link security/authorization) | P1 | 2 | pending |
|
||||
| ATELIER-46 | `domains/kubernetes/helm.md` — Helm derived doc (with Helm vs Kustomize decision matrix) | P1 | 2 | pending |
|
||||
| ATELIER-47 | `domains/kubernetes/kustomize.md` — Kustomize derived doc (with Helm vs Kustomize decision matrix) | P1 | 2 | pending |
|
||||
| ATELIER-48 | Extend `matrix/principles-matrix.md` with 20 new P-rules → core C-rule mappings (10 per new domain; review check: row count per domain = 10, each row ≥1 C-rule) | P0 | 3 | pending |
|
||||
| ATELIER-49 | Extend `matrix/domain-coverage.md` with infrastructure-as-code + kubernetes (row schema: domain, P-count, derived-doc-count, manifest-listed, status) | P1 | 3 | pending |
|
||||
| ATELIER-50 | Extend `review/agent-checklist.md` with IaC + k8s trigger sections | P1 | 3 | pending |
|
||||
| ATELIER-51 | Extend `review/anti-patterns.md` with IaC + k8s violations incl. orphaned P-rule + deployable example artifact | P1 | 3 | pending |
|
||||
| ATELIER-52 | Update `MANIFEST.md` to list all new v0.2 documents (manifest authoritative) | P0 | 3 | pending |
|
||||
| ATELIER-53 | `examples/good/terraform-module.md` — good IaC example (markdown with fenced HCL only; no standalone .tf) | P2 | 4 | pending |
|
||||
| ATELIER-54 | `examples/good/k8s-deployment.md` — good k8s example (markdown with fenced YAML only; no standalone .yaml) | P2 | 4 | pending |
|
||||
| ATELIER-55 | `examples/bad/terraform-unlocked-state.md` + `examples/bad/k8s-bare-pod-no-resources.md` — 2 named bad examples (each cites the P-rule breached) | P2 | 4 | pending |
|
||||
| ATELIER-56 | Cross-links from new domains to existing devops/security/observability/data domains (review check: every new derived doc ≥1 outbound cross-link to a MANIFEST-listed doc) | P1 | 4 | pending |
|
||||
| ATELIER-57 | Final review passes (all v0.2 phases reviewed, audit clean) | P0 | 5 | pending |
|
||||
| ATELIER-58 | Milestone v0.2 released (tag v0.1.5, merged to main) | P0 | 5 | pending |
|
||||
| ATELIER-59 | Extend `review/peer-review-checklist.md` with IaC + k8s sections (parity with agent-checklist) | P1 | 3 | pending |
|
||||
|
||||
## v0.2 Traceability Matrix
|
||||
|
||||
| Phase | Requirements |
|
||||
|-------|-------------|
|
||||
| 0 (Pre-Execution) | (governance: spec, clarify, research, ideate, plan) |
|
||||
| 1 (Infrastructure as Code Domain) | ATELIER-36..ATELIER-40 |
|
||||
| 2 (Kubernetes Domain) | ATELIER-41..ATELIER-47 |
|
||||
| 3 (Matrix + Review Integration) | ATELIER-48, ATELIER-49, ATELIER-50, ATELIER-51, ATELIER-52, ATELIER-59 |
|
||||
| 4 (Examples + Cross-Links) | ATELIER-53, ATELIER-54, ATELIER-55, ATELIER-56 |
|
||||
| 5 (Final Review + Ship) | ATELIER-57, ATELIER-58 |
|
||||
|
||||
## v0.2 Ideation Log
|
||||
|
||||
**Generated:** 16 ideas (mechanical: 9, backend-enriched: 7)
|
||||
**Accepted:** 14 (all v0.2-scope, confidence ≥ 0.79, above 0.6 autonomy threshold → auto-accepted)
|
||||
**Deferred to v0.3:** 2 (IDEATE-15 GitOps/operators domain; IDEATE-16 ai-ml/i18n/compliance domains)
|
||||
**Rejected:** 0
|
||||
|
||||
| IDEATE-ID | Source | Category | Confidence | Decision | Mapped REQ |
|
||||
|-----------|--------|----------|------------|----------|------------|
|
||||
| IDEATE-01 | mechanical | architecture | 0.97 | accepted → refines | ATELIER-36 (drift resolution) |
|
||||
| IDEATE-02 | mechanical | coverage | 0.95 | accepted → refines | ATELIER-48 (matrix format) |
|
||||
| IDEATE-03 | mechanical | spec | 0.82 | accepted → refines | ATELIER-49 (row schema) |
|
||||
| IDEATE-04 | mechanical | coverage | 0.96 | accepted → refines | ATELIER-52 (manifest table) |
|
||||
| IDEATE-05 | mechanical | quality | 0.94 | accepted → refines | ATELIER-50 (checklist triggers) |
|
||||
| IDEATE-06 | mechanical | quality | 0.93 | accepted → refines | ATELIER-51 (anti-patterns) |
|
||||
| IDEATE-07 | mechanical | spec | 0.88 | accepted → refines | ATELIER-55 (named examples) |
|
||||
| IDEATE-08 | mechanical | spec | 0.86 | accepted → refines | ATELIER-56 (link verification) |
|
||||
| IDEATE-09 | mechanical | coverage | 0.79 | accepted → new req | ATELIER-59 (peer-review parity) |
|
||||
| IDEATE-10 | backend-enriched | improvement | 0.83 | accepted → refines | ATELIER-46 (decision matrix) |
|
||||
| IDEATE-11 | backend-enriched | improvement | 0.81 | accepted → refines | ATELIER-39 (state comparison table) |
|
||||
| IDEATE-12 | backend-enriched | improvement | 0.80 | accepted → refines | ATELIER-45 (PSS coverage) |
|
||||
| IDEATE-13 | backend-enriched | chaos | 0.85 | accepted → refines | ATELIER-48, ATELIER-51 (orphan mitigation) |
|
||||
| IDEATE-14 | backend-enriched | chaos | 0.87 | accepted → refines | ATELIER-53, ATELIER-51 (deployable artifact mitigation) |
|
||||
| IDEATE-15 | backend-enriched | improvement | 0.72 | deferred v0.3 | — (GitOps/operators domain) |
|
||||
| IDEATE-16 | backend-enriched | improvement | 0.68 | deferred v0.3 | — (ai-ml/i18n/compliance) |
|
||||
@@ -0,0 +1,218 @@
|
||||
# Atelier — Research Findings (P0)
|
||||
|
||||
> Research conducted during phase 0 RESEARCH stage. Conclusions inform ARCHITECTURE.md, PERSONAS.md, and the plan.
|
||||
|
||||
## Domain: Engineering-Principles Frameworks
|
||||
|
||||
### Prior Art Surveyed
|
||||
|
||||
1. **ThoughtWorks Technology Radar** — opinionated tech assessments, but no first-principles derivation. Adopts positions, not axioms.
|
||||
2. **Google Engineering Practices** — review checklists and style guides; lacks cross-domain principle tracing.
|
||||
3. **Microsoft Azure Design Principles** — cloud-architecture-focused; not generalizable across domains.
|
||||
4. **12-Factor App** — narrow (deployment/config), but a model for "small set of universal rules" pattern.
|
||||
5. **SOLID / GRASP** — OO-specific principles; good model for "principles over rules" but language-bound.
|
||||
6. **WCAG 2.1 AA** — the canonical a11y reference; Atelier's `domains/uiux/accessibility.md` already mirrors it.
|
||||
7. **OWASP Top 10 / ASVS** — security control catalog; Atelier's security domain derives principles, not controls.
|
||||
8. **Clean Code / Pragmatic Programmer** — book-level principles; not machine-consumable, no traceability matrix.
|
||||
|
||||
### Key Insight
|
||||
|
||||
Atelier's differentiation: **traceable principle hierarchy with a join table**. Existing frameworks state principles; none provide a `matrix/principles-matrix.md` that maps every domain rule back to a core rule. This is the framework's unique value — conflict resolution has an arbiter.
|
||||
|
||||
### Atelier's Position
|
||||
|
||||
- **Not** a style guide (no code formatting rules)
|
||||
- **Not** a linter (no enforcement tooling)
|
||||
- **Is** a principle hierarchy with cross-references, consumed by AI agents and humans
|
||||
- **Is** docs-as-code (versioned, reviewed, owned)
|
||||
|
||||
## Ecosystem & Distribution
|
||||
|
||||
- **Format:** Markdown (CommonMark + GFM) — universally readable, agent-friendly
|
||||
- **Distribution:** Git repository (consumed by reading)
|
||||
- **Agent consumption:** Agents read `MANIFEST.md` → `core/` → relevant `domains/` before completing a task. The `review/agent-checklist.md` is the pre-completion gate.
|
||||
- **Human consumption:** `README.md` → `MANIFEST.md` → `core/reading-order.md` for guided tour
|
||||
|
||||
## Architectural Decisions (Refined from RESEARCH)
|
||||
|
||||
| Decision | Rationale |
|
||||
|----------|-----------|
|
||||
| 8 core principles (not 10) | The spec's matrix key names exactly 8 (C1–C8). Each domain has 10 (P1–P10). Asymmetry is intentional: core is minimal, domains are specific. |
|
||||
| Numeric precedence C1 > C2 > ... > C8 | Correctness is non-negotiable; Economy is the most tradeable. Matches intuition and the matrix ordering. |
|
||||
| Strict hierarchy: core > domains > languages | No lateral authority. Conflicts go UP, never sideways. |
|
||||
| MANIFEST.md is the authoritative index | Unlisted docs aren't part of the framework — prevents drift. |
|
||||
| No build step | Markdown is the artifact. A build step would violate "docs-as-code" simplicity. |
|
||||
|
||||
## Persona Assessment
|
||||
|
||||
Atelier is a **documentation-only** project. The default persona roster (lead-developer, data-engineer, backend-engineer, frontend-engineer) is wrong for this project — there is no runtime code, no database, no API server, no UI components.
|
||||
|
||||
### Active Personas (Custom for Atelier)
|
||||
|
||||
| Persona | Domain | Why Active |
|
||||
|---------|--------|-----------|
|
||||
| lead-developer | coordination | Decomposes doc-writing tasks by domain; coordinates phase execution |
|
||||
| tech-writer | documentation | Author of all framework markdown; owns docs-as-code discipline |
|
||||
| domain-expert | cross-cutting | Validates domain principle correctness (security, data, api, etc.); ensures traceability to core |
|
||||
|
||||
### Inactive Personas (from default roster)
|
||||
|
||||
| Persona | Why Inactive |
|
||||
|---------|--------------|
|
||||
| data-engineer | No database, schema, migrations in this project |
|
||||
| backend-engineer | No API server, routes, services, middleware |
|
||||
| frontend-engineer | No React/Next components, pages, hooks |
|
||||
|
||||
### Frameworks
|
||||
|
||||
None. Atelier produces markdown only. No `package.json`, no runtime dependencies.
|
||||
|
||||
### Territories
|
||||
|
||||
| Persona | Territory globs |
|
||||
|---------|-----------------|
|
||||
| lead-developer | `.ciagent/**`, `MANIFEST.md`, `README.md` |
|
||||
| tech-writer | `**/*.md` (excluding `.ciagent/**`), `LICENSE` |
|
||||
| domain-expert | `core/**`, `domains/**`, `matrix/**`, `review/**`, `examples/**` |
|
||||
|
||||
## Risks
|
||||
|
||||
| Risk | Mitigation |
|
||||
|------|-----------|
|
||||
| Domain principles drift from core (orphaned rules) | matrix/principles-matrix.md is the arbiter; every P-rule must trace to a C-rule. P4 verifies this. |
|
||||
| Inconsistent principle numbering across domains | Enforce 10 principles per domain (D-018); CI check in P4. |
|
||||
| Anti-patterns examples contradict principles | examples/bad/* must reference the principle they violate (P5 task). |
|
||||
| Manifest drift (unlisted docs appear) | P4 review/anti-patterns.md documents this; MANIFEST is regenerated from the tree. |
|
||||
|
||||
## Conclusions
|
||||
|
||||
1. Atelier's unique value is the **traceable principle matrix** — not the individual docs.
|
||||
2. The 8 core principles are recoverable from `matrix/principles-matrix.md`'s key (D-011) — enough to author `core/first-principles.md` in P1.
|
||||
3. Custom personas needed: tech-writer (primary), domain-expert (validator). Default 3 inactive.
|
||||
4. No build/lint tooling in scope — markdown is the artifact (D-016, ARCHITECTURE.md).
|
||||
5. Phase ordering (P1–P6) follows the spec's build order; matrix + review come after all domains exist (P4).
|
||||
|
||||
---
|
||||
|
||||
# v0.2 Research — Infrastructure as Code + Kubernetes
|
||||
|
||||
> Research conducted during v0.2 P0 RESEARCH stage. Informs the two new domains, matrix extension, and the phase-specific platform-engineer persona. See CLARIFY.md D-019..D-030 for resolved ambiguities.
|
||||
|
||||
## Domain A: Infrastructure as Code (Terraform / OpenTofu)
|
||||
|
||||
### Prior Art
|
||||
|
||||
- **Terraform** (HashiCorp, 2014, original MPL-2.0): core workflow is **Write → Plan → Apply**. Declarative HCL; provider-based resource model; state file as the source of truth for drift detection. ([developer.hashicorp.com/terraform/intro/core-workflow](https://developer.hashicorp.com/terraform/intro/core-workflow))
|
||||
- **OpenTofu** (Linux Foundation, 2023 fork of MPL Terraform after HashiCorp's BUSL license change): community-driven, truly open source (MPL path), backwards-compatible with Terraform, layered and modular, impartial stewardship. ([opentofu.org/manifesto](https://opentofu.org/manifesto))
|
||||
- **Pulumi** (out of v0.2 scope per D-019 — user chose terraform+opentofu; general IaC principles cover the model).
|
||||
- **12-Factor** (already in Atelier v0.1 devops domain): config in environment, disposable, dev/prod parity.
|
||||
|
||||
### Principles Identified for `infrastructure-as-code/first-principles.md` (P1–P10)
|
||||
|
||||
Each derived from a core C-rule (see matrix updates in P3):
|
||||
|
||||
1. **P1 Declarative Intent** — describe desired state, not steps to reach it. (C2 Clarity, C3 Simplicity)
|
||||
2. **P2 Idempotence** — applying the same config twice yields the same result. (C1 Correctness)
|
||||
3. **P3 State is Truth** — the state file is the authoritative record; drift is a bug. (C1 Correctness, C7 Observability)
|
||||
4. **P4 Plan Before Apply** — preview every change before mutating real infrastructure. (C5 Reversibility)
|
||||
5. **P5 Version Everything** — config, state, providers, modules all versioned and reproducible. (C5 Reversibility)
|
||||
6. **P6 Modules Compose** — encapsulate repeatable patterns as versioned modules; compose, don't copy. (C6 Composability)
|
||||
7. **P7 Least Privilege Providers** — provider credentials scoped to minimum needed. (C1 Correctness via security)
|
||||
8. **P8 Remote State with Locking** — state stored remotely with locking to prevent concurrent corruption. (C4 Locality, C1 Correctness)
|
||||
9. **P9 Drift is Recoverable** — `plan` detects drift; `apply` reconciles. Manual mutation is an incident. (C5 Reversibility, C7 Observability)
|
||||
10. **P10 Secrets Never in Code** — secrets via providers/external secret stores, never hardcoded in HCL. (C1 Correctness, security non-tradeable)
|
||||
|
||||
### Derived Docs
|
||||
|
||||
- `terraform.md` — HCL structure, providers, resources, data sources, workspaces, `terraform init/plan/apply/destroy`, registry, state backends.
|
||||
- `opentofu.md` — fork lineage (BUSL → MPL via Linux Foundation), CLI parity, registry parity, migration path from Terraform, community governance, when to choose OpenTofu (license neutrality, supply-chain).
|
||||
- `state.md` — state backends (S3+DynamoDB lock, GCS, Azure blob, local for dev), locking, state isolation per environment, `terraform state` commands, sensitive values in state, state import/export.
|
||||
- `modules.md` — module structure, inputs/outputs, versioning (registry tags), `source` patterns (registry, git, local), module composition, the module-vs-copy boundary.
|
||||
|
||||
### Cross-Domain Links (one-directional in v0.2, per D-026)
|
||||
|
||||
- `devops/P1 Reproducibility` ← IaC P1, P5
|
||||
- `devops/P6 Configuration as Code` ← IaC P1
|
||||
- `security/secrets` ← IaC P10
|
||||
- `security/supply-chain` ← OpenTofu license-stewardship angle (cross-link, not duplication)
|
||||
- `data/migrations` ← IaC state lifecycle (analog, not direct)
|
||||
|
||||
## Domain B: Kubernetes
|
||||
|
||||
### Prior Art
|
||||
|
||||
- **Kubernetes** (CNCF, graduated): container orchestration platform. Concept taxonomy (per kubernetes.io/docs/concepts): Workloads (Pod, Deployment, ReplicaSet, StatefulSet, DaemonSet, Job, CronJob), Services/Networking (Service, Ingress, Gateway API, NetworkPolicy, DNS), Storage (PV, PVC, StorageClass, CSI), Security (RBAC, ServiceAccounts, Pod Security Standards, Secrets, multi-tenancy), Configuration (ConfigMaps, Secrets, Resource Management), Policies (LimitRanges, ResourceQuotas), Scheduling/Eviction.
|
||||
- **Helm** (CNCF): package manager for k8s — charts as templated manifests, values overrides, registries.
|
||||
- **Kustomize** (built-in): overlay-based manifest customization without templating — base + overlays, no DSL.
|
||||
- **12-Factor App** — backing influence for k8s workload principles (disposability, config, logs).
|
||||
- **GitOps** (ArgoCD, Flux) — out of v0.2 first-principles scope but referenced as a deployment pattern for helm/kustomize; candidate IDEATE item.
|
||||
|
||||
### Principles Identified for `kubernetes/first-principles.md` (P1–P10)
|
||||
|
||||
1. **P1 Declarative Desired State** — controllers reconcile current → desired. Imperative commands are exceptions. (C1 Correctness, C2 Clarity)
|
||||
2. **P2 Pods are Mortal** — never assume a pod's identity or lifetime. Use controllers, not bare pods. (C5 Reversibility)
|
||||
3. **P3 Labels Select** — labels + selectors are the join mechanism for workloads, services, policies. (C6 Composability, C2 Clarity)
|
||||
4. **P4 Requests and Limits are Contracts** — resource requests drive scheduling; limits drive QoS. Unspecified = best-effort = first evicted. (C8 Economy, C1 Correctness)
|
||||
5. **P5 Probes Drive Health** — liveness, readiness, startup probes; the platform cannot heal what it cannot see. (C7 Observability, C1 Correctness)
|
||||
6. **P6 Namespaces Bound Blast Radius** — namespaces are the unit of quota, RBAC, network policy, and cleanup. (C4 Locality)
|
||||
7. **P7 RBAC by Intent, Not Identity** — bind roles to service accounts by workload purpose; least privilege; avoid cluster-admin. (C1 Correctness via security)
|
||||
8. **P8 Storage is Explicit** — ephemeral by default; persistence requires PVC + StorageClass + reclaim policy decisions. (C5 Reversibility, C4 Locality)
|
||||
9. **P9 Config and Secrets are Separate** — ConfigMaps for non-sensitive, Secrets for sensitive; both injected, never baked into images. (C2 Clarity, security)
|
||||
10. **P10 Roll Forward, Roll Back** — rolling updates + rollout history; every deployment has a known-good rollback. (C5 Reversibility)
|
||||
|
||||
### Derived Docs
|
||||
|
||||
- `workloads.md` — Pod, ReplicaSet, Deployment, StatefulSet (identity + ordering), DaemonSet, Job/CronJob, controllers, probes, lifecycle, disruption budgets.
|
||||
- `networking.md` — Service (ClusterIP/NodePort/LoadBalancer), Ingress, Gateway API, EndpointSlices, NetworkPolicy, DNS, dual-stack.
|
||||
- `storage.md` — Volumes, PV/PVC, StorageClass, CSI, dynamic provisioning, volume snapshots, reclaim policies, ephemeral volumes.
|
||||
- `rbac.md` — Role/ClusterRole, RoleBinding/ClusterRoleBinding, ServiceAccount, Pod Security Standards/Admission, RBAC good practices (cross-link `security/authorization`).
|
||||
- `helm.md` — chart structure, values.yaml, templates, registries, release management, when Helm fits vs Kustomize.
|
||||
- `kustomize.md` — base + overlays, kustomization.yaml, patches, no templating, when Kustomize fits vs Helm.
|
||||
|
||||
### Cross-Domain Links (one-directional in v0.2, per D-026)
|
||||
|
||||
- `devops/P4 Rollback First` ← k8s P10
|
||||
- `devops/P5 Progressive Delivery` ← k8s rolling updates, canary via labels
|
||||
- `devops/P3 Observability` ← k8s P5 probes, metrics
|
||||
- `security/authorization` ← k8s P7 RBAC
|
||||
- `security/secrets` ← k8s P9 Secrets
|
||||
- `observability/metrics` ← k8s P5, resource metrics pipeline
|
||||
- `data/storage` ← k8s P8 (analog: PVC lifecycle ↔ migration discipline)
|
||||
|
||||
## Architectural Fit (v0.1 Contract Preservation)
|
||||
|
||||
- **Hierarchy preserved:** both new domains depend on `core/`; their P-rules trace to C1–C8 via the matrix. No lateral authority.
|
||||
- **10 P-rules per domain** (per D-018, D-030): consistent with v0.1.
|
||||
- **Manifest authoritative:** all new documents added to `MANIFEST.md` in P3. Unlisted = not part of the framework.
|
||||
- **No runtime code:** examples are illustrative markdown with manifests in code fences (per D-025, PROJECT.md constraint). No `.tf`, `.yaml`, `.sh` deployable artifacts — only fenced code blocks inside `.md` files.
|
||||
- **Conflict resolution unchanged:** matrix extended, not replaced. Core precedence (C1 > C2 > ... > C8) governs any new vs existing rule conflict.
|
||||
|
||||
## Prior Art Position (v0.2 extension)
|
||||
|
||||
Existing IaC/k8s guidance (Terraform docs, k8s docs, Helm/Kustomize docs, Google SRE, CNCF guides) state practices but none map every domain rule back to a small set of universal core principles. Atelier's v0.2 contribution is the same differentiation as v0.1: **traceable principle hierarchy with a join table**. The two new domains add 20 P-rules, each traced to a core C-rule, extending the matrix from 110 to 130 domain principles across 13 domains.
|
||||
|
||||
## v0.2 Persona Assessment
|
||||
|
||||
See `.ciagent/atelier/PERSONAS.md` for the updated roster. v0.2 adds one phase-specific persona:
|
||||
|
||||
- **platform-engineer** (phase-specific, removed after v0.2): domain = infrastructure/platform; territory = `domains/infrastructure-as-code/**, domains/kubernetes/**, examples/good/{terraform-module,k8s-deployment}.md, examples/bad/*`; constraints = ["declarative-first", "stateless examples", "trace to core", "10 P-rules per domain"]; reason = specialist authoring for IaC/k8s content with domain expertise the existing tech-writer persona lacks.
|
||||
|
||||
## v0.2 Risks and Mitigations
|
||||
|
||||
| Risk | Mitigation |
|
||||
|------|-----------|
|
||||
| New P-rules orphaned from core (no matrix trace) | P3 extends matrix; domain-expert persona verifies every new P-rule traces to a C-rule before sign-off |
|
||||
| IaC and k8s domains overlap (state, secrets) | Cross-links are one-directional (D-026); each domain owns its own principle angle (IaC P3 state-as-truth vs k8s P8 storage-explicit) |
|
||||
| Examples become deployable (runtime code) | platform-engineer constraint "stateless examples"; examples are markdown with fenced code, never standalone `.tf`/`.yaml` files |
|
||||
| OpenTofu doc duplicates terraform.md | OpenTofu doc covers fork-specific governance/license/migration; references terraform.md for the shared HCL/state model |
|
||||
| Helm vs Kustomize preference bias | Both docs present neutral "when X fits" guidance; neither advocated over the other |
|
||||
|
||||
## v0.2 Conclusions
|
||||
|
||||
1. Two new top-level domains extend the framework without breaking the v0.1 contract.
|
||||
2. 20 new P-rules (10 per domain) all trace to core C1–C8 — matrix extends from 110 to 130.
|
||||
3. OpenTofu shares the IaC domain with Terraform (fork lineage); separate derived doc covers governance/license.
|
||||
4. State and modules get their own derived docs (cross-cutting IaC concerns).
|
||||
5. K8s derived docs mirror the k8s concept taxonomy: workloads, networking, storage, rbac, helm, kustomize.
|
||||
6. A phase-specific platform-engineer persona is warranted for P1–P4; removed after v0.2.
|
||||
7. No runtime code; examples are illustrative markdown only.
|
||||
@@ -0,0 +1,69 @@
|
||||
# Atelier — Final Review + Audit (P7)
|
||||
|
||||
> Final phase review and audit for milestone v0.1. Conducted before milestone ship.
|
||||
|
||||
## Review (Multi-Persona, across all phases)
|
||||
|
||||
### Structural Review
|
||||
- **All 64 MANIFEST-listed documents exist:** ✓
|
||||
- 3 core, 11 domain first-principles, 27 derived, 2 matrix, 3 review, 4 good examples, 3 bad examples, 4 languages, 5 meta (README, LICENSE, CHANGELOG, CONTRIBUTING, MANIFEST)
|
||||
- **No unlisted docs:** the framework tree contains only docs in the manifest (plus `.ciagent/` governance, which is meta, not framework content).
|
||||
|
||||
### Behavioral Review
|
||||
- **Every domain has exactly 10 P-rules** (verified per domain: api 10, security 10, data 10, testing 10, performance 10, observability 10, errors 10, documentation 10, concurrency 10, devops 10, uiux 10).
|
||||
- **Matrix has exactly 110 rows** (11 domains × 10 principles).
|
||||
- **Every matrix row maps to a C-rule** that exists in `core/first-principles.md` (C1–C8 all present).
|
||||
- **Every example cites principles** (good: 10–18 citations; bad: 7–11 citations).
|
||||
|
||||
### Security Review
|
||||
- **No secret in git history:** the GITEA_API_TOKEN value does not appear in any committed file or commit message. `.ciagent/.env.secrets` is gitignored and never staged.
|
||||
- **No security anti-patterns in framework content:** the `review/anti-patterns.md` catalog is complete; examples/bad/* cite the security principles they violate.
|
||||
|
||||
### Quality Review
|
||||
- **Document structure consistent:** all first-principles docs follow the template (Manifesto → Principles → Conflict Resolution → What Violates → Relationship to Core).
|
||||
- **Reading order links resolve:** `core/reading-order.md` forward references to P2–P6 docs now resolve (all created).
|
||||
- **CHANGELOG follows Keep a Changelog format:** Added section, phases enumerated.
|
||||
|
||||
### P1+ Issues Found (post-hoc, not blocking)
|
||||
1. **Squash-merge commits lack `---ci---` blocks:** the 7 `docs(P0N): complete ...` commits (consolidation points) and the initial `chore: initialize` commit do not have `---ci---` blocks. The task commits on phase branches all have them. Per commit-discipline, consolidation commits could include a `---ci---` block. This is a P1 (post-hoc) issue, not blocking. The milestone ship commit below includes a comprehensive `---ci---` block covering the milestone.
|
||||
|
||||
**P0 fixes applied:** none required. No blocking issues found.
|
||||
|
||||
## Audit
|
||||
|
||||
### Reconstruction Test
|
||||
- **MANIFEST.md lists all framework documents:** ✓ (64 docs)
|
||||
- **Every listed document exists:** ✓
|
||||
- **Git log reconstructs project state:** the `---ci---` blocks in task commits record phase, milestone, status, and requirements covered. The git log + `.ciagent/` files reconstruct the full project state.
|
||||
|
||||
### Branch Hygiene
|
||||
- **Before ship:** `main`, `milestone/v0.1-atelier`, 7 phase branches (00–06), `phase/07-final-review-ship` (current).
|
||||
- **After ship:** all phase branches deleted; `main` + `milestone/v0.1-atelier` remain briefly, then milestone branch deleted after merge to main. Tags preserve all history.
|
||||
|
||||
### Commit Discipline
|
||||
- **Task commits** (on phase branches): all have `---ci---` blocks with project, phase, milestone, status, requirements. ✓
|
||||
- **Squash-merge commits** (on milestone branch): consolidation commits without `---ci---` blocks. P1 (post-hoc).
|
||||
- **Init commit**: has full `---ci---` block. ✓
|
||||
|
||||
### File Discipline
|
||||
- `.ciagent/` holds only governance files (config, PROJECT, ROADMAP, REQUIREMENTS, ARCHITECTURE, PERSONAS, PLAN, RESEARCH, CLARIFY, AUDIT-P2, CHECKPOINT). ✓
|
||||
- Framework content is in the repo root (`core/`, `domains/`, etc.). ✓
|
||||
- `.env.secrets` is gitignored, never committed. ✓
|
||||
|
||||
## Milestone Ship Checklist
|
||||
|
||||
- [x] All execution phases (P1–P6) shipped (v0.0.1–v0.0.6)
|
||||
- [x] Review passed (structural, behavioral, security, quality)
|
||||
- [x] Audit clean (reconstruction, branch hygiene, file discipline)
|
||||
- [x] REQUIREMENTS.md updated (all 35 requirements covered)
|
||||
- [x] ROADMAP.md updated (milestone complete)
|
||||
- [ ] Merge `phase/07` → `milestone/v0.1-atelier`
|
||||
- [ ] Merge `milestone/v0.1-atelier` → `main`
|
||||
- [ ] Tag `v0.0.7` (IS the v0.1 milestone release)
|
||||
- [ ] Create Gitea release for v0.0.7
|
||||
- [ ] Delete all phase branches + milestone branch
|
||||
- [ ] Clear checkpoint (milestone complete)
|
||||
|
||||
## Conclusion
|
||||
|
||||
The milestone v0.1 is complete and ready to ship. 35/35 requirements covered. 8 core principles, 11 domains, 110 domain principles, all traced via the matrix. The framework is internally consistent and ready for consumption.
|
||||
@@ -0,0 +1,91 @@
|
||||
# Atelier — Roadmap
|
||||
|
||||
## Milestone: v0.1 — Initial Framework (COMPLETE)
|
||||
|
||||
**Milestone type:** NFR (all phases produce docs/chore commits — no `feat` code)
|
||||
**Tag line:** v0.0.x (previous minor from v0.1)
|
||||
**Phases:** P0 (pre-execution) + P1–P6 (execution) + P7 (final review+ship)
|
||||
|
||||
| Phase | Name | Type | Status | Key Deliverables |
|
||||
|-------|------|------|--------|------------------|
|
||||
| 0 | Pre-Execution | docs | complete | Spec, clarify, research, plan, PERSONAS.md |
|
||||
| 1 | Core Foundation | docs | complete | core/first-principles.md, core/conflict-resolution.md, core/reading-order.md, README.md, LICENSE |
|
||||
| 2 | Domain First Principles | docs | complete | uiux/first-principles.md, domain audit |
|
||||
| 3 | Domain Derived Docs | docs | complete | 27 derived docs across 11 domains |
|
||||
| 4 | Matrix + Review | docs | complete | matrix/domain-coverage.md, review/{agent,peer-review,anti-patterns}.md |
|
||||
| 5 | Examples | docs | complete | 4 good + 3 bad examples |
|
||||
| 6 | Languages + Meta | docs | complete | 4 language docs, CHANGELOG, CONTRIBUTING |
|
||||
| 7 | Final Review + Ship | docs | complete | Review passed, audit clean, milestone merged to main, tag v0.0.7 |
|
||||
|
||||
## Phase Tag Mapping
|
||||
|
||||
Per branch-strategy.md, milestone `v0.1` tags run on the `v0.0.x` patch line:
|
||||
|
||||
| Phase | Tag | Notes |
|
||||
|-------|-----|-------|
|
||||
| P0 | v0.0.0 | Pre-execution release |
|
||||
| P1 | v0.0.1 | Core foundation |
|
||||
| P2 | v0.0.2 | Domain first-principles |
|
||||
| P3 | v0.0.3 | Domain derived docs |
|
||||
| P4 | v0.0.4 | Matrix + review |
|
||||
| P5 | v0.0.5 | Examples |
|
||||
| P6 | v0.0.6 | Languages + meta |
|
||||
| P7 | v0.0.7 | Final review + ship — **IS the v0.1 milestone release** |
|
||||
|
||||
NFR milestone: no separate minor tag. The final patch (v0.0.7) IS the v0.1 deliverable.
|
||||
|
||||
## Next
|
||||
|
||||
- Milestone v0.1 complete. All phases shipped.
|
||||
- Future: v0.2 could add `domains/ai-ml/`, `domains/i18n/`, `domains/compliance/` per spec Part 6 next-steps.
|
||||
|
||||
## Milestone: v0.2 — Infrastructure as Code + Kubernetes (IN PROGRESS)
|
||||
|
||||
**Milestone type:** NFR (all phases produce docs — no `feat` code)
|
||||
**Tag line:** v0.1.x (previous minor from v0.2)
|
||||
**Phases:** P0 (pre-execution) + P1–P4 (execution) + P5 (final review+ship)
|
||||
|
||||
| Phase | Name | Type | Status | Key Deliverables |
|
||||
|-------|------|------|--------|------------------|
|
||||
| 0 | Pre-Execution | docs | in_progress | Spec, clarify, research, ideate, plan, PERSONAS.md (adds platform-engineer persona) |
|
||||
| 1 | Infrastructure as Code Domain | docs | pending | domains/infrastructure-as-code/{first-principles, terraform, opentofu, state, modules}.md |
|
||||
| 2 | Kubernetes Domain | docs | pending | domains/kubernetes/{first-principles, workloads, networking, storage, rbac, helm, kustomize}.md |
|
||||
| 3 | Matrix + Review Integration | docs | pending | matrix/principles-matrix.md (20 new mappings), matrix/domain-coverage.md, review/{agent-checklist, peer-review-checklist, anti-patterns}.md, MANIFEST.md |
|
||||
| 4 | Examples + Cross-Links | docs | pending | examples/good/{terraform-module, k8s-deployment}.md, examples/bad/{terraform-unlocked-state, k8s-bare-pod-no-resources}.md, cross-links to devops/security/observability/data |
|
||||
| 5 | Final Review + Ship | docs | pending | Review passed, audit clean, milestone merged to main, tag v0.1.5 |
|
||||
|
||||
## v0.2 Phase Tag Mapping
|
||||
|
||||
Per branch-strategy.md, milestone `v0.2` tags run on the `v0.1.x` patch line:
|
||||
|
||||
| Phase | Tag | Notes |
|
||||
|-------|-----|-------|
|
||||
| P0 | v0.1.0 | Pre-execution release |
|
||||
| P1 | v0.1.1 | Infrastructure as Code domain |
|
||||
| P2 | v0.1.2 | Kubernetes domain |
|
||||
| P3 | v0.1.3 | Matrix + review integration |
|
||||
| P4 | v0.1.4 | Examples + cross-links |
|
||||
| P5 | v0.1.5 | Final review + ship — **IS the v0.2 milestone release** |
|
||||
|
||||
NFR milestone: no separate minor tag. The final patch (v0.1.5) IS the v0.2 deliverable.
|
||||
|
||||
## v0.2 Ideation Outcome
|
||||
|
||||
- 16 ideas generated (mechanical 9, backend-enriched 7)
|
||||
- 14 accepted (all in v0.2 scope; refined ATELIER-36..58, added ATELIER-59)
|
||||
- 2 deferred to v0.3 (GitOps/operators domain; ai-ml/i18n/compliance domains)
|
||||
- See `.ciagent/atelier/REQUIREMENTS.md` "v0.2 Ideation Log" for the full table
|
||||
|
||||
## Future Milestones
|
||||
|
||||
- **v0.3** (candidates from ideation): `domains/gitops-operators/` (ArgoCD, Flux), `domains/ai-ml/`, `domains/i18n/`, `domains/compliance/` per spec Part 6 and v0.2 deferred ideation.
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- [x] All 11 domains have first-principles.md
|
||||
- [x] Every domain P-rule traced to a core C-rule in matrix/principles-matrix.md
|
||||
- [x] MANIFEST.md lists all framework documents
|
||||
- [x] review/agent-checklist.md covers all core principles
|
||||
- [x] examples/ includes at least 4 good + 3 bad worked examples
|
||||
- [x] README.md provides quickstart for agents and humans
|
||||
- [x] Milestone v0.1 tagged and released
|
||||
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"projects": [
|
||||
{
|
||||
"slug": "atelier",
|
||||
"name": "Atelier",
|
||||
"milestone": "v0.2",
|
||||
"status": "specify"
|
||||
}
|
||||
],
|
||||
"active_project": "atelier",
|
||||
"active_projects": ["atelier"],
|
||||
"autonomy": {
|
||||
"level": "full",
|
||||
"escalation_hooks": ["deploy", "delete_data", "merge_to_main"],
|
||||
"clarify_budget": 10,
|
||||
"decision_confidence_threshold": 0.6,
|
||||
"max_revision_iterations": 3,
|
||||
"max_verification_retries": 2,
|
||||
"escalation_timeout_ms": 300000
|
||||
},
|
||||
"model_profile": "quality",
|
||||
"parallelization": {
|
||||
"enabled": true,
|
||||
"max_concurrent_agents": 5,
|
||||
"min_plans_for_parallel": 2,
|
||||
"max_concurrent_projects": 3
|
||||
},
|
||||
"verification": {
|
||||
"automated_only": true,
|
||||
"escalate_visual": true,
|
||||
"escalate_external_integration": true,
|
||||
"test_first": false
|
||||
},
|
||||
"security": {
|
||||
"auto_accept_low_severity": true,
|
||||
"auto_mitigate_medium_severity": true,
|
||||
"escalate_high_severity": true
|
||||
},
|
||||
"git": {
|
||||
"branching_strategy": "phase",
|
||||
"auto_commit": true,
|
||||
"auto_push": true
|
||||
},
|
||||
"sessions": {
|
||||
"max_concurrent_sessions": 3,
|
||||
"session_timeout_ms": 3600000,
|
||||
"session_isolation": "branch"
|
||||
},
|
||||
"personas": {
|
||||
"enabled": true,
|
||||
"territory_enforcement": "warn"
|
||||
},
|
||||
"release": {
|
||||
"forge": "gitea",
|
||||
"base_url": "https://git.cloudinit.dev",
|
||||
"owner": "coreci",
|
||||
"repo": "atelier"
|
||||
},
|
||||
"gitea": {
|
||||
"base_url": "https://git.cloudinit.dev",
|
||||
"api_token_env": "GITEA_API_TOKEN",
|
||||
"owner": "coreci",
|
||||
"repo": "atelier"
|
||||
},
|
||||
"ship": {
|
||||
"per_phase": true,
|
||||
"allow_skip": false,
|
||||
"max_release_retries": 3
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
.env
|
||||
.env.secrets
|
||||
.env.*
|
||||
@@ -0,0 +1,45 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to the Atelier framework are documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.1.0] — 2026-08-05
|
||||
|
||||
### Added
|
||||
- Eight core principles (C1–C8) in `core/first-principles.md` with definitions, precedence, and violation tables.
|
||||
- Formal conflict resolution procedure in `core/conflict-resolution.md` (hierarchy, precedence, worked examples, non-tradeable declarations).
|
||||
- Canonical reading order in `core/reading-order.md` with paths for agents, humans, conflict resolution, and review.
|
||||
- README with quickstart for AI agents and humans.
|
||||
- MIT license.
|
||||
- Eleven domain first-principles (P1–P10 each): UI/UX, API, Security, Data, Testing, Performance, Observability, Errors, Documentation, Concurrency, DevOps.
|
||||
- Twenty-seven domain derived/topic docs (REST, GraphQL, versioning, error-responses, pagination, authentication, authorization, input-validation, secrets, supply-chain, schema-design, migrations, indexing, pyramid, fixtures, frontend, backend, logging, metrics, tracing, tokens, copywriting, patterns, doc-templates, patterns, ci-cd, environments).
|
||||
- `matrix/principles-matrix.md` — full mapping of all 110 domain principles to core derivations.
|
||||
- `matrix/domain-coverage.md` — inverse mapping of core principles to domains.
|
||||
- `review/agent-checklist.md` — pre-completion gate for AI agents.
|
||||
- `review/peer-review-checklist.md` — human review checklist.
|
||||
- `review/anti-patterns.md` — catalog of violations with principle citations.
|
||||
- Four good worked examples: `examples/good/api-endpoint.md`, `react-component.md`, `db-schema.md`, `error-handler.md`.
|
||||
- Three bad worked examples: `examples/bad/god-object.md`, `silent-error.md`, `leaky-abstraction.md`.
|
||||
- Four language application docs: `languages/typescript.md`, `python.md`, `go.md`, `rust.md`.
|
||||
- `MANIFEST.md` — authoritative document index.
|
||||
- `CONTRIBUTING.md` — contribution guide.
|
||||
|
||||
### Framework Properties
|
||||
- 8 core principles, 11 domains, 110 domain principles, all traced via the matrix.
|
||||
- NFR milestone type (documentation only, no runtime code).
|
||||
- Tags: v0.0.0 (pre-execution) through v0.0.7 (final review + ship).
|
||||
- The v0.0.7 patch release IS the v0.1.0 milestone deliverable.
|
||||
|
||||
### Phases
|
||||
- Phase 0 (v0.0.0): pre-execution — specify, clarify, research, plan, ship.
|
||||
- Phase 1 (v0.0.1): core foundation — first-principles, conflict-resolution, reading-order, README, LICENSE.
|
||||
- Phase 2 (v0.0.2): domain first-principles — uiux first-principles + audit.
|
||||
- Phase 3 (v0.0.3): domain derived docs — 27 topic files.
|
||||
- Phase 4 (v0.0.4): matrix + review — domain-coverage, agent-checklist, peer-review-checklist, anti-patterns.
|
||||
- Phase 5 (v0.0.5): examples — 4 good + 3 bad.
|
||||
- Phase 6 (v0.0.6): languages + meta — typescript, python, go, rust, CHANGELOG, CONTRIBUTING.
|
||||
- Phase 7 (v0.0.7): final review + ship — milestone release.
|
||||
@@ -0,0 +1,71 @@
|
||||
# Contributing to Atelier
|
||||
|
||||
Thank you for considering a contribution to Atelier. This framework lives by its principles; contributions are expected to follow them.
|
||||
|
||||
## What We Accept
|
||||
|
||||
- **New domain first-principles** — if a domain is missing (e.g., `domains/ai-ml/`), propose it with 10 principles (P1–P10), each traced to a core principle (C1–C8) in `matrix/principles-matrix.md`.
|
||||
- **New domain derived docs** — topic docs under an existing domain (e.g., `domains/api/webhooks.md`), deriving from the domain's first-principles.
|
||||
- **New language application docs** — `languages/<lang>.md` showing how domain principles apply in a specific language.
|
||||
- **New examples** — `examples/good/*` (with principle citations) or `examples/bad/*` (with violation citations).
|
||||
- **Corrections** — to existing principles, derivations, or examples. A correction to a core principle is a major version change; treat with care.
|
||||
- **Improvements to the matrix** — if a derivation is missing or wrong, propose the fix with the rationale.
|
||||
|
||||
## What We Do Not Accept
|
||||
|
||||
- **Style rules** — Atelier is principles, not style. Use a linter for style.
|
||||
- **Tooling** — linters, analyzers, or enforcement code. Atelier is markdown.
|
||||
- **Unlisted docs** — every document must be in `MANIFEST.md`. An unlisted doc is not part of the framework.
|
||||
- **Principles without derivation** — a domain principle that does not trace to a core principle is orphaned and will be rejected.
|
||||
|
||||
## How to Contribute
|
||||
|
||||
### 1. Read the relevant docs first
|
||||
- `core/first-principles.md` — the eight axioms.
|
||||
- `core/conflict-resolution.md` — how conflicts are resolved.
|
||||
- The domain(s) you are contributing to.
|
||||
- `matrix/principles-matrix.md` — to see existing derivations.
|
||||
|
||||
### 2. Follow the document structure
|
||||
See `domains/documentation/doc-templates.md` for the canonical structure. Every first-principles doc has:
|
||||
- Manifesto
|
||||
- The Principles (P1–P10, named, defined, with "what violates it")
|
||||
- Conflict Resolution
|
||||
- What Violates These Principles (table)
|
||||
- Relationship to Core
|
||||
|
||||
### 3. Update the matrix
|
||||
If you add or change a principle, update `matrix/principles-matrix.md` with the derivation. A PR with a new principle but no matrix row is incomplete.
|
||||
|
||||
### 4. Update the manifest
|
||||
If you add a new document, add it to `MANIFEST.md` in the correct section. An unlisted document is not part of the framework.
|
||||
|
||||
### 5. Add examples
|
||||
If you add a principle, add at least one good example and one bad example in `examples/`. Examples are mandatory (Documentation P3).
|
||||
|
||||
### 6. Write a clear PR description
|
||||
- What principle or document you are adding/changing.
|
||||
- Why (the rationale, not just the what — Documentation P8 Why Over What).
|
||||
- Which core principle(s) it derives from.
|
||||
- What conflicts it might introduce (if any).
|
||||
|
||||
## Review Criteria
|
||||
|
||||
Reviewers will check (see `review/peer-review-checklist.md`):
|
||||
- Does the new principle trace to a core principle?
|
||||
- Is the matrix updated?
|
||||
- Is the manifest updated?
|
||||
- Are there examples?
|
||||
- Does the structure follow the template?
|
||||
- Does it conflict with existing principles? If so, is the conflict resolvable per `core/conflict-resolution.md`?
|
||||
|
||||
## Versioning
|
||||
|
||||
- A new domain or language doc is a minor version (e.g., v0.1 → v0.2).
|
||||
- A new topic doc or example is a patch version (e.g., v0.1.0 → v0.1.1).
|
||||
- A change to `core/first-principles.md` (adding, removing, or reordering a core principle) is a major version (e.g., v0.x → v1.0).
|
||||
- See `CHANGELOG.md` for the version history.
|
||||
|
||||
## License
|
||||
|
||||
By contributing, you agree that your contributions are licensed under the MIT license (see `LICENSE`).
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Atelier contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
# Atelier — Document Manifest
|
||||
|
||||
> Authoritative index. If a document is not listed here, it is not
|
||||
> part of the framework.
|
||||
|
||||
## Reading Order
|
||||
|
||||
1. `core/first-principles.md` — Required first
|
||||
2. `core/conflict-resolution.md` — Required
|
||||
3. `domains/<relevant>/first-principles.md`— Per task
|
||||
4. `domains/<relevant>/<topic>.md` — As needed
|
||||
5. `matrix/principles-matrix.md` — Reference
|
||||
|
||||
## Core
|
||||
|
||||
| Document | Status | Audience |
|
||||
|-----------------------------------------|----------|----------|
|
||||
| `core/first-principles.md` | Stable | All |
|
||||
| `core/conflict-resolution.md` | Stable | All |
|
||||
| `core/reading-order.md` | Stable | All |
|
||||
|
||||
## Domains
|
||||
|
||||
| Domain | First Principles | Derived Documents |
|
||||
|---------------|------------------|----------------------------------|
|
||||
| UI / UX | ✓ | components, accessibility, tokens, copywriting |
|
||||
| API Design | ✓ | rest, graphql, versioning, error-responses, pagination |
|
||||
| Security | ✓ | authentication, authorization, input-validation, secrets, supply-chain |
|
||||
| Data | ✓ | schema-design, migrations, indexing |
|
||||
| Testing | ✓ | pyramid, fixtures |
|
||||
| Performance | ✓ | frontend, backend |
|
||||
| Observability | ✓ | logging, metrics, tracing |
|
||||
| Errors | ✓ | patterns |
|
||||
| Documentation | ✓ | doc-templates |
|
||||
| Concurrency | ✓ | patterns |
|
||||
| DevOps | ✓ | ci-cd, environments |
|
||||
|
||||
## Cross-Cutting
|
||||
|
||||
| Document | Purpose |
|
||||
|-----------------------------------|----------------------------------|
|
||||
| `matrix/principles-matrix.md` | Maps domain → core principles |
|
||||
| `review/agent-checklist.md` | Pre-completion agent checklist |
|
||||
| `review/anti-patterns.md` | Catalog of violations |
|
||||
@@ -0,0 +1,90 @@
|
||||
# Atelier
|
||||
|
||||
A first-principles, docs-as-code engineering framework for AI agents and humans.
|
||||
|
||||
## What This Is
|
||||
|
||||
Atelier is a hierarchical document tree of engineering principles. Eight universal **core principles** (C1–C8) govern eleven **domains** (API, Security, Data, Testing, Performance, Observability, Errors, Documentation, Concurrency, DevOps, UI/UX), each with ten derived principles (P1–P10). A **principles matrix** maps every domain rule back to the core rule it derives from, making conflict resolution deterministic.
|
||||
|
||||
This is not a style guide. This is not a linter. This is a principle hierarchy with cross-references, consumed by AI agents as pre-completion guidance and by humans as engineering canon.
|
||||
|
||||
## Who It's For
|
||||
|
||||
- **AI agents** — read `core/` then the relevant `domains/` before completing a coding task. Run `review/agent-checklist.md` before finishing.
|
||||
- **Human engineers** — read `README.md` → `MANIFEST.md` → `core/` for onboarding, then the domains relevant to your work.
|
||||
- **Reviewers** — use `review/peer-review-checklist.md` and `review/anti-patterns.md`.
|
||||
|
||||
## Quickstart
|
||||
|
||||
### For Agents
|
||||
|
||||
1. Read `core/first-principles.md` (the 8 axioms)
|
||||
2. Read `core/conflict-resolution.md` (how to resolve rule conflicts)
|
||||
3. Read `domains/<your-task-domain>/first-principles.md`
|
||||
4. Read `domains/<your-task-domain>/<topic>.md` as needed
|
||||
5. Run `review/agent-checklist.md` before completing
|
||||
|
||||
### For Humans
|
||||
|
||||
1. Read this README
|
||||
2. Read `MANIFEST.md` (the authoritative document index)
|
||||
3. Read `core/first-principles.md` → `core/conflict-resolution.md` → `core/reading-order.md`
|
||||
4. Pick a domain of interest and read its `first-principles.md`
|
||||
5. Skim `matrix/principles-matrix.md` to see how everything connects
|
||||
|
||||
## The Eight Core Principles
|
||||
|
||||
| # | Principle | One-line |
|
||||
|---|-----------|----------|
|
||||
| C1 | Correctness | The system does what it is supposed to do, and nothing else. |
|
||||
| C2 | Clarity | The intent of the code is obvious to its reader. |
|
||||
| C3 | Simplicity | The solution is as simple as possible, and no simpler. |
|
||||
| C4 | Locality | Decisions and their consequences live near each other. |
|
||||
| C5 | Reversibility | Every decision can be undone, and the cost of undoing is known. |
|
||||
| C6 | Composability | Parts combine into wholes, and the parts are reusable. |
|
||||
| C7 | Observability | The system's behavior is visible to those who must understand it. |
|
||||
| C8 | Economy | The system uses no more resources than the task requires. |
|
||||
|
||||
Precedence: C1 > C2 > C3 > C4 > C5 > C6 > C7 > C8. Correctness is never sacrificed.
|
||||
|
||||
## The Domains
|
||||
|
||||
| Domain | First Principles | Derived Docs |
|
||||
|--------|------------------|--------------|
|
||||
| UI/UX | ✓ | components, accessibility, tokens, copywriting |
|
||||
| API | ✓ | rest, graphql, versioning, error-responses, pagination |
|
||||
| Security | ✓ | authentication, authorization, input-validation, secrets, supply-chain |
|
||||
| Data | ✓ | schema-design, migrations, indexing |
|
||||
| Testing | ✓ | pyramid, fixtures |
|
||||
| Performance | ✓ | frontend, backend |
|
||||
| Observability | ✓ | logging, metrics, tracing |
|
||||
| Errors | ✓ | patterns |
|
||||
| Documentation | ✓ | doc-templates |
|
||||
| Concurrency | ✓ | patterns |
|
||||
| DevOps | ✓ | ci-cd, environments |
|
||||
|
||||
## Repository Structure
|
||||
|
||||
```
|
||||
atelier/
|
||||
├── core/ # The 8 universal principles (read first)
|
||||
├── domains/ # 11 domains, each with first-principles + derived docs
|
||||
├── languages/ # Language-specific application of domain rules
|
||||
├── review/ # Checklists and anti-patterns
|
||||
├── matrix/ # Cross-reference: domain ↔ core
|
||||
├── examples/ # Worked examples (good + bad)
|
||||
├── MANIFEST.md # Authoritative document index
|
||||
└── README.md # This file
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT. See `LICENSE`.
|
||||
|
||||
## Contributing
|
||||
|
||||
See `CONTRIBUTING.md`.
|
||||
|
||||
## Version
|
||||
|
||||
This is v0.1. See `CHANGELOG.md` for version history.
|
||||
@@ -0,0 +1,105 @@
|
||||
# Core — Conflict Resolution
|
||||
|
||||
**Status:** Foundational
|
||||
**Audience:** All agents and humans. Read after `first-principles.md`.
|
||||
|
||||
## 1. Why Conflict Resolution Exists
|
||||
|
||||
Atelier has eight core principles and eleven domains, each with ten principles. Rules will conflict. When they do, there must be a deterministic procedure to resolve the conflict — not a debate. This document is that procedure.
|
||||
|
||||
## 2. The Hierarchy (Strict)
|
||||
|
||||
```
|
||||
core/first-principles.md ← highest authority
|
||||
└── domains/<x>/first-principles.md
|
||||
└── domains/<x>/<topic>.md
|
||||
└── languages/<lang>.md
|
||||
└── examples/<x>.md ← lowest authority
|
||||
```
|
||||
|
||||
**Rules:**
|
||||
|
||||
1. A higher document overrides a lower document when they conflict.
|
||||
2. `core/first-principles.md` is never overridden.
|
||||
3. Within a level, the more specific document wins for its specific scope.
|
||||
4. If two documents at the same level conflict with equal specificity, the core principle they each derive from decides — the higher-precedence core principle wins.
|
||||
|
||||
## 3. Core Principle Precedence (Total Order)
|
||||
|
||||
```
|
||||
C1 Correctness > C2 Clarity > C3 Simplicity > C4 Locality >
|
||||
C5 Reversibility > C6 Composability > C7 Observability > C8 Economy
|
||||
```
|
||||
|
||||
- **C1 is never sacrificed.** No other principle, domain rule, or convenience overrides correctness.
|
||||
- **C2 is sacrificed only for C1.** Clarity gives way only when making code correct makes it unavoidably unclear (rare; usually clarity and correctness align).
|
||||
- **C3 is sacrificed only for C1 or C2.** Simplicity yields to correctness and clarity.
|
||||
- **C4–C8 are tradeable** among themselves, but always below C1–C3. Their relative order is: Locality > Reversibility > Composability > Observability > Economy.
|
||||
- The precedence is **total**: a violation of a higher principle is never justified by appeal to a lower one.
|
||||
|
||||
## 4. Conflict Resolution Procedure
|
||||
|
||||
When two rules conflict:
|
||||
|
||||
### Step 1 — Identify the level of each rule
|
||||
- Is it core, domain-first-principles, domain-topic, language, or example?
|
||||
|
||||
### Step 2 — Apply the hierarchy
|
||||
- The higher-level rule wins. Done.
|
||||
- If same level, proceed to Step 3.
|
||||
|
||||
### Step 3 — Identify the core derivation
|
||||
- Look up each rule in `matrix/principles-matrix.md`.
|
||||
- Each rule derives from one or more core principles (e.g., Security P2 derives from C3 and C8).
|
||||
|
||||
### Step 4 — Apply core precedence
|
||||
- Compare the highest-precedence core principle each rule derives from.
|
||||
- The rule deriving from the higher-precedence core principle wins.
|
||||
- Example: a rule deriving from C1 (Correctness) beats a rule deriving from C8 (Economy), always.
|
||||
|
||||
### Step 5 — If still tied, the more specific scope wins
|
||||
- A rule scoped to "API endpoints" wins over a rule scoped to "all code" for API endpoint conflicts.
|
||||
- A rule scoped to "TypeScript" wins over a rule scoped to "all languages" for TypeScript conflicts.
|
||||
|
||||
### Step 6 — If still tied, the conflict is a defect
|
||||
- File an issue. One of the rules is wrong. Do not silently pick one.
|
||||
- The matrix should not produce ties. If it does, the matrix is incomplete.
|
||||
|
||||
## 5. Worked Examples
|
||||
|
||||
### Example A: Security P9 (Secret Hygiene) vs DevOps P9 (Documentation in Pipeline)
|
||||
- Both are domain-level. Same level → Step 3.
|
||||
- Security P9 derives from C6, C7 (per matrix).
|
||||
- DevOps P9 derives from C2 (per matrix).
|
||||
- C2 > C6 and C7? **No** — C2 is precedence 2, C6 is 6, C7 is 7. C2 is higher.
|
||||
- But wait: Security P9 is **non-tradeable** per the security domain's own conflict resolution. Security's manifesto says 8 of 10 principles are never sacrificed.
|
||||
- **Resolution:** Security domain's non-tradeable declaration is a domain-level rule that itself derives from C1 (Correctness — security failures are correctness failures). So Security P9 effectively derives from C1. C1 > C2. **Security P9 wins.** Do not log secrets in the pipeline.
|
||||
- **Lesson:** A domain's "never sacrificed" declaration promotes those rules to C1-equivalent.
|
||||
|
||||
### Example B: Performance P8 (Budget Discipline) vs Clarity (C2)
|
||||
- Performance P8 is domain-level, derives from C8 (Economy).
|
||||
- Clarity is core-level (C2).
|
||||
- Core > domain. **C2 Clarity wins.** Clear code that is slightly over budget beats opaque code that is under budget.
|
||||
- **Exception:** if the budget is a correctness requirement (e.g., real-time system), the budget derives from C1, and C1 > C2. Then the budget wins.
|
||||
|
||||
### Example C: API P5 (Versioning) vs API P10 (Stability)
|
||||
- Both are in the same domain (api), same level.
|
||||
- API P5 derives from C5 (Reversibility). API P10 derives from C5 (Reversibility). Same core derivation — tied.
|
||||
- API's own conflict resolution (in `domains/api/first-principles.md` §3): "Versioning — never sacrificed." So P5 wins.
|
||||
- **Resolution:** Versioning wins. You version the change rather than break stability, because versioning is the mechanism that enables stability. They are not actually in conflict — versioning is how you preserve stability.
|
||||
|
||||
## 6. Non-Tradeable Declarations
|
||||
|
||||
Some domains declare certain principles non-tradeable (e.g., Security: 8 of 10). These declarations are binding:
|
||||
|
||||
- A non-tradeable domain rule is treated as deriving from C1 (Correctness), regardless of its matrix entry.
|
||||
- This is because the domain has asserted that violating the rule is a correctness failure in that domain's scope.
|
||||
- Non-tradeable rules can still conflict with each other (e.g., two security rules). The domain's own precedence (in its §3) resolves those.
|
||||
|
||||
## 7. The Matrix is the Arbiter
|
||||
|
||||
`matrix/principles-matrix.md` is the single source of truth for derivations. If a rule is not in the matrix, it is not part of the framework. If the matrix and a domain document disagree, **the matrix wins** — the domain document must be corrected to match the matrix, or the matrix must be updated with a new derivation (a docs change, not a silent edit).
|
||||
|
||||
## 8. Conflicts with This Document
|
||||
|
||||
If `core/conflict-resolution.md` conflicts with `core/first-principles.md`, `first-principles.md` wins. This document is the procedure; the principles are the authority.
|
||||
@@ -0,0 +1,114 @@
|
||||
# Core — First Principles
|
||||
|
||||
**Version:** 1.0.0
|
||||
**Status:** Foundational
|
||||
**Audience:** All agents and humans. Read this first.
|
||||
|
||||
## 1. Manifesto
|
||||
|
||||
There are eight principles from which all other principles derive. Every domain rule, every language rule, every review checklist traces back to one or more of these. When two rules conflict, the higher-precedence core principle wins. When a rule has no derivation here, it is orphaned and must be removed or re-derived.
|
||||
|
||||
These principles are universal. They apply to every language, every domain, every system. They are not opinions; they are the axioms of quality software.
|
||||
|
||||
## 2. The Principles
|
||||
|
||||
### C1. Correctness
|
||||
The system does what it is supposed to do, and nothing else.
|
||||
|
||||
- Correctness is the highest principle. No other principle overrides it.
|
||||
- A correct system handles the specified cases, the edge cases, and the failure cases — all defined by the domain.
|
||||
- Incorrect code that is fast, clear, or simple is still wrong.
|
||||
- Security is a subset of correctness: code that is exploitable does not do what it was supposed to do.
|
||||
- Correctness includes temporal correctness: a late answer is a wrong answer when the deadline mattered.
|
||||
|
||||
### C2. Clarity
|
||||
The intent of the code is obvious to its reader.
|
||||
|
||||
- Code is read more than it is written. Optimize for the reader.
|
||||
- Names reveal intent. Comments explain why, not what.
|
||||
- A stranger should understand the code without reading the documentation. The documentation should make the code unnecessary to read.
|
||||
- Clarity serves correctness: unclear code is where bugs hide.
|
||||
|
||||
### C3. Simplicity
|
||||
The solution is as simple as possible, and no simpler.
|
||||
|
||||
- Complexity is the enemy of correctness. Every line of code is a liability.
|
||||
- Simplicity is not laziness. It is the result of removing everything unnecessary.
|
||||
- A simple solution handles the requirement completely. A simpler-than-necessary solution does not.
|
||||
- Simplicity serves economy: less code, less to test, less to maintain.
|
||||
|
||||
### C4. Locality
|
||||
Decisions and their consequences live near each other.
|
||||
|
||||
- State, logic, and side effects that depend on each other live near each other.
|
||||
- Locality serves clarity: to understand a piece of code, you should not need to read the whole system.
|
||||
- Locality serves correctness: coupling that is far apart is coupling you cannot see.
|
||||
- A change that requires touching many distant files is a locality violation.
|
||||
|
||||
### C5. Reversibility
|
||||
Every decision can be undone, and the cost of undoing is known.
|
||||
|
||||
- Reversibility serves safety: an irreversible decision is a bet you cannot hedge.
|
||||
- Migrations, deployments, schema changes, and API changes are reversible by default.
|
||||
- A decision that cannot be reversed is made with the maximum of evidence and the minimum of haste.
|
||||
- Versioning, feature flags, and rollback paths are the mechanisms of reversibility.
|
||||
|
||||
### C6. Composability
|
||||
Parts combine into wholes, and the parts are reusable in new wholes.
|
||||
|
||||
- Composability serves scale: complex systems are built from simple, composable parts.
|
||||
- A part that does one thing well composes. A part that does many things configures.
|
||||
- The boundary of a part is its contract. Contracts are explicit and stable.
|
||||
- Composability serves clarity: composable parts are understandable in isolation.
|
||||
|
||||
### C7. Observability
|
||||
The system's behavior is visible to the people who must understand it.
|
||||
|
||||
- Observability serves correctness: you cannot fix what you cannot see.
|
||||
- Logs, metrics, and traces are first-class. They are designed in, not bolted on.
|
||||
- Observability serves clarity: the system explains itself in production.
|
||||
- An observable system answers "what happened, why, and what next?" without reading the source.
|
||||
|
||||
### C8. Economy
|
||||
The system uses no more resources than the task requires.
|
||||
|
||||
- Resources include time, memory, attention, money, and complexity.
|
||||
- Economy serves simplicity: the cheapest solution is often the simplest.
|
||||
- Economy is the most tradeable principle: it is sacrificed for correctness, clarity, and reversibility.
|
||||
- But unbounded economy is a bug: unbounded growth in any resource is a defect.
|
||||
|
||||
## 3. Precedence
|
||||
|
||||
When principles conflict, the lower-numbered principle wins:
|
||||
|
||||
```
|
||||
C1 Correctness > C2 Clarity > C3 Simplicity > C4 Locality >
|
||||
C5 Reversibility > C6 Composability > C7 Observability > C8 Economy
|
||||
```
|
||||
|
||||
- C1 (Correctness) is never sacrificed.
|
||||
- C2 (Clarity) is sacrificed only for C1 (Correctness).
|
||||
- C3 (Simplicity) is sacrificed only for C1 or C2.
|
||||
- C4–C8 are tradeable among themselves, but always below C1–C3.
|
||||
- The precedence is total: a violation of C1 is never justified by an appeal to C8.
|
||||
|
||||
See `core/conflict-resolution.md` for the formal conflict resolution procedure.
|
||||
|
||||
## 4. What Violates These Principles
|
||||
|
||||
| Violation | Principle Breached |
|
||||
|-----------|-------------------|
|
||||
| Code that "mostly works" | C1 Correctness |
|
||||
| A function named `doStuff` | C2 Clarity |
|
||||
| A 500-line function that could be 50 | C3 Simplicity |
|
||||
| Config in a distant repo, code reads it silently | C4 Locality |
|
||||
| A migration with no rollback | C5 Reversibility |
|
||||
| A component that reads global state implicitly | C6 Composability |
|
||||
| A service with no logs | C7 Observability |
|
||||
| Loading all records into memory | C8 Economy |
|
||||
|
||||
## 5. Relationship to Domains
|
||||
|
||||
Every domain in Atelier derives its own P1–P10 principles from these eight. The mapping is recorded in `matrix/principles-matrix.md`. A domain rule that cannot be traced to a core principle is orphaned and must be removed.
|
||||
|
||||
The eight principles are the floor. The domain principles are the ceiling for their scope. Within a domain, the domain's precedence applies; across domains, the core precedence applies.
|
||||
@@ -0,0 +1,103 @@
|
||||
# Core — Reading Order
|
||||
|
||||
**Status:** Foundational
|
||||
**Audience:** New readers (agents and humans). Read after `first-principles.md` and `conflict-resolution.md`.
|
||||
|
||||
## 1. The Canonical Order
|
||||
|
||||
This is the order in which the framework is designed to be consumed. Following it ensures you build the mental model before the details.
|
||||
|
||||
### For All Readers
|
||||
|
||||
1. **`core/first-principles.md`** — The eight axioms. Everything derives from these. Read first, always.
|
||||
2. **`core/conflict-resolution.md`** — How to resolve disagreements between rules. Read second, always.
|
||||
3. **`core/reading-order.md`** — This document. You are here.
|
||||
|
||||
### For a Specific Task
|
||||
|
||||
4. **`domains/<relevant>/first-principles.md`** — The domain's ten principles. Read the domain(s) relevant to your task.
|
||||
5. **`domains/<relevant>/<topic>.md`** — The domain's derived topic docs. Read only what your task needs.
|
||||
|
||||
### For Reference
|
||||
|
||||
6. **`matrix/principles-matrix.md`** — The full mapping of every domain principle to its core derivation. Consult when a rule's authority is unclear or a conflict arises.
|
||||
7. **`MANIFEST.md`** — The authoritative index of all framework documents. If a document is not here, it is not part of the framework.
|
||||
|
||||
### For Evaluation
|
||||
|
||||
8. **`review/agent-checklist.md`** — The pre-completion checklist. Agents run this before finishing a task.
|
||||
9. **`review/peer-review-checklist.md`** — The human review checklist.
|
||||
10. **`review/anti-patterns.md`** — Catalog of known violations.
|
||||
|
||||
### For Language-Specific Application
|
||||
|
||||
11. **`languages/<lang>.md`** — How the domain principles apply in a specific language. Read only for the language you are working in.
|
||||
|
||||
### For Illustration
|
||||
|
||||
12. **`examples/good/*`** — Worked examples of the framework applied correctly.
|
||||
13. **`examples/bad/*`** — Worked examples of violations, with the breached principles cited.
|
||||
|
||||
## 2. Reading Paths
|
||||
|
||||
### Path A: New Agent (Pre-Task)
|
||||
|
||||
```
|
||||
core/first-principles.md
|
||||
→ core/conflict-resolution.md
|
||||
→ core/reading-order.md
|
||||
→ domains/<task-domain>/first-principles.md
|
||||
→ domains/<task-domain>/<topic>.md
|
||||
→ review/agent-checklist.md (run before completing)
|
||||
```
|
||||
|
||||
Estimated time: 15–25 minutes depending on domain depth.
|
||||
|
||||
### Path B: New Human (Onboarding)
|
||||
|
||||
```
|
||||
README.md
|
||||
→ MANIFEST.md
|
||||
→ core/first-principles.md
|
||||
→ core/conflict-resolution.md
|
||||
→ core/reading-order.md
|
||||
→ one domain of personal interest (e.g., domains/api/first-principles.md)
|
||||
→ matrix/principles-matrix.md (skim)
|
||||
```
|
||||
|
||||
Estimated time: 30–45 minutes for a full pass.
|
||||
|
||||
### Path C: Conflict Resolution
|
||||
|
||||
```
|
||||
core/conflict-resolution.md (procedure)
|
||||
→ matrix/principles-matrix.md (look up both rules' derivations)
|
||||
→ core/first-principles.md (apply precedence)
|
||||
```
|
||||
|
||||
Estimated time: 5 minutes per conflict.
|
||||
|
||||
### Path D: Review
|
||||
|
||||
```
|
||||
review/agent-checklist.md (or peer-review-checklist.md)
|
||||
→ for each failed checklist item, follow Path C
|
||||
→ examples/bad/* for the relevant violation pattern
|
||||
```
|
||||
|
||||
## 3. What Not to Read First
|
||||
|
||||
- **Do not** start with `examples/`. Examples illustrate principles you have not yet read.
|
||||
- **Do not** start with `languages/`. Language rules derive from domain rules you have not yet read.
|
||||
- **Do not** start with `matrix/`. The matrix is a reference, not a tutorial.
|
||||
- **Do not** skip `core/`. Everything depends on it.
|
||||
|
||||
## 4. Skipping is Allowed After Core
|
||||
|
||||
After reading `core/` (the three foundational documents), you may skip freely. The domains are independent of each other. You do not need to read `domains/security/` to use `domains/api/`. You only need the domain relevant to your task.
|
||||
|
||||
The one exception: `matrix/principles-matrix.md` references all domains. You do not need to read all domains to use the matrix, but the matrix is most useful when you have read at least one domain.
|
||||
|
||||
## 5. Versioning
|
||||
|
||||
The framework versions as a whole (semantic versioning). A change to `core/first-principles.md` is a major version. A new domain is a minor version. A new topic doc is a patch. See `CHANGELOG.md` for the version history.
|
||||
@@ -0,0 +1,63 @@
|
||||
# Error Responses — Derived Rules
|
||||
|
||||
> Derives from `domains/api/first-principles.md` P9 (Error Transparency) and `domains/errors/first-principles.md`.
|
||||
|
||||
## The Error Contract
|
||||
|
||||
Every error response is a JSON object with:
|
||||
|
||||
```json
|
||||
{
|
||||
"error": {
|
||||
"code": "STRING_ERROR_CODE",
|
||||
"message": "Human-readable description",
|
||||
"details": {},
|
||||
"request_id": "uuid"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `code`: machine-consumable, stable, UPPER_SNAKE_CASE. Never a free-text message.
|
||||
- `message`: human-readable, for logs and developers. Not for end users (see `domains/errors/` P8).
|
||||
- `details`: structured, typed additional context (which field, what value, what constraint).
|
||||
- `request_id`: correlation ID for tracing. Every error is traceable.
|
||||
|
||||
## Error Codes (P3 Predictability, P9)
|
||||
|
||||
- Codes are stable. Renaming an error code is a breaking change.
|
||||
- Codes are specific: `VALIDATION_FAILED` not `BAD_REQUEST`. `DUPLICATE_EMAIL` not `CONFLICT`.
|
||||
- Codes are namespaced: `USER_NOT_FOUND`, `ORDER_NOT_FOUND` — not just `NOT_FOUND`.
|
||||
|
||||
## Status Code Mapping (P1 Contract Fidelity)
|
||||
|
||||
| Code | Meaning | Error code example |
|
||||
|------|---------|-------------------|
|
||||
| 400 | Malformed request | `MALFORMED_REQUEST` |
|
||||
| 401 | Auth required | `AUTH_REQUIRED` |
|
||||
| 403 | Forbidden | `FORBIDDEN` |
|
||||
| 404 | Not found | `<RESOURCE>_NOT_FOUND` |
|
||||
| 409 | Conflict | `DUPLICATE_<RESOURCE>` |
|
||||
| 422 | Semantic invalid | `VALIDATION_FAILED` |
|
||||
| 429 | Rate limited | `RATE_LIMITED` |
|
||||
| 500 | Server bug | `INTERNAL_ERROR` |
|
||||
|
||||
- Never return 200 with an error body. The status code is the first signal.
|
||||
- Never return 500 for a client error. 500 means "the server has a bug."
|
||||
|
||||
## Information Disclosure (P8 Security, domains/security P9)
|
||||
|
||||
- Error messages do not leak internal state: no stack traces, no SQL fragments, no file paths.
|
||||
- A 401 does not say "user not found" vs "wrong password" — both say "invalid credentials."
|
||||
- A 404 does not confirm the resource exists but is forbidden — return 404, not 403, for unauthenticated requests to hidden resources.
|
||||
- Detailed errors are logged server-side with `request_id`; the client gets the safe version.
|
||||
|
||||
## Retryability (P6 Idempotency)
|
||||
|
||||
- Errors that are safe to retry: 409, 422 (if the fix is applied), 429 (after backoff), 5xx.
|
||||
- Errors that are not safe to retry: 400, 401 (without re-auth), 403.
|
||||
- The error body indicates retryability: `retryable: true/false` or via the code's known semantics.
|
||||
|
||||
## Partial Errors (GraphQL, see `domains/api/graphql.md`)
|
||||
|
||||
- GraphQL returns data and errors together. Do not conflate.
|
||||
- A null field with no error is a bug. A null field with an error is a partial failure.
|
||||
@@ -0,0 +1,84 @@
|
||||
# API Design — First Principles
|
||||
|
||||
**Version:** 1.0.0
|
||||
**Status:** Foundational
|
||||
**Audience:** AI agents and humans designing APIs (REST, GraphQL,
|
||||
gRPC, RPC, libraries).
|
||||
|
||||
## 1. Manifesto
|
||||
|
||||
An API is a contract between systems and the people who build on
|
||||
them. The cost of an API is paid by every consumer, forever. The
|
||||
highest quality API is one that a stranger can use correctly without
|
||||
reading the source.
|
||||
|
||||
## 2. The Principles
|
||||
|
||||
### P1. Contract Fidelity
|
||||
The API does what its documentation says, and the documentation says
|
||||
what the API does. Nothing more, nothing less.
|
||||
|
||||
### P2. Clarity
|
||||
Endpoints, methods, parameters, and responses are named and structured
|
||||
for the consumer — not for the implementer.
|
||||
|
||||
### P3. Predictability
|
||||
Consumers can guess behavior without reading docs. Patterns repeat.
|
||||
Surprises are bugs.
|
||||
|
||||
### P4. Composability
|
||||
Resources and operations combine cleanly. The whole is greater than
|
||||
the sum of its parts, and the parts are reusable in new wholes.
|
||||
|
||||
### P5. Versioning
|
||||
Changes are managed explicitly, not implicitly. Consumers know what
|
||||
will break, and when.
|
||||
|
||||
### P6. Idempotency
|
||||
Repeated identical calls have the same effect as a single call. Retry
|
||||
is a first-class operation.
|
||||
|
||||
### P7. Performance
|
||||
Latency, payload size, and call count are designed in — not optimized
|
||||
out.
|
||||
|
||||
### P8. Security
|
||||
Authentication, authorization, validation, and rate limiting are
|
||||
defaults, not add-ons.
|
||||
|
||||
### P9. Error Transparency
|
||||
Failures are communicated specifically, structurally, and actionably.
|
||||
|
||||
### P10. Stability
|
||||
Consumers can build on the API without fear of breakage. Backward
|
||||
compatibility is a default.
|
||||
|
||||
## 3. Conflict Resolution
|
||||
|
||||
1. Contract Fidelity — never sacrificed.
|
||||
2. Security — never sacrificed.
|
||||
3. Stability — sacrificed only with a documented deprecation cycle.
|
||||
4. Clarity — sacrificed only for Performance with evidence.
|
||||
5. Predictability — sacrificed for Composability when patterns diverge.
|
||||
6. Composability — sacrificed for Clarity when abstractions confuse.
|
||||
7. Idempotency — sacrificed only for genuinely non-idempotent operations.
|
||||
8. Performance — sacrificed only with measurement.
|
||||
9. Error Transparency — sacrificed only for security-sensitive errors.
|
||||
10. Versioning — never sacrificed (always have a version policy).
|
||||
|
||||
## 4. What Violates These Principles
|
||||
|
||||
| Violation | Principle Breached |
|
||||
|------------------------------------|----------------------|
|
||||
| Endpoint name exposes DB schema | P2 Clarity |
|
||||
| Breaking change without deprecation | P10 Stability |
|
||||
| Generic 500 with stack trace | P9 Error Transparency|
|
||||
| Auth as opt-in | P8 Security |
|
||||
| Non-idempotent POST without key | P6 Idempotency |
|
||||
| Inconsistent naming across endpoints | P3 Predictability |
|
||||
| Required response field undocumented | P1 Contract Fidelity |
|
||||
| 10MB response payload by default | P7 Performance |
|
||||
|
||||
## 5. Relationship to Core
|
||||
|
||||
Subordinate to `core/first-principles.md`. See `matrix/principles-matrix.md`.
|
||||
@@ -0,0 +1,53 @@
|
||||
# GraphQL — Derived Rules
|
||||
|
||||
> Derives from `domains/api/first-principles.md`. Applies P1–P10 to GraphQL specifically.
|
||||
|
||||
## Schema First (P1 Contract Fidelity)
|
||||
|
||||
- The schema is the contract. Every field has a type, a description, and a deprecation status.
|
||||
- The schema is versioned. Breaking schema changes (removing a field, changing a type) require a deprecation cycle.
|
||||
- Never expose raw database types in the schema. Map them to domain types.
|
||||
|
||||
## Query Design (P2 Clarity, P3 Predictability)
|
||||
|
||||
- Field names are nouns, camelCase: `userOrders`, not `UserOrders` or `user_orders`.
|
||||
- Arguments are descriptive: `first`, `after`, `orderBy` — not `arg1`, `arg2`.
|
||||
- Connections for lists: `users(first: 10, after: "cursor")` — never bare arrays.
|
||||
- Mutations are verbs: `createUser`, `deleteOrder` — not `userCreate`.
|
||||
|
||||
## N+1 Prevention (P7 Performance)
|
||||
|
||||
- Use a dataloader for every list field that resolves to another resource.
|
||||
- A resolver that does a database query per item is an N+1 bug.
|
||||
- Test resolvers under a list query, not just a single-item query.
|
||||
|
||||
## Authorization at the Field Level (P8 Security)
|
||||
|
||||
- Every resolver checks authorization. The query graph is not a trust boundary by default.
|
||||
- A user who can query `user { email }` is not automatically authorized to query `user { passwordHash }`.
|
||||
- Field-level authz is the floor, not an optimization.
|
||||
|
||||
## Deprecation (P5 Versioning, P10 Stability)
|
||||
|
||||
- Deprecate fields with `@deprecated(reason: "...")`. Never remove a field without deprecating first.
|
||||
- A deprecated field is removed in the next major schema version, not sooner.
|
||||
- Track field usage. A deprecated field with no usage can be removed sooner.
|
||||
|
||||
## Error Handling (P9 Error Transparency)
|
||||
|
||||
- Errors are partial by default: a query can return data and errors simultaneously.
|
||||
- Errors are structured: `{ message, path, extensions: { code, ... } }`.
|
||||
- Use `extensions.code` for machine-consumable error types, not free-text messages.
|
||||
- Never swallow a resolver error silently. A null field with no error is a bug.
|
||||
|
||||
## Complexity Budget (P7 Performance, P8 Security)
|
||||
|
||||
- Enforce a query complexity limit. Unbounded depth/breadth is a DoS vector.
|
||||
- Cost-based analysis (not just depth) catches expensive nested queries.
|
||||
- Reject queries over budget with a 400, not a 500.
|
||||
|
||||
## Federation (P6 Composability)
|
||||
|
||||
- A federated subgraph owns its entities. Cross-graph references use `@external` and `@requires`.
|
||||
- Never reach into another subgraph's database. The graph boundary is the contract.
|
||||
- The gateway composes; subgraphs do not know about each other.
|
||||
@@ -0,0 +1,69 @@
|
||||
# Pagination — Derived Rules
|
||||
|
||||
> Derives from `domains/api/first-principles.md` P7 (Performance) and P3 (Predictability).
|
||||
|
||||
## Three Patterns
|
||||
|
||||
### 1. Offset/Limit (`?page=2&limit=20`)
|
||||
- Simple, supports jumping to a page.
|
||||
- Unstable under inserts: page 2 becomes page 1's content after an insert.
|
||||
- Slow for large offsets: `OFFSET 10000` scans 10000 rows.
|
||||
- Use for: small, stable collections, admin UIs.
|
||||
|
||||
### 2. Cursor (`?cursor=base64token&limit=20`)
|
||||
- Stable under inserts: the cursor points to a position, not a page number.
|
||||
- Fast: indexed lookup, no scan.
|
||||
- No random access (cannot jump to page 5).
|
||||
- Use for: infinite scroll, feeds, large collections, anything user-facing.
|
||||
|
||||
### 3. Keyset (`?after_id=123&limit=20`)
|
||||
- Like cursor but uses the actual sort key (e.g., `after_id=123`).
|
||||
- Most stable and fast. Requires a unique, monotonic sort key.
|
||||
- Use for: ordered collections with a natural unique key.
|
||||
|
||||
## Defaults (P3 Predictability)
|
||||
|
||||
- Default `limit`: 20 or 50. Never unbounded.
|
||||
- Max `limit`: 100 or 200. Reject `limit=10000` with 400.
|
||||
- Default sort: by created_at descending, or by the resource's natural order.
|
||||
- Always return the total count if cheap; never if it requires a separate COUNT query on a large table.
|
||||
|
||||
## Response Shape (P2 Clarity)
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [...],
|
||||
"pagination": {
|
||||
"cursor": "next-base64-token",
|
||||
"has_more": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `cursor` is null when there is no next page.
|
||||
- `has_more` is the boolean convenience (some clients prefer it).
|
||||
- Never return `data` as a bare array — always wrap so you can add pagination without breaking.
|
||||
|
||||
## Link Header (alternative)
|
||||
|
||||
```
|
||||
Link: <https://api.example.com/users?cursor=X>; rel="next", <https://api.example.com/users?cursor=Z>; rel="prev"
|
||||
```
|
||||
|
||||
- Useful for HTTP-level clients (curl, browser fetch).
|
||||
- Less convenient for JSON-parsing clients.
|
||||
|
||||
## Consistency (P8 Consistency across endpoints)
|
||||
|
||||
- Every collection endpoint paginates the same way.
|
||||
- A client that learns pagination on `/users` should know it on `/orders`.
|
||||
- Mixed pagination (cursor here, offset there) is a tax on every consumer.
|
||||
|
||||
## What Violates Pagination
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| Returning 10000 items by default | P7 Performance |
|
||||
| `limit` with no max | P8 Security (DoS) |
|
||||
| Page numbers on a frequently-inserted table | P3 Predictability |
|
||||
| Bare array response (no pagination wrapper) | P1 Contract Fidelity (can't add pagination later without breaking) |
|
||||
@@ -0,0 +1,68 @@
|
||||
# REST — Derived Rules
|
||||
|
||||
> Derives from `domains/api/first-principles.md`. Applies P1–P10 to REST specifically.
|
||||
|
||||
## Resource Naming (P2 Clarity, P3 Predictability)
|
||||
|
||||
- Nouns, not verbs: `/users`, `/orders`, not `/getUsers`.
|
||||
- Plural: `/users` (collection), `/users/{id}` (item).
|
||||
- Lowercase, hyphenated: `/order-items`, not `/OrderItems` or `/order_items`.
|
||||
- Nesting max 2 levels: `/users/{id}/orders`, not `/users/{id}/orders/{oid}/items/{iid}`.
|
||||
|
||||
## HTTP Methods (P1 Contract Fidelity, P6 Idempotency)
|
||||
|
||||
| Method | Semantics | Idempotent | Safe |
|
||||
|--------|-----------|------------|------|
|
||||
| GET | Read | Yes | Yes |
|
||||
| POST | Create | No | No |
|
||||
| PUT | Replace (full) | Yes | No |
|
||||
| PATCH | Update (partial) | No | No |
|
||||
| DELETE | Remove | Yes | No |
|
||||
|
||||
- PUT requires the full resource. PATCH requires only the delta. Never accept a partial PUT.
|
||||
- POST creates; never use POST for read operations. POST is not cacheable.
|
||||
|
||||
## Status Codes (P9 Error Transparency, P1 Contract Fidelity)
|
||||
|
||||
| Code | Meaning | When |
|
||||
|------|---------|------|
|
||||
| 200 | OK | Successful GET, PUT, PATCH |
|
||||
| 201 | Created | Successful POST that created a resource |
|
||||
| 204 | No Content | Successful DELETE, or empty response |
|
||||
| 400 | Bad Request | Malformed request (client error) |
|
||||
| 401 | Unauthorized | Authentication required or failed |
|
||||
| 403 | Forbidden | Authenticated but not permitted |
|
||||
| 404 | Not Found | Resource does not exist |
|
||||
| 409 | Conflict | State conflict (e.g., duplicate) |
|
||||
| 422 | Unprocessable | Well-formed but semantically invalid |
|
||||
| 429 | Too Many Requests | Rate limited |
|
||||
| 500 | Internal Error | Server bug — never leak stack trace |
|
||||
|
||||
- Never return 200 on an error. Never return 500 with a stack trace.
|
||||
- 401 vs 403: 401 = "who are you?", 403 = "I know who you are, but you can't."
|
||||
|
||||
## Idempotency (P6 Idempotency)
|
||||
|
||||
- POST: not idempotent. Use an idempotency key (`Idempotency-Key` header) for safe retry.
|
||||
- PUT: idempotent by definition — same PUT twice = same state.
|
||||
- DELETE: idempotent — deleting a non-existent resource is success (204).
|
||||
- PATCH: not idempotent by default; can be made idempotent with explicit versioning.
|
||||
|
||||
## Pagination (P7 Performance, see `pagination.md`)
|
||||
|
||||
- Default to cursor pagination for collections > 100 items.
|
||||
- Never return unbounded collections.
|
||||
- `Link` header or `cursor` field in response body.
|
||||
|
||||
## Versioning (P5 Versioning, P10 Stability, see `versioning.md`)
|
||||
|
||||
- Version in the URL (`/v1/users`) or in the header (`Accept: application/vnd.atelier.v1+json`).
|
||||
- Pick one. Be consistent across all endpoints.
|
||||
- Never make a breaking change without a new version and a deprecation cycle.
|
||||
|
||||
## Security (P8 Security, see `domains/security/`)
|
||||
|
||||
- HTTPS only. Redirect HTTP to HTTPS.
|
||||
- Authentication on every non-public endpoint. No opt-in auth.
|
||||
- Rate limiting on write endpoints (POST, PUT, PATCH, DELETE).
|
||||
- Validate every input against a schema. Never pass raw request body to the database.
|
||||
@@ -0,0 +1,54 @@
|
||||
# API Versioning — Derived Rules
|
||||
|
||||
> Derives from `domains/api/first-principles.md` P5 (Versioning) and P10 (Stability).
|
||||
|
||||
## The Default: No Breaking Changes
|
||||
|
||||
- A breaking change is a new version. There is no "minor" breaking change.
|
||||
- Breaking changes: removing a field, changing a field type, changing a field's semantics, changing required vs optional, changing error codes.
|
||||
- Non-breaking changes: adding a field, adding an endpoint, adding an optional parameter, loosening validation.
|
||||
|
||||
## Version Policies
|
||||
|
||||
### URL Versioning (`/v1/users`)
|
||||
- Simple, visible, cacheable.
|
||||
- Breaking changes bump the major version: `/v1` → `/v2`.
|
||||
- Old versions are supported in parallel during the deprecation window.
|
||||
|
||||
### Header Versioning (`Accept: application/vnd.atelier.v1+json`)
|
||||
- Invisible in the URL; harder to test.
|
||||
- Useful when the URL must stay stable (e.g., public webhooks).
|
||||
|
||||
### Semantic Versioning (for libraries/SDKs)
|
||||
- Major: breaking. Minor: additive. Patch: fix.
|
||||
- Follow semver strictly. A "minor" that breaks is a lie.
|
||||
|
||||
## Deprecation Cycle (P5 Reversibility)
|
||||
|
||||
1. **Announce**: mark the field/endpoint `@deprecated` with a sunset date.
|
||||
2. **Support**: keep the old version working until the sunset date.
|
||||
3. **Monitor**: track usage of the deprecated surface.
|
||||
4. **Retire**: when usage drops below threshold (or sunset passes), remove.
|
||||
5. **Never** remove without announcing. The cost of a silent break is paid by every consumer.
|
||||
|
||||
## Sunset Headers (P9 Error Transparency)
|
||||
|
||||
- Deprecated endpoints return `Sunset: <date>` header.
|
||||
- Deprecated endpoints return `Deprecation: <date>` header.
|
||||
- A consumer who reads headers knows when to migrate.
|
||||
|
||||
## Versioning vs Compatibility
|
||||
|
||||
- Versioning is the mechanism. Compatibility is the property.
|
||||
- Backward compatibility: old consumers work with the new version.
|
||||
- Forward compatibility: new consumers work with the old version (harder, rarer, usually not worth it).
|
||||
- Aim for backward compatibility. Forward compatibility is for protocols, not APIs.
|
||||
|
||||
## What Violates Versioning
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| Removing a field without deprecation | P5, P10 |
|
||||
| Changing a field's type in a "minor" release | P1, P5 |
|
||||
| No sunset header on a deprecated endpoint | P9 |
|
||||
| Two versions with divergent semantics for the same field | P1 |
|
||||
@@ -0,0 +1,41 @@
|
||||
# Concurrency — First Principles
|
||||
|
||||
## 1. The Principles
|
||||
|
||||
### P1. Immutability by Default
|
||||
Mutable shared state is the enemy. The default is immutable; mutation
|
||||
is justified.
|
||||
|
||||
### P2. Single Responsibility for Threads
|
||||
Each unit of work has one owner. No "anyone can touch this" state.
|
||||
|
||||
### P3. Boundaries are Locks
|
||||
Synchronization happens at well-defined places. Lock scope is
|
||||
minimal and explicit.
|
||||
|
||||
### P4. Determinism Over Speed
|
||||
Correct concurrent code is faster than incorrect concurrent code.
|
||||
Race conditions are not "fast enough" — they are wrong.
|
||||
|
||||
### P5. Lock Minimization
|
||||
Locks are expensive and dangerous. Lock-free, wait-free, and
|
||||
message-passing are preferred where possible.
|
||||
|
||||
### P6. No Silent Races
|
||||
Race conditions are caught, not hidden. Tools (TSan, Go race
|
||||
detector) are part of CI.
|
||||
|
||||
### P7. Cancellation Support
|
||||
Every async operation can be cancelled. Cancellation is fast and
|
||||
complete.
|
||||
|
||||
### P8. Timeout Discipline
|
||||
Every blocking call has a timeout. Forever is not a duration.
|
||||
|
||||
### P9. Bounded Queues
|
||||
Unbounded queues are memory leaks in disguise. Bounded queues
|
||||
expose backpressure.
|
||||
|
||||
### P10. Test for Race Conditions
|
||||
Concurrent code is tested under concurrent load, not just happy-path
|
||||
correctness.
|
||||
@@ -0,0 +1,62 @@
|
||||
# Concurrency Patterns — Derived Rules
|
||||
|
||||
> Derives from `domains/concurrency/first-principles.md`. Common concurrency patterns and when to use them.
|
||||
|
||||
## Pattern 1: Message Passing (P5 Lock Minimization)
|
||||
|
||||
- Threads/goroutines communicate via channels/queues, not shared memory.
|
||||
- "Don't communicate by sharing memory; share memory by communicating." (Go proverb)
|
||||
- Use when the data flows naturally in one direction. Avoids locks entirely.
|
||||
|
||||
## Pattern 2: Read-Write Lock (P5 Lock Minimization)
|
||||
|
||||
- Multiple readers, one writer. A `RwLock` allows concurrent reads, exclusive writes.
|
||||
- Use when reads vastly outnumber writes (e.g., a config cache).
|
||||
- Avoid when writes are frequent — the lock degrades to a mutex.
|
||||
|
||||
## Pattern 3: Actor Model (P2 Single Responsibility)
|
||||
|
||||
- Each actor owns its state. Actors communicate via messages. No shared state.
|
||||
- Use for isolated, long-lived workers (e.g., a session handler, a chat room).
|
||||
- Erlang/Akka/Pony are built on this. Implementable in any language with channels.
|
||||
|
||||
## Pattern 4: Immutable Data Structures (P1 Immutability by Default)
|
||||
|
||||
- Data is never mutated; a "change" creates a new value. Old values are safe to share.
|
||||
- Use in functional languages (Haskell, Clojure) or via persistent data structures (Immer.js).
|
||||
- Eliminates entire classes of races. The trade-off is allocation cost.
|
||||
|
||||
## Pattern 5: Bounded Queue with Backpressure (P9 Bounded Queues)
|
||||
|
||||
- A queue with a max size. When full, the producer is blocked or signaled.
|
||||
- Use to bound memory and propagate slowness from consumer to producer.
|
||||
- An unbounded queue hides a slow consumer until OOM. Always bound.
|
||||
|
||||
## Pattern 6: Timeout on Every Block (P8 Timeout Discipline)
|
||||
|
||||
- Every blocking call (lock acquire, queue send, HTTP request) has a timeout.
|
||||
- Use a timeout, not a forever-block. Forever is not a duration.
|
||||
- On timeout: cancel, retry, or fail. Do not hang.
|
||||
|
||||
## Pattern 7: Cancellation Propagation (P7 Cancellation Support)
|
||||
|
||||
- A cancellation signal propagates to all spawned work. Cancel the parent, the children stop.
|
||||
- Use context (`context.Context` in Go, `AbortController` in JS, `CancellationToken` in C#).
|
||||
- Cancellation is fast and complete. No orphaned goroutines/threads.
|
||||
|
||||
## Pattern 8: Lock-Free Where Possible (P5)
|
||||
|
||||
- Atomic operations (compare-and-swap) for simple state. No lock.
|
||||
- Use for counters, flags, simple pointers.
|
||||
- Avoid for complex state — lock-free code is subtle and easy to get wrong.
|
||||
|
||||
## What Violates Concurrency Patterns
|
||||
|
||||
| Violation | Pattern |
|
||||
|-----------|---------|
|
||||
| Shared mutable state with no lock | (race, P1) |
|
||||
| Unbounded queue | P5 (OOM) |
|
||||
| `channel.send()` with no timeout | P6 (hang) |
|
||||
| Spawned goroutine with no cancellation | P7 (orphan) |
|
||||
| A mutex held across an I/O call | P3 (lock scope) |
|
||||
| `sync.Mutex` for a counter | P8 (use atomic) |
|
||||
@@ -0,0 +1,43 @@
|
||||
# Data — First Principles
|
||||
|
||||
## 1. The Principles
|
||||
|
||||
### P1. Truth
|
||||
The schema reflects the domain, not the application. If the data
|
||||
model lies, every query lies.
|
||||
|
||||
### P2. Normalization Discipline
|
||||
Duplication is a bug waiting to happen. The same fact lives in one
|
||||
place.
|
||||
|
||||
### P3. Invariants in the Schema
|
||||
Constraints live where the data lives. Application-layer checks are
|
||||
defense, not enforcement.
|
||||
|
||||
### P4. Migration Safety
|
||||
Schema changes are reversible, non-destructive, and tested. Production
|
||||
data is sacred.
|
||||
|
||||
### P5. Indexing with Intent
|
||||
Indexes exist for known query patterns. Every index earns its write
|
||||
cost.
|
||||
|
||||
### P6. Naming Consistency
|
||||
Same concept, same name, always. Across tables, columns, code, and
|
||||
APIs.
|
||||
|
||||
### P7. Type Fidelity
|
||||
Types match domain meaning. A `string` is rarely the right type for
|
||||
an email, an ID, or a status.
|
||||
|
||||
### P8. Lifecycle Awareness
|
||||
Data has a creation, a lifetime, and an end. Archival and deletion
|
||||
are first-class.
|
||||
|
||||
### P9. Referential Integrity
|
||||
Relationships are enforced, not assumed. Foreign keys exist. CASCADE
|
||||
is intentional.
|
||||
|
||||
### P10. Performance Awareness
|
||||
Schema choices have cost. Query plans are reviewed. Cardinality is
|
||||
understood.
|
||||
@@ -0,0 +1,49 @@
|
||||
# Indexing — Derived Rules
|
||||
|
||||
> Derives from `domains/data/first-principles.md` P5 (Indexing with Intent), P10 (Performance Awareness).
|
||||
|
||||
## Index for Queries, Not Tables (P5)
|
||||
|
||||
- An index serves a query. No query, no index.
|
||||
- The query plan is the spec. `EXPLAIN` is the test. An index that is not used is dead weight.
|
||||
- Index the columns you filter on (`WHERE`), join on (`JOIN`), and sort on (`ORDER BY`).
|
||||
|
||||
## Composite Indexes (P5, P10)
|
||||
|
||||
- Order matters: `INDEX(a, b)` serves `WHERE a = ? AND b = ?` and `WHERE a = ?`, but NOT `WHERE b = ?`.
|
||||
- Put the most selective column first. Or the column used in every query. Depends on the workload.
|
||||
- An index on every column is not a strategy. It is write amplification.
|
||||
|
||||
## Unique Indexes (P3 Invariants in Schema)
|
||||
|
||||
- A uniqueness constraint is a unique index. Use it for invariants: `email`, `username`.
|
||||
- Unique indexes enforce; application checks defend. Both belong.
|
||||
- A partial unique index: `UNIQUE(email) WHERE deleted_at IS NULL` — allows soft-deleted duplicates.
|
||||
|
||||
## Covering Indexes (P10)
|
||||
|
||||
- An index that covers all columns of a query is an "index-only scan" — no table lookup.
|
||||
- PostgreSQL: `INCLUDE` clause. MySQL: all columns in the index.
|
||||
- Use for hot queries. Don't cover everything; index size matters.
|
||||
|
||||
## Don't Over-Index (P10, core C8 Economy)
|
||||
|
||||
- Every index costs a write. The write budget is the index count.
|
||||
- Indexes take disk and memory. A 1GB index on a 500MB table is a smell.
|
||||
- Remove unused indexes. `pg_stat_user_indexes` shows usage. An unused index is debt.
|
||||
|
||||
## Migration and Indexes (P4 Migration Safety)
|
||||
|
||||
- Adding an index on a large table is expensive. Do it concurrently (`CREATE INDEX CONCURRENTLY`).
|
||||
- An index migration that locks the table blocks writes. Plan for it.
|
||||
- Build the index, then deploy the query that uses it. Not the reverse.
|
||||
|
||||
## What Violates Indexing Discipline
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| Index on every column | P10, C8 Economy |
|
||||
| No index on a foreign key | P10 (join performance) |
|
||||
| `WHERE b = ?` with only `INDEX(a, b)` | P5 (wrong order) |
|
||||
| Index created without checking the query plan | P5 (no intent) |
|
||||
| `CREATE INDEX` (non-concurrent) on a 10M-row table in prod | P4 Migration Safety |
|
||||
@@ -0,0 +1,53 @@
|
||||
# Migrations — Derived Rules
|
||||
|
||||
> Derives from `domains/data/first-principles.md` P4 (Migration Safety), P5 (Reversibility via core C5).
|
||||
|
||||
## Every Change is a Migration (P4)
|
||||
|
||||
- No manual schema changes. No `ALTER TABLE` in a shell. Every change is a versioned migration file.
|
||||
- Migrations are code: reviewed, tested, committed.
|
||||
- The migration tool is the only way to change the schema (`prisma migrate`, `alembic`, `flyway`, `golang-migrate`).
|
||||
|
||||
## Forward and Reverse (P5 Reversibility, core C5)
|
||||
|
||||
- Every migration has an `up` and a `down`. The `down` reverses the `up`.
|
||||
- A migration without a `down` is irreversible. Irreversible migrations are rare and flagged.
|
||||
- Test the `down` in CI. A `down` that fails is a migration that cannot be rolled back.
|
||||
|
||||
## Expand, Migrate, Contract (P5)
|
||||
|
||||
For non-breaking schema changes:
|
||||
|
||||
1. **Expand**: add the new column/ table (nullable, no constraint). Deploy. Old code still works.
|
||||
2. **Migrate**: backfill data, run the data migration. Deploy. Both old and new code work.
|
||||
3. **Contract**: add constraints, remove the old column. Deploy after all code uses the new schema.
|
||||
|
||||
Never do all three in one migration. Each step is its own deploy.
|
||||
|
||||
## Avoid Destructive Changes (P4)
|
||||
|
||||
- Never `DROP COLUMN` in a migration that could be in use. Expand-contract first.
|
||||
- Never `DROP TABLE` without confirming no code references it.
|
||||
- Never `ALTER TYPE` in a way that locks the table on a large dataset. Use a phased approach.
|
||||
|
||||
## Backward Compatibility (P5, P1)
|
||||
|
||||
- A migration must not break the running code. Old code reads the new schema (with expand).
|
||||
- The schema is always compatible with the previous code version. Two-version compatibility.
|
||||
- A breaking migration is deployed in lockstep with the code, with a maintenance window.
|
||||
|
||||
## Testing Migrations (P3 Determinism via testing P3)
|
||||
|
||||
- Run migrations on a copy of production data in CI. A migration that works on dev may fail on prod scale.
|
||||
- Test the `down` on the migrated state, not just the `up`.
|
||||
- Test with the largest table sizes you have. `ALTER TABLE` on 10 rows is fast; on 10M rows, it may lock.
|
||||
|
||||
## What Violates Migration Safety
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| Manual `ALTER TABLE` in prod | P4 |
|
||||
| Migration with no `down` | P5 Reversibility |
|
||||
| `DROP COLUMN` in the same deploy as the new code | P4, P5 |
|
||||
| No migration test on prod-scale data | P3 Determinism |
|
||||
| A migration that locks a table for 10 minutes | P4 (downtime) |
|
||||
@@ -0,0 +1,53 @@
|
||||
# Schema Design — Derived Rules
|
||||
|
||||
> Derives from `domains/data/first-principles.md` P1 (Truth), P3 (Invariants in Schema), P7 (Type Fidelity).
|
||||
|
||||
## The Schema Reflects the Domain (P1 Truth)
|
||||
|
||||
- A `users` table has columns that are attributes of a user, not attributes of the application.
|
||||
- If a column is named `is_active_for_feature_X`, the schema is lying. The domain does not have "feature X."
|
||||
- Normalize until it hurts, then denormalize only with evidence (P10 Performance Awareness).
|
||||
|
||||
## Invariants in the Schema (P3)
|
||||
|
||||
- NOT NULL where the value is required. UNIQUE where the value is unique.
|
||||
- CHECK constraints for range/domain: `age >= 0`, `status IN ('draft', 'published')`.
|
||||
- FOREIGN KEY for relationships. The database enforces; the application defends.
|
||||
- A constraint in the application but not the schema is a constraint that can be bypassed.
|
||||
|
||||
## Types (P7 Type Fidelity)
|
||||
|
||||
- `UUID` for IDs, not `VARCHAR`. `UUID` is a type; `VARCHAR(36)` is a string that looks like a UUID.
|
||||
- `TIMESTAMPTZ` for timestamps, not `VARCHAR` or `INTEGER`. Timezone-aware by default.
|
||||
- `ENUM` for finite domains, `VARCHAR` with CHECK for evolving domains.
|
||||
- `JSONB` for unstructured/semi-structured; not for data that should be a column.
|
||||
- `DECIMAL`/`NUMERIC` for money, never `FLOAT`. Floating point is for measurements, not money.
|
||||
|
||||
## Naming (P6 Naming Consistency)
|
||||
|
||||
- snake_case for tables and columns (PostgreSQL convention): `user_accounts`, `created_at`.
|
||||
- Singular table names (`user` not `users`) OR plural (`users` not `user`) — pick one, be consistent.
|
||||
- Foreign keys: `<singular_table>_id` (`user_id`), not `uid` or `user`.
|
||||
- Junction tables: alphabetical (`order_products`, not `products_orders`).
|
||||
|
||||
## Avoid (P2 Normalization Discipline)
|
||||
|
||||
- Computed columns that duplicate derivable data. Use a view or compute on read.
|
||||
- `created_by_name` (denormalized) when `created_by_id` + JOIN suffices. Denormalize only with evidence.
|
||||
- Soft-delete columns (`is_deleted`) without a corresponding constraint/behavior. Soft delete is a lifecycle decision (P8).
|
||||
|
||||
## Soft Delete vs Hard Delete (P8 Lifecycle Awareness)
|
||||
|
||||
- Soft delete (`deleted_at TIMESTAMP`) preserves auditability but complicates every query.
|
||||
- Hard delete loses history. Choose based on the domain's legal/audit requirements.
|
||||
- If soft delete: every query filters `WHERE deleted_at IS NULL` by default. A missing filter is a bug.
|
||||
|
||||
## What Violates Schema Design
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| `VARCHAR` for a UUID | P7 Type Fidelity |
|
||||
| No FOREIGN KEY on a relationship | P3, P9 Referential Integrity |
|
||||
| `FLOAT` for money | P7, P1 Truth |
|
||||
| `is_deleted` without consistent filtering | P8 Lifecycle |
|
||||
| A column named after a feature, not a domain concept | P1 Truth |
|
||||
@@ -0,0 +1,50 @@
|
||||
# CI/CD — Derived Rules
|
||||
|
||||
> Derives from `domains/devops/first-principles.md` P2 (Automation), P4 (Rollback First), P5 (Progressive Delivery).
|
||||
|
||||
## The Pipeline is the Process (P2)
|
||||
|
||||
- If it is not in the pipeline, it does not happen. Manual deploys are a bug.
|
||||
- The pipeline: lint → test → build → deploy → verify.
|
||||
- Every step is scripted, versioned, and reproducible. No "run this command on the server."
|
||||
|
||||
## Lint (P2, core C2 Clarity)
|
||||
|
||||
- Run the linter on every commit. Fail the build on lint errors.
|
||||
- Format check (prettier, gofmt, rustfmt). Format is not a debate; it is automated.
|
||||
- Security lint (eslint-plugin-security, bandit, gosec). Catch the obvious ones.
|
||||
|
||||
## Test (P2, domains/testing)
|
||||
|
||||
- Unit tests in the pipeline. Fast. Every commit.
|
||||
- Integration tests on merge to main. Slower. Every merge.
|
||||
- E2E tests before deploy. Slowest. Every deploy candidate.
|
||||
|
||||
## Build (P7 Immutability)
|
||||
|
||||
- Build once. The artifact is immutable. The same artifact goes to every environment.
|
||||
- The build is reproducible: same commit → same artifact (modulo timestamps, which are stripped).
|
||||
- Build artifacts are signed and stored. A deploy is a reference to an artifact, not a rebuild.
|
||||
|
||||
## Deploy (P4 Rollback First, P5 Progressive Delivery)
|
||||
|
||||
- Every deploy has a rollback. The rollback is tested before the deploy.
|
||||
- Progressive: canary (1% → 10% → 100%), blue-green, or feature flags.
|
||||
- No big-bang deploys. A big-bang deploy is a rollback with no rehearsal.
|
||||
|
||||
## Verify (P3 Observability)
|
||||
|
||||
- After deploy, verify: health checks, smoke tests, metric watching.
|
||||
- A deploy is not "done" when the code is on the server. It is done when the metrics are healthy.
|
||||
- Auto-rollback on metric regression. The pipeline watches; humans sleep.
|
||||
|
||||
## What Violates CI/CD Discipline
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| Manual deploy script | P2 Automation |
|
||||
| No rollback path | P4 Rollback First |
|
||||
| Big-bang deploy to prod | P5 Progressive Delivery |
|
||||
| Rebuild per environment | P7 Immutability |
|
||||
| Deploy without health check | P3 Observability |
|
||||
| No lint in CI | P2, C2 |
|
||||
@@ -0,0 +1,44 @@
|
||||
# Environments — Derived Rules
|
||||
|
||||
> Derives from `domains/devops/first-principles.md` P1 (Reproducibility), P6 (Configuration as Code), P7 (Immutability).
|
||||
|
||||
## Environment Parity (P1 Reproducibility)
|
||||
|
||||
- Dev, staging, prod are the same system, different data.
|
||||
- The same artifact runs in all three. The same config schema, different values.
|
||||
- "Works on my machine" is a parity failure. The machine is the pipeline.
|
||||
|
||||
## Configuration (P6 Configuration as Code)
|
||||
|
||||
- Config is in the repo (default values) + environment overrides (secrets, endpoints).
|
||||
- No snowflake servers. No "this one is different because we edited it in prod."
|
||||
- Config changes are PRs, not SSH sessions.
|
||||
|
||||
## Secrets (P9 Secret Hygiene via security)
|
||||
|
||||
- Secrets are per-environment. Dev secrets ≠ prod secrets.
|
||||
- Secrets come from a secrets manager (Vault, AWS Secrets Manager, Doppler), not env files in prod.
|
||||
- `.env` files are for local dev only. Prod uses the manager.
|
||||
|
||||
## Promotion (P5 Progressive Delivery via devops)
|
||||
|
||||
- Code moves dev → staging → prod. Never the reverse.
|
||||
- A hotfix to prod is backported to staging and dev. Don't let them diverge.
|
||||
- Promotion is automated. The pipeline decides when code is ready, not a human.
|
||||
|
||||
## Data (P1, domains/data P8 Lifecycle Awareness)
|
||||
|
||||
- Prod data is sacred. Never copy prod to dev without anonymization.
|
||||
- Staging uses prod-like data (anonymized, sampled). Dev uses synthetic data.
|
||||
- A test that runs against prod data is a test that can destroy prod data. Don't.
|
||||
|
||||
## What Violates Environment Discipline
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| "It works on my machine" | P1 Parity |
|
||||
| Manual config edit in prod | P6 Configuration as Code |
|
||||
| Dev secret reused in prod | P9 Secret Hygiene |
|
||||
| Copy prod DB to dev | P1, data P8 |
|
||||
| Hotfix in prod not backported | P5 (divergence) |
|
||||
| A snowflake server | P1, P6 |
|
||||
@@ -0,0 +1,43 @@
|
||||
# DevOps — First Principles
|
||||
|
||||
## 1. The Principles
|
||||
|
||||
### P1. Reproducibility
|
||||
Any environment can be rebuilt from source. Configuration is
|
||||
declarative and versioned.
|
||||
|
||||
### P2. Automation
|
||||
Manual steps are bugs waiting to happen. Everything is scripted,
|
||||
testable, repeatable.
|
||||
|
||||
### P3. Observability
|
||||
You cannot operate what you cannot see. Logs, metrics, and traces
|
||||
are first-class.
|
||||
|
||||
### P4. Rollback First
|
||||
Every deploy has a known-good rollback path. Rollback is rehearsed,
|
||||
not improvised.
|
||||
|
||||
### P5. Progressive Delivery
|
||||
Changes go out gradually — canary, blue-green, feature flags.
|
||||
Big-bang deploys are for prototypes.
|
||||
|
||||
### P6. Configuration as Code
|
||||
No snowflake servers. No "this one is different". Configuration is
|
||||
in the repo.
|
||||
|
||||
### P7. Immutability
|
||||
Build once, deploy many. Artifacts are immutable. Servers are not
|
||||
mutated in place.
|
||||
|
||||
### P8. Security at Every Layer
|
||||
Scanning, signing, SBOM, and supply chain integrity are part of the
|
||||
pipeline, not bolt-ons.
|
||||
|
||||
### P9. Documentation in the Pipeline
|
||||
The pipeline is the documentation. Reading the pipeline tells you
|
||||
how the system ships.
|
||||
|
||||
### P10. Failure as Expected
|
||||
Design for the failure mode, not the happy path. Chaos engineering
|
||||
is a discipline.
|
||||
@@ -0,0 +1,83 @@
|
||||
# Doc Templates — Derived Rules
|
||||
|
||||
> Derives from `domains/documentation/first-principles.md` P7 (Structure), P2 (Audience Awareness), P3 (Examples are Mandatory).
|
||||
|
||||
## Document Structure (P7)
|
||||
|
||||
Every framework document follows a consistent structure:
|
||||
|
||||
```
|
||||
# <Title>
|
||||
|
||||
> One-line purpose. Who reads this and when.
|
||||
|
||||
## 1. Manifesto (or Introduction)
|
||||
Why this document exists. The core belief.
|
||||
|
||||
## 2. The Principles (or Rules)
|
||||
The numbered, named, derivable rules. Each rule has:
|
||||
- A name (P1, P2, ...)
|
||||
- A one-line definition
|
||||
- A "what it means" paragraph
|
||||
- A "what violates it" entry
|
||||
|
||||
## 3. Conflict Resolution (for first-principles docs)
|
||||
Precedence among the rules. Non-tradeable declarations.
|
||||
|
||||
## 4. What Violates These Principles
|
||||
A table of violations and the principle they breach.
|
||||
|
||||
## 5. Relationship to Core
|
||||
Derivation link. See `matrix/principles-matrix.md`.
|
||||
```
|
||||
|
||||
- The structure is the contract. A reader can scan any framework doc and find the same sections.
|
||||
- Derived (topic) docs simplify: drop §3, replace §5 with "Derives from `<domain>/first-principles.md`."
|
||||
|
||||
## Audience Templates (P2)
|
||||
|
||||
### For Agents
|
||||
- Lead with what to check before completing a task.
|
||||
- Bullet lists, not paragraphs.
|
||||
- "Run this checklist" framing.
|
||||
|
||||
### For Humans (Onboarding)
|
||||
- Lead with what this is and who it's for.
|
||||
- Reading order. Quickstart.
|
||||
- Conversational tone, not terse.
|
||||
|
||||
### For Humans (Reference)
|
||||
- Lead with the rules, indexed.
|
||||
- Tables for lookup.
|
||||
- Cross-references to other docs.
|
||||
|
||||
## Example Template (P3 Examples are Mandatory)
|
||||
|
||||
Every rule includes an example. The template:
|
||||
|
||||
```
|
||||
### P<n>. <Name>
|
||||
<one-line definition>
|
||||
|
||||
<what it means>
|
||||
|
||||
Good:
|
||||
<example>
|
||||
|
||||
Bad:
|
||||
<counter-example> (violates P<n>)
|
||||
```
|
||||
|
||||
- The "good" example is realistic, not a strawman.
|
||||
- The "bad" example cites the principle it violates.
|
||||
- Examples are code, not prose. Show, don't tell.
|
||||
|
||||
## What Violates Doc Templates
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| A doc with no examples | P3 Examples are Mandatory |
|
||||
| A first-principles doc with no "what violates" table | P7 Structure |
|
||||
| A doc that does not link to its core derivation | P5 Discoverability |
|
||||
| Inconsistent structure across domain docs | P7 |
|
||||
| A doc with no audience statement | P2 Audience Awareness |
|
||||
@@ -0,0 +1,37 @@
|
||||
# Documentation — First Principles
|
||||
|
||||
## 1. The Principles
|
||||
|
||||
### P1. Documentation is Code
|
||||
It is versioned, reviewed, tested, and owned. Unowned docs rot.
|
||||
|
||||
### P2. Audience Awareness
|
||||
Different readers need different docs. A new user, an operator, and
|
||||
a contributor are different audiences.
|
||||
|
||||
### P3. Examples are Mandatory
|
||||
Code without examples is incomplete. Show, then explain.
|
||||
|
||||
### P4. Currency
|
||||
Docs that lie are worse than no docs. Stale docs are technical debt.
|
||||
|
||||
### P5. Discoverability
|
||||
The right doc is findable in under a minute. Structure, search, and
|
||||
indexing are part of the doc.
|
||||
|
||||
### P6. Conciseness
|
||||
Say what is needed, no more. Verbose docs are skimmed, then ignored.
|
||||
|
||||
### P7. Structure
|
||||
Consistent structure aids scanning. Headings, ordering, and
|
||||
formatting follow conventions.
|
||||
|
||||
### P8. Why Over What
|
||||
Document intent, decisions, and tradeoffs. The "what" is in the code.
|
||||
|
||||
### P9. Living Documents
|
||||
Docs evolve with code, not after. Doc PRs ship with code PRs.
|
||||
|
||||
### P10. Public by Default
|
||||
If it is not documented, it does not exist. The absence of docs is
|
||||
a feature gap.
|
||||
@@ -0,0 +1,41 @@
|
||||
# Error Handling — First Principles
|
||||
|
||||
## 1. The Principles
|
||||
|
||||
### P1. Errors are Data
|
||||
Errors are structured, typed, and intentional. They are values, not
|
||||
exceptions to the flow of code.
|
||||
|
||||
### P2. Fail Loudly
|
||||
Never swallow an error. Silent failure is worse than visible failure.
|
||||
|
||||
### P3. Fail Specifically
|
||||
Generic errors are debugging enemies. "Something went wrong" is
|
||||
never acceptable.
|
||||
|
||||
### P4. Preserve Context
|
||||
Errors carry where (file, line, function), when (timestamp, request),
|
||||
why (cause), and what (user-facing message).
|
||||
|
||||
### P5. Recoverable When Possible
|
||||
Retry, fallback, or degrade. Do not crash what can be salvaged.
|
||||
|
||||
### P6. Unrecoverable Means Stop
|
||||
When recovery is impossible or unsafe, fail fast. Do not limp on
|
||||
after fatal errors.
|
||||
|
||||
### P7. Errors are Boundaries
|
||||
Define how errors cross API, service, and module boundaries. Translation
|
||||
is explicit, not accidental.
|
||||
|
||||
### P8. User-Facing Errors are UX
|
||||
Error messages are a feature. They are written for the user, not the
|
||||
developer.
|
||||
|
||||
### P9. Errors are Logged
|
||||
Even when handled, errors are recorded. The handling is the recovery;
|
||||
the log is the memory.
|
||||
|
||||
### P10. Errors Don't Lie
|
||||
Never catch what you cannot handle. Never claim success on failure.
|
||||
Never claim failure on success.
|
||||
@@ -0,0 +1,94 @@
|
||||
# Error Patterns — Derived Rules
|
||||
|
||||
> Derives from `domains/errors/first-principles.md`. Common error-handling patterns and when to use them.
|
||||
|
||||
## Pattern 1: Result Type (P1 Errors are Data)
|
||||
|
||||
```typescript
|
||||
type Result<T, E> = { ok: true; value: T } | { ok: false; error: E };
|
||||
|
||||
function divide(a, b): Result<number, string> {
|
||||
if (b === 0) return { ok: false, error: "division by zero" };
|
||||
return { ok: true, value: a / b };
|
||||
}
|
||||
```
|
||||
|
||||
- Errors are values, not exceptions. The caller handles them explicitly.
|
||||
- Use when errors are expected (parsing, validation, fallible operations).
|
||||
- Avoid when errors are truly exceptional (out of memory, programmer error) — use exceptions/panics.
|
||||
|
||||
## Pattern 2: Sentinel Error (P4 Preserve Context)
|
||||
|
||||
```go
|
||||
var ErrNotFound = errors.New("not found")
|
||||
if errors.Is(err, ErrNotFound) { ... }
|
||||
```
|
||||
|
||||
- A sentinel is a known error value the caller checks against.
|
||||
- Use for a small, known set of error conditions.
|
||||
- Wrap with context: `fmt.Errorf("load user %d: %w", id, ErrNotFound)`.
|
||||
|
||||
## Pattern 3: Typed Error (P1, P3 Fail Specifically)
|
||||
|
||||
```rust
|
||||
enum AppError {
|
||||
NotFound(String),
|
||||
Invalid(String),
|
||||
Internal(String),
|
||||
}
|
||||
```
|
||||
|
||||
- A typed error carries the kind and the detail.
|
||||
- The caller matches on kind; the detail is for logging/display.
|
||||
- Use when there are distinct error categories the caller handles differently.
|
||||
|
||||
## Pattern 4: Error Wrapping (P4 Preserve Context)
|
||||
|
||||
```go
|
||||
return fmt.Errorf("query users: %w", err)
|
||||
```
|
||||
|
||||
- Wrap errors as they cross boundaries. The outer error says "what was happening"; the inner says "what went wrong."
|
||||
- The error chain is the stack trace of intent. Read it top-down: "I was doing X, which failed because Y, which was caused by Z."
|
||||
- Never wrap with a generic message ("operation failed"). Wrap with the specific operation.
|
||||
|
||||
## Pattern 5: Fail Fast (P6 Unrecoverable Means Stop)
|
||||
|
||||
```typescript
|
||||
if (config.secret === undefined) throw new Error("config.secret is required");
|
||||
```
|
||||
|
||||
- For unrecoverable conditions, fail immediately. Do not limp on.
|
||||
- Use at startup: missing required config, missing database, missing secrets.
|
||||
- Do not use for recoverable conditions (a 404 is recoverable; a missing secret is not).
|
||||
|
||||
## Pattern 6: Retry with Backoff (P5 Recoverable When Possible)
|
||||
|
||||
```python
|
||||
for attempt in range(3):
|
||||
try:
|
||||
return do_thing()
|
||||
except TransientError:
|
||||
sleep(2 ** attempt)
|
||||
raise PermanentError()
|
||||
```
|
||||
|
||||
- Retry transient errors (network, 429, 5xx). Do not retry permanent errors (400, 401).
|
||||
- Exponential backoff with jitter. A retry storm is worse than the original failure.
|
||||
- Bounded retries. Infinite retry is infinite hang (P8 Timeout Discipline).
|
||||
|
||||
## Pattern 7: Circuit Breaker (P3 Defense in Depth via errors P7)
|
||||
|
||||
- After N consecutive failures, stop trying. Return a fallback or error immediately.
|
||||
- Use for external dependencies (a downstream service, an API).
|
||||
- The breaker resets after a cooldown. Protects the system and the downstream.
|
||||
|
||||
## What Violates Error Patterns
|
||||
|
||||
| Violation | Pattern |
|
||||
|-----------|---------|
|
||||
| `catch (e) { return null }` | (anti-pattern, P2 Fail Loudly) |
|
||||
| `throw new Error("error")` | P3 Fail Specifically |
|
||||
| Retry without backoff | P5, P8 |
|
||||
| `return null` for "not found" | P1 (errors are data, not absence) |
|
||||
| `throw` in a recovery path | P6 (fail fast in the wrong place) |
|
||||
@@ -0,0 +1,55 @@
|
||||
# Infrastructure as Code — First Principles
|
||||
|
||||
## 1. The Principles
|
||||
|
||||
### P1. Declarative Intent
|
||||
Describe the desired state, not the steps to reach it. The tool
|
||||
reconciles current → desired. Imperative scripts describe how;
|
||||
declarative config describes what.
|
||||
|
||||
### P2. Idempotence
|
||||
Applying the same configuration twice yields the same result. A
|
||||
second `apply` with no changes is a no-op, not an error. Idempotence
|
||||
is what makes `plan` trustworthy.
|
||||
|
||||
### P3. State is Truth
|
||||
The state file is the authoritative record of what the tool believes
|
||||
exists. Drift between state and reality is a bug to be reconciled,
|
||||
not tolerated. Lose state, lose the ability to reason about
|
||||
infrastructure.
|
||||
|
||||
### P4. Plan Before Apply
|
||||
Preview every change before mutating real infrastructure. `plan` is
|
||||
the contract review; `apply` is the signature. No `apply` without a
|
||||
read `plan`. The plan is the rollback rehearsal.
|
||||
|
||||
### P5. Version Everything
|
||||
Configuration, state, providers, and modules are all versioned and
|
||||
reproducible. A commit pins a complete, rebuildable world. Pin
|
||||
providers; pin module sources; never `latest`.
|
||||
|
||||
### P6. Modules Compose
|
||||
Encapsulate repeatable patterns as versioned modules. Compose
|
||||
modules; do not copy them. A module is the unit of reuse, review,
|
||||
and versioning — the IaC expression of composition.
|
||||
|
||||
### P7. Least Privilege Providers
|
||||
Provider credentials are scoped to the minimum needed for the
|
||||
declared resources. No account-wide admin keys in CI. One credential
|
||||
per environment, per boundary.
|
||||
|
||||
### P8. Remote State with Locking
|
||||
State is stored remotely with locking. Local state is for a single
|
||||
developer on a throwaway sandbox. Concurrent `apply` without a lock
|
||||
is data corruption waiting to happen.
|
||||
|
||||
### P9. Drift is Recoverable
|
||||
`plan` detects drift; `apply` reconciles it. Manual mutation of
|
||||
managed infrastructure is an incident, not a shortcut. Drift is
|
||||
expected; unreconciled drift is the bug.
|
||||
|
||||
### P10. Secrets Never in Code
|
||||
Secrets come from providers, external secret stores, or environment
|
||||
variables — never hardcoded in HCL, never committed to the repo,
|
||||
never written to state in plaintext. State is a secret-bearing
|
||||
artifact; treat it accordingly.
|
||||
@@ -0,0 +1,64 @@
|
||||
# Modules — Derived Rules
|
||||
|
||||
> Derives from `domains/infrastructure-as-code/first-principles.md`. P6 (Modules Compose) lives here. Referenced by `terraform.md` and `opentofu.md`.
|
||||
|
||||
## Why Modules (P6 Modules Compose)
|
||||
|
||||
- A module is the unit of reuse, review, and versioning in IaC. It encapsulates a repeatable pattern behind a typed interface.
|
||||
- Composition — building large from small — is the IaC expression of core C6 Composability. Without modules, every stack is a one-off; with modules, a stack is an assembly of reviewed parts.
|
||||
- A good module has one job (a VPC, a database, a load balancer), a small typed surface, and no hidden side effects.
|
||||
|
||||
## Module Structure (P1 Declarative Intent, C2 Clarity)
|
||||
|
||||
- The conventional layout: `main.tf` (resources), `variables.tf` (inputs), `outputs.tf` (outputs), `versions.tf` (provider/version pins). A `README.md` is required for any published module.
|
||||
- Inputs are typed and validated: `variable "name" { type = string, description = "...", validation { ... } }`. The description is the contract.
|
||||
- Outputs are the module's interface to consumers. Mark sensitive outputs `sensitive = true`. Document non-obvious outputs in the description.
|
||||
- A module does not declare a provider configuration unless it owns the provider. Most modules declare only `required_providers` (the constraint) and let the consumer configure the provider.
|
||||
|
||||
## Versioning (P5 Version Everything)
|
||||
|
||||
- Modules are versioned. The registry expects SemVer tags (`v1.0.0`). A consumer pins to a version or a range (`~> 1.0`).
|
||||
- A breaking change bumps the major. An additive change bumps the minor. A fix bumps the patch. No silent breaking changes within a minor.
|
||||
- Tag the module repo; the tag IS the version. Never `source = "git::...?ref=main"` in prod — unversioned modules drift.
|
||||
|
||||
## Source Patterns (P5 Version Everything)
|
||||
|
||||
| Source | When | Risk |
|
||||
|--------|------|------|
|
||||
| Registry (`<ns>/<name>/<provider>`) | Public, versioned, signed | Verify the publisher; pin the version |
|
||||
| Git (`git::https://...?ref=v1.0.0`) | Private modules across repos | Pin to a tag, not a branch |
|
||||
| Local (`./modules/networking`) | Monorepo, single repo | Re-reviewed on every change; no independent version |
|
||||
| Inline (no module) | Trivial one-off | Becomes a copy-paste anti-pattern at scale |
|
||||
|
||||
- Local modules in a monorepo are fine — they trade independent versioning for co-evolution. The boundary is the review unit: if the module and the consumer always change together, local is correct.
|
||||
- Cross-repo modules must be versioned via git tags; unversioned cross-repo modules are the worst case (drift without a version to pin).
|
||||
|
||||
## The Module-vs-Copy Boundary (P6 Modules Compose)
|
||||
|
||||
- If a block is used more than once, it is a module. If it is used once and will never be reused, inline is acceptable.
|
||||
- If two copies differ in one attribute, that is a module with a variable, not two copies. The variable is the difference; the shared body is the module.
|
||||
- If you find yourself copy-pasting a block and editing it, stop. The edit is a variable. The copy is a module call.
|
||||
- A module that has grown to do many jobs should be split. A module with 20 variables is two modules.
|
||||
|
||||
## Composition (P6 Modules Compose, C6 Composability)
|
||||
|
||||
- Compose by calling modules from a root configuration: `module "vpc" { source = "...", version = "..." }`. The root is the assembly; the modules are the parts.
|
||||
- Outputs of one module feed inputs of another: `module "app" { vpc_id = module.vpc.vpc_id }`. This is the composition edge.
|
||||
- Avoid hidden coupling: a module should not reach into another module's state. If two modules must share state, promote the shared concern to the root or a parent module.
|
||||
|
||||
## Reviewing Modules (P4 Plan Before Apply)
|
||||
|
||||
- A module is reviewed once, at its version. Consumers trust the version pin. A module change requires a new version and a review of the diff.
|
||||
- When a module changes, every consumer that bumps the version gets the change. Treat a module version bump as a real change: review the module diff, run the consumer's `plan`.
|
||||
- A module with a breaking change must not auto-bump in consumers. Pin consumers to the old major until they explicitly migrate.
|
||||
|
||||
## What Violates Module Discipline
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| Copy-pasted block with a one-line difference | P6 Modules Compose |
|
||||
| `source = "git::...?ref=main"` in prod | P5 Version Everything |
|
||||
| Module with 20 variables | P6 Modules Compose (split it) |
|
||||
| Silent breaking change within a minor | P5 Version Everything |
|
||||
| Module reaching into another module's state | C6 Composability, P1 Declarative Intent |
|
||||
| Unpublished module with no README | C2 Clarity |
|
||||
@@ -0,0 +1,50 @@
|
||||
# OpenTofu — Derived Rules
|
||||
|
||||
> Derives from `domains/infrastructure-as-code/first-principles.md`. OpenTofu is the open-source fork of Terraform; this doc covers fork-specific governance, license, and migration. The shared HCL/state/module model is documented in `terraform.md`. See also `state.md` and `modules.md`.
|
||||
|
||||
## Fork Lineage (P5 Version Everything)
|
||||
|
||||
- OpenTofu is a 2023 fork of Terraform, created when HashiCorp switched Terraform from MPL-2.0 to the Business Source License (BUSL), which is not open source.
|
||||
- OpenTofu is stewarded by the Linux Foundation under a genuinely open-source license. The fork's reason for existing is license neutrality.
|
||||
- Both tools implement the same HCL configuration language, the same provider protocol, and the same state model. Configuration written for one runs on the other at the fork point; divergence accrues slowly over time.
|
||||
|
||||
## When to Choose OpenTofu (P7 Least Privilege Providers, supply-chain)
|
||||
|
||||
- **License neutrality matters:** if your organization cannot accept BUSL's "competitive use" ambiguity, OpenTofu removes it.
|
||||
- **Supply-chain provenance:** Linux Foundation stewardship means no single vendor can relicense the tool out from under you.
|
||||
- **Community governance:** features and fixes are accepted on merit, not vendor strategy.
|
||||
- **When NOT to switch:** if you depend on HCP Terraform (HashiCorp's managed platform), BUSL-licensed providers, or provider features that have diverged since the fork, stay on Terraform. The decision is supply-chain, not syntax.
|
||||
|
||||
## CLI Parity (P1 Declarative Intent)
|
||||
|
||||
- `tofu init`, `tofu plan`, `tofu apply`, `tofu destroy` mirror `terraform init/plan/apply/destroy`.
|
||||
- The lock file (`.terraform.lock.hcl` → `.tofu.lock.hcl`) is committed; it makes `init` reproducible.
|
||||
- Workspaces, state backends, and module sources behave as in Terraform — see `terraform.md` and `state.md`.
|
||||
|
||||
## Registry Parity (P6 Modules Compose)
|
||||
|
||||
- OpenTofu can consume the Terraform Registry and the OpenTofu Registry. Module version pinning works identically.
|
||||
- Some providers have BUSL licenses that OpenTofu cannot ship; verify a provider's license before adopting it. An MPL or Apache provider is portable; a BUSL provider is not.
|
||||
- See `modules.md` for module structure, which is unchanged from Terraform.
|
||||
|
||||
## Migration from Terraform (P5 Version Everything, P9 Drift is Recoverable)
|
||||
|
||||
- `terraform state pull > state.json` → `tofu state push state.json` carries state across. Validate with `tofu plan` after the push — the plan should be empty.
|
||||
- Rename the binary in CI: replace `terraform` with `tofu` in scripts. The lock file may need regeneration.
|
||||
- Migrate one workspace at a time. Do not big-bang a migration; rehearse on a non-prod workspace first (P4 Plan Before Apply applies to the migration itself).
|
||||
- Pin the OpenTofu version in CI. A migration is a versioned, reviewed change, not a quiet swap.
|
||||
|
||||
## Governance and Community (cross-link `domains/security/supply-chain.md`)
|
||||
|
||||
- OpenTofu's governance model — impartial, community-driven, layered, modular, backwards-compatible — is itself a supply-chain principle. A tool you cannot trust to remain open is a tool you cannot build on.
|
||||
- This is the OpenTofu angle on `security/supply-chain.md`: license is a supply-chain property, not a legal footnote.
|
||||
|
||||
## What Violates OpenTofu Discipline
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| Assuming OpenTofu == latest Terraform (unverified parity) | P5 Version Everything |
|
||||
| Migrating prod state without a non-prod rehearsal | P4 Plan Before Apply |
|
||||
| Adopting a BUSL-licensed provider into OpenTofu CI | P7 Least Privilege Providers, supply-chain |
|
||||
| Quiet swap of `terraform` for `tofu` without a versioned change | P5 Version Everything |
|
||||
| Losing state during migration | P3 State is Truth |
|
||||
@@ -0,0 +1,77 @@
|
||||
# State — Derived Rules
|
||||
|
||||
> Derives from `domains/infrastructure-as-code/first-principles.md`. State is the cross-cutting IaC concern: P3 (State is Truth) and P8 (Remote State with Locking) live here. Referenced by `terraform.md` and `opentofu.md`.
|
||||
|
||||
## Why State Matters (P3 State is Truth)
|
||||
|
||||
- The state file is the tool's memory. It records every resource it has claimed, every attribute it has set, and every dependency it has inferred.
|
||||
- Without state, `plan` cannot compute a diff — it would have nothing to diff against. Lose state, lose the ability to reason about infrastructure safely.
|
||||
- State can contain plaintext secrets (any sensitive resource attribute). Treat state as a secret-bearing artifact: encrypt at rest, restrict access, never commit it.
|
||||
|
||||
## Remote State is Mandatory (P8 Remote State with Locking)
|
||||
|
||||
- Local state (`terraform.tfstate` on disk) is acceptable only for a single developer on a throwaway sandbox. Any shared or production environment uses a remote backend.
|
||||
- A remote backend provides: durability (state survives workstation loss), shared access (team members and CI read the same state), and locking (concurrent `apply` is serialized).
|
||||
- No locking = data corruption. Two `apply` runs against the same unlocked state race; the loser's changes are silently overwritten.
|
||||
|
||||
## Backend Comparison (P8, C4 Locality)
|
||||
|
||||
| Backend | Locking | Encryption | Best for | Notes |
|
||||
|---------|---------|------------|----------|-------|
|
||||
| S3 + DynamoDB | DynamoDB | SSE-KMS | AWS-hosted | The canonical AWS backend; DynamoDB provides the lock |
|
||||
| GCS | Built-in | CMEK | GCP-hosted | Native locking via GCS object versioning |
|
||||
| Azure Blob | Lease | Customer key | Azure-hosted | Lease-based locking |
|
||||
| HTTP (remote) | Server-side | Server-side | Self-hosted / on-prem | Requires a backend server (e.g., `terraform-backend`) |
|
||||
| Local | None | None | Single-dev sandbox | Never for shared or prod |
|
||||
| Consul | KV lock | — | Consul shops | Locking via Consul sessions |
|
||||
| Postgres | TX | DB encryption | DBA-owned infra | Row-level locking |
|
||||
|
||||
- Pick one backend per environment family. Mixing backends across environments fragments operational knowledge (C4 Locality).
|
||||
- The backend config is part of the configuration, not a runtime secret. Credentials for the backend are runtime secrets.
|
||||
|
||||
## State Isolation per Environment (P4 Plan Before Apply, C4 Locality)
|
||||
|
||||
- One state per environment. Never share a single state file across dev, staging, and prod. A `plan` against a shared state crosses environment boundaries — a prod change could appear in a dev plan.
|
||||
- Isolation patterns: separate workspaces, separate state keys in the same backend, or separate backends entirely. Stricter isolation = safer (separate backends for prod vs non-prod).
|
||||
- Name state keys by environment and stack: `env:/prod/Networking`, not `prod` or `state`.
|
||||
|
||||
## Locking Discipline (P8 Remote State with Locking)
|
||||
|
||||
- `terraform force-unlock` is for a stuck lock after a crashed run, not for impatience. Verify the run is actually dead before forcing.
|
||||
- A forced unlock without verifying the other run is dead causes the corruption the lock prevents.
|
||||
- In CI, set a lock timeout so a wedged job fails rather than hanging.
|
||||
|
||||
## Sensitive Values in State (P10 Secrets Never in Code)
|
||||
|
||||
- Any `sensitive = true` attribute is hidden from plan output but stored in state in plaintext (unless the provider encrypts it).
|
||||
- Backends with at-rest encryption (S3 SSE-KMS, GCS CMEK) protect state at rest. Access to the state file itself is the boundary.
|
||||
- Never log, print, or commit state. Never pipe `terraform show` to a public channel.
|
||||
|
||||
## State Commands (P3 State is Truth)
|
||||
|
||||
- `terraform state list` — enumerate resources in state. First step of any state investigation.
|
||||
- `terraform state show <addr>` — inspect one resource's recorded attributes.
|
||||
- `terraform state mv` — rename a resource's address without destroying and recreating it. Use when refactoring module structure.
|
||||
- `terraform state rm` — stop managing a resource without destroying it. Use when handing a resource to another configuration.
|
||||
- `terraform state pull` / `push` — export and import state. Used in migrations (see `opentofu.md`).
|
||||
- `terraform import` — bring an existing resource under management by recording its state. The resource must already exist; `import` does not create.
|
||||
- All `state` subcommands except `list` and `show` mutate state. Treat them as changes: review the intent, run in CI where possible, and commit the resulting config change that justifies the state move.
|
||||
|
||||
## Drift and Reconciliation (P9 Drift is Recoverable)
|
||||
|
||||
- `terraform plan` reports drift: resources that exist in state but were changed out-of-band, or resources in state that no longer exist in the provider.
|
||||
- `terraform apply` reconciles drift by bringing reality back to the declared state.
|
||||
- Manual changes to managed resources are the cause of drift. Treat a drift report as an incident: find who made the manual change and why, then close the access path or the gap that allowed it.
|
||||
- `terraform plan -refresh=false` skips drift detection. Use only when you know state is current and you want a fast plan; never use it to hide drift.
|
||||
|
||||
## What Violates State Discipline
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| Committed `terraform.tfstate` | P3 State is Truth, P10 Secrets |
|
||||
| Local state in prod | P8 Remote State with Locking |
|
||||
| `force-unlock` without verifying the dead run | P8 Remote State with Locking |
|
||||
| Shared state across environments | P4 Plan Before Apply, C4 Locality |
|
||||
| Unnamed state keys (`env:/prod`) | C4 Locality |
|
||||
| Manual change to a managed resource | P9 Drift is Recoverable |
|
||||
| `state rm` to "fix" a stuck resource | P3 State is Truth |
|
||||
@@ -0,0 +1,58 @@
|
||||
# Terraform — Derived Rules
|
||||
|
||||
> Derives from `domains/infrastructure-as-code/first-principles.md`. Applies P1–P10 to Terraform specifically. See also `opentofu.md` (the open-source fork), `state.md`, and `modules.md`.
|
||||
|
||||
## HCL Structure (P1 Declarative Intent)
|
||||
|
||||
- Resources are declared, not scripted. A resource block states what should exist; Terraform reconciles it.
|
||||
- `resource "aws_s3_bucket" "logs" { ... }` — the type and name are the identity; the body is the desired state.
|
||||
- Data sources read existing state without claiming ownership: `data "aws_caller_identity" "current" {}`.
|
||||
- Variables are the input contract; outputs are the interface to consumers. Both are typed.
|
||||
|
||||
## Providers (P5 Version Everything, P7 Least Privilege)
|
||||
|
||||
- Pin the provider version: `required_providers { aws = { source = "hashicorp/aws", version = "~> 5.0" } }`.
|
||||
- A provider block configures credentials and region. Credentials come from environment, files, or a secrets manager — never inline.
|
||||
- One credential set per environment. Do not reuse a prod credential in a dev workspace.
|
||||
|
||||
## The Core Workflow (P4 Plan Before Apply)
|
||||
|
||||
- `terraform init` — resolve providers and modules. Reproducible from the lock file (`.terraform.lock.hcl`), which is committed.
|
||||
- `terraform plan` — preview the diff. Read it. Every line. The plan is the contract review.
|
||||
- `terraform apply` — execute the plan. Requires a reviewed plan in CI; in interactive use, requires typing `yes`.
|
||||
- `terraform destroy` — tear down. Treat `destroy` as a first-class operation with its own plan review; prod destroys are a change event, not a keystroke.
|
||||
|
||||
## Workspaces (P4 Locality of Environments)
|
||||
|
||||
- Workspaces separate state for the same configuration across environments (dev, staging, prod).
|
||||
- Do not use workspaces to separate unrelated stacks — use separate configurations. A workspace is an environment axis, not a project axis.
|
||||
- State is isolated per workspace (see `state.md`).
|
||||
|
||||
## State Backends (P3 State is Truth, P8 Remote State with Locking)
|
||||
|
||||
- Remote state is mandatory for any shared or production environment. See `state.md` for backend selection and locking.
|
||||
- Never commit `terraform.tfstate` to the repo. It is a secret-bearing artifact and a source of drift.
|
||||
- `terraform state` subcommands inspect and manipulate state directly — use sparingly, only for recovery.
|
||||
|
||||
## Registry and Modules (P6 Modules Compose)
|
||||
|
||||
- The Terraform Registry hosts versioned, signed modules. Reference modules by version: `source = "terraform-aws-modules/vpc/aws"`, `version = "5.x"`.
|
||||
- Compose modules rather than copy-pasting blocks. A module is reviewed once and reused many times.
|
||||
- See `modules.md` for module structure, versioning, and the module-vs-copy boundary.
|
||||
|
||||
## Secrets (P10 Secrets Never in Code)
|
||||
|
||||
- Secrets via provider data sources (`aws_secretsmanager_secret_version`), environment variables, or a dedicated secrets provider. Never a literal string in a resource block.
|
||||
- State may contain plaintext secrets if a resource attribute is sensitive. Mark attributes `sensitive = true` to keep them out of plan output; use a backend that encrypts state at rest (see `state.md`).
|
||||
|
||||
## What Violates Terraform Discipline
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| Unpinned provider (`source` without `version`) | P5 Version Everything |
|
||||
| `terraform apply` without a read `plan` | P4 Plan Before Apply |
|
||||
| Local state in a shared environment | P8 Remote State with Locking |
|
||||
| Hardcoded secret in HCL | P10 Secrets Never in Code |
|
||||
| Copy-pasted resource blocks instead of a module | P6 Modules Compose |
|
||||
| Manual change to a managed resource | P9 Drift is Recoverable |
|
||||
| Admin credentials in CI | P7 Least Privilege Providers |
|
||||
@@ -0,0 +1,43 @@
|
||||
# Observability — First Principles
|
||||
|
||||
## 1. The Principles
|
||||
|
||||
### P1. Structured by Default
|
||||
Logs, metrics, and traces are structured. Free-form text is for
|
||||
humans; machines need fields.
|
||||
|
||||
### P2. Correlation
|
||||
Every event is traceable to a request, a user, an action, a trace ID.
|
||||
Context flows through the system.
|
||||
|
||||
### P3. Sufficient Context
|
||||
The information needed to debug is in the event itself, not in tribal
|
||||
knowledge. "What was the user doing?" is answerable from logs.
|
||||
|
||||
### P4. Cardinality Discipline
|
||||
Labels and tags have bounded cardinality. Unbounded labels are an
|
||||
unbounded bill.
|
||||
|
||||
### P5. Sampling with Intent
|
||||
Sampling is deliberate, documented, and consistent. Head-based,
|
||||
tail-based, or none — chosen with reason.
|
||||
|
||||
### P6. No Secrets in Observability
|
||||
Observability data is not a secrets channel. Tokens, passwords, and
|
||||
PII do not enter logs, metrics, or traces.
|
||||
|
||||
### P7. Actionable Alerts
|
||||
Alerts are for things humans must act on. Every alert has a runbook.
|
||||
Alert fatigue is a defect.
|
||||
|
||||
### P8. SLI/SLO Awareness
|
||||
"Good enough" is defined. SLOs are targets, not aspirations.
|
||||
Error budgets are real.
|
||||
|
||||
### P9. Cost Awareness
|
||||
Observability has a cost — storage, compute, attention. Spend it on
|
||||
what earns it.
|
||||
|
||||
### P10. Debuggability Over Coverage
|
||||
A few high-cardinality traces beat millions of low-context logs.
|
||||
Signal beats volume.
|
||||
@@ -0,0 +1,49 @@
|
||||
# Logging — Derived Rules
|
||||
|
||||
> Derives from `domains/observability/first-principles.md` P1 (Structured by Default), P3 (Sufficient Context), P6 (No Secrets in Observability).
|
||||
|
||||
## Structured by Default (P1)
|
||||
|
||||
- Logs are JSON (or structured key-value). Free-form text is for humans; machines need fields.
|
||||
- Every log entry has: `timestamp`, `level`, `message`, `request_id`, plus domain-specific fields.
|
||||
- A log you cannot query is a log you cannot use. Structure is the query API.
|
||||
|
||||
## Levels (P3 Sufficient Context)
|
||||
|
||||
| Level | When |
|
||||
|-------|------|
|
||||
| ERROR | Something failed; an operator must look |
|
||||
| WARN | Something unexpected; not a failure but notable |
|
||||
| INFO | Significant application events (start, stop, deploy, user signup) |
|
||||
| DEBUG | Diagnostic detail; off in production by default |
|
||||
|
||||
- ERROR is not for "this branch ran." ERROR is for "this failed and someone should know."
|
||||
- Logging everything at ERROR means nothing is an error. Alert fatigue is a defect (observability P7).
|
||||
|
||||
## Context (P3)
|
||||
|
||||
- Every log in a request includes `request_id` (correlation ID). Trace the request across services.
|
||||
- Include the user ID, the action, the resource. "What was the user doing?" is answerable.
|
||||
- A log that says `"failed"` with no context is worse than no log. It is noise.
|
||||
|
||||
## No Secrets (P6, domains/security P9)
|
||||
|
||||
- Never log tokens, passwords, API keys, session IDs, PII.
|
||||
- Redact: replace the secret with `[REDACTED]` or a hash. Log the hash, not the value.
|
||||
- Never log the full request body. It may contain a token, a password, or PII.
|
||||
|
||||
## Volume (P4 Cardinality Discipline, core C8 Economy)
|
||||
|
||||
- Don't log every request at INFO. Log significant events.
|
||||
- A million logs a minute is not "good observability"; it is a storage bill and a signal-to-noise problem.
|
||||
- Sample high-volume logs (P5 Sampling with Intent). Sample deliberately, not randomly.
|
||||
|
||||
## What Violates Logging Discipline
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| `console.log("here")` | P1 (not structured) |
|
||||
| `logger.error("done")` | P3 (wrong level) |
|
||||
| `logger.info(req.body)` | P6 (secrets), volume |
|
||||
| A log with no `request_id` | P3 (no correlation) |
|
||||
| 10M logs/day at INFO | P4, C8 |
|
||||
@@ -0,0 +1,50 @@
|
||||
# Metrics — Derived Rules
|
||||
|
||||
> Derives from `domains/observability/first-principles.md` P1 (Structured by Default), P4 (Cardinality Discipline), P8 (SLI/SLO Awareness).
|
||||
|
||||
## The Four Golden Signals
|
||||
|
||||
| Signal | What |
|
||||
|--------|------|
|
||||
| Latency | Time to serve a request (p50, p95, p99) |
|
||||
| Traffic | Request rate (req/s) |
|
||||
| Errors | Error rate (errors/s, or % of traffic) |
|
||||
| Saturation | How full is the system (CPU, memory, queue depth) |
|
||||
|
||||
- All four are needed. Missing one is a blind spot.
|
||||
- Latency is percentiles, not average. Average hides the long tail.
|
||||
|
||||
## Cardinality (P4)
|
||||
|
||||
- Labels have bounded cardinality. `user_id` as a label = unbounded cardinality = unbounded bill.
|
||||
- High-cardinality dimensions belong in traces, not metrics.
|
||||
- A metric with `user_id` as a label is a 1M-series metric. That is a budget bomb.
|
||||
|
||||
## Counter vs Gauge vs Histogram
|
||||
|
||||
| Type | What | Example |
|
||||
|------|------|---------|
|
||||
| Counter | Monotonically increasing | `http_requests_total` |
|
||||
| Gauge | A value at a point in time | `active_connections` |
|
||||
| Histogram | Distribution of values | `http_request_duration_seconds` |
|
||||
|
||||
- A counter never decreases. Use `rate()` over time to get the rate.
|
||||
- A gauge can go up and down. Use it for saturation.
|
||||
- A histogram gives percentiles. Use it for latency.
|
||||
|
||||
## SLI/SLO (P8)
|
||||
|
||||
- SLI (Service Level Indicator): a metric of good/total (e.g., 99.9% of requests < 500ms).
|
||||
- SLO (Service Level Objective): the target for the SLI (e.g., 99.9% over 30 days).
|
||||
- Error budget: 1 - SLO. If SLO is 99.9%, error budget is 0.1%. Spend it on feature risk, not bugs.
|
||||
- When the error budget is exhausted, freeze features. Fix reliability.
|
||||
|
||||
## What Violates Metrics Discipline
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| `user_id` as a label | P4 Cardinality |
|
||||
| Average latency only | P8 (hides the tail) |
|
||||
| No error rate metric | P8 (no SLI) |
|
||||
| 1000 metrics, no SLO | P8 (no objective) |
|
||||
| A counter that decreases | (type error) |
|
||||
@@ -0,0 +1,45 @@
|
||||
# Tracing — Derived Rules
|
||||
|
||||
> Derives from `domains/observability/first-principles.md` P2 (Correlation), P5 (Sampling with Intent), P10 (Debuggability Over Coverage).
|
||||
|
||||
## Distributed Tracing (P2)
|
||||
|
||||
- A trace is a tree of spans. Each span is a unit of work with a start, end, and context.
|
||||
- `trace_id` ties spans across services. `span_id`/`parent_span_id` form the tree.
|
||||
- Every request has a `trace_id`. Propagate it in headers (`traceparent`, W3C standard).
|
||||
|
||||
## Sampling (P5)
|
||||
|
||||
- Head-based: sample at the start. Simple, but you miss the interesting failures.
|
||||
- Tail-based: sample at the end. Keep all errors, sample the successs. Better signal, harder to build.
|
||||
- A 100% trace rate is too expensive. 1% is often enough for debugging.
|
||||
- Sample deliberately: keep all errors, all slow requests, and a fraction of the rest.
|
||||
|
||||
## Context (P3 Sufficient Context, P10 Debuggability)
|
||||
|
||||
- A span has: name, start time, duration, attributes (key-value), events, status.
|
||||
- Attributes: `http.method`, `http.url`, `db.statement`, `user.id`. The fields you need to debug.
|
||||
- Events: notable points within a span (e.g., "cache miss", "retry").
|
||||
- A span with no attributes is a span that tells you nothing.
|
||||
|
||||
## Where to Span (P4 Locality)
|
||||
|
||||
- Span at service boundaries (HTTP in/out, DB query, queue send/receive).
|
||||
- Span at significant internal operations (a long computation, a batch step).
|
||||
- Don't span every function call. Span the meaningful units.
|
||||
|
||||
## Traces vs Logs (P10)
|
||||
|
||||
- Logs are events; traces are causality. Logs answer "what happened"; traces answer "why it was slow."
|
||||
- A trace contains log events (span events). They are not separate systems.
|
||||
- Use traces for the request flow; use logs for the details.
|
||||
|
||||
## What Violates Tracing Discipline
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| No `trace_id` propagation | P2 Correlation |
|
||||
| 100% trace rate | P5 Sampling |
|
||||
| Span per function | P4 (too noisy) |
|
||||
| Span with no attributes | P10 (no debug value) |
|
||||
| Traces for successful requests only | P5 (miss the failures) |
|
||||
@@ -0,0 +1,51 @@
|
||||
# Backend Performance — Derived Rules
|
||||
|
||||
> Derives from `domains/performance/first-principles.md` P1 (Measure First), P3 (Complexity Awareness), P4 (Resource Bounds).
|
||||
|
||||
## Measure First (P1)
|
||||
|
||||
- p50, p95, p99 latencies. The average hides the long tail.
|
||||
- Throughput (req/s) under load. Saturation point (where latency rises).
|
||||
- Resource utilization: CPU, memory, I/O, network. Each is a budget.
|
||||
|
||||
## N+1 Queries (P3 Complexity Awareness)
|
||||
|
||||
- A query in a loop is an N+1. It is O(N) queries instead of O(1).
|
||||
- Detect with a query counter in tests. A test that issues 100 queries is failing.
|
||||
- Fix with a JOIN, a batch load, or a dataloader. Never "we'll fix it later."
|
||||
|
||||
## Caching (P5 Caching with Intent)
|
||||
|
||||
- Cache what is: expensive to compute, stable, read often.
|
||||
- Invalidation is designed: TTL, event-based, or version-based. Never "we'll just clear it."
|
||||
- A cache without an invalidation strategy is a cache that serves stale data forever.
|
||||
- Multi-level: HTTP cache → CDN → app cache → DB. Each layer has its own rules.
|
||||
|
||||
## Async and Concurrency (P7 Async When Independent, see `domains/concurrency/`)
|
||||
|
||||
- I/O-bound work is async. Don't block a thread on a network call.
|
||||
- CPU-bound work is in a worker, not the request path.
|
||||
- Bounded queues everywhere (P9 Bounded Queues). Unbounded = OOM.
|
||||
|
||||
## Database (P4 Resource Bounds, see `domains/data/indexing.md`)
|
||||
|
||||
- Connection pool: bounded. The DB has a connection limit; the pool respects it.
|
||||
- Slow queries: logged, explained, fixed. A 10-second query is a bug.
|
||||
- Pagination on large tables: cursor, not offset. Offset scans rows.
|
||||
|
||||
## Resource Bounds (P4)
|
||||
|
||||
- Memory: bounded. A request that allocates unbounded memory is a DoS vector.
|
||||
- Timeouts: every external call has one. A call without a timeout is a call that can hang forever (P8 Timeout Discipline).
|
||||
- File handles, DB connections, HTTP connections: all bounded, all pooled.
|
||||
|
||||
## What Violates Backend Performance
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| N+1 query in a loop | P3 |
|
||||
| No timeout on an HTTP call | P4, P8 (concurrency) |
|
||||
| Unbounded in-memory sort | P4 |
|
||||
| Cache with no invalidation | P5 |
|
||||
| `SELECT *` | P4 (data P10) |
|
||||
| Connection pool size = 1000 | P4 (DB limit) |
|
||||
@@ -0,0 +1,40 @@
|
||||
# Performance — First Principles
|
||||
|
||||
## 1. The Principles
|
||||
|
||||
### P1. Measure First
|
||||
No optimization without measurement. Intuition about performance is
|
||||
usually wrong.
|
||||
|
||||
### P2. Critical Path Focus
|
||||
Optimize what users actually wait for. The 95th percentile matters
|
||||
more than the average.
|
||||
|
||||
### P3. Complexity Awareness
|
||||
Algorithmic cost is known. Big-O is a design conversation, not an
|
||||
afterthought.
|
||||
|
||||
### P4. Resource Bounds
|
||||
Memory, CPU, I/O, network — all bounded. Unbounded growth is a bug.
|
||||
|
||||
### P5. Caching with Intent
|
||||
Cache what is expensive, stable, and read often. Invalidation is
|
||||
designed, not bolted on.
|
||||
|
||||
### P6. Lazy by Default
|
||||
Compute only when needed. Pay only for what is used.
|
||||
|
||||
### P7. Async When Independent
|
||||
Work that does not depend on other work runs in parallel.
|
||||
|
||||
### P8. Budget Discipline
|
||||
Performance is a design constraint. The budget is set, not negotiated
|
||||
after the fact.
|
||||
|
||||
### P9. Perceived Performance
|
||||
What the user feels is what matters. A 200ms perceived response beats
|
||||
a 50ms measured one with no feedback.
|
||||
|
||||
### P10. Regression Prevention
|
||||
Performance tests catch what functional tests miss. The slow path
|
||||
is tested as a path.
|
||||
@@ -0,0 +1,55 @@
|
||||
# Frontend Performance — Derived Rules
|
||||
|
||||
> Derives from `domains/performance/first-principles.md` P1 (Measure First), P9 (Perceived Performance).
|
||||
|
||||
## Measure First (P1)
|
||||
|
||||
- Lighthouse, Core Web Vitals (LCP, FID/INP, CLS), RUM (Real User Monitoring).
|
||||
- A performance claim without a measurement is an opinion.
|
||||
- Measure the 75th percentile (P75), not the average. The average hides the long tail.
|
||||
|
||||
## The Three Core Web Vitals
|
||||
|
||||
| Vital | What | Target (P75) |
|
||||
|-------|------|--------------|
|
||||
| LCP (Largest Contentful Paint) | When the main content loads | ≤ 2.5s |
|
||||
| INP (Interaction to Next Paint) | When input is responded to | ≤ 200ms |
|
||||
| CLS (Cumulative Layout Shift) | Visual stability | ≤ 0.1 |
|
||||
|
||||
- LCP > 4s is poor. INP > 500ms is poor. CLS > 0.25 is poor.
|
||||
- Measure on mobile, not just desktop. Mobile is the long tail.
|
||||
|
||||
## Perceived Performance (P9)
|
||||
|
||||
- A skeleton screen beats a spinner. A spinner beats nothing.
|
||||
- Optimistic UI updates: the click responds immediately; the server confirms later.
|
||||
- Prefetch the next page on hover (if cheap). Prefetch is a bet, not a certainty.
|
||||
|
||||
## Bundle Size (P4 Resource Bounds, core C8 Economy)
|
||||
|
||||
- Ship less JavaScript. Every KB is parsed, compiled, and executed on the client.
|
||||
- Code-split routes. Lazy-load below-the-fold. Don't ship the admin bundle to the user bundle.
|
||||
- A 500KB JS bundle is large. A 2MB JS bundle is a defect.
|
||||
|
||||
## Images (P4, P6 Lazy by Default)
|
||||
|
||||
- WebP/AVIF, not JPEG/PNG. Modern formats are 30–50% smaller.
|
||||
- `loading="lazy"` on below-the-fold images. `width`/`height` to prevent CLS.
|
||||
- `srcset` for responsive images. Ship the right size to the right device.
|
||||
- Never ship a 4K image to a 360px screen.
|
||||
|
||||
## Rendering (P7 Async When Independent)
|
||||
|
||||
- Server-render the first paint (SSR/SSG). Hydrate after.
|
||||
- Avoid hydration waterfalls: a 3-second hydration is a 3-second blank page with a "loaded" script.
|
||||
- Defer non-critical hydration. Interactive above the fold first; below the fold later.
|
||||
|
||||
## What Violates Frontend Performance
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| 3MB JS bundle | P4, C8 |
|
||||
| LCP > 4s on mobile | P1 (measured) |
|
||||
| Layout shift on image load | CLS |
|
||||
| Synchronous hydration of a 50KB page | P7 |
|
||||
| No image optimization | P4 |
|
||||
@@ -0,0 +1,62 @@
|
||||
# Authentication — Derived Rules
|
||||
|
||||
> Derives from `domains/security/first-principles.md` P1 (Zero Trust), P2 (Least Privilege), P6 (Crypto Correctness).
|
||||
|
||||
## The Default: Authenticated
|
||||
|
||||
- Every endpoint is authenticated unless explicitly public.
|
||||
- "Public" is an explicit declaration, not a default.
|
||||
- A missing auth check is a bug, not an oversight.
|
||||
|
||||
## Authentication Methods
|
||||
|
||||
### Session-based (browser)
|
||||
- Server-side session, cookie-borne session ID.
|
||||
- Cookie: `HttpOnly`, `Secure`, `SameSite=Lax` (or `Strict`).
|
||||
- Session ID: cryptographically random, ≥ 128 bits.
|
||||
- Session timeout: bounded. Idle timeout + absolute timeout.
|
||||
|
||||
### Token-based (API, SPA)
|
||||
- Bearer token in `Authorization: Bearer <token>`.
|
||||
- Token: JWT (signed) or opaque (server-stored).
|
||||
- JWT: signed (HS256/RS256), never `none`. Short TTL (≤ 1 hour). Refresh token for long sessions.
|
||||
- Opaque: server-stored, revocable. Use when revocation matters.
|
||||
|
||||
### API Keys (service-to-service)
|
||||
- Long-lived, scoped, rotatable.
|
||||
- Sent in header (`X-API-Key`), not query string (logged in URLs).
|
||||
- Stored in a secrets manager, never in code.
|
||||
|
||||
## What Never to Do (P6 Crypto Correctness)
|
||||
|
||||
- Never roll your own auth. Use a vetted library or framework.
|
||||
- Never store passwords in plaintext. Use bcrypt/scrypt/argon2 with a work factor.
|
||||
- Never use MD5 or SHA1 for password hashing.
|
||||
- Never put a token in a URL. URLs are logged.
|
||||
- Never accept `alg: none` in a JWT.
|
||||
- Never trust a token without verifying its signature.
|
||||
|
||||
## Password Rules (P4 Input Validation)
|
||||
|
||||
- Minimum length: 12 characters (NIST 800-63B). No maximum (don't prevent long passwords).
|
||||
- No composition rules (no "must contain a symbol"). They don't help and frustrate users.
|
||||
- Check against a breach corpus (HIBP API or similar).
|
||||
- Rate limit login attempts. Lockout after N failures (with exponential backoff, not a hard lock).
|
||||
|
||||
## Multi-Factor (P3 Defense in Depth)
|
||||
|
||||
- MFA is the default for privileged accounts.
|
||||
- TOTP (RFC 6238) or WebAuthn. SMS is deprecated (SIM swapping).
|
||||
- MFA is a layer, not a replacement for strong primary auth.
|
||||
|
||||
## Session Lifecycle (P2 Least Privilege, P5 Reversibility)
|
||||
|
||||
- Sessions are revocable. A logout invalidates the session server-side, not just client-side.
|
||||
- Tokens are revocable. A refresh token revocation list is maintained.
|
||||
- "Remember me" extends the session, it does not make it permanent.
|
||||
|
||||
## Audit (P7 Auditability)
|
||||
|
||||
- Every auth event is logged: login (success/fail), logout, token issuance, token revocation.
|
||||
- Logs include: user ID, timestamp, IP, user agent, outcome.
|
||||
- Logs do not include: passwords, tokens, session IDs (use a hash).
|
||||
@@ -0,0 +1,61 @@
|
||||
# Authorization — Derived Rules
|
||||
|
||||
> Derives from `domains/security/first-principles.md` P2 (Least Privilege), P3 (Defense in Depth), P1 (Zero Trust).
|
||||
|
||||
## The Default: Deny
|
||||
|
||||
- Every request is denied unless explicitly authorized.
|
||||
- "Authorized by default" is an anti-pattern. The absence of a rule means denial.
|
||||
- A missing authz check is a bug, not a feature gap.
|
||||
|
||||
## Authorization Models
|
||||
|
||||
### RBAC (Role-Based)
|
||||
- Users have roles; roles have permissions.
|
||||
- Roles are coarse: `admin`, `editor`, `viewer`. Permissions are fine: `post:create`, `post:delete`.
|
||||
- Check permissions, not roles: `can(user, 'post:create')`, not `user.role === 'admin'`.
|
||||
- Roles can change; permission checks are stable.
|
||||
|
||||
### ABAC (Attribute-Based)
|
||||
- Authorization based on attributes of the user, resource, and context.
|
||||
- More expressive: "user can edit a post if user.department == post.department and post.status == 'draft'".
|
||||
- Use when RBAC is too coarse. Beware: complex ABAC is hard to audit.
|
||||
|
||||
### ReBAC (Relationship-Based)
|
||||
- Authorization based on relationships (e.g., Zanzibel).
|
||||
- "user:alice is editor of document:42" — check the relationship graph.
|
||||
- Scales for fine-grained, resource-specific access (Google Docs-style).
|
||||
|
||||
## Where to Check (P4 Locality)
|
||||
|
||||
- Check at the boundary: the API endpoint, the resolver, the controller.
|
||||
- Check at the data layer: defense in depth. A query that bypasses the controller still respects row-level security.
|
||||
- Never check only in the UI. The UI is a convenience, not a security boundary.
|
||||
|
||||
## Principle of Least Privilege (P2)
|
||||
|
||||
- A token/role gets the minimum permissions to do its job.
|
||||
- No "admin" role for daily work. Admin is for administration; daily work uses a scoped role.
|
||||
- Service tokens are scoped to one service's resources, not "all resources."
|
||||
|
||||
## IDOR (Insecure Direct Object Reference) (P1 Zero Trust)
|
||||
|
||||
- `/api/users/123` — does the requester own 123? Check.
|
||||
- Never assume the user can access any ID they request. The ID is input; inputs are untrusted.
|
||||
- Use scoped queries: `User.find({ id, owner: userId })`, not `User.find(id)`.
|
||||
|
||||
## Caching and Authz (P3 Defense in Depth)
|
||||
|
||||
- Authorization is not cached across users. A cached response for user A is not served to user B.
|
||||
- Cache keys include the user/role, not just the resource.
|
||||
- "Cache it as public if anyone can see it" — only if truly anyone (no auth).
|
||||
|
||||
## What Violates Authorization
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| `/admin` endpoint with no authz check | P1 Zero Trust |
|
||||
| `user.role === 'admin'` instead of permission check | P2 (roles change) |
|
||||
| IDOR: `User.find(req.params.id)` with no ownership check | P1 Zero Trust |
|
||||
| Cached authz decision reused across users | P3 Defense in Depth |
|
||||
| Service token with "all resources" scope | P2 Least Privilege |
|
||||
@@ -0,0 +1,102 @@
|
||||
# Security — First Principles
|
||||
|
||||
**Version:** 1.0.0
|
||||
**Status:** Foundational
|
||||
**Audience:** AI agents and humans handling authentication, data,
|
||||
trust boundaries, or any security-relevant code.
|
||||
|
||||
## 1. Manifesto
|
||||
|
||||
Security is not a feature. It is a property of correct code. The
|
||||
highest quality code is code that does what it is supposed to do —
|
||||
and nothing else, no matter who asks.
|
||||
|
||||
An AI agent using this framework does not "add security". It writes
|
||||
secure code by default. There is no version of correct code that is
|
||||
insecure.
|
||||
|
||||
## 2. The Principles
|
||||
|
||||
### P1. Zero Trust
|
||||
No request, user, system, or input is trusted by default. Trust is
|
||||
earned at every boundary, every time.
|
||||
|
||||
### P2. Least Privilege
|
||||
Every actor — user, service, process — gets the minimum access
|
||||
required to do its job, for the minimum time required.
|
||||
|
||||
### P3. Defense in Depth
|
||||
Security is layered. No single control is load-bearing. The failure
|
||||
of one control does not compromise the system.
|
||||
|
||||
### P4. Input Validation
|
||||
All input is untrusted until proven otherwise. Validation happens at
|
||||
the boundary, against a schema, with explicit failure modes.
|
||||
|
||||
### P5. Output Safety
|
||||
All output is encoded, escaped, or filtered for its destination
|
||||
context. The system never trusts its callers, including itself.
|
||||
|
||||
### P6. Cryptographic Correctness
|
||||
Crypto is hard. Use vetted, maintained libraries. Never roll your
|
||||
own. Never invent your own primitives. Never bypass a primitive to
|
||||
"make it work".
|
||||
|
||||
### P7. Auditability
|
||||
Security-relevant events — auth attempts, authz decisions, data
|
||||
access, configuration changes — are logged with sufficient context
|
||||
to investigate.
|
||||
|
||||
### P8. Fail Securely
|
||||
When security fails, it fails closed. The default state is denied,
|
||||
disabled, or safe. Errors never grant access by accident.
|
||||
|
||||
### P9. Secret Hygiene
|
||||
Secrets are not in code, configs, logs, error messages, URLs, or
|
||||
screenshots. Secrets are loaded from a secrets manager and treated
|
||||
as transient.
|
||||
|
||||
### P10. Surface Minimization
|
||||
The smaller the attack surface, the smaller the risk. Dependencies
|
||||
are minimized. Endpoints are minimized. Features are minimized. Code
|
||||
that does not exist cannot be exploited.
|
||||
|
||||
## 3. Conflict Resolution
|
||||
|
||||
1. Zero Trust — never sacrificed.
|
||||
2. Least Privilege — never sacrificed.
|
||||
3. Defense in Depth — never sacrificed.
|
||||
4. Input Validation — never sacrificed.
|
||||
5. Output Safety — never sacrificed.
|
||||
6. Cryptographic Correctness — never sacrificed.
|
||||
7. Fail Securely — never sacrificed.
|
||||
8. Auditability — sacrificed only when logging itself is the threat.
|
||||
9. Secret Hygiene — never sacrificed.
|
||||
10. Surface Minimization — sacrificed only when a feature is required.
|
||||
|
||||
Eight of ten principles are non-tradeable. Security does not
|
||||
trade-off. It is either present or it is not.
|
||||
|
||||
## 4. What Violates These Principles
|
||||
|
||||
| Violation | Principle Breached |
|
||||
|------------------------------------|----------------------|
|
||||
| `SELECT *` from user input | P4 Input Validation |
|
||||
| `eval()` of any string | P4, P5 |
|
||||
| Hardcoded API key in source | P9 Secret Hygiene |
|
||||
| Catch-all `catch (e) {}` | P7 Auditability, P8 Fail Securely |
|
||||
| `md5` or `sha1` for security | P6 Crypto Correctness |
|
||||
| Open CORS to `*` in production | P1 Zero Trust, P10 |
|
||||
| Detailed error to end user | P7 Auditability, P5 |
|
||||
| `chmod 777` | P2 Least Privilege |
|
||||
| Long-lived session token | P1, P2 |
|
||||
| Logging the request body | P9 Secret Hygiene |
|
||||
|
||||
These are never acceptable. They are not "to be reviewed later".
|
||||
They are rejected on sight.
|
||||
|
||||
## 5. Relationship to Core
|
||||
|
||||
Subordinate to `core/first-principles.md`. Note: security principles
|
||||
overlap heavily with core Correctness (C1) and Observability (C7).
|
||||
See `matrix/principles-matrix.md`.
|
||||
@@ -0,0 +1,64 @@
|
||||
# Input Validation — Derived Rules
|
||||
|
||||
> Derives from `domains/security/first-principles.md` P4 (Input Validation), P5 (Output Safety).
|
||||
|
||||
## The Rule
|
||||
|
||||
All input is untrusted until validated. Validation happens at the boundary, against a schema, with explicit failure modes.
|
||||
|
||||
## Validate at the Boundary (P4 Locality)
|
||||
|
||||
- The API endpoint, the controller, the message handler — the entry point validates.
|
||||
- Internal code trusts validated input. Unvalidated input never reaches the database.
|
||||
- Defense in depth: the database also has constraints (P3 Defense in Depth).
|
||||
|
||||
## Schema Validation
|
||||
|
||||
- Use a schema library (zod, joi, pydantic, json-schema). Never hand-write validation.
|
||||
- The schema is the contract. The schema is versioned. The schema is tested.
|
||||
- Reject unknown fields (`additionalProperties: false` by default). Be explicit.
|
||||
|
||||
## Validation Types
|
||||
|
||||
### Type Validation
|
||||
- `id` is a UUID, not a string. `age` is an integer ≥ 0. `email` matches a regex (or better, is parsed).
|
||||
- Never accept `any`. Never accept `string` for a typed value.
|
||||
|
||||
### Range Validation
|
||||
- `limit` ≤ 100. `page` ≥ 1. `quantity` ≥ 1 and ≤ stock.
|
||||
- Bounds are explicit. No "unbounded" inputs.
|
||||
|
||||
### Format Validation
|
||||
- `email` is parsed (not just regex). `url` is parsed. `date` is parsed.
|
||||
- A regex for email is wrong (RFC 5322 is not a regular language). Use a parser.
|
||||
|
||||
### Semantic Validation
|
||||
- `start_date < end_date`. `user_id` exists. `product_id` is in stock.
|
||||
- Semantic validation may require a database lookup. That's fine.
|
||||
|
||||
### Presence Validation
|
||||
- Required fields are present. Optional fields are absent or null.
|
||||
- Empty string `""` is not the same as null. Be explicit about which you accept.
|
||||
|
||||
## Failure Modes (P8 Fail Securely)
|
||||
|
||||
- Validation failure → 400 Bad Request with a structured error (`domains/api/error-responses.md`).
|
||||
- Never coerce: `"5" + 3` is not validation. Reject, don't guess.
|
||||
- Never default: a missing required field is an error, not a default value.
|
||||
|
||||
## Output Safety (P5 Output Safety)
|
||||
|
||||
- Validation is for input. Encoding is for output.
|
||||
- Output to HTML: HTML-encode. Output to SQL: parameterize. Output to URL: URL-encode.
|
||||
- Never trust validated input for output. Validate on the way in, encode on the way out.
|
||||
|
||||
## What Violates Input Validation
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| `JSON.parse(req.body)` with no schema | P4 Input Validation |
|
||||
| `parseInt(req.query.id)` with no range check | P4 |
|
||||
| `additionalProperties: true` by default | P1 Contract Fidelity |
|
||||
| Coercing `"5"` to `5` silently | P8 Fail Securely |
|
||||
| SQL string interpolation (even of "validated" input) | P5 Output Safety |
|
||||
| Regex for email validation | P4 (use a parser) |
|
||||
@@ -0,0 +1,72 @@
|
||||
# Secrets — Derived Rules
|
||||
|
||||
> Derives from `domains/security/first-principles.md` P9 (Secret Hygiene), P1 (Zero Trust), P6 (Crypto Correctness).
|
||||
|
||||
## What is a Secret
|
||||
|
||||
A secret is any value whose disclosure compromises the system. Examples:
|
||||
- API keys, access tokens, refresh tokens
|
||||
- Database passwords, service passwords
|
||||
- Private keys (TLS, signing, encryption)
|
||||
- OAuth client secrets, JWT signing keys
|
||||
- Encryption keys (KMS, envelope encryption)
|
||||
|
||||
## Never in Code (P9)
|
||||
|
||||
- No secrets in source files. No secrets in comments. No secrets in string constants.
|
||||
- No secrets in config files committed to git. Use `.env` (gitignored) or a secrets manager.
|
||||
- No secrets in test fixtures. Tests use fake/dummy values, never real secrets.
|
||||
|
||||
## Never in Logs (P9, domains/observability P6)
|
||||
|
||||
- No secrets in log messages, error messages, or stack traces.
|
||||
- Redact before logging: replace the secret with `[REDACTED]` or a hash.
|
||||
- Never log the request body (it may contain a token). Log the request ID, not the body.
|
||||
|
||||
## Never in URLs (P9, P1)
|
||||
|
||||
- URLs are logged (server logs, proxy logs, browser history, referrer headers).
|
||||
- A token in the URL is a token in everyone's logs.
|
||||
- Use headers (`Authorization: Bearer ...`), not query strings.
|
||||
|
||||
## Never in Error Messages (P9, domains/errors)
|
||||
|
||||
- "Authentication failed: invalid API key sk-abc123" — the secret is in the error.
|
||||
- "Authentication failed: invalid API key" — the secret is not.
|
||||
- Error messages are for humans; humans do not need the secret to debug.
|
||||
|
||||
## Storage (P6 Crypto Correctness)
|
||||
|
||||
- At rest: encrypted (KMS, envelope encryption). Never plaintext on disk.
|
||||
- In memory: minimal lifetime. Load on use, not on boot. Zero after use (where the language allows).
|
||||
- In transit: TLS only. No plaintext HTTP for secrets, ever.
|
||||
|
||||
## Rotation (P5 Reversibility, P2 Least Privilege)
|
||||
|
||||
- Secrets are rotatable. A secret that cannot be rotated is a liability.
|
||||
- Rotation is documented and rehearsed. Not improvised during an incident.
|
||||
- Old secrets are revoked after rotation, not "kept just in case."
|
||||
- Short-lived secrets (≤ 1 hour) are better than long-lived secrets (≤ forever).
|
||||
|
||||
## Scope (P2 Least Privilege)
|
||||
|
||||
- A secret has the minimum scope. A secret for service A does not work for service B.
|
||||
- Scoped tokens: `scope: read:orders`, not `scope: *`.
|
||||
- One secret per environment. Dev, staging, prod use different secrets.
|
||||
|
||||
## The `.gitignore` Rule
|
||||
|
||||
- `.env`, `.env.secrets`, `.env.*` are in `.gitignore` by default (see Atelier's own `.gitignore`).
|
||||
- A secret committed to git is a leaked secret. Rotate immediately. History is forever.
|
||||
- Pre-commit hooks scan for high-entropy strings. Use them.
|
||||
|
||||
## What Violates Secret Hygiene
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| `API_KEY = "sk-abc123"` in source | P9 |
|
||||
| `?token=abc` in a URL | P9, P1 |
|
||||
| `console.log(req.body)` where body contains a token | P9, observability P6 |
|
||||
| `catch (e) { throw new Error("DB password is pwd123") }` | P9, errors |
|
||||
| Same secret in dev and prod | P2 |
|
||||
| A 5-year-old API key with no rotation | P5 |
|
||||
@@ -0,0 +1,50 @@
|
||||
# Supply Chain — Derived Rules
|
||||
|
||||
> Derives from `domains/security/first-principles.md` P10 (Surface Minimization), P3 (Defense in Depth), P7 (Auditability).
|
||||
|
||||
## Dependencies are Attack Surface (P10)
|
||||
|
||||
- Every dependency is code you did not write but must trust. Minimize it.
|
||||
- A dependency you do not need is a vulnerability you do not have.
|
||||
- Audit dependencies regularly. Remove unused ones (`npm prune`, `pip-autoremove`).
|
||||
|
||||
## Lockfiles (P1 Correctness, P5 Reversibility)
|
||||
|
||||
- Pin exact versions in a lockfile (`package-lock.json`, `yarn.lock`, `Pipfile.lock`, `Cargo.lock`).
|
||||
- Commit the lockfile. A reproducible build requires a committed lock.
|
||||
- `npm ci` (not `npm install`) in CI. `pip install -r requirements.txt` with pinned versions.
|
||||
|
||||
## Integrity (P6 Crypto Correctness)
|
||||
|
||||
- Subresource integrity for web assets: `<script src="..." integrity="sha384-...">`.
|
||||
- Package signatures where available (signed npm packages, GPG-signed apt packages).
|
||||
- Verify checksums on downloaded artifacts. A tarball without a checksum is untrusted.
|
||||
|
||||
## Vulnerability Scanning (P3 Defense in Depth)
|
||||
|
||||
- `npm audit`, `pip-audit`, `cargo audit`, `trivy`, `snyk` — run in CI.
|
||||
- Fail the build on high/critical vulnerabilities (configurable threshold).
|
||||
- Auto-merge security PRs from Dependabot/Renovate when the patch is non-breaking.
|
||||
|
||||
## Provenance (P7 Auditability)
|
||||
|
||||
- SBOM (Software Bill of Materials): `cyclonedx` or `spdx` output. Know what is in your build.
|
||||
- SLSA (Supply-chain Levels for Software Artifacts): provenance attestation for builds.
|
||||
- Signed artifacts: cosign, sigstore. A build you cannot verify is untrusted.
|
||||
|
||||
## Private Registries (P2 Least Privilege)
|
||||
|
||||
- Internal packages come from a private registry, not public npm/PyPI.
|
||||
- A typo-squatted public package is a supply chain attack (`lodash` vs `lodahs`).
|
||||
- Scope your registry: `@myorg:registry=https://registry.myorg.com`.
|
||||
|
||||
## What Violates Supply Chain
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| `npm install` (no lockfile) in CI | P1, P5 |
|
||||
| Unpinned dependency `^1.2.3` in production | P1 |
|
||||
| No vulnerability scanning in CI | P3 |
|
||||
| `eval` of a package's README | P10 (surface) |
|
||||
| A dependency with 0 weekly downloads | P10 (no eyes) |
|
||||
| No SBOM for a shipped artifact | P7 |
|
||||
@@ -0,0 +1,44 @@
|
||||
# Testing — First Principles
|
||||
|
||||
## 1. The Principles
|
||||
|
||||
### P1. Tests as Specification
|
||||
Tests document what the code should do. Reading the tests is reading
|
||||
the contract.
|
||||
|
||||
### P2. Independence
|
||||
Tests do not depend on each other. Order does not matter. Parallelism
|
||||
is the default.
|
||||
|
||||
### P3. Determinism
|
||||
Same input, same output, every time. No time, randomness, network, or
|
||||
filesystem in the test path unless explicitly modeled.
|
||||
|
||||
### P4. Fast Feedback
|
||||
Tests run in seconds, not minutes. Slow tests are skipped, then
|
||||
deleted.
|
||||
|
||||
### P5. Coverage of Behavior
|
||||
Cover what the code does, not what it is. Lines covered is not the
|
||||
goal. Behaviors exercised is the goal.
|
||||
|
||||
### P6. Failure Specificity
|
||||
A failing test names the file, the function, the input, the
|
||||
expectation, and the actual. A test that fails unhelpfully is
|
||||
broken.
|
||||
|
||||
### P7. Realism
|
||||
Test data resembles production data in shape, distribution, and
|
||||
edge cases. Toy data hides bugs.
|
||||
|
||||
### P8. Maintainability
|
||||
Tests are first-class code. They are read, reviewed, and refactored.
|
||||
Test code is not throwaway.
|
||||
|
||||
### P9. Edge Case Coverage
|
||||
Boundaries, nulls, empty sets, maximums, minimums, and invalid inputs
|
||||
are tested. The middle of the range is the easy part.
|
||||
|
||||
### P10. No Test Theater
|
||||
A test that cannot fail is not a test. A test that asserts nothing
|
||||
is a lie. Tests earn their place by being able to catch real bugs.
|
||||
@@ -0,0 +1,49 @@
|
||||
# Test Fixtures — Derived Rules
|
||||
|
||||
> Derives from `domains/testing/first-principles.md` P7 (Realism), P2 (Independence), P3 (Determinism).
|
||||
|
||||
## Fixtures are Real Data (P7 Realism)
|
||||
|
||||
- A fixture resembles production data in shape, distribution, and edge cases.
|
||||
- A fixture with `name: "test"` and `email: "a@b.c"` hides bugs that real data surfaces.
|
||||
- Use realistic names, realistic emails, realistic dates. `"Jane Doe", "jane.doe@example.com", "2026-03-15"`.
|
||||
|
||||
## Factory Over Fixture (P2 Independence, P3 Determinism)
|
||||
|
||||
- A fixture file is shared state. A factory is fresh state per test.
|
||||
- Prefer factories (e.g., `factory.User()` returning a new instance) over shared fixture files.
|
||||
- A shared fixture is mutated by one test, breaks another. Independence is violated.
|
||||
|
||||
## Builders for Complex Data
|
||||
|
||||
- A builder (`UserBuilder().withEmail().withAdmin().build()`) composes only the fields the test needs.
|
||||
- A builder with defaults: every field has a sensible default; tests override only what they test.
|
||||
- A builder is the test's API to data. Stable, composable, readable.
|
||||
|
||||
## Setup and Teardown (P2 Independence)
|
||||
|
||||
- Every test cleans up after itself. No test leaves state for the next.
|
||||
- `setUp`/`tearDown` (or `beforeEach`/`afterEach`) restore the world.
|
||||
- A test that depends on the order of execution is not independent.
|
||||
|
||||
## Determinism (P3)
|
||||
|
||||
- No `Date.now()`, no `Math.random()` in fixtures. Inject the clock, inject the RNG.
|
||||
- A fixture that uses "now" is non-deterministic. It passes today and fails tomorrow.
|
||||
- Fix timestamps: `createdAt: new Date("2026-01-01T00:00:00Z")`.
|
||||
|
||||
## Edge Case Fixtures (P9 Edge Case Coverage)
|
||||
|
||||
- A fixture set includes: the empty case, the single-item case, the max-size case, the unicode case.
|
||||
- A fixture set includes invalid data: malformed email, negative age, future date.
|
||||
- Edge case fixtures are first-class, not "extra credit."
|
||||
|
||||
## What Violates Fixture Discipline
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| `name: "test"` fixture | P7 Realism |
|
||||
| Shared fixture file mutated across tests | P2 Independence |
|
||||
| `createdAt: new Date()` (now) in fixture | P3 Determinism |
|
||||
| No edge-case fixtures | P9 Edge Case Coverage |
|
||||
| A 500-line fixture file | P3 (complexity) |
|
||||
@@ -0,0 +1,60 @@
|
||||
# Test Pyramid — Derived Rules
|
||||
|
||||
> Derives from `domains/testing/first-principles.md` P4 (Fast Feedback), P5 (Coverage of Behavior), P10 (No Test Theater).
|
||||
|
||||
## The Pyramid
|
||||
|
||||
```
|
||||
/\
|
||||
/e2e\ few, slow, integration
|
||||
/------\
|
||||
/ integ \ some, medium, contract
|
||||
/----------\
|
||||
/ unit \ many, fast, isolated
|
||||
/--------------\
|
||||
```
|
||||
|
||||
- **Unit (many):** test a function/class in isolation. Fast (< 10ms each). The bulk of tests.
|
||||
- **Integration (some):** test components together (DB, API client, queue). Medium (< 1s each).
|
||||
- **E2E (few):** test the whole system from outside. Slow (> 1s each). The tip of the pyramid.
|
||||
|
||||
## Why a Pyramid (P4 Fast Feedback)
|
||||
|
||||
- A pyramid inverts to a "ice cream cone" (many e2e, few unit) when devs avoid unit tests.
|
||||
- Inverted pyramids are slow and flaky. The feedback loop breaks.
|
||||
- The pyramid shape preserves fast feedback: most failures are unit failures, found in < 10ms.
|
||||
|
||||
## What Goes Where
|
||||
|
||||
| Test Type | What it Covers | Speed | Count |
|
||||
|-----------|----------------|-------|-------|
|
||||
| Unit | A function, a class, a pure module | < 10ms | Many |
|
||||
| Integration | DB queries, API contract, queue behavior | < 1s | Some |
|
||||
| E2E | A user flow, an API request → response end-to-end | > 1s | Few |
|
||||
|
||||
- A unit test does not hit the database. A unit test does not make a network call.
|
||||
- An integration test does not test business logic; it tests the integration.
|
||||
- An e2e test does not test edge cases; it tests the happy path. Edge cases are unit tests.
|
||||
|
||||
## Anti-Patterns (P10 No Test Theater)
|
||||
|
||||
- **Ice cream cone:** many e2e, few unit. Slow, flaky, no signal.
|
||||
- **Cupcake:** same count at every level. No pyramid shape. Slow.
|
||||
- **Only unit:** 100% unit coverage, 0% integration. The system is untested as a whole.
|
||||
- **Only e2e:** every edge case is an e2e test. The suite takes an hour.
|
||||
|
||||
## Coverage (P5)
|
||||
|
||||
- Unit coverage of behavior: every branch, every edge case, every error path.
|
||||
- Integration coverage of contracts: every API endpoint, every DB query, every queue interaction.
|
||||
- E2E coverage of flows: the top 3–5 critical user flows. Not every permutation.
|
||||
|
||||
## What Violates the Pyramid
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| E2E test for an edge case | P4 (slow feedback) |
|
||||
| Unit test that hits the DB | P2 (not isolated) |
|
||||
| 0 integration tests | P5 (no coverage of contracts) |
|
||||
| 500 e2e tests, 50 unit tests | P10 (theater) |
|
||||
| A 30-second test suite | P4 (feedback loop broken) |
|
||||
@@ -0,0 +1,50 @@
|
||||
# Accessibility Requirements
|
||||
|
||||
> Detailed, enforceable accessibility rules. Every component, page,
|
||||
> and flow must pass these. Failure is disqualifying — see
|
||||
> `first-principles.md` P2.
|
||||
|
||||
## Perceivable
|
||||
|
||||
- [ ] Every image has `alt` text or is marked `alt=""` if decorative.
|
||||
- [ ] Every video has captions. Every audio has transcripts.
|
||||
- [ ] Color contrast meets WCAG 2.1 AA (4.5:1 text, 3:1 UI).
|
||||
- [ ] Information is not conveyed by color alone.
|
||||
- [ ] Text resizes to 200% without loss of content or function.
|
||||
|
||||
## Operable
|
||||
|
||||
- [ ] Every interactive element is keyboard-reachable.
|
||||
- [ ] Focus order is logical and matches visual order.
|
||||
- [ ] Focus is always visible (≥ 3:1 contrast).
|
||||
- [ ] No keyboard traps.
|
||||
- [ ] Touch targets are ≥ 44×44 CSS pixels.
|
||||
- [ ] Motion can be disabled via `prefers-reduced-motion`.
|
||||
- [ ] No flashing content > 3 flashes per second.
|
||||
|
||||
## Understandable
|
||||
|
||||
- [ ] Page language is declared.
|
||||
- [ ] Form fields have associated labels.
|
||||
- [ ] Error messages identify the field and the problem.
|
||||
- [ ] Navigation is consistent across pages.
|
||||
- [ ] Abbreviations and jargon are explained on first use.
|
||||
|
||||
## Robust
|
||||
|
||||
- [ ] HTML validates.
|
||||
- [ ] ARIA is used correctly (roles, states, properties).
|
||||
- [ ] Components work across assistive technologies.
|
||||
- [ ] No ARIA is used where native HTML would suffice.
|
||||
|
||||
## Testing
|
||||
|
||||
Every accessibility requirement is verified by:
|
||||
|
||||
1. Automated tool (axe-core, Lighthouse, etc.)
|
||||
2. Keyboard-only navigation
|
||||
3. Screen reader (NVDA, VoiceOver) walkthrough
|
||||
4. Zoom to 200%
|
||||
5. Reduced motion enabled
|
||||
|
||||
All five must pass. Automated-only is not acceptance.
|
||||
@@ -0,0 +1,87 @@
|
||||
# Component Design Principles
|
||||
|
||||
> Sibling to `first-principles.md` in this domain. These rules govern
|
||||
> how individual UI components are designed, named, composed, and
|
||||
> evolved.
|
||||
|
||||
## 1. Single Responsibility
|
||||
A component does one thing, completely.
|
||||
|
||||
- If a component's name contains "And", split it.
|
||||
- If a component has more than one primary action, split it.
|
||||
- If a component's props cannot be described in one sentence, split it.
|
||||
|
||||
## 2. Composition Over Configuration
|
||||
Components combine. They do not configure.
|
||||
|
||||
- Prefer small, composable primitives over large, configurable ones.
|
||||
- Variants are separate components, not boolean props.
|
||||
- Layout is composition. The component owns its content, not its
|
||||
position.
|
||||
- A `Button` is not `Button primary large loading disabled`. It is
|
||||
`Button` composed with `<Icon>`, `<Spinner>`, and styled by context.
|
||||
|
||||
## 3. Explicit Boundaries
|
||||
A component's contract is its props and its events.
|
||||
|
||||
- All inputs are typed. Required inputs are required.
|
||||
- All outputs are typed. Events are named for what happened, not what
|
||||
was clicked.
|
||||
- A component never reads from global state implicitly.
|
||||
- A component never mutates its inputs.
|
||||
|
||||
## 4. Predictable State
|
||||
A component's state is owned at the lowest level that can manage it.
|
||||
|
||||
- If only the component cares, the component owns it.
|
||||
- If siblings care, the parent owns it.
|
||||
- If the world cares, the application owns it.
|
||||
- State is never duplicated across levels.
|
||||
|
||||
## 5. Render Purity
|
||||
Given the same props and state, a component renders the same output.
|
||||
|
||||
- No hidden inputs (time, randomness, network) inside the render path.
|
||||
- Side effects are in effects, event handlers, or data loaders — not
|
||||
in render.
|
||||
- A component's render is safe to call repeatedly.
|
||||
|
||||
## 6. Accessible by Default
|
||||
A component is not finished until it is accessible.
|
||||
|
||||
- Every interactive component is keyboard-reachable and screen-reader
|
||||
announced.
|
||||
- Every form control has a label.
|
||||
- Every image has alt text or is marked decorative.
|
||||
- Every focusable element has a visible focus state.
|
||||
- Accessibility is in the component contract, not a wrapper.
|
||||
|
||||
## 7. Style via Tokens
|
||||
A component references design tokens, never raw values.
|
||||
|
||||
- No hardcoded colors, sizes, or fonts in component code.
|
||||
- Tokens are the API to the design system.
|
||||
- A component without a token is a design debt.
|
||||
|
||||
## 8. Stable Identity
|
||||
A component's identity is its public name, not its implementation.
|
||||
|
||||
- Renaming a component is a breaking change.
|
||||
- Removing a prop is a breaking change.
|
||||
- Changing a prop's semantics is a breaking change.
|
||||
- Deprecate before you delete. Migrate before you rename.
|
||||
|
||||
## 9. Testable in Isolation
|
||||
A component can be rendered, interacted with, and verified in isolation.
|
||||
|
||||
- Components ship with stories, examples, or fixtures.
|
||||
- Tests cover behavior, not implementation.
|
||||
- Visual regression is part of the contract.
|
||||
|
||||
## 10. Documented Intent
|
||||
A component is shipped with a "why" and a "when".
|
||||
|
||||
- What is it for?
|
||||
- When should it be used?
|
||||
- When should it NOT be used?
|
||||
- What are the common mistakes with it?
|
||||
@@ -0,0 +1,45 @@
|
||||
# UI Copywriting — Derived Rules
|
||||
|
||||
> Derives from `domains/uiux/first-principles.md` P3 (Clarity), P1 (User Primacy), P5 (Forgiveness).
|
||||
|
||||
## Write for the User (P1 User Primacy)
|
||||
|
||||
- The reader is a user, not a developer. "Sign in" not "Authenticate."
|
||||
- The reader is busy. Short sentences. Active voice. Verbs first.
|
||||
- The reader is anxious. Reassure. "Your changes are saved." Not "State persisted."
|
||||
|
||||
## Clarity (P3)
|
||||
|
||||
- One idea per sentence. One action per button.
|
||||
- Labels are nouns: "Email", "Password". Actions are verbs: "Sign in", "Save".
|
||||
- Errors are specific: "Email is invalid" not "Something went wrong."
|
||||
- Empty states are instructive: "No projects yet. Create one." Not "No data."
|
||||
|
||||
## Forgiveness (P5)
|
||||
|
||||
- Destructive actions warn: "This will delete 42 items. This cannot be undone."
|
||||
- Confirmations name the consequence, not just the action. "Delete user" → "Delete Jane Doe and 12 associated items?"
|
||||
- Undo is offered when possible. "Deleted. Undo." beats "Are you sure?" when undo is cheap.
|
||||
|
||||
## Tone (P3, P1)
|
||||
|
||||
- Helpful, not clever. Cleverness ages. Helpfulness does not.
|
||||
- Apologetic when the system is at fault. "We couldn't save that. Try again." Not "Error."
|
||||
- Neutral, not excited. "Saved." Not "Awesome! Saved!! 🎉" (unless the brand is intentionally playful).
|
||||
|
||||
## Microcopy (P3 Clarity, P9 Simplicity)
|
||||
|
||||
- Buttons: 1–3 words. "Sign in", "Create account", "Send invite".
|
||||
- Tooltips: 1 sentence. Explain what the field does, not what the label says.
|
||||
- Empty states: 1 sentence of explanation + 1 action. "No team members yet. Invite your first."
|
||||
- Loading: present tense. "Saving..." not "Saved" (until it is).
|
||||
|
||||
## What Violates UI Copywriting
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| "Error code 500" to a user | P3, P1 |
|
||||
| "Are you sure?" with no consequence | P5 (no information) |
|
||||
| "Submit" on a delete button | P3 (wrong verb) |
|
||||
| 50-word tooltip | P9 Simplicity |
|
||||
| "Awesome!" on a routine save | P1 (not for the user) |
|
||||
@@ -0,0 +1,115 @@
|
||||
# UI / UX — First Principles
|
||||
|
||||
**Version:** 1.0.0
|
||||
**Status:** Foundational
|
||||
**Audience:** AI agents and humans designing user interfaces, components, pages, and flows.
|
||||
|
||||
## 1. Manifesto
|
||||
|
||||
A user interface is a contract between a system and a person. The cost of a bad interface is paid by every user, every time they use it. The highest quality interface is one that a stranger can use correctly without instructions, regardless of their abilities.
|
||||
|
||||
## 2. The Principles
|
||||
|
||||
### P1. User Primacy
|
||||
The user's goal is the system's goal. The system never optimizes for itself at the user's expense.
|
||||
|
||||
- A loading indicator exists because the user is waiting, not because the system is busy.
|
||||
- A default exists because the user would choose it, not because it is easiest to implement.
|
||||
- The system never makes the user do work the system could do.
|
||||
|
||||
### P2. Accessibility
|
||||
Every user can use the interface, regardless of ability or context.
|
||||
|
||||
- Accessibility is not a feature; it is a property of correct interfaces.
|
||||
- Failure to be accessible is disqualifying — see `domains/uiux/accessibility.md`.
|
||||
- Every interactive element is keyboard-reachable and screen-reader announced.
|
||||
|
||||
### P3. Clarity
|
||||
The interface communicates what it does, what it did, and what will happen next.
|
||||
|
||||
- Labels are nouns. Actions are verbs. States are adjectives.
|
||||
- The user should never wonder "what will this do?"
|
||||
- A confused user is a defect, not a user error.
|
||||
|
||||
### P4. Feedback
|
||||
Every user action produces an immediate, visible response.
|
||||
|
||||
- The system always acknowledges input, even before it processes it.
|
||||
- Feedback is for the user, not the developer. A console log is not feedback.
|
||||
- See `domains/observability/` for the system-side complement.
|
||||
|
||||
### P5. Forgiveness
|
||||
User actions are reversible. Mistakes are recoverable.
|
||||
|
||||
- Destructive actions require confirmation. Irreversible actions require double confirmation.
|
||||
- Undo is a first-class operation, not an afterthought.
|
||||
- The system never traps the user in a state they did not choose.
|
||||
|
||||
### P6. Performance
|
||||
The interface responds in the time the user expects, not the time the system takes.
|
||||
|
||||
- Perceived performance beats measured performance when they diverge.
|
||||
- A 200ms response with feedback feels faster than a 50ms response without.
|
||||
- See `domains/performance/frontend.md` for the technical complement.
|
||||
|
||||
### P7. Hierarchy
|
||||
The interface communicates priority through structure, not decoration.
|
||||
|
||||
- The most important thing is the most visible.
|
||||
- Hierarchy is visual: size, weight, position, contrast. Not noise.
|
||||
- A flat interface hides priority. A cluttered interface invents false priority.
|
||||
|
||||
### P8. Consistency
|
||||
The same action has the same result, the same name, and the same location, everywhere.
|
||||
|
||||
- Consistency serves predictability: the user learns once, applies everywhere.
|
||||
- Inconsistency is a tax on the user's attention.
|
||||
- See `domains/uiux/components.md` for component-level consistency rules.
|
||||
|
||||
### P9. Simplicity
|
||||
The interface shows the user what they need, when they need it, and nothing more.
|
||||
|
||||
- Progressive disclosure: show the common path, hide the rare path.
|
||||
- A simple interface is complete for its purpose. A simpler-than-necessary interface is not.
|
||||
- Simplicity serves clarity: a cluttered interface is unclear.
|
||||
|
||||
### P10. Reversibility
|
||||
The user can always go back, undo, or cancel.
|
||||
|
||||
- Navigation is reversible. Data changes are reversible. Sessions are resumable.
|
||||
- The "back" button always works. The "cancel" button always cancels.
|
||||
- Reversibility serves forgiveness (P5): the cost of a mistake is bounded.
|
||||
|
||||
## 3. Conflict Resolution
|
||||
|
||||
1. Accessibility (P2) — never sacrificed.
|
||||
2. Clarity (P3) — never sacrificed.
|
||||
3. User Primacy (P1) — never sacrificed.
|
||||
4. Forgiveness (P5) — sacrificed only when an action is genuinely irreversible by domain.
|
||||
5. Feedback (P4) — sacrificed only for Performance (P6) with perceived-performance evidence.
|
||||
6. Consistency (P8) — sacrificed for Clarity (P3) when a context demands a different pattern.
|
||||
7. Hierarchy (P7) — sacrificed for Simplicity (P9) when an interface is simple enough to need no hierarchy.
|
||||
8. Simplicity (P9) — sacrificed for Clarity (P3) when simplifying would obscure.
|
||||
9. Performance (P6) — sacrificed for Feedback (P4) when the user needs to know the system is working.
|
||||
10. Reversibility (P10) — sacrificed only for genuinely irreversible operations (e.g., account deletion), with explicit confirmation.
|
||||
|
||||
Three of ten principles are non-tradeable: Accessibility, Clarity, User Primacy. These derive from core C1 (Correctness) and C2 (Clarity).
|
||||
|
||||
## 4. What Violates These Principles
|
||||
|
||||
| Violation | Principle Breached |
|
||||
|-----------|-------------------|
|
||||
| A button that does nothing on click | P4 Feedback |
|
||||
| An image without alt text | P2 Accessibility |
|
||||
| A "delete" with no confirmation | P5 Forgiveness |
|
||||
| A 5-second spinner with no progress | P6 Performance, P4 Feedback |
|
||||
| Two "save" buttons that do different things | P8 Consistency |
|
||||
| A settings page with 50 options visible at once | P9 Simplicity |
|
||||
| A form that cannot be navigated by keyboard | P2 Accessibility |
|
||||
| An action that cannot be undone or cancelled | P10 Reversibility |
|
||||
| A label that says "Submit" when it deletes | P3 Clarity |
|
||||
| A system that optimizes its own load time over the user's wait | P1 User Primacy |
|
||||
|
||||
## 5. Relationship to Core
|
||||
|
||||
Subordinate to `core/first-principles.md`. The three non-tradeable principles (P2, P3, P1) are promoted to C1-equivalent. See `matrix/principles-matrix.md` for the full derivation. Sibling to `domains/uiux/components.md` and `domains/uiux/accessibility.md`.
|
||||
@@ -0,0 +1,48 @@
|
||||
# Design Tokens — Derived Rules
|
||||
|
||||
> Derives from `domains/uiux/first-principles.md` P7 (Hierarchy), P8 (Consistency), P9 (Simplicity).
|
||||
|
||||
## What is a Token
|
||||
|
||||
A design token is the smallest unit of a design system: a color, a spacing, a font size, a radius. It has a name and a value. The name is the API; the value is the implementation.
|
||||
|
||||
## The Token Hierarchy
|
||||
|
||||
```
|
||||
Global tokens (e.g., --color-blue-500)
|
||||
→ Alias tokens (e.g., --color-button-primary)
|
||||
→ Component tokens (e.g., --button-primary-bg)
|
||||
```
|
||||
|
||||
- Global tokens are the raw palette. They have no semantic meaning.
|
||||
- Alias tokens have meaning: "this is the button background." They reference global tokens.
|
||||
- Component tokens are scoped to a component. They reference alias tokens.
|
||||
- Components reference component or alias tokens, never global tokens directly.
|
||||
|
||||
## No Raw Values (P8 Consistency)
|
||||
|
||||
- A component never hardcodes `#3b82f6` or `16px`. It references `--color-button-primary` or `--space-4`.
|
||||
- A hardcoded value is a token that should exist but does not. It is design debt.
|
||||
- The token is the API to the design system. Bypassing it bypasses the system.
|
||||
|
||||
## Naming (P3 Clarity)
|
||||
|
||||
- Tokens are semantic, not visual: `--color-text-primary`, not `--color-dark-gray`.
|
||||
- Visual names couple to the implementation. Semantic names survive a redesign.
|
||||
- `--color-button-primary-bg` is clear. `--color-blue` is not (which button? what state?).
|
||||
|
||||
## Theming via Tokens (P5 Forgiveness, core C5 Reversibility)
|
||||
|
||||
- Themes are sets of token values. Switch theme = switch token values, not switch CSS.
|
||||
- Dark mode: `--color-text-primary: #fff` instead of `#000`. The component code does not change.
|
||||
- A component that hardcodes colors cannot be themed. A component that uses tokens can.
|
||||
|
||||
## What Violates Token Discipline
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| `color: #3b82f6` in a component | P8 Consistency |
|
||||
| `--color-blue-500` referenced by a component directly | P9 (skip alias) |
|
||||
| `--color-dark-gray` (visual name) | P3 Clarity |
|
||||
| A component that cannot be themed | P5, core C5 |
|
||||
| No global tokens, every component invents its own palette | P8 |
|
||||
@@ -0,0 +1,107 @@
|
||||
# Bad Example: God Object
|
||||
|
||||
> A component that violates Atelier principles. Each violation is cited.
|
||||
|
||||
## The Code
|
||||
|
||||
```typescript
|
||||
// UserManager.ts — 1,200 lines
|
||||
class UserManager {
|
||||
private users: User[] = [];
|
||||
private cache: Map<string, User> = new Map();
|
||||
private db: Database;
|
||||
private emailService: EmailService;
|
||||
private logger: Logger;
|
||||
private auditLog: AuditLog;
|
||||
|
||||
constructor(db: Database, email: EmailService, logger: Logger, audit: AuditLog) {
|
||||
this.db = db;
|
||||
this.emailService = email;
|
||||
this.logger = logger;
|
||||
this.auditLog = audit;
|
||||
}
|
||||
|
||||
// CRUD
|
||||
async createUser(data: UserData): Promise<User> { /* 80 lines */ }
|
||||
async getUser(id: string): Promise<User> { /* 40 lines */ }
|
||||
async updateUser(id: string, data: Partial<UserData>): Promise<User> { /* 60 lines */ }
|
||||
async deleteUser(id: string): Promise<void> { /* 50 lines */ }
|
||||
async listUsers(page: number): Promise<User[]> { /* 40 lines */ }
|
||||
|
||||
// Email
|
||||
async sendWelcomeEmail(user: User): Promise<void> { /* 50 lines */ }
|
||||
async sendPasswordReset(user: User): Promise<void> { /* 50 lines */ }
|
||||
async sendDeletionNotice(user: User): Promise<void> { /* 40 lines */ }
|
||||
|
||||
// Auth
|
||||
async authenticate(email: string, password: string): Promise<boolean> { /* 70 lines */ }
|
||||
async authorize(userId: string, action: string): Promise<boolean> { /* 60 lines */ }
|
||||
async hashPassword(password: string): Promise<string> { /* 20 lines */ }
|
||||
|
||||
// Cache
|
||||
private cacheGet(id: string): User | null { /* 20 lines */ }
|
||||
private cacheSet(user: User): void { /* 20 lines */ }
|
||||
private cacheInvalidate(id: string): void { /* 20 lines */ }
|
||||
|
||||
// Audit
|
||||
private logAudit(action: string, userId: string): void { /* 30 lines */ }
|
||||
|
||||
// Validation
|
||||
private validateEmail(email: string): boolean { /* 20 lines */ }
|
||||
private validatePassword(password: string): boolean { /* 20 lines */ }
|
||||
|
||||
// Serialization
|
||||
toJSON(user: User): Record<string, unknown> { /* 30 lines */ }
|
||||
fromJSON(data: Record<string, unknown>): User { /* 30 lines */ }
|
||||
|
||||
// ... 200 more lines of helper methods
|
||||
}
|
||||
```
|
||||
|
||||
## Violations
|
||||
|
||||
### C3 Simplicity (Core)
|
||||
- A 1,200-line class doing 8 different things (CRUD, email, auth, cache, audit, validation, serialization).
|
||||
- The class cannot be understood in one read. Complexity is the liability.
|
||||
- **Fix:** Split into `UserRepository` (CRUD), `UserEmailService` (email), `UserAuthService` (auth), `UserCache` (cache), `UserAuditLogger` (audit), `UserValidator` (validation), `UserSerializer` (serialization).
|
||||
|
||||
### C6 Composability (Core)
|
||||
- The class takes 4 dependencies and does 8 jobs. It is not composable; it is monolithic.
|
||||
- You cannot reuse the email logic without the DB, the cache, the audit log.
|
||||
- **Fix:** Each responsibility is its own class. Compose them: `UserEmailService` takes only `EmailService`.
|
||||
|
||||
### components.md §1 Single Responsibility (UI/UX, applies to code)
|
||||
- The class name is `UserManager`. "Manager" is a smell — it manages what? Everything.
|
||||
- If the name is "Manager," it has no single responsibility.
|
||||
- **Fix:** Name by responsibility: `UserRepository`, `UserAuthService`. Names that cannot be "And"-ed.
|
||||
|
||||
### C4 Locality (Core)
|
||||
- Cache logic is in the same class as email logic. A change to cache touches the email methods' neighbor.
|
||||
- Related logic (cache get/set/invalidate) is grouped, but unrelated logic (email) is adjacent.
|
||||
- **Fix:** `UserCache` is its own class. Cache changes are local to cache.
|
||||
|
||||
### C2 Clarity (Core)
|
||||
- A reader cannot answer "what does `UserManager` do?" in one sentence.
|
||||
- The class has 20+ methods. The reader must scan all of them to find the one they need.
|
||||
- **Fix:** Smaller classes with clear names. The name is the documentation.
|
||||
|
||||
### Security P2 Least Privilege (Security)
|
||||
- The class has `db`, `emailService`, `logger`, `auditLog` — all available to all methods.
|
||||
- `sendWelcomeEmail` has access to `db.delete`. Least privilege is violated.
|
||||
- **Fix:** Each service has only the dependencies it needs. `UserEmailService` has `EmailService`, not `Database`.
|
||||
|
||||
### Testing P2 Independence (Testing)
|
||||
- To test `sendWelcomeEmail`, you must construct `UserManager` with a real/mock DB, email, logger, audit.
|
||||
- The test setup is 4 mocks for one method. Independence is violated.
|
||||
- **Fix:** Test `UserEmailService` with one mock (`EmailService`).
|
||||
|
||||
## What This Example Reveals
|
||||
|
||||
The "God Object" is the cardinal sin of OOP. It violates C3 (Simplicity), C6 (Composability), C4 (Locality), and C2 (Clarity) simultaneously. Every other principle suffers downstream:
|
||||
|
||||
- Testing is hard (T2 Independence).
|
||||
- Security is loose (S2 Least Privilege).
|
||||
- Evolution is brittle (a change to email risks cache).
|
||||
- Review is exhausting (a 1,200-line diff).
|
||||
|
||||
The fix is always the same: **decompose by responsibility**. The class name is the test: if it is "Manager," "Handler," or "Helper," it has no single responsibility.
|
||||
@@ -0,0 +1,87 @@
|
||||
# Bad Example: Leaky Abstraction
|
||||
|
||||
> An abstraction that leaks its implementation details, violating Atelier principles. Each violation is cited.
|
||||
|
||||
## The Code
|
||||
|
||||
```typescript
|
||||
// UserRepository — "abstracts" the database
|
||||
class UserRepository {
|
||||
async findAll(): Promise<UserRow[]> {
|
||||
// Leaks: returns the raw DB row type, not a domain User
|
||||
return db.query('SELECT id, email, password_hash, created_at, deleted_at FROM users');
|
||||
}
|
||||
|
||||
async findByEmail(email: string): Promise<UserRow | null> {
|
||||
// Leaks: the caller must know to filter deleted_at
|
||||
const rows = await db.query('SELECT * FROM users WHERE email = $1', [email]);
|
||||
return rows[0] || null;
|
||||
}
|
||||
|
||||
async save(user: UserRow): Promise<void> {
|
||||
// Leaks: the caller must know the column names and the SQL
|
||||
await db.query(
|
||||
'UPDATE users SET email = $1, password_hash = $2, updated_at = now() WHERE id = $3',
|
||||
[user.email, user.password_hash, user.id]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Usage — the leak is visible
|
||||
const repo = new UserRepository();
|
||||
const user = await repo.findByEmail('jane@example.com');
|
||||
if (user && !user.deleted_at) { // caller must know about soft delete
|
||||
user.password_hash = await hash(newPassword); // caller must know the column
|
||||
await repo.save(user); // caller must know it's an UPDATE
|
||||
}
|
||||
```
|
||||
|
||||
## Violations
|
||||
|
||||
### C6 Composability (Core)
|
||||
- The abstraction is supposed to hide the database. It does not.
|
||||
- The caller must know: the row type (`UserRow`), the soft-delete column (`deleted_at`), the password column (`password_hash`), the SQL operation (`UPDATE`).
|
||||
- The abstraction is a thin wrapper. It composes nothing; it leaks everything.
|
||||
- **Fix:** Return a domain `User` (no `password_hash`, no `deleted_at`). Hide soft delete (the repo filters it). Hide persistence (the caller calls `save`, not `UPDATE`).
|
||||
|
||||
### C2 Clarity (Core)
|
||||
- The caller's code is unclear: `if (user && !user.deleted_at)` — what is `deleted_at`? Why does the caller check it?
|
||||
- The abstraction was supposed to clarify. It muddied.
|
||||
- **Fix:** `repo.findByEmail()` returns `User | null` (already filtered). The caller does not know soft delete exists.
|
||||
|
||||
### API P2 Clarity (API, by analogy)
|
||||
- The repo's API exposes the DB schema in its return types. `UserRow` is a DB concept, not a domain concept.
|
||||
- The public contract (return type) leaks the private implementation (the table).
|
||||
- **Fix:** The return type is `User`, a domain type. `UserRow` is internal.
|
||||
|
||||
### Data P8 Lifecycle Awareness (Data)
|
||||
- The soft-delete lifecycle (`deleted_at`) is the repo's concern. The caller should not manage it.
|
||||
- By exposing `deleted_at`, the repo forces every caller to remember the filter. A forgotten filter is a soft-delete leak.
|
||||
- **Fix:** The repo filters `deleted_at IS NULL` in every query. The caller never sees `deleted_at`.
|
||||
|
||||
### Security P9 Secret Hygiene (Security)
|
||||
- `password_hash` is in the returned `UserRow`. The caller now has access to the password hash.
|
||||
- A caller that logs `user` logs the hash. A caller that serializes `user` serializes the hash.
|
||||
- **Fix:** `User` does not include `password_hash`. Only `UserRepository` and `AuthService` (internal) see it.
|
||||
|
||||
### C4 Locality (Core)
|
||||
- The SQL is in the repo, but the column knowledge (`password_hash`, `deleted_at`) is in the caller.
|
||||
- A column rename touches the repo AND every caller. Locality is violated.
|
||||
- **Fix:** Column names are local to the repo. The caller knows only the domain `User`.
|
||||
|
||||
### C5 Reversibility (Core)
|
||||
- Changing the database (e.g., from SQL to NoSQL, or renaming a column) requires touching every caller.
|
||||
- The abstraction was supposed to make the change local. It does not.
|
||||
- **Fix:** The repo's interface (`findByEmail`, `save`) is stable. The implementation changes; the callers do not.
|
||||
|
||||
## What This Example Reveals
|
||||
|
||||
The leaky abstraction is the false promise of encapsulation. The class is named `UserRepository` (suggesting it abstracts persistence), but it returns raw DB rows, exposes lifecycle columns, and leaks secret fields. The abstraction exists in name only.
|
||||
|
||||
The cost:
|
||||
- Every caller must know the DB schema (C6 violated).
|
||||
- A schema change touches every caller (C5 violated, C4 violated).
|
||||
- Secret fields leak to callers (Security P9 violated).
|
||||
- The lifecycle is the caller's burden (Data P8 violated).
|
||||
|
||||
The fix is always the same: **the abstraction's public type is the domain type, not the implementation type**. `UserRepository.findByEmail()` returns `User | null`, where `User` has `id`, `email`, `name` — and nothing else. `password_hash`, `deleted_at`, `UserRow` are internal. The caller knows nothing about the database.
|
||||
@@ -0,0 +1,88 @@
|
||||
# Bad Example: Silent Error
|
||||
|
||||
> An error-handling pattern that violates Atelier principles. Each violation is cited.
|
||||
|
||||
## The Code
|
||||
|
||||
```typescript
|
||||
async function getUser(id: string): Promise<User | null> {
|
||||
try {
|
||||
const user = await db.query('SELECT * FROM users WHERE id = $1', [id]);
|
||||
return user;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function processOrder(orderId: string): Promise<void> {
|
||||
const order = await getOrder(orderId);
|
||||
if (!order) {
|
||||
return; // silently do nothing
|
||||
}
|
||||
// ... process
|
||||
}
|
||||
|
||||
// Usage in a route
|
||||
router.get('/users/:id', async (req, res) => {
|
||||
const user = await getUser(req.params.id);
|
||||
if (!user) {
|
||||
res.status(404).json({ error: 'Not found' });
|
||||
} else {
|
||||
res.json({ data: user });
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Violations
|
||||
|
||||
### Errors P2 Fail Loudly (Errors)
|
||||
- `catch (e) { return null }` swallows the error. The caller cannot distinguish "user not found" from "database down."
|
||||
- A database outage returns 404s. The operator never knows. Silent failure.
|
||||
- **Fix:** Catch and re-throw with context, or return a typed error (`Result<User, Error>`). Never `null` for "an error happened."
|
||||
|
||||
### Errors P3 Fail Specifically (Errors)
|
||||
- `return null` is the least specific response. It could mean: not found, db error, network error, permission error.
|
||||
- The caller's `if (!user)` cannot distinguish these. The 404 is a lie if the real cause was a 500.
|
||||
- **Fix:** Return `Result` or throw. The error type/code carries the specificity.
|
||||
|
||||
### Errors P1 Errors are Data (Errors)
|
||||
- `null` is not data. It is the absence of data. Conflating "error" with "absence" loses information.
|
||||
- The error (a database failure) was data; it was thrown away and replaced with `null`.
|
||||
- **Fix:** Errors are values. Return the error value, not a sentinel absence.
|
||||
|
||||
### Errors P4 Preserve Context (Errors)
|
||||
- The catch block discards `e`. The stack trace, the error message, the cause — all gone.
|
||||
- The log has no record. The operator cannot debug.
|
||||
- **Fix:** Log the error with context. Wrap and re-throw: `throw new Error('getUser failed', { cause: e })`.
|
||||
|
||||
### Errors P9 Errors are Logged (Errors)
|
||||
- The error is not logged. The handling (return null) is the entire response. The log is missing.
|
||||
- An error that is not logged is an error that cannot be investigated.
|
||||
- **Fix:** `logger.error({ err: e, userId: id })` before returning/rethrowing.
|
||||
|
||||
### Errors P10 Errors Don't Lie (Errors)
|
||||
- `return null` claims "no user" when the truth may be "database down." The function lies.
|
||||
- The 404 response claims "not found" when the truth may be "internal error." The API lies.
|
||||
- **Fix:** The response status must match the actual condition. 500 for server errors, 404 for not found.
|
||||
|
||||
### Observability P2 Correlation, P3 Context (Observability)
|
||||
- No `request_id`. No correlation across services.
|
||||
- No context in the (missing) log. "What was the user doing?" is unanswerable.
|
||||
- **Fix:** Propagate `request_id`. Log with path, method, user_id.
|
||||
|
||||
### Security P8 Fail Securely (Security)
|
||||
- The silent failure is fail-open in disguise. If `getUser` fails due to an authz check throwing, the catch returns `null`.
|
||||
- The caller treats `null` as "not found" and may proceed, or may 404. Either way, the security failure is hidden.
|
||||
- **Fix:** Distinguish "not found" (404) from "authz error" (403) from "db error" (500). Never collapse them into `null`.
|
||||
|
||||
## What This Example Reveals
|
||||
|
||||
The silent error is the most common and most damaging anti-pattern. It violates Errors P2 (Fail Loudly), P3 (Fail Specifically), P1 (Errors are Data), P4 (Preserve Context), P9 (Errors are Logged), P10 (Errors Don't Lie) — six of ten error principles in one catch block.
|
||||
|
||||
The downstream effects:
|
||||
- Operators cannot debug (no log, no context).
|
||||
- Users see wrong errors (404 for a 500).
|
||||
- Security failures hide (authz error becomes "not found").
|
||||
- The system appears healthy when it is not (no metrics, no logs).
|
||||
|
||||
The fix is always the same: **never swallow an error**. Log it, wrap it, rethrow it, or return it as a typed value. Never `return null` for "something went wrong."
|
||||
@@ -0,0 +1,79 @@
|
||||
# Good Example: API Endpoint
|
||||
|
||||
> A REST endpoint that follows Atelier's API principles. Each aspect cites the principle it satisfies.
|
||||
|
||||
## The Endpoint
|
||||
|
||||
```typescript
|
||||
// POST /v1/orders — create an order
|
||||
router.post('/v1/orders', auth, validate(CreateOrderSchema), async (req, res) => {
|
||||
const { userId, items } = req.body;
|
||||
|
||||
const order = await orderService.create({ userId, items });
|
||||
|
||||
res.status(201).location(`/v1/orders/${order.id}`).json({
|
||||
data: order,
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## What Makes It Good
|
||||
|
||||
### Resource Naming (API P2 Clarity, P3 Predictability)
|
||||
- `/v1/orders` — noun, plural, lowercase, hyphenated.
|
||||
- Versioned (`/v1`) — P5 Versioning.
|
||||
- No verb in the URL; the HTTP method is the verb.
|
||||
|
||||
### Method Semantics (API P1 Contract Fidelity, P6 Idempotency)
|
||||
- POST for creation. 201 on success (not 200). `Location` header for the new resource.
|
||||
- Idempotency key supported via middleware (omitted for brevity) — P6.
|
||||
|
||||
### Authentication (API P8 Security, Security P1 Zero Trust)
|
||||
- `auth` middleware runs on every endpoint by default. No opt-in auth.
|
||||
- The endpoint does not re-implement auth; it relies on the boundary check.
|
||||
|
||||
### Input Validation (API P8, Security P4 Input Validation)
|
||||
- `validate(CreateOrderSchema)` — schema-based validation at the boundary.
|
||||
- The schema (zod, joi, etc.) defines types, ranges, required fields.
|
||||
- Unknown fields rejected (`additionalProperties: false` in the schema).
|
||||
|
||||
### Response Shape (API P2 Clarity)
|
||||
- `{ data: order }` — wrapped, not a bare object. Allows adding pagination/metadata without breaking.
|
||||
- The shape is consistent across all endpoints in the API.
|
||||
|
||||
### Error Handling (API P9 Error Transparency, Errors P1 Errors are Data)
|
||||
- Errors thrown in `orderService.create` are caught by centralized middleware.
|
||||
- Errors are structured: `{ error: { code, message, request_id } }`.
|
||||
- 404 → `ORDER_NOT_FOUND`, 409 → `DUPLICATE_ORDER`, 422 → `VALIDATION_FAILED`.
|
||||
|
||||
### Observability (Observability P2 Correlation, P3 Sufficient Context)
|
||||
- `request_id` propagated via middleware. Every log in the request includes it.
|
||||
- Significant events logged: "order created", "order creation failed".
|
||||
|
||||
### Economy (Core C8, Performance P4 Resource Bounds)
|
||||
- The order creation is bounded in time (the service has a timeout).
|
||||
- No unbounded query; no loading all products into memory.
|
||||
|
||||
## The Schema (for completeness)
|
||||
|
||||
```typescript
|
||||
const CreateOrderSchema = z.object({
|
||||
userId: z.string().uuid(),
|
||||
items: z.array(z.object({
|
||||
productId: z.string().uuid(),
|
||||
quantity: z.number().int().positive().max(100),
|
||||
})).min(1).max(50),
|
||||
}).strict(); // additionalProperties: false
|
||||
```
|
||||
|
||||
- `userId` is a UUID (Data P7 Type Fidelity).
|
||||
- `quantity` is bounded (Performance P4).
|
||||
- `items` is bounded (1–50) (Performance P4, Security P10 Surface Minimization).
|
||||
- `.strict()` rejects unknown fields (Security P4).
|
||||
|
||||
## What This Example Does NOT Do (And Why That's Good)
|
||||
|
||||
- Does not return 200 on error — the status code is the first signal (API P9).
|
||||
- Does not log the request body — may contain PII (Observability P6, Security P9).
|
||||
- Does not construct SQL by string interpolation — uses a service layer (Security P5 Output Safety).
|
||||
- Does not skip auth for "internal" callers — Zero Trust (Security P1).
|
||||
@@ -0,0 +1,88 @@
|
||||
# Good Example: Database Schema
|
||||
|
||||
> A SQL schema that follows Atelier's Data principles. Each aspect cites the principle it satisfies.
|
||||
|
||||
## The Schema
|
||||
|
||||
```sql
|
||||
-- Users table
|
||||
CREATE TABLE users (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
email VARCHAR(255) NOT NULL,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
deleted_at TIMESTAMPTZ,
|
||||
|
||||
CONSTRAINT users_email_unique UNIQUE (email),
|
||||
CONSTRAINT users_email_format CHECK (email ~ '^[^@]+@[^@]+\.[^@]+$')
|
||||
);
|
||||
|
||||
CREATE INDEX users_email_idx ON users (email) WHERE deleted_at IS NULL;
|
||||
CREATE INDEX users_created_at_idx ON users (created_at DESC);
|
||||
|
||||
-- Orders table
|
||||
CREATE TABLE orders (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE RESTRICT,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'pending',
|
||||
total_cents INTEGER NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
|
||||
CONSTRAINT orders_status_valid CHECK (status IN ('pending', 'paid', 'shipped', 'cancelled')),
|
||||
CONSTRAINT orders_total_positive CHECK (total_cents >= 0)
|
||||
);
|
||||
|
||||
CREATE INDEX orders_user_id_idx ON orders (user_id);
|
||||
CREATE INDEX orders_status_created_idx ON orders (status, created_at DESC);
|
||||
```
|
||||
|
||||
## What Makes It Good
|
||||
|
||||
### Truth (Data P1)
|
||||
- The schema reflects the domain: `users` have `email`, `name`, lifecycle timestamps. `orders` have `status`, `total`.
|
||||
- No column named after a feature (`is_active_for_X`). No application state in the schema.
|
||||
|
||||
### Invariants in the Schema (Data P3)
|
||||
- `NOT NULL` where required: `email`, `name`, `user_id`, `status`, `total_cents`.
|
||||
- `UNIQUE (email)` — emails are unique. Enforced in the DB, defended in the app.
|
||||
- `CHECK (status IN (...))` — status is a finite domain. Enforced in the DB.
|
||||
- `CHECK (total_cents >= 0)` — totals are non-negative. Enforced in the DB.
|
||||
- `REFERENCES users(id) ON DELETE RESTRICT` — you cannot delete a user with orders. Referential integrity (P9).
|
||||
|
||||
### Type Fidelity (Data P7)
|
||||
- `id` is `UUID`, not `VARCHAR(36)`. The type matches the domain.
|
||||
- `created_at` is `TIMESTAMPTZ`, not `VARCHAR` or `INTEGER`. Timezone-aware.
|
||||
- `total_cents` is `INTEGER`, not `FLOAT`. Money in cents avoids floating point (P1 Truth).
|
||||
- `status` is `VARCHAR(20)` with a CHECK, not a free `TEXT`. Bounded.
|
||||
|
||||
### Naming Consistency (Data P6)
|
||||
- snake_case: `users`, `orders`, `user_id`, `created_at`.
|
||||
- Foreign key: `user_id` (singular table + `_id`), not `uid` or `user`.
|
||||
- Timestamps: `created_at`, `updated_at`, `deleted_at` — consistent suffix `_at`.
|
||||
|
||||
### Lifecycle Awareness (Data P8)
|
||||
- `deleted_at` for soft delete. Lifecycle is first-class.
|
||||
- The unique index on email is partial: `WHERE deleted_at IS NULL` — allows re-registration after soft delete.
|
||||
- Every query must filter `deleted_at IS NULL` (a discipline, not a schema property).
|
||||
|
||||
### Indexing with Intent (Data P5, P10)
|
||||
- `users_email_idx` — queries by email (login, lookup). Partial (excludes soft-deleted).
|
||||
- `users_created_at_idx` — list users by recency. DESC matches the typical query.
|
||||
- `orders_user_id_idx` — list a user's orders. FK index (join performance).
|
||||
- `orders_status_created_idx` — composite for "open orders by recency" (`WHERE status = 'pending' ORDER BY created_at DESC`).
|
||||
- No index on every column. Each index serves a query.
|
||||
|
||||
### Migration Safety (Data P4)
|
||||
- This schema is created via a migration with an `up` and a `down`.
|
||||
- The `down` drops the tables in reverse order (orders, then users) to respect FKs.
|
||||
- Adding a column later uses expand-contract (nullable first, then constrained).
|
||||
|
||||
## What This Example Does NOT Do (And Why That's Good)
|
||||
|
||||
- Does not use `FLOAT` for money — floating point errors (P7, P1).
|
||||
- Does not use `VARCHAR` for the UUID — wrong type (P7).
|
||||
- Does not omit the FK on `orders.user_id` — unenforced relationship (P9).
|
||||
- Does not index every column — write amplification (P10, C8).
|
||||
- Does not use `is_deleted BOOLEAN` without a timestamp — loses the deletion time (P8).
|
||||
- Does not allow `status` to be free text — would lose the finite domain (P3).
|
||||
@@ -0,0 +1,139 @@
|
||||
# Good Example: Error Handler
|
||||
|
||||
> An error handler that follows Atelier's Errors principles. Each aspect cites the principle it satisfies.
|
||||
|
||||
## The Handler
|
||||
|
||||
```typescript
|
||||
// Centralized error middleware
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
type AppError = {
|
||||
code: string;
|
||||
message: string;
|
||||
statusCode: number;
|
||||
details?: Record<string, unknown>;
|
||||
cause?: unknown;
|
||||
};
|
||||
|
||||
function errorHandler(err: unknown, req: Request, res: Response, next: NextFunction) {
|
||||
const requestId = req.headers['x-request-id'] as string || uuidv4();
|
||||
|
||||
const appError = normalizeError(err, requestId);
|
||||
|
||||
// Log with full context (server-side only)
|
||||
logger.error({
|
||||
request_id: requestId,
|
||||
code: appError.code,
|
||||
message: appError.message,
|
||||
details: appError.details,
|
||||
path: req.path,
|
||||
method: req.method,
|
||||
user_id: req.user?.id,
|
||||
stack: err instanceof Error ? err.stack : undefined,
|
||||
});
|
||||
|
||||
// Respond with safe, structured error
|
||||
res.status(appError.statusCode).json({
|
||||
error: {
|
||||
code: appError.code,
|
||||
message: appError.message,
|
||||
request_id: requestId,
|
||||
...(appError.details ? { details: appError.details } : {}),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function normalizeError(err: unknown, requestId: string): AppError {
|
||||
// Known error types — preserve their code and status
|
||||
if (err instanceof ValidationError) {
|
||||
return {
|
||||
code: err.code,
|
||||
message: err.message,
|
||||
statusCode: 422,
|
||||
details: err.details,
|
||||
};
|
||||
}
|
||||
if (err instanceof NotFoundError) {
|
||||
return { code: err.code, message: err.message, statusCode: 404 };
|
||||
}
|
||||
if (err instanceof AuthError) {
|
||||
return { code: 'AUTH_REQUIRED', message: 'Authentication required', statusCode: 401 };
|
||||
}
|
||||
|
||||
// Unknown error — fail securely, do not leak internals
|
||||
return {
|
||||
code: 'INTERNAL_ERROR',
|
||||
message: 'An internal error occurred',
|
||||
statusCode: 500,
|
||||
cause: err,
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## What Makes It Good
|
||||
|
||||
### Errors are Data (Errors P1)
|
||||
- Errors are normalized into a structured `AppError` type. They are values, not raw exceptions.
|
||||
- The `code` is a stable, machine-consumable string (`VALIDATION_FAILED`, not a free message).
|
||||
- The handler treats every error as data to be transformed, not a flow to be caught.
|
||||
|
||||
### Fail Loudly (Errors P2)
|
||||
- No `catch (e) {}`. Every error reaches the handler.
|
||||
- No silent swallowing. The error is logged and responded to.
|
||||
- The middleware is centralized — every route uses it. No route can "forget" to handle errors.
|
||||
|
||||
### Fail Specifically (Errors P3)
|
||||
- `ValidationError` → 422 with `code: err.code` (specific, e.g., `INVALID_EMAIL`).
|
||||
- `NotFoundError` → 404 with `code: err.code` (e.g., `USER_NOT_FOUND`).
|
||||
- Unknown → 500 with `INTERNAL_ERROR` (generic only when truly unknown).
|
||||
- Never "Something went wrong" — always a specific code.
|
||||
|
||||
### Preserve Context (Errors P4)
|
||||
- Server-side log includes: `request_id`, `code`, `message`, `details`, `path`, `method`, `user_id`, `stack`.
|
||||
- Client-side response includes: `code`, `message`, `request_id`, `details`.
|
||||
- The `request_id` ties the client response to the server log. Debuggable.
|
||||
|
||||
### Recoverable When Possible (Errors P5)
|
||||
- 422 (validation) — the client can fix and retry.
|
||||
- 401 (auth) — the client can re-auth and retry.
|
||||
- 404 (not found) — the client can correct the ID.
|
||||
- Only 500 is "the server has a bug" — not recoverable by the client.
|
||||
|
||||
### Unrecoverable Means Stop (Errors P6)
|
||||
- A 500 does not limp on. It logs and returns.
|
||||
- The server does not try to "recover" from an unknown error by guessing. It fails fast.
|
||||
|
||||
### Errors are Boundaries (Errors P7)
|
||||
- The middleware is the boundary. Internal error types (`ValidationError`, `NotFoundError`) are translated to HTTP responses here.
|
||||
- Internal code throws domain errors; the boundary translates to HTTP. No internal error type leaks to the client.
|
||||
|
||||
### User-Facing Errors are UX (Errors P8)
|
||||
- `message` is for the developer (log + response). The client UI renders a user-facing message based on `code`.
|
||||
- `INTERNAL_ERROR` → "Something went wrong. We're on it." (user-facing), not the stack.
|
||||
|
||||
### Errors are Logged (Errors P9)
|
||||
- Every error is logged at ERROR level with full context.
|
||||
- The handling is the recovery (return a response); the log is the memory (investigate later).
|
||||
|
||||
### Errors Don't Lie (Errors P10)
|
||||
- The status code matches the error type. 422 for validation, not 200.
|
||||
- `INTERNAL_ERROR` is returned only for unknown errors. Known errors get their specific code.
|
||||
- Never claim success on failure. Never claim failure on success.
|
||||
|
||||
### Security (Security P5 Output Safety, P9 Secret Hygiene)
|
||||
- Unknown errors return `INTERNAL_ERROR` with no internal details. No stack trace to the client.
|
||||
- The stack is logged server-side, never sent to the client.
|
||||
- The error message does not echo the input (which may contain a token).
|
||||
|
||||
### Observability (Observability P2 Correlation, P3 Context)
|
||||
- `request_id` on every error. Correlatable across services.
|
||||
- Sufficient context in the log: path, method, user_id. "What was the user doing?" is answerable.
|
||||
|
||||
## What This Example Does NOT Do (And Why That's Good)
|
||||
|
||||
- Does not `catch (e) { return null }` — silent failure (P2).
|
||||
- Does not return 200 with `{ error: ... }` — the status code lies (API P9).
|
||||
- Does not send the stack trace to the client — information leak (Security P5).
|
||||
- Does not log the request body — may contain secrets (Security P9, Observability P6).
|
||||
- Does not use a generic `Error("error")` — not specific (P3).
|
||||
@@ -0,0 +1,108 @@
|
||||
# Good Example: React Component
|
||||
|
||||
> A UI component that follows Atelier's UI/UX principles. Each aspect cites the principle it satisfies.
|
||||
|
||||
## The Component
|
||||
|
||||
```tsx
|
||||
import { useId, useState } from 'react';
|
||||
import { Button } from './Button';
|
||||
import { Spinner } from './Spinner';
|
||||
|
||||
type DeleteButtonProps = {
|
||||
/** The resource name to display in the confirmation */
|
||||
resourceName: string;
|
||||
/** Called when the user confirms deletion */
|
||||
onDelete: () => Promise<void>;
|
||||
};
|
||||
|
||||
export function DeleteButton({ resourceName, onDelete }: DeleteButtonProps) {
|
||||
const [isConfirming, setIsConfirming] = useState(false);
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
const confirmId = useId();
|
||||
|
||||
const handleConfirm = async () => {
|
||||
setIsDeleting(true);
|
||||
try {
|
||||
await onDelete();
|
||||
} finally {
|
||||
setIsDeleting(false);
|
||||
setIsConfirming(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (isConfirming) {
|
||||
return (
|
||||
<span role="group" aria-labelledby={confirmId}>
|
||||
<span id={confirmId}>Delete {resourceName}? This cannot be undone.</span>
|
||||
<Button variant="danger" onClick={handleConfirm} disabled={isDeleting}>
|
||||
{isDeleting ? <Spinner label="Deleting" /> : 'Yes, delete'}
|
||||
</Button>
|
||||
<Button variant="ghost" onClick={() => setIsConfirming(false)} disabled={isDeleting}>
|
||||
Cancel
|
||||
</Button>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Button variant="danger" onClick={() => setIsConfirming(true)}>
|
||||
Delete
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## What Makes It Good
|
||||
|
||||
### Single Responsibility (components.md §1)
|
||||
- The component does one thing: confirm and trigger a deletion. No "And" in the name.
|
||||
- The `onDelete` callback is the single output. The component owns the confirmation UI, not the deletion logic.
|
||||
|
||||
### Explicit Boundaries (components.md §3)
|
||||
- Props are typed (`DeleteButtonProps`). Required props are required.
|
||||
- `onDelete` returns a `Promise<void>` — the caller knows it's async.
|
||||
- The component never reads global state. It receives `resourceName` and `onDelete`.
|
||||
|
||||
### Predictable State (components.md §4)
|
||||
- `isConfirming` and `isDeleting` are owned by the component (only it cares).
|
||||
- State is not duplicated. The parent does not know about confirmation.
|
||||
- The component transitions: idle → confirming → deleting → idle.
|
||||
|
||||
### Render Purity (components.md §5)
|
||||
- Given the same props and state, the component renders the same output.
|
||||
- Side effects (`onDelete`) are in the event handler, not in render.
|
||||
- `useId()` is deterministic per component instance (React guarantee).
|
||||
|
||||
### Accessible by Default (components.md §6, uiux P2)
|
||||
- The confirmation group has `role="group"` and `aria-labelledby`.
|
||||
- The Spinner has a `label` (screen reader announces "Deleting").
|
||||
- Buttons have text labels (not icon-only).
|
||||
- Focus order is logical (confirm → cancel).
|
||||
- Keyboard-reachable (buttons are natively focusable).
|
||||
|
||||
### Forgiveness (UI/UX P5, P10 Reversibility)
|
||||
- Destructive action requires confirmation (P5).
|
||||
- "This cannot be undone" names the consequence (P3 Clarity).
|
||||
- "Cancel" is offered and is not disabled during deletion (the user can cancel the *next* action).
|
||||
- The state is reversible: `isConfirming` can be set back to `false` (P10).
|
||||
|
||||
### Style via Tokens (components.md §7)
|
||||
- `variant="danger"` and `variant="ghost"` reference design tokens, not raw colors.
|
||||
- No `style={{ color: 'red' }}` — the token system owns the visual.
|
||||
|
||||
### Feedback (UI/UX P4)
|
||||
- The button shows a Spinner while deleting (P4, P6 Performance perception).
|
||||
- The button is disabled while deleting (prevents double-click).
|
||||
- The label changes: "Yes, delete" → Spinner (state is communicated).
|
||||
|
||||
### Clarity (UI/UX P3)
|
||||
- "Delete {resourceName}? This cannot be undone." — specific, names the resource and the consequence.
|
||||
- No "Are you sure?" — vague. No "Submit" — wrong verb.
|
||||
|
||||
## What This Example Does NOT Do (And Why That's Good)
|
||||
|
||||
- Does not use a `window.confirm()` dialog — not accessible, not styled, not composable.
|
||||
- Does not render a modal — the inline confirmation is lighter and less disruptive (P9 Simplicity).
|
||||
- Does not auto-delete on click — forgiveness (P5).
|
||||
- Does not hardcode "Project" — the resource name is a prop (composability, components.md §3).
|
||||
@@ -0,0 +1,82 @@
|
||||
# Go — Language Application
|
||||
|
||||
> How Atelier's domain principles apply in Go specifically. Derives from `domains/` docs.
|
||||
|
||||
## Type System (C1 Correctness, Data P7 Type Fidelity)
|
||||
|
||||
- **Named types for domain concepts:** `type UserId string`, not bare `string`.
|
||||
- **No `interface{}`/`any` without justification:** Go 1.18+ generics reduce the need.
|
||||
- **`any` requires a type assertion or switch:** never use the value without narrowing.
|
||||
|
||||
```go
|
||||
type UserId string
|
||||
type OrderId string
|
||||
// UserId and OrderId are distinct; cannot be mixed
|
||||
func GetUser(id UserId) (*User, error) { ... }
|
||||
```
|
||||
|
||||
## Error Handling (Errors P1 Errors are Data)
|
||||
|
||||
- **Errors are values:** `error` is an interface, not an exception. Handle explicitly.
|
||||
- **Sentinel errors with `errors.Is`:**
|
||||
```go
|
||||
var ErrNotFound = errors.New("not found")
|
||||
if errors.Is(err, ErrNotFound) { ... }
|
||||
```
|
||||
- **Wrap with context:** `fmt.Errorf("get user %d: %w", id, err)`.
|
||||
- **Never `_ = err`:** swallowed error (Errors P2). Handle or return.
|
||||
- **Custom error types with `errors.As`:**
|
||||
```go
|
||||
type ValidationError struct {
|
||||
Field string
|
||||
Msg string
|
||||
}
|
||||
func (e *ValidationError) Error() string { return e.Field + ": " + e.Msg }
|
||||
```
|
||||
|
||||
## Concurrency (Concurrency — Go's strength)
|
||||
|
||||
- **Goroutines + channels** for message passing (P5 Lock Minimization).
|
||||
- **`context.Context` for cancellation and timeout:** every function that does I/O takes a `ctx`.
|
||||
- **`sync.Mutex` scoped minimally:** not held across I/O (P3 Lock Scope).
|
||||
- **Bounded channels:** `make(chan T, N)`, not unbounded (P9 Bounded Queues).
|
||||
|
||||
```go
|
||||
func fetchWithTimeout(ctx context.Context, url string) (*Response, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
return doFetch(ctx, url)
|
||||
}
|
||||
```
|
||||
|
||||
## Immutability (Concurrency P1)
|
||||
|
||||
- **Pass by value for small structs; pass by pointer for large or mutable.**
|
||||
- **No mutation of method receivers:** use a value receiver, not a pointer receiver, for read-only methods.
|
||||
- **Copy-on-write for shared state:** return a new struct, not a mutated one.
|
||||
|
||||
## Nullability (C1)
|
||||
|
||||
- **Pointers can be nil; values cannot.** Be explicit: `*User` (nullable) vs `User` (not).
|
||||
- **`nil` check before deref:** a nil deref is a panic.
|
||||
- **Return `(T, error)`, not `(*T, nil)`:** avoid the "nil pointer" trap.
|
||||
|
||||
## Testing (Testing)
|
||||
|
||||
- **`testing` package + `testify/assert`** or stdlib only.
|
||||
- **Table-driven tests:** `[]struct{ name string; input X; want Y }`.
|
||||
- **`t.Parallel()`** for independent tests (P2 Independence).
|
||||
- **`httptest` for HTTP handlers; `sqlite` or testcontainers for DB.**
|
||||
|
||||
## Observability (Observability P1)
|
||||
|
||||
- **`slog` (stdlib, Go 1.21+) or `zap`/`zerolog`:** structured logs.
|
||||
- **`context.Context` carries `trace_id`:** propagated via middleware.
|
||||
- **No `fmt.Println`:** use the logger.
|
||||
|
||||
## Tooling (DevOps P2)
|
||||
|
||||
- **`go vet` + `golangci-lint`:** lint.
|
||||
- **`gofmt`/`goimports`:** format (automated, not debated).
|
||||
- **`go test -race` in CI:** race detector (Concurrency P6 No Silent Races).
|
||||
- **`go mod tidy` + committed `go.sum`:** reproducible builds.
|
||||
@@ -0,0 +1,77 @@
|
||||
# Python — Language Application
|
||||
|
||||
> How Atelier's domain principles apply in Python specifically. Derives from `domains/` docs.
|
||||
|
||||
## Type System (C1 Correctness, Data P7 Type Fidelity)
|
||||
|
||||
- **Type hints on every function:** `def get_user(id: UUID) -> User | None:`.
|
||||
- **`mypy --strict` or `pyright` in CI:** type check is not optional.
|
||||
- **No `Any` without justification:** `Any` disables the type checker. Use `object` + narrowing.
|
||||
- **Pydantic for runtime validation:** schemas validate and type at the boundary.
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel
|
||||
from uuid import UUID
|
||||
|
||||
class UserCreate(BaseModel):
|
||||
email: str
|
||||
name: str
|
||||
# additionalProperties: false by default (extra='forbid')
|
||||
```
|
||||
|
||||
## Error Handling (Errors P1 Errors are Data)
|
||||
|
||||
- **Exceptions for exceptional cases,** not control flow. `raise` not `return None` for errors.
|
||||
- **Custom exception hierarchy:**
|
||||
```python
|
||||
class AppError(Exception): pass
|
||||
class ValidationError(AppError): pass
|
||||
class NotFoundError(AppError): pass
|
||||
```
|
||||
- **Never bare `except:`:** `except Exception as e:` (catch specific, not everything).
|
||||
- **Never `except: pass`:** log and re-raise or handle, never swallow (Errors P2).
|
||||
|
||||
## Async (Concurrency P7, P8)
|
||||
|
||||
- **`asyncio` for I/O-bound:** `async def`, `await`. Not threads for I/O.
|
||||
- **`anyio` for portability** if you may switch runtimes (trio compatibility).
|
||||
- **Timeout on every `await`:** `asyncio.wait_for(coro, timeout=5)`, not bare `await`.
|
||||
- **Cancellation propagated:** `asyncio.CancelledError` is not caught; it propagates.
|
||||
|
||||
## Immutability (Concurrency P1)
|
||||
|
||||
- **`frozen=True` dataclasses** for value objects:
|
||||
```python
|
||||
from dataclasses import dataclass
|
||||
@dataclass(frozen=True)
|
||||
class UserId:
|
||||
value: str
|
||||
```
|
||||
- **Tuples over lists** for fixed-length, immutable sequences.
|
||||
- **No in-place mutation of shared state:** return new objects.
|
||||
|
||||
## Nullability (C1)
|
||||
|
||||
- **`Optional[T]` is `T | None`:** explicit, must be checked.
|
||||
- **`None` is not "not found":** raise `NotFoundError` or return `Result`, not `None`.
|
||||
- **`assert` is for invariants,** not for runtime checks (stripped with `-O`).
|
||||
|
||||
## Testing (Testing)
|
||||
|
||||
- **pytest** with fixtures (factories, not shared state).
|
||||
- **`pytest --randomly`** to catch order-dependent tests (P2 Independence).
|
||||
- **`freezegun` for time:** no `datetime.now()` in tests; inject the clock.
|
||||
- **`factory_boy` or `pytest-factoryboy`** for realistic factories.
|
||||
|
||||
## Observability (Observability P1)
|
||||
|
||||
- **`structlog` or `python-json-logger`:** JSON logs, not `print`.
|
||||
- **`logging` with structured formatter:** every log has `request_id`, `user_id`, `event`.
|
||||
- **No secrets in logs:** `mask_secret()` helper, or `structlog` processors.
|
||||
|
||||
## Tooling (DevOps P2)
|
||||
|
||||
- **`ruff` for lint + format:** replaces flake8 + black + isort.
|
||||
- **`mypy --strict` in CI:** type check.
|
||||
- **`pip-tools` or `poetry` for lockfile:** pinned dependencies.
|
||||
- **`pip install --no-deps -r requirements.txt`:** reproducible install.
|
||||
@@ -0,0 +1,79 @@
|
||||
# Rust — Language Application
|
||||
|
||||
> How Atelier's domain principles apply in Rust specifically. Derives from `domains/` docs.
|
||||
|
||||
## Type System (C1 Correctness, Data P7 Type Fidelity)
|
||||
|
||||
- **Newtypes for domain concepts:** `struct UserId(String);` — zero-cost, type-safe.
|
||||
- **`enum` for finite domains:** `enum Status { Pending, Paid, Shipped }` — exhaustive.
|
||||
- **No `unsafe` without justification and review:** `unsafe` opts out of the compiler's guarantees.
|
||||
|
||||
```rust
|
||||
struct UserId(String);
|
||||
struct OrderId(String);
|
||||
// Cannot pass OrderId where UserId is expected
|
||||
fn get_user(id: UserId) -> Result<User, Error> { ... }
|
||||
```
|
||||
|
||||
## Error Handling (Errors P1 Errors are Data)
|
||||
|
||||
- **`Result<T, E>` for fallible operations:** errors are values, not exceptions.
|
||||
- **`thiserror` for error enums, `anyhow` for applications:**
|
||||
```rust
|
||||
#[derive(thiserror::Error)]
|
||||
enum AppError {
|
||||
#[error("not found: {0}")]
|
||||
NotFound(String),
|
||||
#[error("validation: {0}")]
|
||||
Validation(String),
|
||||
#[error(transparent)]
|
||||
Io(#[from] std::io::Error),
|
||||
}
|
||||
```
|
||||
- **`?` for propagation, not `unwrap()`:** `unwrap()` panics in production.
|
||||
- **No `panic::catch_unwind` for control flow:** panics are for bugs, not errors.
|
||||
|
||||
## Concurrency (Concurrency — Rust's ownership model)
|
||||
|
||||
- **`Send` and `Sync` traits enforced by the compiler:** data races are compile errors.
|
||||
- **`Arc<T>` for shared, `Mutex<T>`/`RwLock<T>` for mutation:** the lock is explicit.
|
||||
- **`tokio` for async:** `async fn`, `.await`. Bounded channels (`tokio::sync::mpsc::channel(N)`).
|
||||
- **`Drop` for cleanup:** no leaked resources (no `defer` needed; RAII).
|
||||
|
||||
```rust
|
||||
async fn fetch_with_timeout(url: &str) -> Result<Response, Error> {
|
||||
tokio::time::timeout(Duration::from_secs(5), fetch(url)).await??;
|
||||
}
|
||||
```
|
||||
|
||||
## Immutability (Concurrency P1 Immutability by Default)
|
||||
|
||||
- **Variables are immutable by default:** `let x = 5;` not `let mut x = 5;`.
|
||||
- **`&T` (shared ref) over `&mut T` (exclusive ref):** the compiler enforces aliasing rules.
|
||||
- **Interior mutability (`Cell`/`RefCell`) only when needed:** not as a default.
|
||||
|
||||
## Nullability (C1)
|
||||
|
||||
- **`Option<T>`, not nullable pointers:** `Some(x)` / `None`. The compiler enforces handling.
|
||||
- **No `null`:** Rust has no null. `Option::None` is the explicit absence.
|
||||
- **`?` on `Option` for propagation:** `fn get_name(user: User) -> Option<String> { user.profile?.name }`.
|
||||
|
||||
## Testing (Testing)
|
||||
|
||||
- **`#[test]` + `#[cfg(test)] mod tests`:** tests co-located.
|
||||
- **`proptest` or `quickcheck` for property-based tests:** edge case coverage (P9).
|
||||
- **`tokio::test` for async tests.**
|
||||
- **No `SystemTime::now()` in tests:** inject an `Instant` or a mock clock.
|
||||
|
||||
## Observability (Observability P1)
|
||||
|
||||
- **`tracing` crate:** structured logs + spans + traces. Not `println!`.
|
||||
- **`tracing::instrument` on functions:** automatic span context.
|
||||
- **`tracing-subscriber` with JSON format:** structured output for production.
|
||||
|
||||
## Tooling (DevOps P2)
|
||||
|
||||
- **`cargo clippy`:** lint. `cargo clippy -- -D warnings` in CI.
|
||||
- **`cargo fmt`:** format.
|
||||
- **`cargo test`:** tests. `cargo test --release` for perf-sensitive.
|
||||
- **Committed `Cargo.lock`:** reproducible builds (even for libraries, for CI).
|
||||
@@ -0,0 +1,76 @@
|
||||
# TypeScript — Language Application
|
||||
|
||||
> How Atelier's domain principles apply in TypeScript specifically. Derives from `domains/` docs; this file is the language-specific lens.
|
||||
|
||||
## Type System (C1 Correctness, Data P7 Type Fidelity)
|
||||
|
||||
- **Strict mode on:** `strict: true` in `tsconfig.json`. No `any` without justification.
|
||||
- **No `any`, no `unknown` without narrowing:** `any` disables the type checker. `unknown` requires narrowing before use.
|
||||
- **Discriminated unions over enums:** `type Status = { type: 'pending' } | { type: 'paid'; amount: number }` — exhaustive, type-safe.
|
||||
- **Branded types for domain IDs:** `type UserId = string & { __brand: 'UserId' }` — prevents passing a `PostId` where a `UserId` is expected.
|
||||
|
||||
```typescript
|
||||
type UserId = string & { readonly __brand: 'UserId' };
|
||||
function getUser(id: UserId): User { ... }
|
||||
// getUser("abc") // type error
|
||||
// getUser("abc" as UserId) // ok
|
||||
```
|
||||
|
||||
## Error Handling (Errors P1 Errors are Data)
|
||||
|
||||
- **Result type over exceptions for expected failures:**
|
||||
```typescript
|
||||
type Result<T, E> = { ok: true; value: T } | { ok: false; error: E };
|
||||
```
|
||||
- **Exceptions for programmer errors:** null deref, invariant violation. Not for "user not found."
|
||||
- **Never `any` in catch:** `catch (e: unknown)` then narrow with `instanceof` or a type guard.
|
||||
|
||||
## Async (Concurrency P7 Cancellation Support, P8 Timeout Discipline)
|
||||
|
||||
- **`Promise` with `AbortSignal`:** every async function accepts an optional `AbortSignal` for cancellation.
|
||||
- **`Promise.race` with a timeout:** never `await` without a timeout for external calls.
|
||||
- **No `await` in a hot loop without batching:** use `Promise.all` for parallelism.
|
||||
|
||||
```typescript
|
||||
async function fetchWithTimeout(url: string, signal?: AbortSignal): Promise<Response> {
|
||||
const timeout = new AbortController();
|
||||
signal?.addEventListener('abort', () => timeout.abort());
|
||||
const timer = setTimeout(() => timeout.abort(), 5000);
|
||||
try {
|
||||
return await fetch(url, { signal: timeout.signal });
|
||||
} finally {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Immutability (Concurrency P1 Immutability by Default)
|
||||
|
||||
- **`readonly` on arrays and objects:** `readonly string[]`, `readonly { id: string }`.
|
||||
- **`as const` for literals:** `const status = 'pending' as const`.
|
||||
- **Immutable update patterns:** `spread` or `Immer` for nested updates, never mutation.
|
||||
|
||||
## Nullability (C1 Correctness)
|
||||
|
||||
- **`strictNullChecks: true`:** `null` and `undefined` are distinct and must be handled.
|
||||
- **No `!` (non-null assertion) without justification:** it disables the null check. Use narrowing.
|
||||
- **`optional chaining` over `&&`:** `user?.profile?.name` not `user && user.profile && user.profile.name`.
|
||||
|
||||
## Testing (Testing)
|
||||
|
||||
- **Jest or Vitest** with `ts-jest`/`vite`. Test files co-located: `user.ts` → `user.test.ts`.
|
||||
- **Factories over fixtures:** `makeUser()` returns a fresh object per test.
|
||||
- **No `Date.now()` in tests:** inject the clock. `jest.useFakeTimers()` or pass a `now` function.
|
||||
|
||||
## Observability (Observability P1 Structured by Default)
|
||||
|
||||
- **Structured logger:** `pino` or `winston` in JSON mode. Not `console.log`.
|
||||
- **`request_id` via middleware:** propagated on every log in the request.
|
||||
- **No secrets in logs:** the logger redacts known secret fields (`pino` redact option).
|
||||
|
||||
## Tooling (DevOps P2 Automation)
|
||||
|
||||
- **ESLint with `@typescript-eslint`** — strict ruleset.
|
||||
- **Prettier** — format, not debated.
|
||||
- **`tsc --noEmit` in CI** — type check without emitting.
|
||||
- **`npm ci`** — lockfile install, not `npm install`.
|
||||
@@ -0,0 +1,46 @@
|
||||
# Domain Coverage
|
||||
|
||||
> Where each core principle applies across the domains. The complement to `principles-matrix.md`: the matrix maps domain → core; this maps core → domains.
|
||||
|
||||
## Core Principle Coverage
|
||||
|
||||
| Core Principle | Domains that derive from it | Count |
|
||||
|----------------|---------------------------|-------|
|
||||
| C1 Correctness | All 11 (security, data, api, testing, performance, observability, errors, uiux, documentation, concurrency, devops) | Universal |
|
||||
| C2 Clarity | uiux, api, data, testing, observability, errors, documentation, devops | 8 |
|
||||
| C3 Simplicity | security, data, testing, performance, documentation, concurrency, devops | 7 |
|
||||
| C4 Locality | testing, concurrency | 2 |
|
||||
| C5 Reversibility | api, data, uiux, concurrency, devops | 5 |
|
||||
| C6 Composability | api, security, observability, errors, documentation, concurrency | 6 |
|
||||
| C7 Observability | api, data, testing, performance, observability, errors, devops | 7 |
|
||||
| C8 Economy | security, testing, performance, observability, concurrency | 5 |
|
||||
|
||||
## Interpretation
|
||||
|
||||
- **C1 Correctness** is universal — every domain derives from it. This confirms C1 as the floor.
|
||||
- **C4 Locality** is the narrowest (2 domains: testing independence, concurrency boundaries). Locality is inherently domain-specific; not a gap.
|
||||
- **C2 Clarity** and **C7 Observability** are the next-most-derived, confirming they are cross-cutting concerns.
|
||||
- **C6 Composability** appears in 6 domains — it is most relevant where parts combine (api, errors, observability, documentation, concurrency) and least relevant where the unit is monolithic.
|
||||
|
||||
## Per-Domain Coverage
|
||||
|
||||
| Domain | C-rules derived from | Notes |
|
||||
|--------|---------------------|-------|
|
||||
| UI/UX | C1, C2, C3, C5, C7 | User-facing; clarity + reversibility |
|
||||
| API | C1, C2, C3, C5, C6, C7 | Contracts; composability + stability |
|
||||
| Security | C1, C3, C6, C7, C8 | 8/10 non-tradeable (promoted to C1) |
|
||||
| Data | C1, C2, C3, C5, C7 | Truth + migration safety |
|
||||
| Testing | C1, C2, C3, C4, C7, C8 | Independence (C4) is unique |
|
||||
| Performance | C1, C3, C6, C7, C8 | Economy + measurement |
|
||||
| Observability | C1, C2, C6, C7, C8 | Self-referential (P7 derives from C7) |
|
||||
| Errors | C1, C2, C5, C6, C7 | Errors as data + reversibility |
|
||||
| Documentation | C1, C2, C3, C5, C6 | Docs-as-code + living docs |
|
||||
| Concurrency | C1, C3, C4, C5, C6, C7, C8 | Broadest derivation; locality (C4) unique |
|
||||
| DevOps | C1, C2, C3, C5, C7 | Reproducibility + rollback |
|
||||
|
||||
## Gaps and Notes
|
||||
|
||||
- No domain derives from only one C-rule. The minimum is 4 (UI/UX: C1, C2, C3, C5, C7 — actually 5). Every domain is multi-rooted.
|
||||
- **Concurrency** has the broadest derivation (7 C-rules) — it touches the most core concerns.
|
||||
- **UI/UX** and **API** are the most user-facing; they emphasize C2 (Clarity) heavily.
|
||||
- **Security** is the only domain with explicit non-tradeable declarations; this promotes 8 of its rules to C1-equivalent per `core/conflict-resolution.md` §6.
|
||||
@@ -0,0 +1,176 @@
|
||||
# Principles Matrix
|
||||
|
||||
> Maps every domain principle to the core principle(s) it derives
|
||||
> from. Used to verify that no domain rule is orphaned and to resolve
|
||||
> conflicts.
|
||||
|
||||
**Core principles key:**
|
||||
C1=Correctness · C2=Clarity · C3=Simplicity · C4=Locality ·
|
||||
C5=Reversibility · C6=Composability · C7=Observability · C8=Economy
|
||||
|
||||
---
|
||||
|
||||
## UI / UX
|
||||
|
||||
| UI/UX Principle | Core | Why |
|
||||
|---------------------------|------|---------------------------------------|
|
||||
| P1 User Primacy | C2 | Clarity serves the user |
|
||||
| P2 Accessibility | C1 | Correctness for all abilities |
|
||||
| P3 Clarity | C2 | Direct derivation |
|
||||
| P4 Feedback | C7 | Observability for humans |
|
||||
| P5 Forgiveness | C5 | Reversibility of user actions |
|
||||
| P6 Performance | C1 | Correctness includes temporal correctness |
|
||||
| P7 Hierarchy | C2 | Clarity of priority |
|
||||
| P8 Consistency | C2 | Clarity through uniformity |
|
||||
| P9 Simplicity | C3 | Direct derivation |
|
||||
| P10 Reversibility | C5 | Direct derivation |
|
||||
|
||||
## API Design
|
||||
|
||||
| API Principle | Core | Why |
|
||||
|---------------------------|------|---------------------------------------|
|
||||
| P1 Contract Fidelity | C1 | Correctness of the public contract |
|
||||
| P2 Clarity | C2 | Direct derivation |
|
||||
| P3 Predictability | C3 | Simplicity of mental model |
|
||||
| P4 Composability | C6 | Direct derivation |
|
||||
| P5 Versioning | C5 | Reversibility for consumers |
|
||||
| P6 Idempotency | C1 | Correctness under retry |
|
||||
| P7 Performance | C1 | Correctness includes latency |
|
||||
| P8 Security | C1 | Correctness includes safety |
|
||||
| P9 Error Transparency | C7 | Observability of failures |
|
||||
| P10 Stability | C5 | Reversibility of changes |
|
||||
|
||||
## Security
|
||||
|
||||
| Security Principle | Core | Why |
|
||||
|---------------------------|------|---------------------------------------|
|
||||
| P1 Zero Trust | C1 | Correctness requires no false trust |
|
||||
| P2 Least Privilege | C3, C8 | Simplicity of access; economy of trust |
|
||||
| P3 Defense in Depth | C1 | Correctness under partial failure |
|
||||
| P4 Input Validation | C1 | Correctness of inputs |
|
||||
| P5 Output Safety | C1 | Correctness of outputs |
|
||||
| P6 Crypto Correctness | C1 | Crypto failures = correctness failures |
|
||||
| P7 Auditability | C7 | Observability of security events |
|
||||
| P8 Fail Securely | C1 | Correctness of failure modes |
|
||||
| P9 Secret Hygiene | C6, C7 | Composability of secrets; observability |
|
||||
| P10 Surface Minimization | C3, C8 | Simplicity; economy of code |
|
||||
|
||||
## Data
|
||||
|
||||
| Data Principle | Core | Why |
|
||||
|---------------------------|------|---------------------------------------|
|
||||
| P1 Truth | C1 | Schema must match domain |
|
||||
| P2 Normalization | C1, C3 | Correctness without duplication; simplicity |
|
||||
| P3 Invariants in Schema | C1 | Correctness enforced |
|
||||
| P4 Migration Safety | C5 | Reversibility of changes |
|
||||
| P5 Indexing with Intent | C7 | Observability of query patterns |
|
||||
| P6 Naming Consistency | C2 | Direct derivation |
|
||||
| P7 Type Fidelity | C1, C2 | Correctness; clarity |
|
||||
| P8 Lifecycle Awareness | C1 | Correctness includes lifecycle |
|
||||
| P9 Referential Integrity | C1 | Correctness of relationships |
|
||||
| P10 Performance Awareness | C1 | Correctness includes cost |
|
||||
|
||||
## Testing
|
||||
|
||||
| Testing Principle | Core | Why |
|
||||
|---------------------------|------|---------------------------------------|
|
||||
| P1 Tests as Specification | C2 | Tests document intent |
|
||||
| P2 Independence | C4 | Locality of test execution |
|
||||
| P3 Determinism | C1 | Correctness is reproducible |
|
||||
| P4 Fast Feedback | C3, C8 | Simplicity; economy of time |
|
||||
| P5 Coverage of Behavior | C1 | Correctness of behavior |
|
||||
| P6 Failure Specificity | C7 | Observability of failures |
|
||||
| P7 Realism | C1 | Correctness in real conditions |
|
||||
| P8 Maintainability | C3, C8 | Simplicity; economy of maintenance |
|
||||
| P9 Edge Case Coverage | C1 | Correctness at boundaries |
|
||||
| P10 No Test Theater | C1, C8 | Correctness; economy of test code |
|
||||
|
||||
## Performance
|
||||
|
||||
| Perf Principle | Core | Why |
|
||||
|---------------------------|------|---------------------------------------|
|
||||
| P1 Measure First | C7 | Observability precedes optimization |
|
||||
| P2 Critical Path Focus | C3 | Simplicity of scope |
|
||||
| P3 Complexity Awareness | C1 | Correctness includes cost |
|
||||
| P4 Resource Bounds | C1, C8 | Correctness; economy of resources |
|
||||
| P5 Caching with Intent | C3, C6 | Simplicity; composability |
|
||||
| P6 Lazy by Default | C8 | Economy of computation |
|
||||
| P7 Async When Independent | C3 | Simplicity of flow |
|
||||
| P8 Budget Discipline | C8 | Economy of resources |
|
||||
| P9 Perceived Performance | C1 | Correctness from user's view |
|
||||
| P10 Regression Prevention | C7 | Observability of regressions |
|
||||
|
||||
## Observability
|
||||
|
||||
| Obs Principle | Core | Why |
|
||||
|---------------------------|------|---------------------------------------|
|
||||
| P1 Structured by Default | C2, C7 | Clarity; observability |
|
||||
| P2 Correlation | C7 | Direct derivation |
|
||||
| P3 Sufficient Context | C7, C2 | Observability; clarity |
|
||||
| P4 Cardinality Discipline | C8 | Economy of storage/cost |
|
||||
| P5 Sampling with Intent | C8 | Economy of observability budget |
|
||||
| P6 No Secrets in Obs | C1, C6 | Correctness; composability |
|
||||
| P7 Actionable Alerts | C7, C8 | Observability; economy of attention |
|
||||
| P8 SLI/SLO Awareness | C1 | Correctness defined |
|
||||
| P9 Cost Awareness | C8 | Direct derivation |
|
||||
| P10 Debuggability | C2, C7 | Clarity; observability |
|
||||
|
||||
## Errors
|
||||
|
||||
| Error Principle | Core | Why |
|
||||
|---------------------------|------|---------------------------------------|
|
||||
| P1 Errors are Data | C2, C7 | Clarity; observability |
|
||||
| P2 Fail Loudly | C7 | Direct derivation |
|
||||
| P3 Fail Specifically | C2, C7 | Clarity; observability |
|
||||
| P4 Preserve Context | C7 | Direct derivation |
|
||||
| P5 Recoverable When Possible | C1, C5 | Correctness; reversibility |
|
||||
| P6 Unrecoverable Means Stop | C1 | Correctness of failure mode |
|
||||
| P7 Errors are Boundaries | C6 | Composability |
|
||||
| P8 User-Facing Errors are UX | C2 | Clarity for users |
|
||||
| P9 Errors are Logged | C7 | Direct derivation |
|
||||
| P10 Errors Don't Lie | C1, C2 | Correctness; clarity |
|
||||
|
||||
## Documentation
|
||||
|
||||
| Doc Principle | Core | Why |
|
||||
|---------------------------|------|---------------------------------------|
|
||||
| P1 Documentation is Code | C3, C8 | Simplicity; economy of process |
|
||||
| P2 Audience Awareness | C2 | Direct derivation |
|
||||
| P3 Examples are Mandatory | C2 | Direct derivation |
|
||||
| P4 Currency | C1 | Correctness of docs |
|
||||
| P5 Discoverability | C2 | Direct derivation |
|
||||
| P6 Conciseness | C3, C2 | Simplicity; clarity |
|
||||
| P7 Structure | C2 | Direct derivation |
|
||||
| P8 Why Over What | C2 | Direct derivation |
|
||||
| P9 Living Documents | C1, C5 | Correctness; reversibility |
|
||||
| P10 Public by Default | C6 | Composability |
|
||||
|
||||
## Concurrency
|
||||
|
||||
| Concurrency Principle | Core | Why |
|
||||
|---------------------------|------|---------------------------------------|
|
||||
| P1 Immutability by Default | C1, C3 | Correctness; simplicity |
|
||||
| P2 Single Responsibility | C3, C6 | Simplicity; composability |
|
||||
| P3 Boundaries are Locks | C4, C6 | Locality; composability |
|
||||
| P4 Determinism Over Speed | C1 | Correctness precedes performance |
|
||||
| P5 Lock Minimization | C3 | Simplicity |
|
||||
| P6 No Silent Races | C7 | Observability of races |
|
||||
| P7 Cancellation Support | C5 | Reversibility of work |
|
||||
| P8 Timeout Discipline | C1 | Correctness of blocking |
|
||||
| P9 Bounded Queues | C1, C8 | Correctness; economy of memory |
|
||||
| P10 Test for Races | C1 | Correctness verification |
|
||||
|
||||
## DevOps
|
||||
|
||||
| DevOps Principle | Core | Why |
|
||||
|---------------------------|------|---------------------------------------|
|
||||
| P1 Reproducibility | C1 | Correctness of environments |
|
||||
| P2 Automation | C3, C8 | Simplicity; economy of effort |
|
||||
| P3 Observability | C7 | Direct derivation |
|
||||
| P4 Rollback First | C5 | Direct derivation |
|
||||
| P5 Progressive Delivery | C5, C1 | Reversibility; correctness gradually |
|
||||
| P6 Configuration as Code | C1, C3 | Correctness; simplicity |
|
||||
| P7 Immutability | C1 | Correctness of deploys |
|
||||
| P8 Security at Every Layer | C1 | Correctness includes security |
|
||||
| P9 Documentation in Pipeline | C2 | Clarity of operations |
|
||||
| P10 Failure as Expected | C1, C7 | Correctness; observability |
|
||||
@@ -0,0 +1,135 @@
|
||||
# Agent Pre-Completion Checklist
|
||||
|
||||
> Every AI agent runs this checklist before completing a task. If any item fails, fix it before finishing. This is the gate between "the code is written" and "the task is done."
|
||||
|
||||
## How to Use This
|
||||
|
||||
1. Read the relevant `domains/<x>/first-principles.md` before starting the task.
|
||||
2. Implement the task.
|
||||
3. Run this checklist. Every item must pass (or be explicitly justified).
|
||||
4. If an item fails, fix it. Do not "skip" without a written reason.
|
||||
|
||||
## Core Principles Checklist (C1–C8)
|
||||
|
||||
### C1 Correctness
|
||||
- [ ] Does the code do what the task asked, completely?
|
||||
- [ ] Does it handle the specified edge cases? (nulls, empties, max, min)
|
||||
- [ ] Does it handle the failure cases? (errors, timeouts, invalid input)
|
||||
- [ ] Is there a test that would fail if the code were wrong?
|
||||
|
||||
### C2 Clarity
|
||||
- [ ] Can a stranger read this and understand it without asking you?
|
||||
- [ ] Are names intent-revealing? (No `data`, `temp`, `x`, `doStuff`)
|
||||
- [ ] Do comments explain *why*, not *what*?
|
||||
- [ ] Is the structure scannable? (Short functions, clear sections)
|
||||
|
||||
### C3 Simplicity
|
||||
- [ ] Is this the simplest solution that is complete?
|
||||
- [ ] Is there dead code? (Unreachable branches, unused variables)
|
||||
- [ ] Is there premature abstraction? (An interface with one implementation)
|
||||
- [ ] Could 50 lines do what 200 lines do?
|
||||
|
||||
### C4 Locality
|
||||
- [ ] Does related logic live together?
|
||||
- [ ] Are side effects near their causes?
|
||||
- [ ] Does a change to this feature require touching distant files?
|
||||
|
||||
### C5 Reversibility
|
||||
- [ ] Is this change undoable? (migration has a `down`, deploy has a rollback)
|
||||
- [ ] Did I avoid irreversible actions without explicit confirmation?
|
||||
- [ ] Is state recoverable? (Can the user get back to where they were?)
|
||||
|
||||
### C6 Composability
|
||||
- [ ] Does this component/function do one thing?
|
||||
- [ ] Is the boundary (props/args/return) explicit and typed?
|
||||
- [ ] Can this be reused in a new context without modification?
|
||||
|
||||
### C7 Observability
|
||||
- [ ] Are there logs for significant events?
|
||||
- [ ] Do errors carry enough context to debug? (request ID, user, action)
|
||||
- [ ] Are there metrics for the operation? (count, latency)
|
||||
- [ ] Are there no secrets in logs?
|
||||
|
||||
### C8 Economy
|
||||
- [ ] Is memory bounded? (No unbounded growth, no loading everything)
|
||||
- [ ] Is time bounded? (No N+1, no blocking without timeout)
|
||||
- [ ] Are resources released? (file handles, connections, locks)
|
||||
|
||||
## Domain-Specific Triggers
|
||||
|
||||
If the task touches a domain, run that domain's checklist:
|
||||
|
||||
### If UI/UX (see `domains/uiux/`)
|
||||
- [ ] Every interactive element is keyboard-reachable
|
||||
- [ ] Every image has alt text (or marked decorative)
|
||||
- [ ] Every form control has a label
|
||||
- [ ] Focus is visible
|
||||
- [ ] No color-only information
|
||||
- [ ] Components use design tokens, not raw values
|
||||
|
||||
### If API (see `domains/api/`)
|
||||
- [ ] Endpoints are nouns, plural, lowercase-hyphenated
|
||||
- [ ] Status codes are correct (200/201/204/4xx/5xx per semantics)
|
||||
- [ ] Errors are structured (code, message, request_id)
|
||||
- [ ] Input is validated against a schema
|
||||
- [ ] Auth is required by default
|
||||
|
||||
### If Security (see `domains/security/`)
|
||||
- [ ] No secrets in code, logs, URLs, or error messages
|
||||
- [ ] Input is validated at the boundary
|
||||
- [ ] Output is encoded for its context
|
||||
- [ ] Crypto uses vetted libraries (no MD5/SHA1 for security)
|
||||
- [ ] Authorization is checked, not assumed
|
||||
|
||||
### If Data (see `domains/data/`)
|
||||
- [ ] Schema reflects the domain (not the application)
|
||||
- [ ] Constraints are in the schema (NOT NULL, UNIQUE, FK)
|
||||
- [ ] Migration has an `up` and a `down`
|
||||
- [ ] Types are domain-accurate (UUID, TIMESTAMPTZ, DECIMAL for money)
|
||||
- [ ] No `SELECT *`; no N+1
|
||||
|
||||
### If Testing (see `domains/testing/`)
|
||||
- [ ] Tests are independent (order doesn't matter)
|
||||
- [ ] Tests are deterministic (no `Date.now()`, no `random()`)
|
||||
- [ ] Edge cases are covered (empty, single, max, invalid)
|
||||
- [ ] A failing test names the problem specifically
|
||||
|
||||
### If Performance (see `domains/performance/`)
|
||||
- [ ] No unbounded operations (loops, allocations, queries)
|
||||
- [ ] No N+1 queries
|
||||
- [ ] Every external call has a timeout
|
||||
- [ ] Caches have invalidation strategies
|
||||
|
||||
### If Observability (see `domains/observability/`)
|
||||
- [ ] Logs are structured (JSON, fields)
|
||||
- [ ] Every request has a correlation ID
|
||||
- [ ] No high-cardinality labels in metrics
|
||||
- [ ] Alerts have runbooks
|
||||
|
||||
### If Errors (see `domains/errors/`)
|
||||
- [ ] Errors are not swallowed silently
|
||||
- [ ] Errors are specific (not generic "something went wrong")
|
||||
- [ ] Errors preserve context (where, when, why, what)
|
||||
- [ ] Recovery is attempted when possible; fail fast when not
|
||||
|
||||
### If Concurrency (see `domains/concurrency/`)
|
||||
- [ ] Shared state is minimized; immutability preferred
|
||||
- [ ] Locks are minimal in scope
|
||||
- [ ] Queues are bounded
|
||||
- [ ] Every blocking call has a timeout
|
||||
- [ ] Cancellation is supported
|
||||
|
||||
### If DevOps (see `domains/devops/`)
|
||||
- [ ] The pipeline is the process (no manual steps)
|
||||
- [ ] Rollback path is known
|
||||
- [ ] Config is in code, not on the server
|
||||
- [ ] Environments are parity (dev = prod modulo data)
|
||||
|
||||
## Final Gate
|
||||
|
||||
- [ ] Have I read the relevant domain's first-principles?
|
||||
- [ ] Have I run the domain-specific checklist?
|
||||
- [ ] Have I run the core checklist?
|
||||
- [ ] Are all failures either fixed or explicitly justified in the task notes?
|
||||
|
||||
If any unchecked item is not justified, the task is not complete. Do not mark done.
|
||||
@@ -0,0 +1,132 @@
|
||||
# Anti-Patterns
|
||||
|
||||
> A catalog of violations. Each entry names the principle it breaches. Use this to recognize and reject patterns on sight.
|
||||
|
||||
## How to Use
|
||||
|
||||
When you see a pattern listed here, it is a defect. Cite the principle it violates and require a fix. These are not "to be reviewed later"; they are rejected on sight.
|
||||
|
||||
## Core Anti-Patterns (C1–C8)
|
||||
|
||||
| Anti-Pattern | Breaches | Why |
|
||||
|--------------|----------|-----|
|
||||
| Code that "mostly works" | C1 Correctness | Mostly correct is incorrect |
|
||||
| `function doStuff()` | C2 Clarity | Name reveals nothing |
|
||||
| A 500-line function | C3 Simplicity | Complexity is a liability |
|
||||
| Config in a distant repo, read silently | C4 Locality | Coupling you cannot see |
|
||||
| A migration with no `down` | C5 Reversibility | Irreversible bet |
|
||||
| A component reading global state implicitly | C6 Composability | Hidden dependency |
|
||||
| A service with no logs | C7 Observability | Cannot debug what you cannot see |
|
||||
| Loading all records into memory | C8 Economy | Unbounded = OOM |
|
||||
|
||||
## Domain Anti-Patterns
|
||||
|
||||
### UI/UX
|
||||
| Anti-Pattern | Breaches | Why |
|
||||
|--------------|----------|-----|
|
||||
| Image without `alt` | P2 Accessibility | Disqualifying |
|
||||
| "Delete" with no confirmation | P5 Forgiveness | Irreversible surprise |
|
||||
| `color: #3b82f6` in a component | P8 Consistency (via tokens) | Bypasses design system |
|
||||
| "Submit" on a delete button | P3 Clarity | Wrong verb |
|
||||
| Layout shift on image load | P7 Hierarchy / CLS | Visual instability |
|
||||
|
||||
### API
|
||||
| Anti-Pattern | Breaches | Why |
|
||||
|--------------|----------|-----|
|
||||
| `/getUsers` (verb in URL) | P2 Clarity | Resources are nouns |
|
||||
| 200 with an error body | P9 Error Transparency | Status code lies |
|
||||
| 500 with a stack trace | P8 Security, P9 | Information leak |
|
||||
| No `Idempotency-Key` on a POST | P6 Idempotency | Retry is unsafe |
|
||||
| 10MB response by default | P7 Performance | Unbounded payload |
|
||||
|
||||
### Security
|
||||
| Anti-Pattern | Breaches | Why |
|
||||
|--------------|----------|-----|
|
||||
| `eval()` of any string | P4, P5 | Code injection |
|
||||
| Hardcoded API key in source | P9 Secret Hygiene | Committed secret |
|
||||
| `catch (e) {}` (swallow) | P7, P8 | Silent failure, fail-open |
|
||||
| `md5` for password hashing | P6 Crypto | Broken primitive |
|
||||
| Open CORS `*` in production | P1, P10 | Zero trust violated |
|
||||
| `chmod 777` | P2 Least Privilege | Maximum privilege |
|
||||
| Logging the request body | P9 Secret Hygiene | Token leak |
|
||||
|
||||
### Data
|
||||
| Anti-Pattern | Breaches | Why |
|
||||
|--------------|----------|-----|
|
||||
| `VARCHAR` for a UUID | P7 Type Fidelity | Wrong type |
|
||||
| No FOREIGN KEY | P3, P9 | Unenforced relationship |
|
||||
| `FLOAT` for money | P7, P1 | Floating point error |
|
||||
| `is_deleted` without filtering | P8 Lifecycle | Soft-delete leak |
|
||||
| `SELECT *` | P10 Performance | Unbounded columns |
|
||||
|
||||
### Testing
|
||||
| Anti-Pattern | Breaches | Why |
|
||||
|--------------|----------|-----|
|
||||
| Test that cannot fail | P10 No Test Theater | Not a test |
|
||||
| `Date.now()` in a fixture | P3 Determinism | Flaky |
|
||||
| Shared fixture mutated across tests | P2 Independence | Order-dependent |
|
||||
| 500 e2e tests, 50 unit | P4 Fast Feedback | Inverted pyramid |
|
||||
| `name: "test"` fixture | P7 Realism | Hides bugs |
|
||||
|
||||
### Performance
|
||||
| Anti-Pattern | Breaches | Why |
|
||||
|--------------|----------|-----|
|
||||
| N+1 query in a loop | P3 Complexity | O(N) queries |
|
||||
| No timeout on HTTP call | P4, P8 (concurrency) | Hang forever |
|
||||
| Cache with no invalidation | P5 Caching | Stale forever |
|
||||
| Unbounded in-memory sort | P4 Resource Bounds | OOM |
|
||||
| Optimization without measurement | P1 Measure First | Guesswork |
|
||||
|
||||
### Observability
|
||||
| Anti-Pattern | Breaches | Why |
|
||||
|--------------|----------|-----|
|
||||
| `console.log("here")` | P1 Structured | Not queryable |
|
||||
| `user_id` as a metric label | P4 Cardinality | Unbounded bill |
|
||||
| Average latency only | P8 SLO | Hides the tail |
|
||||
| No `trace_id` propagation | P2 Correlation | Cannot trace |
|
||||
| Logs without `request_id` | P3 Context | No correlation |
|
||||
|
||||
### Errors
|
||||
| Anti-Pattern | Breaches | Why |
|
||||
|--------------|----------|-----|
|
||||
| `catch (e) { return null }` | P2 Fail Loudly | Silent failure |
|
||||
| `throw new Error("error")` | P3 Fail Specifically | Generic |
|
||||
| `return null` for "not found" | P1 Errors are Data | Conflates absence with error |
|
||||
| Retry without backoff | P5, P8 | Retry storm |
|
||||
| `throw` in a recovery path | P6 | Fail fast in wrong place |
|
||||
|
||||
### Documentation
|
||||
| Anti-Pattern | Breaches | Why |
|
||||
|--------------|----------|-----|
|
||||
| Doc with no examples | P3 Examples | Incomplete |
|
||||
| Stale doc (wrong, not updated) | P4 Currency | Worse than no doc |
|
||||
| Unlisted doc (not in MANIFEST) | (framework rule) | Not part of framework |
|
||||
| No audience statement | P2 Audience | Who is this for? |
|
||||
|
||||
### Concurrency
|
||||
| Anti-Pattern | Breaches | Why |
|
||||
|--------------|----------|-----|
|
||||
| Shared mutable state, no lock | P1 Immutability | Race condition |
|
||||
| Unbounded queue | P9 Bounded Queues | OOM |
|
||||
| `channel.send()` with no timeout | P8 Timeout | Hang |
|
||||
| Mutex held across I/O | P3 Lock Scope | Lock too long |
|
||||
| Spawned work with no cancellation | P7 Cancellation | Orphaned work |
|
||||
|
||||
### DevOps
|
||||
| Anti-Pattern | Breaches | Why |
|
||||
|--------------|----------|-----|
|
||||
| Manual deploy script | P2 Automation | Not repeatable |
|
||||
| No rollback path | P4 Rollback | Irreversible deploy |
|
||||
| Big-bang deploy | P5 Progressive | All-or-nothing |
|
||||
| Rebuild per environment | P7 Immutability | Different artifacts |
|
||||
| Snowflake server | P1, P6 | Not reproducible |
|
||||
|
||||
## Cross-Cutting Anti-Patterns
|
||||
|
||||
| Anti-Pattern | Breaches | Why |
|
||||
|--------------|----------|-----|
|
||||
| "I'll add tests later" | C1 (no proof of correctness) | Later never comes |
|
||||
| "It's just a prototype" | C5 (irreversible by default) | Prototypes go to prod |
|
||||
| Copy-paste code | C6 (no composition) | Bug duplicated |
|
||||
| God object | C3, C6 | One thing, many things |
|
||||
| Leaky abstraction | C6, C2 | Hidden coupling |
|
||||
@@ -0,0 +1,82 @@
|
||||
# Peer Review Checklist
|
||||
|
||||
> For human reviewers. Run this when reviewing a PR or a change. Complements `agent-checklist.md` (which the author ran before finishing).
|
||||
|
||||
## Purpose
|
||||
|
||||
A peer review is a second set of eyes on correctness, clarity, and completeness. It is not a gatekeeping ritual; it is a quality multiplier. The author ran the agent checklist; the reviewer checks what the author could not see.
|
||||
|
||||
## The Review
|
||||
|
||||
### Understanding (read first, judge never)
|
||||
- [ ] Read the PR description. What is the change trying to do?
|
||||
- [ ] Read the linked issue/spec. Does the change address it?
|
||||
- [ ] Read the changed files in order. Do not jump to judgment.
|
||||
|
||||
### Correctness (C1)
|
||||
- [ ] Does the change do what it claims?
|
||||
- [ ] Are there edge cases the author missed? (Comment, don't fix in review)
|
||||
- [ ] Are there failure cases unhandled?
|
||||
- [ ] Are the tests testing the right thing? (A test that cannot fail is theater)
|
||||
- [ ] Would this code fail in production? (Different from "does it pass CI")
|
||||
|
||||
### Clarity (C2)
|
||||
- [ ] Can you understand the change without asking the author?
|
||||
- [ ] Are names intent-revealing?
|
||||
- [ ] Is there a comment that explains *why* where the *why* is non-obvious?
|
||||
- [ ] Is the diff minimal? (Unrelated changes are review noise)
|
||||
|
||||
### Simplicity (C3)
|
||||
- [ ] Is there dead code introduced?
|
||||
- [ ] Is there premature abstraction?
|
||||
- [ ] Could the change be simpler? (Suggest, don't block unless egregious)
|
||||
- [ ] Is there a simpler approach the author may not have considered?
|
||||
|
||||
### Locality (C4)
|
||||
- [ ] Are related changes grouped?
|
||||
- [ ] Are unrelated changes separated (different PRs)?
|
||||
- [ ] Does a change require touching distant files unnecessarily?
|
||||
|
||||
### Reversibility (C5)
|
||||
- [ ] Is the change undoable?
|
||||
- [ ] Are there migrations? Do they have rollbacks?
|
||||
- [ ] Are there breaking changes? If so, is there a deprecation path?
|
||||
|
||||
### Composability (C6)
|
||||
- [ ] Does the change respect existing boundaries?
|
||||
- [ ] Does it introduce coupling that should be an interface?
|
||||
- [ ] Is the new code reusable, or one-off?
|
||||
|
||||
### Observability (C7)
|
||||
- [ ] Are there logs/metrics for the new behavior?
|
||||
- [ ] Are errors structured and traceable?
|
||||
- [ ] Are there no secrets in logs?
|
||||
|
||||
### Economy (C8)
|
||||
- [ ] Are there unbounded operations?
|
||||
- [ ] Is memory/time bounded?
|
||||
- [ ] Is the cost proportional to the need?
|
||||
|
||||
## Domain-Specific (if applicable)
|
||||
|
||||
Run the relevant domain section from `agent-checklist.md` (UI/UX, API, Security, Data, Testing, Performance, Observability, Errors, Concurrency, DevOps). The author ran it; the reviewer verifies.
|
||||
|
||||
## Review Etiquette
|
||||
|
||||
- **Comment, don't command.** "This could be X" not "Change this to X."
|
||||
- **Distinguish blocking from suggestions.** "Blocking: correctness bug. Suggestion: naming."
|
||||
- **Praise good code.** Reviews are not just for finding problems.
|
||||
- **Don't review style the linter should catch.** Fix the linter, not the PR.
|
||||
- **Ask questions.** "Why this approach?" often reveals more than "This is wrong."
|
||||
|
||||
## Approving
|
||||
|
||||
- Approve when: the change is correct, clear, and complete. Minor suggestions can be left for the author.
|
||||
- Request changes when: there is a correctness bug, a missing test, or a clarity problem that blocks understanding.
|
||||
- Reject when: the change should not exist (wrong direction, duplicate, scope-violating).
|
||||
|
||||
## What This Checklist is Not
|
||||
|
||||
- Not a style guide. Style is automated (linter, formatter).
|
||||
- Not a gatekeeping tool. The goal is quality, not perfection.
|
||||
- Not a substitute for the author's own checklist. The author runs `agent-checklist.md` first; this is the second pass.
|
||||
Reference in New Issue
Block a user