---
title: Vault Trail Schema + Lifecycle Reference
type: reference
tags: [vault, trails, schema, v1.6, TRAIL-01]
status: locked
locked_at: 2026-05-29
edges:
  refines:
    - "[[vault-edges]]"
  derived-from:
    - "[[v1.6-graph-primitives]]"
  see-also:
    - "[[vault-mochs]]"
---

# Vault Trails (First-Class Narrative Reading Orders)

`Knowledge/Trails/` is the trail-layer subtree — first-class
narrative-reading-order objects, structurally distinct from MOCs. A
trail is an ORDERED + NARRATED + DIRECTIONAL sequence through a
curated set of notes; a reader FOLLOWS a trail; a reader SCANS a MOC.

Trails ride the existing Phase 71 5-value Zettelkasten note-type enum
as `type: structure` (they ARE structure notes; they organize other
notes into reading orders) and are discriminated by a PARALLEL
top-level `kind: trail` field. The 5-value enum stays LOCKED VERBATIM
— there is NO 6th-value amendment; the `kind:` discriminator rides ON
TOP of `type:` per Phase 71 axis-separation discipline.

**No production-shipped cross-repo precedent exists** for a first-class
trail primitive with `sequence:` + `glue:` semantics. DEEP-03 SUMMARY
§Trails Open Question explicitly handed three sub-questions to plan-
phase: (1) glue shape at scale, (2) linear vs DAG, (3) `type: trail`
vs parallel `kind:`. The autonomous --auto chain that produced Phase
73 dispatched a fresh precedent check on **Andy Matuschak's
evergreen-notes pattern** per DEEP-03's hand-off. Matuschak's pattern
emphasizes atomicity, concept-orientation, dense wikilinks,
assertion-style titles, and a working-note → evergreen lifecycle —
but does **NOT** ship a first-class trail primitive with `sequence:` +
`glue:` semantics. Trail-like reading orders in Matuschak's graph
emerge implicitly from backlinks + concept clusters, not from an
authored sequence. Phase 73 ships the SPEC §3.4 shape as
GMD-original design — Matuschak's pattern does not supply the
missing precedent.

This doc captures the trail schema, lifecycle (with three forward-
reference subsections for downstream phases), trail-vs-MOC
distinction, and trail-surfacing mechanism. It is the authoritative
reference for authoring trails by hand and for downstream consumers
(`vault-query` / `vault-orient` `--kind=trail` filter, `vault-moc.js`
trail-surface pass — all shipping in Plan 73-02).

## Schema

Trail frontmatter shape — verbatim from
`.planning/phases/73-v1-6-d-trail-first-class-trails/73-CONTEXT.md`
§decisions TRAIL-01 schema bullet (which itself adopts the DEEP-03
SUMMARY §Trails provisional shape with Open Q1/Q2/Q3 RESOLVED):

```yaml
# Required
type: structure                 # 5-value enum value (Phase 71 LOCKED)
kind: trail                     # parallel discriminator (Phase 73 NEW)
sequence:                       # ordered array of wikilinks (LINEAR only — Open Q2 RESOLVED)
  - [[FirstStep]]
  - [[SecondStep]]
  - [[ThirdStep]]
created: YYYY-MM-DD

# Recommended
glue:                           # array; length = sequence.length - 1 (one per gap)
  - "Brief 'why this next' for [[FirstStep]] → [[SecondStep]]"
  - "Brief 'why this next' for [[SecondStep]] → [[ThirdStep]]"
tags: []

# Optional
domain: <string>                # subject-area scope (e.g., "vault graph primitives")
audience: <string>              # intended reader profile (e.g., "new contributor")
```

### Discriminator decision — parallel `kind:` (Open Q3 RESOLVED)

The trail-shape discriminator is the **PAIR** `type: structure +
kind: trail`. BOTH parts required:

- `type: structure` — the Phase 71 5-value Zettelkasten lifecycle
  stage (`fleeting | literature | permanent | structure | hub`). The
  5-value enum stays LOCKED VERBATIM; trails are structurally a
  SUBTYPE of `structure`, not a new lifecycle stage.
- `kind: trail` — the PARALLEL top-level discriminator field for the
  trail-specific schema. Mirrors the Phase 72 `kind: "atom"` JSON-output
  marker pattern and the Phase 71 `type:` / `source_kind:` axis-
  separation discipline. Future structure-subtypes
  (`kind: moc | kind: hub-of-hubs | ...`) can join cleanly.

**Why not `type: trail` 6th-value amendment:** would amend Phase 71's
locked enum. P3 violation (graph noise — what's a "trail-type
lifecycle stage"? Trails aren't a lifecycle stage). The 5-value enum
is about Zettelkasten lifecycle; trails are structurally a SUBTYPE of
`structure`.

**Why not duck-type via `sequence:` presence alone:** would mis-classify
any regular `type: structure` MOC that happens to carry a `sequence:`
field. The discriminator must be EXPLICIT, not inferred. The audit
harness `tests/audit-trail-schema.sh` M2 mutation arm LOCKS this at
the audit layer — a parser that duck-typed via `sequence:` presence
goes RED.

### Linear-only (Open Q2 RESOLVED)

Each `sequence:` step has exactly ONE successor (except the last).
No branching. No DAG. The named use cases (design evolution, citation
chains, onboarding pathways) are all linear. DAG support is deferred
per `73-CONTEXT.md` §deferred — if real authoring usage demands
branches, reconsider in v1.7 or follow-on. The schema shape `sequence:`
would then extend to per-step `successors:` list; lint would enforce
single-default-successor with optional alternates.

### Glue-array shape at scale (Open Q1 RESOLVED)

`glue:` is an ARRAY with length = `sequence.length - 1` (one entry
per gap between consecutive steps). A 3-step trail carries 2 glue
entries; a 5-step trail carries 4 glue entries. Each entry is a brief
"why this next" prose paragraph framing the transition for the reader.

- 3-8 step trails (the common case) → 2-7 glue entries — manageable.
- 50-step trails are not in named use cases; if they emerge, the lint
  surface will flag the cumbersome shape and we can reconsider.

Missing or short `glue:` arrays fail-open: the trail remains
functional (sequence is still navigable); the parser emits
`glue_missing` lint events per gap with no glue entry. `glue:` is
**recommended** but not required.

**Known limitation — `glue:` quality is a convention, not enforced.**
The lint surface validates `glue:` for **presence and length only**
(`glue_missing` per empty gap; `glue_length_mismatch` when
`glue.length !== sequence.length - 1`). It does NOT — and cannot —
assess the prose QUALITY of a glue entry: whether the "why this next"
narration actually earns the transition is a CONVENTION the trail
author upholds, not a property any lint can verify. A trail with a
present, correctly-sized `glue:` array of empty-but-nonblank or
low-value strings passes the lint clean. This is recorded here as a
known limitation so the gap is explicit (no silent gap): glue-quality
review is a human/editorial concern, deliberately left out of the
parser-layer contract.

### Structural validation at parser layer

The validation rules above are enforced INLINE in
`bin/lib/vault-frontmatter.js::parseFrontmatter` via the
`validateTrailSchema(fm)` hook (Plan 73-01 Task 1). The hook runs
AFTER the scalar/block-mapping parse loops complete + AFTER the Phase
72 atom-shape lint hook. Order: atom-shape lint THEN trail-shape lint
(independent; both fail-open).

Lint events emitted to `frontmatter.__lint[]` (same channel as the
Phase 72 ATOM `claim_exceeds_240_chars` event — NO new channel
introduced):

| Event                          | Shape                                                                                    | When                                                                  |
| ------------------------------ | ---------------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| `trail_sequence_too_short`     | `{ event, key: 'sequence', length, min: TRAIL_MIN_SEQUENCE }`                            | sequence missing or length < 2                                        |
| `glue_length_mismatch`         | `{ event, key: 'glue', glue_length, expected }`                                          | glue present + length !== sequence.length - 1                         |
| `glue_missing`                 | `{ event, key: 'glue', gap_index }` (one per missing gap)                                | glue absent OR shorter than sequence.length - 1                       |

Fail-open per SPEC §4: ALL violations emit lint events; parsing
NEVER throws; `fm.sequence` + `fm.glue` are preserved VERBATIM
(consumer responsibility per Phase 72 ATOM precedent). The Phase 75
`gmd-vault-lint` consumer surface will aggregate and report these
events.

`TRAIL_MIN_SEQUENCE = 2` is exported from
`bin/lib/vault-frontmatter.js` alongside `CLAIM_MAX_CHARS` /
`EDGE_VOCABULARY` / `EDGE_INVERSE_PAIRS` / `EDGE_SYMMETRIC` for
downstream audit consumption (mirrors Phase 72 ATOM precedent).

### Wikilink-target invariant — cites vault-edges.md

Trail steps in `sequence:` are wikilinks. The v1.5.3 FOUND-01 forms-1-3
phantom-edge boundary applies to alias resolution unchanged — see
`references/vault-edges.md` ## Inverse Pairs section as the upstream
contract for the wikilink-target invariant. Phase 73's parser
validates wikilink SHAPE only (`[[...]]` syntax via the existing
block-list scalar quote-strip + parser normalization). It does NOT
walk wikilink TARGETS to verify they resolve — that authoritative-
source check ships in Phase 75 `gmd-vault-lint` per CONTEXT.md
§deferred ("Trail step authoritative-source check").

### Placement — `Knowledge/Trails/` subtree

Trails live at `Knowledge/Trails/YYYY-MM-DD-<descriptive-slug>.md`
(filename convention mirrors Phase 72 ATOM placement). The curator's
`SOURCE_KIND_SUBTREE` / `NOTE_TYPE_FROM_PATH` table in
`bin/lib/vault-curator.js` includes `'Knowledge/Trails/' → 'structure'`
— first-write stamping rides this Phase 71 EDGE-02 mechanism
UNCHANGED. The path-inference entry AND the parser hook ship in the
SAME COMMIT per Plan 73-01 Task 1 MC5 intra-plan window closure;
splitting opens a window where path-inference stamps `type: structure`
on `Knowledge/Trails/` notes WITHOUT `kind: trail` validation.

## Lifecycle

Trails are **MUTABLE** structure notes. This is a key distinction from
Phase 72 ATOM-layer notes, which are IMMUTABLE per
`references/vault-atoms.md` §Lifecycle. Authors edit trails IN PLACE:

- Adding a step (extend `sequence:` + add a new glue entry per the
  new gap)
- Refining a glue entry (rewrite the prose for a transition)
- Swapping a step (replace one wikilink with another in `sequence:`;
  refine the two adjacent glue entries if the transition meaning
  shifted)
- Re-ordering steps (rearrange `sequence:`; re-author glue entries
  if needed)

This MUTABILITY posture is the right default for the named use cases
(design evolution narratives, citation chains, onboarding pathways) —
all of which evolve as understanding grows. The mechanism does not
preclude future immutability discipline (see subsection 1 below).

### Subsection 1 — Trail-supersedes-trail future extension

If real usage shows a read-many/edit-rarely posture and trail
immutability becomes load-bearing (e.g., authoritative onboarding
trails that must not silently shift under returning readers), a
follow-on phase can add a `superseded_by:` field for trails as an
ADDITIVE extension — mirroring the Phase 72 ATOM-layer supersession
forward-pointer.

The extension ships ADDITIVELY alongside the current mutable-edit
semantics: authors who continue to edit in place are unaffected;
authors who choose to supersede mark the old trail with
`superseded_by: <new-trail-id>` and move it to
`Knowledge/Trails/_archive/`. The documentation pattern would mirror
`references/vault-atoms.md` §Lifecycle Phase 75 bi-temporal forward-
reference — additive, not replacement.

Phase 73 does NOT ship this; the protocol is documented here as a
forward-reference contract so future phases bind cleanly.

### Subsection 2 — Atom-step staleness via supersession (cross-layer; lint-event name LOCKED)

**Lint-event name LOCKED for Phase 75 consumer:** `trail_step_superseded`

When an atom referenced in a trail's `sequence:` is superseded via
the Phase 72 `superseded_by:` flow (per `references/vault-atoms.md`
§Lifecycle), the trail step `[[OldAtom]]` becomes LOGICALLY stale
but the wikilink still RESOLVES — the old atom lives in
`Knowledge/Atoms/<branch>/_archive/` (and remains discoverable via
the file's continued presence at vault path) and/or via the v1.5.3
SKOS-lite alias mechanism (`references/vault-edges.md` ## Aliases).
The reader following the trail is now reading a superseded atom
without knowing it.

Phase 73 ships NO ENFORCEMENT for this cross-layer concern. Phase 75
`gmd-vault-lint` will surface the staleness as the `trail_step_superseded`
lint event (per-trail-step, indicating which `sequence:` entry points
at an atom whose `superseded_by:` field is now set). This subsection
LOCKS the lint event name as the cross-layer contract Phase 75 binds
to — the producer-side observation lives in Phase 75 (walking
trails + cross-referencing atom frontmatter); the lint surface that
displays the warnings rides the same channel as Phase 72
`claim_exceeds_240_chars` + Phase 73 `trail_sequence_too_short` /
`glue_length_mismatch` / `glue_missing` events.

Cite `references/vault-atoms.md` §Lifecycle for the upstream atom-
layer supersession mechanism — that doc owns the `superseded_by:`
contract; this subsection consumes the supersession signal
cross-layer.

**Status (known limitation) — `trail_step_superseded` is live but
unexercised by current vault data.** The lint is fully WIRED and
reachable: `bin/lib/vault-lint.js` walks every `type: structure +
kind: trail` note's `sequence:`, resolves each step's wikilink target,
and emits `trail_step_superseded` the moment a referenced atom carries
`superseded_by:` non-null (routed to §Bi-temporal Transitions). It is
quiet today only because **zero atoms in the current vault carry
`superseded_by:`** — there is no live data to trigger the condition.
The honest framing is "live but unexercised by current vault data,"
NOT a disabled or disconnected check: the code path is reachable and
will fire the first time a superseded atom appears in any trail step.
v1.7.1 records this as a documented known limitation (disposition:
DOCUMENT, not wire-to-fixture) — seeding a `superseded_by:` atom purely
to exercise a LOW-severity smell would add net LOC against a
LOC-negative-leaning milestone for no correctness gain. The check earns
its keep the day real supersession data lands; until then it is a
correct-but-silent guard, and that silence is intentional and recorded
here.

### Subsection 3 — Trail-step navigability for human readers (forward-reference)

The MOC `## Trails` auto-block (Plan 73-02 — extends
`bin/lib/vault-moc.js` `MARKER_PAIR_G` convention) renders each
trail's `sequence:` as a chain readers can step through. Each MOC
builder (`buildPerPhaseMOC` / `buildPerMilestoneMOC` /
`buildRootMOC`) scans for `type: structure + kind: trail` notes in
its scope and emits the auto-block with byte-deterministic ordering.

The Plan 73-02 audit `tests/audit-trail-moc-surface.sh` verifies:

- Block presence (the `## Trails` auto-block is emitted when trails
  are in scope)
- Byte-determinism (re-running MOC regeneration produces zero diff;
  deterministic sort order is trail-filename-lexicographic with
  sequence-length-descending tie-break)
- Scope-isolation (per-phase MOCs surface ONLY trails within their
  phase scope; cross-scope leak goes RED)

Navigability — the clickable step-through chain a human reader uses
in an Obsidian-style viewer OR a future GMD CLI surface — is the
downstream UX consumer concern. Phase 73's data plane ships the
invariants (auto-block emission + deterministic ordering + scope
isolation); the UX layer that surfaces the chain to a human reader
is NOT in Phase 73 scope. This subsection documents the consumer
surface as forward-reference for future verification work — Phase 76
VERIFY-03 or a downstream verification milestone may add a
navigability gate.

## Trail vs MOC Distinction

Verbatim from
`.planning/research/v1.6-deep-dive/SUMMARY.md` §Trails semantics block:

> Trails are ORDERED + NARRATED + DIRECTIONAL. MOCs are UNORDERED +
> STRUCTURAL.
> A reader follows a trail; a reader scans a MOC.

The discrimination is semantic, enforced structurally:

- **MOCs** are scaffolding for SCANNING. Broad navigation across a
  domain. Discriminator: `type: structure` (no `kind:` field, or
  `kind: moc` if future structure-subtypes are added). Body content
  is typically auto-blocks (`## Hub Notes`, `## Patterns`, etc.)
  generated by `vault-moc.js`.
- **Trails** are scaffolding for READING. Narrow guided journey
  through a curated sequence. Discriminator: `type: structure +
  kind: trail`. Body content is typically prose framing + a "## How
  to read this trail" subsection + author commentary; the
  `sequence:` field carries the structural order; the `glue:` field
  carries the per-gap "why this next" narration.

Both are `type: structure`; the `kind:` discriminator + `sequence:` /
`glue:` field presence distinguish them at the schema level.

### Concrete examples

The first dogfood instance of this schema is shipped alongside
Plan 73-01 as `Knowledge/Trails/2026-05-29-v1-6-graph-primitives-evolution.md`
— a 5-step trail walking through the v1.6 graph-primitives evolution
(synthesis note → EDGE → ATOM → TRAIL → MOCH preview), with authored
glue at each of the 4 gaps. That note is the live concrete example
this doc refers to; it dogfoods the schema under real authoring
pressure before downstream phases bind.

### Use cases (mirror DEEP-03 SUMMARY §Trails use_cases block)

- **Design evolution narratives** — walk a reader through how a
  primitive evolved (e.g., the dogfood trail above traces v1.6 graph
  primitives from synthesis through EDGE / ATOM / TRAIL / MOCH).
- **Citation chains** — atom-A → atom-B → atom-C provenance reading
  order; the glue per gap explains the inferential leap.
- **Onboarding reading orders** — new-contributor pathway through a
  domain (e.g., "Start here → vault primitives → audit harness
  pattern → first-PR walkthrough").

## Trail Surfacing Mechanism

Phase 73 splits across two plans:

- **Plan 73-01** ships the **data plane** — schema validation at
  parser layer + path-inference + reference doc + dogfood trail +
  mutation-RED audit.
- **Plan 73-02** ships the **surface plane** — runtime filters in
  `vault-query` / `vault-orient`, MOC regeneration trail-surface
  pass in `vault-moc.js`, and the surface-plane audit.

The surfacing mechanisms documented below ship in Plan 73-02; this
doc captures the contract so Plan 73-02 binds cleanly.

### `vault-query` / `vault-orient` `--kind=trail` filter (Plan 73-02)

A generic `--kind=<value>` filter lands symmetrically on
`vault-query` + `vault-orient`. For trail hits, the JSON output
adds:

- `kind: "trail"` marker (mirrors Phase 72 `kind: "atom"` shape)
- `trail_sequence_length: N` summary count
- `trail_first_step: <first wikilink>` preview

Default output does NOT expand the full `sequence:` (would be noisy
on large result sets). `--show-sequence` opt-in expands the full
sequence array in output for trail hits.

NO new default-OFF flag (different posture from Phase 72 ATOM
`--include-atoms` default-off, because atoms feed curator-ingest-
agent consumption while trails feed READERS directly). Trails are
STRUCTURE notes and surface by default in `vault-query` results just
like MOCs.

### MOC regeneration trail-surface pass (Plan 73-02 — extends `bin/lib/vault-moc.js`)

Each MOC builder (`buildPerPhaseMOC`, `buildPerMilestoneMOC`,
`buildRootMOC`) scans for `type: structure + kind: trail` notes in
its scope. Trails are rendered in a NEW `## Trails` auto-block
section, parallel to the existing `## Hub Notes` section. Each trail
renders as:

```
### [[Trail Title]] — N steps
Domain: <domain field if present>
Audience: <audience field if present>
First step: [[FirstStep]] → ... → [[LastStep]]
```

The trail-surface pass uses the existing `MARKER_PAIR_G` auto-block
convention (lines 241-243 of `bin/lib/vault-moc.js`); section name
`trails`. Idempotent regeneration: re-running produces zero diff.
Byte-determinism preserved per v1.3.5 Phase 27 MOC discipline
(deterministic sort: trail filename lexicographic; sequence-length-
descending tie-break).

### No new edge vocabulary — Phase 71 closed-10-verb lock preserved

The trail's `sequence:` field IS the trail structure. Phase 71's
closed 10-verb edge vocabulary stays LOCKED VERBATIM — there is NO
`next-in-trail` verb; there is NO vocab expansion. Individual trail-
step notes can carry existing typed-edge verbs (`refines`,
`supersedes`, `derived-from`, etc.) in their own `edges:` blocks for
cross-trail references; those edges flow through Phase 71's
`closeInversePairs` UNCHANGED.

If reverse traversal via inverse-pair becomes load-bearing — "find
all trails containing `[[NoteX]]`" — implement via direct field
scan first; vocab expansion is a separate decision requiring SPEC
amendment per Phase 71 EDGE-01 closed-vocab discipline.

## DEEP-06 v1.7-A CDEEP forward-reference

`Knowledge/Patterns/v1.6-graph-primitives.md` is the curator-promoted
v1.6 graph-primitives synthesis note (Phase 70 Plan 70-04). It is
the authoritative cross-milestone anchor for the v1.6 graph primitives
family — EDGE-01 vocab, EDGE-02 note-type enum, ATOM-01 schema,
EDGE-03 SKOS-lite aliases, TRAIL-01 schema (this doc). The literal
`graph-primitives` substring in this paragraph satisfies the DEEP-06
grep-discoverability contract.

The DEEP-06 contract marker at
`.planning/phases/70-v1-6-a-deep-research-substrate/deep-06-contract-marker.md`
is grep-discoverable. v1.7-A CDEEP phase (per SPEC §6 Open Q7 LOCKED
vocabulary-divergence contract) MAY define **code-trail** semantics —
navigating call graphs, refactor sequences, or similar code-domain
reading orders. If so, the v1.7-A plan-phase MUST cite this doc as
the v1.6 vault-trail upstream. The trail-layer mechanism (parallel
`kind:` discriminator + linear `sequence:` + glue-per-gap + parser-
layer structural enforcement) is reusable as the mechanism baseline;
the vocabulary (`domain`, `audience` fields; `## Trails` MOC auto-
block) is v1.6-A vault-specific and v1.7-A CDEEP MAY diverge — but
the divergence must be made explicit and routed through the same
SPEC-amendment path that originated this doc.

Mechanism-level consistency preserved; vocabulary-level divergence
permitted but explicit per SPEC §6 Open Q7 LOCKED.

---

*Audit: `tests/audit-trail-schema.sh` (Plan 73-01). Implementation:
`bin/lib/vault-frontmatter.js::parseFrontmatter` + `validateTrailSchema` +
`TRAIL_MIN_SEQUENCE` constant (Plan 73-01 Task 1). Path-inference:
`bin/lib/vault-curator.js::NOTE_TYPE_FROM_PATH` `Knowledge/Trails/` →
`structure` (Plan 73-01 Task 1 same-commit motion). MOC surfacing +
`--kind=trail` runtime filter: Plan 73-02 (extends vault-moc.js +
vault-query.js + vault-orient.js). Upstream edge contract:
`references/vault-edges.md` ## Closed Vocabulary (Phase 71 EDGE-01).
Atom step-staleness cross-layer: `references/vault-atoms.md` §Lifecycle
(Phase 72). DEEP-06 anchor: `Knowledge/Patterns/v1.6-graph-primitives.md`.*
