4 Commits

Author SHA1 Message Date
Jon Chery c3226192f5 docs(P02): complete kubernetes phase
---ci---
project: atelier
phase: 2
milestone: v0.2
status: complete
requirements:
  covered: [ATELIER-41, ATELIER-42, ATELIER-43, ATELIER-44, ATELIER-45, ATELIER-46, ATELIER-47]
  partial: []
---/ci---
2026-08-05 02:09:55 +00:00
Jon Chery 2b602fe49b docs(P01): update REQUIREMENTS + ROADMAP status — ATELIER-36..40 covered
---ci---
project: atelier
phase: 1
milestone: v0.2
status: complete
---/ci---
2026-08-05 02:07:12 +00:00
Jon Chery b997bd63b1 docs(P01): complete infrastructure-as-code phase
---ci---
project: atelier
phase: 1
milestone: v0.2
status: complete
requirements:
  covered: [ATELIER-36, ATELIER-37, ATELIER-38, ATELIER-39, ATELIER-40]
  partial: []
---/ci---
2026-08-05 02:04:11 +00:00
Jon Chery 40e61e6b7b docs(P00): complete pre-execution phase
---ci---
project: atelier
phase: 0
milestone: v0.2
status: complete
requirements:
  covered: [ATELIER-governance]
  partial: []
---/ci---
2026-08-05 02:00:24 +00:00
20 changed files with 42 additions and 665 deletions
+3 -3
View File
@@ -1,10 +1,10 @@
{
"phase": 5,
"phase": 2,
"stage": "execute",
"milestone": "v0.2",
"phase_role": "final",
"phase_role": "execution",
"project": "atelier",
"attempts": 0,
"updated_at": "2026-08-05T02:40:00Z",
"updated_at": "2026-08-05T02:10:00Z",
"milestone_complete": false
}
+17 -17
View File
@@ -69,25 +69,25 @@ All 35 requirements covered. 8 core principles, 11 domains, 110 domain principle
| ATELIER-38 | `domains/infrastructure-as-code/opentofu.md` — OpenTofu derived doc | P1 | 1 | covered |
| ATELIER-39 | `domains/infrastructure-as-code/state.md` — IaC state management derived doc | P1 | 1 | covered |
| ATELIER-40 | `domains/infrastructure-as-code/modules.md` — IaC module composition derived doc | P1 | 1 | covered |
| ATELIER-41 | `domains/kubernetes/first-principles.md` — 10 k8s principles (P1P10) | P0 | 2 | covered |
| ATELIER-42 | `domains/kubernetes/workloads.md` — workloads derived doc | P1 | 2 | covered |
| ATELIER-43 | `domains/kubernetes/networking.md` — networking derived doc | P1 | 2 | covered |
| ATELIER-44 | `domains/kubernetes/storage.md` — storage derived doc | P1 | 2 | covered |
| ATELIER-45 | `domains/kubernetes/rbac.md` — RBAC derived doc incl. Pod Security Standards/Admission (cross-link security/authorization) | P1 | 2 | covered |
| ATELIER-46 | `domains/kubernetes/helm.md` — Helm derived doc (with Helm vs Kustomize decision matrix) | P1 | 2 | covered |
| ATELIER-47 | `domains/kubernetes/kustomize.md` — Kustomize derived doc (with Helm vs Kustomize decision matrix) | P1 | 2 | covered |
| 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 | covered |
| 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 | covered |
| ATELIER-50 | Extend `review/agent-checklist.md` with IaC + k8s trigger sections | P1 | 3 | covered |
| ATELIER-51 | Extend `review/anti-patterns.md` with IaC + k8s violations incl. orphaned P-rule + deployable example artifact | P1 | 3 | covered |
| ATELIER-52 | Update `MANIFEST.md` to list all new v0.2 documents (manifest authoritative) | P0 | 3 | covered |
| ATELIER-53 | `examples/good/terraform-module.md` — good IaC example (markdown with fenced HCL only; no standalone .tf) | P2 | 4 | covered |
| ATELIER-54 | `examples/good/k8s-deployment.md` — good k8s example (markdown with fenced YAML only; no standalone .yaml) | P2 | 4 | covered |
| 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 | covered |
| 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 | covered |
| ATELIER-41 | `domains/kubernetes/first-principles.md` — 10 k8s principles (P1P10) | 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 | covered |
| ATELIER-59 | Extend `review/peer-review-checklist.md` with IaC + k8s sections (parity with agent-checklist) | P1 | 3 | pending |
## v0.2 Traceability Matrix
-68
View File
@@ -1,68 +0,0 @@
# Atelier — v0.2 Final Review + Audit (P5)
> Generated during final phase P5 (REVIEW + AUDIT) of milestone v0.2. Per run.md FINAL PHASE.
## Review (multi-persona, ci-code-reviewer)
**Scope:** all v0.2 changes (32 files, +1787/-34 lines), all commits `main..atelier/phase/05-final-review-ship`.
### P0 checks (all pass)
1. ✅ IaC first-principles: exactly 10 P-rules (P1P10)
2. ✅ K8s first-principles: exactly 10 P-rules (P1P10)
3. ✅ Matrix IaC section: 10 rows, each ≥1 valid C-rule, no orphans
4. ✅ Matrix K8s section: 10 rows, each ≥1 valid C-rule, no orphans
5. ✅ Every P-rule name in first-principles matches its matrix row
6. ✅ All required files exist (ATELIER-36..56, 59): 2 first-principles + 4 IaC derived + 6 k8s derived + 4 examples + matrix/manifest/review extensions
7. ✅ MANIFEST lists both new domains with all derived docs — no manifest drift
8. ✅ No standalone .tf/.yaml/.yml files — "no runtime code" constraint preserved (all code is fenced in .md)
9. ✅ No hardcoded real secrets — bad examples use AWS doc placeholders and `hunter2`; good examples use `registry.example.com` + Secret refs
10. ✅ anti-patterns.md covers secrets-in-HCL and cluster-admin
11. ✅ rbac.md covers Pod Security Standards + Admission
12. ✅ All cross-link targets resolve to MANIFEST-listed docs
**Verdict: PASS — No P0 issues.**
### P1+ issues (flagged, then fixed in this phase per run.md)
| ID | Severity | Issue | Fix applied |
|----|----------|-------|-------------|
| REV-1 | P1 | 5 derived docs missing cross-domain links (terraform, state, modules, workloads, networking) | Added cross-links to `domains/security/secrets.md`, `domains/devops/first-principles.md`, `domains/observability/metrics.md`, `domains/security/authorization.md` |
| REV-2 | P2 | networking.md "Dual-Stack (P4 Locality)" — wrong P-rule label | Corrected to "(C4 Locality)" |
| REV-3 | P2 | domain-coverage.md "Concurrency broadest (7)" stale — IaC + k8s also 7 | Updated to "Concurrency, IaC, Kubernetes tied (7 each)" |
All P1+ issues fixed in commit `87daca3`. No loop back to EXECUTE (per run.md final-phase rule).
## Audit (lead-developer)
### 1. Reconstruction test
- git log `main..atelier/phase/05-final-review-ship` shows 10 v0.2 commits (P00 complete, P01 execute/verify/complete/status, P02, P03, P04, P05 review fix).
- REQUIREMENTS.md status (covered) matches shipped phases: ATELIER-36..56, 59 all `covered`; ATELIER-57, 58 `pending` (final phase, completed at ship).
- ROADMAP.md phase statuses match: P0P4 `complete`, P5 `pending` (→ complete at ship).
- ✅ Reconstruction passes.
### 2. Branch hygiene
- Active branches: `atelier/milestone/v0.2-iac-k8s`, `atelier/phase/05-final-review-ship`.
- All execution phase branches (0004) deleted after ship. ✅
### 3. Commit discipline
- All 10 v0.2 commits contain `---ci---` blocks with project, phase, milestone, status, requirements. ✅
### 4. File discipline
- `.ciagent/atelier/` contains all 8 required files: PROJECT, ROADMAP, REQUIREMENTS, ARCHITECTURE, PERSONAS, PLAN, RESEARCH, CLARIFY. ✅
- (v0.1 legacy AUDIT-P2.md, REVIEW-P7.md also present — not removed, harmless.)
### 5. Tag sequence
- v0.0.0v0.0.7 (milestone v0.1) → v0.1.0v0.1.4 (milestone v0.2 phases 04).
- All v0.1.x strictly > v0.0.7. All v0.1.x strictly increasing. ✅
- Final phase tag will be v0.1.5 (next patch, IS the v0.2 milestone release per NFR rule).
### 6. Manifest discipline
- All 12 new domain docs (infrastructure-as-code/*, kubernetes/*) listed in MANIFEST.md Domains table. ✅
- matrix, review (agent-checklist, peer-review-checklist, anti-patterns) all listed in Cross-Cutting. ✅
**Audit verdict: CLEAN — no critical issues.**
## Conclusion
Review PASS (no P0, all P1+ fixed). Audit CLEAN. Milestone v0.2 is ready to ship as v0.1.5.
+3 -3
View File
@@ -49,9 +49,9 @@ NFR milestone: no separate minor tag. The final patch (v0.0.7) IS the v0.1 deliv
|-------|------|------|--------|------------------|
| 0 | Pre-Execution | docs | complete | Spec, clarify, research, ideate, plan, PERSONAS.md (adds platform-engineer persona) |
| 1 | Infrastructure as Code Domain | docs | complete | domains/infrastructure-as-code/{first-principles, terraform, opentofu, state, modules}.md |
| 2 | Kubernetes Domain | docs | complete | domains/kubernetes/{first-principles, workloads, networking, storage, rbac, helm, kustomize}.md |
| 3 | Matrix + Review Integration | docs | complete | 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 | complete | 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 |
| 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
+3 -7
View File
@@ -34,15 +34,11 @@
| Documentation | ✓ | doc-templates |
| Concurrency | ✓ | patterns |
| DevOps | ✓ | ci-cd, environments |
| Infrastructure as Code | ✓ | terraform, opentofu, state, modules |
| Kubernetes | ✓ | workloads, networking, storage, rbac, helm, kustomize |
## Cross-Cutting
| Document | Purpose |
|-----------------------------------|----------------------------------|
| `matrix/principles-matrix.md` | Maps domain → core principles (13 domains, 130 P-rules post-v0.2) |
| `matrix/domain-coverage.md` | Maps core → domains; per-domain coverage |
| `review/agent-checklist.md` | Pre-completion agent checklist (incl. IaC + k8s triggers) |
| `review/peer-review-checklist.md` | Human peer-review checklist (incl. IaC + k8s sections) |
| `review/anti-patterns.md` | Catalog of violations (incl. IaC + k8s + chaos anti-patterns) |
| `matrix/principles-matrix.md` | Maps domain → core principles |
| `review/agent-checklist.md` | Pre-completion agent checklist |
| `review/anti-patterns.md` | Catalog of violations |
@@ -7,7 +7,6 @@
- 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.
- A versioned module is the IaC expression of `domains/devops/first-principles.md` P1 (Reproducibility) and P6 (Configuration as Code): a module pins a reusable, rebuildable pattern that any environment can call.
## Module Structure (P1 Declarative Intent, C2 Clarity)
+1 -1
View File
@@ -27,7 +27,7 @@
| 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. The state file itself is a secret-bearing artifact — treat it per `domains/security/secrets.md`: encrypt at rest, restrict access, never commit it.
- 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)
+1 -1
View File
@@ -43,7 +43,7 @@
## 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`). This is the IaC angle on `domains/security/secrets.md` — secret hygiene is non-tradeable.
- 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
-1
View File
@@ -49,7 +49,6 @@
- Use Kustomize when you patch existing manifests or keep env deltas in one repo. Use Helm when you distribute a reusable app or consume third-party charts.
- Mixing both is fine and common: Kustomize for the internal apps, Helm for the packaged parts. The decision is per-workload, not per-cluster.
- `commonLabels` is the kustomize-native enforcement of P3 (Labels Select); see `domains/devops/first-principles.md` P6 (Configuration as Code) for the upstream principle that the rendered manifest — not a console click — is the source of truth.
## What Violates Kustomize Discipline
+1 -2
View File
@@ -23,7 +23,6 @@
- A NetworkPolicy is a firewall rule for pods. Default-deny ingress; allow by namespace and pod selector.
- Without a default-deny NetworkPolicy, every pod can reach every other pod. In production, default-deny is the baseline; allows are the exceptions.
- NetworkPolicy is the network-layer expression of zero-trust authorization — see `domains/security/authorization.md`. RBAC (see `rbac.md`) governs the API; NetworkPolicy governs the network; together they bound blast radius (P6).
- NetworkPolicy is enforced by the CNI plugin (Calico, Cilium, etc.). A NetworkPolicy with no supporting CNI is a no-op. Verify the CNI enforces before relying on it.
## DNS (P3 Labels Select)
@@ -32,7 +31,7 @@
- Headless Services (`clusterIP: None`) resolve directly to pod IPs — use for StatefulSet peer discovery (`<statefulset>-0.<service>`).
- DNS is how workloads find each other without hardcoded IPs. Use the DNS name, not the ClusterIP.
## Dual-Stack (C4 Locality)
## Dual-Stack (P4 Locality)
- IPv4/IPv6 dual-stack is opt-in per cluster. Services can be single-stack or dual-stack per Service.
- Decide at cluster creation. Migrating a single-stack cluster to dual-stack is disruptive and rarely worth it.
-1
View File
@@ -39,7 +39,6 @@
- Every container in production has a CPU request, a memory request, and a memory limit. CPU limits are optional but recommended to bound noisy neighbours.
- QoS classes: `Guaranteed` (requests == limits), `Burstable` (requests < limits), `BestEffort` (no requests). `BestEffort` is first evicted under node pressure — never for prod.
- A workload without requests is an unbounded gamble on the scheduler. Set them.
- The rolling update + rollout history described below is the k8s expression of `domains/observability/metrics.md` for health and `domains/devops/first-principles.md` P5 (Progressive Delivery): the platform observes the rollout via probes and metrics and can stop or reverse it.
## What Violates Workload Discipline
-71
View File
@@ -1,71 +0,0 @@
# Bad Example: Bare Pod, No Resources
> A Kubernetes manifest that violates Atelier's Kubernetes principles. Each violation is cited.
## The Code
```yaml
apiVersion: v1
kind: Pod
metadata:
name: api
namespace: default
spec:
containers:
- name: api
image: api:latest # :latest, unversioned
ports:
- containerPort: 8080
env:
- name: DATABASE_URL
value: "postgres://admin:hunter2@db:5432/app" # secret in plaintext, in the manifest
```
The team applies it with `kubectl apply -f api-pod.yaml`. When the pod crashes, they `kubectl delete pod api && kubectl apply -f api-pod.yaml` to "restart" it. There are no probes, no resource requests, no RBAC, no NetworkPolicy.
## What Makes It Bad
### Bare Pod, No Controller (k8s P2 Pods are Mortal)
- A `kind: Pod` with no controller. When the node dies, the pod does not come back. When the team needs three replicas, they copy the YAML twice and rename it.
- The "restart" workflow (`delete pod && apply`) is manual recovery — exactly the manual-mutation anti-pattern from `domains/devops/`.
- **Fix:** use a `Deployment`. The controller replaces dead pods, scales, and rolls back. See `domains/kubernetes/workloads.md`.
### No Resource Requests (k8s P4 Requests and Limits are Contracts)
- The container has no `resources.requests` or `resources.limits`. It is `BestEffort` — first evicted under node pressure. The scheduler has no signal to place it well; it lands wherever there is room, then gets killed when the node is full.
- A workload without requests is an unbounded gamble on the scheduler.
- **Fix:** set CPU and memory requests on every prod container; set a memory limit; consider a CPU limit. See `domains/kubernetes/workloads.md`.
### No Probes (k8s P5 Probes Drive Health)
- No `readinessProbe` — the Service routes traffic to the pod before it is ready. Users see 502s during startup.
- No `livenessProbe` — a wedged container runs forever; no one notices until the outage.
- The platform cannot heal what it cannot see. A pod without probes is invisible to the controller's reconciliation.
- **Fix:** define readiness and liveness probes that check the workload's own health. See `domains/kubernetes/workloads.md`.
### `:latest` Image Tag (k8s P1 + IaC P5 Version Everything)
- `image: api:latest` is unversioned. Every `kubectl apply` pulls whatever is newest at that moment. Two pods "running the same manifest" run different images if `latest` moved between applies.
- Rollback is impossible — there is no version to roll back to.
- **Fix:** pin the image to a version or a digest: `image: registry.example.com/api:v1.4.2` or `image: registry.example.com/api@sha256:...`. See `domains/kubernetes/workloads.md` and `domains/infrastructure-as-code/terraform.md` (P5 Version Everything).
### Secret in Plaintext in the Manifest (k8s P9 Config and Secrets are Separate, IaC P10)
- `DATABASE_URL` with the password is in the manifest in plaintext. If the manifest is committed (it is), the secret is in git.
- Rotating the secret requires editing the manifest and re-applying — no separation of config from secret.
- **Fix:** put the URL in a `Secret` (created out-of-band or via a secrets tool) and reference it with `valueFrom.secretKeyRef`. The manifest contains the reference, not the value. See `domains/kubernetes/rbac.md` and `domains/security/secrets.md`.
### `default` Namespace (k8s P6 Namespaces Bound Blast Radius)
- The pod runs in `default`. There is no namespace boundary for quota, RBAC, or NetworkPolicy. Every other workload in `default` can reach it; an outage in one affects the namespace all share.
- **Fix:** give every prod workload a named namespace sized to its blast radius. `default` is for nothing in production. See `domains/kubernetes/networking.md` and `domains/kubernetes/workloads.md`.
### No RBAC, No NetworkPolicy (k8s P7 RBAC by Intent, P6 Namespaces Bound Blast)
- No `serviceAccountName` — the pod uses the `default` ServiceAccount, a shared identity.
- No `NetworkPolicy` — every pod in the cluster can reach `api`. The network is flat by default.
- **Fix:** a dedicated ServiceAccount with a least-privilege Role bound by intent. A default-deny NetworkPolicy with explicit allows. See `domains/kubernetes/rbac.md` and `domains/kubernetes/networking.md`.
## The Cascade
The violations compound. A bare pod with no probes crashes silently and is not restarted. `:latest` means the "restart" pulls a different image than the one that crashed. The plaintext secret in the manifest is now in git, so rotating it means editing the manifest and re-applying — and the new manifest still has no controller, so the next crash starts the manual loop again. `default` namespace means the blast radius is the whole cluster.
## Cross-Domain Links
- `domains/devops/P4 Rollback First` — with `:latest` and no controller, there is no rollback path.
- `domains/security/authorization` — the shared `default` ServiceAccount and flat network are the k8s expression of zero-trust violation.
- `domains/errors/P2 Fail Loudly` — a pod with no probes fails silently; the platform has no signal to act on.
-74
View File
@@ -1,74 +0,0 @@
# Bad Example: Unlocked Local State
> An IaC configuration that violates Atelier's Infrastructure as Code principles. Each violation is cited.
## The Code
```hcl
# main.tf — committed to the repo
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
# no version pin
}
}
}
provider "aws" {
region = "us-east-1"
access_key = "AKIAIOSFODNN7EXAMPLE" # committed
secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" # committed
}
resource "aws_s3_bucket" "logs" {
bucket = "app-logs-prod"
}
resource "aws_db_instance" "primary" {
identifier = "app-prod"
username = "admin"
password = "hunter2" # committed, in plaintext, in state
allocated_storage = 20
engine = "postgres"
instance_class = "db.t3.micro"
skip_final_snapshot = true
}
```
The team runs `terraform apply` from their laptops. The state file `terraform.tfstate` is committed alongside `main.tf` "so everyone is on the same page."
## What Makes It Bad
### Local State in a Shared Environment (IaC P8 Remote State with Locking)
- State is `terraform.tfstate` on each laptop. Two team members run `terraform apply` simultaneously; the second to finish silently overwrites the first's changes. There is no lock.
- The state file is committed to the repo. It contains the DB password in plaintext. It is a secret-bearing artifact in version control.
- **Fix:** use a remote backend with locking (S3 + DynamoDB, GCS, etc.). Never commit state. See `domains/infrastructure-as-code/state.md`.
### Hardcoded Secrets (IaC P10 Secrets Never in Code)
- `access_key` and `secret_key` are committed in `main.tf`. The DB `password` is committed and also written to state in plaintext.
- The secrets are now in the git history. Rotating them is not optional; the history must be scrubbed or the credentials rotated and the old ones revoked.
- **Fix:** credentials from environment, a secrets manager, or a `data` source (`aws_secretsmanager_secret_version`). Mark sensitive attributes `sensitive = true`. See `domains/security/secrets.md`.
### Unpinned Provider (IaC P5 Version Everything)
- The `aws` provider has no `version`. The next `terraform init` pulls whatever is latest — a different provider version can change resource behavior with no review.
- **Fix:** pin `version = "~> 5.0"`. Commit the lock file (`.terraform.lock.hcl`). See `domains/infrastructure-as-code/terraform.md`.
### Manual Drift, No Plan Review (IaC P4 Plan Before Apply, P9 Drift is Recoverable)
- The team applies from laptops with no `plan` review. When the DB password is wrong, someone SSHes in and changes it manually — drift that `plan` will later report as a surprise.
- Manual changes to managed resources are an incident, not a shortcut. Each one is a future `plan` diff that no one can explain.
- **Fix:** run `terraform plan` in CI; review the diff; `apply` from CI on merge. Treat every drift report as an incident to investigate. See `domains/infrastructure-as-code/state.md` (Drift and Reconciliation).
### No Module Composition (IaC P6 Modules Compose)
- The S3 bucket and DB instance are inline. When the team needs a second bucket, they copy-paste the block and rename it. The two copies drift over time.
- **Fix:** a versioned module for each reusable pattern. The difference is a variable, not a copy. See `domains/infrastructure-as-code/modules.md` (the module-vs-copy boundary).
## The Cascade
The violations compound. Unlocked local state lets two `apply` runs race. Committed secrets mean the race loser's changes — and the secrets — are in the repo. Manual drift hides the corruption until a `plan` surfaces a diff no one can explain. The unpinned provider means that diff might be the provider's fault, not the team's, and no one can tell which.
## Cross-Domain Links
- `domains/security/secrets.md` — secret hygiene is non-tradeable; this example violates it in three places.
- `domains/security/supply-chain.md` — committed credentials in git are a supply-chain incident.
- `domains/devops/P6 Configuration as Code` — config in the repo is correct; committed *state and secrets* is the violation.
-157
View File
@@ -1,157 +0,0 @@
# Good Example: Kubernetes Deployment
> A Kubernetes Deployment that follows Atelier's Kubernetes principles. Each aspect cites the principle it satisfies.
## The Deployment
A stateless web service deployed as a Deployment with probes, resource contracts, RBAC, and a rolling update strategy — the canonical "production workload" pattern.
### Manifest
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
namespace: api-prod
labels:
app: api
tier: web
spec:
replicas: 3
selector:
matchLabels:
app: api
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: api
tier: web
spec:
serviceAccountName: api
automountServiceAccountToken: false
containers:
- name: api
image: registry.example.com/api:v1.4.2 # pinned, not :latest
ports:
- containerPort: 8080
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
memory: 512Mi
readinessProbe:
httpGet:
path: /healthz/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
livenessProbe:
httpGet:
path: /healthz/live
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
env:
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: api-config
key: log_level
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: api-secrets
key: database_url
volumeMounts:
- name: config
mountPath: /etc/api
readOnly: true
volumes:
- name: config
configMap:
name: api-config
---
apiVersion: v1
kind: Service
metadata:
name: api
namespace: api-prod
spec:
selector:
app: api
ports:
- port: 80
targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-deny-ingress
namespace: api-prod
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
tier: edge
```
## What Makes It Good
### Controller, Not Bare Pod (k8s P2 Pods are Mortal)
- A `Deployment` manages the pods. If one dies, the controller replaces it. A bare pod has no recovery.
- See `domains/kubernetes/workloads.md`.
### Resource Contracts (k8s P4 Requests and Limits are Contracts)
- Every container has CPU and memory requests and a memory limit. The workload is `Burstable`, not `BestEffort` (first evicted under pressure).
- See `domains/kubernetes/workloads.md` for QoS classes.
### Probes (k8s P5 Probes Drive Health)
- `readinessProbe` gates traffic: a pod that is not ready is removed from the Service's endpoints.
- `livenessProbe` restarts a wedged container.
- The probes check the workload's own health (`/healthz/ready`, `/healthz/live`), not a dependency. A liveness probe that calls the database would cascade-restart on a DB blip.
- See `domains/kubernetes/workloads.md`.
### Image Pinning (k8s P1 + IaC P5 Version Everything)
- `image: registry.example.com/api:v1.4.2` — pinned to a version, not `:latest`. A pod restart pulls the same image it was built with.
- See `domains/infrastructure-as-code/terraform.md` and `domains/devops/P7 Immutability` for the immutability angle.
### RBAC (k8s P7 RBAC by Intent, Not Identity)
- `serviceAccountName: api` — the workload runs as a dedicated ServiceAccount, not the `default` shared identity.
- `automountServiceAccountToken: false` — the workload does not call the API, so it gets no token. See `domains/kubernetes/rbac.md`.
- A matching `Role` + `RoleBinding` (not shown) would grant `get, list, watch` on `configmaps` in this namespace — least privilege, scoped by intent.
### Config and Secrets Separate (k8s P9 Config and Secrets are Separate)
- `LOG_LEVEL` from a ConfigMap (non-sensitive). `DATABASE_URL` from a Secret (sensitive). Both injected at runtime; neither baked into the image.
- A configuration change does not require a rebuild. A secret rotation does not require an image redeploy.
- See `domains/kubernetes/rbac.md` and `domains/security/secrets.md`.
### Namespaces Bound Blast Radius (k8s P6 Namespaces Bound Blast Radius)
- The workload lives in `api-prod`, not `default`. The namespace is the unit of quota, RBAC, and NetworkPolicy. A problem in `api-prod` does not leak to other workloads.
- See `domains/kubernetes/networking.md`.
### NetworkPolicy Default-Deny (k8s P6, P7)
- The `NetworkPolicy` allows ingress only from the `edge` namespace. Without it, every pod in the cluster could reach `api`. Default-deny is the baseline; allows are the exceptions.
- See `domains/kubernetes/networking.md`.
### Roll Forward, Roll Back (k8s P10 Roll Forward Roll Back)
- `strategy: RollingUpdate` with `maxSurge: 1, maxUnavailable: 0` — the rollout adds a new pod before removing an old one. Availability is maintained.
- `kubectl rollout undo deployment/api` reverts to the previous ReplicaSet. The rollback is tested before it is needed.
- See `domains/kubernetes/workloads.md` and `domains/devops/P5 Progressive Delivery`.
### Cross-Domain Links
- `domains/devops/P4 Rollback First` — the rollout strategy makes the deploy reversible.
- `domains/security/authorization` — the ServiceAccount + Role model is the k8s expression of least-privilege authorization.
- `domains/observability/metrics` — the probes are the platform's observability into the workload's health; the workload's own metrics complete the picture.
-125
View File
@@ -1,125 +0,0 @@
# Good Example: Terraform Module
> A reusable Terraform module that follows Atelier's Infrastructure as Code principles. Each aspect cites the principle it satisfies.
## The Module
A versioned module that provisions an S3 bucket with logging, versioning, and encryption — the canonical "secure bucket" pattern, composed rather than copy-pasted.
### Consumer Call
```hcl
module "logs_bucket" {
source = "registry.example.com/infra/secure-bucket/aws"
version = "1.2.0"
name = "app-logs"
region = "us-east-1"
force_destroy = false
retention_days = 90
}
```
### Module Structure
```
secure-bucket/
├── main.tf # the resource
├── variables.tf # typed inputs
├── outputs.tf # the interface to consumers
├── versions.tf # provider pin
└── README.md # the module contract
```
### `versions.tf` (P5 Version Everything)
```hcl
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
```
### `variables.tf` (P1 Declarative Intent, C2 Clarity)
```hcl
variable "name" {
type = string
description = "Globally unique bucket name."
validation {
condition = can(regex("^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$", var.name))
error_message = "Bucket name must be lowercase, 3-63 chars, DNS-compatible."
}
}
variable "retention_days" {
type = number
default = 30
description = "S3 lifecycle transition age in days."
}
```
### `main.tf` (P1, P3 State is Truth, P10 Secrets Never in Code)
```hcl
resource "aws_s3_bucket" "this" {
bucket = var.name
}
resource "aws_s3_bucket_versioning" "this" {
bucket = aws_s3_bucket.this.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_server_side_encryption_configuration" "this" {
bucket = aws_s3_bucket.this.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
resource "aws_s3_bucket_lifecycle_configuration" "this" {
bucket = aws_s3_bucket.this.id
rule {
id = "retention"
status = "Enabled"
filter { prefix = "" }
expiration { days = var.retention_days }
}
}
```
## What Makes It Good
### Composition (IaC P6 Modules Compose, C6 Composability)
- The bucket pattern is one module, versioned once, consumed many times. A new consumer does not copy 40 lines of HCL — they call the module with a `name` and a `retention_days`.
- See `domains/infrastructure-as-code/modules.md` for the module-vs-copy boundary.
### Pinning (IaC P5 Version Everything)
- The consumer pins `version = "1.2.0"`. The module pins its provider (`version = "~> 5.0"`) and the required Terraform version. A commit is a complete, reproducible world.
- No `latest` anywhere. See `domains/infrastructure-as-code/terraform.md`.
### State Discipline (IaC P3 State is Truth, P8 Remote State with Locking)
- The consumer's root configuration declares a remote backend with locking (S3 + DynamoDB, GCS, etc.). The module itself does not declare a backend — the consumer owns state.
- See `domains/infrastructure-as-code/state.md` for backend selection and locking.
### Secrets Hygiene (IaC P10 Secrets Never in Code)
- The bucket is encrypted at rest (SSE-S3 AES256). No secret is hardcoded; encryption is a provider-managed default. If KMS were used, the key would come from a `data` source or a dedicated KMS module — never a literal.
- See `domains/security/secrets.md` for the general secrets principles.
### Plan Before Apply (IaC P4 Plan Before Apply)
- The consumer runs `terraform plan` before `apply`. The plan shows the new bucket, versioning, encryption, and lifecycle. Every line is reviewed. The plan is the contract review; `apply` is the signature.
### Cross-Domain Links
- `domains/devops/P1 Reproducibility` — the module makes the bucket reproducible from source.
- `domains/devops/P6 Configuration as Code` — the bucket is config, not a console click.
- `domains/security/supply-chain` — a versioned, signed module from a trusted registry is a supply-chain control.
+10 -20
View File
@@ -6,14 +6,14 @@
| Core Principle | Domains that derive from it | Count |
|----------------|---------------------------|-------|
| C1 Correctness | All 13 (v0.1: 11; v0.2: infrastructure-as-code, kubernetes) | Universal |
| C2 Clarity | v0.1: uiux, api, data, testing, observability, errors, documentation, devops; v0.2: infrastructure-as-code, kubernetes | 10 |
| C3 Simplicity | v0.1: security, data, testing, performance, documentation, concurrency, devops | 7 |
| C4 Locality | v0.1: testing, concurrency; v0.2: infrastructure-as-code, kubernetes | 4 |
| C5 Reversibility | v0.1: api, data, uiux, concurrency, devops; v0.2: infrastructure-as-code, kubernetes | 7 |
| C6 Composability | v0.1: api, security, observability, errors, documentation, concurrency; v0.2: infrastructure-as-code, kubernetes | 8 |
| C7 Observability | v0.1: api, data, testing, performance, observability, errors, devops; v0.2: infrastructure-as-code, kubernetes | 9 |
| C8 Economy | v0.1: security, testing, performance, observability, concurrency; v0.2: kubernetes | 6 |
| 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
@@ -37,20 +37,10 @@
| 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 |
| Infrastructure as Code | C1, C2, C3, C4, C5, C6, C7 | Declarative + state + composition; broadest derivation alongside Concurrency |
| Kubernetes | C1, C2, C4, C5, C6, C7, C8 | Declarative + reversibility + economy; broad derivation (7 C-rules) |
## v0.2 Domain Coverage (per IDEATE-03 schema)
| Domain | P-count | Derived-doc-count | Manifest-listed | Status |
|--------|---------|-------------------|-----------------|--------|
| Infrastructure as Code | 10 | 4 (terraform, opentofu, state, modules) | ✓ | complete |
| Kubernetes | 10 | 6 (workloads, networking, storage, rbac, helm, kustomize) | ✓ | complete |
## 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**, **Infrastructure as Code**, and **Kubernetes** are tied for the broadest derivation (7 C-rules each) — these domains touch the most core concerns.
- **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.
- **v0.2 expansion:** C4 (Locality) grew from 2 to 4 domains (added infrastructure-as-code state locality, kubernetes namespace blast-radius). C6 (Composability) grew from 6 to 8. The two new domains are broad-derivation domains (7 C-rules each), consistent with Concurrency's breadth.
- **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.
+1 -37
View File
@@ -173,40 +173,4 @@ C5=Reversibility · C6=Composability · C7=Observability · C8=Economy
| 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 |
## Infrastructure as Code
| IaC Principle | Core | Why |
|---------------------------|------|---------------------------------------|
| P1 Declarative Intent | C2, C3 | Clarity; simplicity of declaration |
| P2 Idempotence | C1 | Correctness of repeated apply |
| P3 State is Truth | C1, C7 | Correctness; observability of reality |
| P4 Plan Before Apply | C5 | Reversibility of changes |
| P5 Version Everything | C5 | Reversibility of reproduction |
| P6 Modules Compose | C6 | Composability of infrastructure |
| P7 Least Privilege Providers | C1 | Correctness via security |
| P8 Remote State with Locking | C1, C4 | Correctness; locality of state |
| P9 Drift is Recoverable | C5, C7 | Reversibility; observability of drift |
| P10 Secrets Never in Code | C1 | Correctness includes secret safety |
## Kubernetes
| K8s Principle | Core | Why |
|---------------------------|------|---------------------------------------|
| P1 Declarative Desired State | C1, C2 | Correctness; clarity of intent |
| P2 Pods are Mortal | C5 | Reversibility of workloads |
| P3 Labels Select | C2, C6 | Clarity; composability of selection |
| P4 Requests and Limits | C1, C8 | Correctness; economy of resources |
| P5 Probes Drive Health | C1, C7 | Correctness; observability of health |
| P6 Namespaces Bound Blast | C4 | Locality of blast radius |
| P7 RBAC by Intent | C1 | Correctness via security |
| P8 Storage is Explicit | C4, C5 | Locality; reversibility of data |
| P9 Config and Secrets Sep | C2 | Clarity of configuration |
| P10 Roll Forward, Roll Back | C5 | Reversibility of deploys |
## Coverage Summary (post-v0.2)
- 13 domains (11 v0.1 + 2 v0.2: infrastructure-as-code, kubernetes)
- 130 domain principles total (110 v0.1 + 20 v0.2)
- Every domain P-rule traces to ≥1 core C-rule (C1C8). No orphans.
| P10 Failure as Expected | C1, C7 | Correctness; observability |
-22
View File
@@ -125,28 +125,6 @@ If the task touches a domain, run that domain's checklist:
- [ ] Config is in code, not on the server
- [ ] Environments are parity (dev = prod modulo data)
### If Infrastructure as Code (see `domains/infrastructure-as-code/`)
- [ ] Configuration is declarative, not scripted (P1)
- [ ] Provider versions are pinned, never `latest` (P5)
- [ ] State is remote with locking; never committed (P3, P8)
- [ ] `plan` is reviewed before every `apply` (P4)
- [ ] No secrets in HCL; secrets via providers/stores (P10)
- [ ] Modules are versioned; copy-paste replaced by module calls (P6)
- [ ] Drift is treated as an incident, not a shortcut (P9)
- [ ] Provider credentials scoped per environment, least privilege (P7)
### If Kubernetes (see `domains/kubernetes/`)
- [ ] No bare pods; controllers used (P2)
- [ ] Resource requests set on every prod container (P4)
- [ ] Liveness/readiness/startup probes defined (P5)
- [ ] RBAC bound to ServiceAccounts by intent; no `cluster-admin` (P7)
- [ ] No `:latest` image tag in prod (P5 Version Everything)
- [ ] StatefulSet PVCs use `volumeClaimTemplates`; `emptyDir` only for scratch (P8)
- [ ] ConfigMaps and Secrets separate; secrets not in image (P9)
- [ ] Default-deny NetworkPolicy baseline (P6)
- [ ] Rollout history retained; rollback tested (P10)
- [ ] Namespaces used to bound blast radius; not `default` in prod (P6)
## Final Gate
- [ ] Have I read the relevant domain's first-principles?
+1 -35
View File
@@ -121,32 +121,6 @@ When you see a pattern listed here, it is a defect. Cite the principle it violat
| Rebuild per environment | P7 Immutability | Different artifacts |
| Snowflake server | P1, P6 | Not reproducible |
### Infrastructure as Code
| Anti-Pattern | Breaches | Why |
|--------------|----------|-----|
| Unpinned provider (`source` without `version`) | P5 Version Everything | Unversioned drift |
| `terraform apply` without a read `plan` | P4 Plan Before Apply | Unreviewed mutation |
| Local state in a shared environment | P8 Remote State with Locking | No lock = corruption |
| Hardcoded secret in HCL | P10 Secrets Never in Code | Committed secret |
| Copy-pasted block instead of a module | P6 Modules Compose | Bug duplicated |
| Manual change to a managed resource | P9 Drift is Recoverable | Unreconciled drift |
| Admin credentials in CI | P7 Least Privilege Providers | Overbroad grant |
| Committed `terraform.tfstate` | P3 State is Truth, P10 | Secret-bearing artifact in repo |
### Kubernetes
| Anti-Pattern | Breaches | Why |
|--------------|----------|-----|
| Bare pod (no controller) | P2 Pods are Mortal | No recovery/scale |
| No resource requests in prod | P4 Requests and Limits | BestEffort, first evicted |
| Liveness probe checks a dependency | P5 Probes Drive Health | Cascade restart |
| `cluster-admin` bound to a workload | P7 RBAC by Intent | Overbroad grant |
| `:latest` image tag in prod | P5 Version Everything | Unversioned drift |
| `emptyDir` for data that must persist | P8 Storage is Explicit | Data lost on pod death |
| Secrets baked into the image | P9 Config and Secrets Separate | No rotation without rebuild |
| `default` namespace in prod | P6 Namespaces Bound Blast Radius | No blast boundary |
| Shared PVC across StatefulSet replicas | P8 Storage is Explicit | Concurrent write corruption |
| `Delete` reclaim policy on prod storage | P8, P5 Reversibility | PVC delete = data delete |
## Cross-Cutting Anti-Patterns
| Anti-Pattern | Breaches | Why |
@@ -155,12 +129,4 @@ When you see a pattern listed here, it is a defect. Cite the principle it violat
| "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 |
## v0.2 Chaos Anti-Patterns (from IDEATE-13, IDEATE-14)
| Anti-Pattern | Breaches | Why |
|--------------|----------|-----|
| Orphaned P-rule (a domain principle with no matrix row) | matrix completeness, C6 | Breaks the conflict-resolution arbiter; the rule has no core trace |
| Deployable example artifact (standalone `.tf`/`.yaml` under `examples/`) | PROJECT.md "no runtime code", D-025 | Violates the docs-only contract; examples must be `.md` with fenced code |
| Unlisted v0.2 doc (new doc not added to MANIFEST) | manifest rule | Not part of the framework by definition |
| Leaky abstraction | C6, C2 | Hidden coupling |
+1 -19
View File
@@ -59,25 +59,7 @@ A peer review is a second set of eyes on correctness, clarity, and completeness.
## Domain-Specific (if applicable)
Run the relevant domain section from `agent-checklist.md` (UI/UX, API, Security, Data, Testing, Performance, Observability, Errors, Concurrency, DevOps, Infrastructure as Code, Kubernetes). The author ran it; the reviewer verifies.
### If Infrastructure as Code
- [ ] Are providers pinned and modules version-pinned? (no `latest`)
- [ ] Is state remote with locking, not committed?
- [ ] Is the `plan` diff reviewed, not just the config?
- [ ] Are secrets via providers/stores, not in HCL?
- [ ] Are credentials scoped per environment?
- [ ] Is drift being treated as an incident?
### If Kubernetes
- [ ] Are controllers used, not bare pods?
- [ ] Are resource requests set on every prod container?
- [ ] Are probes defined and checking the workload, not its dependencies?
- [ ] Is RBAC least-privilege, bound to ServiceAccounts by intent?
- [ ] Are image tags pinned, not `:latest`?
- [ ] Is persistent storage explicit (PVC + reclaim policy decided)?
- [ ] Are ConfigMaps and Secrets separate?
- [ ] Is the rollback path tested, not assumed?
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