= core-artifacts == purpose Defines the artifact model — a structured output produced during a workflow stage. Manages artifact lifecycle phases (created → validating → validated/rejected → superseded/archived) as event-sourced state, provides a kind system for typed artifact payloads (file_written, process_result), and supports materialization of file artifacts to disk. == responsibilities * Defining the `Artifact` sealed interface and its common fields (id, session, stage, lifecycle phase, lineage, metadata) * Managing artifact lifecycle phase transitions with state machine validation * Tracking artifact lineage (parent IDs, relationships, validation report IDs) * Defining and registering `ArtifactKind` implementations for typed payloads * Providing JSON schema derivation for each artifact kind * Materializing file artifacts to disk via `MaterializingArtifactWriter` * Providing a repository interface for querying artifact state by session, stage, or ID == non-responsibilities * Does not manage binary blob storage — that is delegated to `:core:artifacts-store` via `ArtifactStore` * Does not validate artifact payloads — validation is handled by `:core:validation` via `ArtifactPayloadValidator` * Does not produce artifacts — they are created by the orchestrator or infrastructure adapters * Does not manage the full artifact content — only metadata and lifecycle state are projected == key types === Artifact * **kind**: sealed interface * **purpose**: Base type for all artifacts. Declares common fields. * **fields**: `id`, `sessionId`, `stageId`, `schemaVersion`, `createdAt`, `lifecyclePhase`, `lineage`, `metadata` === ArtifactState * **kind**: data class * **purpose**: Event-sourced projection of a single artifact's lifecycle state. * **fields**: `phase` (ArtifactLifecyclePhase), `lineage` (ArtifactLineage) === ArtifactReducer / DefaultArtifactReducer * **kind**: interface / class * **purpose**: Transforms `ArtifactState` given a stored event. Validates lifecycle phase transitions and returns `Result.Failure` for illegal transitions. Handles `ArtifactCreatedEvent`, `ArtifactValidatingEvent`, `ArtifactValidatedEvent`, `ArtifactRejectedEvent`, `ArtifactSupersededEvent`, `ArtifactArchivedEvent`, `ArtifactRelationshipAddedEvent`. === ArtifactProjector * **kind**: class * **purpose**: Wraps `ArtifactReducer` as a `Projection`. Invalid lifecycle transitions are logged as warnings and do not crash replay. === ArtifactLineage * **kind**: data class * **purpose**: Tracks an artifact's provenance. * **fields**: `parentIds`, `relationships` (list of ArtifactRelationship), `validationReportIds` === ArtifactRelationship * **kind**: data class * **purpose**: A directed, typed relationship between two artifacts. * **fields**: `sourceId`, `targetId`, `type` (ArtifactRelationshipType) === ArtifactKind * **kind**: interface * **purpose**: Declares a type of artifact with a payload serializer and JSON schema. * **fields**: `id`, `payloadSerializer`, `llmEmitted` === ArtifactKindRegistry / DefaultArtifactKindRegistry * **kind**: interface / class * **purpose**: Maps artifact kind IDs to `ArtifactKind` instances. Pre-registers `FileWrittenKind` and `ProcessResultKind`. === FileWrittenKind * **kind**: object * **purpose**: Artifact kind for files written to disk. Declares a JSON schema with `path`, `content`, `mode`. === FileWrittenPayload * **kind**: data class * **purpose**: Input payload for a file write operation (before materialization). * **fields**: `path`, `content`, `mode` === FileWrittenArtifact * **kind**: data class * **purpose**: Canonical form of a written file after materialization (content-addressed by hash). * **fields**: `path`, `contentHash`, `size`, `mode` === ProcessResultKind * **kind**: object * **purpose**: Artifact kind for process execution results. Declares a JSON schema with `command`, `exitCode`, `stdout`, `stderr`, `durationMs`. === ProcessResultArtifact * **kind**: data class * **purpose**: Record of a process execution. * **fields**: `command`, `exitCode`, `stdout`, `stderr`, `durationMs` === TypedArtifactSlot * **kind**: data class * **purpose**: Associates a named artifact slot with its expected kind. * **fields**: `name` (ArtifactId), `kind` (ArtifactKind) === MaterializingArtifactWriter * **kind**: interface * **purpose**: Writes a `FileWrittenPayload` to disk within a sandbox root and returns a `FileWrittenArtifact`. Supports content-addressed storage by returning a hash. === MaterializationResult * **kind**: sealed interface * **purpose**: Outcome of a materialization attempt. * **variants**: `Success(artifact, resolvedPath)`, `Failure(reason)` === ArtifactRepository * **kind**: interface * **purpose**: Queries artifact state by session, stage, or ID. === JsonSchema / JsonSchemaProperty * **kind**: data classes * **purpose**: JSON Schema representation for artifact kind derived schemas. == event flow *inbound:* * `ArtifactCreatedEvent` → sets phase to `CREATED` * `ArtifactValidatingEvent` → transitions from `CREATED` to `VALIDATING` (fails if phase is not `CREATED`) * `ArtifactValidatedEvent` → transitions from `VALIDATING` to `VALIDATED` * `ArtifactRejectedEvent` → transitions from `VALIDATING` to `REJECTED` * `ArtifactSupersededEvent` → transitions from `VALIDATED` to `SUPERSEDED` * `ArtifactArchivedEvent` → transitions from `VALIDATED` or `REJECTED` to `ARCHIVED` * `ArtifactRelationshipAddedEvent` → adds a relationship to the lineage *outbound:* * None. This module is purely a projection. == integration points * `:core:events` — `StoredEvent`, artifact lifecycle event types, `ArtifactId`, `ArtifactLifecyclePhase`, `ArtifactRelationshipType` == invariants * Lifecycle phase transitions follow a strict state machine: `CREATED → VALIDATING → {VALIDATED, REJECTED}`, then `VALIDATED → {SUPERSEDED, ARCHIVED}`, `REJECTED → ARCHIVED` * Invalid transitions return `Result.Failure` from the reducer — they do not crash the projector * `ArtifactProjector` catches `IllegalStateException` from invalid transitions, logs a warning, and leaves state unchanged during replay * `ArtifactKind` registrations are idempotent — the registry is a map, so re-registering overwrites the previous entry == PlantUML diagram [plantuml, core-artifacts, "png"] ---- include::../../diagrams/core-artifacts.puml[] ---- == known issues * `Artifact` sealed interface has no concrete subclasses in the current codebase. The `artifactModule` serializers module has a polymorphic block for `Artifact::class` with no subclass registrations. Concrete artifact types are needed before polymorphic serialization works. * `ArtifactState` is a single-artifact projection — each `ArtifactState` tracks one artifact's lifecycle. The `ArtifactRepository` interface returns maps keyed by `ArtifactId`, which suggests the caller is responsible for aggregating multiple states. == open questions * How are multiple artifact states (one per artifact) stored and queried? The repository returns maps, but the event replayer typically rebuilds a single state per session. The bridge between them is not visible in this module. * What concrete `Artifact` subclasses are planned? (The sealed interface exists but has no implementations.)