forjar 1.6.2

Rust-native Infrastructure as Code — bare-metal first, BLAKE3 state, provenance tracing
Documentation
# Binding Registry: provable-contracts <-> forjar
version: "1.0.0"
target_crate: forjar

bindings:
  # ── BLAKE3 State Hashing (I3) ──────────────────────────
  - contract: blake3-state-v1.yaml
    equation: hash_string
    module_path: "forjar::tripwire::hasher::hash_string"
    function: hash_string
    signature: "fn hash_string(s: &str) -> String"
    status: implemented
    notes: "Returns 'blake3:' || hex(BLAKE3(s))"

  - contract: blake3-state-v1.yaml
    equation: hash_file
    module_path: "forjar::tripwire::hasher::hash_file"
    function: hash_file
    signature: "fn hash_file(path: &Path) -> Result<String, String>"
    status: implemented
    notes: "Streaming 64KB buffer for large files"

  - contract: blake3-state-v1.yaml
    equation: composite_hash
    module_path: "forjar::tripwire::hasher::composite_hash"
    function: composite_hash
    signature: "fn composite_hash(components: &[&str]) -> String"
    status: implemented
    notes: "NUL-separated component concatenation"

  # ── DAG Ordering (I5) ──────────────────────────────────
  - contract: dag-ordering-v1.yaml
    equation: topological_sort
    module_path: "forjar::core::resolver::build_execution_order"
    function: build_execution_order
    signature: "fn build_execution_order(config: &ForjarConfig) -> Result<Vec<String>, String>"
    status: implemented
    notes: "Kahn's algorithm with alphabetical tie-breaking"

  - contract: dag-ordering-v1.yaml
    equation: kahn_sort
    module_path: "forjar::core::resolver::kahn_sort"
    function: kahn_sort
    signature: "fn kahn_sort(resource_ids: &[String], in_degree: &mut HashMap<String, usize>, adjacency: &mut HashMap<String, Vec<String>>) -> Vec<String>"
    status: implemented
    notes: "Private helper; tested via build_execution_order"

  # ── Execution Safety (I4, I7) ──────────────────────────
  - contract: execution-safety-v1.yaml
    equation: atomic_write
    module_path: "forjar::core::state::save_lock"
    function: save_lock
    signature: "fn save_lock(state_dir: &Path, lock: &StateLock) -> Result<(), String>"
    status: implemented
    notes: "temp + rename pattern; creates parent dirs"

  - contract: execution-safety-v1.yaml
    equation: jidoka_stop
    module_path: "forjar::core::executor::record_failure"
    function: record_failure
    signature: "fn record_failure(ctx: &mut RecordCtx, resource_id: &str, resource_type: &ResourceType, duration: f64, error: &str) -> bool"
    status: implemented
    notes: "Private helper; returns should_stop based on FailurePolicy"

  # ── Recipe Determinism (I11, I12) ──────────────────────
  - contract: recipe-determinism-v1.yaml
    equation: expand_recipe
    module_path: "forjar::core::recipe::expand_recipe"
    function: expand_recipe
    signature: "fn expand_recipe(recipe_id: &str, recipe_file: &RecipeFile, machine: &MachineTarget, provided_inputs: &HashMap<String, Value>, external_depends_on: &[String]) -> Result<IndexMap<String, Resource>, String>"
    status: implemented
    notes: "Namespaces resource IDs, propagates machine target"

  - contract: recipe-determinism-v1.yaml
    equation: validate_inputs
    module_path: "forjar::core::recipe::validate_inputs"
    function: validate_inputs
    signature: "fn validate_inputs(recipe: &RecipeMetadata, provided: &HashMap<String, Value>) -> Result<HashMap<String, String>, String>"
    status: implemented
    notes: "Type-checks all 6 input types (string, int, bool, path, enum, list)"

  - contract: recipe-determinism-v1.yaml
    equation: validate_input_type
    module_path: "forjar::core::recipe::validate_input_type"
    function: validate_input_type
    signature: "fn validate_input_type(name: &str, type_name: &str, value: &Value, decl: &RecipeInput) -> Result<String, String>"
    status: implemented
    notes: "Private helper; dispatches to type-specific validation"

  # ── Idempotent Apply (#97 KZ-11) ───────────────────────
  - contract: idempotent-apply-v1.yaml
    equation: plan_fixed_point
    module_path: "forjar::core::planner::plan"
    function: plan
    signature: "fn plan(config: &ForjarConfig, execution_order: &[String], locks: &HashMap<String, StateLock>, tag_filter: Option<&str>) -> ExecutionPlan"
    status: implemented
    notes: "Counter-conservation debug_assert; second plan over converged locks yields zero changes"

  - contract: idempotent-apply-v1.yaml
    equation: hash_determinism
    module_path: "forjar::core::planner::hash_desired_state"
    function: hash_desired_state
    signature: "fn hash_desired_state(resource: &Resource) -> String"
    status: implemented
    notes: "FJ-2200 determinism debug_assert_eq; stable field order, BLAKE3 over NUL-joined components"

  - contract: idempotent-apply-v1.yaml
    equation: noop_fixed_point
    module_path: "forjar::core::planner::determine_present_action"
    function: determine_present_action
    signature: "fn determine_present_action(resource_id: &str, resource: &Resource, machine_name: &str, locks: &HashMap<String, StateLock>) -> PlanAction"
    status: implemented
    notes: "Private helper; FJ-2200 debug_assert enforces converged + matching hash → NoOp"

  # ── Plan/Apply Equivalence (#97 KZ-11) ─────────────────
  - contract: plan-apply-equivalence-v1.yaml
    equation: plan_determinism
    module_path: "forjar::core::planner::plan"
    function: plan
    signature: "fn plan(config: &ForjarConfig, execution_order: &[String], locks: &HashMap<String, StateLock>, tag_filter: Option<&str>) -> ExecutionPlan"
    status: implemented
    notes: "Pure function; forjar plan (src/cli/plan.rs) and forjar apply (executor::apply) share it"

  - contract: plan-apply-equivalence-v1.yaml
    equation: apply_executes_plan
    module_path: "forjar::core::executor::apply"
    function: apply
    signature: "fn apply(cfg: &ApplyConfig) -> Result<Vec<ApplyResult>, String>"
    status: implemented
    notes: "Executor iterates plan.changes only; outcome-conservation debug_assert in apply_machine"

  - contract: plan-apply-equivalence-v1.yaml
    equation: noop_no_execution
    module_path: "forjar::core::executor::apply_single_resource"
    function: apply_single_resource
    signature: "fn apply_single_resource(cfg: &ApplyConfig, change: &PlannedChange, machine: &Machine, ctx: &mut RecordCtx, converged_resources: &HashSet<String>) -> Result<ResourceOutcome, String>"
    status: implemented
    notes: "Private helper; planned NoOp without force/trigger short-circuits to Unchanged"

  # ── Destroy/Undo Roundtrip (#97 KZ-11) ─────────────────
  - contract: destroy-undo-roundtrip-v1.yaml
    equation: snapshot_generation
    module_path: "forjar::cli::generation::create_generation"
    function: create_generation
    signature: "fn create_generation(state_dir: &Path, config_path: Option<&Path>) -> Result<u32, String>"
    status: implemented
    notes: "FJ-1386; monotonic numbering, atomic current-symlink switch, debug_assert on current"

  - contract: destroy-undo-roundtrip-v1.yaml
    equation: restore_generation
    module_path: "forjar::cli::generation::rollback_to_generation"
    function: rollback_to_generation
    signature: "fn rollback_to_generation(state_dir: &Path, generation: u32, yes: bool) -> Result<(), String>"
    status: implemented
    notes: "Restores snapshot then atomically re-points current; debug_assert on current"

  - contract: destroy-undo-roundtrip-v1.yaml
    equation: undo_roundtrip
    module_path: "forjar::cli::undo::cmd_undo"
    function: cmd_undo
    signature: "fn cmd_undo(file: &Path, state_dir: &Path, generations: u32, machine_filter: Option<&str>, dry_run: bool, yes: bool) -> Result<(), String>"
    status: implemented
    notes: "FJ-2003; targets current − n, rejects impossible targets, re-applies to converge"

  # ── Codegen Dispatch (I2) ──────────────────────────────
  - contract: codegen-dispatch-v1.yaml
    equation: check_script
    module_path: "forjar::core::codegen::check_script"
    function: check_script
    signature: "fn check_script(resource: &Resource) -> Result<String, String>"
    status: implemented
    notes: "Dispatches to resource-specific check handlers"

  - contract: codegen-dispatch-v1.yaml
    equation: apply_script
    module_path: "forjar::core::codegen::apply_script"
    function: apply_script
    signature: "fn apply_script(resource: &Resource) -> Result<String, String>"
    status: implemented
    notes: "Dispatches to resource-specific apply handlers"

  - contract: codegen-dispatch-v1.yaml
    equation: state_query_script
    module_path: "forjar::core::codegen::state_query_script"
    function: state_query_script
    signature: "fn state_query_script(resource: &Resource) -> Result<String, String>"
    status: implemented
    notes: "Dispatches to resource-specific state query handlers"