diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 8ad9add..5639ef7 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -6,7 +6,7 @@ }, "metadata": { "description": "Skills shared by Baoyu for improving daily work efficiency", - "version": "1.98.0" + "version": "1.99.0" }, "plugins": [ { @@ -21,6 +21,7 @@ "./skills/baoyu-cover-image", "./skills/baoyu-danger-gemini-web", "./skills/baoyu-danger-x-to-markdown", + "./skills/baoyu-diagram", "./skills/baoyu-format-markdown", "./skills/baoyu-imagine", "./skills/baoyu-infographic", diff --git a/.gitignore b/.gitignore index 7b6e507..8a28731 100644 --- a/.gitignore +++ b/.gitignore @@ -154,6 +154,7 @@ illustrations/ comic/ translate/ posts/ +diagram/ ### IntelliJ IDEA ### .idea *.iws diff --git a/CHANGELOG.md b/CHANGELOG.md index 38e6c51..677ba2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ English | [中文](./CHANGELOG.zh.md) +## 1.99.0 - 2026-04-10 + +### Features +- `baoyu-diagram`: add new skill for generating publication-ready SVG diagrams — flowcharts, structural/architecture diagrams, and illustrative intuition diagrams. Claude writes real SVG code directly following a cohesive design system; output is a single self-contained `.svg` file with embedded styles and auto dark-mode, ready to embed in articles, WeChat posts, slides, and docs + ## 1.98.0 - 2026-04-10 ### Features diff --git a/CHANGELOG.zh.md b/CHANGELOG.zh.md index b7f817d..9e8a272 100644 --- a/CHANGELOG.zh.md +++ b/CHANGELOG.zh.md @@ -2,6 +2,11 @@ [English](./CHANGELOG.md) | 中文 +## 1.99.0 - 2026-04-10 + +### 新功能 +- `baoyu-diagram`:新增技能,用于生成可直接发布的 SVG 图表 —— 包括流程图、架构/结构图、示意图(直觉图解)。Claude 直接输出符合统一设计规范的真实 SVG 代码,产物是单个自包含的 `.svg` 文件,内嵌样式并自动支持深色模式,可直接嵌入文章、微信公众号、幻灯片和文档中 + ## 1.98.0 - 2026-04-10 ### 新功能 diff --git a/README.md b/README.md index b034ca8..33fcec5 100644 --- a/README.md +++ b/README.md @@ -272,6 +272,44 @@ Generate professional infographics with 21 layout types and 21 visual styles. An | ![knolling](./screenshots/infographic-styles/knolling.webp) | ![lego-brick](./screenshots/infographic-styles/lego-brick.webp) | | | knolling | lego-brick | | +#### baoyu-diagram + +Generate publication-ready SVG diagrams — flowcharts, structural/architecture diagrams, and illustrative intuition diagrams. Claude writes real SVG code directly following a cohesive design system. Output is a single self-contained `.svg` file with embedded styles and auto dark-mode, ready to embed in articles, WeChat posts, slides, Notion, and docs. + +```bash +# Auto-route on the verb in the prompt +/baoyu-diagram "how JWT authentication works" + +# Force a specific type +/baoyu-diagram "Kubernetes architecture" --type structural +/baoyu-diagram "how attention works" --type illustrative +/baoyu-diagram "CI/CD pipeline" --type flowchart + +# From a markdown source file +/baoyu-diagram path/to/content.md + +# Language and output path +/baoyu-diagram "微服务架构" --lang zh +/baoyu-diagram "build pipeline" --out docs/build-pipeline.svg +``` + +**Options**: +| Option | Description | +|--------|-------------| +| `--type ` | `flowchart`, `structural`, `illustrative`, `auto` (default) | +| `--lang ` | Output language (en, zh, ja, ...) | +| `--out ` | Output file path (default: `diagram/{slug}/diagram.svg`) | + +**Diagram types**: + +| Type | Reader need | Verbs that trigger it | +|------|-------------|------------------------| +| `flowchart` | Walk me through the steps in order | walk through, steps, process, lifecycle, workflow, state machine | +| `structural` | Show me what's inside what, how it's organised | architecture, components, topology, layout, what's inside | +| `illustrative` | Give me the intuition — draw the mechanism | how does X work, explain X, intuition for, why does X do Y | + +Not an image-generation skill — no LLM image model is called. Claude writes the SVG by hand with hand-computed layout math, so every diagram honors the design system. Embedded ` + + + + + + + + + «interface» + Drawable + + + + + draw(): void + + + + + + «abstract» + Shape + + # color: Color + # position: Point + + + area(): number + + + + + + Circle + + − radius: number + + + area(): number + + + + + + Square + + − side: number + + + area(): number + + + + + + + + + + + +``` + +viewBox H: bottom of last box is `260 + 110 = 370`, so `H = 370 + 20 = 390`. The example uses 420 for a 30px bottom margin, which is also fine. + +## Checklist (class-specific) + +On top of the standard `pitfalls.md` checks: + +1. Every class has all **three compartments** (name / attrs / methods), even if one is empty. +2. The class **name is `th` centered**, attributes/methods are **`ts` left-aligned at `rect_x + 12`**. +3. Every attribute/method starts with a **visibility marker** (`+ − # ~`). +4. Dividers use `class="arr"` with **inline `stroke-width="0.5"`**. +5. Inheritance and implementation arrows point **toward the parent/interface** (triangle on parent side). +6. Aggregation and composition diamonds sit on the **container side**, not the part side. +7. Associations carry **multiplicity labels** at both ends (`1`, `0..*`, `1..*`) when they aren't obvious. +8. Dashed arrows are `arr-alt`, solid arrows are `arr` — never mix. +9. Color ramp budget ≤3, one per class kind (concrete / abstract / interface / enum). +10. Association arrows **do not cross inheritance arrows** — if they do, re-route the association. diff --git a/skills/baoyu-diagram/references/design-system.md b/skills/baoyu-diagram/references/design-system.md new file mode 100644 index 0000000..49be087 --- /dev/null +++ b/skills/baoyu-diagram/references/design-system.md @@ -0,0 +1,157 @@ +# Design System + +The rules that make every diagram feel consistent. Adapted from Anthropic's Imagine Visual Creation Suite for standalone SVG output (self-contained, no host CSS, safe to embed anywhere). + +## Philosophy + +- **Flat**: No gradients, no drop shadows, no blur, no glow, no neon. Clean flat surfaces only. (One exception — see Illustrative diagrams.) +- **Compact**: Show the essential inline. If something needs a paragraph of explanation, it doesn't belong inside the diagram — move it to the prose around the diagram. +- **Seamless**: The SVG should feel like a native part of the surrounding article, not a foreign object. Transparent background — the host page provides the background. +- **Self-contained**: Every SVG carries its own ` + + + + + + Start + + + + Enter credentials + + + + + Auth service + + + + Dashboard + + + + + +``` + +## Worked example — Gate with pass/fail split + +Request: "LLM call with a gate that either continues through two more calls or exits early" + +Plan: +- 6 nodes on a single row: `In` (pill) → `LLM Call 1` → `Gate` → `LLM Call 2` → `LLM Call 3` → `Out` (pill), plus an `Exit` pill dropping below the Gate on the Fail branch. +- Colors: pills gray; LLM calls gray; Gate purple (the decision point). +- Arrow labels: *Pass* on Gate → LLM Call 2 (solid), *Fail* on Gate → Exit (dashed `.arr-alt`). +- Horizontal budget: 6 nodes + 5 gaps of 20px must fit in the 600px usable width. `60 + 106 + 60 + 106 + 106 + 60 = 498`, plus `5 × 20 = 100`, total = **598** — fits with 2px to spare. +- viewBox: single row at y=140, plus an Exit row at y=230. `max_y = 230 + 44 = 274` → H = **294**. + +```svg + + + + + + In + + + + LLM Call 1 + + + + + Gate + + + + + LLM Call 2 + + + + + LLM Call 3 + + + + Out + + + Exit + + + + + + + + + Pass + Fail + +``` + +Notes on this example: +- The *Pass* label sits **above** the solid horizontal arrow from Gate to LLM Call 2 at `y = arrow_y − 6 = 156`, centered on the arrow midpoint (316). +- The *Fail* label sits **beside** the dashed vertical arrow at x=286 (10px right of the arrow), with `text-anchor="start"` so it extends rightward — this keeps it clear of the Gate rect's right edge at x=306. +- Both arrow labels are ≤1 word — the rule of thumb at line 159 caps flowchart arrow labels at 3 words. +- The `.arr-alt` class on the Fail arrow matches the stroke color of `.arr` but adds the dashed pattern, so the Fail branch reads as a **conditional exit** rather than a mainline flow. +- The Exit pill is centered at x=276 (same x as the Gate center — both rects share x=246, w=60) so the dashed arrow drops straight down, no bend. +- Rightmost rect edge: Out pill ends at x = 578 + 60 = **638** ≤ 640 ✓ — the pre-save rightmost-edge check from `pitfalls.md#1` passes. + + +## Poster flowchart pattern + +See `references/flowchart-poster.md`. **Load it when ≥3 of the poster triggers fire in Step 4a**: the topic has a short name, a "why it exists" sentence, named phases (2–4), parallel candidates with a judge, a loop termination mechanic, overflow annotations, or a footer quote. If only 0–1 apply, the simple flowchart is the right tool. diff --git a/skills/baoyu-diagram/references/glyphs.md b/skills/baoyu-diagram/references/glyphs.md new file mode 100644 index 0000000..26b6bd6 --- /dev/null +++ b/skills/baoyu-diagram/references/glyphs.md @@ -0,0 +1,633 @@ +# Glyph Library + +Small, reusable SVG primitives that several sub-patterns draw *inside* their boxes or *on top of* their connectors: status circles on an arrow, checkboxes in a TODO list, queue slots inside a worker box, a document icon inside a shared-state store, a labeled annotation circle halfway along an arrow. + +These are intentionally *shared across types*. A status circle belongs in a flowchart (Generator-Verifier) and also in a structural advisor diagram. A checkbox belongs in a structural "TODOs vs Tasks" comparison and also in an illustrative "agent task list" subject. Keeping them in one place keeps the visual vocabulary consistent across diagrams. + +## Hard rules + +Every glyph in this file obeys **all** of the design system's hard rules (`design-system.md` → "Hard rules"). In particular: + +- **SVG primitives only.** Every glyph is composed of ``, ``, ``, ``, ``, or ``. No ``, no ``, no emoji characters. +- **No hardcoded colors on text or strokes that need dark-mode response.** Every glyph uses the existing CSS classes (`c-{ramp}`, `arr-{ramp}`, `arr-alt`, `box`, `t`/`th`/`ts`). This means both light and dark mode are handled automatically — zero template changes required. +- **No rotated text.** (Glyphs are small enough that horizontal text always fits.) +- **Sentence case** on any labels. + +If a new glyph seems to require a hardcoded color or a new CSS rule, stop and reconsider — the shape is probably wrong, or it belongs in one of the existing ramps. + +## Coordinate convention + +Every glyph block below is drawn with its **top-left anchor at (0, 0)**, then wrapped in a `` to place it in the diagram. The glyph's *bounding box width × height* is listed in each subsection so you can reserve space and pick `x, y` from the parent layout. + +```svg + + + + +``` + +All `x`/`y`/`cx`/`cy`/`d` values in the snippets below are **relative to the glyph origin**, not the diagram origin. Don't edit them — just translate the wrapper group. + +--- + +## Status circles + +A circle placed on or near an arrow to tag the outcome of a decision. Three variants: ✓ success, ✗ failure, ● in-progress. + +**Bounding box.** 24 × 24. Place the center of the circle at `(12, 12)` inside the glyph origin. When placed on a diagram, translate so that `(12, 12)` lands at the desired anchor — usually the midpoint of an arrow or just past a gate node. + +**Where they're used.** +- Flowchart (Generator-Verifier, Agent Teams, gate patterns): tag the two branches out of a verifier or judge node. +- Structural (advisor, TODOs→Tasks): mark individual task outcomes inside a state box. + +**Color rules.** Status carries semantic color — not negotiable: +- `check` → `c-green` / `arr-green` (accept, pass, done) +- `x` → `c-coral` / `arr-coral` (reject, fail, blocked) +- `dot` → `c-amber` / `arr-amber` (in-progress, busy, running) + +**Dark mode.** All three use the existing ramp classes, so both the circle fill and the inner stroke invert automatically. + +### status-circle-check + +```svg + + + + +``` + +### status-circle-x + +```svg + + + + +``` + +### status-circle-dot + +```svg + + + + +``` + +The nested small circle shares the ramp but is visually distinct because it sits on top of its own border stroke, reading as a filled dot against the paler ring. + +### Placement on an arrow + +A status circle placed *on* an arrow replaces that arrow's tail: + +``` + status-circle-check (center at x=360, y=160) + │ +(start)────arrow 1──→ ◎ ────arrow 2──→(end) +``` + +Split the original arrow into two segments, both with `marker-end="url(#arrow)"`. The circle sits in the gap. Arrow 1 stops at `circle_cx − 14` (2px before the circle border). Arrow 2 starts at `circle_cx + 14`. + +**Pitfall.** Don't layer the circle on top of a single continuous line — the arrow renders behind the circle and looks as if it pierces straight through. Always split into two segments. + +--- + +## Checkboxes + +A 14 × 14 checkbox glyph for task lists. Two variants: checked (filled green with check) and empty (neutral outline). + +**Bounding box.** 14 × 14. + +**Where they're used.** +- Structural (TODOs → Tasks comparison, checklist interiors inside a subsystem container): one per task row. +- Illustrative (task list subject, sparingly): only if the task list is the whole mechanism being shown. + +**Color rules.** +- `checked` → `c-green` (task done) +- `empty` → `c-gray` (task pending) + +For an "in-progress" task, substitute `status-circle-dot` at half scale, or just leave it empty. Don't invent a third checkbox color. + +### checkbox-checked + +```svg + + + + +``` + +### checkbox-empty + +```svg + + + +``` + +### Checklist rows + +A row of checklist items is laid out with the checkbox on the left and a 14px `t`-class label to its right: + +```svg + + + + + + Draft the outline + +``` + +Row pitch: 22px (14px checkbox height + 8px vertical gap). Label `y = checkbox_y + 11` for vertical centering against the 14px box. + +**Row width budget.** Inside a 315-wide subsystem container with 20px interior padding, the label has `315 − 20 − 20 − 14 − 8 = 253` px of width. At 8px/char (14px Latin) that caps the label at **~31 characters**; at 15px/char (CJK) that caps at **~16 characters**. Longer labels must be truncated or wrapped across two rows. + +--- + +## Queue slots + +A row of small filled/empty squares that represents a work queue or a buffer. Not to be confused with checklists — queue slots are *undifferentiated items*, checklists are *named tasks*. + +**Bounding box per slot.** 16 × 16. Row pitch: 20 (16 + 4 gap). Typical row: 6–8 slots max per row. + +**Where they're used.** +- Flowchart (Agent Teams, task-queue hub node): show the queue of pending work *inside* a source node. +- Structural (any box representing a worker pool with a buffer): same purpose, different frame. + +**Color rules.** +- `filled` → `c-amber` (work waiting in queue) +- `empty` → `c-gray` (slot available) + +### queue-slot-filled + +```svg + +``` + +### queue-slot-empty + +```svg + +``` + +### Queue row inside a box + +For a two-line box (`title + queue row`), the queue row goes at `rect_y + 28` (below the title baseline), starting at `rect_x + 12`: + +```svg + + + Task queue + + + + + + + +``` + +Box height for this variant: **52px** (title row + queue row + vertical padding). Don't reuse the default 44 or 56 heights — the queue row needs its own budget. + +**Row width budget.** `slots × 20 − 4 ≤ rect_width − 24`. For a 160-wide rect that's `(160 − 24 + 4) / 20 = 7` slots maximum. Above 7, split into two rows or widen the host rect. + +--- + +## Document & terminal icons + +Decorative icons that go *inside* a box (usually illustrative or structural) to hint at what kind of thing the box represents: a document store, a terminal/computer, a code script. These are **not allowed in flowchart or sequence diagrams** — label the rect instead. + +**Why only illustrative / structural.** In a flowchart, the reader is tracking sequence; an icon is visual noise competing with the arrow flow. In a structural diagram showing containment, an icon inside a container cell reinforces *what that cell is* and doesn't interrupt any flow. In an illustrative diagram the icon *is* part of the intuition (a document glyph inside a "Shared state" box tells you it's storing artifacts, not messages). + +### doc-icon + +**Bounding box.** 24 × 28. Place it in the lower half of a box that's at least 80 tall. + +```svg + + + + + + + +``` + +The outer silhouette is a rectangle with a folded corner; the three short horizontal lines suggest paragraphs. + +**Placement inside a box.** For a box at `(box_x, box_y)` with width `box_w` and height `box_h ≥ 80`, place the icon's origin at `(box_x + box_w/2 − 12, box_y + box_h − 36)`. This centers horizontally and leaves 8px below. + +### terminal-icon + +**Bounding box.** 24 × 24. + +```svg + + + + + +``` + +A framed box containing a `>` prompt chevron and a short input line. + +**Dark mode.** The `box` class provides a contrast frame that reads correctly in both modes; the inner chevron and cursor line use `arr` which also inverts. + +### script-icon + +**Not a fixed-size glyph.** The "script" pattern in image #12 (Programmatic tool calling) is a tall rect that contains multiple horizontal colored dividers and a `{ }` label at the top, representing an inline script that wraps several tool calls. Because the rect's height depends on how many tool calls it wraps, the script-icon is parameterized — build it inline using this recipe: + +```svg + + + + { } + + + + + + +``` + +`script_h = 48 + N × 36` for N wrapped calls; `bandK_y = script_y + 48 + (K-1) × 36`. See `sequence.md` → "Parallel independent rounds" for the full pattern this participates in. + +--- + +## Code braces + +Inline `{ }` as a label on a connector or inside a small label rect to signal "code" or "programmatic" handoff. + +```svg +{ } +``` + +**Width estimate.** 14px, ~16 char-px. Treat as 2 chars when budgeting for a rect container. + +**Where used.** Only in illustrative or structural diagrams that distinguish "data flow" from "code flow" — usually paired with a `script-icon` or a `c-gray` pill labeled "Programmatic". + +--- + +## Annotation circle on connector + +A labeled circle that sits on an arrow to name *the thing that mediates the exchange* — for example, "Skill" sitting on the Claude ↔ Environment arrow in image #13. + +**Bounding box.** 64 × 64 around the circle. The inner pill is ~52 × 20. + +**Where used.** Illustrative only. This is a "the action on this arrow is X" label, which in a flowchart or sequence would be a simple arrow label — in an illustrative diagram, we promote it to a visual node because the mediator *is* the subject. + +```svg + + + + + + + Skill + + +``` + +**Placement.** Center the glyph on the arrow's midpoint: `translate(arrow_midx − 32, arrow_midy − 32)`. The arrow must be split into two segments on either side of the glyph (same rule as status circles), each stopping 32px before the center. + +**Label length.** The inner pill caps at ~8 Latin chars at 14px (`8 × 8 = 64`, minus 8px padding each side, gives ~48px of label room — 6 chars is a comfortable fit; "Skill" at 5 chars is ideal). For CJK, cap at 3 characters. + +**Pitfall.** Don't use `c-teal` on the outer circle — that double-fills the ramp and the label pill disappears. Keep the outer ring on `box`, the inner pill on a ramp. + +--- + +## Dashed future-state rect + +Used in "TODOs → Tasks" (image #4) to show a task that hasn't been scheduled yet — the node exists in the plan but not in the active DAG. + +```svg + +``` + +Reuses the existing `arr-alt` class — it already provides `fill: none`, `stroke-width: 1.5`, `stroke-dasharray: 5 4`, and dark-mode stroke override. No inline attributes needed. + +**Text inside.** Label with `ts` class (12px, muted) — future-state nodes are always visually demoted relative to active ones: + +```svg + + + Task 4 + +``` + +**Pitfall.** Do *not* create a new `.box-future` CSS class. The `arr-alt` class handles it — adding a new class is template bloat. + +**Pitfall.** Don't align the dashed rect's center with an active `c-{ramp}` rect's center if their heights differ — the visual baseline looks off. Match the top edge instead, or match the baseline of the label text. + +--- + +## Publish/subscribe arrow pair + +Two parallel offset arrows between a node and a bus bar — one labeled "Publish" going down, one labeled "Subscribe" going up. Used in the bus topology pattern (`structural.md` → "Bus topology"). + +**Not a static glyph.** It's a pair of straight arrows with an 8px horizontal offset between them, each with its own label. Write this inline where you use it: + +```svg + + + +Publish +Subscribe +``` + +The 8px offset is the documented spacing — wider looks like two unrelated arrows, narrower looks like a single fat arrow. + +**Label placement.** The two labels go in the gap between the agent and the bar, one flush against the left arrow (anchor `end`) and one flush against the right arrow (anchor `start`). If the agent's bottom and the bar's top are closer than 40px, move the labels to the *side* of the pair rather than between them. + +**Color.** Both arrows use `arr` (default gray). If the pattern needs to highlight one specific agent's channel, upgrade that pair to `arr-{ramp}` where `{ramp}` matches the agent's box — but only that one pair, not all of them. + +--- + +## Concept-to-shape conventions + +Before drawing any node, decide what shape it should be. baoyu-diagram is a **flat-rect aesthetic** — almost every concept maps to a labeled rectangle. The table below is the canonical lookup so you don't invent a new icon shape inline and so you don't reach for mermaid/plantuml-style iconography that clashes with the design system. + +| Concept | Use this shape | Notes | +|----------------------------|--------------------------------------------------------|-------------------------------------------------------| +| User / human / actor | Labeled rect with `ts` subtitle "(user)" or similar | No stick figure. In sequence, an actor header rect. | +| LLM / model | Labeled rect, optionally `c-teal` or `c-purple` | No brain icon. Label with the model name. | +| Agent / orchestrator | Labeled rect, often the centerpiece of the diagram | If it's the mechanism's name, use `.title` class for the whole diagram title instead. | +| Tool / function | Labeled rect; or compact tool card with `tool-card-*` icon in phase-band diagrams | No gear icon in standard diagrams. Phase-band exception: see "Tool card icons" section. | +| API / gateway / endpoint | Labeled rect | No hexagon. Label with the route or service name. | +| Memory (short-term) | Rect with dashed border via `class="arr-alt"` inline | Dashed = ephemeral. See `svg-template.md` → `.arr-alt`. | +| Memory (long-term) / DB | Rect with `doc-icon` inside (structural/illustrative) | No cylinder. In flowchart/sequence, label with "(DB)". | +| Vector store | Rect labeled with dimensions ("768d") + doc-icon | No cylinder with grid lines. | +| Queue / buffer | Rect containing `queue-slot-filled` / `-empty` glyphs | See glyphs → "Queue slots". | +| Task list / checklist | Rect containing `checkbox-checked` / `-empty` rows | See glyphs → "Checkboxes". | +| External service | Labeled rect inside a dashed-border container | The dashed container communicates "not ours". | +| Decision point | Rotated square (diamond), flowchart only | See `flowchart.md` → diamond decision nodes. | +| Process / step | Rounded rect (`rx="6"`) | The default shape. Reach for this first. | +| Start / End boundary | Pill (rounded rect with `rx = height/2`) | See `flowchart.md` → "Pill terminal node". ≤2 per diagram. | +| State in state machine | Rounded rect with title | See `flowchart.md` → "State-machine sub-pattern". | +| Initial / final state | Filled 8px circle / 12px hollow circle with inner 8px | State-machine exception to the "rect only" rule. | +| Class (UML) | 3-compartment rect (name / attrs / methods) | See `class.md`. | + +### Rejected shapes — never use these + +These shapes look great in mermaid/plantuml/fireworks-style diagrams but break baoyu's flat-rect aesthetic and its dark-mode color contract. If the diagram seems to demand one, the *label* is wrong — fix the label. + +- **Stick figure** (for users / actors). Use a labeled rect. +- **Brain glyph** (for LLMs). Use a labeled rect with the model name. +- **Gear / cog glyph** (for tools). Use a labeled rect with the tool name. +- **Cylinder** (for databases). Use a rect with `doc-icon` or a "(DB)" suffix in the label. +- **Cloud silhouette** (for external services). Use a dashed-border container. +- **Disk / drum / tape-reel** (for storage). Same as cylinder — use a rect. +- **Briefcase, folder, envelope, magnifying glass** (for everything else). Use a rect. + +The rationale for each: these glyphs all require either hardcoded fills (breaking dark mode), or non-rectangular paths that don't compose with the 44/56 row heights and the `c-{ramp}` stroke conventions, or visual weight that competes with the actual information in the box labels. baoyu's readability comes from consistent typography and color — not from pictograms. + +**When in doubt, ask: "does the shape carry information the label doesn't?"** If the answer is no, it's decoration and you should drop it. If yes, the label is missing the information and you should add it. + +## Glyph checklist before saving + +When you include any glyph in a diagram, verify: + +1. **Class check** — every shape in the glyph uses one of `c-{ramp}`, `arr-{ramp}`, `arr`, `arr-alt`, `box`, or a text class (`t`/`th`/`ts`). No inline `fill="#..."` or `stroke="#..."` on anything that needs dark-mode inversion. +2. **Positioning check** — the glyph is wrapped in `` and the anchor math lands it where the parent layout expects (box center, arrow midpoint, checklist row). +3. **No-collision check** — for status circles and annotation circles, the arrow they sit on has been split into two segments leaving a gap for the glyph, not routed *through* it. +4. **Ramp meaning check** — status colors (`c-green` for pass, `c-coral` for fail, `c-amber` for in-progress) are not overridden by the surrounding diagram's color budget. Status semantics always win over the "≤2 ramps" rule. +5. **Icon scope check** — `doc-icon`, `terminal-icon`, and `script-icon` only appear in illustrative or structural diagrams. A flowchart that wants to show "this is a document" uses the label, not the icon. `tool-card-*` icons only appear inside compact tool cards in phase-band diagrams. + +--- + +## Tool card icons + +Small 24 × 24 icons for use **exclusively inside compact tool card nodes** in phase-band diagrams (see `references/flowchart-phase-bands.md`). Each icon supplements—never replaces—the text label on the card. Without the label the icon has no meaning; without the icon the label still works fine. + +**Scope rule.** These icons are not allowed in any other diagram type. They require the compact tool card node template below — dropping a tool icon directly into a standard 44px flowchart node looks wrong and breaks the icon-to-card sizing contract. + +**Dark-mode rule.** All strokes use `class="arr"` (inherits the template's stroke color). `fill="none"` on any outer shape is safe. No inline hex colors. + +**Bounding box:** 24 × 24 for all icons. Place the icon origin so the glyph center lands at the top-center of the card (see compact tool card template below). + +--- + +### icon-scan + +Concentric target rings — communicates "scanning / finding". + +```svg + + + + + + +``` + +The small tick line at the top-right angle reads as "aim" without being a literal crosshair. + +--- + +### icon-search + +Document face with text lines — communicates "reading / querying" without using a magnifying glass (which is prohibited as a shape replacement for labels in standard diagrams). + +```svg + + + + + + +``` + +--- + +### icon-data + +Three vertical bars of different heights — bar chart shape for "data analysis / metrics". + +```svg + + + + + + +``` + +--- + +### icon-code + +Angle brackets with a slash — `` pattern for "code / script analysis". + +```svg + + + + + +``` + +--- + +### icon-exploit + +Lightning bolt — communicates "attack / execution / injection". + +```svg + + + +``` + +--- + +### icon-callback + +Two arrows in a circle (↩ motif) — communicates "callback / round-trip / webhook". + +```svg + + + + +``` + +--- + +## Compact tool card node template + +**Used only in phase-band diagrams.** A 80 × 80 node that shows a 24 × 24 icon above a two-line text label. The icon provides visual differentiation at a glance; the label provides the actual meaning. + +**Bounding box:** 80 × 80 (or wider if the label is longer — see sizing rule below). + +**Sizing rule:** +``` +card_w = max(line1_chars × 7, line2_chars × 7, 24) + 24 +card_w = round up to nearest 8, minimum 72, maximum 128 +card_h = 80 (fixed) +``` + +For a single-line label (e.g., "Scan"): +``` +card_w = max(4 × 7, 24) + 24 = max(28, 24) + 24 = 52 → round to 72 (minimum) +``` + +For a two-word label split over two lines (e.g., "Search" / "tool"): +``` +card_w = max(6×7, 4×7) + 24 = 42 + 24 = 66 → round to 72 (minimum) +``` + +For a longer label (e.g., "Code" / "analysis"): +``` +card_w = max(4×7, 8×7) + 24 = 56 + 24 = 80 → round to 80 +``` + +**Template (two-line label, 80-wide card):** + +```svg + + + + + + + + Scan + tool + +``` + +For a single-line label (when the full name fits in ≤10 chars): +```svg + Scanner +``` + +**Color rules.** +- Use `c-gray` for all tool cards by default. Color encodes meaning, not tool identity — don't give each tool a different ramp. +- If **one specific tool** is the subject of the diagram (e.g., the exploitation tool that's being analyzed), promote just that one card to a semantic ramp (`c-coral` for attack tools, `c-amber` for analysis tools). All other cards stay `c-gray`. + +**Row packing.** Align cards in a horizontal row inside the phase band's main flow zone. The gap between cards is **8px minimum**: + +``` +row_width = N × card_w + (N−1) × 8 +required: row_width ≤ 380 (main flow zone width inside a band) +``` + +| Cards | card_w | row_width | Fits? | +|-------|--------|-----------|-------| +| 4 | 80 | 352 | ✓ | +| 5 | 72 | 388 | ✗ — use card_w=72 with 6px gap: 5×72+4×6=384 ✓ | +| 6 | 64 | 408 | ✗ — split into two rows of 3 | + +To center a row of N cards: +``` +row_start_x = band_x + band_padding + (main_flow_w − row_width) / 2 +card[i].x = row_start_x + i × (card_w + gap) +``` + +--- + +## Operator icons + +Larger icons (32 × 40) for the **left operator column** of a phase-band diagram — the human or AI actor that initiates the operation. These appear once per diagram, outside (to the left of) the phase band containers. + +--- + +### operator-human + +A simplified head + shoulders silhouette. 32 × 40. + +```svg + + + + +``` + +Place the icon's origin at `(operator_x, operator_y)`. The icon's visual center is at `(16, 24)`. Add a `ts` label below: `y = operator_y + 44`. + +--- + +### operator-ai + +An asterisk-in-circle — six spokes radiating from center, with an outer ring. 32 × 32. + +```svg + + + + + + + +``` + +For a six-spoke variant (closer to the Anthropic asterisk): +```svg + + + + + + +``` + +--- + +**Placement in a phase-band diagram:** + +Both operator icons sit in the left margin at `x=20`, vertically positioned to align with the band they feed into (usually Phase 1): + +``` +operator-human: x=20, y=phase1_band_y + (band_h / 2) − 20 − 44 − 16 +operator-ai: x=20, y=phase1_band_y + (band_h / 2) − 16 + 16 + +label under each: ts at (36, icon_y + icon_h + 8), text-anchor="middle" +``` + +The stacked human → ai pair is typical (human provides the target; ai runs the operation). Connect them with a short `arr` line, then connect the ai icon to the band's first card with an arrow entering the left edge of the band. + +If any check fails, fix the SVG before saving. See `pitfalls.md` entries #19–#28 for the full list of glyph-related failure modes and their fixes. diff --git a/skills/baoyu-diagram/references/illustrative.md b/skills/baoyu-diagram/references/illustrative.md new file mode 100644 index 0000000..2b86926 --- /dev/null +++ b/skills/baoyu-diagram/references/illustrative.md @@ -0,0 +1,492 @@ +# Illustrative Diagram + +For building *intuition* — the kind of diagram that makes a reader go "oh, *that's* what it's doing". Unlike flowcharts (which document steps) or structural diagrams (which document containment), an illustrative diagram draws the mechanism. The shape of the drawing *is* the explanation. + +## When to use + +This is the default for *"how does X actually work"* questions with no further qualification. Reach for it whenever the breakthrough moment is visual rather than procedural. + +- **Physical subjects** — water heaters, engines, lungs, heart valves, batteries, transistors. Draw simplified cross-sections or cutaways. +- **Abstract subjects** — transformer attention, gradient descent, hash tables, the call stack, embeddings, recursion. Invent a spatial metaphor that makes the mechanism obvious. + +Trigger phrases: "how does X work", "explain X", "I don't get X", "give me an intuition for X". + +## When NOT to use + +- The reader wants a reference, not an intuition. "What are the components of a transformer" is a structural diagram (labeled boxes). "How does attention work" is illustrative (fan of weighted lines). +- The metaphor would be arbitrary rather than revealing. Drawing "the cloud" as a cloud shape teaches nothing about how distributed computing works — skip the illustration and use a structural diagram instead. +- The mechanism is actually a sequence of discrete steps. That's a flowchart. + +## Core principle + +**Draw the mechanism, not a diagram *about* the mechanism.** Spatial arrangement carries the meaning; labels annotate. A good illustrative diagram still reads clearly even if you remove all the text. + +- Color encodes intensity. Warm ramps (amber, coral, red) for heat/energy/pressure/activity. Cool ramps (blue, teal) for cold/calm/dormant. Gray for inert structure. A reader should glance at the drawing and immediately see *where the action is* without reading a label. +- Layout follows the subject's geometry. Tall narrow subject → tall narrow drawing. Wide flat subject → wide flat drawing. Let the thing dictate proportions within the 680px viewBox. +- Layering is encouraged for shapes. Unlike flowcharts where boxes never overlap, illustrative diagrams use z-ordering deliberately: a pipe entering a tank goes *behind* the tank wall, a flame goes *under* the kettle. Later in source = on top. + +## Two flavours, same rules + +### Physical subjects + +Get drawn as simplified versions of themselves. Cross-sections, cutaways, schematics. + +- A water heater is a tall rounded rect with a burner underneath. Not a Bézier portrait of a water heater. +- A flame is three triangles, not a fire. +- A lung is a branching tree in an oval-ish cavity. Not a medical illustration. +- A transistor is three terminals meeting at a junction. Not a 3D render. + +**Fidelity ceiling**: if a `` needs more than ~6 segments, simplify it. Recognizable silhouette beats accurate contour. + +### Abstract subjects + +Get drawn as spatial metaphors that make the mechanism obvious. + +- **Attention in a transformer** — a row of tokens with weight-scaled lines fanning from one query token to every other token. Line thickness = attention weight. +- **Gradient descent** — a contour surface with a dot rolling downhill, trailing a path of discrete steps. +- **Hash table** — a funnel dropping items into a row of labeled buckets. +- **Call stack** — a vertical stack of frames growing upward with each push. +- **Embeddings** — a 2D scatter of labeled dots, clusters visible by position. +- **Convolution** — a small kernel sliding across a grid, highlighting the current receptive field. + +The metaphor *is* the explanation. Don't label the metaphor — if you have to write "this represents attention", the drawing isn't doing its job. + +## What changes from the flowchart/structural rules + +- **Shapes are freeform.** Use ``, ``, ``, ``, curved lines. You're not limited to rounded rects. +- **Color encodes intensity, not category.** Warm = active/high-weight/attended-to, cool or gray = dormant/low-weight/ignored. The reader should see where the energy is without reading a label. +- **Layering and overlap are allowed — for shapes.** Draw a pipe entering the tank body. Draw insulation wrapping a chamber. Use z-order deliberately. +- **Text is still the exception — never let a stroke cross a label.** The overlap permission is for shapes only. Every label needs 8px of clear air between its baseline and the nearest stroke. If there's no quiet region for a label, the drawing is too dense — remove something or split into two diagrams. +- **Small shape-based indicators are allowed** when they tell the reader about physical state. Triangles for flames. Circles for bubbles. Wavy lines for steam or radiation. Keep them simple — basic SVG primitives, not detailed illustrations. +- **Lines stop at component edges.** When a stroke meets a component (a pipe entering a tank, a wire into a terminal, an arrow into a region), draw the line as segments that *end at the component's boundary* — don't draw straight through the component and rely on the fill to hide the overdraw. The host page's background color is not guaranteed (WeChat dark mode, Notion, markdown viewers), so any fill-based occlusion becomes a coupling between stroke color and background color. Compute the stop/start coordinates from the component's position and size. This matters more for illustrative diagrams than for flowcharts because curved and irregular edges make the overdraw more visible. +- **One gradient is permitted** — and only one. This is the single exception to the flat-fills rule. Use it to show a *continuous* physical property (temperature stratification in a tank, pressure drop along a pipe) with a `` between exactly two stops from the same ramp. No radial gradients, no multi-stop fades, no decoration gradients. If two stacked flat-fill rects communicate the same thing, do that instead. + +## Label placement + +At 680px width you don't have room for a drawing *and* label columns on both sides. Pick one side and put all the labels there. + +**Default to right-side labels** with `text-anchor="start"`. Labels on the left with `text-anchor="end"` are the ones that clip — the text extends leftward from x and long labels blow past x=0 without warning. + +Reserve **at least 140px** of horizontal margin on the label side. Your drawing sits in roughly x=40 to x=500, labels sit in x=510 to x=640. + +For each label, draw a dashed leader line from the referenced feature on the drawing to the label: + +```svg + + + + Hot zone + +``` + +The small circle at the anchor end makes the leader read as an intentional pointer rather than a stray stroke. + +**Large internal zones** (big enough to hold text without crowding) can have labels sitting inside them, with at least 20px of clear air from any edge. + +## Composition order + +Write the SVG in this layering order so z-index comes out right: + +1. **Silhouette first.** The largest shape, centered in the viewBox. This defines the subject's footprint. +2. **Internal structure.** Chambers, pipes, membranes, mechanical parts. Drawn inside the silhouette. +3. **State indicators.** Color fills showing temperature/pressure/concentration, the one optional gradient. Applied after shapes so they tint on top of the structure. +4. **External connections.** Pipes entering or exiting, arrows showing flow direction. +5. **Labels and leader lines.** Last, so they sit on top of everything else. + +Don't try to optimize this order — just follow it every time. Z-ordering bugs are hard to debug after the fact. + +## Tiny worked example — attention fan + +Request: "how does attention work in a transformer" + +Plan: +- Spatial metaphor: a row of 5 tokens at the bottom, one highlighted (the query), with 5 lines fanning up to a layer above. Line thickness encodes weight — thickest line to "sat", mid lines to nearby tokens, thin lines to distant ones. +- Colors: gray for dormant tokens, amber for the query token. Fan lines hardcoded amber `#EF9F27` with varying stroke-width and opacity. +- One caption below the fan: "Line thickness = attention weight". + +## Central subject + radial attachments + +For **"how does X interact with its environment"** diagrams where a central subject connects outward to a small set of optional capabilities — an LLM with retrieval, tools, and memory; a browser with DOM, storage, and network; a GPU with shared memory, registers, and global memory. The layout is a hub-and-spoke: one subject in the middle, 2–4 attachments arranged around it, and dashed connectors showing that each attachment is an *available capability*, not a mandatory step. + +**Why illustrative and not structural**: the reader's question is "how does the subject use its environment" — that's intuition about a mechanism, not a documentation of what lives inside what. A structural diagram of "LLM + retrieval + tools + memory" would read like a block diagram; the radial layout instead reads like "this thing reaches out to these things when it needs to". + +### Anatomy + +``` + [ In pill ] → [ LLM Call ] → [ Out pill ] + | + ┌──────┼──────┐ ← three dashed spokes dropping + ↓ ↓ ↓ from the subject center + [Retrieval] [Tools] [Memory] +``` + +- A **horizontal mainline** across the vertical center of the diagram carries the primary flow. Both ends are pill-shaped terminals. +- The **central subject** is the node in the middle of the mainline — a rounded rect (not a pill), typically using a color ramp (`c-purple` is conventional for "the model"). +- **2–4 attachments** sit below the subject as rounded rects, spaced horizontally. +- **Dashed `.arr-alt` connectors** drop from the subject's bottom edge to each attachment's top edge. These are dashed to communicate "optional capability, used when needed" — the mainline is what always runs, the spokes are what the subject reaches for. +- **Short 1–2 word labels** beside each spoke (`Query` / `Call` / `Read`) communicate the relationship. See the `.arr-alt` notes in `svg-template.md`. + +### Layout + +Standard geometry at viewBox width 680: + +``` +Mainline y = 160 # rect top; cy = 182 (rect height 44) +In pill x=60, w=80 # right edge 140 +Subject box x=290, w=180 # center (380, 182), bottom edge 204 +Out pill x=560, w=80 # center (600, 182), right edge 640 ✓ + +Attachment row y = 320 # 116px below subject cy; bottom 364 +Attachment w = 120 # fits "Retrieval" (9 × 8 + 24 = 96) +Attachment 1 x=120, center (180, 342) +Attachment 2 x=320, center (380, 342) # directly below subject +Attachment 3 x=520, center (580, 342) # right edge 640 ✓ + +Spoke channel ymid = 290 # 86px below subject bottom, 30px above att row + +Spoke 1 (L): path (380, 204) → (380, 290) → (180, 290) → (180, 320) +Spoke 2 (M): straight (380, 204) → (380, 320) +Spoke 3 (R): path (380, 204) → (380, 290) → (580, 290) → (580, 320) +``` + +The center spoke is a straight vertical line; the outer spokes are L-bends that drop to a shared horizontal channel at `y = 290` before turning down into the attachments. Don't route the outer spokes as diagonals — diagonal lines in an otherwise orthogonal diagram read as stray strokes. + +Mainline arrow endpoints: `In→Subject` runs from `(140, 182)` to `(290, 182)`; `Subject→Out` runs from `(470, 182)` to `(560, 182)`. Both use solid `.arr`. + +### Rules + +- **At most 4 attachments.** Five overflows the horizontal budget at 140px each. If you need 5+, split into two diagrams (subject + capability group A, subject + capability group B) or step the pattern up to a structural diagram. +- **Dashed spokes only when the attachments are optional.** If every spoke always runs, the pattern isn't radial — it's a fan-out flowchart and should use solid `.arr` connectors in the simple fan-out pattern from `flowchart.md`. +- **Spoke labels stay short.** 1–2 words, `ts`, positioned beside the spoke midpoint with `text-anchor="end"` for labels on the left of a spoke and `"start"` for labels on the right. Bidirectional relationships draw two labels on opposite sides of the spoke ("Query" above, "Results" below — same spoke). +- **No rotated text.** Design-system.md's rotation ban applies here; radial layouts do *not* unlock rotated labels. +- **No literal icons.** A radial hub describing "LLM + tools + memory" uses labeled rounded rects for each capability, not tool wrench glyphs or memory-chip drawings. +- **Central subject stays a rect, not a circle.** Circles read as "state" or "particle"; this pattern is about a subject reaching for capabilities, and a rounded rect labels cleanly at 14px. + +### Worked example — LLM hub + +Request: "how an LLM uses retrieval, tools, and memory" + +Plan: +- Mainline `In` → `LLM Call` → `Out` along the horizontal center; rects at y=160, cy=182 +- `LLM Call` uses `c-purple` to mark it as the central subject +- 3 attachments below at y=320: `Retrieval` / `Tools` / `Memory`, all gray, w=120 each +- Three dashed `.arr-alt` spokes — the middle spoke straight down, the outer two as L-bends through a shared channel at y=290 +- One short label beside each spoke (`Query` / `Call` / `Read`) picking the most representative verb; the bidirectional return path is left implicit so the diagram stays quiet +- viewBox: last element `Retrieval` bottom = 320 + 44 = 364 → H = **384** + +```svg + + + + + + In + + + + LLM Call + + + + Out + + + + + + + Retrieval + + + + Tools + + + + Memory + + + + + + + Query + Call + Read + +``` + +Notes on this example: +- The central subject `LLM Call` uses `c-purple` while attachments stay gray — this is the single-accent rule from the design system, where the accent ramp anchors reader attention on "the thing doing the reaching". +- All three spokes use `class="arr-alt"` because retrieval, tool-calls, and memory reads are **optional** — an LLM invocation might use all three, some, or none. If the diagram were describing a specific pipeline where all three always run, the spokes would be solid `.arr`. +- **One short verb per spoke**, not a bidirectional pair. Two stacked labels per spoke would crowd the horizontal channel at y=290; picking the representative verb (Query, Call, Read) is quieter and the reader infers the return path. If the return direction matters, split into two diagrams rather than stacking labels. +- Each label sits on the horizontal channel (y=284, 6px above y=290) directly over the L-bend corner so it reads as belonging to that spoke. The middle spoke's label sits 32px above to avoid colliding with the other labels. +- Spoke 2 drops straight down from the subject's bottom edge to the middle attachment's top edge because they're vertically aligned — no need for a bend. The arrowhead lands at y=318 (2px short of the attachment's top at y=320) to leave the 10px-ish breathing room flowchart.md recommends. +- Rightmost rect edges: Out pill ends at `560 + 80 = 640` ✓, Memory ends at `520 + 120 = 640` ✓ — both exactly flush with the 640 bound. + +## Spectrum / continuum + +For topics where the reader's question is *"where on the scale between X and Y does Z sit"* — Anthropic's "Finding the sweet spot" diagram (image #5) is the canonical example. The layout is a single horizontal axis with opposing concepts at either end, tick points along it, option boxes hanging below each tick, and optional italic captions under the options. + +This is a **new illustrative sub-pattern**, distinct from the radial-attachments layout above. Use it when the intuition the reader needs is "these options are *positions* on a single continuum, and one of them is better than the others because of where it sits on the continuum". + +### When to use + +- "Finding the sweet spot between A and B" +- "The trade-off between flexibility and rigidity / latency and quality / cost and performance" +- "Where does approach X sit on the X-to-Y spectrum" +- Any topic where the reader has been thinking in a binary and you're showing them it's a gradient + +### When not to use + +- The options are unordered categories (use a structural diagram with sibling boxes instead). +- The trade-off has more than one dimension (spectrum only works for 1-D). +- There's no "wrong" end to the axis — the spectrum works because both extremes are undesirable and there's a sweet spot in between. If one end is just "better", use a chart, not this pattern. + +### Geometry + +See also `layout-math.md` → "Spectrum geometry". + +| Element | Coordinates | +|--------------------|--------------------------------------------------| +| Eyebrow (optional) | `(340, 50)`, class `eyebrow`, `text-anchor="middle"` | +| Axis line | `x1=80 y1=140 x2=600 y2=140`, class `arr`, markers on **both** ends | +| Left-end label | `(80, 120)`, class `ts`, `text-anchor="start"`, uppercase content | +| Right-end label | `(600, 120)`, class `ts`, `text-anchor="end"`, uppercase content | +| Tick points (N) | circles at `(tick_x, 140)` `r=6` | +| Option boxes | `y=200 h=60 w=120 rx=6`, one per tick point | +| Captions (optional)| `y=292 and y=308`, italic `ts`, `text-anchor="middle"` | + +For **3 ticks**, tick_x = `160, 340, 520`; option box x = `100, 280, 460` (each 120 wide, centered on tick_x). +For **4 ticks**, tick_x = `140, 280, 420, 560`; option box x = `80, 220, 360, 500` (each 120 wide). +For **5 ticks**, tick_x = `120, 240, 360, 480, 600`; option box width shrinks to 100 (x = `70, 190, 310, 430, 550`). + +### The axis + +The axis is a single horizontal line with arrow markers at both ends (so it reads "this extends both ways, no natural direction"). Unlike regular arrows which use `marker-end`, the spectrum axis uses **both** `marker-start` and `marker-end`: + +```svg + +``` + +The `context-stroke` marker in the template automatically colors both arrowheads to match the line. + +**Do not** add a color ramp to the axis. It's always `arr` (neutral gray) — colored axes imply that one end is "good" and the other "bad", which defeats the entire point of a spectrum diagram. + +### End labels + +The labels at the two ends describe the **extremes** the axis is measuring between. Keep them to 2–3 words, usually in all caps or title case to signal they're axis labels, not box labels. In image #5 these are "NO STRUCTURE" and "TOO RIGID". + +Place them *above* the axis with at least 8px of clearance between the end of the axis arrowhead and the label's text baseline. Left label uses `text-anchor="start"` anchored at the axis x1; right label uses `text-anchor="end"` anchored at the axis x2. No leader lines — the proximity to the axis end is enough. + +### Tick points and option boxes + +Each tick is a small circle (r=6) sitting on the axis line. Tick circles use `c-gray` by default (subtle dots) — the sweet-spot tick upgrades to an accent ramp (`c-green` or `c-amber`) to draw the eye. + +Each tick has a **matching option box** directly below it. The option box is a two-line node (60 tall, title + 1-word id) centered on the tick's x. The option's id (left subtitle like "A", "B", "C") is optional and mostly for referring back in the caption. + +The sweet-spot option's box takes the same accent ramp as its tick — green/amber highlights "this is the one". All other option boxes stay neutral gray (`c-gray`). + +### Italic per-option captions + +Below each option box, an optional 1–2 line italic `ts` caption explains *what that option feels like*. Keep each line ≤24 characters (≈3 words) so the captions don't spill into each other horizontally. Captions are always italic (use inline `font-style="italic"`), matching the `caption` class from poster flowcharts but placed under each option individually. + +```svg +vague and +untestable +``` + +If the captions need more than 2 lines each, the spectrum is the wrong pattern — the options need their own diagram. + +### Eyebrow + +A spectrum diagram can carry a small `eyebrow`-class label at the very top (y=50) to name the trade-off: "CHOOSING AN EVALUATION APPROACH". This is optional but strongly recommended — without an eyebrow the reader has to infer "what is this spectrum *about*" from the end labels alone, and end labels are short by design. + +### Worked example — sweet spot + +Request: "Show the trade-off between an evaluation that's too loose and one that's too rigid." + +Plan: +- Eyebrow: "CHOOSING AN EVALUATION" +- Axis: "NO STRUCTURE" on the left, "TOO RIGID" on the right +- 3 tick points with option boxes: "A · none", "B · rubric", "C · checklist" +- "B · rubric" is the sweet spot → `c-green` +- Italic captions under each: "vague and / untestable", "structured / and flexible", "brittle, / doesn't generalize" +- viewBox H ≈ 340 + +```svg +Choosing an evaluation + +NO STRUCTURE +TOO RIGID + + + + + + + + + None + A + + + + Rubric + B + + + + Checklist + C + + +vague and +untestable +structured +and flexible +brittle, doesn't +generalize +``` + +viewBox: max_y = 308 + 4 = 312 → H = **332**. + +### Pitfalls + +- **Axis end labels clipped**: if `text-anchor="end"` is used on the left label or `text-anchor="start"` on the right, labels extend past the axis ends and read backwards. Left is `start`, right is `end`. +- **Sweet spot not the right color**: using `c-coral` for the sweet spot because "it's the attention color" is backwards — `c-coral` reads as "warning", `c-green` reads as "good", `c-amber` reads as "deliberate". Match the ramp to the *outcome*, not just the emphasis. +- **Tick count ≠ option count**: every tick needs an option box and vice versa. An orphan tick or an orphan option is a layout bug. +- **Captions too long**: 2 italic lines at ≤24 chars each. Longer captions collide. + +## Annotation circle on connector + +For diagrams where a connector (arrow between two subjects) has a **named mediator** that deserves to be visible as a node itself. Image #13 (Claude ↔ Environment with Skill) is the canonical case: Claude talks to its environment, and the "Skill" that mediates the exchange is too important to be a mere label on the arrow — it becomes a labeled circle sitting on the connector. + +**When to use.** Only in illustrative diagrams. Flowcharts and sequence diagrams label their arrows with text; structural diagrams use arrow labels or inline text. The annotation-circle-on-connector is an *illustrative* convention specifically — it promotes "the thing on the arrow" to a visual node without breaking the two-subject layout. + +**When not to use.** + +- The mediator is actually a step in a sequence — use a flowchart node. +- The mediator is a system component — use a structural box. +- There are two or more mediators on the same connector — the pattern doesn't stack; restructure the diagram. + +### Geometry + +See `glyphs.md` → "Annotation circle on connector" for the full glyph. The placement rule: + +| Element | Coordinates | +|------------------------|--------------------------------------------| +| Subject A | e.g., `x=80 y=160 w=180 h=56` | +| Subject B | e.g., `x=440 y=160 w=180 h=56` | +| Bidirectional arrows | A↔B, offset by 10px vertically | +| Annotation circle | `cx = (A_right + B_left) / 2`, `cy = arrows_y_midpoint` | +| Short connector line | `x1 = cx, y1 = cy, x2 = cx, y2 = cy + 40`, class `arr` (no arrowhead) | + +The annotation circle sits **above** the pair of bidirectional arrows, with a short vertical connector line (no arrowhead) dropping from the bottom of the circle to the midpoint of the arrow pair. This reads as "this circle is a flag attached to the arrow below it". + +### Arrow routing + +The two-way exchange between Subject A and Subject B uses a pair of offset horizontal arrows: + +```svg + + +Action + + +Feedback +``` + +The arrow-pair y midpoint is `(184 + 204) / 2 = 194`. The annotation circle's connector line ends at `(350, 194)` (the midpoint). The circle itself sits above, centered at `(350, 150)` with r=30. + +### The annotation circle itself + +```svg + + + + + Skill + + + +``` + +The outer circle uses the neutral `box` class (so the ring itself doesn't fight for attention), and the inner pill uses a ramp (usually `c-teal` — the mediator is an aid, not an adversary). + +### Label length + +The inner pill caps at ~6 Latin characters. "Skill" fits. "Constraint" does not — shorten or restructure. For CJK, cap at 3 characters. + +### Multiple circles on one arrow + +**Not allowed.** If two mediators sit on the same connector, you're describing a sequence, not a mediator — use a flowchart instead. + +## Decorative icon inside box + +The **only** place in the design system where an icon is allowed inside a rect is inside an illustrative diagram's subject box or a structural diagram's hub/shared-state box. The rule is strict: the icon must come from `glyphs.md`'s icon set, and it lives in the lower half of a box that's at least 80px tall. + +### Why only here + +Flowcharts and sequence diagrams use the label alone — adding an icon to a flowchart box just adds visual noise to a process the reader is tracking sequentially. In an illustrative diagram, the icon reinforces *what kind of thing the box represents* (a document store, a terminal, a code script), and that reinforcement is part of the intuition the diagram is trying to build. + +### Allowed icon set + +Only these, from `glyphs.md`: + +- `doc-icon` — documents, artifacts, shared files, logs +- `terminal-icon` — terminals, computers, shells, CLI environments +- `script-icon` — inline scripts, programmatic tool calls (used in structural "Parallel independent rounds" pattern; rarely in illustrative) + +**No other icons.** Do not draw an LLM brain, a wrench for "tools", a cloud for "cloud", a gear for "settings". Label the rect. + +### Placement + +For a box at `(box_x, box_y, box_w, box_h)`: + +- Title `th` at `(box_x + box_w/2, box_y + 22)`. +- Subtitle `ts` at `(box_x + box_w/2, box_y + 40)` (optional). +- Icon anchor at `(box_x + box_w/2 − 12, box_y + box_h − 36)` (for a 24-wide, 28-tall icon). + +The icon must have at least **8px of clear air** above it (between the subtitle baseline + descender and the icon's top). If the box is too short to fit both text and icon, grow the box — don't squeeze them. + +### Color + +The icon uses `arr` or `box` classes (neutral stroke), **not** a ramp. The parent box carries the ramp color — the icon is a secondary visual cue that inherits the reader's attention from the box, not from its own color. + +```svg + + + Shared state + Task artifacts + + + + + + + + +``` + +### Multiple icons in one diagram + +One icon per box is the cap. Two in the same diagram is fine if they're in different boxes and each serves its own intuition. Three or more icons in one diagram starts to feel like a screenshot — simplify. + +## Rules of thumb + +- **Silhouette > outline.** A simple rounded rect labeled "Reactor" beats a detailed reactor drawing. The reader needs to recognize *what kind of thing* it is, not admire your drafting. +- **Color on what matters.** If the hot zone is the point, color only the hot zone. Leave the rest of the drawing in neutral stroke lines. Color where the meaning is. +- **One gradient, deliberately.** If you reach for a gradient, ask: "is this showing a continuous physical property?" If yes, use it. If no, use flat fills. +- **Labels in the quiet zone.** Before placing a label, check that no stroke will cross its bounding box. If there's no quiet zone, remove something from the drawing. +- **One idea per diagram.** If you catch yourself cramming two mechanisms into one drawing, split them. Two 10-element diagrams beat one 20-element diagram every time. + +## Common failure modes specific to illustrative + +- **Too literal** — tracing a real photo of the subject. Six Bézier curves, not sixty. Stylize. +- **Label crossed by stroke** — text sits where a pipe or arrow also passes. Move the label to the margin with a leader line; never use a background rect to hide the stroke. +- **Gradient as decoration** — using a gradient because it "looks nice". Remove it. Gradients are only for continuous physical properties. +- **Color by category instead of intensity** — dormant and active nodes using different ramps for variety. Use gray for dormant, one accent for active. Let the color tell the story. +- **Both-sides labels** — drawing has labels on the left *and* the right. Pick one side. At 680px wide, you don't have room for both. diff --git a/skills/baoyu-diagram/references/layout-math.md b/skills/baoyu-diagram/references/layout-math.md new file mode 100644 index 0000000..c443fd6 --- /dev/null +++ b/skills/baoyu-diagram/references/layout-math.md @@ -0,0 +1,525 @@ +# Layout Math + +SVG has no auto-layout. Every coordinate, width, and height is hand-computed. Most diagram failures trace back to text overflowing its container or elements landing outside the viewBox. This file is the arithmetic you run *before* writing any ``. + +## The coordinate system + +- **viewBox**: `0 0 680 H`. Width is always 680. Compute H from content. +- **Safe area**: `x ∈ [40, 640]`, `y ∈ [40, H - 40]`. Leave 40px of breathing room on every edge. +- **Usable width**: 600 px (640 − 40). +- **Pixel units are 1:1.** The `width="100%"` with `viewBox="0 0 680 H"` means the browser scales the whole coordinate space to fit the container. A 14px font is really 14px. Character widths below assume this 1:1 ratio. + +## Text width estimation + +SVG `` never auto-wraps. If you put more characters in a box than it can hold, they overflow visibly. Use these character-to-pixel multipliers, calibrated on Anthropic Sans: + +| Case | Approx. width per character | +|------------------------------------------|-----------------------------| +| 14px latin, weight 500 (`th`, titles) | ~8 px | +| 14px latin, weight 400 (`t`, body) | ~8 px | +| 12px latin, weight 400 (`ts`, subtitles) | ~7 px | +| 14px CJK (Chinese, Japanese, Korean) | ~15 px (~2× Latin) | +| 12px CJK | ~13 px (~1.9× Latin) | + +**Special characters:** chemical formulas (C₆H₁₂O₆), math symbols (∑ ∫ √), subscripts, superscripts all take the same horizontal space as normal characters — don't assume subscripts are narrower. For labels that mix Latin and formulas, add 30–50% padding to your width estimate. + +### Empirical calibration samples + +Real measurements of Anthropic Sans at the sizes this skill uses. The multipliers above are the safe ceilings derived from these samples — not the averages. Letter composition matters: narrow letters like `l`/`i`/`f` push the per-char average down, tall letters like `B`/`J`/`P`/`Q` push it up. + +| Text | Chars | Weight | Size | Measured width | Per-char | +|-----------------------------------------|-------|--------|-------|----------------|----------| +| `Authentication Service` | 22 | 500 | 14 px | 167 px | ~7.6 | +| `Background Job Processor` | 24 | 500 | 14 px | 201 px | ~8.4 | +| `Detects and validates incoming tokens` | 37 | 400 | 14 px | 279 px | ~7.5 | +| `forwards request to` | 19 | 400 | 12 px | 123 px | ~6.5 | +| `データベースサーバー接続` | 12 | 400 | 14 px | 181 px | ~15.1 | + +The formula uses **8** for 14px Latin and **7** for 12px Latin because those are the safe ceilings, not the average. A label like "Background Job Processor" at 8.4 px/char would overflow a box sized from a 7.6 px/char average — always round up, never down. + +**Worked sanity check**: `"Detects and validates incoming tokens"` (37 chars, `ts` class at 12px) → formula gives `37 × 7 + 24 = 283 px`. Measured 279 px + 24 px padding = 303 px. The formula is ~7% conservative, which is the correct direction for rect sizing — bias toward extra padding, never toward clipping. + +### Rect sizing formula + +For a two-line node (title + subtitle) the width must fit whichever line is longer: + +``` +width_needed = max(title_chars × 8, subtitle_chars × 7) + 24 +``` + +The `+ 24` is 12px of horizontal padding on each side. If the text is centered, that's 12px of clear air between the text edge and the rect stroke — looks balanced. + +For a single-line node: + +``` +width_needed = title_chars × 8 + 24 +``` + +For CJK content, replace the 8 with 15 and the 7 with 13. + +**Round up to the nearest 10px** for cleaner coordinates. A box that "needs" 167px becomes 170px. + +### Worked examples + +- `"JWT authentication"` (18 chars, title) → `18 × 8 + 24 = 168` → round to **170** +- `"Validates user credentials"` (26 chars, subtitle) in a two-line box with title `"Login service"` (13 chars, 8-wide) → `max(13 × 8, 26 × 7) + 24 = max(104, 182) + 24 = 206` → round to **210** +- `"微服务架构"` (5 CJK chars, title) → `5 × 15 + 24 = 99` → round to **100** +- `"发送 HTTP 请求 forwards request"` (mixed, treat CJK chars as 15px and Latin as 8px) → `3×15 + 4×8 + 13×8 = 45 + 32 + 104 = 181 + spaces + 24 ≈ 220` + +**Rule of thumb**: if you find yourself adding characters to a label, recheck the width. It's easy to write "Authentication" once, size the box to fit, then expand it to "Authentication service" and forget to resize. + +## viewBox height calculation + +After placing every element, compute max_y: + +1. For every ``, compute `y + height`. +2. For every ``, the bottom is `y + 4` (roughly — 4px descent below the baseline). +3. For every ``, it's `cy + r`. +4. `max_y` is the maximum across all of these. +5. `H = max_y + 20` + +**Don't guess.** A 20px buffer below the last element keeps things tight without clipping descenders. + +**Don't leave huge empty space at the bottom.** If the diagram ends at y=280, H should be 300 — not 560. Excess whitespace below the content feels like a rendering bug. + +## Tier packing (horizontal rows) + +When placing N boxes in a horizontal row, check total width before you start writing coordinates: + +``` +total = N × box_width + (N - 1) × gap +``` + +- `box_width`: chosen to fit the longest label in that row +- `gap`: 20px minimum between adjacent boxes + +Required: `total ≤ 600` (the usable width). If not, one of three things must give: + +1. **Shrink box_width** — cut subtitles, shorten titles +2. **Wrap to 2 rows** +3. **Split into an overview diagram + detail diagrams** (one per sub-flow) + +### Worked example — four consumer boxes + +- 4 boxes, each containing `"Consumer N"` (10 chars title) and `"Processes events"` (16 chars subtitle) +- Needed width per box: `max(10×8, 16×7) + 24 = max(80, 112) + 24 = 136` → round to **140** +- 4 boxes × 140 + 3 gaps × 20 = 560 + 60 = **620** → **overflows** by 20 +- Fix: shrink to **130** each (check: `max(10×8, 16×7) + 24 = 136`, so 130 is tight — cut subtitle to 14 chars or drop it entirely) +- Or: drop to 3 boxes per row, stack the fourth below +- Or: 4 × 130 + 3 × 20 = 580, centered at `x = 40 + (600-580)/2 = 50` → boxes at x = 50, 200, 350, 500 + +## Swim-lane (sequence) layout + +Sequence diagrams have their own coordinate system — actor columns ("lanes") along the top, dashed lifelines running down, numbered horizontal messages between them. The numbers below are fixed conventions so every sequence diagram in the skill feels consistent. + +### Lane widths and centers + +N = number of actors. Lane width `lane_w`, header rect width `header_w = lane_w − 20`, lifeline x = lane center. Gap between lanes = 20. + +| N | lane_w | header_w | First offset | Lane centers (x) | +|---|--------|----------|--------------|--------------------------------| +| 2 | 290 | 270 | 40 | 185, 495 | +| 3 | 186 | 166 | 40 | 133, 340, 547 | +| 4 | 140 | 120 | 30 | 100, 260, 420, 580 | +| 5 | 112 | 92 | 26 | 82, 214, 346, 478, 610 | +| 6 | 100 | 80 | 30 | 80, 190, 300, 410, 520, 630 | + +Hard cap: **4 actors** (comfortable), **6 actors** (maximum). 5 is allowed only when every actor title is ≤12 characters and has no subtitle. 6 requires every title ≤9 characters (`9 × 8 + 24 = 96`, just inside `header_w=100`) and no subtitle. N=6 also collapses the intra-lane gap (the 20px padding between `lane_w` and `header_w` becomes 0 — the headers pack shoulder-to-shoulder) and borrows 10px of left-edge margin and all of the right-edge margin: the rightmost header rect sits flush against x=680. Use N=6 only when every actor is genuinely essential — otherwise merge actors or split the diagram. More than 6 doesn't fit 680px legibly. + +### Vertical geometry (fixed for all sequence diagrams) + +``` +actor header box y = 40..88 (height 48, rx 8) +title (th) y = 60 (dominant-baseline="central") +role subtitle (ts) y = 78 (optional, one line, ≤16 chars) +lifeline start y = 92 +first message row arrow_y[0] = 120 +message row pitch 44 px → arrow_y[k] = 120 + 44 · k +lifeline tail y = last_arrow_y + 24 +``` + +### viewBox height + +``` +header_bottom = 88 +first_arrow_y = 120 +last_arrow_y = 120 + 44 × (M − 1) # M = number of messages +lifeline_bottom = last_arrow_y + 24 +note_bottom = lifeline_bottom + 16 + 48 # only if side note is at the bottom +H = max(lifeline_bottom, note_bottom) + 20 +``` + +### Message arrows + +Each message is a horizontal `` at `y = arrow_y[k]`, running from the sender lifeline x to the receiver lifeline x with a 6px offset so the arrowhead doesn't touch the dashes: + +- Left-to-right: `x1 = sender_x + 6`, `x2 = receiver_x − 6` +- Right-to-left: `x1 = sender_x − 6`, `x2 = receiver_x + 6` + +Use `class="arr-{ramp}"` where `{ramp}` matches the sender's actor ramp. Always set `marker-end="url(#arrow)"`. + +### Message labels + +Label sits **above** the arrow at `y = arrow_y − 10` with `text-anchor="middle"`, `class="ts"`, and the number prefix baked into the text (`"1. Click login"`, not separate tspans). Label x is the midpoint of sender and receiver: `(sender_x + receiver_x) / 2`. + +Max characters that fit in a label between two adjacent lanes (e.g. lanes 1↔2 for N=4): + +``` +label_chars_max ≈ (|sender_x − receiver_x| − 8) / 7 +``` + +For N=4 adjacent lanes (|Δx|=160): ~21 chars. For the longest cross-diagram leap (lane 1↔4, |Δx|=480): ~67 chars. In practice, aim for ≤30 chars — short messages read faster, even when there's room. + +### Self-messages + +A self-message is a 16×24 `` using the actor's `c-{ramp}` class, horizontally centered on the lifeline: `rect_x = lifeline_x − 8`, `rect_y = arrow_y − 12`. The label sits **to the left** of the rect (so it stays inside the diagram for the rightmost lane) at `x = rect_x − 8` with `text-anchor="end"`, `class="ts"`, `dominant-baseline="central"`, and the same numbered format. + +### Side note + +A `class="box"` rect with a `th` title and optional `ts` subtitle. Default size 240×48, `rx="10"`. Placement depends on N: + +- **N ≤ 3**: top-left at `(40, 40)` — tucks beside the first actor header, no overlap because `first_offset ≥ 40`. +- **N ≥ 4**: bottom-left at `(40, lifeline_bottom + 16)` — the top is packed tight against the leftmost header, so use the bottom margin. + +### Worked example — OAuth 2.0 (N=4, M=10) + +``` +lane centers 100, 260, 420, 580 (lane_w=140, header_w=120) +header_bottom 88 +first_arrow_y 120 +last_arrow_y 120 + 44×9 = 516 (10 messages) +lifeline_bottom 540 +note_bottom 540 + 16 + 48 = 604 (note at bottom) +H 604 + 20 = 624 +viewBox "0 0 680 624" +``` + +Label budgets: adjacent lanes (|Δx|=160) fit ~21 chars; "Redirect with client_id, scope" at 30 chars needs `|Δx| ≥ 218`, so it must span at least 2 lanes — put it between Client app (260) and Auth server (420), |Δx|=160 — **too tight**. Shorten to "Redirect (client_id + scope)" (28 chars, still over). Shorten further to "Redirect + client_id" (20 chars). Always verify every label against its span before finalizing. + +## Centering inside the safe area + +For N boxes of width `w` with gap `g`: + +``` +total = N × w + (N - 1) × g +offset = 40 + (600 - total) / 2 +box[i].x = offset + i × (w + g) +``` + +Centered content feels deliberate. Left-aligned content feels incomplete. + +## Sibling subsystem containers (2-up) + +The structural subsystem-architecture pattern (see `structural.md` → "Subsystem architecture pattern") places **two sibling dashed-border containers** side by side, each holding a short internal flow. The 2-up layout sacrifices the default 40px horizontal safe margin in exchange for a usable container interior wide enough to hold 3–4 flowchart nodes. + +Standard 2-up geometry at viewBox width 680: + +``` +container_w = 315 # each sibling +container_gap = 10 # space between siblings +left_margin = 20 # leaner than the usual 40 +container_A.x = 20 +container_B.x = 345 # left_margin + container_w + container_gap +rightmost edge = 345 + 315 = 660 # leaves 20px to x=680 +interior_w = 315 − 40 = 275 # after 20px padding on each side +interior_A.x = 40 +interior_B.x = 365 +``` + +Inside each 275-wide interior, node widths follow the normal text-width formula but should cap at ~235 so the L-bend connectors have clear vertical channels on both sides. Typical nodes are 180 wide centered, or 150 wide for a 2-column row. + +A **labeled cross-system arrow** bridges the 10px container gap: + +``` +from B_right_node.right_edge → to A_left_node.left_edge +arrow_x1 = interior_B.rightmost_node.x + 10 +arrow_x2 = interior_A.leftmost_node.x − 10 +arrow_y = matching row y # both nodes on the same tier +label at (x_mid, arrow_y − 6), class="ts", text-anchor="middle" +``` + +The cross-system arrow label is the one place where a flowchart-style connector *must* have a label — the reader cannot infer the relationship from the container titles alone. Keep the label to ≤3 words. + +Dashed container borders use **inline `stroke-dasharray="4 4"`** on the `` rather than a CSS class — dashed is a one-off container-styling concern, not a reusable shape property, so the template's class list stays stable. + +### Worked example — Pi session + Background analyzer + +Two siblings, each with 3 internal nodes stacked vertically: + +``` +container A "Pi session" container B "Background analyzer" +x=20, y=40, w=315, h=260 x=345, y=40, w=315, h=260 +rx=20, stroke-dasharray="4 4" same styling + +Inside A (x_center = 177): Inside B (x_center = 502): + [ User input ] y=96 [ Analyze patterns ] y=96 + [ Pi responds ] y=170 [ Write summary ] y=170 + [ (session ends) ] y=244 [ Update memory ] y=244 + +Cross-system arrow: B top row → A top row at y=118 + x1 = 347 (container B left edge + 2)? No — from interior node B₁ left edge + Actually: from B₁.x − 10 (headed right-to-left pointing at A₁) + +Details below in structural.md's worked example. +``` + +viewBox H for 3 stacked rows: last node bottom y = 244 + 44 = 288; container bottom 40 + 260 = 300; H = 300 + 20 = **320**. + +## Sub-pattern coordinate tables + +Fixed coordinate tables for the sub-patterns introduced in `structural.md`, `illustrative.md`, and `sequence.md`. Use these values verbatim — they're tuned so each sub-pattern respects the 680 viewBox and the 40 safe margin. + +### Bus topology geometry + +For `structural.md` → "Bus topology sub-pattern". A central horizontal bar bridges N agents above it and N agents below it. + +| Element | Coordinates | +|----------------------------|------------------------------------------| +| Bus bar | `x=40 y=280 w=600 h=40 rx=20` | +| Bus label baseline | `y=304`, `text-anchor="middle"` at x=340 | +| Top agent row (box top) | `y=80`, `h=60` | +| Bottom agent row (box top) | `y=400`, `h=60` | +| Default viewBox H | 500 | + +Agent centers by row count: + +| Agents per row | Box width | Centers (x) | +|----------------|-----------|--------------------------------------------| +| 2 | 180 | 180, 500 | +| 3 | 140 | 170, 340, 510 | +| 4 | 110 | 120, 260, 420, 560 | + +Publish/subscribe arrow pair: two vertical lines per agent, offset by 8px horizontally (one at `agent_cx − 8`, one at `agent_cx + 8`). Publish goes down from agent to bar; Subscribe goes up from bar to agent. Labels sit at the channel midpoint y, `text-anchor="end"` (Publish on left) and `text-anchor="start"` (Subscribe on right). + +### Radial star geometry (3 / 4 / 5 / 6 satellites) + +For `structural.md` → "Radial star topology sub-pattern". A central hub surrounded by N peripheral satellites, each bidirectionally connected. + +**Hub (always fixed):** + +| Element | Coordinates | +|---------------|----------------------------| +| Hub rect | `x=260 y=280 w=160 h=80 rx=10` | +| Hub center | `(340, 320)` | +| viewBox H | 560 | + +**Satellite positions:** + +| N | Satellites (box top-left → w=160 h=60) | +|---|-----------------------------------------------------------------------------------| +| 3 | `(60, 120)`, `(460, 120)`, `(260, 460)` | +| 4 | `(60, 120)`, `(460, 120)`, `(60, 460)`, `(460, 460)` | +| 5 | `(260, 60)`, `(60, 200)`, `(460, 200)`, `(60, 460)`, `(460, 460)` | +| 6 | `(20, 120)`, `(260, 60)`, `(500, 120)`, `(20, 460)`, `(260, 520)`, `(500, 460)` | + +Satellite centers: add `(80, 30)` to the box top-left to get `(cx, cy)`. + +**Arrow pairs:** each satellite connects to the hub with two single-headed arrows offset by 8 px perpendicular to the arrow direction. For a satellite at `(sx, sy)` and hub center `(340, 320)`, compute the direction vector, normalize to unit length, then offset each line by `(+4dy, −4dx)` and `(−4dy, +4dx)` respectively (where `(dx, dy)` is the unit direction). + +For straightforward placement, use these pre-computed endpoint offsets for N=4: + +| Satellite center | Outbound start (satellite → hub) | Outbound end | +|------------------|----------------------------------|--------------| +| TL `(140, 150)` | `(224, 176)` | `(264, 276)` | +| TR `(540, 150)` | `(456, 176)` | `(416, 276)` | +| BL `(140, 490)` | `(224, 464)` | `(264, 364)` | +| BR `(540, 490)` | `(456, 464)` | `(416, 364)` | + +Inbound (hub → satellite) is the reverse of each outbound, offset perpendicular by 8. + +### Spectrum geometry + +For `illustrative.md` → "Spectrum / continuum". A 1-D axis with end labels, tick points, option boxes, and italic captions. + +**Fixed vertical positions:** + +| Element | Coordinates | +|---------------------------|--------------------------------------------| +| Eyebrow (optional) | `y=50`, `text-anchor="middle"` at x=340 | +| End labels (L / R) | `y=120`, at `x=80` (start) / `x=600` (end) | +| Axis line | `x1=80 y1=140 x2=600 y2=140` | +| Tick circles | `cy=140`, `r=6` | +| Option box top | `y=200`, `h=60` | +| Caption line 1 | `y=292`, italic `ts` | +| Caption line 2 | `y=308`, italic `ts` | +| Default viewBox H | 332 | + +**Tick and option-box x positions by tick count:** + +| Ticks | Tick centers (x) | Option box x (w=120) | +|-------|-----------------------------|-----------------------------| +| 2 | 200, 480 | 140, 420 | +| 3 | 160, 340, 520 | 100, 280, 460 | +| 4 | 140, 280, 420, 560 | 80, 220, 360, 500 | +| 5 | 120, 240, 360, 480, 600 (w=100) | 70, 190, 310, 430, 550 | + +Axis uses `marker-start` **and** `marker-end` (both ends arrowed to signal no natural direction). Axis stroke class is `arr` (neutral gray) — never a color ramp. + +Sweet-spot tick and its option box use `c-green` or `c-amber`; all other ticks and boxes stay `c-gray`. Captions cap at 24 characters per line, 2 lines per option. + +### Parallel rounds geometry + +For `sequence.md` → "Parallel independent rounds". Stacked independent call/response rounds without shared lifelines. + +**Stacked rounds variant (full-width):** + +| Element | Coordinates | +|----------------------|----------------------------------------| +| Source actor | `x=60 y=120 w=120 h=60` | +| Round k action box | `x=340 y=100 + (k-1)×80 w=160 h=48` | +| Round k call arrow | `y = 124 + (k-1)×80` | +| Round k response | `y = 134 + (k-1)×80` | +| Call label | `y = 115 + (k-1)×80` | +| Response label | `y = 145 + (k-1)×80` | +| Row pitch | 80 | + +Arrow x endpoints: `x1 = source_right_edge + 6 = 186`, `x2 = action_left_edge − 6 = 334` (going right); reverse for the return arrow. + +**Script-wrapper variant:** + +| Element | Coordinates | +|----------------------|----------------------------------------| +| Source actor | `x=60 y=120 w=120 h=60` | +| Script wrapper | `x=240 y=100 w=140 h=260 rx=6` | +| `{ }` label | `(310, 124)`, class `th` | +| Divider line | `x1=256 y1=140 x2=364 y2=140` | +| Wrapped band k | `x=252 y=152 + (k-1)×60 w=116 h=48` | +| External action box | `x=440 y=200 w=160 h=48` (optional) | +| Default viewBox H | 380 | + +Up to 3 wrapped bands inside the script wrapper (with `script_h = 260`). Grow `script_h` by 60 for each additional band. + +**Inside a 315-wide subsystem container** (half-width), scale the coordinates: + +| Element | Container A (x=20) | +|--------------------|---------------------------------| +| Source actor | `x=40 y=120 w=100 h=50` | +| Round k action | `x=200 y=104 + (k-1)×64 w=120 h=40` | +| Row pitch | 64 | + +### Multi-line box body line heights + +For `structural.md` → "Multi-line box body". A three-line box used for advisor-role labels: title + italic role + meta. + +| Line | y-offset from `rect_y` | Class | +|------------|--------------------------|---------------| +| Title | `+22` | `th` | +| Role | `+42` | `ts` + italic | +| Meta | `+62` | `ts` muted | +| Min h | 80 | | + +Row pitch is 20. For a box at y=140, lines sit at `162, 182, 202`. Box height never drops below 80 — if the role or meta line is absent, use a standard 56-tall two-line box instead, don't shrink a three-line box. + +### Annotation circle on connector geometry + +For `illustrative.md` → "Annotation circle on connector". A labeled circle sitting above a pair of bidirectional arrows between two subjects. + +| Element | Coordinates | +|-----------------------|----------------------------------------------| +| Subject A box | `x=80 y=160 w=180 h=56` | +| Subject B box | `x=440 y=160 w=180 h=56` | +| A → B arrow | `(266, 184) → (434, 184)`, `arr` | +| B → A arrow | `(434, 204) → (266, 204)`, `arr` | +| Arrow pair y midpoint | 194 | +| Annotation circle | outer `c="box"` r=30 at `(cx, cy) = (350, 150)` | +| Inner pill | `x=cx−28 y=cy+12 w=56 h=20 rx=10` (uses ramp)| +| Connector line | `(cx, cy+30) → (cx, 194)`, `arr` (no marker) | + +Label goes inside the pill at `(cx, cy+26)`, `th`, centered. Subject A/B labels use `th` at `(subject_cx, subject_cy)`. + +### Container loop geometry (simple flowchart) + +For `flowchart.md` → "Loop container". An outer rounded rect framing a simple flowchart. + +| Element | Coordinates | +|--------------------|--------------------------------------------| +| Container rect | `x=20 y=40 w=640 h=H−60 rx=20`, class `box`| +| Title | `(340, 72)`, class `th` | +| Subtitle | `(340, 92)`, class `ts` | +| First inner box y | ≥ 116 | +| Inner safe area | `x ∈ [40, 620]` | +| Bottom padding | 20px | + +After placing the inner flow, compute `inner_bottom = max y of any inner element`, then `H = inner_bottom + 40` and `container_h = H − 60`. + +## Arrow routing + +Every arrow has a start point (source box edge) and an end point (target box edge), with an optional bend in between. + +**Direct arrow** — when source and target are aligned on an axis (same x for vertical, same y for horizontal): + +```svg + +``` + +Leave **10px** between the arrow's endpoint and the target box's top edge so the arrowhead doesn't touch the border. + +**L-bend arrow** — when the direct line would cross another box: + +```svg + +``` + +- `ymid` is a horizontal "channel" chosen to thread cleanly between the two levels +- Always include `fill="none"` on `` used as a connector — SVG defaults to black fill + +**Collision check.** Before finalizing every arrow, trace its path against every rect you've already placed: + +``` +for each rect R in the diagram: + if rect R is not the source or target of this arrow: + does the arrow's line segment intersect the interior of R? + if yes: use an L-bend to route around R +``` + +This is the #1 diagram failure mode. Arrows slashing through unrelated boxes. + +**Connection-point convention.** Every arrow anchors to the midpoint of a box edge (top, bottom, left, or right), never to a corner. With `rx="6"` or `rx="12"` rounded corners, the actual corner curve occupies the first ~6–12px of each edge — so stay **≥20px from any corner** when picking a connection point. An arrow that enters a box 4px from its corner reads as "touching the rounded curve" instead of "attached to the edge", and the arrowhead looks smeared. For a 180-wide rect, valid top-edge connection points sit at `x ∈ [rect_x + 20, rect_x + 160]`; the outer 20px on each side belong to the corner radius. + +**Multi-arrow stagger.** When two or more arrows terminate on the same edge of the same box from different sources (three fan-in arrows hitting a single aggregator; two feedback lines returning to a start node), stagger their arrival points by **≥12px** along the target edge. Example: three arrows fanning into the top edge of a 180-wide box sit at `target_x − 24`, `target_x`, `target_x + 24`. Same rule applies to horizontal arrows converging on a left/right edge (stagger the y by ≥12px). Without the stagger, the arrowheads stack on top of each other and the reader can't tell how many incoming edges there are — a 3-to-1 fan-in reads as a single thick arrow. + +## text-anchor safety + +`text-anchor` controls which point of the text aligns to the `x` coordinate: + +- `"start"` — x is the left edge. Text extends right. +- `"middle"` — x is the horizontal center. Text extends both ways. +- `"end"` — x is the right edge. Text extends left. + +**Danger**: `text-anchor="end"` at low x values. If `x=50` and the label is 200px wide, the text starts at `x = -150` — outside the viewBox. Check: `label_chars × 8 < x` must hold for `text-anchor="end"` labels. + +**Default to `text-anchor="start"` on the left side and `"middle"` in the center.** Use `"end"` only when you've verified the x coordinate is high enough. + +## Vertical text centering inside a rect + +Every `` inside a rect needs `dominant-baseline="central"`. Without it, SVG treats `y` as the baseline — the glyph body sits ~4px above where you think and descenders hit the next line. + +```svg + +Login +``` + +For a rect at `(x, y, w, h)`: +- Single line centered: `text_x = x + w/2`, `text_y = y + h/2`, `dominant-baseline="central"` +- Two lines (44 + 14 layout inside a 56-tall box): + - Title: `text_y = y + 20` (center of top 40px) + - Subtitle: `text_y = y + 40` (center of bottom 16px) + - Both use `dominant-baseline="central"` + +## Quick-reference sizes + +Standard dimensions that work well and keep the diagram rhythm consistent: + +| Element | Size | +|-----------------------------|----------------------| +| Single-line node height | 44 px | +| Two-line node height | 56 px | +| Default node width | 180 px (adjust up) | +| Minimum gap between nodes | 20 px horizontal, 60 px vertical | +| Container padding | 20 px | +| Rect corner radius | `rx="6"` (subtle), `rx="12"` (container), `rx="20"` (outer container) | +| Connector stroke width | 1.5 px (set by `.arr`) | +| Diagram border stroke | 0.5 px | +| Viewport safe margin | 40 px on each edge | + +Stick to these unless you have a concrete reason to deviate. diff --git a/skills/baoyu-diagram/references/patterns/README.md b/skills/baoyu-diagram/references/patterns/README.md new file mode 100644 index 0000000..e2c499c --- /dev/null +++ b/skills/baoyu-diagram/references/patterns/README.md @@ -0,0 +1,38 @@ +# AI pattern catalog + +Pre-planned diagram starters for recurring multi-agent coordination shapes — research orchestrators, message buses, shared-state stores, agent-with-skills composition. Each file here is a thin wrapper around one specific pattern: a one-line description, the diagram type baoyu prefers for it, the ramp palette that works, a reference mermaid block (industry-standard shorthand you can sanity-check against), and a pre-cooked baoyu SVG plan that saves you a planning pass. + +## How to use this directory + +1. **Check the index below** for a pattern name matching the user's topic. Exact matches are rare — usually the user says *"agents coordinating through a shared channel"* and you recognize *message-bus*, or *"agents building on each other's findings in a store"* and you recognize *shared-state*. +2. **If a pattern matches**, open its file and read end-to-end. The mermaid block tells you *what* to draw (structurally), the baoyu SVG plan tells you *how* (coordinates, widths, arrow routing). +3. **If no pattern matches**, fall back to the normal Step 4 planning flow in `SKILL.md`. Do not force a near-miss — two coordination patterns that share a surface name often have different topologies (message bus ≠ shared state, even though both put a central element between agents). + +The mermaid reference is **authoritative for structure**, not for rendering. Never emit mermaid as the final output; always convert to a hand-written baoyu SVG using the plan in the same file. + +## Scope + +This directory covers **AI-system topologies**, not generic software patterns. For flowchart / sequence / structural / illustrative / class diagram techniques, stay in the top-level references files. If an AI-system pattern needs a technique that isn't documented in those files yet, add the technique upstream — not here. + +## Index + +| Pattern | Default type | One-line hook | +|--------------------------|-----------------|-----------------------------------------------------------------------------------------------------| +| [multi-agent-research](multi-agent-research.md) | flowchart | Lead agent + memory sidecar + parallel search subagents (each looping) + citation stage (Anthropic) | +| [message-bus](message-bus.md) | structural | N agents coordinate via a central publish/subscribe bar — no direct agent-to-agent edges | +| [shared-state](shared-state.md) | structural | N peer agents read/write a central store — no orchestrator, findings immediately visible to all | +| [agent-skills](agent-skills.md) | structural | Agent loop + runtime + MCP servers (left) + skills library on filesystem (right) — composition view | +| [contextual-retrieval](contextual-retrieval.md) | flowchart | Contextualizer LLM prepends 50–100 tokens to each chunk → dual-track (embedding + BM25) + rank fusion | + +## Adding a new pattern + +Keep each file under ~80 lines. A pattern file has six sections in this order: + +1. **Name + 1-line description** +2. **Default diagram type** — plus when to pick an alternate type +3. **Palette** — which ramps, tied to which roles +4. **Sub-pattern** — the specific section in a top-level reference file that does the heavy lifting +5. **Mermaid reference** — the canonical industry-standard sketch, in a ` ```mermaid` block +6. **Baoyu SVG plan** — node list with widths, arrow list, viewBox dimensions, any gotchas + +When you add a new pattern, update this README's index table in the same commit. Do not create orphan files — if you can't write a one-line hook for the index, the pattern isn't well-defined yet. diff --git a/skills/baoyu-diagram/references/patterns/agent-skills.md b/skills/baoyu-diagram/references/patterns/agent-skills.md new file mode 100644 index 0000000..6f764ac --- /dev/null +++ b/skills/baoyu-diagram/references/patterns/agent-skills.md @@ -0,0 +1,92 @@ +# Agent with skills + +**One-line description.** The architectural snapshot of a general-purpose agent composed of four layers — **agent loop** (reasoning), **agent runtime** (code + filesystem), **MCP servers** (outbound connectors to external tools and data), and a **skills library** (local, progressively-disclosed domain expertise on the filesystem). This is the "Skills: the complete picture" diagram from Anthropic's skills blog post — the shape that explains *why you stop building specialized agents and start shipping skills to one general agent*. It is a **system composition** diagram, not a workflow: it answers "what the agent is", not "what it does next". + +## Default diagram type + +**Structural — central hub with bilateral satellite groups.** Single agent box in the middle with a visible interior loop glyph; MCP servers stacked vertically on the **left**; skills stacked vertically inside a **filesystem container** on the **right**. The left/right split is the point: *left is network-reachable tools, right is on-disk expertise*. + +Alternate types: +- **Before/after poster flowchart** (two stacked frames, per `flowchart.md` → "Poster flowchart") when illustrating the evolution from specialized-per-domain agents → general agent + skills. This is Anthropic's Figure 1 / Figure 2 shape. +- **Flowchart with a loop container** when the user wants to show activation order (loop → read skill → call MCP → write file → loop). The structural snapshot loses sequencing; the flowchart gains it. + +## Palette + +Three role ramps + gray. This is a structural diagram where the two satellite groups must be visually distinct: + +- **`c-gray`** — filesystem container, title bar, legend, arrow labels. +- **`c-teal`** — agent (central hub). Teal anchors the LLM/reasoning role. +- **`c-purple`** — MCP servers. Shared across all three (homogeneous satellite group — instances of one role do not get distinct colors). +- **`c-coral`** — skills. Shared across all three; coral reads as Anthropic's own skills brand-orange and sits opposite the MCP group. + +This is a **category coloring**, not a rainbow: one ramp per satellite *group*, not per satellite. The structural radial-star rule in `structural.md` ("satellites stay neutral gray") is deliberately overridden here because we have **two distinct kinds of satellite** that the reader must tell apart at a glance. + +Legend is required. + +## Sub-pattern + +`structural.md` → **Radial star topology** as the base (hub with satellites, bidirectional arrows), extended to an **asymmetric two-group** variant: left group is a loose stack (no container), right group is wrapped in a `structural.md` → **Container box** to show the filesystem boundary. The agent's interior loop uses `glyphs.md` → **terminal-icon** plus a hand-drawn circular arrow pair around it (the "reason · act" cycle) — this is what distinguishes the agent visually from its satellites. + +## Mermaid reference + +```mermaid +flowchart LR + subgraph MCP + M1[MCP server 1] + M2[MCP server 2] + M3[MCP server 3] + end + A[Agent
reason · runtime] + subgraph Filesystem + S1[Skill A] + S2[Skill B] + S3[Skill C] + end + M1 <--> A + M2 <--> A + M3 <--> A + A <--> S1 + A <--> S2 + A <--> S3 + A -. reason · act loop .-> A +``` + +Two subgraphs flanking a central hub plus a self-loop on the hub is the defining shape. Drop either subgraph and the pattern collapses into something else — a plain tool-calling loop (if you drop skills) or a read-only retrieval shape (if you drop MCP). + +## Baoyu SVG plan + +Central agent with an interior loop glyph; three MCP servers stacked on the left; three skills stacked inside a filesystem container on the right. + +- **viewBox**: `0 0 820 460` +- **Agent (hub)** — `c-teal`, `x=310 y=110 w=200 h=240`, large central box: + - Title *Agent* at `(410, 140)` class `th`. + - Interior loop glyph centered at `(410, 230)`: a `terminal-icon` at `(398, 218)` (24×24 from `glyphs.md`), with two curved arrows forming a circle around it — `path d="M 370 230 A 40 40 0 1 1 450 230" class="arr" marker-end="url(#arrow)"` plus the mirror arc below. This is the reason-act loop made visible. + - Subtitle *reason · runtime* at `(410, 310)` class `ts`. +- **MCP server stack** (3 boxes, `c-purple`, single-line title, same size): + - *MCP server 1*, `x=60 y=140 w=170 h=52`. + - *MCP server 2*, `x=60 y=210 w=170 h=52`. + - *MCP server 3*, `x=60 y=280 w=170 h=52`. +- **Filesystem container** — `rect x=570 y=100 w=220 h=260 rx=16` class `box`, title *Filesystem* at `(680, 128)` class `th`, centered. +- **Skills stack** inside the filesystem (3 boxes, `c-coral`, single-line title, same size): + - *Skill A*, `x=600 y=150 w=160 h=52`. + - *Skill B*, `x=600 y=216 w=160 h=52`. + - *Skill C*, `x=600 y=282 w=160 h=52`. + +**Arrow plan.** Six bidirectional pairs, three per side. Each pair uses two single-headed arrows offset 8px perpendicular to direction (same rule as `structural.md` → "Radial star → Arrow pairs"): + +- MCP server *n* ↔ Agent: short horizontal channel from `(230, y_center)` to `(310, y_center)` and back, where `y_center ∈ {166, 236, 306}`. Both solid `arr`. +- Agent ↔ Skill *n*: short horizontal channel from `(510, y_center)` to `(600, y_center)` and back, where `y_center ∈ {176, 242, 308}`. Arrows cross the filesystem container edge — that crossing is semantically important and must not be hidden. + +No external self-loop arc on the agent — the interior loop glyph (`terminal-icon` + circular arrows) already carries that meaning, and an exterior arc would collide with the MCP/skills channels. + +**Legend** (bottom, required): + +``` +[■] Agent [■] MCP server [■] Skill [▭] Filesystem [↔] bidirectional channel +``` + +**Gotchas.** +- Never color the 3 MCP servers differently from each other, nor the 3 skills. Each group is a homogeneous tier; per-item coloring turns an architecture diagram into a role diagram. +- Keep the filesystem container around the skills. Dropping it makes skills look like peers of MCP servers and erases the "local, progressively disclosed from disk" property that is the whole reason skills are not MCP servers. +- Convention: **MCP on the left, skills on the right**, matching Anthropic's own materials. Flipping the sides loses a free point of recognition. +- If the user wants to show **progressive disclosure** (metadata → SKILL.md → references/), add a nested three-tier rect *inside each skill box* — that is an extension of this pattern, not an alternate type. diff --git a/skills/baoyu-diagram/references/patterns/contextual-retrieval.md b/skills/baoyu-diagram/references/patterns/contextual-retrieval.md new file mode 100644 index 0000000..22bc508 --- /dev/null +++ b/skills/baoyu-diagram/references/patterns/contextual-retrieval.md @@ -0,0 +1,98 @@ +# Contextual retrieval + +**One-line description.** Anthropic's preprocessing recipe for RAG: before chunks are embedded and indexed, an LLM (the *contextualizer*) reads the whole document together with each chunk and emits 50–100 tokens of situating context that gets prepended to the chunk. The contextualized chunk then feeds **both** a semantic index (embedding model → vector DB) and a lexical index (TF-IDF → BM25). At runtime the query hits both indices, results merge through rank fusion, and the top-K chunks go to the generative model. The diagram's job is to show the contextualizer as the distinctive new step and the dual-track preprocessing it feeds. + +## Default diagram type + +**Flowchart (poster style) with two stacked phases.** The pattern has a name, a clear preprocessing / runtime split, a fan-out into parallel tracks, and a distinctive new step — that's poster flowchart territory. A flat linear chart would smear the two phases and hide what's new. Stack the phases vertically with eyebrow dividers; each phase reads left-to-right. + +Alternate types: +- **Structural — subsystem containers side by side** when contrasting contextual retrieval against plain RAG (two siblings, each a mini pipeline). See `structural.md` → "Rich interior for subsystem containers". +- **Preprocessing-only flowchart** when the runtime story isn't needed — drop phase 2 and end at the two indices. + +## Palette + +Three accent ramps plus gray, under the poster-flowchart 4-ramp exception (ramps encode role *categories*, not sequence): + +- **`c-gray`** — corpus, query, rank fusion, top-K chunks, response. Neutral data / IO. +- **`c-purple`** — Claude in both its roles: contextualizer and generative model. One ramp for both anchors the "same Claude, two prompts" story without adding a fourth color. +- **`c-teal`** — semantic track (embedding model + vector DB). +- **`c-amber`** — lexical track (TF-IDF + BM25 index). + +Do **not** color the contextualizer and generative model differently — doing so implies different models or different roles, but the whole point is the same Claude doing both jobs. + +## Sub-pattern + +`flowchart.md` → **Poster flowchart pattern** (eyebrow-divided phases, ≤4 ramps for role categories) + **Fan-out + aggregator (simple mode)** applied twice: the contextualizer splits into two tracks that never reconverge in phase 1, and query + both indices converge at rank fusion in phase 2. + +## Mermaid reference + +```mermaid +flowchart TB + subgraph Preprocessing + C[Corpus] -- chunks --> CTX[Contextualizer · Claude] + CTX -- context + chunk --> EM[Embedding model] + CTX -- context + chunk --> TF[TF-IDF] + EM --> VDB[(Vector DB)] + TF --> BM[(BM25 index)] + end + subgraph Runtime + Q[User query] --> RF[Rank fusion] + VDB --> RF + BM --> RF + RF --> TK[Top-K chunks] --> GM[Generative model · Claude] --> R[Response] + end +``` + +Defining edges: `CTX --> EM` *and* `CTX --> TF` (the contextualized chunk goes to both tracks) plus `VDB --> RF` *and* `BM --> RF` (both indices feed fusion). Drop either pair and the diagram collapses into plain RAG or embedding-only retrieval. + +## Baoyu SVG plan + +Two stacked phases with eyebrow labels and a thin horizontal divider between them. + +- **viewBox**: `0 0 680 540` +- **Phase 1 eyebrow** — *Preprocessing · Runs once per corpus update* at `(40, 50)`, class `eyebrow`. + +Phase 1 interior: +- **Corpus** — `c-gray`, `x=40 y=80 w=100 h=56`, two-line (*Corpus*, *Documents*). +- **Contextualizer** — `c-purple`, `x=180 y=72 w=260 h=72`, multi-line (*Contextualizer*, *Claude*, *50–100 tokens per chunk*). Visibly the largest box — it's the pattern's signature step. +- **Embedding model** — `c-teal`, `x=140 y=180 w=160 h=48`, single-line. +- **TF-IDF** — `c-amber`, `x=380 y=180 w=160 h=48`, single-line. +- **Vector DB** — `c-teal`, `x=140 y=260 w=160 h=56`, two-line (*Vector DB*, *Semantic index*). +- **BM25 index** — `c-amber`, `x=380 y=260 w=160 h=56`, two-line (*BM25 index*, *Lexical index*). + +**Phase 1 arrows:** +- *Corpus → Contextualizer*: `(140, 108) → (180, 108)`, label *chunks* at `(160, 102)`. +- *Contextualizer → Embedding model*: L-bend `(260, 144) → (260, 160) → (220, 160) → (220, 180)`, label *context + chunk* at `(170, 164)` `text-anchor="end"`. +- *Contextualizer → TF-IDF*: L-bend `(360, 144) → (360, 160) → (460, 160) → (460, 180)`, label *context + chunk* at `(470, 164)` `text-anchor="start"`. (Both arrows labeled — the reader must see that *both* tracks receive the contextualized chunk.) +- *Embedding model → Vector DB*: straight vertical `(220, 228) → (220, 260)`. +- *TF-IDF → BM25 index*: straight vertical `(460, 228) → (460, 260)`. + +- **Phase divider** — dashed line `x1=40 y1=340 x2=640 y2=340`, class `arr-alt`. +- **Phase 2 eyebrow** — *Runtime · Per user query* at `(40, 362)`, class `eyebrow`. + +Phase 2 interior (single horizontal row at y=400–456): +- *User query* `c-gray` `x=40 w=100`, *Rank fusion* `c-gray` `x=160 w=100`, *Top-K chunks* `c-gray` `x=280 w=100` (two-line with subtitle *Top 20*), *Generative model* `c-purple` `x=400 w=140` (two-line with subtitle *Claude*), *Response* `c-gray` `x=560 w=80`. All `y=400 h=56`. + +**Phase 2 arrows** (straight horizontal, 20px gaps between boxes at y=428): query→fusion, fusion→top-K, top-K→generator, generator→response. + +**Cross-phase arrows** (indices into rank fusion): +- *Vector DB → Rank fusion*: vertical drop `(200, 316) → (200, 400)` — lands inside rank fusion's top edge (x=160–260). +- *BM25 index → Rank fusion*: L-bend `(460, 316) → (460, 372) → (220, 372) → (220, 400)`. The 20px x-offset from the Vector DB arrow keeps the two inbound arrows from stacking. + +Both cross-phase arrows are solid `.arr` — they're the main data flow, nothing alternate. + +**Legend** (bottom, required — 3 accent ramps encode category): + +``` +[■] Claude (contextualizer + generator) [■] Semantic track [■] Lexical track +``` + +Place at `y=510`, centered at `x=340`. + +**Gotchas.** +- Both tracks must show they receive the *contextualized* chunk — label both outgoing arrows from the contextualizer. If only one is labeled, readers assume the other track still uses raw chunks. +- Do not draw the contextualizer as a self-loop on the Corpus. It's a distinct LLM step that runs once per chunk with whole doc + chunk as input, conceptually closer to an orchestrator than an inline transform. +- Keep rank fusion gray, not amber — it merges two tracks but it's a structural aggregator, not an accent role. Giving it amber visually absorbs it into the lexical track. + +**Reranker variant.** The reranking extension inserts a **reranker** box between *Rank fusion* and *Top-K chunks*. Insert `Reranker` at `x=280 y=400 w=120 h=56` (shift Top-K, generator, response right by 140 and widen the viewBox to 820). Annotate the reranker's input arrow with *top 150* and its output with *top 20* — the winnowing ratio is the whole point. diff --git a/skills/baoyu-diagram/references/patterns/message-bus.md b/skills/baoyu-diagram/references/patterns/message-bus.md new file mode 100644 index 0000000..b49fb7e --- /dev/null +++ b/skills/baoyu-diagram/references/patterns/message-bus.md @@ -0,0 +1,73 @@ +# Message bus + +**One-line description.** N agents coordinate through a shared publish/subscribe channel rather than talking to each other directly. Each agent subscribes to the topics it cares about and publishes events others may handle. Unlike orchestrator-subagent (central router) or shared-state (central store), the bus carries *events in flight* — agents react to what's happening, not to what's been accumulated. Canonical use cases: event-driven pipelines, security-ops triage → investigation → response, growing agent ecosystems where new capabilities plug in without rewiring. + +## Default diagram type + +**Structural — bus topology.** The defining visual is a central horizontal bar with agents fanning out above and below, each linked by a pair of offset arrows (publish down, subscribe up). A flowchart would force one specific event sequence, but the whole point of a bus is that the workflow emerges from events; a structural diagram shows the coordination shape without committing to a single path. + +Alternate types: +- **Sequence** when the user wants to show one specific event cascade (alert arrives → triage classifies → network agent investigates → response agent acts) with explicit ordering. Use 4–5 lifelines, not the bus geometry. +- **Flowchart** only if the pipeline really is fixed, in which case it's not a message bus — it's just a linear workflow. + +## Palette + +- **`c-gray`** — the event source / external input (the thing that only publishes, never subscribes). Neutral because it's outside the coordinated agent set. +- **`c-teal`** — the agent role for all subscribed agents. One shared ramp because every agent on the bus is a peer; coloring them differently implies a hierarchy that the pattern explicitly rejects. +- **`c-amber`** — the bus bar itself. Amber is the convention for "the shared channel everyone looks at" per `structural.md` → "Bus topology sub-pattern". + +Do **not** rainbow the agents by role (network / identity / response / enrichment → four different ramps). The reader should feel the agents are interchangeable peers that differ only in what topics they subscribe to, not in what kind of thing they are. + +## Sub-pattern + +`structural.md` → **Bus topology sub-pattern** + `glyphs.md` → **Publish/subscribe arrow pair**. This pattern is the flagship use case for both; the bus topology section is written with this diagram in mind and `layout-math.md` → "Bus topology geometry" has the fixed coordinates. + +## Mermaid reference + +```mermaid +flowchart TD + S[Alert source] -- publish --> B[[Message bus]] + B -- subscribe --> T[Triage agent] + T -- publish --> B + B -- subscribe --> E[Enrichment agent] + E -- publish --> B + B -- subscribe --> N[Network agent] + B -- subscribe --> I[Identity agent] + B -- subscribe --> R[Response agent] +``` + +The `[[bus]]` notation stands in for a central bar — mermaid can't draw the real geometry. What matters structurally is that every agent talks only to the bus, never agent-to-agent, and that most agents both publish *and* subscribe (the source is the exception). + +## Baoyu SVG plan + +Bus bar centered horizontally; 3 agents on top, 3 agents on bottom. Uses the Anthropic security-ops example labels verbatim — swap them for the user's domain when adapting. + +- **viewBox**: `0 0 680 500` +- **Bus bar** — `c-amber`, `x=40 y=280 w=600 h=40 rx=20`. Label *Message bus (publish/subscribe)* at `(340, 304)`, class `th`, centered. +- **Top row** (3 boxes, centers at `x = 170, 340, 510`, `w=140 h=60`, `y=80`, two-line): + - *Alert source*, **`c-gray`**, subtitle *External events* — the only pure publisher. Mark it gray to distance it from the coordinated agent set. + - *Triage agent*, `c-teal`, subtitle *Classifies severity*. + - *Enrichment agent*, `c-teal`, subtitle *Gathers context*. +- **Bottom row** (same centers, `y=400`, `h=60`): + - *Network agent*, `c-teal`, subtitle *Investigates traffic*. + - *Identity agent*, `c-teal`, subtitle *Checks credentials*. + - *Response agent*, `c-teal`, subtitle *Triggers actions*. + +**Publish/subscribe arrow pairs** (use the glyph template verbatim, 8px offset): + +- For each agent centered at `agent_cx`, with top agents at `agent_y_bottom=140` and the bus top at `bar_y_top=280`, draw two vertical lines: + - Publish: `(agent_cx − 8, 140) → (agent_cx − 8, 280)` with arrowhead. + - Subscribe: `(agent_cx + 8, 280) → (agent_cx + 8, 140)` with arrowhead. +- For bottom agents, `agent_y_top=400`, `bar_y_bottom=320`, mirror: Publish goes down from agent to bus, Subscribe goes up from bus to agent. (Yes — publish on a bottom agent still goes *out of* the agent toward the bus, which is visually upward.) +- **Gotcha — Alert source exception.** Alert source is a pure publisher; draw only its Publish arrow (`(162, 140) → (162, 280)`) and omit the Subscribe return. Do *not* draw a subscribe arrow with no label, and do not put a "(source)" parenthetical in the subtitle — the gray ramp + missing return arrow is the signal. +- **Labels.** Only label the Publish/Subscribe arrows for one representative agent (e.g., Triage), not all six. With six pairs on one diagram, labeling every pair becomes text soup — a single labeled example plus the legend below is enough. + +**Legend.** Required because the two arrow directions encode distinct semantics and the color-off source agent needs a key: + +``` +[↓] Publish [↑] Subscribe [■] Event source [■] Subscribed agent [■] Bus +``` + +Place at `y=480`, `text-anchor="middle"` at `x=340`. + +**When to drop to 2+2 or go up to 4+4.** The 3+3 layout is the sweet spot. With 2 agents per row use `w=180` centered at `x=180, 500`. With 4 per row use `w=110` centered at `x=120, 260, 420, 560` and drop the Publish/Subscribe *labels* entirely — four pairs plus labels per row is too dense. Beyond 8 total agents, the diagram is telling you the ecosystem has outgrown a single-canvas structural view; consider grouping agents by topic or splitting into two diagrams. diff --git a/skills/baoyu-diagram/references/patterns/multi-agent-research.md b/skills/baoyu-diagram/references/patterns/multi-agent-research.md new file mode 100644 index 0000000..4c2094f --- /dev/null +++ b/skills/baoyu-diagram/references/patterns/multi-agent-research.md @@ -0,0 +1,84 @@ +# Multi-agent research + +**One-line description.** A lead agent plans a research task, persists its plan to a memory sidecar, spawns a row of parallel search subagents (each running its own search-and-think loop over a different aspect of the question), synthesizes their findings, then hands the synthesized draft to a dedicated citation subagent that attributes sources before the final report goes back to the user. This is Anthropic's Research-feature shape. The parts that distinguish it from a plain orchestrator fan-out are the **memory sidecar**, the **per-subagent iteration loop**, and the **post-processing citation stage**. + +## Default diagram type + +**Structural flowchart — central orchestrator with two flanking peers plus a subagent row.** The topology has one big star (the lead agent) with a memory store on one side and a citation subagent on the other, and a row of interchangeable search subagents underneath. A plain fan-out misses memory; a vertical queue-backed worker pool misses the pre/post peers. Draw the lead agent large and central, flank it with memory + citations, and put search subagents in a horizontal row below. + +Alternate types: +- **Sequence** when the user wants to show turn order — `LeadResearcher → Memory (save plan) → Subagent1/2 (parallel dispatch) → LeadResearcher (synthesize) → CitationAgent → User`. This is the shape of the process diagram in Anthropic's own post. + +## Palette + +Four role ramps (at the `design-system.md` limit for multi-agent diagrams): + +- **`c-gray`** — user box, final report, structural labels. +- **`c-teal`** — lead agent / orchestrator. The primary role anchors the strongest color. +- **`c-purple`** — search subagents, **all the same color**. Instances of one role share a ramp; they are interchangeable workers, not distinct actors. +- **`c-coral`** — citation subagent. Distinct from the search pool because it is a different specialist running at a different stage. +- **`c-amber`** — memory store. Amber is the standing convention for a retriever/store sidecar, which also prevents readers from mistaking memory for a third agent role. + +Legend is required — three distinct non-gray role ramps plus the store color. + +## Sub-pattern + +`flowchart.md` → **Fan-out + aggregator (simple mode)** for the lead ↔ subagents channel, plus `flowchart.md` → **Self-loops** (borrowed from the state-machine section) on each search subagent to show its internal search/think iteration. The flanking peers (memory, citation) are **bidirectional satellites** off the lead agent — not part of the fan-out. + +## Mermaid reference + +```mermaid +flowchart TB + U[User query] --> L[Lead agent
orchestrator] + L <--> M[(Memory
plan + context)] + L <--> C[Citation subagent] + L --> S1[Search subagent A] + L --> S2[Search subagent B] + L --> S3[Search subagent C] + S1 --> L + S2 --> L + S3 --> L + S1 -. search + think .-> S1 + S2 -. search + think .-> S2 + S3 -. search + think .-> S3 + L --> R[Final report] + R --> U +``` + +The distinctive edges vs. a plain orchestrator fan-out: the bidirectional `L <--> M` sidecar, the `L <--> C` post-processor, and the three self-loops on the search subagents. + +## Baoyu SVG plan + +Central lead agent with two flanking peers on the top row and three parallel search subagents in a row below. User box on the far left. + +- **viewBox**: `0 0 820 460` +- **User** — `c-gray`, `x=30 y=150 w=120 h=64`, two-line (*User*, *Submits query*). +- **Lead agent** — `c-teal`, `x=310 y=90 w=260 h=180`, multi-line block: + - Title *Lead agent* at `(440, 120)` class `th`. + - Subtitle *(orchestrator)* at `(440, 140)` class `ts`. + - Tool stack at y=178, 198, 218, 238, class `ts`, centered: *Tools:*, *search · MCP · memory*, *run_subagent*, *complete_task*. +- **Citations subagent** — `c-coral`, `x=170 y=156 w=120 h=60`, two-line (*Citations*, *subagent*). +- **Memory** — `c-amber`, `x=590 y=156 w=200 h=60`, two-line (*Memory*, *plan + context*). +- **Search subagent row** (3 workers, all `c-purple`, same size): + - *Search subagent A*, `x=200 y=340 w=160 h=64`, two-line (*Search subagent*, *Aspect A*). + - *Search subagent B*, `x=390 y=340 w=160 h=64`, two-line (*Search subagent*, *Aspect B*). + - *Search subagent C*, `x=580 y=340 w=160 h=64`, two-line (*Search subagent*, *Aspect C*). + +**Arrow plan.** +- User ↔ Lead — stacked request/response pair: `user → lead` solid `arr` `M 150 174 L 310 174` labeled *query*; `lead → user` dashed `arr-alt` `M 310 198 L 150 198` labeled *final report*. +- Lead ↔ Citations — short bidirectional pair on lead's left edge: `M 290 180 L 310 180` (in), `M 310 204 L 290 204` (out). Both solid `arr`. +- Lead ↔ Memory — matching pair on lead's right edge: `M 570 180 L 590 180`, `M 590 204 L 570 204`. +- Lead → each subagent — vertical L-bends from lead's bottom edge `(y=270)` via a shared channel at `y=305`. Dispatch arrows anchor on lead at `(360, 270)`, `(470, 270)`, `(520, 270)` and land at each subagent's top-center `(280, 340)`, `(470, 340)`, `(660, 340)`. Middle arrow is straight; outer two L-bend through the channel. +- Subagent → Lead — matching return arrows offset 16px to the right of each dispatch arrow, using `arr-alt` to mark the return. +- **Self-loop on each search subagent** — per `flowchart.md` → Self-loops, a short arc off the right edge: `M {x+w} {y+16} C {x+w+24} {y+8}, {x+w+24} {y+h-8}, {x+w} {y+h-16}` class `arr`, with a `ts` label *search + think* at `({x+w+28}, {y+h/2})`. Repeat for all three subagents. This is the key visual that distinguishes research subagents from plain workers. + +**Legend** (bottom, required): + +``` +[■] Lead agent [■] Search subagent [■] Citation subagent [■] Memory [──] dispatch [- -] return +``` + +**Gotchas.** +- Do not color the 3 search subagents differently — they are one pool, not three roles. Rainbow-ing them turns a homogeneous worker pool into a role fan-out and misrepresents the pattern. +- Keep memory amber even though it is a peer of lead — the store convention keeps readers from reading it as a third agent. +- The citation subagent runs *at the end* logically, but the architecture view draws it as a peer of the lead agent (not a downstream successor), matching Anthropic's own diagram. If the user asks for the turn-order view instead, switch to the sequence alternate. diff --git a/skills/baoyu-diagram/references/patterns/shared-state.md b/skills/baoyu-diagram/references/patterns/shared-state.md new file mode 100644 index 0000000..9d851e4 --- /dev/null +++ b/skills/baoyu-diagram/references/patterns/shared-state.md @@ -0,0 +1,64 @@ +# Shared state + +**One-line description.** N peer agents coordinate by reading and writing a persistent shared store — database, filesystem, document — with no central orchestrator, router, or message bus between them. Each agent checks the store for relevant findings, acts on what it sees, and writes its own contributions back. Findings are immediately visible to every other agent; the store becomes an evolving knowledge base. Canonical use case: a research synthesis system where an academic-literature agent, industry-reports agent, patent agent, and news agent all build on each other's discoveries in real time. + +## Default diagram type + +**Structural — radial star.** The point of the pattern is *no central coordinator* — the hub is a passive store, not an active router. A radial star diagram puts the store in the center and the agents around it, with bidirectional read/write channels on every spoke. A flowchart would smuggle in an implied order; a bus topology would suggest events in flight rather than accumulated state. + +Alternate types: +- **Illustrative** when the user wants a *cross-section* metaphor — concentric layers, store as a well, agents as drawers dipping in. Rarely the right call for production docs but occasionally useful for teaching the intuition. +- **Structural (subsystem)** when the user is contrasting shared-state with a different pattern (agent teams, message bus) — in that case use the two-sibling container layout and make this pattern's side a mini radial star inside the right container. See `structural.md` → "Rich interior for subsystem containers". + +## Palette + +- **`c-amber`** — the shared state hub. This is the one place in the diagram that "everybody is looking at," and amber is the documented convention for shared-state hubs per `structural.md` → "Radial star topology sub-pattern". +- **`c-gray`** — the peripheral satellite agents. They are peers doing the same *kind* of work (read + write), and the pattern's whole argument is that they're interchangeable. Giving each satellite its own ramp is rainbow coloring that implies a hierarchy that doesn't exist. +- **`c-teal`** — acceptable *instead of* gray for all satellites together (single ramp for "agent peer"), when the diagram needs to distinguish the agents from other gray scaffolding elsewhere on the canvas. Never mix gray and teal satellites in the same diagram — it looks like two tiers. + +Never promote one satellite to a different ramp "to show the primary agent". If one agent really is primary, the topology isn't radial star — it's orchestrator-subagent or agent-teams. + +## Sub-pattern + +`structural.md` → **Radial star topology sub-pattern**. The central hub carries a `doc-icon` glyph from `glyphs.md` → "Document & terminal icons" to signal it's a store (not just an abstract coordinator). `layout-math.md` → "Radial star geometry (3 / 4 / 5 / 6 satellites)" has the fixed coordinate table for N=3 through N=6. + +## Mermaid reference + +```mermaid +flowchart LR + A[Academic agent] <--> S[(Shared state store)] + I[Industry agent] <--> S + P[Patent agent] <--> S + N[News agent] <--> S +``` + +The defining shape is the bidirectional double-headed arrow between every satellite and the store, with *no arrows between satellites*. Any agent-to-agent edge turns the topology into a mesh and is a signal to switch patterns. + +## Baoyu SVG plan + +N=4 radial star with a doc-icon hub. Uses the Anthropic research-synthesis example labels verbatim — swap them for the user's domain. + +- **viewBox**: `0 0 680 560` +- **Hub (shared state store)** — `c-amber`, `x=260 y=280 w=160 h=80 rx=10`. + - Title *Shared state* at `(340, 302)`, `th`, centered. + - Subtitle *Database / filesystem / doc* at `(340, 320)`, `ts`, centered. + - `doc-icon` glyph at `translate(328, 328)` (bottom-center of hub rect). Copy the 5-line doc-icon path from `structural.md` → "Hub content" verbatim — it's the worked example for exactly this diagram. +- **Satellites** (4, all `c-gray`, `w=160 h=60`, two-line): + - *Academic agent*, `x=60 y=120`, subtitle *Literature search*. + - *Industry agent*, `x=460 y=120`, subtitle *Market reports*. + - *Patent agent*, `x=60 y=460`, subtitle *Patent filings*. + - *News agent*, `x=460 y=460`, subtitle *Current coverage*. + +**Bidirectional arrow pairs** (use the pre-computed N=4 endpoints from `layout-math.md` → "Radial star geometry"): + +- TL Academic: outbound `(224, 176) → (264, 276)`, inbound offset perpendicular by 8. +- TR Industry: outbound `(456, 176) → (416, 276)`, inbound offset. +- BL Patent: outbound `(224, 464) → (264, 364)`, inbound offset. +- BR News: outbound `(456, 464) → (416, 364)`, inbound offset. +- Label each pair with a single `ts` *Read / write* next to the satellite end (not between the two offset lines — the 8px gap is too narrow). For the top satellites, place the label just below the satellite box at `y ≈ 198`. For the bottom satellites, just above at `y ≈ 448`. + +**Centered banner for termination rule.** Shared-state systems cycle indefinitely without an explicit termination condition. Drop a small centered `ts` caption at the top of the canvas — `y=60`, `text-anchor="middle"` at `x=340` — naming the rule the system uses: *Until convergence (no new findings for N cycles)*, *Until time budget exhausted*, or *Until a designated terminator agent signals done*. This is not ornamental — a shared-state diagram without a termination line misrepresents the pattern. See the blog's "reactive loops" failure mode. + +**Legend.** Not needed — the single accent color on the hub and the shared gray on satellites self-document. If you used `c-teal` satellites instead of gray, still no legend: one ramp on all peers means "they're all the same role", which is the whole message. + +**When to use N≠4.** Stick to N=4 unless the source explicitly names a different count. N=3 when the user names three investigators, N=5–6 for larger ecosystems. Beyond 6 satellites, switch to the bus topology pattern — a shared store with 8+ agents suggests event-driven coordination, not accumulated state. diff --git a/skills/baoyu-diagram/references/pitfalls.md b/skills/baoyu-diagram/references/pitfalls.md new file mode 100644 index 0000000..e8eeeb9 --- /dev/null +++ b/skills/baoyu-diagram/references/pitfalls.md @@ -0,0 +1,287 @@ +# Pitfalls Checklist + +Run through this list after writing the SVG and before saving the file. Most diagram failures are one of these ten, and they all happen silently — the SVG is "valid" but looks wrong when rendered. + +Read this as a code review on yourself. If any item fails, fix the SVG and re-check. + +## 1. viewBox clips content + +**Symptom**: the bottom row of boxes is cut off, or a label disappears past the right edge. + +**Check**: +- Find the lowest element. For rects: `y + height`. For text: `y + 4` (descender). For circles: `cy + r`. +- `viewBox` height must be `max(those) + 20` or more. +- Find the rightmost element. For rects: `x + width`. No rect's right edge should exceed 640. + +**Fix**: increase `H` in `viewBox="0 0 680 H"`, or shrink the content horizontally. + +**Exception — subsystem architecture pattern.** The 2-up sibling containers (see `structural.md` → "Subsystem architecture pattern" and `layout-math.md` → "Sibling subsystem containers (2-up)") deliberately sacrifice the 40px horizontal safe margin so each sibling interior is wide enough for a short internal flow. With `container_A.x=20` and `container_B.x=345, width=315`, container B's right edge lands at **660**. That is the documented layout; rects belonging to the 2-up pattern may extend up to x=660 instead of 640. All content *inside* each container (internal nodes, cross-system labels) still respects its own interior edge — no individual flowchart rect inside a sibling should touch the dashed border. + +## 2. Text overflows its box + +**Symptom**: a label spills out past the border of its container. + +**Check**: for every labeled rect, compute `label_chars × char_width + 24` (8 for 14px latin, 7 for 12px latin, 15 for 14px CJK). The rect's width must be ≥ that number. + +**Fix**: widen the rect, or shorten the label. Subtitles > 5 words are always a smell — cut them. + +## 3. Arrow crosses an unrelated box + +**Symptom**: an arrow visibly slashes through the interior of a rect it's not anchored to. + +**Check**: for every arrow, trace its line segment(s). For every other rect in the diagram, does the arrow cross the rect's interior? If yes, it's a bug. + +**Fix**: replace the straight `` with an L-bend `` that routes around the obstacle: + +```svg + +``` + +Pick `ymid` so the horizontal channel runs through empty space between rows. + +## 4. Connector path is missing `fill="none"` + +**Symptom**: a curved connector renders as a giant black blob instead of a thin line. + +**Check**: every `` or `` used as a connector must have `fill="none"` as an explicit attribute. SVG defaults paths to `fill: black`. + +**Fix**: add `fill="none"` — or use the `arr` class, which already sets it. + +## 5. Text has no class, renders as default + +**Symptom**: a label looks slightly different from the rest — wrong size, wrong color, or black in dark mode. + +**Check**: every `` element must have a class: `t`, `ts`, `th`, or (in poster flowcharts only) `title`, `eyebrow`, `caption`, `anno`. No unclassed text. No `fill="inherit"`. No hardcoded `fill="black"`. + +**Fix**: add the appropriate class. `t` for single-line body, `th` for titles (bold), `ts` for subtitles and callouts. In a poster flowchart, `title` for the top label, `eyebrow` for section dividers, `caption` for the footer hook, `anno` for right-column side notes. + +## 6. Title and subtitle use the same color stop + +**Symptom**: a two-line node looks visually flat — the title and subtitle blur together. + +**Check**: inside any `c-{ramp}` box, the `th` and `ts` children must land on different stops. The template handles this automatically (th → stop 800, ts → stop 600), so this only breaks if you manually override a fill. Don't. + +**Fix**: remove the inline `fill=` override; let the template classes do their job. + +## 7. `text-anchor="end"` at low x + +**Symptom**: a label is missing from the left side of the diagram — it's actually there, but it extends past x=0 and is clipped by the viewBox. + +**Check**: for every `` with `text-anchor="end"`, the label's width must fit to the left of its x coordinate: `label_chars × char_width < x`. + +**Fix**: use `text-anchor="start"` and right-align the column manually, or move the text to a higher x. + +## 8. Color rainbow (colors cycle instead of encoding meaning) + +**Symptom**: a 5-step flowchart uses blue-teal-amber-coral-purple, one per step. Reader can't tell if the colors mean anything. + +**Check**: do colors encode categories (all "immune cells" share one color) or do they encode sequence (step-1 blue, step-2 teal)? Sequence is wrong. + +**Fix**: collapse to ≤2 ramps. Use gray for neutral/structural/sequential steps. Reserve one accent color for whichever nodes deserve emphasis — the decision point, the anomaly, the main character of the story. + +## 9. Too many boxes in a full-width row + +**Symptom**: boxes overlap each other, or text spills across box borders. + +**Check**: `N × box_width + (N - 1) × gap ≤ 600`. If you tried to fit 5+ boxes at default width (180 each), you've already overflowed. + +**Fix**: +- Shrink box_width to ≤110 (drops subtitles) +- Wrap to 2 rows +- Split into overview + detail diagrams + +## 10. Cycle drawn as a physical ring + +**Symptom**: a four-stage cycle (Krebs, event loop, GC) is laid out with boxes orbiting a dashed circle. Labels collide with stage boxes; feedback arrows point at weird angles. + +**Check**: does the diagram try to show a loop by arranging boxes in a circle? + +**Fix**: lay the stages out linearly (horizontal or vertical) and draw a single return arrow from the last stage back to the first — or simply add a small `↻ returns to start` label near the endpoint. The loop is conveyed by the return arrow, not by literal ring geometry. + +## 11. Dark-mode invisible text (bonus check) + +**Symptom**: the SVG looks fine in light mode, but text disappears in dark mode. + +**Check**: did you hardcode any `fill="#..."` or `fill="black"` on a `` or ignore the `t`/`ts`/`th` classes? If yes, dark mode won't override it. + +**Fix**: remove hardcoded fills on text. Let the template classes handle both modes. + +**Exception**: physical-color scenes (sky blue, grass green, water teal in an illustrative diagram) *should* stay the same in both modes. Hardcode those hex values deliberately. But all label text — the `` elements with callouts and titles — must use the classes. + +## 12. ` +1. Click login + + + +3. Show consent screen + +``` + +Rules: +- Label `y = arrow_y − 10`, centered between sender and receiver x. +- Arrow `y1 = y2 = arrow_y`. +- Arrow endpoints are offset ±6 from the lifeline x so the arrowhead doesn't touch the dashes: + - Left-to-right: `x1 = sender_x + 6`, `x2 = receiver_x − 6` + - Right-to-left: `x1 = sender_x − 6`, `x2 = receiver_x + 6` +- Stroke class = `arr-{sender_ramp}` — `arr-gray` for User, `arr-teal` for Client app, etc. Never use `class="arr"` (the default mono-gray arrow class) on a sequence message. +- Label text is sentence case with a numeric prefix: `"1. Click login"`, `"3. Show consent screen"`. Numbers make the reading order unambiguous when arrows cross. +- Keep labels short — verify every label fits its lane span using the budget formula in `layout-math.md`. + +## Self-messages + +A self-message stays on one actor's lifeline. Draw it as a small 16×24 rect on the lifeline, colored in that actor's ramp, with the label to the **left** (so it stays inside the diagram even for the rightmost lane). + +```svg + + +9. Validate token +``` + +Rules: +- `rect_x = lifeline_x − 8` (rect is 16 wide, centered on the lifeline). +- `rect_y = arrow_y − 12` (rect is 24 tall, centered on the arrow row). +- Label `x = rect_x − 8`, `text-anchor="end"`, `dominant-baseline="central"`, `class="ts"`. +- Label still gets its numeric prefix (`"9. Validate token"`), following the overall message order. +- Limit self-messages to 1–2 per diagram — more than that and the metaphor of "an actor talking to itself" stops reading clearly. + +## Message frames + +A **frame** is a dashed rounded rect that visually groups a contiguous run of messages under a single condition — *"Until tasks clear"*, *"Iterative research loop"*, *"Only if authenticated"*. It reads like a bracket around the enclosed messages, not like a new container. Anthropic's house-style sequence diagrams use frames for repeating or conditional sub-flows that don't warrant their own diagram. + +```svg + +Until tasks clear +``` + +Rules: +- **Dashed rounded rect**, `rx="8"`, 1px stroke in the muted gray (`#B4B2A9` light / `#5F5E5A` dark — match `.lifeline`). Use inline `stroke-dasharray="5 4"` rather than a new CSS class. +- **Horizontal span**: `x = leftmost_lane_x − 20`, `width = rightmost_lane_x − leftmost_lane_x + 40`. The frame stretches 20px past the outermost lanes it covers, so the label has clear air at the corner. +- **Vertical span**: `y = first_arrow_y − 12`, `height = last_arrow_y − first_arrow_y + 24`. Tight enough that the frame feels like a bracket, not a new section. +- **Label position**: top-left inside the frame, at `(frame_x + 10, frame_y + 12)`, `class="ts"`, `text-anchor="start"`, `dominant-baseline="central"`. Keep the label ≤24 characters. +- **No numeric prefix** on the frame label — numbering is for individual messages. The frame is meta. +- **At most 2 frames per diagram**, and **frames never nest**. Two frames can sit one above the other, but they cannot contain each other. If you need nested scoping, split into two diagrams. +- **Draw frames BEFORE the messages** in source order so the dashed border sits behind the arrows and labels. + +When to use a frame: +- A **loop** — "Until tests pass", "Until tasks clear", "Iterative research loop" +- A **conditional block** — "Only if authenticated", "When cache miss" +- A **named sub-protocol** — "Token refresh", "Retry with backoff" + +Don't use a frame to decorate a single message, and don't use one to group messages that have nothing in common — frames are a scoping tool, not a visual group. + +## Side note + +A titled annotation for the protocol itself. Uses `class="box"` with a `th` title and an optional `ts` subtitle. + +Top-left (for N ≤ 3): + +```svg + +OAuth 2.0 +Authorization code flow +``` + +Bottom-left (for N ≥ 4, because the top row is filled with actor headers): + +```svg + +OAuth 2.0 +Authorization code flow +``` + +Budget: title ≤24 chars, subtitle ≤32 chars. The box is 240 wide, padding 20 per side → usable width 200 → ~24 `th` chars or ~28 `ts` chars. + +## Layout patterns + +### 2-actor request/response (N=2) + +User asks, server responds. Two lanes at x=185 and x=495. Typical message count: 2–4. Side note at top-left. + +### 3-leg handshake (N=2, M=3) + +Classic TCP. Same layout as above, three messages going SYN → SYN-ACK → ACK. Color the initiator arrows in one ramp and the responder arrows in another — the alternation reads as the handshake. + +### 4-actor OAuth-style flow (N=4, M=8–10) + +User, Client app, Auth server, Resource server. The canonical shape. Lane centers at 100, 260, 420, 580. Side note at bottom-left. One self-message on the Resource server for the "validate token" step. + +### Async webhook (N=2 or 3) + +Sender fires an event, receiver acknowledges later. Often has a gap in the middle that you can skip — no need to draw "wait 30 seconds" as a message; just put consecutive messages in the natural reading order. + +## Parallel independent rounds + +A sub-pattern for topics where **multiple independent call/response rounds** happen in sequence but share no lifeline state — each round is its own contained exchange, and the diagram's job is to contrast the structure of the rounds rather than track continuous state on a lifeline. Image #12 (Tool calling vs. Programmatic tool calling) is the canonical case. + +**When to use.** + +- Comparing tool-call vs. programmatic-script patterns where each round is an independent call/response. +- Showing "N separate rounds of the same exchange" to communicate volume or variety. +- The right half of a 2-up comparison where each side shows a different protocol style. + +**When not to use.** + +- There's genuine shared state between rounds (conversation history, pending requests) — that's a regular sequence with full lifelines. +- There are only 2 actors and 2–4 messages — that's a regular 2-actor sequence. +- The rounds are conditional ("if X then do round 2") — that's a frame on a regular sequence. + +### Geometry — stacked rounds variant + +Each round is a **row-sized mini-exchange** between a fixed source on the left and a round-specific action box on the right. Rounds stack vertically with no shared lifeline. See `layout-math.md` → "Parallel rounds geometry". + +| Element | Coordinates | +|--------------------|-------------------------------------------------| +| Source actor | `x=60 y=120 w=120 h=60` (persistent on left) | +| Round 1 action | `x=340 y=100 w=160 h=48 rx=6` | +| Round 2 action | `x=340 y=180 w=160 h=48 rx=6` | +| Round 3 action | `x=340 y=260 w=160 h=48 rx=6` | +| Row pitch | 80 (round_y increases by 80 per round) | + +The source actor is drawn **once** at the top-left, not repeated per round. Its box height is just tall enough to span the first round's arrow row, with no lifeline extending below it — the absence of a lifeline is the signal that the source isn't carrying state across rounds. + +**Why no lifeline.** A lifeline would visually suggest that state persists; in this pattern each round is a fresh stateless call. The source box itself is the anchor — the left edge of every arrow starts at `source_right_edge`, not at a lifeline x. + +**Arrow pairs per round.** Each round is two arrows: + +```svg + +1. Call tool A + +2. Response + +``` + +Round k's arrow y values: `call_y = 100 + 24 + (k-1) × 80`, `response_y = call_y + 10`. Action box for round k: `y = call_y − 24`. + +**Action box colors.** Each round's action box **can** take a different ramp — this is the only place in a sequence diagram where color is allowed to cycle across messages. The reason: each round calls a *different tool* (file write, api call, db query), and the color communicates that variety. Use up to 3 different ramps; more than that and the reader can't remember what each ramp means. + +Source actor ramp stays **constant** across all rounds (usually `c-gray` — "Claude" or "Agent"). + +### Script-wrapper variant + +The right half of image #12 uses the same stacked-rounds shape but wraps **multiple action boxes inside a single tall script rect** that represents an inline program executing several tool calls. Uses the `script-icon` pattern from `glyphs.md`. + +| Element | Coordinates | +|----------------------|-----------------------------------------| +| Source actor | `x=60 y=120 w=120 h=60` | +| Script wrapper | `x=240 y=100 w=140 h=260 rx=6`, `c-gray`| +| `{ }` label in wrapper | `(310, 124)`, `th` | +| Divider line | `(256, 140) → (364, 140)`, `arr` | +| Wrapped action 1 | `x=252 y=152 w=116 h=48 rx=4` | +| Wrapped action 2 | `x=252 y=212 w=116 h=48 rx=4` | +| Wrapped action 3 | `x=252 y=272 w=116 h=48 rx=4` | +| External action | `x=440 y=200 w=160 h=48 rx=6` (optional)| + +The script wrapper replaces the individual action boxes with a single container that contains N smaller colored bands. Only the **script wrapper itself** gets arrows from the source — the inner bands are decorative, showing what's inside the script without each needing its own call arrow. + +```svg + + +run script +``` + +If the script calls an external tool (image #12's right half: a `file_system` external box on the far right), draw a single arrow from the script's right edge to the external box at the script's middle y. + +### Side-by-side comparison + +The most common use of parallel rounds is inside a 2-up structural subsystem layout (`structural.md` → "Subsystem architecture pattern"): left container shows the stacked-rounds variant, right container shows the script-wrapper variant. Each sibling container is 315 wide, so halve the coordinates above: + +- Source actor inside container: `x = container_x + 20, w=100 h=50` +- Action box inside container: `x = container_x + 180, w=120 h=40` +- Round pitch inside container: 64 + +See `structural.md` → "Rich interior for subsystem containers" for how this interior type registers. + +### Rules + +- **No lifelines.** If you draw lifelines, you're describing a regular stateful sequence, not parallel independent rounds. Drop them. +- **Source actor appears once.** Never duplicate the source per round — that multiplies the visual noise and breaks the "same caller, different calls" intuition. +- **Numeric prefixes still apply.** Within each round the two arrows are numbered (`1. Call tool A`, `2. Response`), and the numbering resets per round (or continues across rounds, at your choice — document it in the side note). +- **≤3 rounds per diagram.** 4+ rounds runs out of vertical space and overwhelms the comparison. Use two diagrams if you need to show more. +- **No frames around rounds.** A frame is a scoping bracket for a loop or a condition; parallel rounds are already independent, so a frame would be redundant. +- **Action ramps cap at 3.** Each round can take a distinct ramp, but stop at 3 distinct colors. + +## Rules of thumb + +- **Max 4 actors (comfortable), max 6 actors (absolute ceiling).** 5 is allowed only when every title is ≤12 chars and every subtitle is omitted. 6 requires every title ≤9 chars (so it fits the 100-wide header rect) and no subtitles, and the diagram loses its 40px horizontal safe margin — see `layout-math.md` → "Swim-lane (sequence) layout" for the full N=5 and N=6 coordinate tables. Beyond 6, split into an overview diagram plus one detail diagram per sub-flow — a 7-actor sequence diagram is guaranteed to feel cramped no matter how carefully you pack the labels. +- **6–10 messages is the sweet spot.** 4 is too sparse (use a flowchart); 12+ is too dense (split). +- **One ramp per actor**, up to 4 total. The default `[gray, teal, purple, blue]` works for most protocols. Swap ramps only when a topic needs it. +- **Number every message.** Even "obvious" single-step diagrams benefit from "1. ...". Readers scan numbers before they read words. +- **Every arrow gets a label.** Sequence diagrams are the one place in this skill where arrow labels are mandatory, not optional — the label *is* the message, and without it the diagram says nothing. +- **Sender's ramp colors the arrow.** Never use `class="arr"` (mono-gray) for a sequence message. +- **Labels above the line, never on it.** Labels float 10px above the arrow at the row midpoint, `text-anchor="middle"`, `class="ts"`. +- **No crossings within a single row.** Two messages at the same `arrow_y` collide — always move one to a new row. +- **No arcs, no curves.** Flat horizontal lines only. Self-messages use a labeled rect, not a small arc. diff --git a/skills/baoyu-diagram/references/structural-matrix.md b/skills/baoyu-diagram/references/structural-matrix.md new file mode 100644 index 0000000..2fbe748 --- /dev/null +++ b/skills/baoyu-diagram/references/structural-matrix.md @@ -0,0 +1,113 @@ +# Structural: Comparison Matrix + +Load this file when the content is a **feature comparison**: "which of these databases supports X", side-by-side feature tables, ✓/✗ grids, or any "option × attribute" matrix. + +Sub-pattern for side-by-side feature comparisons — *"which of these three databases supports transactions"*, *"which of these four frameworks has a migration path"*. The subject is a tabular matrix with **rows = features** and **columns = systems** (or the reverse). Structural diagram type is the right home because the matrix is really a structured grid of containers, just with a disciplined layout. + +## When to use it + +- The reader's question is *"which option has property X"*, and they'll compare across rows or columns. +- You have 2–5 columns and 2–8 rows. Beyond 5 columns, split into two matrices. Beyond 8 rows, promote the most important 6 and drop the rest into prose. +- Each cell's value fits in **one word or a ✓/✗ glyph**. If cells need sentences, this isn't a matrix — it's a comparison essay, which belongs in prose around the diagram. + +## Structure + +A comparison matrix is one header row (column labels) plus N body rows (attribute on the left, values in each column). The grid has no visible gridlines — the zebra-striped row backgrounds provide the horizontal separation, and the column alignment provides the vertical separation. Flat aesthetic, no table borders. + +| Element | Width / coordinates | +|--------------------|-------------------------------------------------------------| +| Attribute column | 160 wide | +| Value columns | `(600 − 160) / N` wide, equal to each other | +| Row height | 36 (single-line `t` value) or 52 (two-line title + subtitle)| +| Row gap | 0 (rows touch — the alternating fill creates the band) | +| Header row height | 40 (slightly taller than body rows) | +| Header text | `th` class, centered | +| Body attribute | `t` class, left-anchored at `col_x + 12` | +| Body value (text) | `t` class, centered in column | +| Body value (glyph) | `status-circle-check` / `status-circle-x`, centered in column| + +**Column count formula.** For 3 value columns: value_width = 440/3 ≈ 147. For 4: 110. For 5: 88. At 5 columns the value column barely fits *"Supported"*; past that, any label longer than 8 characters clips. Hard cap at 5 columns — if you have 6 systems to compare, split into *(A, B, C)* and *(D, E, F)* matrices. + +## Alternating row fills + +Zebra striping uses the `.row-alt` class from `svg-template.md` on every other body row. The header row uses `.box` with a slightly darker inline `stroke-width="1"` or the default `.box`: + +```svg + + + + + + + + + + + +``` + +Rounded corners (`rx="6"`) only on the *header* row and on the *last* body row — the middle rows are sharp-cornered so they stack seamlessly. Implementation trick: give the first body row `rx="0"` explicitly, and only round the bottom of the last row using the standard outer-wrapper approach, or accept square corners throughout for simplicity (which is also fine). + +## Cell contents + +Cells fall into three buckets: + +- **Boolean** — use `status-circle-check` (c-green) for *yes*, `status-circle-x` (c-coral) for *no*, from `glyphs.md`. Centered in the cell. No extra text label. ✓/✗ is a visual shortcut that reads faster than the word. +- **Short text** — 1–2 words like *"v2.1"*, *"Postgres"*, *"Yes"*, *"Optional"*. Use class `t`, `text-anchor="middle"`, centered at the cell's x-midpoint. +- **Tier** — when the value is a level (*"Full"*, *"Partial"*, *"None"*), still use short text. Resist the urge to map tiers to colors — that re-categorizes the data and breaks the 2-ramp budget. + +**No subtitle text inside a cell.** If a value *needs* a qualifier (*"Yes, since v3"*), the qualifier belongs in a footnote marker (*"Yes†"*) with the footnote written as `ts` text below the whole matrix. Don't stack two `ts` lines inside a 36px cell. + +## Worked example — 3-system × 5-feature comparison + +Request: *"Compare Postgres, MySQL, and SQLite on: transactions, JSON columns, full-text search, replication, on-disk encryption."* + +Plan: +- Header row: attribute column + 3 system columns. Value_width = 440/3 ≈ 147. +- 5 body rows, alternating `.box` and `.row-alt`. +- Feature names are 14px body text; system values are mostly ✓/✗ glyphs, with one short text ("Built-in"/"Plugin"). +- Single accent: none — the green/coral in the status circles already encodes meaning, and adding a ramp to the matrix would clash with the semantic greens. + +```svg + + + + Feature + Postgres + MySQL + SQLite + + + + Transactions + + + + + + + JSON columns + + + + + Full-text search + Built-in + Built-in + Plugin + + + + Replication + + + + + On-disk encryption + + +``` + +A 5-row × 4-column matrix at 36px row height lands the final body row's bottom at y=260, plus 40 bottom margin = viewBox height 300. Six rows pushes the bottom to 296 + 40 = 336 — still comfortable on the 680-wide canvas. + +**Column x-midpoints (for 3 value columns).** Attribute column center: 120. Value column centers: 160 + 147/2 = 233.5 ≈ 234, 160 + 147 × 1.5 = 380.5 ≈ 381, 160 + 147 × 2.5 = 527.5 ≈ 528. The worked example above uses 267 / 413 / 560 because it centers the value columns on their actual bounding boxes after the 40px left canvas margin — recompute from `40 + 160 + (i + 0.5) × value_width` for your own grids. diff --git a/skills/baoyu-diagram/references/structural-network.md b/skills/baoyu-diagram/references/structural-network.md new file mode 100644 index 0000000..c44926b --- /dev/null +++ b/skills/baoyu-diagram/references/structural-network.md @@ -0,0 +1,136 @@ +# Structural: Network Topology + +Load this file when the prompt asks about **network infrastructure**: "where do the wires go", "which zone is this device in", "DMZ / firewall / VPC topology", security zones, or device connectivity. The reader's question is *"what is connected to what, and across which boundary"*. + +Sub-pattern for diagrams whose subject is **where the wires go** — which devices sit where, which security zones contain them, which links are wired vs wireless. The structural diagram type is the right home for this (devices are containers, zones are outer containers, cables are arrows), but topology drawings have some conventions of their own. + +## When to use it + +- The reader's question is *"what is connected to what, and across which boundary"*, not *"what happens when a request arrives"* (that's a sequence diagram). +- You can name ≤10 devices. More than that, split into *edge topology* and *internal topology* — no single network diagram should try to show the whole datacenter. +- The zones matter as much as the devices. A network drawing without zones is just a bus or radial topology in disguise — use those sub-patterns instead. + +## No custom device icons + +baoyu is a flat-rect aesthetic. A router is a rounded rect labeled *Router*, a firewall is a rounded rect labeled *Firewall*, a cloud/Internet boundary is a rounded rect labeled *Internet*. **Do not draw a cylinder for a server, a trapezoid for a switch, a cloud silhouette for the Internet, or a shield-with-flames for a firewall.** The two-line node (type on top in `th`, name on bottom in `ts`) carries the same information with no aesthetic break: + +```svg + + + Firewall + edge-fw-01 + +``` + +The device *type* is the title; the device's specific *name* or role (hostname, function) is the subtitle. If the diagram only has generic types without specific instances, a single-line 44-tall rect with the type as the title is fine. + +## Zone containers + +Use the existing dashed-container trick from `structural.md` → "Subsystem architecture pattern", with a top-left pill label for the zone name. One container per security zone: + +| Zone | Typical label | Notes | +|----------|---------------|-----------------------------------------------------------------| +| Internet | *Internet* | Anything outside the org — clients, public DNS, SaaS endpoints | +| DMZ | *DMZ* | Reverse proxies, WAFs, public web/API frontends | +| Internal | *Internal* | Application servers, internal APIs, caches | +| Data | *Data* | Databases, object stores, message brokers — isolate from DMZ | +| Mgmt | *Management* | Bastion, monitoring, config — often spans other zones via ACLs | + +Keep containers to **≤4 zones per diagram**. A 5-zone network is better split into two diagrams (*edge flow* + *internal flow*) than crammed into one 680-wide canvas. + +The container rect uses `class="arr-alt"` — it inherits the dashed stroke from the template and dark-mode-safe coloring. The pill-shaped zone label sits in the top-left at `(container_x + 12, container_y − 8)` with class `ts` and an optional small background rect: + +```svg + + +DMZ +``` + +The label's background rect (`class="box"`) sits on top of the container's dashed top edge, masking it — the label reads as a tab attached to the zone rather than text floating inside it. + +## Wired vs wireless + +Two distinct link styles, both already in the template: + +- **Wired link** — solid `class="arr"`. Optional `ts` label on the arrow midpoint carries the bandwidth or protocol (*1 Gbps*, *VPN*, *TLS*). Follows the normal 1–3 word arrow-label rule. +- **Wireless link** — dashed `class="arr-alt"`. Same label rules. Wireless is also the right choice for *logical* links (VPN tunnels across the public internet, cross-AZ replication) where the link isn't a single physical cable. + +**Two link styles means you need a legend.** Per `design-system.md`, any diagram that uses ≥2 arrow styles with distinct meaning must emit a one-line legend. For network topology: + +``` +[──] Wired [- -] Wireless / VPN [■] DMZ [■] Internal +``` + +Place the legend at the bottom of the canvas, 20px above the bottom edge, aligned with the subject matter above it. + +## Tiered top-down layout + +Network topology almost always reads top-down because the conventional mental model is *"traffic flows from the public internet down into the protected core"*. Lay out the zones vertically, one tier per row: + +``` +Row 1 (y=60..96) Internet container +Row 2 (y=116..180) Edge container (reverse proxy, firewall) +Row 3 (y=200..264) Core container (app servers, services) +Row 4 (y=284..348) Access / data container (databases, caches) +``` + +Each tier is a separate dashed container. Links run **between** containers, not inside — intra-zone links are assumed (everything in the same zone can reach everything else in that zone via the switch fabric) and cluttering the diagram with them wastes space. + +## Worked example — 3-tier network + +Request: *"Draw a 3-tier network: public internet talking to a DMZ firewall, which routes to internal app servers, which talk to a database."* + +Plan: +- 3 zones: Internet, DMZ, Internal. +- 4 devices: Client (Internet), Firewall (DMZ), App (Internal), DB (Internal). +- All wired, so no legend needed — single arrow style. +- Direction: top-down. +- Colors: all `c-gray`. Devices aren't categorized by function strongly enough to warrant a ramp split. + +```svg + + + + + Internet + + + Client + + + + + + + + + DMZ + + + Firewall + edge-fw-01 + + + + + + + + + Internal + + + App server + web-api-* + + + + Database + postgres-primary + + + + +``` + +Notice that the diagram shows inter-zone links (Client → Firewall → App) *and* one intra-zone link (App → DB). The intra-zone App → DB link is drawn because it's the whole point of the diagram — otherwise the reader wouldn't know the DB is in the *Internal* zone, not a fourth *Data* zone. When you leave intra-zone links out, make sure the device names alone answer *"who talks to whom"*. diff --git a/skills/baoyu-diagram/references/structural.md b/skills/baoyu-diagram/references/structural.md new file mode 100644 index 0000000..7097827 --- /dev/null +++ b/skills/baoyu-diagram/references/structural.md @@ -0,0 +1,659 @@ +# Structural Diagram + +For concepts where physical or logical containment matters — "things inside other things". A large outer container with smaller regions inside it, labeled, and optionally connected by arrows or external inputs and outputs. + +## When to use + +- "How is X organised" / "what's the architecture" / "where does Y live" +- File systems (blocks inside inodes inside partitions) +- Cloud topologies (instance inside subnet inside VPC) +- Biological containment (organelles inside cells) +- Service architectures with clear boundaries (microservices inside a service mesh inside a cluster) +- CPU cache hierarchies (L1 inside core, L2 shared between cores) + +**When not to use:** if the explanation is about *what happens over time*, reach for a flowchart. Containment diagrams answer "where", flowcharts answer "what order". + +## Container rules + +### Outer container + +- Large rounded rect +- `rx="20"` (or up to 24 if the diagram needs a softer feel) +- Light fill (stop 50) and 0.5px stroke (stop 600) — both come automatically from the `c-{ramp}` class +- Label sits at the top-center inside the container, 20–30px down from the top edge, as a `th` title +- A short subtitle can sit 18px below the title, as a `ts` + +Example frame: + +```svg + + + VPC + us-east-1 + +``` + +### Inner regions + +- Medium rounded rects, `rx="10"` (8–12 range) +- Different color ramp from the parent (see "Color pairing" below) +- 20px minimum padding between the inner region edge and the container edge on all sides +- 16px minimum gap between adjacent inner regions +- Same dimensions as a flowchart node: 44px tall for single-line, 56px for two-line, or larger rects (100+ tall) to represent regions of space + +### Nesting depth + +Maximum **2–3 levels**. Deeper nesting gets unreadable at 680px width. If you need four levels, split into two diagrams: the outer two levels in one diagram, then a detail diagram zooming into one of the inner regions. + +## Color pairing + +When you nest containers, pick ramps that *relate* rather than clash. A library branch + circulation desk is clearly two parts of one building, so use related cool ramps (green outer, teal inner). A library branch + café is two functionally different spaces, so use one cool and one warm (green + amber). + +**Rule of thumb**: +- Same-category nesting → related cool ramps (green + teal, blue + teal, purple + pink) +- Different-category nesting → one cool + one warm (green + amber, blue + coral) +- Structural/neutral containers → gray + any accent + +The key point: don't reuse the same ramp on parent and child. The template's `c-{ramp}` classes resolve to fixed fill/stroke stops, so a nested `c-blue` inside a parent `c-blue` gives identical fills — the hierarchy flattens visually. + +## Layout + +Inner regions sit **side by side** inside the container, not stacked unless you have a tall narrow container (e.g., a CPU cache hierarchy with L1 on top of L2 on top of L3). Horizontal layout is the default because the viewBox is 680 wide — you have room for 2–3 regions across but only 1–2 stacked vertically before it gets too tall. + +Example layout math for 2 inner regions inside a 560-wide container: + +``` +container: x=60, width=560 +inner padding: 20 on each side → inner safe area is x=80 to x=600, width 520 +gap between regions: 20 +region width: (520 - 20) / 2 = 250 +region A: x=80, width=250 +region B: x=350, width=250 +``` + +For 3 inner regions: + +``` +inner safe area: width 520 +gaps: 2 × 20 = 40 +region width: (520 - 40) / 3 ≈ 160 +regions at x = 80, 260, 440 (all width 160) +``` + +## External inputs and outputs + +Arrows entering or leaving the container represent external data, requests, or physical flows. They sit outside the container with arrows pointing in or out: + +```svg +Request + +``` + +Rules: +- External labels are ≤2 words ("Request", "Output", "Cold water", "Sunlight"). Long labels belong in prose around the diagram. +- Place the text label directly above the arrow, `text-anchor="middle"` aligned with the arrow's horizontal center. +- Leave 2px of gap between the arrow endpoint and the container edge. +- If you have multiple inputs/outputs, stack them vertically at the same x coordinate with 40–60px spacing between them. + +## Internal arrows between regions + +Sometimes two inner regions talk to each other. Draw an arrow between them with a short text label centered above or below: + +```svg +Books + +``` + +Keep these labels to 1–2 words. If the arrow's meaning is obvious from the region names (e.g., "Request Router" → "Auth Service"), skip the label entirely. + +## What goes inside a region + +Text only. A region is not a container for another flowchart — if you find yourself drawing a nested flowchart inside a region, you've reached the nesting limit. Split it into a separate diagram. + +Each region has: +- A title (`th` class, 14px bold) +- An optional subtitle (`ts` class, 12px, ≤5 words) describing what happens there +- Nothing else. No icons, no illustrations, no mini-boxes inside + +**Exception — subsystem architecture pattern.** When the topic is "two or three parallel subsystems cooperating" (see "Subsystem architecture pattern" below), each sibling container *may* contain a short internal flow of up to 5 flowchart-style nodes with arrows between them. This is the only structural layout where a region holds more than text, and the 5-node cap is a hard ceiling. If you need more than 5 nodes inside a region, the pattern isn't the right shape — split the inner flow into its own flowchart diagram and keep the structural diagram at the containment level. + +## Worked example + +Request: "AWS VPC with a web tier and a database tier" + +Plan: +- Outer container: VPC (green, subtitle "us-east-1") +- Two inner regions side by side: "Public subnet" (teal, subtitle "Web servers") and "Private subnet" (blue, subtitle "Database") +- External input arrow: "Internet" → public subnet +- Internal arrow: public subnet → private subnet, labeled "SQL" + +Layout: +- Container at x=80, y=40, width=540, height=280 → `rx="20"` +- Public subnet at x=120, y=140, width=220, height=140 → `rx="10"` (inside container with 40px padding from left, 60px below title) +- Private subnet at x=380, y=140, width=220, height=140 +- Container title at y=72, subtitle at y=92 +- External "Internet" arrow: from (48, 210) to (78, 210) pointing right, with text at (48, 196) +- Internal "SQL" arrow: from (350, 210) to (378, 210), with text at (364, 196) + +viewBox: max_y = 40 + 280 = 320 → H = 340 + +## Subsystem architecture pattern + +A variant for topics that have **two or three parallel subsystems cooperating** — the canonical shape is "two sibling dashed-border containers, each holding a short internal flow, with a labeled cross-system arrow linking them". Unlike the default structural diagram (outer container + inner regions), this pattern places the subsystems *side by side at the top level* and lets each one carry its own mini-flowchart. + +**When to use**: + +- "Pi session + background analyzer" — foreground conversation and a separate analyzer running in parallel +- "Prompt engineering vs. context engineering" — two frameworks shown side by side with their internal loops +- "Producer subsystem + consumer subsystem" — a queue-like split where each side has its own internal pipeline +- "Foreground + background" workflows — any time you're comparing two pipelines that share data across a boundary + +**When not to use**: + +- Containment — one thing is *inside* another. Use the default outer-container pattern instead. +- A single pipeline with no peer system. That's just a flowchart. +- More than 3 siblings. Three is the hard ceiling; four already blows the horizontal budget. + +### Sibling containers + +Each sibling is a **dashed-border** rounded rect. Use **inline `stroke-dasharray="4 4"`** on the rect rather than a CSS class — dashed is a one-off container style, not a reusable shape property, so the template's class list stays stable. The stroke color still comes from a `c-{ramp}` class on the wrapping `` so dark mode works. + +```svg + + + Pi session + Foreground conversation + +``` + +Rules: +- **Fill is `none`.** The dashed border reads as "this is a schematic region", not "this is a solid box". A filled sibling container fights with its internal nodes for the reader's attention. +- **Stroke comes from the `c-{ramp}` class.** Dark-mode handling is automatic. +- **Title at top-left**, not top-center — siblings compete for the top-center axis, so pull each title to the inside corner. Title at `(container_x + 20, container_y + 24)`, `text-anchor="start"`. +- **Optional subtitle** (`ts`) directly below the title at `y = title_y + 18`, also left-anchored. ≤5 words. +- **Pick ramps so the siblings feel related but distinct.** Same-category (foreground + background → gray + teal). Different-category (prompt engineering + context engineering → purple + teal). Never use the same ramp on both siblings. + +### Layout math (2-up) + +See `layout-math.md` → "Sibling subsystem containers (2-up)" for the full formula. The standard geometry: + +``` +container A x=20, y=40, w=315, h=260, rx=20 +container B x=345, y=40, w=315, h=260, rx=20 +gap = 10 px +interior A x=40 to x=315 (width 275) +interior B x=365 to x=640 (width 275) +``` + +### Internal nodes inside a sibling + +A sibling container may hold **up to 5 flowchart-style nodes** with arrows between them — this is the explicit exception to the "region is text only" rule in the "What goes inside a region" section above. Use the standard flowchart node templates (`c-{ramp}` rect 44 or 56 tall). Center each node at the interior x (`container_x + container_w/2`) unless there's a specific left-to-right sub-flow. + +Keep the internal flow **vertical** inside each sibling. The 275px interior width isn't enough for a horizontal pipeline of 3+ nodes with proper breathing room, and a vertical stack gives the cross-system arrow somewhere clean to land. + +Rules: +- **≤5 nodes per sibling**. Hard cap. More than that and the pattern isn't the right shape. +- **Same title color as the container** — the internal nodes inherit the sibling's ramp unless one specific node deserves a second accent color. +- **Node width ≤ 235** (interior 275 minus 20px padding each side), typically 180. +- **No nested sibling containers.** A sibling cannot itself be a subsystem architecture diagram. + +### Cross-system arrows + +The point of the pattern is usually the **labeled arrows** crossing between siblings. Draw them across the 10px container gap. Unlike the external-input arrows at the top of this file, cross-system arrows get a label above (not beside) and use the solid `.arr` class. + +```svg +Event + +``` + +Rules: +- **Label ≤3 words.** "Event", "Session context", "Summary write-back". Anything longer belongs in prose. +- **Label position**: centered horizontally at the gap midpoint (`x=340` for the 20-345 layout), `y = arrow_y − 10`, `text-anchor="middle"`, `class="ts"`. +- **Arrow row**: `y` matches the row of the source and target nodes so the line is flat. If the source is on row 1 and the target on row 2, pick the row closer to where the reader's eye lands (usually the target). +- **Bidirectional exchanges**: draw two separate single-headed arrows at slightly offset y values (`y` and `y + 12`), each with its own label. Never use a double-headed marker — the asymmetry of the two labels *is* the information. + +### Worked example — Pi session + Background analyzer + +Two siblings: a foreground conversation (`Pi session`, gray) and a background analyzer (`Background analyzer`, teal). The session emits events to the analyzer; the analyzer writes summaries back into the session's memory. + +Plan: +- Container A "Pi session" at x=20, w=315, h=260 +- Container B "Background analyzer" at x=345, w=315, h=260 +- Three internal nodes in each sibling, stacked vertically +- Two cross-system arrows: row 1 left→right labeled "Event", row 3 right→left labeled "Summary" +- viewBox: `max_y = 40 + 260 = 300` → H = 320 + +```svg + + + + + + + Pi session + Foreground conversation + + + + + Background analyzer + Runs in parallel + + + + + User turn + + + + Pi responds + + + + Memory updated + + + + + Analyze turn + + + + Write summary + + + + Push to memory + + + + + + + + Event + + + Summary + + +``` + +Notes on this example: +- The two sibling containers are drawn **first** in source order so every internal node paints on top of the dashed border. +- The cross-system arrows are labeled above and span the 10px gap plus the 20px padding on each side (so the actual horizontal run is 267 → 412 = 145px). +- The "Event" label sits above the top cross-system arrow, the "Summary" label above the bottom one — both ≤2 words, both `class="ts"`, both `text-anchor="middle"` centered at x=340 (the midpoint of the 10px gap). +- Row-to-row internal arrows inside each sibling are standard vertical `.arr` lines between node top/bottom edges with a 10px stand-off. + +## Bus topology sub-pattern + +For topics where N agents all publish to and subscribe from a **shared message bus** — *"multiple workers coordinating via a central channel"*, image #7 (Message Bus) in the Anthropic reference set. A central horizontal bar represents the bus; agents sit in a row above and below it, each connected via a pair of offset arrows (see `glyphs.md` → "Publish/subscribe arrow pair"). + +**When to use.** + +- "Message bus / event bus coordination" +- "Pub/sub topology with N publishers and N subscribers" +- "Shared queue everyone reads and writes" +- The reader needs to feel that *no agent talks to another agent directly* — all coordination passes through the bus. + +**When not to use.** If the coordination is point-to-point (agent A only talks to agent B), it's just a structural arrow. If there's a clear orchestrator controlling the workers, use the "Rich interior for subsystem containers" fan-out variant below (image #9 left half), not the bus topology. + +### Geometry + +The bus bar is the visual spine of the diagram. Agents fan out above and below it. See also `layout-math.md` → "Bus topology geometry". + +| Element | Coordinates | +|----------------------|-----------------------------------------------------------| +| Bus bar | `x=40 y=280 w=600 h=40 rx=20`, class `c-amber` or `c-gray`| +| Bus label | `(340, 304)`, class `th`, `text-anchor="middle"` | +| Top agent row y | `80` (box top); `h=60` (two-line) | +| Bottom agent row y | `400` (box top); `h=60` | +| Agent row box width | 140 (three agents) or 180 (two agents) or 110 (four) | + +For **N=3 agents per row** (6 total), center the three agents at `x = 170, 340, 510` with `w=140`. For N=4 per row (8 total), center at `x = 120, 260, 420, 560` with `w=110`. For N=2 per row (4 total), center at `x = 180, 500` with `w=180`. + +The bus bar uses `c-amber` when it's the "shared channel" of a message-bus system (amber = attention, the central thing everyone looks at) or `c-gray` when it's just structural scaffolding. Never use a cool ramp for the bus bar — the bus should feel active. + +### Arrow pairs + +Each agent connects to the bus with a **pair** of offset arrows: Publish (agent → bus) and Subscribe (bus → agent). The pair uses an 8px horizontal offset so the two arrows read as parallel, not concentric. See `glyphs.md` → "Publish/subscribe arrow pair" for the exact template. + +For the top row, each agent's bottom edge is at `y=140`, the bus top is at `y=280`, so the arrow channel is 140px tall. Place the Publish/Subscribe labels at the channel midpoint (y=210): + +```svg + + + +Publish +Subscribe +``` + +For the bottom row, mirror the y coordinates: agent top at `y=400`, bus bottom at `y=320`. The Subscribe arrow now goes up out of the bus into the agent; Publish goes down out of the agent into the bus. + +### Labels on the bus bar + +The bus bar is **always labeled** — a reader who can't tell if the central bar is "a bus" or "a queue" or "a shared log" will read the diagram wrong. Put a single `th` label at the bar's center: "Message bus", "Event bus", "Shared log", "Task queue". If the bus semantics need a second word, use a `ts` subtitle below the label (still inside the 40px-tall bar, `y = bar_y + 28`). + +### Dark mode + +All the bus-topology elements use `c-{ramp}` classes and stock `arr` utilities, so dark mode is automatic. The only thing to watch: if you used inline `stroke="#..."` on the Publish/Subscribe arrows, they won't invert. Use the unadorned `.arr` class and let the template's dark-mode override handle color. + +## Radial star topology sub-pattern + +For topics where a **central hub** is surrounded by N peripheral satellites that all talk to it bidirectionally — *"shared state with four workers"*, image #8 (Shared State). The hub is a single labeled box in the center; the satellites are smaller boxes at the four corners (or at radial offsets for N≠4). + +**When to use.** + +- "Shared state store with multiple accessors" +- "Central coordinator with N workers" where coordinator semantics are read/write, not command/control +- "Hub-and-spoke" architectures + +**When not to use.** If the hub is the orchestrator *and* issues commands (one-way), it's a fan-out, not a radial star. If the satellites talk to each other (not just to the hub), the topology is a mesh, not a radial star — that's usually a sign to switch to a bus topology instead. + +### Geometry (N=4) + +See also `layout-math.md` → "Radial star geometry (3 / 4 / 5 / 6 satellites)". + +| Element | Coordinates | +|----------------------|--------------------------------------------| +| Hub | `x=260 y=280 w=160 h=80 rx=10` | +| Satellite TL | `x=60 y=120 w=160 h=60` | +| Satellite TR | `x=460 y=120 w=160 h=60` | +| Satellite BL | `x=60 y=460 w=160 h=60` | +| Satellite BR | `x=460 y=460 w=160 h=60` | +| Hub center | `(340, 320)` | +| Satellite centers | `(140, 150)`, `(540, 150)`, `(140, 490)`, `(540, 490)` | + +Hub is 80 tall to hold a 3-line label (title + subtitle + doc-icon if used). Satellites are 60 tall for a standard two-line label. viewBox H ≈ 560. + +### Arrow pairs (bidirectional) + +Each satellite connects to the hub with two single-headed arrows offset by 8px **perpendicular to the arrow direction**. For the top-left satellite, the arrow runs diagonally from `(220, 180)` to `(260, 280)` (approximately); offset the two lines by `(±4, ∓4)` to get clean parallel channels: + +```svg + + + +``` + +Label each pair with a single `ts` label sitting *next to* the pair, not between the two lines (the 8px offset is too narrow for a label to fit). For diagonal pairs, place the label at the satellite end: + +```svg +Read / write +``` + +Or use one label per direction if the semantics differ (`Query` outbound, `Result` inbound). + +### Hub content + +The hub is the one place in a structural diagram where a **decorative icon** is allowed: drop a `doc-icon` or `terminal-icon` from `glyphs.md` into the lower half of the hub rect to reinforce what kind of hub it is. See `glyphs.md` → "Document & terminal icons" for placement math. + +```svg + + + Shared state + Task artifacts + + + + + + + + + +``` + +### Color budget + +The hub takes the accent ramp (amber is the convention for shared-state hubs — everyone looks at it). Satellites stay **neutral** (`c-gray`) unless one satellite is distinguished from the others. Mixing four different ramps on four satellites is "rainbow" — it implies four different kinds of worker, which isn't what a radial star is saying. + +### N≠4 variants + +See `layout-math.md` for the full coordinate table. Summary: + +- **N=3**: satellites at `(140, 100)`, `(540, 100)`, `(340, 500)` (two on top, one bottom center). +- **N=5**: four corners as in N=4, plus one at `(340, 100)` top center. +- **N=6**: three on top at `x=100, 340, 580` and three on bottom at the same x's (y=100 top, y=500 bottom). + +Beyond N=6 the pattern doesn't fit the 680 viewBox — switch to a bus topology. + +## Rich interior for subsystem containers + +The base subsystem-container rule from "Subsystem architecture pattern" above says each sibling holds *up to 5 flowchart-style nodes in a vertical column*. This sub-section extends that rule: a sibling may instead hold **any one** of the following interior layouts. Each has its own geometry within the 315-wide container; mix-and-match across siblings is fine (e.g., container A holds a checklist, container B holds a DAG). + +Pick the interior type that matches the subsystem's semantics: + +| Interior type | Use for | See | +|---------------------------|--------------------------------------------------------|----------------------------------------------| +| Vertical flowchart | Sequential pipeline (the default) | "Internal nodes inside a sibling" above | +| Fan-out (hub + ≤3 branches) | Orchestrator dispatching to workers (image #9 left) | Below | +| Queue + vertical fan-out | Worker pool with a shared queue (image #9 right) | Below | +| Mini bus topology | Bus-coordinated workers (image #10 right) | Below | +| Checklist | Task list using `checkbox-*` glyphs (image #4 left) | `glyphs.md` → "Checkboxes" | +| DAG (≤6 nodes) | Task graph with dependencies (image #4 right) | Below | +| Nested container + gadget | Thing-within-a-thing + attached tool (image #14) | "Attached gadget box" below | + +### Fan-out interior + +Hub box + three worker boxes in a vertical column, inside a 315-wide container at x=20 or x=345. + +| Element | Container A (x=20) | Container B (x=345) | +|---------------|---------------------------------|---------------------------------| +| Hub box | `x=50 y=120 w=120 h=44 rx=6` | `x=375 y=120 w=120 h=44 rx=6` | +| Worker 1 | `x=195 y=100 w=120 h=44` | `x=520 y=100 w=120 h=44` | +| Worker 2 | `x=195 y=160 w=120 h=44` | `x=520 y=160 w=120 h=44` | +| Worker 3 | `x=195 y=220 w=120 h=44` | `x=520 y=220 w=120 h=44` | +| Bend channel | `x=185` | `x=510` | + +Route each worker arrow as a short L-bend: hub right-edge → bend channel → worker left-edge. The same routing logic as `flowchart.md` → "Vertical fan-out", scaled to the 315-wide container. + +### Queue + vertical fan-out interior + +Same as the fan-out interior but the hub box carries a `queue-slot` row in its bottom half (see `flowchart.md` → "Queue glyph inside a box"). Hub box height grows to 52 to accommodate the glyph row: + +| Element | Coordinates (container A) | +|---------------|-----------------------------------| +| Hub (queue) | `x=50 y=116 w=120 h=52 rx=6` | +| Workers | same as fan-out interior | + +Hub title `y = 134`, queue row `y = 146`, 4 slots at `x = 62, 82, 102, 122`. (Fewer slots than the full 6 because the container is narrow.) + +### Mini bus topology interior + +A small bus bar and 2–3 mini-agents above/below it, scaled down to fit inside a 315-wide container: + +| Element | Container A (x=20) | +|------------------|-----------------------------------| +| Bus bar | `x=40 y=200 w=275 h=28 rx=14` | +| Bus label | `(177, 218)`, `th` | +| Top agent 1 | `x=50 y=110 w=100 h=44` | +| Top agent 2 | `x=195 y=110 w=100 h=44` | +| Bottom agent 1 | `x=50 y=260 w=100 h=44` | +| Bottom agent 2 | `x=195 y=260 w=100 h=44` | + +The mini bus drops the Publish/Subscribe text labels (no room) and just shows the offset arrow pairs — the outer container's title already names the pattern. Use `c-amber` on the bus bar. + +### DAG interior + +Up to 6 small boxes connected by L-bend arrows, representing a task graph with dependencies. Use this for image #4's "Tasks" right-side panel. + +Boxes are small (80 × 32) and laid out on a 3-column × 2-row grid inside the container: + +| Grid position | x (container A) | x (container B) | +|-----------------|-----------------|-----------------| +| Column 1 | 40 | 365 | +| Column 2 | 145 | 470 | +| Column 3 | 250 | 575 | + +Box height: 32. Row y: `120, 200`. `rx=4` (smaller than standard 6 because the boxes are tight). + +Dashed future-state nodes (`arr-alt` class — see "Dashed future-state node" below) intermix with solid nodes in the same grid, representing the not-yet-scheduled parts of the graph. + +### Checklist interior + +A column of `checkbox-*` glyphs plus `t` labels inside the container. See `glyphs.md` → "Checklist rows" for row geometry. For a 315-wide container: + +- First row at `y = 116` +- Row pitch: 22 +- Checkbox anchor at `x = container_x + 20` +- Label anchor at `x = container_x + 42` +- Label width budget: container interior width − 42 − 20 = 253 px (≈31 Latin / 16 CJK chars) + +Up to 8 rows fit in a 260-tall container (8 × 22 = 176, plus header + padding). + +## Mixed arrow semantics + +Sub-pattern for diagrams — especially advisor-strategy and multi-agent architectures (image #11) — where **multiple kinds of relationships** exist on the same canvas: synchronous call, asynchronous notification, read/write data flow, and a reentry loop. Using `.arr` for all of them strips the distinction; using random colors for different line types triggers the "color rainbow" pitfall. + +The solution is a **4-class arrow vocabulary** with a tiny legend strip inside the container. + +### The vocabulary + +| Arrow style | CSS class | Semantic | Typical label | +|---------------------------------|-------------------|--------------------------------------------|---------------------| +| Solid 1.5px | `arr` / `arr-{ramp}` | Synchronous call, required path | "calls", "invokes" | +| Dashed 1.5px | `arr-alt` | Asynchronous, optional, conditional | "notifies", "may call" | +| Two offset single-headed arrows | `arr` ×2 | Bidirectional read/write on shared resource | "read / write" | +| U-shaped reentry path | `arr` | Main loop, reentry to top of flow | "loop" | + +This is the only place in the skill that **four distinct line styles** appear in one diagram — elsewhere, two is the cap. The cost of richness is a required legend strip. + +### Legend strip + +Place a one-line mini-legend inside the container at the top, below the container title, using `ts`-class text and tiny 20px example line segments: + +```svg + + + + calls + + + notifies + + + + read / write + +``` + +Keep the legend to 3–4 entries (the ones that actually appear in the diagram — don't document every possible edge type). The legend is always `ts` muted, never accent color — it's meta-information. + +### U-shaped reentry loop + +A solid arrow path that exits the right side of the final node, arcs around the bottom of the diagram, and re-enters the left side of the first node. Used for "main loop runs every turn" semantics. + +```svg + +runs every turn +``` + +The horizontal return channel (`y=330` in the example) must lie in empty space below all other boxes. If it can't, use a `↻ runs every turn` text label next to the final node instead, same as the simple-flowchart return convention. + +### Pitfall + +Don't invent new arrow classes (`.arr-read`, `.arr-write`, `.arr-async`). The four-style vocabulary above is comprehensive; anything else is a template change and breaks the "minimal CSS" rule. If your diagram genuinely needs a 5th semantic, you're asking one diagram to do too much — split it. + +## Multi-line box body + +Most structural boxes hold one or two lines of text. Advisor-strategy and agent-role diagrams (image #11) sometimes need **three-line boxes**: a title, a role/kind subtitle, and a meta line like "runs every turn" or "executor · sonnet". This sub-section documents the layout. + +### Geometry + +| Element | Coordinates | +|-----------------|------------------------------------| +| Host rect | `h = 80`, `rx=6`, `w ≥ 180` | +| Title `th` | `y = rect_y + 22`, `text-anchor="middle"` | +| Role `ts` | `y = rect_y + 42`, italic (see below) | +| Meta `ts` | `y = rect_y + 62`, muted (see below) | + +Row pitch is 20 (tight). The 80px total is the minimum — if you need more breathing room, grow to 88 or 92, but never less than 80. + +### Style variations on the two subtitles + +The `ts` class renders as muted 12px in both modes. For the role line (middle), apply italic via an inline `font-style="italic"` attribute. For the meta line (bottom), leave it plain `ts` — it inherits the muted color automatically. + +```svg + + + Advisor + opus · reasoning + runs every turn + +``` + +### Pitfall + +Three-line boxes are dense. Don't use them when a two-line box will do. If you find yourself adding a third line *to every box* in the diagram, the diagram is carrying too much information — move the metadata out to the annotation column or a caption. + +## Attached gadget box + +A small "attached tool" box that hangs off a parent box via a short connector with no arrowhead. Used in image #14 (Code execution vs. Dedicated tool) to show that "Tools" is an attached capability of the Claude box, not a peer or a pipeline step. + +### Geometry + +| Element | Coordinates relative to parent | +|---------------|------------------------------------------------------| +| Connector | from `(parent_cx, parent_bottom)` to `(parent_cx, parent_bottom + 24)`, class `arr` **without** `marker-end` | +| Child rect | `x = parent_cx − 60`, `y = parent_bottom + 24`, `w=120 h=56 rx=6` | +| Child title | `y = child_y + 22`, `th` | +| Child subtitle | `y = child_y + 40`, `ts` | + +The child is centered under the parent. The short connector is a plain line (no arrow) — the gadget is attached, not called. + +### Color + +The gadget box inherits the parent's ramp if it's a direct extension (Claude + Tools → both `c-amber`). If the gadget is a *distinct thing attached to* the parent (Claude + External database → `c-amber` Claude + `c-gray` database), color them separately. + +### Pitfall + +Don't over-use this. One attached gadget per diagram is fine; three or more and the gadgets start to look like real workflow steps. If you need to attach three things to a parent, you're describing a fan-out, not gadgetry. + +## Dashed future-state node + +Sub-pattern for diagrams that distinguish **current state** (active, realized) from **future state** (planned, not yet scheduled) — image #4's "Tasks" panel, where Task 4 sits ghosted in the DAG because it hasn't been assigned yet. + +### The rule + +Use the existing `.arr-alt` class on the rect. It already provides `fill: none`, `stroke-width: 1.5`, `stroke-dasharray: 5 4`, and dark-mode handling: + +```svg + + + Task 4 + +``` + +**Do not** create a new `.box-future` CSS class. The template's class list stays stable — future-state styling is fully expressible with what already exists. + +**Do not** put a title (`th`) inside a dashed future rect. Future-state is demoted — use only `ts` (12px muted) for the label. A dashed rect with a bold title reads as contradiction: "this is a real thing but it's tentative?" — pick one. + +### Mixing with active nodes in a DAG + +When a DAG contains both active (solid) and future (dashed) nodes, keep the geometry identical across both types — same rect width, height, rx, y-coordinate. The only difference should be the stroke dasharray and the text weight. Any other difference (padding, shadow, label size) confuses the reader. + +Route arrows into a dashed rect with the `.arr-alt` class too, and *out* of a dashed rect with `.arr-alt` — the "not yet scheduled" status is contagious forward in the graph. Arrows going *from a solid into a dashed* use solid `.arr` (the active task is scheduled to emit into the future task once it runs). + + +## Network topology sub-pattern + +See `references/structural-network.md`. **Load it when the prompt asks about network infrastructure**: "where do the wires go", "which zone is this device in", "DMZ / firewall / VPC topology", security zones, or device connectivity. + +## Comparison matrix sub-pattern + +See `references/structural-matrix.md`. **Load it when the content is a feature comparison**: "which of these databases supports X", side-by-side feature tables, ✓/✗ grids, or any "option × attribute" matrix. + +## Common failure modes + +- **Hierarchy flattens** — parent and child use the same `c-{ramp}` class. Fix: pick different ramps. +- **Overflowing inner regions** — two 250-wide regions + 20 gap + 40 padding = 560, fits a 560-wide outer container exactly with no breathing room. Widen the outer container to 580 or shrink the inner regions. +- **External arrow with `text-anchor="end"` at low x** — the label extends past x=0 and clips. Use `text-anchor="middle"` for centered labels or `"start"` for labels that extend right. +- **Trying to draw literal shapes for the container** — don't draw an actual cell ellipse or a literal server tower. Rounded rects with clear labels read better than shapes that try to be literal. Save illustrative drawing for `illustrative.md` type diagrams. +- **Subsystem pattern with 6+ internal nodes** — the ≤5-node cap per sibling is the ceiling. Beyond that, the internal flow wants to live in its own flowchart diagram, and the structural diagram should step up one level to just the containment picture. +- **Subsystem pattern used for containment** — if one subsystem is logically *inside* the other, don't make them siblings. Nest them the normal way and skip the dashed-border trick. diff --git a/skills/baoyu-diagram/references/svg-template.md b/skills/baoyu-diagram/references/svg-template.md new file mode 100644 index 0000000..d7423cd --- /dev/null +++ b/skills/baoyu-diagram/references/svg-template.md @@ -0,0 +1,219 @@ +# SVG Template + +Every diagram this skill produces starts with the same boilerplate: root `` element, an embedded `