From 344b40210c9a413c64d12637ec7538abd71b89f8 Mon Sep 17 00:00:00 2001 From: Jeremy Eder Date: Sun, 4 Jan 2026 03:01:49 -0500 Subject: [PATCH 1/3] feat: add Mermaid diagrams and enhanced validation Add 5 Mermaid diagrams replacing ASCII art: - Layered Architecture (docs/architecture.md) - Data Flow sequence diagram (docs/architecture.md) - Test Pyramid (docs/architecture.md) - Getting Started Flow (docs/quickstart.md) - CBA Workflow (codebase-agent.md) Also: - Fix malformed code block endings in architecture.md - Enhance validate-mermaid.sh to extract and validate inline diagrams from markdown files (not just standalone .mmd) --- .claude/agents/codebase-agent.md | 23 +++++ docs/architecture.md | 128 +++++++++++++++---------- docs/quickstart.md | 17 ++++ scripts/validate-mermaid.sh | 158 ++++++++++++++++++++++++++----- 4 files changed, 251 insertions(+), 75 deletions(-) diff --git a/.claude/agents/codebase-agent.md b/.claude/agents/codebase-agent.md index 71a8232..5dd34c2 100644 --- a/.claude/agents/codebase-agent.md +++ b/.claude/agents/codebase-agent.md @@ -21,6 +21,29 @@ color: blue You are the Codebase Agent for the Ambient Code Reference Repository. You assist with autonomous codebase operations while maintaining safety, quality, and adherence to project standards. +## CBA Workflow + +```mermaid +flowchart TB + A[Issue Received] --> B{Clear Requirements?} + B -->|No| C[Ask for Clarification] + C --> A + B -->|Yes| D[Analyze Codebase] + D --> E[Create Implementation Plan] + E --> F[Show Plan to User] + F --> G{Approved?} + G -->|No| E + G -->|Yes| H[Implement Changes] + H --> I[Run Linters & Tests] + I --> J{All Pass?} + J -->|No| H + J -->|Yes| K[Create PR] + K --> L[Wait for Human Review] + L --> M{Approved?} + M -->|Changes Requested| H + M -->|Yes| N[Merge] +``` + ## Core Capabilities ### 1. Issue-to-PR Automation diff --git a/docs/architecture.md b/docs/architecture.md index 67a1929..cb5d1c2 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -6,17 +6,37 @@ The Ambient Code Reference Repository demonstrates a **layered architecture** wi ## Layered Architecture -```text -┌─────────────────────────────────────┐ -│ API Layer (FastAPI) │ HTTP, routes, serialization -├─────────────────────────────────────┤ -│ Service Layer (Logic) │ Business rules, orchestration -├─────────────────────────────────────┤ -│ Model Layer (Pydantic) │ Validation, types -├─────────────────────────────────────┤ -│ Core Layer (Utilities) │ Config, security, logging -└─────────────────────────────────────┘ -```text +```mermaid +flowchart TB + subgraph API["API Layer (FastAPI)"] + direction LR + Routes[Routes] + Handlers[Handlers] + Serialization[Serialization] + end + subgraph Service["Service Layer (Logic)"] + direction LR + Business[Business Rules] + Orchestration[Orchestration] + end + subgraph Model["Model Layer (Pydantic)"] + direction LR + Validation[Validation] + Types[Types] + end + subgraph Core["Core Layer (Utilities)"] + direction LR + Config[Config] + Security[Security] + Logging[Logging] + end + + API --> Service + Service --> Model + Model --> Core + API --> Core + Service --> Core +``` ### API Layer @@ -39,7 +59,7 @@ def create_item(data: ItemCreate) -> Item: return item_service.create_item(data) except ValueError as e: raise HTTPException(status_code=409, detail=str(e)) -```text +``` ### Service Layer @@ -73,7 +93,7 @@ def create_item(self, data: ItemCreate) -> Item: self._next_id += 1 return item -```text +``` ### Model Layer @@ -97,7 +117,7 @@ class ItemCreate(ItemBase): @classmethod def sanitize_name(cls, v: str) -> str: return sanitize_string(v, max_length=200) -```text +``` ### Core Layer @@ -121,32 +141,32 @@ class Settings(BaseSettings): env_file = ".env" settings = Settings() -```text +``` ## Data Flow ### Creating an Item -```text -1. Client sends POST /api/v1/items - ↓ -2. API Layer (items.py) - - Pydantic validates request body - - create_item() called - ↓ -3. Service Layer (item_service.py) - - Check business rules (duplicate slug) - - Create Item model - - Store in memory - ↓ -4. Model Layer (item.py) - - Sanitize fields - - Validate types - ↓ -5. API Layer returns 201 Created - - Serialize Item to JSON - - Return to client -```text +```mermaid +sequenceDiagram + participant Client + participant API as API Layer + participant Svc as Service Layer + participant Model as Model Layer + + Client->>+API: POST /api/v1/items + API->>API: Pydantic validates request + API->>+Svc: create_item(data) + Svc->>Svc: Check business rules + Svc->>+Model: Create Item model + Model->>Model: Sanitize fields + Model->>Model: Validate types + Model-->>-Svc: Item instance + Svc->>Svc: Store in memory + Svc-->>-API: Item object + API->>API: Serialize to JSON + API-->>-Client: 201 Created + Item +``` ## Design Patterns @@ -164,7 +184,7 @@ item_service = ItemService() # app/api/v1/items.py from app.services.item_service import item_service -```text +``` ### Dependency Injection (Implicit) @@ -180,7 +200,7 @@ def create_item( service: ItemService = Depends(get_item_service) ): return service.create_item(data) -```text +``` ### Validation Pipeline @@ -197,7 +217,7 @@ class ItemCreate(ItemBase): @classmethod def validate_slug_field(cls, v: str) -> str: return validate_slug(v) -```text +``` ## Security Architecture @@ -232,21 +252,27 @@ class Settings(BaseSettings): class Config: env_file = ".env" -```text +``` ## Testing Architecture ### Test Pyramid -```text - ┌──────────┐ - │ E2E │ Few, slow (workflow tests) - ├──────────┤ - │Integration│ Some, medium (API tests) - ├──────────┤ - │ Unit │ Many, fast (service tests) - └──────────┘ -```text +```mermaid +flowchart TB + subgraph E2E["E2E Tests"] + E[Few, Slow - Workflow Tests] + end + subgraph Integration["Integration Tests"] + I[Some, Medium - API Tests] + end + subgraph Unit["Unit Tests"] + U[Many, Fast - Service Tests] + end + + E2E --> Integration + Integration --> Unit +``` **Unit**: Test service layer in isolation **Integration**: Test API with TestClient @@ -266,7 +292,7 @@ JSON format for log aggregation: "message": "Item created", "request_id": "abc123" } -```text +``` ### Health Endpoints @@ -321,7 +347,7 @@ async def add_request_id(request: Request, call_next): request.state.request_id = generate_id() response = await call_next(request) return response -```text +``` ### Adding Database @@ -330,7 +356,7 @@ async def add_request_id(request: Request, call_next): from sqlalchemy.ext.asyncio import create_async_engine engine = create_async_engine(settings.database_url) -```text +``` ## Best Practices diff --git a/docs/quickstart.md b/docs/quickstart.md index c004731..b2dc5db 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -12,6 +12,23 @@ This is a **documentation-only reference** for AI-assisted development patterns. **Looking for a working application?** See [demo-fastapi](https://github.com/jeremyeder/demo-fastapi) +### Getting Started Flow + +```mermaid +flowchart LR + A[Clone Repo] --> B[Explore Docs] + B --> C{What do you need?} + C -->|CBA Setup| D[Copy .claude/] + C -->|Architecture| E[Read docs/architecture.md] + C -->|Testing| F[Read testing-patterns.md] + C -->|CI/CD| G[Copy .github/workflows/] + D --> H[Customize for your project] + E --> H + F --> H + G --> H + H --> I[Ship It] +``` + ## Prerequisites - Python 3.11 or 3.12 (for documentation tooling) diff --git a/scripts/validate-mermaid.sh b/scripts/validate-mermaid.sh index fd45c6a..6fa3747 100755 --- a/scripts/validate-mermaid.sh +++ b/scripts/validate-mermaid.sh @@ -1,40 +1,150 @@ #!/bin/bash +# Validate Mermaid diagrams in markdown files and standalone .mmd files +# Extracts inline ```mermaid blocks and validates with mermaid-cli (mmdc) + set -e -echo "Validating Mermaid diagrams..." +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +TEMP_DIR=$(mktemp -d) +trap "rm -rf $TEMP_DIR" EXIT -# Find all .mmd files -MMD_FILES=$(find docs -name "*.mmd" 2>/dev/null || true) +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color -if [ -z "$MMD_FILES" ]; then - echo "No .mmd files found. Skipping Mermaid validation." - exit 0 -fi +echo "Validating Mermaid diagrams..." +echo "" # Check if mmdc is installed if ! command -v mmdc &> /dev/null; then - echo "Error: mermaid-cli (mmdc) is not installed" + echo -e "${YELLOW}Warning: mermaid-cli (mmdc) is not installed${NC}" echo "Install with: npm install -g @mermaid-js/mermaid-cli" - exit 1 + echo "" + echo "Falling back to syntax-only validation..." + MMDC_AVAILABLE=false +else + MMDC_AVAILABLE=true fi -# Validate each diagram ERRORS=0 -for file in $MMD_FILES; do - echo "Validating $file..." - if ! mmdc -i "$file" -o /dev/null 2>&1; then - echo "✗ Error in $file" - ERRORS=$((ERRORS + 1)) - else - echo "✓ $file is valid" - fi -done +TOTAL=0 -if [ $ERRORS -gt 0 ]; then - echo "" - echo "Found $ERRORS invalid Mermaid diagram(s)" - exit 1 +# Function to extract and validate mermaid blocks from a file +validate_markdown_file() { + local file="$1" + local block_num=0 + local in_mermaid=false + local mermaid_content="" + local line_num=0 + local block_start_line=0 + + while IFS= read -r line || [[ -n "$line" ]]; do + ((line_num++)) + + if [[ "$line" =~ ^\`\`\`mermaid ]]; then + in_mermaid=true + mermaid_content="" + block_start_line=$line_num + continue + fi + + if [[ "$in_mermaid" == true ]]; then + if [[ "$line" =~ ^\`\`\` ]]; then + in_mermaid=false + ((block_num++)) + ((TOTAL++)) + + # Write to temp file and validate + local temp_file="$TEMP_DIR/diagram_${block_num}.mmd" + echo "$mermaid_content" > "$temp_file" + + if [[ "$MMDC_AVAILABLE" == true ]]; then + # Use mmdc for full validation + if mmdc -i "$temp_file" -o "$TEMP_DIR/output.svg" 2>"$TEMP_DIR/error.log"; then + echo -e "${GREEN}✓${NC} $file:$block_start_line (block $block_num)" + else + echo -e "${RED}✗${NC} $file:$block_start_line (block $block_num)" + echo " Error: $(cat "$TEMP_DIR/error.log" | head -3)" + ((ERRORS++)) + fi + else + # Basic syntax validation without mmdc + # Check for common syntax issues + local has_error=false + + # Check if diagram type is specified + if ! echo "$mermaid_content" | head -1 | grep -qE '^[[:space:]]*(flowchart|sequenceDiagram|classDiagram|stateDiagram|erDiagram|gantt|pie|gitGraph|journey|mindmap|timeline|quadrantChart|sankey|xychart|block)'; then + echo -e "${RED}✗${NC} $file:$block_start_line (block $block_num)" + echo " Error: No valid diagram type found on first line" + has_error=true + ((ERRORS++)) + fi + + if [[ "$has_error" == false ]]; then + echo -e "${YELLOW}?${NC} $file:$block_start_line (block $block_num) - syntax check only" + fi + fi + else + mermaid_content+="$line"$'\n' + fi + fi + done < "$file" +} + +# Find and validate standalone .mmd files +echo "=== Standalone .mmd files ===" +MMD_FILES=$(find "$REPO_ROOT" -name "*.mmd" -not -path "*/node_modules/*" -not -path "*/.git/*" 2>/dev/null || true) + +if [ -z "$MMD_FILES" ]; then + echo "No standalone .mmd files found" +else + for file in $MMD_FILES; do + ((TOTAL++)) + if [[ "$MMDC_AVAILABLE" == true ]]; then + if mmdc -i "$file" -o "$TEMP_DIR/output.svg" 2>"$TEMP_DIR/error.log"; then + echo -e "${GREEN}✓${NC} $file" + else + echo -e "${RED}✗${NC} $file" + echo " Error: $(cat "$TEMP_DIR/error.log" | head -3)" + ((ERRORS++)) + fi + else + echo -e "${YELLOW}?${NC} $file - skipped (mmdc not available)" + fi + done +fi + +echo "" +echo "=== Inline mermaid blocks in markdown ===" + +# Find all markdown files and extract mermaid blocks +MD_FILES=$(find "$REPO_ROOT" -name "*.md" -not -path "*/node_modules/*" -not -path "*/.git/*" 2>/dev/null || true) + +if [ -z "$MD_FILES" ]; then + echo "No markdown files found" +else + for file in $MD_FILES; do + # Check if file contains mermaid blocks + if grep -q '```mermaid' "$file" 2>/dev/null; then + validate_markdown_file "$file" + fi + done fi echo "" -echo "All Mermaid diagrams are valid! ✓" +echo "=== Summary ===" +echo "Total diagrams checked: $TOTAL" + +if [ $ERRORS -gt 0 ]; then + echo -e "${RED}Errors found: $ERRORS${NC}" + exit 1 +elif [ $TOTAL -eq 0 ]; then + echo "No Mermaid diagrams found to validate" + exit 0 +else + echo -e "${GREEN}All $TOTAL diagram(s) valid!${NC}" + exit 0 +fi From d4cb51041bb5123c22b4dd2cafbffcc96feb6e2a Mon Sep 17 00:00:00 2001 From: Jeremy Eder Date: Sun, 4 Jan 2026 03:08:51 -0500 Subject: [PATCH 2/3] fix: use portable arithmetic in validate-mermaid.sh Replace ((var++)) with var=$((var + 1)) to avoid exit code 1 when incrementing from 0 with set -e enabled. --- scripts/validate-mermaid.sh | 35 ++++++++++------------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/scripts/validate-mermaid.sh b/scripts/validate-mermaid.sh index 6fa3747..4cdb159 100755 --- a/scripts/validate-mermaid.sh +++ b/scripts/validate-mermaid.sh @@ -1,7 +1,4 @@ #!/bin/bash -# Validate Mermaid diagrams in markdown files and standalone .mmd files -# Extracts inline ```mermaid blocks and validates with mermaid-cli (mmdc) - set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -9,16 +6,14 @@ REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" TEMP_DIR=$(mktemp -d) trap "rm -rf $TEMP_DIR" EXIT -# Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' -NC='\033[0m' # No Color +NC='\033[0m' echo "Validating Mermaid diagrams..." echo "" -# Check if mmdc is installed if ! command -v mmdc &> /dev/null; then echo -e "${YELLOW}Warning: mermaid-cli (mmdc) is not installed${NC}" echo "Install with: npm install -g @mermaid-js/mermaid-cli" @@ -32,7 +27,6 @@ fi ERRORS=0 TOTAL=0 -# Function to extract and validate mermaid blocks from a file validate_markdown_file() { local file="$1" local block_num=0 @@ -42,7 +36,7 @@ validate_markdown_file() { local block_start_line=0 while IFS= read -r line || [[ -n "$line" ]]; do - ((line_num++)) + line_num=$((line_num + 1)) if [[ "$line" =~ ^\`\`\`mermaid ]]; then in_mermaid=true @@ -54,33 +48,27 @@ validate_markdown_file() { if [[ "$in_mermaid" == true ]]; then if [[ "$line" =~ ^\`\`\` ]]; then in_mermaid=false - ((block_num++)) - ((TOTAL++)) + block_num=$((block_num + 1)) + TOTAL=$((TOTAL + 1)) - # Write to temp file and validate local temp_file="$TEMP_DIR/diagram_${block_num}.mmd" echo "$mermaid_content" > "$temp_file" if [[ "$MMDC_AVAILABLE" == true ]]; then - # Use mmdc for full validation if mmdc -i "$temp_file" -o "$TEMP_DIR/output.svg" 2>"$TEMP_DIR/error.log"; then echo -e "${GREEN}✓${NC} $file:$block_start_line (block $block_num)" else echo -e "${RED}✗${NC} $file:$block_start_line (block $block_num)" - echo " Error: $(cat "$TEMP_DIR/error.log" | head -3)" - ((ERRORS++)) + echo " Error: $(head -3 "$TEMP_DIR/error.log")" + ERRORS=$((ERRORS + 1)) fi else - # Basic syntax validation without mmdc - # Check for common syntax issues local has_error=false - - # Check if diagram type is specified if ! echo "$mermaid_content" | head -1 | grep -qE '^[[:space:]]*(flowchart|sequenceDiagram|classDiagram|stateDiagram|erDiagram|gantt|pie|gitGraph|journey|mindmap|timeline|quadrantChart|sankey|xychart|block)'; then echo -e "${RED}✗${NC} $file:$block_start_line (block $block_num)" echo " Error: No valid diagram type found on first line" has_error=true - ((ERRORS++)) + ERRORS=$((ERRORS + 1)) fi if [[ "$has_error" == false ]]; then @@ -94,7 +82,6 @@ validate_markdown_file() { done < "$file" } -# Find and validate standalone .mmd files echo "=== Standalone .mmd files ===" MMD_FILES=$(find "$REPO_ROOT" -name "*.mmd" -not -path "*/node_modules/*" -not -path "*/.git/*" 2>/dev/null || true) @@ -102,14 +89,14 @@ if [ -z "$MMD_FILES" ]; then echo "No standalone .mmd files found" else for file in $MMD_FILES; do - ((TOTAL++)) + TOTAL=$((TOTAL + 1)) if [[ "$MMDC_AVAILABLE" == true ]]; then if mmdc -i "$file" -o "$TEMP_DIR/output.svg" 2>"$TEMP_DIR/error.log"; then echo -e "${GREEN}✓${NC} $file" else echo -e "${RED}✗${NC} $file" - echo " Error: $(cat "$TEMP_DIR/error.log" | head -3)" - ((ERRORS++)) + echo " Error: $(head -3 "$TEMP_DIR/error.log")" + ERRORS=$((ERRORS + 1)) fi else echo -e "${YELLOW}?${NC} $file - skipped (mmdc not available)" @@ -120,14 +107,12 @@ fi echo "" echo "=== Inline mermaid blocks in markdown ===" -# Find all markdown files and extract mermaid blocks MD_FILES=$(find "$REPO_ROOT" -name "*.md" -not -path "*/node_modules/*" -not -path "*/.git/*" 2>/dev/null || true) if [ -z "$MD_FILES" ]; then echo "No markdown files found" else for file in $MD_FILES; do - # Check if file contains mermaid blocks if grep -q '```mermaid' "$file" 2>/dev/null; then validate_markdown_file "$file" fi From 84826a70c24ef7fa72c36fc8d038beee4d2ca0e9 Mon Sep 17 00:00:00 2001 From: Jeremy Eder Date: Sun, 4 Jan 2026 03:11:59 -0500 Subject: [PATCH 3/3] fix: configure puppeteer for CI sandbox restrictions Add puppeteer-config.json with --no-sandbox for GitHub Actions. Update validate-mermaid.sh to use config when available. --- .github/workflows/docs-validation.yml | 2 ++ scripts/puppeteer-config.json | 3 +++ scripts/validate-mermaid.sh | 13 +++++++++++-- 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 scripts/puppeteer-config.json diff --git a/.github/workflows/docs-validation.yml b/.github/workflows/docs-validation.yml index 6919489..74d75af 100644 --- a/.github/workflows/docs-validation.yml +++ b/.github/workflows/docs-validation.yml @@ -30,6 +30,8 @@ jobs: else echo "No Mermaid diagrams to validate" fi + env: + PUPPETEER_CONFIG: ${{ github.workspace }}/scripts/puppeteer-config.json lint-markdown: runs-on: ubuntu-latest diff --git a/scripts/puppeteer-config.json b/scripts/puppeteer-config.json new file mode 100644 index 0000000..2274c80 --- /dev/null +++ b/scripts/puppeteer-config.json @@ -0,0 +1,3 @@ +{ + "args": ["--no-sandbox", "--disable-setuid-sandbox"] +} diff --git a/scripts/validate-mermaid.sh b/scripts/validate-mermaid.sh index 4cdb159..83958b1 100755 --- a/scripts/validate-mermaid.sh +++ b/scripts/validate-mermaid.sh @@ -24,6 +24,15 @@ else MMDC_AVAILABLE=true fi +MMDC_ARGS="" +if [[ -n "$PUPPETEER_CONFIG" ]] && [[ -f "$PUPPETEER_CONFIG" ]]; then + MMDC_ARGS="-p $PUPPETEER_CONFIG" + echo "Using Puppeteer config: $PUPPETEER_CONFIG" +elif [[ -f "$SCRIPT_DIR/puppeteer-config.json" ]]; then + MMDC_ARGS="-p $SCRIPT_DIR/puppeteer-config.json" + echo "Using Puppeteer config: $SCRIPT_DIR/puppeteer-config.json" +fi + ERRORS=0 TOTAL=0 @@ -55,7 +64,7 @@ validate_markdown_file() { echo "$mermaid_content" > "$temp_file" if [[ "$MMDC_AVAILABLE" == true ]]; then - if mmdc -i "$temp_file" -o "$TEMP_DIR/output.svg" 2>"$TEMP_DIR/error.log"; then + if mmdc $MMDC_ARGS -i "$temp_file" -o "$TEMP_DIR/output.svg" 2>"$TEMP_DIR/error.log"; then echo -e "${GREEN}✓${NC} $file:$block_start_line (block $block_num)" else echo -e "${RED}✗${NC} $file:$block_start_line (block $block_num)" @@ -91,7 +100,7 @@ else for file in $MMD_FILES; do TOTAL=$((TOTAL + 1)) if [[ "$MMDC_AVAILABLE" == true ]]; then - if mmdc -i "$file" -o "$TEMP_DIR/output.svg" 2>"$TEMP_DIR/error.log"; then + if mmdc $MMDC_ARGS -i "$file" -o "$TEMP_DIR/output.svg" 2>"$TEMP_DIR/error.log"; then echo -e "${GREEN}✓${NC} $file" else echo -e "${RED}✗${NC} $file"