Concepts
Root4
Getting started
getting-started Create your first lettuce store and task in fewer than five commands, then learn where to go next.
The first successful path is fewer than five commands.
lettuce init --author jota --bootstrap-author --bootstrap-project default
lettuce task create LET-1 --project default --author jota --title "Try lettuce" --body "First task."
lettuce task list --project default
lettuce task show default/LET-1 --with-body
default is just an ordinary project name — there is no hidden default-project pointer and no project inference from store contents. Pass --project and --author explicitly for mutations; lettuce never infers them from the OS, Git config, or store contents.
Naming guidance
- Task IDs: a short uppercase prefix related to the project, e.g.
API-42,LAT-7. v0.15 does not enforce prefix/project coupling (that would make rename and import workflows harder). - Agent authors: stable, non-secret identifiers such as
codex-local-01,ci-agent,planner-agent.
The core agent loop
Once a store exists, an agent typically:
1. Discovers work — lettuce task list / lettuce query run (FQL). 2. Claims it — lettuce lease acquire (atomic, expiring). 3. Transitions status — lettuce task transition (validated). 4. Records execution — lettuce run start / run finish, artifact add. 5. Validates — lettuce validate --strict and lettuce doctor --format json.
Modes
- filesystem (default) — offline, canonical state under the store root.
- dedicated-git — a strongly-consistent shared store that fetches upstream before every mutation (requires connectivity by design; not an offline mode).
- client — set a server URL (and bearer token) to drive a running
lettuce serveover HTTP instead of mounting the store.
Next
- What is lettuce · Why lettuce
- Command Reference — every command, with flags and examples.
Links to
- What is lettuce
overview - Command Reference
reference/command-reference - Why lettuce (and not just Markdown + a convention)
why-lettuce
Backlinks
- The store and the two data planes
concepts/concept-store - First contact — what a fresh agent sees, reads, and does
guides/guide-first-contact - Lettuce Documentation
index - What is lettuce
overview - Why lettuce (and not just Markdown + a convention)
why-lettuce
Lettuce Documentation
index reserved Concepts, guides, and command reference for humans and agents working with Lettuce.
The source-of-truth knowledge bundle for lettuce, maintained in the lettuce repository itself. It has two layers:
- Curated — concepts, guides, and orientation, authored by humans/agents and gated by
okf check. - Generated — the Command Reference, emitted from the binary by
lettuce usage --format okfand regenerated bymake docs. Never hand-edit anything underreference/.
Orientation
Concepts
- The store and the two data planes
- Tasks and the work plane
- Workflow — states, transitions, and gates
- Cells — the coverage model
- Dimensions and members
- Methodology catalog — the orientation board
- Scopes — the DoD/board partition
- The shipped convention — coverage as data
- Milestones and Definition of Done
- Querying with FQL
- Graphs — authored process, enacted on the ledger
Guides
Agent Playbook — do-this-now guides for driving lettuce at full power:
- First contact — what a fresh agent sees, reads, and does
- Project setup playbook — prepare a project to leverage lettuce
- Agentic loop demo — one development cycle, step by step
Reference guides:
- Running lettuce in autonomous agentic cycles
- Configuring lettuce for useful leverage
- How to read lettuce — orienting in an existing store
- Serving lettuce safely (HTTP)
- Driving lettuce from a file: structured external input
- Retrying a mutation safely: --idempotency-key
Reference (generated)
- Command Reference — the exhaustive CLI surface, regenerated from the binary by
make docs.
Links to
- Cells — the coverage model
concepts/concept-cell - Dimensions and members
concepts/concept-dimension - Graphs — authored process, enacted on the ledger
concepts/concept-graph - Methodology catalog — the orientation board
concepts/concept-methodology-catalog - Milestones and Definition of Done
concepts/concept-milestone-dod - The shipped convention — coverage as data
concepts/concept-pack - Querying with FQL
concepts/concept-query - Scopes — the DoD/board partition
concepts/concept-scope - The store and the two data planes
concepts/concept-store - Tasks and the work plane
concepts/concept-task - Workflow — states, transitions, and gates
concepts/concept-workflow - Getting started
getting-started
+12 more
- Running lettuce in autonomous agentic cycles
guides/guide-agentic-cycles - Agentic loop demo — one development cycle, step by step
guides/guide-agentic-loop-demo - Configuring lettuce for useful leverage
guides/guide-config-for-leverage - First contact — what a fresh agent sees, reads, and does
guides/guide-first-contact - How to read lettuce — orienting in an existing store
guides/guide-how-to-read - Retrying a mutation safely: --idempotency-key
guides/guide-idempotent-mutations - Project setup playbook — prepare a project to leverage lettuce
guides/guide-project-setup-playbook - Serving lettuce safely (HTTP)
guides/guide-server-security - Driving lettuce from a file: structured external input
guides/guide-structured-external-input - What is lettuce
overview - Command Reference
reference/command-reference - Why lettuce (and not just Markdown + a convention)
why-lettuce
Backlinks
No backlinks.
What is lettuce
overview lettuce is a local-first, filesystem-native work tracker and coverage/quality store for humans, agents, and small teams — canonical state as ordinary validated files.
lettuce is a local-first, filesystem-native work tracker and coverage/quality store for humans, agents, and small technical teams that need inspectable work state. Canonical state — projects, tasks, comments, runs, artifacts, cells — lives as ordinary files inside a path-jailed store root. Every mutation is validated and recorded as an event. The same binary is a CLI and an HTTP server, and the same CLI becomes an HTTP client when a server URL is set.
Thesis
Most work trackers optimize for hosted workflows and opaque databases. lettuce optimizes for the opposite:
- plain files that can be inspected and reviewed
- deterministic validation before and after mutations
- stable task references for agents and automation
- Git-friendly history for dedicated stores
- structured diagnostics that explain repair paths
- CLI-first workflows before server or UI complexity
- HTTP-client mode for agents that should not mount the canonical store
Why filesystem-native
The filesystem is the product boundary. Because canonical state is ordinary files, users and agents can review exact changed paths, validate state without a running server, recover interrupted local work, attach Git history to each mutation (in dedicated-git mode), and debug with normal shell tools. This is a deliberate tradeoff: lettuce is not a high-volume database, a real-time collaboration server, or a dashboard-first planning suite in v0.15.
Who it's for
- Solo engineers — create a local store, track tasks, query current work, optionally review changes through Git.
- Agents — discover work, claim it with leases, record execution runs, attach artifacts, and act on structured diagnostics without editing canonical files directly.
- Small teams — project-local work state with explicit author attribution, optional HTTP authorization, and reviewable mutations.
- Tool integrators — stable JSON envelopes, diagnostic codes, and filesystem fixtures over a hosted UI.
Next
- Why lettuce and not just Markdown — the determinism argument.
- Getting started — your first store in under five commands.
- Command Reference — the complete 152-command surface.
Links to
- Getting started
getting-started - Command Reference
reference/command-reference - Why lettuce (and not just Markdown + a convention)
why-lettuce
Backlinks
- Getting started
getting-started - First contact — what a fresh agent sees, reads, and does
guides/guide-first-contact - Lettuce Documentation
index
Why lettuce (and not just Markdown + a convention)
why-lettuce lettuce moves the guarantee from instruction-following to code — validated transitions, atomic leases, and evidence-gated coverage cells the binary refuses to fake.
A Markdown board in your repo is only as honest as the agent editing it. An agent can write done or hardened with nothing behind it, and the next reader believes it. lettuce moves the guarantee from instruction-following to code.
- State transitions are validated, not requested. A task moves between states only along declared, legal transitions; an illegal move is refused, not recorded.
- Leases are atomic, expiring, and audited — not a naming convention. Two agents cannot both believe they hold the same work.
- A coverage cell cannot reach
hardened— or earn hardening depth — without linked evidence. By default the tool refuses the write; the one deliberate exception,--facilitate, proceeds past a failed gate but records the failed verdict on the ledger, so a green that was not earned is visible rather than silent. And the gate establishes provenance — the green traces to a completed, graph-backed ticket — not proof that the cell's subject was actually checked.
The store is still ordinary text: greppable, diffable, exportable. But what that text claims is enforced by the binary, not by the goodwill of whoever wrote it. That is the whole point — determinism over "please follow the convention."
If a rule in CLAUDE.md reliably did the job, you would not need lettuce. You reach for lettuce when verified must mean proven, not merely asserted.
Consequence for autonomous agents
Because claims are enforced, an autonomous loop can trust the store as its orientation and evidence substrate: it can ask "what is unfinished and provable?" and act on the answer without a human re-checking every assertion. See the core agent loop and Command Reference.
Links to
- Getting started
getting-started - Command Reference
reference/command-reference
Backlinks
- Cells — the coverage model
concepts/concept-cell - The shipped convention — coverage as data
concepts/concept-pack - Getting started
getting-started - Running lettuce in autonomous agentic cycles
guides/guide-agentic-cycles - Lettuce Documentation
index - What is lettuce
overview
concepts13
Cells — the coverage model
concepts/concept-cell A cell is one sparse, evidence-asserted point in a declared quality space — proof read backward, roadmap read forward. Covers pack, dimension, member, state/grade, gate, evidence.
The coverage plane grades how proven each part of a product is — and, read the other way, what work remains. It is lettuce's defining capability beyond a task list.
Vocabulary
- Convention — coverage, the convention-as-data lettuce ships: states, transitions, gates, dimensions, families. Bundled in the binary and active on every project by default; you inspect it and tune it per project with the
defaultslayer rather than swapping it. - Dimension — one quality axis (e.g.
test-coverage,input-validation). Grouped into families; each has an applicability (universal|conditional) and is closed (fixed members) or open (members minted ad-hoc). A project's effective dimensions = the pack's ⊕ an additive project runtime layer (dimension declare; never shadows pack vocab). - Member — one enumerated value of a dimension:
{slug, name, description, rank}(empty name renders as slug; rank 0 = unranked, sorts by slug). - Cell — one sparse, evidence-asserted point: a canonical coordinate (
dim=member;dim=member— pairs sorted by dimension, duplicates rejected, slugs validated) carrying one state from the active pack. An untouched coordinate is not stored; it reads as the pack default withstored=false. - State / grade — pack states carry a category (
open,in-progress,review,done,excluded,flagged) that drives engine-enforced honesty invariants: onlydonecounts as green;excludedleaves the denominator; review states never count as done; machine-only transitions (e.g.regress) fire only via the sanctioned path (cell reconcile). - Gate — authorizes a gated transition. Built-in evaluators:
guard-bite(the cell has ≥1 AUTHORISING evidence link:--kind task, citing a task that isdoneand carriescustom/grc, the pin of its verified close walk; a--kind urllink annotates but never authorises) andconsistency(store predicates recompute clean).cell transitionrefuses a gated move that fails its gate (FW-WF-GATE-UNSATISFIED) unless--facilitate(records the verdict but allows the move). - Evidence — links on an asserted cell (
--kind task= validated task ref,--kind url= opaque string).cell reconcilemachine-regresses stale-green cells whose cited evidence no longer resolves.
Two readings: proof and roadmap
The same cells serve a backward and a forward reading. Backward, a cell is proof — quality already earned. Forward, each coordinate is a point in the product's quality space and every thin cell is work waiting to happen:
| Grade (coverage pack) | Forward meaning | |---|---| | untested (or unstored) | not yet discovered or decided | | gap | a known deficiency — a decided TODO | | smoke | shallow proof needing deepening | | hardened | done — a committed guard provably bites | | excluded | decided out of scope (leaves the denominator) |
The grade ladder is thus also a work-discovery ladder, and the board is a roadmap: an agent picks its next target from the frontier of weak coverage (cell rollup for the weakest axes/members, board export for the whole frontier), does the work as a task on the work plane, then closes the loop by linking that task as evidence and grading the cell. Work hardens cells; cells reveal work.
Stored cells vs the declared grid (implicit cells)
Storage stays sparse: only coordinates you explicitly assert are written to disk (cell list enumerates exactly those; the pack-default space is never materialised). Left alone, that makes the coverage denominator the count of stored cells — so a scope with one hardened cell and nothing else reads 1/1 = 100%, a vacuous "done". A declared grid fixes that.
A grid is declared per scope as two axes of member slugs — its units (rows, the things being covered) and its applicable dims (cols) — and the scope's honest denominator becomes their cross-product |units| × |dims|. Every un-worked coordinate in that space is an implicit cell: not stored, but counted in the denominator, reading as the pack default (e.g. untested). You record real progress against a fixed grid instead of minting cells one at a time.
lettuce grid scope add-unit s1 validate recover --project P --author A
lettuce grid scope add-dim s1 functional resilience --project P --author A
lettuce grid show --project P # every scope's units/dims/size + project denominator
lettuce cell rollup --by scope --project P # s1 → untested 0/4 (four implicit cells)
lettuce cell set "scope=s1;unit=validate;dim=functional" --state hardened --project P --author A
lettuce cell rollup --by scope --project P # s1 → untested 1/4 (one stored, three implicit)
Behaviour to know:
- Both axes required. A scope with units but no applicable dims (or the reverse) has size 0 and contributes nothing to the denominator — a half-declared grid never reads a vacuous ratio.
- In-grid only. Once a grid governs a scope, a stored cell counts only if its
unit=member is a declared row and itsdim=member a declared col; off-grid coordinates never move the applicable / hardened / DoD numbers. The rendered board follows the same partition (LET-531): an off-grid cell is not drawn as a grid row and never reaches the panel's "N hardened of M testable"; the panel announces the count withlettuce grid scope show SCOPEinstead. - Backward-compatible. A project with no
grid/subtree readsdeclared=falseand every consumer (cell rollup,board,dod) falls back to the observed stored-cell denominator — the pre-grid behaviour. - One denominator, every surface.
cell rollup,board export,board next,board renderand the DoD verdict all read the same declared applicable space, so they cannot disagree.board nextalso lists the un-worked grid coordinates as implicit-untested open cells; the rendered heatmap draws rows only for units that hold a stored in-grid cell.
Freshness and depth — is a green cell's proof still current?
A hardened cell answers "was this proven?" — but proof decays as the code moves on. Two derived, never-stored signals disclose whether a green cell's evidence is still trustworthy, and both feed the Definition of Done:
- Freshness (FRESH-3) — a categorical recency bucket,
fresh | aging | stale, computed per cell at read time. Crucially it is measured in store revisions, NOT wall-clock time:distance = current store revision − the revision the cell's evidence was last asserted at, where the store's revision is its event count (rev@N— the same counter the board masthead uses), and the distance is clamped at 0 (an anchor ahead of the reference reads fresh, never "future-stale"). The default window: fresh within 50 revisions of the assertion (≈ one working wave), aging past 50 but within 2× (≤100 — a re-check is advised), stale beyond 100 (a re-check is due). It is a pure function of that distance — derived on every read, never persisted (a stored value could drift out of sync; a derivation cannot lie). - Basis — alongside the bucket each cell discloses whether its freshness is observed (the cell has a real revision anchor — an evidence link carrying an asserted revision) or presumed (no anchor at all → it is anchored at store genesis, so it reads maximally distant). Presumed is the honest "this cell never once bound its currency to a store revision"; a presumed cell counts as unknown against a recency floor, remedied by re-affirming with real evidence.
- Depth (FRESH-2) — how many distinct store revisions have independently confirmed the grade (via
cell verify/cell affirm). Confirming twice at the same revision buys no depth (the anti-farming rule); averifyearns strong depth only when it carries per-cell--evidence(the anti-rubber-stamp rule).
The DoD's optional --recency fresh|aging and --depth N floors gate on these signals (see Milestones & DoD). Recency is this one revision-distance measure — there is nothing to select or configure; the way it is computed is fixed.
The coverage ladder
Every project runs the shipped coverage convention by default; its grade ladder is:
| Grade ladder (default → … → done) | Meaning | |---|---| | untested → planned → in_progress → gap → evidence_linked → smoke → hardened; blocked, regressed (flagged); excluded, waived (excluded) | lettuce's own 88-dimension quality taxonomy; harden is guard-bite-gated (a committed guard that provably bites under fault-injection); waive requires a reason |
* = the default an untouched coordinate reads as (on a fresh project, untested). You do not swap the convention; you tune it per project with the defaults layer.
Coverage: 88 dimensions in 15 families
Inspect any with lettuce dimension list --project P (or dimension show <slug> --project P for one axis).
| Family | Dimensions | |---|---| | correctness | A functional · C spec-impl fidelity · N domain-model · O compat/migration · Q release-compat · U internal consistency | | usability | B UX/DX · S self-documentation · T error-messaging · M docs & examples · AY accessibility · BI i18n/l10n · VD visual & product design · AX agent experience · KB knowledge-base quality · WL white-label · BC browser-compat | | interface | H HTTP/API · J import/export round-trip · BX agent-readiness · CB multichannel parity · AP agent-protocol interop · DP data portability | | security | I attack surface · V privacy · AO rate-limiting · AZ bounded resources · BD auditability · RT red teaming · PT purple teaming · TM threat modeling · RC regulatory compliance | | reliability | BZ temporal · D data integrity · E resilience · F concurrency · FP cross-process concurrency · EL service liveness · AM idempotency · CC cleanup · AN backup/DR · AT determinism · OP ops & incident response | | distributed | G multi-node/sync · CA storage-backend equivalence | | performance | L performance · P observability · SC scalability patterns · DT distributed tracing | | maintainability | K code-quality · AA maintainability · AB testability · R configurability · BL architecture · CS code smells | | delivery | AC licensing · AR supply-chain/SBOM · AS deploy/release · BM CI-CD · BN packaging · BY stack maturity · BP cross-platform portability · LG legal, IP & terms · CN cloud-native/k8s · RG reproducible generation & provenance | | process | TD TDD discipline · GQ quality-gate quality · TK tracking & board discipline · QA QA procedure · RV adversarial/peer review · AL agentic dev-loop quality · PM process maturity & artifacts · RQ research & inquiry quality | | product | PA product analytics & north-star · XP experimentation & A/B testing · PD product discovery & prioritization | | growth | PR pricing & packaging · MN revenue, billing & unit economics | | customer | ON onboarding & activation · SU customer support & service · CX success, retention & churn · FB feedback & voice-of-customer | | market | PO positioning & messaging · DG demand generation & campaigns · SE content, SEO & GEO/AEO discoverability · IR investor & stakeholder communications | | lifecycle | FO cloud cost & FinOps efficiency · SN deprecation, EOL & sunset |
Worked example
lettuce cell set "area=auth;layer=api" --state in_progress --project "$P" --author "$A"
lettuce cell evidence add "area=auth;layer=api" --ref "$P/TASK-1" --kind task --project "$P" --author "$A"
lettuce cell transition "area=auth;layer=api" link-evidence --project "$P" --author "$A"
lettuce cell transition "area=auth;layer=api" harden --project "$P" --author "$A"
lettuce cell rollup --by area --project "$P" # per-member floor state + hardened ratio, N/A-excluded
Adding evidence does not advance the cell's state: cell evidence add records the link, and cell transition link-evidence is the separate step that moves in_progress to evidence_linked. Skipping it makes harden refuse with FW-WF-TRANSITION-NOT-ALLOWED (harden is declared only from evidence_linked and smoke), which reads like a gate failure but is not one.
harden is gated on evidence that authorises, which is a higher bar than evidence that exists. A linked task authorises only when it is done and carries a custom/grc run-case pin — a task that is merely done still refuses, with FW-WF-GATE-UNSATISFIED. Use --facilitate to record-not-enforce when the pin is genuinely not applicable.
See also
- Cells — Commands — the commands in the Cells group (
cell,board,dimension). - Coverage grid — Commands — the
gridsubcommands. - Dimensions · Scopes · Packs · Why lettuce
Links to
- Dimensions and members
concepts/concept-dimension - Milestones and Definition of Done
concepts/concept-milestone-dod - The shipped convention — coverage as data
concepts/concept-pack - Scopes — the DoD/board partition
concepts/concept-scope - Tasks and the work plane
concepts/concept-task - Cells — Commands
reference/cmd-cells - Coverage Grid — Commands
reference/cmd-coverage-grid - Why lettuce (and not just Markdown + a convention)
why-lettuce
Backlinks
- Dimensions and members
concepts/concept-dimension - Graphs — authored process, enacted on the ledger
concepts/concept-graph - Methodology catalog — the orientation board
concepts/concept-methodology-catalog - Milestones and Definition of Done
concepts/concept-milestone-dod - The shipped convention — coverage as data
concepts/concept-pack - Querying with FQL
concepts/concept-query - Scopes — the DoD/board partition
concepts/concept-scope - The store and the two data planes
concepts/concept-store - Tasks and the work plane
concepts/concept-task - Workflow — states, transitions, and gates
concepts/concept-workflow - Running lettuce in autonomous agentic cycles
guides/guide-agentic-cycles - Agentic loop demo — one development cycle, step by step
guides/guide-agentic-loop-demo
+3 more
- How to read lettuce — orienting in an existing store
guides/guide-how-to-read - Project setup playbook — prepare a project to leverage lettuce
guides/guide-project-setup-playbook - Lettuce Documentation
index
Dependencies — what depends-on asserts, and which end you start from
concepts/concept-dependency depends-on and blocks are two independent task-reference lists. An edge is stored on the DEPENDENT and names its prerequisite; query graph reads them as a DAG to report cycles, the critical path, and which tasks are ready to start.
> Not the same "graph" as Graphs — authored process. That page is about > graph-defs and run-cases — authored process enacted on the ledger. This page is about the > task dependency DAG: the plain depends-on / blocks lists on tasks. The two are unrelated > except by name.
What an edge asserts
A depends-on B means B must be complete before A can start. The edge is stored on the dependent and names its prerequisite, so edges run dependent -> prerequisite.
lettuce task create P/A --depends-on P/B # at create time, repeatable
lettuce task set-list P/A depends-on --value P/B # afterwards — REPLACES the whole list
task set-list replaces rather than appends: pass every value you want to keep.
What does not belong on it. The edge feeds a schedule computation, so it should carry only real work prerequisites:
- Not merge ordering. "These two branches touch the same generated file" is a fact about a branch, and it evaporates when either lands. It is not a dependency of the work.
- Not topical relatedness. "These are both about auth" is not a schedule constraint. Use
--label/--componentfor classification, or the generic non-blockingrelates-tolist (lettuce task set-list P/A relates-to --value P/B) when you want an explicit see-also link between two tickets. A relatedness edge ondepends-oninflates the critical path with a constraint nobody is waiting on, which is exactly whyrelates-tois kept OUT of the precedence graph. - Not "A cannot finish until B". This one is the easiest to get wrong, because it is a real constraint and it feels like a prerequisite. But the edge asserts B gates starting A, and a task already in progress is by definition startable. Writing it would claim the work had not begun and would extend the critical path by a constraint nobody is waiting on to begin.
There is deliberately no relation for finish-to-finish. The edge exists to feed one schedule computation — cycles, the critical path, and which tasks are ready to start — and a finish-to-finish constraint feeds none of the three. Record it where it is true: a comment on the ticket, or the ticket's own status. Leaving it unmodelled keeps the schedule honest; modelling it here would make every reader of critical_path wrong instead.
Which end is ready to start
query graph reports roots and leaves, and the names read backwards from planning intuition:
| | definition | means | when to start it | |---|---|---|---| | leaves | no outgoing edge | depends on nothing | now — these are the ready work | | roots | no incoming edge | nothing depends on them | last — the final deliverables |
Demonstrated on a three-task chain, SHIP depends-on BUILD depends-on DESIGN:
critical_path : ["demo/D-3", "demo/D-2", "demo/D-1"] length 3
roots : ["demo/D-3"] SHIP — depends on everything, startable LAST
leaves : ["demo/D-1"] DESIGN — depends on nothing, startable NOW
To answer "what should I work on next", read leaves, not roots.
What reads the edges
lettuce query graph --project P [--relation depends-on|blocks|precedence] # whole project
lettuce task graph P/A --relation depends-on --depth 2 # one task's neighbourhood
query graphbuilds the DAG over every task in the project and reportscycles,acyclic, thecritical_path(the longest chain by node count — the sequence that gates completion),rootsandleaves.--relation precedencereadsdepends-onandblockstogether, folding each into a single precedence direction. The two single-relation modes each see only their own list, so a cycle whose closing path alternates between them is invisible to both. The write gate refuses such a cycle (see below), so a store holds one only after an import or a direct edit; read three ways, the three tasks of that store give:
--relation depends-on acyclic true edge_count 1
--relation blocks acyclic true edge_count 2
--relation precedence acyclic FALSE edge_count 3 cycles 1
Reach for it when a project populates both lists. depends-on and blocks remain the right answer for "what does this one relation say".
task graphtraverses outward from one task. A traversal capped by--depthsetstruncated: trueand marks each boundary nodehas_more, so an incomplete closure is never silently returned.- Soft-archived tasks are hidden by default in both — as nodes and as edge targets — so they cannot distort roots, leaves, cycles or the critical path.
--include-archivedopts them back in.
blocks is a second, independent list — not the inverse
Setting depends-on does not populate anything's blocks. The two relations are parallel lists you maintain separately:
D-2 depends-on = demo/D-1 blocks = (empty)
query graph --relation blocks -> edge_count 0, every task both a root and a leaf
So a store with a fully populated depends-on graph answers zero for --relation blocks. Pick one relation as the project's convention and populate that one; a half-populated pair is worse than either alone, because both queries look answerable and only one is.
If a project does populate both, read it with --relation precedence. That mode is the only one that sees the two lists as one order — and it does not merge them into each other: blocks and depends-on stay separate lists that point opposite ways, which is exactly why folding them needs a mode of its own rather than an inverse.
A cycle means the store was changed outside the CLI
Ordinary writes refuse both of the states that would corrupt the analysis: a dangling edge, and an edge that would close a cycle. On the three-task chain above (D-3 depends-on D-2 depends-on D-1):
task set-list P/D-2 depends-on --value P/D-1 -> ok: true
task set-list P/D-3 depends-on --value P/D-2 -> ok: true
task set-list P/D-1 depends-on --value P/D-9 -> FW-REF-MISSING-TASK referenced task does not exist
task set-list P/D-1 depends-on --value P/D-3 -> FW-REF-CYCLE edge would create a cycle
(A value that is not a task reference at all, such as P/NOPE, is refused earlier, by the reference grammar: FW-NAME-TASK-ID.)
The cycle check reads the two lists together, across every project (LET-619, v0.19.0). A cycle that alternates between blocks and depends-on is refused on the write that would close it, exactly like a cycle within one relation:
task set-list P/T-1 blocks --value P/T-2 -> ok: true
task set-list P/T-3 depends-on --value P/T-2 -> ok: true
task set-list P/T-3 blocks --value P/T-1 -> FW-REF-CYCLE would close T-1 -> T-2 -> T-3 -> T-1
So neither a dangling edge nor a cycle of any shape can be authored through task set-list or task create. When query graph reports acyclic: false, under any --relation, that is an integrity finding, not a planning mistake: the edges arrived by import or by a direct file edit. validate --strict reports such a store at rest. Here the closing edge was written straight into the file:
validate --strict -> reports FW-REF-CYCLE task depends-on/blocks relations form a precedence cycle
query graph --relation precedence -> ok: true acyclic: false, one cycle
Note that validate still answers with ok: true while data.valid is false (and exits 1): the command succeeded, the store did not. Treat it as a repair task: see lettuce doctor and lettuce repair plan.
When cycles are present the critical path is still well-defined: it is computed over the graph with the cycle back-edges removed.
See also
Links to
- Graphs — authored process, enacted on the ledger
concepts/concept-graph - Tasks and the work plane
concepts/concept-task - Queries — Commands
reference/cmd-queries - Tasks — Commands
reference/cmd-tasks
Backlinks
- Graphs — authored process, enacted on the ledger
concepts/concept-graph - Tasks and the work plane
concepts/concept-task
Dimensions and members
concepts/concept-dimension A dimension is one quality axis; members are its enumerated values. Dimensions group into families with applicability, and are closed or open. Effective set = pack ⊕ project-declared layer.
A dimension is one quality axis along which cells are graded — for example test-coverage, input-validation, or the coverage pack's lettered axes (A functional, I attack-surface, …).
Structure
- Family — dimensions are grouped into families. The coverage pack's 88 dimensions span 15 families: ten engineering families (correctness, usability, interface, security, reliability, distributed, performance, maintainability, delivery, process) plus a five-family product-lifecycle super-family (product, market, customer, growth, lifecycle).
- Applicability — each dimension is
universal(always applies) orconditional(applies only when relevant to the unit). Which conditional axes a given product-type should require vs treat as N/A is a judgment you record by declaring a scope's applicable grid dims and marking the rest N/A (exclude) deliberately. - Closed vs open — a closed dimension has a fixed member enumeration; an open dimension mints members ad-hoc as coordinates use them.
Members
A member is one enumerated value of a dimension, first-class: {slug, name, description, rank}.
- An empty
namerenders as the slug. rank0 = unranked (sorts by slug); otherwise members sort by rank.dimension member add/dimension member updateedit only project-declared dimensions — never pack-owned vocabulary.
Methodology — how to apply a dimension
Every dimension carries two distinct texts, and both matter:
descriptionsays what the dimension *is* — the quality axis itself (e.g. dimD: "Writes are atomic and durable; a crash mid-write leaves a recoverable, consistent store.").methodologysays how to apply it to a unit — the procedure an agent follows to grade a cell honestly. In the coverage pack a methodology has up to six facets. Every dimension carries at least Procedure and Hardened; the enriched axes add the middle four:- Procedure — the concrete steps to exercise this dimension on a unit.
- Best practices — what "good" looks like; the do's and don'ts for that dimension's family (correctness / usability / security / …).
- Tools — named, current tooling that exercises this axis well.
- Current approaches — the present-day state of the art for the axis.
- Evaluation — the metric + evidence bar per member; how to score honestly.
- Hardened — the evidence bar: what a bite-proven guard must do. Across the whole pack,
hardenedmeans the same thing — a committed guard that provably bites under fault injection (green → break → RED → green); for non-code axes (process, compliance) the bar is an audit or freshness gate that fails on a missing or stale artifact. Attestation and prose never reachhardened.
> The methodology is a free-form string; the six-facet form above is the > current standard, and every bundled coverage dimension now carries it in full.
Example — coverage dim D (Data integrity / atomicity / persistence):
Procedure: exercise the write path; assert ATOMICITY (write-temp-then-atomic-
rename or a WAL) and DURABILITY (fsync the file AND its parent dir before ack);
inject a torn/partial write (crash mid-write, truncated temp, kill -9) and assert
the store stays consistent + read-back returns the exact bytes. Then name the
isolation level and prove the anomalies it forbids don't occur.
Best practices: make the ACID story EXPLICIT; writes are write-temp-then-atomic-
rename never in-place; never ack before durable; "validated but never persisted"
is a real bug class, so read the canonical file back and assert exact bytes.
Tools: atomic rename(2), fsync/fdatasync + directory fsync, WAL engines (SQLite
WAL, LMDB, BoltDB); Jepsen elle for isolation; ALICE/CrashMonkey crash injection.
Current approaches: MVCC + snapshot isolation as the default; serializable via SSI;
elle infers isolation anomalies from histories; deterministic simulation.
Evaluation: atomicity (no torn write survives a crash); durability (an fsync'd
write survives power loss); isolation (forbidden anomalies never observed);
multi-object atomicity (a reader never sees a partial commit).
Hardened: a committed torn/partial-write guard bites (green → break → RED → green)
if atomicity/durability is removed; layer an isolation guard + a fsync-durability
guard. Distinct lens vs F (F = in-process race-safety; D = the store's contract).
Read a dimension's methodology before grading any of its cells: lettuce dimension show <slug> --project <p> (one axis, full text) or lettuce dimension list --project <p> --format json (the whole effective set) surfaces it. The methodology catalog is the orientation board — the six-facet contract, a family-by-family map of all 88 dimensions, and fully worked exemplars. The methodology guidance is what distinguishes a smoke (exercised) grade from a hardened (bite-proven) one.
> Rule: a dimension without a methodology is incomplete. A quality axis you > cannot tell an agent how to grade is not yet a usable dimension — the > description/methodology pair is mandatory, not decorative.
Effective dimensions = pack ⊕ project layer
A project's effective dimension set is the active pack's dimensions plus an additive project runtime layer declared with dimension declare. The project layer is strictly additive: it can add axes and members but never shadows or overrides pack vocabulary.
Renaming a project dimension
A project-declared dimension's slug can be changed with dimension rename OLD NEW --project P. It is a whole-store structural migration, not a directory move, because the slug is three things at once: a path segment (projects/P/dimensions/<slug>/), the leading component of a qualified reference (P/dimensions/<slug>, the target of every dimension event), and a key inside every cell coordinate. Since a coordinate is the cell's identity, changing the key changes each affected cell's address — so the rename moves each cell directory to its new coordinate hash, carrying the whole cell across (state, note, revision, its own events ledger, and its evidence links with their asserted_at/asserted_rev provenance). The dimension keeps its family, closed-ness, members and its own ledger.
It is applied atomically: on any failure the store is left byte-unchanged, and it refuses rather than shipping a partial rename when a reference cannot be rewritten faithfully — one folded into a content-addressed identity (a graph-run-case advance event, a carrier directory), a stored saved query naming the dimension (the FQL cells source projects scope/unit/dim/group/ kind as fixed columns), or a move off/onto a reserved coordinate axis that the project's declared grid or DoD keys off. A pack-bundled dimension is compiled into the binary and is not renameable.
Coordinates
A cell lives at a coordinate — dim=member;dim=member. Pairs are sorted by dimension, duplicates are rejected, and every slug is validated against the effective vocabulary. An untouched coordinate is not stored; it reads as the pack default with stored=false.
See also
- Methodology catalog — the orientation board: the six-facet contract, all 88 dimensions by family, and worked exemplars.
- Dimensions — Commands (the
dimensionsubcommands live in the Cells group) - Cells · Packs
Links to
- Cells — the coverage model
concepts/concept-cell - Methodology catalog — the orientation board
concepts/concept-methodology-catalog - Milestones and Definition of Done
concepts/concept-milestone-dod - The shipped convention — coverage as data
concepts/concept-pack - Cells — Commands
reference/cmd-cells
Backlinks
- Cells — the coverage model
concepts/concept-cell - Methodology catalog — the orientation board
concepts/concept-methodology-catalog - Milestones and Definition of Done
concepts/concept-milestone-dod - The shipped convention — coverage as data
concepts/concept-pack - Scopes — the DoD/board partition
concepts/concept-scope - First contact — what a fresh agent sees, reads, and does
guides/guide-first-contact - Project setup playbook — prepare a project to leverage lettuce
guides/guide-project-setup-playbook - Lettuce Documentation
index
Graphs — authored process, enacted on the ledger
concepts/concept-graph A graph-def is the authored, versioned shape of a repeatable process; a graph-run-case is one event-sourced enactment of it on the real ledger. Definitions are sound by construction and composable; runs carry write-once evidence and a reproducibility pin.
> Not the task dependency graph. depends-on/blocks between tasks — and the > cycles/critical-path analysis query graph runs over them — are > Dependencies. This page is about graph-defs and run-cases: > authored process enacted on the ledger. The two share a name and nothing else.
Most of lettuce tracks what is true: a cell asserts a state, a task moves through workflow, evidence hardens a claim. Graphs track how work is meant to proceed — and then record what actually happened when it did.
Two objects, deliberately separate:
| object | what it is | mutability | |---|---|---| | graph-def | the authored shape of a process — nodes wired by edges, from a start node | versioned data, revised deliberately | | graph-run-case | one enactment of a def on the real ledger | event-sourced; state == f(events) |
Reach for a graph when a process is repeatable, multi-step, and worth auditing — when you want the same shape run many times and each run to leave falsifiable evidence. For a single piece of work, a task is the right unit; for legal states and transitions of that work, see Workflow; for what is true about an area, see Cells.
Definitions are sound by construction
A graph-def is data at projects/<p>/graph-defs/<slug>/. Its spec body is stored append-only-versioned, the same way a task body is.
Creation folds in the soundness gate: a dangling edge, an unreachable node, a missing start, or an uncapped cycle is refused and never stored. So "stored" and "sound" are the same statement — you never have to ask whether a def in the store is coherent. Linting is separate and advisory: it reports design smells as warnings, never fatal.
Composition: a def can use another def
A node may be a subgraph that inlines another def by name and binds its open ports. Compilation resolves the transitive closure, inlines every referenced def, and hashes the whole expansion. Two consequences worth holding onto:
- editing a shared pattern moves the effective-hash of every parent that uses it;
- a composition cycle is refused, as is a reference to a def that does not exist.
This is what makes patterns reusable without making them silently divergent.
Enactment: what a run-case records
Opening a run-case positions it at the start node and mints an id. From there it is advanced across edges and finally resolved. Because state is derived from events, the ledger — not a scalar someone wrote — is the authority on where a run got to.
Carriers are the data that moves along edges: content-addressed, write-once, stored at projects/<p>/carriers/. Write-once is the point — a run's evidence cannot be quietly restated after the fact.
Two terminal dispositions, one terminal event. A run that was walked to the end is closed, with an optional --outcome. A run that was opened and never walked to the end is abandoned, and there --reason is required — an abandonment records no work, so the reason is the whole of what the ledger can say. Both write the same case-closed event through the same writer: one terminal, not two. A run that already has a disposition refuses a second one, because two dispositions is a fork in the record. Abandoning is never a delete — the run stays listed and inspectable, and it is excluded from the conformance denominator rather than judged for a walk that never happened.
The disposition is projected onto every read surface, so an abandoned run never has to be mistaken for a completed one:
showcarriesdispositionbesidestate, and the required--reasonis readable asoutcome— the same field a closed run uses for--outcome.listcarriesdispositionper case, and counts them:total,open,abandoned.vizwrites the disposition into the diagram bytes as a%%comment, not just into the envelope. That placement is deliberate:viz > graph.mmdis the normal way the command is used, and an envelope-only field would vanish exactly there. An abandoned walk otherwise draws the same picture as a completed one.
conform remains the other half of the answer — it reports excluded: true for an abandoned case rather than judging a walk that never happened.
The reproducibility pin, and what it does not do. When the named def has a stored effective-hash, opening a run-case copies that hash verbatim onto the open event. Revising the def later moves the def's hash without re-pointing this run's evidence, so a completed run stays falsifiable rather than retroactively re-interpreted.
What the pin does not do is constrain the walk as it happens. The runtime is deliberately graph-def-free: advance reads no spec, so --to and --edge accept any string. A run-case can traverse an edge its def never declared, name a node absent from it, and still close with validate --strict reporting a perfectly clean store. That is by design, not an oversight — but it means the pin names the topology a run claims to have followed rather than proving it followed one.
Conformance replay is what makes the pin binding. It resolves the pinned hash back to the spec version that minted it and replays the recorded walk against that text, reporting an undeclared edge, an unknown node, or a join fired below its declared quorum. Judging against the pinned version rather than the latest is the whole point: revising a def cannot retroactively change the verdict on a run that finished before the revision.
This matters because every other check over a run-case re-derives its verdict from parameters the run itself recorded — the join re-folds the quorum the caller wrote. Each is therefore a fixed point: internally honest, and structurally unable to notice that the recorded number is not the declared one. Conformance is the read-time counterpart that can. It is a report, not a refusal — a non-conforming walk is evidence to read, not corruption to block — and it exits non-zero so a script can gate on it without parsing JSON.
Runtime patterns
Structured parallelism and iteration are authored in the def (and folded into its hash) but enacted in the run-case:
- fork / join — a fork opens concurrent branches; a K-of-M join fires only once quorum actually arrives, and refuses otherwise;
- loops — a capped back-edge exits only when its oracle is satisfied (converged, drained, a minimum count, a circuit-breaker), and refuses if not;
- routers — a router picks its out-edge from the run-case's own recorded evidence (carriers, wave outcomes, produced effects), evaluated through a small deterministic guard grammar. Never from the caller's assertion.
Each of these is guarded by an explicit refusal rather than a best-effort guess. That is the same stance as the rest of lettuce: a gate that cannot be satisfied is reported, not papered over.
Where to go next
- Graph Authoring — Commands — creating, revising, compiling, linting and visualising defs, and the catalog of shared patterns.
- Graph Run-Cases — Commands — opening, advancing, closing or abandoning run-cases, replaying conformance, and producing carriers.
- Cells — what a run-case's effects can harden.
- Store — how event-sourced state and derived scalars stay coherent.
Links to
- Cells — the coverage model
concepts/concept-cell - Dependencies — what depends-on asserts, and which end you start from
concepts/concept-dependency - The store and the two data planes
concepts/concept-store - Workflow — states, transitions, and gates
concepts/concept-workflow - Graph Authoring — Commands
reference/cmd-graph-authoring - Graph Run-Cases — Commands
reference/cmd-graph-run-cases
Backlinks
- Dependencies — what depends-on asserts, and which end you start from
concepts/concept-dependency - Lettuce Documentation
index
Methodology catalog — the orientation board
concepts/concept-methodology-catalog The orientation board for the coverage pack: the 6-facet methodology contract (Procedure · Best practices · Tools · Current approaches · Evaluation · Hardened evidence bar), a family-by-family map of all 88 dimensions, and worked exemplars — what you could/should do for a dimension to be properly considered, and the evidence-quality bar a hardened grade demands.
This is the orientation board for the coverage pack. Before you grade any cell on a dimension, read that dimension's methodology: it tells you everything you could and should do to consider the dimension properly, and the evidence-quality bar a hardened grade demands.
The coverage pack ships 88 dimensions across 15 families, each carrying a methodology. This page teaches the methodology contract, maps every dimension to its family, and shows three fully-worked exemplars. It deliberately does not reproduce all 88 methodologies — that content lives in one always-current place:
lettuce dimension list --project P --format json # all 88 dims + full methodology
lettuce dimension list --project P --format json \
| jq '.data.dimensions[] | select(.slug=="AL")' # one dimension, full text
> The binary is authoritative. coverage.yaml is embedded in the binary, so the > command above always reflects the exact methodology the current release grades > against — the exemplars below are a readable snapshot, not the source of truth.
The methodology contract — how to read a dimension
Every dimension carries two distinct texts, and both matter:
description— what the axis is (the quality property itself).methodology— how to apply it: the procedure an agent follows to grade a cell honestly, and the bar each grade must clear.
A methodology has up to six facets. Every dimension carries at least Procedure and the Hardened evidence bar; the enriched axes add the middle four:
| Facet | What it answers | |---|---| | Procedure | The concrete steps to exercise this dimension on a unit. | | Best practices | What "good" looks like — the do's and don'ts for the family. | | Tools | Named, current tooling that does this well. | | Current approaches | The 2025–26 state of the art for this axis. | | Evaluation | The metric + evidence bar per member; how to score honestly. | | Hardened | The evidence bar for hardened — a committed guard that provably bites under fault injection (green → break → RED → green), or, for non-code axes, an audit/freshness gate that fails on a missing or stale artifact. Attestation and prose never reach hardened. |
> Every dimension now carries the full six-facet form (Procedure · Best practices · > Tools · Current approaches · Evaluation · Hardened). Either way, a dimension without > a methodology is incomplete — you cannot grade what you cannot be told how to > grade.
The evidence bar, stated plainly. hardened is never earned by "we looked at it and it seemed fine." For a code axis it demands a committed guard that bites — remove the safeguard and the guard goes RED. For a non-code axis (process, compliance, agentic-loop) it demands an audit or freshness gate that fails on a missing, unsigned, or expired artifact. The Hardened facet of each dimension spells out exactly what its bar is.
The grading ladder (coverage's state machine): untested → gap | smoke → hardened, via the exercise, flag-gap, exercise-gap, and harden transitions. harden is gated by guard-bite — at least one evidence link to a done task that carries custom/grc (the pin of its verified close walk) — the only path to hardened. exclude marks a dimension structurally N/A (it leaves the denominator). See Cells — the coverage model for the full grade/state model.
Families — the navigation map
All 88 dimensions across 15 families. Read a dimension's full methodology with the jq snippet above (slugs are upper-case, e.g. AL).
| Family | Dimensions | |---|---| | correctness (6) | A functional correctness · C spec-impl fidelity · N domain-model completeness · O compat/versioning/migration · Q release-compat contract · U internal consistency | | usability (11) | B UX/DX · S self-documentation · T error-handling & messaging · M docs & examples · AY accessibility (a11y) · BI i18n/l10n · VD visual & product design · AX agent experience · KB knowledge-base & self-doc quality · WL white-label & theming · BC browser/client compatibility | | interface (6) | H HTTP/API surface · J import/export round-trip · BX agent-readiness · CB multichannel parity · AP agent-protocol interoperability · DP data portability & anti-lock-in | | security (9) | I attack surface · V privacy · AO rate-limiting/abuse · AZ bounded resources · BD auditability · RT red teaming · PT purple teaming · TM threat modeling · RC regulatory compliance & enforcement | | reliability (11) | BZ temporal-correctness · D data integrity/atomicity · E resilience/recovery · F concurrency/race-safety · FP cross-process concurrency & lock-safety · EL service liveness & crash-isolation · AM idempotency · CC resource-cleanup · AN backup/DR · AT determinism · OP operations & incident response | | distributed (2) | G multi-node/sync · CA storage-backend equivalence | | performance (4) | L performance/efficiency · P observability · SC scalability patterns · DT distributed tracing & trace ownership | | maintainability (6) | K code-quality · AA maintainability · AB testability · R configurability · BL architecture · CS code smells & anti-patterns | | delivery (10) | AC licensing · AR supply-chain/SBOM · AS deploy/release · BM CI-CD health · BN packaging · BY stack maturity · BP cross-platform portability · LG legal, IP & terms · CN cloud-native / kubernetes deployment · RG reproducible generation & asset provenance | | process (8) | TD TDD discipline · GQ quality-gate quality · TK tracking & board discipline · QA QA procedure (full) · RV adversarial/peer review · AL agentic development-loop quality · PM engineering-process maturity & artifacts · RQ research & inquiry quality | | product (3) | PA product analytics & north-star instrumentation · XP experimentation & A/B testing · PD product discovery & prioritization · _(product-lifecycle super-family — growing)_ | | growth (2) | PR pricing & packaging · MN revenue, billing & unit economics | | customer (4) | ON onboarding & activation · SU customer support & service · CX success, retention & churn · FB feedback & voice-of-customer | | market (4) | PO positioning & messaging · DG demand generation & campaigns · SE content, SEO & GEO/AEO discoverability · IR investor & stakeholder communications | | lifecycle (2) | FO cloud cost & FinOps efficiency · SN deprecation, EOL & sunset |
Worked exemplars
Three dimensions rendered in full — one from reliability (a code axis whose bar is a biting guard), one from security (a mixed code-and-artifact axis), and one from process (an axis whose bar is deliberately mixed-gateability). This is the pattern every dimension follows; pull any other with the jq snippet above. In the Phase-2 generated reference/coverage-catalog.md, every dimension renders like these.
---
D — Data integrity / atomicity / persistence
Family: reliability · Applicability: universal
What it is. Writes are atomic and durable (a crash mid-write leaves a recoverable, consistent store) AND the store's transaction/isolation contract is explicit and honest — the ACID story is named and proven, not assumed.
Procedure. Exercise the write path; assert ATOMICITY (all-or-nothing — write-temp-then-atomic-rename, or a WAL/journal) and DURABILITY (fsync the file AND its parent directory before ack — an un-fsync'd rename can vanish on power loss); inject a torn/partial write (crash mid-write, truncated temp, kill -9 between write and rename) and assert the store stays consistent + read-back returns the exact bytes. Then the TRANSACTION depth: name the isolation level the store offers (read-committed / snapshot / serializable) and prove the anomalies it forbids do not occur — dirty read, non-repeatable read, phantom, and write-skew (snapshot isolation still permits write-skew; only serializable forbids it); if multi-object/multi-file atomicity is claimed, prove a concurrent reader never observes a half-applied multi-key commit (it sees pre- or post-state, never mid).
Best practices. Make the ACID story EXPLICIT — Atomicity (temp+rename/WAL, single commit point), Consistency (invariants hold across the commit), Isolation (name the level; document that snapshot permits write-skew), Durability (fsync file
- parent dir; group-commit for throughput); writes are
write-temp-then-atomic-rename never in-place; never ack before durable; "validated but never persisted" is a real bug class, so read the canonical file back and assert exact bytes (no more, no less — a single-version test misses append-only latest-version bugs); never expose a partially-applied multi-key mutation; treat isolation level as a documented contract. Don't: fsync the file but not its dir, rely on OS buffering for durability, or claim serializable while shipping snapshot.
Tools. atomic rename(2) (same filesystem), fsync/fdatasync + directory fsync, WAL/journal engines (SQLite WAL, LMDB, BoltDB single-writer MVCC); isolation reasoning ANSI SQL levels, snapshot-isolation/MVCC, Jepsen elle (cycle-detection isolation checker over observed histories); crash injection ALICE/CrashMonkey-style FS fault injection, kill -9 harnesses, power-cut simulation, deterministic simulation (FoundationDB/TigerBeetle VOPR).
Current approaches. MVCC + snapshot isolation is the default store concurrency model; serializable via SSI (PostgreSQL) or lock-based (CockroachDB/TiDB/YugabyteDB proved true serializability via Jepsen); elle infers isolation anomalies from histories — the modern way to PROVE an isolation claim rather than assert it; deterministic simulation crash-tests the durability path exhaustively; group-commit + fsync batching for durable throughput.
Evaluation. atomicity (no torn write survives a mid-write crash); durability (an fsync'd write survives power loss — verified with a crash/barrier harness); isolation (the claimed level's forbidden anomalies never observed via an elle-style history check); multi-object atomicity (a reader never sees a partial commit). Evidence = a crash-injection log + an isolation-history check.
Hardened (evidence bar). KEEP the committed torn/partial-write guard — it bites (green → break → RED → green) if atomicity/durability is removed and the store is left inconsistent. LAYER: an isolation guard that bites when a concurrent reader observes a half-applied multi-key commit (interleave a multi-file mutation with a read; assert the read sees pre- or post-state, never mid), and a durability guard asserting the write is fsync-durable (remove the dir-fsync → a crash harness loses the rename → red). Distinct lens vs F (F = in-process race-safety of the mechanism; D = the atomicity/durability/isolation CONTRACT of the store).
---
RC — Regulatory compliance & enforcement
Family: security · Applicability: conditional
What it is. The product is graded against a regulatory or platform-policy REGIME (the member = hipaa/gdpr/soc2/pci-dss/iso27001/app-store-review/play-store-policy/…) control-by-control with linked evidence, and enforcement = a committed control-check that BITES when a required safeguard is absent, misconfigured, or removed.
Procedure. Pick the regime (member: hipaa/gdpr/soc2/pci-dss/iso27001/nist-800-53/fedramp/ccpa, OR a PLATFORM-POLICY gatekeeper regime — apple-app-store-review / google-play-policy, whose "controls" are the store guidelines + content-rating + data-safety/privacy-nutrition-label declarations and whose "audit" is the store's pre-publish review that can reject the artifact) and scope the compliance boundary (in-scope systems/data — PCI Cardholder Data Environment, HIPAA ePHI systems, GDPR personal-data systems) as a versioned artifact (data-flow diagram, asset inventory, network map); enumerate the regime's applicable controls (PCI's 12 requirements, ISO 27001:2022 Annex A's 93 controls, HIPAA Security Rule 164.308/.310/.312 safeguards, SOC 2 Trust Services Criteria, NIST 800-53 families) marking each applicable or N/A with WRITTEN justification; map each control → implemented safeguard + dated evidence (the control-to-evidence traceability matrix); record every gap (owner, severity, remediation date) in a gap register; remediate; wire an automated control-check that FAILS when a technical safeguard is absent/drifts, and an artifact-freshness check that fails when a signed process-control artifact is missing or past its review-by date; run continuously in CI + scheduled scans (audit-ready always, not point-in-time).
Best practices. compliance-as-code (controls as executable policy in git, not GRC-spreadsheet prose — a control's truth is its passing check, reviewable in a PR); control-to-evidence traceability (dated reproducible evidence, not "the policy says X"); never check the box without proof; continuous over point-in-time; scope discipline (tokenize/segment/pseudonymize out of scope — the cheapest control is the one you removed from scope); cross-framework mapping (one safeguard e.g. encryption-at-rest satisfies HIPAA 164.312, PCI Req 3, ISO A.8.24, SOC 2 CC6.1, GDPR Art 32 — evidence once, reuse everywhere); separation of duty on evidence (the implementer is not the sole signer); least-privilege + immutability on the audit/evidence store itself (a mutable audit trail defeats the control).
Tools. control catalog OSCAL (NIST machine-readable catalog/profile/SSP/POA&M) + IBM Trestle; policy-as-code OPA/Rego + Conftest, Checkov (1000+ IaC policies mapped to CIS/NIST/PCI/SOC2/HIPAA), Cloud Custodian (CNCF, auto-remediation), Prowler (multi-cloud, built-in HIPAA/PCI/SOC2/GDPR/FedRAMP frameworks), Trivy/Kubescape/Terrascan/kube-bench; continuous-compliance platforms Vanta/Drata/Secureframe/Sprinto (+ Scytale/Anecdotes); cloud-native AWS Config conformance packs + Security Hub, Azure Policy + Defender for Cloud, GCP Security Command Center; evidence pipeline SIEM/CloudTrail, Vault, IdP logs (Okta/Entra) for access + MFA evidence.
Current approaches. continuous compliance / compliance-as-code is dominant (controls are code, evidence auto-collected on a schedule, drift alerts near-real-time — "audit-ready always" replaces the annual scramble); cross-framework control mapping (implement+evidence a safeguard once, inherit across SOC2/ISO/HIPAA/PCI/GDPR — so "add HIPAA" onto an existing SOC 2 program is incremental, not a restart); machine-readable regulation (FedRAMP 20x + RFC-0024, Jan 2026, mandates OSCAL packages + Key Security Indicators with continuous validation — tooling lag is real but the direction is set); automated evidence collection (read-only integrations snapshot cloud config/IdP-MFA/vuln results on a cadence); shift-left compliance (IaC scanners gate the PR so a non-compliant resource never reaches production).
Evaluation. control coverage (% of the regime's APPLICABLE controls with an implemented + fresh-evidenced safeguard; HIPAA/PCI demand ~100% of applicable, ISO/SOC 2 tolerate documented risk-accepted exceptions justified in the Statement of Applicability / management assertion); evidence freshness (each artifact timestamped + max-age — technical-check evidence within the scan window ~24h, process artifacts within their review cycle: BAA current, DPIA annual, pentest ≤12mo, access review ≤quarterly; stale evidence = uncovered); gap-register health (open gaps + severity + owner + date; open criticals block hardened); scope correctness (boundary documented, asset inventory matches reality, N/A justifications hold). Evidence QUALITY auditors expect: reproducible, independent (not self-attestation), complete (whole population not a hand-picked sample), timestamped + attributable, tamper-evident. SOC 2 Type I (design at a point in time) vs Type II (operating effectiveness over a 3–12mo window) — Type II needs evidence the control operated CONTINUOUSLY, exactly what a biting automated check produces.
Hardened (evidence bar). A regime cell is hardened only when EVERY applicable control is enforced by either a biting automated check OR a fresh signed artifact, the two classes are paired, and the gap register is empty. Technical controls = a committed check that fails when the safeguard is absent/drifted: encryption at rest/in transit fails on any in-scope datastore storing PHI/PAN/personal-data with encryption off (flip a test bucket insecure → red); MFA on all privileged/in-scope access; audit logging enabled + retained + immutable; config drift from baseline; the data-subject-rights erasure/portability/access path ACTUALLY EXECUTES (a technical control weak programs wrongly attest — make it bite); vuln/patch SLA. Process/attestation controls = a signed, dated, non-expired artifact graded by a freshness check: BAA per PHI-touching vendor; DPIA/RoPA; ISO Statement of Applicability + certificate; SOC 2 Type II report; PCI ROC/SAQ + pentest; FedRAMP SSP/POA&M/3PAO. Enforcement principle: wherever a control CAN be technically verified it MUST be a biting check — attestation is not acceptable for what a machine can prove ("we encrypt PHI" must be a failing test on unencrypted PHI, never a signed sentence); pair a signed report with the biting check of its underlying technical control, either alone is insufficient.
---
AL — Agentic development-loop quality
Family: process · Applicability: conditional
What it is. When an AI agent (or fleet) is doing the development, the build→verify→improve→reflect LOOP itself is a graded engineered system — autonomous, resilient, observable, convergent, cost-bounded — proven by instrumented evidence, not agent vibes. Member = a loop property (autonomy / resilience / provenance / observability / stability / scalability / effectiveness / speed-quality / overhead / self-reflection / self-tuning).
Procedure. Name the loop under test (agent/scaffold — Claude Code, OpenHands, SWE-agent, Cursor, a custom harness; repo/scope; the gates it drives) and its unit of progress (a landed PR, closed ticket, hardened cell, passed task); INSTRUMENT before judging (OpenTelemetry GenAI / OpenInference spans for every model call, tool call, reasoning step, token+cost — you cannot grade a loop you cannot see); then exercise each loop PROPERTY with a deliberate probe not a happy-path watch: autonomy (measure human-interventions-per-unit over a real unattended window → SAE-style L0–L5), resilience (INJECT a mid-loop failure — kill a step, corrupt a tool result — and observe self-recovery without human rescue), stability (run N≥5 iterations, watch for green→red oscillation vs monotone convergence), scalability (1 then k parallel agents on isolated worktrees, measure throughput + collisions), effectiveness (resolve rate on SWE-bench Verified/-Live/Terminal-Bench or an internal set), speed-quality (iterations/wall-clock against a FIXED quality floor), overhead (cost-per-completed-task = attempt-cost / solve-rate), provenance (sample decisions from the trace, each must link a rationale+evidence), self-reflection + self-tuning (the loop critiques its own runs and adjusts from a scored record, not folklore); score each member on evidence quality; aggregate + name the weakest property + its next probe.
Best practices. instrument first, judge second (an un-traced loop is capped at 'covered' — 'we watched it, it seemed fine' is the vibe score this dim kills); autonomy is a dial not a switch (state the OBSERVED SAE level + intervention rate; most 2025-26 production loops are L2-L3, ceiling L3 conditional; never claim 'fully autonomous' without an unattended trace); converge not churn (every iteration reduces distance-to-done and never regresses an already-green gate — green→red→green oscillation is a first-class loop defect); every decision carries its receipt (rationale + the failing test/file/prior result that drove it); recover without a human (retry-safe/idempotent steps + checkpoints; distinguish transient infra faults — retry/circuit-break — from semantic faults — Specification Drift/Reasoning/Tool-Call — which blind retry just burns tokens on); parallelize with isolation, merge with judgment (worktrees give filesystem isolation only; decomposition/semantic-conflict/ merge-selection are still yours; adding agents must add LANDED output); govern speed-quality explicitly (a velocity floor the gate suite may not breach); budget on cost-per-completed-task not raw tokens; benchmark against a contamination-resistant set (SWE-bench-Live ~19% vs Verified 60%+ exposes overfitting).
Tools. eval/effectiveness SWE-bench + SWE-bench Verified (500-task human-validated), SWE-bench-Live (anti-contamination), Terminal-Bench + Long-Horizon-Terminal-Bench, Holistic Agent Leaderboard (HAL)/Live-SWE-agent (cost-annotated); scaffolds SWE-agent, AutoCodeRover, OpenHands, Devin, Claude Code (--worktree), Cursor 2.0 (multi-agent), Aider, ccswarm; observability/AgentOps LangSmith, Langfuse (OSS), AgentOps, Arize Phoenix (OpenInference), W&B Weave, Traceloop/OpenLLMetry over the OpenTelemetry GenAI semantic conventions (the portable substrate); resilience/durable-execution Restate, Temporal (checkpoint+resume, exactly-once), circuit-breaker/retry libs, MAPE-K loops; parallelism git worktrees + lock-safety (ties to FP); cost provider token/usage APIs + cost-per-resolved-task.
Current approaches. the instrumented agentic SDLC (every run traced with OTel GenAI/OpenInference; the decision graph — not a re-run — is the primary debugging artifact; observability consolidated on LangSmith/Langfuse/AgentOps/Phoenix/Weave); autonomy as a measured SAE-style dial (L2-L3 consensus, L3 ceiling); self-healing loops (failure-classified repair — circuit-break transient, corrective-feedback/rollback semantic — over durable-execution state so a recovered agent CONTINUES not restarts); contamination-aware effectiveness (Live/fresh sets, cost reported alongside score); parallel-agent fleets on worktrees (Cursor up to 8 concurrent) with the honest caveat that worktrees solve low-level isolation only; governed speed-with-a-floor as the loop-level 'keep-green'.
Evaluation. each member has a distinct metric + evidence bar — evidence must be reproducible (re-run yields the same class of outcome), instrumented (from the trace/harness, not a screenshot/prose), windowed (over a real run of N iterations or a benchmark subset, not one cherry-picked pass), and where gate-able biting (fails when the property is broken). autonomy = interventions/unit threshold audited off an unattended trace; resilience = self-recovery rate from injected failures; observability = span-coverage %; stability = green→red regressions across N runs + convergence trend; scalability = output(k)/(k*output(1)) + collision count; effectiveness = resolve rate vs a floor; speed-quality = iterations/wall-clock with the full floor still green; overhead = cost-per-completed-task budget; provenance = decision-linkage coverage; self-reflection/self-tuning = a scored eval record showing runs are critiqued + params evolve from measured winners. A single successful demo run is 'covered', never 'hardened'.
Hardened (evidence bar). deliberately mixed-gateability — its honesty is being explicit about which members bite via a committed check, which are metric-threshold-gated, and which are audit-graded. GATE-ABLE (a committed check that BITES): resilience (fault-injection test — SIGKILL a step / corrupt a tool response — asserts self-recovery; red when the loop wedges or restarts from scratch), stability (green-stays-green repeated-run harness over N≥5; red on any previously-green gate regressing), effectiveness (benchmark-score-floor CI gate; red when a loop change drops resolve rate below floor), overhead (cost-per-completed-task budget gate — the least-fakeable member, straight off the token meter), speed-quality ('faster AND floor-still-green'; red when speed came from skipping a gate), scalability (scaling-efficiency threshold + zero-cross-agent-collision assertion; red when adding agents adds no landed output or collides). METRIC-THRESHOLD-GATED (a number vs a bar, trace-audited): autonomy (interventions/unit ≤ bar for the claimed SAE level), observability (required span kinds present; red if instrumentation is dropped). AUDIT-GRADED (a review that fails on a missing artifact): provenance + self-reflection/self-tuning (fails when a sampled decision lacks a linked rationale+evidence, or a prompt/param change ships with no eval record). Cross-cutting rule: no member exceeds 'covered' from a demo or prose claim — every claim about the loop is a check that bites, a number against a bar, or an audit that fails on a missing receipt. That is precisely how this dim avoids collapsing into an 'agent vibe' score.
---
See also
- Dimensions and members — the concept: what a dimension / methodology / member is.
- Cells — the coverage model — the grade ladder, gates, and evidence model this catalog grades against.
- The shipped convention — coverage as data — how coverage is bundled as the default convention, and how you inspect and tune it.
- Project setup playbook — choosing dimensions and grading cells against their methodology.
lettuce dimension list --project P --format json— the authoritative live read of all 88 methodologies.
Links to
- Cells — the coverage model
concepts/concept-cell - Dimensions and members
concepts/concept-dimension - The shipped convention — coverage as data
concepts/concept-pack - Project setup playbook — prepare a project to leverage lettuce
guides/guide-project-setup-playbook
Backlinks
- Dimensions and members
concepts/concept-dimension - Lettuce Documentation
index
Milestones and Definition of Done
concepts/concept-milestone-dod Milestones are a staged hypothesis ladder; a Definition of Done declares the grade floor a project must reach. Together they give board next its verdict line.
Milestones
A milestone groups work toward an outcome and advances through stages — a hypothesis ladder you progress as evidence accrues.
lettuce milestone create <slug> --project P --author A --title "..."
lettuce milestone list --project P
lettuce milestone show <slug> --project P
lettuce milestone set-stage <slug> --stage <s> --project P --author A # advance the ladder
lettuce milestone close <slug> --project P --author A
Milestones live on the Work plane alongside tasks; they organize why work is being done, while tasks are the what.
Definition of Done (DoD)
A Definition of Done declares the grade floor a project (or scope) must reach on the coverage plane — the objective bar that separates "in progress" from "done".
lettuce dod set --project P --author A ... # declare the grade floor
lettuce dod show --project P # inspect the current DoD
lettuce dod clear --project P --author A # remove it
Once a DoD is declared, board next prints a leading DoD: … verdict line — the honest, computed answer to "are we done yet?" A DoD turns "done" from an assertion into a measurement: the floor is met only when the cells actually grade at or above it (with excluded cells leaving the denominator).
What the floor is made of
dod set writes up to three coverage floors, checked as a strict per-scope AND-gate over the applicable (non-excluded) cells — a scope is met only when every such cell clears every declared floor, reported as a k/n count, never a blended percentage:
--grade STATE(required on first declaration) — the minimum pack state a cell must reach to count (e.g.hardened).--depth N(optional) — the minimum confirmation depth (FRESH-2): how many distinct store revisions independently confirmed the grade.--recency fresh|aging(optional) — the minimum freshness bucket (FRESH-3):fresh(must be fresh) oraging(fresh-or-aging, neverstale). A cell whose freshness is presumed (no revision anchor) counts as unknown — unmet until re-affirmed. See freshness and depth for how both are computed.
--scope S sets a per-scope override of any floor (an unset field inherits the project default). Setting a floor never moves the hardened ratio: a scope can read 100% hardened yet DoD-unmet because its proof is stale or shallow.
Beyond the coverage floors, the overall verdict layers two auto-derived outer gates you never set by hand — every committed milestone reached and zero non-terminal tickets — so a project can clear every coverage floor and still read not done. Inspect all of them via dod show / board export.
Organizing a project
A typical project shape:
1. Declare the working model — every project runs the shipped coverage convention by default; optionally add project dimensions (dimension declare) or tune the ladder with the defaults layer. 2. Declare the bar — dod set a grade floor (per project and/or per scope). 3. Frame the outcomes — create milestones with stages. 4. Do the work — tasks claimed with leases, evidenced by runs/artifacts, closing cells. 5. Steer by the board — board next weakest-first until the DoD verdict is met.
See also
- Registries & Workflow — Commands (
milestonesubcommands) - Definition of Done — Commands (
dodsubcommands) - Cells · Agentic cycles
Links to
- Cells — the coverage model
concepts/concept-cell - Dimensions and members
concepts/concept-dimension - The shipped convention — coverage as data
concepts/concept-pack - The store and the two data planes
concepts/concept-store - Tasks and the work plane
concepts/concept-task - Running lettuce in autonomous agentic cycles
guides/guide-agentic-cycles - Definition Of Done — Commands
reference/cmd-definition-of-done - Registries And Workflow — Commands
reference/cmd-registries-and-workflow
Backlinks
- Cells — the coverage model
concepts/concept-cell - Dimensions and members
concepts/concept-dimension - The shipped convention — coverage as data
concepts/concept-pack - Querying with FQL
concepts/concept-query - Scopes — the DoD/board partition
concepts/concept-scope - Running lettuce in autonomous agentic cycles
guides/guide-agentic-cycles - Agentic loop demo — one development cycle, step by step
guides/guide-agentic-loop-demo - Configuring lettuce for useful leverage
guides/guide-config-for-leverage - First contact — what a fresh agent sees, reads, and does
guides/guide-first-contact - How to read lettuce — orienting in an existing store
guides/guide-how-to-read - Project setup playbook — prepare a project to leverage lettuce
guides/guide-project-setup-playbook - Lettuce Documentation
index
The shipped convention — coverage as data
concepts/concept-pack Coverage is the one convention lettuce ships: its states, transitions, gates, dimensions, and families are data, not code. Every project runs it by default; you inspect it and tune it per project — you do not swap it.
lettuce ships one working convention, and it is expressed as data, not code: the set of states, transitions, gates, dimensions, and families a project grades against. That convention is coverage, and every project runs it by default — a fresh project is already on coverage with nothing to enable.
This is the mechanism-vs-convention split at the heart of lettuce: the engine is pure mechanism (it owns the containers for meaning and method), and the convention supplies the meaning (which states exist, which transitions are legal, which gates authorize them, which quality axes to grade). The convention lives as data in the binary, so the engine and the on-disk store format never have to know what any particular state or dimension means.
You do not swap the convention at the user surface. You inspect it (read what coverage declares for your project) and tune it per project with an additive defaults layer. The internal envelope still names the active convention on some read surfaces — e.g. dimension list and cell show carry a pack: coverage field — which is the convention's internal identifier, not a command you run.
The default: coverage
Coverage is a 88-dimension / 15-family quality taxonomy (the families run from correctness and security through market and lifecycle). Every coordinate you have not yet worked reads as the sparse default untested; you record real progress by transitioning cells along coverage's ladder.
The ladder (each state carries a one-letter board glyph):
| State | Glyph | Meaning | |---|---|---| | untested | U | the default — never worked | | planned | N | scheduled, not started | | in_progress | I | actively being worked | | gap | G | worked and found deficient (needs a reason) | | evidence_linked | E | progress backed by linked evidence | | smoke | S | exercised end-to-end | | hardened | H | done* — reached only through a gate that provably bites | | blocked | B | flagged: work stalled (needs a reason) | | regressed | R | flagged: a hardened cell that broke | | excluded | X | N/A for this scope (needs a reason) | | waived | W | deliberately not pursued (needs a reason) |
* = the default a fresh coordinate reads as. hardened is the only done state and the only one behind a gate: the guard-bite gate requires a committed check that provably goes green → break → RED → green under fault injection — strictly stronger than "the tests pass". The transition actions that move a cell are plan, start, exercise, flag-gap, exercise-gap, link-evidence, harden, block, unblock, waive, exclude, regress, and reopen; an action with no legal transition from a cell's current state is refused.
Inspecting the convention
Coverage is read-only data you can query per project (every read needs a store root — an explicit --root, else the nearest .lettuce directory found by walking up from the working directory, else the command refuses — and --project):
lettuce defaults show --project P # the effective LADDER — states/transitions/gates, each source-tagged + hidden states
lettuce dimension list --project P # the project's effective dimensions (source-tagged)
lettuce dimension show A --project P # one dimension by slug, with its full methodology
lettuce dod show --project P # the Definition-of-Done floor + current verdict
lettuce grid show --project P # the declared coverage grid + denominator
lettuce board render --project P # the whole board as a self-contained HTML page
Each read answers a different question: defaults show = the ladder (which states, transitions, and gates the project grades against); dimension list = the axes; dod show = the Definition-of-Done floor. defaults show resolves a project's effective ladder — the bundled convention ⊕ the project's tweak layer — and tags every element with a source of default or project, so it is the read complement to the defaults tweak commands: it shows exactly what a tweak changed, base states the project has hidden (dropped from the effective view, listed under a hidden section), and where the grading-at-entry default landed. A fresh project (no tweak) resolves byte-identically to the shipped convention — every element reads source=default; after a tweak, authored states/transitions/gates read source=project and any hidden base state drops out of the ladder and appears under hidden.
dimension list reports the effective dimensions — coverage's declared axes plus any additive per-project runtime dimensions — each tagged with its source. A dimension's slug is its coverage letter (A, B, H, …); dimension show prints that axis's full procedure, best-practices, tooling, and evidence bar.
Tuning the convention (per project)
When one project needs coverage with a small adjustment, you do not author a new convention — you record an additive per-project tweak layer over the ladder / gates / DoD. A project that authors no tweak resolves byte-identically to the shipped convention; a tweak is stored under projects/<p>/… and merged at resolve time, so it never edits the bundled convention.
The defaults command family authors that layer (every subcommand is a mutation — pass --project and --author):
# additive working state (done/excluded are structural and refused)
lettuce defaults state declare triaging --category in-progress --letter T --rank 15 --project P --author A
lettuce defaults state set-default triaging --project P --author A # move the grading-at-entry default
lettuce defaults state hide blocked --project P --author A # subtractive: hide a base state + its edges
# additive workflow edge (--from/--to must resolve; --gate optional, must resolve —
# but REQUIRED when --to is the done state: only a gate promotes to done)
lettuce defaults transition declare expedite --from untested --to gap --project P --author A
lettuce defaults transition declare fast-harden --from smoke --to hardened --gate guard-bite --project P --author A
# additive guard (--check-kind must be a shipped evaluator, e.g. guard-bite, consistency)
lettuce defaults gate declare peer-review --check-kind consistency --description 'two approvals' --project P --author A
# clear the tweak layer back to the shipped convention (per-scope: ladder | dod | dimensions)
lettuce defaults reset --only ladder --project P --author A
Two safety rules keep the layer honest:
1. Additive-only + validate-on-write. A tweak that would break the ladder's honesty invariants — a transition to an undeclared state, a gate naming an unknown check-kind, a set-default that would leave the ladder without exactly one default, or a state declare whose slug shadows an existing state (including a base state this project has hidden — re-declaring it would create an invisible phantom) — is refused with nothing written (exit 1, FW-CMD-USAGE). Every refusal happens before any write. There is no unhide command; to bring a hidden base state back, clear the overlay with defaults reset --only ladder. 2. A reset can't strand work. Per-scope orphan guards refuse a reset that would leave cells holding a state or dimension it removes (reassign or clear those cells first); a clean no-op reset still succeeds. 3. Only a gate promotes to done. A transition whose --to is the done-category state (hardened) must carry a --gate; an ungated one is refused, citing spec §5 invariant 5. requires: [evidence] is not a substitute — recording a citation is the agent's act, promotion is the gate's. This is what makes the direct-set gate un-opt-out-able: cell set --state hardened is refused only while every declared entry into hardened is gated, so before this rule one ungated transition declare switched that refusal off for the whole project. Transitions out of done (regress, reopen) and into excluded/waived are unaffected — they are not promotion.
The done-state slug is hardened — use it (not done) in any transition example, because --to done names no state and hits the honesty gate.
To see the result of any tweak, read the effective ladder with defaults show (above) — it renders the resolved states/transitions/gates with each element tagged source=default|project plus the hidden base states, so you can confirm exactly what a tweak moved. The defaults subcommands are documented in full under Definition of Done — Commands.
See also
Links to
- Cells — the coverage model
concepts/concept-cell - Dimensions and members
concepts/concept-dimension - Milestones and Definition of Done
concepts/concept-milestone-dod - Definition Of Done — Commands
reference/cmd-definition-of-done - Why lettuce (and not just Markdown + a convention)
why-lettuce
Backlinks
- Cells — the coverage model
concepts/concept-cell - Dimensions and members
concepts/concept-dimension - Methodology catalog — the orientation board
concepts/concept-methodology-catalog - Milestones and Definition of Done
concepts/concept-milestone-dod - Tasks and the work plane
concepts/concept-task - Workflow — states, transitions, and gates
concepts/concept-workflow - Configuring lettuce for useful leverage
guides/guide-config-for-leverage - First contact — what a fresh agent sees, reads, and does
guides/guide-first-contact - Project setup playbook — prepare a project to leverage lettuce
guides/guide-project-setup-playbook - Lettuce Documentation
index
The plain format — the output contract for the shell pipeline
concepts/concept-plain-format plain belongs to the shell pipeline and has one meaning: newline-separated primary references. Every command declares its output kind (object, collection, report, verdict, content) in the usage catalog, and plain is decided by that declaration and the flags, never by the data.
Four readers consume lettuce's output, and each owns one surface:
| reader | surface | contract | |---|---|---| | agent automation, integrators, HTTP | --format json (and yaml) | the complete, stable envelope {ok, data, warnings, meta} | | a human at a terminal | --format table (the default) | the complete human rendering: headers, ordinals, alignment, (none), the <command> ok acknowledgement | | a shell pipeline | --format plain | newline-separated primary references | | documents, diagrams, the board page | markdown, html, okf, mermaid, dot | the document, scoped to the commands that produce it |
The operator ruled on 2026-09-27 that plain belongs to the pipeline and has one meaning everywhere (ADR 0026, rulings D1-D4). A field you want to read is on table (to look at) or json (to parse). Plain answers only "which objects?", so that
lettuce task list --status ready --format plain | xargs -n1 lettuce task show --format json
works for every command, not just the ones that happen to collapse.
Output kinds
Every catalog command declares the KIND of its successful stdout. lettuce usage --format json publishes it per command as output_kind (and output_record, the record kind whose reference it prints). The kind is a property of the command, never inferred from the payload's shape — the old shape-keyed rules are how every "plain changed when the data changed" defect arose (LET-900).
| kind | what it is | --format plain prints | |---|---|---| | object | one record: most shows, and every mutation that creates or changes ONE object | exactly its primary reference, one line | | collection | 0..N records: every list, the queries, audits, bulk mutations (task set-where, cell set-where/clear-where/import/reconcile, cell verify/affirm); lease show is a 0..1 collection | one primary reference per row, in order; nothing (zero bytes, exit 0) when empty | | report | a computed document with no enumerable identities: status, doctor, validate, dod show, defaults show, grid show, board next, and the configuration/maintenance mutations (dod, defaults, import, repair, recover, cleanup, reconcile, sync) | the table bytes, byte-identical | | verdict | a yes/no answer about a subject: task exists, cell gate check, graph-run-case conform, graph-def lint | the subject's reference iff the answer is yes; nothing on stdout otherwise, the explanation on stderr; the exit code is the answer | | content | a document or byte payload that IS the product: usage, skill, docs, board render, the viz diagrams | the content (artifact file get: the raw file bytes) |
Rules that complete the picture
- Mutations print what they changed (ruling D2).
REF=$(lettuce task create TSK-9 --title x --format plain)setsREFto the new task's reference; a delete prints the deleted reference; a bulk mutation prints one reference per affected row. The<command> okacknowledgement stays on table. Project configuration with no record identity (dod set,defaults …,import,repair …) is declared a report and prints the table bytes. - Inclusion flags are refused under plain (ruling D1).
--with-*,--fulland--body-versionsask for CONTENT, which plain cannot carry:lettuce task show TSK-1 --full --format plainis refusedFW-CMD-USAGE, naming--format tableand--format json, on objects and collections alike, locally and in hosted client mode. (--include-archivedwidens the row set, not the content, and stays legal.) - An explicit projection prints the requested columns, tab-separated and headerless, one row per line (
query run … select …,query tasks --fields …). This is the one sanctioned widening of "references": the caller named the columns.--refs-onlyasks for references while naming fields onquery tasks.--group-byasks for buckets and prints them exactly as table does. - Verdicts are filters (ruling D3).
… | xargs -n1 lettuce task exists --format plainprints the references that exist; the exit code is 0 for yes and 2 for no in every format, for every verdict (task exists,cell gate check,graph-def lint,graph-run-case conform). Exit 1 means the command itself failed (ok:false).lettuce usage <command> --format jsonpublishes each command'sexit_codes. - Rows that are not a clean answer are named on stderr (R1a).
lease listprints one task reference per lease — the same row set as json — and names the rows whose task could not be judged;task set-wherenames the rows that failed. The row set never changes with the format. - Errors leave stdout empty and print
lettuce: error [CODE]: …plus-> actionon stderr; warnings go to stderr. Both are the same under plain and table. - Never: zero bytes for an object, report or a yes verdict; a different object than the one read (a carrier's
refs, a project'sauthors); the input echoed as the answer; a shape that follows the data.
Primary references
The primary reference of a record is the token its own show command accepts as its identity, spelled in its §16.5 form where one exists — so every reference plain prints round-trips into that command (TestOutputContract checks it for every kind that has a show — events round-trip into query audit, workflow transitions into workflow transition show, grid scopes into grid scope show; only kinds with no show command at all — authors, id blocks, evidence rows, dimension members, migrate sessions, domains, paths — are named by the command that takes them).
| record kind | primary reference | example | |---|---|---| | task (also a lease, a task-graph node) | {project}/{task} | p/TSK-1 | | comment, run, run log, run summary, artifact, version | the §16.5 sub-object form | p/TSK-1/run/run_…/log/log_… | | registry object, milestone, workflow | {project}/{kind}/{slug} | p/milestone/m1 | | workflow transition | {project}/workflow/{workflow}/transition/{n} (workflow transition show takes it) | p/workflow/default/transition/3 | | saved query | {project}/{slug} (its event target {project}/saved-query/{slug} is also accepted as input) | p/ready | | event | {object reference}/event/{id} — ONE spelling in task audit, query audit, query timeline and FQL from events: a registry object in its singular §16.5 form, a saved query {project}/saved-query/{slug}, the project ledger {project}; query audit takes it and answers that one event | p/TSK-1/event/evt_…, p/label/red/event/1 | | project | its name | p | | author | its name | a1 | | cell | its coordinate (cell show also accepts the {project}/cells/{hash} spelling FQL rows carry) | area=auth;layer=api | | cell evidence row | its index — what cell evidence remove --index takes, not its ref pointer | evd_… | | dimension, graph definition, grid scope | slug (grid scope show is an object) | rp | | dimension member | member slug | unit-a | | graph pattern | name@vN | review-panel@v1 | | graph-run-case, carrier | id | grc_…, car_… | | id block | its first id | LET-2000 | | migrate session | session id | mig_… | | domain | name | acme | | store (init) / bundle (export) | the path | /data/store |
FQL rows (query run) and search hits (query search --scope all) mix kinds: each row prints its own reference.
Every command
The table below is generated from the declarations (outputContracts) and guarded: a test fails if it differs from what the binary declares, and TestOutputContract fails if the binary's output differs from the declaration.
| command | output kind | --format plain prints | |---|---|---| | usage | content | the content, as table prints it | | skill | content | the content, as table prints it | | docs | content | the content, as table prints it | | docs list | collection | one concept reference (the row itself) per row of concepts; nothing when empty | | docs show | content | the content, as table prints it | | docs export | object | its docs-export reference (out) | | okf | content | the content, as table prints it | | version | report | the table bytes | | self-update | report | the table bytes | | init | object | its store reference (root) | | status | report | the table bytes | | agents | collection | one author reference (name) per row of agents; nothing when empty | | work | report | the table bytes | | validate | report | the table bytes | | doctor | report | the table bytes | | recover | report | the table bytes | | cleanup | report | the table bytes | | reconcile | report | the table bytes | | author add | object | its author reference (author) | | author list | collection | one author reference (the row itself) per row of authors; nothing when empty | | author deactivate | object | its author reference (author) | | author reactivate | object | its author reference (author) | | project create | object | its project reference (project) | | project set-name | object | its project reference (project) | | project rename | object | its project reference (new_project) | | project merge | object | its project reference (target_project) | | project list | collection | one project reference (the row itself) per row of projects; nothing when empty | | project show | object | its project reference (project) | | project archive | object | its project reference (project) | | project unarchive | object | its project reference (project) | | project move | object | its project reference (project) | | project delete | object | its project reference (project) | | project id-block grant | object | its id-block reference (first_id) | | project id-block list | collection | one id-block reference (first_id) per row of blocks; nothing when empty | | project author add | object | its author reference (author) | | project author list | collection | one author reference (the row itself) per row of authors; nothing when empty | | registry create | object | its registry-object reference (reference) | | registry update | object | its registry-object reference (reference) | | registry list | collection | one registry-object reference (reference) per row of items; nothing when empty | | registry show | object | its registry-object reference (reference) | | milestone create | object | its milestone reference (reference) | | milestone list | collection | one milestone reference (reference) per row of items; nothing when empty | | milestone show | object | its milestone reference (reference) | | milestone close | object | its milestone reference (reference) | | milestone reopen | object | its milestone reference (reference) | | milestone set-stage | object | its milestone reference (reference) | | workflow list | collection | one workflow reference (reference) per row of items; nothing when empty | | workflow show | object | its workflow reference (reference) | | workflow transition list | collection | one workflow-transition reference ({^project}/workflow/{^workflow}/transition/{number}) per row of transitions; nothing when empty | | workflow transition show | object | its workflow-transition reference (reference) | | workflow revise | object | its workflow reference ({project}/workflow/{workflow}) | | task create | object | its task reference (reference) | | task show | object | its task reference (reference) | | task list | collection | one task reference (reference) per row of tasks; nothing when empty | | task exists | verdict | the task reference (reference) iff exists is true; nothing otherwise (the explanation on stderr; the exit code is the answer) | | task set | object | its task reference (reference) | | task set-where | collection | one task reference (reference) per row of results; nothing when empty; rows it could not judge, or that failed, named on stderr | | task unset | object | its task reference (reference) | | task set-list | object | its task reference (reference) | | task transition | object | its task reference (reference) | | task clone | object | its task reference (reference) | | task reopen | object | its task reference (reference) | | task archive | object | its task reference (reference) | | task unarchive | object | its task reference (reference) | | task delete | object | its task reference (reference) | | task body add | object | its task reference (reference) | | task audit | collection | one event reference (reference) per row of events; nothing when empty | | task graph | collection | one task reference (reference) per row of nodes; nothing when empty | | custom set | object | its task reference (reference) | | custom clear | object | its task reference (reference) | | comment add | object | its comment reference (reference) | | comment edit | object | its comment reference (reference) | | comment status | object | its comment reference (reference) | | comment list | collection | one comment reference (reference) per row of comments; nothing when empty | | comment show | object | its comment reference (reference) | | comment archive | object | its comment reference (reference) | | comment unarchive | object | its comment reference (reference) | | comment delete | object | its comment reference (reference) | | lease acquire | object | its lease reference (reference) | | lease renew | object | its lease reference (reference) | | lease release | object | its lease reference (reference) | | lease steal | object | its lease reference (reference) | | lease show | collection | its lease reference (reference) when present is true, nothing otherwise (a 0..1 collection) | | lease list | collection | one lease reference (reference) per row of leases; nothing when empty; rows it could not judge, or that failed, named on stderr | | run start | object | its run reference (reference) | | run finish | object | its run reference (reference) | | run log add | object | its run-log reference (reference) | | run summary add | object | its run-summary reference (reference) | | run list | collection | one run reference (reference) per row of runs; nothing when empty | | run show | object | its run reference (reference) | | run log list | collection | one run-log reference (reference) per row of logs; nothing when empty | | run log show | object | its run-log reference (reference) | | run summary list | collection | one run-summary reference (reference) per row of summaries; nothing when empty | | run summary show | object | its run-summary reference (reference) | | artifact add | object | its artifact reference (reference) | | artifact replace | object | its artifact reference (reference) | | artifact list | collection | one artifact reference (reference) per row of artifacts; nothing when empty | | artifact show | object | its artifact reference (reference) | | artifact archive | object | its artifact reference (reference) | | artifact unarchive | object | its artifact reference (reference) | | artifact delete | object | its artifact reference (reference) | | artifact file get | content | the raw file bytes | | version add | object | its version reference (reference) | | version list | collection | one version reference (reference) per row of versions; nothing when empty | | version show | object | its version reference (reference) | | query run | collection | one row reference (reference) per row of rows; nothing when empty; an explicit projection prints the requested columns, --group-by the buckets (as table) | | query search | collection | one search-hit reference (reference) per row of hits; nothing when empty | | query tasks | collection | one task reference (reference) per row of rows; nothing when empty; an explicit projection prints the requested columns, --group-by the buckets (as table) | | query audit | collection | one event reference (reference) per row of events; nothing when empty | | query timeline | collection | one event reference (reference) per row of events; nothing when empty | | query graph | report | the table bytes | | query saved create | object | its saved-query reference (reference) | | query saved update | object | its saved-query reference (reference) | | query saved archive | object | its saved-query reference (reference) | | query saved unarchive | object | its saved-query reference (reference) | | query saved list | collection | one saved-query reference (reference) per row of saved_queries; nothing when empty | | query saved show | object | its saved-query reference (reference) | | query saved run | collection | one row reference (reference) per row of result.rows; nothing when empty; an explicit projection prints the requested columns, --group-by the buckets (as table) | | export | object | its bundle reference (bundle) | | import | report | the table bytes | | migrate | report | the table bytes | | migrate verify | report | the table bytes | | migrate status | report | the table bytes | | migrate list | collection | one migrate-session reference (session_id) per row of sessions; nothing when empty | | migrate abandon | object | its migrate-session reference (session_id) | | migrate release | object | its migrate-session reference (session_id) | | migrate rollback | object | its migrate-session reference (session_id) | | repair plan | report | the table bytes | | repair check | report | the table bytes | | repair apply | report | the table bytes | | repair automatic | report | the table bytes | | sync status | report | the table bytes | | sync push | report | the table bytes | | sync pull | report | the table bytes | | conflict bundle | object | its conflict-bundle reference (bundle_id) | | serve | report | the table bytes | | domain create | object | its domain reference (name) | | domain list | collection | one domain reference (name) per row of domains; nothing when empty | | client list | collection | one client reference ({subject}/{actor}@{client_version}) per row of clients; nothing when empty | | github init-repo | object | its github-repository reference (repository.full_name) | | cell list | collection | one cell reference (coordinate) per row of cells; nothing when empty | | cell rollup | report | the table bytes | | cell evidence list | collection | one evidence reference (index) per row of evidence; nothing when empty | | cell evidence add | object | its evidence reference (index) | | cell evidence remove | object | its evidence reference (index) | | cell gate check | verdict | the cell reference (coordinate) iff passed is true; nothing otherwise (the explanation on stderr; the exit code is the answer) | | cell show | object | its cell reference (coordinate) | | cell set | object | its cell reference (coordinate) | | cell clear | object | its cell reference (coordinate) | | cell set-where | collection | one cell reference (coordinate) per row of set or coordinates; nothing when empty; its report findings announced on stderr | | cell clear-where | collection | one cell reference (coordinate) per row of cleared or coordinates; nothing when empty; its report findings announced on stderr | | cell note | object | its cell reference (coordinate) | | cell import | collection | one cell reference (coordinate) per row of set or assertions; nothing when empty; its report findings announced on stderr | | cell transition | object | its cell reference (coordinate) | | cell verify | collection | one cell reference (coordinate) per row of confirmed, or the one cell it acted on | | cell affirm | collection | one cell reference (coordinate) per row of confirmed, or the one cell it acted on | | board export | report | the table bytes | | board next | report | the table bytes | | board render | content | the content, as table prints it | | cell reconcile | collection | one cell reference (coordinate) per row of regressed; nothing when empty; its report findings announced on stderr | | dimension list | collection | one dimension reference (slug) per row of dimensions; nothing when empty | | dimension show | object | its dimension reference (slug) | | dimension member list | collection | one dimension-member reference (member) per row of members; nothing when empty | | dimension member add | object | its dimension-member reference (member) | | dimension member update | object | its dimension-member reference (member) | | dimension declare | object | its dimension reference (slug) | | dimension rename | object | its dimension reference (new_dimension) | | dimension close | object | its dimension reference (dimension) | | dod set | report | the table bytes | | dod clear | report | the table bytes | | dod show | report | the table bytes | | defaults show | report | the table bytes | | defaults state declare | report | the table bytes | | defaults state set-default | report | the table bytes | | defaults state hide | report | the table bytes | | defaults transition declare | report | the table bytes | | defaults transition hide | report | the table bytes | | defaults gate declare | report | the table bytes | | defaults reset | report | the table bytes | | grid scope add-unit | object | its grid-scope reference (scope) | | grid scope add-dim | object | its grid-scope reference (scope) | | grid scope remove-unit | object | its grid-scope reference (scope) | | grid scope remove-dim | object | its grid-scope reference (scope) | | grid scope show | object | its grid-scope reference (scope) | | grid show | report | the table bytes | | graph-def create | object | its graph-def reference (slug) | | graph-def revise | object | its graph-def reference (slug) | | graph-def show | object | its graph-def reference (slug) | | graph-def list | collection | one graph-def reference (slug) per row of items; nothing when empty | | graph-def compile | object | its graph-def reference (slug) | | graph-def lint | verdict | the graph-def reference (slug) iff clean is true; nothing otherwise (the explanation on stderr; the exit code is the answer) | | graph-def viz | content | the content, as table prints it | | graph-def catalog list | collection | one graph-pattern reference (key) per row of patterns; nothing when empty | | graph-def catalog show | object | its graph-pattern reference (pattern.key) | | graph-def use | object | its graph-def reference (slug) | | graph-run-case open | object | its graph-run-case reference (id) | | graph-run-case advance | object | its graph-run-case reference (id) | | graph-run-case close | object | its graph-run-case reference (id) | | graph-run-case abandon | object | its graph-run-case reference (id) | | graph-run-case acknowledge | object | its graph-run-case reference (id) | | graph-run-case show | object | its graph-run-case reference (id) | | graph-run-case viz | content | the content, as table prints it | | graph-run-case list | collection | one graph-run-case reference (id) per row of cases; nothing when empty | | graph-run-case conform | verdict | the graph-run-case reference (id) iff conforms is true; nothing otherwise (the explanation on stderr; the exit code is the answer) | | graph-run-case refs-to | collection | one graph-run-case reference (graph_run_case) per row of references; nothing when empty | | carrier produce | object | its carrier reference (id) | | carrier list | collection | one carrier reference (id) per row of carriers; nothing when empty | | carrier show | object | its carrier reference (id) | | carrier acknowledge | object | its carrier reference (id) |
Migrating a script
If a script parsed --format plain as a field listing, it was reading the human surface. Switch it to --format json (fields under .data; a mutation's produced object is under .data.data) or, to look at the output yourself, --format table. docs/operations/RELEASE-NOTES.md lists every command whose plain output changed in v0.20.0, before and after.
Links to
No outgoing links.
Backlinks
- Querying with FQL
concepts/concept-query
Querying with FQL
concepts/concept-query FQL reads the store: from SOURCE where EXPR select fields order/limit. Sources include tasks, events, cells; the cells source exposes DoD-derived axes (freshness, depth, dod).
FQL (the lettuce query language) reads the store deterministically. Shape:
from SOURCE [where EXPR] [select f1,f2] [order by f [desc]] [limit N] [offset N]
- Sources:
tasks,events,registry,authors,projects,dimensions(the active pack's declared dimensions),cells(stored cell assertions — sparse defaults are not rows). - Strings use double quotes (single quotes are rejected).
containsmatches case-insensitively unless--case-sensitive. - Task-reference fields —
parent,depends-on,blocks— are stored project-qualified (proj/TASK-1), and the comparison operand is canonicalised against--projectbefore matching. Both spellings therefore find the same rows:where parent = "TASK-1"is equivalent towhere parent = "proj/TASK-1". Beforev0.15.0-3312only the qualified spelling matched: the bare one returned an empty result at exit 0 rather than an error, so if an older build reports no rows for a relation you can see on disk, that is why. --group-by FIELD(tasks source only) buckets by assignee, component, milestone, severity, status, type, or workflow.- Soft-archived tasks are hidden from every query/list surface by default; pass
--include-archived.
lettuce query run "from tasks where status = active select task,title" --root "$ROOT" --format json
lettuce task show "$P/TASK-1" --with-body --format json # or --full
lettuce task audit "$P/TASK-1" --format json # event history
lettuce query timeline ... # chronological events
Querying coverage with the cells source
The cells source interrogates the coverage plane the same way you query tasks. Stored fields: coordinate, state, hash (plus cell/project/pack identity). It also carries DoD-derived axes:
| Axis | Values | Meaning | |---|---|---| | freshness | fresh | aging | stale | how current the cell's evidence is | | depth | int | distinct-revision confirmation count | | dod | blocking | met | is this cell blocking the Definition of Done | | dod-reason | grade | depth | recency | unknown | why a blocking cell blocks |
These turn "what is not yet done?" into a query:
lettuce query run "from cells where dod = blocking select coordinate,state" --project "$P" --format json
lettuce query run "from cells where freshness = stale select coordinate" --project "$P" --format json
lettuce query run "from cells where depth >= 2 select coordinate" --project "$P" --format json
lettuce query run "from cells where dod-reason = grade select coordinate" --project "$P" --format json
Slicing by scope / member
FQL has no dedicated scope/unit/dimension filter operator today — slice by a coordinate member instead: where coordinate contains "layer=api" or where coordinate contains "scope=login". See scopes.
Saved queries
lettuce query saved create <slug> --title "<title>" --fql "<fql>" --project P --author A
lettuce query saved run <name> --project P
lettuce query saved list --project P
See also
- Queries — Commands (all
query/query savedsubcommands) - Cells · Scopes · Milestones & DoD
- The plain format — what
--format plainemits, and when an explicit projection changes it
Links to
- Cells — the coverage model
concepts/concept-cell - Milestones and Definition of Done
concepts/concept-milestone-dod - The plain format — the output contract for the shell pipeline
concepts/concept-plain-format - Scopes — the DoD/board partition
concepts/concept-scope - Queries — Commands
reference/cmd-queries
Backlinks
- First contact — what a fresh agent sees, reads, and does
guides/guide-first-contact - How to read lettuce — orienting in an existing store
guides/guide-how-to-read - Lettuce Documentation
index
Scopes — the DoD/board partition
concepts/concept-scope scope is a reserved dimension that partitions the coverage board and Definition of Done. Cells without a scope fall into the synthetic unscoped bucket, never silently outside the DoD frame.
A scope is the reserved partition by which the Definition of Done and the board evaluate coverage. It is a modeled dimension, but a special one: scope is how a project carves its quality space into evaluable regions (e.g. scope=auth, scope=login, scope=storage).
How to shape scopes
Scopes are declared implicitly by the cells that use them — you assert cells at coordinates that include a scope= member, and those scope values become the partitions the board and DoD report on. Which units and dimensions belong to a scope is a modeling choice: a scope groups the coordinates whose scope= member matches, across whatever other dimensions those coordinates declare.
lettuce cell set "scope=auth;layer=api" --state in_progress --project P --author A
lettuce cell set "scope=auth;layer=db" --state planned --project P --author A
lettuce board next --project P --scope auth # steer within one scope
lettuce cell rollup --by scope --project P # per-scope floor + hardened ratio
The unscoped bucket (honesty invariant)
A cell that declares no scope= is bucketed under the synthetic scope unscoped at evaluation time — so it is never silently outside the DoD frame. You cannot assert scope=unscoped on a cell; the name is reserved for that bucket. This guarantees every asserted cell is accounted for by exactly one scope when the DoD verdict is computed.
Board & DoD interplay
board next --scope <name>steers within one scope; naming an undeclared scope returns an honest error listing available scopes (never a silent empty report).dod setcan declare a grade floor per project and/or per scope; the board's leadingDoD: …line is the computed verdict against that floor.grid scope add-unit/add-dimdeclare a scope's coverage grid — its units × applicable dims — which sets the scope's honest denominator (every un-worked coordinate counts as an implicit-untested cell), independent of how many cells are actually stored.
See also
- Cells · Dimensions · Milestones & DoD
- Cells — Commands (
board,cell rollup --by scope)
Links to
- Cells — the coverage model
concepts/concept-cell - Dimensions and members
concepts/concept-dimension - Milestones and Definition of Done
concepts/concept-milestone-dod - Running lettuce in autonomous agentic cycles
guides/guide-agentic-cycles - Cells — Commands
reference/cmd-cells
Backlinks
- Cells — the coverage model
concepts/concept-cell - Querying with FQL
concepts/concept-query - Configuring lettuce for useful leverage
guides/guide-config-for-leverage - How to read lettuce — orienting in an existing store
guides/guide-how-to-read - Project setup playbook — prepare a project to leverage lettuce
guides/guide-project-setup-playbook - Lettuce Documentation
index
The store and the two data planes
concepts/concept-store The store is a path-jailed root of ordinary validated files. Objects fall into two planes: Work (projects/tasks/leases/runs/artifacts) and Coverage (packs/dimensions/cells).
Canonical state lives in a store — a path-jailed root of ordinary files. Files are the source of truth: you can inspect, diff, and validate the store with normal tools. The two checks differ: lettuce validate --strict verifies the store is well-formed (grammar), but it is silent on a derived scalar forged out of band — a hand-edited task status passes it. lettuce doctor is what tells you the store is healthy: it additionally catches that derived/status drift (FW-STORE-DERIVED-DRIFT) that validate --strict does not, and tells you how to repair it (their divergence is tracked in LET-1723).
Foundational invariants
- Files are the source of truth. No hidden database; every object is a file.
- Mutations are explicit and attributed. Always pass
--projectand--author; lettuce never infers them from the OS, Git config, or store contents. Every mutation is recorded as an event (task audit,query timeline). - Concurrency is coordinated by leases and revisions. Claim work with a lease before touching it; pass
--expect-revision Nwhen retrying or racing another agent. Writes are serialized by a single-writer generation lock. - Output is a stable envelope. With
--format json|yaml, every result is{ ok, data, warnings, meta }; errors are{ ok:false, error:{ code, message, diagnostics:[...] } }with machine-readable diagnostic codes.
The two data planes
| Plane | Objects | Question it answers | |---|---|---| | Work | projects, tasks, workflow, leases, runs, artifacts, comments | what is being done, by whom, with what evidence | | Coverage | packs, dimensions, members, cells, gates, boards | how proven each part of the product is |
The two planes meet at evidence: a task on the Work plane is linked as the evidence that hardens a cell on the Coverage plane.
Store modes
- filesystem (default, offline) — canonical state under the store root.
- dedicated-git — a strongly-consistent shared store that fetches upstream before every mutation (requires connectivity by design).
- client — set a server URL to drive a running
lettuce serveover HTTP instead of mounting the store.
See also
- Core & Runtime — Commands (
init,validate,doctor,recover,cleanup) - Tasks · Cells · Getting started
Links to
- Cells — the coverage model
concepts/concept-cell - Tasks and the work plane
concepts/concept-task - Getting started
getting-started - Core And Runtime — Commands
reference/cmd-core-and-runtime
Backlinks
- Graphs — authored process, enacted on the ledger
concepts/concept-graph - Milestones and Definition of Done
concepts/concept-milestone-dod - Tasks and the work plane
concepts/concept-task - Configuring lettuce for useful leverage
guides/guide-config-for-leverage - How to read lettuce — orienting in an existing store
guides/guide-how-to-read - Serving lettuce safely (HTTP)
guides/guide-server-security - Lettuce Documentation
index
Tasks and the work plane
concepts/concept-task A task is the unit of work: created under a project, moved along validated workflow transitions, claimed with leases, and evidenced by runs and artifacts.
A task is the unit of work on the Work plane. Tasks live under a project, carry a stable reference (project/TASK-ID), and move through a workflow of states via validated transitions.
Lifecycle
lettuce task create LET-1 --project P --author A --title "..." --body "..."
lettuce task show P/LET-1 --with-body
lettuce task transition P/LET-1 <transition> --project P --author A # only legal moves
lettuce task list --project P
- Workflow is supplied by the active pack: which states exist and which transitions are legal. An illegal transition is refused, not recorded (
lettuce workflow list/workflow show). - Stable references —
project/TASK-IDis durable across renames; prefer it when project context is ambiguous. - Attribution & audit — every mutation records an event; inspect with
task auditandquery timeline.
Coordination for agents
- Leases — claim a task before working it:
lettuce lease acquire(atomic, expiring, audited). Renew withlease renew; steal only expired leases. This is the honest alternative to a "who's working on it" naming convention. - Revisions — pass
--expect-revision Non event-bearing mutations when retrying or racing another agent; a stale revision is rejected.
Recording execution (evidence)
- Runs —
run start/run finish, withrun log addandrun summary add— the record of an execution attempt. - Artifacts —
artifact add/replaceattach outputs to a task. - Comments —
comment addfor human/agent narrative.
A finished task becomes evidence: link it to a cell (cell evidence add --kind task) to harden coverage. Work hardens cells; cells reveal work.
Task-local objects
Comments, leases, runs, and artifacts are task-local — 35 subcommands in the Task-Local-Objects group.
See also
- Dependencies — what
depends-onasserts —depends-on/blocks, and which end is ready to start - Tasks — Commands · Task-Local Objects — Commands
- The store & two planes · Cells · Queries (FQL)
Links to
- Cells — the coverage model
concepts/concept-cell - Dependencies — what depends-on asserts, and which end you start from
concepts/concept-dependency - The shipped convention — coverage as data
concepts/concept-pack - The store and the two data planes
concepts/concept-store - Queries — Commands
reference/cmd-queries - Task Local Objects — Commands
reference/cmd-task-local-objects - Tasks — Commands
reference/cmd-tasks
Backlinks
- Cells — the coverage model
concepts/concept-cell - Dependencies — what depends-on asserts, and which end you start from
concepts/concept-dependency - Milestones and Definition of Done
concepts/concept-milestone-dod - The store and the two data planes
concepts/concept-store - Workflow — states, transitions, and gates
concepts/concept-workflow - Running lettuce in autonomous agentic cycles
guides/guide-agentic-cycles - Agentic loop demo — one development cycle, step by step
guides/guide-agentic-loop-demo - Lettuce Documentation
index
Workflow — states, transitions, and gates
concepts/concept-workflow Workflow is the pack-supplied model of legal states and transitions for tasks and cells. Transitions are validated; gated transitions require a satisfied gate or explicit --facilitate.
Workflow is the model of legal states and the transitions between them. It is supplied by the active pack as data — not hard-coded — and it governs both tasks (the work plane) and cells (the coverage plane).
lettuce workflow list --project P # available workflows
lettuce workflow show <name> --project P # its states + transitions
lettuce workflow transition list --project P # the legal moves
Validated transitions
A transition moves an object from one state to another only along a declared, legal edge. An illegal move is refused, not recorded:
lettuce task transition "$P/TASK-1" start-work --project P --author A # -> ok: true (A holds the lease)
lettuce task transition "$P/TASK-1" start-work --project P --author A # -> FW-WF-TRANSITION-NOT-ALLOWED: already started
# positional: lettuce task transition REF ACTION [--reason TEXT] [--expect-revision N]
- Some transitions are requirement-gated — e.g.
start-workneeds an active lease; without it, the move fails withFW-WF-REQUIREMENT-UNSATISFIED. --expect-revision Nguards against racing another agent (a stale revision is rejected).
Gates (coverage plane)
On cells, a gate authorizes a gated transition. Built-in evaluators:
guard-bite— the cell has ≥1 authorising evidence link: a--kind tasklink to a task that isdoneand carriescustom/grc(its verified close walk). A--kind urllink annotates but never authorises.consistency— store predicates recompute clean.
cell transition refuses a gated move that fails its gate (FW-WF-GATE-UNSATISFIED) unless --facilitate records the verdict but allows the move.
> Never --facilitate past a failed gate to "make progress." One unproven > green poisons every future orientation. See agentic cycles.
See also
- Registries & Workflow — Commands (
workflowsubcommands) - Tasks · Cells · Packs
Links to
- Cells — the coverage model
concepts/concept-cell - The shipped convention — coverage as data
concepts/concept-pack - Tasks and the work plane
concepts/concept-task - Running lettuce in autonomous agentic cycles
guides/guide-agentic-cycles - Registries And Workflow — Commands
reference/cmd-registries-and-workflow
Backlinks
- Graphs — authored process, enacted on the ledger
concepts/concept-graph - Agentic loop demo — one development cycle, step by step
guides/guide-agentic-loop-demo - Lettuce Documentation
index
guides9
Running lettuce in autonomous agentic cycles
guides/guide-agentic-cycles Use the store as your loop — read forward it is the plan, written backward it is the proof. The orient→act→reflect→file cycle via board next, and the invariant that keeps it honest.
lettuce is not primarily an autonomy tool — every command works one at a time, human or agent. But one powerful way to use it: if you are an agent with standing goals and a store, run lettuce as your loop. The store read forward is the plan; the store written backward is the proof — no external planner needed.
The core loop (one task at a time)
ROOT=.lettuce; P=myproject; A=my-agent
# 1. Once: seed a store (idempotent).
lettuce init --root "$ROOT" --author "$A" --bootstrap-project "$P" --bootstrap-author --idempotent --format json
# 2. Discover — concrete items AND the coverage frontier.
lettuce task list --project "$P" --root "$ROOT" --format json
lettuce board next --project "$P" --root "$ROOT" --format json
# 3. Create work. 4. Claim (lease) then start. 5. Record runs/artifacts. 6. Complete + release lease.
lettuce lease acquire "$P/TASK-1" --expires-at "$(date -u -d '+1 hour' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v+1H +%Y-%m-%dT%H:%M:%SZ)" --project "$P" --author "$A" --format json
lettuce task transition "$P/TASK-1" start-work --project "$P" --author "$A" --format json
# ... work, narrate with `run log add` / `comment add`, attach `artifact add` ...
lettuce task transition "$P/TASK-1" complete --reason "Done + verified" --project "$P" --author "$A"
lettuce lease release "$P/TASK-1" --project "$P" --author "$A"
> Lease-gated transitions (start-work) fail with FW-WF-REQUIREMENT-UNSATISFIED > unless you hold the lease. Acquire first. Transition syntax is positional: > lettuce task transition REF ACTION [--reason TEXT] [--expect-revision N].
The autonomous cycle — orient → act → reflect → file
# ORIENT — ask the store what matters most right now.
lettuce board next --project "$P" --root "$ROOT" --format json
# → every scope's meter weakest-first, shape-aware suggestions, concrete
# gap/untested coordinates, each with the exact drill command (--depth 0|1|2).
# Pick ONE target: gap (known defect) before untested (undiscovered),
# weakest scope first. Act on the coordinate it printed — don't hand-compose one.
# ACT — make the work claimed, attributed, and visible.
lettuce task create TASK-N --title "Harden <coord>" ...
lettuce lease acquire "$P/TASK-N" ...; lettuce task transition "$P/TASK-N" start-work ...
# REFLECT — record what happened WITH evidence, then close the cell.
lettuce artifact add "$P/TASK-N" --type test-report ... # the proof itself
lettuce task transition "$P/TASK-N" complete --reason "guard bites" ...
lettuce cell evidence add "<coord>" --ref "$P/TASK-N" --kind task ...
lettuce cell transition "<coord>" harden ... # guard-bite gate must pass
# FILE — organize everything you discovered before looping.
lettuce task create ... # one ticket per unfixed finding
lettuce cell set "<coord>" --state gap --reason "<defect>" ... # defects get addresses
lettuce dimension member add <dim> <member> ... # new surface → new map row
lettuce milestone set-stage <slug> --stage <s> ... # progress the hypothesis ladder
lettuce cell reconcile ... # regress stale greens
lettuce validate --strict ...
Then run board next again — the frontier has changed, partly because you hardened a cell and partly because you filed what you found. Work hardens cells; cells reveal work. FILE is not bookkeeping — it is how the map stays truthful enough to steer the next iteration.
What board next needs to be useful
- A store and a valid
--project(a missing/unknown project is refused — no default, no cross-project scan). - At least one stored cell — the frontier is built from asserted cells only; sparse pack-defaults are never rows. An empty board is not an error (every scope meter reads
n/a). - A declared DoD to get the leading
DoD: …verdict line (dod set). --depthaccepts only0,1,2;--scope <name>naming an undeclared scope returns an honest error listing available scopes, never a silent empty.
The invariant that keeps the loop honest
A gap/untested cell spawns a task (the cell is the work's address); the finished task, linked as evidence, authorizes hardening (cell transition is guard-bite gated); and if the cited evidence later unresolves, cell reconcile machine-regresses the cell. Neither direction can lie to the next iteration — which is why you never --facilitate past a failed gate to "make progress": one unproven green poisons every future ORIENT.
Sharing the board: export and render
board next steers your loop; two sibling read-only projections publish the same board for a human or a dashboard, from the identical BoardExport:
board exportemits a stable, versioned JSON data contract on stdout (schema_version, the pack's dimension/state legend, the stored cells with evidence, per-axis coverage rollups, the blended headline ratio, milestones, tickets, and the activity histogram). It is data ⟂ visual: the document carries no styling, so a separate renderer owns the presentation.board renderturns that same export into a self-contained HTML page natively in Go (no Python, no JSON round-trip) — you pipe stdout to a file. Every byte is inline: nosrc=, no external references, and no network call of any kind. The page is pure-CSS with one authorized exception — a project carrying graph-run-cases also emits the grc replay simulator as exactly one inline<script>plus one inert<script type="application/json">data island per grc; a project without graph-run-cases emits no<script>at all.--themeselects only the CSS frame (today the sole built-in isdefault; an unknown name is refused with the available list). Both default their provenance from the store (data revision + latest-event instant), never the wall clock, so a render is reproducible;--source-revstamps an explicit revision into the masthead.
Both require --project and read only that project — no cross-project scan.
The activity/velocity band is store operations, not git commits. The board's histogram is derived purely from stored event timestamps (a per-calendar-day count of store mutations), bucketed into ISO weeks. It is deliberately the self-containable substitute for git-commit velocity — the board never reads a git repo, which is what keeps the HTML fully offline and portable. Read it as "how much work landed in the store over time", not as a commit log.
See also
- Cells · Tasks · Milestones & DoD
- Cells — Commands (
board next,board export,board render,cell rollup,cell reconcile) - Why lettuce — determinism is what makes the loop trustworthy.
Links to
- Cells — the coverage model
concepts/concept-cell - Milestones and Definition of Done
concepts/concept-milestone-dod - Tasks and the work plane
concepts/concept-task - Cells — Commands
reference/cmd-cells - Why lettuce (and not just Markdown + a convention)
why-lettuce
Backlinks
- Milestones and Definition of Done
concepts/concept-milestone-dod - Scopes — the DoD/board partition
concepts/concept-scope - Workflow — states, transitions, and gates
concepts/concept-workflow - Agentic loop demo — one development cycle, step by step
guides/guide-agentic-loop-demo - Configuring lettuce for useful leverage
guides/guide-config-for-leverage - First contact — what a fresh agent sees, reads, and does
guides/guide-first-contact - How to read lettuce — orienting in an existing store
guides/guide-how-to-read - Retrying a mutation safely: --idempotency-key
guides/guide-idempotent-mutations - Project setup playbook — prepare a project to leverage lettuce
guides/guide-project-setup-playbook - Driving lettuce from a file: structured external input
guides/guide-structured-external-input - Lettuce Documentation
index
Agentic loop demo — one development cycle, step by step
guides/guide-agentic-loop-demo A single realistic dev loop worked end to end: at each orient→act→reflect→file step, exactly what the agent GETS from lettuce and what it DOES, with real commands and output.
The Agentic cycles guide gives you the theory of the orient→act→reflect→file loop and the invariant that keeps it honest. This guide walks one concrete loop, showing at every step what lettuce hands you (GET) and what you do with it (DO). The commands are real, and so is every GET block: it is what the binary prints for the store the step describes (a guard runs each one; … marks elided lines and run-specific ids).
The scenario. A webapp project on the shipped coverage convention (the default — every project runs it with nothing to enable). To keep the board compact, this walkthrough grades a deliberately small slice: two coverage axes, a (Functional correctness) and i (Security), across an auth scope (fully hardened) and a checkout scope (weak). Our job this cycle: harden one cell. We do not choose it — the board does.
> New to a project? Start with First contact. Standing > a project up from nothing? Use the Project setup > playbook.
Step 1 — ORIENT · GET the frontier and the DoD verdict
DO: ask the store what matters most, and read the verdict first.
ROOT=.lettuce; P=webapp; A=codex-01
lettuce board next --root "$ROOT" --project "$P" --depth 1 --format plain
GET:
board next · project webapp · DoD: NOT DONE (1/2 scopes met) · 2 open cell(s)
DoD bar: hardened
DoD-blocked (close these to reach done):
[✕] checkout checkout — 0/2 DoD-applicable met (2 below bar)
↳ re-verify: lettuce board next --project webapp --scope checkout --depth 2
start here: checkout / dim=a;scope=checkout;unit=cart [In_progress · Functional correctness]
↳ asks: Specified output for valid input across the happy path and every flag.
↳ state: Work on this cell is underway; nothing is proven yet. Next: link the evidence the work produced.
↳ detail: lettuce cell show "dim=a;scope=checkout;unit=cart" --project webapp
[CHECKOUT] — 0.0% · 2 open
• A Functional correctness 0.0% 1I
↳ lettuce dimension show A --project webapp
• I Security (attack surface) 0.0% 1U
↳ lettuce dimension show I --project webapp
↳ full units + coordinates: lettuce board next --project webapp --scope checkout --depth 2
[AUTH] — 100.0% · 0 open
• A Functional correctness 100.0% 1H
• I Security (attack surface) 100.0% 1H
[UNVISITED] <n> of <n> coverage dimensions have NEVER been assessed — no cell names them.
…
(The report goes on to list the coverage axes no cell names yet and a ticket summary; they are elided here.)
What the store just told you, for free:
- The DoD verdict (
NOT DONE (1/2 scopes met)) — the honest answer to "are we there yet?" — leads the report.authis met;checkoutblocks done. - The single target —
start here:names one coordinate,dim=a;scope=checkout;unit=cart, currentlyIn_progress, with what the axis asks and what the state means. Take this coordinate verbatim; do not hand-compose one. Weakest scope, weakest cell, chosen deterministically. - The drill commands — every line prints the exact
lettuce …to descend.
Step 2 — ORIENT · GET the cell's own history
DO: before touching code, read what the target cell already knows.
lettuce cell show "dim=a;scope=checkout;unit=cart" \
--root "$ROOT" --project "$P" --format table
GET — its current state, canonical coordinate (note the axes are sorted dim;scope;unit), pack, and any first-class note:
project : webapp
state : in_progress
coordinate : dim=a;scope=checkout;unit=cart
hash : <id>
pack : coverage
stored : true
Now you know: this cell is a genuine open item on the coverage frontier, and your job is to move it to hardened — which the convention's harden gate will not let you do without proof.
Step 3 — ACT · claim the work, attributed and visible
DO: create a ticket for the change (if none exists), claim its lease, and start it. Leases make the claim atomic and prevent two agents fighting over the same work.
lettuce task create LET-2 --title "Test cart rejects tampered price" \
--body "Add guard test." --root "$ROOT" --project "$P" --author "$A" --format plain
lettuce lease acquire webapp/LET-2 --expires-at "$(date -u -d '+1 hour' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v+1H +%Y-%m-%dT%H:%M:%SZ)" \
--root "$ROOT" --project "$P" --author "$A" --format plain
lettuce task transition webapp/LET-2 start-work \
--root "$ROOT" --project "$P" --author "$A" --format plain
GET (--format plain prints each mutated object's reference; the missing-milestone advisory goes to stderr):
webapp/LET-2
webapp/LET-2
webapp/LET-2
> start-work is lease-gated: without the lease it fails > FW-WF-REQUIREMENT-UNSATISFIED. Acquire first. --expires-at is a required > RFC-3339 timestamp — the lease auto-expires so a crashed agent never wedges the > board.
...now do the actual engineering — write the failing test, make it pass, rebuild, and verify the guard bites (green → break the code → RED → fix → green). Narrate durable notes on the task as you go:
lettuce comment add webapp/LET-2 \
--body "Guard added; verified it bites when price is tampered." \
--root "$ROOT" --project "$P" --author "$A" --format plain
Step 4 — REFLECT · GET the honesty gate, DO record proof
DO: the cell is in_progress; walk it up the coverage ladder and attach the proving task as evidence. The harden transition is guard-bite gated: it refuses unless at least one linked evidence is a task that is done and carries custom/grc, the pin of the ticket-claim-verify run-case its close walk recorded (docs/guides/CLOSE-WALK.md). Linked evidence alone is not proof; finished, verified work is. Link it now and try harden while LET-2 is still open, and the store refuses:
C="dim=a;scope=checkout;unit=cart"
lettuce cell transition "$C" link-evidence --root "$ROOT" --project "$P" --author "$A" --format plain
lettuce cell evidence add "$C" --ref webapp/LET-2 --kind task \
--root "$ROOT" --project "$P" --author "$A" --format plain
lettuce cell transition "$C" harden --root "$ROOT" --project "$P" --author "$A" --format plain # -> FW-WF-GATE-UNSATISFIED
GET:
dim=a;scope=checkout;unit=cart
evd_<id>
lettuce: error [FW-WF-GATE-UNSATISFIED]: transition is blocked by its gate
expected: gate guard-bite to pass (or --facilitate to record-not-enforce)
actual: guard-bite failed: 1 evidence link(s) present but none authorise: task webapp/LET-2 is active, not done
That refusal is the gate working. Close the ticket through its close walk, which completes it and pins custom/grc (a plain task transition … complete does not pin it, and harden keeps refusing with "done but carries no custom/grc run-case pin"), release the lease, then harden:
lettuce task transition webapp/LET-2 complete --reason "guard bites on tampered price" \
--root "$ROOT" --project "$P" --author "$A" --format plain
lettuce lease release webapp/LET-2 --root "$ROOT" --project "$P" --author "$A" --format plain
lettuce cell transition "$C" harden --root "$ROOT" --project "$P" --author "$A" --format plain
Once LET-2 is done and pinned, the last command prints the cell's coordinate: the cell is hardened.
> Discipline, not shortcut. cell set --state hardened runs the same gate > and is refused the same way; only --facilitate gets past it, and that records > the bypass on the cell's event — an unproven green. Flip cells through > cell transition (gated), and reserve cell set for declaring planned / > blocked / waived / seeding. One unproven green poisons every future ORIENT.
Step 5 — FILE · DO organize what you discovered, GET the moved frontier
Found a second defect while working? File it as its own cell/ticket now, so the next ORIENT sees it. Then re-run the loop's first command — the frontier has moved, partly because you hardened a cell and partly because you filed what you found:
lettuce board next --root "$ROOT" --project "$P" --format plain
GET:
board next · project webapp · DoD: NOT DONE (1/2 scopes met) · 1 open cell(s)
DoD bar: hardened
DoD-blocked (close these to reach done):
[✕] checkout checkout — 1/2 DoD-applicable met (1 below bar)
↳ re-verify: lettuce board next --project webapp --scope checkout --depth 2
start here: checkout / dim=i;scope=checkout;unit=cart [Planned · Security (attack surface)]
↳ asks: …
↳ state: …
↳ detail: lettuce cell show "dim=i;scope=checkout;unit=cart" --project webapp
[CHECKOUT] — 50.0% · 1 open
↳ detail: lettuce board next --project webapp --scope checkout --depth 1
[AUTH] — 100.0% · 0 open
…
Checkout climbed 0.0% → 50.0%, the open count dropped from 2 to 1, and start here now points at the next weakest cell. That is the whole method: work hardens cells; cells reveal work.
Adapt this to your own cycle
- One cell per loop. Take the
start heretarget, prove it, harden it, re-orient. Small, honest increments beat batch grading. - Evidence is the currency. A cell reaches
hardenedonly through the gatedcell transition, with a real proving task linked. Never--facilitatepast a failed gate to "make progress". - Read the DoD first, every loop.
board nextleads with the verdict; when it readsDONE, you are actually done — measured, not asserted. - The full ladder. This walkthrough graded a compact slice of the shipped
coverageconvention; its full ladder isuntested → planned → in_progress → gap → evidence_linked → smoke → hardened, viaplan/start/flag-gap/exercise/exercise-gap/link-evidence/harden(plusblock/unblock/exclude/waive/regress/reopen) — the loop is identical across every axis. See the Project setup playbook.
See also
- Agentic cycles — the loop theory and the honesty invariant.
- First contact · Project setup playbook
- Cells · Tasks · Milestones & DoD
- Cells — Commands · Registries & Workflow — Commands
Links to
- Cells — the coverage model
concepts/concept-cell - Milestones and Definition of Done
concepts/concept-milestone-dod - Tasks and the work plane
concepts/concept-task - Workflow — states, transitions, and gates
concepts/concept-workflow - Running lettuce in autonomous agentic cycles
guides/guide-agentic-cycles - First contact — what a fresh agent sees, reads, and does
guides/guide-first-contact - Project setup playbook — prepare a project to leverage lettuce
guides/guide-project-setup-playbook - Cells — Commands
reference/cmd-cells - Registries And Workflow — Commands
reference/cmd-registries-and-workflow
Backlinks
- First contact — what a fresh agent sees, reads, and does
guides/guide-first-contact - Project setup playbook — prepare a project to leverage lettuce
guides/guide-project-setup-playbook - Lettuce Documentation
index
Configuring lettuce for useful leverage
guides/guide-config-for-leverage Wire lettuce into a real project so an agent gets maximum leverage: root/project/author resolution, the config precedence chain, pack selection, mode choice, author identity, saved queries, and DoD floors.
lettuce ships with working defaults, but leverage comes from wiring it into a real project deliberately: a store an agent can find, a pack that models the work, a Definition of Done floor that makes "done" a measurement, and saved reads that survive restarts. This guide covers every configuration surface and how to compose them.
The configuration precedence chain
Every resolvable setting follows one order, everywhere:
explicit flag → environment variable → --config JSON file → built-in default
| Setting | Flag | Env var | Default | |---|---|---|---| | Store root | --root PATH | LETTUCE_ROOT | nearest .lettuce walking up from the working directory, else refused (filesystem); cwd (dedicated-git) | | Project | --project NAME | LETTUCE_PROJECT | none — explicit where required | | Author | --author NAME | LETTUCE_AUTHOR | none — required for every mutation, never inferred | | Mode | --mode | LETTUCE_MODE | filesystem (other: dedicated-git) | | Output | --format | — | table (also plain, json, yaml; markdown only for doctor/usage/skill) | | Server URL | --server-url URL | LETTUCE_SERVER_URL (_FILE) | none. The --server-url flag switches the CLI to HTTP-client mode; LETTUCE_SERVER_URL does too unless a repo-local .lettuce is discovered, which overrides the env (the flag still wins). Fallback http://127.0.0.1:8727 | | Bearer / actor | --bearer | LETTUCE_BEARER (_FILE), LETTUCE_ACTOR (_FILE) | none | | Listen | serve --listen | LETTUCE_LISTEN | 127.0.0.1:8727 | | Config file | --config PATH | LETTUCE_CONFIG | none |
--config PATH is a JSON file for client context (server URL, bearer, actor) — useful when a machine drives a remote served store and you do not want tokens on the command line.
> --author is never inferred. lettuce refuses to guess the acting author > from the OS, Git config, or store contents. This is the honesty invariant that > makes every mutation attributable — set LETTUCE_AUTHOR once per agent so you > never forget it, but the tool will still never fill it in for you.
init — seeding a store idempotently
lettuce init --root .lettuce --author agent-1 \
--bootstrap-project myproject --bootstrap-author --idempotent --format json
init requires a root but neither a project nor an author of its own; the flags bootstrap them in one call. It advertises idempotency_key=true and accepts the external-input keys bootstrap_project, bootstrap_author, idempotent, so --idempotent makes re-running it on an already-initialized store a success, not an error — safe to run on every agent wake as a cron-guarded seed step.
Each new project ships with the default workflow (open → ready → active → review → done, plus blocked/needs-human and terminals), the standard severities (low/medium/high/critical), task types (epic, story, task, bug, research, …), and artifact types — inspect them with lettuce workflow show default and lettuce registry list.
Root and project resolution
--rootis the path-jailed store root. Name it explicitly per project (.lettucein the repo). There is no home-directory default: when no root is stated and none is discoverable by walking up from the working directory, the command refuses instead of guessing. That refusal is deliberate — two stores can each hold a project of the same name, so a wrong-store write succeeds silently and is discovered late.--projectis a trusted context for shorthand references and project-scoped commands. There is no default-project pointer and no inference from store contents — a missing or unknown--projecton a project-scoped command is refused (FW-NAME-PROJECT), never a silent cross-project scan. Use fully qualified references (myproject/TASK-1) when context is ambiguous.
Choosing a mode
| Mode | Activation | When to use | |---|---|---| | filesystem (default) | --mode filesystem / LETTUCE_MODE | Offline, single-worktree, canonical state under --root. The everyday choice. | | dedicated-git | --mode dedicated-git | A strongly-consistent shared store: every mutation is a Git commit and fetches upstream first. Requires connectivity by design — not an offline mode; needs a clean, synced worktree. sync status/push/pull manage it. | | HTTP client | --server-url (always), or LETTUCE_SERVER_URL when no repo-local .lettuce is discovered | The identical CLI dispatches over HTTP to a running lettuce serve instead of mounting the store. A discovered .lettuce overrides the env LETTUCE_SERVER_URL; the flag always wins. |
Pick filesystem for a local agent loop; dedicated-git when several writers share one authoritative store; client when the store lives behind a server. See serving lettuce safely for the client-mode auth posture.
Inspecting and tuning the convention
The shipped convention is the working model — states, transitions, gates, dimensions. lettuce ships exactly one, coverage, and every project runs it by default with nothing to enable. You inspect it and tune it per project rather than swapping it (every read needs a store root and --project):
lettuce dimension list --project P # the project's effective dimensions
lettuce dimension show A --project P # one dimension by slug, full methodology
lettuce dod show --project P # the Definition-of-Done floor + verdict
lettuce grid show --project P # the declared coverage grid + denominator
lettuce board render --project P # the whole board as a self-contained HTML page
coverage is lettuce's 88-dimension / 15-family quality taxonomy. For that same convention with one project-specific adjustment, use the defaults layer — an additive per-project tweak over the ladder / gates / DoD — rather than authoring a whole new convention.
Author identity
Authors are stable, non-secret identifiers (codex-local-01, ci-agent, planner-agent) — one per acting agent.
lettuce author add planner-agent --idempotent # register YOUR name
lettuce project author add myproject planner-agent --author planner-agent --idempotent # link YOURSELF
lettuce project author add myproject agent-2 --author planner-agent --idempotent # a member links a registered teammate
--bootstrap-author on init/project create/project author add creates and links the author in the same call. Linking or bootstrapping YOUR OWN name is a writer act on a hosted server; bootstrapping ANOTHER name (project author add P <other> --bootstrap-author) registers someone else and needs admin on a server with an authz policy (LET-1885) — let the teammate register itself instead. See the membership table in the server security guide.
Saved queries — freezing the reads that matter
Reads an agent runs every cycle should be stored once, not retyped:
lettuce query saved create ready-tasks --project P --author A \
--title 'Ready Tasks' --fql 'from tasks where status = ready select task,title'
lettuce query saved run ready-tasks --project P --format json
lettuce query saved update ready-tasks --fql '<new fql>' --expect-revision 1 --project P --author A
lettuce query saved list --project P
Saved queries are project-scoped, versioned (guard updates with --expect-revision), and archivable. Pair them with the reading surfaces so orientation is one command.
DoD floors — make "done" a measurement
A Definition of Done turns "are we there yet?" from an assertion into a computed verdict. Declare the floor once:
lettuce dod set --grade hardened --depth 2 --recency aging --project P --author A
lettuce dod set --scope s3 --depth 3 --project P --author A # per-scope override
lettuce dod show --project P --format json # policy + verdict
--grade(required for a first declaration) is the minimum pack state a cell must reach to count as done (validated against the active pack).--depthis an optional minimum distinct-revision confirmation depth (FRESH-2).--recencyis an optional minimum freshness bucket (FRESH-3:freshoraging, never stale).- Without
--scopeyou set project defaults; with--scopeyou override one scope's floors (unset fields inherit the default).
The verdict is a strict per-scope AND-gate — a scope is met only when every applicable cell clears the bar, never a blended percentage — and it layers two auto-derived outer gates you never set here: every committed milestone reached and zero non-terminal tickets. dod clear removes a floor or the whole policy.
Wiring lettuce in for maximum agentic leverage
The store is only leverage if an agent can rediscover it after a restart. Anchor the coordinates where they survive compaction — your CLAUDE.md (or equivalent always-injected instruction file), not memory:
1. Export the coordinates — LETTUCE_ROOT, LETTUCE_PROJECT, LETTUCE_AUTHOR so every command inherits them. 2. Cron-guard the seed — run idempotent init on each wake; it is a no-op on an existing store. 3. Model the work — declare project scopes/units on the default coverage convention (tune it with defaults if needed), assert the first cells. 4. Declare the bar — dod set a grade floor so board next leads with a verdict line. 5. Freeze the reads — saved queries for the frontier you check every cycle. 6. Run the loop — then board next both answers "are we done?" and hands you the next target.
Work that lives only in your context dies with your context; work reflected into the store is durable memory. That reflection is the whole point of configuring lettuce well.
See also
Links to
- Milestones and Definition of Done
concepts/concept-milestone-dod - The shipped convention — coverage as data
concepts/concept-pack - Scopes — the DoD/board partition
concepts/concept-scope - The store and the two data planes
concepts/concept-store - Running lettuce in autonomous agentic cycles
guides/guide-agentic-cycles - How to read lettuce — orienting in an existing store
guides/guide-how-to-read - Serving lettuce safely (HTTP)
guides/guide-server-security
Backlinks
- How to read lettuce — orienting in an existing store
guides/guide-how-to-read - Project setup playbook — prepare a project to leverage lettuce
guides/guide-project-setup-playbook - Serving lettuce safely (HTTP)
guides/guide-server-security - Driving lettuce from a file: structured external input
guides/guide-structured-external-input - Lettuce Documentation
index
First contact — what a fresh agent sees, reads, and does
guides/guide-first-contact From zero prior context to productively driving lettuce: the exact first moves (skill, docs, usage, status, board), what each returns, and how the shipped default is the 88-dimension coverage taxonomy (with the smaller huru ladder available on request).
Your entry point may be lettuce.huru.ca or api.lettuce.huru.ca. You have no prior context. This guide is the fastest honest path from "just arrived" to "productively driving lettuce", written from your point of view. Every command below is real — run it.
> This is the entry point of the Agent Playbook. Once oriented, go to > Project setup playbook to prepare a > project, Agentic loop demo to watch one dev loop > end to end, and Agentic cycles for the loop theory.
Before Move 0 — get the binary and point it at a store
Lettuce is driven by the lettuce binary. The Huru hosted API is used through that binary — do not hand-craft curl/JSON against /v1/…; direct HTTP is discouraged and unsupported for agents.
1. Get the binary — pinned to the server's release. Your client must be the release the server runs. Ask the server (raw HTTP — you have no binary yet), download exactly that release FROM THE SERVER (your token is all it needs), verify its checksum:
V=$(curl -fsS -H @<(printf 'Authorization: Bearer %s\n' "$LETTUCE_BEARER") https://api.lettuce.huru.ca/v1/version | jq -r .data.version)
OS=$(uname -s | tr '[:upper:]' '[:lower:]'); ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
curl -fsS -H @<(printf 'Authorization: Bearer %s\n' "$LETTUCE_BEARER") -o "lettuce-$OS-$ARCH" "https://api.lettuce.huru.ca/v1/client/$OS-$ARCH"
curl -fsS -H @<(printf 'Authorization: Bearer %s\n' "$LETTUCE_BEARER") -o "lettuce-$OS-$ARCH.sha256" "https://api.lettuce.huru.ca/v1/client/$OS-$ARCH.sha256"
# fallback (FW-CLIENT-BINARY-UNAVAILABLE): gh release download "$V" -R huru-io/lettuce -p "lettuce-$OS-$ARCH*"
sha256sum --check "lettuce-$OS-$ARCH.sha256" # macOS: shasum -a 256 --check …
A provisioned lettuce on PATH (or the fleet alias flt-issue, a symlink to the same binary) must report the same release in lettuce version; a mismatch warns FW-CLIENT-VERSION-SKEW on every command. When you see it, stop writing and run lettuce self-update (it replaces the binary from the server, verified), then lettuce status. The canonical recipe is SKILL.md §0.1 (lettuce skill).
2. Bind the store and its project — what .lettuce is decides the mode: a file is remote, a folder is a local in-repo store. The URL is the credential-only form (your token is the bare userinfo before @). Bind the project either in the URL path or on a project: line (do not state both and disagree — that is refused as ambiguous):
https://<token>@api.lettuce.huru.ca/<projectname>
# or
https://<token>@api.lettuce.huru.ca/
project: <projectname>
If you have no binding, discover the project with lettuce project list (no context needed) and pass --project / LETTUCE_PROJECT. If you cannot tell which project the work belongs to, ask the human/operator; never guess, and never create a project unasked. Your token also decides which domains (independent stores on one server) you reach: lettuce domain list, then --domain NAME / LETTUCE_DOMAIN / a domain: line in the pointer (precedence in that order, then the token's default; SKILL.md §0.2). In remote mode lettuce status names the server, domain and project you reached and where each came from.
3. Authenticate and identify yourself. Preferred, once enabled: your own Wordmade ID agent identity (bearer + LETTUCE_AUTH_MODE=wordmade-id). Fallback: a shared bearer token (a named token) a Huru human provides — keep it in LETTUCE_BEARER_FILE (or LETTUCE_BEARER), never in a committed pointer. In shared mode you MUST declare your own stable actor name (--author/LETTUCE_ACTOR, sent as X-Lettuce-Actor); never lettuce-operator. A long anonymous session must pick a name and keep it in its memory for the whole session and restarts. First time on a hosted project: lettuce author add <name> --idempotent, then link yourself with lettuce project author add <project> <name> --idempotent --author <name>. A writer token is enough for both. On a server with --auto-provision-actors, skip both and just write as --author <name> (SKILL.md §0.3 "First time on a hosted project").
4. Lease short (~3 hours). lettuce lease acquire <project>/<TASK> --author <you> --expires-at "$(date -u -d '+3 hours' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v+3H +%Y-%m-%dT%H:%M:%SZ)". A short lease lets another agent take over an expired or abandoned one (lease steal / re-acquire); hold longer work by renewing, never one long window.
Then continue below.
Move 0 — read the model, not the whole reference
The binary documents itself. Two commands, no store required:
lettuce skill # the embedded agent guide (SKILL.md) — read this ONCE, top to bottom
lettuce docs # the embedded wiki (HTML explorer); `docs show <page>` streams one page
lettuce skill prints the model-first guide — its opening frame:
…
# Using lettuce
`lettuce` is a work tracker **and coverage/quality store**. Canonical state
(projects, tasks, comments, runs, artifacts, cells) is held in a path-jailed
store; every mutation is validated and recorded as an event. ...
…
lettuce docs show <page> streams one wiki page as markdown (concepts and guides by slug), e.g. lettuce docs show concept-cell or lettuce docs show guide-first-contact. When prose and the binary disagree, the binary wins — so treat usage (below) as ground truth.
Move 1 — get the exhaustive contract on demand
You never guess a flag. For any command, ask the binary:
lettuce usage # every command, every flag, examples
lettuce usage cell set # just one command's contract
lettuce usage cell set --format json # machine metadata: kind, requires_root/project/author, flags, examples
The top of usage carries the Agent Guidance you should internalize before your first mutation:
## Agent Guidance
- Prefer --format json for automation. JSON and YAML outputs use stable machine
envelopes with ok, data, warnings, and meta.
- Pass --project and --author explicitly for mutations. Do not infer project or
author from the operating system, Git config, or store contents.
- Use fully qualified task references such as project/TASK-1 when project context
might be ambiguous.
...
Two rules that will save you an hour: mutations need explicit --project and --author (lettuce never infers them), and task IDs need a 1–9 char uppercase prefix (LET-1, not t-1).
Move 2 — find out where you are
lettuce status --root .lettuce --format plain
root=/repo/.lettuce mode=filesystem project= author= initialized=true diagnostics=0
initialized=true diagnostics=0 means a healthy store already exists at .lettuce. initialized=false means there is no store yet — jump to the Project setup playbook to stand one up. If diagnostics is non-zero, run lettuce doctor --format json for the diagnosis and repair suggestions before touching anything.
The store root resolves through --root, then LETTUCE_ROOT, then config; in a repo the convention is a git-tracked .lettuce/.
Move 3 — see the shipped convention (the default is 88 dimensions)
Coverage grading runs on the one convention-as-data lettuce ships — bundled in the binary and active on every project by default, with nothing to enable. Read a project's effective dimensions (this needs a store root and --project):
lettuce dimension list --root .lettuce --project <P> --format plain # this project's EFFECTIVE dimensions
project : <P>
pack : coverage
dimensions:
ID SLUG NAME FAMILY …
coverage-a A Functional correctness correctness …
coverage-b B UX / DX (usability) usability …
coverage-h H HTTP / API surface interface …
…
count : 88
The shipped default is coverage — lettuce's own 88-dimension quality taxonomy across 15 families, each dimension carrying a full methodology (Procedure / Best practices / Tools / Current approaches / Evaluation / Hardened). The pack: coverage line is the convention's internal identifier surfacing in the envelope — not a command you run. A dimension's slug is its coverage letter (A, B, H, …); read one axis in full with:
lettuce dimension show A --root .lettuce --project <P> --format json # one axis + its methodology
Its state ladder is untested → planned → in_progress → gap → evidence_linked → smoke → hardened (plus blocked/regressed flagged and excluded/waived out-of-denominator); hardened is reached only through the guard-bite gate — a committed check that provably bites under fault-injection.
Add your own axis. On top of the shipped convention you can declare a project-specific dimension (strictly additive — it can never shadow the convention's vocabulary):
lettuce dimension declare <slug> --family <F> --applicability universal|conditional --project <P> --author <A>adds a project axis. See Dimensions.- To tune the ladder / gates / DoD for one project without authoring a new convention, use the
defaultslayer (covered in the setup playbook).
Move 4 — ask the store what to do next
If cells have been asserted, one command orients you and hands you a target:
lettuce board next --root .lettuce --project <P> --format plain
For the coordination picture — who is working, what needs help, what is available — use the single global view:
lettuce work --root .lettuce --project <P> --format plain
lettuce work composes the tasks and leases: IN FLIGHT (holder + expiry + last event), NEEDS HELP (blocked/needs-human or a stale expired lease), and AVAILABLE (no active lease). To find available work directly, lettuce task list --unleased; to take over stalled work, lettuce lease list --status expired then lettuce lease steal.
On an empty store this is honest, not an error:
board next · project <P> · 0 open cell(s)
no cells defined yet — this project is UNSCOPED, not complete. Nothing has been assessed.
…
Once there is coverage, board next ranks every scope weakest-first, prints a single start here pointer, and — if a Definition of Done is declared — leads with the DoD verdict line:
board next · project webapp · DoD: NOT DONE (1/2 scopes met) · 1 open cell(s)
DoD bar: hardened
...
start here: auth / dim=a;scope=auth;unit=login [Planned · Functional correctness]
...
↳ detail: lettuce cell show "dim=a;scope=auth;unit=login" --project webapp
…
Every summarized line prints the exact lettuce … command to descend, so you are never stuck. --depth 0|1|2 deepens the report; --scope <name> restricts it to one scope.
You are now oriented — what to do with it
1. Read the DoD verdict first (lettuce dod show --project <P>), then take the single target board next hands you — don't hand-compose a coordinate. 2. Discover concrete work with lettuce task list --project <P> and lettuce query run … (FQL). 3. Drive the loop: claim a lease, transition status, do the work, record evidence, grade the cell. The full worked example is the Agentic loop demo.
See also
- Project setup playbook — prepare a project from scratch.
- Agentic loop demo · Agentic cycles · How to read a store
- Getting started · Overview · Command Reference
Links to
- Dimensions and members
concepts/concept-dimension - Milestones and Definition of Done
concepts/concept-milestone-dod - The shipped convention — coverage as data
concepts/concept-pack - Querying with FQL
concepts/concept-query - Getting started
getting-started - Running lettuce in autonomous agentic cycles
guides/guide-agentic-cycles - Agentic loop demo — one development cycle, step by step
guides/guide-agentic-loop-demo - How to read lettuce — orienting in an existing store
guides/guide-how-to-read - Project setup playbook — prepare a project to leverage lettuce
guides/guide-project-setup-playbook - What is lettuce
overview - Command Reference
reference/command-reference
Backlinks
- Agentic loop demo — one development cycle, step by step
guides/guide-agentic-loop-demo - Project setup playbook — prepare a project to leverage lettuce
guides/guide-project-setup-playbook - Lettuce Documentation
index
How to read lettuce — orienting in an existing store
guides/guide-how-to-read Before acting, an agent orients: board next for the frontier, FQL over tasks/cells/events, task show/audit, rollups and timelines, and the leading DoD verdict line — read machine-first with --format json.
The first thing an agent does with an unfamiliar store is not act — it reads. lettuce is built so the store read forward is a plan and read backward is proof, and every reading surface is a pure, deterministic projection that never mutates. This guide is the orientation pass: what to read, in what order, and how to interpret the verdict.
The one-command orient: board next
Start here. board next distills the whole coverage board into an agent-facing orientation report — "what should I harden next?" — without exporting and re-parsing anything:
lettuce board next --project P --format json # meters + start-here
lettuce board next --project P --depth 2 --format json # + weakest units + concrete coords
- It is a pure read over the same BoardExport that
board export/renderproduce: a project context is required — resolved from--project, elseLETTUCE_PROJECT, elseproject:in config (an empty value counts as MISSING, not malformed — the resolver skips it, so an empty--projectand no flag at all reach the same refusal) — and a MISSING context is refusedFW-CMD-MISSING-PROJECT-CONTEXTwhile an INVALID name is refusedFW-NAME-PROJECT(LET-264 split them). It reads only that project's board, never a cross-project scan. - Output is weakest-first: every scope's meter ranked, plus a single "start here" pointer (the globally weakest open cell).
--depthdeepens detail:0(default) meters + start-here;1adds each scope's shape-aware subcounts;2adds the weakest units and the concretegap/untestedcoordinates — each capped with an honest+N-moredrill string, never a silent truncation.--scope <slug>restricts the report; a slug that names no scope on the board is refused with the available list.
The frontier is the not-done, not-excluded, not-smoke cells (gap + untested). An empty board is not an error — every scope meter reads n/a. The report is self-guiding: each summarized node prints the exact lettuce … command to descend, so you are never stuck.
Reading the DoD verdict line
If the project has declared a Definition of Done, board next leads with it — the first line is the verdict:
DoD: NOT DONE (0/1 scopes met)
Read this first, before picking any target. The verdict is a strict per-scope AND-gate over applicable (non-excluded) cells — a scope is done only when every cell clears the bar, reported as a k/n count, never a blended percentage. Two consequences to internalize:
- 100% hardened can still be NOT DONE. Setting a DoD never moves the hardened ratio; a scope may read fully hardened yet be unmet because its evidence is stale or too shallow (depth/recency floors).
- Outer gates apply. Beyond per-scope coverage, overall DONE also requires every committed milestone reached and zero non-terminal tickets — visible via
lettuce dod showasmilestone_gateandticket_gate.
A project that declared no floor reports declared=false and the board renders unchanged (DoD is opt-in).
The three freshness/depth/dod axes
The cells query source carries the DoD-derived axes that explain why a cell is or isn't done — the vocabulary the verdict is computed from:
| Axis | Values | Meaning | |---|---|---| | freshness | fresh | aging | stale | how current the cell's evidence is | | depth | int | distinct-revision confirmation count (FRESH-2) | | dod | blocking | met | is this cell blocking the Definition of Done | | dod-reason | grade | depth | recency | unknown | why a blocking cell blocks |
Reading these turns "what is not yet done?" into a precise query rather than a guess.
Querying with FQL over tasks, cells, and events
FQL reads the store deterministically: from SOURCE [where EXPR] [select f1,f2] [order by f [desc]] [limit N]. Strings use double quotes (single quotes are rejected).
# Work plane — what is in flight.
lettuce query run 'from tasks where status = active select task,title' --project P --format json
lettuce query tasks --group-by status --project P --format json # bucketed counts
# Coverage plane — what remains, by axis.
lettuce query run 'from cells where dod = blocking select coordinate,state' --project P --format json
lettuce query run 'from cells where freshness = stale select coordinate' --project P --format json
lettuce query run 'from cells where depth >= 2 select coordinate' --project P --format json
lettuce query run 'from cells where dod-reason = grade select coordinate' --project P --format json
FQL has no dedicated scope/unit filter operator — slice by a coordinate member instead: where coordinate contains "scope=login". See scopes. Sources include tasks, events, cells, registry, authors, projects, dimensions; the cells source lists stored assertions only — sparse pack-defaults are never rows. Soft-archived tasks are hidden everywhere by default (--include-archived opts in).
Reading one object: task show and task audit
lettuce task show P/TASK-1 --with-body --format json # or --full
lettuce task audit P/TASK-1 --format json # full event history
Every mutation is an event, so task audit is the object's complete provenance — who did what, when, with which reason. Bodies are versioned, never edited in place, so the history is faithful.
Rollups and timelines — the aggregate reads
lettuce cell rollup --by test-coverage --project P --format json # per-member floor + hardened ratio
lettuce query timeline --project P --kind object-created --format json
lettuce query timeline --project P --task P/TASK-1 --format json
cell rollup --by DIMpools a project's stored cells by one dimension: the floor (least-progressed) state, hardened count, non-excluded denominator, and ratio per observed member — N/A-excluded, never blended. This is where you see which axis is weakest before drilling withboard next.query timelinemerges events chronologically, filterable by--task,--author,--kind,--since,--until— the history of the whole project or one object.
How an agent orients before acting
A disciplined orientation pass, in order:
1. Verdict — board next (reads the DoD: … line first): are we done, and what's blocking? 2. Frontier — the same report's weakest scope + start-here coordinate: where is the next target. 3. In-flight work — query tasks --status active and your held leases (lease list --holder <you>): what is already claimed, so you don't collide. 4. Provenance, if needed — task audit / query timeline on the target: why the cell is in its current state.
Only then act. The store, not your context, is your memory — read it first every cycle. See running lettuce in autonomous agentic cycles for the full orient → act → reflect → file loop.
Read machine-first: --format json
For any automated reader, pass --format json (or yaml). Every result is the stable envelope { ok, data, warnings, meta }; errors are { ok:false, error:{ code, message, diagnostics:[...] } } with machine-readable codes. Branch on the code, not the message text — diagnostics carry expected/actual, repairability, and suggested_actions, so the error tells you the fix. Exit codes are distinct too: 1 invalid input, 2 not found, 3 expected-revision mismatch, 6 interrupted operation requires recovery, 0 success.
See also
Links to
- Cells — the coverage model
concepts/concept-cell - Milestones and Definition of Done
concepts/concept-milestone-dod - Querying with FQL
concepts/concept-query - Scopes — the DoD/board partition
concepts/concept-scope - The store and the two data planes
concepts/concept-store - Running lettuce in autonomous agentic cycles
guides/guide-agentic-cycles - Configuring lettuce for useful leverage
guides/guide-config-for-leverage
Backlinks
- Configuring lettuce for useful leverage
guides/guide-config-for-leverage - First contact — what a fresh agent sees, reads, and does
guides/guide-first-contact - Project setup playbook — prepare a project to leverage lettuce
guides/guide-project-setup-playbook - Serving lettuce safely (HTTP)
guides/guide-server-security - Lettuce Documentation
index
Retrying a mutation safely: --idempotency-key
guides/guide-idempotent-mutations Make a retried mutation land at most once with --idempotency-key: which commands accept it, how a replay identifies itself, what a reused key with a different payload does, and why the record is runtime-only.
A retry is only safe if the second attempt can tell it is a second attempt. An agent whose network call times out does not know whether the mutation landed, and re-running it blind is how one intended task becomes two.
--idempotency-key closes that gap: the first call performs the mutation and remembers its response; a repeat of the same call with the same key returns that stored response instead of mutating again.
lettuce task create DEMO-1 --project demo --author agent-1 \
--title "Wire the importer" --idempotency-key run-42-create-demo-1
lettuce task create DEMO-1 --project demo --author agent-1 \
--title "Wire the importer" --idempotency-key run-42-create-demo-1 -> ok: true the replay
A replay says so, in meta
This is the part worth wiring into a caller. The replay returns the original response — including the same operation_id — and marks itself:
first call ok=true operation_id=op-20260827-171309-067f48d57b6ddb4
replay ok=true operation_id=op-20260827-171309-067f48d57b6ddb4
meta.idempotency_replayed = true
So a caller distinguishes "my retry worked" from "my retry was a no-op because the first attempt had already succeeded" by reading meta.idempotency_replayed, not by comparing timestamps or diffing the store.
Reusing a key for a different mutation is refused
A key identifies one specific mutation, not a session or a batch. Reuse it with a different payload and lettuce refuses rather than guessing which one you meant:
lettuce task create DEMO-1 --title "Wire the importer" --idempotency-key k1 -> ok: true
lettuce task create DEMO-1 --title "Something else" --idempotency-key k1 -> FW-CMD-IDEMPOTENCY-CONFLICT: idempotency key was already used with a different mutation scope
The output format is not part of the mutation (LET-1867). The record is the JSON envelope whatever --format the first call used, and a replay renders it in the replay's own format — a first call in table and a retry in json get the same result, the json one with meta.idempotency_replayed = true. (Before v0.20.1 the format was part of the identity locally, and such a retry was refused with this code; over --server-url it never was.) A human replay does not repeat the first call's warnings on stderr; the json/yaml envelope still carries them.
There is a second form of the same code: idempotency key is already in progress, raised when another call holds the same key and has not completed. The mismatched-scope form is a bug in the caller and retrying will never help. Read the message, not just the code — they share it.
For the in-progress form, the suggested actions follow the claimant recorded on the claim (its host and pid):
| claimant | what the refusal tells you | |---|---| | still running on this host | retry with the same key shortly; the retry replays its response | | gone (process no longer exists) | the claim is orphaned: run lettuce recover, which completes it from its cached response (or reclaims it if none was written), then retry | | cannot be verified (another host, no recorded pid, or a pid owned by another user) | retry later if it may still be running; once you have confirmed it is gone, lettuce recover --abandon reclaims it — otherwise it lasts until its TTL, or forever without one |
lettuce doctor lists every such orphaned claim as FW-RUNTIME-IDEMPOTENCY-ORPHANED.
Only mutation commands accept it
The flag is refused, rather than silently ignored, on commands that do not support it:
lettuce doctor --idempotency-key k9 -> FW-CMD-USAGE: idempotency key is not supported for this command yet
Supported today: init; author add; project create, project author add; task create/set/unset/set-list/transition/body; version add; comment add/edit/status; lease acquire/renew/steal/release; run start/finish/log/summary; artifact add/replace; registry create/update; custom set/clear; and the milestone wrappers over registry. The command reference marks each command with idempotency key: true, which is the authoritative list — the enumeration above is a convenience and can age.
The record is runtime-only, and expires
The stored response lives under .runtime/idempotency/<key-hash>/ in the store directory — alongside a scope-hash (what the key was used for) and an expires-at. Two consequences worth knowing:
- It does not travel with the store.
.runtime/is git-ignored and carries no tracked files, so a clone, an export or a fresh checkout starts with no idempotency history. A key that replayed on one machine will perform the mutation on another. - It expires after 24 hours. Past that, the same key is a fresh key and the mutation runs again. Idempotency here protects a retry loop, not a permanent record of "this was already done" — the store's own state is that record.
Choosing keys
Derive the key from the work, not from the clock: something like <run-id>-<step>-<target> is stable across a retry of the same step, which is exactly when you need it to match. A key containing a timestamp defeats the mechanism, because the retry generates a different one.
See also
- Driving lettuce from a file: structured external input — the other half of automating mutations; note that
idempotency_keyis a denied envelope key and stays on the command line. - Running lettuce in autonomous agentic cycles — the loop this flag exists to make re-runnable.
Links to
- Running lettuce in autonomous agentic cycles
guides/guide-agentic-cycles - Driving lettuce from a file: structured external input
guides/guide-structured-external-input
Backlinks
- Lettuce Documentation
index
Project setup playbook — prepare a project to leverage lettuce
guides/guide-project-setup-playbook Do-this-now setup: init and bootstrap, a decision procedure for designing YOUR scopes, choosing and ideating dimensions (with methodology), an evaluable Definition of Done via milestones and tickets, and a per-cycle hygiene checklist.
This is the do-this-now guide for turning a repository into a project lettuce can steer. By the end you have a store, a coverage model that fits your project, an evaluable Definition of Done, and the discipline to keep it honest.
Everything here is real — run each command. It builds on the concepts (Scopes, Dimensions, Cells, Milestones & DoD) for the what; this page supplies the how. New to lettuce? Read First contact first.
1. Init & bootstrap (store, project, author)
One idempotent command creates the store skeleton, a project, and your author:
ROOT=.lettuce; P=webapp; A=codex-01
lettuce init --root "$ROOT" --author "$A" \
--bootstrap-project "$P" --bootstrap-author --idempotent --format plain
initialized /repo/.lettuce
Confirm health before anything else:
lettuce status --root "$ROOT" --format plain
# root=/repo/.lettuce mode=filesystem project= author= initialized=true diagnostics=0
Attribution discipline from command one: lettuce never infers --project or --author from the OS, Git, or store contents — pass both explicitly on every mutation. Pick a stable, non-secret author id (codex-01, ci-agent). Track .lettuce/ in git so the store travels with the repo (only .lettuce/.runtime/ is ignored). For tuning the client context (default root, mode, format), see Configuring lettuce for leverage.
2. The working model (the coverage convention)
Coverage grading runs on the one convention-as-data lettuce ships. The shipped default is coverage (lettuce's 88-dimension quality taxonomy across 15 families), and every project runs it by default — nothing to enable or choose.
The coverage convention ships 88 dimensions across 15 families, each carrying a full methodology (Procedure / Best practices / Tools / Current approaches / Evaluation / Hardened). Inspect it per project (needs a store root and --project):
lettuce dimension list --root "$ROOT" --project "$P" --format json # every dim + its methodology
lettuce dimension show A --root "$ROOT" --project "$P" --format json # one axis in full
Its ladder is untested → planned → in_progress → gap → evidence_linked → smoke → hardened (plan, start, flag-gap, exercise/exercise-gap, link-evidence, harden [guard-bite gated], exclude/waive, block, regress).
> For the same convention with one project-specific adjustment (an extra > working state, an added edge or guard) you don't need another pack — record a > per-project tweak with the defaults layer.
3. Design YOUR scopes (a decision procedure, not a template)
A scope is a partition of the board scored separately and never blended — blending hides gaps. lettuce ships four scope archetypes as reference examples, not a mandatory template:
| Archetype | Shape | Row axis | Good for | |---|---|---|---| | s1 Feature | grid | a feature/command (unit=) | user-facing features × quality dims | | s2 Component | grid | a subsystem (unit=) | internal modules × quality dims | | s3 Product | scalars | none (rows are the dims) | one grade per whole-product axis | | s4 Ecosystem | ladder | a milestone | staged bets that mature over time |
The slugs s1–s4 are magic: they carry those built-in shapes. Any other scope name (scope=auth, scope=checkout) is legal and gets a default heatmap shape. Do not force your project into s1–s4. Instead:
The decision procedure. 1. Enumerate the real surfaces of work that need independent coverage — the things you would not want averaged together (e.g. auth, checkout, api, data-migration). Each becomes one scope. 2. Pick a shape per surface. A grid of units × dimensions? Reuse the s1/s2 archetype. One grade per axis for the whole product? s3. A staged hypothesis that matures (sellable, GA)? s4 + milestones. A flat set of items? use a named scope (heatmap). 3. Define "done" per scope — the grade floor each must reach (§5). 4. Keep scopes few and orthogonal. If two scopes always move together, they are one scope.
A named-scope board needs nothing but cells that carry a scope= member:
lettuce cell set "scope=auth;unit=login;dim=input-validation" --state in_progress \
--root "$ROOT" --project "$P" --author "$A" --format plain
lettuce cell set "scope=checkout;unit=cart;dim=test-coverage" --state planned \
--root "$ROOT" --project "$P" --author "$A" --format plain
To get the richer archetype shapes (grid/scalars/ladder) and real row names, declare scope/unit as dimensions and use the magic slugs — see the worked recipe in lettuce skill ("Standing up a coverage board from scratch"):
lettuce dimension declare scope --family interface --applicability universal \
--name "Scope" --root "$ROOT" --project "$P" --author "$A" --format plain
lettuce dimension member add scope s2 --name "Component" \
--root "$ROOT" --project "$P" --author "$A" --format plain
lettuce dimension declare unit --family interface --applicability universal \
--name "Command / unit" --root "$ROOT" --project "$P" --author "$A" --format plain
lettuce dimension member add unit store --name "store" \
--root "$ROOT" --project "$P" --author "$A" --format plain
# an S2 component-grid cell (has a unit row):
lettuce cell set "dim=am;scope=s2;unit=store" --state gap --reason "durability not yet proven" \
--root "$ROOT" --project "$P" --author "$A" --format plain
> A cell that declares no scope= is bucketed under the reserved unscoped > scope at evaluation — never silently outside the DoD frame. scope=unscoped > is refused (FW-NAME-RESERVED).
4. Units & dimensions per scope
4a. Pick from the shipped palette
Each cell grades a unit (a row: a command, a subsystem) along a dimension (a quality axis). Start from the active pack's dimensions as a palette:
lettuce dimension list --root "$ROOT" --project "$P" --format plain # this project's EFFECTIVE set
lettuce dimension show A --root "$ROOT" --project "$P" --format plain # one axis in full
Read a dimension's methodology before grading any of its cells — it is what separates a shallow smoke grade from a hardened one. For coverage dim D (Data integrity):
Procedure: exercise the write path; assert ATOMICITY (write-temp-then-atomic-
rename or a WAL) and DURABILITY (fsync the file AND its parent dir before ack);
inject a torn/partial write (crash mid-write, truncated temp, kill -9) and assert
the store stays consistent + read-back returns the exact bytes. Then name the
isolation level and prove the anomalies it forbids don't occur.
Best practices: make the ACID story EXPLICIT; writes are write-temp-then-atomic-
rename never in-place; never ack before durable; "validated but never persisted"
is a real bug class, so read the canonical file back and assert exact bytes.
Tools: atomic rename(2), fsync/fdatasync + directory fsync, WAL engines (SQLite
WAL, LMDB, BoltDB); Jepsen elle for isolation; ALICE/CrashMonkey crash injection.
Current approaches: MVCC + snapshot isolation as the default; serializable via SSI;
elle infers isolation anomalies from histories; deterministic simulation.
Evaluation: atomicity (no torn write survives a crash); durability (an fsync'd
write survives power loss); isolation (forbidden anomalies never observed);
multi-object atomicity (a reader never sees a partial commit).
Hardened: a committed torn/partial-write guard bites (green → break → RED → green)
if atomicity/durability is removed; layer an isolation guard + a fsync-durability
guard. Distinct lens vs F (F = in-process race-safety; D = the store's contract).
4b. Ideate a custom dimension (with its methodology)
When your project's nature needs an axis no pack ships, declare one. It is strictly additive — it can add axes and members but never shadow pack vocabulary. Ideate it in four parts:
1. Name the risk — the failure this axis guards against (e.g. "a schema migration corrupts or loses data under partial failure"). 2. Pass criterion — what "covered" means (migrations reversible + loss-free under injected partial failure). 3. Methodology — the procedure an agent follows to grade a unit on it (Procedure / Best practices / Tools / Current approaches / Evaluation / Hardened), so the axis is gradeable, not decorative. 4. Hardened-evidence bar — what a bite-proven guard must do.
Declare it (--family must be one of the active pack's families):
lettuce dimension declare data-migration --family reliability --applicability conditional \
--name "Data migration safety" \
--description "Schema/data migrations are reversible and loss-free under partial failure." \
--root "$ROOT" --project "$P" --author "$A" --format plain
# dimension declare ok
lettuce dimension show data-migration --root "$ROOT" --project "$P" --format plain
Add first-class members if the axis enumerates values:
lettuce dimension member add data-migration forward --name "Forward migration" --rank 10 \
--root "$ROOT" --project "$P" --author "$A" --format plain
# dimension member add ok
> Honest limitation (current build): dimension declare persists a one-line > description but has no methodology field — only shipped pack > dimensions carry the structured six-facet (Procedure / Best practices / Tools / > Current approaches / Evaluation / Hardened) text. So write your > custom dimension's full methodology somewhere durable anyway (a project doc, > a pinned cell note, or the proving task's body), because a dimension without > a methodology is incomplete. For a methodology that travels with the binary, > prefer a coverage-pack dimension or contribute your axis + methodology to a > pack. Do not assert grades on an axis whose methodology you have not written > down.
5. A Definition of Done that's actually evaluable
Turn "done" from an assertion into a measurement. Represent the plan as milestones + tickets, then declare the grade floor so board next can compute a verdict.
# Frame outcomes as milestones (the hypothesis ladder / S4 rungs).
lettuce milestone create v1-launch --title "v1 launch" \
--root "$ROOT" --project "$P" --author "$A" --format plain
lettuce milestone list --root "$ROOT" --project "$P" --format plain
# webapp/milestone/v1-launch
# Declare the bar: the minimum grade every cell must reach to count as done.
lettuce dod set --grade hardened --root "$ROOT" --project "$P" --author "$A" --format plain
# dod set ok (add --depth N / --recency for confirmation-depth + freshness floors,
# or --scope S to override one scope's floor)
Now the DoD is a live verdict — a strict per-scope AND-gate plus outer milestone and ticket gates:
lettuce dod show --root "$ROOT" --project "$P" --format plain
project : webapp
declared : true
grade : hardened
verdict:
coverage_met : false
met_scopes : 0
milestone_gate:
met : false
reached : 0
total : 1
scopes:
APPLICABLE BLOCKED_GRADE MET SCOPE VERDICT
2 1 1 auth unmet
1 1 0 checkout unmet
ticket_gate:
met : false
open : 1
total : 1
total_scopes : 2
verdict : unmet
How tickets map to units. A cell is the address of work; a task is the work. The disciplined loop is: board next names a weak cell → you create a ticket to fix it → link that finished ticket as the cell's evidence → the guard-bite gate then authorizes harden. DoD's ticket_gate refuses "done" while any ticket is still open, and milestone_gate refuses it until every committed milestone is reached — so the verdict reflects both proven coverage and closed work. The full closing loop is the Agentic loop demo.
6. Hygiene & discipline — the per-cycle checklist
The map only steers if it stays truthful. Every cycle:
- [ ] Orient from
board next, take its target. Don't hand-pick coordinates; act on the one the store surfaces (weakest scope, weakest cell first). - [ ] Read the dimension's methodology before grading any cell on it.
- [ ] Flip cells with
cell transition(gated), nevercell set --state hardened.cell setasserts a state directly and bypasses the guard-bite gate — an unproven green. Usecell setonly forplanned/blocked/waived/ seeding. Reachhardenedthrough the gated transition with a real proving task linked as evidence. - [ ] One ticket per finding. Discover a defect mid-work? File it as its own cell (
cell set --state gap/planned) + ticket so the next ORIENT sees it. - [ ] Never
--facilitatepast a failed gate to "make progress" — one unproven green poisons every future orientation. - [ ] Keep the board and tickets current — close tickets, harden cells, and let
cell reconcilemachine-regress stale greens whose evidence unresolved. - [ ] Validate after structural work —
lettuce validate --strictandlettuce doctor --format jsonafter import/repair/conflict resolution. - [ ] Re-orient. Run
board nextagain; confirm the frontier moved for a real reason (a hardened cell, a filed finding), not drift.
See also
Links to
- Cells — the coverage model
concepts/concept-cell - Dimensions and members
concepts/concept-dimension - Milestones and Definition of Done
concepts/concept-milestone-dod - The shipped convention — coverage as data
concepts/concept-pack - Scopes — the DoD/board partition
concepts/concept-scope - Running lettuce in autonomous agentic cycles
guides/guide-agentic-cycles - Agentic loop demo — one development cycle, step by step
guides/guide-agentic-loop-demo - Configuring lettuce for useful leverage
guides/guide-config-for-leverage - First contact — what a fresh agent sees, reads, and does
guides/guide-first-contact - How to read lettuce — orienting in an existing store
guides/guide-how-to-read - Cells — Commands
reference/cmd-cells - Definition Of Done — Commands
reference/cmd-definition-of-done
Backlinks
- Methodology catalog — the orientation board
concepts/concept-methodology-catalog - Agentic loop demo — one development cycle, step by step
guides/guide-agentic-loop-demo - First contact — what a fresh agent sees, reads, and does
guides/guide-first-contact - Lettuce Documentation
index
Serving lettuce safely (HTTP)
guides/guide-server-security lettuce serve is fail-closed by design (ADR-0008): a non-loopback listener with no shared secret refuses to start. Covers the serve surface, bearer auth, per-actor identity, rate gates, IP allowlists, and what is safe to expose.
The same binary that is a CLI is also an HTTP server: lettuce serve exposes the store over HTTP so the identical CLI, pointed at it in client mode, can drive it remotely. Exposing a store to the network is a security decision, and lettuce is built fail-closed so the unsafe path is the one you have to opt into explicitly.
The fail-closed model (ADR-0008)
lettuce serve --root . --listen 127.0.0.1:8727 # safe default: loopback only
The default listen address is 127.0.0.1:8727 — loopback only, reachable solely from the same host. The invariant:
> A non-loopback --listen with no shared secret and no --tokens-file refuses to start.
A secret can come from any of --secret, --secret-file, LETTUCE_API_SECRET, or LETTUCE_API_SECRET_FILE. If none is present and you bind to a non-loopback address, startup refuses unless you pass --allow-unauthenticated-nonloopback — and even then startup warns. You cannot accidentally serve an unauthenticated store to the network; you have to state the override in words.
The serve surface
serve is kind: server — it requires a root but neither a project nor an author (those arrive per-request). Its flags:
| Flag | Env | Purpose | |---|---|---| | --listen ADDR | LETTUCE_LISTEN | Listen address (default 127.0.0.1:8727; non-loopback requires a secret) | | --secret SECRET / --secret-file PATH | LETTUCE_API_SECRET (_FILE) | The bearer token secret | | --allow-ip IP-OR-CIDR | LETTUCE_ALLOWED_IPS | Allowed client IP/CIDR (repeatable) | | --trusted-proxy-cidr IP-OR-CIDR | — | Bounded trusted proxy source range (repeatable; /0 is refused) | | --rate-ip-rpm RPM | LETTUCE_RATE_IP_RPM | Per-IP requests/minute (ADR default 600) | | --rate-actor-mutation-rpm RPM | LETTUCE_RATE_ACTOR_MUTATION_RPM | Per-actor mutating requests/minute (default 300) | | --rate-global-mutation-rpm RPM | LETTUCE_RATE_GLOBAL_MUTATION_RPM | Global mutations/minute across all actors (default 60) | | --rate-burst N | LETTUCE_RATE_BURST | Per-IP burst: max requests in a 1s window (default 60) | | --rate-limit RPM | LETTUCE_RATE_LIMIT | Legacy per-IP limit (seeds --rate-ip-rpm) | | --authz-policy PATH | LETTUCE_AUTHZ_POLICY_FILE | Authorization policy JSON file | | --auth-subject SUBJECT | — | Authenticated subject name of the legacy shared secret | | --domains-root BASE | LETTUCE_DOMAINS_ROOT | Serve several independent stores ("domains"), one per initialized folder BASE/<name> | | --tokens-file PATH | LETTUCE_TOKENS_FILE | Named bearer tokens, each its own subject with its own domains and default domain (secrets never inline) | | --auto-provision-actors | LETTUCE_AUTO_PROVISION_ACTORS | Create missing X-Lettuce-Actor authors for authorized mutations | | --allow-unauthenticated-nonloopback | — | Explicitly permit a non-loopback listener WITHOUT a secret (warns) | | --recover-on-start | LETTUCE_RECOVER_ON_START | Filesystem: reclaim only a provably dead local writer. Dedicated-git: first run recover (re-commits acknowledged operations whose durable commit failed), then discard only the remaining unattributed torn state under the sole-writer assertion. Startup is refused (FW-RUNTIME-RECOVERY-FAILED, nothing discarded) while an acknowledged operation is still uncommitted or a non-store file (e.g. a serve.log inside the store root) is in the worktree. | | --push-interval DURATION | LETTUCE_PUSH_INTERVAL | Auto-push to git remote at interval (dedicated-git only) |
Endpoints
GET / and GET /SKILL.md serve the embedded agent guide unauthenticated — this is intentional: the guide is public documentation, not store data (so are /docs, /docs/flat.md and the /v1/health liveness probe). The store API lives under /v1/… (for example, DELETE /v1/projects/{project}/dod clears a Definition of Done) and is what the bearer credential protects. The authenticated GET /v1/version reports the server's release, and every authenticated response carries it in X-Lettuce-Server-Version, so agents pin a matching client (SKILL.md §0.1). The server's own client binaries (GET /v1/client/{os}-{arch} and .sha256, LET-1932) need the bearer but no role: any token may download them, which is what lets lettuce self-update work with nothing but a token. They are the release's public-to-token-holders build artifacts, never store data. The admin-only GET /v1/clients names which token subjects and actors run which client release; serve --min-client-version (off by default) refuses mutations from older released clients.
Auth and identity posture
lettuce serve does have an auth mechanism — a bearer model plus a per-actor identity header:
- Bearer credential (authentication). Either the legacy
--secret(one shared bearer, one subject, reaches only thedefaultdomain) or named tokens from--tokens-file(each its own subject, with its own reachable domains and default domain —docs/operations/API-DOMAINS.md). Clients present it as--bearer/LETTUCE_BEARER(_FILE) or embedded in the store URL. Requests to/v1/…without a valid bearer are rejected. Neither is a per-user identity. - Actor identity (attribution). A client also sends its acting author via the
X-Lettuce-Actorheader (LETTUCE_ACTORin client mode). Because mutations must be attributed, an unknown actor is refused unless--auto-provision-actorsis set (which creates the missing author for authorized mutations). - Authorization policy (optional).
--authz-policy PATHpoints at an authorization policy JSON file, with--auth-subjectnaming the authenticated subject. The sources describe the flags but not the schema/contents of that policy file — treat the policy format as a binary detail to read fromlettuce usage serveand the shipped spec, not something to guess at here.
Who may register authors and manage membership (LET-1885)
Under an authz policy a per-person writer token lets that person's agents onboard themselves; they do not need an admin:
register your OWN acting name lettuce author add <you> --idempotent writer
link your OWN name to a project lettuce project author add P <you> --author <you> writer
(+ --bootstrap-author registers your own name in the same call) writer
link another REGISTERED author, as a member of P writer
link another author as a NON-member refused FW-CMD-MISSING-AUTHOR for every role: join first
register ANOTHER name author add <other>, or --bootstrap-author for another admin
author deactivate / reactivate, remove a member (no unlink command) admin
The authoritative copy is docs/guides/SERVER-SECURITY.md ("Who may register authors and manage membership"); the HTTP spec's "Author and membership routes" table is the route list, and a test keeps it equal to the code.
> Honest gap: the gold sources describe the transport as plain HTTP and > document no built-in TLS termination. For any remote exposure, terminate TLS at a > trusted fronting proxy — the --trusted-proxy-cidr flag exists precisely to name > that proxy's source range so client-IP allowlisting still works behind it. Do not > assume the served port encrypts traffic on its own; the sources do not claim it.
Rate limiting (ADR-0003)
Four independent gates bound abuse, each with an ADR default: per-IP requests (--rate-ip-rpm, 600), per-actor mutations (--rate-actor-mutation-rpm, 300), global mutations (--rate-global-mutation-rpm, 60), and per-IP burst (--rate-burst, 60 in a 1s window). The legacy --rate-limit seeds the per-IP gate. Tighten these below the defaults for a public-facing instance. Burst refusals do not spend per-minute IP tokens. Route, actor, and JSON refusals do not spend mutation tokens; only an admitted mutation attempt does. The admission decision is shared by idempotent and non-idempotent mutations and is charged once per request.
With --recover-on-start, filesystem mode never force-clears a live, cross-host, or unreadable writer lock. The server reports that unresolved condition both on stderr and in machine output warnings[]; it must not call an unresolved store auto-healed.
In dedicated-git mode the order is recover FIRST, reset SECOND (LET-448). A mutation whose durable commit failed was still acknowledged to its client (ok:true with an FW-GIT-COMMIT-FAILED warning), so uncommitted state at boot is not automatically a torn partial. Recover re-commits every canonical operation it can attribute; only what is left is discarded back to HEAD. If recover itself fails, nothing is discarded and mutations stay fail-closed on FW-GIT-DIRTY until an operator runs lettuce recover.
Local vs remote exposure
| Exposure | How | Posture | |---|---|---| | Loopback (default) | --listen 127.0.0.1:8727 | Same-host only. No secret required; safe for a local agent driving its own store over HTTP. | | Trusted LAN | non-loopback --listen + secret + --allow-ip CIDR | Secret mandatory (fail-closed). Restrict clients with IP allowlists. | | Public / remote | non-loopback + secret + allowlist + TLS-terminating proxy + tight rate gates | Every guard on. Front with a proxy for TLS (see honest gap above); name it with --trusted-proxy-cidr. |
Never reach for --allow-unauthenticated-nonloopback on anything beyond a fully trusted, isolated network — it is the deliberate override of the whole fail-closed model, and startup warns for a reason.
Driving a served instance (client mode)
Point the identical CLI at the server instead of mounting the store:
export LETTUCE_SERVER_URL=https://lettuce.example:8727 # or --server-url
export LETTUCE_BEARER_FILE=/run/secrets/lettuce-bearer # or --bearer
export LETTUCE_ACTOR=agent-1 # X-Lettuce-Actor identity
# For an organization-gated service, configure the pair together:
export LETTUCE_ORGANIZATION=huru
export LETTUCE_ORGANIZATION_TOKEN_FILE=/run/secrets/lettuce-organization/token
lettuce task list --project P --format json # dispatched over HTTP
LETTUCE_SERVER_URL is overridden by a discoverable repo-local .lettuce: if the working directory (or an ancestor) holds a .lettuce, that store answers and the env is ignored. Run these commands where no .lettuce is in scope, or pass the --server-url flag — which always wins over a repo-local .lettuce — to force client mode regardless.
The organization token is an additional admission boundary, not an agent identity. In Wordmade ID mode, LETTUCE_BEARER[_FILE] carries each agent's own OAuth token and the server derives the actor from its verified UUID.
Almost every command works over the HTTP client — the authoritative per-command answer is the kind field of lettuce usage <cmd> --format json. The narrow exceptions are refused in client mode: serve itself, sync status/push/pull, and github init-repo (they act on a local process or the local working copy + Git remote), plus the bulk/confirmation cell ops (cell set-where, clear-where, verify, affirm, local import) which are local / dedicated-git only.
What is and isn't safe to expose
- Safe by default: loopback serve for a co-located agent; the
/and/SKILL.mdguide endpoints (public docs by design). - Safe with the guards on: non-loopback serve with a secret, an IP allowlist, tuned rate gates, and TLS terminated at a trusted proxy.
- Not safe: any non-loopback listener without a secret. lettuce refuses it unless you explicitly override — take the refusal as the correct answer rather than reaching for the override.
See also
- The store and the two data planes · Server & GitHub — Commands
- Configuring lettuce for leverage (modes, config precedence,
--config) - How to read an existing store
Links to
- The store and the two data planes
concepts/concept-store - Configuring lettuce for useful leverage
guides/guide-config-for-leverage - How to read lettuce — orienting in an existing store
guides/guide-how-to-read - Server And GitHub — Commands
reference/cmd-server-and-github
Backlinks
- Configuring lettuce for useful leverage
guides/guide-config-for-leverage - Lettuce Documentation
index
Driving lettuce from a file: structured external input
guides/guide-structured-external-input Supply command fields from a JSON or YAML envelope with --from-json / --from-yaml: the wrapper, what the envelope may not set, how it merges with CLI flags, how YAML scalars are converted, and the refusals you will actually hit.
Most commands take their fields as flags. When the values come from another program — a generator, a form, an agent assembling a task body — putting them on a command line means quoting prose into a shell, and that is where multi-line bodies and apostrophes go wrong.
--from-json and --from-yaml take those fields from a file instead. The command is still named on the command line; only its field values move into the envelope.
The wrapper is required
Both flags accept the same envelope. The wrapper is not optional and not inferred — a bare object of fields is refused.
schema_version: v0.16
kind: lettuce-command-input
data:
task_id: DEMO-1
title: Wire the importer
body: |
Multi-line prose, apostrophes, "quotes" — none of it touches a shell.
{"schema_version":"v0.16","kind":"lettuce-command-input",
"data":{"task_id":"DEMO-1","title":"Wire the importer","body":"..."}}
lettuce task create --project demo --author agent-1 --from-yaml ./new-task.yml -> ok: true
A wrong schema_version is refused FW-CMD-USAGE (unsupported external input schema_version), and only schema_version, kind and data are accepted at the top level.
Not every command accepts an envelope
The support check runs before the file is parsed, so a command that does not accept external input tells you that plainly instead of complaining about your file:
lettuce doctor --from-yaml ./anything.yml -> FW-CMD-USAGE: --from-yaml is not supported for this command
That ordering matters when you are debugging: a message about the file means the command was accepted and the payload is the problem.
The envelope cannot set your execution context
data may carry a command's own fields. It may not carry the trusted context or anything credential-shaped — root, project, author, config, format, mode, bearer, token, PATs, listen addresses and proxy/rate settings are all refused:
lettuce task create --from-json ./new-task.json -> FW-CMD-CONTEXT-IN-EXTERNAL-INPUT: external input cannot set trusted execution context
This is deliberate. A file you received decides what to record; it never decides which store it lands in, who it is attributed to, or what credentials are used. Those stay on the command line, where the operator sets them. The scan is recursive, so a denied key nested deep in the payload is caught too.
CLI flags win; the envelope fills the gaps
The two sources compose rather than conflict, and the rule is one-directional:
> A value supplied on the command line always wins. The envelope only fills > fields the command line did not supply.
envelope: title: from-envelope
command: --title from-flag
stored: from-flag
The same holds for positionals, by index: a CLI positional keeps its own slot and the envelope supplies only the slots you left empty. So you can keep one envelope as a template and override a single field per invocation without editing it.
How YAML scalars are converted
YAML resolves unquoted scalars to types, and lettuce converts them back to the text the command would have received as a flag. For a string field, every one of these round-trips to its literal bytes:
| You write | Stored in a string field | Why | |---|---|---| | 007 | 007 | integer literal preserved, not renumbered | | 0x10 | 0x10 | not converted to 16 | | True | True | casing preserved (see below) | | 2026-01-02T03:04:05Z | 2026-01-02T03:04:05Z | timestamp kept as text | | << | << | merge token kept as text | | yes | yes | not a boolean in YAML 1.2 |
The reason is a single principle: the downstream validator is the one source of truth about a value. If the YAML layer silently reshaped values, the same input would mean different things through --from-yaml than through --from-json or a plain flag.
True/TRUE/False/FALSE are the case worth calling out, because YAML does resolve them to booleans. A string field keeps your literal casing; a genuine boolean field (--force, --idempotent, --bootstrap-author, --dry-run) still resolves normally, so force: True and force: true both work.
A null carries no value at all — supplying a required field as null fails exactly as if you had omitted it.
Rejected outright, so an envelope cannot mean two things: anchors, aliases, explicit tags, duplicate object keys, and non-string object keys.
Limits and refusals worth knowing
| Condition | Result | |---|---| | File larger than 64 MiB | refused before any write | | Container nesting deeper than 128 | external input nesting is too deep | | Duplicate key at any level | refused (both JSON and YAML) | | A file path escaping the store root | FW-PATH-ESCAPES-ROOT |
The size and nesting checks run before any mutation, so a rejected envelope never leaves a half-written store.
A note on where the file lives
Keep envelopes outside the store root. The store validates its own top-level paths, so a stray .yml inside --root is refused FW-PATH-UNKNOWN: unknown top-level store path — a confusing error to hit while debugging an unrelated payload.
See also
- Configuring lettuce for useful leverage — where
--root,--projectand--authorcome from, and why they stay on the command line. - Running lettuce in autonomous agentic cycles — the loop this input path is built for.
Links to
- Running lettuce in autonomous agentic cycles
guides/guide-agentic-cycles - Configuring lettuce for useful leverage
guides/guide-config-for-leverage
Backlinks
- Retrying a mutation safely: --idempotency-key
guides/guide-idempotent-mutations - Lettuce Documentation
index
reference15
Authors And Projects — Commands
reference/cmd-authors-and-projects lettuce Authors And Projects commands — 18 entries — author add, author list, author deactivate, author reactivate, project create, project set-name, project rename, project merge, project list, project show, project archive, project unarchive, project move, project delete, project id-block grant, p
lettuce command group Authors And Projects — 18 commands. Generated from lettuce usage --format okf (always in sync with the binary).
Back to Command Reference.
Commands in this group
author addauthor listauthor deactivateauthor reactivateproject createproject set-nameproject renameproject mergeproject listproject showproject archiveproject unarchiveproject moveproject deleteproject id-block grantproject id-block listproject author addproject author list
---
Manage root authors, projects, and project author membership.
author add
Usage: lettuce author add AUTHOR [--idempotent]
Create a root author.
- kind:
mutation - output:
object— --format plain prints its author reference (author) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
false - idempotency key:
true - external input keys (these go INSIDE
data, not at the top level):name - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--idempotent- Treat an existing author as success.
Examples:
lettuce author add agent-1 --idempotent --format json
lettuce author add agent-1 --idempotent --format plain
Notes:
- LETTUCE_PROJECT, configuration and .lettuce project bindings are session defaults: this command still registers a store-wide author and does not link it to a project. An explicit --project is refused. Use lettuce project author add PROJECT AUTHOR for project membership.
author list
Usage: lettuce author list
List root authors. Deactivated authors (LET-693) are still listed, because they stay registered, and are also named in an inactive list.
- kind:
read - output:
collection— --format plain prints one author reference (the row itself) per row ofauthors; nothing when empty - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
listing— proportional to the collection it reads once (paging or the task projection bound it) - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Examples:
lettuce author list --format json
lettuce author list --format plain
Notes:
- LETTUCE_PROJECT, configuration and .lettuce project bindings do not filter this store-wide roster. For example, LETTUCE_PROJECT=p lettuce author list still includes authors not linked to p. An explicit --project is refused. Use lettuce project author list p to read that project's membership.
author deactivate
Usage: lettuce author deactivate AUTHOR --reason TEXT
Deactivate (revoke) a root author, append-only: the registration and all history remain, an author-deactivated event is recorded on the store-root ledger with the acting author and reason, and inactive-authors/<name> marks the author inactive. The admission gate then refuses the author as --author and as an author-valued field (FW-AUTHOR-INACTIVE).
- kind:
mutation - output:
object— --format plain prints its author reference (author) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--reason text- Why the author is deactivated (required; recorded on the ledger). Required.
Examples:
lettuce author deactivate agent-1 --reason 'left the team' --author admin --format json
Notes:
- Never a delete: authors are append-only, so every record naming the author keeps resolving. The target matches case-insensitively and the stored spelling is recorded. The acting author must be registered and active; a deactivated author cannot reactivate itself. Revoking a server API token is a separate control — a token authenticates a caller, an author is an attributed name, and deactivating one never revokes the other. Over HTTP: POST /v1/authors/{author}/deactivate with a JSON reason body (admin role under --authz-policy); client mode uses it. Audit with lettuce query audit authors/<name>.
author reactivate
Usage: lettuce author reactivate AUTHOR --reason TEXT
Reverse a deactivation: records author-reactivated on the store-root ledger and removes the inactive-authors/<name> marker, so the author may act again.
- kind:
mutation - output:
object— --format plain prints its author reference (author) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--reason text- Why the author is reactivated (required; recorded on the ledger). Required.
Examples:
lettuce author reactivate agent-1 --reason 'rejoined' --author admin --format json
Notes:
- Must be run by ANOTHER active author. validate reports FW-AUTHOR-STATUS-DRIFT when a marker disagrees with the ledger's latest decision (an out-of-band edit); re-running the intended verb re-aligns them.
project create
Usage: lettuce project create PROJECT [--name NAME] [--description TEXT] [--bootstrap-author] [--idempotent] [--yes] [--force]
Create a project skeleton and bootstrap registry objects. Optionally sets a human display name/description (persisted at projects/<slug>/name, /description). Creating an ADDITIONAL project into a store that already holds one requires --yes.
- kind:
mutation - output:
object— --format plain prints its project reference (project) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
true - external input keys (these go INSIDE
data, not at the top level):name, bootstrap_author - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--name text- Optional human display name (defaults to the slug on the board title when unset).--description text- Optional human description of the project.--bootstrap-author- Create/link the acting author as project author.--idempotent- Treat an existing valid project as success.--yes- Confirm creating an ADDITIONAL project in a store that already holds one (also accepts --force).--force- Alias of --yes: confirm an additional project.
Examples:
lettuce project create lettuce --name Lettuce --author agent-1 --bootstrap-author --format json
lettuce project create lettuce --author agent-1 --bootstrap-author --idempotent --format plain
lettuce project create sandbox --author agent-1 --bootstrap-author --yes --format json
Notes:
- A store normally holds exactly ONE project: the board, cell coverage, run-cases and most queries are project-scoped, so a store split across several projects has no single view able to relate its tickets, cells and run-cases. Creating a SECOND project is therefore refused with FW-PROJECT-ADDITIONAL-UNCONFIRMED (naming the projects already present) unless --yes (or --force) is passed. This is a confirmation, not a ban — multi-project stores stay fully supported. The FIRST project of an empty store is never gated, and re-running create for a project that already exists (with or without --idempotent) is unaffected because it adds no project. Over HTTP the same guard applies and the acceptance is "confirm": true in the POST /v1/projects body.
project set-name
Usage: lettuce project set-name PROJECT --name NAME [--description TEXT]
Set or update an existing project's human display name (and optional description). The board title renders this name in place of the slug. Emits a project-updated lifecycle event.
- kind:
mutation - output:
object— --format plain prints its project reference (project) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--name text- Human display name to set (required; empty OR whitespace-only clears back to the slug default). Required.--description text- Optional human description to set (empty or whitespace-only clears it).
Examples:
lettuce project set-name dashboard --name Lettuce --author agent-1 --format json
Notes:
- PROJECT (here, dashboard) must already EXIST — substitute the slug of a project you created (e.g. lettuce project create dashboard --author agent-1 --format json first, or reuse a project you already have); an unknown PROJECT is refused FW-REF-MISSING-PROJECT (check lettuce project list first). Use lettuce project create --name to name a project at creation time instead. The board title (board render) prefers this name over the project slug.
project rename
Usage: lettuce project rename OLD NEW
Rename a project's slug: move projects/OLD to projects/NEW AND rewrite every stored OLD-qualified reference (event targets, cross-object depends-on/blocks refs, object refs) so the renamed store stays strict-valid. A whole-store structural migration, applied atomically under the mutation lock. Preserves the display name and all data. Delete tombstones are write-once and are NOT rewritten (LET-1945): they move with the project, and query audit NEW/... / query timeline --task still answer an object deleted before the rename (the tombstone shows the name it was deleted under).
- kind:
mutation - output:
object— --format plain prints its project reference (new_project) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
false
Examples:
lettuce project rename dashboard lettuce --author agent-1 --format json
Notes:
- OLD and NEW are POSITIONAL slug arguments (not --project). Refuses if OLD does not exist (FW-PATH-NOT-FOUND), NEW already exists (a collision), or NEW is an invalid slug (FW-NAME-SLUG). REFUSES (FW-PROJECT-REF-REWRITE-UNSAFE, writing nothing) when a reference to OLD is folded into a CONTENT-ADDRESSED identity — a graph-run-case advance event or a carrier directory is named by the hash of its own payload, so rewriting such a ref would leave the object no longer addressing its own content. On any failure the store is left byte-unchanged (atomic). Verify after with lettuce validate --strict.
project merge
Usage: lettuce project merge SRC DST
Fold project SRC into project DST and remove the emptied SRC: every task, cell, artifact, comment, run, saved query, graph-def and run-case MOVES into DST, shared registry vocabulary is reconciled, and every stored SRC-qualified reference is rewritten to DST so the merged store stays valid. Rename's sibling whole-store structural migration, applied atomically under the mutation lock. Nothing is ever overwritten, auto-renamed, or silently dropped. SRC's delete tombstones move into DST's collection unrewritten (write-once, LET-1945), so query audit DST/... answers an object SRC deleted.
- kind:
mutation - output:
object— --format plain prints its project reference (target_project) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
false
Examples:
lettuce project merge merge-healing lettuce --author agent-1 --format json
Notes:
- SRC and DST are POSITIONAL slug arguments (not --project); BOTH must already exist. REFUSES the whole merge, writing nothing, when an object id or cell coordinate exists in BOTH projects (FW-PROJECT-MERGE-CONFLICT naming every collision) or when a registry slug both projects share differs SEMANTICALLY (the differing field is named; created/updated/revision provenance is ignored). A shared slug that matches keeps DST's copy; SRC-only objects and vocabulary move in; SRC's own display name/description are dropped with the dissolved project and echoed in dropped_source_meta. Also refuses (FW-PROJECT-REF-REWRITE-UNSAFE) when a reference to SRC is folded into a CONTENT-ADDRESSED identity (a graph-run-case advance event or a carrier directory is named by the hash of its own payload, which includes recorded effect/enrichment refs). On any failure the store is left byte-unchanged (atomic). Verify after with lettuce validate --strict.
project list
Usage: lettuce project list [--include-archived]
List project names. Archived projects are hidden unless --include-archived is passed; when they are included, the result marks which of the returned rows are archived.
- kind:
read - output:
collection— --format plain prints one project reference (the row itself) per row ofprojects; nothing when empty - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
listing— proportional to the collection it reads once (paging or the task projection bound it) - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--include-archived- Include archived projects in the results (hidden by default).
Examples:
lettuce project list --format json
lettuce project list --include-archived --format plain
Notes:
- With --include-archived the JSON result carries an
archivedarray naming which entries ofprojectsare archived (LET-359). Before that, the inclusive listing returned archived and live projects in ONE flat list with nothing to tell them apart, so a caller who asked to SEE archived rows could not IDENTIFY them. Without the flag the key is omitted entirely and the output is byte-identical to the default listing.
project show
Usage: lettuce project show PROJECT
Show project metadata, authors, registry summary counts, and archived_at when archived. A successful read reflects THIS project's own consistency only (LET-1434 scoped gate): corruption in an unrelated part of the store is surfaced by doctor / validate --strict, not refused here.
- kind:
read - output:
object— --format plain prints its project reference (project) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Examples:
lettuce project show lettuce --format json
lettuce project show lettuce --format plain
Notes:
- On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
project archive
Usage: lettuce project archive PROJECT [--reason TEXT]
Soft-archive a project: hide it from default project list and refuse NEW work into it (FW-LIFECYCLE-PROJECT-ARCHIVED) — task create/reopen, registry/dimension/graph-def/workflow additions, leases, runs, saved queries, and (MH-12) new cells, carriers and graph-run-cases — without deleting history. Edits and wind-down of existing objects stay permitted (task set/transition, comments, re-grading an existing cell, advancing or abandoning an existing run-case). Reversible with lettuce project unarchive.
- kind:
mutation - output:
object— --format plain prints its project reference (project) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--reason text- Optional reason recorded on the archive event.
Examples:
lettuce project archive lettuce --author agent-1 --reason 'Retired.' --format json
Notes:
- Archived projects are hidden from project list unless --include-archived is passed, and refuse new task create until restored. This is reversible and preserves history.
project unarchive
Usage: lettuce project unarchive PROJECT [--reason TEXT]
Restore a previously archived project back to normal visibility and re-permit new work (task create and every other accrue verb project archive refuses).
- kind:
mutation - output:
object— --format plain prints its project reference (project) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--reason text- Optional reason recorded on the unarchive event.
Examples:
lettuce project unarchive lettuce --author agent-1 --format json
Notes:
- Only an archived project can be unarchived; the inverse of lettuce project archive.
project move
Usage: lettuce project move PROJECT --to-domain DEST [--from-domain SRC] [--domains-root PATH] [--yes] [--force] [--dry-run]
Move a project to ANOTHER DOMAIN of the same server (LET-1874, ADR-0028): one atomic rename of projects/PROJECT from the source domain's folder to the destination's, under BOTH domains' write locks. The destination gains every author the project names (registered with the normal author-created record) and the project-moved-in record on its root ledger; the source records project-moved-out, and a read of PROJECT there is refused FW-REF-MISSING-PROJECT naming the new domain. Nothing inside the project is rewritten: it reads byte-identically afterwards. O(1) in the project's size apart from its read-only checks. Admin-only over HTTP; needs a token that reaches BOTH domains.
- kind:
mutation - output:
object— --format plain prints its project reference (project) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--to-domain domain- The destination domain (must differ from the source). Required.--from-domain domain- The source domain. Client mode: the request's domain for this command (default: --domain / LETTUCE_DOMAIN / the .lettuce pointer / the token's default). Operator mode: required.--domains-root path- Operator mode, on the server host: the server's domains root (also LETTUCE_DOMAINS_ROOT); both domains are folders under it. Refused in client mode.--dry-run- Report the plan — counts, the authors that would be registered in the destination, warnings and EVERY refusal — and write nothing (no lock is taken).--yes- Confirm the move (also accepts --force). Required unless --dry-run.--force- Alias of --yes.
Examples:
lettuce project move trading-bot --to-domain huru --dry-run --format json
lettuce project move trading-bot --to-domain huru --yes --format json
lettuce project move trading-bot --from-domain default --to-domain huru --domains-root /data/domains --author ops --yes
Notes:
- REFUSES, writing nothing: the destination already has PROJECT (FW-REF-EXISTS); the source lacks it (FW-REF-MISSING-PROJECT); source == destination (FW-CMD-USAGE); either domain is recovering, fenced or migrating (FW-MIGRATION-FENCED, FW-PROJECT-MOVE-DOMAIN-BUSY, runtime codes); an earlier move is unfinished (FW-PROJECT-MOVE-PENDING); the source project fails its consistency gate (FW-STORE-CONSISTENCY-GATE-FAILED); the project references, or is referenced by, ANOTHER project of either domain (FW-PROJECT-MOVE-CROSS-REFERENCE, naming every path); an author the project needs is spelled differently in the destination (FW-AUTHOR-CASE-COLLISION) or is deactivated in the source and absent from the destination (FW-AUTHOR-INACTIVE); the domains are on different filesystems (FW-PROJECT-MOVE-CROSS-DEVICE) or declare different store schemas (FW-PROJECT-MOVE-INCOMPATIBLE).
- Crash-safe: a recovery intent is written into BOTH domains before the first write;
lettuce recoveron either domain (or the server when it opens the domain) completes a move whose rename happened and rolls back one whose rename did not. Until then every mutation of both domains is refused FW-PROJECT-MOVE-PENDING. - A move changes NO token grant: clients update the
domain:line of their .lettuce pointer and need a token that reaches the destination. Archived projects move and stay archived. Audit: lettuce query audit PROJECT (either domain), lettuce query timeline --project PROJECT (destination).
project delete
Usage: lettuce project delete PROJECT --yes [--cascade] [--force] [--reason TEXT]
Permanently delete a project and all its history. Irreversible (unlike project archive, there is no undo). A non-empty project is refused unless --cascade is passed. Requires --yes to confirm. The delete leaves a root-surviving tombstone (actor, reason, time, every removed task) and a project-deleted event on the store-root ledger carrying the same reason, which query audit PROJECT reads back, and it carries the project's earlier task/comment/artifact tombstones with it, each with the task-deleted/comment-deleted/artifact-deleted event its delete recorded (under the same event reference), so query audit PROJECT/TASK still answers for every task the project ever deleted, with the event that deleted it.
- kind:
mutation - output:
object— --format plain prints its project reference (project) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--yes- Confirm the irreversible delete (also accepts --force).--force- Alias of --yes: confirm the irreversible delete.--cascade- Also delete the project's tasks when it is non-empty.--reason text- Reason recorded on the project tombstone (optional; blank means none).
Examples:
lettuce project delete lettuce --author agent-1 --yes --format json
lettuce project delete lettuce --author agent-1 --yes --cascade --reason 'merged into huru' --format plain
Notes:
- Irreversible: prefer project archive when you may need the project or its history again. An empty project deletes with just --yes; a non-empty one needs --cascade.
project id-block grant
Usage: lettuce project id-block grant PROJECT AUTHOR --prefix PREFIX --start N --size N
Reserve a disjoint range of task ids to one author so task create --next cannot mint an id another clone already used. Refused if the range overlaps an existing grant.
- kind:
mutation - output:
object— --format plain prints its id-block reference (first_id) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--prefix PREFIX- Id prefix the block applies to (LET, BUG). Blocks under different prefixes never conflict. Required.--start n- First id in the block, inclusive. Required.--size n- How many ids the block holds. Required.
Examples:
lettuce project id-block grant lettuce agent-2 --prefix LET --start 2000 --size 100 --author agent-1 --format json
Notes:
- The guarantee is DISJOINTNESS: an id inside a granted block cannot have been minted by another author, including in a clone this machine has never seen. That is why an overlapping grant is refused rather than merged.
- An author holding no block is unaffected and still allocates from the highest id in the local tree, with the FW-CMD-ID-ALLOCATED-FROM-LOCAL-TREE warning that says so.
- Run 'lettuce project id-block list PROJECT' first to see which ranges are already held.
project id-block list
Usage: lettuce project id-block list PROJECT
List every id block granted in a project, with each range's first and last id.
- kind:
read - output:
collection— --format plain prints one id-block reference (first_id) per row ofblocks; nothing when empty - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
listing— proportional to the collection it reads once (paging or the task projection bound it) - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Examples:
lettuce project id-block list lettuce --format json
Notes:
- Read this before granting: a grant that overlaps an existing block is refused, and this is the only way to see which ranges are taken.
- On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
project author add
Usage: lettuce project author add PROJECT AUTHOR [--bootstrap-author] [--idempotent]
Link an author into a project.
- kind:
mutation - output:
object— --format plain prints its author reference (author) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
true - external input keys (these go INSIDE
data, not at the top level):project_name, target_author, bootstrap_author - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--bootstrap-author- Create the root author if missing.--idempotent- Treat an existing link as success.
Examples:
lettuce project author add lettuce agent-2 --author agent-2 --idempotent --format json
lettuce project author add lettuce agent-3 --author agent-3 --bootstrap-author --idempotent --format json
lettuce project author add lettuce agent-4 --author agent-1 --idempotent --format plain
Notes:
- Link YOURSELF first: AUTHOR equal to the acting --author is a writer act on a hosted server (LET-1885), so a newcomer joins a project on its own (
lettuce author add <you> --idempotent, then the first example with your name; the second example does both in one call). A MEMBER may then link another REGISTERED author (third example); a non-member linking someone else is refused FW-CMD-MISSING-AUTHOR: join first. - --bootstrap-author for ANOTHER name registers someone else's author, which is
adminon a server with an authz policy (FW-API-AUTHZ-DENIED names the role); let the teammate register itself instead. The full table is docs/guides/SERVER-SECURITY.md "Who may register authors and manage membership".
project author list
Usage: lettuce project author list PROJECT
List authors linked to a project.
- kind:
read - output:
collection— --format plain prints one author reference (the row itself) per row ofauthors; nothing when empty - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
listing— proportional to the collection it reads once (paging or the task projection bound it) - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Examples:
lettuce project author list lettuce --format json
lettuce project author list lettuce --format plain
Notes:
- On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
Links to
- Command Reference
reference/command-reference
Backlinks
- Command Reference
reference/command-reference - reference/index
reference/index
Cells — Commands
reference/cmd-cells lettuce Cells commands — 28 entries — cell list, cell rollup, cell evidence list, cell evidence add, cell evidence remove, cell gate check, cell show, cell set, cell clear, cell set-where, cell clear-where, cell note, cell import, cell transition, cell verify, cell affirm, board export, board next
lettuce command group Cells — 28 commands. Generated from lettuce usage --format okf (always in sync with the binary).
Back to Command Reference.
Commands in this group
cell listcell rollupcell evidence listcell evidence addcell evidence removecell gate checkcell showcell setcell clearcell set-wherecell clear-wherecell notecell importcell transitioncell verifycell affirmboard exportboard nextboard rendercell reconciledimension listdimension showdimension member listdimension member adddimension member updatedimension declaredimension renamedimension close
---
Assert and inspect per-project coverage cells — the coordinate/state grid governed by the project's effective dimensions, states, and gates.
cell list
Usage: lettuce cell list --project PROJECT [--include-archived]
List a project's STORED cells — the explicitly-asserted coordinates and their states; the sparse pack-default space is not enumerated.
- kind:
read - output:
collection— --format plain prints one cell reference (coordinate) per row ofcells; nothing when empty - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
listing— proportional to the collection it reads once (paging or the task projection bound it) - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--include-archived- Read an ARCHIVED project on purpose: without this flag a read scoped to an archived project is refused (FW-READ-PROJECT-ARCHIVED); with it the read succeeds and is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning, and project_archived_at in the json/yaml data).
Examples:
lettuce cell list --project lettuce --format json
lettuce cell list --project lettuce --format plain
Notes:
- On an ARCHIVED project this read is refused with FW-READ-PROJECT-ARCHIVED (exit 1) unless --include-archived is passed; with it the result is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning and project_archived_at in the json/yaml data).
cell rollup
Usage: lettuce cell rollup --by DIMENSION --project PROJECT [--include-archived]
Roll a project's stored cells up by one dimension: the pooled floor (least-progressed) state, hardened count, non-excluded denominator, and ratio per OBSERVED member (N/A-excluded, never blended).
- kind:
read - output:
report— --format plain prints the table bytes - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
listing— proportional to the collection it reads once (paging or the task projection bound it) - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--by dimension- Dimension to roll the project's stored cells up by (pooled floor, hardened count, non-excluded denominator, and ratio per observed member). Required.--include-archived- Read an ARCHIVED project on purpose: without this flag a read scoped to an archived project is refused (FW-READ-PROJECT-ARCHIVED); with it the read succeeds and is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning, and project_archived_at in the json/yaml data).
Examples:
lettuce cell rollup --by A --project lettuce --format json
lettuce cell rollup --by A --project lettuce --format plain
Notes:
- On an ARCHIVED project this read is refused with FW-READ-PROJECT-ARCHIVED (exit 1) unless --include-archived is passed; with it the result is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning and project_archived_at in the json/yaml data).
cell evidence list
Usage: lettuce cell evidence list COORDINATE --project PROJECT
List the evidence links on a cell. Each link's index field is its evd_<32hex> SLOT ID, NOT a numeric position — cell evidence remove accepts that slot id (or a 1-based position). The SAME evidence ledger is also projected inline by cell show --with-audit, next to the cell's derived confirmation depth and freshness — reach for that when you want the evidence in the context of the cell's audit signals rather than the links alone.
- kind:
read - output:
collection— --format plain prints one evidence reference (index) per row ofevidence; nothing when empty - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Examples:
lettuce cell evidence list area=auth;layer=api --project lettuce --format json
lettuce cell evidence list area=auth;layer=api --project lettuce --format plain
Notes:
- On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
cell evidence add
Usage: lettuce cell evidence add COORDINATE --ref REF --kind KIND --project PROJECT
Link evidence to an asserted cell (kind task=an existing task ref, validated; url=an opaque external reference); records a cell-evidence event.
- kind:
mutation - output:
object— --format plain prints its evidence reference (index) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--ref ref- Evidence reference: an existing task ref (with --kind task, validated to exist) or an opaque external reference (with --kind url). Required.--kind kind- Evidence kind: task (ref validated to exist) or url (any non-empty opaque string). Required.
Examples:
lettuce cell evidence add area=auth;layer=api --ref lettuce/LET-1 --kind task --project lettuce --author agent-1 --format json
Notes:
- The cell must already be asserted (cell set/transition). A task ref is validated to exist; url is any non-empty string.
cell evidence remove
Usage: lettuce cell evidence remove COORDINATE --index N --project PROJECT
Remove one evidence link from an asserted cell; records a cell-evidence-remove event. The inverse of cell evidence add — drop stale evidence so a guard-bite gate must be re-proven on fresh evidence. --index names the link EITHER as its evd_<32hex> slot id (the index field from cell evidence list) OR as its 1-based positive position (1 = the first link); --index 0 is refused.
- kind:
mutation - output:
object— --format plain prints its evidence reference (index) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--index n- The evidence link to remove: itsevd_<32hex>slot id (shown asindexbycell evidence list), or its 1-based positive position (1 = the first link).--index 0is refused; an absent target is refused FW-PATH-NOT-FOUND. Required.
Examples:
lettuce cell evidence remove area=auth;layer=api --index 2 --project lettuce --author agent-1 --format json
Notes:
- The cell must be asserted and carry an evidence entry at the named link. Surviving links keep their slot ids (no renumber).
cell gate check
Usage: lettuce cell gate check COORDINATE --gate SLUG --project PROJECT
Evaluate one gate declared by the active pack against a cell, returning {gate, coordinate, passed, reason}. Built-in evaluators: consistency (a STORE-WIDE predicate: the whole store root validates clean. It IGNORES the COORDINATE, so every cell in the store gets the same verdict, and it is not project-scoped either — a fault in another project sharing the root fails this check) and guard-bite (the cell carries >=1 AUTHORISING evidence link — a task link that still RESOLVES to a DONE task carrying custom/grc, so a link citing a DELETED task is a phantom and fails; a url link is stored and counted as evidence but ANNOTATES ONLY and cannot open a gated state, so a cell whose only evidence is a url still fails). Read-only; an unknown gate is refused FW-GATE-UNKNOWN. The exit code encodes the verdict (LET-1819, the LET-415 query shape): a well-formed check always returns ok:true with the verdict in data, and exits 0 on PASS or 2 on FAIL. Exit 1 is a usage error or refusal.
- kind:
read - output:
verdict— --format plain prints the cell reference (coordinate) iffpassedis true; nothing otherwise (the explanation on stderr; the exit code is the answer) - exit codes:
0yes (passed);2no: the answer, not a failure (ok:true; output contract R6); any failure:1-7by diagnostic class (spec §24.11) - read cost:
store-inline— walks the whole store inside the request (not yet a server-owned job; it can outlast the client timeout on a large hosted store) - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--gate slug- Gate slug to evaluate (built-in evaluators: consistency, guard-bite); an unknown gate is refused FW-GATE-UNKNOWN. Required.
Examples:
lettuce cell gate check area=auth;layer=api --gate guard-bite --project lettuce --format json
lettuce cell gate check area=auth;layer=api --gate consistency --project lettuce --format plain
Notes:
- On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
cell show
Usage: lettuce cell show COORDINATE [--with-references] [--with-audit] --project PROJECT
Show the asserted state of a cell at a coordinate; an untouched coordinate reports the active pack's sparse DEFAULT state (stored=false) computed, not stored. --with-references adds a referenced_by projection — the graph-run-cases whose effects reference this cell (INT-15b provenance surfaced inline; the reverse of graph-run-case refs-to). --with-audit adds the LET-418 evidentiary projection — the cell's EVIDENCE links (the same ledger cell evidence list reads) surfaced under audit.evidence, alongside the derived confirmation depth/freshness and the confirmation ledger. So a cell's evidence IS reachable from cell show --with-audit; you do not have to go to cell evidence list to see it.
- kind:
read - output:
object— --format plain prints its cell reference (coordinate) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--with-references- Include the referenced_by back-reference projection: the graph-run-cases whose recorded produced-effects reference THIS cell (which run-case decisions hardened/touched it). Opt-in (default off keeps the cheap bare read). Surfaced in the machine formats (json/yaml) and the human table — always present there when set, an empty [] when nothing references the cell. An inclusion flag: refused under --format plain (FW-CMD-USAGE, output contract D1), which prints the bare coordinate; read the projection with table/json/yaml.--with-audit- Include the LET-418 evidentiary projection underaudit: the cell's EVIDENCE links (audit.evidence— the SAME ledgercell evidence listreads, via ops.ListCellEvidence), plus the derived depth/depth_verify/depth_affirm, freshness/freshness_basis, and the confirmation LEDGER those figures are derived from. This is where a cell's evidence surfaces oncell show. Before this flagcell showreturned state/stored/note only and the confirmation ledger had NO read surface at all, so the signals that reveal DoD gaming — is this hardened cell evidence-backed, how deep, how stale, who attested it — were readable only via board export and FQL. Composed from the SAME readers the board and the DoD verdict use, so the audit can never disagree with the gate. OPT-IN because it needs a whole-project provenance fold (the freshness reference revisions), cached by writer-generation; a bare cell show stays a cheap point read. Local store only: refused in client mode rather than silently answering without the projection.
Examples:
lettuce cell show area=auth;layer=api --project lettuce --format json
lettuce cell show area=auth;layer=api --with-references --project lettuce --format json
lettuce cell show area=auth;layer=api --with-audit --project lettuce --format json
lettuce cell show area=auth;layer=api --project lettuce --format plain
Notes:
- To see a cell's EVIDENCE links, run
cell show --with-audit— it projects the evidence ledger (audit.evidence) alongside the derived confirmation depth, freshness, and confirmation ledger — orcell evidence list COORDINATEfor the evidence links alone. A barecell show(no flag) omits evidence by design, so the absence of an evidence field there does NOT mean the cell has none. - On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
cell set
Usage: lettuce cell set COORDINATE --state STATE [--facilitate] [--note TEXT] [--reason REASON] --project PROJECT
Assert a cell's state at a coordinate (canonicalized dim=member;dim=member), recording a cell-set event; the state must be in the project's active pack vocabulary. --note persists a first-class rationale scalar on the cell (queryable via cell show, projected as cells[].reason in board export).
- kind:
mutation - output:
object— --format plain prints its cell reference (coordinate) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--state state- State to assert at the coordinate; must be in the active pack's state vocabulary. Required.--reason reason- Optional justification for the assertion, recorded on the cell-set EVENT (most meaningful when asserting a waived/excluded state — captures WHY the cell is out of scope).--note text- Optional FIRST-CLASS rationale (GAP-2) persisted as a scalar ON the cell — read back by cell show and projected as cells[].reason in board export. Distinct from --reason (event-only). Omitting --note preserves any existing note; clear a note with cell note COORD "".--facilitate- Record-not-enforce a direct set into a GATE-ENTRY-ONLY state: assert it even though no gate guarding entry passed, recording the failed gate verdict on the cell-set event (the same annotation cell transition --facilitate writes).
Examples:
lettuce cell set area=auth;layer=api --state in_progress --project lettuce --author agent-1 --format json
lettuce cell set area=auth;layer=api --state waived --reason 'platform not in scope for v1' --project lettuce --author agent-1 --format json
lettuce cell set area=auth;layer=api --state hardened --note 'covered by TestAuthApiHardened (bite-verified)' --project lettuce --author agent-1 --format json
lettuce cell set area=auth;layer=api --state hardened --facilitate --reason 'onboarding a hand-graded corpus' --project lettuce --author agent-1 --format json
Notes:
- The coordinate is canonicalized (dimensions sorted, deduplicated) and the state validated against the active pack's states; a malformed coordinate or unknown state is refused. An optional --reason justification is durably recorded on the cell-set event (the same rationale mechanism cell transition uses); an optional --note persists a first-class rationale scalar on the cell itself (GAP-2), read back by cell show and projected as cells[].reason in board export. The reserved
scope=member partitions the DoD/board: a coordinate with ascope=lands in that scope, one WITHOUT ascope=is bucketed under the synthetic scopeunscopedat evaluation time (so it always counts — never vacuously done); asserting an explicitscope=unscopedis refused (FW-NAME-RESERVED) since that name is reserved for the bucket. PROMOTION INTEGRITY: a state the active pack lets you enter ONLY through gated transitions (e.g.hardened, entered only byhardenbehind the guard-bite gate) is GATE-ENTRY-ONLY — a direct set into it must satisfy at least one of those gates or it is refused FW-WF-GATE-UNSATISFIED; --facilitate applies it anyway and records the failed verdict on the event. A state with ANY ungated way in (or none at all, like the pack default) is unaffected. UNREASONED EXCLUSIONS (LET-417): a state whose every declared entryrequires: [reason](waived, excluded, gap, blocked on the bundled pack) is NEVER refused, but a direct set into it with neither --reason nor --note is recorded UNREASONED (the result'sunreasoned: true, a cellunreasonedmarker, an[unreasoned]event segment). An unreasoned waived/excluded cell does NOT leave the hardened_ratio/DoD denominator — it stays applicable (and fails the DoD grade floor) and is counted asunreasoned_exclusionson the board headline, the rollup rows and the DoD verdict. Re-set it with --reason/--note (or reach it via cell transition) to make it a real N/A.
cell clear
Usage: lettuce cell clear COORDINATE --project PROJECT
Remove a stored cell so its coordinate reverts to the active pack's sparse default (stored=false); a coordinate that was never asserted is refused.
- kind:
mutation - output:
object— --format plain prints its cell reference (coordinate) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Examples:
lettuce cell clear area=auth;layer=api --project lettuce --author agent-1 --format json
Notes:
- Hard-removes the cell's stored assertion; clearing an un-asserted coordinate is refused.
cell set-where
Usage: lettuce cell set-where --where '<fql>' --state GRADE [--reason TEXT] [--confirm] [--dry-run] [--facilitate] --project PROJECT
Bulk-assert one state (grade) on EVERY stored cell matching an FQL predicate over the cells source (the same engine query run / cell list back — reuse the scope/unit/dim/group/kind/grade columns to slice a dimension). Requires --confirm to mutate more than one cell; --dry-run lists the matched cells without writing, and predicts what the apply will do with them: for a gate-entry-only target it names the gates (gate_entry_only, gates) and lists the matched cells the apply will SKIP on a failed entry gate under skipped/skipped_count, with the apply's own reason (LET-1576; not under --facilitate, which records the bypass instead). The whole batch is applied under ONE store mutation lock (byte-identical to N individual cell set calls), so no concurrent writer interleaves.
- kind:
mutation - output:
collection— --format plain prints one cell reference (coordinate) per row ofsetorcoordinates; nothing when empty; its report findings announced on stderr - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--where fql- FQL predicate over the cells source selecting the cells to mutate (same syntax as query run 'from cells where ...'). Required.--state grade- Grade to assert on every matched cell; must be in the active pack's state vocabulary. Required.--reason text- Optional justification recorded on each cell-set event (most meaningful when asserting a waived/excluded grade).--facilitate- Record-not-enforce a bulk assert into a GATE-ENTRY-ONLY grade: apply it to matched cells whose entry gates fail, recording the failed verdict on each cell-set event. Without it those cells are skipped and reported.--confirm- Confirm applying the change to all matched cells (required for more than one match).--dry-run- List the matched cells and intended change without writing.
Examples:
lettuce cell set-where --where 'scope = s1 and grade = evidence_linked' --state hardened --confirm --project lettuce --author agent-1 --format json
lettuce cell set-where --where 'unit = cell-set' --state hardened --dry-run --project lettuce --author agent-1 --format json
Notes:
- Bulk operation over an FQL-selected set; local filesystem or dedicated-git mode only. In HTTP client mode, mutate individual coordinates with
cell set.
cell clear-where
Usage: lettuce cell clear-where --where '<fql>' [--confirm] [--dry-run] --project PROJECT
Bulk-remove EVERY stored cell matching an FQL predicate so each coordinate reverts to the active pack's sparse default (the bulk inverse of cell clear). Requires --confirm to clear more than one cell; --dry-run lists the matched cells without writing. Applied under ONE store mutation lock.
- kind:
mutation - output:
collection— --format plain prints one cell reference (coordinate) per row ofclearedorcoordinates; nothing when empty; its report findings announced on stderr - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--where fql- FQL predicate over the cells source selecting the cells to clear (same syntax as query run 'from cells where ...'). Required.--confirm- Confirm clearing all matched cells (required for more than one match).--dry-run- List the matched cells without writing.
Examples:
lettuce cell clear-where --where 'grade = untested' --confirm --project lettuce --author agent-1 --format json
Notes:
- Bulk operation over an FQL-selected set; local filesystem or dedicated-git mode only. In HTTP client mode, clear individual coordinates with
cell clear.
cell note
Usage: lettuce cell note COORDINATE [TEXT] --project PROJECT
Set (or, with no/empty TEXT, CLEAR) a cell's first-class NOTE — the operator rationale scalar (GAP-2), orthogonal to the cell's state, so you can annotate WHY a cell holds its grade without re-asserting the grade. Read back by cell show and projected as cells[].reason in board export. The cell must already be asserted (a note needs a cell).
- kind:
mutation - output:
object— --format plain prints its cell reference (coordinate) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Examples:
lettuce cell note area=auth;layer=api 'grade held by TestAuthApiHardened; last audited 2026-07' --project lettuce --author agent-1 --format json
lettuce cell note area=auth;layer=api --project lettuce --author agent-1 --format json
Notes:
- Records a cell-note event and persists (or removes) the cell's note scalar under one lock with a read-back verify. Annotating an un-asserted coordinate is refused (assert it with cell set first). Omitting TEXT (or passing "") clears the note. Same rationale reachable at assert time via cell set --note, and over HTTP through the cell set PUT body; the dedicated cell note command is local and dedicated-git modes only.
cell import
Usage: lettuce cell import --file PATH [--confirm] [--dry-run] [--facilitate] --project PROJECT
Bulk-ASSERT many cells to their OWN explicit states from a FILE in one locked batch (GAP-8) — the bulk assert cell verify --coords-file (bulk confirm) and cell set-where (bulk set by predicate) left. Each file line is coordinate<TAB>state[<TAB>note]; blank lines and #-comments are ignored. Requires --confirm to assert more than one cell; --dry-run previews the parsed assertions without writing, listing under skipped every line the apply will skip — malformed, off a closed dimension, an unknown state, or (LET-1576) a gate-entry-only state whose entry gate fails, with the apply's own reason. The whole batch applies under ONE store mutation lock (byte-identical to N cell set calls).
- kind:
mutation - output:
collection— --format plain prints one cell reference (coordinate) per row ofsetorassertions; nothing when empty; its report findings announced on stderr - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--file path- Path to the import file: one assertion per line, coordinate<TAB>state[<TAB>note]. Blank lines and #-comments ignored; a malformed line (missing TAB-separated state, empty coordinate/state), an invalid coordinate, or an unknown state is skipped and reported, never aborting the batch. Required.--facilitate- Record-not-enforce rows asserting a GATE-ENTRY-ONLY state: apply them even though no entry gate passed, recording the failed verdict on each cell-set event. Without it those rows are skipped and reported — this is the flag for onboarding an externally-graded corpus.--confirm- Confirm applying every parsed assertion (required for more than one assertion).--dry-run- List the parsed assertions and skipped lines without writing.
Examples:
lettuce cell import --file ./cells.tsv --confirm --project lettuce --author agent-1 --format json
lettuce cell import --file ./cells.tsv --dry-run --project lettuce --author agent-1 --format json
Notes:
- Bulk file operation; local filesystem or dedicated-git mode only. Input is supplied with
--file PATH(coordinate<TAB>state[<TAB>note]); this command has no--coords-fileform.
cell transition
Usage: lettuce cell transition COORDINATE ACTION [--reason TEXT] [--facilitate] --project PROJECT
Move a cell to a new state through the active pack's workflow (an action with no legal transition from the cell's current state is refused); records a cell-transition event carrying the fired action and the from/to states it resolved (MH-9), so readers need not replay the pack. A transition that declares a gate must PASS it (FW-WF-GATE-UNSATISFIED otherwise); --facilitate records the gate verdict on the event but allows the move (record-not-enforce). A transition whose pack entry declares requires: [reason] is refused (FW-CMD-MISSING-ARGUMENT) without --reason.
- kind:
mutation - output:
object— --format plain prints its cell reference (coordinate) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--reason text- Why this move was made — REQUIRED by any transition whose pack entry declares requires: [reason]. In the shipped coverage pack that is flag-gap, block, waive and exclude: each records a judgement someone must later be able to question (this is a gap / this is blocked / this is out of scope), so the reason is the durable half of the assertion. Recorded on the cell-transition event. Runlettuce defaults showand read the REQUIRES column for the transitions in force on YOUR project.--facilitate- Record the gate verdict on the event but allow a gated transition to proceed even if its gate fails (record-not-enforce).
Examples:
lettuce cell transition area=auth;layer=api plan --project lettuce --author agent-1 --format json
lettuce cell transition area=auth;layer=api flag-gap --reason 'the cited spec is not in this repo' --project lettuce --author agent-1 --format json
lettuce cell transition area=auth;layer=api harden --facilitate --project lettuce --author agent-1 --format json
Notes:
- The action must name a transition legal from the cell's current state in the active pack (see lettuce defaults show); an untouched coordinate starts at the pack default. A gated transition (e.g. harden requires the guard-bite gate) is refused unless the gate passes or --facilitate is given.
lettuce defaults showis the authority on which actions your project requires a --reason for: its REQUIRES column is resolved from the base convention PLUS the project's own tweak layer, so a project that declares an extra reason-requiring transition is reflected there and cannot be read off this page.
cell verify
Usage: lettuce cell verify COORDINATE [--evidence REF] [--reason TEXT] --project PROJECT | lettuce cell verify --coords-file PATH [--evidence REF] [--reason TEXT] --project PROJECT
Append a VERIFY confirmation to a cell's FRESH-2 confirmation ledger — an INDEPENDENT re-check that the cell's current grade still holds (the stronger durability signal), recorded with the store revision at confirmation time so the cell's distinct-rev hardening DEPTH deepens. Never changes the grade or the hardened ratio; depth is derived (never a settable counter). The STRONG depth (depth_verify — the one the DoD depth floor keys off) counts a verify ONLY when it carries --evidence (a per-cell re-exercise reference); an evidence-less verify records but is demoted to affirm-tier depth, so a bulk stamp with no per-cell proof earns NO verify-depth (the un-fakeable anti-rubber-stamp rule). A given --evidence ref earns verify-depth ONCE PER CELL: the first verify citing it deepens depth_verify, and a later verify on the SAME cell citing the SAME ref is demoted to affirm-tier EVEN AT A NEW STORE REVISION, because one unit of proof is not N independent re-exercises (LET-414-B distinct-EVIDENCE, which is stricter than the distinct-rev rule below). The de-duplication is per-cell, so one ref shared across DIFFERENT cells — the --coords-file pattern — still deepens each of them. Confirming twice at the same store revision buys no depth (the distinct-rev anti-farming rule); a grade change resets depth to the new grade's confirmations only. --coords-file confirms MANY cells in one call (bulk), computing the store subject revision ONCE for the whole batch — the O(n^2)->O(n) fix for a large hardening pass, byte-identical to N individual verifies.
- kind:
mutation - output:
collection— --format plain prints one cell reference (coordinate) per row ofconfirmed, or the one cell it acted on - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--evidence ref- Per-cell re-exercise reference (guard-test name, run id, or URL) proving this verify independently re-checked the cell. REQUIRED for the verify to strengthen depth_verify (the DoD depth floor keys off it); an evidence-less verify still records but only as affirm-tier depth. Re-using the SAME ref on the SAME cell deepens depth_verify only the FIRST time — later repeats record as affirm-tier even at a new revision. With --coords-file the one ref is applied to EVERY coordinate in the batch.--reason text- Optional justification recorded on the cell-confirm event (auditable; a rubber-stamp confirmation stays attributable).--coords-file path- Bulk mode: confirm every newline-separated coordinate in this file in ONE call (blank lines and #-comments ignored). Computes the subject revision ONCE for the whole batch (O(n^2)->O(n)); each cell's confirmation is byte-identical to an individual cell verify. Mutually exclusive with the COORDINATE positional; a coordinate with no asserted cell (or a malformed line) is skipped and reported, never aborting the batch.
Examples:
lettuce cell verify area=auth;layer=api --evidence 'go test ./... -run TestAuthAPI' --project lettuce --author agent-1 --format json
lettuce cell verify --coords-file ./coords.txt --evidence CI#1234 --project lettuce --author agent-1 --format json
Notes:
- The cell must already be asserted (cell set/transition). Depth = count of DISTINCT confirmation revisions of the current grade (verify + affirm reported separately, never blended); an unconfirmed cell reads depth 0. depth_verify counts ONLY evidence-backed verifies (--evidence) whose ref is DISTINCT for that cell — the DoD depth floor is satisfied by proven re-exercise, never a bare stamp and never one proof cited N times. Both mutations still return ok:true when a repeat is demoted, so read depth_verify back (query
from cells select depth_verify, or board export) rather than inferring it from the call succeeding. --coords-file bulk-confirms a coordinate list (skipping unasserted/malformed lines) with one shared --evidence; the batch is semantically identical to N individual verifies but O(n) not O(n^2). A single coordinate is available through the local or hosted backend; --coords-file remains store-local.
cell affirm
Usage: lettuce cell affirm COORDINATE [--reason TEXT] --project PROJECT | lettuce cell affirm --coords-file PATH [--reason TEXT] --project PROJECT
Append an AFFIRM confirmation to a cell's FRESH-2 confirmation ledger — a RESTATEMENT that the cell's current grade still holds (an agentic re-examination, the weaker signal vs verify), recorded with the store revision at confirmation time so the cell's distinct-rev hardening DEPTH deepens. Never changes the grade or the hardened ratio; depth is derived (never a settable counter). Confirming twice at the same store revision buys no depth (the distinct-rev anti-farming rule); a grade change resets depth to the new grade's confirmations only. --coords-file confirms MANY cells in one call (bulk), computing the store subject revision ONCE for the whole batch — the O(n^2)->O(n) fix for a large hardening pass, byte-identical to N individual affirms.
- kind:
mutation - output:
collection— --format plain prints one cell reference (coordinate) per row ofconfirmed, or the one cell it acted on - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--reason text- Optional justification recorded on the cell-confirm event (auditable; a rubber-stamp confirmation stays attributable).--coords-file path- Bulk mode: confirm every newline-separated coordinate in this file in ONE call (blank lines and #-comments ignored). Computes the subject revision ONCE for the whole batch (O(n^2)->O(n)); each cell's confirmation is byte-identical to an individual cell affirm. Mutually exclusive with the COORDINATE positional; a coordinate with no asserted cell (or a malformed line) is skipped and reported, never aborting the batch.
Examples:
lettuce cell affirm area=auth;layer=api --reason 're-reviewed against head' --project lettuce --author agent-1 --format json
lettuce cell affirm --coords-file ./coords.txt --project lettuce --author agent-1 --format json
Notes:
- The cell must already be asserted (cell set/transition). Depth = count of DISTINCT confirmation revisions of the current grade (verify + affirm reported separately, never blended); an unconfirmed cell reads depth 0. --coords-file bulk-confirms a coordinate list (skipping unasserted/malformed lines); the batch is semantically identical to N individual affirms but O(n) not O(n^2). A single coordinate is available through the local or hosted backend; --coords-file remains store-local.
board export
Usage: lettuce board export --project PROJECT [--source-rev REV] [--wait[=DURATION] | --no-wait] [--include-archived]
Project a project's coverage board into a stable, versioned BoardExport v0.1 JSON DATA CONTRACT (schema_version, generated_at, source_rev, pack dimensions + state legend, stored cells with evidence, per-axis coverage rollups, blended headline ratio, milestones, and tickets) for a dashboard renderer. Read-only projection: emits the document as the machine envelope data on stdout (pipe it or read .data), never writes a file or mutates the store; visuals live outside the contract (data separate from visual). Distinct from export (a whole-store backup bundle).
- kind:
read - output:
report— --format plain prints the table bytes - exit codes:
0success;8hosted result not ready yet (FW-API-HEALTH-PENDING / FW-API-READ-PENDING): retry later or --wait; not a finding; any failure:1-7by diagnostic class (spec §24.11) - read cost:
project— walks the whole project: on a hosted server a background build (--wait[=DURATION] / --no-wait; exit 8 while it is pending) - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--source-rev rev- Optional provenance revision (repo commit short-SHA or store revision) stamped into source_rev. generated_at is NOT a wall-clock stamp: with none supplied it defaults from the STORE — the instant of the latest event — so repeated exports of an unchanged store are byte-identical and it advances only when the store does. The whole projection, provenance included, is reproducible.--wait- Hosted stores only (LET-1846): written --wait or --wait=DURATION. How long to wait for a PENDING server-side build (FW-API-READ-PENDING), polling with the server's Retry-After; progress on stderr. The default already waits 3m; an explicit --wait also polls while the answer is STALE and its rebuild is running, until a current one is built or the budget ends. A spent budget exits 8. No effect locally.--no-wait- Hosted stores only (LET-1846): do not wait for a pending server-side build; exit 8 (FW-API-READ-PENDING, try again later) at once. A current or stale result is still printed. No effect locally.--include-archived- Read an ARCHIVED project on purpose: without this flag a read scoped to an archived project is refused (FW-READ-PROJECT-ARCHIVED); with it the read succeeds and is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning, and project_archived_at in the json/yaml data).
Examples:
lettuce board export --project lettuce --format json
lettuce board export --project lettuce --source-rev a1b2c3d --format json
Notes:
- BoardExport is a lossy presentation-oriented projection of the coverage board only (cells + rollups + milestones + tickets), NOT the store-bundle export (lettuce export --bundle), which is a faithful full backup. The two do not share a schema. See docs/specs/board-export-v0.1.md.
- Per-axis rollups are keyed by the real coordinate axis name (e.g. by_axis.dim, by_axis.command); the blended headline is Sigma-hardened / Sigma-non-excluded over every stored cell. cells[].reason is reserved but empty in this release (GAP-2: cells carry no first-class reason field). REST parity is deferred (CLI-first).
- Hosted (LET-1846): the server builds the board in the BACKGROUND (single-flight per domain, project and options, detached from the request, cached per writer generation, rebuilt after writes) and never inline, so a proxy timeout cannot cancel it. You get the board current at this generation, the LAST built board marked STALE while it rebuilds (a slightly old board still says what to work on), or 503 FW-API-READ-PENDING when none was built yet. The client waits for a pending build by default (polling with Retry-After, progress on stderr, up to --wait=DURATION, default 3m); --no-wait, or a spent budget, exits 8 (try again later, never 1). Provenance: one
board:line on stderr in human formats (fresh | current | STALE + age); meta.read_kind, read_source, generation, computed_at, age_seconds, stale, current_generation, read_refresh in machine formats. An explicit --wait also waits out a stale board's rebuild. - On an ARCHIVED project this read is refused with FW-READ-PROJECT-ARCHIVED (exit 1) unless --include-archived is passed; with it the result is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning and project_archived_at in the json/yaml data).
board next
Usage: lettuce board next --project PROJECT [--depth 0|1|2] [--scope SLUG] [--wait[=DURATION] | --no-wait] [--include-archived]
Agent-facing coverage ORIENTATION report — "what should I harden next?" — distilled from the same board board export produces, so an agent picks its next target WITHOUT exporting and re-parsing the whole board. Read-only: every scope's meter ranked WEAKEST-FIRST plus a single "start here" pointer (the globally weakest open cell). --depth deepens the detail (the operator's global-to-increasing-detail): 0 (default) meters + start-here; 1 adds every scope's shape-aware suggestions (a heatmap scope → its dimensions with H/G/U subcounts, a scalars scope → its scalar grades, a ladder scope → its milestones) weakest-first; 2 adds the weakest units + the concrete open (gap/untested) cell coordinates, capped with an honest +N-more pointer. --scope restricts the report to one scope. The default output is a SELF-GUIDING plain-text report: every summarised node prints the exact lettuce ... command to descend, so the agent is never stuck; --format json emits the same data structured (scopes[] each with meter + suggestions[] + coordinates[] + a more drill string), depth-trimmed to match.
- kind:
read - output:
report— --format plain prints the table bytes - exit codes:
0success;8hosted result not ready yet (FW-API-HEALTH-PENDING / FW-API-READ-PENDING): retry later or --wait; not a finding; any failure:1-7by diagnostic class (spec §24.11) - read cost:
project— walks the whole project: on a hosted server a background build (--wait[=DURATION] / --no-wait; exit 8 while it is pending) - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--depth 0|1|2- How much detail to surface: 0 (default) per-scope meters + start-here; 1 adds every scope's dimension/milestone subcounts weakest-first; 2 adds the weakest units + concrete open coordinates. Never hides a whole scope — only how deep each goes.--scope slug- Restrict the report to one scope member (e.g. s1); a slug that names no scope on the board is refused with the available list.--wait- Hosted stores only (LET-1846): written --wait or --wait=DURATION. How long to wait for a PENDING server-side build (FW-API-READ-PENDING), polling with the server's Retry-After; progress on stderr. The default already waits 3m; an explicit --wait also polls while the answer is STALE and its rebuild is running, until a current one is built or the budget ends. A spent budget exits 8. No effect locally.--no-wait- Hosted stores only (LET-1846): do not wait for a pending server-side build; exit 8 (FW-API-READ-PENDING, try again later) at once. A current or stale result is still printed. No effect locally.--include-archived- Read an ARCHIVED project on purpose: without this flag a read scoped to an archived project is refused (FW-READ-PROJECT-ARCHIVED); with it the read succeeds and is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning, and project_archived_at in the json/yaml data).
Examples:
lettuce board next --project lettuce --format json
lettuce board next --project lettuce --depth 2 --format json
Notes:
- A pure READ over the same BoardExport as
board export/board render: it never mutates the store and honours the identical project-scoping contract — a PROJECT CONTEXT is REQUIRED — resolved from --project, else LETTUCE_PROJECT, else configproject:— and a MISSING context is refused FW-CMD-MISSING-PROJECT-CONTEXT while an INVALID name is refused FW-NAME-PROJECT (LET-264 split them; an EMPTY value counts as missing, not malformed). The diagnostic names CONTEXT, not the flag, for exactly this reason and it reads ONLY the named project's board, never a default or a cross-project scan. Frontier = the not-done, not-excluded, not-smoke cells (gap + untested), derived from the pack's state vocabulary, never a hardcoded slug. Same honesty invariants as the board: never invents a cell, never blends across scopes, an empty denominator reads n/a (in JSON a PRESENThardened_ratio: null/ suggestionratio: nullwith itstestable: 0— the same encoding board export and cell rollup publish, never an omitted key; LET-1509), and a capped list always announces its full count + the drill command (no silent truncation). An empty frontier is only complete when cells exist: the JSON always carries total_cells, and a project with ZERO stored cells reads UNSCOPED ('no cells defined yet'), never 'every cell is hardened or excluded' — with or without a declared DoD (LET-465). Under a DoD the '✓ DoD met' line prints only when the verdict IS met: a project with no DoD-applicable cell (zero cells, or every cell excluded) lists '[✕] coverage — no DoD-applicable cell' as its blocker instead. With a declared grid, an off-grid cell (unit or dim not declared) never counts toward its scope (LET-531). REST parity is deferred (CLI-first), mirroring board export/render. - Hosted (LET-1846): the server builds the board in the BACKGROUND (single-flight per domain, project and options, detached from the request, cached per writer generation, rebuilt after writes) and never inline, so a proxy timeout cannot cancel it. You get the board current at this generation, the LAST built board marked STALE while it rebuilds (a slightly old board still says what to work on), or 503 FW-API-READ-PENDING when none was built yet. The client waits for a pending build by default (polling with Retry-After, progress on stderr, up to --wait=DURATION, default 3m); --no-wait, or a spent budget, exits 8 (try again later, never 1). Provenance: one
board:line on stderr in human formats (fresh | current | STALE + age); meta.read_kind, read_source, generation, computed_at, age_seconds, stale, current_generation, read_refresh in machine formats. An explicit --wait also waits out a stale board's rebuild. - On an ARCHIVED project this read is refused with FW-READ-PROJECT-ARCHIVED (exit 1) unless --include-archived is passed; with it the result is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning and project_archived_at in the json/yaml data).
board render
Usage: lettuce board render --project PROJECT [--theme SLUG] [--source-rev REV] [--wait[=DURATION] | --no-wait] [--include-archived]
Render a project's coverage board as a SELF-CONTAINED HTML page (zero-JS, except the one inline grc replay script a project with graph-run-cases carries) NATIVELY in Go from the same BoardExport board export produces — no external python, no JSON round-trip. Read-only projection: the raw HTML is written to stdout so a publisher pipes it to a file; a machine --format (json/yaml) wraps it in the envelope's data as a {theme, format, content} object. DATA ⟂ PRESENTATION: the data/structure is theme-independent and a --theme selects only the presentation (an unknown theme is refused with the available list). Reproducible: with no --source-rev the provenance defaults from the store (data revision + latest-event instant), never the wall clock. REST parity is deferred (CLI-first), mirroring board export/next.
- kind:
read - output:
content— --format plain prints the content, as table prints it - exit codes:
0success;8hosted result not ready yet (FW-API-HEALTH-PENDING / FW-API-READ-PENDING): retry later or --wait; not a finding; any failure:1-7by diagnostic class (spec §24.11) - read cost:
project— walks the whole project: on a hosted server a background build (--wait[=DURATION] / --no-wait; exit 8 while it is pending) - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--theme slug- Presentation theme selecting only the CSS/frame (data is theme-independent); defaults to the built-indefaulttheme. An unknown theme is refused with the available list.--source-rev rev- Optional provenance revision (repo commit short-SHA or store revision) stamped into the masthead; with none it defaults from the store (data revision + latest-event instant) for a wall-clock-free, reproducible render.--wait- Hosted stores only (LET-1846): written --wait or --wait=DURATION. How long to wait for a PENDING server-side build (FW-API-READ-PENDING), polling with the server's Retry-After; progress on stderr. The default already waits 3m; an explicit --wait also polls while the answer is STALE and its rebuild is running, until a current one is built or the budget ends. A spent budget exits 8. No effect locally.--no-wait- Hosted stores only (LET-1846): do not wait for a pending server-side build; exit 8 (FW-API-READ-PENDING, try again later) at once. A current or stale result is still printed. No effect locally.--include-archived- Read an ARCHIVED project on purpose: without this flag a read scoped to an archived project is refused (FW-READ-PROJECT-ARCHIVED); with it the read succeeds and is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning, and project_archived_at in the json/yaml data).
Examples:
lettuce board render --project lettuce
lettuce board render --project lettuce --theme default
lettuce board render --project lettuce --format json
Notes:
- A pure READ over the same BoardExport as
board export/board next: it never mutates the store and honours the identical project-scoping contract — a PROJECT CONTEXT is REQUIRED — resolved from --project, else LETTUCE_PROJECT, else configproject:— and a MISSING context is refused FW-CMD-MISSING-PROJECT-CONTEXT while an INVALID name is refused FW-NAME-PROJECT (LET-264 split them; an EMPTY value counts as missing, not malformed). The diagnostic names CONTEXT, not the flag, for exactly this reason and it reads ONLY the named project's board. The HTML is strictly self-contained: every byte is inline — no src=, no external http(s) reference the browser resolves (the only http(s) strings are the SVG xmlns and inert provenance URLs inside title= tooltips), and no network call of any kind (no cdn/fetch/XHR/WebSocket/dynamic import). It is pure-CSS with ONE authorized exception (operator 20974/21127): a project carrying graph-run-cases also emits the grc replay simulator as exactly one INLINE <script> (an IIFE) plus one inert <script type="application/json"> data island per grc. A project with no graph-run-cases emits no <script> at all. Only the html output format is supported for the raw body (an unsupported --format is refused); a global machine --format wraps the HTML in the envelope. TO PUBLISH: the raw HTML goes to STDOUT, so redirect it to a file —lettuce board render --project P > board.html— and open that file. A machine --format (json/yaml) does NOT write a viewable page: it returns the HTML escaped inside the envelope as data.content, for a programmatic consumer that will unwrap it. Use board next to decide what to harden, board export for the data contract, and board render only when you want the page. The page states what board next states: a zero-cell project renders an UNSCOPED coverage section (never a completion line — with a DoD the conclusion names the empty frame instead of 'all scopes at bar', LET-465), a nil headline reads n/a in both meter branches (LET-1509), and with a declared grid a scope panel counts and draws only IN-grid cells — the same k/n as cell rollup, dod show, board export and board next — announcing any off-grid cells withlettuce grid scope show SCOPEinstead of drawing them as grid rows (LET-531). - Hosted (LET-1846): the server builds the board in the BACKGROUND (single-flight per domain, project and options, detached from the request, cached per writer generation, rebuilt after writes) and never inline, so a proxy timeout cannot cancel it. You get the board current at this generation, the LAST built board marked STALE while it rebuilds (a slightly old board still says what to work on), or 503 FW-API-READ-PENDING when none was built yet. The client waits for a pending build by default (polling with Retry-After, progress on stderr, up to --wait=DURATION, default 3m); --no-wait, or a spent budget, exits 8 (try again later, never 1). Provenance: one
board:line on stderr in human formats (fresh | current | STALE + age); meta.read_kind, read_source, generation, computed_at, age_seconds, stale, current_generation, read_refresh in machine formats. An explicit --wait also waits out a stale board's rebuild. - On an ARCHIVED project this read is refused with FW-READ-PROJECT-ARCHIVED (exit 1) unless --include-archived is passed; with it the result is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning and project_archived_at in the json/yaml data).
cell reconcile
Usage: lettuce cell reconcile [--dry-run] --project PROJECT
Reconcile STALE-GREEN cells: scan the project's stored cells and, for each in a guard-bite-gated done state (e.g. hardened) whose gate NO LONGER passes because its cited evidence broke (the cited task was deleted), machine-fire the active pack's regress transition so the stored state stops silently reading green (conformance §106: a gate-fail must regress, no silent stay-green). Detects a phantom with the exact evidence resolution the honest rollup uses, so reconcile and rollup agree; a cell whose gate still passes is untouched, making a second run a no-op. --dry-run reports what WOULD regress and writes nothing. It also rebuilds the cell side from the tickets alone and reports DECLARATION/CELL DRIFT (a ticket declares a coordinate with no cell, or an unparseable/unreadable declaration; LET-966 Gate 3). EXIT 1 when it reports any such drift (LET-1618) so a script can gate on a ticket/cell disagreement without parsing JSON — mirroring reconcile and graph-run-case conform; healing a stale-green phantom is a successful mutation and does not raise the code.
- kind:
mutation - output:
collection— --format plain prints one cell reference (coordinate) per row ofregressed; nothing when empty; its report findings announced on stderr - exit codes:
0success;1declaration/cell drift was found (LET-1618); any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--dry-run- Report which stale-green cells WOULD regress and write nothing.
Examples:
lettuce cell reconcile --project lettuce --author agent-1 --format json
lettuce cell reconcile --dry-run --project lettuce --format json
Notes:
- Available through both local and hosted backends. In hosted mode,
--dry-runis an actorless GET read; a real reconciliation is an attributed POST mutation and requires--author. This project-wide command takes no coordinate and no--coords-file.
dimension list
Usage: lettuce dimension list --project PROJECT [--include-archived]
List a project's EFFECTIVE dimensions — the active convention pack's declared dimensions plus the project's additive runtime dimension layer (cells-v0.16 §4) — each tagged with its source (pack vs project). Read-only; the additive project layer never overrides pack vocabulary (decision A6).
- kind:
read - output:
collection— --format plain prints one dimension reference (slug) per row ofdimensions; nothing when empty - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
listing— proportional to the collection it reads once (paging or the task projection bound it) - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--include-archived- Read an ARCHIVED project on purpose: without this flag a read scoped to an archived project is refused (FW-READ-PROJECT-ARCHIVED); with it the read succeeds and is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning, and project_archived_at in the json/yaml data).
Examples:
lettuce dimension list --project lettuce --format json
lettuce dimension list --project lettuce --format plain
Notes:
- The effective dimension space is the active pack's dimensions (source=pack) plus any dimensions the project declared at runtime (source=project). Until a project declares runtime dimensions, every dimension is source=pack. REST parity: GET /v1/projects/{project}/dimensions.
- On an ARCHIVED project this read is refused with FW-READ-PROJECT-ARCHIVED (exit 1) unless --include-archived is passed; with it the result is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning and project_archived_at in the json/yaml data).
dimension show
Usage: lettuce dimension show SLUG --project PROJECT
Show ONE of a project's EFFECTIVE dimensions by slug — the resolved dimension (active convention pack ⊕ the project's additive runtime layer, cells-v0.16 §4/§8) with all its declared fields and its source (pack vs project). Read-only; an unknown slug is refused FW-CMD-USAGE.
- kind:
read - output:
object— --format plain prints its dimension reference (slug) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Examples:
lettuce dimension show A --project lettuce --format json
lettuce dimension show A --project lettuce --format plain
Notes:
- Resolves against the same effective space
dimension listenumerates: the active pack's dimensions (source=pack) first, then the project's runtime layer (source=project). A slug present in neither is refused FW-CMD-USAGE. REST parity: GET /v1/projects/{project}/dimensions/{slug}. - On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
dimension member list
Usage: lettuce dimension member list DIMENSION --project PROJECT
List the members of ONE effective dimension — the base pack's enumerated members (source=pack, in declared order) plus any members the project's runtime layer added (source=project). Read-only; an open dimension that enumerates no members yields an empty list, and an unknown dimension is refused FW-CMD-USAGE.
- kind:
read - output:
collection— --format plain prints one dimension-member reference (member) per row ofmembers; nothing when empty - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Examples:
lettuce dimension member list A --project lettuce --format json
lettuce dimension member list A --project lettuce --format plain
Notes:
memberis a sub-noun underdimension(likecell evidence). Members are the base pack's declared members for a closed dimension (source=pack) plus any the project added at runtime (source=project); an OPEN dimension may also list members — its observed vocabulary (INT-100) — which never restrict its coordinates (only a closed dimension constrains a coordinate's member). REST parity: GET /v1/projects/{project}/dimensions/{dimension}/members.- On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
dimension member add
Usage: lettuce dimension member add DIMENSION MEMBER [--name N] [--description D] [--rank R] [--attr K=V ...] --project PROJECT
Add a FIRST-CLASS member {slug,name,description,rank,attributes} to a project's runtime dimension (GA-1), recording a dimension-member-added event; the member's human name + order become visible everywhere the effective pack resolves (dimension member list, dimension show, board export axes[].members[]). Additive-only (decision A6): members can only be added to a PROJECT-declared dimension — a pack-declared dimension's members are fixed by the pack (refused FW-CMD-USAGE), and a member slug already enumerated by the dimension is refused (no duplicate). --name is the human label (empty renders as the slug); --rank orders members (rank 0 = unranked, sorts by slug); repeatable --attr declares first-class key=value data beyond name/description/rank (self-described-scopes Phase 1 — e.g. a scope member's shape/row_noun/stages), which the board export carries inline and a generic renderer reads from data.
- kind:
mutation - output:
object— --format plain prints its dimension-member reference (member) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--name name- Human label for the member; defaults to rendering as the slug when omitted.--description text- Optional one-line description of the member (shown on hover in a board).--rank int- Optional integer sort order (ascending; rank 0 = unranked, falls back to slug order).--attr key=value- Optional repeatable first-class attribute declaration (key: lowercase-letter start then [a-z0-9_-], max 64). Carried inline on the board export; Phase-1 scopes use shape/row_noun/stages. Repeatable.
Examples:
lettuce dimension declare unit --family delivery --applicability universal --project lettuce --author agent-1 --format json
lettuce dimension member add unit task-set-where --name 'task set-where' --description 'Bulk-set a field on all matching tasks' --rank 210 --project lettuce --author agent-1 --format json
Notes:
member addis a mutation under thedimension membersub-noun (likedimension declare). The dimension must be one the project DECLARED at runtime (source=project); adding to a pack dimension is refused FW-CMD-USAGE. The member slug is validated and must be unique within the dimension; every refusal happens BEFORE any write, so a refused add leaves NO partial member on disk. REST parity: POST /v1/projects/{project}/dimensions/{dimension}/members.
dimension member update
Usage: lettuce dimension member update DIMENSION MEMBER [--name N] [--description D] [--rank R] [--attr K=V ...] --project PROJECT
EDIT an existing first-class member's name/description/rank/attributes on a project's runtime dimension (Phase 0c), recording a dimension-member-updated event. A PARTIAL update touches only the fields you pass — updating just --rank leaves the name/description intact; a repeatable --attr sets/overwrites the named keys and leaves unmentioned attributes intact. The inverse-companion to dimension member add: add creates a member (refuses a duplicate), update edits one (refuses an ABSENT member, FW-PATH-NOT-FOUND). Additive-only (decision A6): only a member of a PROJECT-declared dimension is editable — a pack-declared dimension's members are fixed by the pack (refused FW-CMD-USAGE). At least one of --name/--description/--rank/--attr is required (FW-CMD-MISSING-ARGUMENT otherwise).
- kind:
mutation - output:
object— --format plain prints its dimension-member reference (member) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--name name- New human label for the member (empty or whitespace-only clears it — renders as the slug).--description text- New one-line description of the member.--rank int- New integer sort order (ascending; rank 0 = unranked, falls back to slug order).--attr key=value- Optional repeatable first-class attribute to set/overwrite (unmentioned keys stay intact). Carried inline on the board export; Phase-1 scopes use shape/row_noun/stages. Repeatable.
Examples:
lettuce dimension declare unit --family delivery --applicability universal --project lettuce --author agent-1 --format json
lettuce dimension member add unit task-set-where --project lettuce --author agent-1 --format json
lettuce dimension member update unit task-set-where --rank 150 --project lettuce --author agent-1 --format json
lettuce dimension member update unit task-set-where --name 'Task set-where' --description 'Bulk-set a field on matching tasks' --project lettuce --author agent-1 --format json
Notes:
member updateis a mutation under thedimension membersub-noun (likedimension member add). The dimension must be one the project DECLARED at runtime (source=project); editing a pack dimension's member is refused FW-CMD-USAGE. The member MUST already exist — updating an absent member is refused FW-PATH-NOT-FOUND. Only the provided scalars change; every refusal happens BEFORE any write, so a refused update leaves the member UNCHANGED on disk. REST parity: PATCH /v1/projects/{project}/dimensions/{dimension}/members/{member}.
dimension declare
Usage: lettuce dimension declare SLUG --family FAMILY --applicability universal|conditional [--closed --member M ...] [--name N] [--description D] [--id ID] [--member-kind K] --project PROJECT
Author a project's runtime dimension in its additive layer (cells-v0.16 §4/§8), recording a dimension-declared event; the declared dimension becomes visible everywhere the effective pack resolves (cell set/show/rollup, board, query). Additive-only (decision A6): the slug must be UNIQUE against BOTH the active pack's dimensions AND the project's existing runtime dims — a runtime dimension can never shadow a pack dimension. A --closed dimension enumerates its members (repeatable --member); an open dimension may also list members (its observed vocabulary, the same state dimension member add reaches — INT-100), which never restrict its coordinates.
- kind:
mutation - output:
object— --format plain prints its dimension reference (slug) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--family family- Family this dimension groups under; required, and must resolve to one of the active pack's declared families. Required.--applicability universal|conditional- Applicability class; required. universal = applies to every unit; conditional = applies only when a precondition holds. Required.--closed- Mark the dimension closed (its members are a fixed enumeration). Requires at least one --member.--member slug- A member (repeatable). Each must be a valid, distinct slug. On a --closed dimension the members are its fixed enumeration; on an open dimension they are its observed vocabulary and do not restrict coordinates. Repeatable.--name name- Human label for the dimension; defaults to the slug when omitted.--description text- Optional one-line description of what the dimension measures.--id id- Optional stable identifier for the dimension.--member-kind kind- Optional member value-kind (e.g. string or a typed entity reference).
Examples:
lettuce dimension declare deployment-readiness --family delivery --applicability conditional --project lettuce --author agent-1 --format json
Notes:
- The slug is validated (a valid single-path-component slug) and refused FW-CMD-USAGE if it collides with a pack dimension or a dimension the project already declared (additive-only, A6). --family must resolve to a pack-declared family; --applicability must be universal or conditional; a --closed dimension must enumerate at least one --member, and members must be distinct valid slugs (the same admission rule
dimension member addcrosses). Every refusal happens BEFORE any write, so a refused declare leaves NO partial dimension on disk. The dimension is born active. REST parity: POST /v1/projects/{project}/dimensions.
dimension rename
Usage: lettuce dimension rename OLD NEW --project PROJECT
Rename a project's RUNTIME dimension: move projects/PROJECT/dimensions/OLD to .../NEW preserving EVERYTHING inside (family, closed-ness, name, description, members and the dimension's own event ledger), RE-ADDRESS every cell whose coordinate names OLD (a coordinate IS the cell's identity, so each affected cell directory moves to its new coordinate hash carrying its whole aggregate — state, note, revision, its events ledger, and its evidence links with their asserted_at/asserted_rev provenance), rewrite every stored reference to the dimension and to the moved cells, and move every declared-grid column named after it (grid/scopes/<scope>/dims/OLD -> NEW, reported as grid_dims_renamed + grid_scopes; LET-1498). A whole-store structural migration like project rename/merge, applied atomically under the mutation lock. The lower-harm alternative to declaring a fresh dimension and recreating the cells, which would lose every cell event, both ledgers and all evidence provenance.
- kind:
mutation - output:
object— --format plain prints its dimension reference (new_dimension) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Examples:
lettuce dimension rename unit coverage-unit --project lettuce --author agent-1 --format json
Notes:
- OLD and NEW are POSITIONAL slug arguments (not --project). Only a dimension the project DECLARED is renameable — a pack-bundled dimension is compiled into the binary, so naming one is refused FW-PATH-NOT-FOUND. Refuses if NEW is already declared by the project or the active pack (a collision, FW-CMD-USAGE, additive-only A6) or is an invalid slug (FW-NAME-SLUG). REFUSES with FW-DIMENSION-RENAME-CONFLICT when the renamed coordinates cannot coexist: two cells collapsing onto one coordinate, a coordinate that would name one dimension twice, or one that would assert the reserved scope=unscoped bucket. REFUSES with FW-DIMENSION-RENAME-UNSAFE, writing nothing, for a reference it cannot rewrite: one folded into a CONTENT-ADDRESSED identity (a graph-run-case advance event or a carrier directory is named by the hash of its own payload), a stored saved-query FQL naming the dimension (the FQL cells source projects scope/unit/dim/group/kind as fixed columns, so no rewrite can re-point the query), a move off/onto a reserved coordinate axis the project's DECLARED grid or DoD keys off, or a grid column it cannot carry without moving a denominator (NEW is already a column of that scope, or stored cells file under the column as dim=OLD and would be stranded off-grid). Prose is never rewritten, so the dimension-declared event's reason slot keeps the slug the dimension was declared under — an append-only ledger records what happened. On any failure the store is left byte-unchanged (atomic). Verify after with lettuce validate --strict. REST parity: POST /v1/projects/{project}/dimensions/{old}/rename. PREREQUISITE for the example above: OLD (here, unit) must already be a project-declared dimension — lettuce dimension declare unit --family FAMILY --applicability universal|conditional --project lettuce --author agent-1 --format json.
dimension close
Usage: lettuce dimension close DIMENSION --project PROJECT
CLOSE a project's runtime dimension: flip its closed flag from false to true so the coordinate validator begins ENFORCING declare-before-use on that axis. From here on a cell set naming a member the dimension does not enumerate is refused instead of silently minting it — which is the whole point, since a coordinate is the cell's identity and a typo mints a NEW cell rather than editing the intended one. Enforcement and its refusal already existed; this is the transition into it that no command previously offered (declare is additive-only and refuses an existing slug, rename re-addresses, and member add/update never touch closed). CLOSED MEANS DECLARE-BEFORE-USE, NOT FROZEN: a new member can be added with dimension member add at any time and is usable immediately.
- kind:
mutation - output:
object— --format plain prints its dimension reference (dimension) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Examples:
lettuce dimension declare unit --family delivery --applicability universal --project lettuce --author agent-1 --format json
lettuce dimension member add unit task-set-where --project lettuce --author agent-1 --format json
lettuce dimension close unit --project lettuce --author agent-1 --format json
Notes:
- DIMENSION is a POSITIONAL slug argument. Only a PROJECT-declared dimension is closable; the bundled pack's dimensions carry uppercase slugs, which the slug grammar refuses (FW-NAME-SLUG) before the pack-declared check is ever reached. REFUSES, writing nothing, when: the dimension is already closed (FW-CMD-USAGE naming the current state — a second close is an error, not a silent no-op); it enumerates NO members (mirroring
declare --closed, which requires at least one); or — the gate that makes this safe to run against a live store — any member OBSERVED in a stored cell's coordinate on that axis is not declared. That last refusal NAMES the undeclared members in itsactualslot, because closing then would start refusing coordinates that are valid today. The coverage gate reads the CELLS, not the registry: a registry-only check confirms the declared members are declared and stays blind to the ones the store actually uses. The result echoes cells_scanned so the no-op claim is checkable rather than asserted. There is deliberately no re-open counterpart: closing is the transition coordinate-integrity work needs, and an un-close would need its own coverage story. REST parity: POST /v1/projects/{project}/dimensions/{dimension}/close.
Links to
- Command Reference
reference/command-reference
Backlinks
- Cells — the coverage model
concepts/concept-cell - Dimensions and members
concepts/concept-dimension - Scopes — the DoD/board partition
concepts/concept-scope - Running lettuce in autonomous agentic cycles
guides/guide-agentic-cycles - Agentic loop demo — one development cycle, step by step
guides/guide-agentic-loop-demo - Project setup playbook — prepare a project to leverage lettuce
guides/guide-project-setup-playbook - Command Reference
reference/command-reference - reference/index
reference/index
Core And Runtime — Commands
reference/cmd-core-and-runtime lettuce Core And Runtime commands — 18 entries — usage, skill, docs, docs list, docs show, docs export, okf, version, self-update, init, status, agents, work, validate, doctor, recover, cleanup, reconcile.
lettuce command group Core And Runtime — 18 commands. Generated from lettuce usage --format okf (always in sync with the binary).
Back to Command Reference.
Commands in this group
usageskilldocsdocs listdocs showdocs exportokfversionself-updateinitstatusagentsworkvalidatedoctorrecovercleanupreconcile
---
Initialize stores, inspect runtime health, validate data, recover locks, clean runtime state, and print usage.
usage
Usage: lettuce usage [command-prefix] [--format table|plain|json|yaml|markdown|okf] [--out DIR]
Print complete CLI usage documentation for humans and agents. --format okf --out DIR emits the OKF command-reference bundle regenerated by 'make docs'.
- kind:
local - output:
content— --format plain prints the content, as table prints it - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
false - requires project:
false - requires author:
false - stable snapshot:
false - idempotency key:
false
Flags:
--out dir- Destination directory for the OKF command-reference bundle (only with --format okf). A file lettuce usage generated there is regenerated; any other file of the same name is refused FW-PATH-EXISTS before anything is written (LET-1972).
Examples:
lettuce usage --format markdown
lettuce usage query saved --format json
lettuce usage --format okf
Notes:
- Does not require an initialized store.
- Add --out DIR with --format okf to write the command-reference bundle (this is what 'make docs' runs); without --out it streams the bundle as a single document to stdout.
skill
Usage: lettuce skill
Print the embedded SKILL.md agent guide (also served at / and /SKILL.md by serve).
- kind:
local - output:
content— --format plain prints the content, as table prints it - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
false - requires project:
false - requires author:
false - stable snapshot:
false - idempotency key:
false
Examples:
lettuce skill
lettuce skill --format json
Notes:
- Does not require an initialized store.
docs
Usage: lettuce docs
Print the embedded, build-fresh wiki as the self-contained HTML explorer (pipe it to a file and open it in a browser). Its verbs are separate commands: 'docs list' ENUMERATES every concept id, 'docs show CONCEPT' streams one concept, 'docs export --out DIR' writes the artifacts for an agent. Also served at /docs and /docs/flat.md by serve.
- kind:
local - output:
content— --format plain prints the content, as table prints it - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
false - requires project:
false - requires author:
false - stable snapshot:
false - idempotency key:
false
Examples:
lettuce docs
lettuce docs --format json
Notes:
- Does not require an initialized store. The wiki is regenerated by 'make docs' and embedded in the binary.
- --out belongs to 'docs export': typed on bare docs it is refused FW-CMD-USAGE with the export command to run instead (LET-1973).
docs list
Usage: lettuce docs list
ENUMERATE every concept id of the embedded wiki: one per line for humans, a {concepts,count} envelope for machines. docs list and docs show resolve against the SAME conceptIDs source, so the list can never advertise a concept show rejects (LET-1001: concept was an enumerable noun whose only positive discovery path was provoking a refusal and reading its expected field).
- kind:
local - output:
collection— --format plain prints one concept reference (the row itself) per row ofconcepts; nothing when empty - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
false - requires project:
false - requires author:
false - stable snapshot:
false - idempotency key:
false
Examples:
lettuce docs list
lettuce docs list --format json
Notes:
- Does not require an initialized store.
docs show
Usage: lettuce docs show CONCEPT
Stream one concept of the embedded wiki (frontmatter + body) as markdown. CONCEPT is the full bundle id (concepts/concept-task), its basename (concept-task) or a unique suffix; docs list enumerates them. An unknown CONCEPT is refused FW-REF-MISSING-CONCEPT, which lists the valid ids.
- kind:
local - output:
content— --format plain prints the content, as table prints it - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
false - requires project:
false - requires author:
false - stable snapshot:
false - idempotency key:
false
Examples:
lettuce docs show concept-task
lettuce docs show reference/cmd-tasks
Notes:
- Does not require an initialized store.
docs export
Usage: lettuce docs export --out DIR
Write the embedded wiki artifacts (wiki.html + wiki-flat.md) into DIR for an agent to read offline.
- kind:
local - output:
object— --format plain prints its docs-export reference (out) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
false - requires project:
false - requires author:
false - stable snapshot:
false - idempotency key:
false
Flags:
--out dir- The directory to write wiki.html and wiki-flat.md into, created if missing. An earlier docs export there is replaced; any other file of those names is refused FW-PATH-EXISTS before anything is written (LET-1972). Required.
Examples:
lettuce docs export --out /tmp/lettuce-wiki
Notes:
- Does not require an initialized store.
okf
Usage: lettuce okf <okf-command> [args...] | lettuce okf docs <okf-command> [flags...]
Mount the embeddable okf CLI as a subcommand: lettuce okf validate ./bundle behaves exactly like okf validate ./bundle. The mount forwards EVERY okf command, mutating ones included — e.g. validate, lint, render (--site), export, graph, search alongside init, apply, page, move; this is a PARTIAL example, so run lettuce okf help for the full command set. lettuce okf docs <okf-command> runs the okf command against lettuce's OWN embedded wiki bundle (docs/wiki, compiled into the binary) — e.g. lettuce okf docs render --site ./site writes a single self-contained navigable HTML site of lettuce's documentation with no files on disk and no network.
- kind:
local - output:
content— --format plain prints the content, as table prints it - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
false - requires project:
false - requires author:
false - stable snapshot:
false - idempotency key:
false
Examples:
lettuce okf version
lettuce okf docs validate
lettuce okf docs lint
lettuce okf help
Notes:
- Does not require an initialized store. Delegates to okf's embeddable pkg/okf facade; okf is vendor-neutral and fully offline.
lettuce okf docsappends lettuce's materialized embedded-wiki bundle root as the trailing positional and passes every other flag through verbatim. Point okf at an arbitrary bundle withlettuce okf validate ./bundleorlettuce okf render --site ./out ./bundle(a bundle path / output dir the examples census does not fabricate, so those forms are shown here rather than auto-executed).
version
Usage: lettuce version
Print the lettuce binary version.
- kind:
local - output:
report— --format plain prints the table bytes - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
false - requires project:
false - requires author:
false - stable snapshot:
false - idempotency key:
false
Examples:
lettuce version --format json
lettuce version --format plain
self-update
Usage: lettuce self-update [--check | --dry-run]
Install the lettuce client release the bound server runs, in place (LET-1932). The server serves the client binaries of its own build (GET /v1/client/{os}-{arch} and .sha256, any role), so a lettuce token is all it needs — no GitHub access. It downloads the binary for this platform, verifies its sha256 against the server's checksum, runs the new binary's version to prove it runs here and reports the server's release, then replaces this executable ATOMICALLY (a temporary file beside it, flushed, renamed over it). This is FW-CLIENT-VERSION-SKEW's and FW-CLIENT-TOO-OLD's first suggested action.
- kind:
server - output:
report— --format plain prints the table bytes - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
false - requires project:
false - requires author:
false - stable snapshot:
false - idempotency key:
false
Flags:
--check- Report only: this binary's version, the server's, whether the server serves a binary for this platform and whether this executable may be replaced here. Downloads and changes nothing.--dry-run- Download, verify the checksum and prove the new binary runs, then discard it: everything except the replace.
Examples:
lettuce self-update --check --format json
lettuce self-update --dry-run
lettuce self-update --format json
Notes:
- Refusals (nothing is replaced): FW-SELF-UPDATE-CHECKSUM-MISMATCH (the download does not match the server's checksum), FW-SELF-UPDATE-PATH-NOT-WRITABLE (the executable is not owned by you in a directory you can write, or this runs under sudo — self-update never escalates), FW-SELF-UPDATE-VERIFY-FAILED (the new binary did not run here or did not report the server's release), FW-CLIENT-BINARY-UNAVAILABLE (the server has no binary of its own release for this platform; fall back to SKILL.md §0.1). A crash between the write and the rename leaves the old binary in place; the next run removes the leftover temporary file (.lettuce-self-update-<pid>-*) of a process that is gone.
- Symlinks are resolved: the real file behind
lettuceon PATH is replaced. The server and bearer come from the usual client binding (pointer, --server-url, LETTUCE_BEARER_FILE); the domain does not change the binary. Status values: up-to-date, update-available (--check), would-update (--dry-run), updated. Needs a hosted store; locally it is refused FW-CMD-USAGE.
init
Usage: lettuce init [--author AUTHOR] [--bootstrap-project PROJECT] [--bootstrap-author] [--idempotent] [--yes] [--force] [--legacy-root PATH]
Create a store root skeleton and optionally create a root author and bootstrap project.
- kind:
mutation - output:
object— --format plain prints its store reference (root) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
false - idempotency key:
true - external input keys (these go INSIDE
data, not at the top level):bootstrap_project, bootstrap_author, idempotent - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--author author- Root author to create or use for bootstrap project.--bootstrap-project project- Create one project during initialization.--bootstrap-author- Create/link the bootstrap author when needed.--idempotent- Treat existing initialized state as success when valid.--yes- Confirm that --bootstrap-project may add an ADDITIONAL project to a store that already holds one (without it, init skips an absent bootstrap project there).--force- Alias of --yes: confirm an additional project.--legacy-root path- Store-root cutover guard (local only): the store root this --root replaces. The Helm chart passes persistence.storeRoot when initializing <domainsRoot>/default, and <domainsRoot>/default when initializing storeRoot with domains disabled (a rollback). init refuses with FW-DOMAIN-CUTOVER-PENDING, writing nothing, while PATH still holds an initialized store and --root does not, while both hold one, or when --root holds a whole store nested one level down.
Examples:
lettuce init --root .lettuce --author agent-1 --bootstrap-project lettuce --bootstrap-author --idempotent --format json
Notes:
- --bootstrap-project creates the project only while init creates the store (the store holds no project yet). On a store that already holds a project, an absent bootstrap project is SKIPPED: init writes nothing, exits 0 and warns FW-PROJECT-BOOTSTRAP-SKIPPED (LET-1878; the Helm init container runs this on every pod start, so a deleted or moved bootstrap project must never crash-loop it). Pass --yes (or --force) to add it there anyway; over HTTP send "confirm": true in the POST /v1/init body. The first project of a fresh store never needs it.
- --legacy-root is the guard that keeps a domains upgrade from bootstrapping a silent EMPTY default domain over a store that was not moved yet; the cutover procedure is docs/operations/PVC-CUTOVER-RUNBOOK.md §8.
- An --idempotent re-run that would write nothing (initialized store, the author present, and the bootstrap project present or skipped) takes no store lock and is not refused by a stranded writer (FW-RUNTIME-WRITER-ACTIVE), e.g. the lock a replaced pod left: the Helm init container runs exactly this on every pod start, and
serve's startup recovery is what reclaims that lock (LET-1826). The project-scoped consistency gate still applies; an init that would write stays fully gated.
status
Usage: lettuce status [--full]
Report store validity, runtime state, Git state, and capability flags — GRAMMAR + runtime/Git state only. It does NOT run the doctor probes (terminal-lease, workflow-policy, graph-run-case, completion-gate), so a clean status is not a clean probe verdict; run lettuce doctor --format json for those.
- kind:
read - output:
report— --format plain prints the table bytes - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
listing— proportional to the collection it reads once (paging or the task projection bound it) - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--full- List EVERY retained runtime operation record in runtime.operations (LET-2003). By default status lists the open (not released) records and the newest 20, and runtime.operations_summary counts the rest (retained, open, listed, truncated): released records are kept until the runtime cleanup removes them, and listing thousands of them made status time out on a hosted store.
Examples:
lettuce status --root . --format json
lettuce status --root . --format plain
lettuce status --full --format json
Notes:
statusis the CHEAP health check: it must not pay a whole-store scan, and the doctor probes (e.g. the terminal-lease sweep over every project) would. The operator ruling that reads "Doctor/status can show current issues" is therefore satisfied bydoctorfor these findings;lease list --task-terminalis the categorical form of the terminal-lease one. This boundary is deliberate (LET-1628), not an omission.data.connectionsays which store this invocation reached:{"backend":"local"}locally; in client mode alsoserver(endpoint, never a token),server_version,client_version, the EFFECTIVEdomainanddomain_source(flag|env|pointer|token-default),project(the resolved project name, omitted when none is bound),project_source(flag|env|pointer|store-url) andproject_exists(omitted when no project is bound or it could not be checked). A bound project that does not exist in the effective domain adds an FW-REF-MISSING-PROJECT WARNING naming the domain selectors — the silent wrong-domain case.data.capabilities.output_formatslists every value--formataccepts, anddata.capabilities.format_supportsays per format whether it is a machine envelope, which commands it is scoped to, and whether other commands refuse it (exclusive). Both are derived from the same catalog the--formatparser and the per-command format gates read, so an advertised format is always an accepted one (LET-331).
agents
Usage: lettuce agents --project PROJECT [--format table|plain|json|yaml] [--include-archived] [--wait[=DURATION] | --no-wait]
The actor roster: every identity (author) seen in the project with the last time they acted, what they last did (last event kind + target), their event count, and how many ACTIVE leases they hold. Read-only; folds the same per-object ledgers query timeline merges plus the active leases. Use it to avoid reusing an existing name and to see who is around.
- kind:
read - output:
collection— --format plain prints one author reference (name) per row ofagents; nothing when empty - exit codes:
0success;8hosted result not ready yet (FW-API-HEALTH-PENDING / FW-API-READ-PENDING): retry later or --wait; not a finding; any failure:1-7by diagnostic class (spec §24.11) - read cost:
project— walks the whole project: on a hosted server a background build (--wait[=DURATION] / --no-wait; exit 8 while it is pending) - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--include-archived- Read an ARCHIVED project on purpose: without this flag a read scoped to an archived project is refused (FW-READ-PROJECT-ARCHIVED); with it the read succeeds and is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning, and project_archived_at in the json/yaml data).--wait duration- Hosted only (LET-2003): this read is a server-owned background build; wait for a pending build up to DURATION (default 3m0s; a bare --wait also waits out a STALE answer's rebuild), printing progress on stderr. Exit 8 when the budget ends with the build still pending. Locally the flag is accepted and inert.--no-wait- Hosted only (LET-2003): do not wait for a pending build; exit 8 at once (the build keeps running). Locally inert.
Examples:
lettuce agents --project lettuce --format json
lettuce agents --project lettuce
Notes:
- On an ARCHIVED project this read is refused with FW-READ-PROJECT-ARCHIVED (exit 1) unless --include-archived is passed; with it the result is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning and project_archived_at in the json/yaml data).
work
Usage: lettuce work --project PROJECT [--format table|plain|json|yaml] [--include-archived]
The single global coordination view: what is IN FLIGHT (and who holds it), what NEEDS HELP (blocked / needs-human, or a stale EXPIRED lease that can be taken over), and what is AVAILABLE (no active lease). Read-only; composes the same task + lease reads task list and lease list expose, so it cannot disagree with them. Terminal tasks are excluded and archived tasks are hidden.
- kind:
read - output:
report— --format plain prints the table bytes - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
listing— proportional to the collection it reads once (paging or the task projection bound it) - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--include-archived- Read an ARCHIVED project on purpose: without this flag a read scoped to an archived project is refused (FW-READ-PROJECT-ARCHIVED); with it the read succeeds and is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning, and project_archived_at in the json/yaml data).
Examples:
lettuce work --project lettuce --format json
lettuce work --project lettuce
Notes:
- On an ARCHIVED project this read is refused with FW-READ-PROJECT-ARCHIVED (exit 1) unless --include-archived is passed; with it the result is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning and project_archived_at in the json/yaml data).
validate
Usage: lettuce validate [--scope store|project|task|runtime] [--task REF] [--strict|--loose] [--wait[=DURATION]] [--project PROJECT]
Validate canonical store or runtime state against the on-disk grammar. NOT the strictest available check, despite --strict: doctor runs this SAME validation plus a derived-divergence probe, so a store this reports clean can still be damaged (forged derived scalars, merge conflicts, ladder-overlay drift). Prefer doctor as a CI gate; use validate for a fast structural check.
- kind:
read - output:
report— --format plain prints the table bytes - exit codes:
0success;1the store is invalid: the report (ok:true) lists the findings;8hosted result not ready yet (FW-API-HEALTH-PENDING / FW-API-READ-PENDING): retry later or --wait; not a finding; any failure:1-7by diagnostic class (spec §24.11) - read cost:
store— walks the whole store: on a hosted server a background health job (--wait[=DURATION]; exit 8 while it is pending) - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--scope scope- Validation scope. Defaults to project when an explicit --project is given, else store. --scope store validates EVERY project even when --project is also set.--project project- Project context. With no --scope it implies --scope project (validate ONLY that project) — the form a project agent on a shared multi-tenant server should use. An explicit --scope always wins.--task task-ref- Task reference for task scope.--strict- Strictest user-facing GRAMMAR check — but NOT a superset of doctor (LET-1723): --strict is silent on derived-divergence drift (forged/hand-edited derived scalars, merge conflicts, ladder-overlay drift) AND on graph-run-case CONFORMANCE (LET-1823, LET-1212 checker contract) — whether a run-case's advance history matches a replay of its graph-def is doctor's (DoctorGraphRunCaseProbe) andgraph-run-case conform's judgment alone, never validate's. So it can exit 0 on a store doctor exits nonzero on with FW-STORE-DERIVED-DRIFT or a non-conforming run-case. Do not gate CI on validate --strict expecting a health verdict; run doctor for that.--loose- Inspection mode. Core filesystem integrity remains enforced.--wait- Hosted stores only (LET-1835 phase 2): written --wait or --wait=DURATION (default 30m). Poll a pending (FW-API-HEALTH-PENDING) or stale-with-refresh-running report with the server's Retry-After until a current one is ready or the budget ends; progress on stderr. Without it a pending report exits 8 at once. No effect locally.
Examples:
lettuce validate --scope store --strict --format json
lettuce validate --project lettuce --loose --format json
Notes:
- Hosted (ADR-0022, LET-1835): every scope except runtime is an admin-only, single-flight, server-owned background scan cached per writer generation, exactly like doctor: never run inline, the last report served marked stale while a refresh runs, FW-API-HEALTH-PENDING (exit 8) while none exists; the envelope carries meta.health_source (fresh|cached), meta.generation, meta.computed_at, meta.age_seconds and meta.stale. --scope runtime stays a cheap reader call. Readers use
lettuce doctor --summary.
doctor
Usage: lettuce doctor [--shallow | --summary] [--wait[=DURATION]] [--project PROJECT] [--format table|plain|json|yaml|markdown]
Run validation plus a read-only derived-divergence probe (detects forged derived scalars, merge conflicts, ladder-overlay drift, and graph-run-case conformance judged against the pre-enforcement landmark (LET-1823) — none of which grammar validation computes, by the LET-1212 checker contract) with LLM-friendly explanations and repair suggestions. --shallow skips the probe for performance. Against a hosted server (ADR-0022) the scan is a server-owned background job (admin-only, single-flight, cached per writer generation) that a request never runs inline: you get the report current at this generation, the last completed report marked stale while a refresh runs, or FW-API-HEALTH-PENDING (exit 8, try again later; --wait polls); --summary reads the server's last computed health instead and never scans.
- kind:
read - output:
report— --format plain prints the table bytes - exit codes:
0success;1findings: the store is not healthy (ok:true report);8hosted result not ready yet (FW-API-HEALTH-PENDING / FW-API-READ-PENDING): retry later or --wait; not a finding; any failure:1-7by diagnostic class (spec §24.11) - read cost:
store— walks the whole store: on a hosted server a background health job (--wait[=DURATION]; exit 8 while it is pending) - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--shallow- Skip the read-only derived-divergence probe and run grammar/structure validation only (faster; use when only the fast checks are needed).--project project- Scope the health report to ONE project: its grammar pass plus only the probe findings under projects/PROJECT, so a project agent is not handed repair suggestions for data it does not own. Omit it to diagnose the whole store (the repo-local owner's CI gate).--summary- Hosted stores only (LET-1835): print the server's LAST COMPUTED health for the domain (GET /v1/health/domain: status, counts by code, generation, age, whether each record is current) without starting a scan. Reader role. Always exits 0 on success: it reports recorded health and is never a gate; runlettuce doctor(admin) for a verdict.--wait- Hosted stores only (LET-1835 phase 2): written --wait or --wait=DURATION (default 30m). While the server answers FW-API-HEALTH-PENDING (no report yet) or serves a STALE report whose refresh is running, poll with the server's Retry-After until a current report is ready or the budget ends; progress goes to stderr. Without it a pending report exits 8 at once. No effect locally (the local scan is the wait).
Examples:
lettuce doctor --format json
lettuce doctor --project lettuce --format json
lettuce doctor --shallow --format markdown
lettuce doctor --summary --format json
Notes:
- Hosted (ADR-0022): GET /v1/doctor and GET /v1/validate (except --scope runtime) require the admin role; a project-scoped grant may scan only its project. Scans are server-owned background jobs (phase 2): a request waits at most a few seconds, never runs the scan inline, and a disconnect never cancels one. Concurrent requests for the same report join one scan, a repeat at the same writer generation is served from cache, and after a write the LAST report is served marked stale while a refresh runs. The envelope says which: meta.health_source (fresh|cached), meta.generation, meta.computed_at, meta.age_seconds, meta.stale (+ meta.current_generation and meta.health_refresh: queued|running|rate-limited|busy), meta.health_joined when it joined a running scan; human output prints one
health:line (STALE when stale). With no report yet the server answers 503 FW-API-HEALTH-PENDING with Retry-After and the job's progress: the CLI printsnot ready yetand exits 8 (not a finding);--waitpolls. A full scan queue answers 503 FW-API-HEALTH-SCAN-BUSY and a spent hourly scan budget 429 FW-API-HEALTH-SCAN-RATE-LIMITED, both with Retry-After (with a stale report to serve, the report is served instead). Cached health is never a gate. Hosted doctor skips the local-only git and runtime-skeleton families and lists them in not_applicable.
recover
Usage: lettuce recover [--abandon | --report-only]
Inspect runtime operations and recover safe interrupted mutations. Resolves a stuck active/failed recovery attempt so blocked mutations proceed. Use --abandon to force-clear an orphaned active operation, write lock, or in-progress idempotency claim whose owner cannot be proven dead (another host, no usable pid, or a pid owned by another user); a provably-alive idempotency claimant is never touched. Without --abandon recover completes or reclaims an idempotency claim only when its claimant is provably dead on this host (LET-592). NOT RECLAIMED BY DESIGN (LET-1226): an operation left in a spec §17.2 non-terminal WRITE PHASE (preparing, writing, validating, committing) by a provably dead owner is reported and deliberately left in place, because a writer that died mid-write may have left a partially-written canonical file and the phase marker is the only evidence that happened — clearing it would destroy exactly what an operator needs to investigate. recover emits FW-RUNTIME-WRITE-PHASE-STRANDED naming each such record (doctor reports the same); run validate --strict to check the objects it was writing. Once recover has SETTLED an interrupted create such a record names (rolled it back, or found it committed) in a store that validates, it keeps the record but marks it settled (settled_by, settled_at, settlement — LET-1966): the warning stops on recover and doctor, and the record never again proves a directory's create never committed. An interrupted create (a task, comment, saved-query, graph-def, registry-object, run, dimension, project state/gate/transition, carrier or graph-run-case directory whose commit marker never landed) is finished from its ledger or rolled back ONLY when the directory holds nothing beyond the create and the create is proven never committed (the dead create's operation record names that exact directory: task create, comment add, query saved create, graph-def create, registry create, run start, dimension declare, defaults state|gate|transition declare, carrier produce or graph-run-case open — a record naming only its parent or a sibling proves nothing — AND that record wrote THIS object: its operation id is the operation-id the object's own create recorded, which every create writes first, or the directory is empty; a stale record naming an object created again at the same path proves nothing, LET-2001); a directory the store's committed ledger records (committed history beyond its create, its commit marker, a create event committed by its marker or passed through by the owner's committed revision chain, a reply naming a comment) is KEPT and reported as FW-RUNTIME-RECOVERY-OBJECT-KEPT with the markers to restore, an event the committed chain passes through or whose own revision-after is present is never discarded or reverted, even below a lowered revision (b32, b37). Recover never deletes and then refuses (b37): every removal or lowering it makes is held in its undo journal (.runtime/recovery-undo) until the verdict, so a pass that ends with the store still invalid removes nothing and reports each undone removal as kept; a pass that died before its verdict is undone by the next. Use --report-only (LET-1533) to INSPECT without changing anything: three refusals (FW-RUNTIME-WRITER-ACTIVE, the interrupted-evidence advisory, and the lock advisory) tell an operator to "run lettuce recover to inspect", and until that flag existed there was no way to do so — a plain recover reaps released operation records, heals forward, quarantines foreign runtime directories and may reset the generation. --report-only takes no lock, writes nothing, and reports what a real recover WOULD reclaim, using the same released-state predicate the reaper uses so the preview cannot promise what the apply declines. It also lists, in warnings[], each orphaned idempotency claim (FW-RUNTIME-IDEMPOTENCY-ORPHANED, LET-1776), each active operation record missing from the open-operation index (FW-RUNTIME-OPEN-OPERATION-UNINDEXED, LET-1775) and each leftover recovery undo journal of a recover pass that died before its verdict, with the number of changes it holds (FW-RUNTIME-RECOVERY-UNDO-PENDING, LET-1993) — the same findings doctor reports — since recover acts on all three. It is also the affordable form: a normal recover pays a whole-store validation (61.4s of CPU on lettuce's own store) whose diagnostics only the heal passes consume, while the report reads the runtime scope alone. Mutually exclusive with --abandon, which forces a reclamation.
- kind:
maintenance - output:
report— --format plain prints the table bytes - exit codes:
0success;1the canonical store is still invalid after recovery;6the store still needs recovery (a present writer lock, an active operation or an unfinished recovery); any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
false - idempotency key:
false
Flags:
--abandon- Force-clear an orphaned active operation, write lock, or in-progress idempotency claim whose owner liveness is undeterminable.--report-only- Inspect without writing: report what recover WOULD reclaim (including orphaned idempotency claims, unindexed active operations and leftover recovery undo journals with their change counts, in warnings[]), taking no lock and changing nothing. Mutually exclusive with --abandon.
Examples:
lettuce recover --author operator --format json
lettuce recover --report-only --format json
lettuce recover --abandon --author operator --format json
Notes:
- May mutate runtime and recovery state.
cleanup
Usage: lettuce cleanup
Remove released runtime operation data and expired idempotency-key records without changing canonical files.
- kind:
maintenance - output:
report— --format plain prints the table bytes - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
false - idempotency key:
false
Examples:
lettuce cleanup --format json
reconcile
Usage: lettuce reconcile --project PROJECT [--dry-run|--apply]
Store-merge-healing: scan a project for derived state that diverged from its event log (the shape a blind git merge leaves) and, with --apply, heal the deterministically-recomputable Class-A divergences (cell revision/state/note, task revision/status, field-set and custom-field values, run status/timing, comment status, saved-query status, registry object fields, dimension active, graph-run-case revision/state, graph-def revision, and the archived-at markers of tasks, comments, artifacts and the project itself) under the write lock. It also reports (never rewrites) an artifact whose payload files no longer match the content-hash its own creation/replacement event recorded (LET-1263). Dry-run report is the default; --apply requires an author and mutates. Class-B conflicts — a genuine fork rather than a lagging scalar (e.g. a broken revision-chain, cell evidence/confirmation counts, a graph-run-case whose value/coordinate diverged, or an UNEXPLAINED task or registry scalar that neither the create-time fields record nor any field-set / registry-update event accounts for — LET-440, LET-1797) — are reported, never rewritten.
- kind:
maintenance - output:
report— --format plain prints the table bytes - exit codes:
0success;1unresolved Class-B conflicts remain (ok:true report); any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
false - idempotency key:
false
Flags:
--dry-run- Report divergences without writing (the default).--apply- Heal Class-A divergences under the write lock (requires --author).
Examples:
lettuce reconcile --project lettuce --format json
lettuce reconcile --project lettuce --apply --author agent-1 --format json
Notes:
- Distinct from
cell reconcile(which re-derives cell grades against the pack); this checks derived==f(events) coherence across cells, tasks, runs, comments, saved-queries, registry objects, dimensions, graph-run-cases, graph-defs, and every archive marker. Exit 1 when unresolved Class-B conflicts remain. Every conflict reports the divergedstored/expected; a revision-chain fork additionally namesforked_at_revision, thecolliding_eventsthat both consumed it (id, author, kind, at, revision pair) and asuggested_actionsrepair path — for a class that is deliberately never rewritten, the report is the entire deliverable. - An unexplained task scalar (LET-440: a phantom a blind merge kept, a hand-edited title) is reported with suggested actions that record the intended value with
task set/task unset/custom set/custom clear, which the mutation path deliberately does not block on this finding. Tasks created before the create-time fields record existed are EXEMPT and reported only as the countunrecorded_create_fields(human output: anexempt N task(s)line), never as conflicts. - An unexplained registry scalar (LET-1797: the registry analog of LET-440 — a phantom a blind merge kept, a hand-edited title/status/color/owner/due-at/rank/stage/confidence/value-kind/required) is reported the same way, with suggested actions naming
registry update KIND SLUG --FIELD VALUE. Registry objects created before the create-time fields record existed are EXEMPT and reported only as the countunrecorded_registry_create_fields(human output: anexempt N registry object(s)line), never as conflicts. - An artifact blob mismatch (LET-1263, MH-8's unbuilt third clause) is reported as a Class-B conflict with field
content-hash,storedthe digest of the live payload manifest (every file under files/, one<sha256> <path>line each in sorted path order) andexpectedthe digest its own creation/replacement event recorded — never rewritten, since there is no safe automatic repair for corrupted binary content. It covers every payload file (a changed, added, removed or renamed file all diverge); validate --strict still names the individual file whose checksums/ entry disagrees. Artifacts created before the content-hash record existed are EXEMPT and reported only as the countunrecorded_artifact_content_hashes(human output: anexempt N artifact(s)line), never as conflicts.
Links to
- Command Reference
reference/command-reference
Backlinks
- The store and the two data planes
concepts/concept-store - Command Reference
reference/command-reference - reference/index
reference/index
Coverage Grid — Commands
reference/cmd-coverage-grid lettuce Coverage Grid commands — 6 entries — grid scope add-unit, grid scope add-dim, grid scope remove-unit, grid scope remove-dim, grid scope show, grid show.
lettuce command group Coverage Grid — 6 commands. Generated from lettuce usage --format okf (always in sync with the binary).
Back to Command Reference.
Commands in this group
grid scope add-unitgrid scope add-dimgrid scope remove-unitgrid scope remove-dimgrid scope showgrid show
---
Declare a project's per-scope coverage GRID (implicit cells, cells-v0.16 §A2): the units (rows) and applicable dims (cols) whose cross-product is the honest coverage denominator. Declare the grid once and every un-worked coordinate counts as implicit-untested — record real progress instead of minting cells one by one.
grid scope add-unit
Usage: lettuce grid scope add-unit SCOPE UNIT... --project PROJECT
Declare unit (row) members on a scope's grid — the things being covered under that scope (cells-v0.16 §A2). Records a project-updated event; each member slug is validated (a valid single-path-component slug) and refused FW-CMD-USAGE otherwise, BEFORE any write. Idempotent: a member already declared is a no-op. Once units AND applicable dims are declared, the scope's honest denominator is |units|×|dims| and every un-worked coordinate reads implicit-untested in rollup/board/dod.
- kind:
mutation - output:
object— --format plain prints its grid-scope reference (scope) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Examples:
lettuce grid scope add-unit s1 validate recover reconcile --project lettuce --author agent-1 --format json
Notes:
- Members are additive marker declarations under projects/<p>/grid/scopes/<scope>/units/<member>; storage stays sparse (only asserted cells are stored) while the denominator counts the declared grid. A malformed slug is refused before any write, so a refused add leaves nothing on disk. REST parity: declared over HTTP via POST /v1/projects/<p>/grid/scopes/<scope>/units — CLI local and client mode land the same project-updated event (differential parity guarded).
grid scope add-dim
Usage: lettuce grid scope add-dim SCOPE DIM... --project PROJECT
Declare applicable dimension (col) members on a scope's grid — the coverage dimensions that apply to that scope (cells-v0.16 §A2). Records a project-updated event; each slug is validated and refused FW-CMD-USAGE otherwise, BEFORE any write. Idempotent. Together with the scope's units, the applicable dims define the declared applicable space |units|×|dims| that becomes the honest coverage denominator.
- kind:
mutation - output:
object— --format plain prints its grid-scope reference (scope) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Examples:
lettuce grid scope add-dim s1 t s --project lettuce --author agent-1 --format json
Notes:
- Marker declarations under projects/<p>/grid/scopes/<scope>/dims/<member>; a scope that declares units but no applicable dims (or vice-versa) has grid size 0 and never reads a vacuous ratio. A malformed slug is refused before any write. REST parity: declared over HTTP via POST /v1/projects/<p>/grid/scopes/<scope>/dims (differential parity guarded).
grid scope remove-unit
Usage: lettuce grid scope remove-unit SCOPE UNIT... --project PROJECT
UNDECLARE unit (row) members from a scope's grid — the inverse of add-unit (LET-1473). It matters because the declared grid IS the coverage denominator: before this verb a single mistyped member slug permanently inflated |units|×|dims| on every surface (cell rollup, board export/next, the DoD gate) with no supported repair, so a scope with 100% of its real work done read 4/6 = 66.7% forever. REFUSES with FW-CMD-USAGE when a member still holds stored cells in that scope, naming the COUNT ("recovry holds 2 cell(s)") — they would be orphaned, so reassign or clear them first. Excluded (N/A) cells block removal too, and are named apart ("recovry holds 2 cell(s), all 2 excluded (N/A)") with the unwind recipe: clear the member's cells (cell clear per coordinate, listed by query run; cell clear-where on a local store), then remove it — the hardened ratio DIPS between the two steps (the cleared coordinates read untested inside the grid) and recovers once the member is gone (LET-1599). A member with NO stored cell removes without a refusal, and that shrinks the denominator with no work done, so the ratio RISES (LET-1601: reported, not blocked — grid scope show lists such members as unworked_units/unworked_dims). Idempotent: removing an undeclared member is a no-op, mirroring add.
- kind:
mutation - output:
object— --format plain prints its grid-scope reference (scope) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Examples:
lettuce grid scope remove-unit s1 recovry --project lettuce --author agent-1 --format json
Notes:
- Removes the marker directory under projects/<p>/grid/scopes/<scope>/units/<member>; the denominator shrinks immediately and every ratio that consumed it is corrected. The refuse-when-in-use choice is NOT invented here — it mirrors
defaults state hide, which already refuses a state still holding cells and reports the count, so the product carries one answer rather than two. The guard is scoped to the member's OWN scope: an identically-named member under a different scope is a different grid row and never blocks. Event-first ordering, so a crash mid-batch heals forward. REST parity: DELETE /v1/projects/<p>/grid/scopes/<scope>/units with the member list in the body — the same route and body shape the POST uses (differential parity guarded).
grid scope remove-dim
Usage: lettuce grid scope remove-dim SCOPE DIM... --project PROJECT
UNDECLARE applicable dimension (col) members from a scope's grid — the inverse of add-dim (LET-1473). Same refusal (excluded cells named apart with the unwind recipe, LET-1599), same idempotence, same arithmetic consequence as remove-unit: the declared grid is the denominator, so an un-removable mistyped dim capped every ratio for that scope permanently, and removing a never-worked dim raises the ratio with no work done (LET-1601; see unworked_dims in grid scope show).
- kind:
mutation - output:
object— --format plain prints its grid-scope reference (scope) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Examples:
lettuce grid scope remove-dim s1 tt --project lettuce --author agent-1 --format json
Notes:
- Marker removal under projects/<p>/grid/scopes/<scope>/dims/<member>. REFUSED (FW-CMD-USAGE, with the count) while stored cells still name that dim in this scope. Removing the last dim leaves grid size 0, which reads as no declared grid rather than a vacuous ratio — the same state the scope had before any dim was declared. REST parity: DELETE /v1/projects/<p>/grid/scopes/<scope>/dims (differential parity guarded).
grid scope show
Usage: lettuce grid scope show SCOPE --project PROJECT
Show ONE scope's declared grid — its unit (row) members, applicable dim (col) members, the resulting size (|units|×|dims|), and the declared members that hold NO stored cell in this scope (unworked_units / unworked_dims, LET-1601: exactly the members remove-unit/remove-dim removes without a refusal, each removal deflating the denominator). Read-only; a scope that was never declared is REFUSED (FW-PATH-NOT-FOUND) — a declared scope always carries at least one unit or dim, so a member-less scope is a miss, not an empty grid (LET-1514).
- kind:
read - output:
object— --format plain prints its grid-scope reference (scope) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Examples:
lettuce grid scope show s1 --project lettuce --format json
Notes:
- A pure read over projects/<p>/grid/scopes/<scope>; size is |units|×|dims| — the scope's contribution to the honest coverage denominator. REST parity: read over HTTP via GET /v1/projects/<p>/grid (one scope filtered client-side).
- On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
grid show
Usage: lettuce grid show --project PROJECT [--include-archived]
Show a project's WHOLE declared grid — every scope with its units, applicable dims, size and unworked_units/unworked_dims (declared members holding no stored cell in that scope, LET-1601), plus the project denominator (Σ per-scope |units|×|dims|). Read-only; a project that declares no grid reads declared=false with denominator 0 (the coverage denominator then falls back to the observed stored-cell count — backward-compatible).
- kind:
read - output:
report— --format plain prints the table bytes - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
listing— proportional to the collection it reads once (paging or the task projection bound it) - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--include-archived- Read an ARCHIVED project on purpose: without this flag a read scoped to an archived project is refused (FW-READ-PROJECT-ARCHIVED); with it the read succeeds and is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning, and project_archived_at in the json/yaml data).
Examples:
lettuce grid show --project lettuce --format json
Notes:
- The denominator here is the same declared applicable space rollup/board/dod use once a grid is declared (cells-v0.16 §A2). No declared grid ⇒ the observed-cell denominator (pre-implicit-cells behaviour). REST parity: read over HTTP via GET /v1/projects/<p>/grid.
- On an ARCHIVED project this read is refused with FW-READ-PROJECT-ARCHIVED (exit 1) unless --include-archived is passed; with it the result is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning and project_archived_at in the json/yaml data).
Links to
- Command Reference
reference/command-reference
Backlinks
- Cells — the coverage model
concepts/concept-cell - Command Reference
reference/command-reference - reference/index
reference/index
Definition Of Done — Commands
reference/cmd-definition-of-done lettuce Definition Of Done commands — 11 entries — dod set, dod clear, dod show, defaults show, defaults state declare, defaults state set-default, defaults state hide, defaults transition declare, defaults transition hide, defaults gate declare, defaults reset.
lettuce command group Definition Of Done — 11 commands. Generated from lettuce usage --format okf (always in sync with the binary).
Back to Command Reference.
Commands in this group
dod setdod cleardod showdefaults showdefaults state declaredefaults state set-defaultdefaults state hidedefaults transition declaredefaults transition hidedefaults gate declaredefaults reset
---
Declare and inspect a project's tunable Definition of Done — a required grade floor plus optional depth/recency floors, with per-scope overrides, evaluated as a strict per-scope AND-gate (never a blended percentage).
dod set
Usage: lettuce dod set --project PROJECT [--grade STATE] [--depth N] [--recency fresh|aging] [--scope SCOPE]
Persist a project's Definition-of-Done knobs. Without --scope it sets the PROJECT DEFAULTS: --grade is the required grade floor (a state the active pack declares, e.g. hardened — the minimum grade a cell must reach to count as done); --depth is the optional minimum distinct-rev confirmation DEPTH (FRESH-2, >=1); --recency is the optional minimum freshness bucket (FRESH-3: fresh or aging — never stale). Freshness is cell-local (LET-1612): the number of the cell's OWN events (re-grades, transitions, notes, evidence removals) since its most-recent evidence anchor — fresh <= 3, aging <= 6, stale beyond; unrelated project activity and elapsed time never age a cell, and a presumed (unanchored) cell never satisfies a recency floor. With --scope it sets a PER-SCOPE OVERRIDE of any of those floors (an unset field inherits the project default). At least one knob is required; a first project-level declaration requires --grade. Records a project-updated event.
- kind:
mutation - output:
report— --format plain prints the table bytes - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--grade state- Required grade floor: the minimum pack state a cell must reach to count toward done (e.g. hardened). Validated against the active pack's states.--depth n- Optional minimum distinct-rev confirmation depth (FRESH-2; a positive integer). A cell below the floor is unmet.--recency fresh|aging- Optional minimum freshness bucket (FRESH-3): fresh (must be fresh) or aging (fresh or aging, never stale). A cell whose freshness is presumed (no revision anchor) is UNKNOWN — unmet, remedied by re-affirming.--scope scope- Set a per-scope override (e.g. s2) instead of the project defaults; an unset field inherits the project default.
Examples:
lettuce dod set --grade hardened --depth 2 --recency aging --project lettuce --author agent-1 --format json
lettuce dod set --scope s3 --depth 3 --project lettuce --author agent-1 --format json
Notes:
- The verdict is a strict per-scope AND-gate over applicable (non-excluded) cells — a scope is met only when EVERY cell meets the bar, reported as a k/n count, never a blended percentage (docs/specs/dod-freshness-metrics-datascience.md). Setting knobs NEVER moves the hardened ratio: a scope can read 100% hardened yet DoD unmet (stale/shallow). The board masthead/section visual is DOD-2; this command sets the DATA.
- This command sets ONLY the coverage floors (grade/depth/recency); the overall DoD verdict additionally layers two AUTO-DERIVED project-level outer gates you never set here — every committed milestone reached and zero non-terminal tickets — so a project can meet every coverage floor yet still read NOT DONE. Inspect them via dod show / board export (milestone_gate + ticket_gate).
dod clear
Usage: lettuce dod clear --project PROJECT [--scope SCOPE] [--depth] [--recency] [--grade]
Remove Definition-of-Done policy that dod set wrote. A BARE dod clear UNDECLARES the whole project DoD (removes every floor). --scope S removes scope S's override entirely. --depth/--recency (booleans here, optionally with --scope) clear just that floor. Clearing the PROJECT grade floor while the DoD stays declared is refused (a project DoD requires a grade) — undeclare the whole DoD instead. Records a project-updated event. Clearing a DoD/scope that is not declared returns FW-PATH-NOT-FOUND.
- kind:
mutation - output:
report— --format plain prints the table bytes - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--scope scope- Target a scope override (e.g. s2) instead of the project defaults; with no floor flags it removes that scope's whole override.--grade- Clear the grade floor (scope override only; refused at the project level — undeclare instead).--depth- Clear the depth floor at the target.--recency- Clear the recency floor at the target.
Examples:
lettuce dod clear --project lettuce --author agent-1 --format json
lettuce dod clear --scope s3 --project lettuce --author agent-1 --format json
lettuce dod clear --depth --project lettuce --author agent-1 --format json
Notes:
- The DoD mutation surface was previously set-only; this closes that gap so a floor or override can be removed through lettuce instead of by hand-editing the store. HTTP-client parity: DELETE /v1/projects/{project}/dod.
dod show
Usage: lettuce dod show --project PROJECT [--include-archived] [--wait[=DURATION] | --no-wait]
Show a project's Definition-of-Done policy (project-default grade/depth/recency floors + any per-scope overrides) AND the current verdict: per-scope met/unmet with a k/n met count and any unknown count, plus the project-level met_scopes/total_scopes (a strict AND-gate — done iff every scope is met or n/a, with at least one applicable cell). A scope with no applicable cell (every cell excluded) reads n/a, never met, and is counted in na_scopes (LET-689). Read-only.
- kind:
read - output:
report— --format plain prints the table bytes - exit codes:
0success;8hosted result not ready yet (FW-API-HEALTH-PENDING / FW-API-READ-PENDING): retry later or --wait; not a finding; any failure:1-7by diagnostic class (spec §24.11) - read cost:
project— walks the whole project: on a hosted server a background build (--wait[=DURATION] / --no-wait; exit 8 while it is pending) - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--include-archived- Read an ARCHIVED project on purpose: without this flag a read scoped to an archived project is refused (FW-READ-PROJECT-ARCHIVED); with it the read succeeds and is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning, and project_archived_at in the json/yaml data).--wait duration- Hosted only (LET-2003): this read is a server-owned background build; wait for a pending build up to DURATION (default 3m0s; a bare --wait also waits out a STALE answer's rebuild), printing progress on stderr. Exit 8 when the budget ends with the build still pending. Locally the flag is accepted and inert.--no-wait- Hosted only (LET-2003): do not wait for a pending build; exit 8 at once (the build keeps running). Locally inert.
Examples:
lettuce dod show --project lettuce --format json
lettuce dod show --project lettuce --format plain
Notes:
- A project that has declared no DoD (no grade floor) reports declared=false with no verdict — the board renders unchanged (DoD is opt-in). The verdict is the same strict-AND, per-scope, tri-state block
board exportcarries underdod; a cell whose recency cannot be observed (presumed freshness) is unknown⇒unmet, counted separately so the remedy is visible. - Beyond the per-scope coverage gate, the verdict layers two ADDITIVE project-level outer gates the JSON carries as coverage_met plus milestone_gate {met,reached,total} and ticket_gate {met,open,total}: overall DONE ⟺ coverage-met AND every committed milestone reached (hypothesis-stage + canceled milestones are excluded from the committed set) AND zero non-terminal tickets. An unmet outer gate flips the overall verdict to unmet without touching the per-scope coverage rollup.
- A present-but-unparseable depth/recency floor (e.g. a hand-edited
dod/depthof " 2") is never read as 'no floor': the verdict fails CLOSED on it (its cells block on that floor) and names it under invalid_floors; validate --strict flags the same raw scalar (LET-579). Every scope whose effective depth floor is invalid carries depth_invalid:true, and board next / board render name the broken floor and never draw a cell at that bar (LET-1779). - On an ARCHIVED project this read is refused with FW-READ-PROJECT-ARCHIVED (exit 1) unless --include-archived is passed; with it the result is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning and project_archived_at in the json/yaml data).
defaults show
Usage: lettuce defaults show --project PROJECT [--include-archived]
Show a project's EFFECTIVE ladder — the resolved states, transitions, and gates (the bundled convention ⊕ the project's tweak layer), each tagged source=default|project, plus any hidden states and hidden project transitions (hidden_transitions, LET-1926). Each bundled state carries its stored description — what the state asserts about a cell and what moves it on (LET-950). Read-only; the companion to the defaults tweak commands so an agent can inspect exactly what it changed without the (removed) pack show.
- kind:
read - output:
report— --format plain prints the table bytes - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--include-archived- Read an ARCHIVED project on purpose: without this flag a read scoped to an archived project is refused (FW-READ-PROJECT-ARCHIVED); with it the read succeeds and is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning, and project_archived_at in the json/yaml data).
Examples:
lettuce defaults show --project lettuce --format json
lettuce defaults show --project lettuce --format plain
Notes:
- A project that authors no tweak resolves byte-identically to the bundled default (every element source=default). CLI-only in this release (no REST route yet).
- On an ARCHIVED project this read is refused with FW-READ-PROJECT-ARCHIVED (exit 1) unless --include-archived is passed; with it the result is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning and project_archived_at in the json/yaml data).
defaults state declare
Usage: lettuce defaults state declare SLUG --category open|in-progress|review|flagged --letter L [--rank N] [--name NAME] --project PROJECT
TWEAK the bundled convention's ladder: author an additive project STATE ('defaults not packs'). A project no longer authors a whole pack — it tweaks the default here. Additive-only: the slug must not already exist in the effective ladder. G3: only a working category (open/in-progress/review/flagged) — done/excluded are pack-structural and refused. Validate-on-write: a declare that would break the ladder's honesty invariants is refused with nothing written.
- kind:
mutation - output:
report— --format plain prints the table bytes - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--category open|in-progress|review|flagged- Required working category for the new state (done/excluded are refused — they are pack-structural). Required.--letter L- Required short grade glyph for the state (e.g. T). Required.--rank int- Optional ladder rung (0 = unranked, sorts by slug).--name name- Human label; defaults to the slug.
Examples:
lettuce defaults state declare triaging --category in-progress --letter T --rank 15 --project lettuce --author agent-1 --format json
Notes:
- The effective ladder is the bundled default's states plus this additive project layer; a project that authors no tweak resolves byte-identically to the default. Every refusal happens BEFORE any write. REST parity: POST /v1/projects/{project}/states.
defaults state set-default
Usage: lettuce defaults state set-default SLUG --project PROJECT
Move the grading-at-entry DEFAULT onto a named state (a base or a project-declared state). Refused if the slug is not in the effective ladder, or if the move would leave the ladder without exactly one default (validate-on-write). A re-settable pointer.
- kind:
mutation - output:
report— --format plain prints the table bytes - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Examples:
lettuce defaults state set-default smoke --project lettuce --author agent-1 --format json
Notes:
- Records projects/<p>/ladder/default-state; the default flag moves everywhere the effective pack resolves. REST parity: PUT /v1/projects/{project}/ladder/default-state.
defaults state hide
Usage: lettuce defaults state hide SLUG --project PROJECT
HIDE a BASE-pack state (and its workflow edges) from the project's effective ladder — the subtractive tweak. Only a base state is hideable (a project-declared state is removed by deleting it). Refused if hiding would leave no default or no done state, if it would leave the done state with no transition into it (LET-1926), or if the state still holds CELLS (they would be orphaned). Hiding cascade-drops the transitions into/out of the state.
- kind:
mutation - output:
report— --format plain prints the table bytes - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Examples:
lettuce defaults state hide blocked --project lettuce --author agent-1 --format json
Notes:
- Records a marker under projects/<p>/ladder/hidden/<slug>; ResolveEffectivePack drops the state + its edges. REST parity: PUT /v1/projects/{project}/ladder/hidden/{slug}.
defaults transition declare
Usage: lettuce defaults transition declare ACTION --from STATE --to STATE [--gate GATE] [--machine] --project PROJECT
Author an additive project TRANSITION (a workflow edge). The (action, from) pair is its identity; additive-only, create-once. Validate-on-write: --from/--to must resolve to declared states and --gate to a declared gate, else refused with nothing written. --gate is REQUIRED when --to is the done-category state (hardened): only a gate promotes to done (spec cells-v0.16.md §5 invariant 5), and an ungated entry would make cell set --state hardened unguarded for the whole project.
- kind:
mutation - output:
report— --format plain prints the table bytes - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--from state- Required source state slug (must resolve in the effective ladder). Required.--to state- Required target state slug (must resolve in the effective ladder). Required.--gate gate- Guard gate slug (must resolve to a declared gate). Optional in general, REQUIRED when --to is the done state.--machine- Mark the transition machine-applicable.
Examples:
lettuce defaults transition declare expedite --from untested --to gap --project lettuce --author agent-1 --format json
lettuce defaults transition declare fast-harden --from smoke --to hardened --gate guard-bite --project lettuce --author agent-1 --format json
Notes:
- REST parity: POST /v1/projects/{project}/transitions.
defaults transition hide
Usage: lettuce defaults transition hide ACTION --from STATE --project PROJECT
Retire ONE project-declared transition with a RECORDED superseding marker (LET-1926) — the targeted repair of a bad ladder entry, e.g. an ungated edge into the done state (FW-LADDER-UNGATED-DONE-ENTRY). A ladder entry is create-once and is never edited or erased: the hide writes projects/<p>/ladder/hidden-transitions/<action>/<from> with a project-transition-hidden event, and the effective ladder (defaults show, the cell gates, validate) drops that edge. Only a transition THIS project declared can be hidden (a bundled edge leaves with its state, defaults state hide, or with the whole tweak layer, defaults reset --only ladder). Refused with nothing written for an unknown or already-hidden (action, from), and when the hide would leave a done-category state with no transition into it (it would then be reachable only by an ungated direct assertion).
- kind:
mutation - output:
report— --format plain prints the table bytes - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--from state- Required from-state of the transition to hide (its (action, from) is the identity). Required.
Examples:
lettuce defaults transition hide fast-harden --from untested --project lettuce --author agent-1 --format json
Notes:
- The hidden transition stays on disk and is listed under hidden_transitions by defaults show; its (action, from) cannot be declared again (use a new action name, or defaults reset --only ladder, which clears the markers with the layer). REST parity: PUT /v1/projects/{project}/ladder/hidden-transitions/{action}/{from}.
defaults gate declare
Usage: lettuce defaults gate declare SLUG --check-kind KIND [--description D] --project PROJECT
Author an additive project GATE (a transition guard) a transition may then reference. Additive-only, create-once. Validate-on-write: --check-kind must be a shipped evaluator (e.g. guard-bite, consistency), else refused with nothing written.
- kind:
mutation - output:
report— --format plain prints the table bytes - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--check-kind kind- Required gate evaluator (a shipped check-kind, e.g. guard-bite or consistency). Required.--description text- Optional one-line description of what the gate checks.
Examples:
lettuce defaults gate declare peer-review --check-kind consistency --description 'two approvals' --project lettuce --author agent-1 --format json
Notes:
- REST parity: POST /v1/projects/{project}/gates.
defaults reset
Usage: lettuce defaults reset [--only ladder|dod|dimensions] --project PROJECT
Clear a project's tweak layer back to the bundled convention. --only ladder (the default) removes the states/transitions/gates/set-default/hidden overlay; --only dod removes the Definition-of-Done policy (the project reverts to UNDECLARED); --only dimensions removes the project's runtime dimension layer. Per-scope orphan guards refuse a reset that would strand cells: the ladder reset is refused if a cell holds a project-declared state it would remove, and the dimensions reset if a cell coordinate names a project-declared dimension it would remove (reassign or clear those cells first); the dod reset carries no cell-orphan concern. Like the declare/tweak writers, the reset VALIDATES what it leaves (LET-926): the prospective effective pack must not gain a pack.Validate fault, and a ladder reset is refused while the DoD names a project-declared state as a grade floor (project default or a --scope override) — that floor would name a state that no longer exists and every verdict would read unmet; move the floor with dod set --grade or reset --only dod first. Nothing is written on a refusal. A no-op-clean reset (nothing to clear) still succeeds.
- kind:
mutation - output:
report— --format plain prints the table bytes - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--only ladder|dod|dimensions- Which tweak layer to clear (default: ladder). dod reverts the project's Definition-of-Done to undeclared; dimensions removes the project's declared runtime dimensions.
Examples:
lettuce defaults reset --only ladder --project lettuce --author agent-1 --format json
lettuce defaults reset --only dod --project lettuce --author agent-1 --format json
lettuce defaults reset --only dimensions --project lettuce --author agent-1 --format json
Notes:
- Deleting the tweak subtrees is recorded as a project-defaults-reset lifecycle event scoped to the cleared layer. CLI-only in this release (no REST route yet).
Links to
- Command Reference
reference/command-reference
Backlinks
- Milestones and Definition of Done
concepts/concept-milestone-dod - The shipped convention — coverage as data
concepts/concept-pack - Project setup playbook — prepare a project to leverage lettuce
guides/guide-project-setup-playbook - Command Reference
reference/command-reference - reference/index
reference/index
Graph Authoring — Commands
reference/cmd-graph-authoring lettuce Graph Authoring commands — 10 entries — graph-def create, graph-def revise, graph-def show, graph-def list, graph-def compile, graph-def lint, graph-def viz, graph-def catalog list, graph-def catalog show, graph-def use.
lettuce command group Graph Authoring — 10 commands. Generated from lettuce usage --format okf (always in sync with the binary).
Back to Command Reference.
Commands in this group
graph-def creategraph-def revisegraph-def showgraph-def listgraph-def compilegraph-def lintgraph-def vizgraph-def catalog listgraph-def catalog showgraph-def use
---
Author graph-defs — the first-class, named, versioned authoring object a run-case enacts (mission-graph-engineering SCH-3a/3b/4). A graph-def is DATA at projects/<p>/graph-defs/<slug>/: nodes (typed by concern) wired by edges, plus a start node; its spec body is stored append-only-versioned like a task body. CREATE folds in the CORE soundness gate — an unsound spec (dangling edge, unreachable node, missing start, uncapped cycle) is refused FW-GRAPH-DEF-UNSOUND and never stored, so every stored graph-def is sound by construction, and pins the compiled effective-hash (F30). A node may also be a SUBGRAPH that inlines another def by name (uses: <slug>@<version>) and binds its open ports (bind) — COMPOSITION (SCH-4): COMPILE resolves the transitive uses closure, inlines every referenced def, and hashes the WHOLE expansion, so editing a used pattern moves every parent's effective-hash; a composition cycle is refused FW-GRAPH-DEF-CYCLE and a missing used def FW-PATH-NOT-FOUND. LINT reports advisory design smells (warnings, never fatal). STRUCTURED PARALLELISM (fork/join/quorum, SCH-2b) and LOOP ITERATION primitives (min_times/until_stable/until_drain/circuit_breaker on a capped back-edge, SCH-2b-2) are authored, validated, and hashed here; their RUNTIME enactment is live in graph-run-case — fork opens concurrent branches, a K-of-M join fires only once quorum arrives (else FW-GRAPH-JOIN-UNSATISFIED), and a loop back-edge exits only when its oracle is satisfied (ENACT-1/2/3). ROUTER GUARDS (ENACT-5) are authored here too: a router's out-edge MAY carry a when condition over recorded run-case facts, refused FW-GRAPH-DEF-UNSOUND (invalid-guard) if malformed / off a router / leaving two default edges, and folded into the effective-hash — its RUNTIME enactment is live in graph-run-case (FW-GRAPH-ROUTE-UNSATISFIED / -AMBIGUOUS / -NO-MATCH). The remaining schema surface (carrier-types) remains deferred. Hosted mode supports show, list and viz; authoring, compile, lint, catalog and use remain local.
graph-def create
Usage: lettuce graph-def create SLUG --spec-file PATH --project PROJECT --author AUTHOR
Author a graph-def from a JSON spec file. Reads and parses the spec, runs the CORE soundness gate (every edge references a declared node; no unreachable node; a declared start node; every loop back-edge capped), and — only if sound — stores it as a versioned object with provenance (author/created-at/created-by) plus the compiled effective-hash pin. A node may be a SUBGRAPH (uses: <slug>@<version>) that inlines another def and binds its open ports (bind); an EXPLICIT <slug>@N reference need not exist yet at authoring time (a forward or deliberately-cyclic reference pins the hash provisionally and is enforced at compile). A FLOATING reference (<slug>@latest or an omitted version) is RESOLVED and PINNED to the current highest stored version of <slug> AT AUTHOR TIME, so the stored spec is reproducible: its effective-hash can never move when a child later gains a version. A @latest (or omitted) reference to a child that does not exist yet cannot be pinned and is refused FW-GRAPH-DEF-UNPINNABLE — name an explicit <slug>@N or author the child first. The soundness gate also covers STRUCTURED PARALLELISM (SCH-2b): a fork must branch (out-degree ≥ 2, else degenerate-fork), a join must merge (in-degree ≥ 2, else degenerate-join), and a join's quorum K must satisfy 0≤K≤M over its M inputs (else invalid-quorum; a non-zero quorum off a join is also invalid). A declared fork and a declared join must also PAIR (LET-1660), judged over the AUGMENTED edge set (bind-implied edges included, so a composed subgraph is judged on its effective wiring): a declared fork with NO declared join reachable from EVERY branch is fork-without-join, and a declared join that is the reconvergence of NO declared fork is join-without-fork. Both are refused because of the close-time consequence: the fired-join gate demands that a walk which opened branches also FIRED its join, so an unpaired fork is a def every walk of which is refused at close, and an unpaired join is a station that can never fire and therefore never gates. A PLAIN (undeclared) fan-out is untouched — it stays an OR-split and only raises the advisory fan-out-no-join lint smell. Across COMPOSITION the verdict DEFERS rather than refuses: a subgraph (uses) node is opaque (its child need not even be stored yet), so a fork reconverging at one — or a join a subgraph node reaches — is accepted here and DECIDED on the expansion, where compile runs the same gate with every subgraph node inlined. Composition adds one MORE named kind at that point, the composite half of the same family (LET-1664): a subgraph's EXIT port IS its set of terminal-concern nodes, so a child whose sink is a declared join resolves to ZERO exits, and a parent that edges or binds OUT of such a node is refused subgraph-without-exit naming the uses node and the empty port. It is the DUAL of unbound-port (an entry nothing feeds, an exit that feeds nothing) and it is refused because the alternative is silence: every parent edge leaving that node expanded to no edge at all, which left the parent's own sink unreachable and read downstream as an incidental unreachable-node about a node the author wired correctly. A subgraph the parent never leaves may legitimately have no exit — it is then the composite's own sink. Full dominator matching (a UNIQUE join per fork, no crossed/unbalanced split-merge) stays deliberately deferred. It also covers LOOP ITERATION (SCH-2b-2): the min_times/until_stable/until_drain/circuit_breaker primitives are valid only on a capped loop back-edge (else invalid-iteration) and every bound must stay inside 0..cap. It also covers the NODE EXEC-POLICY (SCH-7): an optional per-node exec_policy {model, effort, residency} must use the closed effort ladder (low|medium|high) and the closed residency set (resident|ephemeral), a model/capability-tier token matching the open token grammar, and must not be an empty object — anything else is invalid-exec-policy naming the node. A subgraph (uses) node carries no exec_policy of its own — it may instead carry exec_policy_default (SCH-7b), the COMPOSITION-SEAM policy port: a default applied to the BEHAVIORAL stations of the subtree it inlines, knob by knob and ONLY where that station left the knob unset, so a composite can staff a reuse at its tier's price while an atom that declared its own policy always wins. exec_policy on a uses node, or exec_policy_default on a leaf, is refused. And it covers ROUTING GUARDS (ENACT-5): a when guard is valid only on a ROUTER's out-edge, must parse against the closed fact vocabulary (carrier./outcome./effect.), and a guarded router may keep at most ONE unguarded default out-edge — anything else is invalid-guard, so a bad guard is learned at AUTHORING time, never as a runtime surprise. An unsound spec is refused FW-GRAPH-DEF-UNSOUND naming the offending node/edge, and nothing is written. A malformed (non-JSON) spec, or a malformed uses/bind ref, is a usage error. UNKNOWN KEYS ARE WARNED, NOT REFUSED (LET-734): a spec is stored VERBATIM and its bytes fold into effective-hash, so a key the schema does not recognise reads BACK as declared (graph-def show's spec field returns it, the hash covers it, and a run-case pins that hash) while NOTHING reads it. create/revise scan the raw JSON and report every unrecognised key with its path (nodes[<id>].<key>, edges[<from>-><to>].<key>, nodes[<id>].exec_policy.<key>, or a bare top-level key) on BOTH surfaces (LET-913): the human line on stderr, and the same diagnostic in the success envelope's warnings[] array under --format json|yaml, so an automated author sees it too; exit stays 0 and the def is still stored, because unknown-field tolerance is deliberate and refusing would make every already-stored spec unrewritable. The truth is the PARSED view — the nodes/edges arrays of the same payload — and it shows by ABSENCE. Note produces on an EDGE is NOT a schema field: the produces post-check validates the CALLER's --produces flag against stored carriers, never the edge's declaration.
- kind:
mutation - output:
object— --format plain prints its graph-def reference (slug) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--spec-file path- Path to the graph-def JSON spec: {"start":NODE,"nodes":[{"id":…,"concern":producer|reviewer|router|gate|human|verifier|terminal|fork|join,"quorum":K?,"instruction":TEXT?,"exec_policy":{"model":TOKEN?,"effort":low|medium|high?,"residency":resident|ephemeral?}?} | {"id":…,"uses":"<slug>@<version>","bind":{"start":NODE,"terminal":NODE},"exec_policy_default":{"model":TOKEN?,"effort":low|medium|high?,"residency":resident|ephemeral?}?}],"edges":[{"from":…,"to":…,"edge":…,"cap":N?,"min_times":K?,"until_stable":N?,"until_drain":bool?,"circuit_breaker":M?,"when":GUARD?}]}. A loop back-edge must carry a positive cap; a subgraph node carries uses (not concern) and binds its open ports. SHAPE vs BEHAVIOR (SCH-6): concern is the SHAPE/role term (the structure the runtime enforces); instruction is the optional per-node BEHAVIORAL CONTRACT — what the agent at this station DOES (e.g. an interrogator's 'generate probing questions over edge cases and require a satisfactory answer to each'). It is a leaf-node attribute (a subgraph/uses node inherits behavior from the referenced def and carries none) and it FOLDS INTO the effective-hash, so behavior is part of the content-addressed identity: two defs of identical shape but different instructions hash differently. It is optional — a leaf def without one still validates (only the advisory missing-instruction lint warns on a behavioral node that lacks one). STRUCTURED PARALLELISM (SCH-2b): a fork must branch (out-degree ≥ 2) and a join must merge (in-degree ≥ 2), and the two must PAIR — a declared fork needs a declared join reachable from EVERY branch (else fork-without-join) and a declared join must be the reconvergence of some declared fork (else join-without-fork), LET-1660; a join MAY carry quorum:K (a K-of-M threshold over its M inputs; 0/omitted = an AND-join over all M) — quorum is valid only on a join and folds into the effective-hash. LOOP ITERATION (SCH-2b-2): a capped loop back-edge MAY refine how it iterates/exits — min_times (a floor of K iterations), until_stable (exit after N consecutive clean waves), until_drain (exit when the work-queue drains), circuit_breaker (trip after M unproductive iterations). Each is valid ONLY on a cap>0 back-edge (else invalid-iteration) and every bound must stay inside 0..cap; all fold into the effective-hash. Loop runtime enactment is live in graph-run-case (ENACT-3): a back-edge taken before the oracle is satisfied is refused FW-GRAPH-LOOP-NOT-CONVERGED / FW-GRAPH-ITERATION-FLOOR, and a visit past the cap FW-GRAPH-LOOP-CAP-EXCEEDED. NODE EXEC-POLICY (SCH-7) — the THIRD vocabulary beside SHAPE (concern) and BEHAVIOR (instruction): exec_policy declares HOW a station is RUN. model is the model or capability-tier the agent at this node should use (an OPEN, grammar-checked token — model names evolve, so the set is deliberately not closed; tier words like frontier/balanced/fast work too); effort is the reasoning-effort tier from the CLOSED ladder low|medium|high; residency is the CLOSED actor lifecycle resident|ephemeral — whether the actor KEEPS ITS CONTEXT across loop iterations (resident) or is spawned FRESH each wave (ephemeral). Every knob is independently optional, but an EMPTY exec_policy object is refused. Like instruction it is a leaf-node attribute and it FOLDS INTO the effective-hash, so HOW a node runs is part of the content-addressed identity and flipping resident->ephemeral mints a new version. THE COMPOSITION SEAM (SCH-7b): a SUBGRAPH (uses) node may carry exec_policy_default — the same three knobs, but read as a DEFAULT for the behavioral stations of the subtree it inlines. It FILLS BLANKS and never overrides: a station keeps every knob it set and gains only the knobs it left unset, so a composite can run one reuse cheap and another dear (the composite knows the TIER; the atom cannot) while an atom author's explicit staffing stays authoritative. It applies to behavioral concerns only (fork/join/terminal have no agent to staff), reaches the WHOLE inlined subtree, and nests nearest-wins. It does not appear in the expansion — the uses node disappears when inlined — so what folds into the hash is the RESULT: a def declaring no default expands byte-identically to before the field existed. The advisory missing-residency lint warns when a behavioral node INSIDE a declared loop leaves its residency implicit. ROUTER GUARDS (ENACT-5): an out-edge of a ROUTER node MAY carry when:GUARD — a boolean condition over facts the run-case RECORDS, in a tiny deterministic grammar: REF == "VALUE" / REF != "VALUE" combined with and/or/not and parentheses, where REF is carrier.<key>, outcome.<clean|progress|level-up|queue-remaining> or effect.<cell-hardened|task-opened|artifact-added|dod-progressed|transition> (e.g. carrier.decision == "approved"). Semantics are EXACTLY-ONE-MATCH: at most one out-edge may hold, and a guarded router may declare at most ONE unguarded out-edge as its default (else) route. A malformed guard, a guard on a non-router out-edge, or a second default edge is refused invalid-guard; the guard folds into the effective-hash (changing it moves the def's identity). Runtime enactment is live in graph-run-case (FW-GRAPH-ROUTE-UNSATISFIED / FW-GRAPH-ROUTE-AMBIGUOUS / FW-GRAPH-ROUTE-NO-MATCH). Required.
Examples:
lettuce graph-def create review-linear --spec-file ./graph-def.json --project lettuce --author agent-1 --format json
Notes:
- The spec is stored verbatim (a fuller authored spec's extra fields are preserved, ignored by this slice) EXCEPT that a floating uses: <slug>@latest / omitted-version ref is rewritten to the concrete <slug>@N it resolved to at author time (so a stored version is reproducible). Soundness is checked BEFORE storage so a run-case can never enact a broken topology. Available through the local or hosted backend; over HTTP (POST /v1/projects/{project}/graph-defs) it needs the admin role (LET-1814).
graph-def revise
Usage: lettuce graph-def revise SLUG --spec-file PATH --project PROJECT --author AUTHOR
REVISE an existing graph-def: append a NEW spec version (spec/{N+1}) from a JSON spec file and re-pin the effective-hash from that NEW version. Revise is the in-band spec-version authoring path (SCH-4b) — the counterpart to create: create is for a NEW slug (a slug already in use is refused), revise APPENDS to a slug that already exists (a missing slug is refused FW-PATH-NOT-FOUND). It reads and parses the new spec, runs the SAME CORE soundness gate as create (every edge references a declared node; no unreachable node; a declared start; every loop back-edge capped; the fork/join/quorum + loop-iteration checks) and — only if sound — appends it as the next version and moves the pin: after revise, graph-def show/compile reflect the LATEST spec and its new effective-hash. An UNSOUND spec is refused FW-GRAPH-DEF-UNSOUND and NOTHING is appended (the latest version is unchanged). The append is version-safe: earlier versions are never touched, so a parent that pins uses: <slug>@1 still compiles to the OLD expansion while uses: <slug>@2 picks up the new one — that is the version-pin payoff. Revise applies the SAME author-time pinning as create: a floating uses: <slug>@latest / omitted-version ref in the NEW spec is resolved and PINNED to the current highest stored version before the version is appended (so every stored version is reproducible); a @latest of an unauthored child is refused FW-GRAPH-DEF-UNPINNABLE. A malformed (non-JSON) spec, or a malformed uses/bind ref, is a usage error. UNKNOWN KEYS ARE WARNED, NOT REFUSED (LET-734): a spec is stored VERBATIM and its bytes fold into effective-hash, so a key the schema does not recognise reads BACK as declared (graph-def show's spec field returns it, the hash covers it, and a run-case pins that hash) while NOTHING reads it. create/revise scan the raw JSON and report every unrecognised key with its path (nodes[<id>].<key>, edges[<from>-><to>].<key>, nodes[<id>].exec_policy.<key>, or a bare top-level key) on BOTH surfaces (LET-913): the human line on stderr, and the same diagnostic in the success envelope's warnings[] array under --format json|yaml, so an automated author sees it too; exit stays 0 and the def is still stored, because unknown-field tolerance is deliberate and refusing would make every already-stored spec unrewritable. The truth is the PARSED view — the nodes/edges arrays of the same payload — and it shows by ABSENCE. Note produces on an EDGE is NOT a schema field: the produces post-check validates the CALLER's --produces flag against stored carriers, never the edge's declaration.
- kind:
mutation - output:
object— --format plain prints its graph-def reference (slug) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--spec-file path- Path to the NEW graph-def JSON spec to append as the next version (same schema as graph-def create). It is parsed + soundness-checked before storage; an unsound spec is refused and nothing is appended. Unrecognised keys are WARNED on stderr AND in the success envelope's warnings[] array (exit 0), never refused — see the command description. Required.
Examples:
lettuce graph-def revise review-linear --spec-file ./review-linear-v2.json --project lettuce --author agent-1 --format json
Notes:
- A <slug>@<version> reference PINS a specific spec version; a floating @latest (or omitted version) is pinned to the current highest version AT AUTHOR TIME so the stored spec is reproducible. Reusing a slug in create is still refused (create=new, revise=append). Available through the local or hosted backend; over HTTP (POST /v1/projects/{project}/graph-defs/{slug}/revise) it needs the admin role (LET-1814).
graph-def show
Usage: lettuce graph-def show SLUG --project PROJECT
Render a stored graph-def: its identity, start node, nodes (id + concern/SHAPE + a behavioral node's instruction/BEHAVIOR + a node's exec_policy/EXECUTION (or a subgraph node's exec_policy_default seam default) + a join's quorum), edges (from/to + optional carrier/cap + a router out-edge's ENACT-5 when routing guard), the compiled effective-hash, and the raw stored spec. concern is the SHAPE (role/geometry), instruction is the BEHAVIOR (what the agent does), and exec_policy is the EXECUTION policy (model/effort/residency — how the station is run) — three distinct fields so you can SEE what a node IS, what it DOES, and how it RUNS. Read-only.
- kind:
read - output:
object— --format plain prints its graph-def reference (slug) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Examples:
lettuce graph-def show review-linear --project lettuce --format json
Notes:
- Reads the latest spec version back and re-parses it for the node/edge view; the effective-hash is recomputed and agrees with the stored pin (it folds in each node's instruction AND its exec_policy, so behavior and execution are both part of identity).
- On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
graph-def list
Usage: lettuce graph-def list --project PROJECT [--include-archived]
List a project's graph-defs (slug, identity, start node, and node/edge counts), sorted by slug. Read-only; a project with no graph-defs lists empty.
- kind:
read - output:
collection— --format plain prints one graph-def reference (slug) per row ofitems; nothing when empty - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
listing— proportional to the collection it reads once (paging or the task projection bound it) - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--include-archived- Read an ARCHIVED project on purpose: without this flag a read scoped to an archived project is refused (FW-READ-PROJECT-ARCHIVED); with it the read succeeds and is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning, and project_archived_at in the json/yaml data).
Examples:
lettuce graph-def list --project lettuce --format json
Notes:
- A light summary; use graph-def show SLUG for the full spec.
- On an ARCHIVED project this read is refused with FW-READ-PROJECT-ARCHIVED (exit 1) unless --include-archived is passed; with it the result is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning and project_archived_at in the json/yaml data).
graph-def compile
Usage: lettuce graph-def compile SLUG --project PROJECT --author AUTHOR
Compile a stored graph-def to its CANONICAL effective form and content-hash it, re-pinning the effective-hash scalar (the F30 pin a run-case records at open). Canonicalization is deterministic and order-invariant: nodes are sorted by id, edges by their full tuple, and insignificant whitespace is dropped via a canonical JSON re-encode, so any spelling of the same topology yields the same sha256 hash. Idempotent — re-compiling an unchanged def rewrites the identical hash (a no-op). For a leaf def compile is exactly canonicalize + hash; for a COMPOSITE def (SCH-4) it resolves the transitive uses closure, inlines every referenced def (namespacing inlined node ids), and hashes the WHOLE expansion — so editing a used def and recompiling moves the parent's hash. Compile is the composition ENFORCEMENT point: a cycle is refused FW-GRAPH-DEF-CYCLE, a missing used def FW-PATH-NOT-FOUND, and an unbound port / unsound expansion FW-GRAPH-DEF-UNSOUND.
- kind:
mutation - output:
object— --format plain prints its graph-def reference (slug) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Examples:
lettuce graph-def compile review-linear --project lettuce --author agent-1 --format json
Notes:
- The effective-hash covers the executable topology (start/nodes/edges) of the full expansion, NOT the graph-identity label — a rename does not move the hash; a real topology change (here or in any used subgraph) does. Local + dedicated-git only.
graph-def lint
Usage: lettuce graph-def lint SLUG --project PROJECT
Lint a stored graph-def for ADVISORY design smells — legal-but-questionable topological shapes: an UNDECLARED fan-out with no join (a leaked parallel split; a DECLARED unpaired fork/join is refused by soundness as fork-without-join / join-without-fork, LET-1660, so lint never sees one on a stored def), a terminal node with an out-edge (a terminal should be a sink), a gate/verifier that dead-ends (routes nowhere), a review/verify edge carrying no evidence carrier, a behavioral node with no instruction (missing-instruction — its SHAPE is declared but its BEHAVIOR is left implicit; SCH-6), a behavioral node INSIDE a declared loop with no exec_policy.residency (missing-residency — a looping actor is either resident, keeping context across iterations, or ephemeral, respawned each wave, and the same topology behaves differently either way; SCH-7), and a SUBGRAPH node whose uses child cannot be resolved in this project (unresolvable-subgraph — an explicit <slug>@N reference is deliberately storable before the child exists, so a def can sit in this state indefinitely; LET-1202). Smells are WARNINGS surfaced via warnings[] (machine) / stderr (human) — distinct from soundness, which is fatal. A VERDICT (output contract, ADR 0026): exit 0 when clean, exit 2 when any smell is found, in every format; under --format plain the slug prints only when clean. A smelly def is still sound and still runs; lint is advice, not a gate. Read-only.
- kind:
read - output:
verdict— --format plain prints the graph-def reference (slug) iffcleanis true; nothing otherwise (the explanation on stderr; the exit code is the answer) - exit codes:
0yes (clean);2no: the answer, not a failure (ok:true; output contract R6); any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Examples:
lettuce graph-def lint review-linear --project lettuce --format json
Notes:
- A clean def lints with an empty warnings[] and clean:true. Each smell names its kind + offending node/edge (FW-GRAPH-DEF-SMELL).
- On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
graph-def viz
Usage: lettuce graph-def viz SLUG [--format mermaid|dot] --project PROJECT
VISUALIZE a stored graph-def as a diagram (INT-4) — mermaid (default) or dot. Node SHAPE encodes the concern: producer (stadium), gate (diamond), human (parallelogram), router (hexagon), terminal (subroutine box), fork/join (trapezoids), reviewer/verifier (rectangle); a SUBGRAPH (uses) node renders as ONE cylinder labelled with its uses ref (the default view does NOT recursively inline it). SHAPE vs BEHAVIOR (SCH-6): the node shape + concern tag are the SHAPE; a behavioral node ALSO gets a labelled 'behavior: <short instruction>' line (a short tooltip form of its instruction) so the diagram shows both what a node IS and what it DOES, kept on distinct lines. SCH-7 adds a THIRD labelled line, 'exec: model=… effort=… residency=…', for a node carrying an exec_policy — how the station is RUN; a SUBGRAPH node renders its SCH-7b seam default as 'exec default: …' instead, since it staffs everything inside it rather than a station of its own. A join's label annotates its quorum (join (2 of 3)); a loop back-edge (cap>0) renders DASHED with its cap and iteration primitives (cap=5 min_times:1 until_stable:2); a ROUTER out-edge carrying an ENACT-5 routing guard appends it (when: carrier.decision == "approved"), so the diagram SHOWS the condition under which each branch is taken. The diagram is DETERMINISTIC (nodes sorted by id, edges by tuple) so it is stable/diffable, and always VALID mermaid (ids are made safe, labels escaped). The global --format selects the wrapper: mermaid|dot print the raw diagram to stdout (pipe it to a .mmd/.dot file or a renderer, or paste into a GitHub/artifact mermaid block); json|yaml wrap it in the envelope's diagram field; the human default prints the raw mermaid. Read-only; an unknown slug is FW-PATH-NOT-FOUND.
- kind:
read - output:
content— --format plain prints the content, as table prints it - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Examples:
lettuce graph-def viz review-linear --project lettuce --format mermaid
Notes:
- mermaid renders natively on GitHub, mobile, and artifacts and converts cleanly to PNG. A --expand flag that inlines composite (uses) subgraphs is deferred; the default view shows a uses node as one node.
- On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
graph-def catalog list
Usage: lettuce graph-def catalog list
List the SHIPPED named-pattern catalog (mission-graph-engineering SCH-5a) — canonical, versioned, VERIFIED graph-def patterns embedded in the binary (usage by name). Each row is a pattern key (name@vN), its declared cost_hint (the WORST-CASE cost band for one full run of the pattern — an ordinal minimal/low/moderate/high/very-high, deliberately not a currency or a duration), its one-line description, and its compiled effective-hash. Browse by cost_hint to pick the CHEAPEST process that will do. Read-only and project-independent (the catalog is embedded, not stored per-project); browse it, then materialize a pattern with graph-def use.
- kind:
read - output:
collection— --format plain prints one graph-pattern reference (key) per row ofpatterns; nothing when empty - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
listing— proportional to the collection it reads once (paging or the task projection bound it) - requires root:
false - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Examples:
lettuce graph-def catalog list --format json
Notes:
- The catalog ships gate@v1, tracer-bullet@v1, review-panel@v1, claim-verification@v1, audit-wave@v1 (a review panel looped until N consecutive clean waves — convergence), harden-loop@v1 (a fix→verify cycle with a circuit-breaker), red-team@v1 (an adversarial panel whose join is unanimous — every attack branch must clear), question-based-verification@v1 (an interrogator verifier feeding a must-answer gate), external-audit@v1 (a convergence audit loop signed off by a distinct out-of-loop auditor), review-panel@v2 (a raised-bar voting panel — 5 reviewers, 4-of-5 supermajority quorum), claim-verification@v2 (a raised-bar unanimous 3-of-3 claim check), and deep-review@v1 (a COMPOSITE-AND-high-bar pipeline that REUSES the atoms via version-pinned uses: subgraph nodes — tracer-bullet@1 → review-panel@2 → red-team@1 → claim-verification@2 → question-based-verification@1 → external-audit@1 → harden-loop@1 → gate@1 — pinning the raised-quorum @v2 atoms for the panel and claim stages), and escalating-review@v1 (the CHEAP-FIRST cost gradient: a Tier-0 screening gate staffed at low effort on a fast model, a ROUTER that climbs to Tier-1 review-panel@1 only when a carrier recorded tier0-verdict == "escalate", a second router that climbs to Tier-2 deep-review@1 only when a carrier recorded tier1-verdict == "escalate", and each router's single UNGUARDED out-edge landing on a terminal — so an unrecorded verdict EXITS cheap instead of buying an expensive review), escalating-review@v2 (the same gradient with its INTERIORS staffed — the two uses: nodes carry an exec_policy_default that fills in whatever staffing the inlined atoms left open), tournament@v1 (pairwise competition whose crown ROUTER DECLARES NO DEFAULT EDGE: an advance with no recorded comparison dead-ends FW-GRAPH-ROUTE-NO-MATCH instead of falling through to a champion, and a 2-of-2 join is the match barrier — a winner must have BEATEN someone), and generate-and-filter@v1 (many candidates against ONE bar pinned BEFORE generation, whose tally router's single UNGUARDED out-edge lands on an EMPTY terminal — rejecting every candidate is the DEFAULT outcome and reaching the shortlist is what a recorded keep verdict earns, so the filter can always return zero). Every pattern declares a cost_hint: an AUTHORING-TIME ordinal band for the WORST case of one run (the ceiling over every route, never the expected cost, which depends on the population of work). It is catalog metadata beside the description, so it does NOT fold into the effective-hash, and the RUNTIME NEVER READS IT — escalation is decided by router guards over recorded evidence, while observed cost stays ENACT-6 evidence. It is validated against the closed ladder and must be MONOTONE over composition: a composite's band is at least the band of every pattern in its uses-closure, so a thin wrapper can never advertise deep-review@v1 as cheap. A pattern may be a uses:-composite of other catalog patterns; its effective-hash covers the fully-inlined expansion (catalog-hash == compile-hash == the hash a materialized def compiles to), and re-pinning a stage to a new atom version is what moves the composite's hash while patterns pinned to an older version stay reproducible (older atom versions are immutable). Every pattern is sound by construction and compiles to a stable effective-hash; a def materialized from a pattern (graph-def use) compiles to that SAME hash. Does not require an initialized store.
graph-def catalog show
Usage: lettuce graph-def catalog show NAME@vVERSION
Show ONE shipped catalog pattern (SCH-5a): its declared cost_hint (the worst-case cost band for one full run), its start node, nodes (id + concern/SHAPE, a join's quorum, each behavioral node's instruction/BEHAVIOR, and any exec_policy/EXECUTION), edges (from/to + optional carrier/cap + a router out-edge's ENACT-5 when routing guard), the compiled effective-hash, and the raw pattern spec. Every shipped pattern carries a real instruction on each behavioral node (the catalog holds itself to that bar), so a pattern named for a behavior actually carries the words that make it do it. Read-only and project-independent; an unknown pattern is refused FW-PATH-NOT-FOUND naming the requested name@version.
- kind:
read - output:
object— --format plain prints its graph-pattern reference (pattern.key) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
false - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Examples:
lettuce graph-def catalog show review-panel@v1 --format json
Notes:
- The effective-hash shown here is the pin a def materialized from this pattern (graph-def use) will compile to — the pattern is faithfully reproduced. Does not require an initialized store.
graph-def use
Usage: lettuce graph-def use NAME@vVERSION --as SLUG --project PROJECT --author AUTHOR
MATERIALIZE a shipped catalog pattern (SCH-5a) as a NEW graph-def SLUG in the project — usage by name. It resolves the pattern by name (unknown -> FW-PATH-NOT-FOUND), then stores its spec via the same path as graph-def create: the CORE soundness gate runs and the compiled effective-hash is pinned, so the new def compiles to the SAME hash the catalog reports (a faithful materialize). The result is an ordinary stored graph-def — composable (a uses node), compilable, and enactable by a run-case. A slug already in use is refused exactly as create. --bind re-wiring of a pattern's open ports at use time is DEFERRED; specialize a materialized pattern the normal way (a parent def that uses it and binds its ports).
- kind:
mutation - output:
object— --format plain prints its graph-def reference (slug) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--as slug- The slug for the new graph-def the pattern is materialized as (required). A slug already in use is refused, exactly as graph-def create. Required.
Examples:
lettuce graph-def use review-panel@v1 --as my-review --project lettuce --author agent-1 --format json
Notes:
- A straight materialize: the pattern's verified spec becomes the new def's body, so soundness + the effective-hash pin apply. Compile the new def (graph-def compile SLUG) to confirm its hash equals the catalog's. Local + dedicated-git only.
Links to
- Command Reference
reference/command-reference
Backlinks
- Graphs — authored process, enacted on the ledger
concepts/concept-graph - Command Reference
reference/command-reference - reference/index
reference/index
Graph Run-Cases — Commands
reference/cmd-graph-run-cases lettuce Graph Run-Cases commands — 14 entries — graph-run-case open, graph-run-case advance, graph-run-case close, graph-run-case abandon, graph-run-case acknowledge, graph-run-case show, graph-run-case viz, graph-run-case list, graph-run-case conform, graph-run-case refs-to, carrier produce, carrie
lettuce command group Graph Run-Cases — 14 commands. Generated from lettuce usage --format okf (always in sync with the binary).
Back to Command Reference.
Commands in this group
graph-run-case opengraph-run-case advancegraph-run-case closegraph-run-case abandongraph-run-case acknowledgegraph-run-case showgraph-run-case vizgraph-run-case listgraph-run-case conformgraph-run-case refs-tocarrier producecarrier listcarrier showcarrier acknowledge
---
Enact a linear graph run-case on the real ledger (mission-graph-engineering slice 1): open a run-case, produce its write-once edge carriers, advance it across gated edges, and close it. A graph-run-case is an event-sourced object at projects/<p>/graph-run-cases/ whose state == f(events); carriers are content-addressed write-once data at projects/<p>/carriers/. Fork branches, K-of-M join quorum firing, loop iteration/convergence, and CONDITIONAL ROUTING out of a router are all enacted at runtime here (ENACT-1/2/3/5), each guarded by a refusal (FW-GRAPH-JOIN-UNSATISFIED, FW-GRAPH-LOOP-NOT-CONVERGED, FW-GRAPH-ROUTE-UNSATISFIED). A router picks its out-edge from the run-case's OWN recorded evidence (carriers, wave outcomes, produced effects) evaluated through a small deterministic guard grammar — never from the caller's say-so. Hosted lifecycle and read surfaces dispatch through the configured backend; acknowledgement remains store-local.
graph-run-case open
Usage: lettuce graph-run-case open --graph GRAPH --start NODE --project PROJECT
Open a new run-case enacting a graph-def, positioned at its declared start node. --graph must name a graph-def the project DECLARES (an unknown slug is refused FW-GRAPH-DEF-UNKNOWN listing the defs that exist) and --start must be that def's declared start (anything else is refused FW-GRAPH-START-UNDECLARED, stating the start that works) — a reference must resolve at the moment it is recorded, else the pin below binds to nothing. Writes a case-opened event (revision 1) asserting state==start-node, and returns the minted grc id for advance/close. The event ALSO records the REPRODUCIBILITY PIN when the named graph-def has a stored effective-hash: graph_effective_hash binds this run to the exact compiled topology it started on, so a later graph-def revise/compile moves the DEF's hash without re-pointing this run's evidence — the ledger stays falsifiable. The pin is copied verbatim from the def's effective-hash scalar (never recomputed here) and cached on the run-case as a derived scalar the merge-heal derived-check re-reads from the event.
- kind:
mutation - output:
object— --format plain prints its graph-run-case reference (id) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--graph graph-def- The graph-def SLUG this run-case enacts; it must name a def stored in the project, else FW-GRAPH-DEF-UNKNOWN (e.g. review-linear). The stored def's effective-hash is pinned onto the case-opened event as graph_effective_hash. Required.--start node- The start node the case opens at (the case-opened event asserts it as the run-case state). It must equal the graph-def's declared start, else FW-GRAPH-START-UNDECLARED; a composite whose start is a uses-node also accepts the inlined effective start. Required.
Examples:
lettuce graph-run-case open --graph review-linear --start producer --project lettuce --author agent-1 --format json
Notes:
- After open every walk door reads the def version this run PINNED (never the latest), so an in-flight run is never stranded by a def edit: since LET-1944 an advance --to/--edge (and a carrier --node/--edge) naming a node or edge that pinned version does not declare is refused FW-GRAPH-WALK-UNDECLARED, listing the declared next edges. OPEN is the exception (LET-733): --graph must resolve to a stored def and --start must be its declared start, because the reproducibility pin taken here is what binds the run to a process. The gate is on NEW opens only — a run-case already recorded still shows, lists, advances, closes, conforms and validates even if its def is later revised, renamed or deleted, and a historical case with no pin stays legal. Available through the local or hosted backend.
- GUIDANCE (LET-1902): the result lists the legal moves out of the position it reached as next_edges (JSON .data.data.next_edges), read from the graph-def version this run PINNED — never the latest — with next_edges_basis naming that source (pinned; latest-unpinned for a pre-pin run) or why there is none (def-missing, pin-unresolvable, composite-unresolvable, position-undeclared, no-def, unreadable). Each move carries the exact from/to/edge and an intent: advance (with --branch when it names a branch), open-branch (out of a fork), fire-join (a branch reached its join: --fire-join --quorum K from the fork on that arrival edge), loop-exit (out of a loop head: --loop-exit --loop-edge LOOP_EDGE); carrier names a carrier the edge declares (produce it before crossing) and when a router guard. Omitted at a terminal (close the run). A composite is guided on its compiled form (node@sub). Advisory: every move is still judged by the same guards.
graph-run-case advance
Usage: lettuce graph-run-case advance GRC --from NODE --to NODE --edge EDGE [--visit N] [--open-branch | --branch COORD | --fire-join --quorum K | --wave-close (--clean|--not-clean) [--progress] [--level-up] [--queue-remaining N] | --loop-exit --loop-edge EDGE [--min-times K] [--until-stable N] [--until-drain] [--circuit-breaker M]] [--cap N] [--route-edge EDGE=WHEN]... [--route-else EDGE] [--duration-ms N] [--cost-usd AMOUNT] [--produces KEY]... [--effect KIND|REF[|CARRIER]]... [--gate-task REF --gate-action ACTION --gate-reason TEXT]
Advance a run-case across an edge. Before writing the case-advanced event it runs the produces post-check — TWO readings of one requirement, both refusing with FW-GRAPH-PRODUCES-UNSATISFIED: each --produces KEY must already exist as a carrier on the leaving edge×visit (the promise the CALLER made), and, since LET-1676, a carrier the GRAPH-DEF declares on the edge being crossed (carrier: K) must be bound at that crossing whether or not --produces names it (the promise the EDGE made). --produces is therefore how you SATISFY an edge's declaration, not how you opt into checking it; produce the carrier first (lettuce carrier produce --graph-run-case … --node … --edge … --key K --value V), which is the order docs/guides/CLOSE-WALK.md already prescribes. A --to/--edge/--loop-edge/--route-edge/--route-else naming a node or edge the def version this run PINNED does not declare is REFUSED FW-GRAPH-WALK-UNDECLARED before anything is written or the gate trio runs (LET-1944), with the declared next edges in the refusal; --from (pinned by the from-guard) and a --fire-join arrival edge (judged from the branch ledger) are not re-judged. A def that cannot be read (missing, pin unresolvable, composite not compiling) stays a WARNING rather than a refusal and bites at close instead (FW-GRAPH-CLOSE-NON-CONFORMING). The check runs on a plain and a --branch advance; the structural intents --open-branch/--fire-join/--wave-close/--loop-exit dispatch before it and are untouched. And, when the gate trio is set, the reused task requires-* PRE-gate (a missing requirement bites FW-WF-REQUIREMENT-UNSATISFIED). Each --effect records an INT-5 produced-effect (the mutation this node performed as it left) on the case-advanced event, foldable back via the event log. On success state==--to, revision bumps by one. ENACT-1 concurrent branches: --open-branch opens a branch OUT of the fork node --from (the main state stays on the fork so its OTHER out-edges each open their own branch) instead of moving the single state; --branch COORD advances WITHIN an open branch, guarded on THAT branch's tip. A plain advance (neither flag) is the unchanged linear single-state walk. ENACT-3 loop iteration: --wave-close records one closed wave's outcome (a self-transition at the loop head) on the loop back-edge×visit; a NORMAL back-edge advance is bounded by --cap (visit<=cap, else FW-GRAPH-LOOP-CAP-EXCEEDED) and barred once --circuit-breaker M consecutive non-progress waves have tripped (FW-GRAPH-CIRCUIT-TRIPPED); --loop-exit advances out of the loop only when the derived oracle (--min-times/--until-stable/--until-drain over the wave-closed log) is met or the breaker tripped. Every loop decision is a pure fold over the wave-closed event log (replay-stable), and a fabricated exit is refused at merge-heal (Class-B). ENACT-5 conditional routing: when the advance leaves a ROUTER, pass the router's out-edge guard set with repeatable --route-edge 'EDGE=WHEN' (plus --route-else EDGE for the single unguarded default). The engine resolves the guards' facts from THIS run-case's ledger — carrier values on this visit, the latest closed wave's outcome, recorded produced-effect kinds — evaluates every guard, and refuses unless --edge is the edge the guards SELECT (FW-GRAPH-ROUTE-UNSATISFIED), the selection is unique (FW-GRAPH-ROUTE-AMBIGUOUS: two guards true, or a carrier fact with two divergent values) and something matches (FW-GRAPH-ROUTE-NO-MATCH when no guard holds and no default is declared). A refused route leaves the main state UNCHANGED. The decision and the evidence it consumed are recorded on the case-advanced event (route-taken/route-guard + the route/ and route-facts/ groups) and folded into its content-addressed id, so the routing is auditable, replay-stable, de-duping across clones, and a fabricated route is refused at merge-heal (Class-B). ENACT-6 run telemetry: --duration-ms and --cost-usd record the OBSERVED cost of the step this advance performed, stored VERBATIM on the case-advanced / branch-advanced event as immutable evidence (the engine reads no clock — the CALLER supplies the numbers). Telemetry is EVIDENCE, NEVER CONTROL INPUT: no derived-state reader folds it, so routing, joins, loops, the terminal and replay are all untouched by what a step cost — the same run with different telemetry folds to an identical state, revision and transition sequence. It IS folded into the content-addressed event id (like --effect and the routing decision), so an identical re-record de-dupes while a DIVERGENT reading for the same transition forks the chain (Class-B) instead of being silently unioned. It is refused (never silently dropped) on the control-bookkeeping intents --open-branch/--fire-join/--wave-close/--loop-exit, which do not perform the work; graph-run-case show reports the per-run totals.
- kind:
mutation - output:
object— --format plain prints its graph-run-case reference (id) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--from node- The node being left. Required.--to node- The resolved node being entered (stored verbatim so state is always recomputable). Required.--edge edge- The producing/traversed edge, e.g. producer->gate. With --fire-join it is instead the ARRIVING BRANCH edge (e.g. v1->join), because a join fire collapses the fork onto the join rather than traversing an edge — see --fire-join. Required.--visit n- Loop-iteration index for the traversal (default 0 for the single-flow linear slice).--open-branch- ENACT-1: OPEN a concurrent branch OUT of the fork node --from along --edge, instead of moving the single state. The branch id is content-addressed (sha256 of grc·fork·edge·visit); the fork stays the main state so each out-edge opens a distinct branch. Mutually exclusive with --branch.--branch coord- ENACT-1: advance WITHIN the open branch of this content-addressed coordinate (from--open-branch/show), guarded on the branch's own tip rather than the main state. Mutually exclusive with --open-branch.--fire-join- ENACT-2: FIRE a K-of-M join — collapse the main state from the fork --from onto the join --to, exactly-once, once at least --quorum distinct branches have ARRIVED (tip == the join). Refused with FW-GRAPH-JOIN-UNSATISFIED below quorum. A fire is NOT an edge traversal, so --edge names one of those ARRIVING BRANCH edges (v1->join) and never <from>-><to>:fork->join— the value --from/--to make obvious — is refused FW-GRAPH-JOIN-EDGE-UNARRIVED, naming the edges branches did arrive on. That fact is read from THIS run-case's branch ledger, not the graph-def, so the runtime stays graph-def-free. The join-fired event is content-addressed over the fork→join transition ALONE — grc·fork·visit·join, independent of the arrived-set, the quorum AND the arrival --edge — and records the fired-on branch coords as a coordinate-named set that unions coordination-free, so two clones firing the same join de-dupe to one fired state whichever arrived edge each of them named (LET-918: with three branches arrived there are three equally TRUE edges, so folding the caller's pick forked the chain on honest concurrent work). The recorded --edge is therefore provenance rather than identity, and stays accountable through the derived check: a fire must cite an edge one of its recorded arrived branches genuinely came in on, else FW-STORE-MERGE-CONFLICT at heal. Because the id no longer varies with --edge, firing a LOOPED fork/join a second time with the FIRST lap's --visit would address the first lap's event: that is refused FW-GRAPH-JOIN-ALREADY-FIRED (pass the --visit this lap's branches were opened with), where before it overwrote the earlier fire in place. Mutually exclusive with --open-branch and --branch.--quorum k- ENACT-2: the effective K-of-M threshold for --fire-join (>=1) — the number of distinct arrived branches required to fire. The caller resolves the join schema's quorum=0 (AND-join) to K=M before firing (this runtime slice is graph-def-free, like --produces).--wave-close- ENACT-3: record a WAVE-CLOSED event — one wave (a traversal producer→…→loop head) has completed. A self-transition at the loop head (--from must equal --to; state unchanged) that bumps the revision and records the wave's outcome (--clean/--not-clean, --progress, --level-up, --queue-remaining) on --edge (the loop back-edge) × --visit. Content-addressed over (grc·edge·visit·outcome): two clones closing the same wave with the same outcome de-dupe; a divergent outcome forks the chain (Class-B). Mutually exclusive with --loop-exit/--open-branch/--branch/--fire-join.--loop-exit- ENACT-3: advance OUT of the loop (loop head → --to), guarded on the derived exit oracle folded from the wave-closed log for --loop-edge. Refused (FW-GRAPH-ITERATION-FLOOR / FW-GRAPH-LOOP-NOT-CONVERGED, state unchanged) unless min_times/until_stable/until_drain are satisfied — or the circuit breaker has tripped (a forced failure exit). The loop-exited event records the effective params so the exit stays derivable (a fabricated exit is Class-B). Mutually exclusive with --wave-close/--open-branch/--branch/--fire-join.--loop-edge edge- ENACT-3: the loop back-edge whose wave-closed log the --loop-exit oracle folds (e.g. gate->fork).--clean- ENACT-3 (--wave-close): the wave converged cleanly — extends the consecutive-clean run (until_stable). Opposite of --not-clean; the default is not-clean.--not-clean- ENACT-3 (--wave-close): the wave did NOT converge — resets the consecutive-clean run (until_stable).--progress- ENACT-3 (--wave-close): the wave made a defined outcome delta — resets the consecutive-non-progress run (circuit_breaker). Its absence is a non-progressing wave.--level-up- ENACT-3 (--wave-close): the wave recorded a level-up finding — RESETS the consecutive-clean run even if the wave was clean (waves cover the full scope, so a level-up restarts convergence).--queue-remaining n- ENACT-3 (--wave-close): the work-queue depth the wave observed (until_drain). The loop exits when the LATEST wave records --queue-remaining 0.--cap n- ENACT-3: the effective hard cap on a loop back-edge advance — a back-edge advance at --visit > --cap is refused (FW-GRAPH-LOOP-CAP-EXCEEDED). The provably-terminating backstop.--min-times k- ENACT-3 (--loop-exit): the loop floor — the exit is refused until at least K waves have closed.--until-stable n- ENACT-3 (--loop-exit): convergence — the exit is enabled only after N consecutive clean (non-level-up) waves.--until-drain- ENACT-3 (--loop-exit): the exit is enabled when the latest wave records --queue-remaining 0 (the work-queue drained).--circuit-breaker m- ENACT-3: trip the loop after M consecutive non-progressing waves. On a back-edge advance a tripped breaker bars the loop (FW-GRAPH-CIRCUIT-TRIPPED); on --loop-exit a tripped breaker permits a forced failure exit.--route-edge edge=when- ENACT-5: one GUARDED out-edge of the router being left — the edge name, an =, and thewhenguard authored on it (only the FIRST = separates, so the guard's own == stays intact). Repeatable: pass the router's WHOLE guard set, because the semantics are exactly-one-match (two guards true is FW-GRAPH-ROUTE-AMBIGUOUS). The guard is a boolean over facts the run-case RECORDS: REF == "VALUE" / REF != "VALUE" with and/or/not and parentheses, REF being carrier.<key>, outcome.<clean|progress|level-up|queue-remaining> or effect.<kind>. An unrecorded fact reads as the empty string. Graph-def-free like --quorum/--produces: the caller resolves the guards from the stored def. Repeatable.--route-else edge- ENACT-5: the router's single UNGUARDED out-edge — the DEFAULT (else) route taken when no guard holds. Without it a no-match is a loud dead-end (FW-GRAPH-ROUTE-NO-MATCH) rather than an arbitrary fall-through. A matching guard always beats the default.--duration-ms n- ENACT-6: the OBSERVED wall-clock duration of this step, in milliseconds (non-negative). Recorded verbatim on the advance event as immutable evidence — the engine measures nothing itself. An explicit 0 means "observed, cost nothing" and is distinct from omitting the flag (not observed). Valid on a plain or --branch advance only.--cost-usd amount- ENACT-6: the OBSERVED cost of this step in USD (a non-negative decimal with at most 6 decimal places, e.g. 0.42). Converted EXACTLY to micro-USD (1 USD = 1000000) and stored as an integer, so sums are exact and 0.42 / 0.420000 record identical evidence; a finer precision is refused FW-CMD-USAGE rather than rounded away. Valid on a plain or --branch advance only.--produces key- An output carrier key the leaving node declares; the produces post-check refuses the advance (FW-GRAPH-PRODUCES-UNSATISFIED) unless a carrier exists on this edge×visit for the key. REPEATABLE, and NOT the whole requirement: since LET-1676 the engine also opens the graph-def and requires any carrier the EDGE declares at the crossing, so omitting this flag no longer skips the edge's own demand — it only means you made no additional promise of your own. Repeatable.--effect kind|ref[|carrier]- An INT-5 produced-effect recorded on the case-advanced event, pipe-separated: an effect KIND (one of cell-hardened, task-opened, artifact-added, dod-progressed, transition), the object-REF it mutated, and an OPTIONAL carrier back-ref to the justifying evidence. Both refs must name an EXISTING object: a well-formed ref that resolves to nothing is REFUSED with FW-REF-DANGLING before the write-once event exists (LET-1557). Pipe is unambiguous (it appears in neither a kind nor an object-ref). Valid on a plain or --branch advance only: like the ENACT-6 telemetry flags it is REFUSED, never silently dropped, on the control-bookkeeping intents --open-branch/--fire-join/--wave-close/--loop-exit, which open/reconverge/count rather than perform the work an effect attests to (LET-1507 — the drop returned ok:true and left the completion gate reporting the task referenced 0 times). Repeatable.--gate-task task-ref- Backing task whose workflow transition is the reused requires-* PRE-gate; runs before the advance.--gate-action action- Workflow action to fire on --gate-task (the gated transition).--gate-reason text- Reason recorded on the gate transition (for transitions that require one).
Examples:
lettuce graph-run-case advance grc_… --from producer --to gate --edge producer->gate --produces artifact --project lettuce --author agent-1 --format json
lettuce graph-run-case advance grc_… --from fork --to r1 --edge fork->r1 --open-branch --project lettuce --author agent-1 --format json
lettuce graph-run-case advance grc_… --from r1 --to join --edge r1->join --branch 4f… --project lettuce --author agent-1 --format json
lettuce graph-run-case advance grc_… --from producer --to gate --edge producer->gate --duration-ms 1250 --cost-usd 0.42 --project lettuce --author agent-1 --format json
lettuce graph-run-case advance grc_… --from route --to approve --edge route->approve --route-edge 'route->approve=carrier.decision == "approved"' --route-edge 'route->reject=carrier.decision == "rejected"' --project lettuce --author agent-1 --format json
Notes:
- The produces post-check and the reused-task PRE-gate are the contract's teeth; a refused advance leaves state unchanged. An --effect is a RECORD of a mutation performed through the shipped ops path (declared by the executor, never sniffed). ENACT-1 opens/advances branches; ENACT-2 --fire-join reconverges them (K-of-M quorum gating the fork→join main-state fire, exactly-once and coordination-free); ENACT-5 --route-edge/--route-else make a ROUTER pick its own out-edge from recorded evidence — they guard a plain main-state advance and are mutually exclusive with --open-branch/--branch/--fire-join/--wave-close/--loop-exit (routing INSIDE an open branch is deferred). Available through the local or hosted backend.
- GUIDANCE (LET-1902): the result lists the legal moves out of the position it reached as next_edges (JSON .data.data.next_edges), read from the graph-def version this run PINNED — never the latest — with next_edges_basis naming that source (pinned; latest-unpinned for a pre-pin run) or why there is none (def-missing, pin-unresolvable, composite-unresolvable, position-undeclared, no-def, unreadable). Each move carries the exact from/to/edge and an intent: advance (with --branch when it names a branch), open-branch (out of a fork), fire-join (a branch reached its join: --fire-join --quorum K from the fork on that arrival edge), loop-exit (out of a loop head: --loop-exit --loop-edge LOOP_EDGE); carrier names a carrier the edge declares (produce it before crossing) and when a router guard. Omitted at a terminal (close the run). A composite is guided on its compiled form (node@sub). Advisory: every move is still judged by the same guards.
graph-run-case close
Usage: lettuce graph-run-case close GRC --from NODE [--outcome TEXT] [--disposition VALUE] --project PROJECT
Close a run-case at a terminal node. Writes a case-closed event asserting the terminal sentinel state (__closed__) with the given outcome (default completed). THREE preconditions, each refusing before any write: the case must not already be __closed__ (FW-GRAPH-CASE-CLOSED), --from must be the node the case is currently on (FW-GRAPH-FROM-MISMATCH), and — since LET-1676, widened by LET-1680 — --disposition completed must be EARNED: a case whose ledger CONTRADICTS the graph-def it pinned is REFUSED with FW-GRAPH-CLOSE-NON-CONFORMING, naming the blocking finding's code. The verdict is the SAME reader graph-run-case conform and doctor use, never a second implementation, so a refusal here is a finding you can read in full with graph-run-case conform GRC. It is still NARROWER than conform at both edges, deliberately: every CONTRADICTION finding blocks (CARRIER-MISSING, CARRIER-AMBIGUOUS, UNDECLARED-EDGE, UNKNOWN-NODE, QUORUM-BELOW-DECLARED, JOIN-NEVER-FIRED, NO-TRANSITIONS, DECLARED-EPHEMERALITY-VIOLATED) while conform's UNKNOWN arm — FW-GRAPH-CONFORM-DEF-MISSING and -PIN-UNRESOLVABLE — stays REPORTED by conform and doctor and does NOT block the close, because refusing on "I could not check" is not the same act as refusing on "I checked and it is false" and a case whose def was deleted must stay closable (the engine is graph-def-free after open) — and --disposition abandoned / --disposition failed stay ALLOWED on a non-conforming case, because refusing them would make recording the truth cost more than the silence it replaced (LET-728) and failed is a claim about the WORK rather than about the walk. A refused close leaves the store byte-unchanged and the case still closable by an honest disposition.
- kind:
mutation - output:
object— --format plain prints its graph-run-case reference (id) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--from node- The terminal node being closed from. Required.--disposition value- LET-728: the CLOSED structural claim about this run — completed | abandoned | failed (default completed). Separate from --outcome because it decides whether the run-case ledger is CONFORMANCE-CHECKED, and a claim expressible in free prose would let the checked party suppress a real error with text it writes itself.completedclaims the process was walked to a terminal node — and since LET-1676 (widened by LET-1680) that claim is TESTED at the close, not merely recorded: a ledger that contradicts the pinned def is refused FW-GRAPH-CLOSE-NON-CONFORMING (every contradiction finding; conform's UNKNOWN arm does not block — see that code);failedasserts the process DID run and the work did not pass, so it stays checked BY conform and BY doctor while remaining closable here (a claim about the work is not a claim about the walk);abandonedclaims nothing and is NOT checked — which is what stops honest closure of an unfinished run being punished with an error while leaving it open stayed a mere warning.--outcome text- The close outcome recorded on the event (default completed). FREE PROSE, deliberately — the codebase records meaningful domain results here (haiku-accepted, cleared-at-tier-0, again) and a closed set would destroy that. The structural claim lives in --disposition.
Examples:
lettuce graph-run-case close grc_… --from terminal --outcome completed --project lettuce --author agent-1 --format json
Notes:
- Available through the local or hosted backend.
graph-run-case abandon
Usage: lettuce graph-run-case abandon GRC --reason TEXT --project PROJECT
LET-915: record a terminal ABANDONED disposition on a run-case that was opened and never walked to the end — the recorded answer to "what happened to this run", never a delete. It resolves the node the case is parked on ITSELF (there is no --from: abandoning claims nothing about where the run got to, so it bypasses no gate, and having to show a stranded case before you could dispose of it was most of why such cases read as unreachable). --reason is REQUIRED, unlike close's --outcome: an abandonment records no work, so the reason is the whole of what the ledger can say. It writes the SAME case-closed event close --disposition abandoned writes, through the same writer under the same lock — no second terminal, no second event kind — and refuses a CLOSED run-case (FW-GRAPH-CASE-CLOSED): terminal is terminal, and two dispositions is a fork in the record. An abandoned run is then EXCLUDED from the conformance denominator (graph-run-case conform reports excluded=true and answers no, exit 2, rather than judging a walk that never happened (LET-1831)) while staying fully VISIBLE in list/show/doctor. Abandoning a run with concurrent branches still open is allowed and WARNS (FW-GRAPH-RUN-CASE-BRANCHES-UNRESOLVED) naming each leftover tip: a branch has no disposition of its own, so the abandonment collapses the main state and leaves each tip where it stood rather than inventing a terminal nobody enacted.
- kind:
mutation - output:
object— --format plain prints its graph-run-case reference (id) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--reason text- Why the run was stopped. REQUIRED — recorded verbatim as the close event's outcome (the free-prose field), alongside disposition=abandoned. Without it the ledger says no more than the stranded state it replaces. Required.
Examples:
lettuce graph-run-case abandon grc_… --reason 'superseded by a rerun; the walk was never completed' --project lettuce --author agent-1 --format json
Notes:
- Available through the local or hosted backend.
- NOT a delete: the run-case directory, its events, its branch ledger and its reproducibility pin all stay exactly where they are; one terminal event is appended. Deleting an event-sourced object to tidy a count is the tamper shape this family exists to refuse.
graph-run-case acknowledge
Usage: lettuce graph-run-case acknowledge GRC --path PATH --reason TEXT --project PROJECT
LET-1590: record a canonical ACKNOWLEDGEMENT of a dangling produced-effect reference on a run-case — the honest alternative to forging the referent or editing a write-once event. Since LET-1557, advance --effect REFUSES a well-formed effect ref (or carrier back-ref) whose object does not exist (FW-REF-DANGLING, before the event is written), so this command is for refs already on disk: legacy refs written before that refusal, or ones placed out of band, which validate still checks at rest — a ref to an object that does not exist holds the whole store at valid=false. Creating the missing object would forge a record of work that never happened; editing the event is impossible (events are write-once, and the effect set is folded into the content-addressed event id, INT-11). Instead this writes projects/<p>/graph-run-cases/<grc>/acknowledgements/<key>/{code,path,ref,reason,acknowledged-by,acknowledged-at}, keyed by sha256(code,path) so one finding has exactly one acknowledgement. validate then reports that finding under the distinct WARNING FW-REF-DANGLING-ACKNOWLEDGED instead of the ERROR FW-REF-DANGLING: the store is VALID while the breakage stays VISIBLE and ATTRIBUTED (reason + author + time). --path is the exact finding path from lettuce validate --format json; --reason is REQUIRED. The command REFUSES a path that is not currently a dangling finding, so an acknowledgement can never pre-empt future breakage, and re-recording the same finding is idempotent while a different ref/reason is refused rather than silently overwriting the decision. Local + dedicated-git only.
- kind:
mutation - output:
object— --format plain prints its graph-run-case reference (id) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--path path- The store-relative path of the FW-REF-DANGLING finding, exactly aslettuce validate --format jsonreports it: projects/<p>/graph-run-cases/<grc>/events/<evt>/effects/<n>/{ref|carrier}. Refused unless that path currently holds a dangling reference. Required.--reason text- REQUIRED. Why this dangling reference is accepted. Stored verbatim and surfaced in the validated warning, so the acceptance is accountable to a reader. Required.
Examples:
lettuce graph-run-case acknowledge grc_… --path 'projects/lettuce/graph-run-cases/grc_…/events/evt_…/effects/0/ref' --reason 'placeholder id LET-9999 recorded while dogfooding the graph engine; kept as honest history' --project lettuce --author agent-1 --format json
Notes:
- This does NOT edit the event, delete the run-case, or invent the referent. It records a decision ABOUT a known finding. Withdrawing it means removing the acknowledgements/<key>/ directory; an acknowledgement whose finding is gone is reported FW-REF-DANGLING-ACK-STALE so a stale record cannot silently suppress a future finding.
graph-run-case show
Usage: lettuce graph-run-case show GRC --project PROJECT
Read a run-case's derived scalars (graph, graph_effective_hash, state, revision) plus, for a CLOSED case, its CLOSE RECORD — disposition (the structural claim: completed|abandoned|failed) and outcome (the prose explaining it, which for an abandonment is the REQUIRED --reason). LET-1100: that reason was WRITE-ONLY, projected by no read surface, so retrieving the one field whose stated purpose is to answer "what happened to this run" meant cat-ing a file inside a content-addressed event directory. Both are omitted for an open case. Plus its PROVENANCE (opened_by/opened_at, folded from the case-opened event on each read — never a stored scalar, so the event stays the evidence) back from the ledger, plus the ENACT-1 in-flight branch projection (branches[]: each open branch's content-addressed coordinate, fork node, edge, visit, and current tip) and the ENACT-6 observed-cost totals (telemetry: how many steps carried an observation, the summed duration_ms, the exact cost_micro_usd and its human cost_usd rendering). The totals are DERIVED = f(events) — refolded from the durable event log (main chain + every branch) on each read, never a stored cache — and are EVIDENCE only: nothing in the run's control flow reads them. Read-only. The response also projects the event-derived main-chain timeline, produced effects, and fired joins (including quorum and arrived branch coordinates), identically in local and HTTP client modes.
- kind:
read - output:
object— --format plain prints its graph-run-case reference (id) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Examples:
lettuce graph-run-case show grc_… --project lettuce --format json
Notes:
- Reads the committed derived cache; the cache is a faithful projection of the event log (a crash mid-write is heal-forward-completable to the same fold). branches[] is empty/absent for a linear (no-fork) run-case, and telemetry is absent for a run that recorded no observations. opened_by/opened_at answer WHO opened this run and WHEN — the question
graph-run-case abandonraises first, since on a shared store you do not dispose of a colleague's stranded case. They are DISTINCT fromgraph-run-case list's updated_by/updated_at, which report who last TOUCHED the run: for a never-walked case the two coincide, which is exactly why the near-miss misleads once anybody else advances it. Both are omitted (legally) when the case-opened event records no author — a half-written event is a real on-disk shape, and show reports what the ledger says rather than inventing a value. graph_effective_hash is the reproducibility pin recorded at open — the graph-def content identity this run walked; it is absent (legally, and permanently) for a run-case opened without one, and it does NOT move when the graph-def is later revised. - On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
graph-run-case viz
Usage: lettuce graph-run-case viz GRC [--format mermaid] --project PROJECT
VISUALIZE a run-case as a MERMAID diagram with its run state OVERLAID (INT-4): the run-case's underlying graph-def is rendered (same shape-by-concern as graph-def viz), then the CURRENT node is highlighted and every VISITED node is styled distinctly (classDef current / classDef visited). It reuses the grc read path — the underlying def (by the grc's graph slug) plus the current state and the visited trail folded from the event log. The topology drawn is the spec version the run-case PINNED at open, resolved from its graph_effective_hash by the same resolver graph-run-case conform uses — NOT the def's latest version, so revising a graph-def never redraws a run that walked the earlier text. A uses: composite is drawn in its COMPILED form (subgraphs inlined, child nodes named node@use), the basis every judge of the walk reads (LET-1935), so a run standing inside a subgraph is drawn there (envelope compiled: true). The global --format selects the wrapper: mermaid or the human default print the raw diagram to stdout; json|yaml wrap it in the envelope's diagram field (also carrying current + the visited trail). dot is not offered (the overlay is mermaid-specific). Read-only; an unknown run-case is FW-PATH-NOT-FOUND.
- kind:
read - output:
content— --format plain prints the content, as table prints it - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Examples:
lettuce graph-run-case viz grc_… --project lettuce --format mermaid
Notes:
- Every diagram STATES which spec text produced it, both as the envelope's basis field (pinned | latest-unpinned | trail-pin-unresolvable | trail-composite-unresolvable | trail-def-missing, alongside pinned_version / latest_version) and as a mermaid %% comment on the line under the flowchart header, so the provenance survives redirecting the raw diagram to a file. LET-1100: a NON-PROVING disposition (abandoned|failed) rides that same mechanism — a second %% comment under the basis, plus a
dispositionenvelope field beside state — because an abandoned walk otherwise draws exactly the picture a completed one does, and an envelope-only fix is lost the moment the diagram is redirected. An ordinary completed or still-open run emits no such comment: a banner on every healthy run is the noise that trains a reader to skim the one that matters. A run-case that recorded NO pin is drawn against the latest spec and says so — absence is the pre-pin shape, not an error. If the graph-def is not stored, or the pin matches none of its stored spec versions (so the latest is a topology the run provably did not walk), a minimal graph is synthesized from the visited trail so viz always yields a diagram. A closed run-case (state __closed__) highlights no current node; all occupied nodes remain visited. - On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
graph-run-case list
Usage: lettuce graph-run-case list [--open|--state STATE] --project PROJECT [--include-archived]
ENUMERATE a project's graph-run-cases with their disposition (LET-726). Before this, run-cases were not listable AT ALL — no list command, no FQL source (from graph_run_cases is refused), and doctor carries no grc family — so SKILL.md 9.1's rule that "an opened-and-abandoned run-case is exactly as unproving as no run-case at all" was a doctrine nobody could measure; counting abandonment required ls on the store. Reports open AND abandoned as FIRST-CLASS counts. LET-1100: open alone was advertised as the abandonment figure and is not one — it counts cases with no close event, while an ABANDONED case IS closed. The unproving population §9.1 names is open+abandoned, so a reader taking total-open as "walks that proved something" over-counted by exactly the abandoned figure. Each case also carries its own disposition (completed|abandoned|failed, empty while open): before LET-1100 an abandoned case was byte-identical to a completed one here, so the census built to find unproving walks reported them as finished ones. CLOSED is the only name for finished: a run-case parked on a terminal NODE has NOT been closed (close is a distinct, explicit act), and conflating the two is exactly how a stalled case reads as complete. --open keeps only unclosed cases; --state NODE keeps only cases parked on that node (the abandonment query); the two are mutually exclusive and combining them is refused rather than silently resolved. Directory names are grammar-checked, so a stray directory cannot inflate the count. Deterministic order (run-case id). Read-only.
- kind:
read - output:
collection— --format plain prints one graph-run-case reference (id) per row ofcases; nothing when empty - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
listing— proportional to the collection it reads once (paging or the task projection bound it) - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--open- Keep only run-cases that have NOT been closed — the population SKILL.md 9.1 calls unproving. Mutually exclusive with --state.--state state- Keep only run-cases in this state.__closed__selects finished cases; any node id selects cases parked on that node. Mutually exclusive with --open.--include-archived- Read an ARCHIVED project on purpose: without this flag a read scoped to an archived project is refused (FW-READ-PROJECT-ARCHIVED); with it the read succeeds and is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning, and project_archived_at in the json/yaml data).
Examples:
lettuce graph-run-case list --project lettuce --format json
lettuce graph-run-case list --open --project lettuce --format json
Notes:
- On an ARCHIVED project this read is refused with FW-READ-PROJECT-ARCHIVED (exit 1) unless --include-archived is passed; with it the result is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning and project_archived_at in the json/yaml data).
graph-run-case conform
Usage: lettuce graph-run-case conform GRC --project PROJECT
CONFORMANCE REPLAY (LET-720): does this run-case's WALK match the graph-def it PINNED at open? The runtime is graph-def-free by design — advance reads no spec, so --to/--edge accept any string and a case can traverse an edge the def does not declare, name a node absent from it, or fire a K-of-M join below the declared K, and still close validate --strict clean. Every OTHER check over a run-case re-derives its verdict from parameters the run itself recorded (the join re-folds the quorum the caller wrote), which makes each one a fixed point: internally honest, and structurally unable to notice that the RECORDED number is not the DECLARED one. conform is the read-time counterpart that makes graph_effective_hash a binding rather than a label naming the topology a run merely claims to have followed. THE BASIS IS THE PINNED VERSION (LET-919): the pinned hash is resolved back to the spec/N that mints it and the walk is judged against THAT text, so revising a def cannot retroactively change a prior verdict — until LET-919 the pin was unresolvable and every walk was in fact judged against the def's LATEST spec, which moved the yardstick under every completed run. pinned_version/latest_version report the basis; a run-case that recorded NO pin (the pre-pin shape) is still judged against the latest and says so, because absence is history, not guilt. Findings: FW-GRAPH-CONFORM-UNDECLARED-EDGE (a transition the def does not declare), FW-GRAPH-CONFORM-QUORUM-BELOW-DECLARED (a join fired under its declared threshold), FW-GRAPH-CONFORM-QUORUM-AUTHORS-BELOW (a quorum-K join fired on arrived verdict carriers PRODUCED by fewer than K distinct acting authors, or naming fewer than K distinct author refs — recorded acting authors, not authenticated identities; run-cases opened before the LET-1242 landmark report the ref rule as the …-PRE-ENFORCEMENT advisory, LET-1242), FW-GRAPH-CONFORM-UNKNOWN-NODE (a node the def does not contain), FW-GRAPH-CONFORM-JOIN-NEVER-FIRED (the walk ARRIVED at a declared join and the join never fired — PATH-AWARE since LET-1681: a join no traversed edge enters is not "never fired", so a router that legitimately bypasses its join is not a finding), FW-GRAPH-CONFORM-CARRIER-MISSING (the walk CROSSED an edge whose pinned spec declares carrier: K and no carrier with that key is bound to it — reported per DECLARED EDGE, not per run-case, so a walk holding some of its verdicts and not others is caught; edges the walk never crossed are NOT reported, because demanding evidence on an edge a run-case has not reached would fire on honest in-flight work), FW-GRAPH-CONFORM-DEF-MISSING (the pinned def is gone, so conformance is UNKNOWN — never reported as conforming, because an absent def reading as a pass is the always-passes shape this command exists to remove), FW-GRAPH-CONFORM-PIN-UNRESOLVABLE (the def is here but NO stored spec version mints the pinned hash, so the topology walked cannot be recovered — no verdict is computed rather than one silently computed against the latest), FW-GRAPH-CONFORM-COMPOSITE-UNRESOLVABLE (the pinned version is a uses: composite whose subgraph closure no longer compiles — unknown, like PIN-UNRESOLVABLE). A COMPOSITE IS JUDGED ON ITS COMPILED FORM (LET-1923): the inlined, namespaced nodes (c1@sub) its walkers address and next_edges advertises, an inlined edge named by its declared name or its endpoints (c1@sub->c2@sub) — the same basis the advance carrier door and the task-gate arms read. A REPORT, not a refusal — THIS COMMAND writes nothing and blocks nothing; existing run-cases predate the check and a non-conforming walk is evidence to read rather than corruption to block. Its CALLERS escalate: validate --strict on a closed case, doctor (FW-GRAPH-RUN-CASE-NON-CONFORMING), and — since LET-1676 — graph-run-case close --disposition completed, which REFUSES with FW-GRAPH-CLOSE-NON-CONFORMING on this same verdict, over every CONTRADICTION finding (LET-1680) and the completed disposition alone; conform's UNKNOWN arm (DEF-MISSING, PIN-UNRESOLVABLE) is reported here and never blocks. So this is the command to run when a close is refused, and the command that tells you about every OTHER non-conformance — which this report flags and that door deliberately does not block. It is a VERDICT: EXIT 2 with an ok:true report when the walk does not conform (or the def is missing, or the pin or a composite closure is unresolvable, or the run was abandoned) so a script can gate on it without parsing JSON — the same "no" as task exists, cell gate check and graph-def lint (LET-1831; it was exit 1 before v0.20.2). Exit 1 means the command itself failed (ok:false, e.g. an unknown run-case). A revised def is still reported as hash drift — that answers a different question, namely whether the def has moved since the run, and stays useful alongside the resolved basis. Read-only.
- kind:
read - output:
verdict— --format plain prints the graph-run-case reference (id) iffconformsis true; nothing otherwise (the explanation on stderr; the exit code is the answer) - exit codes:
0yes (conforms);2no: the answer, not a failure (ok:true; output contract R6); any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Examples:
lettuce graph-run-case conform grc_5e9f90e2e3ec22c4588ef09c0b442b57 --project lettuce --format json
Notes:
- A join FIRE is not an edge traversal: it collapses the main state from the fork onto the join, so its recorded
fromis the FORK while itsedgenames one of the ARRIVING BRANCH edges (declared out of the branch node, not out of the fork). conform checks a fire against the join's IN-edges accordingly. - On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
graph-run-case refs-to
Usage: lettuce graph-run-case refs-to OBJECT-REF --project PROJECT [--wait[=DURATION] | --no-wait]
BACK-REFERENCE from an object to the run-cases that touched it (INT-15) — the REVERSE of the INT-5 forward effect edge. Given an OBJECT-REF (a cell coordinate-hash ref lettuce/cells/<hash>, a task, or any well-formed project/kind-plural/id ref), it scans every graph-run-case's case-advanced events and returns each recorded produced-effect that referenced that object: the run-case, the transition it was recorded on (from/to/edge), the effect kind, the exact stored effect ref, and the optional carrier back-ref to the justifying evidence. So a cell (or an evidence artifact) answers WHICH DECISIONS TOUCHED ME. The reverse index is DERIVED = f(events) — recomputed from the durable event log, never a cache — with deterministic ordering (grc id, then event order). Matching is on object identity (project/kind/id): a query for lettuce/cells/<hash> matches an effect that pinned lettuce/cells/<hash>@4 (the revision pin narrows a citation, not the object). An object nothing references returns an EMPTY references[] with ok=true — 'no back-references' is a valid answer, like an empty query, NOT a not-found error. A malformed OBJECT-REF is refused FW-CMD-USAGE with the ref grammar. Read-only.
- kind:
read - output:
collection— --format plain prints one graph-run-case reference (graph_run_case) per row ofreferences; nothing when empty - exit codes:
0success;8hosted result not ready yet (FW-API-HEALTH-PENDING / FW-API-READ-PENDING): retry later or --wait; not a finding; any failure:1-7by diagnostic class (spec §24.11) - read cost:
project— walks the whole project: on a hosted server a background build (--wait[=DURATION] / --no-wait; exit 8 while it is pending) - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--wait duration- Hosted only (LET-2003): this read is a server-owned background build; wait for a pending build up to DURATION (default 3m0s; a bare --wait also waits out a STALE answer's rebuild), printing progress on stderr. Exit 8 when the budget ends with the build still pending. Locally the flag is accepted and inert.--no-wait- Hosted only (LET-2003): do not wait for a pending build; exit 8 at once (the build keeps running). Locally inert.
Examples:
lettuce graph-run-case refs-to lettuce/cells/9f3a1c2b4d5e6f70 --project lettuce --format json
Notes:
- The forward edge is recorded by graph-run-case advance --effect 'KIND|OBJECT-REF[|CARRIER]'; refs-to inverts it. Works for ANY object kind, not just cells — the operator's cell→GCR navigation and the general reverse lookup are one command. Available through the local or hosted backend. The cell-facing view of this reverse index is surfaced inline by
cell show --with-references(INT-15b), so a cell viewer sees its provenance without a separate refs-to call. - On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
carrier produce
Usage: lettuce carrier produce --graph-run-case GRC --node NODE --edge EDGE --key KEY --value VALUE [--visit N] [--carrier-type TYPE] [--executed ARTIFACT@REVISION] [--ref ROLE|REF[|NOTE]]... --project PROJECT
Produce the write-once carrier an edge emits on one visit — an evented, content-addressed datum at projects/<p>/carriers/. The produces post-check on graph-run-case advance reads it back. Two clones producing the SAME value mint the SAME dir (git de-dupe); a divergent value on the same coordinate is a Class-B double-produce, refused HERE at the front door (FW-CARRIER-DOUBLE-PRODUCE) and, for divergence that arrives by merge rather than by a local call, at merge-heal. Before LET-1545 only the merge-heal half existed, so on a single filesystem-mode clone the refusal never ran. Each --ref attaches an INT-5 typed enrichment ref (folded into the carrier identity alongside value), turning the flat value into a navigable graph edge.
- kind:
mutation - output:
object— --format plain prints its carrier reference (id) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--graph-run-case grc-id- The run-case this carrier belongs to. Required.--node node- The producing node (the node being left). Required.--edge edge- The producing edge, e.g. producer->gate. Required.--key key- The carrier key, e.g. artifact. Required.--value value- The produced value (a ref/string in this slice, not a blob). Required.--visit n- Loop-iteration index (default 0 for the single-flow linear slice). Must be a NON-NEGATIVE INTEGER with no leading zeros (0, 1, 2, …): the value is hashed VERBATIM into the coordinate, so "00" or " 0 " would give one iteration a second identity, and a non-index value mints a carrier no visit lookup can ever reach.--carrier-type type- The value-kind tag (default string).--executed artifact@revision- The identity of the ARTIFACT this carrier's verdict was produced by RUNNING, as ARTIFACT@REVISION — an artifact identity (a path, or run:<id>) then a lowercase-hex revision of 7-64 chars, optionally sha256:-prefixed. Optional and additive: omit it and the carrier is byte-for-byte the pre-LET-1598 shape, id included. Supplied, it is folded into the carrier identity ALONGSIDE value and refs, so two arms recording the same verdict against DIFFERENT artifacts are a Class-B double-produce rather than one carrier silently keeping whichever ran first.--ref role|ref[|note]- An INT-5 typed enrichment ref, pipe-separated: a ROLE (one of evidence, subject, tool, author, derived-from, produced-effect), the object-REF it points at, and an OPTIONAL free-text NOTE. Pipe is unambiguous (it appears in neither a role nor an object-ref); the note MAY contain pipes (only the first two are separators). Repeatable.
Examples:
lettuce carrier produce --graph-run-case grc_… --node producer --edge producer->gate --key artifact --value art:GATE-1/1 --project lettuce --author agent-1 --format json
lettuce carrier produce --graph-run-case grc_… --node producer --edge producer->gate --key artifact --value art:GATE-1/1 --ref 'subject|lettuce/tasks/LET-1|the ticket' --ref 'derived-from|lettuce/graph-run-cases/grc_…' --project lettuce --author agent-1 --format json
lettuce carrier produce --graph-run-case grc_… --node verifier --edge verifier->gate --key verdict --value MET --executed scripts/ci-guard.sh@32a82bc65446 --project lettuce --author agent-1 --format json
Notes:
- Write-once: the carrier's coordinate is sha256(grc·edge·key·visit) and its dir id content-addresses (coordinate·value·refs·executed).
keyis load-bearing — two distinct keys on one edge×visit are distinct carriers.nodeandcarrier-typeare RECORDED BUT NOT part of the identity (an edge already names its source node), so a produce differing only in those de-dupes onto the existing carrier, writes nothing, and keeps what the FIRST produce recorded — reported as a FW-CARRIER-DEDUPE-FIELD-DISCARDED warning rather than a refusal, since an exact re-produce is legitimate. A malformed --ref (bad role, malformed object-ref) is refused at the ops front door before any write. A --node or --edge the def version the run PINNED does not declare is refused FW-GRAPH-WALK-UNDECLARED, listing the declared next edges (LET-1944); --key is not (a def declares the carriers an edge demands, not a closed key set). Available through the local or hosted backend. - A carrier PRESENCE is what the LET-1446 transition gate and the LET-1448 conform check both test; NEITHER reads its VALUE. --value "." satisfies both exactly as well as a considered verdict. That is deliberate, because quality is not mechanizable, but it means a green gate proves evidence was RECORDED and nothing about whether it is any good.
- --executed records WHAT a verdict was produced by running, and it is CALLER-ASSERTED: nothing here re-runs the artifact, resolves the revision, or checks that the producing author ran anything. What the grammar buys is that the claim cannot be made without naming a revision, so a later reader can check the citation instead of trusting a memory of it — the LET-1598 near-miss was an arm citing a CI run whose script had since moved 59 lines, on the very failure path the arm claimed to cover. NO gate requires the field: it is optional by construction, because a required one would refuse every walk in flight.
carrier list
Usage: lettuce carrier list [--graph-run-case GRC [--visit N]] [--type TYPE|--carrier-type TYPE] --project PROJECT [--include-archived]
List the project's carriers, optionally narrowed to one graph-run-case, one visit, and/or one carrier-type. --visit narrows WITHIN a run-case and therefore REQUIRES --graph-run-case: a carrier stores its coordinate = sha256(grc·edge·key·visit) but NOT the visit, so membership of a visit is decided by RECOMPUTING the coordinate, which is impossible without the run-case. The same walk and the same recomputation back the router's fact projection, so carrier list and graph-run-case route can never disagree about which carriers belong to a visit. --carrier-type narrows to carriers whose recorded carrier-type equals the given value (LET-1554): a read-side scalar filter, so a caller auditing the store can count e.g. how many verdict carriers exist without dumping all and post-filtering. Unlike --graph-run-case it is NOT existence-checked — a carrier-type is free-form at produce time, so an unmatched type is a legitimate count-0 answer, not a refusable reference.
- kind:
read - output:
collection— --format plain prints one carrier reference (id) per row ofcarriers; nothing when empty - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
listing— proportional to the collection it reads once (paging or the task projection bound it) - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--graph-run-case grc- Narrow to one graph-run-case id. The id is checked to EXIST: a run-case that is not stored is REFUSED (FW-PATH-NOT-FOUND), never reported as a run-case holding no carriers.--visit n- Narrow to one loop-iteration index (non-negative integer, no leading zeros). Requires --graph-run-case.--carrier-type type- Narrow to carriers whose recorded carrier-type equals TYPE (e.g. string, verdict). A read-side filter over the same projection; the count reflects the filtered set. Free-form and NOT existence-checked — a type nothing carries answers count 0, not an error.--include-archived- Read an ARCHIVED project on purpose: without this flag a read scoped to an archived project is refused (FW-READ-PROJECT-ARCHIVED); with it the read succeeds and is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning, and project_archived_at in the json/yaml data).--type type- Preferred alias for --carrier-type. Supplying both with different values is refused.
Examples:
lettuce carrier list --project lettuce --format json
lettuce carrier list --graph-run-case grc_… --visit 0 --project lettuce --format table
Notes:
- A carrier with no key or no edge is omitted: both are components of the coordinate, so such a carrier cannot be addressed by any lookup and is a member of no visit.
- An unstored --graph-run-case is REFUSED rather than answered (LET-1029). Otherwise a mistyped or stale id and a real run-case holding nothing return the same bytes —
(none) / count 0 / exit 0— and the caller reads a typo as a finding. A run-case that EXISTS and holds no carriers still answers count 0, which is the distinction the refusal preserves. - On an ARCHIVED project this read is refused with FW-READ-PROJECT-ARCHIVED (exit 1) unless --include-archived is passed; with it the result is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning and project_archived_at in the json/yaml data).
carrier show
Usage: lettuce carrier show CARRIER-ID --project PROJECT
Show ONE carrier by its dir id, with its INT-5 typed enrichment refs. Reads through the same projection as carrier list, so show can never surface a carrier list omits nor render a field differently. An id that resolves to nothing is REFUSED (FW-PATH-NOT-FOUND) rather than reported as an empty carrier — a typo and a carrier with no fields are different answers.
- kind:
read - output:
object— --format plain prints its carrier reference (id) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Examples:
lettuce carrier show car_0123456789abcdef --project lettuce --format json
Notes:
- The id is the content-addressed dir name: car_ + sha256(coordinate·value·refs·executed) truncated. Get one from
carrier list. - On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
carrier acknowledge
Usage: lettuce carrier acknowledge CARRIER-ID --path PATH --reason TEXT --project PROJECT
LET-1700: record a canonical ACKNOWLEDGEMENT of a dangling carrier enrichment reference — the carrier-side sibling of graph-run-case acknowledge (LET-1590). A carrier ref is validated at REST, never at write time (INT-5: carrier produce validates grammar + role only; existence is validate's job), so a ref to an object that does not exist holds the whole store at valid=false. Creating the missing object would forge a record of work; instead this writes projects/<p>/carriers/<car>/acknowledgements/<key>/{code,path,ref,reason,acknowledged-by,acknowledged-at}, keyed by sha256(code,path). validate then reports the finding under the distinct WARNING FW-REF-DANGLING-ACKNOWLEDGED instead of the ERROR FW-REF-DANGLING, so the store is VALID while the breakage stays visible and attributed. --path is the exact finding path from lettuce validate --format json; --reason is REQUIRED. Refused unless the path is currently a dangling finding; re-recording is idempotent; a different ref/reason is refused; a stale record is reported FW-REF-DANGLING-ACK-STALE. LET-1677: --path projects/<p>/carriers/<car>/coordinate instead acknowledges doctor's FW-STORE-MERGE-CONFLICT on a divergent double-produce (two carriers on one write-once coordinate with different values) — the one Class-B conflict with no in-place repair, since deleting a carrier rewrites history and choosing one forges a verdict. It records the divergent sibling carriers as ref, is refused when no live collision exists, and doctor then reports FW-STORE-MERGE-CONFLICT-ACKNOWLEDGED (warning) while that sibling set is unchanged, FW-STORE-MERGE-CONFLICT-ACK-STALE when it is not. Local + dedicated-git only.
- kind:
mutation - output:
object— --format plain prints its carrier reference (id) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--path path- The store-relative path of the finding: projects/<p>/carriers/<car>/refs/<n>/ref for an FW-REF-DANGLING finding (exactly aslettuce validate --format jsonreports it), or projects/<p>/carriers/<car>/coordinate for doctor's FW-STORE-MERGE-CONFLICT on that carrier's coordinate. Refused unless that finding is live. Required.--reason text- REQUIRED. Why this dangling reference is accepted. Stored verbatim and surfaced in the validated warning. Required.
Examples:
lettuce carrier acknowledge car_… --path 'projects/lettuce/carriers/car_…/refs/0/ref' --reason 'placeholder ref recorded while dogfooding; kept as honest history' --project lettuce --author agent-1 --format json
Notes:
- This does NOT edit the carrier, delete it, or invent the referent. It records a decision ABOUT a known finding; withdrawing it means removing the acknowledgements/<key>/ directory.
Links to
- Command Reference
reference/command-reference
Backlinks
- Graphs — authored process, enacted on the ledger
concepts/concept-graph - Command Reference
reference/command-reference - reference/index
reference/index
Import Export Repair And Sync — Commands
reference/cmd-import-export-repair-and-sync lettuce Import Export Repair And Sync commands — 17 entries — export, import, migrate, migrate verify, migrate status, migrate list, migrate abandon, migrate release, migrate rollback, repair plan, repair check, repair apply, repair automatic, sync status, sync push, sync pull, conflict bundle.
lettuce command group Import Export Repair And Sync — 17 commands. Generated from lettuce usage --format okf (always in sync with the binary).
Back to Command Reference.
Commands in this group
exportimportmigratemigrate verifymigrate statusmigrate listmigrate abandonmigrate releasemigrate rollbackrepair planrepair checkrepair applyrepair automaticsync statussync pushsync pullconflict bundle
---
Move stores, repair issues, inspect conflicts, and synchronize dedicated Git mode.
export
Usage: lettuce export --bundle PATH [--force]
Write a deterministic export bundle outside the store root. The destination is checked before the bundle is built (LET-1972): export never replaces a file it did not write (FW-PATH-EXISTS, --force included; the refusal names a free path), and replaces an EARLIER lettuce export bundle only with --force, because a bundle is a point-in-time backup the store can no longer produce. The write is atomic (temp file, fsync, publish).
- kind:
read - output:
object— --format plain prints its bundle reference (bundle) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
store-inline— walks the whole store inside the request (not yet a server-owned job; it can outlast the client timeout on a large hosted store) - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--bundle path- Output bundle path outside the store root. An existing file there is refused FW-PATH-EXISTS unless it is an earlier lettuce export bundle and --force is given. Required.--force- Replace an EARLIER lettuce export bundle at --bundle (recognized by its content, not its name). Never replaces any other file.
Examples:
lettuce export --bundle /tmp/lettuce-export.json --format json
lettuce export --bundle /tmp/lettuce-export.json --force --format json
import
Usage: lettuce import --bundle PATH
Import a supported bundle.
- kind:
mutation - output:
report— --format plain prints the table bytes - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--bundle path- Input bundle path. Required.
Examples:
lettuce import --bundle /tmp/lettuce-export.json --author importer --format json
migrate
Usage: lettuce migrate --to URL|DIR [--project NAME ...|--all-projects] [--dry-run] [--release] [--write-pointer-candidate PATH] [--receipt PATH] [--progress|--no-progress] [--skip-doctor-compare]
One-shot hosted-store migration orchestration (HOSTED-STORE-MIGRATION runbook). HARD PRECONDITION (dry run and real run, no override): the selected projects and the root objects they reference must pass validate --strict and doctor with ZERO errors, and the source's own .runtime/ must be clean; otherwise it refuses FW-MIGRATION-SOURCE-INVALID listing every error with its repair (and the graph-run-case/carrier acknowledge verbs for unrepairable history). Warnings are allowed and recorded (source_check in the plan and receipt). After live parity it runs validate --strict + doctor SERVER-SIDE on the migrated projects and compares them with the source: a new error code or more errors fails the run FW-MIGRATION-DESTINATION-REGRESSED, naming the inspect and rollback commands (destination_check in the receipt; --skip-doctor-compare opts out). Payload travels as gzip CHUNKS of many files (several in flight); a resumed run asks the server which chunks are recorded and sends only the rest. Progress: a meter on stderr (automatic on a terminal, forced by --progress) with rates and ETA, heartbeats while the server runs a long step; with --format json, --progress writes NDJSON progress lines to STDERR only (stdout stays the one result envelope). A server refuses a group above its admission bound with FW-MIGRATION-TOO-LARGE (serve --migration-max-entries) before any work. --dry-run prints a deterministic, NO-MUTATION plan and, for a hosted URL, first PROBES the destination (authenticated GET /v1/domains: reachable, bearer accepted, --domain granted, server release) so a bad token, dead port or ungranted domain fails the dry run; the plan carries destination_probe (domain, token default domain, server version): each selected project's frozen chunked export, the deduplicated referenced-author closure, destination collisions/deduplication (a LOCAL destination root is inspected directly; a HOSTED destination answers through its read-only collision probe, POST /v1/migrations/probe, reported in destination_probe — LET-1813), the required server capabilities and the estimated transfer. A real run against a LOCAL destination directory drives the proven upload/finalize/activate/release primitives in runbook order and emits a credential-free receipt with a per-phase status ledger. A HOSTED destination (http/https) drives the server's authenticated /v1/migrations transport end-to-end — session, manifest, upload, complete, staged parity, finalize, activate and live parity, with the server rollback path on a failed live check — and STOPS with the group activated, live-verified and still FENCED (receipt awaiting_release: true, release step held): the repository's authority switch happens while nothing can write the hosted copy, and lettuce migrate release --session ID is the separate explicit confirmation that opens it. --release instead releases right after live parity (rehearsals, or moves that need no repository switch). The run pins ONE domain (--domain/LETTUCE_DOMAIN, or the token default resolved once) and records it in the receipt. RESUMABLE: re-running the identical command after an interruption at ANY step (upload, complete, finalize, activate, live parity, release) resumes the group's durable session to release, or finishes an interrupted rollback; steps whose outcome is unknown (client timeout, dropped connection, gateway 502/504) are re-issued as idempotent replays, and the server completes an admitted activation or rollback independent of the client connection. Every refusal after a session exists names the exact resume, migrate status and migrate rollback commands and carries the partial receipt (session_id, domain, failed_step, steps) in error.details. A hosted receipt carries an authority_switch section (a credential-free .lettuce pointer candidate — URL without token, project: and domain: lines — with its sha256 and the plan) and next_steps: the exact archive → pointer → discovery → release → notice → commit commands. Tokens are never accepted on the command line — a token embedded in the --to URL (https://<token>@host/) or --bearer is refused FW-CMD-USAGE before any network contact; the bearer comes only from LETTUCE_BEARER_FILE (preferred) or LETTUCE_BEARER — and never appear in a receipt. Every receipt (dry run, held, released, failed — the failure in error.details) records client_build and server_build (version, commit, build_date and their source: this binary; the destination via GET /v1/version, or this binary for a local rehearsal). A real run also writes its receipt DURABLY before printing it — to --receipt PATH, else the SOURCE store's .runtime/migration-receipts/<session-id|plan-<sha>|attempt-<utc>>.json (noncanonical; it travels with the archived store) — and names the path in receipt_path and next_steps (relative to the working directory when it lies under it, like every path a receipt records, so the receipt names no host path: LET-1889); a dry run writes a file only with --receipt.
- kind:
read - output:
report— --format plain prints the table bytes - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
local— never reads a hosted store - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--to URL|DIR- Destination: a credential-free hosted http(s) URL (authenticated production path; the bearer comes from LETTUCE_BEARER_FILE or LETTUCE_BEARER, never from the URL) or an existing LOCAL store directory for a rehearsal run. Required.--project name- Source project to migrate; repeat for an explicit multi-project group. Repeatable.--all-projects- Explicitly select every project in the source store (mutually exclusive with --project).--dry-run- Print the no-mutation plan and exit; the source store is byte-identical afterwards.--release- Release the group right after a passing live parity check instead of holding it fenced for the authority switch. Without it the run stops at activated-fenced andmigrate release --session IDopens the group after the pointer switch.--write-pointer-candidate path- Hosted runs only: the separate, explicit confirmation to PREPARE the authority switch — once the group is activated and live-verified, write the credential-free pointer candidate to this NEW path (refused for a path named .lettuce, and FW-PATH-EXISTS for any existing path). It never replaces the repository's .lettuce/ directory. A pointer binds ONE project: when the run migrates several, PATH gets the project-less candidate and PATH.<project> one candidate per project (LET-1812); every one of those paths — for --all-projects too, resolved from the same project set the run migrates — is checked before anything migrates (LET-1907).--receipt path- Where to write the credential-free receipt (JSON; an earlier migrate receipt there is overwritten, as a resumed run does). Default for a real run: the SOURCE store's .runtime/migration-receipts/<session-id>.json; a dry run writes a file only when this is given. Refused for a path named .lettuce, a directory, a missing parent directory, or an existing file that is not a migrate receipt (FW-PATH-EXISTS, checked before anything migrates; LET-1972).--progress- Force the progress meter on stderr (human lines; NDJSON progress records under --format json). Default: on for the human format when stderr is a terminal.--no-progress- Never print progress (mutually exclusive with --progress).--skip-doctor-compare- Skip the post-activation server-side validate --strict + doctor comparison against the source (minutes on a large store); the receipt records destination_check.skipped.
Examples:
lettuce migrate --to https://api.example.test --project lettuce --dry-run --format json
Notes:
- Differing canonical bytes are a COLLISION, never an implicit merge: a destination path whose bytes differ from the frozen source refuses the whole group (LET-1752-style reconciliation is deliberately not claimed).
- Order (the ticket's safety requirement): the run holds the group FENCED after live parity; switch the pointer and verify discovery; then
migrate release. Authority-switch preparation (--write-pointer-candidate) and release (migrate releaseor --release) are separate explicit confirmations, and the command never replaces a repository's .lettuce/ directory. - The pointer grammar binds a domain:
domain: <name>; effective domain = --domain > LETTUCE_DOMAIN > pointer domain: > token default. - A hosted destination uses the authenticated /v1/migrations transport; the client reads the bearer from LETTUCE_BEARER_FILE or LETTUCE_BEARER (never argv) and the acting author from --author/LETTUCE_ACTOR, and never writes the bearer to a receipt.
- MIGRATING INTO A DOMAIN TAKES IT OFFLINE FOR THE WHOLE RUN (LET-1836): the session takes the destination domain's fence at BEGIN and holds it through upload, parity, activation and live parity until release, rollback, abandon or expiry; every ordinary request to that domain — reads included — is refused FW-MIGRATION-FENCED (owner, state, progress, ETA, Retry-After). Migrate into a new or empty domain, or plan a window. A second group into the same domain is refused FW-MIGRATION-BUSY (re-running the identical command resumes instead); the server also caps concurrent sessions across domains (FW-MIGRATION-BUSY), refuses a group above its entry bound (FW-MIGRATION-TOO-LARGE) and a volume too small for the session (FW-MIGRATION-INSUFFICIENT-CAPACITY).
- One migrate per SOURCE store: the run holds an advisory lock on the source (a second run is refused FW-MIGRATION-SOURCE-LOCKED; a killed run releases it) and stops FW-MIGRATION-SOURCE-CHANGED at the next manifest page or chunk if the source is written mid-run, abandoning its own pre-activation session so the domain reopens. Stop the source's writers for the duration.
migrate verify
Usage: lettuce migrate verify (--against DIR | --session ID [--view staged|live] --author ACTOR)
Migration PARITY CHECK (HOSTED-STORE-MIGRATION runbook §7). Local mode compares two roots. Client mode asks the authenticated hosted server to verify a completed session against its frozen manifest and persisted composite candidate inventory, writes a credential-free receipt containing the server build identity, canonical writer generation, canonical/store fingerprints and semantic-reader fingerprint, and exits 1 on any mismatch. Staged verifies quarantine plus the unchanged destination baseline; live verifies the complete canonical destination including unrelated siblings. Release accepts a live receipt only while those exact canonical bindings still match under the active admission fence.
- kind:
read - output:
report— --format plain prints the table bytes - exit codes:
0success;1the stores are not in parity (ok:true receipt); any failure:1-7by diagnostic class (spec §24.11) - read cost:
store-inline— walks the whole store inside the request (not yet a server-owned job; it can outlast the client timeout on a large hosted store) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
true - idempotency key:
false
Flags:
--against dir- Local mode: frozen reference store root.--session id- Client mode: completed hosted migration session id. A session belongs to the principal that created it, so pass the same --author the migrate --to run used (as for migrate status).--view staged|live- Client mode candidate view (default staged).
Examples:
lettuce migrate verify --root ./hosted-store --against ./frozen-source --format json
lettuce migrate verify --server-url https://api.example.test --session mig_abc --view staged --author operator --format json
Notes:
- The hosted receipt is durable under the session runtime directory and contains no bearer credentials.
- requires_author applies to the client-mode --session form (LET-1788): the actor is the session's principal key, not attribution — the verify step mints a durable receipt that finalize/release consume, and every session route (status, verify, release, rollback) is bound to the (credential subject, acting author) pair that created the session. Local --against needs no author.
migrate status
Usage: lettuce migrate status --session ID --author ACTOR
Inspect a migration session — hosted (client mode: --server-url/LETTUCE_SERVER_URL, --domain, LETTUCE_BEARER) or, with --root DIR in local mode, the LOCAL destination of a migrate --to DIR run (LET-1836): its durable state (manifest-uploading, uploading, complete, finalized, activating, activated-fenced, released, rolling-back, rolled-back, abandoned, stale), manifest/candidate/parity digests, generations, whether the store is fenced, and the ONE next step its state implies. Read-only (GET /v1/migrations/{id}); the session id is in every migrate --to receipt and FW-MIGRATION-FENCED refusal. A session belongs to the principal that created it, so pass the same --author the migrate --to run used.
- kind:
read - output:
report— --format plain prints the table bytes - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
true - idempotency key:
false
Flags:
--session id- Migration session id (mig_<32 hex>). Required.
Examples:
lettuce migrate status --server-url https://api.example.test --session mig_0123456789abcdef0123456789abcdef --author operator --format json
Notes:
- Resuming is never a separate command: re-run the identical migrate --to command and it continues this session from its durable state.
migrate list
Usage: lettuce migrate list
List every migration session of the destination domain (LET-1836) — hosted (client mode, GET /v1/migrations, admin role) or a LOCAL destination (--root DIR): per session its owner (actor and subject), state, fence phase, age, expiry, uploaded/expected files and bytes, percent done, ETA, and for an abandoned/stale session who ended it and why; plus the domain's current admission fence. Read-only, and it answers while the domain is fenced and while a migration's activation or rollback holds the write lock, like migrate status (LET-1949: it reads only the session records and the fence, never the canonical store) — it is how an operator sees what holds a domain.
- kind:
read - output:
collection— --format plain prints one migrate-session reference (session_id) per row ofsessions; nothing when empty - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
listing— proportional to the collection it reads once (paging or the task projection bound it) - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Examples:
lettuce migrate list --server-url https://api.example.test --domain acme --format json
Notes:
- A migration takes its destination domain's fence at session BEGIN and holds it for the whole run: the domain is offline (reads and writes refused FW-MIGRATION-FENCED) until release, rollback, abandon or expiry.
migrate abandon
Usage: lettuce migrate abandon --session ID --reason TEXT [--takeover]
End a PRE-ACTIVATION migration session (manifest-uploading, uploading, complete, finalized) (LET-1836): the session becomes abandoned with who, why and when recorded (audited in the server log), its quarantine copy is freed and the domain fence is LIFTED, so the domain serves again. Only the principal that started it may abandon it, unless an admin passes --takeover (reason required). A session whose activation has started holds canonical changes and is refused: roll it back instead. Hosted (client mode) or a LOCAL destination (--root DIR). Replaying an abandon is a no-op.
- kind:
maintenance - output:
object— --format plain prints its migrate-session reference (session_id) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--session id- Migration session id (mig_<32 hex>). Required.--reason text- Why the session is abandoned; recorded on the session and in the audit trail. Required.--takeover- Admin takeover: abandon a session another principal started (e.g. a crashed client's).
Examples:
lettuce migrate abandon --server-url https://api.example.test --session mig_0123456789abcdef0123456789abcdef --reason "client crashed; restarting the move" --takeover --author admin --format json
Notes:
- A crashed or killed client never fences a domain forever: the server's session GC abandons a pre-activation session past its TTL (default 24h) and lifts its fence; abandon does it now.
migrate release
Usage: lettuce migrate release --session ID --author ACTOR
Open an activated, still-fenced migration group (hosted in client mode, or a LOCAL destination with --root DIR, LET-1836) — the explicit confirmation AFTER the repository's authority switch. It re-runs the live parity check against the fenced group and releases with that fresh receipt, so nothing is released on a stale observation; a mismatch refuses and names the rollback. A released session is reported again (idempotent), which also lifts a fence an interrupted release left behind.
- kind:
maintenance - output:
object— --format plain prints its migrate-session reference (session_id) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--session id- Hosted migration session id (mig_<32 hex>). Required.
Examples:
lettuce migrate release --server-url https://api.example.test --session mig_0123456789abcdef0123456789abcdef --author operator --format json
Notes:
- From the repository root after the pointer switch, the pointer supplies the server and domain:
lettuce migrate release --session ID --author you.
migrate rollback
Usage: lettuce migrate rollback --session ID [--allow-released]
Roll a migration group back to its frozen destination baseline (hosted in client mode, or a LOCAL destination with --root DIR, LET-1836) through the server's journaled rollback: valid for an activated-fenced group (e.g. after an interrupted run you decide not to finish) and to complete an interrupted rollback. --allow-released additionally rolls back a RELEASED group only while the server proves zero writes since release; any post-release write refuses it (fix the pointer forward instead). The rollback runs to its durable end state server-side even if the client disconnects.
- kind:
maintenance - output:
object— --format plain prints its migrate-session reference (session_id) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--session id- Hosted migration session id (mig_<32 hex>). Required.--allow-released- Also roll back a released group, only with the server's zero-post-release-write proof.
Examples:
lettuce migrate rollback --server-url https://api.example.test --session mig_0123456789abcdef0123456789abcdef --author operator --format json
Notes:
- After a rollback, local discovery (the repository's .lettuce/ directory) stays or becomes authoritative again; re-run migrate --to to start a fresh session.
repair plan
Usage: lettuce repair plan
Produce a repair plan for current diagnostics.
- kind:
read - output:
report— --format plain prints the table bytes - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
store-inline— walks the whole store inside the request (not yet a server-owned job; it can outlast the client timeout on a large hosted store) - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Examples:
lettuce repair plan --format json
lettuce repair plan --format table
repair check
Usage: lettuce repair check --plan PATH [--allow-high-intrusion] [--semantic-plan PATH]
Validate a repair plan without applying it.
- kind:
maintenance - output:
report— --format plain prints the table bytes - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false - external input keys (these go INSIDE
data, not at the top level):plan, semantic_plan, allow_high_intrusion - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--plan path- Repair plan JSON file.--semantic-plan path- Conflict semantic plan JSON file.--allow-high-intrusion- Allow high-intrusion semantic actions during checks.
Examples:
lettuce repair check --plan ./repair-plan.json --format json
repair apply
Usage: lettuce repair apply --plan PATH [--dry-run] [--allow-high-intrusion] [--semantic-plan PATH]
Apply or dry-run a validated repair plan.
- kind:
maintenance - output:
report— --format plain prints the table bytes - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
true - external input keys (these go INSIDE
data, not at the top level):plan, semantic_plan, dry_run, allow_high_intrusion - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--plan path- Repair plan JSON file.--semantic-plan path- Conflict semantic plan JSON file.--dry-run- Preview without writing.--allow-high-intrusion- Allow high-intrusion semantic actions.
Examples:
lettuce repair apply --plan ./repair-plan.json --dry-run --author agent-1 --format json
repair automatic
Usage: lettuce repair automatic --code CODE --path PATH [--dry-run]
Apply a supported safe automatic repair action. The action is selected by --code: FW-RUNTIME-MISSING and FW-RUNTIME-DIR-MISSING (runtime-skeleton-create), FW-RUNTIME-GITIGNORE-MISSING (runtime-git-exclude), FW-RUNTIME-CACHE-CORRUPT and FW-RUNTIME-CACHE-STALE (runtime-cache-delete), FW-PATH-MISSING-REQUIRED-DIR (required-empty-dir-create), FW-FILE-MISSING-NEWLINE (scalar-final-lf), FW-FILE-LIST-DUPLICATE and FW-FILE-LIST-UNSORTED (list-canonicalize), FW-FILE-EMPTY-NOT-ALLOWED (blank-text-normalize: a whitespace-only dimension member name, description or attribute becomes the empty scalar, LET-1943), FW-REVISION-CHAIN-BROKEN (revision-heal-forward, or revision-field-heal-forward when the path is a settable task field). Any other code refuses with FW-REPAIR-UNSUPPORTED-ACTION.
- kind:
maintenance - output:
report— --format plain prints the table bytes - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--code diagnostic-code- Diagnostic code to repair. Required.--path path- Diagnostic path to repair. Required.--dry-run- Preview without writing.
Examples:
lettuce repair automatic --code FW-FILE-MISSING-NEWLINE --path projects/lettuce/tasks/LET-1/title --dry-run --format json
Notes:
- The example above only succeeds against a store that ALREADY carries the named diagnostic at the named path (here: projects/lettuce/tasks/LET-1/title missing its trailing newline) — run
validate --strictordoctorfirst to find a real --code/--path pair on your store. --code and --path are re-checked against the CURRENT on-disk state immediately before writing, so a stale pair (the diagnostic already repaired, or never present) is refused FW-REPAIR-INVALID-PLAN even with --dry-run; that refusal is expected on a store with no matching corruption, not a bug in the command. - Exception (LET-1840): FW-RUNTIME-MISSING and FW-RUNTIME-DIR-MISSING (runtime-skeleton-create) create the WHOLE runtime skeleton in one call, so a single repair resolves every sibling runtime-skeleton finding too — the result's
also_resolvedlists them. Re-running the action for one of those NAMED siblings afterward is NOT a stale-pair refusal: it succeeds as a no-op (applied:false, no error) because the whole skeleton is already complete. Do not repair each listed FW-RUNTIME-* runtime-skeleton finding one at a time — one call is enough.
sync status
Usage: lettuce sync status
Inspect dedicated Git sync state. Fetches the upstream first (the same freshness the mutation gate enforces) so ahead/behind are current; if the fetch fails it reports fetched=false with an FW-GIT-SYNC-REQUIRED warning instead of a stale in-sync.
- kind:
read - output:
report— --format plain prints the table bytes - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
local— never reads a hosted store - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Examples:
lettuce sync status --mode dedicated-git --format json
sync push
Usage: lettuce sync push
Push dedicated Git changes upstream.
- kind:
maintenance - output:
report— --format plain prints the table bytes - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
false - idempotency key:
false
Examples:
lettuce sync push --mode dedicated-git --format json
sync pull
Usage: lettuce sync pull
Pull dedicated Git upstream changes and validate. With no remote configured it refuses FW-CMD-USAGE (like sync push); with no upstream tracking branch it refuses FW-GIT-SYNC-REQUIRED naming the missing upstream.
- kind:
maintenance - output:
report— --format plain prints the table bytes - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
false - idempotency key:
false
Examples:
lettuce sync pull --mode dedicated-git --format json
conflict bundle
Usage: lettuce conflict bundle
Build a conflict-resolution bundle for semantic repair.
- kind:
read - output:
object— --format plain prints its conflict-bundle reference (bundle_id) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
store-inline— walks the whole store inside the request (not yet a server-owned job; it can outlast the client timeout on a large hosted store) - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Examples:
lettuce conflict bundle --format json
Links to
- Command Reference
reference/command-reference
Backlinks
- Command Reference
reference/command-reference - reference/index
reference/index
Queries — Commands
reference/cmd-queries lettuce Queries commands — 13 entries — query run, query search, query tasks, query audit, query timeline, query graph, query saved create, query saved update, query saved archive, query saved unarchive, query saved list, query saved show, query saved run.
lettuce command group Queries — 13 commands. Generated from lettuce usage --format okf (always in sync with the binary).
Back to Command Reference.
Commands in this group
query runquery searchquery tasksquery auditquery timelinequery graphquery saved createquery saved updatequery saved archivequery saved unarchivequery saved listquery saved showquery saved run
---
Run task queries, FQL, timelines, audits, full-text search, and saved queries.
query run
Usage: lettuce query run FQL [--case-sensitive] [--group-by FIELD] [--include-archived]
Execute one FQL statement.
- kind:
read - output:
collection— --format plain prints one row reference (reference) per row ofrows; nothing when empty; an explicit projection prints the requested columns,--group-bythe buckets (as table) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
listing— proportional to the collection it reads once (paging or the task projection bound it) - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false - external input keys (these go INSIDE
data, not at the top level):fql - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--case-sensitive- Make the contains operator match case-sensitively.--group-by field- Aggregate the matching tasks into buckets by a task field (one of assignee, component, milestone, ref, reference, severity, status, type, workflow); returns ordered {value,count} groups (count desc, value asc; a missing value falls into (none)) instead of the flat row list. With --include-archived a bucket holding archived tasks also carriesarchived(how many of its count; omitted when 0) and the table shows an ARCHIVED column (LET-1858).--include-archived- Include archived tasks in the results (hidden by default). Read an ARCHIVED project on purpose: without this flag a read scoped to an archived project is refused (FW-READ-PROJECT-ARCHIVED); with it the read succeeds and is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning, and project_archived_at in the json/yaml data).
Examples:
lettuce query run 'from tasks where status = ready select task,title' --project lettuce --format json
lettuce query run 'from tasks select task,title,status' --project lettuce --format plain
lettuce query run 'from tasks select task,status' --group-by status --project lettuce --format json
lettuce query run 'from dimensions select dimension,family,applicability' --project lettuce --format json
lettuce query run 'from cells where dod = blocking select coordinate,state' --project lettuce --format json
lettuce query run 'from cells where freshness = stale select coordinate' --project lettuce --format json
lettuce query run 'from cells where depth >= 2 select coordinate' --project lettuce --format json
lettuce query run 'from cells where dod-reason = grade select coordinate' --project lettuce --format json
lettuce query run 'from cells where scope = s1 and grade = hardened select coordinate,scope,unit,grade' --project lettuce --format json
Notes:
- FQL is positional. Read files yourself and pass the statement as an argument.
- Sources: tasks, events, registry, authors, projects, dimensions (a project's EFFECTIVE dimensions: its active pack's, source=pack, plus the ones the project declared, source=project —
where source = projectlists the runtime layer), and cells (the project's STORED coverage cells — sparse pack-defaults are NOT rows). - Every row's
referenceis what the paired show command resolves within the row'sproject: a dimensions row's reference (=dimension) is the bare slug fordimension show SLUG, and a cells row's reference (=cell) is the cell's COORDINATE forcell show COORDINATE(LET-627/628). A cell's object ref forgraph-run-case refs-tois<project>/cells/<hash>, composed from the row'sprojectandhash. - The cells source exposes the stored fields coordinate, state, hash (plus cell/project/pack identity; cell = the coordinate) AND the DOD-4 derived axes freshness (fresh|aging|stale), depth (distinct-rev confirmation count, an int), dod (blocking|met), and dod-reason (grade|depth|recency|unknown for a blocking cell) — so
from cells where dod = blocking/where freshness = stale/where depth >= 2orient you at exactly what is not yet done. It ALSO exposes the coordinate-DIMENSION columns scope, unit, dim, group, kind (each the member the cell's coordinate names for that dimension) and grade (an alias of the cell's state slug), sowhere scope = s1 and grade = hardenedslices a dimension directly instead of the opaquewhere coordinate contains "scope=s1"; a cell whose coordinate omits a dimension leaves that column absent. - Soft-archived tasks are hidden by default (matching task list / query tasks); pass --include-archived to include them. An included archived row carries an archived_at field (task list's own present-on-archived/omitted-on-live marker), so it is never indistinguishable from a live row.
- On an ARCHIVED project this read is refused with FW-READ-PROJECT-ARCHIVED (exit 1) unless --include-archived is passed; with it the result is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning and project_archived_at in the json/yaml data).
query search
Usage: lettuce query search TEXT [--scope SCOPE] [--limit N] [--offset N] [--include-archived] [--wait[=DURATION] | --no-wait]
Search canonical task, comment, run, artifact, version, and registry text.
- kind:
read - output:
collection— --format plain prints one search-hit reference (reference) per row ofhits; nothing when empty - exit codes:
0success;8hosted result not ready yet (FW-API-HEALTH-PENDING / FW-API-READ-PENDING): retry later or --wait; not a finding; any failure:1-7by diagnostic class (spec §24.11) - read cost:
project— walks the whole project: on a hosted server a background build (--wait[=DURATION] / --no-wait; exit 8 while it is pending) - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false - external input keys (these go INSIDE
data, not at the top level):text, scope, limit, offset - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--scope tasks|comments|runs|artifacts|versions|registry|all- Search scope.--limit n- Maximum hits (positive integer).--offset n- Hit offset (non-negative integer).--include-archived- Include soft-archived tasks in the results (hidden by default). Read an ARCHIVED project on purpose: without this flag a read scoped to an archived project is refused (FW-READ-PROJECT-ARCHIVED); with it the read succeeds and is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning, and project_archived_at in the json/yaml data).--wait- Hosted stores only (LET-1846): written --wait or --wait=DURATION. How long to wait for a PENDING server-side search index build (FW-API-READ-PENDING), polling with the server's Retry-After; progress on stderr. The default already waits 3m; an explicit --wait also polls while the answer comes from a STALE index whose rebuild is running, until a current one is built or the budget ends. A spent budget exits 8. No effect locally.--no-wait- Hosted stores only (LET-1846): do not wait for a pending server-side build; exit 8 (FW-API-READ-PENDING, try again later) at once. A current or stale result is still printed. No effect locally.
Examples:
lettuce query search migration --project lettuce --scope tasks --format json
lettuce query search task --project lettuce --scope all --limit 20 --offset 0 --format json
Notes:
- Hosted (LET-1846): the search INDEX is a server-owned background build (single-flight per domain and project, detached from the request, cached per writer generation, rebuilt after writes); your query is scored against the index the server has — the current one, the last one marked STALE while it rebuilds (hits from the old index, statuses read live), or 503 FW-API-READ-PENDING when none was built yet. The client waits for a pending build by default (up to --wait=DURATION, default 3m, progress on stderr); --no-wait or a spent budget exits 8. Provenance: a
search index:line on stderr (human) or meta.read_* (machine). - Soft-archived tasks are hidden by default (the same hidden-by-default contract as task list / query tasks); pass --include-archived to include them, and an included archived task hit carries archived_at. Archived comments and artifacts remain searchable — archive hides them from list views, not from discovery. Every comment hit carries its own status (active/resolved/deleted/hidden), matching comment list, so a deleted/hidden comment's hit is never indistinguishable from an active one's.
- On an ARCHIVED project this read is refused with FW-READ-PROJECT-ARCHIVED (exit 1) unless --include-archived is passed; with it the result is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning and project_archived_at in the json/yaml data).
query tasks
Usage: lettuce query tasks [--status STATUS] [filters] [--fields a,b] [--group-by FIELD] [--refs-only] [--include-archived] [--limit N] [--offset N]
Run the structured task list query.
- kind:
read - output:
collection— --format plain prints one task reference (reference) per row ofrows; nothing when empty; an explicit projection prints the requested columns,--group-bythe buckets (as table) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
listing— proportional to the collection it reads once (paging or the task projection bound it) - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false - external input keys (these go INSIDE
data, not at the top level):status, priority_min, assignee, limit, offset, fields - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--status status- Filter by workflow status.--workflow workflow- Filter by workflow reference.--assignee author- Filter by assignee.--reporter author- Filter by reporter.--label slug- Filter by label. Repeatable.--component slug- Filter by component.--milestone slug- Filter by milestone.--type slug- Filter by task type.--severity slug- Filter by severity.--priority-min n- Minimum priority, inclusive.--priority-max n- Maximum priority, inclusive.--due-before timestamp- Due at or before this timestamp.--due-after timestamp- Due at or after this timestamp.--updated-before timestamp- Updated at or before this timestamp.--updated-after timestamp- Updated at or after this timestamp.--parent task-ref- Filter by parent task reference.--watcher author- Filter by watcher. Repeatable.--text text- Filter by free-text match.--custom key=value- Filter by a custom-field value (repeatable; AND across keys). Repeatable.--fields csv- Selected fields.--group-by field- Aggregate the matching tasks into buckets by a task field (one of assignee, component, milestone, ref, reference, severity, status, type, workflow); returns ordered {value,count} groups (count desc, value asc; a missing value falls into (none)) instead of the flat row list. With --include-archived a bucket holding archived tasks also carriesarchived(how many of its count; omitted when 0) and the table shows an ARCHIVED column (LET-1858). Parity with query run --group-by.--refs-only- Return task references only.--limit n- Maximum tasks.--offset n- Task offset.--include-archived- Include archived tasks in the results (hidden by default). Read an ARCHIVED project on purpose: without this flag a read scoped to an archived project is refused (FW-READ-PROJECT-ARCHIVED); with it the read succeeds and is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning, and project_archived_at in the json/yaml data).
Examples:
lettuce query tasks --project lettuce --status ready --fields task,title,status --format json
lettuce query tasks --project lettuce --refs-only --format json
lettuce query tasks --project lettuce --group-by status --format json
Notes:
- Soft-archived tasks are hidden by default (the same hidden-by-default contract as task list); pass --include-archived to include them. An included archived row carries an archived_at field (task list's own present-on-archived/omitted-on-live marker), so it is never indistinguishable from a live row.
- On an ARCHIVED project this read is refused with FW-READ-PROJECT-ARCHIVED (exit 1) unless --include-archived is passed; with it the result is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning and project_archived_at in the json/yaml data).
query audit
Usage: lettuce query audit REF
Read audit events for a supported object reference.
- kind:
read - output:
collection— --format plain prints one event reference (reference) per row ofevents; nothing when empty - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false - external input keys (these go INSIDE
data, not at the top level):reference - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Examples:
lettuce query audit lettuce/LET-1 --format json
lettuce query audit lettuce/LET-1 --format plain
lettuce query audit lettuce --format json
lettuce query audit authors/agent-1 --format json
Notes:
- Every ledger
query timelineand FQLfrom eventsread is an audit target, under the reference they name it by, and lists exactly their events of it (LET-1967): a task (and its comments, runs, artifacts, versions), a registry object, saved query, graph definition, run-case, carrier, dimension, state or cell, the ladder overlay ({project}/ladder, also as {project}/ladder/default-state; {project}/ladder/hidden/{state}; {project}/ladder/hidden-transitions/{action}/{from}), a declared ladder transition ({project}/transitions/{action}/{from}) or gate ({project}/gates/{slug}), a root author (authors/{author}) or a project author link ({project}/authors/{author}). - Every event keeps ONE reference on every surface (LET-1968): a project author link's events ride the project ledger and are printed {project}/event/{id}, as the timeline and FQL
from eventsprint them. - A bare NAME is a live PROJECT first (LET-1969): its own ledger (project-created, author links, task-deleted, ...) with its store-root history (project-moved-in), the {project}/event/{id} events the timeline lists; otherwise a root author; otherwise a moved-out or deleted project's history. When a live project and a root author share the name, the project answers with the advisory FW-READ-AUDIT-NAME-AMBIGUOUS; read the author with
query audit authors/NAME. - A reference a hard delete removed answers
deleted: truewith its tombstone (LET-347). Its events are what the delete left on the nearest surviving ledger that target it (LET-1937): task-deleted on the project ledger for a task; for a comment or artifact, its dual-written history on the task ledger ending in comment-deleted / artifact-deleted; plus the event of the delete that removed it, wherever that delete recorded it (LET-347: a --cascade member or a comment of a deleted task carries the task-deleted, a task of a deleted project the project-deleted).query timeline --taskof a deleted task answers the same tombstone and events. - On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
query timeline
Usage: lettuce query timeline --project PROJECT [filters] [--include-archived] [--wait[=DURATION] | --no-wait]
Read a merged event timeline. It is a faithful MERGE of the project's per-object event ledgers (spec 20.8: there is no canonical project-wide stream), ordered by at, then each event's causal position (its revision-after, else a legacy numeric slot, so same-second events keep their causal order, LET-1857/LET-1953), then creation first, then operation-id and reference; from events lists the exact reverse. A child-object operation (comment/run/artifact) is recorded on BOTH that object and the task; the timeline lists it ONCE (LET-589: deduplicated on operation-id, kind and target, keeping the task-ledger copy), so its count per task equals lettuce task audit <ref>. query run 'from events' lists it once the same way (LET-1855) and reads the same ledgers (LET-1894: the project's own, registry objects, saved queries, graph defs, run cases, carriers, dimensions, states, cells), so the two list the same events; query audit <child ref> reads the child's own copy. Pass --involving AUTHOR for the per-author INBOX: every event on a task that author is involved with (its creator, assignee, reporter, a watcher, or its lease holder), including events OTHER authors wrote — the surface --author cannot give, since --author keeps only events that author authored. --author is grammar-checked and a well-formed name outside the project roster returns FW-FILTER-UNKNOWN-AUTHOR instead of a silent empty view. The result's count is the number of events in the view; combine with --since to ask what involves me since <watermark>. --task of a task a hard delete removed answers deleted: true with its tombstone, exactly as query audit does (LET-1801), also when a project delete removed the task or its project was deleted later (LET-347: the PROJECT may be gone; only a task no tombstone speaks for refuses FW-REF-MISSING-PROJECT); its events are what survived the delete: those targeting the task plus the event of the delete that removed it (task-deleted on the project ledger, LET-1937; a --cascade member carries its parent's; a task of a deleted project carries project-deleted from the store-root ledger), and --author/--kind/--since/--until apply to them as to a live task (--involving does not: involvement is read from the task record the delete removed).
- kind:
read - output:
collection— --format plain prints one event reference (reference) per row ofevents; nothing when empty - exit codes:
0success;8hosted result not ready yet (FW-API-HEALTH-PENDING / FW-API-READ-PENDING): retry later or --wait; not a finding; any failure:1-7by diagnostic class (spec §24.11) - read cost:
project— walks the whole project: on a hosted server a background build (--wait[=DURATION] / --no-wait; exit 8 while it is pending) - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false - external input keys (these go INSIDE
data, not at the top level):task_ref, author_filter, involving, kind, since, until - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--task task-ref- Restrict to a task.--author author- Restrict to an event author; malformed names are refused and unknown project authors warn.--involving author- Inbox: every event on a task this author is involved with (creator/assignee/reporter/watcher/lease-holder), whoever authored it.--kind event-kind- Restrict to an event kind.--since timestamp- Inclusive lower bound.--until timestamp- Inclusive upper bound.--include-archived- Read an ARCHIVED project on purpose: without this flag a read scoped to an archived project is refused (FW-READ-PROJECT-ARCHIVED); with it the read succeeds and is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning, and project_archived_at in the json/yaml data).--wait duration- Hosted only (LET-2003): this read is a server-owned background build; wait for a pending build up to DURATION (default 3m0s; a bare --wait also waits out a STALE answer's rebuild), printing progress on stderr. Exit 8 when the budget ends with the build still pending. Locally the flag is accepted and inert.--no-wait- Hosted only (LET-2003): do not wait for a pending build; exit 8 at once (the build keeps running). Locally inert.
Examples:
lettuce query timeline --project lettuce --kind object-created --format json
lettuce query timeline --project lettuce --task lettuce/LET-1 --format json
lettuce query timeline --project lettuce --involving agent-1 --since 2026-09-18T00:00:00Z --format json
Notes:
- On an ARCHIVED project this read is refused with FW-READ-PROJECT-ARCHIVED (exit 1) unless --include-archived is passed; with it the result is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning and project_archived_at in the json/yaml data).
query graph
Usage: lettuce query graph --project PROJECT [--relation depends-on|blocks] [--include-archived]
Analyze the project dependency DAG: report cycles and the critical (longest) dependency path.
- kind:
read - output:
report— --format plain prints the table bytes - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
listing— proportional to the collection it reads once (paging or the task projection bound it) - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--relation depends-on|blocks- Dependency relation to analyze (default depends-on).--include-archived- Include soft-archived tasks in the DAG (default: hidden, consistent with query run / task graph); archived tasks otherwise distort roots/cycles/critical-path. Read an ARCHIVED project on purpose: without this flag a read scoped to an archived project is refused (FW-READ-PROJECT-ARCHIVED); with it the read succeeds and is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning, and project_archived_at in the json/yaml data).
Examples:
lettuce query graph --project lettuce --format json
lettuce query graph --project lettuce --relation blocks --format json
Notes:
- On an ARCHIVED project this read is refused with FW-READ-PROJECT-ARCHIVED (exit 1) unless --include-archived is passed; with it the result is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning and project_archived_at in the json/yaml data).
query saved create
Usage: lettuce query saved create SLUG --title TITLE --fql FQL [--description TEXT] [--description-file PATH]
Create a project-scoped saved FQL query.
- kind:
mutation - output:
object— --format plain prints its saved-query reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
true - external input keys (these go INSIDE
data, not at the top level):slug, title, fql, description - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--title title- Saved query title. Required.--fql statement- Stored FQL statement. Required.--description text- Optional Markdown description.--description-file path- Read description from file.
Examples:
lettuce query saved create ready-tasks --project lettuce --author agent-1 --title 'Ready Tasks' --fql 'from tasks where status = ready select task,title' --format json
query saved update
Usage: lettuce query saved update REF [--title TITLE] [--fql FQL] [--expect-revision N] [--description TEXT] [--description-file PATH]
Update saved query fields.
- kind:
mutation - output:
object— --format plain prints its saved-query reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
true - external input keys (these go INSIDE
data, not at the top level):reference, title, fql, description, expected_revision - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--title title- Replacement title.--fql statement- Replacement FQL statement.--description text- Replacement Markdown description.--description-file path- Read replacement description from file.--expect-revision n- Expected saved query revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch.
Examples:
lettuce query saved update ready-tasks --project lettuce --author agent-1 --title 'Ready Work' --expect-revision 1 --format json
lettuce query saved update ready-tasks --project lettuce --author agent-1 --fql 'from tasks where status = ready select task,title,priority' --expect-revision 2 --format plain
query saved archive
Usage: lettuce query saved archive REF --expect-revision N
Archive a saved query without hard deleting history. Reversible with query saved unarchive.
- kind:
mutation - output:
object— --format plain prints its saved-query reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
true - external input keys (these go INSIDE
data, not at the top level):reference, expected_revision - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--expect-revision n- Expected saved query revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch. Required.
Examples:
lettuce query saved archive ready-tasks --project lettuce --author agent-1 --expect-revision 2 --format json
query saved unarchive
Usage: lettuce query saved unarchive REF --expect-revision N
Restore an archived saved query to active. The inverse of query saved archive, and like every other archival pair a NORMAL mutation in both directions: it requires an explicit author, records a saved-query-unarchived event, and is refused on a query that is not archived. Before LET-1516 this verb did not exist, so a single archive disabled the object permanently — re-creating under the same slug is refused, a saved query is not a registry kind so registry update cannot reach it, and query saved list kept showing a tombstone that could not be revived.
- kind:
mutation - output:
object— --format plain prints its saved-query reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
true - external input keys (these go INSIDE
data, not at the top level):reference, expected_revision - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--expect-revision n- Expected saved query revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch. Required.
Examples:
lettuce query saved unarchive ready-tasks --project lettuce --author agent-1 --expect-revision 3 --format json
query saved list
Usage: lettuce query saved list --project PROJECT [--include-archived]
List saved query metadata.
- kind:
read - output:
collection— --format plain prints one saved-query reference (reference) per row ofsaved_queries; nothing when empty - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
listing— proportional to the collection it reads once (paging or the task projection bound it) - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--include-archived- Read an ARCHIVED project on purpose: without this flag a read scoped to an archived project is refused (FW-READ-PROJECT-ARCHIVED); with it the read succeeds and is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning, and project_archived_at in the json/yaml data).
Examples:
lettuce query saved list --project lettuce --format json
lettuce query saved list --project lettuce --format plain
Notes:
- On an ARCHIVED project this read is refused with FW-READ-PROJECT-ARCHIVED (exit 1) unless --include-archived is passed; with it the result is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning and project_archived_at in the json/yaml data).
query saved show
Usage: lettuce query saved show REF
Read one saved query.
- kind:
read - output:
object— --format plain prints its saved-query reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false - external input keys (these go INSIDE
data, not at the top level):reference - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Examples:
lettuce query saved show lettuce/ready-tasks --format json
lettuce query saved show lettuce/ready-tasks --format plain
Notes:
- On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
query saved run
Usage: lettuce query saved run REF [--include-archived]
Execute a stored saved query.
- kind:
read - output:
collection— --format plain prints one row reference (reference) per row ofresult.rows; nothing when empty; an explicit projection prints the requested columns,--group-bythe buckets (as table) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
listing— proportional to the collection it reads once (paging or the task projection bound it) - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false - external input keys (these go INSIDE
data, not at the top level):reference - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--include-archived- Include archived tasks in the results (hidden by default). Read an ARCHIVED project on purpose: without this flag a read scoped to an archived project is refused (FW-READ-PROJECT-ARCHIVED); with it the read succeeds and is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning, and project_archived_at in the json/yaml data).
Examples:
lettuce query saved run lettuce/ready-tasks --format json
lettuce query saved run lettuce/ready-tasks --format plain
Notes:
- Soft-archived tasks are hidden by default (matching query run); pass --include-archived to include them.
- On an ARCHIVED project this read is refused with FW-READ-PROJECT-ARCHIVED (exit 1) unless --include-archived is passed; with it the result is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning and project_archived_at in the json/yaml data).
Links to
- Command Reference
reference/command-reference
Backlinks
- Dependencies — what depends-on asserts, and which end you start from
concepts/concept-dependency - Querying with FQL
concepts/concept-query - Tasks and the work plane
concepts/concept-task - Command Reference
reference/command-reference - reference/index
reference/index
Registries And Workflow — Commands
reference/cmd-registries-and-workflow lettuce Registries And Workflow commands — 15 entries — registry create, registry update, registry list, registry show, milestone create, milestone list, milestone show, milestone close, milestone reopen, milestone set-stage, workflow list, workflow show, workflow transition list, workflow transitio
lettuce command group Registries And Workflow — 15 commands. Generated from lettuce usage --format okf (always in sync with the binary).
Back to Command Reference.
Commands in this group
registry createregistry updateregistry listregistry showmilestone createmilestone listmilestone showmilestone closemilestone reopenmilestone set-stageworkflow listworkflow showworkflow transition listworkflow transition showworkflow revise
---
Manage labels, components, milestones, workflow, task types, artifact types, severities, and custom fields.
registry create
Usage: lettuce registry create KIND SLUG [registry flags]
Create a registry object.
- kind:
mutation - output:
object— --format plain prints its registry-object reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
true - external input keys (these go INSIDE
data, not at the top level):kind, slug, title, status, color, owner, due_at, rank, value_kind, required, description, allowed_values, workflow - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--title title- Registry title.--status status- Registry object status (active/inactive; milestones also open/done/canceled).--color #rrggbb- Display color.--owner author- Owner author.--due-at timestamp- Due timestamp.--rank rank- Sort rank.--value-kind kind- Custom field value kind: string, number, boolean, date, timestamp, author, label, task, graph-run-case, url, or slug. The REFERENCE kinds (author/label/task/graph-run-case) are resolved against the store, not pattern-matched —custom setrefuses a value that names nothing, and a workflow requires-field gate over such a field is satisfied only by a value that still resolves.graph-run-caseis the strongest: the gate additionally requires the cited run-case to have recorded a produced-effect on the gated task (the reverse ofgraph-run-case refs-to), so citing a real run-case that never touched the ticket is refused.--required true|false- Custom field required policy.--allowed-value value- Allowed custom field value. Repeatable.--description text- Markdown description. An empty or whitespace-only value is the explicit EMPTY description (a stored empty version: on update it clears the description; it is distinct from never setting one, LET-1487); whitespace is never stored (LET-680).--description-file path- Read description from file.--workflow-file path- Workflow definition JSON file.--workflow-json json- Inline workflow definition JSON.--idempotent- Treat an existing matching registry object as success.
Examples:
lettuce registry create label backend --project lettuce --author agent-1 --title Backend --color '#12abef' --format json
Notes:
- KIND values include label, component, milestone, workflow, task-type, artifact-type, severity, and custom-field.
registry update
Usage: lettuce registry update KIND SLUG [registry flags]
Update mutable registry object fields.
- kind:
mutation - output:
object— --format plain prints its registry-object reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
true - external input keys (these go INSIDE
data, not at the top level):kind, slug, title, status, color, owner, due_at, rank, stage, confidence, value_kind, required, description, allowed_values - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--title title- Registry title.--status status- Registry object status (active/inactive; milestones also open/done/canceled).--color #rrggbb- Display color.--owner author- Owner author.--due-at timestamp- Due timestamp.--rank rank- Sort rank.--value-kind kind- Custom field value kind: string, number, boolean, date, timestamp, author, label, task, graph-run-case, url, or slug. The REFERENCE kinds (author/label/task/graph-run-case) are resolved against the store, not pattern-matched —custom setrefuses a value that names nothing, and a workflow requires-field gate over such a field is satisfied only by a value that still resolves.graph-run-caseis the strongest: the gate additionally requires the cited run-case to have recorded a produced-effect on the gated task (the reverse ofgraph-run-case refs-to), so citing a real run-case that never touched the ticket is refused.--required true|false- Custom field required policy.--allowed-value value- Allowed custom field value. Repeatable.--description text- Markdown description. An empty or whitespace-only value is the explicit EMPTY description (a stored empty version: on update it clears the description; it is distinct from never setting one, LET-1487); whitespace is never stored (LET-680).--description-file path- Read description from file.--stage stage- Milestone hypothesis-ladder stage (milestone kind only; drives its S4 ladder rung).--confidence confidence- Milestone stage confidence label (milestone kind only).
Examples:
lettuce registry update label backend --project lettuce --author agent-1 --status inactive --format json
registry list
Usage: lettuce registry list KIND [--include-archived]
List registry objects of one kind.
- kind:
read - output:
collection— --format plain prints one registry-object reference (reference) per row ofitems; nothing when empty - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
listing— proportional to the collection it reads once (paging or the task projection bound it) - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--include-archived- Read an ARCHIVED project on purpose: without this flag a read scoped to an archived project is refused (FW-READ-PROJECT-ARCHIVED); with it the read succeeds and is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning, and project_archived_at in the json/yaml data).
Examples:
lettuce registry list label --project lettuce --format json
lettuce registry list label --project lettuce --format plain
Notes:
- On an ARCHIVED project this read is refused with FW-READ-PROJECT-ARCHIVED (exit 1) unless --include-archived is passed; with it the result is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning and project_archived_at in the json/yaml data).
registry show
Usage: lettuce registry show (KIND SLUG | REF) [--with-description]
Read one registry object.
- kind:
read - output:
object— --format plain prints its registry-object reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
listing— proportional to the collection it reads once (paging or the task projection bound it) - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--with-description- Include the object's description text in the payload. OMITTED by default: descriptions are free-form prose and can be long, so a plain read stays compact and the caller opts in when they want it (LET-615).
Examples:
lettuce registry show label backend --project lettuce --format json
lettuce registry show label backend --with-description --project lettuce --format json
lettuce registry show label backend --project lettuce --format yaml
Notes:
- REF is {project}/{kind}/{slug}, exactly what
registry list --format plainprints. It is read in the project it names, with or without a project context; a typed --project that differs warns FW-CMD-AMBIGUOUS-PROJECT-CONTEXT (LET-1799, LET-1916). - On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
milestone create
Usage: lettuce milestone create SLUG [--title TITLE] [--due-at TS] [milestone flags]
Create a milestone. Convenience wrapper over registry create milestone with the milestone kind implied.
- kind:
mutation - output:
object— --format plain prints its milestone reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
true
Flags:
--title title- Registry title.--status status- Registry object status (active/inactive; milestones also open/done/canceled).--color #rrggbb- Display color.--owner author- Owner author.--due-at timestamp- Due timestamp.--rank rank- Sort rank.--value-kind kind- Custom field value kind: string, number, boolean, date, timestamp, author, label, task, graph-run-case, url, or slug. The REFERENCE kinds (author/label/task/graph-run-case) are resolved against the store, not pattern-matched —custom setrefuses a value that names nothing, and a workflow requires-field gate over such a field is satisfied only by a value that still resolves.graph-run-caseis the strongest: the gate additionally requires the cited run-case to have recorded a produced-effect on the gated task (the reverse ofgraph-run-case refs-to), so citing a real run-case that never touched the ticket is refused.--required true|false- Custom field required policy.--allowed-value value- Allowed custom field value. Repeatable.--description text- Markdown description. An empty or whitespace-only value is the explicit EMPTY description (a stored empty version: on update it clears the description; it is distinct from never setting one, LET-1487); whitespace is never stored (LET-680).--description-file path- Read description from file.--workflow-file path- Workflow definition JSON file.--workflow-json json- Inline workflow definition JSON.--idempotent- Treat an existing matching registry object as success.
Examples:
lettuce milestone create release-1 --project lettuce --author agent-1 --title 'Release 1' --format json
Notes:
- Equivalent to
lettuce registry create milestone SLUG [flags].
milestone list
Usage: lettuce milestone list [--include-archived]
List milestones in a project.
- kind:
read - output:
collection— --format plain prints one milestone reference (reference) per row ofitems; nothing when empty - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
listing— proportional to the collection it reads once (paging or the task projection bound it) - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--include-archived- Read an ARCHIVED project on purpose: without this flag a read scoped to an archived project is refused (FW-READ-PROJECT-ARCHIVED); with it the read succeeds and is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning, and project_archived_at in the json/yaml data).
Examples:
lettuce milestone list --project lettuce --format json
lettuce milestone list --project lettuce --format plain
Notes:
- On an ARCHIVED project this read is refused with FW-READ-PROJECT-ARCHIVED (exit 1) unless --include-archived is passed; with it the result is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning and project_archived_at in the json/yaml data).
milestone show
Usage: lettuce milestone show (SLUG | REF)
Read one milestone, including its task-completion progress rollup.
- kind:
read - output:
object— --format plain prints its milestone reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
listing— proportional to the collection it reads once (paging or the task projection bound it) - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Examples:
lettuce milestone show release-1 --project lettuce --format json
lettuce milestone show release-1 --project lettuce --format yaml
Notes:
- REF is {project}/milestone/{slug}, exactly what
milestone list --format plainprints. It is read in the project it names, with or without a project context; a typed --project that differs warns FW-CMD-AMBIGUOUS-PROJECT-CONTEXT (LET-1799, LET-1916). - On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
milestone close
Usage: lettuce milestone close SLUG
Close a milestone (sets status=done).
- kind:
mutation - output:
object— --format plain prints its milestone reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
true
Examples:
lettuce milestone close release-1 --project lettuce --author agent-1 --format json
Notes:
- Equivalent to
lettuce registry update milestone SLUG --status done.
milestone reopen
Usage: lettuce milestone reopen SLUG
Reopen a milestone (sets status=active). The symmetric inverse of milestone close, and the same status a freshly created milestone carries, so a closed or LET-436 strict-invalid milestone can be reactivated from the milestone surface itself.
- kind:
mutation - output:
object— --format plain prints its milestone reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
true
Examples:
lettuce milestone reopen release-1 --project lettuce --author agent-1 --format json
Notes:
- Equivalent to
lettuce registry update milestone SLUG --status active.
milestone set-stage
Usage: lettuce milestone set-stage SLUG --stage STAGE [--confidence C]
Progress a milestone's first-class hypothesis-ladder STAGE (and optional CONFIDENCE) — the fields the board S4 ladder renders its rung from. --stage is required; --confidence is optional. Both are stored as first-class scalar fields (not parsed from the description), so the ladder position is real data. Each is an evented mutation.
- kind:
mutation - output:
object— --format plain prints its milestone reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--stage stage- The milestone's hypothesis-ladder stage (drives its S4 ladder rung; e.g. hypothesis, validated). Required. An empty or whitespace-only value CLEARS the stage (an optional scalar, spec §3.3.1), so the ladder falls back to its unset reading. Required.--confidence confidence- Confidence label in that stage (e.g. low, high). Optional. An empty or whitespace-only value CLEARS it.
Examples:
lettuce milestone set-stage release-1 --stage validated --confidence high --project lettuce --author agent-1 --format json
Notes:
- The stage/confidence are also settable one-at-a-time via
registry update milestone SLUG --stage STAGE/--confidence C;set-stageis the compound convenience. The S4 ladder falls back to parsing a legacy milestone'sstage (confidence):description prefix only when these fields are empty.
workflow list
Usage: lettuce workflow list [--include-archived]
List workflow registry objects.
- kind:
read - output:
collection— --format plain prints one workflow reference (reference) per row ofitems; nothing when empty - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
listing— proportional to the collection it reads once (paging or the task projection bound it) - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--include-archived- Read an ARCHIVED project on purpose: without this flag a read scoped to an archived project is refused (FW-READ-PROJECT-ARCHIVED); with it the read succeeds and is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning, and project_archived_at in the json/yaml data).
Examples:
lettuce workflow list --project lettuce --format json
lettuce workflow list --project lettuce --format plain
Notes:
- On an ARCHIVED project this read is refused with FW-READ-PROJECT-ARCHIVED (exit 1) unless --include-archived is passed; with it the result is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning and project_archived_at in the json/yaml data).
workflow show
Usage: lettuce workflow show (WORKFLOW | REF)
Show workflow states and transitions, plus the LET-727 judgement-policy version record: which append-only version under workflows/<slug>/spec/ the live states and transitions correspond to (policy.matched_version), the hash of the policy as it stands right now (policy.live_hash), and any finding that the two disagree. A workflow is the BAR a task is judged against, so 'what does it enforce' is only half the answer without 'and is that the policy it has on record'. The policy block is OMITTED entirely for a workflow with no recorded versions — every store predating LET-727 — because absent history is not a finding, and an all-zero verdict would read as one.
- kind:
read - output:
object— --format plain prints its workflow reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false - external input keys (these go INSIDE
data, not at the top level):workflow - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Examples:
lettuce workflow show default --project lettuce --format json
lettuce workflow show default --project lettuce --format plain
Notes:
- An in-place edit of a gate (say removing transitions/N/requires-lease by hand) leaves the workflow perfectly well-formed, so validate --strict still passes; it shows up HERE as a live_hash matching no recorded version, and as FW-WF-POLICY-UNVERSIONED from lettuce doctor.
- REF is {project}/workflow/{slug}, exactly what
workflow list --format plainprints. It is read in the project it names, with or without a project context; a typed --project that differs warns FW-CMD-AMBIGUOUS-PROJECT-CONTEXT (LET-1799, LET-1916). - On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
workflow transition list
Usage: lettuce workflow transition list WORKFLOW
List workflow transitions.
- kind:
read - output:
collection— --format plain prints one workflow-transition reference ({^project}/workflow/{^workflow}/transition/{number}) per row oftransitions; nothing when empty - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false - external input keys (these go INSIDE
data, not at the top level):workflow - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Examples:
lettuce workflow transition list default --project lettuce --format json
lettuce workflow transition list default --project lettuce --format plain
Notes:
- On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
workflow transition show
Usage: lettuce workflow transition show (REF | WORKFLOW NUMBER)
Show one workflow transition: its action, from/to states and requirements. REF is the transition's reference {project}/workflow/{workflow}/transition/{number} — exactly what workflow transition list --format plain prints.
- kind:
read - output:
object— --format plain prints its workflow-transition reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Examples:
lettuce workflow transition show lettuce/workflow/default/transition/3 --project lettuce --format json
lettuce workflow transition show default 3 --project lettuce --format table
Notes:
- REF is {project}/workflow/{workflow}/transition/{number}, exactly what
workflow transition list --format plainprints. It is read in the project it names, with or without a project context; a typed --project that differs warns FW-CMD-AMBIGUOUS-PROJECT-CONTEXT (LET-1799, LET-1916). - On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
workflow revise
Usage: lettuce workflow revise WORKFLOW (--workflow-file PATH|--workflow-json JSON|--declare-default-outcomes) [--yes]
REVISE an existing workflow: replace its states and transitions with a complete new definition and append the resulting judgement policy as the next immutable version under workflows/<slug>/spec/{N+1}, re-pinning effective-hash from it. This is the verb that INSTALLS a gate on a store that already has tasks — registry create workflow --idempotent refuses (FW-CMD-IDEMPOTENCY-CONFLICT, correctly: idempotent means no-op, not overwrite) and a task cannot be moved between workflows, so before this a gated workflow could be authored but never applied. An IDENTICAL definition is a NO-OP: it reports revised=false and mints no version, so re-running a deploy script cannot inflate the history. An UNSOUND definition is refused with NOTHING written, and prior versions are never mutated. A revision that would leave in-flight tasks unable to make the transition in front of them is refused FW-WF-REVISE-UNCONFIRMED, NAMING those tasks, unless --yes is passed — which makes an unconfirmed run a safe dry run of what a revision would cost.
- kind:
mutation - output:
object— --format plain prints its workflow reference ({project}/workflow/{workflow}) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--workflow-file path- Path to the COMPLETE new workflow definition (same JSON schema as registry create's --workflow-file). Required unless --workflow-json is given; there is no default, because defaulting a revision to the built-in workflow would silently reset a customised policy.--workflow-json json- The COMPLETE new workflow definition as inline JSON (same schema as --workflow-file).--declare-default-outcomes- LET-1261 (ADR-0025): revise the LIVE definition to declare the bundled default terminal outcomes (done=completed, canceled=abandoned, failed=failed) and nothing else — the recorded, idempotent migration for a workflow that declares none (doctor: FW-WF-OUTCOME-UNDECLARED). Takes no definition; refused FW-WF-OUTCOME-NOT-DEFAULT unless the terminal states are exactly done/canceled/failed or a state already declares a different outcome. A definition file may instead carry "outcome" on each terminal state.--yes- Confirm a revision that would strand in-flight tasks (--force is accepted too).
Examples:
lettuce workflow revise default --workflow-file ./gated.json --project lettuce --author agent-1 --format json
lettuce workflow revise default --workflow-file ./gated.json --yes --project lettuce --author agent-1 --format json
Notes:
- Run it WITHOUT --yes first: the refusal lists every task the new gates would block and writes nothing.
- requires_lease and requires_reason are never reported as breakage — they are satisfied by the caller at transition time, not by task state.
- Available through local, dedicated-git, or hosted backends. HTTP uses POST /v1/projects/{project}/workflows/{workflow}/revise and requires the admin role, including --declare-default-outcomes (LET-1865).
Links to
- Command Reference
reference/command-reference
Backlinks
- Milestones and Definition of Done
concepts/concept-milestone-dod - Workflow — states, transitions, and gates
concepts/concept-workflow - Agentic loop demo — one development cycle, step by step
guides/guide-agentic-loop-demo - Command Reference
reference/command-reference - reference/index
reference/index
Server And GitHub — Commands
reference/cmd-server-and-github lettuce Server And GitHub commands — 5 entries — serve, domain create, domain list, client list, github init-repo.
lettuce command group Server And GitHub — 5 commands. Generated from lettuce usage --format okf (always in sync with the binary).
Back to Command Reference.
Commands in this group
servedomain createdomain listclient listgithub init-repo
---
Run the HTTP server and bootstrap GitHub repositories.
serve
Usage: lettuce serve [--listen ADDR] [--secret SECRET | --wordmade-id-verify-url URL ...] [--authz-policy PATH] [server flags]
Start the HTTP API server. Fail-closed (ADR-0008): a non-loopback --listen requires authentication (a shared secret, a --tokens-file, or complete Wordmade ID authentication), unless --allow-unauthenticated-nonloopback is given, in which case startup warns.
- kind:
server - output:
report— --format plain prints the table bytes - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
false - idempotency key:
false
Flags:
--listen addr- Listen address. Default 127.0.0.1:8727. A non-loopback address requires authentication (see fail-closed note).--secret secret- Legacy bearer token secret; cannot be combined with Wordmade ID mode.--secret-file path- Read legacy bearer token secret from file (also via LETTUCE_API_SECRET_FILE).--allow-ip ip-or-cidr- Allowed client IP or CIDR. Repeatable.--trusted-proxy-cidr ip-or-cidr- Trusted proxy source range. Unbounded 0.0.0.0/0 and ::/0 are refused because they permit forwarding-header spoofing. Repeatable.--rate-limit rpm- Legacy per-IP requests/minute (seeds --rate-ip-rpm; see the four ADR-0003 gates below). Also LETTUCE_RATE_LIMIT.--rate-ip-rpm rpm- Per-IP requests/minute (ADR default 600). Also LETTUCE_RATE_IP_RPM.--rate-actor-mutation-rpm rpm- Per-actor admitted mutations/minute (default 300; the Helm chart sets 600); route, actor, and JSON refusals do not spend it. Also LETTUCE_RATE_ACTOR_MUTATION_RPM.--rate-global-mutation-rpm rpm- Global admitted mutations/minute across all actors (default 600, matching the Helm chart; ADR-0003's original 60 tripped with a handful of concurrent agents, LET-1805); pre-admission refusals do not spend it. Also LETTUCE_RATE_GLOBAL_MUTATION_RPM.--rate-burst n- Per-IP burst: max requests in a 1s window (ADR default 60). Also LETTUCE_RATE_BURST.--authz-policy path- Authorization policy JSON file. Required in Wordmade ID mode.--auth-subject subject- Legacy authenticated subject name (ignored in Wordmade ID mode).--wordmade-id-verify-url url- Wordmade ID /v1/verify endpoint. Enables Wordmade ID mode when combined with its required settings. Also LETTUCE_WORDMADE_ID_VERIFY_URL.--wordmade-id-audience audience- Legacy exact OAuth audience expected from Wordmade ID. Also LETTUCE_WORDMADE_ID_AUDIENCE.--wordmade-id-audiences audience,...- Bounded comma-separated additional exact OAuth audiences for a client rotation. Also LETTUCE_WORDMADE_ID_AUDIENCES; wildcard and duplicate values are refused.--wordmade-id-organization name- Required X-Lettuce-Organization header value. Also LETTUCE_WORDMADE_ID_ORGANIZATION.--wordmade-id-organization-token-file path- Required X-Lettuce-Organization-Token secret file. Also LETTUCE_WORDMADE_ID_ORGANIZATION_TOKEN_FILE.--auto-provision-actors- Create missing derived agent authors for authorized mutations.--allow-unauthenticated-nonloopback- Explicitly permit serving on a non-loopback address WITHOUT authentication (ADR fail-closed override); startup warns.--recover-on-start- Self-heal a crashed prior writer at startup. Filesystem mode reclaims only a provably dead local writer and never discards canonical data; uncertain/cross-host ownership stays fail-closed. Dedicated-git first runs the same recover aslettuce recover --abandon(re-committing any acknowledged operation whose durable commit failed, FW-GIT-COMMIT-FAILED), then discards only the remaining unattributed torn state back to HEAD under the sole-writer assertion; if that recover fails, an acknowledged operation is still uncommitted, or a file outside the store's canonical entries (e.g. serve.log) is in the worktree, startup is refused with FW-RUNTIME-RECOVERY-FAILED and nothing is discarded. Also LETTUCE_RECOVER_ON_START.--push-interval duration- Auto-push to git remote at interval (e.g. 5m). dedicated-git only.--domains-root path- Serve MULTIPLE domains (LET-1764): every initialized store folder BASE/<name> is the domain <name>, resolved per request (a folder created withlettuce domain createis served without a restart); the default domain is BASE/default. Mutually exclusive with --root; filesystem mode only. Also LETTUCE_DOMAINS_ROOT.--health-max-scans n- Most doctor/validate scans run at once in this process, across all domains (default 1; LET-1835, ADR-0022). Scans are background jobs: the rest QUEUE (requests ahead of server refreshes; at most 32 queued or running, beyond that 503 FW-API-HEALTH-SCAN-BUSY with Retry-After). Also LETTUCE_HEALTH_MAX_SCANS.--health-scans-per-hour n- Doctor/validate scans one authorization subject may START per hour (default 12); current reports, joining a queued/running scan and server-triggered refreshes are free. Over it: 429 FW-API-HEALTH-SCAN-RATE-LIMITED with Retry-After, or the last report served stale with meta.health_refresh=rate-limited. Also LETTUCE_HEALTH_SCANS_PER_HOUR.--heavy-read-max-builds n- Most board-export and search-index builds run at once in this process, across all domains (default 1; LET-1846).board next|export|renderandquery searchread a result the server builds in the BACKGROUND (single-flight per domain, project and options; a request waits at most a few seconds and never runs the build itself): the current result, the last one marked stale while it rebuilds, or 503 FW-API-READ-PENDING with Retry-After. The rest queue (requests ahead of the rebuilds a write triggers; at most 32 queued or running, beyond that 503 FW-API-READ-BUSY). Also LETTUCE_HEAVY_READ_MAX_BUILDS (Helm server.heavyReadMaxBuilds).--health-refresh-interval duration- Server-owned health (LET-1835 phase 2): every interval, refresh (low priority, uncharged) each doctor/validate report the server knows about — in memory or persisted under .runtime/state/health — that is stale or older than the interval; also refresh a domain's reports and its store-wide doctor after a migration release or rollback. Default 6h; 0 turns every server-triggered scan off. Also LETTUCE_HEALTH_REFRESH_INTERVAL (Helm server.healthRefreshInterval).--migration-max-entries n- Migration ADMISSION GUARD: the most manifest entries (files + directories) one migrate --to group may declare; a larger group is refused FW-MIGRATION-TOO-LARGE at session create and seal before any work, and an interrupted activation above it is parked (fenced, not resumed) at restart. Default 1000000, sized for a 512 MiB container. Also LETTUCE_MIGRATION_MAX_ENTRIES (Helm server.migrationMaxEntries).--tokens-file path- YAML/JSON bearer-token file (LET-1764): tokens: [{name, secret_file|secret_sha256, default_domain, domains, default_access}]. Each token authenticates as its NAME (the --authz-policy subject), reaches its listed domains plus default (unless default_access: false, which then requires a default_domain among its listed domains), and uses default_domain when a request sends no X-Lettuce-Domain. Secrets are never inline. Works beside --secret; not with Wordmade ID. Also LETTUCE_TOKENS_FILE. SIGHUP re-reads and re-validates the file and swaps the token set atomically (no restart); an invalid file is refused, the previous set keeps serving and the refusal is logged (LET-1809).--migration-max-sessions n- LET-1836: cap on concurrent non-terminal migration sessions across EVERY domain this server serves (each holds a quarantine copy and working memory); a begin over the cap is refused 409 FW-MIGRATION-BUSY. Default 2. Also LETTUCE_MIGRATION_MAX_SESSIONS / Helm server.migrationMaxSessions. One session per domain is structural (the domain fence).--migration-retention duration- LET-1836: how long TERMINAL migration session records (abandoned, stale, released, rolled back) are kept formigrate listand audit before the session GC (every 5m) removes them. Default 168h. Also LETTUCE_MIGRATION_RETENTION / Helm server.migrationRetention.--single-writer-host-takeover- LET-1826: declare this server the ONLY writer of its volume (filesystem mode; refused with dedicated-git). Off by default. At startup / first open a domain's writer recovery then also reclaims a stranded owner recorded by ANOTHER host (the pod this one replaced after an OOM kill, node death or SIGKILL), through the same bounded dead-writer recovery, but only when that host's serve heartbeat (.runtime/state/serve-heartbeats/<host>, renewed every 5s by every serve) stayed unchanged for a 15s observation window on this server's clock AND the record predates this server's start; otherwise it is refused and stays forlettuce recover --abandon. A same-host owner keeps the pid proof, so a live local writer is never reclaimed, and a running server never takes a lock over mid-life. Logged as FW-RUNTIME-WRITER-HOST-TAKEOVER / -REFUSED with the old owner's host and pid; counted in GET /v1/health/domain writer_recovery. Only safe with one replica, strategy Recreate and a single-node (ReadWriteOnce/ReadWriteOncePod) volume — the Helm chart refuses to render it otherwise. Also LETTUCE_SINGLE_WRITER_HOST_TAKEOVER=true (Helm server.singleWriterHostTakeover).--runtime-cleanup-interval duration- LET-1808: every interval, prune each served domain's RELEASED operation records older than --runtime-retention and its COMPLETED idempotency records past their replay window (24h), under the store write lock taken fail-fast (a busy store is skipped until the next pass). Active and write-phase operations and in-progress claims are never touched. One pass removes at most 250 records of each kind (oldest released records first) and examines at most 1,000 idempotency records, resuming where the previous pass stopped (LET-1914), so it holds the lock briefly however large the backlog; a backlog drains over the following passes (runtime_cleanup.pending). Default 10m; 0 turns it off. Logged as "runtime cleanup" per domain; the last pass is in GET /v1/health/domain runtime_cleanup. Also LETTUCE_RUNTIME_CLEANUP_INTERVAL (Helm server.runtimeCleanupInterval).--runtime-retention duration- LET-1808: how long a released operation record is kept after it ended before the periodic runtime cleanup removes it (idempotency records follow their own 24h replay window). Default 24h. Also LETTUCE_RUNTIME_RETENTION (Helm server.runtimeRetention).--client-binaries-dir path- LET-1932: the directory holding the client binaries of THIS server's release (lettuce-{os}-{arch}, its .sha256 and a VERSION manifest), served to any authenticated caller at GET /v1/client/{os}-{arch}[.sha256] forlettuce self-update. The release image bakes them at the default /usr/local/share/lettuce/clients; a missing directory, a manifest naming another release, or a binary that does not match its checksum answers FW-CLIENT-BINARY-UNAVAILABLE (never a wrong binary). Also LETTUCE_CLIENT_BINARIES_DIR.--min-client-version vX.Y.Z- LET-1932, OFF by default: the oldest lettuce client release allowed to MUTATE. A mutation whose X-Lettuce-Client-Version is an older release is refused 426 FW-CLIENT-TOO-OLD (first actionlettuce self-update); reads always pass so the agent can read the fix, and a request naming no release (raw HTTP, a dev build) is never judged. Must be an exact release tag no newer than this server. Also LETTUCE_MIN_CLIENT_VERSION (Helm server.minClientVersion).--refuse-unversioned-clients- LET-2002, OFF by default: refuse every MUTATION from a lettuce client that sends no X-Lettuce-Client-Version (a release older than v0.19.0, which --min-client-version cannot judge) with 426 FW-CLIENT-TOO-OLD, whose actions are the by-hand install from GET /v1/client/{os}-{arch} (such a client has nolettuce self-update). Reads always pass. A client is recognized as an old lettuce binary by sending no version and Go's default User-Agent; raw HTTP callers (curl, scripts) and versioned clients are never refused. Without it such a client is only TOLD to update (the FW-CLIENT-VERSION-UNKNOWN advisory, once per subject and actor every 10 minutes). Also LETTUCE_REFUSE_UNVERSIONED_CLIENTS=true (Helm server.refuseUnversionedClients).--identity-coordination- Require Kubernetes Lease-qualified identity-writer coordination and recover a selected resolved enrollment before serving. Also LETTUCE_IDENTITY_COORDINATION.
Examples:
lettuce serve --root . --listen 127.0.0.1:8727
Notes:
- Domains: a request selects one with the X-Lettuce-Domain header (client: --domain / LETTUCE_DOMAIN); absent means the token's default domain. An unknown domain and one the token may not reach are the SAME 403 FW-DOMAIN-FORBIDDEN, so domain existence is not disclosed; a malformed name is 400 FW-DOMAIN-INVALID. GET /v1/domains lists the calling token's reachable domains. Rate limits stay process-wide across domains; a 429 carries Retry-After = the seconds until the tripped limit resets (LET-1805). Wordmade ID principals reach only the default domain for now.
- Operations: GET /v1/readyz is credential-free (like /v1/health) for readiness probes: 503 only when the DEFAULT domain is unserviceable, plus counts (never names) of other domains that are fenced, recovery-required or unavailable. A structured JSON log on stderr records the effective configuration (no secrets), each domain's open/recovery outcome and every 5xx response.
domain create
Usage: lettuce domain create NAME --domains-root BASE
Create an API-service DOMAIN server-side (LET-1764): initialize an empty lettuce store at BASE/NAME, the folder lettuce serve --domains-root BASE serves as domain NAME. Idempotent: an existing domain reports created=false and is left untouched. Domains are created ONLY here, on the server host — no HTTP route creates one — and a running server picks the new folder up without a restart.
- kind:
server - output:
object— --format plain prints its domain reference (name) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
false - requires project:
false - requires author:
false - stable snapshot:
false - idempotency key:
false
Flags:
--domains-root path- The server's domains root (also LETTUCE_DOMAINS_ROOT). Required.
Examples:
lettuce domain create acme --domains-root /data/domains --format json
Notes:
- NAME matches [a-z0-9][a-z0-9-]{0,62}, e.g. acme or team-2;
defaultis the default domain's folder. A symlink or non-directory at BASE/NAME is refused. Not available in client mode.
domain list
Usage: lettuce domain list [--domains-root BASE]
List domains (LET-1764). In client mode it calls GET /v1/domains and shows ONLY the domains the bearer token can reach (plus which is its default). Locally, with --domains-root, it lists every initialized store folder under BASE.
- kind:
server - output:
collection— --format plain prints one domain reference (name) per row ofdomains; nothing when empty - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
false - requires project:
false - requires author:
false - stable snapshot:
false - idempotency key:
false
Flags:
--domains-root path- Local mode: the server's domains root to enumerate (also LETTUCE_DOMAINS_ROOT). Not used in client mode.
Examples:
lettuce domain list --server-url https://lettuce.example.test --format json
lettuce domain list --domains-root /data/domains --format json
Notes:
- Select a domain for other commands with the global --domain flag (or LETTUCE_DOMAIN).
client list
Usage: lettuce client list [--stale]
List the lettuce client releases the bound server has seen on this domain (LET-1932): one row per token subject, actor and client version (X-Lettuce-Client-Version), with first/last seen, request count, stale (a release other than the server's, FW-CLIENT-VERSION-SKEW), superseded (the same subject and actor were since seen on the current release), unversioned (LET-2002: a lettuce client that sent no version, i.e. a release older than v0.19.0, listed with client_version unknown (pre-v0.19.0); always stale, and it cannot self-update) and below_floor (under serve --min-client-version). Reads GET /v1/clients, which needs the ADMIN role. The record is the server process's in-memory runtime state (bounded to 1024 rows, rows unseen for 30 days pruned), so a restart starts it again (since).
- kind:
server - output:
collection— --format plain prints one client reference ({subject}/{actor}@{client_version}) per row ofclients; nothing when empty - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
false - requires project:
false - requires author:
false - stable snapshot:
false - idempotency key:
false
Flags:
--stale- Only the rows that still count as stale (stale and not superseded): who must runlettuce self-update.
Examples:
lettuce client list --format json
lettuce client list --stale
Notes:
lettuce doctor --summary(reader role) prints the same count without names:clients stale_subjects=N unversioned_subjects=M .... Requests that name no lettuce client (raw HTTP, other tools) are not recorded; a request with no version but Go's default User-Agent (every lettuce release before v0.19.0) is recorded as unversioned. Needs a hosted store; locally it is refused FW-CMD-USAGE.
github init-repo
Usage: lettuce github init-repo --org ORG --repo REPO [--pat TOKEN] [repo flags]
Create and initialize a GitHub repository for a store.
- kind:
maintenance - output:
object— --format plain prints its github-repository reference (repository.full_name) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--org org- GitHub organization. Required.--repo repo- Repository name. Any name GitHub accepts; no prefix is required or enforced. Required.--pat token- GitHub personal access token.--pat-file path- Read GitHub token from file.--remote name- Git remote name.--default-branch branch- Default branch name.--api-url url- GitHub API base URL (defaults through LETTUCE_GITHUB_API_URL). Set for GitHub Enterprise.--private- Create the repository private (the default).--public- Create the repository public.--idempotent- Accepted for compatibility; NO-OP. Repository creation is ALWAYS idempotent here (GitHub 422 already-exists is treated as success whether or not this flag is passed), so it toggles nothing. Unlikeinit --idempotent, which does gate behaviour.
Examples:
lettuce github init-repo --org huru-io --repo lettuce-demo --author agent-1 --format json
Notes:
- With a PAT and an HTTPS remote the token is written to .runtime/credentials/github-token (0600, in a 0700 directory, git-excluded) and core.askpass names .runtime/credentials/github-askpass.sh; .git/config and the remote URL never carry the token (LET-1219). The credential is never exported, migrated, uploaded or logged. Re-run with a new PAT to rotate it (on an existing store add --mode dedicated-git); delete .runtime/credentials/ to revoke local push access.
- A store configured by v0.19.2 or v0.19.3 has the token in .runtime/github-credential; doctor accepts it, and the next run moves it into .runtime/credentials/ and deletes the old files.
Links to
- Command Reference
reference/command-reference
Backlinks
- Serving lettuce safely (HTTP)
guides/guide-server-security - Command Reference
reference/command-reference - reference/index
reference/index
Task Local Objects — Commands
reference/cmd-task-local-objects lettuce Task Local Objects commands — 35 entries — comment add, comment edit, comment status, comment list, comment show, comment archive, comment unarchive, comment delete, lease acquire, lease renew, lease release, lease steal, lease show, lease list, run start, run finish, run log add, run summar
lettuce command group Task Local Objects — 35 commands. Generated from lettuce usage --format okf (always in sync with the binary).
Back to Command Reference.
Commands in this group
comment addcomment editcomment statuscomment listcomment showcomment archivecomment unarchivecomment deletelease acquirelease renewlease releaselease steallease showlease listrun startrun finishrun log addrun summary addrun listrun showrun log listrun log showrun summary listrun summary showartifact addartifact replaceartifact listartifact showartifact archiveartifact unarchiveartifact deleteartifact file getversion addversion listversion show
---
Manage comments, leases, runs, artifacts, and versioned documents under tasks.
comment add
Usage: lettuce comment add REF [--body TEXT|--body-file PATH] [--parent COMMENT] [--expect-revision N]
Add a task comment.
- kind:
mutation - output:
object— --format plain prints its comment reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
true - external input keys (these go INSIDE
data, not at the top level):task_ref, body, parent, expected_revision - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--body text- Comment body.--body-file path- Read comment body from file.--parent comment-id- Parent comment id.--expect-revision n- Expected task revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch.
Examples:
lettuce comment add lettuce/LET-1 --author agent-1 --body 'Looks good.' --format json
comment edit
Usage: lettuce comment edit REF COMMENT (--body TEXT|--body-file PATH) [--expect-revision N]
Add a new comment body version.
- kind:
mutation - output:
object— --format plain prints its comment reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
true - external input keys (these go INSIDE
data, not at the top level):task_ref, comment_id, body, expected_revision - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--body text- Body text. Required.--body-file path- Read body from file.--expect-revision n- Expected task revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch.
Examples:
lettuce comment edit lettuce/LET-1 1 --author agent-1 --body 'Updated.' --format json
comment status
Usage: lettuce comment status REF COMMENT STATUS [--expect-revision N]
Change comment status.
- kind:
mutation - output:
object— --format plain prints its comment reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
true - external input keys (these go INSIDE
data, not at the top level):task_ref, comment_id, status, expected_revision - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--expect-revision n- Expected current revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch.
Examples:
lettuce comment status lettuce/LET-1 1 resolved --author agent-1 --format json
comment list
Usage: lettuce comment list REF [--include-archived] [--with-body]
List task comments.
- kind:
read - output:
collection— --format plain prints one comment reference (reference) per row ofcomments; nothing when empty - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--include-archived- Include archived comments in the results (hidden by default).--with-body- Include each comment's body (omitted by default). The same body projectioncomment show --with-bodyapplies to one comment, so a list can carry bodies for parity. Opt-in because bodies are unbounded and a task can carry many.
Examples:
lettuce comment list lettuce/LET-1 --format json
lettuce comment list lettuce/LET-1 --include-archived --format json
lettuce comment list lettuce/LET-1 --with-body --format json
Notes:
- Archived comments are hidden by default, the same hidden-by-default contract task list and project list document. Archiving hides a comment from this list view, not from query search. Bodies are omitted by default — pass --with-body to include them.
- A live reply remains listed when its parent is archived. FW-READ-COMMENT-PARENT-HIDDEN names the parent omitted from this result; pass --include-archived to return both and silence that advisory. A genuinely missing parent is not this advisory's concern.
- On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
comment show
Usage: lettuce comment show REF COMMENT [--with-body] [--body-version V]
Read one task comment.
- kind:
read - output:
object— --format plain prints its comment reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--with-body- Include comment body content.--body-version v- Read a SPECIFIC historical body version instead of the latest (requires --with-body). Comment bodies are versioned bycomment editexactly as task bodies are bytask body add, and this is the same flag the task show sibling documents. The value is a version slot or a 1-based position in creation order, exactly as task show --body-version (LET-1859). A version that does not exist is REFUSED FW-REF-MISSING-VERSION and the existing ones are named — never silently served as the latest. Works over --server-url too (GET ...?body_version=V, LET-1794).
Examples:
lettuce comment show lettuce/LET-1 1 --with-body --format json
lettuce comment show lettuce/LET-1 1 --with-body --body-version 1 --format json
lettuce comment show lettuce/LET-1 1 --format plain
Notes:
- This explicit read returns the named comment even when archived, with archived_at populated. It does not emit FW-READ-COMMENT-PARENT-HIDDEN: that advisory belongs to comment list when the parent is filtered from its result. Read the parent with comment show REF PARENT, or the whole thread with comment list REF --include-archived. comment show accepts no --include-archived flag.
- On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
comment archive
Usage: lettuce comment archive REF COMMENT [--reason TEXT] [--expect-revision N]
Soft-archive a task comment without deleting it. Reversible with lettuce comment unarchive.
- kind:
mutation - output:
object— --format plain prints its comment reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--reason text- Reason text.--expect-revision n- Expected task revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch.
Examples:
lettuce comment archive lettuce/LET-1 1 --author agent-1 --reason 'Off-topic.' --format json
Notes:
- Reversible and history-preserving; use lettuce comment delete for a hard removal.
comment unarchive
Usage: lettuce comment unarchive REF COMMENT [--reason TEXT] [--expect-revision N]
Restore a previously archived task comment.
- kind:
mutation - output:
object— --format plain prints its comment reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--reason text- Reason text.--expect-revision n- Expected task revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch.
Examples:
lettuce comment unarchive lettuce/LET-1 1 --author agent-1 --format json
Notes:
- Only an archived comment can be unarchived; the inverse of lettuce comment archive.
comment delete
Usage: lettuce comment delete REF COMMENT (--yes|--force) [--reason TEXT] [--expect-revision N]
Hard-delete a task comment. Non-reversible: requires --yes (or --force) to confirm.
- kind:
mutation - output:
object— --format plain prints its comment reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--yes- Confirm the non-reversible deletion.--force- Alias for --yes: confirm the non-reversible deletion.--reason text- Reason text.--expect-revision n- Expected task revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch.
Examples:
lettuce comment delete lettuce/LET-1 1 --yes --author agent-1 --format json
Notes:
- This is a hard, non-reversible removal; prefer lettuce comment archive when you only want to hide the comment.
lease acquire
Usage: lettuce lease acquire REF --expires-at TIMESTAMP [--token TOKEN]
Acquire a task lease.
- kind:
mutation - output:
object— --format plain prints its lease reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
true - external input keys (these go INSIDE
data, not at the top level):task_ref, expires_at, lease_token, expected_revision - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--expires-at timestamp- Lease expiration timestamp (UTC, YYYY-MM-DDTHH:MM:SSZ). Must be in the FUTURE and at most the store's maximum lease window ahead of the operation clock — a longer window is refused with FW-LEASE-WINDOW-TOO-LONG (LET-721), the upper-end mirror of the FW-LEASE-EXPIRED refusal at the past end. The maximum defaults to 288h (12 days) — the longest genuine hold measured in this store's own ledger, 266.38h, rounded up to a clean unit — and is a store-level policy: setmax_lease_windowin the config file or LETTUCE_MAX_LEASE_WINDOW, as a Go duration. There is no per-command flag for it on purpose — a caller who could raise the ceiling in the same breath as the request is not bounded at all. A lease protects work in progress, so holding a task for longer is done by renewing, not by taking one long window. The timestamps in the examples are ILLUSTRATIVE: the maximum is a duration from NOW, so no absolute timestamp printed in a static document stays inside it — substitute a fresh one, e.g. --expires-at "$(date -u -d '+1 hour' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v+1H +%Y-%m-%dT%H:%M:%SZ)". Required.--token token- Lease token of the form lease-YYYYMMDD-HHMMSS-<4..32 alphanumerics>. Generated when omitted for acquire.--expect-revision n- Expected task revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch.
Examples:
lettuce lease acquire lettuce/LET-1 --author agent-1 --expires-at 2026-09-10T00:00:00Z --format json
Notes:
- --expires-at is bounded at BOTH ends (LET-721): strictly in the future, and at most the store's maximum lease window beyond the operation clock, else FW-LEASE-WINDOW-TOO-LONG. The maximum defaults to 288h (12 days) and is set per store with
max_lease_windowin the config file or LETTUCE_MAX_LEASE_WINDOW (a Go duration). It is a duration from NOW, so the absolute timestamp in the example above cannot stay inside it — substitute a fresh one, e.g. --expires-at "$(date -u -d '+1 hour' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v+1H +%Y-%m-%dT%H:%M:%SZ)". Hold a task for longer by renewing, which is what makes an abandoned lease recoverable: a window that never ends is a permanent lock whose only remedy is steal --force.
lease renew
Usage: lettuce lease renew REF --expires-at TIMESTAMP [--token TOKEN]
Renew a task lease.
- kind:
mutation - output:
object— --format plain prints its lease reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
true - external input keys (these go INSIDE
data, not at the top level):task_ref, expires_at, lease_token, expected_revision - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--expires-at timestamp- Lease expiration timestamp (UTC, YYYY-MM-DDTHH:MM:SSZ). Must be in the FUTURE and at most the store's maximum lease window ahead of the operation clock — a longer window is refused with FW-LEASE-WINDOW-TOO-LONG (LET-721), the upper-end mirror of the FW-LEASE-EXPIRED refusal at the past end. The maximum defaults to 288h (12 days) — the longest genuine hold measured in this store's own ledger, 266.38h, rounded up to a clean unit — and is a store-level policy: setmax_lease_windowin the config file or LETTUCE_MAX_LEASE_WINDOW, as a Go duration. There is no per-command flag for it on purpose — a caller who could raise the ceiling in the same breath as the request is not bounded at all. A lease protects work in progress, so holding a task for longer is done by renewing, not by taking one long window. The timestamps in the examples are ILLUSTRATIVE: the maximum is a duration from NOW, so no absolute timestamp printed in a static document stays inside it — substitute a fresh one, e.g. --expires-at "$(date -u -d '+1 hour' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v+1H +%Y-%m-%dT%H:%M:%SZ)". Required.--token token- Lease token of the form lease-YYYYMMDD-HHMMSS-<4..32 alphanumerics>. Generated when omitted for acquire.--expect-revision n- Expected task revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch.
Examples:
lettuce lease renew lettuce/LET-1 --author agent-1 --expires-at 2026-09-10T01:00:00Z --format json
Notes:
- --expires-at is bounded at BOTH ends (LET-721): strictly in the future, and at most the store's maximum lease window beyond the operation clock, else FW-LEASE-WINDOW-TOO-LONG. The maximum defaults to 288h (12 days) and is set per store with
max_lease_windowin the config file or LETTUCE_MAX_LEASE_WINDOW (a Go duration). It is a duration from NOW, so the absolute timestamp in the example above cannot stay inside it — substitute a fresh one, e.g. --expires-at "$(date -u -d '+1 hour' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v+1H +%Y-%m-%dT%H:%M:%SZ)". Hold a task for longer by renewing, which is what makes an abandoned lease recoverable: a window that never ends is a permanent lock whose only remedy is steal --force.
lease release
Usage: lettuce lease release REF [--force --reason TEXT]
Release a task lease.
- kind:
mutation - output:
object— --format plain prints its lease reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
true - external input keys (these go INSIDE
data, not at the top level):task_ref, force, reason, expected_revision - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--force- Explicit safety override for supported cases.--reason text- Reason text.--reason-file path- Read reason from file.--expect-revision n- Expected task revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch.
Examples:
lettuce lease release lettuce/LET-1 --author agent-1 --format json
Notes:
- --force requires --reason (or --reason-file): a forced release overrides another holder's lease, so an audit reason is mandatory. A normal (non-forced) release takes neither.
lease steal
Usage: lettuce lease steal REF --expires-at TIMESTAMP --reason TEXT [--force]
Steal an expired or abandoned task lease.
- kind:
mutation - output:
object— --format plain prints its lease reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
true - external input keys (these go INSIDE
data, not at the top level):task_ref, expires_at, lease_token, force, reason, expected_revision - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--expires-at timestamp- Lease expiration timestamp (UTC, YYYY-MM-DDTHH:MM:SSZ). Must be in the FUTURE and at most the store's maximum lease window ahead of the operation clock — a longer window is refused with FW-LEASE-WINDOW-TOO-LONG (LET-721), the upper-end mirror of the FW-LEASE-EXPIRED refusal at the past end. The maximum defaults to 288h (12 days) — the longest genuine hold measured in this store's own ledger, 266.38h, rounded up to a clean unit — and is a store-level policy: setmax_lease_windowin the config file or LETTUCE_MAX_LEASE_WINDOW, as a Go duration. There is no per-command flag for it on purpose — a caller who could raise the ceiling in the same breath as the request is not bounded at all. A lease protects work in progress, so holding a task for longer is done by renewing, not by taking one long window. The timestamps in the examples are ILLUSTRATIVE: the maximum is a duration from NOW, so no absolute timestamp printed in a static document stays inside it — substitute a fresh one, e.g. --expires-at "$(date -u -d '+1 hour' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v+1H +%Y-%m-%dT%H:%M:%SZ)". Required.--token token- Lease token of the form lease-YYYYMMDD-HHMMSS-<4..32 alphanumerics>. Generated when omitted for acquire.--expect-revision n- Expected task revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch.--force- Explicit safety override for supported cases.--reason text- Reason text.--reason-file path- Read reason from file.
Examples:
lettuce lease steal lettuce/LET-1 --author agent-2 --force --reason 'Worker expired.' --expires-at 2026-09-10T00:00:00Z --format json
Notes:
- --expires-at is bounded at BOTH ends (LET-721): strictly in the future, and at most the store's maximum lease window beyond the operation clock, else FW-LEASE-WINDOW-TOO-LONG. The maximum defaults to 288h (12 days) and is set per store with
max_lease_windowin the config file or LETTUCE_MAX_LEASE_WINDOW (a Go duration). It is a duration from NOW, so the absolute timestamp in the example above cannot stay inside it — substitute a fresh one, e.g. --expires-at "$(date -u -d '+1 hour' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v+1H +%Y-%m-%dT%H:%M:%SZ)". Hold a task for longer by renewing, which is what makes an abandoned lease recoverable: a window that never ends is a permanent lock whose only remedy is steal --force.
lease show
Usage: lettuce lease show REF
Read current lease state — the reference and whether a lease is present, plus for a lease that IS present its holder, expires-at, and a derived active/expired status. The status answers the question the lease family exists for — is this lease stale, can I take it — without the caller re-deriving it against the clock and having to reproduce the boundary convention exactly to agree with lease list.
- kind:
read - output:
collection— --format plain prints its lease reference (reference) whenpresentis true, nothing otherwise (a 0..1 collection) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Examples:
lettuce lease show lettuce/LET-1 --format json
lettuce lease show lettuce/LET-1 --format plain
Notes:
- status is OMITTED when no lease is present: present:false already states there is no lease, so there is nothing to classify. An absent status means "no lease", never "unknown". This differs from lease list, whose rows always carry a status because a row only exists for a lease that is there.
- Derived from the operation clock by the same classifier lease list uses, so the two reads cannot disagree: active means expires-at is strictly in the future, at or after it is expired, and an unparseable or absent expires-at is conservatively expired (the same "not provably still held" stance the acquire and steal guards take). The ownership-asserting mutations — acquire, renew, steal, release — report what the caller just did rather than a state read, and carry no status.
- On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
lease list
Usage: lettuce lease list --project PROJECT [--holder AUTHOR] [--status active|expired] [--task-terminal] [--limit N] [--offset N] [--include-archived]
Enumerate a project's held leases — each row carries the task reference, holder, expires-at, a derived active/expired status, and the task's own state plus whether that state is terminal, so an operator can see who holds leases, which are stale to steal, and which are held on already-closed tasks without walking every task. A leased task that cannot be read is reported with task_readable=false rather than failing the listing, so one malformed task does not hide every other row. Terminality that cannot be DETERMINED — the workflow or the named state is absent from the store, or the state's terminal marker is present but unreadable — is reported with task_terminal_known=false, and such a row is deliberately NOT filtered out by --task-terminal, so an unanswered question surfaces instead of vanishing with exit 0. task_terminal is only meaningful when task_terminal_known is true.
- kind:
read - output:
collection— --format plain prints one lease reference (reference) per row ofleases; nothing when empty; rows it could not judge, or that failed, named on stderr - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
listing— proportional to the collection it reads once (paging or the task projection bound it) - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--holder author- Only leases held by this author (the filter is --holder, not --author: --author is the global acting-author flag).--status active|expired- Only leases with this derived status (active = expires-at in the future, expired = at/after it).--task-terminal- Only leases whose TASK is in a terminal workflow state (LET-1249). Distinct from --status, which describes the LEASE: the two are independently true, and an ACTIVE lease on a DONE task is the combination worth finding. Terminality is read from the workflow's own terminal field, so custom workflows with differently-named sinks are covered.--limit n- Return at most N rows (unbounded when omitted).--offset n- Skip the first N rows.--include-archived- Read an ARCHIVED project on purpose: without this flag a read scoped to an archived project is refused (FW-READ-PROJECT-ARCHIVED); with it the read succeeds and is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning, and project_archived_at in the json/yaml data).
Examples:
lettuce lease list --project lettuce --format json
lettuce lease list --project lettuce --status expired --format plain
lettuce lease list --project lettuce --task-terminal --format plain
lettuce lease list --project lettuce --holder agent-1 --limit 20 --offset 0 --format json
Notes:
- Project-scoped (requires --project), unlike the task-scoped lease acquire/renew/release/steal/show verbs. Rows are sorted by task reference ascending; status is derived from the operation clock. The lease record stores no acquired-at, so rows report expires-at + status rather than an acquired-at.
lease acquirealready refuses a terminal task (LET-645), so a lease on a closed task cannot be created by acquiring onto one — it is only reachable by CLOSING a task that already holds a lease. That is why this report exists rather than a second acquire-side guard.- --format plain prints one task reference per lease — the SAME row set as json — and names on stderr every row whose task could not be judged (unreadable, or terminality unknown), so a row this report could not judge is never byte-identical to a confirmed hit (LET-1249; output contract R1a). The per-row task_state/task_terminal/task_readable/task_terminal_known fields are on table and json.
- The result also states its POPULATION —
projectandarchived— so a count cannot be misread:lease listis single-project, andarchivedsays whether a whole-store probe (the doctor terminal-lease sweep) would include this project's leases (LET-1621). - On an ARCHIVED project this read is refused with FW-READ-PROJECT-ARCHIVED (exit 1) unless --include-archived is passed; with it the result is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning and project_archived_at in the json/yaml data).
run start
Usage: lettuce run start REF [--force] [--reason TEXT] [--expect-revision N]
Start a task run.
- kind:
mutation - output:
object— --format plain prints its run reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
true - external input keys (these go INSIDE
data, not at the top level):task_ref, force, reason, expected_revision - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--force- Explicit safety override for supported cases.--reason text- Reason text.--reason-file path- Read reason from file.--expect-revision n- Expected task revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch.
Examples:
lettuce run start lettuce/LET-1 --author agent-1 --format json
Notes:
- HARD PREREQUISITE: the acting author must already hold an unexpired lease on REF (lettuce lease acquire REF --expires-at TS --author AGENT --format json) before run start succeeds — otherwise it is refused FW-WF-REQUIREMENT-UNSATISFIED "run start requires an active lease". The one exception is --force together with --reason, which bypasses the lease check and records the reason on the run-started event.
run finish
Usage: lettuce run finish REF RUN STATUS [--force --reason TEXT] [--expect-revision N]
Finish a task run.
- kind:
mutation - output:
object— --format plain prints its run reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
true - external input keys (these go INSIDE
data, not at the top level):task_ref, run_id, status, expected_revision - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--force- Explicit safety override for supported cases.--reason text- Reason text.--reason-file path- Read reason from file.--expect-revision n- Expected task revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch.
Examples:
lettuce run finish lettuce/LET-1 1 succeeded --author agent-1 --format json
Notes:
- By default the run's agent or the holder of the task's active lease may finish it. --force finishes a run you neither started nor hold the lease for and REQUIRES --reason (or --reason-file): a forced finish overrides the lease holder, so an audit reason is mandatory (BUG-77).
run log add
Usage: lettuce run log add REF RUN --type TYPE --message MESSAGE [--details TEXT] [--details-file PATH] [--expect-revision N]
Add a run log entry. A run log is an APPEND-ONLY ledger of semantic notable entries, not raw stdout, so entries MAY be appended after the run reaches a terminal status (succeeded/failed/aborted) — a terminal run is NOT append-closed. That run finish is gated (FW-RUN-NOT-RUNNING) is not an inconsistency: finish writes the run's single authoritative OUTCOME and the gate stops a recorded result being overwritten, whereas this appends and overwrites nothing. A post-terminal entry is self-describing rather than ambiguous — every entry carries an at, and ended-at is required once status is not running, so an annotation is detectable as at >= ended-at (LET-399, spec section 14).
- kind:
mutation - output:
object— --format plain prints its run-log reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
true - external input keys (these go INSIDE
data, not at the top level):task_ref, run_id, type, message, details, expected_revision - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--type type- Log entry type. Required.--message text- Log message. Required.--details text- Optional details.--details-file path- Read details from file.--expect-revision n- Expected task revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch.
Examples:
lettuce run log add lettuce/LET-1 1 --author agent-1 --type progress --message 'Started.' --format json
lettuce run log add lettuce/LET-1 1 --author agent-1 --type progress --message 'Tests running.' --details 'go test ./...' --format json
Notes:
- A terminal run stays APPENDABLE: this is a decided allowance (LET-399), not an unguarded path. Only
run finishis gated, because it writes the outcome rather than appending to a ledger.
run summary add
Usage: lettuce run summary add REF RUN (--body TEXT|--body-file PATH) [--reason TEXT] [--expect-revision N]
Add a run summary version.
- kind:
mutation - output:
object— --format plain prints its run-summary reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
true - external input keys (these go INSIDE
data, not at the top level):task_ref, run_id, body, reason, expected_revision - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--body text- Summary body. Required.--body-file path- Read summary body from file.--reason text- Reason for summary.--expect-revision n- Expected task revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch.
Examples:
lettuce run summary add lettuce/LET-1 1 --author agent-1 --body 'Tests pass.' --format json
lettuce run summary add lettuce/LET-1 1 --author agent-1 --body 'Revised summary.' --reason 'Corrected results.' --format json
Notes:
- Summaries are VERSIONED, so a later revision adds a new version and leaves earlier ones readable. A summary version MAY therefore be added after the run reaches a terminal status — a terminal run is not append-closed (LET-399, spec section 14).
run list
Usage: lettuce run list REF
List task runs.
- kind:
read - output:
collection— --format plain prints one run reference (reference) per row ofruns; nothing when empty - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Examples:
lettuce run list lettuce/LET-1 --format json
lettuce run list lettuce/LET-1 --format plain
Notes:
- On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
run show
Usage: lettuce run show REF RUN [--full] [--with-logs] [--with-summaries]
Read one task run.
- kind:
read - output:
object— --format plain prints its run reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--full- Include every sub-object (logs and summaries).--with-logs- Include the run's log entries.--with-summaries- Include the run's summary versions.
Examples:
lettuce run show lettuce/LET-1 1 --format json
lettuce run show lettuce/LET-1 1 --with-logs --with-summaries --format json
Notes:
- On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
run log list
Usage: lettuce run log list REF RUN [--with-body]
List a run's log entries.
- kind:
read - output:
collection— --format plain prints one run-log reference (reference) per row oflogs; nothing when empty - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--with-body- Include each entry's details body (hidden by default).
Examples:
lettuce run log list lettuce/LET-1 1 --format json
Notes:
- On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
run log show
Usage: lettuce run log show REF RUN ENTRY [--with-body]
Read one run log entry.
- kind:
read - output:
object— --format plain prints its run-log reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--with-body- Include the entry's details body (hidden by default).
Examples:
lettuce run log show lettuce/LET-1 1 1 --with-body --format json
Notes:
- On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
run summary list
Usage: lettuce run summary list REF RUN [--with-body]
List a run's summary versions.
- kind:
read - output:
collection— --format plain prints one run-summary reference (reference) per row ofsummaries; nothing when empty - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--with-body- Include each version's summary body (hidden by default).
Examples:
lettuce run summary list lettuce/LET-1 1 --format json
Notes:
- On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
run summary show
Usage: lettuce run summary show REF RUN VERSION [--with-body]
Read one run summary version.
- kind:
read - output:
object— --format plain prints its run-summary reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--with-body- Include the summary body (hidden by default).
Examples:
lettuce run summary show lettuce/LET-1 1 1 --with-body --format json
Notes:
- On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
artifact add
Usage: lettuce artifact add REF --type TYPE --primary-file PATH --file SRC:DEST
Create a task artifact.
- kind:
mutation - output:
object— --format plain prints its artifact reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
true - external input keys (these go INSIDE
data, not at the top level):task_ref, type, title, primary_file, from_run, files, expected_revision - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--type artifact-type- Artifact type. Required.--title title- Artifact title.--primary-file path- Primary artifact file path. Required.--file source:dest- Payload descriptor. Required. Repeatable.--from-run run-id- Associated run id.--expect-revision n- Expected task revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch.
Examples:
lettuce artifact add lettuce/LET-1 --author agent-1 --type test-report --primary-file report.md --file ./report.md:report.md --format json
artifact replace
Usage: lettuce artifact replace REF ARTIFACT --type TYPE --primary-file PATH --file SRC:DEST
Replace an artifact with a new artifact revision.
- kind:
mutation - output:
object— --format plain prints its artifact reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
true - external input keys (these go INSIDE
data, not at the top level):task_ref, artifact_id, type, title, primary_file, from_run, files, expected_revision - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--type artifact-type- Artifact type. Required.--title title- Artifact title.--primary-file path- Primary artifact file path. Required.--file source:dest- Payload descriptor. Required. Repeatable.--from-run run-id- Associated run id.--expect-revision n- Expected task revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch.
Examples:
lettuce artifact replace lettuce/LET-1 1 --author agent-1 --type test-report --primary-file report.md --file ./report.md:report.md --format json
artifact list
Usage: lettuce artifact list REF [--include-archived]
List task artifacts.
- kind:
read - output:
collection— --format plain prints one artifact reference (reference) per row ofartifacts; nothing when empty - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--include-archived- Include archived artifacts in the results (hidden by default).
Examples:
lettuce artifact list lettuce/LET-1 --format json
lettuce artifact list lettuce/LET-1 --include-archived --format json
Notes:
- Archived artifacts are hidden by default, the same hidden-by-default contract task list and project list document. Archiving hides an artifact from this list view, not from query search.
- On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
artifact show
Usage: lettuce artifact show REF ARTIFACT
Read artifact metadata.
- kind:
read - output:
object— --format plain prints its artifact reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Examples:
lettuce artifact show lettuce/LET-1 1 --format json
lettuce artifact show lettuce/LET-1 1 --format plain
Notes:
- On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
artifact archive
Usage: lettuce artifact archive REF ARTIFACT [--reason TEXT] [--expect-revision N]
Soft-archive a task artifact without deleting it. Reversible with lettuce artifact unarchive.
- kind:
mutation - output:
object— --format plain prints its artifact reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--reason text- Reason text.--expect-revision n- Expected task revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch.
Examples:
lettuce artifact archive lettuce/LET-1 1 --author agent-1 --reason 'Stale.' --format json
Notes:
- Reversible and history-preserving; use lettuce artifact delete for a hard removal.
artifact unarchive
Usage: lettuce artifact unarchive REF ARTIFACT [--reason TEXT] [--expect-revision N]
Restore a previously archived task artifact.
- kind:
mutation - output:
object— --format plain prints its artifact reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--reason text- Reason text.--expect-revision n- Expected task revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch.
Examples:
lettuce artifact unarchive lettuce/LET-1 1 --author agent-1 --format json
Notes:
- Only an archived artifact can be unarchived; the inverse of lettuce artifact archive.
artifact delete
Usage: lettuce artifact delete REF ARTIFACT (--yes|--force) [--reason TEXT] [--expect-revision N]
Hard-delete a task artifact. Non-reversible: requires --yes (or --force) to confirm.
- kind:
mutation - output:
object— --format plain prints its artifact reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--yes- Confirm the non-reversible deletion.--force- Alias for --yes: confirm the non-reversible deletion.--reason text- Reason text.--expect-revision n- Expected task revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch.
Examples:
lettuce artifact delete lettuce/LET-1 1 --yes --author agent-1 --format json
Notes:
- This is a hard, non-reversible removal; prefer lettuce artifact archive when you only want to hide the artifact.
artifact file get
Usage: lettuce artifact file get REF ARTIFACT PATH
Read an artifact payload file.
- kind:
read - output:
content— --format plain prints the raw file bytes - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Examples:
lettuce artifact file get lettuce/LET-1 1 report.md --format json
lettuce artifact file get lettuce/LET-1 1 report.md --format plain
Notes:
- On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
version add
Usage: lettuce version add REF CATEGORY NAME (--body TEXT|--body-file PATH) [--reason TEXT]
Create a task versioned document.
- kind:
mutation - output:
object— --format plain prints its version reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
true - external input keys (these go INSIDE
data, not at the top level):task_ref, category, name, body, reason, expected_revision - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--body text- Document body. Required.--body-file path- Read document body from file.--reason text- Reason text.--expect-revision n- Expected task revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch.
Examples:
lettuce version add lettuce/LET-1 plans implementation --author agent-1 --body 'Plan.' --format json
version list
Usage: lettuce version list REF [--category CATEGORY] [--name NAME]
List task versioned documents.
- kind:
read - output:
collection— --format plain prints one version reference (reference) per row ofversions; nothing when empty - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--category category- Filter by category.--name name- Filter by name.
Examples:
lettuce version list lettuce/LET-1 --category plans --format json
lettuce version list lettuce/LET-1 --category plans --name implementation --format json
Notes:
- On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
version show
Usage: lettuce version show REF CATEGORY NAME VERSION [--with-body]
Read one versioned document.
- kind:
read - output:
object— --format plain prints its version reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--with-body- Include body content.
Examples:
lettuce version show lettuce/LET-1 plans implementation 1 --with-body --format json
lettuce version show lettuce/LET-1 plans implementation 1 --format plain
Notes:
- On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
Links to
- Command Reference
reference/command-reference
Backlinks
- Tasks and the work plane
concepts/concept-task - Command Reference
reference/command-reference - reference/index
reference/index
Tasks — Commands
reference/cmd-tasks lettuce Tasks commands — 19 entries — task create, task show, task list, task exists, task set, task set-where, task unset, task set-list, task transition, task clone, task reopen, task archive, task unarchive, task delete, task body add, task audit, task graph, custom set, custom clear.
lettuce command group Tasks — 19 commands. Generated from lettuce usage --format okf (always in sync with the binary).
Back to Command Reference.
Commands in this group
task createtask showtask listtask existstask settask set-wheretask unsettask set-listtask transitiontask clonetask reopentask archivetask unarchivetask deletetask body addtask audittask graphcustom setcustom clear
---
Create, inspect, mutate, transition, audit, and graph tasks.
task create
Usage: lettuce task create (TASK-ID | --next PREFIX) --title TITLE [--body TEXT|--body-file PATH] [task flags]
Create a task and initial body.
- kind:
mutation - output:
object— --format plain prints its task reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
true - requires author:
true - stable snapshot:
false - idempotency key:
true - external input keys (these go INSIDE
data, not at the top level):task_id, title, body, workflow, status, priority, type, severity, assignee, reporter, reviewer, component, milestone, no_milestone, estimate, due_at, parent, external_url, external_ci, external_merged, label, depends_on, blocks, relates_to, watcher - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--next prefix- Allocate the task id as PREFIX-N instead of naming one, where N is one past the highest existing number in that series. Mutually exclusive with the positional TASK-ID; supplying both is refused rather than silently preferring one. Closes the read-to-write window in which two agents reading the same maximum pick the same id.--title title- Task title. Required.--body text- Initial body text.--body-file path- Read initial body from file.--workflow workflow- Workflow slug.--status state- Initial workflow state.--priority 0..100- Task priority.--type task-type- Task type.--severity severity- Severity slug.--assignee author- Assignee author.--reporter author- Reporter author.--reviewer author- Author assigned to review the ticket (LET-1753). Author-valued like --assignee.--component component- Component slug.--milestone milestone- Open or active milestone slug.--no-milestone- Declare that the task has NO milestone — a recorded decision, not an omission (LET-1517, ADR 0029). The create writes the marker declared-absent/milestone (its content is the creation time) and records it in the creation event's fields record, so the task readsabsence: {"milestone": {"state": "declined"}}instead of untriaged, FQLmilestone declinedfinds it, and the FW-TASK-MILESTONE-OMITTED advisory (about an UNTRIAGED milestone) stays silent at create and at close. Mutually exclusive with --milestone (refused in ops, so HTTP cannot bypass it). A milestone is never required: omitting both still creates the task (untriaged, with the advisory). Declare later withtask set REF milestone --none.--estimate estimate- Estimate scalar.--due-at timestamp- Due timestamp.--parent task-ref- Same-project parent task.--external-url url- External reference URL for the ticket's branch or PR (LET-1743).--external-ci status- CI status of the external change (free-form, e.g. passing/pending/failing).--external-merged true|false|unknown- Merge state of the external change. A terminal ticket whose external-merged is false is surfaced with a derived awaiting_merge signal and an FW-TASK-AWAITING-MERGE warning.--label slug- Label to attach. Repeatable.--depends-on task-ref- Task this task depends on. Repeatable.--blocks task-ref- Task this task blocks. Repeatable.--relates-to task-ref- Task this task relates to (generic, NON-blocking). The referent must exist, but the link carries no precedence and never participates in the cycle graph, so a relates-to cycle is allowed. Repeatable.--watcher author- Watcher to add. Repeatable.--cell coordinate- Cell coordinate this task declares, e.g. dim=cells;unit=docs. Validated against the pack at write time, so a member undeclared on a CLOSED axis is refused, and canonicalized so two spellings of one coordinate collapse to a single entry. The cell is a standing map point with its own state and evidence; the task declares it, and the list of tasks on a cell is that same fact read backwards. Repeatable.--custom key=value- Set a custom field value; active required custom fields must be supplied. Repeatable.
Examples:
lettuce task create LET-1 --project lettuce --author agent-1 --title 'First task' --body 'Do the work.' --format json
lettuce task create lettuce/LET-2 --author agent-1 --title 'Qualified id' --format json
Notes:
- TASK-ID also accepts the qualified spelling PROJECT/ID (the one
task showaccepts). With no project context (no --project, LETTUCE_PROJECT, config or .lettuce binding) the qualified id supplies the project (LET-1892); a project context that names a DIFFERENT project is refused rather than redirecting the write.
task show
Usage: lettuce task show REF [--full] [--with-body] [--body-version V] [--body-versions] [--with-comments] [--with-runs] [--with-artifacts] [--with-events]
Read one task. The result includes next_actions: the transitions available from the task's current status, each with the action, the state it lands in, and its gates. Read it BEFORE a scripted task transition to see whether the action you are about to fire terminates the task (a terminal state has no next actions) or lands in a non-terminal one that needs a follow-up — e.g. complete may route to review, whose next_actions name approve.
- kind:
read - output:
object— --format plain prints its task reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--full- Include the body and every sub-object (comments, runs, artifacts, events).--with-body- Include task body content. Also populates body_version/body_position/body_created_at/body_created_by describing the VERSION actually loaded (LET-1492, mirroring comment show; body_position is its 1-based place in creation order, LET-1859) — distinct from the task-level created_at/created_by, which describe who opened the task, not who wrote this text.--body-version v- Read a SPECIFIC historical body version instead of the latest (requires --with-body). The value is a version SLOT as stored (the create-time body is slot1; everytask body addwrites a bod_<id>) or a 1-based POSITION in creation order (LET-1859, the rule comment/run/artifact/version references follow): a slot of that exact name wins, otherwise N is the Nth version, so--body-version 2is the first rewrite. A version that does not exist is refused FW-REF-MISSING-VERSION (exit 2), naming the versions that DO oldest first;--body-versionslists them with their position. Works over --server-url too (GET ...?body_version=V, LET-1794); a server that does not honour the selector is refused rather than its latest body being returned as history.--body-versions- List the task body-version history, oldest first (creation order) — each slot with its 1-based position and the author and timestamp of that VERSION (not of the task). This is the command the --body-version refusal names; LET-159 shipped the refusal naming it before the flag existed, so following the guidance used to land in FW-CMD-UNKNOWN-FLAG. Same ordering as the refusal listing and as --body-version's position, so the three cannot disagree. Works over --server-url too (GET ...?body_versions=true, LET-1794).--with-comments- Include task comments, METADATA ONLY — comment bodies are not included, because they are unbounded and a task can carry many. A warning (FW-READ-COMMENT-BODIES-OMITTED) announces the omission whenever at least one comment is projected. Use --full for every body, or comment show REF ID --with-body for one.--with-runs- Include task runs.--with-artifacts- Include task artifacts.--with-events- Include task-affecting events.
Examples:
lettuce task show lettuce/LET-1 --with-body --format json
lettuce task show lettuce/LET-1 --full --format json
Notes:
- The result carries the optional external-reference fields external_url/external_ci/external_merged when set (LET-1743), plus a derived awaiting_merge:true and a non-fatal FW-TASK-AWAITING-MERGE warning when the task is TERMINAL and external_merged is false (LET-1744) — the signal that distinguishes a done ticket from a landed change. An unknown or absent merge state stays silent, and the advisory never refuses.
- For every declared-absence field (milestone) the task has NO value for, the result carries
absence: {"<field>": {"state": "declined"|"untriaged", "declared_at": …}}(ADR 0029): declined is a recorded decision (task create --no-milestone, task set REF milestone --none), untriaged means nobody decided. Who declared and why are on the field-set event (--with-events, task audit). task list rows carry the same key. - On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
task list
Usage: lettuce task list [filters] [--refs-only] [--include-archived] [--with-lease] [--unleased|--leased] [--limit N] [--offset N]
List tasks in a project, filtered by the required Section 20.4 task-list flags. Machine output always keeps the native data.tasks/count/total/limit/offset envelope regardless of which filters are present; richer filters may use FQL internally but never switch the command to data.rows. --with-lease adds each task's lease; --unleased/--leased filter by lease state (coordination).
- kind:
read - output:
collection— --format plain prints one task reference (reference) per row oftasks; nothing when empty - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
listing— proportional to the collection it reads once (paging or the task projection bound it) - requires root:
true - requires project:
true - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--status status- Filter by workflow status.--workflow workflow- Filter by workflow reference.--assignee author- Filter by assignee.--reporter author- Filter by reporter.--label slug- Filter by label. Repeatable.--component slug- Filter by component.--milestone slug- Filter by milestone.--type slug- Filter by task type.--severity slug- Filter by severity.--priority-min n- Minimum priority, inclusive.--priority-max n- Maximum priority, inclusive.--due-before timestamp- Due at or before this timestamp.--due-after timestamp- Due at or after this timestamp.--updated-before timestamp- Updated at or before this timestamp.--updated-after timestamp- Updated at or after this timestamp.--parent task-ref- Filter by parent task reference.--watcher author- Filter by watcher. Repeatable.--text text- Filter by free-text match.--custom key=value- Filter by a custom-field value (repeatable; AND across keys). Repeatable.--refs-only- Return task references only.--limit n- Maximum tasks.--offset n- Task offset.--include-archived- Include archived tasks in the results (hidden by default). Read an ARCHIVED project on purpose: without this flag a read scoped to an archived project is refused (FW-READ-PROJECT-ARCHIVED); with it the read succeeds and is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning, and project_archived_at in the json/yaml data).--with-lease- Include each task's lease (holder, status, expires_at) in the result (coordination).--unleased- Only tasks with no ACTIVE lease — available to take over (an expired lease counts as unleased); implies --with-lease. Mutually exclusive with --leased.--leased- Only tasks with an ACTIVE lease; implies --with-lease. Mutually exclusive with --unleased.
Examples:
lettuce task list --project lettuce --status ready --format json
lettuce task list --project lettuce --unleased --format json
lettuce task list --project lettuce --assignee jota --label backend --format plain
lettuce task list --project lettuce --limit 20 --offset 0 --format json
Notes:
- On an ARCHIVED project this read is refused with FW-READ-PROJECT-ARCHIVED (exit 1) unless --include-archived is passed; with it the result is labelled ARCHIVED (an FW-READ-PROJECT-ARCHIVED-SHOWN warning and project_archived_at in the json/yaml data).
task exists
Usage: lettuce task exists REF
Query whether a task exists; the exit code encodes the answer (LET-415). A well-formed REF always returns ok:true with data {reference, exists}: exit 0 = exists, exit 2 = does not exist (human formats print <project>/<id> does not exist on stdout). A malformed REF or usage error is an error envelope with exit 1; a missing project stays FW-REF-MISSING-PROJECT.
- kind:
read - output:
verdict— --format plain prints the task reference (reference) iffexistsis true; nothing otherwise (the explanation on stderr; the exit code is the answer) - exit codes:
0yes (exists);2no: the answer, not a failure (ok:true; output contract R6); any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Examples:
lettuce task exists lettuce/LET-1 --format json
lettuce task exists lettuce/LET-1 --format plain
Notes:
- On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
task set
Usage: lettuce task set REF FIELD (VALUE | --none [--reason TEXT]) [--expect-revision N]
Set a mutable scalar task field (title, priority, severity, type, assignee, reporter, reviewer, component, milestone, estimate, due-at, parent, workflow, external-url, external-ci, external-merged), or with --none DECLARE that the task has no value for a declared-absence field (milestone). Status is NOT settable here — use task transition. Reassigning workflow carries two conditions: the task's CURRENT status must exist in the target workflow (spec §23.10), and the target may not DROP a requirement the current workflow enforces on a shared outgoing action — moving a task gated on custom/grc into an ungated workflow would otherwise let it close with no evidence. Neither field can be cleared: title and workflow are required.
- kind:
mutation - output:
object— --format plain prints its task reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
true - external input keys (these go INSIDE
data, not at the top level):task_ref, field, value, none, reason, expected_revision - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--none- Instead of a VALUE: DECLARE that the task has no value for FIELD — a recorded decision (declared absence, ADR 0029), only for a declared-absence field. One field-set event clears the value (if any) and carries declared-absent=true, and the marker declared-absent/<field> is written; the task then readsdeclinedinstead ofuntriaged. Refused with a VALUE and for any other field.--reason text- Optional reason recorded on the declaring event (with --none only; refused without it). A blank reason is no reason.--expect-revision n- Expected current revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch.
Examples:
lettuce task set lettuce/LET-1 priority 50 --author agent-1 --expect-revision 1 --format json
lettuce task set lettuce/LET-1 workflow hardened --author agent-1 --format json
lettuce task set lettuce/LET-1 milestone --none --reason 'process work, no milestone applies' --author agent-1 --format json
Notes:
- A workflow reassignment refused with FW-TASK-WORKFLOW-GATE-WEAKENED names every requirement the target would lose; FW-TASK-WORKFLOW-STATUS-ABSENT lists the states the target does have.
- Declared absence (ADR 0029): a task's milestone is SET, DECLINED (--none: a recorded decision) or UNTRIAGED (nobody decided — every task filed without one and never triaged). --none is ONE field-set event that clears the value (if any) and carries declared-absent=true plus the optional reason, and writes the marker declared-absent/<field> with the event's time;
task show/task listthen reportabsence: {"<field>": {"state": "declined"}}. Setting a VALUE later supersedes the declaration (the marker goes);task unsetreturns the task to untriaged. Find them with FQL<field> declined/<field> untriaged(<field> missingmatches both). A workflow can require triage with the requires-field entrytriaged/<field>(set OR declined).
task set-where
Usage: lettuce task set-where --where '<fql>' FIELD (VALUE | --none [--reason TEXT]) [--confirm] [--dry-run] [--include-archived]
Bulk-set a mutable scalar field on every task matching an FQL where-filter, or with --none bulk-DECLARE the field's absence (triage: --where 'milestone untriaged and …' milestone --none --reason TEXT). Requires --confirm to mutate more than one task; --dry-run previews the matched refs without writing.
- kind:
mutation - output:
collection— --format plain prints one task reference (reference) per row ofresults; nothing when empty; rows it could not judge, or that failed, named on stderr - exit codes:
0success;2some matched tasks failed (ok:true with per-row results); FW-BULK-ALL-FAILED (ok:false) when every one did; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
false - external input keys (these go INSIDE
data, not at the top level):where, field, value, none, reason, confirm, dry_run - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--where fql- FQL where-clause selecting the tasks to mutate (same syntax as query tasks/task list --where). Required.--confirm- Confirm applying the change to all matched tasks (required for more than one match).--dry-run- List the matched tasks and intended change without writing.--include-archived- Also mutate soft-archived tasks matching the filter (excluded by default so a retired, list-hidden task is never silently bulk-mutated).--none- Instead of a VALUE: DECLARE that the task has no value for FIELD — a recorded decision (declared absence, ADR 0029), only for a declared-absence field. One field-set event clears the value (if any) and carries declared-absent=true, and the marker declared-absent/<field> is written; the task then readsdeclinedinstead ofuntriaged. Refused with a VALUE and for any other field.--reason text- Optional reason recorded on the declaring event (with --none only; refused without it). A blank reason is no reason.
Examples:
lettuce task set-where --where 'status = "open"' priority 50 --confirm --author agent-1 --format json
lettuce task set-where --where 'milestone untriaged and labels contains "process"' milestone --none --reason 'process work' --confirm --author agent-1 --format json
lettuce task set-where --where 'component = "api"' milestone release-1 --dry-run --author agent-1 --format json
Notes:
- Soft-archived tasks are EXCLUDED from the where-filter match by default: a task you archived to retire it is not silently bulk-mutated. Pass --include-archived to consciously mutate archived tasks too.
task unset
Usage: lettuce task unset REF FIELD [--expect-revision N]
Clear an optional scalar task field.
- kind:
mutation - output:
object— --format plain prints its task reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
true - external input keys (these go INSIDE
data, not at the top level):task_ref, field, expected_revision - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--expect-revision n- Expected current revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch.
Examples:
lettuce task unset lettuce/LET-1 priority --author agent-1 --format json
task set-list
Usage: lettuce task set-list REF FIELD --value VALUE [--value VALUE...] [--expect-revision N]
Replace a mutable task list field.
- kind:
mutation - output:
object— --format plain prints its task reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
true - external input keys (these go INSIDE
data, not at the top level):task_ref, field, values, expected_revision - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--value value- List value. Required. Repeatable.--expect-revision n- Expected task revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch.
Examples:
lettuce task set-list lettuce/LET-1 labels --value backend --value api --author agent-1 --format json
Notes:
- The referenced custom field, label, or component must first be created with lettuce registry create.
task transition
Usage: lettuce task transition REF ACTION [--reason TEXT] [--facilitate] [--expect-revision N]
Apply a workflow transition. Every requires-* gate the transition declares is checked first, and a gate whose value is a REFERENCE is satisfied only by a reference that still resolves — an archived task, an inactive label, or a custom field whose declared value-kind names something missing does not count. Refusals distinguish the reason so a client can branch on the code: FW-WF-REQUIREMENT-UNSATISFIED (the bar is not met at all), FW-WF-REQUIREMENT-REFERENT-MISSING (the cited object does not exist or was withdrawn), FW-WF-REQUIREMENT-REFERENT-UNLINKED (the cited object exists but recorded no link back to this task — the graph-run-case case), FW-WF-REQUIREMENT-REFERENT-INCOMPLETE (the cited run-case exists and is linked, and never produced the carriers its own graph-def declares — linkage is one edge, a graph is a walk), FW-WF-REQUIREMENT-REFERENT-UNFIRED (it produced every declared carrier and its join never fired, so the evidence was never judged; scoped to defs declaring a fork or join station).
- kind:
mutation - output:
object— --format plain prints its task reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
true - external input keys (these go INSIDE
data, not at the top level):task_ref, action, reason, expected_revision - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--reason text- Reason text.--reason-file path- Read reason from file.--expect-revision n- Expected task revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch.--facilitate- Record-not-enforce an unsatisfied requires-* gate (LET-1560): proceed with the transition even though a requirement is not met, recording the unsatisfied requirement AND the mandatory --reason on the transition event as afacilitatedannotation — the task-level twin ofcell transition --facilitate. The default is refusal (FW-WF-REQUIREMENT-UNSATISFIED); --facilitate REQUIRES --reason (the exception's rationale), records rather than bypasses (a reader of task state encounters it), never touches the lease gate, and is inert when the gate already passes.
Examples:
lettuce task transition lettuce/LET-1 start-work --author agent-1 --reason 'Ready.' --format json
lettuce task transition lettuce/LET-966 complete --facilitate --reason 'closing on a documented exception; grc gate unsatisfiable, see LET-1560' --author agent-1 --format json
Notes:
- start-work and other lease-gated actions require an active lease; acquire one with lettuce lease acquire first.
- --facilitate (LET-1560) is record-not-enforce, not a bypass: it proceeds past an unsatisfied requires-* gate while writing the unsatisfied requirement and the mandatory --reason onto the transition event, so the exception is a ledger fact. It REQUIRES --reason, never relaxes the gate for a non-facilitate transition, and never bypasses the lease gate.
- A requires-field gate over a custom field of value-kind graph-run-case demands THREE things: that the run-case exists, that it recorded a produced-effect on this task (the reverse of graph-run-case refs-to), and that it produced a carrier for every edge its own graph-def declares one on. WHAT IT DOES NOT COVER (LET-1449): the gate checks that a carrier EXISTS on each declared edge and never reads its VALUE, so --value "." satisfies it exactly as well as a real verdict, and it cannot tell whether the verifier did any work. Presence is not quality; a green gate proves evidence was recorded, not that it is any good. The demand is read from the def, so a def declaring no carriers is asked for none, and a run-case whose def or pin cannot be resolved is left unjudged rather than refused. Full conformance of the walk is still graph-run-case conform. The RESULT reports the landed status and
next_actions— the transitions reachable from it — so a caller can branch: acompletethat lands in a non-terminal state carries the follow-up action (e.g.approve) instead of silently leaving the task short of terminal.
task clone
Usage: lettuce task clone REF NEW-ID
Duplicate a task's authored content (title, body, scalar + custom fields, and the list-relation fields labels/depends-on/blocks/watchers) into a fresh task.
- kind:
mutation - output:
object— --format plain prints its task reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
false
Examples:
lettuce task clone lettuce/LET-1 LET-2 --author agent-1 --format json
Notes:
- The clone resets id, status (to the workflow's initial state), revision, and timestamps; and it does NOT copy the cells declaration (a clone is a fresh ticket that has not made that coverage claim — it re-declares its own cells via --cell; LET-1586, cells-v0.16 §1), runs, comments, artifacts, the lease, or the event history.
- NEW-ID accepts the qualified spelling PROJECT/ID when it names the SOURCE task's project; a clone never moves a task across projects, so another project is refused (LET-1892).
task reopen
Usage: lettuce task reopen REF [--reason TEXT] [--expect-revision N]
Move a terminal task (done/failed/canceled) back to its workflow's initial state.
- kind:
mutation - output:
object— --format plain prints its task reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--reason text- Reason text.--reason-file path- Read reason from file.--expect-revision n- Expected task revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch.
Examples:
lettuce task reopen lettuce/LET-1 --author agent-1 --reason 'Regression found.' --format json
Notes:
- Only a terminal task can be reopened; a non-terminal task returns FW-TASK-NOT-TERMINAL. Records a transition-applied event so the audit trail matches a normal transition.
task archive
Usage: lettuce task archive REF [--reason TEXT] [--expect-revision N]
Soft-archive a task: hide it from default listings without deleting history. Reversible with lettuce task unarchive.
- kind:
mutation - output:
object— --format plain prints its task reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--reason text- Reason text.--expect-revision n- Expected task revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch.
Examples:
lettuce task archive lettuce/LET-1 --author agent-1 --reason 'Superseded.' --format json
Notes:
- Archived tasks are hidden from task list unless --include-archived is passed. This is reversible and preserves the full event history; use lettuce task delete for a hard, non-reversible removal.
task unarchive
Usage: lettuce task unarchive REF [--reason TEXT] [--expect-revision N]
Restore a previously archived task back to normal (unarchived) visibility.
- kind:
mutation - output:
object— --format plain prints its task reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--reason text- Reason text.--expect-revision n- Expected task revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch.
Examples:
lettuce task unarchive lettuce/LET-1 --author agent-1 --format json
Notes:
- Only an archived task can be unarchived; the inverse of lettuce task archive.
task delete
Usage: lettuce task delete REF (--yes|--force) [--cascade] [--reason TEXT] [--expect-revision N]
Hard-delete a task and its sub-objects. Non-reversible: requires --yes (or --force) to confirm. A task with child tasks needs --cascade. The delete writes a tombstone (query audit answers the removed task, its comments and artifacts) and keeps, inside it, the deletion events of the removed tasks' comments and artifacts deleted earlier, whose ledger it removes (LET-1990).
- kind:
mutation - output:
object— --format plain prints its task reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
false
Flags:
--yes- Confirm the non-reversible deletion.--force- Alias for --yes: confirm the non-reversible deletion.--cascade- Also delete child tasks; without it a task with children is refused rather than orphaning them.--reason text- Reason text.--expect-revision n- Expected task revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch.
Examples:
lettuce task delete lettuce/LET-1 --yes --author agent-1 --format json
Notes:
- This is a hard, non-reversible removal recorded in the operation ledger; prefer lettuce task archive when you only want to hide the task. --cascade also deletes child tasks; without it a task that has children returns an error rather than orphaning them.
task body add
Usage: lettuce task body add REF (--body TEXT|--body-file PATH) [--expect-revision N]
Add a new task body version.
- kind:
mutation - output:
object— --format plain prints its task reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
true - external input keys (these go INSIDE
data, not at the top level):task_ref, body, expected_revision - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--body text- Body text. Required.--body-file path- Read body text from a file.--expect-revision n- Expected task revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch.
Examples:
lettuce task body add lettuce/LET-1 --author agent-1 --body 'Updated body.' --format json
task audit
Usage: lettuce task audit REF
Read task event history.
- kind:
read - output:
collection— --format plain prints one event reference (reference) per row ofevents; nothing when empty - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Examples:
lettuce task audit lettuce/LET-1 --format json
lettuce task audit lettuce/LET-1 --format plain
Notes:
- On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
task graph
Usage: lettuce task graph REF [--relation REL] [--depth N] [--include-archived]
Traverse task relations.
- kind:
read - output:
collection— --format plain prints one task reference (reference) per row ofnodes; nothing when empty - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - read cost:
bounded— bounded by the objects it names, independent of the project's size - requires root:
true - requires project:
false - requires author:
false - stable snapshot:
true - idempotency key:
false
Flags:
--relation depends-on|blocks|parent|children- Relation to traverse.--depth n- Traversal depth (default 1). A traversal capped by --depth sets truncated:true and marks each boundary node has_more, so an incomplete closure is never silently returned — raise --depth to see the rest.--include-archived- Include soft-archived tasks in the traversal (default: hidden, consistent with query run / task list); an included archived node is marked archived.
Examples:
lettuce task graph lettuce/LET-1 --relation depends-on --depth 2 --format json
lettuce task graph lettuce/LET-1 --relation blocks --depth 1 --format json
Notes:
- On an ARCHIVED project this read is answered (the explicit reference is the explicit ask) and labelled ARCHIVED: an FW-READ-PROJECT-ARCHIVED-SHOWN warning (stderr in table/plain, warnings[] in json/yaml) and project_archived_at in the json/yaml data.
custom set
Usage: lettuce custom set REF FIELD VALUE [--expect-revision N]
Set a task custom field value. A BLANK value (empty or whitespace-only) for an OPTIONAL string field means no value: it CLEARS the field exactly like custom clear (spec §3.3.1, LET-1909); a required field refuses it. The value is validated against the field's declared value-kind, and the REFERENCE kinds (author, label, task, graph-run-case) must resolve to something that exists — a value naming a missing author/label/task/run-case is refused FW-REF-MISSING-* rather than stored. A graph-run-case value must name an existing run-case; the additional requirement that the run-case recorded an effect on THIS task is enforced where the claim is cashed in — the workflow requires-field gate on task transition.
- kind:
mutation - output:
object— --format plain prints its task reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
true - external input keys (these go INSIDE
data, not at the top level):task_ref, field, value, expected_revision - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--expect-revision n- Expected current revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch.
Examples:
lettuce custom set lettuce/LET-1 customer acme --author agent-1 --format json
lettuce custom set lettuce/LET-1 grc grc_9f3a1c2b --project lettuce --author agent-1 --format json
Notes:
- The referenced custom field, label, or component must first be created with lettuce registry create.
custom clear
Usage: lettuce custom clear REF FIELD [--expect-revision N]
Clear an optional task custom field value.
- kind:
mutation - output:
object— --format plain prints its task reference (reference) - exit codes:
0success; any failure:1-7by diagnostic class (spec §24.11) - requires root:
true - requires project:
false - requires author:
true - stable snapshot:
false - idempotency key:
true - external input keys (these go INSIDE
data, not at the top level):task_ref, field, expected_revision - external input envelope:
{"schema_version":"v0.16","kind":"lettuce-command-input","data":{ ...command fields... }}
Flags:
--expect-revision n- Expected current revision. Compared against the store THIS COMMAND READS — fetched shared state in dedicated-git mode, the LOCAL tree in filesystem mode, where a concurrent write by another agent is invisible. Seelettuce usageAgent Guidance for what that does not catch.
Examples:
lettuce custom clear lettuce/LET-1 customer --author agent-1 --format json
Notes:
- The referenced custom field, label, or component must first be created with lettuce registry create.
Links to
- Command Reference
reference/command-reference
Backlinks
- Dependencies — what depends-on asserts, and which end you start from
concepts/concept-dependency - Tasks and the work plane
concepts/concept-task - Command Reference
reference/command-reference - reference/index
reference/index
Command Reference
reference/command-reference Exhaustive lettuce CLI reference — 209 commands across 13 groups, generated from the binary.
The complete lettuce command-line surface — 209 commands across 13 groups. Generated by lettuce usage --format okf, so it never drifts from the binary.
Command groups
- Core And Runtime — 18 commands
- Authors And Projects — 18 commands
- Registries And Workflow — 15 commands
- Tasks — 19 commands
- Task Local Objects — 35 commands
- Queries — 13 commands
- Import Export Repair And Sync — 17 commands
- Server And GitHub — 5 commands
- Cells — 28 commands
- Definition Of Done — 11 commands
- Coverage Grid — 6 commands
- Graph Authoring — 10 commands
- Graph Run-Cases — 14 commands
Links to
- Authors And Projects — Commands
reference/cmd-authors-and-projects - Cells — Commands
reference/cmd-cells - Core And Runtime — Commands
reference/cmd-core-and-runtime - Coverage Grid — Commands
reference/cmd-coverage-grid - Definition Of Done — Commands
reference/cmd-definition-of-done - Graph Authoring — Commands
reference/cmd-graph-authoring - Graph Run-Cases — Commands
reference/cmd-graph-run-cases - Import Export Repair And Sync — Commands
reference/cmd-import-export-repair-and-sync - Queries — Commands
reference/cmd-queries - Registries And Workflow — Commands
reference/cmd-registries-and-workflow - Server And GitHub — Commands
reference/cmd-server-and-github - Task Local Objects — Commands
reference/cmd-task-local-objects
+1 more
- Tasks — Commands
reference/cmd-tasks
Backlinks
- Getting started
getting-started - First contact — what a fresh agent sees, reads, and does
guides/guide-first-contact - Lettuce Documentation
index - What is lettuce
overview - Authors And Projects — Commands
reference/cmd-authors-and-projects - Cells — Commands
reference/cmd-cells - Core And Runtime — Commands
reference/cmd-core-and-runtime - Coverage Grid — Commands
reference/cmd-coverage-grid - Definition Of Done — Commands
reference/cmd-definition-of-done - Graph Authoring — Commands
reference/cmd-graph-authoring - Graph Run-Cases — Commands
reference/cmd-graph-run-cases - Import Export Repair And Sync — Commands
reference/cmd-import-export-repair-and-sync
+7 more
- Queries — Commands
reference/cmd-queries - Registries And Workflow — Commands
reference/cmd-registries-and-workflow - Server And GitHub — Commands
reference/cmd-server-and-github - Task Local Objects — Commands
reference/cmd-task-local-objects - Tasks — Commands
reference/cmd-tasks - reference/index
reference/index - Why lettuce (and not just Markdown + a convention)
why-lettuce
reference/index
reference/index reserved
- Command Reference — exhaustive lettuce CLI reference, 209 commands across 13 groups.
- Core And Runtime — Commands — 18 commands.
- Authors And Projects — Commands — 18 commands.
- Registries And Workflow — Commands — 15 commands.
- Tasks — Commands — 19 commands.
- Task Local Objects — Commands — 35 commands.
- Queries — Commands — 13 commands.
- Import Export Repair And Sync — Commands — 17 commands.
- Server And GitHub — Commands — 5 commands.
- Cells — Commands — 28 commands.
- Definition Of Done — Commands — 11 commands.
- Coverage Grid — Commands — 6 commands.
- Graph Authoring — Commands — 10 commands.
- Graph Run-Cases — Commands — 14 commands.
Links to
- Authors And Projects — Commands
reference/cmd-authors-and-projects - Cells — Commands
reference/cmd-cells - Core And Runtime — Commands
reference/cmd-core-and-runtime - Coverage Grid — Commands
reference/cmd-coverage-grid - Definition Of Done — Commands
reference/cmd-definition-of-done - Graph Authoring — Commands
reference/cmd-graph-authoring - Graph Run-Cases — Commands
reference/cmd-graph-run-cases - Import Export Repair And Sync — Commands
reference/cmd-import-export-repair-and-sync - Queries — Commands
reference/cmd-queries - Registries And Workflow — Commands
reference/cmd-registries-and-workflow - Server And GitHub — Commands
reference/cmd-server-and-github - Task Local Objects — Commands
reference/cmd-task-local-objects
+2 more
- Tasks — Commands
reference/cmd-tasks - Command Reference
reference/command-reference
Backlinks
No backlinks.
Broken links (0)
No broken internal links.