# Visual primitive contract v0.1

Status: existing prototype compatibility contract. It does not define the target five-form product. The [project specification](project-spec.md) governs new development; automatic PNG fallback below is transitional behavior, not the final quality guarantee.

## Purpose

Keep agent-authored diagrams flexible at the semantic level while making layout, typography, colors, and edge routing deterministic. The external interface remains one `render_visual(spec)` call.

## Presentation

`render_visual` accepts `presentation: "terminal" | "image" | "both"`, defaulting to `image` for backward compatibility.

- `terminal`: return deterministic fenced Mermaid for Pi's Unicode Mermaid renderer when terminal checks pass; otherwise return PNG and explicit degradation reasons.
- `image`: return the PNG image block and SVG/PNG artifact paths.
- `both`: return Mermaid, PNG, and artifact paths when terminal checks pass; omit Mermaid and return PNG with reasons on fallback.

Mermaid is an output adapter over the validated VisualSpec, never an accepted input format. The agent does not author Mermaid directly.

The adapter consumes normalized Card IDs and Relation endpoints. Edge labels are quoted and escaped for the validated Pi parser, preserving punctuation as text rather than graph syntax.

The terminal adapter cannot preserve a Stack directly nested inside another Stack. Such inputs remain valid and render normally to SVG/PNG; terminal requests explicitly degrade to PNG. Parsing warnings, render failure, roadmap cycle degradation, and actual Unicode width above the renderer-owned 120-column budget also trigger fallback. No new containment or Relations are invented to force Mermaid layout.

Results distinguish `requestedPresentation` from the actual `presentation`. `mermaidDiagnostics.terminal` contains `width` (null if rendering failed), `maxColumns`, `warnings`, and `fallbackRecommended`. `degraded` and `degradationReasons` describe terminal degradation; `splitRecommended` includes excessive terminal width. SVG diagnostics and canvas dimensions remain separate. MCP does not return the rejected Mermaid candidate, including in structured content.

## VisualSpec

| Field | Required | Contract |
| --- | --- | --- |
| `version` | yes | Exactly `"0.1"`. |
| `title` | yes | Non-empty artifact title. |
| `subtitle` | no | Short supporting context. |
| `composition` | yes | One Primitive, maximum layout-container depth 4. Card and Note leaves do not add container depth. |
| `relations` | no | Typed Card-to-Card relationships. |
| `options.legend` | no | `auto` by default, or `hidden`. |

VisualSpec rejects arbitrary coordinates, dimensions, colors, fonts, CSS, and unknown fields.

## Stack

Controls reading order but never creates an implicit Relation. Fields: `type`, `direction` (`horizontal` or `vertical`), optional tokenized `gap`, and one or more `children`.

Horizontal Stack children are vertically center-aligned. When connected Card centers differ by no more than 6 pixels after layout, the renderer snaps the Relation to one straight horizontal path instead of drawing a short dogleg.

## Group

Expresses semantic containment and owns no layout policy. Fields: `type`, optional `id`, required `title`, and exactly one `child`. Use a Stack to arrange multiple items.

## Card

The only addressable Primitive. Fields: `type`, unique `id`, `title`, optional `description`, visual `tone`, and domain `badge`. Tones are `neutral`, `info`, `positive`, `warning`, and `danger`.

## Note

Annotates a constraint, risk, or conclusion but is not an entity in the relationship graph. Fields: `type`, optional `title`, required `body`, and `tone` (`info`, `warning`, or `danger`). It has no public ID and cannot be a Relation endpoint.

## Relation

Spatial proximity and ordering never imply a Relation. Both endpoints must reference existing Card IDs.

| Kind | Meaning | Default rendering |
| --- | --- | --- |
| `flow` | Data or control moves between Cards. | Solid arrow. |
| `dependency` | The source requires the target. | Dashed arrow. |
| `association` | Related without direction. | Solid line without arrow. |
| `feedback` | Retry, correction, or return path. | Accented dashed feedback lane. |

Only an explicit `feedback` Relation is routed as feedback.

## Derived legend

Legend is not a Primitive. With `options.legend: "auto"`, the renderer derives it from non-neutral tones, badges, and non-default Relation kinds so the legend cannot contradict the visual.

## Template compilation

`architecture`, `gap-map`, and `roadmap` are macros over this contract. They compile to Stack → Group → Stack → Card compositions and then use the same validation, scene layout, output, and diagnostics path as direct composition.

## Required diagnostics

Primitive and Relation counts, maximum container depth, text overflow IDs, feedback count, derived-legend state, canvas dimensions, split recommendation, and degradation reasons.

## Error modes

Reject unsupported fields or types, duplicate Card IDs, invalid tones or Relation kinds, missing required fields, Relations to non-Cards, and layout-container depth greater than 4.
