cordance-cli 0.1.2

Cordance CLI — installs the `cordance` binary. The umbrella package `cordance` re-exports this entry; either install command works.
Documentation
//! `cordance cortex push` — emit a Cortex candidate receipt JSON.
//!
//! ADR 0005: Cordance never invokes Cortex directly. The operator hands the
//! produced receipt to Cortex's `cortex_memory_accept` flow.

use anyhow::Result;
use camino::Utf8PathBuf;
use cordance_core::pack::CordancePack;

/// Build and persist a Cortex candidate receipt.
///
/// `BUILD_SPEC` §6.6: the receipt JSON is always written to disk, regardless
/// of dry-run mode. The `dry_run` flag only suppresses the operator-handoff
/// text (the lines that tell the operator to feed the file into Cortex). This
/// lets CI and watch loops generate receipts without printing instructions
/// that don't apply to them.
///
/// # Errors
///
/// Returns `Err` if the receipt cannot be built, serialised, or written to
/// disk.
pub fn run_push(pack: &CordancePack, target: &Utf8PathBuf, dry_run: bool) -> Result<()> {
    let receipt = cordance_cortex::build_receipt(pack)?;
    let json = serde_json::to_string_pretty(&receipt)?;
    let out_path = target.join(".cordance/cortex-receipt.json");

    // Always write the receipt regardless of mode (BUILD_SPEC §6.6).
    //
    // Round-5 redteam #4: a hostile target can pre-plant
    // `<target>/.cordance/cortex-receipt.json` as a symlink to any
    // operator-owned file. Route through `safe_write_with_mkdir` so the
    // helper refuses to follow the link. The helper handles parent-dir
    // creation, so the explicit `create_dir_all` is gone.
    cordance_core::fs::safe_write_with_mkdir(out_path.as_std_path(), json.as_bytes())?;
    println!("Cortex receipt: {out_path}");

    if !dry_run {
        println!("To submit: hand this file to Cortex's cortex_memory_accept flow.");
        println!("(Cordance never invokes cortex directly — ADR 0005)");
    }
    Ok(())
}