artifactize 0.8.0

The AI-native collaboration layer for one-of-a-kind teammates and their agents: split a project into Artifacts with their own evals, and reuse every review while it still holds.
Documentation
//! Independent schema-5 wire fixtures pin graph capture and hash representation.
use super::*;
use crate::project::selection::Selection;
use serde_json::json;
use sha2::{Digest, Sha256};

#[test]
fn saved_graph_keeps_known_null_missing_and_extensible_unknown_fields() {
    let mut wire = json!({
        "version":1,"repoPath":"/saved/workspace","selection":{"kind":"all"},"futureTop":{"v":7},
        "artifacts":{"page":{"path":"pages","name":"page","family":{"name":"pages","instances":null,"material":["file.md"],"futureMembership":true},"children":{},"mounts":{},"basis":null,"fingerprint":{"script":{"command":"echo","args":["v1"],"files":[],"timeoutMs":null,"futureScript":true}},"reviewPolicy":{"dependencyGates":null,"futurePolicy":1},"views":{"agentTools":{"read":{"builtin":"read","futureTool":true}},"humanTools":{"inspect":{"description":"Inspect","kind":"output","command":"cat","args":["file.md"],"futureHumanTool":1}},"futureView":[]},"futureArtifact":"kept"}},
        "evals":[{"id":"page/check","target":"page","references":{},"deps":["input"],"declaration":{"id":"check","title":"Inspect","profile":{"kind":"runtime","command":"true","args":[],"timeoutMs":null},"profileVariants":{},"payload":{"instruction":"Inspect {input}.","owner":[true,null,42]},"passSchema":{"type":"object","properties":{"accepted":{"const":true}}},"failSchema":null,"futureDeclaration":true},"futureEval":2}],
        "relations":[{"source":"input","target":"page","kind":"instruction","evalId":"page/check","name":"input","cyclic":true,"futureRelation":false}],
        "components":[{"id":0,"artifacts":["input","page"],"dependencies":[],"gates":["input/check"],"cyclic":true,"futureComponent":3}],
        "families":{"pages":{"path":"pages","artifactIds":["page"],"futureFamily":null}}
    });
    wire["selection"]["futureSelection"] = json!({"v":true});
    wire["evals"][0]["declaration"]["profile"]["futureProfile"] = json!({"v":1});
    wire["evals"][0]["declaration"]["profileVariants"] =
        json!({"extra":{"kind":"human","futureVariant":[1,2]}});
    wire["relations"].as_array_mut().unwrap().push(
        json!({"source":"page","target":"input","kind":"future-edge","note":{"future":true}}),
    );
    let snapshot: Definitions = serde_json::from_value(wire.clone()).unwrap();
    let restored = serde_json::to_value(&snapshot).unwrap();
    assert_eq!(restored, wire);
    assert_eq!(
        Sha256::digest(serde_json::to_vec(&restored).unwrap()),
        Sha256::digest(serde_json::to_vec(&wire).unwrap())
    );
    let graph = snapshot.graph().unwrap();
    assert_eq!(
        graph.repo_path.value().unwrap(),
        &PathBuf::from("/saved/workspace")
    );
    assert_eq!(
        graph.components()[0].artifacts.value().unwrap(),
        &["input", "page"]
    );
    assert!(matches!(graph.artifact("page").unwrap().basis, Field::Null));
    for wire in [
        Value::Null,
        json!({}),
        json!({"artifacts":null,"evals":null}),
        json!({"artifacts":{"old":{"path":"","basis":true}},"components":[{"artifacts":["old"]}]}),
    ] {
        let snapshot: Definitions = serde_json::from_value(wire.clone()).unwrap();
        assert_eq!(serde_json::to_value(snapshot).unwrap(), wire);
    }
    let missing: crate::store::Run = serde_json::from_value(json!({"id":"run-old","repoPath":"/repo","stateDir":"/state","status":"RUNNING","createdAt":"2026-01-01T00:00:00Z","selection":{"kind":"all"},"validation":null})).unwrap();
    assert!(missing.definitions.graph().is_none());
}

#[test]
fn current_graph_capture_serializes_identically_to_pinned_query_view() {
    let root = tempfile::tempdir().unwrap();
    std::fs::write(root.path().join("artifactize.json"), json!({"name":"app","views":{"agentTools":{"read":{"builtin":"read"}}},"fingerprint":{"script":{"command":"echo","args":["v1"]}},"evals":[{"id":"check","title":"Check","profile":{"kind":"agent","backend":"openai","model":"fixture"},"payload":{"instruction":"Check.","owner":{"nested":[1,2]}},"passSchema":{"type":"object"}}]}).to_string()).unwrap();
    let config = config::read_workspace_config(root.path()).unwrap();
    let selection = Selection::All;
    let view = crate::query::graph(&config, &selection).unwrap();
    let before = serde_json::to_value(&view).unwrap();
    let snapshot = Definitions::from_view(&view).unwrap();
    assert_eq!(serde_json::to_value(snapshot).unwrap(), before);
    // The eval definition and reuse key are calculated from live declarations, never this display snapshot.
    let eval_hash = crate::cache::eval_definition_hash(&config.evals[0].declaration);
    let saved_profile = &before["evals"][0]["declaration"]["profile"];
    assert_eq!(
        saved_profile,
        &json!({"kind":"agent","backend":"openai","model":"fixture","reasoning":null,"timeoutMs":null,"maxToolCalls":null,"maxTokens":null})
    );
    assert_eq!(
        eval_hash,
        "bc7083ad4c1041de824b8080a6bd576c8cc39caedd1db4b63d073ae973e6790b"
    );
    let fingerprints = BTreeMap::from([("app".into(), "v1".parse().unwrap())]);
    assert_eq!(
        crate::cache::key(&eval_hash, &fingerprints).as_str(),
        "37e7570aa8d259201517ad28ccbe755d8413d74371b26650729fb872296c262f"
    );
}

#[tokio::test]
async fn saved_definition_timeouts_reject_unrepresentable_writes() {
    let root = tempfile::tempdir().unwrap();
    let repo = root.path().join("repo");
    let state = root.path().join("state");
    std::fs::create_dir_all(&repo).unwrap();
    let receipts = crate::store::Receipts::open(&state, &repo).await.unwrap();
    let mut run: crate::store::Run = serde_json::from_value(json!({"id":"run-definition","repoPath":repo,"stateDir":state,"status":"RUNNING","createdAt":"2026-01-01T00:00:00Z","selection":{"kind":"all"},"validation":null,"definitions":{"artifacts":{"app":{"fingerprint":{"script":{"command":"echo","args":[],"timeoutMs":1}}}}}})).unwrap();
    receipts.create_run(&run, &[]).await.unwrap();
    let wire = json!({"artifacts":{"app":{"fingerprint":{"script":{"command":"echo","args":[],"timeoutMs":1}}}}});
    for duration in [
        std::time::Duration::ZERO,
        std::time::Duration::from_millis(2147483648),
        std::time::Duration::from_nanos(1),
    ] {
        let mut snapshot: Definitions = serde_json::from_value(wire.clone()).unwrap();
        let artifact = snapshot
            .0
            .as_mut()
            .unwrap()
            .artifacts
            .value()
            .unwrap()
            .get("app")
            .unwrap()
            .clone();
        let mut artifact = artifact;
        let mut fingerprint = artifact.fingerprint.value().unwrap().clone();
        let mut script = fingerprint.script.value().unwrap().clone();
        script.timeout_ms = Field::Value(duration);
        fingerprint.script = Field::Value(script);
        artifact.fingerprint = Field::Value(fingerprint);
        snapshot.0.as_mut().unwrap().artifacts =
            Field::Value(BTreeMap::from([("app".into(), artifact)]));
        assert!(serde_json::to_value(&snapshot).is_err());
        run.definitions = snapshot;
        assert!(receipts.save_run(&run).await.is_err());
        let saved = crate::store::read_run(&state, "run-definition")
            .await
            .unwrap();
        assert_eq!(serde_json::to_value(saved.run.definitions).unwrap(), wire);
        run.id = "run-invalid-definition".parse().unwrap();
        assert!(receipts.create_run(&run, &[]).await.is_err());
        assert!(
            crate::store::read_run(&state, "run-invalid-definition")
                .await
                .is_err()
        );
        run.id = "run-definition".parse().unwrap();
    }
}