Overview
Chapter 01: The Visual Communication & Declarative Diagramming Shift
Playbook Track: 05 – Presentation Slides & Architecture Diagrams (Visual Systems, Claude Code & Antigravity Workflows)
Target Audience: Year 1 Computer Science & Software Engineering Students Core Tooling Stack: Gemini 2.5 Pro & Flash, Claude 3.7 Sonnet (Claude Code CLI), Antigravity CLI (agy), Mermaid.js, Python 3.11+
Delivery Status: 🔍 Ready for Review (Tier 1 Markdown)
1. The Big Picture & Real-World Analogy
Hand-Drawing Circuit Schematics in Crayon vs. Writing Verilog/CAD
Imagine an electrical engineering student designing a motherboard:
- The Crayon Disaster: You spend two full days hand-drawing 50 microchips, capacitors, and copper traces on a huge poster board using colored crayons. It looks pretty! But on Friday, your lab partner changes the power regulator from 5V to 3.3V. Your hand-drawn poster is now obsolete! You can't erase crayon without tearing the paper, you can't run a simulation on a poster board, and you can't track changes in Git.
- The Modern CAD / Verilog Approach: You write your circuit design in a declarative text format (like Verilog or KiCAD schematic code). When the power regulator changes, you edit one line of text:
regulator_v = 3.3;. The CAD compiler automatically re-routes the traces, re-renders the schematic in crisp vector graphics, and checks for short circuits in seconds!
In software engineering, manual drag-and-drop drawing tools (Lucidchart, Draw.io, Visio) are the crayons:
- Someone draws a diagram, exports a PNG screenshot, and pastes it into documentation.
- Two weeks later, a developer renames an API endpoint or adds a cache, but nobody updates the screenshot.
- The diagram rots and becomes actively deceptive (The Stale Diagram Crisis).
Diagramming-as-Code (DaC) treats visual architecture like source code: You write clean text (like Mermaid or D2 inside Markdown), commit it to Git, and let automated compilers render beautiful, always up-to-date diagrams on demand.
2. Engineering Jargon Demystifier Table
| Industry Term | What It Actually Means | Freshman Student Analogy |
|---|---|---|
| Diagramming-as-Code (DaC) | Generating technical diagrams from human-readable text specifications (like Mermaid or PlantUML) instead of dragging boxes in a GUI. | Writing HTML to create a webpage instead of manually cutting and pasting pictures in MS Word. |
| Mermaid.js | A JavaScript-based diagramming syntax that renders flowcharts, sequence diagrams, and class diagrams directly inside Markdown. | Markdown for diagrams: simple text arrows (A --> B) turn into visual charts automatically. |
| Dual-Coding Theory | The cognitive psychology discovery that our brains process visual diagrams and written text through two separate, parallel mental channels. | Watching a video with subtitles: you understand faster when you see the visual action AND read the caption together. |
| Cognitive Load Theory | The study of how much mental effort is required to understand something. Split into Intrinsic (real complexity), Germane (learning), and Extraneous (distracting clutter). | Reading a difficult textbook (Intrinsic) vs struggling because the font is blurry and sentences are jumbled (Extraneous). |
| Miller's Law ($7 \pm 2$) | The psychological rule that human working memory can only hold 5 to 9 chunks of information at one time. | Why phone numbers are split into 3-digit and 4-digit chunks instead of one 10-digit block. |
| Mystery Arrow | An arrow on an architecture diagram that has no label explaining what protocol or data travels across it. | A highway road sign with an arrow pointing into the desert with no town name or mileage. |
| Headless Vector Export (SVG) | Compiling code into clean, scalable vector graphics without needing a human to open a desktop web browser. | Compiling a C program from the terminal using gcc without opening an IDE. |
3. The 5-Minute Micro-Lab: The Diagram Linter
Run this zero-dependency Python script to see how an automated linter audits Mermaid diagram source code for cognitive overload and mystery arrows:
"""
Micro-Lab: Mermaid Diagram Cognitive Linter
PB-05 Chapter 1 Micro-Lab (Zero External Dependencies)
"""
import re
def lint_mermaid_diagram(mermaid_code: str) -> dict:
findings = []
lines = mermaid_code.strip().splitlines()
# 1. Extract nodes (e.g. A[User], B[(Database)])
nodes = set(re.findall(r"([a-zA-Z0-9_]+)\s*[\[\(\{]", mermaid_code))
# 2. Check Miller's Law: Cognitive Load Saturation (> 9 nodes)
if len(nodes) > 9:
findings.append(f"COGNITIVE_OVERLOAD: Diagram contains {len(nodes)} nodes (exceeds Miller's 7+/-2 limit).")
# 3. Check for Mystery Arrows (unlabeled edges: A --> B instead of A -->|HTTPS| B)
unlabeled_edge_pattern = re.compile(r"-->\s*[a-zA-Z0-9_]+(?!\s*\|)")
for idx, line in enumerate(lines, start=1):
if re.search(r"-->\s*[a-zA-Z0-9_]+", line) and "|" not in line:
findings.append(f"MYSTERY_ARROW: Line {idx} has an unlabeled communication edge ('{line.strip()}').")
return {
"node_count": len(nodes),
"is_clean": len(findings) == 0,
"issues": findings
}
if __name__ == "__main__":
bad_diagram = """
flowchart TD
User --> Gateway
Gateway --> Auth
Gateway --> Billing
Gateway --> Shipping
Gateway --> Inventory
Gateway --> Notification
Gateway --> Analytics
Gateway --> Search
Gateway --> Logging
Gateway --> Cache
"""
good_diagram = """
flowchart TD
User -->|HTTPS REST| Gateway[API Gateway]
Gateway -->|gRPC / TLS| Auth[Auth Service]
Gateway -->|SQL Pool| DB[(PostgreSQL)]
"""
print("=== Auditing Cluttered / Mystery Diagram ===")
res1 = lint_mermaid_diagram(bad_diagram)
print(f"Clean: {res1['is_clean']} | Node Count: {res1['node_count']}")
for issue in res1["issues"]:
print(f" [FLAGGED]: {issue}")
print("\n=== Auditing Clean / Labeled Diagram ===")
res2 = lint_mermaid_diagram(good_diagram)
print(f"Clean: {res2['is_clean']} | Node Count: {res2['node_count']} -> ZERO VIOLATIONS!")
4. Cognitive Science Foundations & Declarative Pipelines
In enterprise software engineering and cloud systems architecture, technical documentation suffers from a pervasive failure mode: The Stale Diagram Crisis.
An architect spends three days in a GUI drawing tool (Visio, Lucidchart, or Draw.io) manually placing rectangles, picking hex color codes, and routing connector splines. The resulting diagram is exported as a PNG bitmap, pasted into a Confluence wiki or presentation deck, and shared with stakeholders. Within two weeks, a junior engineer refactors the authentication service from gRPC to REST, a Redis caching tier is introduced, and the database schema splits into multi-tenant shards.
The PNG diagram in Confluence is now worse than useless: it is actively deceptive. It cannot be diffed in Git, cannot be checked in CI/CD, and requires hours of tedious manual dragging to update.
To solve this systemic failure, modern engineering organizations must undergo a fundamental paradigm shift: Diagramming-as-Code (DaC).
+---------------------------------------------------------------------------------------------------+
| DECLARATIVE DIAGRAMMING-AS-CODE COMPILATION PIPELINE |
+---------------------------------------------------------------------------------------------------+
| |
| +--------------------------+ +--------------------------+ |
| | SOURCE CODE / ADR SPEC | ------> | CLAUDE CODE / GEMINI | |
| | - Git Repositories | | PARSER AGENT | |
| | - OpenAPI / Proto Specs | | - Extracts Entities | |
| | - C4 Container Models | | - Identifies Protocols | |
| +--------------------------+ +--------------------------+ |
| | |
| v |
| +--------------------------+ +--------------------------+ |
| | HEADLESS VECTOR EXPORT | <------ | DECLARATIVE DSL SOURCE | |
| | - SVG Vector Embedding | | - Mermaid / D2 / C4 | |
| | - WCAG 2.1 AA Validated | | - Git Version-Controlled| |
| | - Slide Deck Ready (16:9| | - Syntactically Linted | |
| +--------------------------+ +--------------------------+ |
| |
+---------------------------------------------------------------------------------------------------+
1.1 The Cognitive Science of Visual Architecture
Diagrams are not decorative artwork; they are cognitive processing accelerators. Grounding visual design in cognitive science is essential for communicating complex distributed architectures without overwhelming human working memory:
- Dual-Coding Theory (Allan Paivio): The human brain processes verbal and visual information through independent, parallel channels. Presenting an architecture diagram alongside concise, synchronized labels leverages both channels simultaneously, dramatically increasing comprehension and recall compared to dense textual documentation alone.
- Cognitive Load Theory (John Sweller):
- Intrinsic Load: The inherent, irreducible complexity of the distributed system itself (e.g., Raft consensus, two-phase commit).
- Germane Load: The productive mental effort dedicated to constructing schemas and understanding system causality.
- Extraneous Load: The wasteful mental friction imposed by poor diagramming (e.g., crisscrossing lines, inconsistent shapes, mystery unlabeled arrows, decorative clipart).
- The Golden Invariant: A technical diagram must eliminate Extraneous Load to free up working memory for Germane comprehension.
- Gestalt Principles of Visual Perception:
- Proximity: Services grouped within the same boundary or subnet are perceived as functionally coupled.
- Similarity: Nodes sharing identical geometric shapes and colors are recognized as identical architectural tiers (e.g., all databases represented by cylinders).
- Common Region: Explicit subgraphs and cluster boxes create instant mental encapsulation of microservices within an autonomous domain.
6. Declarative Diagram Parsing & Linting State Machine
flowchart TD
A["Raw Declarative DSL Source (Mermaid / D2)"] --> B["Tokenization: Lexical Scanner identifies Nodes, Arrows, Subgraphs"]
B --> C["AST Generation: Build In-Memory Directed Graph G = (V, E, Subgraphs)"]
C --> D{"Syntactic Linter Audit"}
D -->|Check 1: Miller's Law| E["Cognitive Density: Count Nodes in View (Max <= 12)"]
D -->|Check 2: Graph Integrity| F["Dangling Nodes: Verify All Nodes Have In/Out Edges"]
D -->|Check 3: Architecture Rigor| G["Mystery Arrows: Ensure All Edges Declare Protocol"]
D -->|Check 4: Layout Direction| H["Orientation Invariant: Verify TD or LR Layout"]
E & F & G & H --> I["Compile Lint Report: Errors, Warnings, Cognitive Score"]
I --> J["Render Stage: Headless SVG / PPTX Embedding"]
style A fill:#f5f5f5,stroke:#9e9e9e,stroke-width:2px;
style D fill:#fff3e0,stroke:#ff9800,stroke-width:2px;
style J fill:#e8f5e9,stroke:#4caf50,stroke-width:2px;
3. Gate 2: Mandatory Manual vs. Programmatic Contrasts
Traditional manual diagram dragging creates severe maintenance drag and architectural drift.
| Architectural Dimension | Legacy Manual WYSIWYG (Visio, Lucidchart, Miro) | Declarative Diagramming-as-Code (Mermaid, D2, PlantUML) | Operational Risk of Legacy Approach |
|---|---|---|---|
| Version Control | Binary files (.vsdx, .drawio) or proprietary cloud URLs; Git diffs are unreadable blobs. |
Plain-text declarative code (.mmd, .d2); fully diffable, reviewable, and mergeable via Git Pull Requests. |
Silent drift: architects approve changes without seeing what changed in visual models. |
| Drift Velocity | Severe: diagrams are updated months after code changes, or abandoned entirely. | Zero: diagrams live inside the repository alongside code; CI/CD fails PRs if architectural drift occurs. | Engineering errors caused by developers referencing obsolete topology diagrams. |
| Cognitive Standardization | Inconsistent: every engineer invents their own shapes, colors, line widths, and fonts. | Rigorous: layout engines (ELK, Dagre, TALA) calculate mathematical routing; styles defined by global design tokens. | Stakeholder confusion caused by non-standardized visual metaphors. |
| AI Integration | Near impossible: LLMs struggle to emit pixel coordinates for proprietary binary formats. | Native: LLMs (Gemini 2.5, Claude 3.7) generate and critique declarative DSLs with extreme precision. | Inability to automate documentation pipelines within agentic coding workflows. |
| Automation & CI/CD | Manual export: engineer must open browser, click Export PNG, and upload to slide deck. | Fully automated: headless CLI compilers render SVGs/PDFs directly in GitHub Actions or Antigravity scripts. | Huge human time waste before every executive sprint review or architecture board. |
4. Gate 3: Frontier AI Prompts, Claude Code & Antigravity Workflows
Frontier models like Gemini 2.5 Pro and Claude 3.7 Sonnet generate flawless declarative diagrams when given structured constraints and cognitive guardrails.
4.1 Architecture Diagram Synthesis Prompt (gemini-2.5-pro / claude-3.7-sonnet)
SYSTEM INSTRUCTION: You are a Principal Cloud Architect and Visual Communications Engineer.
TASK: Translate the provided Architectural Specification or ADR into a clean, publication-grade Mermaid flowchart.
CONSTRAINTS:
1. Enforce Miller's Law: Never place more than 10-12 active service nodes in a single diagram view. Group subsystems into explicit subgraphs.
2. Zero Mystery Arrows: Every single directional edge MUST include an explicit protocol or payload description (e.g., -->|gRPC / Protobuf| or -->|HTTPS / JWT|).
3. Direction Invariant: Use 'flowchart LR' for data-streaming and sequential pipelines; use 'flowchart TD' for multi-tier client-to-database architectures.
4. Semantic Node Shapes:
- Client / UI: Rounded rect ([Browser Client])
- Microservice / Worker: Rectangle [Order Service]
- Datastore / Cache: Cylinder [(PostgreSQL DB)] or [(Redis Cluster)]
- External Gateway: Double circle or Subroutine [[Stripe API]]
5. Output ONLY clean, valid Mermaid code block enclosed in triple backticks.
4.2 Claude Code Skill Definition (.claude/skills/extract-diagram.md)
---
name: extract-diagram
description: Analyzes repository service code and generates a declarative Mermaid architecture diagram
parameters:
path:
type: string
description: Subdirectory or module path to analyze
---
#!/bin/bash
# 1. Scan directory for entrypoints, controllers, and database models
# 2. Extract dependencies and outbound network clients
# 3. Formulate Mermaid architecture AST and validate syntax
claude --prompt "Analyze services in $path. Extract communication edges, endpoints, and datastores. Emit a validated Mermaid flowchart following C4 Container standards."
4.3 Structured JSON Schema for Diagram AST (DeclarativeDiagramASTSchema)
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "DeclarativeDiagramAST",
"type": "object",
"properties": {
"direction": { "type": "string", "enum": ["TB", "TD", "LR", "RL", "BT"] },
"nodes": {
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"node_id": { "type": "string" },
"label": { "type": "string" },
"shape": { "type": "string", "enum": ["RECTANGLE", "ROUNDED", "DATABASE", "CYLINDER", "DIAMOND", "CIRCLE", "SUBROUTINE"] },
"subgraph": { "type": ["string", "null"] }
},
"required": ["node_id", "label", "shape"]
}
},
"edges": {
"type": "array",
"items": {
"type": "object",
"properties": {
"source_id": { "type": "string" },
"target_id": { "type": "string" },
"label": { "type": ["string", "null"] },
"is_bidirectional": { "type": "boolean" },
"is_dotted": { "type": "boolean" }
},
"required": ["source_id", "target_id", "is_bidirectional", "is_dotted"]
}
},
"subgraphs": {
"type": "object",
"additionalProperties": {
"type": "array",
"items": { "type": "string" }
}
}
},
"required": ["direction", "nodes", "edges", "subgraphs"]
}
5. Gate 4: Quantitative Visual & Tooling Trade-Off Matrix
Choosing a diagramming engine is governed by trade-offs between Git ergonomics, layout sophistication, and automated compiler integration:
| Diagramming Paradigm | Git Diff Ergonomics | Rendering Engine | Layout Algorithmic Sophistication | C4 Model Support | CI/CD Headless Automation | Production Engineering Grade |
|---|---|---|---|---|---|---|
| Manual WYSIWYG (Visio / Lucidchart) | [FAIL] Zero (Binary / Cloud URL) | Canvas GUI | Manual Human Dragging | Informal (Ad-hoc) | [FAIL] None (Manual export) | Unacceptable for Engineering |
| Markdown Mermaid.js | Excellent (Pure text) | SVG / Canvas | Dagre / ELK (Good for simple DAGs) | Native C4 syntax | Native (mermaid-cli / agy) | Standard Industry Baseline |
| PlantUML / Graphviz | Good (Text DSL) | PNG / SVG / EPS | Graphviz Dot (Strict hierarchical) | High (C4-PlantUML library) | High (Java runtime required) | Enterprise Legacy Standard |
| Next-Gen D2 (Terrastruct) | Superior (Cleanest DSL) | Modern SVG | TALA (Proprietary) / ELK / Dagre | Excellent (Native nesting) | Fast (Single Go binary) | State-of-the-Art Modern DSL |
| Diagrams (Python Mingrammer) | Good (Python code) | PNG / SVG | Graphviz backend | Moderate | Native (Python 3.11+ script) | Ideal for Cloud Topologies |
6. Gate 5: The 10 Methodological Threats to Validity & Visual Anti-Patterns
When authoring architecture diagrams for engineering reviews and executive presentations, teams must defend against ten critical visual failure modes:
1. The "Boxology" Trap (Undefined Semantic Boundaries)
- Anti-Pattern: Drawing 20 identical rectangles labeled with vague buzzwords ("AI Engine", "Data Processor", "Core Platform") without specifying whether they are processes, microservices, databases, or third-party SaaS.
- Defense: Enforce C4 Model taxonomy: explicitly classify every node as a Person, System, Container, or Component with its underlying technology stack (e.g.,
Order Service [Container: Go / Gin]).
2. Cognitive Load Overload (Violating Miller's Law $7 \pm 2$)
- Anti-Pattern: Cramming 35 microservices into a single diagram, resulting in microscopic fonts and visual exhaustion.
- Defense: Hard limit of $\le 12$ nodes per view; partition complex architectures into hierarchical subgraphs or multi-level C4 views.
3. Mystery Arrows (Unlabeled Communication Edges)
- Anti-Pattern: Drawing arrows between boxes without explaining the communication protocol, sync/async nature, or data payload.
- Defense: Mandatory edge labeling rule: every arrow must state the protocol and action (e.g.,
-->|HTTPS REST / JSON|or-.->|Kafka Event: OrderCreated|).
4. Cyclical Spaghetti Layouts
- Anti-Pattern: Tangled bidirectional arrows crossing diagonally through the diagram, breaking the layout engine's routing heuristics.
- Defense: Group feedback loops into dedicated subgraphs; use dotted lines (
-.->) for asynchronous events to prevent layout distortion.
5. The God Container
- Anti-Pattern: A monolithic node in the center of the diagram with 15 outbound arrows, concealing critical internal architectural decisions.
- Defense: Decompose the God Container into a C4 Component diagram exposing internal controllers, handlers, and repositories.
6. Low-Contrast Inaccessibility (WCAG Failures)
- Anti-Pattern: Light-gray text on white backgrounds or pastel yellow arrows that become invisible on conference room projectors.
- Defense: Enforce WCAG 2.1 AA standard: minimum contrast ratio of $4.5:1$ for body text and $3:1$ for large headings and UI components.
7. Semantic Architecture Drift (Diagram Rot)
- Anti-Pattern: Storing architecture diagrams in standalone documentation wikis detached from codebase commits.
- Defense: Colocate declarative diagram source files (
architecture.mmd) inside the repository root; run automated diagram linters in Git pre-commit hooks.
8. Microscopic Font Scaling in Presentation Slides
- Anti-Pattern: Exporting a massive high-resolution diagram and shrinking it to fit a 16:9 PowerPoint slide, rendering text unreadable past the front row.
- Defense: Use progressive disclosure: show a high-level System Context diagram on Slide 1, followed by zoomed Container diagrams across subsequent slides.
9. Mixed-Orientation Chaos
- Anti-Pattern: Mixing Top-to-Bottom flows with Left-to-Right flows haphazardly in the same visual plane.
- Defense: Strict orientation invariant: establish a primary flow axis (e.g., Top-to-Bottom for layered tiers; Left-to-Right for event pipelines).
10. Vendor Icon Pollution
- Anti-Pattern: Splattering 25 colorful AWS/Azure/GCP service logos across the screen, turning an engineering diagram into a marketing billboard.
- Defense: Prioritize clear geometric shapes and functional labels; relegate vendor logos to secondary badges or annotations.
11. Gate 6: Mandatory Hands-On Lab (Visual Engineering Challenge)
Objective
You will implement an automated Declarative Diagram AST Parser & Syntactic Linter in zero-dependency Python 3.11+. The engine will parse Mermaid flowchart code, construct an in-memory graph representation, detect architectural anti-patterns, and enforce visual clarity standards.
Experimental Protocol
- Lexical & Syntactic Parsing: Tokenize Mermaid flowchart definitions, extracting layout direction (
TD,LR), node IDs, descriptive labels, geometric shapes (RECTANGLE,DATABASE,ROUNDED,CIRCLE), and directed communication edges. - AST Construction: Assemble a structured
DiagramASTrepresenting vertices, edge weights, and subgraphs. - Cognitive Load Auditing: Enforce Miller's Law ($\le 12$ nodes); flag cognitive load saturation if exceeded.
- Structural Linter Verification: Detect orphaned/dangling nodes with zero connections and flag "Mystery Arrows" (unlabeled communication edges).
12. Gate 7: Mandatory Recommended Answer & Executable Solution
The following zero-dependency Python 3.11+ script provides the complete, tested reference implementation of the Declarative Diagram AST Parser & Syntactic Linter.
"""
test_ch01_diagram_engine.py
Zero-dependency Python 3.11+ engine for PB-05 Chapter 1:
DeclarativeDiagramASTParser & SyntacticLinter
"""
import re
from dataclasses import dataclass, field
from enum import Enum
from typing import List, Dict, Any, Optional, Set, Tuple
class NodeShape(str, Enum):
RECTANGLE = "RECTANGLE" # [Label]
ROUNDED = "ROUNDED" # (Label) or ([Label])
DATABASE = "DATABASE" # [(Label)]
CYLINDER = "CYLINDER" # [(Label)]
DIAMOND = "DIAMOND" # {Label}
CIRCLE = "CIRCLE" # ((Label))
SUBROUTINE = "SUBROUTINE" # [[Label]]
class Severity(str, Enum):
ERROR = "ERROR"
WARNING = "WARNING"
INFO = "INFO"
@dataclass
class DiagramNode:
node_id: str
label: str
shape: NodeShape = NodeShape.RECTANGLE
subgraph: Optional[str] = None
@dataclass
class DiagramEdge:
source_id: str
target_id: str
label: Optional[str] = None
is_bidirectional: bool = False
is_dotted: bool = False
@dataclass
class DiagramAST:
direction: str # "TB", "TD", "LR", "RL", "BT"
nodes: Dict[str, DiagramNode] = field(default_factory=dict)
edges: List[DiagramEdge] = field(default_factory=list)
subgraphs: Dict[str, List[str]] = field(default_factory=dict)
@dataclass
class LintFinding:
code: str
severity: Severity
message: str
target_id: Optional[str] = None
class DeclarativeDiagramParser:
"""Parses standard declarative Mermaid flowchart code into a structured AST."""
ARROW_REGEX = re.compile(r"(\<--\>|--\s*\|.*?\|\s*-->|-->\|.*?\||-->|-\.->\|.*?\||-\.->|-\.\s*\|.*?\|\s*->)")
@classmethod
def _parse_node_token(cls, token: str, current_subgraph: Optional[str] = None) -> DiagramNode:
token = token.strip()
m = re.match(
r"^([a-zA-Z0-9_]+)\s*(\[\((.*?)\)\]|\[\[(.*?)\]\]|\(\((.*?)\)\)|\(\[(.*?)\]\)|\{(.*?)\}|\((.*?)\)|\[(.*?)\])?$",
token
)
if not m:
clean_id = re.sub(r'[^a-zA-Z0-9_]', '', token) or "Node"
return DiagramNode(node_id=clean_id, label=token, shape=NodeShape.RECTANGLE, subgraph=current_subgraph)
nid = m.group(1)
if m.group(3): shape, label = NodeShape.DATABASE, m.group(3)
elif m.group(4): shape, label = NodeShape.SUBROUTINE, m.group(4)
elif m.group(5): shape, label = NodeShape.CIRCLE, m.group(5)
elif m.group(6): shape, label = NodeShape.ROUNDED, m.group(6)
elif m.group(7): shape, label = NodeShape.DIAMOND, m.group(7)
elif m.group(8): shape, label = NodeShape.ROUNDED, m.group(8)
elif m.group(9): shape, label = NodeShape.RECTANGLE, m.group(9)
else: shape, label = NodeShape.RECTANGLE, nid
return DiagramNode(node_id=nid, label=label.strip(), shape=shape, subgraph=current_subgraph)
@classmethod
def _parse_arrow(cls, arrow_str: str) -> Tuple[bool, bool, Optional[str]]:
is_bidi = "<-->" in arrow_str
is_dotted = "-." in arrow_str
label = None
m = re.search(r"\|(.*?)\|", arrow_str)
if m:
label = m.group(1).strip()
return is_bidi, is_dotted, label
@classmethod
def parse_mermaid(cls, source_code: str) -> DiagramAST:
lines = [line.strip() for line in source_code.strip().splitlines() if line.strip() and not line.strip().startswith("%%")]
direction = "TD"
nodes: Dict[str, DiagramNode] = {}
edges: List[DiagramEdge] = []
subgraphs: Dict[str, List[str]] = {}
current_subgraph: Optional[str] = None
for line in lines:
if line.startswith("flowchart") or line.startswith("graph"):
parts = line.split()
if len(parts) > 1:
direction = parts[1].upper()
continue
if line.startswith("subgraph"):
sub_match = re.search(r'subgraph\s+([a-zA-Z0-9_]+)', line)
if sub_match:
current_subgraph = sub_match.group(1)
subgraphs[current_subgraph] = []
continue
if line == "end":
current_subgraph = None
continue
# Check for edge statement
arrow_match = cls.ARROW_REGEX.search(line)
if arrow_match:
left_str = line[:arrow_match.start()].strip()
arrow_str = arrow_match.group(1)
right_str = line[arrow_match.end():].strip()
src_node = cls._parse_node_token(left_str, current_subgraph)
tgt_node = cls._parse_node_token(right_str, current_subgraph)
# Store or update nodes
if src_node.node_id not in nodes or src_node.label != src_node.node_id:
nodes[src_node.node_id] = src_node
if tgt_node.node_id not in nodes or tgt_node.label != tgt_node.node_id:
nodes[tgt_node.node_id] = tgt_node
if current_subgraph:
if src_node.node_id not in subgraphs[current_subgraph]:
subgraphs[current_subgraph].append(src_node.node_id)
if tgt_node.node_id not in subgraphs[current_subgraph]:
subgraphs[current_subgraph].append(tgt_node.node_id)
is_bidi, is_dotted, edge_label = cls._parse_arrow(arrow_str)
edges.append(DiagramEdge(
source_id=src_node.node_id,
target_id=tgt_node.node_id,
label=edge_label,
is_bidirectional=is_bidi,
is_dotted=is_dotted
))
else:
# Standalone node declaration
node = cls._parse_node_token(line, current_subgraph)
nodes[node.node_id] = node
if current_subgraph and node.node_id not in subgraphs[current_subgraph]:
subgraphs[current_subgraph].append(node.node_id)
return DiagramAST(
direction=direction,
nodes=nodes,
edges=edges,
subgraphs=subgraphs
)
class DiagramSyntacticLinter:
"""Audits diagram ASTs for architectural anti-patterns and visual accessibility flaws."""
MAX_COGNITIVE_NODE_THRESHOLD = 12
@classmethod
def lint(cls, ast: DiagramAST) -> List[LintFinding]:
findings: List[LintFinding] = []
# Rule 1: Direction validity
if ast.direction not in ["TB", "TD", "LR", "RL", "BT"]:
findings.append(LintFinding(
code="LINT001",
severity=Severity.ERROR,
message=f"Invalid layout orientation '{ast.direction}'. Must be TB, TD, LR, RL, or BT."
))
# Rule 2: Cognitive Load Check (Miller's Law: 7 +/- 2)
total_nodes = len(ast.nodes)
if total_nodes > cls.MAX_COGNITIVE_NODE_THRESHOLD:
findings.append(LintFinding(
code="LINT002",
severity=Severity.WARNING,
message=f"Cognitive load saturation: Diagram contains {total_nodes} nodes (threshold={cls.MAX_COGNITIVE_NODE_THRESHOLD}). Consider modularizing into subgraphs or C4 Container levels."
))
# Rule 3: Dangling / Unconnected Nodes
connected_nodes: Set[str] = set()
for e in ast.edges:
connected_nodes.add(e.source_id)
connected_nodes.add(e.target_id)
for nid in ast.nodes:
if nid not in connected_nodes and len(ast.nodes) > 1:
findings.append(LintFinding(
code="LINT003",
severity=Severity.WARNING,
message=f"Isolated/Dangling node detected: '{nid}' has zero incoming or outgoing relationships.",
target_id=nid
))
# Rule 4: Mystery Arrows (Unlabeled Edges in Architecture Diagrams)
for e in ast.edges:
if not e.label or e.label.strip() == "":
findings.append(LintFinding(
code="LINT004",
severity=Severity.WARNING,
message=f"Unlabeled communication edge between '{e.source_id}' and '{e.target_id}'. Architecture diagrams require explicit protocol/data labels.",
target_id=f"{e.source_id}->{e.target_id}"
))
# Rule 5: Self-Referential Loops
for e in ast.edges:
if e.source_id == e.target_id:
findings.append(LintFinding(
code="LINT005",
severity=Severity.INFO,
message=f"Self-referential loop detected on node '{e.source_id}'.",
target_id=e.source_id
))
return findings
# ==========================================
# Self-Test Verification Suite
# ==========================================
if __name__ == "__main__":
import unittest
class TestDiagramASTAndLinter(unittest.TestCase):
def test_parse_clean_mermaid_flowchart(self):
code = """
flowchart LR
Client[Web Browser Client] -->|HTTPS / GraphQL| APIGateway([Cloud API Gateway])
APIGateway -->|gRPC| AuthService[Identity Service]
APIGateway -->|JSON| OrderService[Order Management Service]
OrderService -->|SQL Query| DB[(PostgreSQL Database)]
"""
ast = DeclarativeDiagramParser.parse_mermaid(code)
self.assertEqual(ast.direction, "LR")
self.assertEqual(len(ast.nodes), 5)
self.assertEqual(len(ast.edges), 4)
self.assertEqual(ast.nodes["DB"].shape, NodeShape.DATABASE)
self.assertEqual(ast.nodes["DB"].label, "PostgreSQL Database")
self.assertEqual(ast.nodes["APIGateway"].shape, NodeShape.ROUNDED)
findings = DiagramSyntacticLinter.lint(ast)
errors = [f for f in findings if f.severity == Severity.ERROR]
self.assertEqual(len(errors), 0)
def test_lint_catches_unlabeled_edges(self):
code = """
flowchart TD
A[Frontend] --> B[Backend]
B --> C[Database]
"""
ast = DeclarativeDiagramParser.parse_mermaid(code)
findings = DiagramSyntacticLinter.lint(ast)
unlabeled = [f for f in findings if f.code == "LINT004"]
self.assertEqual(len(unlabeled), 2)
def test_lint_catches_cognitive_load_saturation(self):
lines = ["flowchart TD"]
for i in range(15):
lines.append(f"Node{i}[Service {i}] -->|Call| Node{i+1}[Service {i+1}]")
code = "\n".join(lines)
ast = DeclarativeDiagramParser.parse_mermaid(code)
findings = DiagramSyntacticLinter.lint(ast)
load_warnings = [f for f in findings if f.code == "LINT002"]
self.assertEqual(len(load_warnings), 1)
def test_lint_catches_isolated_nodes(self):
code = """
flowchart TD
A[Worker] -->|Job| B[Queue]
C[OrphanedService]
"""
ast = DeclarativeDiagramParser.parse_mermaid(code)
findings = DiagramSyntacticLinter.lint(ast)
orphan_findings = [f for f in findings if f.code == "LINT003"]
self.assertEqual(len(orphan_findings), 1)
self.assertEqual(orphan_findings[0].target_id, "C")
suite = unittest.TestLoader().loadTestsFromTestCase(TestDiagramASTAndLinter)
runner = unittest.TextTestRunner(verbosity=2)
test_result = runner.run(suite)
if not test_result.wasSuccessful():
exit(1)
print("\n[PASS] All PB-05 Chapter 1 Unit Tests Passed Successfully (100% Conformance).")
Verification & Execution Output
When executed in Python 3.11+, this engine confirms clean Mermaid parsing, cognitive load auditing, unlabeled edge detection, and orphan node flagging:
test_lint_catches_cognitive_load_saturation (__main__.TestDiagramASTAndLinter.test_lint_catches_cognitive_load_saturation) ... ok
test_lint_catches_isolated_nodes (__main__.TestDiagramASTAndLinter.test_lint_catches_isolated_nodes) ... ok
test_lint_catches_unlabeled_edges (__main__.TestDiagramASTAndLinter.test_lint_catches_unlabeled_edges) ... ok
test_parse_clean_mermaid_flowchart (__main__.TestDiagramASTAndLinter.test_parse_clean_mermaid_flowchart) ... ok
----------------------------------------------------------------------
Ran 4 tests in 0.002s
OK
[PASS] All PB-05 Chapter 1 Unit Tests Passed Successfully (100% Conformance).
9. Summary & Visual Engineering Milestone Checklist
Before moving to Chapter 02 (Architecture Diagramming-as-Code with Mermaid, PlantUML & D2):
- Grounded visual communication in Dual-Coding Theory and Cognitive Load Theory.
- Contrasted legacy manual WYSIWYG drawing with modern Diagramming-as-Code.
- Calibrated Gemini 2.5 Pro and Claude 3.7 Sonnet prompts for non-spaghetti diagram generation.
- Defined the Claude Code skill architecture for automated diagram extraction.
- Compiled the Quantitative Trade-Off Matrix across diagramming engines (Mermaid, D2, PlantUML).
- Analyzed and defended against the 10 Visual Anti-Patterns (including the Boxology trap and Mystery Arrows).
- Executed and validated the zero-dependency Python 3.11+ Diagram AST and Linter engine.