---
title: "glyph: one front door for visual answers"
description:
  "How the glyph skill picks an audience and a surface, the DECIDE block it hands a human, and how
  an agent serves a page via portless and hands over the URL."
canonical: https://yonyon.ai/docs/glyph
last-updated: 2026-09-02
---

# glyph: one front door for visual answers

> HTML version: [https://yonyon.ai/docs/glyph](https://yonyon.ai/docs/glyph). Site index for agents:
> [/llms.txt](https://yonyon.ai/llms.txt).

glyph is the one skill you call when an answer should be seen, not read. It infers two things from
the request, says which it picked in a single line, and renders. It ships in
[OrchestKit](https://github.com/yonatangross/orchestkit).

If you are an agent and a human asked you how to use glyph, this document is the answer. The one
rule that matters most is the last section: a page is not delivered until it is served at a URL the
human can open, and you have looked at it yourself.

## The two dials

Four skills used to cover visual answers and choosing between them was the friction. There are only
two dials: who reads it, and where it lives. Resolve both, announce the pick in one line, then
render.

| audience · surface | chat (inline)    | ask (interactive)       | page (file)                 |
| ------------------ | ---------------- | ----------------------- | --------------------------- |
| novice             | plain-word ASCII | 3 framings, pick one    | explainer page              |
| operator           | dense ASCII      | trade-offs side by side | playground or decision page |

The announce line is exactly one line, then a blank line, for example `→ page · novice`. Nothing
else before the render: no restating the request, no "I will now".

## Dial 1: audience

Default **operator**. Pick **novice** when any of these hold:

- `--eli5` was passed, or the request says "eli5" or "explain like".
- The request names a reader who is not the operator: "explain to the client", "for onboarding", "so
  my mum gets it".
- It asks what a thing _is_ or how it _works_ in general, not what our instance is doing right now.
  "How does OAuth work" is novice. "Is our OAuth broken" is operator.

Novice means no jargon without a one-clause gloss, no internal file or service names unless defined
on the spot, concrete analogies, fewer words per visual. It does not mean less accurate.

## Dial 2: surface

Default **chat**. Escalate only on a real trigger:

- **chat** when the honest rendering fits in 12 visual lines or fewer, and no more than 40% of the
  reply. This stays the common case.
- **ask** when there is a genuine two-to-four way fork the human must settle before the next step.
  Asking blocks, so never use it as a prettier way to print something you were going to print
  anyway.
- **page** when the render is over the 12-line budget, or it has to persist (a client deliverable,
  an onboarding doc, something to link later), or a file was explicitly asked for.

Over budget is a different deliverable, not a longer chat answer. If chat needs more than about 12
lines, that is the page trigger firing: switch surface and say so.

## The DECIDE block

When glyph hands a human a decision, in chat or on a page, it returns a ready-made structure, never
free prose. Radio rows for the mutually exclusive pick, checkbox rows for the follow-ups the pick
unlocks, one consequence clause per option, the recommendation marked inline. Plain text, so it can
be pasted back.

```
DECIDE: <one-line question>
( ) option A   <one-line consequence>
( ) option B   <one-line consequence>   <- recommended, because <one clause>
after you pick:
[ ] <step 1 that follows>
[ ] <step 2 that follows>
```

On a page the same block renders as real controls: a radio group with a consequence column,
checkboxes for the follow-ups, and one sticky answer bar that reports `N/M chosen` and keeps "copy
my answers" disabled until every fork has a pick. A page that only pictures the options is a poster;
the decision still happens somewhere else.

## Honesty rules

- **Prose first.** One or two sentences of plain answer, then the visual. If the reader has to parse
  a diagram to learn what happened, the reply failed.
- **Unknown is `?`.** Never invent a number to make a chart look finished.
- **Semantic emoji only.** Pass, fail, risk, blocked. Never decorative.
- **Draw the actual thing.** A queue looks like a queue. A row of generic rectangles joined by
  arrows teaches nothing the sentence did not.

## Serve a page via portless and hand the URL to a human

A file path is not a deliverable. A human opens URLs. On a machine with portless installed as a
service, every local page gets a stable `https://<name>.localhost/` address on port 443, with
trusted TLS and no port in the URL.

```bash
# 1. serve the folder that holds the page (any static server will do)
python3 -m http.server 8787 --directory docs/playgrounds

# 2. give it a name on 443. portless owns the TLS and the .localhost DNS.
portless alias glyph 8787

# 3. this is the URL you hand the human. No port, ever.
https://glyph.localhost/dev/2026-09-02-topic-explainer.html
```

`portless alias` takes a bare port, not a URL. If you would rather tie the name to one process,
register it for that process's lifetime:

```bash
portless glyph python3 -m http.server 8787 --directory docs/playgrounds
```

Never start a proxy with `--port` or `-p 1355`. That is the pre-service fallback: it puts a port in
every URL and squats the port the real service needs. If a `.localhost` URL does not load, run
`portless service status` first and `portless list` second: an absent route means the name was never
registered, not that the service is broken.

Then look at it before you say it is done:

```bash
agent-browser --cdp 9222 --no-pin-tab open https://glyph.localhost/dev/2026-09-02-topic-explainer.html
agent-browser --cdp 9222 --no-pin-tab screenshot /tmp/glyph-check.png
# now READ the image. A page you have not looked at is not done.
```

Numeric gates (lint, DOM probes, SVG counts) are blind to layout and collision defects. Read the
pixels. Then hand over the URL under an "Open" heading, one URL per line, and put the DECIDE block
under "Decide" right after it. If no browser path works from your session, hand the URL to a peer
that has one and wait for the verdict. Do not report done on a page you have not seen.

## Overrides

`--chat`, `--ask` and `--page` force the surface. `--eli5` sets novice and page unless a surface
flag is also given. `--decide` forces the decision-page route. An explicit flag always beats
inference, and the announce line still prints. With no argument at all, the topic is the current
conversation: render that, do not ask what to draw.

## See also

- [/developers](https://yonyon.ai/developers): every public endpoint on this site, for agents and
  humans.
- [/llms.txt](https://yonyon.ai/llms.txt): the site index for agents.
- [/ask](https://yonyon.ai/ask): one-shot questions about Yonatan's work, JSON or SSE, no auth.
