dispatches:
- target: /Users/antony/Development/SisterProject/LLM_STATE/core
kind: child # child | parent | sibling
summary: |
One to three paragraphs describing the learnings and suggested
backlog/memory updates for the target plan. Be specific —
reference file paths, function names, or task ids on the
target side wherever possible.
- target: /Users/antony/Development/PeerProject/LLM_STATE/core
kind: sibling
summary: |
A second dispatch in the same file is dispatched as a
separate subagent in parallel.
Two files coordinate work that spans more than one plan or more than one project. subagent-dispatch.yaml is the triage phase’s hand-off mechanism for propagating cycle learnings to sibling, parent, or child plans within the same project. related-components.yaml is the workstation-wide graph of how separate projects relate to each other — what depends on what, what generates what, what tests what — and is the substrate the cross-project discovery and dispatch flows reason against.
The two files have very different lifecycles. Dispatch files are one-shot scratch — written by triage, consumed by the driver, deleted after use. The related-components graph is durable — generated by the discover pipeline, accepted by the user, and shared (it lives at the config-dir level, not the plan level).
This page documents both formats in full, then covers the controlled vocabularies that the related-components graph draws on: edge kinds, lifecycle scopes, and evidence grades.
subagent-dispatch.yaml
A scratch file written by triage when it has decided one or more sibling/parent/child plans should receive an asynchronous summary of this cycle’s learnings.
The schema is DispatchFile in src/subagent.rs (a thin wrapper around Vec<SubagentDispatch>). Each entry has three fields, all required:
-
target— absolute path to a related plan directory. Relative paths are not supported; the orchestrator does not assume any working directory at dispatch time. -
kind— the relationship as the dispatching plan sees it (child,parent, orsibling). Surfaced verbatim in the dispatched subagent’s prompt so the receiving agent knows the directional context. The vocabulary is conventional, not enforced — anything will parse. -
summary— free-text. Use a|block scalar for multi-line summaries. The receiving subagent is told to "apply only what the summary warrants", so be specific.
Writers. The triage phase writes this file directly with the Write tool — like commits.yaml, it is not a state-CLI-managed file. The contract is documented in the "Cross-plan subagent dispatch" section of defaults/phases/triage.md. Triage may also write nothing (omit the file entirely) when no propagation is warranted; that is the default case.
Readers. The driver only, after the triage phase exits. The driver loads the file via subagent::parse_dispatch_file, spawns one parallel subagent per entry through the active agent backend’s dispatch_subagent implementation, waits for every subagent to complete, then deletes the file. Subsequent cycles never see a stale dispatch.
The dispatched subagent. Each entry produces a fresh agent invocation scoped to the target plan, with a generated prompt naming the target path, the relationship kind, and the summary. The subagent is told to read the target plan’s backlog.yaml and memory.yaml and apply only the changes the summary warrants — conservative by default. For Claude Code that subprocess is claude --add-dir <target> so the agent has read access to the target subtree.
Why one-shot scratch. The driver consumes and deletes the file because dispatch is a within-cycle action — once the dispatched subagents have run, the dispatching plan has nothing more to do with that hand-off. Keeping the file around would invite re-dispatch on the next cycle (a real bug observed before the deletion was added) and would confuse anyone reading the plan dir between cycles.
Hand-editing. You can write this file by hand if you want to dispatch a one-off summary to a related plan without going through triage. Drop it at <plan>/subagent-dispatch.yaml, then trigger any phase transition that walks the driver — the driver will pick it up. The orchestrator does not validate paths up front; a typo in target surfaces as a subagent-spawn error.
related-components.yaml
The workstation-wide component-relationship graph. It lives at <config-dir>/related-components.yaml — not in any plan directory. Participants reference components by name, resolved per-user via the projects catalog (<config-dir>/projects.yaml), so the file is shareable across users with different on-disk paths.
schema_version: 2
edges:
- kind: orchestrates # see <<edge-kinds>>
lifecycle: dev-workflow # see <<lifecycle-scopes>>
participants:
- Ravel-Lite # component names from projects.yaml
- APIAnyware-MacOS
evidence_grade: strong # see <<evidence-grades>>
evidence_fields:
- Ravel-Lite.surface.produces_files
- APIAnyware-MacOS.surface.consumes_files
- APIAnyware-MacOS.surface.explicit_cross_project_mentions
rationale: |
APIAnyware-MacOS explicitly states in its notes that 'the
project uses Ravel-Lite orchestration for its development
lifecycle; plan state is stored under LLM_STATE/ as typed
YAML files consumed by Ravel-Lite agents.' Ravel-Lite produces
<plan-dir>/backlog.yaml, memory.yaml, session-log.yaml, and
phase.md, which map directly to the LLM_STATE/**/*.yaml paths
listed in APIAnyware-MacOS.surface.consumes_files.
- kind: depends-on
lifecycle: build
participants:
- Ravel-Lite
- Atlas
evidence_grade: strong
evidence_fields:
- Atlas.surface.explicit_cross_project_mentions
- Ravel-Lite.surface.explicit_cross_project_mentions
rationale: |
Both projects explicitly name each other in their
cross-project mentions. Atlas is planned to absorb the
component-ontology crate currently living inside Ravel-Lite,
after which Ravel-Lite will depend on Atlas crates rather
than owning the ontology itself.
The schema is RelatedComponentsFile in src/ontology/schema.rs, with Edge as the per-edge type. Two top-level fields:
-
schema_version— must be2. The loader hard-errors on any other value (no in-memory upgrade path; the supported "upgrade" is delete-and-regenerate viastate related-components discover --apply). -
edges— a list ofEdgerecords. Order is not significant for semantics, but is preserved through round-trips.
Each Edge has six fields, all required except evidence_fields (which may be omitted on weak evidence):
-
kind— one of the controlled values in Edge kinds below. Kebab-case in YAML. -
lifecycle— one of the controlled values in Lifecycle scopes. Kebab-case in YAML. -
participants— exactly two distinct component names. Symmetric kinds (communicates-with,co-implements) require participants in sorted order; directed kinds preserve the caller’s order, which is semantic (the "from" → "to" direction defined for that kind). -
evidence_grade— one ofstrong,medium,weak(snake_case in YAML, per the serde derive). Drives the validation rule forevidence_fieldsand influences how downstream consumers treat the edge. -
evidence_fields— list of surface-field paths (e.g.Ravel-Lite.surface.produces_files) that justify the edge. Required when grade isstrongormedium; may be empty only when grade isweak. -
rationale— free-text justification, non-empty after trim. The discover pipeline writes a paragraph here; hand-authored edges should match the same depth.
Writers. The discover pipeline (ravel-lite state related-components discover followed by discover-apply) writes most edges. Hand-authored edges go through ravel-lite state related-components add-edge. Both paths route through RelatedComponentsFile::add_edge, which validates the edge before insertion and dedups against existing edges by canonical key (kind + lifecycle + sorted-or-not participants per the kind’s directionality).
Readers. The work phase reads it via ravel-lite state related-components list --plan <plan> to surface peer-project relationships in the work prompt. The triage phase consults it indirectly — its prompt is fed a "Related plans" block computed from the catalog and graph, and it dispatches against the resulting peer set. The discover pipeline reads it to dedup new proposals against existing edges.
Hand-editing. Possible but unusual — the file is a generated artifact in normal operation. If you do edit by hand, be aware: every edge is validate-d on load, so a malformed file fails fast. The validation rules are the ones documented above (two distinct participants, sorted symmetric participants, non-empty rationale, evidence_fields non-empty unless weak). The supported upgrade path between schema versions is delete and regenerate, not in-place migration.
Filename and location. The constant is RELATED_COMPONENTS_FILENAME = "related-components.yaml" in src/state/filenames.rs; the file sits at <config-dir>/related-components.yaml (the host adapter is in src/related_components.rs).
The CLI workflow
Three verbs cover the typical lifecycle. All live under ravel-lite state related-components.
Add an edge by hand. Useful when you want to record a relationship the discover pipeline has not surfaced (or has not yet been run for):
ravel-lite state related-components add-edge \
depends-on build Ravel-Lite Atlas \
--evidence-grade strong \
--evidence-field 'Atlas.surface.explicit_cross_project_mentions' \
--evidence-field 'Ravel-Lite.surface.explicit_cross_project_mentions' \
--rationale 'Both projects name each other; Atlas owns the ontology crate Ravel-Lite consumes.'
kind and lifecycle are positional; everything else is a flag. Repeat --evidence-field for each field that justifies the edge. Component names must already be in the projects catalog — the CLI rejects unknown names with a pointer to state projects add.
List with filters. Filters compose by AND — supply more flags to narrow further:
ravel-lite state related-components list --plan ./LLM_STATE/core
ravel-lite state related-components list --kind orchestrates
ravel-lite state related-components list --lifecycle dev-workflow --plan ./LLM_STATE/core
--plan <dir> resolves the component name from the catalog (using <plan>/../.. as the project path) and filters to edges that involve that component.
Discover and apply. The two-stage discovery pipeline analyses every catalogued component’s surface metadata, proposes edges, and (after a review gate) merges accepted proposals into related-components.yaml:
ravel-lite state related-components discover # writes discover-proposals.yaml
# … review/edit <config-dir>/discover-proposals.yaml …
ravel-lite state related-components discover-apply # merges into related-components.yaml
ravel-lite state related-components discover --apply # one-shot variant, skips the review gate
A removal verb (remove-edge) exists for the rare cases where an edge needs to be retracted without a full regeneration; see the State commands reference for full flag listings.
Edge kinds
Edge kinds are a closed set defined in src/ontology/schema.rs::EdgeKind. The canonical authoring spec is docs/component-ontology.md §5; this is the user-facing summary.
Most kinds are directed — the participant order is semantic and meaningful ("X depends on Y" is not the same as "Y depends on X"). Two kinds are symmetric — participant order does not matter, and the file format requires participants stored in sorted order to make dedup deterministic.
Kinds are grouped into families:
Dependency family.
depends-on
|
Directed. X requires Y to function. The default lifecycle distinction is |
has-optional-dependency
|
Directed. X functions without Y but adds capability when Y is present. |
provided-by-host
|
Directed. X relies on Y being supplied by the host environment (operating system, runtime container, browser). |
Linkage family.
links-statically
|
Directed. X bundles Y at build time as a static dependency. |
links-dynamically
|
Directed. X loads Y at runtime as a shared library or plugin. |
Generation family.
generates
|
Directed. X produces files that Y consumes (code generators, schema generators, asset pipelines). |
scaffolds
|
Directed. X bootstraps a Y instance once at creation time. Y is independent thereafter. |
Communication family.
communicates-with
|
Symmetric. Two systems exchange messages over a network or IPC channel. |
calls
|
Directed. X invokes Y’s API directly (function call, RPC, HTTP request). |
Orchestration family.
invokes
|
Directed. X spawns Y as a subprocess or job, typically transient. |
orchestrates
|
Directed. X manages Y’s lifecycle, state, and multi-step workflow over time. The relationship Ravel-Lite has with the projects whose plans it runs is |
embeds
|
Directed. X contains Y as a subordinate component in its address space (an embedded interpreter, a vendored library). |
Testing family.
tests
|
Directed. X verifies Y’s behaviour. The lifecycle for a real test relationship is |
provides-fixtures-for
|
Directed. X supplies test data, mocks, or environments to Y’s tests. |
Specification family.
conforms-to
|
Directed. X implements an interface or protocol defined by Y. |
co-implements
|
Symmetric. Two implementations of the same external specification (multiple language bindings; alternative client implementations). |
describes
|
Directed. X is documentation, schema, or specification about Y. |
A given pair of components may have multiple edges as long as they differ in kind or lifecycle (or both). For example, Ravel-Lite and Ravel could carry both orchestrates/dev-workflow (their relationship today, while Ravel’s daemon is unfinished) and co-implements/design (the long-term shared design they implement).
Lifecycle scopes
Lifecycle scopes describe when the relationship matters. Closed set in src/ontology/schema.rs::LifecycleScope.
design
|
At specification or RFC time. Two co-implementations of the same external spec relate at |
codegen
|
At generation time. A |
build
|
At compile or assembly time. |
test
|
While running tests. |
deploy
|
At packaging or shipping time. Relationships specific to release artefacts (a Homebrew formula consumed by an installer) live here. |
runtime
|
At execution time. |
dev-workflow
|
During the development loop itself. Tooling that orchestrates developer activity — like Ravel-Lite running a project’s plans — lives here. |
The same edge kind at different lifecycles records distinct facts. depends-on/build and depends-on/runtime between the same pair are two edges, not one — and they should be, because a build-time dependency is operationally different from a runtime one.
Evidence grades
Evidence grades describe how confident the recorded edge is. Three values, with concrete rules:
strong — the edge is justified by concrete cross-references in both projects' surface metadata. Examples: both projects' consumes_files / produces_files lists corroborate the connection; both projects name each other in explicit_cross_project_mentions. A strong edge requires at least one entry in evidence_fields; the validator rejects an empty list.
medium — one side’s metadata clearly supports the edge, but the other side’s metadata is silent or only mentions the relationship in prose (notes) rather than in structured fields. A medium edge also requires at least one evidence_fields entry.
weak — the edge is inferred from indirect signals (an aspirational backlog item, a one-sided mention, a planned-but-not-realised coupling). weak is the only grade allowed to omit evidence_fields. Use this for edges that are informative as design intent rather than true today.
Downstream behaviour. The triage phase uses evidence grade to decide which related components are confident enough to dispatch to. A strong or medium edge against a peer plan is a candidate for subagent-dispatch.yaml; a weak edge typically is not. The discover pipeline writes the grade based on the strength of the cross-references it finds during stage-2 analysis.
Cross-references
The triage phase’s dispatch behaviour and the dream-trigger mechanics that interact with this graph live on the Phase cycle reference. The state related-components CLI verbs are documented in full on the State commands reference. Plan-state files (backlog.yaml, memory.yaml, session-log.yaml, latest-session.yaml, phase.md, commits.yaml) — distinct from the cross-plan files documented here — are on the State files reference.