Skip to content

YAML schema (full)

Status: spec stream 1, project semver 0.1.0 (pre-1.0). In-place tightening allowed until the project hits 1.0.0. See the Versioning policy.

A sightmap is a directory of YAML files at the root of a project, under .sightmap/. It describes the app’s views, components, and requests, with optional memory entries that carry notes agents can use at runtime.

This document is the human-readable reference. The machine-readable contract is sightmap.schema.json.

  • Every *.yaml and *.yml file under .sightmap/ is discovered recursively.
  • All files are loaded and merged at load time. The directory layout is a convenience for authors; it has no semantic meaning.
  • Every file must begin with version: 1.
  • Merging is shallow-append per top-level collection (views, components, requests). Two files may define the same view; the runtime behavior in that case is implementation-defined and SDKs SHOULD emit a warning.
version: 1
memory: # optional, string[] — file-level notes (see "Memory")
views: # optional, View[]
components: # optional, Component[] — global, matched on every view
requests: # optional, Request[] — global, matched on every view
FieldTypeRequiredDescription
versionintegeryesMust be 1.
memorystring[]noFile-level memory entries. Surfaced as context to the agent for any view in this file.
viewsView[]noViews defined in this file.
componentsComponent[]noGlobal components — matched against every view.
requestsRequest[]noGlobal requests — matched against every view.

A named screen in the app, identified by a URL route.

- name: FlightSearch
route: /search
description: Main search page with date and origin/destination pickers
source: src/pages/FlightSearch.tsx
memory:
- The search form lives inside a modal on mobile; selectors differ
components: [...]
requests: [...]
FieldTypeRequiredDescription
namestringyesShown in the snapshot header. Should be unique across the sightmap.
routestringyesGlob pattern matched against the URL pathname. See Route matching.
descriptionstringnoFree-text. Not surfaced at runtime but useful for PR review and future maintenance.
sourcestringnoRelative path to the source file.
dependenciesstring[]noSupplementary files (minimatch globs, project-root-anchored, ! negates) whose changes should trigger re-curation of this view. See Dependencies.
memorystring[]noView-level memory entries.
components(Component | ComponentRef)[]noView-scoped components. Additive with globals. Entries may be inline definitions or $ref reference objects.
requestsRequest[]noView-scoped requests. Additive with globals.

A named DOM subtree, identified by one or more CSS selectors.

- name: DepartureDatePicker
selector: '[data-picker="departure"]'
source: src/components/DatePicker.tsx
description: Departure date picker, calendar + typed input
memory:
- Accepts typed YYYY-MM-DD — skips the calendar
- Past dates render but are aria-disabled
children:
- name: date-input
selector: input
- name: day
selector: '[role="gridcell"]'
FieldTypeRequiredDescription
namestringyesReplaces the generic a11y role in enriched snapshots.
selectorstring | string[]yesCSS selector, or a list of alternatives. First match wins.
sourcestringnoPath to the source file. Rendered inline as [src: …] in enriched snapshots.
dependenciesstring[]noSupplementary files (minimatch globs, project-root-anchored, ! negates) whose changes should trigger re-curation of this component. See Dependencies.
descriptionstringnoFree-text. Not surfaced at runtime.
memorystring[]noComponent-level memory entries.
children(Component | ComponentRef)[]noNested components. Child selectors are scoped to the parent’s subtree. Entries may be inline definitions or $ref reference objects.
  • A single string is a standard CSS selector.
  • A list of strings is tried in order; the first selector that matches wins.
  • Selectors in children are evaluated within their parent’s matched subtree, not globally. This scoping is how Sightmap avoids naming collisions between, say, two different card components that both contain a button.primary.
  • Selectors are not required to be unique at their level. If a selector matches multiple elements, all matches are named.

Any entry in a components: array — at file root, within a view, or under children: — may be a reference object instead of an inline definition:

- $ref: ComponentName

A $ref entry is expanded inline (deep copy) to the named component’s full definition before matching. The name is resolved against a registry built from the root-level components: arrays of all loaded files.

FieldTypeRequiredDescription
$refstringyesName of a component defined at file root. The entry MUST contain no other keys.

Lookup scope. Only components defined at the root of some file’s components: array are addressable. Components nested under children:, or defined inside a view’s components:, are not in the registry. First-seen wins on duplicate names (sorted by source-file path); conforming SDKs SHOULD emit a merge-collision-component warning.

Recursive expansion. Nested children of an inlined definition are themselves re-expanded if they contain further $ref entries. The cloned subtree is fully resolved before being inlined.

Source attribution. The expanded clone carries the registry definition’s source and dependencies verbatim — not the $ref site’s. A $ref attests where a component is expected to appear; the definition still lives at its root entry.

View attestation. When a view lists - $ref: ComponentName, it asserts “this component is expected on this page.” When the same name is also present in the file-root registry (i.e. the global auto-match would otherwise add it), the view-attested expansion takes precedence: the component appears once in the resolved output for that view, scoped to the view.

Conformance. SDKs MUST expand $ref before matching, MUST emit ref-unresolved (error) for an unknown name, MUST emit ref-circular (error) for a self-referential chain, and MUST NOT produce two matches for the same view when a view-scoped $ref and a file-root global share a name. See SEP-0002 for the full proposal and rationale.

# .sightmap/components.yaml — definitions
version: 1
components:
- name: SiteHeader
selector: '#site-header'
children:
- name: SearchBox
selector: 'form#header-search'
- name: CartButton
selector: 'a[data-testid="header-cart-icon"]'
- name: NavFooter
selector: '#site-footer'
# .sightmap/views/plp.yaml — view-scoped, attests its expected globals by reference
version: 1
views:
- name: PLP
route: /b/**
components:
- $ref: SiteHeader # attests SiteHeader is expected on this view
- $ref: NavFooter # attests NavFooter is expected on this view
- name: ProductGrid # view-specific definition
selector: '[data-component="ProductGrid"]'
children:
- name: ProductCard
selector: '[data-testid="product-pod"]'

The dependencies field on a view or component declares supplementary files whose changes SHOULD trigger re-curation of the entry. It is purely curation-time metadata — runtime adapters (sightmap_runtime_snapshot, browser-driving SDKs) read DOM/fiber state, not source files, and MUST NOT introduce page-load runtime cost on the basis of this field.

  • Hooks the view or component consumes (e.g. useChecklist, useAuth)
  • Services, stores, and shared utilities
  • CSS / style files the entry loads
  • Helper modules that don’t warrant their own entry
  • Tests (*.test.ts, *.spec.tsx)
  • Type-only imports
  • Framework code (React, Vue, etc.)
  • Files that have their own component or request entry — use the existing entry-level binding, don’t restate it here

Strings in dependencies are interpreted as minimatch globs, project-root-anchored (the directory containing .sightmap/). A ! prefix negates. When multiple positive globs match the same file, the first positive glob in declaration order wins for provenance reporting (matchedBy in explain --by path output).

A conforming SDK MUST surface a diagnostic when:

  1. An entry’s resolved dependencies[] set contains its own source. Diagnostic code: dependencies.self-redundant.
  2. An entry’s resolved dependencies[] set contains a path that is the source of any other entry in the same .sightmap/. Diagnostic code: dependencies.overlaps-entry.
  3. A glob in dependencies[] resolves to zero files. Diagnostic code: unknown-source (existing vocabulary, narrowed to apply to dependencies[] globs).
views:
- name: ListView
route: /lists/:id
source: src/views/ListView.tsx
dependencies:
- src/hooks/useChecklist.ts
- src/stores/listStore.ts
- src/styles/list-card.css
components:
- name: ListCard
selector: '[data-component="ListCard"]'
source: src/components/ListCard.tsx
dependencies:
- src/components/ListCard.module.css

See SEP-0001 for the full proposal and rationale.

A named API endpoint.

- name: SearchFlights
route: /api/flights/search
method: POST
description: Run a flight search and return results
source: src/api/flights.ts
request:
fields:
- name: origin
type: string
- name: destination
type: string
- name: departureDate
type: string
description: ISO-8601 date
response:
fields:
- name: results
type: array
headers: [x-request-id]
memory:
- 429s on more than 10 requests/min per user
FieldTypeRequiredDescription
namestringyesShown in network list and detail output.
routestringyesGlob pattern. Express-style :param segments are normalized to *. See Route matching.
methodstringnoHTTP method filter (GET, POST, …). Match-any if omitted.
descriptionstringnoWhat the endpoint does.
sourcestringnoRelative path to the source file.
requestPayloadnoExpected payload shape.
responsePayloadnoExpected response shape.
headersstring[]noNotable header names to highlight in the network detail view.
memorystring[]noRequest-level memory entries.
request:
fields:
- name: origin
type: string
description: IATA code
FieldTypeRequiredDescription
fieldsField[]noExpected fields. Not exhaustive; extra fields are not rejected.
FieldTypeRequiredDescription
namestringyesThe field name.
typestringnoFree-text type label: string, number, boolean, array, object, or anything else an SDK author finds useful.
descriptionstringnoFree-text description.

Memory entries are short freeform notes attached to any definition — file, view, component, or request. They exist so that agents can carry forward context that isn’t recoverable from the source code: quirks, invariants, workarounds, “you have to click this twice” lore.

memory:
- Past dates render but are aria-disabled
- Range: 1st click = start, 2nd = end, 3rd resets

Design points:

  • Each entry is a single human-readable sentence or short bullet.
  • Entries on a component apply whenever that component is matched on the current view.
  • Entries on a view apply whenever the current URL matches that view’s route.
  • File-level entries apply whenever any definition from that file is active.
  • Entries on a request apply in the network-trace detail view.
  • Conforming SDKs SHOULD surface applicable memory entries in a [Guide] section at the top of enriched output.

Routes use glob patterns against the URL pathname.

  • * matches exactly one path segment: /users/* matches /users/42, not /users/42/edit
  • ** matches any depth of segments: /admin/** matches /admin, /admin/users, /admin/users/42/edit
  • Literal segments match themselves
  • Matching is case-sensitive
  • Query string and fragment are ignored
  • Trailing slashes are normalized away before matching

For requests, Express-style :param segments are normalized to *. These are equivalent:

- route: /api/users/:id/orders # same as below
- route: /api/users/*/orders

Views: most specific wins. When several views match the URL, the most specific route wins — scored per segment (literal 3, :param 2, * 1, **/empty 0; root / scores 1). Definition order does not set priority; it only breaks ties between equally specific routes. See Views › Most specific wins. Requests are matched independently — see Requests.

Components and requests can be declared at the file root or nested inside a view.

  • Global (components: or requests: at file root): matched against every view.
  • View-scoped (nested inside a view): matched only when that view is active.
  • They are additive. A view that defines its own components receives both the global components and its own.
components:
- name: Navigation # global — matched everywhere
selector: 'nav[data-component="Navigation"]'
views:
- name: Dashboard
route: /dashboard
components:
- name: DashboardLayout # scoped — only on /dashboard
selector: '[data-component="DashboardLayout"]'

A conforming SDK:

  • MUST accept any file that validates against sightmap.schema.json
  • MUST reject any file that does not
  • MUST implement route matching as specified
  • MUST implement global vs view-scoped precedence as specified
  • SHOULD surface memory entries to the agent when the parent definition is active
  • MAY ignore fields it doesn’t use (e.g. description is never surfaced at runtime by Subtext today)
  • MAY implement additional, non-standard behavior as long as it doesn’t change the meaning of conforming inputs

See Conformance fixtures for the language-agnostic test suite.

These are explicitly unresolved in v1 and candidates for SEPs:

  • Inheritance or import across files (today the only merge is implicit directory-level concatenation)
  • Parameterized memory — interpolating runtime values into memory entries
  • Schema for validating the shape of response.fields against real responses (today fields is documentary, not enforced)
  • Macros — learned trajectories that replay and heal when the site changes (teased in the website; not yet in the spec)

See seps/README.md to propose.