| name | effort-graph |
| description | Journal reasoning (decisions, findings, issues, constraints, risks) into a Flatbread Effort Graph and recall it with bounded reads. Use when starting or resuming a thread of work, recording a decision or finding, resolving an issue, checking what is blocking or still open on an effort, or when the user mentions effort graph, journaling, blocking decisions, or agent memory. |
Effort Graph — agent journaling and recall
The Effort Graph is persistent, queryable memory for long-horizon work, stored
as markdown records in the repo. Six primitives: Effort (the anchor thread
of work), Issue, Finding, Decision, Constraint, Risk.
Every record belongs to exactly one Effort. You write through 13 typed
mutations and read through 5 bounded queries — never by hand-editing record
frontmatter (bodies may be edited freely).
Read glossary.md for the primitive and edge semantics before
inventing a new record kind or relation.
All commands run from the project root via the flatbread CLI (pnpm exec flatbread, npm exec -- flatbread, yarn flatbread, or bunx flatbread).
Commands print one JSON object to stdout;
errors print JSON to stderr and exit 1.
First activation
Read setup.md, make the reviewed config and gitignore edits, then
run flatbread effort bootstrap followed by flatbread effort bootstrap --verify. Bootstrap is report-only and never edits project files.
Prerequisites
Your flatbread.config.* must include the preset:
import {
defineConfig,
sourceFilesystem,
transformerMarkdown,
effortGraphContent,
} from 'flatbread';
export default defineConfig({
source: sourceFilesystem(),
transformer: transformerMarkdown(),
content: [...effortGraphContent()],
});
Records live under <root>/{efforts,issues,findings,decisions,constraints,risks}/.
The write journal is <root>/.journal/; read digests cache under
.flatbread/effort-graph/read-cache/ (both gitignored).
Writing (journaling)
One command for all 13 mutations — pass the payload as a single JSON argument:
flatbread effort write '{"type":"WriteDecision","effort":"<eff-id>","title":"...","body":"...","derives_from":["<id>"]}'
Response: {"generation":"<token>","artifacts":[{"id","path","operation"}],"touched":[...]}.
Capture artifacts[0].id to wire later edges, and keep generation
for strict read-your-writes.
Full payload shapes for all 13 mutations: read reference.md.
Critical semantics:
- Creates always start in the initial lifecycle state:
WriteDecision →
proposed, WriteIssue → open, WriteRisk → open. You cannot pass a
state; use lifecycle mutations (AcceptDecision, ResolveIssue,
MitigateRisk, SetRiskState) to transition.
AcceptDecision defaults rejectSiblings: true, which rejects ALL other
proposed Decisions in the same Effort. Pass "rejectSiblings": false
unless you deliberately want the competing proposals closed.
- Edges are forward-only in payloads (
derives_from, supersedes,
invalidates); back-edges are materialized automatically.
- When superseding, open the new record's body with a short rollup of what
changed and why — reads render ancestors only as one-line checkpoints.
- For a hard-to-reverse, surprising decision made after a real trade-off, use
the Decision body as the durable rationale: include context, alternatives,
consequences, and reversal criteria. Do not create a parallel ADR; use
effort-modeling when the decision is still
being grilled.
Reading (recall)
Every read returns a bounded envelope, not records: a ≤160-token summary,
an artifact_path to a rendered markdown digest (the evidence — spend one
Read on it, or grep it), served_generation, page info, and ≤10 executable
hints. Digests cap at 25 records / one-hop expansion / 50 edges / 64 KiB.
Browse digests (list, records, relations, blocking-decisions) excerpt
each body at 600 chars / 12 lines ([…truncated]). effort get digests
always include the full record body (still subject to the 64 KiB digest
byte cap). Zoom in with get, then Read/grep that digest — do not open
.flatbread-efforts/**/*.md for normal full-body recall.
flatbread effort blocking-decisions <effortId>
flatbread effort list --status active
flatbread effort records <effortId> --kinds issue --status open --since 2026-07-01T00:00:00Z --limit 10
flatbread effort records <effortId> --kinds decision --state proposed --limit 10
flatbread effort relations <effortId> <fromId> --relations derives_from,superseded_by
flatbread effort get <id> [--resolve head]
Flags shared by reads: --strict-min-generation <token> (with optional
--timeout-ms <ms>, default 3000) and, on list/records/relations, --limit
(≤25) and --cursor (opaque next_cursor from a prior page; only valid for
the same query at the same generation).
effort list is bounded Effort discovery. It defaults to active; valid
statuses are exactly active, paused, completed, and abandoned.
Comma-separated statuses are ORed. Results use the shared created_at, then
id ordering. After discovery, use bounded effort-scoped reads.
Consistency: reads are eventual by default. Immediately after a write,
pass the returned generation as --strict-min-generation — you get either
fresh data or an EFFORT_GRAPH_GENERATION_WAIT_TIMEOUT error (exit 1),
never silently stale results. Do not build polling loops; the wait is
server-side.
Recommended session workflow
- Resume / status briefing (bounded fast-path):
effort list --status active
and trust the returned digest. For each active Effort, run
effort records <effortId> --kinds issue,decision and read each record's
status/state from that one digest. Run effort blocking-decisions <effortId>
only for an Effort whose digest shows an open blocker Issue — skip it
otherwise. Do not open raw .flatbread-efforts/**/*.md for briefing;
browse digests are authoritative for status/state. Budget ≈ (1 + number
of active Efforts) digest reads. A 12-run experiment across three model
families showed this roughly halves recall tool calls with no loss of
answer quality (Decision
dec-adopt-a-bounded-status-briefing-fast-path-for-ef--kcw0rw39g3b2ym2h).
- When a browse digest shows
[…truncated] and you need the body: run
flatbread effort get <id>, then Read/grep that digest (artifact_path)
for the full body. Reserve opening .flatbread-efforts/**/*.md for rare
cases (e.g. digest byte-cap miss on an oversized record), not normal
zoom-in.
- During work: journal Findings as evidence lands; open Issues for real
gaps/blockers; record Decisions with
derives_from citing the Findings,
Constraints, and Issues they respond to.
- On commitment:
AcceptDecision (mind rejectSiblings), ResolveIssue
with resolvedBy citing the closing Decision/Findings.
- Maintenance:
flatbread effort cache prune deletes digests older than
24h / over the 100 MiB ceiling.