| Authority | CANONICAL |
|---|---|
| Version | v1 |
| Last Updated | 2026-03-27 |
| Owner | Claude (Structural Auditor) |
| Scope | Loader responsibility boundary, decode contract |
| Change Rule | Tracks implementation |
Crate: crates/prod/core/loader
The loader owns project discovery, graph file transport, format
decode, and cluster discovery. It operates entirely without catalog
access. The
catalog-access boundary (yaml-format.md §8.3) is the loader/kernel
divide.
The loader does:
- Project root discovery via
ergo.toml - Project manifest loading plus per-profile resolution into graph, adapter, ingress, egress, capture, and cluster path references
- YAML/JSON decode of graph content into
ClusterDefinition - Shorthand expansion and format-level coercions tied to file format
- Cluster discovery and candidate resolution across filesystem paths or logical in-memory source ids
- Source map construction for diagnostics
The loader does NOT:
- Access the primitive catalog
- Perform semantic validation (wiring legality, type rules)
- Define or return
RuleViolationtypes (LAYER-2) - Perform semantic graph expansion, signature inference, or execution
load_graph_sourcesFile path plus search paths toFilesystemGraphBundle(primary filesystem entry point).load_in_memory_graph_sourcesRoot source ID plus ordered in-memory sources plus logical search roots toInMemoryGraphBundle.load_graph_assets_from_pathsLower-level path loader to sealedPreparedGraphAssetsfor host prep and validation.load_graph_assets_from_memoryLower-level in-memory loader to sealedPreparedGraphAssetsfor host prep and validation.discover_project_rootNested path to project root containingergo.toml.load_projectProject root discovery plusergo.tomlparse intoResolvedProject { root, manifest }. Individual profiles are resolved later viaResolvedProject::resolve_run_profile(...).decode_graph_yamlYAML string toClusterDefinition.decode_graph_yaml_labeledYAML string plus caller-supplied human-facing source label toClusterDefinition.decode_graph_jsonJSON string toClusterDefinition.parse_graph_fileFile path to decoded cluster.discovery::discover_cluster_treeModule-public advanced helper underergo_loader::discovery; root path plus search paths to discovery output including the internally parsed root, decoded clusters, and their source paths.discovery::discover_in_memory_cluster_treeModule-public advanced helper underergo_loader::discovery; root source ID plus ordered in-memory sources plus logical search roots to in-memory discovery output, including the internally parsed root.load_cluster_treeRoot path plus search paths to the full cluster tree keyed by(id, version).resolve_cluster_candidatesBase directory plus cluster ID plus search paths to deduplicated candidate file paths.
pub struct FilesystemGraphBundle {
pub root: ClusterDefinition,
pub discovered_files: Vec<PathBuf>,
pub source_map: BTreeMap<PathBuf, String>,
}pub struct PreparedGraphAssets {
root: ClusterDefinition,
clusters: HashMap<(String, Version), ClusterDefinition>,
cluster_diagnostic_labels: HashMap<(String, Version), String>,
pub(crate) _sealed: (),
}pub struct ClusterDiscovery {
pub root: ClusterDefinition,
pub clusters: HashMap<(String, Version), ClusterDefinition>,
pub cluster_sources: HashMap<(String, Version), PathBuf>,
pub cluster_diagnostic_labels: HashMap<(String, Version), String>,
}pub struct InMemorySourceInput {
pub source_id: String,
pub source_label: String,
pub content: String,
}pub struct InMemoryClusterDiscovery {
pub root: ClusterDefinition,
pub clusters: HashMap<(String, Version), ClusterDefinition>,
pub cluster_source_ids: HashMap<(String, Version), String>,
pub cluster_source_labels: HashMap<(String, Version), String>,
pub cluster_diagnostic_labels: HashMap<(String, Version), String>,
}pub struct InMemoryGraphBundle {
pub root: ClusterDefinition,
pub discovered_source_ids: Vec<String>,
pub source_map: BTreeMap<String, String>,
pub source_labels: BTreeMap<String, String>,
}For the in-memory surface:
source_idis the public logical source identity and lookup path- callers should use path-like
source_idvalues such asgraphs/root.yamlbecause referrer-sensitive cluster resolution and search traces are derived from logical path structure - logical paths are loader-defined, platform-independent, and use
/separators source_idandsearch_rootsmust be relative logical paths; rooted paths, backslashes,:, empty path segments, and./..segments are rejectedsource_labelis the human-facing diagnostic label- the current in-memory API requires unique
source_labelvalues per call so diagnostics remain unambiguous source_labelis not semantic identity or lookup pathcontentis graph authoring text parsed through the loader's string decode path; YAML and JSON authoring text are both accepted- cluster lookup still follows the existing filename-style contract on
source_id; a resolvable cluster source id must end in<cluster_id>.yaml source_idvalues must be unique and non-emptysource_labelvalues must be non-emptyroot_source_idmust be present in the provided source listdiscovery::discover_in_memory_cluster_treeis a module-public advanced helper underergo_loader::discovery; it parses the root internally fromroot_source_idand returns it inInMemoryClusterDiscovery.rootdiscovery::discover_cluster_treeis the filesystem twin; it parses the root internally fromroot_pathand returns it inClusterDiscovery.root- both discovery outputs expose
cluster_diagnostic_labels; host/error consumers should read labels from that field rather than deriving them fromPathBuforsource_labeldirectly - when multiple in-memory sources could satisfy the same lookup, caller
sourcesorder decides which matching source is chosen;search_rootsdefine candidate scope and search trace order rather than source precedence InMemoryGraphBundle.discovered_source_idsfollows lexicographicsource_idorder, matchingsource_mapkey order- logical
search_rootspreserve referrer-sensitive discovery scope without inventing fake filesystem paths
ClusterDefinition is a kernel-owned type. The loader produces it
directly. There is no parallel intermediate representation.
DecodedAuthoringGraph remains as a type alias, not a separate IR.
PreparedGraphAssets is the lower-level sealed handoff used by the host prep
lane. It is constructed by the loader and re-exported by the host for advanced
callers, but it remains loader-owned and does not carry host runtime options.
The payload is externally immutable: callers read it through root(),
clusters(), and cluster_diagnostic_labels() accessors rather than mutating
the asset bundle directly.
Loader errors are transport and decode failures, exposed via LoaderError:
LoaderIoError— file not found, permission deniedLoaderDecodeError— malformed YAML, missing required fields (and malformed JSON / labeled string decode failures)LoaderDiscoveryError— discovery-time lookup and cluster-tree failures such as missing clusters, nested parse failures, ID mismatches, duplicate definitions, and circular references
The crate root re-exports LoaderIoError, LoaderDecodeError, and
LoaderDiscoveryError alongside LoaderError so callers can name the
payload types without reaching into ergo_loader::io.
These are NOT rule violations. The loader never produces
RuleViolation or references invariant IDs. Semantic errors begin at
the kernel boundary when expansion/validation consumes the
ClusterDefinition.
- project-convention.md — Defines how project resolution supplies graph paths, cluster search paths, and profiles to the loader
- yaml-format.md — Defines the YAML schema the loader decodes
- cluster-spec.md — Defines
ClusterDefinitionand the expansion algorithm the kernel applies after loading - kernel.md — Defines the boundary rules (LAYER-1, LAYER-2) that constrain the loader