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/)
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
// 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)
|
||||
}
|
||||
Reference in New Issue
Block a user