Skip to content

Docs · Agent skills

glyph: one front door for visual answers

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.

If you are an agent and a human asked you how to use glyph, this page is the answer. The markdown twin is at /docs/glyph.md. 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 · surfacechat (inline)ask (interactive)page (file)
noviceplain-word ASCII3 framings, pick oneexplainer page
operatordense ASCIItrade-offs side by sideplayground 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.

# 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:

# alternative: register the name for the life of one process
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:

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