weavatrix-refactor-plan 0.1.1

Evidence metadata, validation profiles, and canonical fingerprints for Weavatrix refactor plans
Documentation
mod common;

use blazingly_json::Value;
use weavatrix_refactor_plan::{
    RefactorPlan, RefactorPlanLimits, fingerprint_plan, validate_consumer_plan,
};

fn next(state: &mut u64) -> u64 {
    *state = state
        .wrapping_mul(6_364_136_223_846_793_005)
        .wrapping_add(1_442_695_040_888_963_407);
    *state
}

#[test]
fn generated_plans_roundtrip_and_obey_fingerprint_properties() {
    let mut state = 0x5eed_cafe_f00d_u64;
    for case in 0_u64..512 {
        let mut plan = common::minimal_plan();
        let random = next(&mut state);
        plan.operation = format!("operation_{case}_{}", random % 17);
        plan.evidence.extensions.insert(
            format!("vendor_{:02}", random % 23),
            blazingly_json::json!({
                "sequence": case,
                "enabled": random & 1 == 1,
                "nested": {"z": random % 101, "a": format!("v{random}")}
            }),
        );
        validate_consumer_plan(&plan, RefactorPlanLimits::default()).unwrap();
        let fingerprint = fingerprint_plan(&plan).unwrap();

        let encoded = blazingly_json::to_vec(&plan).unwrap();
        let decoded: RefactorPlan = blazingly_json::from_slice(&encoded).unwrap();
        assert_eq!(fingerprint, fingerprint_plan(&decoded).unwrap());

        let mut timestamp_only = decoded.clone();
        timestamp_only.evidence.created_at = Some(format!("2026-08-02T12:{:02}:00Z", case % 60));
        assert_eq!(fingerprint, fingerprint_plan(&timestamp_only).unwrap());

        let mut changed = decoded;
        changed
            .evidence
            .extensions
            .insert("mutation".to_owned(), Value::Number((case + 1).into()));
        assert_ne!(fingerprint, fingerprint_plan(&changed).unwrap());
    }
}