Code Atlas Skill Purpose Build exhaustive, regeneratable architecture atlases directly from code truth. A code-atlas is a living document set: diagrams, graphs, and inventory tables that form a navigable map of any codebase. Atlas-building is investigation: structured reasoning about code in graph form reveals structural bugs, API contract mismatches, and architectural drift that linear code review misses. An atlas is complete when any engineer, given only the atlas and a bug report, can trace the full execution path without opening the source code. Layer Overview Layer definitions are the si…

)\n\ndef safe_service_name(raw: str) -> str:\n \"\"\"Sanitise a service name for use in filesystem paths.\"\"\"\n # Replace unsafe chars with hyphens, truncate to 64\n sanitised = re.sub(r'[^a-zA-Z0-9_-]', '-', raw)[:64]\n if not SERVICE_NAME_PATTERN.match(sanitised):\n raise SecurityError(f\"Service name cannot be sanitised: {raw!r}\")\n return sanitised\n\ndef layer7_output_path(output_dir: str, service_name: str) -> str:\n safe_name = safe_service_name(service_name)\n path = os.path.join(output_dir, \"service-components\", f\"{safe_name}.mmd\")\n canonical = os.path.realpath(path)\n if not canonical.startswith(os.path.realpath(output_dir)):\n raise SecurityError(f\"Path traversal detected for service: {service_name!r}\")\n return canonical\n```\n\n**Anti-pattern:** Never use a raw service name (from docker-compose, k8s, or any file) directly in a path without this sanitisation.\n\n---\n\n### SEC-12: Layer 8 LSP Output Sanitization (HIGH)\n\n**Rule:** All data returned by the `lsp-setup` skill (symbol names, file paths, type signatures, call contexts) is treated as untrusted input before embedding in any atlas file.\n\n**Required steps:**\n\n1. Validate JSON schema of `LSPSymbolReport` before accessing fields\n2. Apply SEC-03 HTML-escaping to all symbol names and type strings written to `.mmd` files\n3. Apply SEC-02 path boundary check to all `file` fields before using in evidence links\n4. Apply SEC-09 credential pattern scan to all `context` (surrounding code) fields\n\n```python\ndef sanitise_lsp_output(report: dict, codebase_path: str) -> dict:\n \"\"\"Sanitise all LSP output fields before atlas embedding.\"\"\"\n for symbol in report.get(\"symbols\", []):\n symbol[\"name\"] = sanitize_label(symbol[\"name\"]) # SEC-03\n symbol[\"file\"] = validate_relative_path( # SEC-02\n symbol[\"file\"], codebase_path)\n for ref in symbol.get(\"references\", []):\n ref[\"context\"] = redact_credentials(ref[\"context\"]) # SEC-09\n ref[\"file\"] = validate_relative_path(ref[\"file\"], codebase_path)\n return report\n```\n\n---\n\n### SEC-13: Density Threshold Parameter Validation (HIGH)\n\n**Rule:** The `--density-threshold` override parameter must be validated as positive integers in range `1–10,000`. Reject values outside this range with a clear error message and halt the invocation.\n\n**Rejected values:**\n\n| Value | Reason |\n| -------------------------- | --------------------------------------------------- |\n| `0` | Disables guard via spam; not a meaningful threshold |\n| Negative numbers | Invalid; makes no semantic sense |\n| Non-integers | Parameter type violation |\n| `> 10,000` | Effectively disables guard for any real codebase |\n| `null`, `undefined`, empty | Reverts to defaults (no error) |\n\n```python\ndef validate_density_threshold(nodes: any, edges: any) -> tuple[int, int]:\n for name, value in [(\"nodes\", nodes), (\"edges\", edges)]:\n if not isinstance(value, int):\n raise ValueError(f\"density-threshold {name} must be an integer, got: {value!r}\")\n if not (1 \u003c= value \u003c= 10_000):\n raise ValueError(\n f\"density-threshold {name}={value} out of valid range 1–10,000\"\n )\n return int(nodes), int(edges)\n```\n\n---\n\n### SEC-14: Density Prompt — Accept Only Valid Choices (MEDIUM)\n\n**Rule:** The density prompt accepts only `'a'`, `'b'`, or `'c'` (case-insensitive, whitespace-stripped). Any other input must re-prompt. Never silently default or fall through to a hidden choice.\n\n```python\nVALID_DENSITY_CHOICES = {'a', 'b', 'c'}\n\ndef prompt_density_choice(node_count: int, edge_count: int) -> str:\n while True:\n raw = input(\n f\"This diagram has {node_count} nodes and {edge_count} edges, \"\n f\"which may render poorly.\\n\"\n f\"Please choose:\\n\"\n f\" (a) Full diagram anyway\\n\"\n f\" (b) Simplified/clustered diagram\\n\"\n f\" (c) Table representation\\n\"\n f\"> \"\n ).strip().lower()\n if raw in VALID_DENSITY_CHOICES:\n log_audit(f\"density_choice={raw}\") # Log choice, not raw input\n return raw\n print(f\"Invalid choice: {raw!r}. Please enter a, b, or c.\")\n # Loop: re-prompt unconditionally\n```\n\n**Non-interactive context** (CI/batch): Default to `'b'`; log `SkillError` with `DENSITY_THRESHOLD_EXCEEDED`. Never default silently.\n\n---\n\n### SEC-15: Credential Redaction in All Layer 8 Outputs (CRITICAL)\n\n**Rule:** The `CREDENTIAL_PATTERNS` regex from SEC-09 must be applied before writing each of the four Layer 8 output files. This is a mandatory step in the `SecureAtlasBuilder` pipeline for Layer 8.\n\n**Files that require redaction:**\n\n- `docs/atlas/ast-lsp-bindings/symbol-references.mmd`\n- `docs/atlas/ast-lsp-bindings/dead-code.md`\n- `docs/atlas/ast-lsp-bindings/mismatched-interfaces.md`\n- `docs/atlas/ast-lsp-bindings/README.md`\n\n**Caution:** The `CREDENTIAL_PATTERNS` for Layer 8 must use targeted `key=value` format patterns — not bare base64 scanning — to avoid false positives on legitimate symbol names that happen to be long alphanumeric strings.\n\n```python\nLAYER8_CREDENTIAL_PATTERNS = [\n # key=value patterns only (not bare base64, to avoid false positives on symbol names)\n (r'(?i)(password|passwd|secret|token|api_key|apikey|private_key)\\s*=\\s*\\S+',\n r'\\1=***REDACTED***'),\n (r'-----BEGIN.*?PRIVATE KEY-----.*?-----END.*?PRIVATE KEY-----',\n '***REDACTED PRIVATE KEY***'),\n (r'https?://[^@\\s]+@', # URLs with embedded credentials\n 'https://***@'),\n]\n```\n\n---\n\n### SEC-16: Relative-Path Enforcement in Evidence Fields (MEDIUM)\n\n**Rule:** All `file:line` evidence references in Pass 3 verdict blocks and in all bug reports must be relative to `codebase_path`. Absolute paths are rejected.\n\n```python\nimport os\n\ndef validate_relative_path(path: str, codebase_path: str) -> str:\n \"\"\"Ensure path is relative to codebase_path and stays within it.\"\"\"\n if os.path.isabs(path):\n raise SecurityError(f\"Absolute path in evidence: {path!r}. Must be relative.\")\n canonical = os.path.realpath(os.path.join(codebase_path, path))\n if not canonical.startswith(os.path.realpath(codebase_path)):\n raise SecurityError(f\"Path escapes codebase root: {path!r}\")\n return os.path.relpath(canonical, os.path.realpath(codebase_path))\n```\n\n**Anti-pattern:** Never write `/home/user/project/src/orders.ts:47` in an evidence field. Write `src/orders.ts:47` instead.\n\n---\n\n### SEC-17: Recipe YAML Parameter Injection Prevention (HIGH)\n\n**Rule:** Recipe YAML parameters (`codebase_path`, `output_dir`) must be passed as structured data to sub-skills — never interpolated into shell command strings. Use `yaml.safe_load()` for recipe loading. Validate `codebase_path` at recipe entry.\n\n**Validation at recipe entry:**\n\n```python\nimport yaml\nimport re\n\nNULL_BYTE_PATTERN = re.compile(r'\\x00')\nSHELL_META_PATTERN = re.compile(r'[;&|`$>\u003c\\\\!]')\n\ndef validate_recipe_path(raw: str) -> str:\n \"\"\"Validate a path parameter from recipe YAML.\"\"\"\n if NULL_BYTE_PATTERN.search(raw):\n raise ValueError(f\"Null byte in codebase_path: {raw!r}\")\n if SHELL_META_PATTERN.search(raw):\n raise ValueError(f\"Shell metacharacter in codebase_path: {raw!r}\")\n return raw\n\n# Safe recipe loading (never yaml.load())\nwith open(\"amplifier-bundle/recipes/code-atlas.yaml\") as f:\n recipe = yaml.safe_load(f)\n\ncodebase_path = validate_recipe_path(recipe[\"parameters\"][\"codebase_path\"])\n```\n\n---\n\n### SEC-18: Experiment Filename Date/Layer Validation (LOW)\n\n**Rule:** Experiment filenames under `docs/atlas/experiments/` use system date (`datetime.date.today().isoformat()`) and a validated layer ID from the allowlist `{1, 2, 3, 4, 5, 6, 7, 8}`. The layer ID must never come from raw user input without integer validation against this allowlist.\n\n```python\nimport datetime\n\nVALID_LAYER_IDS = frozenset({1, 2, 3, 4, 5, 6, 7, 8})\n\ndef experiment_filename(layer_id: any, renderer: str) -> str:\n if not isinstance(layer_id, int) or layer_id not in VALID_LAYER_IDS:\n raise ValueError(f\"Layer ID must be in {VALID_LAYER_IDS}, got: {layer_id!r}\")\n safe_renderer = re.sub(r'[^a-z-]', '', renderer.lower())[:20]\n date_str = datetime.date.today().isoformat()\n return f\"{date_str}-{safe_renderer}-L{layer_id}.md\"\n```\n\n---\n\n### SEC-19: Git Push Output Credential Sanitization (HIGH)\n\n**Rule:** Git push stdout and stderr must be sanitised with the `CREDENTIAL_URL_PATTERN` before any display or logging. Replace embedded credentials in URLs with `https://***@`.\n\n```python\nimport re\nimport subprocess\n\nCREDENTIAL_URL_PATTERN = re.compile(r'https?://[^@\\s]+@')\n\ndef safe_git_push(remote: str, branch: str) -> None:\n result = subprocess.run(\n [\"git\", \"push\", remote, branch],\n capture_output=True, text=True\n )\n safe_stdout = CREDENTIAL_URL_PATTERN.sub('https://***@', result.stdout)\n safe_stderr = CREDENTIAL_URL_PATTERN.sub('https://***@', result.stderr)\n print(safe_stdout)\n if result.returncode != 0:\n raise RuntimeError(f\"git push failed: {safe_stderr}\")\n```\n\n---\n\n## SecureAtlasBuilder Pipeline\n\nAll layer implementations MUST follow this pipeline order:\n\n```\n 1. Receive codebase_path (validated by SEC-02 at skill entry; SEC-17 if from recipe YAML)\n 2. Discover files (SEC-07: skip symlinks; SEC-08: skip >10MB)\n 3. Parse manifests/configs (SEC-04: safe parsers only)\n 4. Extract key names for env vars (SEC-01: values never collected)\n 5. Build node/edge data structures (plain Python objects — no shell)\n 6. Sanitize all labels (SEC-03: escape HTML specials; SEC-11 for Layer 7 service names)\n 7. Check density (ALL layers): if node_count > 50 OR edge_count > 100 → invoke density prompt\n a. Validate threshold override (SEC-13: integer range 1–10,000)\n b. Prompt user, accept only a/b/c (SEC-14: re-prompt on invalid input)\n 8. Generate diagram syntax (SEC-10: injection-safe label wrapping)\n 9. If Layer 8: sanitise all LSP output before embedding (SEC-12)\n10. Write to output_dir (SEC-05: path confinement validated)\n11. If Layer 8: apply LAYER8_CREDENTIAL_PATTERNS to all four output files (SEC-15)\n12. If writing bug reports or Pass 3 verdicts:\n a. Redact credentials (SEC-09)\n b. Validate all evidence paths are relative (SEC-16: reject absolute paths)\n13. If writing experiments/: validate layer ID from allowlist; use system date (SEC-18)\n14. If git push: sanitise stdout/stderr with CREDENTIAL_URL_PATTERN (SEC-19)\n```\n\n---\n\n## Per-Language Safe Parsing\n\n| Source | Safe Method | Unsafe — Never Use |\n| -------------------- | ------------------------------- | ------------------------------------------ |\n| `.env` files | `grep \"^[A-Z_]\" \\| cut -d= -f1` | `source .env`, `eval $(cat .env)` |\n| `docker-compose.yml` | `yaml.safe_load()`, `yq e` | `yaml.load()`, bash eval |\n| `package.json` | `json.load()`, `jq` | `eval`, `require()` with untrusted paths |\n| Go source | Regex on file content | `go run` with untrusted code |\n| `.csproj` | `xml.etree.ElementTree.parse()` | `lxml` with `resolve_entities=True` |\n| Kubernetes Secrets | Extract `metadata.name` only | Never read `data:` or `stringData:` blocks |\n\n---\n\n## Security Checklist\n\nBefore any layer implementation is considered complete:\n\n- [ ] SEC-01: Env var values are never written to any output file\n- [ ] SEC-02: All file reads use `realpath()` boundary validation\n- [ ] SEC-03: All diagram labels have HTML special characters escaped\n- [ ] SEC-04: All YAML/JSON parsed with safe loaders (no eval, no source)\n- [ ] SEC-05: All output files written inside `output_dir` with path validation\n- [ ] SEC-06: No shell=True subprocess calls with variable interpolation\n- [ ] SEC-07: Symlinks excluded from file discovery\n- [ ] SEC-08: Files >10MB skipped with SkillError logged\n- [ ] SEC-09: Bug report evidence fields (+ Layer 8 outputs + Pass 3 verdicts) scanned for credential patterns\n- [ ] SEC-10: DOT/Mermaid label strings are injection-safe (includes experiments/ output)\n- [ ] SEC-11: Layer 7 service names sanitised to `[a-zA-Z0-9_-]{1,64}` before path construction\n- [ ] SEC-12: Layer 8 LSP output validated (schema + SEC-03 + SEC-02 + SEC-09 applied to all fields)\n- [ ] SEC-13: `--density-threshold` values validated as integers in range 1–10,000\n- [ ] SEC-14: Density prompt only accepts `a`, `b`, `c`; re-prompts on any other input\n- [ ] SEC-15: LAYER8_CREDENTIAL_PATTERNS applied before writing all four Layer 8 output files\n- [ ] SEC-16: All evidence `file:line` references use `os.path.relpath()` — no absolute paths\n- [ ] SEC-17: Recipe YAML `codebase_path`/`output_dir` validated (no null bytes, no shell metacharacters)\n- [ ] SEC-18: Experiment filenames use system date + allowlisted layer ID only\n- [ ] SEC-19: Git push output sanitised with CREDENTIAL_URL_PATTERN before display or logging\n\n---\n\n_This document must be read before implementing Layer 1 (env discovery), Layer 3 (route extraction), Layer 6 (inventory tables), or the bug-hunting passes._\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":23797,"content_sha256":"03a5963a7ebe0c8e0a2c743d509d27b5d350f1c395b6d49903cb21a2357e1570"},{"filename":"tests/run_all_tests.sh","content":"#!/bin/bash\n# .claude/skills/code-atlas/tests/run_all_tests.sh\n#\n# Unified test runner for the code-atlas skill.\n# Runs all test suites and reports a combined pass/fail summary.\n#\n# Usage:\n# bash .claude/skills/code-atlas/tests/run_all_tests.sh\n# bash .claude/skills/code-atlas/tests/run_all_tests.sh --fast # skip integration tests\n#\n# Exit: 0 = all suites passed, non-zero = one or more failures\n#\n# Test Suites:\n# 1. test_staleness_triggers.sh — Layer detection for all 8 layer patterns\n# 2. test_rebuild_script.sh — rebuild-atlas-all.sh behaviors\n# 3. test_security_controls.sh — SEC-01 through SEC-10 controls\n# 4. test_atlas_output_structure.sh — docs/atlas/ output directory structure\n# 5. test_layer_contracts.sh — Per-layer content contracts (Layers 1–8)\n# 6. test_bug_hunt_workflow.sh — Three-pass bug hunt report format\n# 7. test_ci_workflow.sh — CI YAML structure and script path checks\n# 8. test_publication_workflow.sh — SVG generation and GitHub Pages readiness\n# 9. test_layer7_8.sh — Layer 7/8 output contracts + SEC-11/12/15/16\n# 10. test_no_silent_degradation.sh — Density guard FORBIDDEN_PATTERNS §2 compliance\n\n# Intentionally omits -e: test failures must not abort the suite runner.\n# Individual test scripts use set -euo pipefail.\nset -uo pipefail\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\n\nFAST_MODE=false\n[[ \"${1:-}\" == \"--fast\" ]] && FAST_MODE=true\n\n# ---------------------------------------------------------------------------\n# ANSI colors (if terminal supports them)\n# ---------------------------------------------------------------------------\nif [[ -t 1 ]]; then\n RED='\\033[0;31m'; GREEN='\\033[0;32m'; YELLOW='\\033[1;33m'; RESET='\\033[0m'\n BOLD='\\033[1m'\nelse\n RED=''; GREEN=''; YELLOW=''; RESET=''; BOLD=''\nfi\n\n# ---------------------------------------------------------------------------\n# Suite runner\n# ---------------------------------------------------------------------------\nTOTAL_PASS=0\nTOTAL_FAIL=0\nSUITE_RESULTS=()\n\nrun_suite() {\n local name=\"$1\"\n local script=\"$2\"\n local skip_in_fast=\"${3:-false}\"\n\n if [[ \"$FAST_MODE\" == \"true\" && \"$skip_in_fast\" == \"true\" ]]; then\n echo -e \"${YELLOW}SKIP${RESET}: $name (--fast mode)\"\n SUITE_RESULTS+=(\"SKIP: $name\")\n return\n fi\n\n echo \"\"\n echo -e \"${BOLD}━━━ Suite: $name ━━━${RESET}\"\n\n if [[ ! -f \"$script\" ]]; then\n echo -e \"${RED}ERROR${RESET}: Script not found: $script\"\n SUITE_RESULTS+=(\"ERROR: $name — script not found\")\n TOTAL_FAIL=$((TOTAL_FAIL + 1))\n return\n fi\n\n # Run the suite, capture output and exit code\n suite_output=$(bash \"$script\" 2>&1)\n suite_exit=$?\n\n # Show output\n echo \"$suite_output\"\n\n # Extract pass/fail counts — single grep, then pure bash string ops (no PCRE, no extra forks).\n local results_line\n results_line=$(grep -o 'Results: [0-9]* passed, [0-9]* failed' \u003c\u003c\u003c \"$suite_output\" | tail -1)\n if [[ -n \"$results_line\" ]]; then\n pass_count=\"${results_line#Results: }\"; pass_count=\"${pass_count%% *}\"\n fail_count=\"${results_line##*, }\"; fail_count=\"${fail_count%% *}\"\n else\n pass_count=0\n fail_count=0\n fi\n\n TOTAL_PASS=$((TOTAL_PASS + pass_count))\n TOTAL_FAIL=$((TOTAL_FAIL + fail_count))\n\n if [[ \"$suite_exit\" -eq 0 ]]; then\n SUITE_RESULTS+=(\"$(echo -e \"${GREEN}PASS${RESET}\"): $name ($pass_count passed)\")\n else\n SUITE_RESULTS+=(\"$(echo -e \"${RED}FAIL${RESET}\"): $name ($pass_count passed, $fail_count failed)\")\n fi\n}\n\n# ---------------------------------------------------------------------------\n# Header\n# ---------------------------------------------------------------------------\necho \"\"\necho -e \"${BOLD}╔════════════════════════════════════════╗${RESET}\"\necho -e \"${BOLD}║ Code Atlas — TDD Test Runner ║${RESET}\"\necho -e \"${BOLD}╚════════════════════════════════════════╝${RESET}\"\necho \"\"\necho \"Running all test suites...\"\n[[ \"$FAST_MODE\" == \"true\" ]] && echo \"(Fast mode: integration tests skipped)\"\n\n# ---------------------------------------------------------------------------\n# Suite 1: Staleness Triggers\n# ---------------------------------------------------------------------------\nrun_suite \\\n \"Staleness Triggers (check-atlas-staleness.sh)\" \\\n \"${SCRIPT_DIR}/test_staleness_triggers.sh\"\n\n# ---------------------------------------------------------------------------\n# Suite 2: Rebuild Script\n# ---------------------------------------------------------------------------\nrun_suite \\\n \"Rebuild Script (rebuild-atlas-all.sh)\" \\\n \"${SCRIPT_DIR}/test_rebuild_script.sh\"\n\n# ---------------------------------------------------------------------------\n# Suite 3: Security Controls\n# ---------------------------------------------------------------------------\nrun_suite \\\n \"Security Controls (SEC-01 through SEC-10)\" \\\n \"${SCRIPT_DIR}/test_security_controls.sh\"\n\n# ---------------------------------------------------------------------------\n# Suite 4: Atlas Output Structure\n# ---------------------------------------------------------------------------\nrun_suite \\\n \"Atlas Output Structure (docs/atlas/ directory contract)\" \\\n \"${SCRIPT_DIR}/test_atlas_output_structure.sh\" \\\n \"false\" # not skipped in fast mode — but tests will fail until /code-atlas runs\n\n# ---------------------------------------------------------------------------\n# Suite 5: Layer Content Contracts\n# ---------------------------------------------------------------------------\nrun_suite \\\n \"Layer Content Contracts (per-layer output requirements)\" \\\n \"${SCRIPT_DIR}/test_layer_contracts.sh\"\n\n# ---------------------------------------------------------------------------\n# Suite 6: Bug Hunt Workflow\n# ---------------------------------------------------------------------------\nrun_suite \\\n \"Bug Hunt Workflow (Pass 1 + Pass 2 report format)\" \\\n \"${SCRIPT_DIR}/test_bug_hunt_workflow.sh\"\n\n# ---------------------------------------------------------------------------\n# Suite 7: CI Workflow\n# ---------------------------------------------------------------------------\nrun_suite \\\n \"CI Workflow (atlas-ci.yml structure + integration)\" \\\n \"${SCRIPT_DIR}/test_ci_workflow.sh\"\n\n# ---------------------------------------------------------------------------\n# Suite 8: Publication Workflow\n# ---------------------------------------------------------------------------\nrun_suite \\\n \"Publication Workflow (SVG generation + GitHub Pages readiness)\" \\\n \"${SCRIPT_DIR}/test_publication_workflow.sh\"\n\n# ---------------------------------------------------------------------------\n# Suite 9: Layer 7 and Layer 8 Contracts (v1.1.0)\n# ---------------------------------------------------------------------------\nrun_suite \\\n \"Layer 7 and 8 Contracts (service components + AST/LSP bindings)\" \\\n \"${SCRIPT_DIR}/test_layer7_8.sh\"\n\n# ---------------------------------------------------------------------------\n# Suite 10: No Silent Degradation — FORBIDDEN_PATTERNS §2 Compliance (v1.1.0)\n# ---------------------------------------------------------------------------\nrun_suite \\\n \"No Silent Degradation (density guard + FORBIDDEN_PATTERNS §2)\" \\\n \"${SCRIPT_DIR}/test_no_silent_degradation.sh\"\n\n# ---------------------------------------------------------------------------\n# Summary\n# ---------------------------------------------------------------------------\necho \"\"\necho -e \"${BOLD}════════════════════════════════════════${RESET}\"\necho -e \"${BOLD} Test Suite Summary ${RESET}\"\necho -e \"${BOLD}════════════════════════════════════════${RESET}\"\necho \"\"\nfor result in \"${SUITE_RESULTS[@]}\"; do\n echo \" $result\"\ndone\necho \"\"\necho -e \" Total: ${GREEN}${TOTAL_PASS} passed${RESET}, ${RED}${TOTAL_FAIL} failed${RESET}\"\necho \"\"\n\nif [[ \"$TOTAL_FAIL\" -gt 0 ]]; then\n echo -e \"${RED}Some tests failed.${RESET}\"\n echo \"\"\n echo \"Expected failures (require /code-atlas to run first):\"\n echo \" • test_atlas_output_structure.sh: docs/atlas/ doesn't exist (run /code-atlas first)\"\n echo \" • test_layer_contracts.sh: docs/atlas/ doesn't exist (run /code-atlas first)\"\n echo \" • test_publication_workflow.sh: SVGs not generated (run /code-atlas publish first)\"\n echo \" • test_bug_hunt_workflow.sh: bug reports not generated (run /code-atlas first)\"\n echo \" • test_layer7_8.sh (output structure section): layer7/8 dirs not yet created (run /code-atlas first)\"\n echo \" • test_no_silent_degradation.sh: should pass on documentation alone (no atlas run needed)\"\n echo \"\"\n echo \"Unexpected failures need investigation.\"\n exit 1\nelse\n echo -e \"${GREEN}All 10 test suites passed. Atlas skill is fully implemented.${RESET}\"\n exit 0\nfi\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":9212,"content_sha256":"5dd97ee445b1198706d42a79ab4c556b8599e9530ccb8107d5c109871c7f1881"},{"filename":"tests/test_atlas_output_structure.sh","content":"#!/bin/bash\n# .claude/skills/code-atlas/tests/test_atlas_output_structure.sh\n#\n# TDD tests for atlas output directory structure and file naming contracts.\n# Validates that when /code-atlas runs on a fixture codebase, the output\n# at docs/atlas/ matches the exact structure defined in SKILL.md.\n#\n# THESE TESTS WILL FAIL until the atlas layer generation is implemented.\n#\n# Usage: bash .claude/skills/code-atlas/tests/test_atlas_output_structure.sh [fixture_dir]\n# Exit: 0 = all tests passed, non-zero = failures\n\nset -uo pipefail\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nREPO_ROOT=\"$(cd \"$SCRIPT_DIR/../../../..\" && pwd)\"\nFIXTURES_DIR=\"${SCRIPT_DIR}/fixtures\"\n\nPASS=0\nFAIL=0\n\n# ---------------------------------------------------------------------------\n# Test harness\n# ---------------------------------------------------------------------------\nassert_file_exists() {\n local label=\"$1\"\n local path=\"$2\"\n if [[ -f \"$path\" ]]; then\n echo \"PASS: $label\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: $label — file not found: $path\"\n FAIL=$((FAIL + 1))\n fi\n}\n\nassert_dir_exists() {\n local label=\"$1\"\n local path=\"$2\"\n if [[ -d \"$path\" ]]; then\n echo \"PASS: $label\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: $label — directory not found: $path\"\n FAIL=$((FAIL + 1))\n fi\n}\n\nassert_file_contains() {\n local label=\"$1\"\n local pattern=\"$2\"\n local file=\"$3\"\n if [[ ! -f \"$file\" ]]; then\n echo \"FAIL: $label — file not found: $file\"\n FAIL=$((FAIL + 1))\n return\n fi\n if grep -q \"$pattern\" \"$file\" 2>/dev/null; then\n echo \"PASS: $label\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: $label — pattern '$pattern' not found in $file\"\n FAIL=$((FAIL + 1))\n fi\n}\n\nassert_file_not_empty() {\n local label=\"$1\"\n local file=\"$2\"\n if [[ ! -f \"$file\" ]]; then\n echo \"FAIL: $label — file not found: $file\"\n FAIL=$((FAIL + 1))\n return\n fi\n if [[ -s \"$file\" ]]; then\n echo \"PASS: $label\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: $label — file is empty: $file\"\n FAIL=$((FAIL + 1))\n fi\n}\n\nassert_valid_mermaid() {\n local label=\"$1\"\n local file=\"$2\"\n if [[ ! -f \"$file\" ]]; then\n echo \"FAIL: $label — .mmd file not found: $file\"\n FAIL=$((FAIL + 1))\n return\n fi\n # A valid Mermaid file must start with a diagram type keyword\n first_token=$(head -3 \"$file\" | grep -oE \"^(graph|flowchart|sequenceDiagram|classDiagram|stateDiagram|erDiagram|journey|gantt|pie|gitGraph)\" | head -1 || true)\n if [[ -n \"$first_token\" ]]; then\n echo \"PASS: $label (starts with: $first_token)\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: $label — not a valid Mermaid diagram (must start with diagram type keyword)\"\n echo \" First 3 lines of $file:\"\n head -3 \"$file\" | sed 's/^/ /'\n FAIL=$((FAIL + 1))\n fi\n}\n\nassert_valid_dot() {\n local label=\"$1\"\n local file=\"$2\"\n if [[ ! -f \"$file\" ]]; then\n echo \"FAIL: $label — .dot file not found: $file\"\n FAIL=$((FAIL + 1))\n return\n fi\n # Valid DOT file must contain digraph or graph keyword\n if grep -qE \"^(di)?graph\\s\" \"$file\" 2>/dev/null; then\n echo \"PASS: $label\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: $label — not a valid DOT file (must start with 'graph' or 'digraph')\"\n FAIL=$((FAIL + 1))\n fi\n}\n\nassert_markdown_table() {\n local label=\"$1\"\n local file=\"$2\"\n local min_rows=\"${3:-1}\"\n if [[ ! -f \"$file\" ]]; then\n echo \"FAIL: $label — inventory file not found: $file\"\n FAIL=$((FAIL + 1))\n return\n fi\n # Count table separator rows (|---|) as a proxy for having a proper table\n separator_count=$(grep -c \"^|[-| ]*|$\" \"$file\" 2>/dev/null || echo \"0\")\n row_count=$(grep -c \"^|\" \"$file\" 2>/dev/null || echo \"0\")\n if [[ \"$row_count\" -ge \"$((min_rows + 2))\" ]]; then # header + separator + data rows\n echo \"PASS: $label ($row_count table rows including header)\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: $label — expected markdown table with at least $min_rows data rows, found $row_count total rows\"\n FAIL=$((FAIL + 1))\n fi\n}\n\n# ---------------------------------------------------------------------------\n# Create minimal Go fixture codebase (Scenario 1 from test_scenarios.md)\n# ---------------------------------------------------------------------------\nsetup_go_fixture() {\n local dir=\"$1\"\n mkdir -p \"$dir\"/{cmd/server,internal/{handlers,models}}\n\n # Entry point\n cat > \"$dir/cmd/server/main.go\" \u003c\u003c 'EOF'\npackage main\n\nimport (\n \"github.com/gin-gonic/gin\"\n \"myapp/internal/handlers\"\n)\n\nfunc main() {\n r := gin.Default()\n handlers.RegisterUserRoutes(r)\n r.Run(\":8080\")\n}\nEOF\n\n # Handler with routes\n cat > \"$dir/internal/handlers/user_handler.go\" \u003c\u003c 'EOF'\npackage handlers\n\nimport (\n \"net/http\"\n \"github.com/gin-gonic/gin\"\n)\n\nfunc RegisterUserRoutes(r *gin.Engine) {\n r.GET(\"/users\", ListUsers)\n r.POST(\"/users\", CreateUser)\n}\n\nfunc ListUsers(c *gin.Context) {\n c.JSON(http.StatusOK, gin.H{\"users\": []string{}})\n}\n\nfunc CreateUser(c *gin.Context) {\n c.JSON(http.StatusCreated, gin.H{\"id\": \"new-user\"})\n}\nEOF\n\n # Model\n cat > \"$dir/internal/models/user_model.go\" \u003c\u003c 'EOF'\npackage models\n\ntype User struct {\n ID string `json:\"id\"`\n Email string `json:\"email\"`\n Name string `json:\"name\"`\n}\nEOF\n\n # Docker compose\n cat > \"$dir/docker-compose.yml\" \u003c\u003c 'EOF'\nversion: \"3.8\"\nservices:\n api:\n build: .\n ports:\n - \"8080:8080\"\n environment:\n - DATABASE_URL\n - PORT\nEOF\n\n # Env example\n cat > \"$dir/.env.example\" \u003c\u003c 'EOF'\nDATABASE_URL=postgres://localhost/mydb\nPORT=8080\nEOF\n\n # Go module\n cat > \"$dir/go.mod\" \u003c\u003c 'EOF'\nmodule myapp\n\ngo 1.21\n\nrequire github.com/gin-gonic/gin v1.9.1\nEOF\n\n # Git init\n git -C \"$dir\" init -q\n git -C \"$dir\" config user.email \"[email protected]\"\n git -C \"$dir\" config user.name \"Test\"\n git -C \"$dir\" add .\n git -C \"$dir\" commit -q -m \"init go fixture\"\n}\n\n# ---------------------------------------------------------------------------\n# Test Suite: Fixture-based output structure validation\n# ---------------------------------------------------------------------------\n# NOTE: These tests create a fixture codebase, then expect /code-atlas to\n# have been run against it. Since we cannot invoke the Claude skill directly,\n# these tests validate the OUTPUT STRUCTURE that the skill must produce.\n# They FAIL until the atlas generation is implemented.\n# ---------------------------------------------------------------------------\n\nATLAS_DIR=\"${1:-${REPO_ROOT}/docs/atlas}\"\n\necho \"\"\necho \"=== Atlas Output Structure Tests ===\"\necho \"Testing against: $ATLAS_DIR\"\necho \"(Run /code-atlas first, then re-run these tests)\"\necho \"\"\n\n# ---------------------------------------------------------------------------\n# Test Group 1: Top-level structure\n# ---------------------------------------------------------------------------\n\necho \"--- Top-level directory structure ---\"\nassert_dir_exists \"docs/atlas/ directory exists\" \"$ATLAS_DIR\"\nassert_file_exists \"docs/atlas/index.md exists\" \"$ATLAS_DIR/index.md\"\nassert_file_contains \"index.md mentions Layer 1\" \"layer1\\|Layer 1\\|Runtime Topology\" \"$ATLAS_DIR/index.md\"\nassert_file_contains \"index.md mentions Layer 2\" \"layer2\\|Layer 2\\|Dependenc\" \"$ATLAS_DIR/index.md\"\nassert_file_contains \"index.md mentions Layer 3\" \"layer3\\|Layer 3\\|API|Contracts|Routing\\|API|Contracts|Routing\" \"$ATLAS_DIR/index.md\"\nassert_file_contains \"index.md mentions Layer 4\" \"layer4\\|Layer 4\\|Data Flow\" \"$ATLAS_DIR/index.md\"\nassert_file_contains \"index.md mentions Layer 5\" \"layer5\\|Layer 5\\|User Journey\\|Scenario\" \"$ATLAS_DIR/index.md\"\nassert_file_contains \"index.md mentions Layer 6\" \"layer6\\|Layer 6\\|Inventory\" \"$ATLAS_DIR/index.md\"\n\n# ---------------------------------------------------------------------------\n# Test Group 2: Layer 1 — Runtime Topology\n# ---------------------------------------------------------------------------\n\necho \"\"\necho \"--- Layer 1: Runtime Topology ---\"\nL1=\"$ATLAS_DIR/repo-surface\"\nassert_dir_exists \"repo-surface/ exists\" \"$L1\"\nassert_file_exists \"repo-surface/topology.dot\" \"$L1/topology.dot\"\nassert_file_exists \"repo-surface/topology.mmd\" \"$L1/topology.mmd\"\nassert_file_exists \"repo-surface/topology.svg\" \"$L1/topology.svg\"\nassert_file_exists \"repo-surface/README.md\" \"$L1/README.md\"\nassert_valid_dot \"topology.dot is valid DOT syntax\" \"$L1/topology.dot\"\nassert_valid_mermaid \"topology.mmd is valid Mermaid syntax\" \"$L1/topology.mmd\"\nassert_file_not_empty \"topology.svg is not empty\" \"$L1/topology.svg\"\nassert_file_contains \"topology.dot has at least one node\" \"label\\|graph\\|digraph\" \"$L1/topology.dot\"\n\n# ---------------------------------------------------------------------------\n# Test Group 3: Layer 2 — Compile-time Dependencies\n# ---------------------------------------------------------------------------\n\necho \"\"\necho \"--- Layer 2: Compile-time Dependencies ---\"\nL2=\"$ATLAS_DIR/compile-deps\"\nassert_dir_exists \"compile-deps/ exists\" \"$L2\"\nassert_file_exists \"compile-deps/dependencies.mmd\" \"$L2/dependencies.mmd\"\nassert_file_exists \"compile-deps/dependencies.svg\" \"$L2/dependencies.svg\"\nassert_file_exists \"compile-deps/inventory.md\" \"$L2/inventory.md\"\nassert_file_exists \"compile-deps/README.md\" \"$L2/README.md\"\nassert_valid_mermaid \"dependencies.mmd is valid Mermaid\" \"$L2/dependencies.mmd\"\nassert_markdown_table \"inventory.md has package table\" \"$L2/inventory.md\" 1\n\n# ---------------------------------------------------------------------------\n# Test Group 4: Layer 3 — API|Contracts|Routing API|Contracts|Routing\n# ---------------------------------------------------------------------------\n\necho \"\"\necho \"--- Layer 3: API Contracts ---\"\nL3=\"$ATLAS_DIR/api-contracts\"\nassert_dir_exists \"api-contracts/ exists\" \"$L3\"\nassert_file_exists \"api-contracts/routing.mmd\" \"$L3/routing.mmd\"\nassert_file_exists \"api-contracts/routing.svg\" \"$L3/routing.svg\"\nassert_file_exists \"api-contracts/route-inventory.md\" \"$L3/route-inventory.md\"\nassert_file_exists \"api-contracts/README.md\" \"$L3/README.md\"\nassert_valid_mermaid \"routing.mmd is valid Mermaid\" \"$L3/routing.mmd\"\nassert_markdown_table \"route-inventory.md has route rows\" \"$L3/route-inventory.md\" 1\n# Route inventory must have API|Contracts|Routing method column\nassert_file_contains \"route-inventory.md has Method column\" \"[Mm]ethod\\|GET\\|POST\\|PUT\\|DELETE\\|PATCH\" \"$L3/route-inventory.md\"\n# Route inventory must have Path column\nassert_file_contains \"route-inventory.md has Path column\" \"[Pp]ath\\|/\\|endpoint\" \"$L3/route-inventory.md\"\n\n# ---------------------------------------------------------------------------\n# Test Group 5: Layer 4 — Data Flows\n# ---------------------------------------------------------------------------\n\necho \"\"\necho \"--- Layer 4: Data Flows ---\"\nL4=\"$ATLAS_DIR/data-flow\"\nassert_dir_exists \"data-flow/ exists\" \"$L4\"\nassert_file_exists \"data-flow/dataflow.mmd\" \"$L4/dataflow.mmd\"\nassert_file_exists \"data-flow/dataflow.svg\" \"$L4/dataflow.svg\"\nassert_file_exists \"data-flow/README.md\" \"$L4/README.md\"\nassert_valid_mermaid \"dataflow.mmd is valid Mermaid\" \"$L4/dataflow.mmd\"\n\n# ---------------------------------------------------------------------------\n# Test Group 6: Layer 5 — User Journey Scenarios\n# ---------------------------------------------------------------------------\n\necho \"\"\necho \"--- Layer 5: User Journey Scenarios ---\"\nL5=\"$ATLAS_DIR/user-journeys\"\nassert_dir_exists \"user-journeys/ exists\" \"$L5\"\nassert_file_exists \"user-journeys/README.md\" \"$L5/README.md\"\n# At least one journey diagram must exist\njourney_count=$(find \"$L5\" -name \"journey-*.mmd\" 2>/dev/null | wc -l)\nif [[ \"$journey_count\" -ge 1 ]]; then\n echo \"PASS: layer5 has $journey_count journey diagram(s)\"\n PASS=$((PASS + 1))\nelse\n echo \"FAIL: layer5 must have at least one journey-*.mmd file\"\n FAIL=$((FAIL + 1))\nfi\n\n# ---------------------------------------------------------------------------\n# Test Group 7: Layer 6 — Inventory Tables\n# ---------------------------------------------------------------------------\n\necho \"\"\necho \"--- Layer 6: Inventory Tables ---\"\nL6=\"$ATLAS_DIR/inventory\"\nassert_dir_exists \"inventory/ exists\" \"$L6\"\nassert_file_exists \"inventory/services.md\" \"$L6/services.md\"\nassert_file_exists \"inventory/env-vars.md\" \"$L6/env-vars.md\"\nassert_file_exists \"inventory/data-stores.md\" \"$L6/data-stores.md\"\nassert_file_exists \"inventory/external-deps.md\" \"$L6/external-deps.md\"\nassert_markdown_table \"services.md has service rows\" \"$L6/services.md\" 1\nassert_markdown_table \"env-vars.md has env var rows\" \"$L6/env-vars.md\" 1\n\n# CRITICAL: env-vars.md must NOT contain real values\n# Check for common secret value patterns\nif [[ -f \"$L6/env-vars.md\" ]]; then\n if grep -qE \"=.{8,}\" \"$L6/env-vars.md\" 2>/dev/null; then\n # Found something that looks like key=value with a real value\n if grep -qE \"REDACTED|\\*\\*\\*\" \"$L6/env-vars.md\" 2>/dev/null; then\n echo \"PASS: env-vars.md: values are redacted\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: env-vars.md: may contain unredacted env var values (SEC-01)\"\n FAIL=$((FAIL + 1))\n fi\n else\n echo \"PASS: env-vars.md: no raw key=value assignments found\"\n PASS=$((PASS + 1))\n fi\nfi\n\n# ---------------------------------------------------------------------------\n# Test Group 8: Bug Reports from both passes\n# ---------------------------------------------------------------------------\n\necho \"\"\necho \"--- Bug Reports ---\"\nBR=\"$ATLAS_DIR/bug-reports\"\nassert_dir_exists \"bug-reports/ exists\" \"$BR\"\nassert_file_exists \"bug-reports/merged-findings.md\" \"$BR/merged-findings.md\"\nassert_file_exists \"bug-reports/validated-bugs.md\" \"$BR/validated-bugs.md\"\n\n# Bug reports must have required fields\nassert_file_contains \"merged report has Severity field\" \"[Ss]everity\" \"$BR/merged-findings.md\"\nassert_file_contains \"merged report has Evidence field\" \"[Ee]vidence\\|Code quote\\|code_quote\" \"$BR/merged-findings.md\"\nassert_file_contains \"validated report has journey reference\" \"[Jj]ourney\\|scenario\\|[Uu]ser\" \"$BR/validated-bugs.md\"\n\n# ---------------------------------------------------------------------------\n# Test Group 9: .build-stamp freshness metadata\n# ---------------------------------------------------------------------------\n\necho \"\"\necho \"--- Build Stamp ---\"\nassert_file_exists \"docs/atlas/.build-stamp exists\" \"$ATLAS_DIR/.build-stamp\"\nif [[ -f \"$ATLAS_DIR/.build-stamp\" ]]; then\n assert_file_contains \".build-stamp has git hash\" \"[0-9a-f]\\{7,40\\}\" \"$ATLAS_DIR/.build-stamp\"\n assert_file_contains \".build-stamp has date\" \"[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}\" \"$ATLAS_DIR/.build-stamp\"\nfi\n\n# ---------------------------------------------------------------------------\n# Results\n# ---------------------------------------------------------------------------\necho \"\"\necho \"==================================\"\necho \"Results: ${PASS} passed, ${FAIL} failed\"\necho \"==================================\"\necho \"\"\necho \"NOTE: Most failures are expected — this is a TDD test suite.\"\necho \"Implement the /code-atlas skill to make these tests pass.\"\n\n[[ $FAIL -eq 0 ]] && exit 0 || exit 1\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":15747,"content_sha256":"9b7f1cca6f0e2721dce5fa885d703ffe50fb7bf9c08c9b88d2182e1355ec574d"},{"filename":"tests/test_bug_hunt_workflow.sh","content":"#!/bin/bash\n# .claude/skills/code-atlas/tests/test_bug_hunt_workflow.sh\n#\n# TDD tests for the three-pass bug-hunting workflow.\n# Tests validate that the bug-hunt workflow produces structured, evidence-backed\n# bug reports with the correct format and without security violations.\n#\n# Pass 1: Contradiction hunt — find route/DTO mismatches, orphaned env vars, dead paths\n# Pass 2: Fresh-eyes cross-check — independent re-examination + confirm/overturn Pass 1 findings\n# Pass 3: Scenario deep-dive — per-journey PASS/FAIL/NEEDS_ATTENTION verdicts\n#\n# THESE TESTS WILL FAIL until the bug-hunt workflow is implemented.\n#\n# Usage: bash .claude/skills/code-atlas/tests/test_bug_hunt_workflow.sh\n# Exit: 0 = all tests passed, non-zero = failures\n\nset -uo pipefail\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nREPO_ROOT=\"$(cd \"$SCRIPT_DIR/../../../..\" && pwd)\"\n\nPASS=0\nFAIL=0\n\n# ---------------------------------------------------------------------------\n# Test harness\n# ---------------------------------------------------------------------------\nassert_contains() {\n local label=\"$1\"; local pattern=\"$2\"; local content=\"$3\"\n if echo \"$content\" | grep -q \"$pattern\"; then\n echo \"PASS: $label\"; PASS=$((PASS + 1))\n else\n echo \"FAIL: $label — pattern '$pattern' not found\"\n echo \" Content preview: ${content:0:200}\"\n FAIL=$((FAIL + 1))\n fi\n}\n\nassert_file_contains() {\n local label=\"$1\"; local pattern=\"$2\"; local file=\"$3\"\n if [[ ! -f \"$file\" ]]; then\n echo \"FAIL: $label — file not found: $file\"; FAIL=$((FAIL + 1)); return\n fi\n if grep -q \"$pattern\" \"$file\" 2>/dev/null; then\n echo \"PASS: $label\"; PASS=$((PASS + 1))\n else\n echo \"FAIL: $label — '$pattern' not in $file\"; FAIL=$((FAIL + 1))\n fi\n}\n\nassert_not_in_file() {\n local label=\"$1\"; local pattern=\"$2\"; local file=\"$3\"\n if [[ ! -f \"$file\" ]]; then\n echo \"FAIL: $label — file not found: $file\"; FAIL=$((FAIL + 1)); return\n fi\n if grep -q \"$pattern\" \"$file\" 2>/dev/null; then\n echo \"FAIL: $label — forbidden '$pattern' found in $file\"; FAIL=$((FAIL + 1))\n else\n echo \"PASS: $label\"; PASS=$((PASS + 1))\n fi\n}\n\n# ---------------------------------------------------------------------------\n# Bug Report Format Contract\n# ---------------------------------------------------------------------------\n# Every bug report entry must conform to this structure:\n#\n# ## BUG-NNN: Short description\n# **Severity:** CRITICAL | HIGH | MEDIUM | LOW\n# **Layer:** X → Y (where detected, from which layer context)\n# **Evidence:**\n# - Layer N: \u003cobservation>\n# - Layer M: \u003cobservation>\n# **Code quote:**\n# ```\n# \u003cactual code from codebase>\n# ```\n# **Recommendation:** \u003cactionable fix>\n# ---------------------------------------------------------------------------\n\nATLAS=\"${REPO_ROOT}/docs/atlas\"\nP1_REPORT=\"${ATLAS}/bug-reports/mermaid-arm/pass1-findings.md\"\nP2_REPORT=\"${ATLAS}/bug-reports/graphviz-arm/pass1-findings.md\"\n\n# ============================================================================\n# Test Group 1: Bug Report Schema Validation\n# ============================================================================\n\necho \"\"\necho \"=== Bug Report Schema Tests ===\"\n\nvalidate_bug_report_schema() {\n local report_file=\"$1\"\n local report_name=\"$2\"\n\n if [[ ! -f \"$report_file\" ]]; then\n echo \"FAIL: $report_name — file does not exist: $report_file\"\n FAIL=$((FAIL + 6))\n return\n fi\n\n # 1.1: Must have at least one BUG entry header\n bug_count=$(grep -cE \"^## BUG-[0-9]+\" \"$report_file\" 2>/dev/null || echo 0)\n if [[ \"$bug_count\" -ge 1 ]]; then\n echo \"PASS: $report_name — has $bug_count BUG-NNN entries\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: $report_name — no BUG-NNN entries found (need ## BUG-001 format)\"\n FAIL=$((FAIL + 1))\n fi\n\n # 1.2: Every BUG entry must have Severity\n severity_count=$(grep -cE \"\\*\\*Severity:\\*\\*\" \"$report_file\" 2>/dev/null || echo 0)\n if [[ \"$severity_count\" -ge \"$bug_count\" && \"$bug_count\" -gt 0 ]]; then\n echo \"PASS: $report_name — all $bug_count bugs have Severity field\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: $report_name — $severity_count Severity fields for $bug_count bugs\"\n FAIL=$((FAIL + 1))\n fi\n\n # 1.3: Severity values must be valid\n assert_file_contains \"$report_name: has valid severity level\" \\\n \"Severity.*\\(CRITICAL\\|HIGH\\|MEDIUM\\|LOW\\)\" \"$report_file\"\n\n # 1.4: Must have Evidence fields\n evidence_count=$(grep -cE \"\\*\\*Evidence:\\*\\*|\\*\\*Code quote:\\*\\*\" \"$report_file\" 2>/dev/null || echo 0)\n if [[ \"$evidence_count\" -ge \"$bug_count\" && \"$bug_count\" -gt 0 ]]; then\n echo \"PASS: $report_name — all bugs have Evidence/Code quote\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: $report_name — $evidence_count evidence sections for $bug_count bugs\"\n FAIL=$((FAIL + 1))\n fi\n\n # 1.5: Must have Recommendation fields\n rec_count=$(grep -cE \"\\*\\*Recommendation:\\*\\*\" \"$report_file\" 2>/dev/null || echo 0)\n if [[ \"$rec_count\" -ge \"$bug_count\" && \"$bug_count\" -gt 0 ]]; then\n echo \"PASS: $report_name — all bugs have Recommendation\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: $report_name — $rec_count Recommendations for $bug_count bugs\"\n FAIL=$((FAIL + 1))\n fi\n\n # 1.6: SEC-09 — no raw secret values in code quotes\n assert_not_in_file \"$report_name SEC-09: no raw passwords\" \\\n \"password\\s*=\\s*[a-zA-Z0-9!@#$%^&*]\\{4,\\}\" \"$report_file\"\n assert_not_in_file \"$report_name SEC-09: no connection strings with passwords\" \\\n \"://.*:.*@\" \"$report_file\"\n}\n\nvalidate_bug_report_schema \"$P1_REPORT\" \"mermaid-arm-findings\"\nvalidate_bug_report_schema \"$P2_REPORT\" \"graphviz-arm-findings\"\n\n# ============================================================================\n# Test Group 2: Pass 1 Contradiction Hunt — Specific Detections\n# ============================================================================\n\necho \"\"\necho \"=== Pass 1: Specific Contradiction Detections ===\"\n\n# CONTRACT: The Go fixture has these known contradictions to detect:\n# - user_model.go declares Password field with json:\"-\" (hidden in API but visible in code)\n# - .env.example has JWT_SECRET but no route handler explicitly reads JWT_SECRET\n# - CreateUserRequest requires 3 fields but POST /api/users doesn't validate them explicitly\n\n# 2.1: Test that route/DTO mismatch detection produces entries in Pass 1 report\n# This simulates a codebase where a route expects a DTO that doesn't exist yet\ntmpdir=$(mktemp -d)\nmkdir -p \"$tmpdir/docs/atlas/bug-reports\"\n\n# Create a pass1 report that correctly identifies a route/DTO mismatch\ncat > \"$tmpdir/docs/atlas/bug-reports/mermaid-arm/pass1-findings.md\" \u003c\u003c 'EOF'\n# Pass 1 Bug Report — Contradiction Hunt\n\nGenerated: 2026-03-16T10:00:00Z\n\n## BUG-001: Route POST /api/orders references missing OrderRequest DTO\n\n**Severity:** HIGH\n**Layer:** 3 → 4\n\n**Evidence:**\n- Layer 3 (route-inventory.md line 12): `POST /api/orders → handler CreateOrder`\n- Layer 4 (dataflow.mmd): No `OrderRequest` or `CreateOrderRequest` struct found\n\n**Code quote:**\n```go\n// internal/handlers/order_handler.go line 23\nr.POST(\"/api/orders\", CreateOrder)\n```\n\n**Recommendation:** Create `internal/models/order_model.go` with `CreateOrderRequest` struct,\nor update the route handler to reference an existing DTO.\n\n## BUG-002: Orphaned env var PAYMENT_SECRET not consumed by any handler\n\n**Severity:** MEDIUM\n**Layer:** 6 → 3\n\n**Evidence:**\n- Layer 6 (env-vars.md line 5): `PAYMENT_SECRET` declared in .env.example\n- Layer 3 (route-inventory.md): No handler references `PAYMENT_SECRET` or payment routes\n\n**Code quote:**\n```\n# .env.example line 5\nPAYMENT_SECRET=***REDACTED***\n```\n\n**Recommendation:** Either add a payment route/handler that consumes PAYMENT_SECRET,\nor remove it from .env.example if the payment feature is not implemented.\nEOF\n\nvalidate_bug_report_schema \"$tmpdir/docs/atlas/bug-reports/mermaid-arm/pass1-findings.md\" \"fixture-mermaid-arm\"\n\n# 2.2: Route/DTO mismatch is the primary Pass 1 contradiction type\nassert_file_contains \"P1 fixture: route/DTO mismatch detected\" \\\n \"[Rr]oute.*[Mm]issing\\|DTO.*not found\\|route.*DTO\\|missing.*DTO\\|[Mm]ismatch\" \\\n \"$tmpdir/docs/atlas/bug-reports/mermaid-arm/pass1-findings.md\"\n\n# 2.3: Orphaned env var detection\nassert_file_contains \"P1 fixture: orphaned env var detected\" \\\n \"[Oo]rphan\\|env.*var.*not.*consumed\\|PAYMENT_SECRET\" \\\n \"$tmpdir/docs/atlas/bug-reports/mermaid-arm/pass1-findings.md\"\n\n# 2.4: Values are redacted in evidence\nassert_not_in_file \"P1 fixture SEC-09: no raw secret values\" \\\n \"payment-secret-value\\|real-secret-here\" \\\n \"$tmpdir/docs/atlas/bug-reports/mermaid-arm/pass1-findings.md\"\n\nrm -rf \"$tmpdir\"\n\n# ============================================================================\n# Test Group 3: Pass 2 Journey Trace — Specific Journey Coverage\n# ============================================================================\n\necho \"\"\necho \"=== Pass 2: Journey Trace Coverage ===\"\n\ntmpdir=$(mktemp -d)\nmkdir -p \"$tmpdir/docs/atlas/bug-reports\"\n\n# Create a well-formed pass2 report\ncat > \"$tmpdir/docs/atlas/bug-reports/graphviz-arm/pass1-findings.md\" \u003c\u003c 'EOF'\n# Pass 2 Bug Report — User Journey Trace\n\nGenerated: 2026-03-16T10:05:00Z\n\n## Journey: User Registration\n\nSteps traced through atlas layers:\n1. User submits POST /api/users (Layer 3: route)\n2. Handler calls CreateUser function (Layer 3: handler)\n3. CreateUserRequest deserialized (Layer 4: DTO)\n4. User struct created and stored (Layer 4: model)\n\n## BUG-003: CreateUserRequest.Password validation bypassed in test route\n\n**Severity:** HIGH\n**Layer:** 3 → 4 (journey: User Registration, step 3)\n\n**Evidence:**\n- Layer 4 (user_model.go): CreateUserRequest has `binding:\"required\"` on Password\n- Layer 3 (user_handler.go test route): A test endpoint POST /api/test-users skips binding validation\n- Journey trace: Registration journey step 3 can succeed without password via test endpoint\n\n**Code quote:**\n```go\n// internal/handlers/test_handler.go line 8\nr.POST(\"/api/test-users\", func(c *gin.Context) {\n // NOTE: No binding validation - allows empty password\n var u models.User\n c.BindJSON(&u)\n c.JSON(201, u)\n})\n```\n\n**Recommendation:** Remove POST /api/test-users from production code or add equivalent validation.\n\n## Journey: User Listing (Admin)\n\nSteps traced:\n1. Client sends GET /api/users (Layer 3: route)\n2. ListUsers handler returns all users (Layer 3: handler)\n3. User struct serialized to JSON (Layer 4: model)\n\n## BUG-004: User.Password field exposed via JSON serialization despite json:\"-\" tag\n\n**Severity:** CRITICAL\n**Layer:** 4 → 3 (journey: User Listing, step 3)\n\n**Evidence:**\n- Layer 4 (user_model.go): User.Password has json:\"-\" tag (excluded from JSON)\n- Layer 4 (dataflow.mmd): ListUsers returns []User, which includes Password\n- Journey trace: GET /api/users response includes Password field in some test configurations\n\n**Code quote:**\n```go\n// internal/models/user_model.go line 7\nPassword string `json:\"-\"` // Should be excluded from JSON responses\n```\n\n**Recommendation:** Verify Password is excluded in all serialization paths. Add integration test\nasserting Password field is absent from GET /api/users response body.\nEOF\n\nvalidate_bug_report_schema \"$tmpdir/docs/atlas/bug-reports/graphviz-arm/pass1-findings.md\" \"fixture-graphviz-arm\"\n\n# 3.1: Pass 2 must reference journey names\nassert_file_contains \"P2: references journey names\" \\\n \"[Jj]ourney:\\|[Ss]cenario:\" \\\n \"$tmpdir/docs/atlas/bug-reports/graphviz-arm/pass1-findings.md\"\n\n# 3.2: Pass 2 must trace steps through layers\nassert_file_contains \"P2: traces steps through layers\" \\\n \"[Ss]tep.*Layer\\|Layer.*step\\|journey.*step\\|[Ss]teps traced\" \\\n \"$tmpdir/docs/atlas/bug-reports/graphviz-arm/pass1-findings.md\"\n\n# 3.3: Pass 2 bugs must reference the originating journey\nassert_file_contains \"P2: BUG entries reference journey\" \\\n \"journey:\\|[Ss]cenario:\" \\\n \"$tmpdir/docs/atlas/bug-reports/graphviz-arm/pass1-findings.md\"\n\n# 3.4: Layer references in Pass 2 must be in format \"Layer N → M\"\nassert_file_contains \"P2: layer cross-references\" \\\n \"Layer [1-6].*→.*[1-6]\\|[1-6] → [1-6]\" \\\n \"$tmpdir/docs/atlas/bug-reports/graphviz-arm/pass1-findings.md\"\n\nrm -rf \"$tmpdir\"\n\n# ============================================================================\n# Test Group 4: Bug Hunt Completeness Checks\n# ============================================================================\n\necho \"\"\necho \"=== Bug Hunt Completeness ===\"\n\n# 4.1: Both pass reports must exist after a full atlas run\nif [[ -f \"$P1_REPORT\" ]] && [[ -f \"$P2_REPORT\" ]]; then\n echo \"PASS: both pass reports exist\"\n PASS=$((PASS + 1))\nelse\n missing=\"\"\n [[ ! -f \"$P1_REPORT\" ]] && missing=\"mermaid-arm-findings.md\"\n [[ ! -f \"$P2_REPORT\" ]] && missing=\"$missing graphviz-arm-findings.md\"\n echo \"FAIL: missing bug reports:$missing\"\n FAIL=$((FAIL + 1))\nfi\n\n# 4.2: Pass 1 must document the DETECTION METHODOLOGY\nif [[ -f \"$P1_REPORT\" ]]; then\n assert_file_contains \"P1: documents contradiction types checked\" \\\n \"route.*DTO\\|DTO.*route\\|orphan\\|dead.*path\\|stale.*doc\\|mismatch\" \"$P1_REPORT\"\nfi\n\n# 4.3: Pass 2 must reference Pass 1 or follow-up on its findings\nif [[ -f \"$P2_REPORT\" ]]; then\n # Pass 2 is journey-focused — it must trace specific routes\n assert_file_contains \"P2: references specific routes or endpoints\" \\\n \"/api/\\|GET\\|POST\\|PUT\\|DELETE\" \"$P2_REPORT\"\nfi\n\n# ============================================================================\n# Test Group 5: SKILL.md Protocol Compliance\n# ============================================================================\n\necho \"\"\necho \"=== Protocol Compliance ===\"\n\nSKILL=\"${REPO_ROOT}/.claude/skills/code-atlas/SKILL.md\"\n\n# 5.1: SKILL.md must define the two-pass structure\nassert_file_contains \"SKILL.md: Pass 1 defined\" \"[Pp]ass 1\\|pass.1\\|First pass\" \"$SKILL\"\nassert_file_contains \"SKILL.md: Pass 2 defined\" \"[Pp]ass 2\\|pass.2\\|Second pass\" \"$SKILL\"\n\n# 5.2: SKILL.md must define contradiction types for Pass 1\nassert_file_contains \"SKILL.md: route/DTO mismatch mentioned\" \"route.*DTO\\|DTO.*route\\|mismatch\\|contradict\" \"$SKILL\"\n\n# 5.3: SKILL.md must define user journey concept\nassert_file_contains \"SKILL.md: user journey concept defined\" \"[Uu]ser.*[Jj]ourney\\|journey.*scenario\\|scenario\" \"$SKILL\"\n\n# 5.4: SKILL.md must describe bug evidence format\nassert_file_contains \"SKILL.md: bug evidence format defined\" \"[Ee]vidence\\|code.*quote\\|code_quote\" \"$SKILL\"\n\n# 5.5: SECURITY.md must exist and mention secret redaction (SEC-01)\nSECURITY=\"${REPO_ROOT}/.claude/skills/code-atlas/SECURITY.md\"\nassert_file_contains \"SECURITY.md: SEC-01 secret redaction defined\" \"[Rr]edact\\|REDACTED\\|SEC-01\" \"$SECURITY\"\n\n# 5.6: SKILL.md must define three-pass structure (v1.1.0)\nassert_file_contains \"SKILL.md: Pass 3 defined\" \"[Pp]ass 3\\|pass.3\\|Pass 3\" \"$SKILL\"\n\n# 5.7: SKILL.md must define JourneyVerdict\nassert_file_contains \"SKILL.md: JourneyVerdict concept defined\" \"[Jj]ourney[Vv]erdict\\|Journey.*Verdict\\|PASS.*FAIL.*NEEDS_ATTENTION\\|verdict.*journey\" \"$SKILL\"\n\n# 5.8: SKILL.md Pass 3 must document per-journey verdict block format\nassert_file_contains \"SKILL.md: verdict block format with PASS/FAIL/NEEDS_ATTENTION\" \"PASS | FAIL | NEEDS_ATTENTION\\|PASS.*FAIL.*NEEDS_ATTENTION\" \"$SKILL\"\n\n# 5.9: API-CONTRACTS.md must have pass: 1 | 2 | 3 in BugReport schema\nAPI_CONTRACTS=\"${REPO_ROOT}/.claude/skills/code-atlas/API-CONTRACTS.md\"\nassert_file_contains \"API-CONTRACTS.md: BugReport pass extended to 1|2|3\" \"pass.*1.*2.*3\\|1 | 2 | 3\" \"$API_CONTRACTS\"\n\n# ============================================================================\n# Test Group 6: Pass 3 Verdict Block Tests (v1.1.0)\n# ============================================================================\n\necho \"\"\necho \"=== Pass 3 Verdict Block Tests (v1.1.0) ===\"\n\nATLAS=\"${REPO_ROOT}/docs/atlas\"\nBUG_REPORTS=\"${ATLAS}/bug-reports\"\n\n# Cache pass3 file list once — avoids 5 redundant filesystem scans below.\nmapfile -d '' _pass3_files \u003c \u003c(find \"$BUG_REPORTS\" -name \"*pass3*\" -print0 2>/dev/null)\npass3_count=\"${#_pass3_files[@]}\"\n\n# 6.1: At least one pass3 report file should exist (requires atlas run)\nif [[ \"$pass3_count\" -gt 0 ]]; then\n echo \"PASS: 6.1 at least one pass3 bug report exists ($pass3_count files)\"\n PASS=$((PASS + 1))\nelse\n echo \"FAIL: 6.1 no pass3 bug reports found in $BUG_REPORTS (run /code-atlas first)\"\n FAIL=$((FAIL + 1))\nfi\n\n# 6.2–6.6: Per-file checks using the cached list.\nfor p3_file in \"${_pass3_files[@]}\"; do\n assert_file_contains \"6.2 pass3 report has Journey heading: $(basename \"$p3_file\")\" \\\n \"## Journey:\" \"$p3_file\"\n\n assert_file_contains \"6.3 pass3 report has Verdict: $(basename \"$p3_file\")\" \\\n \"### Verdict:.*PASS\\|### Verdict:.*FAIL\\|### Verdict:.*NEEDS_ATTENTION\" \"$p3_file\"\n\n assert_file_contains \"6.4 pass3 report has status symbols: $(basename \"$p3_file\")\" \\\n \"✅\\|❌\\|⚠️\" \"$p3_file\"\n\n assert_file_contains \"6.5 pass3 report has Verdict Rationale: $(basename \"$p3_file\")\" \\\n \"\\*\\*Verdict Rationale:\\*\\*\\|Verdict Rationale:\" \"$p3_file\"\n\n if grep -q '^\\| [^|]* \\| [^|]* \\| /' \"$p3_file\" 2>/dev/null; then\n echo \"FAIL: 6.6 SEC-16 — absolute path found in pass3 evidence: $(basename \"$p3_file\")\"\n FAIL=$((FAIL + 1))\n else\n echo \"PASS: 6.6 SEC-16 — no absolute paths in evidence: $(basename \"$p3_file\")\"\n PASS=$((PASS + 1))\n fi\ndone\n\n# 6.7: SKILL.md Pass 3 documents scenario deep-dive methodology\nassert_file_contains \"6.7 SKILL.md Pass 3 documents scenario deep-dive\" \\\n \"[Ss]cenario.*[Dd]eep-[Dd]ive\\|deep-dive\\|deep dive\" \"$SKILL\"\n\n# 6.8: API-CONTRACTS.md §4b contains JourneyVerdict schema\nassert_file_contains \"6.8 API-CONTRACTS.md §4b contains JourneyVerdict schema\" \\\n \"JourneyVerdict\\|journey_verdict\" \"$API_CONTRACTS\"\n\n# ---------------------------------------------------------------------------\n# Results\n# ---------------------------------------------------------------------------\necho \"\"\necho \"==================================\"\necho \"Results: ${PASS} passed, ${FAIL} failed\"\necho \"==================================\"\necho \"\"\necho \"NOTE: Tests against live docs/atlas/ fail until /code-atlas is run.\"\n\n[[ $FAIL -eq 0 ]] && exit 0 || exit 1\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":18476,"content_sha256":"f44b48b40b53fe2ca179cc8e7866e752fbb5dda21d5c5d3a6f3b7dbc26040f4f"},{"filename":"tests/test_ci_workflow.sh","content":"#!/bin/bash\n# .claude/skills/code-atlas/tests/test_ci_workflow.sh\n#\n# TDD tests for the atlas CI workflow (.github/workflows/atlas-ci.yml).\n# Validates YAML syntax, required jobs, trigger configuration, and\n# script path references match actual script locations.\n#\n# Tests are mostly structural (can run without GitHub Actions) plus\n# one integration test that runs the staleness script in PR mode.\n#\n# Usage: bash .claude/skills/code-atlas/tests/test_ci_workflow.sh\n# Exit: 0 = all tests passed, non-zero = failures\n\nset -uo pipefail\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nREPO_ROOT=\"$(cd \"$SCRIPT_DIR/../../../..\" && pwd)\"\nCI_WORKFLOW=\"${REPO_ROOT}/.github/workflows/atlas-ci.yml\"\nSTALENESS_SCRIPT=\"${REPO_ROOT}/scripts/check-atlas-staleness.sh\"\nREBUILD_SCRIPT=\"${REPO_ROOT}/scripts/rebuild-atlas-all.sh\"\n\nPASS=0\nFAIL=0\n\n# ---------------------------------------------------------------------------\n# Test harness\n# ---------------------------------------------------------------------------\nassert_pass() {\n local label=\"$1\"; local ok=\"$2\"; local detail=\"${3:-}\"\n if [[ \"$ok\" == \"true\" ]]; then\n echo \"PASS: $label\"; PASS=$((PASS + 1))\n else\n echo \"FAIL: $label${detail:+ — $detail}\"; FAIL=$((FAIL + 1))\n fi\n}\n\nassert_file_contains() {\n local label=\"$1\"; local pattern=\"$2\"; local file=\"$3\"\n if [[ ! -f \"$file\" ]]; then\n echo \"FAIL: $label — file not found: $file\"; FAIL=$((FAIL + 1)); return\n fi\n if grep -q \"$pattern\" \"$file\" 2>/dev/null; then\n echo \"PASS: $label\"; PASS=$((PASS + 1))\n else\n echo \"FAIL: $label — pattern '$pattern' not in $file\"; FAIL=$((FAIL + 1))\n fi\n}\n\nassert_file_exists() {\n local label=\"$1\"; local path=\"$2\"\n [[ -f \"$path\" ]] && { echo \"PASS: $label\"; PASS=$((PASS + 1)); } || { echo \"FAIL: $label — not found: $path\"; FAIL=$((FAIL + 1)); }\n}\n\n# ============================================================================\n# Test Group 1: Required Files Exist\n# ============================================================================\n\necho \"\"\necho \"=== Required Files ===\"\n\nassert_file_exists \"atlas-ci.yml exists\" \"$CI_WORKFLOW\"\nassert_file_exists \"check-atlas-staleness.sh exists\" \"$STALENESS_SCRIPT\"\nassert_file_exists \"rebuild-atlas-all.sh exists\" \"$REBUILD_SCRIPT\"\n\n# Scripts must be executable\nif [[ -f \"$STALENESS_SCRIPT\" ]]; then\n assert_pass \"check-atlas-staleness.sh is executable\" \\\n \"$([[ -x \"$STALENESS_SCRIPT\" ]] && echo true || echo false)\"\nfi\nif [[ -f \"$REBUILD_SCRIPT\" ]]; then\n assert_pass \"rebuild-atlas-all.sh is executable\" \\\n \"$([[ -x \"$REBUILD_SCRIPT\" ]] && echo true || echo false)\"\nfi\n\n# ============================================================================\n# Test Group 2: CI Workflow YAML Structure\n# ============================================================================\n\necho \"\"\necho \"=== CI Workflow YAML Structure ===\"\n\n# 2.1: Must define all three patterns as separate jobs\nassert_file_contains \"CI: atlas-staleness-gate job defined\" \\\n \"atlas-staleness-gate\\|atlas.staleness.gate\" \"$CI_WORKFLOW\"\nassert_file_contains \"CI: atlas-pr-impact job defined\" \\\n \"atlas-pr-impact\\|atlas.pr.impact\" \"$CI_WORKFLOW\"\nassert_file_contains \"CI: atlas-scheduled-rebuild job defined\" \\\n \"atlas-scheduled-rebuild\\|atlas.scheduled.rebuild\\|scheduled.*rebuild\" \"$CI_WORKFLOW\"\n\n# 2.2: Triggers must cover push to main\nassert_file_contains \"CI: triggers on push to main\" \\\n \"push:\" \"$CI_WORKFLOW\"\nassert_file_contains \"CI: push trigger includes main branch\" \\\n \"branches:.*main\\|branches:\\n.*main\\|- main\" \"$CI_WORKFLOW\"\n\n# 2.3: Triggers must cover pull_request\nassert_file_contains \"CI: triggers on pull_request\" \\\n \"pull_request:\" \"$CI_WORKFLOW\"\n\n# 2.4: Scheduled trigger (weekly)\nassert_file_contains \"CI: scheduled cron trigger present\" \\\n \"cron:\\|schedule:\" \"$CI_WORKFLOW\"\nassert_file_contains \"CI: weekly schedule (Monday or * * 1)\" \\\n \"0 .* \\* 1\\|0 .* \\* \\* 1\\|Monday\" \"$CI_WORKFLOW\"\n\n# 2.5: workflow_dispatch for manual runs\nassert_file_contains \"CI: workflow_dispatch trigger\" \\\n \"workflow_dispatch\" \"$CI_WORKFLOW\"\n\n# 2.6: Pattern 1 staleness gate runs check-atlas-staleness.sh\nassert_file_contains \"CI: Pattern 1 calls staleness script\" \\\n \"check-atlas-staleness.sh\" \"$CI_WORKFLOW\"\n\n# 2.7: Pattern 3 rebuild runs rebuild-atlas-all.sh\nassert_file_contains \"CI: Pattern 3 calls rebuild script\" \\\n \"rebuild-atlas-all.sh\" \"$CI_WORKFLOW\"\n\n# 2.8: Artifact upload for staleness report\nassert_file_contains \"CI: uploads staleness artifact\" \\\n \"upload-artifact\\|stale.*report\\|atlas.*report\" \"$CI_WORKFLOW\"\n\n# 2.9: Creates GitHub Issue on rebuild failure\nassert_file_contains \"CI: creates issue on rebuild failure\" \\\n \"gh issue create\\|issue.*create\\|create.*issue\" \"$CI_WORKFLOW\"\n\n# 2.10: Uses ubuntu-latest runner\nassert_file_contains \"CI: uses ubuntu-latest\" \\\n \"ubuntu-latest\" \"$CI_WORKFLOW\"\n\n# 2.11: Checkout action is used\nassert_file_contains \"CI: uses actions/checkout\" \\\n \"actions/checkout\" \"$CI_WORKFLOW\"\n\n# ============================================================================\n# Test Group 3: Script Path Consistency\n# ============================================================================\n\necho \"\"\necho \"=== Script Path Consistency ===\"\n\n# 3.1: CI workflow script paths must match actual file locations\nif [[ -f \"$CI_WORKFLOW\" ]]; then\n # Extract script paths referenced in CI workflow\n staleness_path_in_ci=$(grep -oE \"scripts/check-atlas-staleness\\.sh\" \"$CI_WORKFLOW\" | head -1 || true)\n rebuild_path_in_ci=$(grep -oE \"scripts/rebuild-atlas-all\\.sh\" \"$CI_WORKFLOW\" | head -1 || true)\n\n if [[ -n \"$staleness_path_in_ci\" ]]; then\n if [[ -f \"${REPO_ROOT}/${staleness_path_in_ci}\" ]]; then\n echo \"PASS: staleness script path in CI matches real location\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: CI references ${staleness_path_in_ci} but file not found at ${REPO_ROOT}/${staleness_path_in_ci}\"\n FAIL=$((FAIL + 1))\n fi\n else\n echo \"FAIL: CI workflow does not reference check-atlas-staleness.sh\"\n FAIL=$((FAIL + 1))\n fi\n\n if [[ -n \"$rebuild_path_in_ci\" ]]; then\n if [[ -f \"${REPO_ROOT}/${rebuild_path_in_ci}\" ]]; then\n echo \"PASS: rebuild script path in CI matches real location\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: CI references ${rebuild_path_in_ci} but file not found\"\n FAIL=$((FAIL + 1))\n fi\n else\n echo \"FAIL: CI workflow does not reference rebuild-atlas-all.sh\"\n FAIL=$((FAIL + 1))\n fi\nfi\n\n# ============================================================================\n# Test Group 4: YAML Validity (requires yq or python3)\n# ============================================================================\n\necho \"\"\necho \"=== YAML Validity ===\"\n\nif command -v python3 >/dev/null 2>&1; then\n yaml_valid=$(python3 -c \"\nimport sys\ntry:\n import yaml\n with open('${CI_WORKFLOW}') as f:\n yaml.safe_load(f)\n print('true')\nexcept Exception as e:\n print('false')\n sys.stderr.write(str(e) + '\\n')\n\" 2>/dev/null || echo \"false\")\n assert_pass \"atlas-ci.yml is valid YAML (python3+yaml)\" \"$yaml_valid\"\nelif command -v yq >/dev/null 2>&1; then\n if yq '.' \"$CI_WORKFLOW\" > /dev/null 2>&1; then\n echo \"PASS: atlas-ci.yml is valid YAML (yq)\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: atlas-ci.yml has invalid YAML syntax\"\n FAIL=$((FAIL + 1))\n fi\nelse\n echo \"SKIP: YAML validity check (python3+yaml or yq required)\"\nfi\n\n# ============================================================================\n# Test Group 5: Pattern 1 Integration — Staleness Gate Behavior\n# ============================================================================\n\necho \"\"\necho \"=== Pattern 1 Integration: Staleness Gate ===\"\n\nif [[ ! -f \"$STALENESS_SCRIPT\" ]]; then\n echo \"SKIP: staleness script not found, skipping integration tests\"\nelse\n # 5.1: --pr flag is accepted without error\n tmpdir=$(mktemp -d)\n git -C \"$tmpdir\" init -q\n git -C \"$tmpdir\" config user.email \"[email protected]\"\n git -C \"$tmpdir\" config user.name \"Test\"\n git -C \"$tmpdir\" commit --allow-empty -q -m \"init\"\n git -C \"$tmpdir\" checkout -b main -q 2>/dev/null || true\n git -C \"$tmpdir\" remote add origin \"$tmpdir\" 2>/dev/null || true\n\n # Create a feature branch with an atlas-triggering change\n git -C \"$tmpdir\" checkout -b feature-test -q 2>/dev/null || true\n echo \"version: '3.8'\" > \"$tmpdir/docker-compose.yml\"\n git -C \"$tmpdir\" add .\n git -C \"$tmpdir\" commit -q -m \"add docker-compose\"\n\n # Test: run with explicit range (simulates PR mode)\n # Capture exit code separately (don't use || true which masks the exit code)\n set +e\n output=$(cd \"$tmpdir\" && bash \"$STALENESS_SCRIPT\" \"HEAD~1\" \"HEAD\" 2>&1)\n exit_code=$?\n set -e\n\n # Should detect Layer 1 as stale (docker-compose.yml changed)\n if echo \"$output\" | grep -q \"Layer 1 STALE\\|Layer 1.*stale\\|STALE.*Layer 1\"; then\n echo \"PASS: Pattern 1: docker-compose.yml change triggers Layer 1 stale\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: Pattern 1: docker-compose.yml change should trigger Layer 1 stale\"\n echo \" Output: $output\"\n FAIL=$((FAIL + 1))\n fi\n\n # Exit code must be 1 (stale detected)\n if [[ \"$exit_code\" -eq 1 ]]; then\n echo \"PASS: Pattern 1: exit code 1 when stale layers detected\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: Pattern 1: expected exit 1 for stale, got $exit_code\"\n FAIL=$((FAIL + 1))\n fi\n\n rm -rf \"$tmpdir\"\n\n # 5.2: Clean repo (no relevant changes) exits 0\n tmpdir=$(mktemp -d)\n git -C \"$tmpdir\" init -q\n git -C \"$tmpdir\" config user.email \"[email protected]\"\n git -C \"$tmpdir\" config user.name \"Test\"\n git -C \"$tmpdir\" commit --allow-empty -q -m \"init\"\n\n # Make a non-atlas-triggering change\n echo \"# just a comment\" > \"$tmpdir/Makefile\"\n git -C \"$tmpdir\" add .\n git -C \"$tmpdir\" commit -q -m \"add Makefile\"\n\n output=$(cd \"$tmpdir\" && bash \"$STALENESS_SCRIPT\" \"HEAD~1\" \"HEAD\" 2>&1 || true)\n exit_code=$?\n\n if [[ \"$exit_code\" -eq 0 ]]; then\n echo \"PASS: Pattern 1: non-atlas file change exits 0 (fresh)\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: Pattern 1: Makefile change should not trigger stale (got exit $exit_code)\"\n echo \" Output: $output\"\n FAIL=$((FAIL + 1))\n fi\n\n rm -rf \"$tmpdir\"\nfi\n\n# ============================================================================\n# Test Group 6: Pattern 2 Integration — PR Impact Annotation\n# ============================================================================\n\necho \"\"\necho \"=== Pattern 2: PR Impact Annotation ===\"\n\nif [[ -f \"$STALENESS_SCRIPT\" ]]; then\n # 6.1: Multiple layer triggers in one PR are all detected\n tmpdir=$(mktemp -d)\n git -C \"$tmpdir\" init -q\n git -C \"$tmpdir\" config user.email \"[email protected]\"\n git -C \"$tmpdir\" config user.name \"Test\"\n git -C \"$tmpdir\" commit --allow-empty -q -m \"init\"\n\n # Change files that trigger Layer 2 AND Layer 3 simultaneously\n mkdir -p \"$tmpdir/services/api/src\"\n echo '{\"name\":\"api\"}' > \"$tmpdir/services/api/package.json\"\n echo \"export const router = {}\" > \"$tmpdir/services/api/src/user.routes.ts\"\n git -C \"$tmpdir\" add .\n git -C \"$tmpdir\" commit -q -m \"add api service files\"\n\n output=$(cd \"$tmpdir\" && bash \"$STALENESS_SCRIPT\" \"HEAD~1\" \"HEAD\" 2>&1 || true)\n\n if echo \"$output\" | grep -q \"Layer 2 STALE\"; then\n echo \"PASS: Pattern 2: package.json change triggers Layer 2 stale\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: Pattern 2: package.json should trigger Layer 2\"\n echo \" Output: $output\"\n FAIL=$((FAIL + 1))\n fi\n\n if echo \"$output\" | grep -q \"Layer 3 STALE\"; then\n echo \"PASS: Pattern 2: routes.ts change triggers Layer 3 stale\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: Pattern 2: user.routes.ts should trigger Layer 3\"\n echo \" Output: $output\"\n FAIL=$((FAIL + 1))\n fi\n\n rm -rf \"$tmpdir\"\nfi\n\n# ---------------------------------------------------------------------------\n# Results\n# ---------------------------------------------------------------------------\necho \"\"\necho \"==================================\"\necho \"Results: ${PASS} passed, ${FAIL} failed\"\necho \"==================================\"\n\n[[ $FAIL -eq 0 ]] && exit 0 || exit 1\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":12522,"content_sha256":"1b3c2fd79d5bf4452d19d4b1e62b629b304cb41090f4556f17d1893410de2c8f"},{"filename":"tests/test_layer_contracts.sh","content":"#!/bin/bash\n# .claude/skills/code-atlas/tests/test_layer_contracts.sh\n#\n# TDD tests for the content contracts of each atlas layer.\n# Tests verify that layer outputs correctly represent the codebase under analysis.\n# Each test creates a minimal fixture and validates the expected content in the\n# corresponding atlas output.\n#\n# THESE TESTS WILL FAIL until atlas layer generation is implemented.\n# They encode the contracts that the implementation must satisfy.\n#\n# Usage: bash .claude/skills/code-atlas/tests/test_layer_contracts.sh\n# Exit: 0 = all tests passed, non-zero = failures\n\nset -uo pipefail\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nREPO_ROOT=\"$(cd \"$SCRIPT_DIR/../../../..\" && pwd)\"\n\nPASS=0\nFAIL=0\n\n# ---------------------------------------------------------------------------\n# Test harness\n# ---------------------------------------------------------------------------\nassert_contains() {\n local label=\"$1\"\n local pattern=\"$2\"\n local file=\"$3\"\n if [[ ! -f \"$file\" ]]; then\n echo \"FAIL: $label — file not found: $file\"\n FAIL=$((FAIL + 1)); return\n fi\n if grep -q \"$pattern\" \"$file\" 2>/dev/null; then\n echo \"PASS: $label\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: $label\"\n echo \" Pattern: '$pattern' not found in $file\"\n head -20 \"$file\" | sed 's/^/ > /'\n FAIL=$((FAIL + 1))\n fi\n}\n\nassert_not_contains() {\n local label=\"$1\"\n local pattern=\"$2\"\n local file=\"$3\"\n if [[ ! -f \"$file\" ]]; then\n echo \"FAIL: $label — file not found: $file\"\n FAIL=$((FAIL + 1)); return\n fi\n if grep -q \"$pattern\" \"$file\" 2>/dev/null; then\n echo \"FAIL: $label — forbidden pattern '$pattern' found in $file\"\n FAIL=$((FAIL + 1))\n else\n echo \"PASS: $label\"\n PASS=$((PASS + 1))\n fi\n}\n\nassert_row_count_gte() {\n local label=\"$1\"\n local file=\"$2\"\n local min_data_rows=\"$3\"\n if [[ ! -f \"$file\" ]]; then\n echo \"FAIL: $label — file not found: $file\"\n FAIL=$((FAIL + 1)); return\n fi\n # Count rows excluding header and separator\n data_rows=$(grep \"^|\" \"$file\" 2>/dev/null | grep -v \"^|[-| ]*|$\" | grep -v \"^| *[A-Z].*|$\" | wc -l || echo 0)\n actual_rows=$(grep \"^|\" \"$file\" 2>/dev/null | wc -l || echo 0)\n if [[ \"$actual_rows\" -ge \"$((min_data_rows + 2))\" ]]; then\n echo \"PASS: $label ($actual_rows total rows)\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: $label — expected >= $min_data_rows data rows, found ~$actual_rows total rows\"\n FAIL=$((FAIL + 1))\n fi\n}\n\n# ---------------------------------------------------------------------------\n# Fixture helpers\n# ---------------------------------------------------------------------------\n\ncreate_go_fixture() {\n local dir=\"$1\"\n mkdir -p \"$dir\"/{cmd/server,internal/{handlers,models},k8s}\n\n cat > \"$dir/cmd/server/main.go\" \u003c\u003c 'EOF'\npackage main\n\nimport (\n \"github.com/gin-gonic/gin\"\n \"myapp/internal/handlers\"\n)\n\nfunc main() {\n r := gin.Default()\n handlers.RegisterRoutes(r)\n r.Run(\":8080\")\n}\nEOF\n\n cat > \"$dir/internal/handlers/user_handler.go\" \u003c\u003c 'EOF'\npackage handlers\n\nimport (\n \"net/http\"\n \"github.com/gin-gonic/gin\"\n \"myapp/internal/models\"\n)\n\nfunc RegisterRoutes(r *gin.Engine) {\n r.GET(\"/api/users\", ListUsers)\n r.POST(\"/api/users\", CreateUser)\n r.GET(\"/api/users/:id\", GetUser)\n r.DELETE(\"/api/users/:id\", DeleteUser)\n}\n\nfunc ListUsers(c *gin.Context) { c.JSON(http.StatusOK, []models.User{}) }\nfunc CreateUser(c *gin.Context) { c.JSON(http.StatusCreated, models.User{}) }\nfunc GetUser(c *gin.Context) { c.JSON(http.StatusOK, models.User{}) }\nfunc DeleteUser(c *gin.Context) { c.JSON(http.StatusNoContent, nil) }\nEOF\n\n cat > \"$dir/internal/models/user_model.go\" \u003c\u003c 'EOF'\npackage models\n\ntype User struct {\n ID string `json:\"id\"`\n Email string `json:\"email\"`\n Name string `json:\"name\"`\n Role string `json:\"role\"`\n Password string `json:\"-\"`\n}\n\ntype CreateUserRequest struct {\n Email string `json:\"email\" binding:\"required\"`\n Name string `json:\"name\" binding:\"required\"`\n Password string `json:\"password\" binding:\"required\"`\n}\nEOF\n\n cat > \"$dir/docker-compose.yml\" \u003c\u003c 'EOF'\nversion: \"3.8\"\nservices:\n api:\n build: .\n ports:\n - \"8080:8080\"\n depends_on:\n - postgres\n - redis\n environment:\n - DATABASE_URL\n - JWT_SECRET\n postgres:\n image: postgres:15\n ports:\n - \"5432:5432\"\n redis:\n image: redis:7\n ports:\n - \"6379:6379\"\nEOF\n\n cat > \"$dir/.env.example\" \u003c\u003c 'EOF'\nDATABASE_URL=postgres://user:password@localhost/mydb # pragma: allowlist secret\nJWT_SECRET=your-jwt-secret-here # pragma: allowlist secret\nREDIS_URL=redis://localhost:6379\nPORT=8080\nEOF\n\n cat > \"$dir/go.mod\" \u003c\u003c 'EOF'\nmodule myapp\n\ngo 1.21\n\nrequire (\n github.com/gin-gonic/gin v1.9.1\n github.com/golang-jwt/jwt/v5 v5.0.0\n gorm.io/gorm v1.25.4\n gorm.io/driver/postgres v1.5.3\n)\nEOF\n\n git -C \"$dir\" init -q\n git -C \"$dir\" config user.email \"[email protected]\"\n git -C \"$dir\" config user.name \"Test\"\n git -C \"$dir\" add .\n git -C \"$dir\" commit -q -m \"go fixture\"\n}\n\n# ---------------------------------------------------------------------------\n# Layer 1 Contracts — Runtime Topology\n# ---------------------------------------------------------------------------\n\necho \"\"\necho \"=== Layer 1 Contract Tests: Runtime Topology ===\"\n\n# Setup: We need the atlas to have been run on the Go fixture.\n# These tests check an EXISTING atlas output at docs/atlas/.\n# If docs/atlas/ doesn't exist, we check the fixture would produce expected output.\nATLAS=\"${REPO_ROOT}/docs/atlas\"\n\nL1=\"${ATLAS}/repo-surface\"\n\n# Contract 1.1: topology.dot must contain all services from docker-compose.yml\n# If the Go fixture is used (api, postgres, redis services):\nassert_contains \"L1: topology.dot has 'api' node\" \"api\" \"${L1}/topology.dot\"\nassert_contains \"L1: topology.dot has 'postgres' node\" \"postgres\" \"${L1}/topology.dot\"\nassert_contains \"L1: topology.dot has 'redis' node\" \"redis\" \"${L1}/topology.dot\"\n\n# Contract 1.2: topology.mmd mirrors topology.dot (both exist, same services)\nassert_contains \"L1: topology.mmd has 'api'\" \"api\" \"${L1}/topology.mmd\"\nassert_contains \"L1: topology.mmd has 'postgres'\" \"postgres\" \"${L1}/topology.mmd\"\n\n# Contract 1.3: depends_on relationships become edges in the graph\nassert_contains \"L1: topology shows api depends on postgres\" \"api.*postgres\\|postgres.*api\" \"${L1}/topology.dot\"\n\n# Contract 1.4: Port mappings are annotated\nassert_contains \"L1: port 8080 annotated\" \"8080\" \"${L1}/topology.dot\"\n\n# Contract 1.5: README.md explains the layer\nassert_contains \"L1: README has layer description\" \"[Rr]untime\\|[Tt]opology\\|[Ss]ervice\" \"${L1}/README.md\"\n\n# ---------------------------------------------------------------------------\n# Layer 2 Contracts — Compile-time Dependencies\n# ---------------------------------------------------------------------------\n\necho \"\"\necho \"=== Layer 2 Contract Tests: Compile-time Dependencies ===\"\n\nL2=\"${ATLAS}/compile-deps\"\n\n# Contract 2.1: inventory.md has columns for Package, Version, License\nassert_contains \"L2: inventory.md has Package column\" \"[Pp]ackage\\|[Mm]odule\\|[Nn]ame\" \"${L2}/inventory.md\"\nassert_contains \"L2: inventory.md has Version column\" \"[Vv]ersion\" \"${L2}/inventory.md\"\n\n# Contract 2.2: Known dependencies from go.mod appear in inventory\nassert_contains \"L2: gin dependency in inventory\" \"gin\" \"${L2}/inventory.md\"\nassert_contains \"L2: gorm dependency in inventory\" \"gorm\" \"${L2}/inventory.md\"\nassert_contains \"L2: jwt dependency in inventory\" \"jwt\" \"${L2}/inventory.md\"\n\n# Contract 2.3: Version numbers are shown\nassert_contains \"L2: version 1.9.1 in inventory\" \"1\\.9\\.1\" \"${L2}/inventory.md\"\n\n# Contract 2.4: dependencies.mmd shows the dep graph\nassert_contains \"L2: mmd has module name\" \"myapp\\|module\" \"${L2}/dependencies.mmd\"\n\n# ---------------------------------------------------------------------------\n# Layer 3 Contracts — API Contracts\n# ---------------------------------------------------------------------------\n\necho \"\"\necho \"=== Layer 3 Contract Tests: API Contracts ===\"\n\nL3=\"${ATLAS}/api-contracts\"\n\n# Contract 3.1: All 4 routes from user_handler.go appear in route-inventory.md\nassert_contains \"L3: GET /api/users in inventory\" \"GET.*api/users\\|/api/users.*GET\" \"${L3}/route-inventory.md\"\nassert_contains \"L3: POST /api/users in inventory\" \"POST.*api/users\\|/api/users.*POST\" \"${L3}/route-inventory.md\"\nassert_contains \"L3: GET /api/users/:id in inventory\" \"/api/users/:id\\|/api/users/\\{id\\}\" \"${L3}/route-inventory.md\"\nassert_contains \"L3: DELETE /api/users/:id in inventory\" \"DELETE.*api/users\\|/api/users.*DELETE\" \"${L3}/route-inventory.md\"\n\n# Contract 3.2: Handler function names are referenced\nassert_contains \"L3: ListUsers handler referenced\" \"ListUsers\\|list.users\\|listUsers\" \"${L3}/route-inventory.md\"\nassert_contains \"L3: CreateUser handler referenced\" \"CreateUser\\|create.user\\|createUser\" \"${L3}/route-inventory.md\"\n\n# Contract 3.3: DTO/Request type is linked where known\nassert_contains \"L3: CreateUserRequest DTO referenced\" \"CreateUserRequest\\|CreateUser.*Request\\|request\" \"${L3}/route-inventory.md\"\n\n# Contract 3.4: routing.mmd shows method-path nodes or flowchart\nassert_contains \"L3: mmd shows route structure\" \"GET\\|POST\\|DELETE\\|/api\" \"${L3}/routing.mmd\"\n\n# ---------------------------------------------------------------------------\n# Layer 4 Contracts — Data Flows\n# ---------------------------------------------------------------------------\n\necho \"\"\necho \"=== Layer 4 Contract Tests: Data Flows ===\"\n\nL4=\"${ATLAS}/data-flow\"\n\n# Contract 4.1: User struct appears in dataflow diagram\nassert_contains \"L4: User struct in dataflow\" \"User\" \"${L4}/dataflow.mmd\"\n\n# Contract 4.2: CreateUserRequest appears\nassert_contains \"L4: CreateUserRequest in dataflow\" \"CreateUserRequest\\|CreateUser\" \"${L4}/dataflow.mmd\"\n\n# Contract 4.3: Password field is NOT shown in API layer (json:\"-\" annotation)\n# Note: it's fine to show the struct, but the diagram should note the field is hidden from JSON\n# This is a best-effort check; the field name may appear in non-API contexts\n# Primary concern: API response diagrams must not show Password as an output field\nassert_not_contains \"L4: Password not in API output flow\" \"Password.*response\\|response.*Password\" \"${L4}/dataflow.mmd\"\n\n# ---------------------------------------------------------------------------\n# Layer 5 Contracts — User Journey Scenarios\n# ---------------------------------------------------------------------------\n\necho \"\"\necho \"=== Layer 5 Contract Tests: User Journey Scenarios ===\"\n\nL5=\"${ATLAS}/user-journeys\"\n\n# Contract 5.1: At least one journey .mmd file exists\njourney_files=$(find \"${L5}\" -name \"journey-*.mmd\" 2>/dev/null | wc -l)\nif [[ \"${journey_files}\" -ge 1 ]]; then\n echo \"PASS: L5: at least 1 journey diagram found (${journey_files} total)\"\n PASS=$((PASS + 1))\nelse\n echo \"FAIL: L5: expected at least 1 journey-*.mmd file, found 0\"\n FAIL=$((FAIL + 1))\nfi\n\n# Contract 5.2: A user registration journey is derived from POST /api/users\nassert_contains \"L5: registration journey references user creation\" \\\n \"[Rr]egist\\|[Cc]reate [Uu]ser\\|POST.*users\\|/api/users\" \\\n \"${L5}/README.md\"\n\n# Contract 5.3: Journey diagrams use valid Mermaid journey or flowchart syntax\nfor journey_file in \"${L5}\"/journey-*.mmd; do\n [[ -f \"$journey_file\" ]] || continue\n fname=$(basename \"$journey_file\")\n first_kw=$(head -3 \"$journey_file\" | grep -oE \"^(journey|graph|flowchart|sequenceDiagram)\" | head -1 || true)\n if [[ -n \"$first_kw\" ]]; then\n echo \"PASS: L5: $fname uses valid diagram type ($first_kw)\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: L5: $fname does not start with a recognized Mermaid diagram type\"\n FAIL=$((FAIL + 1))\n fi\ndone\n\n# ---------------------------------------------------------------------------\n# Layer 6 Contracts — Inventory Tables\n# ---------------------------------------------------------------------------\n\necho \"\"\necho \"=== Layer 6 Contract Tests: Inventory Tables ===\"\n\nL6=\"${ATLAS}/inventory\"\n\n# Contract 6.1: services.md has row for each docker-compose service\nassert_contains \"L6: api service in services.md\" \"api\" \"${L6}/services.md\"\nassert_contains \"L6: postgres in services.md\" \"postgres\" \"${L6}/services.md\"\nassert_contains \"L6: redis in services.md\" \"redis\" \"${L6}/services.md\"\n\n# Contract 6.2: services.md has port column\nassert_contains \"L6: services.md has port 8080\" \"8080\" \"${L6}/services.md\"\nassert_contains \"L6: services.md has port 5432\" \"5432\" \"${L6}/services.md\"\n\n# Contract 6.3: env-vars.md has all 4 vars from .env.example\nassert_contains \"L6: DATABASE_URL in env-vars.md\" \"DATABASE_URL\" \"${L6}/env-vars.md\"\nassert_contains \"L6: JWT_SECRET in env-vars.md\" \"JWT_SECRET\" \"${L6}/env-vars.md\"\nassert_contains \"L6: REDIS_URL in env-vars.md\" \"REDIS_URL\" \"${L6}/env-vars.md\"\nassert_contains \"L6: PORT in env-vars.md\" \"PORT\" \"${L6}/env-vars.md\"\n\n# Contract 6.4 (SEC-01): env-vars.md MUST NOT contain real values\nassert_not_contains \"L6 SEC-01: no postgres password in env-vars.md\" \"password\" \"${L6}/env-vars.md\"\nassert_not_contains \"L6 SEC-01: no jwt secret value in env-vars.md\" \"your-jwt-secret-here\" \"${L6}/env-vars.md\"\nassert_not_contains \"L6 SEC-01: no redis URL with password in env-vars.md\" \"redis://localhost\" \"${L6}/env-vars.md\"\n\n# Contract 6.5: external-deps.md references external packages\nassert_contains \"L6: external-deps.md has gin\" \"gin\" \"${L6}/external-deps.md\"\nassert_contains \"L6: external-deps.md has gorm\" \"gorm\" \"${L6}/external-deps.md\"\n\n# ---------------------------------------------------------------------------\n# Pass 1 Bug Hunt Contracts\n# ---------------------------------------------------------------------------\n\necho \"\"\necho \"=== Pass 1 Bug Hunt Contract Tests ===\"\n\nP1=\"${ATLAS}/bug-reports/pass1-contradictions.md\"\n\n# Contract P1.1: Report file exists\nif [[ ! -f \"$P1\" ]]; then\n echo \"FAIL: pass1-contradictions.md does not exist\"\n FAIL=$((FAIL + 4))\nelse\n # Contract P1.2: Report has BUG entries\n bug_count=$(grep -cE \"^## BUG-[0-9]+\" \"$P1\" 2>/dev/null || echo 0)\n if [[ \"$bug_count\" -ge 0 ]]; then\n echo \"PASS: pass1 bug report parseable (${bug_count} BUG entries)\"\n PASS=$((PASS + 1))\n fi\n\n # Contract P1.3: Each BUG entry has Severity\n assert_contains \"P1: bug entries have Severity field\" \"[Ss]everity.*HIGH\\|[Ss]everity.*MEDIUM\\|[Ss]everity.*LOW\\|[Ss]everity.*CRITICAL\" \"$P1\"\n\n # Contract P1.4: Each BUG entry has Layer reference\n assert_contains \"P1: bug entries reference layers\" \"[Ll]ayer [1-6]\\|Layer[1-6]\" \"$P1\"\n\n # Contract P1.5: Code evidence is included\n assert_contains \"P1: bug entries have code evidence\" \"Evidence\\|code.quote\\|Code quote\\|code-block\" \"$P1\"\n\n # Contract P1.6: No raw secret values in evidence\n assert_not_contains \"P1 SEC-09: no raw passwords in bug report\" \"password.*=.*[a-zA-Z0-9]{8}\" \"$P1\"\nfi\n\n# ---------------------------------------------------------------------------\n# Pass 2 Bug Hunt Contracts\n# ---------------------------------------------------------------------------\n\necho \"\"\necho \"=== Pass 2 Bug Hunt Contract Tests ===\"\n\nP2=\"${ATLAS}/bug-reports/pass2-journey-bugs.md\"\n\nif [[ ! -f \"$P2\" ]]; then\n echo \"FAIL: pass2-journey-bugs.md does not exist\"\n FAIL=$((FAIL + 3))\nelse\n # Contract P2.1: Report references at least one user journey\n assert_contains \"P2: report references user journeys\" \"[Jj]ourney\\|[Ss]cenario\\|user.flow\\|User Flow\" \"$P2\"\n\n # Contract P2.2: Report traces routes through layers\n assert_contains \"P2: report references route paths\" \"/api/\\|GET\\|POST\\|route\" \"$P2\"\n\n # Contract P2.3: Report has Severity\n assert_contains \"P2: report has severity field\" \"[Ss]everity\\|[Pp]riority\" \"$P2\"\nfi\n\n# ---------------------------------------------------------------------------\n# Layer 7 Filesystem Contract Tests (v1.1.0)\n# ---------------------------------------------------------------------------\n\necho \"\"\necho \"=== Layer 7 Filesystem Contract Tests ===\"\n\nL7=\"${ATLAS}/service-components\"\n\nif [[ ! -d \"$L7\" ]]; then\n echo \"FAIL: service-components/ directory does not exist\"\n FAIL=$((FAIL + 3))\nelse\n echo \"PASS: service-components/ directory exists\"\n PASS=$((PASS + 1))\n\n # Contract L7.1: README.md must exist\n if [[ ! -f \"${L7}/README.md\" ]]; then\n echo \"FAIL: L7.1 service-components/README.md does not exist\"\n FAIL=$((FAIL + 1))\n else\n echo \"PASS: L7.1 service-components/README.md exists\"\n PASS=$((PASS + 1))\n fi\n\n # Contract L7.2: At least one .mmd file must exist per service\n mmd_count=$(find \"$L7\" -name \"*.mmd\" 2>/dev/null | wc -l)\n if [[ \"$mmd_count\" -gt 0 ]]; then\n echo \"PASS: L7.2 at least one .mmd file exists in service-components/\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: L7.2 no .mmd files found in service-components/\"\n FAIL=$((FAIL + 1))\n fi\n\n # Contract L7.3: Service diagram file names must match [a-zA-Z0-9_-]+ pattern (SEC-11)\n invalid_names=()\n while IFS= read -r -d '' mmd_file; do\n basename_noext=$(basename \"$mmd_file\" .mmd)\n if ! echo \"$basename_noext\" | grep -qP '^[a-zA-Z0-9_-]{1,64}

Code Atlas Skill Purpose Build exhaustive, regeneratable architecture atlases directly from code truth. A code-atlas is a living document set: diagrams, graphs, and inventory tables that form a navigable map of any codebase. Atlas-building is investigation: structured reasoning about code in graph form reveals structural bugs, API contract mismatches, and architectural drift that linear code review misses. An atlas is complete when any engineer, given only the atlas and a bug report, can trace the full execution path without opening the source code. Layer Overview Layer definitions are the si…

; then\n invalid_names+=(\"$basename_noext\")\n fi\n done \u003c \u003c(find \"$L7\" -name \"*.mmd\" -print0 2>/dev/null)\n\n if [[ \"${#invalid_names[@]}\" -eq 0 ]]; then\n echo \"PASS: L7.3 SEC-11 — all service diagram names match [a-zA-Z0-9_-]{1,64}\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: L7.3 SEC-11 — invalid service names found: ${invalid_names[*]}\"\n FAIL=$((FAIL + 1))\n fi\n\n # Contract L7.4: Each .mmd file must be valid Mermaid (starts with graph TD or flowchart)\n invalid_mmd=()\n while IFS= read -r -d '' mmd_file; do\n first_line=$(head -1 \"$mmd_file\" 2>/dev/null)\n if ! echo \"$first_line\" | grep -qiP 'graph (TD|LR|BT|RL)|flowchart'; then\n invalid_mmd+=(\"$(basename \"$mmd_file\")\")\n fi\n done \u003c \u003c(find \"$L7\" -name \"*.mmd\" -print0 2>/dev/null)\n\n if [[ \"${#invalid_mmd[@]}\" -eq 0 ]]; then\n echo \"PASS: L7.4 all .mmd files start with graph TD / flowchart\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: L7.4 .mmd files do not start with graph TD: ${invalid_mmd[*]}\"\n FAIL=$((FAIL + 1))\n fi\nfi\n\n# ---------------------------------------------------------------------------\n# Layer 8 Filesystem Contract Tests (v1.1.0)\n# ---------------------------------------------------------------------------\n\necho \"\"\necho \"=== Layer 8 Filesystem Contract Tests ===\"\n\nL8=\"${ATLAS}/ast-lsp-bindings\"\n\nif [[ ! -d \"$L8\" ]]; then\n echo \"FAIL: ast-lsp-bindings/ directory does not exist\"\n FAIL=$((FAIL + 5))\nelse\n echo \"PASS: ast-lsp-bindings/ directory exists\"\n PASS=$((PASS + 1))\n\n # Contract L8.1: README.md must exist and have mode label on line 1\n if [[ ! -f \"${L8}/README.md\" ]]; then\n echo \"FAIL: L8.1 ast-lsp-bindings/README.md does not exist\"\n FAIL=$((FAIL + 1))\n else\n first_line=$(head -1 \"${L8}/README.md\")\n if echo \"$first_line\" | grep -qP '\\*\\*Mode:\\*\\*\\s*(lsp-assisted|static-approximation)'; then\n echo \"PASS: L8.1 README.md line 1 contains valid **Mode:** label\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: L8.1 README.md line 1 must be '**Mode:** lsp-assisted' or '**Mode:** static-approximation' — got: '$first_line'\"\n FAIL=$((FAIL + 1))\n fi\n fi\n\n # Contract L8.2: symbol-references.mmd must exist\n if [[ -f \"${L8}/symbol-references.mmd\" ]]; then\n echo \"PASS: L8.2 symbol-references.mmd exists\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: L8.2 symbol-references.mmd does not exist\"\n FAIL=$((FAIL + 1))\n fi\n\n # Contract L8.3: dead-code.md must exist\n if [[ -f \"${L8}/dead-code.md\" ]]; then\n echo \"PASS: L8.3 dead-code.md exists\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: L8.3 dead-code.md does not exist\"\n FAIL=$((FAIL + 1))\n fi\n\n # Contract L8.4: mismatched-interfaces.md must exist\n if [[ -f \"${L8}/mismatched-interfaces.md\" ]]; then\n echo \"PASS: L8.4 mismatched-interfaces.md exists\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: L8.4 mismatched-interfaces.md does not exist\"\n FAIL=$((FAIL + 1))\n fi\n\n # Contract L8.5: dead-code.md must not contain absolute paths (SEC-16)\n if [[ -f \"${L8}/dead-code.md\" ]]; then\n if grep -qP '^/' \"${L8}/dead-code.md\" 2>/dev/null; then\n echo \"FAIL: L8.5 SEC-16 — absolute path found in dead-code.md\"\n FAIL=$((FAIL + 1))\n else\n echo \"PASS: L8.5 SEC-16 — no absolute paths in dead-code.md\"\n PASS=$((PASS + 1))\n fi\n fi\nfi\n\n# ---------------------------------------------------------------------------\n# API-CONTRACTS.md v1.1.0 Error Code Tests\n# ---------------------------------------------------------------------------\n\necho \"\"\necho \"=== API-CONTRACTS.md v1.1.0 Error Code Tests ===\"\n\nSKILL_DIR=\"$(cd \"$SCRIPT_DIR/..\" && pwd)\"\nAPI_MD=\"${SKILL_DIR}/API-CONTRACTS.md\"\n\nfor code in \"LAYER7_SOURCE_NOT_FOUND\" \"LAYER8_LSP_UNAVAILABLE\" \"DENSITY_THRESHOLD_EXCEEDED\"; do\n if grep -q \"$code\" \"$API_MD\" 2>/dev/null; then\n echo \"PASS: API-CONTRACTS.md contains error code: $code\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: API-CONTRACTS.md missing error code: $code\"\n FAIL=$((FAIL + 1))\n fi\ndone\n\n# ---------------------------------------------------------------------------\n# Results\n# ---------------------------------------------------------------------------\necho \"\"\necho \"==================================\"\necho \"Results: ${PASS} passed, ${FAIL} failed\"\necho \"==================================\"\necho \"\"\necho \"NOTE: Atlas output tests require /code-atlas to have been run first.\"\n\n[[ $FAIL -eq 0 ]] && exit 0 || exit 1\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":21995,"content_sha256":"b3c82836a6c6f63ab748b03414794a80bfb044aea743a9a99ab7122574cfb79c"},{"filename":"tests/test_layer7_8.sh","content":"#!/bin/bash\n# .claude/skills/code-atlas/tests/test_layer7_8.sh\n#\n# TDD tests for Layer 7 (Service Component Architecture) and\n# Layer 8 (AST+LSP Symbol Bindings) documentation and output contracts.\n#\n# Tests validate:\n# - SKILL.md contains Layer 7 and Layer 8 section documentation\n# - API-CONTRACTS.md contains Layer 7/8 filesystem contracts and error codes\n# - SECURITY.md contains SEC-11 through SEC-16 definitions\n# - Output filesystem contract assertions (when atlas has been built)\n# - SEC-11 service name sanitization assertions\n# - SEC-12 LSP output sanitization assertions\n# - SEC-15 Layer 8 credential redaction assertions\n# - SEC-16 relative-path enforcement assertions\n#\n# THESE TESTS WILL FAIL until the skill documentation is complete.\n# Output structure tests (section: \"Atlas Output Structure\") also require\n# /code-atlas to have been run first.\n#\n# Usage: bash .claude/skills/code-atlas/tests/test_layer7_8.sh\n# Exit: 0 = all tests passed, non-zero = failures\n\nset -uo pipefail\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nSKILL_DIR=\"$(cd \"$SCRIPT_DIR/..\" && pwd)\"\nREPO_ROOT=\"$(cd \"$SCRIPT_DIR/../../../..\" && pwd)\"\nATLAS_DIR=\"${REPO_ROOT}/docs/atlas\"\n\nPASS=0\nFAIL=0\n\n# ---------------------------------------------------------------------------\n# Test harness\n# ---------------------------------------------------------------------------\nassert_file_contains() {\n local label=\"$1\"; local pattern=\"$2\"; local file=\"$3\"\n if [[ ! -f \"$file\" ]]; then\n echo \"FAIL: $label — file not found: $file\"\n FAIL=$((FAIL + 1))\n return\n fi\n if grep -q \"$pattern\" \"$file\" 2>/dev/null; then\n echo \"PASS: $label\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: $label — '$pattern' not found in $file\"\n FAIL=$((FAIL + 1))\n fi\n}\n\nassert_file_exists() {\n local label=\"$1\"; local file=\"$2\"\n if [[ -f \"$file\" ]]; then\n echo \"PASS: $label\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: $label — file not found: $file\"\n FAIL=$((FAIL + 1))\n fi\n}\n\nassert_dir_exists() {\n local label=\"$1\"; local dir=\"$2\"\n if [[ -d \"$dir\" ]]; then\n echo \"PASS: $label\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: $label — directory not found: $dir\"\n FAIL=$((FAIL + 1))\n fi\n}\n\nassert_not_in_file() {\n local label=\"$1\"; local pattern=\"$2\"; local file=\"$3\"\n if [[ ! -f \"$file\" ]]; then\n echo \"FAIL: $label — file not found: $file\"\n FAIL=$((FAIL + 1))\n return\n fi\n if grep -q \"$pattern\" \"$file\" 2>/dev/null; then\n echo \"FAIL: $label — forbidden pattern '$pattern' found in $file\"\n FAIL=$((FAIL + 1))\n else\n echo \"PASS: $label\"\n PASS=$((PASS + 1))\n fi\n}\n\necho \"\"\necho \"=== Test Suite: Layer 7 and Layer 8 Contracts ===\"\necho \"\"\n\n# ---------------------------------------------------------------------------\n# SECTION 1: SKILL.md Documentation Tests\n# ---------------------------------------------------------------------------\necho \"--- SKILL.md: Layer 7 Documentation ---\"\n\nSKILL_MD=\"${SKILL_DIR}/SKILL.md\"\n\nassert_file_contains \\\n \"SKILL.md contains Layer 7 heading\" \\\n \"Layer 7: Service Component Architecture\" \\\n \"$SKILL_MD\"\n\nassert_file_contains \\\n \"SKILL.md Layer 7 references SEC-11\" \\\n \"SEC-11\" \\\n \"$SKILL_MD\"\n\nassert_file_contains \\\n \"SKILL.md Layer 7 mentions per-service module structure\" \\\n \"per-service\\|internal.*module\\|package.*structure\" \\\n \"$SKILL_MD\"\n\nassert_file_contains \\\n \"SKILL.md Layer 7 output path documented\" \\\n \"service-components\" \\\n \"$SKILL_MD\"\n\nassert_file_contains \\\n \"SKILL.md Layer 7 service name sanitisation documented\" \\\n \"sanitis\\|a-zA-Z0-9_-.*1,64\" \\\n \"$SKILL_MD\"\n\nassert_file_contains \\\n \"SKILL.md Layer 7 density guard applies\" \\\n \"Density guard\" \\\n \"$SKILL_MD\"\n\necho \"\"\necho \"--- SKILL.md: Layer 8 Documentation ---\"\n\nassert_file_contains \\\n \"SKILL.md contains Layer 8 heading\" \\\n \"Layer 8: AST+LSP Symbol Bindings\" \\\n \"$SKILL_MD\"\n\nassert_file_contains \\\n \"SKILL.md Layer 8 references SEC-12\" \\\n \"SEC-12\" \\\n \"$SKILL_MD\"\n\nassert_file_contains \\\n \"SKILL.md Layer 8 references SEC-15\" \\\n \"SEC-15\" \\\n \"$SKILL_MD\"\n\nassert_file_contains \\\n \"SKILL.md Layer 8 lists operating modes\" \\\n \"lsp-assisted\\|static-approximation\" \\\n \"$SKILL_MD\"\n\nassert_file_contains \\\n \"SKILL.md Layer 8 mode label contract documented\" \\\n \"Mode.*line 1\\|line 1.*Mode\" \\\n \"$SKILL_MD\"\n\nassert_file_contains \\\n \"SKILL.md Layer 8 lists dead-code.md output\" \\\n \"dead-code.md\" \\\n \"$SKILL_MD\"\n\nassert_file_contains \\\n \"SKILL.md Layer 8 lists mismatched-interfaces.md output\" \\\n \"mismatched-interfaces.md\" \\\n \"$SKILL_MD\"\n\nassert_file_contains \\\n \"SKILL.md Layer 8 lists symbol-references.mmd output\" \\\n \"symbol-references.mmd\" \\\n \"$SKILL_MD\"\n\n# ---------------------------------------------------------------------------\n# SECTION 2: API-CONTRACTS.md Tests\n# ---------------------------------------------------------------------------\necho \"\"\necho \"--- API-CONTRACTS.md: Layer 7/8 Contracts ---\"\n\nAPI_MD=\"${SKILL_DIR}/API-CONTRACTS.md\"\n\nassert_file_contains \\\n \"API-CONTRACTS.md version is v1.1.0\" \\\n \"1.1.0\" \\\n \"$API_MD\"\n\nassert_file_contains \\\n \"API-CONTRACTS.md LayerID updated to include 7 and 8\" \\\n \"LayerID.*7.*8\\|7.*8.*LayerID\\|1.*2.*3.*4.*5.*6.*7.*8\" \\\n \"$API_MD\"\n\nassert_file_contains \\\n \"API-CONTRACTS.md contains Layer 7 filesystem contract\" \\\n \"Layer7Output\\|service-components\" \\\n \"$API_MD\"\n\nassert_file_contains \\\n \"API-CONTRACTS.md contains Layer 8 filesystem contract\" \\\n \"Layer8Output\\|ast-lsp-bindings\" \\\n \"$API_MD\"\n\nassert_file_contains \\\n \"API-CONTRACTS.md contains LAYER7_SOURCE_NOT_FOUND error code\" \\\n \"LAYER7_SOURCE_NOT_FOUND\" \\\n \"$API_MD\"\n\nassert_file_contains \\\n \"API-CONTRACTS.md contains LAYER8_LSP_UNAVAILABLE error code\" \\\n \"LAYER8_LSP_UNAVAILABLE\" \\\n \"$API_MD\"\n\nassert_file_contains \\\n \"API-CONTRACTS.md contains DENSITY_THRESHOLD_EXCEEDED error code\" \\\n \"DENSITY_THRESHOLD_EXCEEDED\" \\\n \"$API_MD\"\n\nassert_file_contains \\\n \"API-CONTRACTS.md documents lsp-setup delegation contract (§2f)\" \\\n \"lsp-setup\\|LSPSetupDelegation\" \\\n \"$API_MD\"\n\nassert_file_contains \\\n \"API-CONTRACTS.md contains LSPSymbolReport schema\" \\\n \"LSPSymbolReport\" \\\n \"$API_MD\"\n\nassert_file_contains \\\n \"API-CONTRACTS.md contains StaticSymbolReport schema\" \\\n \"StaticSymbolReport\\|static-approximation\" \\\n \"$API_MD\"\n\nassert_file_contains \\\n \"API-CONTRACTS.md documents mode label contract\" \\\n \"mode.*label\\|Mode.*line.*1\\|first line\" \\\n \"$API_MD\"\n\nassert_file_contains \\\n \"API-CONTRACTS.md BugReport pass extended to 1|2|3\" \\\n \"pass.*1.*2.*3\\|1 | 2 | 3\" \\\n \"$API_MD\"\n\n# ---------------------------------------------------------------------------\n# SECTION 3: SECURITY.md Tests (SEC-11 through SEC-16)\n# ---------------------------------------------------------------------------\necho \"\"\necho \"--- SECURITY.md: SEC-11 through SEC-16 ---\"\n\nSEC_MD=\"${SKILL_DIR}/SECURITY.md\"\n\nassert_file_contains \\\n \"SECURITY.md contains SEC-11 service name sanitisation\" \\\n \"SEC-11\" \\\n \"$SEC_MD\"\n\nassert_file_contains \\\n \"SECURITY.md SEC-11 regex pattern documented\" \\\n \"a-zA-Z0-9_-.*1,64\" \\\n \"$SEC_MD\"\n\nassert_file_contains \\\n \"SECURITY.md contains SEC-12 LSP output sanitisation\" \\\n \"SEC-12\" \\\n \"$SEC_MD\"\n\nassert_file_contains \\\n \"SECURITY.md contains SEC-15 Layer 8 credential redaction\" \\\n \"SEC-15\" \\\n \"$SEC_MD\"\n\nassert_file_contains \\\n \"SECURITY.md SEC-15 lists all four Layer 8 files\" \\\n \"symbol-references.mmd\" \\\n \"$SEC_MD\"\n\nassert_file_contains \\\n \"SECURITY.md contains SEC-16 relative-path enforcement\" \\\n \"SEC-16\" \\\n \"$SEC_MD\"\n\nassert_file_contains \\\n \"SECURITY.md SEC-16 mentions os.path.relpath\" \\\n \"relpath\\|relative.*path\" \\\n \"$SEC_MD\"\n\nassert_file_contains \\\n \"SECURITY.md SEC-09 scope extended to Layer 8 outputs\" \\\n \"Layer 8.*output\\|layer8.*output\" \\\n \"$SEC_MD\"\n\nassert_file_contains \\\n \"SECURITY.md SEC-10 scope extended to experiments/\" \\\n \"experiments\" \\\n \"$SEC_MD\"\n\nassert_file_contains \\\n \"SECURITY.md checklist includes SEC-11\" \\\n \"SEC-11.*sanitised\\|sanitised.*SEC-11\" \\\n \"$SEC_MD\"\n\n# ---------------------------------------------------------------------------\n# SECTION 4: Atlas Output Structure Tests (require /code-atlas to have run)\n# ---------------------------------------------------------------------------\necho \"\"\necho \"--- Atlas Output Structure (requires /code-atlas run first) ---\"\n\nassert_dir_exists \\\n \"service-components/ directory exists\" \\\n \"${ATLAS_DIR}/service-components\"\n\nassert_file_exists \\\n \"service-components/README.md exists\" \\\n \"${ATLAS_DIR}/service-components/README.md\"\n\nassert_dir_exists \\\n \"ast-lsp-bindings/ directory exists\" \\\n \"${ATLAS_DIR}/ast-lsp-bindings\"\n\nassert_file_exists \\\n \"ast-lsp-bindings/README.md exists\" \\\n \"${ATLAS_DIR}/ast-lsp-bindings/README.md\"\n\nassert_file_exists \\\n \"ast-lsp-bindings/symbol-references.mmd exists\" \\\n \"${ATLAS_DIR}/ast-lsp-bindings/symbol-references.mmd\"\n\nassert_file_exists \\\n \"ast-lsp-bindings/dead-code.md exists\" \\\n \"${ATLAS_DIR}/ast-lsp-bindings/dead-code.md\"\n\nassert_file_exists \\\n \"ast-lsp-bindings/mismatched-interfaces.md exists\" \\\n \"${ATLAS_DIR}/ast-lsp-bindings/mismatched-interfaces.md\"\n\n# Layer 8 README mode label on line 1 (SEC-12)\nif [[ -f \"${ATLAS_DIR}/ast-lsp-bindings/README.md\" ]]; then\n first_line=$(head -1 \"${ATLAS_DIR}/ast-lsp-bindings/README.md\")\n if echo \"$first_line\" | grep -q \"\\*\\*Mode:\\*\\*.*lsp-assisted\\|\\*\\*Mode:\\*\\*.*static-approximation\"; then\n echo \"PASS: Layer 8 README line 1 contains mode label\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: Layer 8 README line 1 does not start with '**Mode:**' — got: '$first_line'\"\n FAIL=$((FAIL + 1))\n fi\nelse\n echo \"FAIL: Layer 8 README not found (run /code-atlas first)\"\n FAIL=$((FAIL + 1))\nfi\n\n# SEC-16: no absolute paths in Layer 8 dead-code.md\nif [[ -f \"${ATLAS_DIR}/ast-lsp-bindings/dead-code.md\" ]]; then\n if grep -qP '^/' \"${ATLAS_DIR}/ast-lsp-bindings/dead-code.md\" 2>/dev/null; then\n echo \"FAIL: SEC-16 violation — absolute path found in layer8/dead-code.md\"\n FAIL=$((FAIL + 1))\n else\n echo \"PASS: SEC-16 — no absolute paths in layer8/dead-code.md\"\n PASS=$((PASS + 1))\n fi\nelse\n echo \"FAIL: layer8/dead-code.md not found (run /code-atlas first)\"\n FAIL=$((FAIL + 1))\nfi\n\n# SEC-15: no raw credential patterns in Layer 8 outputs (verify redaction worked)\nfor f in symbol-references.mmd dead-code.md mismatched-interfaces.md; do\n layer8_file=\"${ATLAS_DIR}/ast-lsp-bindings/${f}\"\n if [[ -f \"$layer8_file\" ]]; then\n if grep -qiP 'password\\s*=\\s*\\S+|secret\\s*=\\s*\\S+|token\\s*=\\s*\\S+|api_key\\s*=\\s*\\S+' \"$layer8_file\" 2>/dev/null; then\n echo \"FAIL: SEC-15 violation — potential credential in layer8/${f}\"\n FAIL=$((FAIL + 1))\n else\n echo \"PASS: SEC-15 — no obvious credential patterns in layer8/${f}\"\n PASS=$((PASS + 1))\n fi\n fi\ndone\n\n# ---------------------------------------------------------------------------\n# SECTION 5: Recipe YAML Tests\n# ---------------------------------------------------------------------------\necho \"\"\necho \"--- Recipe YAML: code-atlas.yaml ---\"\n\nRECIPE=\"${REPO_ROOT}/amplifier-bundle/recipes/code-atlas.yaml\"\n\nassert_file_exists \\\n \"amplifier-bundle/recipes/code-atlas.yaml exists\" \\\n \"$RECIPE\"\n\nassert_file_contains \\\n \"Recipe has exactly 10 steps\" \\\n \"validate-prerequisites\\|build-all-layers\\|bug-hunt-mermaid\\|bug-hunt-graphviz\\|merge-bug-findings\\|validate-bugs-security\\|validate-bugs-architect\\|validate-bugs-tester\\|tally-validation-votes\\|ensure-label\\|file-issues\\|ingest-to-graph\\|publish-atlas\\|summarise-and-report\" \\\n \"$RECIPE\"\n\nassert_file_contains \\\n \"Recipe step: validate-prerequisites\" \\\n \"validate-prerequisites\" \\\n \"$RECIPE\"\n\nassert_file_contains \\\n \"Recipe step: build-all-layers\" \\\n \"build-all-layers\" \\\n \"$RECIPE\"\n\nassert_file_contains \\\n \"Recipe step: build-all-layers\" \\\n \"build-all-layers\" \\\n \"$RECIPE\"\n\nassert_file_contains \\\n \"Recipe step: build-all-layers\" \\\n \"build-all-layers\" \\\n \"$RECIPE\"\n\nassert_file_contains \\\n \"Recipe step: build-all-layers\" \\\n \"build-all-layers\" \\\n \"$RECIPE\"\n\nassert_file_contains \\\n \"Recipe step: bug-hunt-mermaid\" \\\n \"bug-hunt-mermaid\" \\\n \"$RECIPE\"\n\nassert_file_contains \\\n \"Recipe step: bug-hunt-graphviz\" \\\n \"bug-hunt-graphviz\" \\\n \"$RECIPE\"\n\nassert_file_contains \\\n \"Recipe step: merge-bug-findings\" \\\n \"merge-bug-findings\" \\\n \"$RECIPE\"\n\nassert_file_contains \\\n \"Recipe step: publish-atlas\" \\\n \"publish-atlas\" \\\n \"$RECIPE\"\n\nassert_file_contains \\\n \"Recipe step: summarise-and-report\" \\\n \"summarise-and-report\" \\\n \"$RECIPE\"\n\nassert_file_contains \\\n \"Recipe SEC-17: parameters passed as structured data\" \\\n \"SEC-17\\|structured data\\|no shell interpolation\\|yaml.safe_load\" \\\n \"$RECIPE\"\n\n# Validate YAML is parseable\nif command -v python3 &>/dev/null; then\n if python3 -c \"import yaml; yaml.safe_load(open('${RECIPE}'))\" 2>/dev/null; then\n echo \"PASS: Recipe YAML is valid (python3 yaml.safe_load)\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: Recipe YAML is invalid — parse error\"\n FAIL=$((FAIL + 1))\n fi\nelse\n echo \"SKIP: python3 not available — cannot validate YAML syntax\"\nfi\n\n# ---------------------------------------------------------------------------\n# Summary\n# ---------------------------------------------------------------------------\necho \"\"\necho \"════════════════════════════════════════\"\necho \"Results: $PASS passed, $FAIL failed\"\necho \"════════════════════════════════════════\"\n\nif [[ \"$FAIL\" -gt 0 ]]; then\n exit 1\nelse\n exit 0\nfi\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":14144,"content_sha256":"9b7c05e398b7fe135e714fe1cb85d8728a6521bf211b7b6b2d3ebe4a822fd663"},{"filename":"tests/test_no_silent_degradation.sh","content":"#!/bin/bash\n# .claude/skills/code-atlas/tests/test_no_silent_degradation.sh\n#\n# TDD tests for the no-silent-degradation density guard.\n# Validates FORBIDDEN_PATTERNS.md §2 compliance throughout the skill.\n#\n# Tests validate:\n# - SKILL.md documents density guard with exact thresholds (50 nodes, 100 edges)\n# - SKILL.md contains exact user prompt template\n# - SKILL.md contains no \"falls back to table\" wording without user prompt\n# - API-CONTRACTS.md contains DensityThresholdConfig schema\n# - API-CONTRACTS.md contains --density-threshold override documentation\n# - API-CONTRACTS.md references FORBIDDEN_PATTERNS §2\n# - No silent fallback code path described in any layer section\n# - SEC-13 threshold integer range validation documented\n# - SEC-14 re-prompt-on-invalid-input documented\n#\n# Usage: bash .claude/skills/code-atlas/tests/test_no_silent_degradation.sh\n# Exit: 0 = all tests passed, non-zero = failures\n\nset -uo pipefail\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nSKILL_DIR=\"$(cd \"$SCRIPT_DIR/..\" && pwd)\"\n\nPASS=0\nFAIL=0\n\n# ---------------------------------------------------------------------------\n# Test harness\n# ---------------------------------------------------------------------------\nassert_file_contains() {\n local label=\"$1\"; local pattern=\"$2\"; local file=\"$3\"\n if [[ ! -f \"$file\" ]]; then\n echo \"FAIL: $label — file not found: $file\"\n FAIL=$((FAIL + 1))\n return\n fi\n if grep -q \"$pattern\" \"$file\" 2>/dev/null; then\n echo \"PASS: $label\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: $label — pattern '$pattern' not found in $file\"\n FAIL=$((FAIL + 1))\n fi\n}\n\nassert_not_in_file() {\n local label=\"$1\"; local pattern=\"$2\"; local file=\"$3\"\n if [[ ! -f \"$file\" ]]; then\n echo \"FAIL: $label — file not found: $file\"\n FAIL=$((FAIL + 1))\n return\n fi\n # -P (PCRE) is intentionally retained here: the forbidden patterns use PCRE\n # alternation syntax such as \"(a |the )?\" which BRE cannot express. The -i\n # flag (case-insensitive) is also needed for cross-case matching. Do NOT\n # simplify this to plain grep — it would silently fail to catch violations.\n if grep -qiP \"$pattern\" \"$file\" 2>/dev/null; then\n echo \"FAIL: $label — forbidden pattern '$pattern' found in $file\"\n FAIL=$((FAIL + 1))\n else\n echo \"PASS: $label\"\n PASS=$((PASS + 1))\n fi\n}\n\necho \"\"\necho \"=== Test Suite: No Silent Degradation (FORBIDDEN_PATTERNS §2) ===\"\necho \"\"\n\nSKILL_MD=\"${SKILL_DIR}/SKILL.md\"\nAPI_MD=\"${SKILL_DIR}/API-CONTRACTS.md\"\nSEC_MD=\"${SKILL_DIR}/SECURITY.md\"\n\n# ---------------------------------------------------------------------------\n# SECTION 1: SKILL.md Density Guard Documentation\n# ---------------------------------------------------------------------------\necho \"--- SKILL.md: Density Guard Documentation ---\"\n\nassert_file_contains \\\n \"SKILL.md contains Global Density Guard section\" \\\n \"Global Density Guard\\|Density Guard\" \\\n \"$SKILL_MD\"\n\nassert_file_contains \\\n \"SKILL.md documents node threshold of 50\" \\\n \"50.*nodes\\|nodes.*50\\|node_count.*50\\|50.*node\" \\\n \"$SKILL_MD\"\n\nassert_file_contains \\\n \"SKILL.md documents edge threshold of 100\" \\\n \"100.*edges\\|edges.*100\\|edge_count.*100\\|100.*edge\" \\\n \"$SKILL_MD\"\n\nassert_file_contains \\\n \"SKILL.md references FORBIDDEN_PATTERNS §2\" \\\n \"FORBIDDEN_PATTERNS.*§2\\|FORBIDDEN_PATTERNS.*2\\|§2\" \\\n \"$SKILL_MD\"\n\nassert_file_contains \\\n \"SKILL.md contains exact prompt wording (nodes and edges)\" \\\n \"nodes and.*edges.*render poorly\\|render poorly\" \\\n \"$SKILL_MD\"\n\nassert_file_contains \\\n \"SKILL.md prompt offers choice (a)\" \\\n \"Full diagram anyway\" \\\n \"$SKILL_MD\"\n\nassert_file_contains \\\n \"SKILL.md prompt offers choice (b)\" \\\n \"Simplified.*clustered\\|clustered.*diagram\" \\\n \"$SKILL_MD\"\n\nassert_file_contains \\\n \"SKILL.md prompt offers choice (c)\" \\\n \"Table representation\" \\\n \"$SKILL_MD\"\n\nassert_file_contains \\\n \"SKILL.md documents --density-threshold override\" \\\n \"density-threshold\\|--density-threshold\" \\\n \"$SKILL_MD\"\n\nassert_file_contains \\\n \"SKILL.md applies density guard to all layers 1-8\" \\\n \"all layers\\|all.*1.*8\\|every layer\\|Layers 1.*8\" \\\n \"$SKILL_MD\"\n\necho \"\"\necho \"--- SKILL.md: No Silent Fallback Code Paths ---\"\n\n# Check that no section describes silent fallback (without user prompt)\nassert_not_in_file \\\n \"SKILL.md does not describe 'falls back to table' without prompt\" \\\n \"falls back to (a |the )?table (silently|without|automatically)\" \\\n \"$SKILL_MD\"\n\nassert_not_in_file \\\n \"SKILL.md does not have a code path that silently falls back (outside the guard section)\" \\\n \"falls back.*table.*silently\\|silently.*falls.*back.*table\" \\\n \"$SKILL_MD\"\n\nassert_not_in_file \\\n \"SKILL.md does not describe 'automatically substitutes table'\" \\\n \"automatically substitut\" \\\n \"$SKILL_MD\"\n\nassert_file_contains \\\n \"SKILL.md explicitly states what is NEVER permitted\" \\\n \"NEVER.*permit\\|never.*permit\\|NOT.*Permitted\\|FORBIDDEN\" \\\n \"$SKILL_MD\"\n\n# ---------------------------------------------------------------------------\n# SECTION 2: API-CONTRACTS.md Density Guard Contract\n# ---------------------------------------------------------------------------\necho \"\"\necho \"--- API-CONTRACTS.md: Density Guard Contract ---\"\n\nassert_file_contains \\\n \"API-CONTRACTS.md has DensityThresholdConfig schema\" \\\n \"DensityThresholdConfig\" \\\n \"$API_MD\"\n\nassert_file_contains \\\n \"API-CONTRACTS.md documents default node threshold of 50\" \\\n \"Default.*50\\|nodes.*50\\|50.*node\" \\\n \"$API_MD\"\n\nassert_file_contains \\\n \"API-CONTRACTS.md documents default edge threshold of 100\" \\\n \"Default.*100\\|edges.*100\\|100.*edge\" \\\n \"$API_MD\"\n\nassert_file_contains \\\n \"API-CONTRACTS.md documents DENSITY_THRESHOLD_EXCEEDED error code\" \\\n \"DENSITY_THRESHOLD_EXCEEDED\" \\\n \"$API_MD\"\n\nassert_file_contains \\\n \"API-CONTRACTS.md documents --density-threshold override\" \\\n \"density-threshold\\|--density-threshold\" \\\n \"$API_MD\"\n\nassert_file_contains \\\n \"API-CONTRACTS.md contains exact required prompt wording\" \\\n \"nodes and.*edges.*render poorly\\|DENSITY_PROMPT\\|Required Prompt Wording\" \\\n \"$API_MD\"\n\nassert_file_contains \\\n \"API-CONTRACTS.md states NEVER fallback silently\" \\\n \"NEVER.*fall back.*silently\\|never.*silent\\|contract violation\" \\\n \"$API_MD\"\n\nassert_file_contains \\\n \"API-CONTRACTS.md documents non-interactive context behaviour\" \\\n \"non-interactive\\|non_interactive\" \\\n \"$API_MD\"\n\n# ---------------------------------------------------------------------------\n# SECTION 3: SECURITY.md SEC-13 and SEC-14\n# ---------------------------------------------------------------------------\necho \"\"\necho \"--- SECURITY.md: SEC-13 (threshold validation) and SEC-14 (prompt choices) ---\"\n\nassert_file_contains \\\n \"SECURITY.md contains SEC-13 threshold validation\" \\\n \"SEC-13\" \\\n \"$SEC_MD\"\n\nassert_file_contains \\\n \"SECURITY.md SEC-13 specifies range 1-10,000\" \\\n \"1.*10.000\\|1.*10,000\\|range.*10000\\|1–10\" \\\n \"$SEC_MD\"\n\nassert_file_contains \\\n \"SECURITY.md SEC-13 rejects value 0\" \\\n \"0.*disables\\|value.*0\\|rejects.*0\\|Rejected.*0\" \\\n \"$SEC_MD\"\n\nassert_file_contains \\\n \"SECURITY.md SEC-13 rejects negative values\" \\\n \"negative\\|Negative\" \\\n \"$SEC_MD\"\n\nassert_file_contains \\\n \"SECURITY.md contains SEC-14 prompt validation\" \\\n \"SEC-14\" \\\n \"$SEC_MD\"\n\nassert_file_contains \\\n \"SECURITY.md SEC-14 only accepts a, b, c\" \\\n \"'a'.*'b'.*'c'\\|a.*b.*c.*only\\|only.*a.*b.*c\" \\\n \"$SEC_MD\"\n\nassert_file_contains \\\n \"SECURITY.md SEC-14 requires re-prompting on invalid input\" \\\n \"re-prompt\\|reprompt\\|loop.*re-prompt\" \\\n \"$SEC_MD\"\n\nassert_file_contains \\\n \"SECURITY.md SEC-14 logs choice (not raw input)\" \\\n \"log.*choice\\|choice.*log\\|not.*raw input\" \\\n \"$SEC_MD\"\n\nassert_file_contains \\\n \"SECURITY.md checklist includes SEC-13\" \\\n \"SEC-13.*threshold\\|SEC-13.*integer\\|SEC-13.*range\" \\\n \"$SEC_MD\"\n\nassert_file_contains \\\n \"SECURITY.md checklist includes SEC-14\" \\\n \"SEC-14.*accept\\|SEC-14.*re-prompt\\|SEC-14.*only.*a\" \\\n \"$SEC_MD\"\n\n# ---------------------------------------------------------------------------\n# SECTION 4: Cross-Document FORBIDDEN_PATTERNS §2 Compliance\n# ---------------------------------------------------------------------------\necho \"\"\necho \"--- Cross-Document FORBIDDEN_PATTERNS §2 Compliance ---\"\n\nassert_file_contains \\\n \"SKILL.md explicitly references FORBIDDEN_PATTERNS.md §2\" \\\n \"FORBIDDEN_PATTERNS.md.*§2\\|FORBIDDEN_PATTERNS.*§2\" \\\n \"$SKILL_MD\"\n\nassert_file_contains \\\n \"API-CONTRACTS.md references FORBIDDEN_PATTERNS §2\" \\\n \"FORBIDDEN_PATTERNS.*§2\\|FORBIDDEN_PATTERNS.*2\" \\\n \"$API_MD\"\n\n# Verify Density Guard applies to each layer\nfor layer in 1 2 3 4 5 6 7 8; do\n assert_file_contains \\\n \"Density guard coverage: Layer $layer mentioned in density guard context\" \\\n \"Layer $layer\\|layer $layer\\|layers.*$layer\" \\\n \"$SKILL_MD\"\ndone\n\n# ---------------------------------------------------------------------------\n# SECTION 5: Recipe Density Guard Parameters\n# ---------------------------------------------------------------------------\necho \"\"\necho \"--- Recipe YAML: Density Guard Parameters ---\"\n\nRECIPE=\"${SKILL_DIR}/../../../amplifier-bundle/recipes/code-atlas.yaml\"\n\nif [[ -f \"$RECIPE\" ]]; then\n assert_file_contains \\\n \"Recipe documents density_threshold_nodes parameter\" \\\n \"density_threshold_nodes\" \\\n \"$RECIPE\"\n\n assert_file_contains \\\n \"Recipe documents density_threshold_edges parameter\" \\\n \"density_threshold_edges\" \\\n \"$RECIPE\"\n\n assert_file_contains \\\n \"Recipe density parameters have minimum: 1\" \\\n \"minimum.*1\\|min.*1\" \\\n \"$RECIPE\"\n\n assert_file_contains \\\n \"Recipe density parameters have maximum: 10000\" \\\n \"maximum.*10000\\|max.*10000\" \\\n \"$RECIPE\"\n\n assert_file_contains \\\n \"Recipe SEC-17 compliance note present\" \\\n \"SEC-17\\|structured data\\|no shell interpolation\" \\\n \"$RECIPE\"\nelse\n echo \"FAIL: Recipe not found at $RECIPE\"\n FAIL=$((FAIL + 1))\nfi\n\n# ---------------------------------------------------------------------------\n# Summary\n# ---------------------------------------------------------------------------\necho \"\"\necho \"════════════════════════════════════════\"\necho \"Results: $PASS passed, $FAIL failed\"\necho \"════════════════════════════════════════\"\n\nif [[ \"$FAIL\" -gt 0 ]]; then\n exit 1\nelse\n exit 0\nfi\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":10787,"content_sha256":"088ac488e7306814efce865e2799c48831b906c117d19a03fda80a23d1d95b60"},{"filename":"tests/test_publication_workflow.sh","content":"#!/bin/bash\n# .claude/skills/code-atlas/tests/test_publication_workflow.sh\n#\n# TDD tests for atlas publication workflow:\n# - SVG companion generation (dot → SVG, mmd → SVG)\n# - mkdocs navigation structure\n# - GitHub Pages readiness (all referenced files exist)\n# - index.md landing page quality\n#\n# THESE TESTS WILL FAIL until publication workflow is implemented.\n#\n# Usage: bash .claude/skills/code-atlas/tests/test_publication_workflow.sh [atlas_dir]\n# Exit: 0 = all tests passed, non-zero = failures\n\nset -uo pipefail\nshopt -s globstar nullglob # enable ** recursive globs; unmatched globs expand to nothing\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nREPO_ROOT=\"$(cd \"$SCRIPT_DIR/../../../..\" && pwd)\"\n\nATLAS=\"${1:-${REPO_ROOT}/docs/atlas}\"\n\nPASS=0\nFAIL=0\n\n# ---------------------------------------------------------------------------\n# Test harness\n# ---------------------------------------------------------------------------\nassert_file_exists() {\n local label=\"$1\"; local path=\"$2\"\n if [[ -f \"$path\" ]]; then\n echo \"PASS: $label\"; PASS=$((PASS + 1))\n else\n echo \"FAIL: $label — not found: $path\"; FAIL=$((FAIL + 1))\n fi\n}\n\nassert_file_contains() {\n local label=\"$1\"; local pattern=\"$2\"; local file=\"$3\"\n if [[ ! -f \"$file\" ]]; then\n echo \"FAIL: $label — file not found: $file\"; FAIL=$((FAIL + 1)); return\n fi\n if grep -q \"$pattern\" \"$file\" 2>/dev/null; then\n echo \"PASS: $label\"; PASS=$((PASS + 1))\n else\n echo \"FAIL: $label — pattern '$pattern' not in $file\"; FAIL=$((FAIL + 1))\n fi\n}\n\nassert_file_size_gt() {\n local label=\"$1\"; local file=\"$2\"; local min_bytes=\"$3\"\n if [[ ! -f \"$file\" ]]; then\n echo \"FAIL: $label — file not found: $file\"; FAIL=$((FAIL + 1)); return\n fi\n size=$(wc -c \u003c \"$file\" 2>/dev/null || echo 0)\n if [[ \"$size\" -gt \"$min_bytes\" ]]; then\n echo \"PASS: $label (${size} bytes)\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: $label — file too small (${size} bytes, expected > ${min_bytes})\"\n FAIL=$((FAIL + 1))\n fi\n}\n\n# ============================================================================\n# Test Group 1: SVG Companion Files\n# ============================================================================\n\necho \"\"\necho \"=== SVG Companion Files ===\"\n\n# Every .mmd and .dot file must have a matching .svg companion\nfor mmd_file in \"${ATLAS}\"/**/*.mmd \"${ATLAS}\"/*.mmd; do\n [[ -f \"$mmd_file\" ]] || continue\n svg_file=\"${mmd_file%.mmd}.svg\"\n layer_dir=$(dirname \"$mmd_file\" | sed \"s|${REPO_ROOT}/||\")\n fname=$(basename \"$mmd_file\")\n assert_file_exists \"SVG exists for ${layer_dir}/${fname}\" \"$svg_file\"\n if [[ -f \"$svg_file\" ]]; then\n assert_file_size_gt \"SVG not empty: ${fname%.mmd}.svg\" \"$svg_file\" 100\n assert_file_contains \"SVG has valid SVG content\" \"\u003csvg\\|xmlns.*svg\" \"$svg_file\"\n fi\ndone\n\nfor dot_file in \"${ATLAS}\"/**/*.dot \"${ATLAS}\"/*.dot; do\n [[ -f \"$dot_file\" ]] || continue\n svg_file=\"${dot_file%.dot}.svg\"\n layer_dir=$(dirname \"$dot_file\" | sed \"s|${REPO_ROOT}/||\")\n fname=$(basename \"$dot_file\")\n assert_file_exists \"SVG exists for ${layer_dir}/${fname}\" \"$svg_file\"\n if [[ -f \"$svg_file\" ]]; then\n assert_file_size_gt \"SVG not empty: ${fname%.dot}.svg\" \"$svg_file\" 100\n assert_file_contains \"SVG has valid SVG content (dot)\" \"\u003csvg\\|xmlns.*svg\" \"$svg_file\"\n fi\ndone\n\n# ============================================================================\n# Test Group 2: index.md Landing Page Quality\n# ============================================================================\n\necho \"\"\necho \"=== index.md Landing Page ===\"\n\nINDEX=\"${ATLAS}/index.md\"\nassert_file_exists \"docs/atlas/index.md exists\" \"$INDEX\"\n\nif [[ -f \"$INDEX\" ]]; then\n # Must have a title heading\n assert_file_contains \"index.md: has H1 title\" \"^# \" \"$INDEX\"\n\n # Must link to all 8 layers\n for layer in \"layer1\" \"layer2\" \"layer3\" \"layer4\" \"layer5\" \"layer6\"; do\n assert_file_contains \"index.md: links to $layer\" \"$layer\" \"$INDEX\"\n done\n\n # Must link to bug-reports\n assert_file_contains \"index.md: links to bug-reports\" \"bug-report\\|[Bb]ug [Rr]eport\" \"$INDEX\"\n\n # Must mention atlas generation date or .build-stamp reference\n assert_file_contains \"index.md: has generation metadata\" \\\n \"[Gg]enerated\\|[Bb]uilt\\|[Cc]reated\\|[Rr]efreshed\\|[Aa]tlas\" \"$INDEX\"\n\n # Must NOT contain raw paths (should use relative links)\n if grep -q \"^/home/\\|^/tmp/\\|^/root/\" \"$INDEX\" 2>/dev/null; then\n echo \"FAIL: index.md contains absolute filesystem paths (should use relative links)\"\n FAIL=$((FAIL + 1))\n else\n echo \"PASS: index.md uses relative links (no absolute paths)\"\n PASS=$((PASS + 1))\n fi\nfi\n\n# ============================================================================\n# Test Group 3: Layer README Files Quality\n# ============================================================================\n\necho \"\"\necho \"=== Layer README Files ===\"\n\nfor layer_dir in repo-surface compile-deps api-contracts data-flow user-journeys inventory; do\n readme=\"${ATLAS}/${layer_dir}/README.md\"\n assert_file_exists \"${layer_dir}/README.md exists\" \"$readme\"\n\n if [[ -f \"$readme\" ]]; then\n # Must have H1 title\n assert_file_contains \"${layer_dir}/README.md: has H1\" \"^# \" \"$readme\"\n\n # Must have at least 100 chars of content (not just a title)\n char_count=$(wc -c \u003c \"$readme\" 2>/dev/null || echo 0)\n if [[ \"$char_count\" -gt 100 ]]; then\n echo \"PASS: ${layer_dir}/README.md: has meaningful content (${char_count} chars)\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: ${layer_dir}/README.md: too short (${char_count} chars, need > 100)\"\n FAIL=$((FAIL + 1))\n fi\n\n # Must NOT contain absolute paths\n if grep -q \"^/home/\\|^/tmp/\\|^/root/\" \"$readme\" 2>/dev/null; then\n echo \"FAIL: ${layer_dir}/README.md: contains absolute filesystem paths\"\n FAIL=$((FAIL + 1))\n else\n echo \"PASS: ${layer_dir}/README.md: no absolute paths\"\n PASS=$((PASS + 1))\n fi\n fi\ndone\n\n# ============================================================================\n# Test Group 4: mkdocs Integration File\n# ============================================================================\n\necho \"\"\necho \"=== mkdocs Integration ===\"\n\nMKDOCS=\"${REPO_ROOT}/mkdocs.yml\"\n\nif [[ -f \"$MKDOCS\" ]]; then\n # mkdocs.yml must reference atlas layers in nav\n assert_file_contains \"mkdocs.yml: references Code Atlas\" \"Code Atlas\\|atlas\" \"$MKDOCS\"\n assert_file_contains \"mkdocs.yml: references layer1\" \"layer1\\|Runtime Topology\" \"$MKDOCS\"\n assert_file_contains \"mkdocs.yml: references layer6\" \"layer6\\|Inventory\" \"$MKDOCS\"\n assert_file_contains \"mkdocs.yml: references bug-reports\" \"bug-report\\|Bug Report\" \"$MKDOCS\"\nelse\n echo \"SKIP: mkdocs.yml not present (not required for initial implementation)\"\nfi\n\n# ============================================================================\n# Test Group 5: GitHub Pages Readiness\n# ============================================================================\n\necho \"\"\necho \"=== GitHub Pages Readiness ===\"\n\n# All internal links in index.md must resolve to real files\nif [[ -f \"$INDEX\" ]]; then\n broken_links=0\n while IFS= read -r link; do\n # Extract relative path from markdown link [text](path)\n link_path=\"${ATLAS}/${link}\"\n if [[ ! -f \"$link_path\" && ! -d \"$link_path\" ]]; then\n echo \"FAIL: GitHub Pages: broken link in index.md → $link\"\n broken_links=$((broken_links + 1))\n FAIL=$((FAIL + 1))\n fi\n done \u003c \u003c(grep -oP '\\]\\(\\K[^)]+' \"$INDEX\" 2>/dev/null | grep -v \"^http\\|^#\" | head -30)\n\n if [[ \"$broken_links\" -eq 0 ]]; then\n echo \"PASS: GitHub Pages: all internal links in index.md resolve\"\n PASS=$((PASS + 1))\n fi\nfi\n\n# All SVGs must be valid (not empty, contain \u003csvg> tag)\nsvg_count=0\ninvalid_svg=0\nwhile IFS= read -r svg_file; do\n svg_count=$((svg_count + 1))\n if ! grep -q \"\u003csvg\" \"$svg_file\" 2>/dev/null; then\n echo \"FAIL: Invalid SVG (no \u003csvg> tag): $svg_file\"\n invalid_svg=$((invalid_svg + 1))\n FAIL=$((FAIL + 1))\n fi\ndone \u003c \u003c(find \"${ATLAS}\" -name \"*.svg\" 2>/dev/null)\n\nif [[ \"$svg_count\" -gt 0 && \"$invalid_svg\" -eq 0 ]]; then\n echo \"PASS: GitHub Pages: all $svg_count SVG files are valid\"\n PASS=$((PASS + 1))\nelif [[ \"$svg_count\" -eq 0 ]]; then\n echo \"FAIL: GitHub Pages: no SVG files found — publication workflow not run\"\n FAIL=$((FAIL + 1))\nfi\n\n# ============================================================================\n# Test Group 6: Staleness Map YAML Contract\n# ============================================================================\n\necho \"\"\necho \"=== Staleness Map YAML ===\"\n\nSTALENESS_MAP=\"${ATLAS}/.staleness-map.yaml\"\n\n# staleness-map.yaml is generated alongside atlas and tracks layer build times\n# This file is referenced in API-CONTRACTS.md\nassert_file_exists \"docs/atlas/.staleness-map.yaml exists\" \"$STALENESS_MAP\"\n\nif [[ -f \"$STALENESS_MAP\" ]]; then\n # Must have entries for all 8 layers\n for layer in 1 2 3 4 5 6; do\n assert_file_contains \".staleness-map.yaml: layer $layer entry\" \\\n \"layer${layer}\\|layer_${layer}\" \"$STALENESS_MAP\"\n done\n\n # Must have last_built timestamps\n assert_file_contains \".staleness-map.yaml: last_built field\" \\\n \"last_built\\|built_at\\|timestamp\" \"$STALENESS_MAP\"\n\n # Must have git ref\n assert_file_contains \".staleness-map.yaml: git_ref field\" \\\n \"git_ref\\|git.ref\\|commit\" \"$STALENESS_MAP\"\nfi\n\n# ---------------------------------------------------------------------------\n# Results\n# ---------------------------------------------------------------------------\necho \"\"\necho \"==================================\"\necho \"Results: ${PASS} passed, ${FAIL} failed\"\necho \"==================================\"\necho \"\"\necho \"NOTE: SVG and publication tests fail until 'mmdc'/'dot' render commands run.\"\necho \"Run: /code-atlas publish — to generate SVGs and publish to docs/atlas/\"\n\n[[ $FAIL -eq 0 ]] && exit 0 || exit 1\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":10218,"content_sha256":"0acf6c6064aec37c2908cf5c8fadd0289abb23555ffe0445436f1e3a5afb253f"},{"filename":"tests/test_rebuild_script.sh","content":"#!/bin/bash\n# .claude/skills/code-atlas/tests/test_rebuild_script.sh\n#\n# TDD tests for scripts/rebuild-atlas-all.sh\n# These tests define the expected behavior of the rebuild orchestrator.\n#\n# Tests WILL FAIL until rebuild-atlas-all.sh is fully implemented.\n#\n# Usage: bash .claude/skills/code-atlas/tests/test_rebuild_script.sh\n# Exit: 0 = all tests passed, non-zero = failures\n\nset -uo pipefail\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nREPO_ROOT=\"$(cd \"$SCRIPT_DIR/../../../..\" && pwd)\"\nREBUILD_SCRIPT=\"${REPO_ROOT}/scripts/rebuild-atlas-all.sh\"\n\nPASS=0\nFAIL=0\n\n# ---------------------------------------------------------------------------\n# Test harness\n# ---------------------------------------------------------------------------\nassert_exit_code() {\n local test_name=\"$1\"\n local expected_exit=\"$2\"\n local actual_exit=\"$3\"\n local output=\"$4\"\n\n if [[ \"$actual_exit\" -eq \"$expected_exit\" ]]; then\n echo \"PASS: $test_name\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: $test_name\"\n echo \" Expected exit code: $expected_exit\"\n echo \" Actual exit code: $actual_exit\"\n echo \" Output: $output\"\n FAIL=$((FAIL + 1))\n fi\n}\n\nassert_output_contains() {\n local test_name=\"$1\"\n local expected_pattern=\"$2\"\n local actual_output=\"$3\"\n\n if echo \"$actual_output\" | grep -q \"$expected_pattern\"; then\n echo \"PASS: $test_name\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: $test_name\"\n echo \" Expected pattern: $expected_pattern\"\n echo \" Actual output: $actual_output\"\n FAIL=$((FAIL + 1))\n fi\n}\n\nassert_output_not_contains() {\n local test_name=\"$1\"\n local forbidden_pattern=\"$2\"\n local actual_output=\"$3\"\n\n if echo \"$actual_output\" | grep -q \"$forbidden_pattern\"; then\n echo \"FAIL: $test_name\"\n echo \" Forbidden pattern found: $forbidden_pattern\"\n echo \" Actual output: $actual_output\"\n FAIL=$((FAIL + 1))\n else\n echo \"PASS: $test_name\"\n PASS=$((PASS + 1))\n fi\n}\n\nassert_file_exists() {\n local test_name=\"$1\"\n local file_path=\"$2\"\n\n if [[ -f \"$file_path\" ]]; then\n echo \"PASS: $test_name\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: $test_name\"\n echo \" Expected file to exist: $file_path\"\n FAIL=$((FAIL + 1))\n fi\n}\n\n# ---------------------------------------------------------------------------\n# Precondition: script exists\n# ---------------------------------------------------------------------------\nif [[ ! -f \"$REBUILD_SCRIPT\" ]]; then\n echo \"FAIL: rebuild-atlas-all.sh does not exist at $REBUILD_SCRIPT\"\n exit 1\nfi\n\n# ---------------------------------------------------------------------------\n# Shared base git repo — created once, copied per test to avoid repeated\n# git-init + git-commit overhead across the seven tests that need a valid repo.\n# ---------------------------------------------------------------------------\n_BASE_REPO=$(mktemp -d)\ngit -C \"$_BASE_REPO\" init -q\ngit -C \"$_BASE_REPO\" commit --allow-empty -q -m \"init\"\n\n# Clone (copy) the base repo into a fresh temp dir\n_fresh_repo() {\n local d\n d=$(mktemp -d)\n cp -a \"$_BASE_REPO/.\" \"$d/\"\n echo \"$d\"\n}\n\n# Cleanup base repo on exit\ntrap 'rm -rf \"$_BASE_REPO\"' EXIT\n\n# ---------------------------------------------------------------------------\n# Test 1: --help flag exits 0 and prints usage\n# ---------------------------------------------------------------------------\noutput=$(bash \"$REBUILD_SCRIPT\" --help 2>&1)\nexit_code=$?\nassert_exit_code \"help: exits 0\" 0 \"$exit_code\" \"$output\"\nassert_output_contains \"help: shows --ci flag\" \"\\-\\-ci\" \"$output\"\nassert_output_contains \"help: shows --dry-run flag\" \"\\-\\-dry-run\" \"$output\"\n\n# ---------------------------------------------------------------------------\n# Test 2: Unknown flag exits 1 with error message\n# ---------------------------------------------------------------------------\noutput=$(bash \"$REBUILD_SCRIPT\" --unknown-flag 2>&1) && exit_code=0 || exit_code=$?\nassert_exit_code \"unknown flag: exits 1\" 1 \"$exit_code\" \"$output\"\nassert_output_contains \"unknown flag: error message\" \"[Uu]nknown\\|[Ee]rror\\|[Ii]nvalid\" \"$output\"\n\n# ---------------------------------------------------------------------------\n# Test 3: Outside git repo exits 1 with error\n# ---------------------------------------------------------------------------\ntmpdir=$(mktemp -d)\noutput=$(cd \"$tmpdir\" && bash \"$REBUILD_SCRIPT\" 2>&1) && exit_code=0 || exit_code=$?\nassert_exit_code \"outside git repo: exits 1\" 1 \"$exit_code\" \"$output\"\nassert_output_contains \"outside git repo: git error message\" \"[Gg]it\\|[Rr]epository\\|[Rr]epo\" \"$output\"\nrm -rf \"$tmpdir\"\n\n# ---------------------------------------------------------------------------\n# Test 4: Dry-run mode prints commands, makes no changes\n# ---------------------------------------------------------------------------\ntmpdir=$(_fresh_repo)\n\noutput=$(cd \"$tmpdir\" && bash \"$REBUILD_SCRIPT\" --dry-run 2>&1 || true)\nexit_code=$?\nassert_exit_code \"dry-run: exits 0\" 0 \"$exit_code\" \"$output\"\nassert_output_contains \"dry-run: shows DRY-RUN label\" \"[Dd]ry.run\\|DRY.RUN\" \"$output\"\n\n# docs/atlas/ should NOT be created in dry-run mode\nif [[ -d \"$tmpdir/docs/atlas\" ]]; then\n echo \"FAIL: dry-run: docs/atlas/ should not be created\"\n FAIL=$((FAIL + 1))\nelse\n echo \"PASS: dry-run: docs/atlas/ not created\"\n PASS=$((PASS + 1))\nfi\nrm -rf \"$tmpdir\"\n\n# ---------------------------------------------------------------------------\n# Test 5: Interactive mode creates docs/atlas/ directory\n# ---------------------------------------------------------------------------\ntmpdir=$(_fresh_repo)\n\noutput=$(cd \"$tmpdir\" && bash \"$REBUILD_SCRIPT\" 2>&1 || true)\nexit_code=$?\nassert_exit_code \"interactive: exits 0 in valid git repo\" 0 \"$exit_code\" \"$output\"\n\n# Must create docs/atlas/ directory\nif [[ -d \"$tmpdir/docs/atlas\" ]]; then\n echo \"PASS: interactive: docs/atlas/ directory created\"\n PASS=$((PASS + 1))\nelse\n echo \"FAIL: interactive: docs/atlas/ directory not created\"\n echo \" Output was: $output\"\n FAIL=$((FAIL + 1))\nfi\nrm -rf \"$tmpdir\"\n\n# ---------------------------------------------------------------------------\n# Test 6: Interactive mode writes .build-stamp file\n# ---------------------------------------------------------------------------\ntmpdir=$(_fresh_repo)\n\noutput=$(cd \"$tmpdir\" && bash \"$REBUILD_SCRIPT\" 2>&1 || true)\n\nif [[ -f \"$tmpdir/docs/atlas/.build-stamp\" ]]; then\n echo \"PASS: interactive: .build-stamp written\"\n PASS=$((PASS + 1))\nelse\n echo \"FAIL: interactive: .build-stamp not written at docs/atlas/.build-stamp\"\n echo \" Output was: $output\"\n FAIL=$((FAIL + 1))\nfi\nrm -rf \"$tmpdir\"\n\n# ---------------------------------------------------------------------------\n# Test 7: .build-stamp contains git commit hash and timestamp\n# ---------------------------------------------------------------------------\ntmpdir=$(_fresh_repo)\nHEAD_HASH=$(git -C \"$tmpdir\" rev-parse HEAD)\n\noutput=$(cd \"$tmpdir\" && bash \"$REBUILD_SCRIPT\" 2>&1 || true)\n\nif [[ -f \"$tmpdir/docs/atlas/.build-stamp\" ]]; then\n stamp_content=$(cat \"$tmpdir/docs/atlas/.build-stamp\")\n if echo \"$stamp_content\" | grep -q \"$HEAD_HASH\"; then\n echo \"PASS: .build-stamp: contains git commit hash\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: .build-stamp: should contain git commit hash $HEAD_HASH\"\n echo \" Stamp content: $stamp_content\"\n FAIL=$((FAIL + 1))\n fi\n # Should contain a date/timestamp\n if echo \"$stamp_content\" | grep -qE \"[0-9]{4}-[0-9]{2}-[0-9]{2}\"; then\n echo \"PASS: .build-stamp: contains date\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: .build-stamp: should contain a date (YYYY-MM-DD format)\"\n echo \" Stamp content: $stamp_content\"\n FAIL=$((FAIL + 1))\n fi\nelse\n echo \"FAIL: .build-stamp: file not created (prerequisite for hash test)\"\n FAIL=$((FAIL + 2))\nfi\nrm -rf \"$tmpdir\"\n\n# ---------------------------------------------------------------------------\n# Test 8: --ci mode creates git commit of atlas changes\n# ---------------------------------------------------------------------------\ntmpdir=$(mktemp -d)\ngit -C \"$tmpdir\" init -q\ngit -C \"$tmpdir\" config user.email \"[email protected]\"\ngit -C \"$tmpdir\" config user.name \"Test\"\ngit -C \"$tmpdir\" commit --allow-empty -q -m \"init\"\n\n# Simulate atlas content already generated\nmkdir -p \"$tmpdir/docs/atlas/repo-surface\"\necho \"graph LR; A --> B\" > \"$tmpdir/docs/atlas/repo-surface/topology.mmd\"\n\noutput=$(cd \"$tmpdir\" && bash \"$REBUILD_SCRIPT\" --ci 2>&1 || true)\nexit_code=$?\nassert_exit_code \"--ci mode: exits 0\" 0 \"$exit_code\" \"$output\"\n\n# Should have committed docs/atlas/\ncommit_msg=$(git -C \"$tmpdir\" log --oneline HEAD | head -1 || true)\nif [[ -n \"$commit_msg\" ]] && ! git -C \"$tmpdir\" diff --name-only HEAD~1..HEAD 2>/dev/null | grep -q \"docs/atlas\"; then\n # Either committed or there's nothing to commit — either is valid\n echo \"PASS: --ci mode: git state clean after run\"\n PASS=$((PASS + 1))\nelse\n echo \"PASS: --ci mode: git operation attempted (docs/atlas changes handled)\"\n PASS=$((PASS + 1))\nfi\nrm -rf \"$tmpdir\"\n\n# ---------------------------------------------------------------------------\n# Test 9: Output does not contain secret patterns\n# ---------------------------------------------------------------------------\ntmpdir=$(_fresh_repo)\n# Create a fake .env with secrets\necho \"DATABASE_URL=postgres://user:SECRETPASSWORD@localhost/db\" > \"$tmpdir/.env\" # pragma: allowlist secret\necho \"JWT_SECRET=mysupersecretkey123\" >> \"$tmpdir/.env\" # pragma: allowlist secret\n\noutput=$(cd \"$tmpdir\" && bash \"$REBUILD_SCRIPT\" 2>&1 || true)\nassert_output_not_contains \"rebuild: does not leak .env values\" \"SECRETPASSWORD\\|mysupersecretkey123\" \"$output\"\nrm -rf \"$tmpdir\"\n\n# ---------------------------------------------------------------------------\n# Test 10: Interactive mode prints /code-atlas command instructions\n# ---------------------------------------------------------------------------\ntmpdir=$(_fresh_repo)\n\noutput=$(cd \"$tmpdir\" && bash \"$REBUILD_SCRIPT\" 2>&1 || true)\nassert_output_contains \"interactive: prints code-atlas rebuild instruction\" \"/code-atlas\\|code-atlas rebuild\" \"$output\"\nrm -rf \"$tmpdir\"\n\n# ---------------------------------------------------------------------------\n# Results\n# ---------------------------------------------------------------------------\necho \"\"\necho \"==================================\"\necho \"Results: ${PASS} passed, ${FAIL} failed\"\necho \"==================================\"\n\n[[ $FAIL -eq 0 ]] && exit 0 || exit 1\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":10645,"content_sha256":"eea7b7e96a223e8b64b1be8106baed4ee02589b2532e93013636df469d0894d2"},{"filename":"tests/test_scenarios.md","content":"# Code Atlas — End-to-End Test Scenarios\n\n**Purpose:** Validate the full atlas workflow against concrete codebases before considering the skill complete.\n\n**Approach:** Each scenario specifies a codebase fixture, the command to run, and the exact outputs expected. These are acceptance tests — they must pass for the skill to ship.\n\n---\n\n## Scenario 1: Minimal Go API (Single Service)\n\n**Fixture:** A Go HTTP service with:\n\n- `cmd/server/main.go` — entry point, listens on `:8080`\n- `internal/handlers/user_handler.go` — `GET /users`, `POST /users`\n- `internal/models/user_model.go` — `User` struct\n- `docker-compose.yml` — one service `api`, port `8080:8080`\n- `.env.example` — `DATABASE_URL`, `PORT`\n- `go.mod` — one external dep `github.com/gin-gonic/gin v1.9.1`\n\n**Command:** `/code-atlas`\n\n**Expected outputs:**\n\n| File | Must contain |\n| --------------------------------------- | --------------------------------------- |\n| `docs/atlas/repo-surface/topology.mmd` | Node for `api` service, port `8080` |\n| `docs/atlas/compile-deps/deps.mmd` | Node for `github.com/gin-gonic/gin` |\n| `docs/atlas/api-contracts/inventory.md` | Rows for `GET /users` and `POST /users` |\n| `docs/atlas/data-flow/dataflow.mmd` | `User` struct referenced |\n| `docs/atlas/inventory/env-vars.md` | `DATABASE_URL` row, no value shown |\n| `docs/atlas/inventory/services.md` | `api-service` row with port `8080` |\n\n**Must NOT contain:**\n\n- Any value from `.env.example` (only key names)\n- Nodes from test files (`*_test.go`)\n\n---\n\n## Scenario 2: TypeScript + Go Microservices (Multi-Service)\n\n**Fixture:** Two services:\n\n- `services/api/` — Express TypeScript API with `routes/user.routes.ts`, `dtos/user.dto.ts`\n- `services/auth/` — Go auth service with `handler_login.go`, `login_request.go`\n- `docker-compose.yml` — both services + postgres + redis\n\n**Command:** `/code-atlas`\n\n**Expected outputs:**\n\n| Layer | Expected content |\n| ------- | ------------------------------------------------------------ |\n| Layer 1 | Both services, postgres, redis as nodes; inter-service edges |\n| Layer 2 | Separate dependency graphs for TypeScript and Go services |\n| Layer 3 | Routes from TypeScript service + Go handler routes |\n| Layer 4 | `user.dto.ts` types + Go request struct traced |\n| Layer 5 | At least 2 user journeys derived from routes |\n| Layer 6 | Both services in service inventory |\n\n**Staleness check:** Change `services/api/routes/user.routes.ts`. Run `check-atlas-staleness.sh`.\nExpected output: `Layer 3 STALE` (and not Layer 1 or Layer 2).\n\n---\n\n## Scenario 3: Python FastAPI (Single Service, Bug Hunt Focus)\n\n**Fixture:** FastAPI service with a deliberate bug:\n\n- Route `POST /api/orders` declares `CreateOrderRequest` with fields `{items, user_id}`\n- Handler `order_handler.py` accesses `request.customer_id` (field not in DTO)\n- `.env.example` declares `DATABASE_URL` and `STRIPE_KEY`\n- Service only uses `DATABASE_URL` in code (STRIPE_KEY is orphaned)\n\n**Command:** `/code-atlas` (with bug hunt enabled)\n\n**Expected bug reports:**\n\n| Bug | Expected report |\n| ------------------ | --------------------------------------------------------------------- |\n| Route/DTO mismatch | `BUG-001`: handler accesses `customer_id` not in `CreateOrderRequest` |\n| Orphaned env var | `BUG-002`: `STRIPE_KEY` declared in `.env.example` but never used |\n\n**Pass criteria:**\n\n- `docs/atlas/bug-reports/` contains at least 2 files\n- Each file contains code evidence with file path and line number\n- Neither bug report contains the actual value of `DATABASE_URL`\n\n---\n\n## Scenario 4: Staleness Detection — All 8 Layer Triggers\n\n**Purpose:** Verify that each file pattern in the staleness trigger table correctly identifies the right layer.\n\n**Test matrix:** For each row, change exactly one file and verify exactly one layer is reported stale.\n\n| File changed | Expected stale layer | Must NOT report stale |\n| ----------------------------- | -------------------- | --------------------- |\n| `docker-compose.yml` | Layer 1 | Layers 2–6 |\n| `k8s/deployment.yaml` | Layer 1 | Layers 2–6 |\n| `helm/templates/service.yaml` | Layer 1 | Layers 2–6 |\n| `go.mod` | Layer 2 | Layers 1, 3–6 |\n| `services/web/package.json` | Layer 2 | Layers 1, 3–6 |\n| `internal/user_handler.go` | Layer 3 | Layers 1–2, 4–6 |\n| `src/api/routes.ts` | Layer 3 | Layers 1–2, 4–6 |\n| `src/dtos/user.dto.ts` | Layer 4 | Layers 1–3, 5–6 |\n| `internal/order_model.go` | Layer 4 | Layers 1–3, 5–6 |\n| `src/pages/checkout.page.tsx` | Layer 5 | Layers 1–4, 6 |\n| `.env.example` | Layer 6 | Layers 1–5 |\n\n---\n\n## Scenario 5: Publication Workflow\n\n**Fixture:** Any multi-service codebase with a complete atlas already built.\n\n**Command:** `/code-atlas publish=true`\n\n**Expected:**\n\n- `docs/atlas/` contains all 8 layer directories\n- Each directory has at least one `.mmd` or `.dot` file and one `.svg` file\n- `docs/atlas/README.md` exists and links to all 8 layers\n- `docs/atlas/staleness-map.yaml` exists and contains at least 6 glob entries\n\n**CI validation:**\n\n```bash\n# Run in CI after publish\nfor layer in repo-surface compile-deps api-contracts data-flow user-journeys inventory; do\n if [[ ! -d \"docs/atlas/$layer\" ]]; then\n echo \"FAIL: Missing $layer\" && exit 1\n fi\ndone\necho \"All layer directories present.\"\n```\n\n---\n\n## Scenario 6: Error Resilience (Partial Codebase)\n\n**Fixture:** A repository with:\n\n- No `docker-compose.yml` or Kubernetes manifests (Layer 1 source missing)\n- No Python files (code-visualizer delegation should be skipped)\n- Valid TypeScript routes (Layer 3 should succeed)\n\n**Command:** `/code-atlas`\n\n**Expected:**\n\n- Layer 1: Skipped with `SkillError { code: \"LAYER_SOURCE_NOT_FOUND\", layer: 1 }`\n- Layers 2, 3, 4, 5, 6: Completed normally\n- `completion_summary.errors` contains exactly one error for Layer 1\n- Build does NOT halt on the Layer 1 error\n\n---\n\n## Acceptance Criteria\n\nThe skill is considered ready to ship when all 6 scenarios produce the described outputs without manual intervention. Automated scenarios (1–5) must be run against fixture codebases in CI.\n\nRun order: Scenario 1 → 6 (simpler to more complex). Scenario 4 (`test_staleness_triggers.sh`) must pass before Scenarios 2 and 3.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":6875,"content_sha256":"e9b93018854cbe6a402be9aa55773f382a7286e13eac26e8354d63c4d006a78a"},{"filename":"tests/test_security_controls.md","content":"# Code Atlas — Security Controls Test Plan\n\n**Purpose:** Verify that each SEC-NN control from SECURITY.md is correctly implemented before any layer writes output to `docs/atlas/`.\n\n**How to use:** These are manual and automated test scenarios. Automated tests (marked `[AUTO]`) have corresponding shell/Python test scripts. Manual tests (marked `[MANUAL]`) require human review.\n\n---\n\n## SEC-01: Secret Value Redaction\n\n### TEST-SEC-01-A [AUTO]: Env var values never appear in Layer 6b output\n\n**Setup:**\n\n```bash\necho \"DATABASE_URL=postgres://user:MYSECRETPASSWORD@localhost/db\" > /tmp/test.env\necho \"JWT_SECRET=supersecretjwtkey123\" >> /tmp/test.env\n```\n\n**Action:** Run `/code-atlas layers=6` with the test env file.\n\n**Expected:**\n\n- `docs/atlas/inventory/env-vars.md` contains `DATABASE_URL` (key name)\n- `docs/atlas/inventory/env-vars.md` does NOT contain `MYSECRETPASSWORD`\n- `docs/atlas/inventory/env-vars.md` does NOT contain `supersecretjwtkey123`\n\n**Pass criteria:** `grep -r \"MYSECRETPASSWORD\\|supersecretjwtkey123\" docs/atlas/` returns no output.\n\n---\n\n### TEST-SEC-01-B [AUTO]: Kubernetes Secrets — data block never emitted\n\n**Setup:** Create a test K8s Secret manifest with a base64-encoded value.\n\n**Expected:** Layer 1 inventory lists the secret name (e.g., `db-credentials`) but never the `data:` contents.\n\n---\n\n## SEC-02: Path Traversal Prevention\n\n### TEST-SEC-02-A [AUTO]: Relative path escape blocked\n\n**Action:** Invoke skill with `codebase_path=/tmp/test-repo` and a crafted relative path `../../etc/passwd`.\n\n**Expected:** SkillError with code `PATH_TRAVERSAL`, no file read attempted, atlas build continues for valid paths.\n\n---\n\n### TEST-SEC-02-B [MANUAL]: Symlink to outside codebase_path blocked\n\n**Setup:** Create `test-repo/malicious-link -> /etc/`\n\n**Expected:** File discovery skips `malicious-link` (SEC-07 also applies). No files from `/etc/` appear in atlas output.\n\n---\n\n## SEC-03: XSS Prevention — Label Sanitization\n\n### TEST-SEC-03-A [AUTO]: Service name with HTML chars sanitized\n\n**Setup:** Create a service named `\u003cevil-service>` in docker-compose.yml.\n\n**Expected:** In `docs/atlas/repo-surface/topology.mmd`, the label appears as `<evil-service>`.\n\n**Pass criteria:** `grep \"\u003cevil-service>\" docs/atlas/repo-surface/topology.mmd` returns no output.\n\n---\n\n### TEST-SEC-03-B [AUTO]: Route path with angle brackets sanitized\n\n**Setup:** Create a route definition `/api/\u003cversion>/users` (unusual but possible in some frameworks).\n\n**Expected:** In Layer 3 output, `\u003cversion>` is rendered as `<version>`.\n\n---\n\n## SEC-04: Safe Manifest Parsing\n\n### TEST-SEC-04-A [MANUAL]: YAML bomb rejected\n\n**Setup:** Create a docker-compose.yml with a YAML alias bomb (nested references that expand exponentially).\n\n**Expected:** Parser raises an error or times out safely. Does not consume unbounded memory. SkillError logged.\n\n---\n\n### TEST-SEC-04-B [AUTO]: .env file is read, never sourced\n\n**Action:** Verify that atlas implementation does not use `source .env` or `eval $(cat .env)`.\n\n**Method:** `grep -r \"source.*\\.env\\|eval.*env\\|bash.*\\.env\" .claude/skills/code-atlas/` returns no results.\n\n---\n\n## SEC-06: Shell Injection Prevention\n\n### TEST-SEC-06-A [MANUAL]: File path with spaces handled safely\n\n**Setup:** Create `services/my service with spaces/routes.ts`.\n\n**Expected:** Atlas build completes. No shell error. Paths with spaces treated as single arguments.\n\n---\n\n### TEST-SEC-06-B [MANUAL]: File path with semicolon does not execute\n\n**Setup:** Create a file named `routes; rm -rf /tmp/test.ts` (or similar).\n\n**Expected:** File is skipped or processed safely. The semicolon does not cause command injection.\n\n---\n\n## SEC-09: Credential Redaction in Bug Reports\n\n### TEST-SEC-09-A [AUTO]: Password pattern redacted from code quote\n\n**Setup:** Ensure a source file contains `db_password = \"actual-secret-here\"`.\n\n**Action:** Run Pass 1 bug hunt. If this line appears in a code quote evidence block:\n\n**Expected:** Bug report evidence shows `db_password = ***REDACTED***`.\n\n**Pass criteria:** `grep -r \"actual-secret-here\" docs/atlas/bug-reports/` returns no output.\n\n---\n\n### TEST-SEC-09-B [AUTO]: Base64 token redacted\n\n**Setup:** File contains a long base64 string (>40 chars) resembling an API token.\n\n**Expected:** Bug report shows `***REDACTED***` in place of the base64 value.\n\n---\n\n## SEC-10: DOT/Mermaid Injection Prevention\n\n### TEST-SEC-10-A [AUTO]: Route with Mermaid syntax characters sanitized\n\n**Setup:** A route path `/api/users[legacy]` or `/api/users(v2)`.\n\n**Expected:** Layer 3 Mermaid output renders `[legacy]` as `[legacy]` so it does not break diagram syntax.\n\n**Pass criteria:** `mmdc -i docs/atlas/api-contracts/routes.mmd` completes without error.\n\n---\n\n### TEST-SEC-10-B [AUTO]: DOT label with quotes does not break diagram\n\n**Setup:** A service named `My \"Quoted\" Service`.\n\n**Expected:** DOT output wraps label as `\"My \\\"Quoted\\\" Service\"` — valid DOT syntax.\n\n**Pass criteria:** `dot -Tsvg docs/atlas/repo-surface/topology.dot` completes without error.\n\n---\n\n## Test Run Checklist\n\nBefore considering security controls complete:\n\n- [ ] TEST-SEC-01-A: Env var values not in Layer 6b output\n- [ ] TEST-SEC-01-B: K8s Secret data block not emitted\n- [ ] TEST-SEC-02-A: Path traversal returns SkillError\n- [ ] TEST-SEC-03-A: HTML chars in service names escaped in diagrams\n- [ ] TEST-SEC-04-B: No `source .env` in implementation\n- [ ] TEST-SEC-09-A: Password patterns redacted in bug reports\n- [ ] TEST-SEC-10-A: Mermaid injection chars escaped in route labels\n- [ ] TEST-SEC-10-B: DOT quotes escaped in service labels\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":5614,"content_sha256":"89aa71960d21bffaa424f2fd4689f4d6b915db3c0271d9e819308316b30ce326"},{"filename":"tests/test_security_controls.sh","content":"#!/bin/bash\n# .claude/skills/code-atlas/tests/test_security_controls.sh\n#\n# TDD tests for Code Atlas security controls (SEC-01 through SEC-10).\n# Tests validate that each security control is enforced when atlas layers write output.\n#\n# THESE TESTS WILL FAIL until the corresponding security validators are implemented.\n# The tests define the expected behavior — implement to make them pass.\n#\n# Usage: bash .claude/skills/code-atlas/tests/test_security_controls.sh\n# Exit: 0 = all tests passed, non-zero = failures\n#\n# Requires:\n# - scripts/validate_atlas_output.sh (SEC-01, SEC-03, SEC-09, SEC-10)\n# - scripts/safe_read.sh (SEC-02, SEC-07)\n# - scripts/check_file_size.sh (SEC-08)\n\nset -uo pipefail\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nREPO_ROOT=\"$(cd \"$SCRIPT_DIR/../../../..\" && pwd)\"\n\nVALIDATE_SCRIPT=\"${REPO_ROOT}/scripts/validate_atlas_output.sh\"\nSAFE_READ_SCRIPT=\"${REPO_ROOT}/scripts/safe_read.sh\"\n\nPASS=0\nFAIL=0\n\n# ---------------------------------------------------------------------------\n# Test harness\n# ---------------------------------------------------------------------------\nassert_pass() {\n local test_name=\"$1\"\n local condition=\"$2\" # \"true\" or \"false\"\n local detail=\"${3:-}\"\n\n if [[ \"$condition\" == \"true\" ]]; then\n echo \"PASS: $test_name\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: $test_name${detail:+ — $detail}\"\n FAIL=$((FAIL + 1))\n fi\n}\n\nassert_exit_code() {\n local test_name=\"$1\"\n local expected=\"$2\"\n local actual=\"$3\"\n local output=\"${4:-}\"\n if [[ \"$actual\" -eq \"$expected\" ]]; then\n echo \"PASS: $test_name\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: $test_name (expected exit $expected, got $actual)\"\n [[ -n \"$output\" ]] && echo \" Output: ${output:0:200}\"\n FAIL=$((FAIL + 1))\n fi\n}\n\nassert_not_in_file() {\n local test_name=\"$1\"\n local pattern=\"$2\"\n local file=\"$3\"\n\n if [[ ! -f \"$file\" ]]; then\n echo \"FAIL: $test_name — file not found: $file\"\n FAIL=$((FAIL + 1))\n return\n fi\n if grep -q \"$pattern\" \"$file\" 2>/dev/null; then\n echo \"FAIL: $test_name — forbidden pattern '$pattern' found in $file\"\n FAIL=$((FAIL + 1))\n else\n echo \"PASS: $test_name\"\n PASS=$((PASS + 1))\n fi\n}\n\nassert_in_file() {\n local test_name=\"$1\"\n local pattern=\"$2\"\n local file=\"$3\"\n\n if [[ ! -f \"$file\" ]]; then\n echo \"FAIL: $test_name — file not found: $file\"\n FAIL=$((FAIL + 1))\n return\n fi\n if grep -q \"$pattern\" \"$file\" 2>/dev/null; then\n echo \"PASS: $test_name\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: $test_name — expected pattern '$pattern' not found in $file\"\n FAIL=$((FAIL + 1))\n fi\n}\n\n# ============================================================================\n# SEC-01: Secret Value Redaction (CRITICAL)\n# ============================================================================\n\necho \"\"\necho \"=== SEC-01: Secret Value Redaction ===\"\n\n# Precondition: validate_atlas_output.sh must exist\nif [[ ! -f \"$VALIDATE_SCRIPT\" ]]; then\n echo \"FAIL: SEC-01 prerequisite — validate_atlas_output.sh not found at $VALIDATE_SCRIPT\"\n FAIL=$((FAIL + 5))\nelse\n\n # TEST-SEC-01-A: .env values never appear in atlas output\n tmpdir=$(mktemp -d)\n mkdir -p \"$tmpdir/docs/atlas/inventory\"\n\n # Create a fake .env with a real secret\n cat > \"$tmpdir/.env\" \u003c\u003c 'EOF'\nDATABASE_URL=postgres://user:MYSECRETPASSWORD@localhost/db # pragma: allowlist secret\nJWT_SECRET=supersecretjwtkey123 # pragma: allowlist secret\nREDIS_URL=redis://:REDISPASS@localhost:6379 # pragma: allowlist secret\nEOF\n\n # Simulate what atlas layer6 should produce: keys ONLY\n cat > \"$tmpdir/docs/atlas/inventory/env-vars.md\" \u003c\u003c 'EOF'\n| Variable | Description | Required |\n|----------|-------------|----------|\n| DATABASE_URL | PostgreSQL connection | Yes |\n| JWT_SECRET | JWT signing key | Yes |\n| REDIS_URL | Redis connection | Yes |\nEOF\n\n # Run validator — it should PASS because values are not in the file\n output=$(bash \"$VALIDATE_SCRIPT\" --atlas-dir \"$tmpdir/docs/atlas\" 2>&1 || true)\n exit_val=$?\n\n assert_not_in_file \"SEC-01-A: MYSECRETPASSWORD not in env-vars.md\" \\\n \"MYSECRETPASSWORD\" \\\n \"$tmpdir/docs/atlas/inventory/env-vars.md\"\n assert_not_in_file \"SEC-01-A: supersecretjwtkey123 not in env-vars.md\" \\\n \"supersecretjwtkey123\" \\\n \"$tmpdir/docs/atlas/inventory/env-vars.md\"\n assert_not_in_file \"SEC-01-A: REDISPASS not in env-vars.md\" \\\n \"REDISPASS\" \\\n \"$tmpdir/docs/atlas/inventory/env-vars.md\"\n\n # TEST-SEC-01-B: Kubernetes Secret data block values never emitted\n mkdir -p \"$tmpdir/docs/atlas/repo-surface\"\n cat > \"$tmpdir/docs/atlas/repo-surface/README.md\" \u003c\u003c 'EOF'\n# Layer 1 - Runtime Topology\nServices: api, auth, db-proxy\nSecrets mounted: db-credentials (key names: DATABASE_URL, DB_PASSWORD)\nEOF\n\n assert_not_in_file \"SEC-01-B: K8s secret values not in layer1 README\" \\\n \"dXNlcjpwYXNz\\|cGFzc3dvcmQ=\\|base64\" \\\n \"$tmpdir/docs/atlas/repo-surface/README.md\"\n\n rm -rf \"$tmpdir\"\nfi\n\n# TEST-SEC-01-C: validate_atlas_output.sh itself detects secrets if they leaked\ntmpdir=$(mktemp -d)\nmkdir -p \"$tmpdir/docs/atlas/inventory\"\n\n# Intentionally bad output with a leaked secret (env-var assignment format triggers SEC-01)\ncat > \"$tmpdir/docs/atlas/inventory/env-vars.md\" \u003c\u003c 'EOF'\n# Leaked secrets example (bad atlas output)\nJWT_SECRET=supersecretjwtkey123 # pragma: allowlist secret\nDATABASE_URL=postgres://user:MYSECRETPASSWORD@localhost/db # pragma: allowlist secret\nEOF\n\nif [[ -f \"$VALIDATE_SCRIPT\" ]]; then\n output=$(bash \"$VALIDATE_SCRIPT\" --atlas-dir \"$tmpdir/docs/atlas\" --strict 2>&1) && exit_val=0 || exit_val=$?\n # Should exit non-zero (detected a problem)\n assert_exit_code \"SEC-01-C: validator exits 1 when secret value found in output\" 1 \"$exit_val\" \"$output\"\nfi\nrm -rf \"$tmpdir\"\n\n# ============================================================================\n# SEC-02: Path Traversal Prevention (CRITICAL)\n# ============================================================================\n\necho \"\"\necho \"=== SEC-02: Path Traversal Prevention ===\"\n\n# Precondition: safe_read.sh must exist\nif [[ ! -f \"$SAFE_READ_SCRIPT\" ]]; then\n echo \"FAIL: SEC-02 prerequisite — safe_read.sh not found at $SAFE_READ_SCRIPT\"\n FAIL=$((FAIL + 3))\nelse\n\n # TEST-SEC-02-A: Relative path traversal rejected\n tmpdir=$(mktemp -d)\n mkdir -p \"$tmpdir/codebase\"\n echo \"safe content\" > \"$tmpdir/codebase/safe.txt\"\n\n output=$(bash \"$SAFE_READ_SCRIPT\" \"$tmpdir/codebase/../../etc/passwd\" --boundary \"$tmpdir/codebase\" 2>&1) && exit_val=0 || exit_val=$?\n assert_exit_code \"SEC-02-A: relative path escape blocked (exit 1)\" 1 \"$exit_val\" \"$output\"\n # Output should mention PATH_TRAVERSAL or security error\n if echo \"$output\" | grep -qi \"path.traversal\\|security\\|blocked\\|denied\\|invalid\\|Error\\|boundary\"; then\n echo \"PASS: SEC-02-A: error message mentions traversal/security\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: SEC-02-A: error message should mention traversal/security\"\n echo \" Got: $output\"\n FAIL=$((FAIL + 1))\n fi\n rm -rf \"$tmpdir\"\n\n # TEST-SEC-02-B: Absolute path outside codebase_path rejected\n tmpdir=$(mktemp -d)\n mkdir -p \"$tmpdir/codebase\"\n\n output=$(bash \"$SAFE_READ_SCRIPT\" \"/etc/passwd\" --boundary \"$tmpdir/codebase\" 2>&1) && exit_val=0 || exit_val=$?\n assert_exit_code \"SEC-02-B: absolute path outside codebase blocked (exit 1)\" 1 \"$exit_val\" \"$output\"\n rm -rf \"$tmpdir\"\n\n # TEST-SEC-02-C: Valid absolute path within codebase reads successfully\n tmpdir=$(mktemp -d)\n mkdir -p \"$tmpdir/codebase/src\"\n echo \"package main\" > \"$tmpdir/codebase/src/main.go\"\n\n output=$(bash \"$SAFE_READ_SCRIPT\" \"$tmpdir/codebase/src/main.go\" --boundary \"$tmpdir/codebase\" 2>&1)\n exit_val=$?\n assert_exit_code \"SEC-02-C: valid path within codebase reads (exit 0)\" 0 \"$exit_val\" \"$output\"\n rm -rf \"$tmpdir\"\n\nfi\n\n# ============================================================================\n# SEC-03: XSS Prevention — Mermaid/DOT Label Sanitization (HIGH)\n# ============================================================================\n\necho \"\"\necho \"=== SEC-03: XSS Prevention ===\"\n\n# TEST-SEC-03-A: HTML chars in service name are escaped in Mermaid output\ntmpdir=$(mktemp -d)\nmkdir -p \"$tmpdir/docs/atlas/repo-surface\"\n\n# Simulate atlas output that was generated from a service named \"\u003cevil-service>\"\ncat > \"$tmpdir/docs/atlas/repo-surface/topology.mmd\" \u003c\u003c 'EOF'\ngraph LR\n api[\"<evil-service>\"]\n auth[\"safe-auth\"]\n api --> auth\nEOF\n\nassert_not_in_file \"SEC-03-A: raw \u003c not in Mermaid label\" \"\u003cevil-service>\" \\\n \"$tmpdir/docs/atlas/repo-surface/topology.mmd\"\n\n# TEST-SEC-03-B: JavaScript-like content in service name is escaped\ncat > \"$tmpdir/docs/atlas/repo-surface/topology.mmd\" \u003c\u003c 'EOF'\ngraph LR\n xss[\"<script>alert(1)</script>\"]\nEOF\nassert_not_in_file \"SEC-03-B: \u003cscript> not raw in Mermaid output\" \\\n \"\u003cscript>\" \\\n \"$tmpdir/docs/atlas/repo-surface/topology.mmd\"\n\nrm -rf \"$tmpdir\"\n\nif [[ -f \"$VALIDATE_SCRIPT\" ]]; then\n # TEST-SEC-03-C: Validator catches unescaped HTML in .mmd files\n tmpdir=$(mktemp -d)\n mkdir -p \"$tmpdir/docs/atlas/repo-surface\"\n cat > \"$tmpdir/docs/atlas/repo-surface/topology.mmd\" \u003c\u003c 'EOF'\ngraph LR\n xss[\"\u003cscript>alert(1)\u003c/script>\"]\nEOF\n output=$(bash \"$VALIDATE_SCRIPT\" --atlas-dir \"$tmpdir/docs/atlas\" --strict 2>&1) && exit_val=0 || exit_val=$?\n assert_exit_code \"SEC-03-C: validator catches unescaped HTML in .mmd (exit 1)\" 1 \"$exit_val\" \"$output\"\n rm -rf \"$tmpdir\"\nfi\n\n# ============================================================================\n# SEC-05: Output Confinement to docs/atlas/ (HIGH)\n# ============================================================================\n\necho \"\"\necho \"=== SEC-05: Output Confinement ===\"\n\nif [[ -f \"$VALIDATE_SCRIPT\" ]]; then\n # TEST-SEC-05-A: Validator detects symlink in docs/atlas/ escaping the boundary\n tmpdir=$(mktemp -d)\n mkdir -p \"$tmpdir/docs/atlas/repo-surface\"\n\n # Create a file outside atlas, then symlink it into atlas\n echo \"sensitive outside content\" > \"$tmpdir/outside.txt\"\n ln -s \"$tmpdir/outside.txt\" \"$tmpdir/docs/atlas/repo-surface/topology.mmd\"\n\n output=$(bash \"$VALIDATE_SCRIPT\" --atlas-dir \"$tmpdir/docs/atlas\" --strict 2>&1) && exit_val=0 || exit_val=$?\n assert_exit_code \"SEC-05-A: validator detects symlink escaping docs/atlas/ (exit 1)\" 1 \"$exit_val\" \"$output\"\n rm -rf \"$tmpdir\"\nfi\n\n# ============================================================================\n# SEC-09: Credential Redaction in Bug Reports (CRITICAL)\n# ============================================================================\n\necho \"\"\necho \"=== SEC-09: Credential Redaction in Bug Reports ===\"\n\ntmpdir=$(mktemp -d)\nmkdir -p \"$tmpdir/docs/atlas/bug-reports\"\n\n# A well-formed bug report: code_quote should not contain secrets\ncat > \"$tmpdir/docs/atlas/bug-reports/pass1-contradictions.md\" \u003c\u003c 'EOF'\n# Pass 1 Bug Report\n\n## BUG-001: Orphaned env var DATABASE_URL\n\n**Severity:** HIGH\n**Layer:** 6 → 3\n\n**Evidence:**\n- Layer 6: DATABASE_URL declared in .env.example\n- Layer 3: No route handler reads DATABASE_URL directly\n\n**Code quote:**\n```\n# .env.example line 1\nDATABASE_URL=***REDACTED***\n```\n\n**Recommendation:** Verify DATABASE_URL is consumed by the database module, not raw routes.\nEOF\n\nassert_not_in_file \"SEC-09-A: pass1 bug report no raw secret values\" \\\n \"postgres://\\|password\\|secret\\|SECRETPASSWORD\" \\\n \"$tmpdir/docs/atlas/bug-reports/pass1-contradictions.md\"\n\nassert_in_file \"SEC-09-B: pass1 bug report uses REDACTED placeholder\" \\\n \"REDACTED\" \\\n \"$tmpdir/docs/atlas/bug-reports/pass1-contradictions.md\"\n\nif [[ -f \"$VALIDATE_SCRIPT\" ]]; then\n # TEST-SEC-09-C: Validator rejects bug report with raw credentials\n cat > \"$tmpdir/docs/atlas/bug-reports/bad-report.md\" \u003c\u003c 'EOF'\n# Bug\nCode quote: DATABASE_URL=postgres://user:MYSECRETPASSWORD@localhost/db # pragma: allowlist secret\nEOF\n output=$(bash \"$VALIDATE_SCRIPT\" --atlas-dir \"$tmpdir/docs/atlas\" --strict 2>&1) && exit_val=0 || exit_val=$?\n assert_exit_code \"SEC-09-C: validator catches credential in bug report (exit 1)\" 1 \"$exit_val\" \"$output\"\nfi\n\nrm -rf \"$tmpdir\"\n\n# ============================================================================\n# SEC-10: DOT/Mermaid Injection Prevention (HIGH)\n# ============================================================================\n\necho \"\"\necho \"=== SEC-10: DOT/Mermaid Injection Prevention ===\"\n\nif [[ -f \"$VALIDATE_SCRIPT\" ]]; then\n # TEST-SEC-10-A: XSS payload in DOT label detected\n tmpdir=$(mktemp -d)\n mkdir -p \"$tmpdir/docs/atlas/repo-surface\"\n # Use a \u003cscript> tag in a DOT label — caught by SEC-10 XSS pattern check\n cat > \"$tmpdir/docs/atlas/repo-surface/topology.dot\" \u003c\u003c 'EOF'\ndigraph topology {\n \"api\" -> \"auth\" [label=\"\u003cscript>alert(1)\u003c/script>\"];\n}\nEOF\n output=$(bash \"$VALIDATE_SCRIPT\" --atlas-dir \"$tmpdir/docs/atlas\" --strict 2>&1) && exit_val=0 || exit_val=$?\n assert_exit_code \"SEC-10-A: XSS payload in DOT label detected (exit 1)\" 1 \"$exit_val\" \"$output\"\n rm -rf \"$tmpdir\"\nfi\n\n# ============================================================================\n# SEC-04: Safe YAML Parsing (HIGH)\n# ============================================================================\n\necho \"\"\necho \"=== SEC-04: Safe YAML Parsing ===\"\n\n# TEST-SEC-04-A: Verify yaml.safe_load() is used instead of yaml.load()\n# Scan all Python source files under scripts/ and the skill directory for unsafe yaml.load usage\nREPO_ROOT_LOCAL=\"$(cd \"${SCRIPT_DIR}/../../../..\" && pwd)\"\n\nunsafe_yaml_files=()\nwhile IFS= read -r pyfile; do\n # Check for yaml.load( without safe_load — exclude safe_load lines\n if grep -qP 'yaml\\.load\\s*\\(' \"$pyfile\" 2>/dev/null; then\n # Confirm it is NOT safe_load\n if grep -P 'yaml\\.load\\s*\\(' \"$pyfile\" 2>/dev/null | grep -qv 'safe_load'; then\n unsafe_yaml_files+=(\"$pyfile\")\n fi\n fi\ndone \u003c \u003c(find \"$REPO_ROOT_LOCAL/scripts\" \"$REPO_ROOT_LOCAL/.claude/skills/code-atlas\" \\\n -name '*.py' 2>/dev/null)\n\nif [[ ${#unsafe_yaml_files[@]} -eq 0 ]]; then\n echo \"PASS: SEC-04-A: no yaml.load() (unsafe) calls found — yaml.safe_load() enforced\"\n PASS=$((PASS + 1))\nelse\n echo \"FAIL: SEC-04-A: unsafe yaml.load() found in: ${unsafe_yaml_files[*]}\"\n FAIL=$((FAIL + 1))\nfi\n\n# ============================================================================\n# SEC-06: Shell Metacharacter Rejection (HIGH)\n# ============================================================================\n\necho \"\"\necho \"=== SEC-06: Shell Metacharacter Rejection ===\"\n\nif [[ -f \"$SAFE_READ_SCRIPT\" ]]; then\n tmpdir=$(mktemp -d)\n mkdir -p \"$tmpdir/codebase\"\n echo \"safe content\" > \"$tmpdir/codebase/safe.txt\"\n\n # TEST-SEC-06-A: Path with $() shell substitution is rejected\n output=$(bash \"$SAFE_READ_SCRIPT\" '$tmpdir/codebase/$(whoami)' \\\n --boundary \"$tmpdir/codebase\" 2>&1) && exit_val=0 || exit_val=$?\n assert_exit_code \"SEC-06-A: path with \\$() rejected (exit 1)\" 1 \"$exit_val\" \"$output\"\n\n # TEST-SEC-06-B: Path with backtick substitution is rejected\n output=$(bash \"$SAFE_READ_SCRIPT\" '$tmpdir/codebase/`id`' \\\n --boundary \"$tmpdir/codebase\" 2>&1) && exit_val=0 || exit_val=$?\n assert_exit_code \"SEC-06-B: path with backtick rejected (exit 1)\" 1 \"$exit_val\" \"$output\"\n\n # TEST-SEC-06-C: Path with semicolon is rejected\n output=$(bash \"$SAFE_READ_SCRIPT\" \"$tmpdir/codebase/safe.txt;ls\" \\\n --boundary \"$tmpdir/codebase\" 2>&1) && exit_val=0 || exit_val=$?\n assert_exit_code \"SEC-06-C: path with semicolon rejected (exit 1)\" 1 \"$exit_val\" \"$output\"\n\n rm -rf \"$tmpdir\"\nfi\n\n# ============================================================================\n# SEC-08: Large File DoS Prevention (MEDIUM)\n# ============================================================================\n\necho \"\"\necho \"=== SEC-08: Large File DoS Prevention ===\"\n\nif [[ -f \"$SAFE_READ_SCRIPT\" ]]; then\n # TEST-SEC-08-A: Files over 10MB are rejected by safe_read.sh (SEC-08)\n tmpdir=$(mktemp -d)\n mkdir -p \"$tmpdir/docs/atlas\"\n\n # Create a 12MB file to test size limit\n dd if=/dev/zero of=\"$tmpdir/docs/atlas/huge.mmd\" bs=1M count=12 2>/dev/null\n\n output=$(bash \"$SAFE_READ_SCRIPT\" \"$tmpdir/docs/atlas/huge.mmd\" --boundary \"$tmpdir/docs/atlas\" 2>&1) && exit_val=0 || exit_val=$?\n assert_exit_code \"SEC-08-A: 12MB file rejected (exit 1)\" 1 \"$exit_val\" \"$output\"\n\n rm -rf \"$tmpdir\"\nfi\n\n# ============================================================================\n# Summary\n# ============================================================================\necho \"\"\necho \"==================================\"\necho \"Results: ${PASS} passed, ${FAIL} failed\"\necho \"==================================\"\n\n[[ $FAIL -eq 0 ]] && exit 0 || exit 1\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":17037,"content_sha256":"15a270b63eddf06d711f1ceb67b577a8351cc83f03e419047086b140fe870dad"},{"filename":"tests/test_staleness_triggers.sh","content":"#!/bin/bash\n# test_staleness_triggers.sh\n#\n# Tests that check-atlas-staleness.sh correctly detects stale layers\n# for all documented trigger patterns.\n#\n# Usage: bash .claude/skills/code-atlas/tests/test_staleness_triggers.sh\n# Exit: 0 = all tests passed, 1 = one or more tests failed\n\nset -euo pipefail\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nREPO_ROOT=\"$(cd \"$SCRIPT_DIR/../../../..\" && pwd)\"\nSTALENESS_SCRIPT=\"${REPO_ROOT}/scripts/check-atlas-staleness.sh\"\n\nPASS=0\nFAIL=0\n\n# ---------------------------------------------------------------------------\n# Test harness\n# ---------------------------------------------------------------------------\nassert_layer_detected() {\n local test_name=\"$1\"\n local expected_layer=\"$2\"\n local changed_file=\"$3\"\n\n # Create a temp git repo with a fake changed file to test pattern matching\n local tmpdir\n tmpdir=$(mktemp -d)\n cd \"$tmpdir\"\n git init -q\n git commit --allow-empty -m \"init\" -q\n\n # Stage a fake file change\n mkdir -p \"$(dirname \"$changed_file\")\"\n echo \"test\" > \"$changed_file\"\n git add .\n git commit -q -m \"add $changed_file\"\n\n # Run staleness check — should detect the expected layer\n output=$(bash \"$STALENESS_SCRIPT\" 2>&1 || true)\n\n if echo \"$output\" | grep -q \"STALE:\"; then\n echo \"PASS: $test_name — Layer $expected_layer detected for $changed_file\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL: $test_name — Expected Layer $expected_layer stale for $changed_file\"\n echo \" Output was: $output\"\n FAIL=$((FAIL + 1))\n fi\n\n cd \"$REPO_ROOT\"\n rm -rf \"$tmpdir\"\n}\n\nassert_no_layer_detected() {\n local test_name=\"$1\"\n local changed_file=\"$2\"\n\n local tmpdir\n tmpdir=$(mktemp -d)\n cd \"$tmpdir\"\n git init -q\n git commit --allow-empty -m \"init\" -q\n\n mkdir -p \"$(dirname \"$changed_file\")\"\n echo \"test\" > \"$changed_file\"\n git add .\n git commit -q -m \"add $changed_file\"\n\n output=$(bash \"$STALENESS_SCRIPT\" 2>&1 || true)\n\n if echo \"$output\" | grep -q \"STALE\"; then\n echo \"FAIL: $test_name — Unexpected stale detection for $changed_file\"\n echo \" Output was: $output\"\n FAIL=$((FAIL + 1))\n else\n echo \"PASS: $test_name — No false positive for $changed_file\"\n PASS=$((PASS + 1))\n fi\n\n cd \"$REPO_ROOT\"\n rm -rf \"$tmpdir\"\n}\n\n# ---------------------------------------------------------------------------\n# Layer 1: Runtime Topology triggers\n# ---------------------------------------------------------------------------\nassert_layer_detected \"Layer1: docker-compose.yml\" 1 \"docker-compose.yml\"\nassert_layer_detected \"Layer1: docker-compose.override.yml\" 1 \"docker-compose.override.yml\"\nassert_layer_detected \"Layer1: k8s manifest\" 1 \"k8s/deployment.yaml\"\nassert_layer_detected \"Layer1: kubernetes manifest\" 1 \"kubernetes/service.yaml\"\nassert_layer_detected \"Layer1: helm chart\" 1 \"helm/templates/deployment.yaml\"\n\n# ---------------------------------------------------------------------------\n# Layer 2: Dependency triggers\n# ---------------------------------------------------------------------------\nassert_layer_detected \"Layer2: go.mod\" 2 \"go.mod\"\nassert_layer_detected \"Layer2: nested go.mod\" 2 \"services/api/go.mod\"\nassert_layer_detected \"Layer2: package.json\" 2 \"package.json\"\nassert_layer_detected \"Layer2: nested package.json\" 2 \"services/web/package.json\"\nassert_layer_detected \"Layer2: csproj\" 2 \"MyApp.csproj\"\nassert_layer_detected \"Layer2: Cargo.toml\" 2 \"Cargo.toml\"\nassert_layer_detected \"Layer2: requirements.txt\" 2 \"requirements.txt\"\nassert_layer_detected \"Layer2: pyproject.toml\" 2 \"pyproject.toml\"\n\n# ---------------------------------------------------------------------------\n# Layer 3: API Contracts triggers (including previously undocumented patterns)\n# ---------------------------------------------------------------------------\nassert_layer_detected \"Layer3: routes.ts\" 3 \"src/api/routes.ts\"\nassert_layer_detected \"Layer3: route file\" 3 \"services/api/route_users.go\"\nassert_layer_detected \"Layer3: controller (Go)\" 3 \"internal/controller_auth.go\"\nassert_layer_detected \"Layer3: controller (TS)\" 3 \"src/controllers/user.controller.ts\"\nassert_layer_detected \"Layer3: views.py\" 3 \"app/views.py\"\nassert_layer_detected \"Layer3: router.ts\" 3 \"src/router.ts\"\nassert_layer_detected \"Layer3: handler.go (was undocumented)\" 3 \"internal/user_handler.go\"\n\n# ---------------------------------------------------------------------------\n# Layer 4: Data Flow triggers (including previously undocumented patterns)\n# ---------------------------------------------------------------------------\nassert_layer_detected \"Layer4: dto.ts\" 4 \"src/dtos/user.dto.ts\"\nassert_layer_detected \"Layer4: schema.py\" 4 \"app/schemas.py\"\nassert_layer_detected \"Layer4: _request.go\" 4 \"internal/auth/login_request.go\"\nassert_layer_detected \"Layer4: _response.go\" 4 \"internal/auth/login_response.go\"\nassert_layer_detected \"Layer4: types.ts\" 4 \"src/types.ts\"\nassert_layer_detected \"Layer4: model.go (was undocumented)\" 4 \"internal/order_model.go\"\n\n# ---------------------------------------------------------------------------\n# Layer 5: User Journey triggers\n# ---------------------------------------------------------------------------\nassert_layer_detected \"Layer5: page.tsx\" 5 \"src/pages/checkout.page.tsx\"\nassert_layer_detected \"Layer5: cmd Go file\" 5 \"cmd/server.go\"\nassert_layer_detected \"Layer5: cli Python file\" 5 \"cli/main.py\"\n\n# ---------------------------------------------------------------------------\n# Layer 6: Inventory triggers\n# ---------------------------------------------------------------------------\nassert_layer_detected \"Layer6: .env.example\" 6 \".env.example\"\nassert_layer_detected \"Layer6: service README\" 6 \"services/api/README.md\"\n\n# ---------------------------------------------------------------------------\n# Negative tests: irrelevant files should not trigger\n# ---------------------------------------------------------------------------\nassert_no_layer_detected \"No trigger: unrelated .md\" \"docs/CHANGELOG.md\"\nassert_no_layer_detected \"No trigger: test file\" \"src/__tests__/unit.test.ts\"\nassert_no_layer_detected \"No trigger: Makefile\" \"Makefile\"\n\n# ---------------------------------------------------------------------------\n# Results\n# ---------------------------------------------------------------------------\necho \"\"\necho \"==================================\"\necho \"Results: ${PASS} passed, ${FAIL} failed\"\necho \"==================================\"\n\n[[ $FAIL -eq 0 ]] && exit 0 || exit 1\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":6887,"content_sha256":"504b97c177e71766642939a5f4d659ac73a716132c9ff660a6d797c3fc3fa457"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Code Atlas Skill","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Purpose","type":"text"}]},{"type":"paragraph","content":[{"text":"Build exhaustive, regeneratable architecture atlases directly from code truth. A code-atlas is a living document set: diagrams, graphs, and inventory tables that form a navigable map of any codebase. Atlas-building is investigation: structured reasoning about code in graph form reveals structural bugs, API contract mismatches, and architectural drift that linear code review misses.","type":"text"}]},{"type":"paragraph","content":[{"text":"An atlas is complete when any engineer, given only the atlas and a bug report, can trace the full execution path without opening the source code.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Layer Overview","type":"text"}]},{"type":"paragraph","content":[{"text":"Layer definitions are the single source of truth in ","type":"text"},{"text":"LAYERS.yaml","type":"text","marks":[{"type":"link","attrs":{"href":"./LAYERS.yaml","title":null}}]},{"text":". All references below use slugs from that file.","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Slug","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Name","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Description","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Recommended Diagram Type","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"repo-surface","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Repository Surface","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"All source files, project structure, build systems","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Mermaid ","type":"text"},{"text":"flowchart TD","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"ast-lsp-bindings","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"AST+LSP Symbol Bindings","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Cross-file symbol references, dead code, interface mismatches","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Mermaid ","type":"text"},{"text":"flowchart LR","type":"text","marks":[{"type":"code_inline"}]},{"text":" or DOT digraph","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"compile-deps","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Compile-time Dependencies","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Package imports, dependency trees, circular deps","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"DOT digraph (handles large trees better)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"runtime-topology","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Runtime Topology","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Services, containers, ports, inter-service connections","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"DOT digraph with subgraph clusters","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"api-contracts","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"API Contracts","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"HTTP routes, gRPC, GraphQL, middleware chains","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Mermaid ","type":"text"},{"text":"flowchart TD","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"data-flow","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Data Flow","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"DTO-to-storage chains, transformation steps","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Mermaid ","type":"text"},{"text":"flowchart LR","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"service-components","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Service Component Architecture","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Per-service internal module/package structure","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Mermaid ","type":"text"},{"text":"graph TD","type":"text","marks":[{"type":"code_inline"}]},{"text":" (one per service)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"user-journeys","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"User Journey Scenarios","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"End-to-end paths from entry to outcome","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Mermaid ","type":"text"},{"text":"sequenceDiagram","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Per-Layer Scope Guidance","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Slug","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Scope Target","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"repo-surface","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Top-level directories and build entry points. Do not enumerate every file.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"ast-lsp-bindings","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Exported symbols and their cross-file references. Focus on public API surface.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"compile-deps","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Direct dependencies and one level of transitive. Include version constraints.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"runtime-topology","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"All deployed services, their ports, and inter-service protocols.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"api-contracts","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Every HTTP/gRPC/GraphQL endpoint with auth, DTOs, and middleware.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"data-flow","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Primary read/write paths per service. Skip internal caching flows unless relevant.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"service-components","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Top-level packages/modules within each service. Show coupling edges.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"user-journeys","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Derive from api-contracts routes + pages/CLI entries. Trace 3-8 key journeys.","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Skill Delegation Architecture","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"code-atlas (this skill)\n Responsibilities:\n - Atlas layer orchestration (all 8 layers)\n - Language-agnostic code exploration\n - Three-pass bug-hunting workflow\n - Staleness detection triggers\n - Density management (split, not prompt)\n - Publication workflow (GitHub Pages, mkdocs, SVG)\n\n Delegates to:\n code-visualizer skill Python AST module analysis (compile-deps + service-components fallback)\n mermaid-diagram-generator Mermaid syntax generation and formatting\n lsp-setup skill Layer ast-lsp-bindings: LSP-assisted symbol queries (optional)\n visualization-architect Complex DOT graph rendering and cross-layer layouts\n analyzer agent Deep codebase investigation and dependency mapping\n reviewer agent Contradiction hunting (Passes 1, 2, 3)","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"When to Use This Skill","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Trigger","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Use Case","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Starting work on an unfamiliar codebase","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Full atlas build before coding","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Onboarding a new engineer","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Share atlas as navigation guide","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Before a major refactor","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Map current state; plan changes against topology","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Bug hunt stalled","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Pass 1 + Pass 2 bug-hunting through graphs","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Docs feel stale","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Staleness check + targeted rebuild","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Adding CI/CD quality gate","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Register atlas freshness checks","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Publishing documentation site","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"GitHub Pages / mkdocs publication workflow","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Reviewing an unfamiliar PR","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"PR impact view using diff against current atlas","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Quick Start","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"# Build a full atlas\nUser: Build a complete code atlas for this repository\n\n# Run bug hunting\nUser: Run code atlas bug hunting passes on this service\n\n# Check freshness\nUser: Are our architecture diagrams still accurate?\n\n# Publish\nUser: Publish our code atlas to GitHub Pages","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Why Both Mermaid and Graphviz","type":"text"}]},{"type":"paragraph","content":[{"text":"The skill defaults to building atlas diagrams in both formats because they find different bugs. A controlled experiment across 7 repos showed only ~15% overlap in bugs found -- running both finds ~1.7x the bugs of either alone. The different syntax forces different reasoning paths through the same code. Evidence is documented in PR #3221.","type":"text"}]},{"type":"paragraph","content":[{"text":"The user can override to a single format:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"User: Build a code atlas using only Mermaid\nUser: Build a code atlas in DOT format only","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Diagram Density Policy","type":"text"}]},{"type":"paragraph","content":[{"text":"There are no hard node/edge limits. Instead:","type":"text"}]},{"type":"paragraph","content":[{"text":"If a diagram would be unreadably dense, split into sub-diagrams by package or service boundary.","type":"text","marks":[{"type":"strong"}]},{"text":" In batch mode, auto-group without prompting the user. Each sub-diagram should target 15-40 nodes for readability.","type":"text"}]},{"type":"paragraph","content":[{"text":"For example, a runtime-topology diagram with 80 services should be split into sub-diagrams by domain (e.g., ","type":"text"},{"text":"runtime-topology-payments.mmd","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"runtime-topology-auth.mmd","type":"text","marks":[{"type":"code_inline"}]},{"text":") plus one high-level overview diagram showing inter-domain connections.","type":"text"}]},{"type":"paragraph","content":[{"text":"A table is only produced as a companion to a diagram, never as a replacement.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Recipe: 12-Phase Atlas Build","type":"text"}]},{"type":"paragraph","content":[{"text":"The atlas build follows these phases in order:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Validate Prerequisites","type":"text","marks":[{"type":"strong"}]},{"text":" -- Check tools (mmdc, dot, kuzu), detect LSP mode","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Build Layers 1-4","type":"text","marks":[{"type":"strong"}]},{"text":" (structural) -- repo-surface, ast-lsp-bindings, compile-deps, runtime-topology","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Build Layers 5-8","type":"text","marks":[{"type":"strong"}]},{"text":" (behavioral) -- api-contracts, data-flow, service-components, user-journeys","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Verify All 8 Layers","type":"text","marks":[{"type":"strong"}]},{"text":" -- Hard gate: every slug must have .mmd + .dot + rendered .svg + README with embedded images","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Bug Hunt (Mermaid arm)","type":"text","marks":[{"type":"strong"}]},{"text":" -- 3-pass hunt using only .mmd diagrams","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Bug Hunt (Graphviz arm)","type":"text","marks":[{"type":"strong"}]},{"text":" -- 3-pass hunt using only .dot diagrams (parallel with step 5)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Merge Findings","type":"text","marks":[{"type":"strong"}]},{"text":" -- Deduplicate across both arms","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Multi-Agent Validation","type":"text","marks":[{"type":"strong"}]},{"text":" -- 3 specialists vote independently; threshold >= 2/3 to confirm","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"File Issues","type":"text","marks":[{"type":"strong"}]},{"text":" -- Validated bugs filed as GitHub issues (never stored in atlas)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Kuzu Ingestion + OpenCypher","type":"text","marks":[{"type":"strong"}]},{"text":" -- Ingest to graph (REQUIRED) + generate standalone .cypher files","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Publish Atlas","type":"text","marks":[{"type":"strong"}]},{"text":" -- Render SVGs, write index, update mkdocs nav","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Final Checklist Review","type":"text","marks":[{"type":"strong"}]},{"text":" -- Independent reviewer verifies completeness of all deliverables","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"After each build phase, diagrams are written to ","type":"text"},{"text":"docs/atlas/{slug}/","type":"text","marks":[{"type":"code_inline"}]},{"text":" with ","type":"text"},{"text":".mmd","type":"text","marks":[{"type":"code_inline"}]},{"text":" source, ","type":"text"},{"text":".dot","type":"text","marks":[{"type":"code_inline"}]},{"text":" source, rendered ","type":"text"},{"text":"*-mermaid.svg","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"*-dot.svg","type":"text","marks":[{"type":"code_inline"}]},{"text":", and a README.md that embeds the SVGs inline using ","type":"text"},{"text":"![alt](file.svg)","type":"text","marks":[{"type":"code_inline"}]},{"text":" syntax.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Bug-Hunting Workflow Overview","type":"text"}]},{"type":"paragraph","content":[{"text":"The atlas is an active investigation tool. Three passes transform it from a map into a high-confidence bug-detection engine. Each pass runs in a fresh context window to prevent anchoring bias.","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Pass 1 (Comprehensive Build + Hunt)","type":"text","marks":[{"type":"strong"}]},{"text":": Build all layers, then systematically hunt contradictions between them. Route/DTO mismatches, orphaned env vars, dead runtime paths, stale doc references.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Pass 2 (Fresh-Eyes Cross-Check)","type":"text","marks":[{"type":"strong"}]},{"text":": A new context window re-examines the atlas independently. Confirms, overturns, or escalates Pass 1 findings.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Pass 3 (Scenario Deep-Dive)","type":"text","marks":[{"type":"strong"}]},{"text":": Every user-journeys journey is traced end-to-end through api-contracts, data-flow, runtime-topology, service-components, and ast-lsp-bindings. Each journey receives a verdict: PASS, FAIL, or NEEDS_ATTENTION.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Full checklists, templates, and output formats: ","type":"text"},{"text":"bug-hunt-guide.md","type":"text","marks":[{"type":"link","attrs":{"href":"./bug-hunt-guide.md","title":null}}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Layer 8: ast-lsp-bindings Operating Modes","type":"text"}]},{"type":"paragraph","content":[{"text":"Layer ast-lsp-bindings operates in one of two modes, always labelled on line 1 of its README:","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Mode","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Trigger","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Mechanism","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"lsp-assisted","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"lsp-setup reports active LSP server","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Delegates symbol queries to LSP","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"static-approximation","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"LSP unavailable","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"ripgrep + code-visualizer AST","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"The mode label is never absent, never defaulted silently.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Output Structure","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"docs/atlas/\n index.md\n staleness-map.yaml\n {slug}/\n README.md (embeds SVG diagrams inline with ![alt](file.svg))\n *-mermaid.svg (rendered Mermaid diagrams)\n *-dot.svg (rendered Graphviz diagrams)\n *.mmd (Mermaid source)\n *.dot (Graphviz source)\n inventory.md (where applicable)\n cypher/\n schema.cypher (CREATE NODE/REL TABLE statements)\n atlas-layers.cypher\n atlas-services.cypher\n atlas-bugs.cypher\n atlas-relationships.cypher\n queries.cypher (ready-to-run example queries)","type":"text"}]},{"type":"paragraph","content":[{"text":"Three non-negotiable output rules:","type":"text","marks":[{"type":"strong"}]}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Bug hunt results are ","type":"text"},{"text":"never stored in the atlas","type":"text","marks":[{"type":"strong"}]},{"text":". All findings are filed as GitHub issues with the ","type":"text"},{"text":"code-atlas-bughunt","type":"text","marks":[{"type":"code_inline"}]},{"text":" label.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Kuzu ingestion is ","type":"text"},{"text":"required, not optional","type":"text","marks":[{"type":"strong"}]},{"text":". If Kuzu is unavailable, fail loudly and attempt to fix (install kuzu package). Never silently skip.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"OpenCypher ","type":"text"},{"text":".cypher","type":"text","marks":[{"type":"code_inline"}]},{"text":" files are ","type":"text"},{"text":"always generated","type":"text","marks":[{"type":"strong"}]},{"text":" alongside Kuzu ingestion for portability to any graph database.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Staleness Detection","type":"text"}]},{"type":"paragraph","content":[{"text":"Staleness triggers are defined per-layer in ","type":"text"},{"text":"LAYERS.yaml","type":"text","marks":[{"type":"link","attrs":{"href":"./LAYERS.yaml","title":null}}]},{"text":" as glob patterns. When ","type":"text"},{"text":"git diff","type":"text","marks":[{"type":"code_inline"}]},{"text":" matches a trigger pattern, the corresponding layer is marked stale.","type":"text"}]},{"type":"paragraph","content":[{"text":"Full trigger table, rebuild commands, and incremental rebuild strategy: ","type":"text"},{"text":"reference.md","type":"text","marks":[{"type":"link","attrs":{"href":"./reference.md","title":null}}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"CI Integration","type":"text"}]},{"type":"paragraph","content":[{"text":"Three GitHub Actions patterns are available:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Post-merge staleness gate with auto-commit","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"PR impact check with layer annotations","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Scheduled weekly full rebuild with issue creation on failure","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Full workflow YAML and configuration: ","type":"text"},{"text":"publication-guide.md","type":"text","marks":[{"type":"link","attrs":{"href":"./publication-guide.md","title":null}}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Publication","type":"text"}]},{"type":"paragraph","content":[{"text":"Outputs GitHub Pages-ready ","type":"text"},{"text":"docs/atlas/","type":"text","marks":[{"type":"code_inline"}]},{"text":" structure. Compatible with mkdocs-material and plain GitHub Pages. SVGs are committed so no render step is needed at read time.","type":"text"}]},{"type":"paragraph","content":[{"text":"SVG generation commands, mkdocs integration, and deployment workflows: ","type":"text"},{"text":"publication-guide.md","type":"text","marks":[{"type":"link","attrs":{"href":"./publication-guide.md","title":null}}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Diagram Examples","type":"text"}]},{"type":"paragraph","content":[{"text":"Per-layer Mermaid and DOT examples with recommended diagram types: ","type":"text"},{"text":"examples.md","type":"text","marks":[{"type":"link","attrs":{"href":"./examples.md","title":null}}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Security Controls","type":"text"}]},{"type":"paragraph","content":[{"text":"All security controls (SEC-01 through SEC-19) are defined in ","type":"text"},{"text":"SECURITY.md","type":"text","marks":[{"type":"link","attrs":{"href":"./SECURITY.md","title":null}}]},{"text":". Key controls:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Secret values never emitted (env files parsed for key names only)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Path traversal prevented via realpath() boundary validation","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Mermaid/DOT/SVG labels sanitized (XSS prevention)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Bug report code quotes redacted of credential patterns","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"All file:line references use relative paths (SEC-16)","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"API Contracts","type":"text"}]},{"type":"paragraph","content":[{"text":"Typed contracts for all skill delegations and filesystem layout: ","type":"text"},{"text":"API-CONTRACTS.md","type":"text","marks":[{"type":"link","attrs":{"href":"./API-CONTRACTS.md","title":null}}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Reference","type":"text"}]},{"type":"paragraph","content":[{"text":"Error codes, Kuzu ingestion schema, staleness trigger table: ","type":"text"},{"text":"reference.md","type":"text","marks":[{"type":"link","attrs":{"href":"./reference.md","title":null}}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Success Criteria","type":"text"}]},{"type":"paragraph","content":[{"text":"A complete atlas satisfies:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"All 8 layers produced with diagrams in ","type":"text"},{"text":"docs/atlas/{slug}/","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Both DOT and Mermaid source files present (unless user requested single format)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"SVG renders alongside source files","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Bug hunt findings filed as GitHub issues (never stored in atlas docs)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Every filed bug includes: layer reference, file path, line number, code evidence","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"No orphaned nodes in diagrams","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"ast-lsp-bindings README states mode on line 1","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Limitations","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Not a static analysis tool","type":"text","marks":[{"type":"strong"}]},{"text":": Uses grep, AST, config parsing -- not a compiler","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Staleness is heuristic","type":"text","marks":[{"type":"strong"}]},{"text":": Git diff pattern matching, not semantic analysis","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Python AST delegation","type":"text","marks":[{"type":"strong"}]},{"text":": Python module graphs delegate to code-visualizer (Python-only)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"SVG rendering requires Graphviz/Mermaid CLI","type":"text","marks":[{"type":"strong"}]},{"text":": CI environments need these installed","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Bug hunting is probabilistic","type":"text","marks":[{"type":"strong"}]},{"text":": Human review required before filing","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Single-repository focus","type":"text","marks":[{"type":"strong"}]},{"text":": Cross-repo deps require manual configuration","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"No runtime instrumentation","type":"text","marks":[{"type":"strong"}]},{"text":": Call frequencies and latency require APM tools","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Remember","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"Diagramming is investigation, not just documentation.","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"paragraph","content":[{"text":"The most valuable output of a code atlas is the bugs and contradictions discovered while reasoning about the system in graph form.","type":"text"}]},{"type":"paragraph","content":[{"text":"Five rules that are never negotiable:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"No silent diagram-to-table substitution.","type":"text","marks":[{"type":"strong"}]},{"text":" If density is high, split into sub-diagrams.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Mode is always visible.","type":"text","marks":[{"type":"strong"}]},{"text":" ast-lsp-bindings README always states its mode on line 1.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Three-pass bug hunting.","type":"text","marks":[{"type":"strong"}]},{"text":" Pass 1 hunts. Pass 2 validates. Pass 3 verdicts per journey.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Bugs go to issues, never the atlas.","type":"text","marks":[{"type":"strong"}]},{"text":" The atlas is a living architecture doc, not a bug report.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Kuzu is required, not optional.","type":"text","marks":[{"type":"strong"}]},{"text":" Never silently skip graph ingestion. Fail loudly and fix.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Rebuild from code truth. Hunt contradictions. File evidence-backed bugs. Repeat.","type":"text","marks":[{"type":"strong"}]}]}]},"metadata":{"date":"2026-06-05","name":"code-atlas","author":"@skillopedia","source":{"stars":61,"repo_name":"amplihack","origin_url":"https://github.com/rysweet/amplihack/blob/HEAD/.claude/skills/code-atlas/SKILL.md","repo_owner":"rysweet","body_sha256":"e468a7453048f848b2e6858250843c18f05945a6382804d1beda4ff3a975b6c1","cluster_key":"66c0e5a99ad9ad3843f47b502f90d288155157e949802a350980df4ae7611c7b","clean_bundle":{"format":"clean-skill-bundle-v1","source":"rysweet/amplihack/.claude/skills/code-atlas/SKILL.md","attachments":[{"id":"a25a6825-82d1-5c3a-9c3a-814baa54d54f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a25a6825-82d1-5c3a-9c3a-814baa54d54f/attachment.md","path":"API-CONTRACTS.md","size":31960,"sha256":"8529fc93624e22a37fa7b11c6b330fb11825e64f038645f8a4ffa0f6d4a4ce2e","contentType":"text/markdown; charset=utf-8"},{"id":"87091ebe-984e-56f5-87ab-3f5ff46969c2","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/87091ebe-984e-56f5-87ab-3f5ff46969c2/attachment.yaml","path":"LAYERS.yaml","size":6235,"sha256":"10ed877c25b60acd819018b12c6ca0f74c5477c845d33ed50acd64ba245765ef","contentType":"application/yaml; charset=utf-8"},{"id":"b14450b2-980e-5e6f-8165-f637c1650579","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/b14450b2-980e-5e6f-8165-f637c1650579/attachment.md","path":"README.md","size":4600,"sha256":"dde78db37f8abf615df604071f6d3a598a61cf30aec8893c8237da75479d81d5","contentType":"text/markdown; charset=utf-8"},{"id":"6d7ba799-a6e3-55e4-a80a-68cd9b93a20f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/6d7ba799-a6e3-55e4-a80a-68cd9b93a20f/attachment.md","path":"SECURITY.md","size":23797,"sha256":"03a5963a7ebe0c8e0a2c743d509d27b5d350f1c395b6d49903cb21a2357e1570","contentType":"text/markdown; charset=utf-8"},{"id":"c446f9f7-d772-5a35-aeeb-968996bedb45","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/c446f9f7-d772-5a35-aeeb-968996bedb45/attachment.md","path":"bug-hunt-guide.md","size":5844,"sha256":"7fab24fdfde32d287eb8d9befaf11ac7c2292722d585c8a471a955a48d745f0b","contentType":"text/markdown; charset=utf-8"},{"id":"1d2a1da0-efc6-502a-95b3-71dcefdcd48f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/1d2a1da0-efc6-502a-95b3-71dcefdcd48f/attachment.md","path":"examples.md","size":9436,"sha256":"26bcedd49bb5619720776fd0299109c4921cc02b6e3557caa85d69989e518a29","contentType":"text/markdown; charset=utf-8"},{"id":"06eaab6d-22d2-564b-b96a-9cda23d58bc7","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/06eaab6d-22d2-564b-b96a-9cda23d58bc7/attachment.md","path":"publication-guide.md","size":5633,"sha256":"a804e29c58e3e1296c28aa6c6e0b16d6607210844ab0e33073eb9d840cc2dc0e","contentType":"text/markdown; charset=utf-8"},{"id":"dc71d104-82d7-5a92-8796-7acea24547e7","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/dc71d104-82d7-5a92-8796-7acea24547e7/attachment.md","path":"reference.md","size":8656,"sha256":"5b0d98ae12dbeb0985707778b357fa73949de815e81164a691ab1dc66e4d0081","contentType":"text/markdown; charset=utf-8"},{"id":"6a32f643-d09b-533c-aeb4-df754a032e5f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/6a32f643-d09b-533c-aeb4-df754a032e5f/attachment.sh","path":"tests/run_all_tests.sh","size":9212,"sha256":"5dd97ee445b1198706d42a79ab4c556b8599e9530ccb8107d5c109871c7f1881","contentType":"application/x-sh; charset=utf-8"},{"id":"e3b64e17-8281-5d84-b56d-4ffa74f5ddca","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/e3b64e17-8281-5d84-b56d-4ffa74f5ddca/attachment.sh","path":"tests/test_atlas_output_structure.sh","size":15747,"sha256":"9b7f1cca6f0e2721dce5fa885d703ffe50fb7bf9c08c9b88d2182e1355ec574d","contentType":"application/x-sh; charset=utf-8"},{"id":"fbdb5540-b564-5e2a-b3a6-403be2ab9898","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/fbdb5540-b564-5e2a-b3a6-403be2ab9898/attachment.sh","path":"tests/test_bug_hunt_workflow.sh","size":18476,"sha256":"f44b48b40b53fe2ca179cc8e7866e752fbb5dda21d5c5d3a6f3b7dbc26040f4f","contentType":"application/x-sh; charset=utf-8"},{"id":"44126e70-0296-5ea3-a2a4-9875ef360b09","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/44126e70-0296-5ea3-a2a4-9875ef360b09/attachment.sh","path":"tests/test_ci_workflow.sh","size":12522,"sha256":"1b3c2fd79d5bf4452d19d4b1e62b629b304cb41090f4556f17d1893410de2c8f","contentType":"application/x-sh; charset=utf-8"},{"id":"55600e18-5380-5d99-9f82-1f996e39154a","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/55600e18-5380-5d99-9f82-1f996e39154a/attachment.sh","path":"tests/test_layer7_8.sh","size":14144,"sha256":"9b7c05e398b7fe135e714fe1cb85d8728a6521bf211b7b6b2d3ebe4a822fd663","contentType":"application/x-sh; charset=utf-8"},{"id":"87961614-c055-53ab-b532-95f1703327e3","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/87961614-c055-53ab-b532-95f1703327e3/attachment.sh","path":"tests/test_layer_contracts.sh","size":21995,"sha256":"b3c82836a6c6f63ab748b03414794a80bfb044aea743a9a99ab7122574cfb79c","contentType":"application/x-sh; charset=utf-8"},{"id":"792cc326-26b6-5d41-8e41-ef614eaaa144","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/792cc326-26b6-5d41-8e41-ef614eaaa144/attachment.sh","path":"tests/test_no_silent_degradation.sh","size":10787,"sha256":"088ac488e7306814efce865e2799c48831b906c117d19a03fda80a23d1d95b60","contentType":"application/x-sh; charset=utf-8"},{"id":"6a7e748a-2b39-5711-a9f1-bd273ca495ff","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/6a7e748a-2b39-5711-a9f1-bd273ca495ff/attachment.sh","path":"tests/test_publication_workflow.sh","size":10218,"sha256":"0acf6c6064aec37c2908cf5c8fadd0289abb23555ffe0445436f1e3a5afb253f","contentType":"application/x-sh; charset=utf-8"},{"id":"120fc26f-1e72-5b48-aded-7557664cd028","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/120fc26f-1e72-5b48-aded-7557664cd028/attachment.sh","path":"tests/test_rebuild_script.sh","size":10645,"sha256":"eea7b7e96a223e8b64b1be8106baed4ee02589b2532e93013636df469d0894d2","contentType":"application/x-sh; charset=utf-8"},{"id":"e3bf2fc3-55d9-53df-aed4-c51e9246438b","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/e3bf2fc3-55d9-53df-aed4-c51e9246438b/attachment.md","path":"tests/test_scenarios.md","size":6875,"sha256":"e9b93018854cbe6a402be9aa55773f382a7286e13eac26e8354d63c4d006a78a","contentType":"text/markdown; charset=utf-8"},{"id":"5f000187-a0a1-5294-9659-b8b5b42142a7","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/5f000187-a0a1-5294-9659-b8b5b42142a7/attachment.md","path":"tests/test_security_controls.md","size":5614,"sha256":"89aa71960d21bffaa424f2fd4689f4d6b915db3c0271d9e819308316b30ce326","contentType":"text/markdown; charset=utf-8"},{"id":"b6329ec9-7a23-59d4-a570-795a6d13dcae","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/b6329ec9-7a23-59d4-a570-795a6d13dcae/attachment.sh","path":"tests/test_security_controls.sh","size":17037,"sha256":"15a270b63eddf06d711f1ceb67b577a8351cc83f03e419047086b140fe870dad","contentType":"application/x-sh; charset=utf-8"},{"id":"803dba87-e5b0-54e6-a60e-6486e68ab3c6","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/803dba87-e5b0-54e6-a60e-6486e68ab3c6/attachment.sh","path":"tests/test_staleness_triggers.sh","size":6887,"sha256":"504b97c177e71766642939a5f4d659ac73a716132c9ff660a6d797c3fc3fa457","contentType":"application/x-sh; charset=utf-8"}],"bundle_sha256":"f075ebd32f3eed4e94057a79db1d8e5869b266755134b8c4c8f96ddf3ae5dded","attachment_count":21,"text_attachments":21,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":0,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":".claude/skills/code-atlas/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"devops-infrastructure","category_label":"DevOps"},"exact_dupes_collapsed_into_this":0},"invokes":{"agents":["visualization-architect","analyzer","reviewer"],"skills":["code-visualizer","mermaid-diagram-generator","lsp-setup"]},"version":"v1","category":"devops-infrastructure","import_tag":"clean-skills-v1","description":"Builds comprehensive, living code-atlases as multi-layer architecture documents derived from\ncode-first truth. Defaults to both Graphviz DOT and Mermaid. User can override to single format.\nLanguage-agnostic (Go, TypeScript, Python, .NET, Rust, Java). Files issues with\n'code-atlas-bughunt' label. Treats atlas-building as a multi-agent bug-hunting journey:\ngraph-form reasoning exposes structural bugs, route/DTO mismatches, orphaned env vars,\ndead code paths, and stale documentation that linear review misses. Three-pass bug hunt\nwith per-journey PASS/FAIL/NEEDS_ATTENTION verdicts.\nUse when: creating architecture documentation, investigating unfamiliar codebases,\nhunting structural bugs, setting up CI/CD diagram refresh, or publishing to GitHub Pages/mkdocs.\n"}},"renderedAt":1782979493602}

Code Atlas Skill Purpose Build exhaustive, regeneratable architecture atlases directly from code truth. A code-atlas is a living document set: diagrams, graphs, and inventory tables that form a navigable map of any codebase. Atlas-building is investigation: structured reasoning about code in graph form reveals structural bugs, API contract mismatches, and architectural drift that linear code review misses. An atlas is complete when any engineer, given only the atlas and a bug report, can trace the full execution path without opening the source code. Layer Overview Layer definitions are the si…