---
title: "Glyph"
description: "Render an answer as ASCII art plus semantic emojis inline with no setup questions: one render per reply, verdict first. Use for any answer with shape: status, inventories, audits, budgets, comparisons, rankings, pipelines, 'what is using X', or any ad-hoc 'show me X visually' ask. Not for definitions, conceptual explanations, or one-liner asks. For a full multi-artifact plan playground, use visualize-plan instead."
canonical: "https://orchestkit.yonyon.ai/docs/reference/skills/glyph"
---

# Glyph

Render an answer as ASCII art plus semantic emojis inline with no setup questions: one render per reply, verdict first. Use for any answer with shape: status, inventories, audits, budgets, comparisons, rankings, pipelines, 'what is using X', or any ad-hoc 'show me X visually' ask. Not for definitions, conceptual explanations, or one-liner asks. For a full multi-artifact plan playground, use visualize-plan instead.

<span className="badge badge-blue">Command</span> <span className="badge badge-green">low</span>

```bash title="Invoke"
/ork:glyph
```

<ContextualSkillSidebar slug="glyph" />

> **Glyph** Render an answer as ASCII art plus semantic emojis inline with no setup questions: one render per reply, verdict first. Use for any answer with shape: status, inventories, audits, budgets, comparisons, rankings, pipelines, 'what is using X', or any ad-hoc 'show me X visually' ask. Not for definitions, conceptual explanations, or one-liner asks.

## Examples

Glyph draws the answer instead of describing it: an inventory with sections and totals, a
status board, a comparison, a flow, in plain box-drawing characters that survive any
terminal, with a small fixed set of emojis that each mean one thing. It does this inline,
right now, with no setup questions. One render per reply, up to about 50 lines, 76 cells
wide, verdict first. If the honest drawing needs more than that, it is not a chat answer
any more and glyph hands off to a page instead.

### The two picks it makes for you

| Pick | Options | Default | It escalates when |
|---|---|---|---|
| **audience** | operator (dense, internal names allowed) or novice (plain words, an analogy first) | operator | you name a reader who is not you ("for Nir", "explain to the client", "eli5") |
| **surface** | chat (inline, one render up to ~50 lines) or page (a served HTML explainer) | chat | the drawing needs more than ~50 lines, or it has to persist, or you asked for a file |

Both picks are announced in one line before the render, so you can override with a word.

### The reference render: an inventory

`/ork:glyph what is using my disk` on a 460 G Mac. One prose line with the verdict, then
the render, then what the numbers told me. This is the shape every inventory-like answer
takes (disk, spend, backlog, dependencies, hooks): header meter, traffic-light sections,
rows of icon / label / bar / value / action, a total per section, one arithmetic summary
line, caveat lines for the numbers that lie.

87 G comes back without asking anyone; 39 G more is real work and needs a yes.

```
🖥️  MACINTOSH HD  ·  /System/Volumes/Data
[▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░]
 used 355 G    free 73 G    83 % full · 460 G total

────────────────────────────────────────────────
🟢 SAFE, pure caches; nothing lost, tools refill them
────────────────────────────────────────────────
🐳 Docker dangling images+build cache  ▓▓▓▓░░░  14.5 G  docker prune
🌐 Chrome caches (Google + Island)     ▓▓░░░░░   8.9 G  browsers refill
🐍 uv cache  ~/.cache/uv  (unused)     ▓▓░░░░░   6.9 G  uv cache prune
📦 npm cache  ~/.npm                   ▓▓░░░░░   6.2 G  npm cache clean
🌐 Playwright + Puppeteer browsers     ▓░░░░░░   2.5 G  re-fetched
🗂️ JetBrains caches, 3 old PyCharms    ▓░░░░░░   2.2 G  3 old versions
🐳 Docker installer cache              ▓░░░░░░   2.1 G  old installers
🗂️ VSCode ShipIt logs + payloads       ▓░░░░░░   1.4 G  update leftovers
🗑️ Trash + Homebrew cleanup            ▓░░░░░░   0.6 G  brew: 148 MB
                                       ── 45.3 G

────────────────────────────────────────────────
🟡 REGENERABLE, safe, but the next run rebuilds or re-downloads
────────────────────────────────────────────────
📦 node_modules ×58 repos/worktrees    ▓▓▓░░░░   9.7 G  install again
🗂️ ~/projects/wellness/.build          ▓▓▓░░░░   8.6 G  one repo's build
🧠 Chrome on-device model ×2 copies    ▓▓▓░░░░   8.0 G  Chrome profiles
🐳 ~/.docker/sandboxes VM              ▓▓░░░░░   7.2 G  feature removed
🧠 Whisper models large-v3 + medium    ▓▓░░░░░   4.3 G  re-download
🐍 .venv ×13 repos                     ▓░░░░░░   3.6 G  uv sync rebuilds
                                       ── 41.4 G

────────────────────────────────────────────────
🔴 ASK FIRST, in use, or yours to judge
────────────────────────────────────────────────
📱 iOS simulators, 7 devices iOS 26.1  ▓▓▓▓▓▓▓  21.8 G  booted YESTERDAY
🧠 Claude Desktop VM rootfs.img        ▓▓▓░░░░   9.5 G  the Claude app
💾 Docker volumes (2, active)          ▓▓░░░░░   5.1 G  live data
📥 ~/Downloads                         ▓░░░░░░   3.0 G  many small files
                                       ── 39.4 G

🟢 safe 45 G + 🟡 regenerable 41 G ≈ 87 G  →  free 73 G → ~159 G
🔴 39 G more, only with your say-so

ℹ️  Docker.raw shows 432 G but is sparse: 37 G real. Prune shrinks it.
ℹ️  9 wellness worktrees share one 0.4 G .git; the cost is node_modules.
```

Three things the numbers told me that are not obvious from the chart:

- **Docker is the biggest single lever**: 93 images, 78 dangling, 227 build-cache entries;
  Docker's own accounting says 14.5 G reclaimable, and Docker.raw shrinks to match.
- **The iOS simulators are real work, not cruft**: Xcode is installed and a device booted
  yesterday. Only worth trimming if you name devices you do not use.
- **Two things look like caches but are payloads**: the Claude Desktop VM image (9.5 G,
  part of the app) and the Whisper models (4.3 G, re-downloadable but slow). Both kept
  out of "safe".

### Two more real renders

**A status board** (`/ork:glyph` with no argument draws where the conversation is):

```
✅ 1 probe      measured, transcript on #3877 (CLOSED)
✅ 2 lane 5     #3949 merged c326ca5fe (my #3948 closed as its duplicate)
🔥 3 beta.1     #3950 alpha.86 is open; beta.1 needs a Release-As footer
✅ 4 worktrees  done
✅ 5 stashes    done
```

**A comparison** (`/ork:glyph compare monolith vs services`):

```
BEFORE                          AFTER
┌────────────┐                  ┌────────────┐
│  Monolith  │                  │  Service A │──┐
│  (all-in-1)│                  └────────────┘  │  ┌──────────┐
└────────────┘                  ┌────────────┐  ├─>│  Shared  │
                                │  Service B │──┘  │  Queue   │
                                └────────────┘     └──────────┘
```

The status board and the flow came from one working night (2026-09-06, milestone 164's
close-out); the inventory is the 2026-09-17 reference render that set the v3 shape. None
is a mock-up.

### The exact invocation

```bash
/ork:glyph                  # draw where the conversation is right now
/ork:glyph <topic>          # one thing: an inventory, a comparison, a state
/ork:glyph --eli5 <topic>   # novice audience, and a page if it needs one
/glyph                      # same skill as /ork:glyph; renders here, does not hand off
```

What it will never do: ask you a setup question, use a status emoji that is not in its
closed set (done, failed, warning, in progress, waiting, idea, hard block, goal, top
priority, doc, agent, hook, caveat, and the three risk colours), put a domain icon
anywhere but the leading column of a row, or stack two renders in one reply. Over about
50 lines means a different deliverable, and it says so.

### When it is the wrong tool

A multi-section interactive playground or a persisted plan artifact is `visualize-plan`.
A real chart with numbers on axes goes through `dataviz` first. Anything that must be
handed to a human as a URL is served with `/ork:page-serve`.

---


# Glyph

Render the answer as ASCII art plus semantic emojis, inline, immediately. All output renders in a monospace terminal with no external tools.

**Core principle:** Encode information into structure, not decoration. Every diagram element should communicate something meaningful.

## Execution (run this, do not ask first)

**A human asking "how do I use this" gets a URL, not a paraphrase:**
https://orchestkit.yonyon.ai/docs/reference/skills/glyph#examples (what it draws,
the two picks, three real renders, the exact invocation; generated from this
skill's `examples/_featured.md`, so it cannot drift from the skill).

The whole point is speed, so there is no setup phase.

**Already loaded means render, not hand off.** `glyph` and `ork:glyph` are one skill: this file. If the host already invoked either name, draw the answer here. Do not call the Skill tool for `glyph`, `/glyph`, or `/ork:glyph`. A second invocation loads this file again and re-runs the planning step. The 2026-09-15 Devin transcript showed "Invoked skill glyph" then "Invoked skill ork:glyph", one render stacked on another. Whether that re-entry is also why the Thoughts text repeated is not verified here; do not re-enter either way. The front door does not delegate.

**With no argument, the topic is the current conversation.** Measured over a real 13-prompt session: zero asks supplied a self-contained topic, and the one direct invocation passed nothing at all. `glyph` on its own means "render where we are right now": the open work, the decision just reached, the state of the thing being discussed. Render that; do not ask what to draw.

Given a topic (or the conversation, when none is given):

1. **Render immediately.** Do NOT call `AskUserQuestion` to pick a format, do NOT call `TaskCreate`, do NOT spawn an `Agent`. Choose the form yourself from the topic shape and draw it. Asking first defeats the skill.
2. **Pick the form from the shape of the data**, using the pattern library below:

   | Topic shape | Form |
   |---|---|
   | inventory, audit, budget, "what is using X" | **inventory render** (`templates/inventory.md`): header meter, traffic-light sections, icon / label / bar / value / action rows, totals, arithmetic summary, caveats |
   | state / progress / health | status box + bar meters |
   | A vs B, options, trade-offs | comparison table or side-by-side boxes (one narrow table in a non-TTY surface) |
   | steps, pipeline, hand-offs | left-to-right flow with `──▶` |
   | containment, layers, layout | nested boxes / tree |
   | ranked list, scores, counts | table + bar meters |
   | over time | sparkline or milestone track |
   | triage open issues, what is left, categorize the backlog | **triage page** (`templates/triage.html`). Not an inline render. |

3. **Emit inline in the reply, except the triage page.** Never write a file unless the user asked for one. The triage row above is the exception: that answer is `templates/triage.html`, not an inline render. Every other answer stays in the reply.
4. **Use the closed status set, the domain-icon legend and the box-drawing vocabulary** defined in `rules/visual-style.md` (shipped with this skill) and `tokens.json` (`icons.*`). Status icons pair with a word (✅ done, ❌ failed, ⚠️ warning, 🔴 high risk, ℹ️ caveat). Domain icons (🐳 docker, 📦 package, 🧪 test, ...) go one per row in the leading column so they scan as a legend; never inside a prose sentence, never in chains.
5. **Stay inside the budget: ONE render per reply, up to about 50 lines, every line 76 cells or fewer.** Two competing renders in one reply is flooding. Above 50 lines it is a page, not a chat answer. The 2026-08-09 budget (12 lines, 40 percent) over-corrected and was reset by operator word on 2026-09-17; the history is in `rules/visual-style.md`.
6. **Verdict first, then the render, then what the numbers told me.** One prose line states the point before the render. After the render, two or three bold-led bullets say what is not obvious from the chart. If the reader has to parse the render to find out what happened, the reply failed.
7. **Stay honest.** If a number is unknown, print `?` rather than inventing one. A confident-looking chart built on guesses is worse than prose.
8. **Match the width to the host.** A render that fits a terminal wraps into a wall in a host that reflows text: CI logs, chat widgets, VS Code chat, web transcripts, agent desktops. Read the surface from the environment the same way you already infer audience and surface: with a terminal (TTY), render as below; without one, cap every diagram line at 72 columns, and render key/value or comparison data as one narrow table or a vertical list, never side-by-side columns. The line budget does not change. Measured failure the rule prevents: a side-by-side key/value board for 5 rows lands at 113 columns, and `scripts/render-ascii.sh key-value` renders the narrow form of the same data (GH-4159).

**When NOT to use this skill:** if the deliverable is a multi-section HTML playground, a persisted plan artifact, or any file output other than the triage page, use `visualize-plan` instead. Glyph is the cheap inline path; visualize-plan is the full pipeline. The triage page stays here.


**Triage is a page, every time.** Asks shaped like "triage open issues", "what is left", or "categorize this for me" use `templates/triage.html` and no other layout. This overrides the inline-only rule and the visualize-plan handoff: the page is glyph's, then `/page-serve PATH`. Fill issues from one snapshot, `gh issue list --state open --json number,title,labels,milestone`. Put every gh-sourced string in the hidden snapshot textarea as one base64 blob of the JSON, not as raw JSON and not in the lane markup or the raw dump. A title that contains `</textarea` must never appear as raw markup inside that textarea, or it breaks out before escapeHtml runs. The page decodes the blob, then calls escapeHtml when it inserts each lane item and the raw dump, so a title stays literal text. The open-PRs tile is not in that snapshot. Fill it from `gh pr list --state open --json number`, and if that command was not run print `?`, never a guessed count. Keep the six parts in order: KPI strip, lanes, a route tag on every issue (`devin`, `ork:NAME`, `hq-ext`, `21st-dev`, `human`, `external`), capability map, DECIDE block, collapsed raw snapshot. Hand the file over with `/page-serve PATH`. Never paste a bare path. The copy button must emit `wave=LANE followups=CSV`. When no follow-up is checked, that is `followups=` with nothing after the equals, never `followups=0`.

**Over budget is the same signal.** If the honest rendering needs more than ~50 lines, that is not a bigger chat answer, it is a different deliverable: write the playground or file, then hand the human a URL with `/page-serve PATH` (a port-free `https://<name>.localhost/` route, with a stop) instead of a bare file path or a hand-started `python3 -m http.server`. Keep a 10-line excerpt in chat next to the URL. The old escape hatch fired on artifact TYPE only, so an over-budget inline reply never tripped it.


## Render anatomy (the v3 reference shape)

The shape below is the default for anything inventory-like (disk, spend,
backlog, dependencies, hooks, "what is using X"). Draw it top to bottom; drop a
part only when the data has nothing for it. The full template with the
column widths is `templates/inventory.md`; the worked example is in
`examples/_featured.md`.

```
🖥️  MACINTOSH HD  ·  /System/Volumes/Data              header: icon, subject
[▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░]                                 headline meter
 used 355 G    free 73 G    83 % full · 460 G total     numbers under it
────────────────────────────────────────────────        light rule
🟢 SAFE, pure caches; nothing lost, tools refill them   traffic-light word
────────────────────────────────────────────────
🐳 Docker dangling images+build cache  ▓▓▓▓░░░  14.5 G  docker prune
🌐 Chrome caches (Google + Island)     ▓▓░░░░░   8.9 G  browsers refill
📦 npm cache  ~/.npm                   ▓▓░░░░░   6.2 G  npm cache clean
                                       ── 45.3 G        per-section total
🟢 safe 45 G + 🟡 regenerable 41 G ≈ 87 G  →  free 73 G → ~159 G
ℹ️  Docker.raw shows 432 G but is sparse: 37 G real.    caveat line
```

Parts, in order:

1. **Header line**: one domain icon, the subject, `·`, the path or scope.
2. **Headline meter**: `[▓▓▓░░]` with the numbers printed on the line under it.
3. **Sections**: opened by a light rule `────` and a traffic-light word
   (🟢 SAFE, 🟡 REGENERABLE, 🔴 ASK FIRST, or the domain's own words).
4. **Rows**: icon, label, bar `▓▓▓░░`, value, one-word action, aligned in
   columns. One domain icon per row, leading column.
5. **`── total`** line per section, right-aligned under the values.
6. **Summary line** that shows the arithmetic, so the reader can check it.
7. **`ℹ️` caveat lines** for the numbers that lie (sparse files, shared
   caches, double counts). `?` for a number you do not have.
8. Then prose: two or three bold-led bullets, "what the numbers told me".

The vocabulary is closed. Status icons come from `tokens.json` `icons.status`
(plus `risk` and `ranking`); row icons from `icons.domain`. Add there, not ad
hoc. No emoji chains, no emoji in prose sentences, no full-width rules, no
"★ Insight" blocks, no mermaid in chat, no em or en dashes.

## Box-Drawing Character Reference

This block intentionally shows multiple sets together as a key. Authors
should use ONE set per real diagram; the `single-set` lint rule enforces
this on production diagrams.

```
default:   ┌─┐ │ └─┘  ├─┤ ┬ ┴ ┼
emphasis:  ┏━┓ ┃ ┗━┛  ┣━┫ ┳ ┻ ╋
title:     ╔═╗ ║ ╚═╝  ╠═╣ ╦ ╩ ╬
soft:      ╭─╮ │ ╰─╯
portable:  +-+ | +-+  +-+ + + +
Arrows:    → ← ↑ ↓ ─> <─ ──> <──
Blocks:    █ ▓ ░ ▏▎▍▌▋▊▉
Status:    ● ○ ✓ ✗ ⚠ ◆ ◇ ▶ ▷  ↑↓→ ▓▒░  (closed-set vocab, see rules)
```

### Set Conventions (D8: intent-driven naming)

Tokens live in `tokens.json`. Names describe USE not APPEARANCE.

| Set | Characters | Use For |
|-----|-----------|---------|
| `default` `─│` | Normal boxes and connectors | Most diagrams |
| `emphasis` `━┃` | Headers, focus, draw the eye | Key components, outer frames |
| `title` `═║` | Document titles | §0-style banners only |
| `soft` `╭╮╰╯ ─│` | Status cards, ambient UI | Diff blocks |
| `portable` `+-\|` | NO_COLOR / CI / bare TTY | Fallback |

Rename codemod (D8): old `light/heavy/double/rounded/ascii-fallback` → new names above. Old names accepted with warning for one minor release.

### Status Glyph Vocabulary

Closed-set v1 of 11 semantic glyphs (`●○✓✗⚠◆◇▶▷ ↑↓→ ▓▒░`). Single source of truth: see `rules/status-glyph-vocabulary.md`. Add-a-glyph process in `CONTRIBUTING.md`.


## Diagram Patterns

### Architecture Diagrams

```
┌────────┐ ┌────────┐
│Frontend│─>│Backend │
│React 19│ │FastAPI │
└────────┘ └───┬────┘
           ┌───┴──────┐
           │PostgreSQL│
           └──────────┘
```

### File Trees with Annotations

```
src/
├── api/
│   ├── routes.py          [M] +45 -12    !! high-traffic path
│   └── schemas.py         [M] +20 -5
├── services/
│   └── billing.py         [A] +180       ** new file
└── tests/
    └── test_billing.py    [A] +120       ** new file

Legend: [A]dd [M]odify [D]elete  !! Risk  ** New
```

### Progress Bars

```
[████████░░] 80% Complete
+ Design    (2 days)
+ Backend   (5 days)
~ Frontend  (3 days)
- Testing   (pending)
```

### Swimlane / Timeline Diagrams

```
Backend  ===[Schema]======[API]===========================[Deploy]====>
                |            |                                ^
                |            +------blocks------+             |
                |                               |             |
Frontend ------[Wait]--------[Components]=======[Integration]=+

=== Active work   --- Blocked/waiting   | Dependency
```

### Blast Radius (Concentric Rings)

```
            Ring 3: Tests (8 files)
       +-------------------------------+
       |    Ring 2: Transitive (5)      |
       |   +------------------------+   |
       |   |  Ring 1: Direct (3)     |   |
       |   |   +--------------+      |   |
       |   |   | CHANGED FILE |      |   |
       |   |   +--------------+      |   |
       |   +------------------------+   |
       +-------------------------------+
```

### Comparison Tables

```
BEFORE        AFTER
┌──────────┐  ┌─────────┐
│Monolith  │  │Service A│──┐
│(all-in-1)│  └─────────┘  │ ┌───────┐
└──────────┘  ┌─────────┐  ├─>│Shared │
              │Service B│──┘ │Queue  │
              └─────────┘    └───────┘
```

### Reversibility Timeline

```
Phase 1  [================]  FULLY REVERSIBLE    (add column)
Phase 2  [================]  FULLY REVERSIBLE    (new endpoint)
Phase 3  [============....]  PARTIALLY           (backfill)
              --- POINT OF NO RETURN ---
Phase 4  [........????????]  IRREVERSIBLE        (drop column)
```


## Key Rules

| Rule | Description |
|------|-------------|
| Font | Always monospace; box-drawing requires fixed-width |
| Weight | Standard for normal, Heavy for emphasis, Double for titles |
| Arrows | `─>`, `──>`, or `│` with `v`/`^` for direction |
| Alignment | Right-pad labels to match column widths |
| Annotations | `!!` for risk, `**` for new, `[A/M/D]` for change type |
| Width | 76 cells or fewer in a terminal; at most 72 per line in a non-TTY surface, as one narrow table or vertical list (GH-4159) |
| Nesting | Max 3 levels of box nesting before readability degrades |


## When to Use Each Pattern

| Pattern | Use Case |
|---------|----------|
| Layered boxes | System architecture, deployment topology |
| Concentric rings | Blast radius, impact analysis |
| Timeline bars | Reversibility, migration phases |
| Swimlanes | Execution order, parallel work streams |
| Annotated trees | File change manifests, directory structures |
| Comparison tables | Cross-layer consistency, before/after |
| Progress bars | Status tracking, completion metrics |
| Inventory render | Disk, spend, backlog, dependency audits; anything with sections and totals |

## Related Skills

- `brainstorm`: Design exploration where diagrams communicate ideas
- `architecture-patterns`: System architecture that benefits from ASCII diagrams
- `code-review-playbook`: Review comments with inline diagrams


---

## Rules (4)

### Create structured ASCII architecture diagrams to communicate system design without external tools: MEDIUM


## ASCII Architecture Visualization Patterns

**Incorrect, flat text descriptions:**
```
The system has a frontend that talks to a backend API which uses
a database and a cache layer. There's also a message queue for
async processing.
```

**Correct, layered architecture diagram:**
```
┌─────────────────────────────────────────────────────┐
│                    Load Balancer                      │
└──────────┬──────────────────────────┬────────────────┘
           │                          │
┌──────────v──────────┐  ┌───────────v────────────┐
│   API Gateway       │  │   API Gateway          │
│   (instance 1)      │  │   (instance 2)         │
└──────────┬──────────┘  └───────────┬────────────┘
           │                          │
           └──────────┬───────────────┘
                      │
        ┌─────────────┼────────────────┐
        │             │                │
┌───────v──────┐ ┌────v─────┐ ┌───────v──────┐
│  PostgreSQL  │ │  Redis   │ │  RabbitMQ    │
│  (primary)   │ │  (cache) │ │  (queue)     │
└──────────────┘ └──────────┘ └──────────────┘
```

### Blast Radius Visualization

```
                Ring 3: Tests (8 files)
           +-------------------------------+
           |    Ring 2: Transitive (5)      |
           |   +------------------------+   |
           |   |  Ring 1: Direct (3)     |   |
           |   |   +--------------+      |   |
           |   |   | CHANGED FILE |      |   |
           |   |   +--------------+      |   |
           |   +------------------------+   |
           +-------------------------------+

Direct dependents:   auth.py, routes.py, middleware.py
Transitive:          app.py, config.py, utils.py, cli.py, server.py
```

### Reversibility Timeline

```
REVERSIBILITY TIMELINE
Phase 1  [================]  FULLY REVERSIBLE    (add column, nullable)
Phase 2  [================]  FULLY REVERSIBLE    (new endpoint, additive)
Phase 3  [============....]  PARTIALLY           (backfill data)
              --- POINT OF NO RETURN ---
Phase 4  [........????????]  IRREVERSIBLE        (drop old column)
Phase 5  [================]  FULLY REVERSIBLE    (frontend toggle)
```

### Comparison Tables

```
CROSS-LAYER CONSISTENCY
Backend Endpoint          Frontend Consumer     Status
POST /invoices            createInvoice()       PLANNED
GET  /invoices/:id        useInvoice(id)        PLANNED
GET  /invoices            InvoiceList.tsx        MISSING  !!
```

### Key Patterns

| Pattern | Use Case |
|---------|----------|
| Layered boxes | System architecture, deployment topology |
| Concentric rings | Blast radius, impact analysis |
| Timeline bars | Reversibility, migration phases |
| Swimlanes | Execution order, parallel work streams |
| Annotated trees | File change manifests, directory structures |
| Comparison tables | Cross-layer consistency, before/after |


### Use consistent box-drawing characters and formatting for correct terminal rendering: MEDIUM


## ASCII Diagram Fundamentals

**Incorrect, inconsistent characters and alignment:**
```
+-------+    +-------+
| Frontend | -> | Backend |
+-------+    +-------+
              |
          +--------+
          | Database |
          +--------+
```

**Correct, proper box-drawing characters with alignment:**
```
Box-Drawing Characters:
┌─┐│└─┘  Standard weight
┏━┓┃┗━┛  Heavy weight
├─┤┬┴    Connectors
╔═╗║╚═╝  Double lines
```

```
┌──────────────┐      ┌──────────────┐
│   Frontend   │─────>│   Backend    │
│   React 19   │      │   FastAPI    │
└──────────────┘      └───────┬──────┘
                              │
                              v
                      ┌──────────────┐
                      │  PostgreSQL  │
                      └──────────────┘
```

### Progress Tracking

```
[████████░░] 80% Complete
+ Design    (2 days)
+ Backend   (5 days)
~ Frontend  (3 days)
- Testing   (pending)
```

### File Trees

```
src/
├── api/
│   ├── routes.py          [M] +45 -12    !! high-traffic path
│   └── schemas.py         [M] +20 -5
├── services/
│   └── billing.py         [A] +180       ** new file
└── tests/
    └── test_billing.py    [A] +120       ** new file

Legend: [A]dd [M]odify [D]elete  !! Risk  ** New
```

### Workflow Diagrams

```
Backend  ===[Schema]======[API]===========================[Deploy]====>
                |            |                                ^
                |            +------blocks------+             |
                |                               |             |
Frontend ------[Wait]--------[Components]=======[Integration]=+

=== Active work   --- Blocked/waiting   | Dependency
```

### Key Rules

| Rule | Description |
|------|-------------|
| Font | Always monospace, box-drawing characters require fixed-width |
| Weight | Standard (─│) for normal, Heavy (━┃) for emphasis |
| Arrows | Use ─>, ──>, or │ with v/^ for direction |
| Alignment | Right-pad labels to match column widths |
| Annotations | Use !! for risk, ** for new, [A/M/D] for change type |


### Use the closed-set status glyph vocabulary for semantic indicators: HIGH


## Closed Status Glyph Vocabulary (v1)

Eleven glyphs, frozen at v1.0.0. Adding a 12th is a MINOR bump via the
process in `CONTRIBUTING.md`. Removing or repurposing any of the 11 is
a MAJOR bump (rare).

### Vocabulary

```
GLYPH   SEMANTIC                    DON'T USE FOR
─────   ────────                    ─────────────
●       active / on / current       generic bullet
○       inactive / off / pending    decoration
✓       passed / completed          decoration
✗       failed / blocked            general "no"
⚠       warning / attention         hard error
◆       primary / focused           random emphasis
◇       secondary / unfocused       decoration
▶       running / in-progress       generic arrow
▷       paused / awaiting input     generic arrow
↑↓→     trend up/down/forward       random direction
▓▒░     fill: high/med/low          decoration
```

### Incorrect: generic decoration

```
* Build passed
> Tests running
- Linter clean
```

### Correct: semantic glyphs

```
✓ Build passed
▶ Tests running
✓ Linter clean
```

### Anti-pattern: repurposing a semantic for decoration

```
✓ Item 1
✓ Item 2
✓ Item 3
```

A list of items isn't passing/failing, `✓` is reserved for pass/completed
states. Use `●` for active items and `○` for inactive/pending ones, or
plain `-` for an unmarked list.

### Anti-pattern: mixing semantically-loaded glyphs with decoration

```
■  Phase 1   ✓  Phase 2   ◉  Phase 3
```

`■` and `◉` are not in the vocabulary, they read as decoration. Pick
exactly one glyph from the table above per slot:

```
✓  Phase 1   ▶  Phase 2   ○  Phase 3
   done       running       pending
```


### Visual Style, glyph renders in chat: MEDIUM


# Visual Style in Chat (glyph renders, one per reply, up to ~50 lines)

Reset 2026-09-17 by operator word after seeing a peer's disk-cleanup render:
"we don't use it enough, ASCII and emoji art in chat in general". This
supersedes the 2026-08-09 budget (12 lines, 40 percent), which had swung too
far the other way: replies answered shape-shaped questions in prose and one
thin table.

## The three rules

1. **An answer with shape gets a render.** Status, inventories, audits,
   budgets, comparisons, rankings, pipelines, "what is using X", "where do we
   stand". A single fact, a definition, or a one-line ask gets prose only.
2. **Verdict first, then the render, then what the numbers told me.** One
   prose line states the point before the render. After the render, two or
   three bold-led bullets say what is not obvious from the chart. If the reader
   has to parse the render to learn the verdict, the reply failed.
3. **One render, up to about 50 lines, 76 cells wide.** Two competing renders
   in one reply is flooding. Above 50 lines it is a page: write the HTML or
   markdown, print the URL or path, keep a 10-line excerpt in chat.

## Anatomy of a render (the reference shape)

```
🖥️  MACINTOSH HD  ·  /System/Volumes/Data
[▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░]
 used 355 G    free 73 G    83 % full · 460 G total
────────────────────────────────────────────────
🟢 SAFE, pure caches; nothing lost, tools refill them
────────────────────────────────────────────────
🐳 Docker dangling images+build cache  ▓▓▓▓░░░  14.5 G  docker prune
🌐 Chrome caches (Google + Island)     ▓▓░░░░░   8.9 G  browsers refill
📦 npm cache  ~/.npm                   ▓▓░░░░░   6.2 G  npm cache clean
                                       ── 45.3 G
🟢 safe 45 G + 🟡 regenerable 41 G ≈ 87 G  →  free 73 G → ~159 G
ℹ️  Docker.raw shows 432 G but is sparse: 37 G real. Prune shrinks it.
```

Parts, in order: header line with subject; headline meter with the numbers
printed under it; a light rule and a traffic-light section word; rows aligned
in columns (icon, label, bar, value, one-word action); a `── total` line per
section; a summary line that shows the arithmetic; `ℹ️` caveat lines for the
numbers that lie. Then prose.

## Vocabulary

### The status set (closed)

Every status icon comes from this list and is paired with a word. Adding one
requires a PR that also updates `bin/validate-visual-style.py`.

| Glyph | Meaning | Use for |
|-------|---------|---------|
| ✅ | done / pass | completed tasks, passing tests, met criteria |
| ❌ | failed / no | failed tests, denied actions, explicit "don't" |
| ⚠️ | warning | non-fatal issues, edge cases, soft constraints |
| 🔄 | in progress | running tasks, retry loops, async work |
| ⏸ | waiting | blocked on a dependency or user input |
| 💡 | idea | optional improvements, "consider this" |
| 🚨 | hard block | security violations, hook denies, must-fix |
| 🎯 | goal | objectives, success criteria, focus |
| 🔥 | top priority | recommended path, the one thing to do first |
| 📜 | doc | references to SKILL.md, documentation |
| 🤖 | agent | references to subagents |
| ⚡ | hook | references to hooks, fast/automated paths |
| ℹ️ | caveat | the line under a render for a number that lies |

Risk and ranking glyphs (always paired, never solo):

- Risk: 🟢 low · 🟡 medium · 🔴 high
- Ranking: 🥇 1st · 🥈 2nd · 🥉 3rd

### Domain icons (rows)

Row icons for domain objects are encouraged, one per row, leading column, so
they read as a legend: 🐳 docker, 🌐 browser, 📦 package, 🐍 python,
🧠 model, 📱 device, 💾 volume, 🗑️ trash, 🗂️ files, 📥 downloads,
🔑 secret, 🧪 test, 🖥️ host, ☁️ cloud, 📧 mail, 💬 chat. The glyph skill's
`tokens.json` (`icons.domain`) is the canonical list; add there, not ad hoc.
Domain icons are chat-only; the PR-body validator does not admit them.

### Glyphs

- Light box drawing ┌─┐ │ └─┘, section rules ────, arrows → ↓ ──▶,
  bullets • ◦, bar meters ▓▓▓░░ with the value beside the bar, `?` for an
  unknown number, never an invented one.
- Never: emoji chains, emoji inside prose sentences, full-width rules across
  the terminal, "★ Insight" blocks, mermaid in chat, em or en dashes.

## ASCII Palette

Use these character sets only; it keeps rendering predictable across
terminals, GitHub, and CI logs.

```
Box drawing:  ┌─┐ │ ┘─└   (light)
              ╔═╗ ║ ╝═╚   (heavy, reserve for top-level section headers)
Dividers:     ───  ═══
Arrows:       → ↓ ▶ ◀
Progress:     ▓▓▓░░  ████████░░
Bullets:      • ◦ ‣
```

Do not use: 3D box characters, shaded blocks for decoration, ASCII-art logos,
or any character that requires a non-default monospace font.

## Triggering

- The `visual-style-nudge` hook fires on every qualifying prompt. It means "if
  this answer has shape, render it", not "draw something every turn".
- `/ork:glyph` is the reference implementation; use it unprompted whenever
  rule 1 applies. `glyph` and `ork:glyph` are that same skill. If either name
  is already loaded, render in place and do not invoke the other: a second
  invocation re-enters this file instead of drawing (GH-4159).
- "Where do we stand / what's next" always gets a render plus ranked next
  steps.

## Per-Surface Rules

### ✅ Assistant chat output

The three rules above. One render per reply, up to about 50 lines, 76 cells
wide, verdict first. A render that fits a terminal wraps into a wall in a host
that reflows text (CI logs, chat widgets, VS Code chat, web transcripts):
without a TTY, cap every line at 72 columns and render key/value or comparison
data as one narrow table or a vertical list, never side-by-side columns.

### ✅ SKILL.md files

- **Allowed:** H2 emoji prefix from the status set (e.g. `## 🎯 When to use`),
  ASCII diagrams in workflow sections, emoji in table-status columns.
- **Forbidden:** decorative inline emoji in body prose, emoji-only headings
  (always pair with text), emoji in code blocks or shell examples.
- 500-line cap still applies. Diagrams pushing the cap move to
  `references/*.md`.

### ✅ Agent .md files

- **Allowed:** role emoji in `description:` field, one ASCII "card" block in
  body showing scope/tools/cost, emoji in capability tables.
- **Forbidden:** emoji in YAML keys, `name:` field, `tools:` array, or
  anywhere automation parses structurally.

### 🔥 Hook source (TypeScript), strongest yes

Hook stdout renders directly in chat. Every hook message should lead with a
severity glyph:

```
🚨 BLOCK · ⚠ WARN · 💡 HINT · ⏭ SKIP
```

Wrap multi-line deny messages in `┌─ ─┐` boxes so they disambiguate from
assistant output.

### ✅ README, CHANGELOG, docs/*.md

GitHub renders both natively. Use ASCII architecture diagrams and emoji
section headers freely. CHANGELOG entries lead with type emoji: ✨ feat ·
🐛 fix · 📝 docs · 🔥 perf · ♻ refactor.

### ⚠️ CLAUDE.md (project root + .claude/)

Hard 4800-byte cap enforced by `tests/perf/test-token-overhead.sh`.
Box-drawing chars are 3 bytes each; a single 60-char border row costs ~180
bytes.

- **Allowed:** one emoji per H2 heading.
- **Forbidden:** ASCII boxes, multi-line diagrams, decorative dividers.
  Extract to `docs/<topic>.md` with a one-line pointer.

### ✅ PR bodies

GitHub renders these natively, same as README. ASCII diagrams from the palette
above are encouraged; a before/after box or a file tree carries a review point
faster than a paragraph. Emoji are limited to the status set (plus risk and
ranking); domain icons stay in chat.

Enforced by `bin/validate-visual-style.py --mode body` in the Visual-Style PR
Lint check. The palette and the emoji vocabulary are two separate lists: box
drawing, dividers, arrows and progress blocks are governed by **ASCII
Palette**, everything emoji by **The status set**. The validator once conflated
them (both report Unicode category `So`) and rejected plain box-drawn
diagrams; keep the two tables distinct when editing either.

### ❌ Code, commit messages, PR titles

No emoji. No ASCII art. Conventional Commits stays plain (`feat:`, `fix:`,
etc.); the type prefix is already the signal. Code comments stay plain so grep
stays useful.

The PR **title** is the strict surface; the PR **body** is not. `--mode title`
rejects every non-ASCII symbol including the palette, which is why a diagram
belongs in the body.

## Greppability Contract

Any emoji-prefixed heading must keep the original text intact so `grep` still
works:

```
✅ ## 🎯 When to use      → grep "## " finds it, grep "When to use" finds it
❌ ## 🎯                  → text-less heading; ungreppable
```

## Accessibility

- Screen readers announce every emoji literally ("check mark", "warning
  sign"). Don't stack them: one glyph per heading or status, not 🎯🔥✅
  chains.
- Box-drawing characters are pronounced as their Unicode names; use them in
  visual chunks (diagrams, table borders), not inline with prose.
- Never convey meaning via emoji alone. Always pair with text: `✅ Passed`
  not `✅`.

## Echo-Chamber Mitigation

Heavy emoji in OrchestKit's own SKILL.md files trains the model to emoji-bomb
every response when those skills are loaded. The closed status set is the
mitigation: when Claude sees the same glyphs used consistently with the same
semantics across 100+ files, it learns "these glyphs = these meanings" rather
than "emoji = always-on style". Drift from the vocabulary defeats the
mitigation; reject PRs that add status glyphs outside the list.

## History

- 2026-08-09: "this is mostly flooding me" after five stacked blocks (~60
  lines) with the point at the bottom. Fix was a 12-line, 40 percent budget.
- 2026-08-19: measured 151 replies; block length p90 was 13 lines, visual
  share p75 was 43 percent. The budget bound the tail.
- 2026-09-17: the budget over-corrected. Rule 2 (verdict first) is what
  actually prevented the 08-09 failure, so it stays; the line budget rises to
  ~50 with one render per reply.



---

## References (1)

### Page Route

# Glyph page route: the full contract

`SKILL.md` carries the six normative rules. This file carries the detail behind them, in the order the route runs.

## 1. The house kit comes first

Before writing a single line of HTML, check whether the repo already owns a playground pipeline. A repo that does will also have a CI gate on it, and an earlier version of this route hand-wrote three pages that all failed that gate.

```bash
ls docs/playgrounds/_kit/base.css 2>/dev/null && echo "HOUSE KIT: use it"
```

If a kit exists, the whole page route is the repo's own commands, typically a scaffold script that pre-wires the metadata comment the gate reads, a dated filename, and a link to the shared stylesheet; a verify script that must pass; and an index refresh. Then:

- write NO new CSS: use the kit's classes (cards, meters, pills, flows, callouts);
- keep any metadata comment the gate reads, with the category equal to the directory name;
- follow the kit's theme (a dark house default is common) rather than arguing for light;
- localized twins are siblings produced by the kit, never a separate design.

Only when the kit is absent does `templates/explainer.html` in this skill apply. It is a self-contained baseline plus authoring rules, not a skeleton to fill in.

## 2. Where a page lands (no kit)

| Topic is about | Write to |
|---|---|
| how we build / ship / test | `docs/playgrounds/dev/<slug>-explainer.html` |
| servers, CI, deploys, cost | `docs/playgrounds/infra/<slug>-explainer.html` |
| a named client or prospect | `clients/<clientSlug>/playgrounds/<slug>-explainer.html` |
| the business, money, billing | `docs/playgrounds/ops/<slug>-explainer.html` |
| design, UI, brand | `docs/playgrounds/design/<slug>-explainer.html` |
| marketing, content, social | `docs/playgrounds/marketing/<slug>-explainer.html` |

Slug is kebab-case from the topic. If a topic straddles two rows (a CI pipeline is both "how we ship" and "servers"), prefer `dev` when the reader cares about the process and `infra` when they care about the machines.

**Do not copy another skill's anchor table.** A table is not the territory: a root listed somewhere may not exist on disk. `ls -d` the root before writing; if it is missing, fall back to `docs/playgrounds/dev/` and say plainly that you re-routed.

## 3. Quality bar for a novice page (normative)

`test-cases.json` asserts against this list and `templates/explainer.html` points at it rather than restating it. Two copies drift, and they already did once.

- **Draw the actual thing.** A queue looks like a queue, a backlog like a pile. A row of generic rectangles joined by arrows draws the shape of the idea instead of the idea and teaches nothing the sentence did not already say.
- **Analogy before mechanism.** Say what it is like, then what it is.
- **Every beat ends in a consequence AND a do-nothing line.** A picture of a gate with lamps is not an explanation until the reader knows what happens and what happens if nothing is done. Two side-by-side cards under the drawing, "What it means" and "If we do nothing" (the second styled as a warning), ≤ 12 words each, in the reader's terms.
- **≤ 20 words per beat** (the two consequence lines are extra), 4 to 6 beats total. More than six means split the topic.
- **One idea per beat**, and the visual carries it, not the caption.
- **If the topic IS a decision, the page carries the controls.** One decision block per fork: radio group, consequence column beside each option, checkboxes for the follow-ups the pick unlocks. ONE sticky bottom answer bar collects every block into a fixed-width `key: value` summary the agent can parse, reports `N/M chosen`, and keeps "copy my answers" DISABLED until every block has a pick, so a half-answered page cannot be pasted back as if complete. Clipboard needs a select-and-Cmd+C fallback (`navigator.clipboard` is denied in some contexts). A page that only pictures the options is a poster; the decision still happens somewhere else, which defeats the page. Source the controls from a component library or house kit when one is reachable; the template's built-in block is the floor so every decision page looks the same, not the ceiling.
- **Plain words inside the diagram too.** Visual labels are where insider language leaks most: check names, branch names and service names as chart labels are a FAIL for a novice page.
- **No leftover template markers.** The baseline ships bare `TITLE` / `DATE`; none may survive into the emitted file.
- **Light theme**, deliberately: this is usually read by someone outside the team.
- **Self-contained**: opens from `file://`, zero network references. Inline SVG, not a CDN-loaded diagram library.
- **No em-dash characters** in the emitted page; several house verify scripts grep for them.

## 4. Not done until you have looked at it

Operator feedback, three rounds in one evening across four pages ("how didn't you verify it yourself, as part of glyph"). These are normative for every page route.

1. **Screenshot and read the pixels before claiming done.** Verify scripts, SVG counts and DOM probes are blind to layout and collision defects; a numeric gate that passes is not a render that looks right. Serve the page via portless (`portless alias <name> <port>` in front of a static server; URL `https://<name>.localhost/...`, never a `:port`), open it with `agent-browser --cdp <port> --no-pin-tab` (a pinned session dies with `tab_gone` after tab churn), take viewport screenshots at several scroll positions, and READ the images. Not computer-use. Never open `file://` in the operator's live browser window with a forced viewport: it lands as a squished window on their screen. If no browser path works from your session, hand the URL to a peer with one and wait for the measured verdict; do not report done on a page you have not seen.
2. **Two CDP traps.** (a) `screenshot --full` over CDP can stitch a tile mosaic (hero repeated per tile, sticky bars stamped into each) rather than one page; take viewport shots at scroll positions instead. (b) with `--no-pin-tab`, a later `eval` or `screenshot` targets whatever tab is ACTIVE in that Chrome; after `open`, re-target explicitly or verify the page title in the result before trusting a capture. A PASS on the wrong page is the worst false positive of all.
3. **RTL in SVG.** Under `dir=rtl`, SVG `<text>` runs RTL from its anchor with no bidi isolation: Hebrew phrases, punctuation AND digit groups flip or spill. Hebrew goes in HTML captions, never SVG text. Numbers that must stay in-SVG use `text-anchor="middle" direction="ltr"`. Single-word Hebrew labels survive only while they stay single words, center-anchored, with a warning comment beside them.
4. **What "richer than prose" meant when it was accepted.** Semantic emoji in tab labels, headings, card titles and list leads; a KPI strip under the hero; a sticky tab bar with hash routing when the page has more than one view; several real diagrams per page. Hex colors only inside any diagram library's class definitions (hsl is a known bug in one of them). Never inline a multi-megabyte diagram bundle; it hung a renderer and read as "broken".
5. **Kit markup gotchas seen in the wild.** A meter is `span.name / div.track > div.fill / span.val`; a `<span><i><b>` shape renders no bar. Keep `.val` strings short. A pill is a TAG, not a form control: radios, checkboxes and buttons need real control markup or the page reads as "ugly and broken".

## 5. Reference pages that passed all rounds

- `docs/playgrounds/dev/cc-latest-capitalize-2026-08-29.html` in OrchestKit: KPI strip, six beats with inline SVG, three decision beats, sticky answer bar; corrected twice from screenshots (label collision, clipped annotations) before it was called done.
- The route was built on 2026-08-22 and rebuilt by hand the same night after the operator rejected a pictures-only version ("not explaining good enough"): the pictures read, the reader still did not know what to DO. That is why the consequence cards and the decision beat are mandatory, not decorative.
