Files
will.anderson b77f537dc6 fix cross-repo path deps; remove el compiler from neuron-lang/
- neuron-runtime, neuron-store, neuron-migrate: fix path deps
  (products/engram/ → foundation/engram/engrams/, products/el/ → foundation/el/engrams/)
- neuron-rs workspace: fix axon-events/axon-protocol paths
  (../../../platform/ → ../../platform/, paths were off by one level)
- neuron-lang/compiler/ removed (el self-hosting compiler now lives in foundation/el/el-compiler/)
2026-04-29 03:27:39 -05:00

163 lines
5.4 KiB
EmacsLisp

// artifact.el Versioned deliverables subsystem.
//
// Artifacts are the tangible outputs of Neuron-assisted work: plans,
// specifications, architecture docs, reports, design documents.
// Unlike raw memories (ephemeral) or knowledge (reference), artifacts are
// polished, versioned deliverables that external stakeholders consume.
//
// WHY explicit versioning?
// A plan that gets revised without version tracking looks like a single
// authoritative document. With version tracking, you can see when a plan
// changed and compare the reasoning at each revision. This is especially
// important for architectural decisions that need an audit trail.
//
// Status machine:
// draft review approved archived
//
// Artifacts are never deleted only archived. A deleted plan loses its
// history. An archived plan retains it.
from types import {
Artifact,
NeuronError,
}
// Public API
// draft_artifact create a new artifact in draft status.
//
// artifact_types is a list because a document can be multiple things at once:
// ["plan", "spec"] for a planning spec, ["report", "analysis"] for an
// analysis report.
@manager
fn draft_artifact(
title: String,
content: String,
artifact_types: [String],
project: String,
) -> Result<Artifact, NeuronError> {
let id = native_uuid()
let now = native_now()
// Serialize artifact_types to a comma-joined string for the store.
// The Axon response formatter re-splits on display.
let artifact_type = artifact_types[0]
let artifact = Artifact {
id: id,
title: title,
content: content,
artifact_type: artifact_type,
status: "draft",
project: project,
version: 1,
created_at: now,
updated_at: now,
}
native_store_artifact(artifact)?
native_emit("artifact.drafted", {"id": id, "project": project, "type": artifact_type})
Ok(artifact)
}
// find_artifacts list or search artifacts for a project.
//
// When query is provided, uses semantic search (activate) to find artifacts
// by content similarity. When query is empty, returns all project artifacts.
@accessor
fn find_artifacts(project: String, query: String) -> Result<[Artifact], NeuronError> {
if query == "" {
let artifacts = native_list_artifacts(project)?
return Ok(artifacts)
}
let results = activate Artifact where "{query} project:{project}"
Ok(results)
}
// retrieve_artifact fetch a single artifact by ID.
@accessor
fn retrieve_artifact(id: String) -> Result<Artifact, NeuronError> {
let artifact = native_get_artifact(id)?
Ok(artifact)
}
// revise_artifact update an artifact's content and increment its version.
//
// Every revision is a distinct event in the graph. The version number is
// monotonically increasing no rollbacks, no overwriting. If you need to
// revert, draft a new artifact that references the old version.
@manager
fn revise_artifact(
id: String,
content: String,
change_summary: String,
) -> Result<Artifact, NeuronError> {
let artifact = native_get_artifact(id)?
let now = native_now()
// Increment version this is the only place version numbers are assigned.
let new_version = artifact.version + 1
let revised = Artifact {
id: artifact.id,
title: artifact.title,
content: content,
artifact_type: artifact.artifact_type,
status: artifact.status,
project: artifact.project,
version: new_version,
created_at: artifact.created_at,
updated_at: now,
}
native_update_artifact(revised)?
native_emit("artifact.revised", {
"id": id,
"version": "new_version",
"summary": change_summary,
})
Ok(revised)
}
// manage_artifact transition an artifact's status.
//
// action: "review" | "approve" | "archive"
// Valid transitions:
// review: draft review
// approve: review approved
// archive: * archived
@manager
fn manage_artifact(id: String, action: String) -> Result<Artifact, NeuronError> {
let artifact = native_get_artifact(id)?
let new_status = if action == "review" {
if artifact.status == "draft" {
"review"
} else {
return Err(NeuronError::InvalidInput("can only send a draft artifact for review"))
}
} else {
if action == "approve" {
if artifact.status == "review" {
"approved"
} else {
return Err(NeuronError::InvalidInput("can only approve an artifact under review"))
}
} else {
if action == "archive" {
"archived"
} else {
return Err(NeuronError::InvalidInput("unknown artifact action"))
}
}
}
let now = native_now()
let updated = Artifact {
id: artifact.id,
title: artifact.title,
content: artifact.content,
artifact_type: artifact.artifact_type,
status: new_status,
project: artifact.project,
version: artifact.version,
created_at: artifact.created_at,
updated_at: now,
}
native_update_artifact(updated)?
native_emit("artifact.status_changed", {"id": id, "status": new_status, "action": action})
Ok(updated)
}