improve(engineering): enhance tdd-guide, env-secrets-manager, senior-secops, database-designer, senior-devops
tdd-guide (164 → 412 lines): - Spec-first workflow, per-language examples (TS/Python/Go) - Bounded autonomy rules, property-based testing, mutation testing env-secrets-manager (78 → 260 lines): - Cloud secret store integration (Vault, AWS SM, Azure KV, GCP SM) - Secret rotation workflow, CI/CD injection, pre-commit detection, audit logging senior-secops (422 → 505 lines): - OWASP Top 10 quick-check, secret scanning tools comparison - Supply chain security (SBOM, Sigstore, SLSA levels) database-designer (66 → 289 lines): - Query patterns (JOINs, CTEs, window functions), migration patterns - Performance optimization (indexing, EXPLAIN, N+1, connection pooling) - Multi-DB decision matrix, sharding & replication senior-devops (275 → 323 lines): - Multi-cloud cross-references (AWS, Azure, GCP architects) - Cloud-agnostic IaC section (Terraform/OpenTofu, Pulumi) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -270,6 +270,54 @@ kubectl get pods -n production -l app=myapp
|
||||
curl -sf https://app.example.com/healthz || echo "ROLLBACK FAILED — escalate"
|
||||
```
|
||||
|
||||
## Multi-Cloud Cross-References
|
||||
|
||||
Use these companion skills for cloud-specific deep dives:
|
||||
|
||||
| Skill | Cloud | Use When |
|
||||
|-------|-------|----------|
|
||||
| **aws-solution-architect** | AWS | ECS/EKS, Lambda, VPC design, cost optimization |
|
||||
| **azure-cloud-architect** | Azure | AKS, App Service, Virtual Networks, Azure DevOps |
|
||||
| **gcp-cloud-architect** | GCP | GKE, Cloud Run, VPC, Cloud Build *(coming soon)* |
|
||||
|
||||
**Multi-cloud vs single-cloud decision:**
|
||||
- **Single-cloud** (default) — lower operational complexity, deeper managed-service integration, better cost leverage with committed-use discounts
|
||||
- **Multi-cloud** — required when mandated by compliance/data residency, acquiring companies on different clouds, or needing best-of-breed services across providers (e.g., AWS for compute + GCP for ML)
|
||||
- **Hybrid** — on-prem + cloud; use when regulated workloads must stay on-prem while burst/non-sensitive workloads run in the cloud
|
||||
|
||||
> Start single-cloud. Add a second cloud only when there is a concrete business or compliance driver — not for theoretical redundancy.
|
||||
|
||||
---
|
||||
|
||||
## Cloud-Agnostic IaC
|
||||
|
||||
### Terraform / OpenTofu (Default Choice)
|
||||
|
||||
Terraform (or its open-source fork OpenTofu) is the recommended IaC tool for most teams:
|
||||
- Single language (HCL) across AWS, Azure, GCP, and 3,000+ providers
|
||||
- State management with remote backends (S3, GCS, Azure Blob)
|
||||
- Plan-before-apply workflow prevents drift surprises
|
||||
- Cross-reference **terraform-patterns** for module structure, state isolation, and CI/CD integration
|
||||
|
||||
### Pulumi (Programming Language IaC)
|
||||
|
||||
Choose Pulumi when the team strongly prefers TypeScript, Python, Go, or C# over HCL:
|
||||
- Full programming language — loops, conditionals, unit tests native
|
||||
- Same cloud provider coverage as Terraform
|
||||
- Easier onboarding for dev teams that resist learning HCL
|
||||
|
||||
### When to Use Cloud-Native IaC
|
||||
|
||||
| Tool | Use When |
|
||||
|------|----------|
|
||||
| **CloudFormation** | AWS-only shop; need native AWS support (StackSets, Service Catalog) |
|
||||
| **Bicep** | Azure-only shop; simpler syntax than ARM templates |
|
||||
| **Cloud Deployment Manager** | GCP-only; rare — most GCP teams prefer Terraform |
|
||||
|
||||
> **Rule of thumb:** Use Terraform/OpenTofu unless you are 100% committed to a single cloud AND the cloud-native tool offers a feature Terraform cannot replicate (e.g., AWS Service Catalog integration).
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
Check the comprehensive troubleshooting section in `references/deployment_strategies.md`.
|
||||
|
||||
@@ -413,6 +413,89 @@ app.use((req, res, next) => {
|
||||
|
||||
---
|
||||
|
||||
## OWASP Top 10 Quick-Check
|
||||
|
||||
Rapid 15-minute assessment — run through each category and note pass/fail. For deep-dive testing, hand off to the **security-pen-testing** skill.
|
||||
|
||||
| # | Category | One-Line Check |
|
||||
|---|----------|----------------|
|
||||
| A01 | Broken Access Control | Verify role checks on every endpoint; test horizontal privilege escalation |
|
||||
| A02 | Cryptographic Failures | Confirm TLS 1.2+ everywhere; no secrets in logs or source |
|
||||
| A03 | Injection | Run parameterized query audit; check ORM raw-query usage |
|
||||
| A04 | Insecure Design | Review threat model exists for critical flows |
|
||||
| A05 | Security Misconfiguration | Check default credentials removed; error pages generic |
|
||||
| A06 | Vulnerable Components | Run `vulnerability_assessor.py`; zero critical/high CVEs |
|
||||
| A07 | Auth Failures | Verify MFA on admin; brute-force protection active |
|
||||
| A08 | Software & Data Integrity | Confirm CI/CD pipeline signs artifacts; no unsigned deps |
|
||||
| A09 | Logging & Monitoring | Validate audit logs capture auth events; alerts configured |
|
||||
| A10 | SSRF | Test internal URL filters; block metadata endpoints (169.254.169.254) |
|
||||
|
||||
> **Deep dive needed?** Hand off to `security-pen-testing` for full OWASP Testing Guide coverage.
|
||||
|
||||
---
|
||||
|
||||
## Secret Scanning Tools
|
||||
|
||||
Choose the right scanner for each stage of your workflow:
|
||||
|
||||
| Tool | Best For | Language | Pre-commit | CI/CD | Custom Rules |
|
||||
|------|----------|----------|:----------:|:-----:|:------------:|
|
||||
| **gitleaks** | CI pipelines, full-repo scans | Go | Yes | Yes | TOML regexes |
|
||||
| **detect-secrets** | Pre-commit hooks, incremental | Python | Yes | Partial | Plugin-based |
|
||||
| **truffleHog** | Deep history scans, entropy | Go | No | Yes | Regex + entropy |
|
||||
|
||||
**Recommended setup:** Use `detect-secrets` as a pre-commit hook (catches secrets before they enter history) and `gitleaks` in CI (catches anything that slips through).
|
||||
|
||||
```bash
|
||||
# detect-secrets pre-commit hook (.pre-commit-config.yaml)
|
||||
- repo: https://github.com/Yelp/detect-secrets
|
||||
rev: v1.4.0
|
||||
hooks:
|
||||
- id: detect-secrets
|
||||
args: ['--baseline', '.secrets.baseline']
|
||||
|
||||
# gitleaks in GitHub Actions
|
||||
- name: gitleaks
|
||||
uses: gitleaks/gitleaks-action@v2
|
||||
env:
|
||||
GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Supply Chain Security
|
||||
|
||||
Protect against dependency and artifact tampering with SBOM generation, artifact signing, and SLSA compliance.
|
||||
|
||||
**SBOM Generation:**
|
||||
- **syft** — generates SBOMs from container images or source dirs (SPDX, CycloneDX formats)
|
||||
- **cyclonedx-cli** — CycloneDX-native tooling; merge multiple SBOMs for mono-repos
|
||||
|
||||
```bash
|
||||
# Generate SBOM from container image
|
||||
syft packages ghcr.io/org/app:latest -o cyclonedx-json > sbom.json
|
||||
```
|
||||
|
||||
**Artifact Signing (Sigstore/cosign):**
|
||||
```bash
|
||||
# Sign a container image (keyless via OIDC)
|
||||
cosign sign ghcr.io/org/app:latest
|
||||
# Verify signature
|
||||
cosign verify ghcr.io/org/app:latest --certificate-identity=ci@org.com --certificate-oidc-issuer=https://token.actions.githubusercontent.com
|
||||
```
|
||||
|
||||
**SLSA Levels Overview:**
|
||||
| Level | Requirement | What It Proves |
|
||||
|-------|-------------|----------------|
|
||||
| 1 | Build process documented | Provenance exists |
|
||||
| 2 | Hosted build service, signed provenance | Tamper-resistant provenance |
|
||||
| 3 | Hardened build platform, non-falsifiable provenance | Tamper-proof build |
|
||||
| 4 | Two-party review, hermetic builds | Maximum supply-chain assurance |
|
||||
|
||||
> **Cross-references:** `security-pen-testing` (vulnerability exploitation testing), `dependency-auditor` (license and CVE audit for dependencies).
|
||||
|
||||
---
|
||||
|
||||
## Reference Documentation
|
||||
|
||||
| Document | Description |
|
||||
|
||||
@@ -148,6 +148,254 @@ Additional scripts: `framework_adapter.py` (convert between frameworks), `metric
|
||||
|
||||
---
|
||||
|
||||
## Spec-First Workflow
|
||||
|
||||
TDD is most effective when driven by a written spec. The flow:
|
||||
|
||||
1. **Write or receive a spec** — stored in `specs/<feature>.md`
|
||||
2. **Extract acceptance criteria** — each criterion becomes one or more test cases
|
||||
3. **Write failing tests (RED)** — one test per acceptance criterion
|
||||
4. **Implement minimal code (GREEN)** — satisfy each test in order
|
||||
5. **Refactor** — clean up while all tests stay green
|
||||
|
||||
### Spec Directory Convention
|
||||
|
||||
```
|
||||
project/
|
||||
├── specs/
|
||||
│ ├── user-auth.md # Feature spec with acceptance criteria
|
||||
│ ├── payment-processing.md
|
||||
│ └── notification-system.md
|
||||
├── tests/
|
||||
│ ├── test_user_auth.py # Tests derived from specs/user-auth.md
|
||||
│ ├── test_payments.py
|
||||
│ └── test_notifications.py
|
||||
└── src/
|
||||
```
|
||||
|
||||
### Extracting Tests from Specs
|
||||
|
||||
Each acceptance criterion in a spec maps to at least one test:
|
||||
|
||||
| Spec Criterion | Test Case |
|
||||
|---------------|-----------|
|
||||
| "User can log in with valid credentials" | `test_login_valid_credentials_returns_token` |
|
||||
| "Invalid password returns 401" | `test_login_invalid_password_returns_401` |
|
||||
| "Account locks after 5 failed attempts" | `test_login_locks_after_five_failures` |
|
||||
|
||||
**Tip:** Number your acceptance criteria in the spec. Reference the number in the test docstring for traceability (`# AC-3: Account locks after 5 failed attempts`).
|
||||
|
||||
> **Cross-reference:** See `engineering/spec-driven-workflow` for the full spec methodology, including spec templates and review checklists.
|
||||
|
||||
---
|
||||
|
||||
## Red-Green-Refactor Examples Per Language
|
||||
|
||||
### TypeScript / Jest
|
||||
|
||||
```typescript
|
||||
// test/cart.test.ts
|
||||
describe("Cart", () => {
|
||||
describe("addItem", () => {
|
||||
it("should add a new item to an empty cart", () => {
|
||||
const cart = new Cart();
|
||||
cart.addItem({ id: "sku-1", name: "Widget", price: 9.99, qty: 1 });
|
||||
|
||||
expect(cart.items).toHaveLength(1);
|
||||
expect(cart.items[0].id).toBe("sku-1");
|
||||
});
|
||||
|
||||
it("should increment quantity when adding an existing item", () => {
|
||||
const cart = new Cart();
|
||||
cart.addItem({ id: "sku-1", name: "Widget", price: 9.99, qty: 1 });
|
||||
cart.addItem({ id: "sku-1", name: "Widget", price: 9.99, qty: 2 });
|
||||
|
||||
expect(cart.items).toHaveLength(1);
|
||||
expect(cart.items[0].qty).toBe(3);
|
||||
});
|
||||
|
||||
it("should throw when quantity is zero or negative", () => {
|
||||
const cart = new Cart();
|
||||
expect(() =>
|
||||
cart.addItem({ id: "sku-1", name: "Widget", price: 9.99, qty: 0 })
|
||||
).toThrow("Quantity must be positive");
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Python / Pytest (Advanced Patterns)
|
||||
|
||||
```python
|
||||
# tests/conftest.py — shared fixtures
|
||||
import pytest
|
||||
from app.db import create_engine, Session
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def db_engine():
|
||||
engine = create_engine("sqlite:///:memory:")
|
||||
yield engine
|
||||
engine.dispose()
|
||||
|
||||
@pytest.fixture
|
||||
def db_session(db_engine):
|
||||
session = Session(bind=db_engine)
|
||||
yield session
|
||||
session.rollback()
|
||||
session.close()
|
||||
|
||||
# tests/test_pricing.py — parametrize for multiple cases
|
||||
import pytest
|
||||
from app.pricing import calculate_discount
|
||||
|
||||
@pytest.mark.parametrize("subtotal, expected_discount", [
|
||||
(50.0, 0.0), # Below threshold — no discount
|
||||
(100.0, 5.0), # 5% tier
|
||||
(250.0, 25.0), # 10% tier
|
||||
(500.0, 75.0), # 15% tier
|
||||
])
|
||||
def test_calculate_discount(subtotal, expected_discount):
|
||||
assert calculate_discount(subtotal) == pytest.approx(expected_discount)
|
||||
```
|
||||
|
||||
### Go — Table-Driven Tests
|
||||
|
||||
```go
|
||||
// cart_test.go
|
||||
package cart
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestApplyDiscount(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
subtotal float64
|
||||
want float64
|
||||
}{
|
||||
{"no discount below threshold", 50.0, 0.0},
|
||||
{"5 percent tier", 100.0, 5.0},
|
||||
{"10 percent tier", 250.0, 25.0},
|
||||
{"15 percent tier", 500.0, 75.0},
|
||||
{"zero subtotal", 0.0, 0.0},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := ApplyDiscount(tt.subtotal)
|
||||
if got != tt.want {
|
||||
t.Errorf("ApplyDiscount(%v) = %v, want %v", tt.subtotal, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Bounded Autonomy Rules
|
||||
|
||||
When generating tests autonomously, follow these rules to decide when to stop and ask the user:
|
||||
|
||||
### Stop and Ask When
|
||||
|
||||
- **Ambiguous requirements** — the spec or user story has conflicting or unclear acceptance criteria
|
||||
- **Missing edge cases** — you cannot determine boundary values without domain knowledge (e.g., max allowed transaction amount)
|
||||
- **Test count exceeds 50** — large test suites need human review before committing; present a summary and ask which areas to prioritize
|
||||
- **External dependencies unclear** — the feature relies on third-party APIs or services with undocumented behavior
|
||||
- **Security-sensitive logic** — authentication, authorization, encryption, or payment flows require human sign-off on test scenarios
|
||||
|
||||
### Continue Autonomously When
|
||||
|
||||
- **Clear spec with numbered acceptance criteria** — each criterion maps directly to tests
|
||||
- **Straightforward CRUD operations** — create, read, update, delete with well-defined models
|
||||
- **Well-defined API contracts** — OpenAPI spec or typed interfaces available
|
||||
- **Pure functions** — deterministic input/output with no side effects
|
||||
- **Existing test patterns** — the codebase already has similar tests to follow
|
||||
|
||||
---
|
||||
|
||||
## Property-Based Testing
|
||||
|
||||
Property-based testing generates random inputs to verify invariants instead of relying on hand-picked examples. Use it when the input space is large and the expected behavior can be described as a property.
|
||||
|
||||
### Python — Hypothesis
|
||||
|
||||
```python
|
||||
from hypothesis import given, strategies as st
|
||||
from app.serializers import serialize, deserialize
|
||||
|
||||
@given(st.text())
|
||||
def test_roundtrip_serialization(data):
|
||||
"""Serialization followed by deserialization returns the original."""
|
||||
assert deserialize(serialize(data)) == data
|
||||
|
||||
@given(st.integers(), st.integers())
|
||||
def test_addition_is_commutative(a, b):
|
||||
assert a + b == b + a
|
||||
```
|
||||
|
||||
### TypeScript — fast-check
|
||||
|
||||
```typescript
|
||||
import fc from "fast-check";
|
||||
import { encode, decode } from "./codec";
|
||||
|
||||
test("encode/decode roundtrip", () => {
|
||||
fc.assert(
|
||||
fc.property(fc.string(), (input) => {
|
||||
expect(decode(encode(input))).toBe(input);
|
||||
})
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
### When to Use Property-Based Over Example-Based
|
||||
|
||||
| Use Property-Based | Example |
|
||||
|-------------------|---------|
|
||||
| Data transformations | Serialize/deserialize roundtrips |
|
||||
| Mathematical properties | Commutativity, associativity, idempotency |
|
||||
| Encoding/decoding | Base64, URL encoding, compression |
|
||||
| Sorting and filtering | Output is sorted, length preserved |
|
||||
| Parser correctness | Valid input always parses without error |
|
||||
|
||||
---
|
||||
|
||||
## Mutation Testing
|
||||
|
||||
Mutation testing modifies your production code (creates "mutants") and checks whether your tests catch the changes. If a mutant survives (tests still pass), your tests have a gap that coverage alone cannot reveal.
|
||||
|
||||
### Tools
|
||||
|
||||
| Language | Tool | Command |
|
||||
|----------|------|---------|
|
||||
| TypeScript/JavaScript | **Stryker** | `npx stryker run` |
|
||||
| Python | **mutmut** | `mutmut run --paths-to-mutate=src/` |
|
||||
| Java | **PIT** | `mvn org.pitest:pitest-maven:mutationCoverage` |
|
||||
|
||||
### Why Mutation Testing Matters
|
||||
|
||||
- **100% line coverage != good tests** — coverage tells you code was executed, not that it was verified
|
||||
- **Catches weak assertions** — tests that run code but assert nothing meaningful
|
||||
- **Finds missing boundary tests** — mutants that change `<` to `<=` expose off-by-one gaps
|
||||
- **Quantifiable quality metric** — mutation score (% mutants killed) is a stronger signal than coverage %
|
||||
|
||||
**Recommendation:** Run mutation testing on critical paths (auth, payments, data processing) even if overall coverage is high. Target 85%+ mutation score on P0 modules.
|
||||
|
||||
---
|
||||
|
||||
## Cross-References
|
||||
|
||||
| Skill | Relationship |
|
||||
|-------|-------------|
|
||||
| `engineering/spec-driven-workflow` | Spec → acceptance criteria → test extraction pipeline |
|
||||
| `engineering-team/focused-fix` | Phase 5 (Verify) uses TDD to confirm the fix with a regression test |
|
||||
| `engineering-team/senior-qa` | Broader QA strategy; TDD is one layer in the test pyramid |
|
||||
| `engineering-team/code-reviewer` | Review generated tests for assertion quality and coverage completeness |
|
||||
| `engineering-team/senior-fullstack` | Project scaffolders include testing infrastructure compatible with TDD workflows |
|
||||
|
||||
---
|
||||
|
||||
## Limitations
|
||||
|
||||
| Scope | Details |
|
||||
|
||||
Reference in New Issue
Block a user