Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions .agents/skills/effort-graph/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ The write journal is `<root>/.journal/`; read digests cache under

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH — Prerequisites still list only six record folders (efforts…risks). Opening copy above the hunk still says “Six primitives” / “13 typed mutations” while this file later documents 15 mutations and Citation/Blob.

Minimal fix: Extend this path list to include citations/blobs, and update the opening paragraph + YAML description to eight collections / 15 mutations (sync packages/effort-graph/skills/… copy).

## Writing (journaling)

One command for all 13 mutations — pass the payload as a single JSON argument:
One command for all 15 mutations — pass the payload as a single JSON argument:

```bash
flatbread effort write '{"type":"WriteDecision","effort":"<eff-id>","title":"...","body":"...","derives_from":["<id>"]}'
Expand All @@ -59,18 +59,23 @@ Response: `{"generation":"<token>","artifacts":[{"id","path","operation"}],"touc
**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](./reference.md).
Full payload shapes for all 15 mutations: read [reference.md](./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.
`MitigateRisk`, `SetRiskState`) to transition. `WriteCitation` and
`WriteBlob` have no lifecycle state.
- `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.
- Cites: `WriteCitation` (body may be a URL; optional `blob` + `role`), then
`cites: ["<cit-id>"]` on any epistemic create. Flatbread `refs` link
records → Citation → optional Blob. Bounded digests omit Blob bodies —
use `effort get`.
- 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
Expand Down
33 changes: 31 additions & 2 deletions .agents/skills/effort-graph/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,46 @@ A prospective negative outcome with likelihood and severity. It is open,
mitigated by an accepted Decision, realized with evidence, or explicitly
accepted.

### Citation

A first-class cite target that explains what evidence is and how it relates.
Epistemic records point at Citations via the homogeneous `cites` ref so
Flatbread validates and GraphQL-resolves the edge. A Citation body alone is
valid — often a URL string. An optional `blob` ref attaches a longform/opaque
payload when needed. Optional `role` labels the relationship (e.g. evidence,
context). Citations have no proposed/accepted lifecycle.

### Blob

A citeable, non-epistemic payload: opaque content of any format (markdown,
JSON, images, etc.) with no proposed/accepted lifecycle. Blobs are ordinary
Flatbread content (filesystem-first; other sources such as S3/CDN may back
them later). Records do not cite Blobs directly — they cite a Citation that
may optionally ref a Blob. Bounded digests omit Blob bodies by default; use
`effort get <blob-id>` to read the payload.

## Edges

`derives_from` is causal upstream evidence or context. `supersedes` replaces a
record of the same primitive, while `invalidates` says a record was wrong.
Those forward edges are authoritative; `superseded_by` and `invalidated_by` are
writer-materialized reverse projections. New edge vocabulary needs a
dogfooded query the existing vocabulary cannot express.
writer-materialized reverse projections.

`cites` is a homogeneous Flatbread `refs` edge to Citation on every epistemic
record (Effort, Issue, Finding, Decision, Constraint, Risk). Citation may
optionally `blob` → Blob.

New edge vocabulary needs a dogfooded query the existing vocabulary cannot
express.

## Intentional non-models

Session, Run, Plan, Artifact, Agent, Investigation, Question, Proposal,
Retrospective, and Branch are not collections. Use provenance fields for
operational data; represent questions as Issues, proposals as proposed
Decisions, retrospectives as Findings, and branch history through Git.

**Citation** carries relationship context (and optional Blob). **Blob** is the
longform/payload collection. **Artifact** remains a non-model for run/build
outputs (CLI write-result `artifacts`, digest `artifact_path`, Proof
transcripts) — do not promote those into graph records.
58 changes: 37 additions & 21 deletions .agents/skills/effort-graph/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,26 @@ consumer ground truth.
## IDs

Generated as `<prefix>-<slug>--<16-char-crockford>` with prefixes `eff`,
`iss`, `fnd`, `dec`, `con`, `rsk`. Filenames never define identity. Let the
writer generate ids; capture them from mutation results (`artifacts[0].id`
for creates).
`iss`, `fnd`, `dec`, `con`, `rsk`, `cit`, `blb`. Filenames never define
identity. Let the writer generate ids; capture them from mutation results
(`artifacts[0].id` for creates).

## The 13 mutations (`flatbread effort write '<json>'`)
## The 15 mutations (`flatbread effort write '<json>'`)

Common optional fields on all creates: `id`, `created_at` (ISO with offset),
`produced_in`, `created_by` (opaque provenance strings). Forward edge fields
on all creates except `CreateEffort`: `derives_from[]`, `supersedes[]`,
`invalidates[]` (arrays of existing ids; targets are validated).
on all creates except `CreateEffort`, `WriteCitation`, and `WriteBlob`:
`derives_from[]`, `supersedes[]`, `invalidates[]` (arrays of existing ids;
targets are validated).

Optional `cites[]` on all epistemic creates (`CreateEffort` and every `Write*`
except `WriteCitation` / `WriteBlob`): existing **Citation** ids (homogeneous
Flatbread `refs` → Citation).

### Effort lifecycle

```json
{"type":"CreateEffort","title":"...","body":"...","slug":"optional"}
{"type":"CreateEffort","title":"...","body":"...","slug":"optional","cites":["<cit-id>"]}
{"type":"SetEffortStatus","effortId":"<eff-id>","status":"active|paused|completed|abandoned"}
```

Expand All @@ -33,10 +38,15 @@ on all creates except `CreateEffort`: `derives_from[]`, `supersedes[]`,
{"type":"WriteDecision","effort":"<eff-id>","title":"...","body":"..."}
{"type":"WriteConstraint","effort":"<eff-id>","title":"...","body":"...","kind":"hard|soft"}
{"type":"WriteRisk","effort":"<eff-id>","title":"...","body":"...","likelihood":"low|medium|high","severity":"low|medium|high"}
{"type":"WriteCitation","effort":"<eff-id>","title":"...","body":"https://example.com/...","role":"evidence|context|<free-form>","blob":"<blb-id>"}
{"type":"WriteBlob","effort":"<eff-id>","title":"...","body":"...","kind":"markdown|json|<free-form>"}
```

Initial states: Issue `status: open`; Decision `state: proposed`; Risk
`state: open`.
`state: open`. Citation and Blob have no lifecycle state.

A Citation body alone is valid (commonly a URL). `blob` is optional — attach
a Blob only when you need a longform/opaque payload behind the cite.

### Edge retro-linking (records must already exist)

Expand Down Expand Up @@ -96,9 +106,12 @@ The digest at `artifact_path` is deterministic markdown: YAML query header,
anchor index, per-record sections (selected frontmatter, body, relation
lists), one-hop related records, and an edge table. Body policy:

- **`effort get`:** full record body (the normal zoom-in path).
- **`effort get`:** full record body (the normal zoom-in path), including
Blob payloads and Citation bodies (URLs / short notes).
- **`list` / `records` / `relations` / `blocking-decisions`:** body excerpt
capped at 600 chars / 12 lines (`[…truncated]`).
capped at 600 chars / 12 lines (`[…truncated]`). **Blob bodies are omitted**
from these digests — use `effort get` for the payload. Citation bodies
(usually short) still excerpt normally.

Caps: 25 primary records, one hop, 50 edges, 64 KiB; hitting a cap sets
`complete: false` with named `cap_reasons` — narrow the query or page rather
Expand All @@ -117,16 +130,16 @@ flatbread effort blocking-decisions <effortId> [consistency flags]
flatbread effort cache prune
```

- `--kinds`: `effort|issue|finding|decision|constraint|risk` (records:
default all non-effort kinds).
- `--kinds`: `effort|issue|finding|decision|constraint|risk|citation|blob`
(records: default all non-effort kinds, including `citation` and `blob`).
- `list --status`: defaults to `active`; valid values are exactly `active`,
`paused`, `completed`, and `abandoned`. Values are ORed and results are
ordered by `created_at` ascending, then `id`.
- Filter semantics: AND across different flags, OR within a comma list.
`--since`/`--until` bound `created_at` (gte/lte, ISO strings).
- `--relations` values: `derives_from`, `supersedes`, `superseded_by`,
`invalidates`, `invalidated_by`, `rejected_by`, `mitigated_by`,
`resolved_by`, `evidence` (one hop, explicit only).
`resolved_by`, `evidence`, `cites` (one hop, explicit only).
- `--resolve head`: follow `superseded_by` to the current tip; ancestors
render as checkpoint lines (max 5, then a count).
- `blocking-decisions` membership (frozen): Decision in the effort with
Expand All @@ -145,14 +158,14 @@ flatbread effort cache prune

## Configuration surface

| Option | Where | Default | Notes |
| ---------------- | --------------------------------------------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| Graph root | `effortGraphContent(root)` in `flatbread.config.js` | `.flatbread-efforts` | All six collection paths + refs derive from it; the preset must appear complete and unmodified for detection. |
| Config discovery | cwd of the CLI invocation | — | Exactly one `flatbread.config.*` must exist in cwd. |
| Digest cache | fixed | `<cwd>/.flatbread/effort-graph/read-cache/` | Generation-keyed; gitignore it. `cache prune`: >24h old deleted, then oldest-first to ≤100 MiB. |
| Journal | fixed | `<root>/.journal/` | Writer-owned; gitignored. Never edit. |
| Strict timeout | `--timeout-ms` per read | 3000 ms | |
| Page limit | `--limit` per read | 25 | Hard max 25. |
| Option | Where | Default | Notes |
| ---------------- | --------------------------------------------------- | ------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| Graph root | `effortGraphContent(root)` in `flatbread.config.js` | `.flatbread-efforts` | All eight collection paths + refs derive from it; the preset must appear complete and unmodified for detection. |
| Config discovery | cwd of the CLI invocation | — | Exactly one `flatbread.config.*` must exist in cwd. |
| Digest cache | fixed | `<cwd>/.flatbread/effort-graph/read-cache/` | Generation-keyed; gitignore it. `cache prune`: >24h old deleted, then oldest-first to ≤100 MiB. |
| Journal | fixed | `<root>/.journal/` | Writer-owned; gitignored. Never edit. |
| Strict timeout | `--timeout-ms` per read | 3000 ms | |
| Page limit | `--limit` per read | 25 | Hard max 25. |

## What not to do

Expand All @@ -164,3 +177,6 @@ flatbread effort cache prune
server-side.
- Do not model sessions/plans/agents as records — put provenance in
`produced_in` / `created_by` fields.
- Do not put citation metadata objects into `cites` — use Citation records
(body/role/optional blob) and bare Citation ids in `cites`.
- Do not expect Blob bodies in list/records digests; zoom with `effort get`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
id: blb-blob-cite-design-notes--j3jbgc0cymdb2t9g
effort: eff-effort-graph-memory-and-agent-wedge--szeqvmgqjqnhd002
title: Blob cite design notes
kind: markdown
created_at: '2026-07-19T11:21:00.970Z'
---

# Blob + Citation dogfood

Longform notes for the citeable payload slice.

- Citations are first-class refs targets
- Blob is optional on Citation
- Citation body may be a URL alone
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
id: cit-flatbread-refs-docs-intent--ew3x4qpx1x3c1cy2
effort: eff-effort-graph-memory-and-agent-wedge--szeqvmgqjqnhd002
title: Flatbread refs docs intent
role: context
created_at: '2026-07-19T11:21:02.547Z'
---

https://github.com/FlatbreadLabs/flatbread
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
id: cit-local-blob-design-dump--pg7g6qy1td7yjqpy
effort: eff-effort-graph-memory-and-agent-wedge--szeqvmgqjqnhd002
title: Local blob design dump
role: evidence
blob: blb-blob-cite-design-notes--j3jbgc0cymdb2t9g
created_at: '2026-07-19T11:21:04.044Z'
---

Repo dogfood blob capturing the Citation/Blob split.
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
id: dec-ship-citation-collection-with-optional-blob--fyga3x876n7rcnmn
effort: eff-effort-graph-memory-and-agent-wedge--szeqvmgqjqnhd002
title: Ship Citation collection with optional Blob
state: accepted
created_at: '2026-07-19T11:21:07.225Z'
derives_from:
- fnd-citation-collection-keeps-flatbread-refs-intact--z33ar5vyjxzr7ys0
- iss-implement-blob-collection-and-crumb-graph-cites--g2c7m6j39we5xy3z
cites:
- cit-local-blob-design-dump--pg7g6qy1td7yjqpy
---

## Context

Epistemic records need cite metadata without breaking Flatbread homogeneous refs (string ids only).

## Decision

Add Citation as a first-class collection. Epistemic records use cites→Citation. Citation body alone is valid (e.g. URL). Optional blob→Blob attaches longform payloads. No cite_meta sidecar.

## Alternatives considered

- cite_meta objects alongside cites→Blob: works but bypasses refs for annotations
- cites→Blob only: no place for relationship/URL-only cites

## Consequences

WriteCitation + WriteBlob mutations; digests omit Blob bodies by default.

## Reversal criteria

Revisit if Citation churn is too heavy for simple URL cites or if polymorphic cite targets become necessary.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
id: fnd-citation-collection-keeps-flatbread-refs-intact--z33ar5vyjxzr7ys0
effort: eff-effort-graph-memory-and-agent-wedge--szeqvmgqjqnhd002
title: Citation collection keeps Flatbread refs intact
kind: measurement
created_at: '2026-07-19T11:21:05.639Z'
derives_from:
- iss-implement-blob-collection-and-crumb-graph-cites--g2c7m6j39we5xy3z
cites:
- cit-flatbread-refs-docs-intent--ew3x4qpx1x3c1cy2
- cit-local-blob-design-dump--pg7g6qy1td7yjqpy
---

Homogeneous cites→Citation (optional blob; URL body alone is valid) preserves core refs validation and GraphQL resolve without cite_meta sidecars.
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ id: iss-implement-blob-collection-and-crumb-graph-cites--g2c7m6j39we5xy3z
effort: eff-effort-graph-memory-and-agent-wedge--szeqvmgqjqnhd002
title: Implement Blob collection and Crumb Graph cites
kind: gap
status: open
status: resolved
created_at: '2026-07-19T10:48:48.349Z'
derives_from:
- con-mutation-enum-stays-deliberately-small--45v1ae3neq26g1rz
- dec-name-citeable-longform-payloads-blob--9b11q14fgsx2ytve
resolved_by:
- dec-ship-citation-collection-with-optional-blob--fyga3x876n7rcnmn
- fnd-citation-collection-keeps-flatbread-refs-intact--z33ar5vyjxzr7ys0
---

Implement the accepted Blob decision so Crumb Graph records can cite longform/dynamic payloads.
Expand Down
10 changes: 6 additions & 4 deletions packages/effort-graph/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ finishes completely or is undone.

Version 1 supports these actions: `CreateEffort`, `SetEffortStatus`,
`WriteIssue`, `WriteFinding`, `WriteDecision`, `WriteConstraint`, `WriteRisk`,
`Supersede`, `Invalidate`, `ResolveIssue`, `AcceptDecision`, `MitigateRisk`,
and `SetRiskState`.
`WriteCitation`, `WriteBlob`, `Supersede`, `Invalidate`, `ResolveIssue`,
`AcceptDecision`, `MitigateRisk`, and `SetRiskState`.

The journal is stored in `<root>/.journal` and is ignored by Git. Use
`effortGraphContent()` to add Efforts, Issues, Findings, Decisions,
Constraints, and Risks to a Flatbread configuration. The writer checks links
between those record types.
Constraints, Risks, Citations, and Blobs to a Flatbread configuration. The
writer checks links between those record types. Epistemic records may
optionally `cites` Citation ids (Flatbread `refs`). A Citation body alone is
valid (e.g. a URL); an optional `blob` ref attaches a longform payload.

Read [`skills/effort-graph/glossary.md`](./skills/effort-graph/glossary.md) for
the portable Effort Graph domain model.
Expand Down
11 changes: 8 additions & 3 deletions packages/effort-graph/skills/effort-graph/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ The write journal is `<root>/.journal/`; read digests cache under

## Writing (journaling)

One command for all 13 mutations — pass the payload as a single JSON argument:
One command for all 15 mutations — pass the payload as a single JSON argument:

```bash
flatbread effort write '{"type":"WriteDecision","effort":"<eff-id>","title":"...","body":"...","derives_from":["<id>"]}'
Expand All @@ -59,18 +59,23 @@ Response: `{"generation":"<token>","artifacts":[{"id","path","operation"}],"touc
**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](./reference.md).
Full payload shapes for all 15 mutations: read [reference.md](./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.
`MitigateRisk`, `SetRiskState`) to transition. `WriteCitation` and
`WriteBlob` have no lifecycle state.
- `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.
- Cites: `WriteCitation` (body may be a URL; optional `blob` + `role`), then
`cites: ["<cit-id>"]` on any epistemic create. Flatbread `refs` link
records → Citation → optional Blob. Bounded digests omit Blob bodies —
use `effort get`.
- 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
Expand Down
Loading
Loading