Declarations
The yaml contract that defines a tracker: fields, states, verbs, guards, and display, reviewed like code and enforced by the engine on every write.
Schema as a reviewed change#
A tracker is born from a declaration: one yaml file, owned by the agent that runs the tracker, shipped through the same review-and-merge governance as code. Applying a declaration is validated end to end; a declaration that names a field type, renderer, or section the engine does not ship is refused at the door with a precise error, never silently degraded. A typo cannot become a production surface.
This is the property that makes the engine safe to hand to a workforce that ships fast: the schema is a diff a reviewer can read, the validator is the same code that enforces writes, and these docs' own vocabulary tables are generated from that validator, so contract, enforcement, and documentation cannot disagree.
Anatomy of a declaration#
A declaration carries, at minimum: a slug (the surface URL), a code (the reference-code prefix), a title and description, an owner, and its fields, states, and verbs. Most trackers add a guide (the cold-reader explainer rendered beside the rows), visibility, and display.
Fields#
Each field declares a key, a type, and optionally a label, required, and type-specific attributes (an enum's options, a reference's target tracker:).
| Type | What it holds |
|---|---|
string |
Short single-line text. |
text |
Long-form text. With render: markdown it renders as prose. |
enum |
One value from a closed set declared on the field, each option carrying a tone chip. |
int |
A whole number. |
decimal |
A decimal number. |
bool |
Yes or no. |
datetime |
A point in time, stored UTC and rendered in the viewer's timezone. |
url |
A link, rendered clickable. |
person |
A workspace human, resolved against the directory. |
agent |
A fleet agent, resolved against the roster. |
file |
One attached file, served with fetch-time authorization and an access log. |
file_list |
A list of attached files. |
image_list |
A list of images, rendered inline on the row. |
row_ref |
A reference to another row, by full reference code. Same tracker by default; a tracker: attribute on the field makes it cross-tracker, and the target renders as a live card. |
thread_ref |
A reference to a conversation thread. |
json |
Structured data. Pair with a render hint (keyvalue, list, chips, code) to shape the cell. |
Structured cells take a render hint, so a JSON cell arrives shaped rather than raw:
| Hint | How the cell renders |
|---|---|
markdown |
Render the text as formatted prose. |
keyvalue |
Render object keys and values as a two-column list. |
list |
Render array items as a bulleted list. |
chips |
Render array items as compact chips. |
code |
Render preformatted, monospaced. |
States and tones#
States declare the row lifecycle. Each state (and each enum option) can carry a tone, which is how a row communicates health at a glance without anyone reading it:
| Tone | What it signals |
|---|---|
neutral |
No signal: the resting state. |
info |
Informational: in motion, no judgment. |
success |
A good outcome (resolved, verified, done). |
warning |
Needs attention before it becomes a problem. |
danger |
A bad outcome or a blocking state. |
Transitions can declare requires_fields: the fields that must be present before a row may enter that state. A bug cannot reach resolved without its "what changed for you" line; a review cannot close without its verdict. The guard lives in the engine, so it holds for every writer through every door.
Verbs#
Verbs are the tracker's closed action vocabulary. Each declares its effect and who may run it:
Verb effects (what a verb does to the row):
| Effect | What it does |
|---|---|
transition |
Move the row to a named target state, optionally requiring fields at the door. |
patch |
Update named cells without changing state. |
link |
Attach a reference (a thread, a row) to the row. |
Who tokens (who may run a verb):
| Token | Who that is |
|---|---|
owner-agent |
Only the tracker's owning agent, through its own authenticated gateway identity. |
admin |
Workspace admins, acting in the console under their own session. |
any-member |
Any signed-in workspace member. |
A verb call outside the declared vocabulary, with a wrong enum value, or from a caller outside the declared audience refuses loudly with the reason. Silent no-ops are treated as defects in Relay, not conveniences.
Renderers and display#
Most trackers use the default renderer; specialized shapes are named renderers from a closed set:
Row renderers (renderer: on the declaration):
| Renderer | The shape it draws |
|---|---|
generic-v1 |
The default: a filterable row list with a detail pane composed from the declared sections. |
staged-gate-v1 |
A stage spine across the top of each row detail: ordered stages, each empty, in progress, or done. |
report-v1 |
A run-ledger report: periodic rows rendered as a dense typed-column table. |
Dashboard renderers (renderer: on a declared dashboard):
| Renderer | The page it composes |
|---|---|
seat-custody-v1 |
The seat-custody board: who holds which Claude seat, utilization, and custody events, composed as one page. |
The report renderer's dense table cells come from their own closed vocabulary:
| Column type | What it renders |
|---|---|
id |
The row's reference code, linked. |
text |
Plain text. |
percent |
A percentage. |
meter |
A small horizontal meter. |
bar_money |
A money amount with a proportional bar. |
number |
A formatted number. |
money |
A money amount. |
money_cents |
A money amount stored in cents. |
split |
A two-part value rendered as a split cell. |
share_bar |
A proportional share bar across the row set. |
multiple_or_dash |
A multiplier, or a dash when absent. |
Charts are declared under display.charts; the kinds are enumerated in The tracker model.
A real declaration#
The worked example below is not a sample: it is the live declaration of our own bug tracker, re-emitted from the engine's stored copy at render time, so what you read here is what the engine is enforcing right now. It shows the full grammar in one place: typed fields (including person, enum with tones, image_list, url), a guarded lifecycle, a cold-reader guide, and display configuration.
code: GBUG
slug: gtos-bug-reports
guide: "Every **Report a bug** button on a GTOS surface (the console and every\nagent dashboard) files a row here. The runtime triages around the clock:\nit diagnoses, dedupes, DMs the reporter a receipt, and opens a per-bug\ntriage thread, so a `triaged` row already carries a diagnosis before any\nhuman looks.\n\n**Fix route** says whose hands fix it: `agent` rows the runtime ships\nitself; `terminal` rows wait for one of Norman's terminal sessions (the\nsweep); `product` rows belong to Gotrade product repos and get routed out.\n\n**States** are the lifecycle: `new` → `triaged` → one of `resolved`\n(always with a \"what changed for you\" line; the reporter's DM leads with\nit), `wont_fix`, or `duplicate`. A report that turns out to be a feature\nask converts: the work gets its FR and the row stays OPEN, wearing the\ndestination in **Tracks** and resolving when it ships (a weekly watcher\nfollows the destination; `routed` is retired since 2026-08-08 and only\nolder rows wear it). A `triaged` row can also be shelved to `backlog`:\nvalid but deliberately deprioritized on Norman's explicit ruling, reason\non the row. No resurface date (unlike a snooze), and reversible: a later\nsweep can pull it back to `triaged`. The **disposition chip** on an open\nrow is the other axis (who has it and why it waits), explained in the\nlegend below.\n\n**Module** is the product area the defect lives in (nine values, from\n`messaging` to `mobile-shell`; the canonical vocabulary is the console's\nfeature inventory, served at /api/product/modules). Pre-guessed from the\npage URL at filing, confirmed or corrected at triage: it is the sweep's\ngrouping axis, and the facet that answers \"show me the apps-engine\nqueue\". ENGINE ASKS (requests to expand the tracker engine itself: a new\nchart kind, verb semantics, a renderer) file as ordinary bugs from /apps\npages and wear `module: apps-engine` (the 2026-08-08 T8 amendment).\n\nReporters: your receipt DM tracks the row for you; you'll hear there the\nmoment the fix ships. The triage thread linked on each row is where the\ninvestigation lives."
owner: dario-soh
title: 'GTOS bug reports'
verbs:
-
key: set-status
who:
- owner-agent
- admin
effect: transition
-
key: annotate
who:
- owner-agent
- admin
effect: patch
fields:
- triage_note
- fix_route
- module
-
to: duplicate
key: mark-duplicate
who:
- owner-agent
- admin
effect: transition
requires:
- duplicate_of
fields:
- key: description
type: text
label: 'What happened'
required: true
- key: reporter
type: person
required: true
- key: source_app
type: string
label: Surface
- key: module
type: string
label: Module
- key: page_url
type: url
label: Page
- key: app_version
type: string
label: 'App version'
- key: user_agent
type: string
label: Browser
- key: viewport
type: string
- key: screenshots
type: image_list
- key: attachments
type: file_list
- key: triage_note
type: text
label: 'Triage note'
-
key: fix_route
type: enum
label: 'Fix route'
tones:
agent: info
product: neutral
terminal: warning
options:
- agent
- terminal
- product
- key: routed_to
type: string
label: 'Tracks (FR / PR)'
linkify: true
- key: user_impact
type: string
label: 'What changed for you'
- key: backlog_reason
type: string
label: 'Backlog reason'
- key: duplicate_of
type: row_ref
label: 'Duplicate of'
no_chains: true
- key: triage_thread
type: thread_ref
label: 'Triage thread'
- key: receipt_dm
type: thread_ref
label: 'Receipt DM'
- key: resolved_at
type: datetime
label: 'Fix shipped'
states:
- key: new
tone: warning
initial: true
- key: triaged
tone: info
-
key: backlog
tone: neutral
requires_fields:
- backlog_reason
-
key: resolved
tone: success
terminal: true
stamp_on_enter: resolved_at
requires_fields:
- user_impact
- key: wont_fix
tone: neutral
terminal: true
-
key: duplicate
tone: neutral
terminal: true
requires_fields:
- duplicate_of
-
key: routed
tone: info
terminal: true
requires_fields:
- routed_to
display:
list:
- code
- description
- state
- fix_route
- source_app
- claim
- reporter
- created_at
charts:
- x: created_at
key: intake
kind: bar
split: fix_route
title: Intake
bucket: week
- x: created_at
key: by-surface
kind: heatmap
scale: global
split: source_app
title: 'Intake by surface'
bucket: week
- x: state
key: by-state
kind: donut
title: 'By state'
-
x: resolved_at
'y':
to: resolved_at
agg: avg_days
from: created_at
key: turnaround
kind: line
title: 'Resolution turnaround'
bucket: week
- key: open
kind: burndown
title: 'Open over time'
bucket: week
detail:
lead: description
meta:
- reporter
- source_app
- module
- fix_route
- triage_note
- user_impact
- backlog_reason
- page_url
- app_version
- viewport
- user_agent
- resolved_at
links:
- triage_thread
- receipt_dm
- duplicate_of
- routed_to
media:
- screenshots
- attachments
density: dense
filters:
- disposition
- module
- fix_route
- source_app
- reporter
renderer: generic-v1
dense_list:
- code
- description
- state
- fix_route
- source_app
- module
- reporter
- page_url
- resolved_at
- created_at
section: operations
workflow: workflow-gtos-bug-report-intake
visibility:
class: restricted
audience:
roles:
- owner
- admin
description: 'User-filed bugs on GTOS fleet and subsystem surfaces, triaged and driven to resolution by Dario.'
capabilities:
claim: true
snooze: true
Evolving a schema#
Additive changes are a yaml edit: add the field, ship the PR, and new writes carry it while old rows render an honest gap. Renames and removals are deliberate, reviewed events, and because every row is soft-deleted and every write audited, no schema change can silently destroy history. When a tracker outgrows the engine (real joins, computation, bespoke logic), the substrate ladder names the exit ramp: records migrate to a typed home with their history, and the engine keeps doing what it is for.