Artifact and Run Model¶
This document defines the architecture of the Artifact and Run Model in Episteme.
The pipeline executes entirely on durable, run-scoped research artifacts and first-class run manifests, rather than ephemeral inter-phase execution values or graph-as-run-state.
1. Purpose & Motivation¶
In earlier iterations of the pipeline, intermediate outputs were transient Python objects passed directly in memory, and the Neo4j database was overloaded as both execution state tracker and target knowledge graph.
The artifact and run model solves this by establishing two decoupled architectural foundations:
- Run Model — Tracks what execution took place, with which configuration snapshot, input fingerprints, method fingerprints, and phase records.
- Artifact Model — Tracks what was produced by each phase, with full provenance, stable identity keys, and content-addressed dependency fingerprints.
Key Architectural Benefits¶
- Graph-Independent Execution: Intermediate outputs are evaluated, replayed, and inspected without querying Neo4j. Graph persistence is isolated to an explicit projection step.
- Reproducibility & Lineage: Every artifact carries provenance pointing back to source files, chunks, upstream artifact IDs, and run manifests.
- Safe Resumption & Caching: Downstream phases can hydrate upstream artifacts across a chain of parent runs without re-executing valid prior work.
- Fine-Grained Invalidation: Runs record deterministic fingerprints of inputs, configs, prompts, and model parameters to detect exactly which phases or artifacts require recomputation.
2. Core Architecture¶
flowchart TD
subgraph Orchestration ["Pipeline Orchestration (pipeline/pipeline.py)"]
ManifestStore["JsonRunManifestStore<br/>(runs_dir/run-id/manifest.json)"]
ArtifactStore["JsonArtifactStore<br/>(artifacts_dir/run-id/kind/id.json)"]
Context["ArtifactExecutionContext<br/>(run_id, manifest, input, previous)"]
end
subgraph PhaseExecution ["Phase Runner Execution"]
InputView["Typed ArtifactsView<br/>(e.g., Phase2ArtifactsView)"]
Runner["PhaseRunner.run(input, context)"]
Collection["ArtifactCollection<br/>([ArtifactEnvelope, ...])"]
end
subgraph Downstream ["Persistence & Projection"]
Projector["ArtifactGraphProjector<br/>(pipeline/projection/)"]
Neo4j[("Neo4j Projection Graph")]
end
ManifestStore --> Context
Context --> Runner
InputView --> Runner
Runner --> Collection
Collection --> ArtifactStore
Collection --> Projector
Projector --> Neo4j
3. The Run Model¶
Located in pipeline/runs/models.py and persisted via pipeline/runs/persistence.py.
3.1 RunManifest¶
The RunManifest is the central execution record for a pipeline run:
run_id: Unique run identifier (e.g.,run-<uuid4>).parent_run_id: Identifier of the parent run when resuming or reusing prior phases.status: Current lifecycle status (RunStatus.PLANNED,RUNNING,COMPLETED,FAILED,ABORTED).schema_version: ActiveSchemaConfig.versiontracking ontology/taxonomy versions.source_fingerprint&input_fingerprint: Deterministic hashes of source document paths, bibtex files, and structural anchors.config_snapshot: Full JSON-serialized snapshot ofPipelineConfig.phase_config_fingerprints: Per-phase configuration hashes derived from serialized phase configs (including fine-grained nested leaf fingerprints).method_fingerprints: Fine-grained fingerprints of models, extractors, and prompts used by each phase (e.g., LLM parameters, embedding model dimensions, and prompt bundles).phase_records: List ofRunPhaseRecordtracking execution status and artifact outputs per phase.
3.2 RunPhaseRecord¶
Tracks execution state for an individual phase:
phase_nameandphase_ordinal(1-based index).status:RunStatusfor this specific phase.started_atandcompleted_attimestamps.input_artifact_ids: IDs of artifacts consumed as input.output_artifact_ids: IDs of artifacts produced by this phase.output_fingerprint: Stable hash of the emitted artifact collection.reused: Boolean flag indicating whether the phase was reused from a prior run.
3.3 RunReport¶
At run completion, a RunReport is generated summarizing:
- Overall status and execution durations.
- Counts of artifacts by
kindand byphase. - Breakdown of
newvs.reusedartifacts. - Invalidation decisions and reasons.
- Full list of
ArtifactReportEntryreferences.
4. The Artifact Model¶
Located in pipeline/artifacts/models.py and persisted via pipeline/artifacts/store.py.
4.1 ArtifactEnvelope[T]¶
Every artifact emitted by the pipeline is wrapped in a generic ArtifactEnvelope[T]:
class ArtifactEnvelope(BaseModel, Generic[ArtifactPayloadT]):
artifact_id: str
identity_key: str | None = None
kind: ArtifactKind
run_id: str
phase_name: str
method: str
method_version: str = "v1"
config_fingerprint: str | None = None
dependency_fingerprint: str | None = None
created_at: datetime = Field(default_factory=utc_now)
provenance: ArtifactProvenance = Field(default_factory=ArtifactProvenance)
payload: ArtifactPayloadT
metadata: dict[str, Any] = Field(default_factory=dict)
Dual Identifier System¶
Every artifact carries two complementary identifiers:
artifact_id(artifact::<uuid>): Unique identifier representing the specific artifact instance in a particular run. Used inprovenance.upstream_artifact_idsto track concrete data lineage.identity_key(<kind>::<canonical_identifier>): Stable semantic identity across runs (e.g.,linked_entity::kant_crp_space). Used by the invalidation DAG and cross-run comparison tools to correlate equivalent entities across parameter changes.
4.2 Payload Families¶
The pipeline defines typed payloads for each construction stage:
| Artifact Kind | Payload Class | Emitting Phase | Description |
|---|---|---|---|
document |
DocumentArtifact |
Phase 1 | Ingested source document with structural anchor |
chunk |
ChunkArtifact |
Phase 1 | Segmented and embedded text chunks |
entity_mention |
EntityMentionArtifact |
Phase 2 | Extracted surface mentions in text |
linked_entity |
LinkedEntityArtifact |
Phase 2 | Canonicalized and linked entities |
local_relation |
LocalRelationArtifact |
Phase 2 | Intra-chunk entity relationships |
global_relation |
GlobalRelationArtifact |
Phase 3 | Cross-chunk / document-wide relationships |
theory_atom |
TheoryAtomArtifact |
Phase 4 | Argument components (premises, claims) |
theory_relation |
TheoryRelationArtifact |
Phase 4 | Argumentative relations (supports, attacks) |
fusion_decision |
FusionDecisionArtifact |
Phase 5 | Entity alignment & fusion decisions |
canonicalization |
CanonicalizationArtifact |
Phase 5 | Canonical entity representations |
theoretical_enrichment |
TheoreticalEnrichmentArtifact |
Post-processing | Theoretical enrichment & tenability scores |
5. Execution & Inter-Phase Handoff¶
Located in pipeline/artifacts/execution.py.
5.1 ArtifactCollection¶
An ArtifactCollection is a typed container holding a list of ArtifactEnvelope instances. It provides filtering utilities such as collection.of_kind(*kinds).
5.2 Typed ArtifactsView Classes¶
Phases consume upstream data through typed views. Rather than receiving untyped collections or raw database connections, each phase runner specifies an input_view class:
Phase1ArtifactsView: Providesdocuments: list[L1Document]andchunks: list[L1Chunk]. Consumed by Phase 2.Phase2ArtifactsView: Providesentities: list[L2Entity],local_triples: list[L2Triple], and type distributions. Consumed by Phase 3.Phase3ArtifactsView: Providesglobal_triples: list[L2Triple],chunks: list[L1Chunk], and relation distributions. Consumed by Phase 3b, Phase 4 Maturation, and Phase 4 Argument Mining.Phase4ArtifactsView: Providestheory_atoms: list[TheoryAtom]andtheory_relations: list[TheoryRelation]. Consumed by Phase 5, Phase 6, and Theoretical Enrichment.TheoreticalEnrichmentArtifactsView: Providesenrichments: list[TheoreticalEnrichmentArtifact].
5.3 Runner Interface¶
All pipeline phase runners implement the uniform async interface:
async def run(
self,
input: ViewT,
context: ArtifactExecutionContext,
) -> ArtifactCollection: ...
The orchestrator builds the appropriate view via entry.input_view.from_collection(previous) and passes it to the runner alongside the ArtifactExecutionContext (which contains run_id, manifest, pipeline_input, and previous).
6. Graph Projection & Decoupling¶
Located in pipeline/projection/artifact_projector.py and pipeline/projection/theorynet_projector.py.
Execution is cleanly decoupled from graph storage:
- Phases emit artifacts: Phase runners have no direct write dependencies on the final projection graph.
- Independent projection: When
config.execution.project_artifacts_to_graphis enabled (defaultTrue), the orchestrator passes emitted artifacts throughArtifactGraphProjector.project(art). - Dual-Store Architecture: The temporary
checkpoint_store(operational graph) is used strictly for working memory, co-occurrence blocking, and intermediate index lookups during extraction, whileprojection_graphreceives canonical projections derived from validated artifacts. - Dual Projection Streams:
- Property Graph Projection (
ArtifactGraphProjector): Projects artifact envelopes into concrete Neo4j nodes and relationships. - TheoryNet Formalism Projection (
TheoryNetProjector): Projects Phase 4 artifacts into the formal mathematical \(\mathcal{G}_{\text{TheoryNet}}\) structure, executing iterative QBAF gradual semantics.
6.1 Artifact-to-Graph Projection Mapping¶
ArtifactGraphProjector maps artifact payloads into the projection graph according to the following rules:
| Artifact Payload | Graph Mutation Target | Labels / Rel Types | Properties & Metadata |
|---|---|---|---|
DocumentArtifact |
Node | :Document |
document_id, title, source_path |
ChunkArtifact |
Node | :Chunk |
chunk_id, text, document_id, sequence_index, token_count |
EntityMentionArtifact |
Edge | (:Entity)-[:EXTRACTED_FROM]->(:Chunk) |
entity_type, surface_form |
LinkedEntityArtifact |
Node | :<entity_type> / :Entity |
entity_id, name, description, textual_envelope, is_mature, confidence |
LocalRelationArtifact |
Edge | (:Entity)-[:<PREDICATE> {scope: "local"}]->(:Entity) |
confidence, source_chunk_id |
GlobalRelationArtifact |
Edge | (:Entity)-[:<PREDICATE> {scope: "global"}]->(:Entity) |
confidence, supporting_chunk_ids |
TheoryAtomArtifact |
Node | :<component_type> / :TheoryAtom |
component_id, text, plausibility, epistemic_status, scope_type |
TheoryRelationArtifact |
Edge | (:TheoryAtom)-[:<REL_TYPE>]->(:TheoryAtom) |
scope, confidence, weight |
CanonicalizationArtifact |
Edge | (:Entity)-[:SAME_AS]->(:Entity) |
confidence: 1.0, source: "phase3b_consolidation" |
FusionDecisionArtifact |
None | _NOT_PROJECTED |
Deliberately omitted; record of decision rather than graph mutation |
6.2 Current Limitations & Roadmap¶
As the pipeline transitions fully toward an artifact-first runtime, the projection layer currently serves as an explicit bridge. The following enhancements are planned:
- Transactional Projection Batches: Grouping multi-artifact projections into single atomic Cypher transactions.
- Projector Plugins: Pluggable projectors allowing custom export targets (e.g., RDF/OWL, NetworkX, GraphML).
- Projection Planning: Dependency-aware projection ordering ensuring referenced nodes are guaranteed to exist prior to edge creation.
- Projection-Time Schema Validation: Validating projected graph structures against
SchemaConfigconstraints before committing writes. - Replay Tooling: Command-line utilities to re-project historical runs from
.pipeline_artifacts/without re-running extraction.
7. Artifact Hydration & Parent-Run Lineage¶
When a run is resumed via resume_from_run() or reuses phases via _choose_reuse_source():
- The orchestrator identifies the source run (
parent_run_id). _hydrate_previous_collection(phase_number, source_run_id)retrieves all artifacts produced prior to the resume boundary.- If an artifact was inherited across multiple resume hops, the store traverses the parent-run chain (
run_id -> parent_run_id -> ...), always selecting the nearest occurrence of eachidentity_key. - Downstream phases execute seamlessly with a fully populated
ArtifactCollectionas if all upstream phases had executed in the current process.
8. Configuration Controls¶
The artifact and run model is configured via ExecutionConfig in pipeline/config.py:
| Parameter | Type | Default | Description |
|---|---|---|---|
runs_dir |
Path |
.pipeline_runs |
Base directory for serialized RunManifest files |
artifacts_dir |
Path |
.pipeline_artifacts |
Base directory for serialized JSON artifact files |
persist_run_manifests |
bool |
True |
Whether to write manifest.json on disk |
project_artifacts_to_graph |
bool |
True |
Whether to project emitted artifacts to Neo4j |
allow_phase_reuse |
bool |
True |
Whether to reuse valid phases from compatible prior runs |
allow_artifact_hydration |
bool |
True |
Whether to hydrate upstream artifacts across runs |
9. Related Documentation¶
- Invalidation and Resumption: Invalidation and Resume
- Runtime and Complexity: Pipeline Runtime Analysis
- System Overview: System Architecture Overview
- Pipeline Architecture: Pipeline Architecture
- ADRs: ADR 0001: Neo4j Async Graph Backend, ADR 0014: Deterministic Identity Diffing