Frontmatter¶
A fiber's frontmatter has two halves. felt owns a fixed set of native fields. It preserves every other top-level key without interpreting it; the tool that writes a key owns its meaning.
The rule: felt owns the fiber; each additional field has its own owner.
Native fields¶
| Key | Type | Set by |
|---|---|---|
id |
ULID string | minted at felt add |
name |
string | felt add <slug> <name>, felt edit --name |
status |
open / active / closed |
felt edit -s/--status; shuttle lifecycle verbs |
tags |
list of strings | -t/--tag, --untag |
created-at |
timestamp | felt, on create |
updated-at |
timestamp | felt, on every write |
closed-at |
timestamp | felt, when status becomes closed |
outcome |
string | felt edit -o/--outcome; shuttle set-outcome |
due |
date | -D/--due (YYYY-MM-DD) |
description |
string | file only — no CLI flag |
felt parses, validates, and formats these. description stands apart: felt
owns it natively, but only a file edit sets it.
Project-owned fields¶
felt treats every other top-level YAML key as opaque. It preserves the key
verbatim across read/edit/write, exposes it in --json, and lets you filter
and read it. It never validates the semantics. If your project wants
dataset:, instrument:, or an inputs: block describing data flow, put it
in.
---
name: Cosebis data vector
instrument: KiDS-1000
inputs:
- id: shear_catalogue
from: catalogue-cuts
---
Set scalars from the CLI. felt reads the value as a YAML scalar, so types survive:
felt edit damping-prior --set instrument=KiDS-1000 --set n_patches=200
felt edit damping-prior --unset instrument --unset n_patches
Edit structured blocks — lists, nested maps — in the file directly. --set
handles scalars only.
Reading them back¶
felt show damping-prior --field instrument # one key, shell-friendly
felt show damping-prior -d compact # metadata + the extra keys' names
felt ls --has-field instrument # fibers that carry the key
felt ls --json --json-field instrument # just that field, as JSON
felt ls -j # everything, as JSON
--field formats for shell consumers: scalars on one line, sequences of
scalars one per line, structured values as YAML. shuttle ls and shuttle
show accept the corresponding list/read flags and add the resolved Shuttle
facet to JSON; the felt commands leave the block as plain frontmatter.
One convention felt does understand: inputs.from names another fiber as a
data-flow input. Every inputs: entry with a from: is an edge, whether or
not it carries an id: (the id only labels it). felt show <id> --consumers
gives you the reverse edge, felt check flags broken from references, and
felt nest rewrites the ones a move would break. That is all felt itself
reads. The rest of the block stays yours.
Conventions other tools read¶
A key felt treats as opaque can still mean something to whatever else looks at your fibers. These are the ones in this repo, so you recognise them if you meet them — and so you do not pick the same names for something else. felt validates none of them; shuttle validates and resolves its own block.
| Key | Read by | Meaning |
|---|---|---|
inputs.from |
felt | A data-flow edge to another fiber |
shuttle: |
shuttle CLI and daemon | Dispatch configuration and lifecycle state; felt preserves the block without validating or resolving it |
tag cycle + start: / due: |
the shuttle board | A named span of calendar time, drawn as a band. Written by dragging in the Chronicle |
horizon: stashed |
the shuttle board | Takes a card off the Now board to Resting. A due: day at or past today overrides it and pulls the card back |
cold: true |
the shuttle board | Marks a stashed card held open: its Resting cluster renders dimmer, wears a held open tag, and sorts below the warm clusters. Cleared alongside horizon when the card returns to the desk |
The board writes these itself, through the daemon, which shells the same felt
edit --set you would type. See Cycles and eras and
The board.
Ids¶
felt mints id as an intrinsic ULID at felt add and never changes it. It
identifies the fiber for federation and dispatch consumers, and it holds steady
when a fiber is renamed, nested, or moved.
You type the slug path as the address. The ULID carries the identity. JSON
output distinguishes them: id gives the slug address, uid gives the ULID.
felt show bao-analysis/damping-prior -j | jq '{id, uid}'
Every fiber carries one. A felt write to a fiber that has none (a file made
by hand) stamps a fresh ULID into it, and shuttle refuses to dispatch a fiber
without one, since its worker's tmux session is named <leaf>-<id>-shuttle.
Backfill a whole store with felt backfill-ids. Run it on the canonical store
only — see Fibers — and sync before
editing an id-less fiber elsewhere, so two replicas do not mint different ids
for it.
Never hand-edit timestamps¶
created-at and updated-at belong to felt. It restamps them on every write,
so it silently overwrites a value you type in by hand. updated-at anchors
recency in a git-durable way: it survives the clone, checkout, and reorg
rewrites that cross-machine sync inflicts, where file mtime does not. So felt
insists on owning it.
Edit content fields. Leave the clocks alone.