artifactize-core 0.14.2

The library behind artifactize: Artifacts, evals, reviews, the monitor and review models, and the shared operations every interface calls.
Documentation
//! Independent saved wire fixtures pin graph capture and legacy field preservation.
use super::*;
use crate::project::selection::Selection;
use serde_json::json;

#[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",
                "children":{},
                "basis":null,
                "fingerprint":{
                    "script":{
                        "command":crate::test_os::echo_program(),
                        "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":crate::test_os::cat_program(),
                            "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":crate::test_os::true_program(),"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,
            },
        ],
    });
    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);
    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_preserves_the_independently_pinned_saved_wire() {
    let root = crate::test_os::tempdir();
    crate::test_declaration::write(
        root.path().join("index.artf"),
        json!({
            "name":"app",
            "views":{"agent_tools":{"read":{"builtin":"read"}}},
            "fingerprint":{"script":{"command":crate::test_os::echo_program(),"args":["v1"]}},
            "evals":[
                {
                    "id":"check",
                    "title":"Check",
                    "profile":{"kind":"agent","backend":"openai","model":"fixture"},
                    "payload":{"instruction":"Check.","owner":{"nested":[1,2]}},
                    "pass_schema":{"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 expected = json!({
        "version":1,
        "repoPath":crate::platform::path_text(&crate::platform::canonicalize(root.path()).unwrap()),
        "selection":{"kind":"all"},
        "artifacts":{
            "app":{
                "path":"",
                "kind":"folder",
                "name":"app",
                "tags":[],
                "children":{},
                "basis":null,
                "views":{"agentSessions":{},"agentTools":{"read":{"builtin":"read"}},"humanTools":{}},
                "fingerprint":{"script":{
                    "command":crate::test_os::echo_program(),
                    "args":["v1"],
                    "files":[],
                    "timeoutMs":null,
                }},
            },
        },
        "evals":[{
            "id":"app/check",
            "target":"app",
            "references":{},
            "deps":[],
            "declaration":{
                "id":"check",
                "title":"Check",
                "profile":{
                    "kind":"agent","backend":"openai","model":"fixture",
                    "reasoning":null,"timeoutMs":null,"maxToolCalls":null,"maxTokens":null,
                },
                "payload":{"instruction":"Check.","owner":{"nested":[1,2]}},
                "passSchema":{"type":"object"},
                "failSchema":null,
            },
        }],
        "relations":[],
        "components":[{"id":0,"artifacts":["app"],"dependencies":[],"gates":[],"cyclic":false}],
    });
    let snapshot = Definitions::from_view(&view).unwrap();
    assert_eq!(serde_json::to_value(snapshot).unwrap(), expected);
}

#[tokio::test]
async fn saved_definition_timeouts_reject_unrepresentable_writes() {
    let root = crate::test_os::tempdir();
    let repo = root.path().join("repo");
    let state = root.path().join("state");
    std::fs::create_dir_all(&repo).unwrap();
    let receipts = crate::store::OwnedReceipts::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":crate::test_os::echo_program(),"args":[],"timeoutMs":1}}},
            },
        },
    }))
    .unwrap();
    receipts.create_run(&run, &[]).await.unwrap();
    let wire = json!({
        "artifacts":{"app":{"fingerprint":{"script":{"command":crate::test_os::echo_program(),"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".parse().unwrap(), 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();
    }
}

#[test]
fn saved_component_ids_and_known_declaration_keys_keep_their_wire_shapes() {
    let saved = r#"{"id":17,"artifacts":["app"],"dependencies":[0,10],"gates":["spec/check"],"cyclic":false}"#;
    let component: Component = serde_json::from_str(saved).unwrap();
    assert_eq!(serde_json::to_string(&component).unwrap(), saved);
    let saved = r#"{"children":{"nested/child":"child"}}"#;
    let artifact: Artifact = serde_json::from_str(saved).unwrap();
    assert_eq!(serde_json::to_string(&artifact).unwrap(), saved);
    let saved = r#"{"profileVariants":{"sol-max":{"kind":"human"}}}"#;
    let declaration: Declaration = serde_json::from_str(saved).unwrap();
    assert_eq!(serde_json::to_string(&declaration).unwrap(), saved);

    for invalid in ["-1", "1.5", "\"1\""] {
        assert!(serde_json::from_str::<crate::graph::ComponentId>(invalid).is_err());
    }
    for invalid in [
        json!({"artifacts":{"app":{"children":{"../outside":"child"}}}}),
        json!({"evals":[{"id":"app/check","target":"app","declaration":{"profileVariants":{"bad.name":{"kind":"human"}}}}]}),
        json!({"relations":[{"source":"input","target":"app","kind":"instruction","evalId":"app/check","name":"bad.name"}]}),
    ] {
        assert!(
            serde_json::from_value::<Definitions>(invalid.clone()).is_err(),
            "{invalid}"
        );
    }
}