# BRIEF.code-presence.md — Code presence as display state (DEC-025)
**Project:** relay-creative / display direction for DEC-025 identity-and-codes
**Status:** direction proposal — nothing here binds app work until EC ratifies
**Live concepts:** `/concepts/code-presence-v1.html` (app surfaces) ·
`/mcp/code-presence-display.html` (MCP rich + plain tiers)
**Upstream decision:** relay-platform `docs/DESIGN-NOTE.identity-and-codes.md`,
`docs/GUIDE.identity-and-codes-rollout.md` (branch
`claude/relay-code-access-control-ydwgbj`) · Linear RCTX-1066 · relay `AJA9TN`
**Date:** 2026-08-01 · v3.5 neutral-first

---

## Why

Today every object mints a Code at creation, shared or not. That makes the Code
**wallpaper** — the most distinctive thing in the product, rendered on things
nobody ever handed to anyone.

DEC-025 fixes it: a Code exists **if and only if** an actor deliberately elevated
context for handoff — human, agent, or system alike. Presence of a Code is
evidence of that act. Absence means the object never left.

The consequence for design is blunt: **most objects will have no Code.** Every
surface built on the assumption that a Code is always there now has a second
state it was never drawn for.

## The question this brief answers

Not *"what replaces the Code."* That was tried and rejected — see
[Rejected drafts](#rejected-drafts).

**How does a surface look when the hero artifact is legitimately absent, without
the absence reading as an error or a missing field?**

The answer the design must express, in one line a person could say out loud:

> **A Code means someone chose to share this. No Code means it never left.**

Both states are correct and normal. The difference should be readable at a
glance, and neither should look like the other one broken.

## The four laws

### L1 · Mark presence, never absence

No placeholder. No em-dash in the Code slot. No dashed outline, no ghost
lozenge, no "Not shared" stamp on the row. **A symbol that stands for
*nothing happened* is exactly what reads as an error** — a person scanning a
list cannot tell your deliberate em-dash from a failed fetch, and neither can a
screen reader.

Shared objects are marked. Unshared objects are simply quiet.

### L2 · One teal moment per object

The accent tracks **where the intent lives**:

| State | The one teal moment |
|-------|--------------------|
| Shared | the **Code** — `--accent-text`, mono, the brightest thing in the card |
| Unshared | the **invitation** — the share action, `--accent-fill` + `--on-accent` |

So the surface never loses its accent and never doubles it. On an unshared
object the brightest element is the *act that would create a Code* — which is
the honest hierarchy, because that act is the only thing of consequence
available on that object.

This also gives the two states a **shape-level** difference (a mono token vs. a
filled button) rather than a colour-level one, which survives greyscale,
low-vision, and the plain-text MCP tier.

### L3 · The title never changes rank

Same slot, same size, same weight, same colour in both states. Nothing is
promoted to fill the gap.

The title's job is **recall** — finding your own work again. The Code's job is
**sharing**. Different jobs; they do not compete and they do not substitute.
On an unshared object the title is simply a label, which is what it always was.

This law is the direct lesson of the rejected draft, and it is the one most
likely to be violated by accident: a designer sees empty space where the Code
was and reaches for the nearest text field.

### L4 · Absence is addressable, not annotated

On list surfaces, "never shared" is a **filter you can select**, not a badge
stamped on every row. Make the set selectable and absence becomes legible as a
*population* — "41 of 68 never left this workspace" — instead of a mark repeated
41 times that each individually looks like missing data.

This is what satisfies "absence of a Code is information and should be legible
in the UI" without violating L1.

## Surface-by-surface

| Surface | Shared | Unshared |
|---------|--------|----------|
| **Relay detail** | Code in the hero slot (`.relay-code-hero`), copy affordance, provenance line | Hero slot **absent** — the layout closes up, it is not left empty. Title in its normal label slot. Share is the primary action. |
| **Share / elevation** | Code hero at full weight; this is the surface the Code was designed for | The panel *is* the act — the Code slot is occupied by the invitation until the moment of elevation, then by the Code, arriving once |
| **List / inbox** | Code in the leading rail, mono, `--accent-text` | Rail carries nothing. Row is otherwise identical. Segment control (`All · In circulation · Never shared`) carries the state |
| **Claim / notification / onboarding** | unchanged — sharing *is* the subject on these surfaces, the Code stays hero | n/a — a codeless object cannot reach these surfaces |

The old canon line — *"the Code is the product — hero artifact on every relay
surface"* — is **narrowed, not replaced**:

> The Code is the hero artifact on every surface where **sharing is the
> subject**: share panel, handoff, claim, notifications, onboarding.

## The elevation moment

This is where the Code is most hero, and lazy mint makes it *better*: minting on
every object makes the Code wallpaper; minting on intent makes it **earned**.
The Code's first appearance is now an event with a cause, so it gets the one
event animation on the surface — `.rly-materialize` (pulse family #7,
characters land top-down, 55ms stagger, once, system teal) and nothing else
moving.

Copy stays at **capability** level, never mechanism — RCTX-1073. "A Code means
someone chose to share this" clears the gate. Describing *how* minting works
does not. UI labels name the act ("Share this relay"), never the machinery.

## Rejected drafts

Kept here so they are not re-proposed.

| # | Draft | Why it fails |
|---|-------|--------------|
| 1 | **Title as hero** on codeless objects | Rejected by EC. Title is the most generic field in the schema — every competing tool has one. It cannot carry a verb surface, and promoting it teaches that the hero slot is a *container for whatever is available* rather than a claim about identity. |
| 2 | **Title in Code costume** — mono, uppercase, letterspaced in the Code slot | A costume. It fakes the artifact with a field that has none of its properties: not short, not shareable, not unique, not speakable over a phone. Worse than #1 because it is #1 wearing a disguise. |
| 3 | **Show the ID** (`r7f3a91c…`) where the Code would be | IDs are lowercase hex machine identifiers and are **not a display surface** (invariant C2). Their narrow alphabet is deliberate — it keeps them shape-disjoint from uppercase Codes, which is what lets one resolver dispatch on shape alone. Rendering one to a person leaks a machine handle and invites the demand to prettify it, which is identity churn. |
| 4 | **Em-dash / "—" or "Not shared" chip** in the Code slot | Violates L1. Indistinguishable from a failure state at a glance, and it spends the row's attention budget on a non-event. |

## Accessibility (non-negotiable)

- Entity prefix glyphs are **visual-only** and must never reach the accessible
  tree as raw text: `aria-hidden="true"` on the symbol plus a visually-hidden
  plain-English type label. A screen reader reads **"Relay A3BF72KQ"**, never
  the Unicode character name.
- The absence of a Code produces **no announcement**. There is nothing to say;
  saying "no code" would be narrating a non-event to the people least able to
  ignore it. State reaches assistive tech through the list filter and through
  the share action's own label.
- Codes as text use `--accent-text` (5.5:1 light / 10.4:1 dark). Teal fills use
  `--accent-fill` + `--on-accent`; dark mode takes the `#0d1514` ink-flip —
  white-on-teal is 1.9:1 and is never used.
- The one event animation disables under `prefers-reduced-motion`; the Code
  still arrives, it just arrives instantly.
- **Finding, needs a call:** the ratified light-mode button fill `#0d9488` is
  **3.74:1** with a white label, which fails AA at the 13px/600 used on product
  buttons. `relay-tokens-v35.css` already names the alternate inline
  (`swap to #0f766e for strict AA`, 5.5:1), and both concept pages use it,
  because under L2 that button *is* the unshared state's one accent and carries
  the law. Dark is unaffected: `#2dd4bf` with the `#0d1514` ink flip is 9.9:1.
  Either ratify `#0f766e` as `--rly-accent-fill` in light, or accept that
  primary button labels must be ≥18.66px bold to qualify as large text.

## MCP tiers

Per-harness rich/plain — relay-platform `docs/DESIGN-NOTE.mcp-rich-display.md`,
`rui-*` kit in `src/relay/mcp/ui/`.

- **Rich:** the same four laws, expressed in `rui-*` components. Code hero in
  the card head when shared; head closes up when not, and the card's action row
  carries the invitation.
- **Plain:** bracketed type labels, never Unicode glyphs — `[Relay] A3BF72KQ`.
  An unshared object renders its title alone with no bracket-and-token line at
  all. L1 holds hardest here: in a plain-text transcript, any placeholder
  character is indistinguishable from a truncation artifact.

## What ratification unblocks

relay-app `REFERENCE.product.md:47` and `REFERENCE.screens.md:19` both still
carry the old "hero artifact on every relay surface" line. Rewriting them is
**gated on this direction**, and they in turn gate all Train 3 app work.

Note for sizing: the rollout guide's touchpoint #1 counts 52 code references in
`relay-detail.ts`. That is the mechanical part. The real work is the visual
hierarchy of every relay surface, and it is not counted anywhere yet.

## Open questions → EC

1. **Ratify the four laws** (or amend). L3 is the load-bearing one.
2. **List default filter** — proposed `All`. Alternative: default to
   `In circulation` on the inbox surface, where the shared set is the subject.
3. **Does an unshared object show any provenance at all** (created-by, stream,
   time), or is the quiet state fully quiet? Proposed: yes, provenance stays —
   it is recall metadata, and recall is the unshared object's whole job.
