#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;
use std::process::Command;
use tempfile::tempdir;

#[test]
fn forge_provider_status_reports_current_provider_capabilities() {
    let catalog = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["forge-provider-status"])
        .output()
        .unwrap();
    assert!(catalog.status.success());
    let catalog_text = String::from_utf8_lossy(&catalog.stdout);
    assert!(catalog_text.contains("default_provider: mock"));
    assert!(catalog_text.contains("name: mock"));
    assert!(catalog_text.contains("name: vegvisir"));
    assert!(catalog_text.contains("structured_envelope: true"));
    assert!(catalog_text.contains("live_reasoning: false"));
    assert!(catalog_text.contains("SkillInference"));
    assert!(catalog_text.contains("RegistryReadiness"));
    assert!(catalog_text.contains("adapter_command_configured: false"));
    assert!(catalog_text.contains("SKILLER_VEGVISIR_FORGE_ADAPTER"));

    let vegvisir = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["forge-provider-status", "--provider", "vegvisir"])
        .output()
        .unwrap();
    assert!(vegvisir.status.success());
    let vegvisir_text = String::from_utf8_lossy(&vegvisir.stdout);
    assert!(vegvisir_text.contains("name: vegvisir"));
    assert!(vegvisir_text.contains("display_name: Vegvisir structured-envelope Forge adapter"));
    assert!(vegvisir_text.contains("deterministic: true"));
    assert!(vegvisir_text.contains("live_reasoning: false"));
    assert!(vegvisir_text.contains("adapter_command_configured: false"));
    assert!(vegvisir_text.contains("adapter_mode: deterministic fallback"));
    assert!(vegvisir_text.contains("SKILLER_VEGVISIR_FORGE_ADAPTER"));
    assert!(vegvisir_text.contains("adapter_timeout_secs: 120"));
    assert!(vegvisir_text.contains("SKILLER_VEGVISIR_FORGE_ADAPTER_TIMEOUT_SECS"));

    let configured = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["forge-provider-status", "--provider", "vegvisir"])
        .env(
            "SKILLER_VEGVISIR_FORGE_ADAPTER",
            env!("CARGO_BIN_EXE_skiller"),
        )
        .env("SKILLER_VEGVISIR_FORGE_ADAPTER_TIMEOUT_SECS", "3")
        .output()
        .unwrap();
    assert!(configured.status.success());
    let configured_text = String::from_utf8_lossy(&configured.stdout);
    assert!(configured_text.contains("adapter_command_configured: true"));
    assert!(configured_text.contains("deterministic: false"));
    assert!(configured_text.contains("live_reasoning: true"));
    assert!(configured_text.contains("adapter_mode: external strict-envelope command"));
    assert!(configured_text.contains("adapter_preflight_ok: true"));
    assert!(configured_text.contains("adapter_timeout_secs: 3"));
}

#[test]
fn forge_adapter_preflight_reports_missing_adapter_before_execution() {
    let missing = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["forge-adapter-preflight"])
        .env(
            "SKILLER_VEGVISIR_FORGE_ADAPTER",
            "/tmp/skiller-definitely-missing-adapter",
        )
        .output()
        .unwrap();
    assert!(!missing.status.success());
    let text = String::from_utf8_lossy(&missing.stdout);
    assert!(text.contains("valid: false"));
    assert!(text.contains("adapter_command_configured: true"));
    assert!(text.contains("adapter command does not exist"));

    let fallback = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["forge-adapter-preflight"])
        .output()
        .unwrap();
    assert!(fallback.status.success());
    let fallback_text = String::from_utf8_lossy(&fallback.stdout);
    assert!(fallback_text.contains("valid: true"));
    assert!(fallback_text.contains("adapter_command_configured: false"));
    assert!(fallback_text.contains("deterministic fallback"));
}

#[test]
#[cfg(unix)]
fn forge_adapter_self_test_exercises_strict_envelope_contract() {
    let temp = tempdir().unwrap();

    let missing = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["forge-adapter-self-test"])
        .output()
        .unwrap();
    assert!(!missing.status.success());
    let missing_text = String::from_utf8_lossy(&missing.stdout);
    assert!(missing_text.contains("valid: false"));
    assert!(missing_text.contains("must be configured for adapter self-test"));

    let adapter = temp.path().join("self-test-adapter.py");
    std::fs::write(
        &adapter,
        r#"#!/usr/bin/env python3
import sys
request_id = ""
pass_type = ""
for line in sys.stdin.read().splitlines():
    if line.startswith("request_id:"):
        request_id = line.split(":", 1)[1].strip().strip("'\"")
    if line.startswith("pass_type:"):
        pass_type = line.split(":", 1)[1].strip().strip("'\"")
print(f"request_id: {request_id}")
print(f"pass_type: {pass_type}")
print("generated_items: []")
print("modified_items: []")
print("review_findings:")
print("- self-test adapter valid strict envelope")
print("confidence_updates: {}")
print("evidence_records: []")
print("required_human_review: false")
print("audit_notes:")
print("- self-test adapter completed")
"#,
    )
    .unwrap();
    let mut perms = std::fs::metadata(&adapter).unwrap().permissions();
    perms.set_mode(0o755);
    std::fs::set_permissions(&adapter, perms).unwrap();

    let ok = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["forge-adapter-self-test"])
        .env("SKILLER_VEGVISIR_FORGE_ADAPTER", adapter.to_str().unwrap())
        .env("SKILLER_VEGVISIR_FORGE_ADAPTER_TIMEOUT_SECS", "3")
        .output()
        .unwrap();
    assert!(
        ok.status.success(),
        "stdout={}\nstderr={}",
        String::from_utf8_lossy(&ok.stdout),
        String::from_utf8_lossy(&ok.stderr)
    );
    let ok_text = String::from_utf8_lossy(&ok.stdout);
    assert!(ok_text.contains("valid: true"));
    assert!(ok_text.contains("preflight_valid: true"));
    assert!(ok_text.contains("response_parsed: true"));
    assert!(ok_text.contains("response_valid: true"));
    assert!(ok_text.contains("pass_type: Interpretation"));
    assert!(ok_text.contains("self-test adapter completed"));

    let bad_adapter = temp.path().join("bad-self-test-adapter.py");
    std::fs::write(
        &bad_adapter,
        r#"#!/usr/bin/env python3
import sys
_ = sys.stdin.read()
print("request_id: wrong")
print("pass_type: Interpretation")
print("generated_items: []")
print("modified_items: []")
print("review_findings: []")
print("confidence_updates: {}")
print("evidence_records: []")
print("required_human_review: false")
print("audit_notes: []")
"#,
    )
    .unwrap();
    let mut perms = std::fs::metadata(&bad_adapter).unwrap().permissions();
    perms.set_mode(0o755);
    std::fs::set_permissions(&bad_adapter, perms).unwrap();

    let bad = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["forge-adapter-self-test"])
        .env(
            "SKILLER_VEGVISIR_FORGE_ADAPTER",
            bad_adapter.to_str().unwrap(),
        )
        .env("SKILLER_VEGVISIR_FORGE_ADAPTER_TIMEOUT_SECS", "3")
        .output()
        .unwrap();
    assert!(!bad.status.success());
    let bad_text = String::from_utf8_lossy(&bad.stdout);
    assert!(bad_text.contains("valid: false"));
    assert!(bad_text.contains("response_parsed: true"));
    assert!(bad_text.contains("response_valid: false"));
    assert!(bad_text.contains("forge response request_id mismatch"));
}

#[test]
#[cfg(unix)]
fn vegvisir_forge_provider_uses_configured_strict_envelope_adapter() {
    let temp = tempdir().unwrap();
    let docs = temp.path().join("docs");
    std::fs::create_dir_all(&docs).unwrap();
    std::fs::write(
        docs.join("ops.md"),
        "# Diagnose Pods\n\nUse `kubectl get pods` before changing anything. Warning: ask approval before mutation.\n",
    )
    .unwrap();
    let bundle = temp.path().join("bundle");
    let forged = temp.path().join("forged");

    let compile = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args([
            "compile",
            docs.to_str().unwrap(),
            "--out",
            bundle.to_str().unwrap(),
            "--name",
            "adapter-smoke",
        ])
        .output()
        .unwrap();
    assert!(
        compile.status.success(),
        "{}",
        String::from_utf8_lossy(&compile.stderr)
    );

    let adapter = temp.path().join("adapter.py");
    std::fs::write(
        &adapter,
        r#"#!/usr/bin/env python3
import sys
request_id = ""
pass_type = ""
for line in sys.stdin.read().splitlines():
    if line.startswith("request_id:"):
        request_id = line.split(":", 1)[1].strip().strip("'\"")
    if line.startswith("pass_type:"):
        pass_type = line.split(":", 1)[1].strip().strip("'\"")
print(f"request_id: {request_id}")
print(f"pass_type: {pass_type}")
print("generated_items: []")
print("modified_items: []")
print("review_findings:")
print("- external adapter smoke finding")
print("confidence_updates: {}")
print("evidence_records: []")
print("required_human_review: true")
print("audit_notes:")
print("- external adapter smoke response")
"#,
    )
    .unwrap();
    let mut perms = std::fs::metadata(&adapter).unwrap().permissions();
    perms.set_mode(0o755);
    std::fs::set_permissions(&adapter, perms).unwrap();

    let forge = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args([
            "forge",
            bundle.to_str().unwrap(),
            "--out",
            forged.to_str().unwrap(),
            "--provider",
            "vegvisir",
            "--max-skills",
            "1",
        ])
        .env("SKILLER_VEGVISIR_FORGE_ADAPTER", adapter.to_str().unwrap())
        .output()
        .unwrap();
    assert!(
        forge.status.success(),
        "stdout={}\nstderr={}",
        String::from_utf8_lossy(&forge.stdout),
        String::from_utf8_lossy(&forge.stderr)
    );

    let responses = std::fs::read_to_string(forged.join("forge_responses.yaml")).unwrap();
    assert!(
        responses.contains("external adapter smoke response"),
        "{responses}"
    );
    assert!(
        responses.contains("external adapter smoke finding"),
        "{responses}"
    );

    let validate = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["validate", forged.to_str().unwrap()])
        .output()
        .unwrap();
    assert!(
        validate.status.success(),
        "{}",
        String::from_utf8_lossy(&validate.stdout)
    );
}

#[test]
#[cfg(unix)]
fn vegvisir_forge_adapter_timeout_is_bounded_and_diagnostic() {
    let temp = tempdir().unwrap();
    let docs = temp.path().join("docs");
    std::fs::create_dir_all(&docs).unwrap();
    std::fs::write(
        docs.join("ops.md"),
        "# Diagnose Pods\n\nUse `kubectl get pods` before changing anything.\n",
    )
    .unwrap();
    let bundle = temp.path().join("bundle");
    let forged = temp.path().join("forged");

    let compile = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args([
            "compile",
            docs.to_str().unwrap(),
            "--out",
            bundle.to_str().unwrap(),
            "--name",
            "adapter-timeout-smoke",
        ])
        .output()
        .unwrap();
    assert!(
        compile.status.success(),
        "{}",
        String::from_utf8_lossy(&compile.stderr)
    );

    let adapter = temp.path().join("sleepy-adapter.py");
    std::fs::write(
        &adapter,
        r#"#!/usr/bin/env python3
import sys, time
_ = sys.stdin.read()
print("adapter started", file=sys.stderr, flush=True)
time.sleep(5)
"#,
    )
    .unwrap();
    let mut perms = std::fs::metadata(&adapter).unwrap().permissions();
    perms.set_mode(0o755);
    std::fs::set_permissions(&adapter, perms).unwrap();

    let forge = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args([
            "forge",
            bundle.to_str().unwrap(),
            "--out",
            forged.to_str().unwrap(),
            "--provider",
            "vegvisir",
            "--max-skills",
            "1",
        ])
        .env("SKILLER_VEGVISIR_FORGE_ADAPTER", adapter.to_str().unwrap())
        .env("SKILLER_VEGVISIR_FORGE_ADAPTER_TIMEOUT_SECS", "1")
        .output()
        .unwrap();
    assert!(!forge.status.success());
    let stderr = String::from_utf8_lossy(&forge.stderr);
    assert!(stderr.contains("timed out after 1 seconds"), "{stderr}");
}

#[test]
fn compile_forge_review_and_agent_pack_workflow() {
    let temp = tempdir().unwrap();
    let docs = temp.path().join("docs");
    std::fs::create_dir_all(&docs).unwrap();
    std::fs::write(
        docs.join("ops.md"),
        "# Diagnose Pods\n\nYou should inspect pod status first.\n\n```\nkubectl get pods\nkubectl logs demo\n```\n\nWarning: never delete production pods without approval.\n",
    )
    .unwrap();

    let bundle = temp.path().join("bundle");
    let forged = temp.path().join("forged");
    let review = temp.path().join("review");
    let agent = temp.path().join("agent");

    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "compile",
                docs.to_str().unwrap(),
                "--out",
                bundle.to_str().unwrap(),
                "--name",
                "workflow",
                "--domain",
                "kubernetes",
            ])
            .status()
            .unwrap()
            .success()
    );

    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "forge",
                bundle.to_str().unwrap(),
                "--out",
                forged.to_str().unwrap(),
                "--provider",
                "vegvisir",
                "--domain-profile",
                "kubernetes-operations",
                "--max-skills",
                "2",
            ])
            .status()
            .unwrap()
            .success()
    );

    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "review-agent",
                forged.to_str().unwrap(),
                "--out",
                review.to_str().unwrap(),
            ])
            .status()
            .unwrap()
            .success()
    );
    assert!(review.join("verifier-review.yaml").exists());
    assert!(review.join("verifier-review.md").exists());

    let forge_requests_text = std::fs::read_to_string(forged.join("forge_requests.yaml")).unwrap();
    let forge_responses_text =
        std::fs::read_to_string(forged.join("forge_responses.yaml")).unwrap();
    for pass in [
        "Interpretation",
        "SkillExpansion",
        "SkillInference",
        "SafetyAndGovernance",
        "EvalGeneration",
        "Critique",
        "VerifierReview",
        "AgentRoleMapping",
        "RegistryReadiness",
    ] {
        assert!(
            forge_requests_text.contains(&format!("pass_type: {pass}")),
            "missing request pass {pass}: {forge_requests_text}"
        );
        assert!(
            forge_responses_text.contains(&format!("pass_type: {pass}")),
            "missing response pass {pass}: {forge_responses_text}"
        );
    }
    assert!(
        forge_responses_text.contains("READY-CANDIDATE")
            || forge_responses_text.contains("requires review before publication")
    );
    assert!(forge_responses_text.contains("VERIFY:"));
    assert!(forge_responses_text.contains("WARNING:") || forge_responses_text.contains("OK:"));

    let forge_summary_text = std::fs::read_to_string(forged.join("forge_summary.yaml")).unwrap();
    let forge_summary_md = std::fs::read_to_string(forged.join("forge_summary.md")).unwrap();
    assert!(forge_summary_text.contains("summary_id: forge-summary-"));
    assert!(forge_summary_text.contains("provider_provenance:"));
    assert!(
        forge_summary_text.contains("provider: vegvisir")
            || forge_summary_text.contains("provider: \"vegvisir\"")
    );
    assert!(forge_summary_text.contains("adapter_mode: deterministic fallback"));
    assert!(forge_summary_text.contains("deterministic: true"));
    assert!(forge_summary_text.contains("live_reasoning: false"));
    assert!(forge_summary_text.contains("pass_count: 9"));
    assert!(forge_summary_text.contains("pass_type: RegistryReadiness"));
    assert!(forge_summary_text.contains("required_human_review: true"));
    assert!(forge_summary_text.contains("review_finding_count:"));
    assert!(forge_summary_md.contains("# Forge Summary"));
    assert!(forge_summary_md.contains("Provider mode: `deterministic fallback`"));
    assert!(forge_summary_md.contains("Live reasoning: false"));
    assert!(forge_summary_md.contains("## Registry Readiness Notes"));
    assert!(forge_summary_md.contains("Human review required: true"));

    let mut inferred_count = 0;
    for entry in std::fs::read_dir(forged.join("skills")).unwrap() {
        let path = entry.unwrap().path();
        if path.extension().and_then(|s| s.to_str()) == Some("yaml") {
            let skill_text = std::fs::read_to_string(path).unwrap();
            if skill_text.contains("inferred-workflow") || skill_text.contains("inference_records:")
            {
                inferred_count += 1;
            }
        }
    }
    assert!(
        inferred_count > 0,
        "Forge did not store inferred/review records"
    );

    let eval_output = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["eval", forged.to_str().unwrap()])
        .output()
        .unwrap();
    let eval_stdout = String::from_utf8_lossy(&eval_output.stdout);
    assert!(
        eval_stdout.contains("total_eval_cases:"),
        "eval stdout was: {eval_stdout}"
    );

    let reviewed = temp.path().join("reviewed");
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "apply-review",
                forged.to_str().unwrap(),
                "--review",
                review.join("verifier-review.yaml").to_str().unwrap(),
                "--out",
                reviewed.to_str().unwrap(),
            ])
            .status()
            .unwrap()
            .success()
    );
    assert!(reviewed.join("audit/events.yaml").exists());

    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "build-agent-pack",
                forged.to_str().unwrap(),
                "--agent",
                "Cluster Diagnostic Agent",
                "--out",
                agent.to_str().unwrap(),
            ])
            .status()
            .unwrap()
            .success()
    );
    let pack = std::fs::read_to_string(agent.join("agent-pack.yaml")).unwrap();
    assert!(pack.contains("required_skills"));
    assert!(pack.contains("optional_skills"));
    assert!(pack.contains("approval_policy"));
    assert!(pack.contains("eval_status:"));
    assert!(pack.contains("selected_skill_count:"));
    assert!(pack.contains("system_prompt_material:"));
    assert!(pack.contains(
        "You are Cluster Diagnostic Agent, a specialized Vegvisir agent created by Skiller"
    ));
    assert!(pack.contains("# Operating rules"));
    assert!(pack.contains("# Runtime posture"));
    assert!(pack.contains("ka:"));
    assert!(pack.contains("id: vegvisir_default"));
    assert!(pack.contains("display_name: Vegvisir Default"));
    assert!(pack.contains("# Communication ka"));
    assert!(pack.contains("pragmatic_agentic_working_partner"));
    assert!(pack.contains("Ka/persona controls delivery style only"));
    assert!(pack.contains("ka_must_not_override_usrl"));
    assert!(pack.contains("clarity_over_character"));
    assert!(pack.contains("# Embedded USRL contract"));
    assert!(pack.contains("```usrl"));
    assert!(pack.contains("contract ClusterDiagnosticAgentAgentContract"));
    assert!(pack.contains("section Metadata"));
    assert!(pack.contains("section RuntimeFacts"));
    assert!(pack.contains("section Permissions"));
    assert!(pack.contains("section Constraints"));
    assert!(pack.contains("section Stages"));
    assert!(pack.contains("section Triggers"));
    assert!(pack.contains("deny \"request_plaintext_credentials\""));
    assert!(pack.contains("require \"approval_when_runtime_or_skill_policy_requires\""));
}

#[test]
fn forge_handoff_and_validate_template_workflow() {
    let temp = tempdir().unwrap();
    let docs = temp.path().join("docs");
    std::fs::create_dir_all(&docs).unwrap();
    std::fs::write(
        docs.join("cli.md"),
        "# Inspect Cache\n\nUse status commands before mutations.\n\n```\ncachectl status\n```\n",
    )
    .unwrap();

    let bundle = temp.path().join("bundle");
    let handoff = temp.path().join("handoff");

    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "compile",
                docs.to_str().unwrap(),
                "--out",
                bundle.to_str().unwrap(),
                "--name",
                "handoff-workflow",
                "--domain",
                "cli",
            ])
            .status()
            .unwrap()
            .success()
    );

    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "forge-handoff",
                bundle.to_str().unwrap(),
                "--out",
                handoff.to_str().unwrap(),
                "--pass",
                "skill-expansion",
                "--max-skills",
                "1",
            ])
            .status()
            .unwrap()
            .success()
    );

    assert!(handoff.join("forge-request.yaml").exists());
    assert!(handoff.join("forge-response-template.yaml").exists());
    let request_text = std::fs::read_to_string(handoff.join("forge-request.yaml")).unwrap();
    assert!(request_text.contains("source_context:"));
    assert!(request_text.contains("bundle_context:"));
    assert!(request_text.contains("validation_constraints:"));
    assert!(request_text.contains("response_schema_guide:"));
    assert!(request_text.contains("envelope_type: ForgeResponseEnvelope"));
    assert!(request_text.contains("required_fields:"));
    assert!(request_text.contains("field: generated_items"));
    assert!(request_text.contains("skill_output_rules:"));
    assert!(request_text.contains("evidence_record_rules:"));
    assert!(request_text.contains("confidence_update_rules:"));
    assert!(request_text.contains("forbidden_outputs:"));
    assert!(request_text.contains("minimal_valid_response:"));
    assert!(request_text.contains("selected_skill_count: 1"));
    assert!(request_text.contains("existing_forge_request_count: 0"));
    assert!(
        request_text
            .contains("Return a ForgeResponseEnvelope with matching request_id and pass_type.")
    );
    assert!(request_text.contains("source_trust:"));
    let prompt = std::fs::read_to_string(handoff.join("vegvisir-prompt.md")).unwrap();
    assert!(prompt.contains("Return ONLY a valid `ForgeResponseEnvelope`"));
    assert!(prompt.contains("`bundle_context` summarizes"));
    assert!(prompt.contains("`validation_constraints` are hard requirements"));
    assert!(
        prompt.contains(
            "Use `response_schema_guide` from the request as the authoritative field guide"
        )
    );

    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "forge-validate",
                bundle.to_str().unwrap(),
                "--request",
                handoff.join("forge-request.yaml").to_str().unwrap(),
                "--response",
                handoff
                    .join("forge-response-template.yaml")
                    .to_str()
                    .unwrap(),
            ])
            .status()
            .unwrap()
            .success()
    );
}

#[test]
fn forge_apply_writes_summary_and_manifest_for_external_responses() {
    let temp = tempdir().unwrap();
    let docs = temp.path().join("docs");
    std::fs::create_dir_all(&docs).unwrap();
    std::fs::write(
        docs.join("cli.md"),
        "# Inspect Cache\n\nUse status commands before mutations.\n\n```\ncachectl status\n```\n\nWarning: require approval before flushing caches.\n",
    )
    .unwrap();

    let bundle = temp.path().join("bundle");
    let handoff = temp.path().join("handoff");
    let applied = temp.path().join("applied");
    let apply_report_path = temp.path().join("forge-apply-report.yaml");

    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "compile",
                docs.to_str().unwrap(),
                "--out",
                bundle.to_str().unwrap(),
                "--name",
                "external-forge-apply",
                "--domain",
                "cli",
            ])
            .status()
            .unwrap()
            .success()
    );

    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "forge-handoff",
                bundle.to_str().unwrap(),
                "--out",
                handoff.to_str().unwrap(),
                "--pass",
                "skill-expansion",
                "--max-skills",
                "1",
            ])
            .status()
            .unwrap()
            .success()
    );

    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "forge-apply",
                bundle.to_str().unwrap(),
                "--request",
                handoff.join("forge-request.yaml").to_str().unwrap(),
                "--response",
                handoff
                    .join("forge-response-template.yaml")
                    .to_str()
                    .unwrap(),
                "--out",
                applied.to_str().unwrap(),
                "--report",
                apply_report_path.to_str().unwrap(),
            ])
            .status()
            .unwrap()
            .success()
    );

    assert!(apply_report_path.exists());
    let apply_report = std::fs::read_to_string(&apply_report_path).unwrap();
    assert!(apply_report.contains("apply_id: forge-apply-"));
    assert!(apply_report.contains("pass_type: SkillExpansion"));
    assert!(apply_report.contains("valid: true"));
    assert!(apply_report.contains("before_skill_count: 1"));
    assert!(apply_report.contains("after_skill_count: 1"));
    assert!(apply_report.contains("review_finding_count: 1"));
    assert!(apply_report.contains("required_human_review: true"));
    assert!(apply_report.contains("validation_errors: []"));

    assert!(applied.join("forge_requests.yaml").exists());
    assert!(applied.join("forge_responses.yaml").exists());
    assert!(applied.join("forge_summary.yaml").exists());
    assert!(applied.join("forge_summary.md").exists());

    let summary = std::fs::read_to_string(applied.join("forge_summary.yaml")).unwrap();
    assert!(summary.contains("summary_id: forge-summary-"));
    assert!(summary.contains("pass_count: 1"));
    assert!(summary.contains("pass_type: SkillExpansion"));

    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args(["validate", applied.to_str().unwrap()])
            .status()
            .unwrap()
            .success()
    );
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args(["verify-manifest", applied.to_str().unwrap()])
            .status()
            .unwrap()
            .success()
    );

    std::fs::write(applied.join("forge_summary.md"), "# stale\n").unwrap();
    let output = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["validate", applied.to_str().unwrap()])
        .output()
        .unwrap();
    assert!(!output.status.success());
    let combined = format!(
        "{}{}",
        String::from_utf8_lossy(&output.stdout),
        String::from_utf8_lossy(&output.stderr)
    );
    assert!(
        combined.contains("forge_summary.md is stale"),
        "output was: {combined}"
    );
}

#[test]
fn older_minimal_skill_bundles_load_with_expanded_defaults() {
    let temp = tempdir().unwrap();
    let bundle = temp.path().join("old-bundle");
    std::fs::create_dir_all(bundle.join("skills")).unwrap();
    std::fs::create_dir_all(bundle.join("sources")).unwrap();
    std::fs::create_dir_all(bundle.join("graph")).unwrap();
    std::fs::create_dir_all(bundle.join("audit")).unwrap();

    std::fs::write(
        bundle.join("package.yaml"),
        r#"---
bundle_id: bundle-old
name: old-format
version: 0.1.0
domain: null
source_corpus: []
review_status: Candidate
publish_status: Unpublished
compatibility: {}
created_at: 2025-01-01T00:00:00Z
"#,
    )
    .unwrap();
    std::fs::write(bundle.join("sources/index.yaml"), "[]\n").unwrap();
    std::fs::write(bundle.join("sources/sections.yaml"), "[]\n").unwrap();
    std::fs::write(bundle.join("graph/concepts.yaml"), "[]\n").unwrap();
    std::fs::write(bundle.join("graph/dependencies.yaml"), "[]\n").unwrap();
    std::fs::write(bundle.join("audit/events.yaml"), "[]\n").unwrap();
    std::fs::write(bundle.join("candidates.yaml"), "[]\n").unwrap();
    std::fs::write(
        bundle.join("skills/skill-old.yaml"),
        r#"---
id: skill-old
title: Old bundle skill
summary: Minimal pre-expansion skill artifact.
skill_type: Procedure
scope: TaskLevel
status: Candidate
"#,
    )
    .unwrap();

    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args(["list", bundle.to_str().unwrap()])
            .status()
            .unwrap()
            .success()
    );
}

#[test]
fn forge_validate_rejects_unsupported_source_and_out_of_range_scores() {
    let temp = tempdir().unwrap();
    let docs = temp.path().join("docs");
    std::fs::create_dir_all(&docs).unwrap();
    std::fs::write(
        docs.join("ops.md"),
        "# Inspect Service\n\nYou should inspect service status before making changes.\n\n```\nsvc status\n```\n\nWarning: never restart production services without approval.\n",
    )
    .unwrap();

    let bundle = temp.path().join("bundle");
    let handoff = temp.path().join("handoff");
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "compile",
                docs.to_str().unwrap(),
                "--out",
                bundle.to_str().unwrap(),
                "--name",
                "forge-validation",
            ])
            .status()
            .unwrap()
            .success()
    );
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "forge-handoff",
                bundle.to_str().unwrap(),
                "--out",
                handoff.to_str().unwrap(),
                "--pass",
                "skill-expansion",
                "--max-skills",
                "1",
            ])
            .status()
            .unwrap()
            .success()
    );

    let request_text = std::fs::read_to_string(handoff.join("forge-request.yaml")).unwrap();
    let request: serde_yaml::Value = serde_yaml::from_str(&request_text).unwrap();
    let request_id = request["request_id"].as_str().unwrap();
    let skill = request["candidate_skills"][0].clone();
    let skill_id = skill["id"].as_str().unwrap().to_string();
    let section_id = skill["source_section_ids"][0].as_str().unwrap().to_string();

    let mut bad_skill = skill;
    bad_skill["confidence"]["raw"] = serde_yaml::Value::from(1.2);
    bad_skill["evidence_breakdown"]["direct_extraction"] = serde_yaml::Value::from(0.8);
    bad_skill["evidence_breakdown"]["supporting_inference"] = serde_yaml::Value::from(0.4);
    bad_skill["citations"] = serde_yaml::to_value(vec![serde_yaml::Mapping::from_iter([
        (
            serde_yaml::Value::from("citation_id"),
            serde_yaml::Value::from("forged-bad-citation"),
        ),
        (
            serde_yaml::Value::from("source_id"),
            serde_yaml::Value::from("invented-source"),
        ),
        (
            serde_yaml::Value::from("section_id"),
            serde_yaml::Value::from(section_id.as_str()),
        ),
        (
            serde_yaml::Value::from("excerpt"),
            serde_yaml::Value::from("unsupported"),
        ),
    ])])
    .unwrap();

    let bad_confidence = serde_yaml::Mapping::from_iter([
        (
            serde_yaml::Value::from("raw"),
            serde_yaml::Value::from(-0.1),
        ),
        (
            serde_yaml::Value::from("extraction"),
            serde_yaml::Value::from(0.7),
        ),
        (
            serde_yaml::Value::from("inference"),
            serde_yaml::Value::from(0.1),
        ),
        (
            serde_yaml::Value::from("procedure"),
            serde_yaml::Value::from(0.5),
        ),
        (
            serde_yaml::Value::from("guardrail"),
            serde_yaml::Value::from(0.5),
        ),
        (
            serde_yaml::Value::from("eval"),
            serde_yaml::Value::from(0.4),
        ),
        (
            serde_yaml::Value::from("routing"),
            serde_yaml::Value::from(0.5),
        ),
        (
            serde_yaml::Value::from("source_quality"),
            serde_yaml::Value::from(0.5),
        ),
        (
            serde_yaml::Value::from("human_review"),
            serde_yaml::Value::from(0.0),
        ),
        (
            serde_yaml::Value::from("runtime"),
            serde_yaml::Value::from(0.0),
        ),
    ]);

    let response = serde_yaml::to_string(&serde_yaml::Mapping::from_iter([
        (
            serde_yaml::Value::from("request_id"),
            serde_yaml::Value::from(request_id),
        ),
        (
            serde_yaml::Value::from("pass_type"),
            serde_yaml::Value::from("SkillExpansion"),
        ),
        (
            serde_yaml::Value::from("generated_items"),
            serde_yaml::Value::Sequence(vec![]),
        ),
        (
            serde_yaml::Value::from("modified_items"),
            serde_yaml::Value::Sequence(vec![bad_skill]),
        ),
        (
            serde_yaml::Value::from("review_findings"),
            serde_yaml::Value::Sequence(vec![]),
        ),
        (
            serde_yaml::Value::from("confidence_updates"),
            serde_yaml::to_value(serde_yaml::Mapping::from_iter([(
                serde_yaml::Value::from(skill_id.as_str()),
                serde_yaml::Value::Mapping(bad_confidence),
            )]))
            .unwrap(),
        ),
        (
            serde_yaml::Value::from("evidence_records"),
            serde_yaml::Value::Sequence(vec![]),
        ),
        (
            serde_yaml::Value::from("required_human_review"),
            serde_yaml::Value::from(true),
        ),
        (
            serde_yaml::Value::from("audit_notes"),
            serde_yaml::Value::Sequence(vec![]),
        ),
    ]))
    .unwrap();
    let response_path = temp.path().join("bad-response.yaml");
    std::fs::write(&response_path, response).unwrap();

    let report_path = temp.path().join("forge-validation-report.yaml");
    let output = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args([
            "forge-validate",
            bundle.to_str().unwrap(),
            "--request",
            handoff.join("forge-request.yaml").to_str().unwrap(),
            "--response",
            response_path.to_str().unwrap(),
            "--report",
            report_path.to_str().unwrap(),
        ])
        .output()
        .unwrap();
    assert!(!output.status.success());
    let stderr = String::from_utf8_lossy(&output.stderr);
    assert!(
        stderr.contains("references missing source invented-source")
            || stderr.contains("must be between 0.0 and 1.0")
            || stderr.contains("total exceeds 1.0"),
        "stderr was: {stderr}"
    );

    let report_text = std::fs::read_to_string(report_path).unwrap();
    let report: serde_yaml::Value = serde_yaml::from_str(&report_text).unwrap();
    assert_eq!(report["request_id"].as_str().unwrap(), request_id);
    assert_eq!(report["pass_type"].as_str().unwrap(), "SkillExpansion");
    assert!(!report["valid"].as_bool().unwrap());
    assert!(report["error_count"].as_u64().unwrap() >= 4);
    let errors = report["errors"]
        .as_sequence()
        .unwrap()
        .iter()
        .map(|value| value.as_str().unwrap())
        .collect::<Vec<_>>()
        .join(
            "
",
        );
    assert!(errors.contains("references missing source invented-source"));
    assert!(errors.contains("confidence.raw must be between 0.0 and 1.0"));
    assert!(errors.contains("evidence_breakdown total exceeds"));
    assert!(errors.contains("confidence update"));
}

#[test]
fn validation_rejects_invalid_stored_forge_history() {
    let temp = tempdir().unwrap();
    let docs = temp.path().join("docs");
    std::fs::create_dir_all(&docs).unwrap();
    std::fs::write(
        docs.join("ops.md"),
        "# Inspect Service\n\nYou should inspect service status before making changes.\n\n```\nsvc status\n```\n\nWarning: never restart production services without approval.\n",
    )
    .unwrap();

    let bundle = temp.path().join("bundle");
    let forged = temp.path().join("forged");
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "compile",
                docs.to_str().unwrap(),
                "--out",
                bundle.to_str().unwrap(),
                "--name",
                "stored-forge-validation",
            ])
            .status()
            .unwrap()
            .success()
    );
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "forge",
                bundle.to_str().unwrap(),
                "--out",
                forged.to_str().unwrap(),
                "--provider",
                "mock",
                "--max-skills",
                "1",
            ])
            .status()
            .unwrap()
            .success()
    );

    let responses_path = forged.join("forge_responses.yaml");
    let responses_text = std::fs::read_to_string(&responses_path).unwrap();
    let mut responses: serde_yaml::Value = serde_yaml::from_str(&responses_text).unwrap();
    responses[0]["modified_items"][0]["confidence"]["raw"] = serde_yaml::Value::from(42.0);
    std::fs::write(&responses_path, serde_yaml::to_string(&responses).unwrap()).unwrap();

    let output = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["validate", forged.to_str().unwrap()])
        .output()
        .unwrap();
    assert!(!output.status.success());
    let combined = format!(
        "{}{}",
        String::from_utf8_lossy(&output.stdout),
        String::from_utf8_lossy(&output.stderr)
    );
    assert!(
        combined.contains("stored Forge response")
            && combined.contains("must be between 0.0 and 1.0"),
        "output was: {combined}"
    );
}

#[test]
fn validation_rejects_stale_forge_summary_artifacts() {
    let temp = tempdir().unwrap();
    let docs = temp.path().join("docs");
    std::fs::create_dir_all(&docs).unwrap();
    std::fs::write(
        docs.join("ops.md"),
        "# Inspect Service\n\nUse service status before changes.\n\n```\nsvc status\n```\n\nWarning: require approval before restart.\n",
    )
    .unwrap();

    let bundle = temp.path().join("bundle");
    let forged = temp.path().join("forged");
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "compile",
                docs.to_str().unwrap(),
                "--out",
                bundle.to_str().unwrap(),
                "--name",
                "stale-forge-summary",
            ])
            .status()
            .unwrap()
            .success()
    );
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "forge",
                bundle.to_str().unwrap(),
                "--out",
                forged.to_str().unwrap(),
                "--provider",
                "mock",
                "--max-skills",
                "1",
            ])
            .status()
            .unwrap()
            .success()
    );

    let summary_path = forged.join("forge_summary.yaml");
    let mut summary: serde_yaml::Value =
        serde_yaml::from_str(&std::fs::read_to_string(&summary_path).unwrap()).unwrap();
    summary["pass_count"] = serde_yaml::Value::from(999);
    std::fs::write(&summary_path, serde_yaml::to_string(&summary).unwrap()).unwrap();

    let output = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["validate", forged.to_str().unwrap()])
        .output()
        .unwrap();
    assert!(!output.status.success());
    let combined = format!(
        "{}{}",
        String::from_utf8_lossy(&output.stdout),
        String::from_utf8_lossy(&output.stderr)
    );
    assert!(
        combined.contains("forge_summary.yaml is stale"),
        "output was: {combined}"
    );
}

#[test]
fn validation_rejects_mismatched_citation_source_and_duplicate_skill_ids() {
    let temp = tempdir().unwrap();
    let docs = temp.path().join("docs");
    std::fs::create_dir_all(&docs).unwrap();
    std::fs::write(
        docs.join("ops.md"),
        "# Inspect Service\n\nYou should inspect service status before making changes.\n\n```\nsvc status\n```\n\nWarning: never restart production services without approval.\n",
    )
    .unwrap();

    let bundle = temp.path().join("bundle");
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "compile",
                docs.to_str().unwrap(),
                "--out",
                bundle.to_str().unwrap(),
                "--name",
                "citation-validation",
            ])
            .status()
            .unwrap()
            .success()
    );

    let mut skill_files: Vec<_> = std::fs::read_dir(bundle.join("skills"))
        .unwrap()
        .map(|entry| entry.unwrap().path())
        .filter(|path| path.extension().and_then(|s| s.to_str()) == Some("yaml"))
        .collect();
    skill_files.sort();
    let first_skill = skill_files.first().expect("compiled skill exists");
    let skill_text = std::fs::read_to_string(first_skill).unwrap();
    let mut duplicate_name = first_skill.file_stem().unwrap().to_os_string();
    duplicate_name.push("-duplicate.yaml");
    std::fs::write(bundle.join("skills").join(duplicate_name), &skill_text).unwrap();

    let section_text = std::fs::read_to_string(bundle.join("sources/sections.yaml")).unwrap();
    let section_id = section_text
        .lines()
        .find_map(|line| {
            let line = line.trim();
            line.strip_prefix("section_id: ")
                .or_else(|| line.strip_prefix("- section_id: "))
        })
        .expect("section id in generated bundle")
        .to_string();

    let bad_skill = format!(
        r#"---
id: forged-unsupported-citation
title: Unsupported citation should fail
summary: Citation source does not match the referenced section.
skill_type: Procedure
scope: TaskLevel
status: Candidate
source_section_ids:
  - {section_id}
citations:
  - citation_id: forged-bad-citation
    source_id: missing-source
    section_id: {section_id}
    excerpt: unsupported
"#
    );
    std::fs::write(
        bundle.join("skills/forged-unsupported-citation.yaml"),
        bad_skill,
    )
    .unwrap();

    let mut first_skill_yaml: serde_yaml::Value = serde_yaml::from_str(&skill_text).unwrap();
    first_skill_yaml["confidence"]["raw"] = serde_yaml::Value::from(1.2);
    first_skill_yaml["evidence_breakdown"]["direct_extraction"] = serde_yaml::Value::from(0.8);
    first_skill_yaml["evidence_breakdown"]["supporting_inference"] = serde_yaml::Value::from(0.4);
    first_skill_yaml["inference_records"] =
        serde_yaml::Value::Sequence(vec![serde_yaml::Value::Mapping({
            let mut m = serde_yaml::Mapping::new();
            m.insert(
                serde_yaml::Value::from("inference_id"),
                serde_yaml::Value::from("bad-inference"),
            );
            m.insert(
                serde_yaml::Value::from("candidate_ids_used"),
                serde_yaml::Value::Sequence(vec![serde_yaml::Value::from("missing-candidate")]),
            );
            m.insert(
                serde_yaml::Value::from("source_refs_used"),
                serde_yaml::Value::Sequence(vec![serde_yaml::Value::from("missing-section")]),
            );
            m.insert(
                serde_yaml::Value::from("reasoning_summary"),
                serde_yaml::Value::from("bad stored inference"),
            );
            m.insert(
                serde_yaml::Value::from("inference_type"),
                serde_yaml::Value::from("Expansion"),
            );
            m.insert(
                serde_yaml::Value::from("evidence_type"),
                serde_yaml::Value::from("SupportingInference"),
            );
            m.insert(
                serde_yaml::Value::from("confidence"),
                serde_yaml::Value::from(-0.1),
            );
            m.insert(
                serde_yaml::Value::from("unsupported_assumptions"),
                serde_yaml::Value::Sequence(vec![]),
            );
            m.insert(
                serde_yaml::Value::from("required_review"),
                serde_yaml::Value::from(true),
            );
            m.insert(
                serde_yaml::Value::from("risk_flags"),
                serde_yaml::Value::Sequence(vec![]),
            );
            m.insert(
                serde_yaml::Value::from("generated_by_agent"),
                serde_yaml::Value::from("test"),
            );
            m.insert(
                serde_yaml::Value::from("created_at"),
                serde_yaml::Value::from("2025-01-01T00:00:00Z"),
            );
            m
        })]);
    std::fs::write(
        first_skill,
        serde_yaml::to_string(&first_skill_yaml).unwrap(),
    )
    .unwrap();

    let output = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["validate", bundle.to_str().unwrap()])
        .output()
        .unwrap();
    assert!(!output.status.success());
    let stdout = String::from_utf8_lossy(&output.stdout);
    assert!(
        stdout.contains("duplicate skill id"),
        "stdout was: {stdout}"
    );
    assert!(
        stdout.contains("references missing source missing-source"),
        "stdout was: {stdout}"
    );
    assert!(
        stdout.contains("confidence.raw must be between 0.0 and 1.0"),
        "stdout was: {stdout}"
    );
    assert!(
        stdout.contains("evidence_breakdown total exceeds 1.0 tolerance"),
        "stdout was: {stdout}"
    );
    assert!(
        stdout
            .contains("inference record bad-inference references missing section missing-section"),
        "stdout was: {stdout}"
    );
    assert!(
        stdout.contains(
            "inference record bad-inference references unknown candidate missing-candidate"
        ),
        "stdout was: {stdout}"
    );
}

#[test]
fn readiness_blocks_unsafe_and_underreviewed_high_risk_publication() {
    let temp = tempdir().unwrap();
    let docs = temp.path().join("docs");
    std::fs::create_dir_all(&docs).unwrap();
    std::fs::write(
        docs.join("deploy.md"),
        "# Deploy Service\n\nUse status first, then deploy only after approval.\n\n```\nsvcctl status demo\nsvcctl deploy demo\n```\n\nWarning: deployment changes external systems and requires rollback.\n",
    )
    .unwrap();

    let bundle = temp.path().join("bundle");
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "compile",
                docs.to_str().unwrap(),
                "--out",
                bundle.to_str().unwrap(),
                "--name",
                "publication-gates",
            ])
            .status()
            .unwrap()
            .success()
    );

    let skill_path = std::fs::read_dir(bundle.join("skills"))
        .unwrap()
        .map(|entry| entry.unwrap().path())
        .find(|path| path.extension().and_then(|s| s.to_str()) == Some("yaml"))
        .unwrap();
    let skill_text = std::fs::read_to_string(&skill_path).unwrap();
    let mut skill_yaml: serde_yaml::Value = serde_yaml::from_str(&skill_text).unwrap();
    skill_yaml["status"] = serde_yaml::Value::from("Reviewed");
    skill_yaml["maturity"] = serde_yaml::Value::from("Level3Verified");
    skill_yaml["confidence"]["human_review"] = serde_yaml::Value::from(0.2);
    skill_yaml["runtime_policy"]["modify_external_systems"] = serde_yaml::Value::from(true);
    skill_yaml["runtime_policy"]["requires_user_approval"] = serde_yaml::Value::from(true);
    skill_yaml["runtime_policy"]["requires_backup_or_rollback"] = serde_yaml::Value::from(true);
    std::fs::write(&skill_path, serde_yaml::to_string(&skill_yaml).unwrap()).unwrap();

    let readiness = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["readiness", bundle.to_str().unwrap()])
        .output()
        .unwrap();
    assert!(readiness.status.success());
    let stdout = String::from_utf8_lossy(&readiness.stdout);
    assert!(stdout.contains("ready: false"), "stdout was: {stdout}");
    assert!(
        stdout.contains("high-risk and requires human-approved Level4+ maturity"),
        "stdout was: {stdout}"
    );

    let registry = temp.path().join("registry");
    let publish = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args([
            "publish",
            bundle.to_str().unwrap(),
            "--registry",
            registry.to_str().unwrap(),
        ])
        .output()
        .unwrap();
    assert!(!publish.status.success());

    skill_yaml["status"] = serde_yaml::Value::from("Unsafe");
    skill_yaml["maturity"] = serde_yaml::Value::from("Level4HumanApproved");
    skill_yaml["confidence"]["human_review"] = serde_yaml::Value::from(0.9);
    std::fs::write(&skill_path, serde_yaml::to_string(&skill_yaml).unwrap()).unwrap();
    let readiness = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["readiness", bundle.to_str().unwrap()])
        .output()
        .unwrap();
    let stdout = String::from_utf8_lossy(&readiness.stdout);
    assert!(
        stdout.contains("unsafe and cannot be published"),
        "stdout was: {stdout}"
    );
}

#[test]
fn agent_pack_does_not_promote_unsafe_archived_or_deprecated_skills() {
    let temp = tempdir().unwrap();
    let docs = temp.path().join("docs");
    std::fs::create_dir_all(&docs).unwrap();
    std::fs::write(
        docs.join("ops.md"),
        "# Inspect Service\n\nUse status before mutation.\n\n```\nsvcctl status demo\n```\n",
    )
    .unwrap();

    let bundle = temp.path().join("bundle");
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "compile",
                docs.to_str().unwrap(),
                "--out",
                bundle.to_str().unwrap(),
                "--name",
                "agent-pack-gates",
            ])
            .status()
            .unwrap()
            .success()
    );

    let skill_path = std::fs::read_dir(bundle.join("skills"))
        .unwrap()
        .map(|entry| entry.unwrap().path())
        .find(|path| path.extension().and_then(|s| s.to_str()) == Some("yaml"))
        .unwrap();
    let skill_text = std::fs::read_to_string(&skill_path).unwrap();
    let mut unsafe_skill: serde_yaml::Value = serde_yaml::from_str(&skill_text).unwrap();
    let unsafe_id = unsafe_skill["id"].as_str().unwrap().to_string();
    unsafe_skill["status"] = serde_yaml::Value::from("Unsafe");
    unsafe_skill["maturity"] = serde_yaml::Value::from("Level6Certified");
    unsafe_skill["tool_requirements"] = serde_yaml::from_str(
        r#"- name: dangerous-tool
  requirement_type: Required
  permission_level: ExternalMutation
  dry_run_available: false
  rollback_required: true
  notes: dangerous tool from unsafe skill
"#,
    )
    .unwrap();
    unsafe_skill["evals"] = serde_yaml::from_str(
        r#"- id: unsafe-eval
  eval_type: Safety
  prompt: Do an unsafe thing.
  expected_behavior: Refuse or escalate.
  negative_cases: []
  safety_cases: []
  safety_notes: []
"#,
    )
    .unwrap();
    std::fs::write(&skill_path, serde_yaml::to_string(&unsafe_skill).unwrap()).unwrap();

    let mut deprecated_skill = unsafe_skill.clone();
    let deprecated_id = format!("{unsafe_id}-deprecated");
    deprecated_skill["id"] = serde_yaml::Value::from(deprecated_id.clone());
    deprecated_skill["status"] = serde_yaml::Value::from("Deprecated");
    std::fs::write(
        bundle.join("skills").join(format!("{deprecated_id}.yaml")),
        serde_yaml::to_string(&deprecated_skill).unwrap(),
    )
    .unwrap();

    let mut reviewed_skill = unsafe_skill.clone();
    let reviewed_id = format!("{unsafe_id}-reviewed");
    reviewed_skill["id"] = serde_yaml::Value::from(reviewed_id.clone());
    reviewed_skill["status"] = serde_yaml::Value::from("Reviewed");
    reviewed_skill["maturity"] = serde_yaml::Value::from("Level3Verified");
    reviewed_skill["tool_requirements"] = serde_yaml::from_str(
        r#"- name: safe-tool
  requirement_type: Optional
  permission_level: ReadOnly
  dry_run_available: true
  rollback_required: false
  notes: safe tool from reviewed skill
"#,
    )
    .unwrap();
    reviewed_skill["evals"] = serde_yaml::from_str(
        r#"- id: reviewed-eval
  eval_type: Positive
  prompt: Do a safe reviewed thing.
  expected_behavior: Answer with citations.
  negative_cases: []
  safety_cases: []
  safety_notes: []
"#,
    )
    .unwrap();
    std::fs::write(
        bundle.join("skills").join(format!("{reviewed_id}.yaml")),
        serde_yaml::to_string(&reviewed_skill).unwrap(),
    )
    .unwrap();

    let out = temp.path().join("agent");
    let build_report_path = temp.path().join("agent-build-report.yaml");
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "build-agent-pack",
                bundle.to_str().unwrap(),
                "--agent",
                "Technical Documentation Agent",
                "--out",
                out.to_str().unwrap(),
                "--report",
                build_report_path.to_str().unwrap(),
            ])
            .status()
            .unwrap()
            .success()
    );
    let pack = std::fs::read_to_string(out.join("agent-pack.yaml")).unwrap();
    let pack_yaml: serde_yaml::Value = serde_yaml::from_str(&pack).unwrap();
    let required = pack_yaml["required_skills"].as_sequence().unwrap();
    let forbidden = pack_yaml["forbidden_skills"].as_sequence().unwrap();
    assert!(required.iter().any(|v| v.as_str() == Some(&reviewed_id)));
    assert!(!required.iter().any(|v| v.as_str() == Some(&unsafe_id)));
    assert!(!required.iter().any(|v| v.as_str() == Some(&deprecated_id)));
    assert!(forbidden.iter().any(|v| v.as_str() == Some(&unsafe_id)));
    assert!(forbidden.iter().any(|v| v.as_str() == Some(&deprecated_id)));
    let tool_permissions = pack_yaml["tool_permissions"].as_sequence().unwrap();
    assert!(
        tool_permissions
            .iter()
            .any(|v| v.as_str().is_some_and(|s| s.contains("safe-tool")))
    );
    assert!(
        !tool_permissions
            .iter()
            .any(|v| v.as_str().is_some_and(|s| s.contains("dangerous-tool")))
    );
    let evals = pack_yaml["evals"].as_sequence().unwrap();
    assert!(
        evals
            .iter()
            .any(|v| v["id"].as_str() == Some("reviewed-eval"))
    );
    assert!(
        !evals
            .iter()
            .any(|v| v["id"].as_str() == Some("unsafe-eval"))
    );
    let eval_status = &pack_yaml["eval_status"];
    assert_eq!(eval_status["selected_skill_count"].as_i64(), Some(1));
    assert_eq!(eval_status["total_eval_cases"].as_i64(), Some(1));
    assert_eq!(eval_status["skills_without_evals"].as_i64(), Some(0));
    assert_eq!(eval_status["safety_eval_count"].as_i64(), Some(0));
    assert_eq!(
        eval_status["skill_eval_counts"][&reviewed_id].as_i64(),
        Some(1)
    );
    assert_eq!(eval_status["skill_eval_counts"][&unsafe_id].as_i64(), None);
    let eval_warnings = eval_status["warnings"].as_sequence().unwrap();
    assert!(eval_warnings.iter().any(|v| {
        v.as_str()
            .is_some_and(|s| s.contains("missing routing eval"))
    }));
    assert!(eval_warnings.iter().any(|v| {
        v.as_str()
            .is_some_and(|s| s.contains("missing source-grounding eval"))
    }));
    let pack_readiness = &pack_yaml["pack_readiness"];
    assert_eq!(pack_readiness["selected_skill_count"].as_i64(), Some(1));
    assert_eq!(pack_readiness["required_skill_count"].as_i64(), Some(1));
    assert_eq!(pack_readiness["forbidden_skill_count"].as_i64(), Some(2));
    assert_eq!(pack_readiness["evals_passed"].as_bool(), Some(true));
    assert_eq!(
        pack_readiness["ready_for_runtime_use"].as_bool(),
        Some(true)
    );
    assert_eq!(
        pack_readiness["ready_for_default_use"].as_bool(),
        Some(false)
    );
    let readiness_warnings = pack_readiness["warnings"].as_sequence().unwrap();
    assert!(readiness_warnings.iter().any(|v| {
        v.as_str()
            .is_some_and(|s| s.contains("lacks routing eval coverage"))
    }));
    assert!(readiness_warnings.iter().any(|v| {
        v.as_str()
            .is_some_and(|s| s.contains("lacks source-grounding eval coverage"))
    }));

    let manifest_path = out.join("agent-pack-manifest.yaml");
    let manifest_md_path = out.join("agent-pack-manifest.md");
    assert!(manifest_path.exists());
    assert!(manifest_md_path.exists());
    let manifest: serde_yaml::Value =
        serde_yaml::from_str(&std::fs::read_to_string(&manifest_path).unwrap()).unwrap();
    assert!(
        manifest["pack_id"]
            .as_str()
            .is_some_and(|s| s.starts_with("agent-pack-")),
        "{manifest:?}"
    );
    assert_eq!(
        manifest["agent_pack_file"].as_str(),
        Some("agent-pack.yaml")
    );
    assert_eq!(
        manifest["manifest_file"].as_str(),
        Some("agent-pack-manifest.yaml")
    );
    assert_eq!(
        manifest["markdown_file"].as_str(),
        Some("agent-pack-manifest.md")
    );
    assert_eq!(manifest["selected_skill_count"].as_i64(), Some(1));
    assert_eq!(manifest["required_skill_count"].as_i64(), Some(1));
    assert_eq!(manifest["forbidden_skill_count"].as_i64(), Some(2));
    assert_eq!(manifest["tool_permission_count"].as_i64(), Some(1));
    assert_eq!(manifest["eval_case_count"].as_i64(), Some(1));
    assert_eq!(manifest["ready_for_runtime_use"].as_bool(), Some(true));
    assert_eq!(manifest["ready_for_default_use"].as_bool(), Some(false));
    assert_eq!(manifest["evals_passed"].as_bool(), Some(true));
    let manifest_required = manifest["required_skill_ids"].as_sequence().unwrap();
    let manifest_forbidden = manifest["forbidden_skill_ids"].as_sequence().unwrap();
    assert!(
        manifest_required
            .iter()
            .any(|v| v.as_str() == Some(&reviewed_id))
    );
    assert!(
        manifest_forbidden
            .iter()
            .any(|v| v.as_str() == Some(&unsafe_id))
    );
    assert!(
        manifest_forbidden
            .iter()
            .any(|v| v.as_str() == Some(&deprecated_id))
    );
    let manifest_tools = manifest["tool_permissions"].as_sequence().unwrap();
    assert!(
        manifest_tools
            .iter()
            .any(|v| v.as_str().is_some_and(|s| s.contains("safe-tool")))
    );
    assert!(
        !manifest_tools
            .iter()
            .any(|v| v.as_str().is_some_and(|s| s.contains("dangerous-tool")))
    );
    let manifest_files = manifest["files"].as_sequence().unwrap();
    assert!(
        manifest_files
            .iter()
            .any(|v| v.as_str() == Some("agent-pack.yaml"))
    );
    assert!(
        manifest_files
            .iter()
            .any(|v| v.as_str() == Some("agent-pack-manifest.yaml"))
    );
    assert!(
        manifest_files
            .iter()
            .any(|v| v.as_str() == Some("agent-pack-manifest.md"))
    );
    let manifest_md = std::fs::read_to_string(manifest_md_path).unwrap();
    assert!(
        manifest_md.contains("# Agent Pack Manifest"),
        "{manifest_md}"
    );
    assert!(manifest_md.contains("## Tool Permissions"), "{manifest_md}");
    assert!(manifest_md.contains("safe-tool"), "{manifest_md}");
    assert!(!manifest_md.contains("dangerous-tool"), "{manifest_md}");

    let verify = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["verify-agent-pack", out.to_str().unwrap()])
        .output()
        .unwrap();
    assert!(verify.status.success());
    let verify_stdout = String::from_utf8_lossy(&verify.stdout);
    assert!(verify_stdout.contains("valid: true"), "{verify_stdout}");

    std::fs::write(
        &manifest_path,
        manifest_yaml_with_count(&manifest, "selected_skill_count", 99),
    )
    .unwrap();
    let verify_bad = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["verify-agent-pack", out.to_str().unwrap()])
        .output()
        .unwrap();
    assert!(!verify_bad.status.success());
    let bad_stdout = String::from_utf8_lossy(&verify_bad.stdout);
    assert!(bad_stdout.contains("valid: false"), "{bad_stdout}");
    assert!(
        bad_stdout.contains("agent-pack-manifest.yaml is stale"),
        "{bad_stdout}"
    );
    let build_report: serde_yaml::Value =
        serde_yaml::from_str(&std::fs::read_to_string(&build_report_path).unwrap()).unwrap();
    assert!(
        build_report["build_id"]
            .as_str()
            .is_some_and(|id| id.starts_with("agent-pack-build-")),
        "{build_report:?}"
    );
    assert_eq!(
        build_report["agent_name"].as_str(),
        Some("Technical Documentation Agent")
    );
    assert_eq!(build_report["selected_skill_count"].as_i64(), Some(1));
    assert_eq!(build_report["required_skill_count"].as_i64(), Some(1));
    assert_eq!(build_report["forbidden_skill_count"].as_i64(), Some(2));
    assert_eq!(build_report["omitted_skill_count"].as_i64(), Some(2));
    assert_eq!(build_report["tool_permission_count"].as_i64(), Some(1));
    assert_eq!(build_report["eval_case_count"].as_i64(), Some(1));
    assert_eq!(build_report["verification_valid"].as_bool(), Some(true));
    assert_eq!(build_report["ready_for_runtime_use"].as_bool(), Some(true));
    assert_eq!(build_report["ready_for_default_use"].as_bool(), Some(false));
    assert!(
        build_report["selected_skill_ids"]
            .as_sequence()
            .unwrap()
            .iter()
            .any(|v| v.as_str() == Some(&reviewed_id))
    );
    assert!(
        build_report["forbidden_skill_ids"]
            .as_sequence()
            .unwrap()
            .iter()
            .any(|v| v.as_str() == Some(&unsafe_id))
    );
    assert!(
        build_report["omitted_skill_ids"]
            .as_sequence()
            .unwrap()
            .iter()
            .any(|v| v.as_str() == Some(&deprecated_id))
    );
    assert!(
        build_report["tool_permissions"]
            .as_sequence()
            .unwrap()
            .iter()
            .any(|v| v.as_str().is_some_and(|s| s.contains("safe-tool")))
    );
    assert!(
        build_report["verification_errors"]
            .as_sequence()
            .unwrap()
            .is_empty()
    );
}

fn manifest_yaml_with_count(manifest: &serde_yaml::Value, key: &str, value: i64) -> String {
    let mut manifest = manifest.clone();
    manifest[key] = serde_yaml::Value::Number(value.into());
    serde_yaml::to_string(&manifest).unwrap()
}

#[test]
fn readiness_enforces_source_rights_and_secret_scan_policy() {
    let temp = tempdir().unwrap();
    let docs = temp.path().join("docs");
    std::fs::create_dir_all(&docs).unwrap();
    std::fs::write(
        docs.join("ops.md"),
        "# Inspect Service

Use status before mutation.

```
svcctl status demo
```
",
    )
    .unwrap();

    let bundle = temp.path().join("bundle");
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "compile",
                docs.to_str().unwrap(),
                "--out",
                bundle.to_str().unwrap(),
                "--name",
                "source-policy-gates",
            ])
            .status()
            .unwrap()
            .success()
    );

    let package_path = bundle.join("package.yaml");
    let mut package_yaml: serde_yaml::Value =
        serde_yaml::from_str(&std::fs::read_to_string(&package_path).unwrap()).unwrap();
    package_yaml["compatibility"]["publish_visibility"] = serde_yaml::Value::from("public");
    std::fs::write(&package_path, serde_yaml::to_string(&package_yaml).unwrap()).unwrap();

    let sources_path = bundle.join("sources/index.yaml");
    let mut sources_yaml: serde_yaml::Value =
        serde_yaml::from_str(&std::fs::read_to_string(&sources_path).unwrap()).unwrap();
    let sources = sources_yaml.as_sequence_mut().unwrap();
    sources[0]["export_policy"] = serde_yaml::Value::from("PrivateOnly");
    sources[0]["permission_status"] =
        serde_yaml::from_str("!Blocked license forbids derived export").unwrap();
    sources[0]["secret_scan_status"] = serde_yaml::from_str(
        "!Findings
- token redacted",
    )
    .unwrap();
    std::fs::write(&sources_path, serde_yaml::to_string(&sources_yaml).unwrap()).unwrap();

    let validate = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["validate", bundle.to_str().unwrap()])
        .output()
        .unwrap();
    assert!(!validate.status.success());
    let validate_stdout = String::from_utf8_lossy(&validate.stdout);
    assert!(
        validate_stdout.contains("permission blocked"),
        "stdout was: {validate_stdout}"
    );
    assert!(
        validate_stdout.contains("unresolved secret-scan findings"),
        "stdout was: {validate_stdout}"
    );

    let readiness = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["readiness", bundle.to_str().unwrap()])
        .output()
        .unwrap();
    assert!(readiness.status.success());
    let readiness_stdout = String::from_utf8_lossy(&readiness.stdout);
    assert!(
        readiness_stdout.contains("ready: false"),
        "stdout was: {readiness_stdout}"
    );
    assert!(
        readiness_stdout.contains("export policy does not allow public publication"),
        "stdout was: {readiness_stdout}"
    );
}

#[test]
fn telemetry_improvement_proposals_are_deterministic_risk_specific_and_indexed() {
    let temp = tempdir().unwrap();
    let docs = temp.path().join("docs");
    std::fs::create_dir_all(&docs).unwrap();
    std::fs::write(
        docs.join("ops.md"),
        "# Restart Service\n\nUse status before mutation.\n\n```\nsvcctl status demo\nsvcctl restart demo\n```\n\nWarning: restart requires approval and rollback plan.\n",
    )
    .unwrap();

    let bundle = temp.path().join("bundle");
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "compile",
                docs.to_str().unwrap(),
                "--out",
                bundle.to_str().unwrap(),
                "--name",
                "telemetry-proposals",
            ])
            .status()
            .unwrap()
            .success()
    );

    let skill_path = std::fs::read_dir(bundle.join("skills"))
        .unwrap()
        .next()
        .unwrap()
        .unwrap()
        .path();
    let mut skill_yaml: serde_yaml::Value =
        serde_yaml::from_str(&std::fs::read_to_string(&skill_path).unwrap()).unwrap();
    skill_yaml["confidence"]["routing"] = serde_yaml::Value::from(0.2);
    skill_yaml["confidence"]["runtime"] = serde_yaml::Value::from(0.0);
    skill_yaml["confidence"]["human_review"] = serde_yaml::Value::from(0.0);
    skill_yaml["evidence_breakdown"]["direct_extraction"] = serde_yaml::Value::from(0.70);
    skill_yaml["evidence_breakdown"]["speculative_candidate"] = serde_yaml::Value::from(0.30);
    skill_yaml["runtime_policy"]["modify_files"] = serde_yaml::Value::from(true);
    skill_yaml["runtime_policy"]["requires_user_approval"] = serde_yaml::Value::from(true);
    skill_yaml["tool_requirements"] = serde_yaml::from_str(
        r#"
- name: svcctl
  requirement_type: Mutating
  permission_level: FileMutation
  dry_run_available: false
  rollback_required: true
"#,
    )
    .unwrap();
    skill_yaml["evals"] = serde_yaml::Value::Sequence(vec![]);
    std::fs::write(&skill_path, serde_yaml::to_string(&skill_yaml).unwrap()).unwrap();

    let out = temp.path().join("improvements");
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "improve-from-telemetry",
                bundle.to_str().unwrap(),
                "--out",
                out.to_str().unwrap(),
            ])
            .status()
            .unwrap()
            .success()
    );

    let index = std::fs::read_to_string(out.join("index.yaml")).unwrap();
    assert!(index.contains("low-routing-confidence"));
    assert!(index.contains("missing-evals"));
    assert!(index.contains("speculative-evidence"));
    assert!(index.contains("operational-review-required"));

    let low_routing = std::fs::read_to_string(
        out.join(
            index
                .lines()
                .find(|line| line.contains("low-routing-confidence"))
                .unwrap()
                .trim_start_matches("- "),
        ),
    )
    .unwrap();
    assert!(low_routing.contains("static-analysis:routing-confidence"));
    assert!(low_routing.contains("requires_review: true"));

    let operational = std::fs::read_to_string(
        out.join(
            index
                .lines()
                .find(|line| line.contains("operational-review-required"))
                .unwrap()
                .trim_start_matches("- "),
        ),
    )
    .unwrap();
    assert!(operational.contains("risk: High"));
    assert!(operational.contains("approval/rollback guardrails"));

    let mut proposal_files: Vec<_> = std::fs::read_dir(&out)
        .unwrap()
        .map(|entry| entry.unwrap().file_name().to_string_lossy().to_string())
        .filter(|name| name.starts_with("proposal-"))
        .collect();
    proposal_files.sort();
    assert!(
        proposal_files
            .iter()
            .all(|name| !name.contains("proposal-") || !name.contains("00000000"))
    );
    assert_eq!(proposal_files.len(), 4);
}

#[test]
fn agent_proposals_are_deterministic_and_exclude_forbidden_skills() {
    let temp = tempdir().unwrap();
    let docs = temp.path().join("docs");
    std::fs::create_dir_all(&docs).unwrap();
    std::fs::write(
        docs.join("ops.md"),
        "# Inspect Service\n\nUse status before mutation.\n\n```\nsvcctl status demo\n```\n",
    )
    .unwrap();

    let bundle = temp.path().join("bundle");
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "compile",
                docs.to_str().unwrap(),
                "--out",
                bundle.to_str().unwrap(),
                "--name",
                "agent-proposal-gates",
            ])
            .status()
            .unwrap()
            .success()
    );

    let skill_path = std::fs::read_dir(bundle.join("skills"))
        .unwrap()
        .map(|entry| entry.unwrap().path())
        .find(|path| path.extension().and_then(|s| s.to_str()) == Some("yaml"))
        .unwrap();
    let skill_text = std::fs::read_to_string(&skill_path).unwrap();
    let mut base_skill: serde_yaml::Value = serde_yaml::from_str(&skill_text).unwrap();
    let base_id = base_skill["id"].as_str().unwrap().to_string();
    base_skill["status"] = serde_yaml::Value::from("Reviewed");
    base_skill["maturity"] = serde_yaml::Value::from("Level3Verified");
    base_skill["role_suitability"] = serde_yaml::from_str(
        r#"
- role: Technical Documentation Agent
  suitability: 0.9
  rationale: Source-grounded operational support.
"#,
    )
    .unwrap();
    base_skill["tool_requirements"] = serde_yaml::from_str(
        r#"
- name: safe-tool
  requirement_type: Required
  permission_level: ReadOnly
  dry_run_available: true
  rollback_required: false
"#,
    )
    .unwrap();
    std::fs::write(&skill_path, serde_yaml::to_string(&base_skill).unwrap()).unwrap();

    let mut unsafe_skill = base_skill.clone();
    let unsafe_id = format!("{base_id}-unsafe");
    unsafe_skill["id"] = serde_yaml::Value::from(unsafe_id.clone());
    unsafe_skill["status"] = serde_yaml::Value::from("Unsafe");
    unsafe_skill["tool_requirements"] = serde_yaml::from_str(
        r#"
- name: dangerous-tool
  requirement_type: Dangerous
  permission_level: Dangerous
  dry_run_available: false
  rollback_required: true
"#,
    )
    .unwrap();
    std::fs::write(
        bundle.join("skills").join(format!("{unsafe_id}.yaml")),
        serde_yaml::to_string(&unsafe_skill).unwrap(),
    )
    .unwrap();

    let out_a = temp.path().join("agents-a");
    let out_b = temp.path().join("agents-b");
    for out in [&out_a, &out_b] {
        assert!(
            Command::new(env!("CARGO_BIN_EXE_skiller"))
                .args([
                    "propose-agents",
                    bundle.to_str().unwrap(),
                    "--out",
                    out.to_str().unwrap(),
                ])
                .status()
                .unwrap()
                .success()
        );
    }

    let proposal_a =
        std::fs::read_to_string(out_a.join("technical-documentation-agent.yaml")).unwrap();
    let proposal_b =
        std::fs::read_to_string(out_b.join("technical-documentation-agent.yaml")).unwrap();
    assert_eq!(proposal_a, proposal_b);
    let proposal: serde_yaml::Value = serde_yaml::from_str(&proposal_a).unwrap();
    let recommended = proposal["recommended_skills"].as_sequence().unwrap();
    let required_tools = proposal["required_tools"].as_sequence().unwrap();
    assert!(recommended.iter().any(|v| v.as_str() == Some(&base_id)));
    assert!(!recommended.iter().any(|v| v.as_str() == Some(&unsafe_id)));
    assert!(
        required_tools
            .iter()
            .any(|v| v.as_str() == Some("safe-tool"))
    );
    assert!(
        !required_tools
            .iter()
            .any(|v| v.as_str() == Some("dangerous-tool"))
    );
}

#[test]
fn agent_role_selection_is_specific_not_small_bundle_broad() {
    let temp = tempdir().unwrap();
    let bundle = temp.path().join("bundle");
    std::fs::create_dir_all(bundle.join("skills")).unwrap();
    std::fs::create_dir_all(bundle.join("sources")).unwrap();
    std::fs::create_dir_all(bundle.join("graph")).unwrap();
    std::fs::create_dir_all(bundle.join("audit")).unwrap();

    std::fs::write(
        bundle.join("package.yaml"),
        r#"---
bundle_id: bundle-role-specific
name: role-specific
version: 0.1.0
domain: kubernetes
source_corpus: []
review_status: Reviewed
publish_status: Unpublished
compatibility: {}
created_at: 2025-01-01T00:00:00Z
"#,
    )
    .unwrap();
    std::fs::write(bundle.join("sources/index.yaml"), "[]\n").unwrap();
    std::fs::write(bundle.join("sources/sections.yaml"), "[]\n").unwrap();
    std::fs::write(bundle.join("graph/concepts.yaml"), "[]\n").unwrap();
    std::fs::write(bundle.join("graph/dependencies.yaml"), "[]\n").unwrap();
    std::fs::write(bundle.join("audit/events.yaml"), "[]\n").unwrap();
    std::fs::write(bundle.join("candidates.yaml"), "[]\n").unwrap();

    std::fs::write(
        bundle.join("skills/cluster-diagnostic.yaml"),
        r#"---
id: skill-cluster-diagnostic
title: Diagnose Kubernetes CrashLoopBackOff
summary: Diagnose Kubernetes pods with kubectl logs, events, and status.
skill_type: Diagnostic
scope: TaskLevel
status: Reviewed
maturity: Level3Verified
domain: kubernetes
role_suitability:
  - role: Cluster Diagnostic Agent
    suitability: 0.95
    rationale: Focused on live cluster diagnostics.
tool_requirements:
  - name: kubectl
    requirement_type: ReadOnly
    permission_level: ReadOnly
    dry_run_available: true
    rollback_required: false
evals:
  - id: eval-cluster-routing
    prompt: Route a CrashLoopBackOff diagnosis task.
    expected_behavior: Selects cluster diagnostic skill.
    eval_type: Routing
    safety_notes: []
"#,
    )
    .unwrap();
    std::fs::write(
        bundle.join("skills/manifest-review.yaml"),
        r#"---
id: skill-manifest-review
title: Review Kubernetes deployment manifest
summary: Review manifests for resource requests, probes, labels, and unsafe configuration.
skill_type: Review
scope: TaskLevel
status: Reviewed
maturity: Level3Verified
domain: kubernetes
role_suitability:
  - role: Manifest Review Agent
    suitability: 0.95
    rationale: Focused on static manifest review.
tool_requirements:
  - name: kubeconform
    requirement_type: ReadOnly
    permission_level: ReadOnly
    dry_run_available: true
    rollback_required: false
evals:
  - id: eval-manifest-routing
    prompt: Route a deployment manifest review task.
    expected_behavior: Selects manifest review skill.
    eval_type: Routing
    safety_notes: []
"#,
    )
    .unwrap();

    let proposals_dir = temp.path().join("proposals");
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "propose-agents",
                bundle.to_str().unwrap(),
                "--out",
                proposals_dir.to_str().unwrap(),
            ])
            .status()
            .unwrap()
            .success()
    );

    let cluster =
        std::fs::read_to_string(proposals_dir.join("cluster-diagnostic-agent.yaml")).unwrap();
    assert!(cluster.contains("skill-cluster-diagnostic"), "{cluster}");
    assert!(!cluster.contains("skill-manifest-review"), "{cluster}");
    assert!(cluster.contains("kubectl"), "{cluster}");
    assert!(!cluster.contains("kubeconform"), "{cluster}");
    assert!(cluster.contains("selection_rationale:"), "{cluster}");
    assert!(cluster.contains("proposal_readiness:"), "{cluster}");
    assert!(cluster.contains("ready_for_packaging: true"), "{cluster}");
    assert!(
        cluster.contains("ready_for_default_use_candidate: false"),
        "{cluster}"
    );
    assert!(cluster.contains("selected_skill_count: 1"), "{cluster}");
    assert!(cluster.contains("reviewed_skill_count: 1"), "{cluster}");
    assert!(cluster.contains("routing_eval_count: 1"), "{cluster}");
    assert!(
        cluster.contains("proposal lacks source-grounding eval coverage"),
        "{cluster}"
    );
    assert!(cluster.contains("score:"), "{cluster}");
    assert!(
        cluster.contains("exact role suitability match 'Cluster Diagnostic Agent'"),
        "{cluster}"
    );
    assert!(
        cluster.contains("reviewed skill quality bonus"),
        "{cluster}"
    );

    let proposal_index =
        std::fs::read_to_string(proposals_dir.join("agent-proposals-index.yaml")).unwrap();
    assert!(
        proposal_index.contains("proposal_count: 2"),
        "{proposal_index}"
    );
    assert!(
        proposal_index.contains("ready_for_packaging_count: 2"),
        "{proposal_index}"
    );
    assert!(
        proposal_index.contains("default_use_candidate_count: 0"),
        "{proposal_index}"
    );
    assert!(
        proposal_index.contains("blocked_proposal_count: 0"),
        "{proposal_index}"
    );
    assert!(
        proposal_index.contains("warning_count:"),
        "{proposal_index}"
    );
    assert!(
        proposal_index.contains("cluster-diagnostic-agent.yaml"),
        "{proposal_index}"
    );
    assert!(
        proposal_index.contains("manifest-review-agent.yaml"),
        "{proposal_index}"
    );
    assert!(proposal_index.contains("kubectl"), "{proposal_index}");
    assert!(proposal_index.contains("kubeconform"), "{proposal_index}");

    let verify = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["verify-agent-proposals", proposals_dir.to_str().unwrap()])
        .output()
        .unwrap();
    assert!(
        verify.status.success(),
        "stdout={} stderr={}",
        String::from_utf8_lossy(&verify.stdout),
        String::from_utf8_lossy(&verify.stderr)
    );
    let verify_stdout = String::from_utf8_lossy(&verify.stdout);
    assert!(verify_stdout.contains("valid: true"), "{verify_stdout}");
    assert!(
        verify_stdout.contains("proposal_count: 2"),
        "{verify_stdout}"
    );

    let proposal_index_md =
        std::fs::read_to_string(proposals_dir.join("agent-proposals-index.md")).unwrap();
    assert!(
        proposal_index_md.contains("# Agent Proposal Index"),
        "{proposal_index_md}"
    );
    assert!(
        proposal_index_md.contains("## Proposals"),
        "{proposal_index_md}"
    );
    assert!(
        proposal_index_md.contains("### Cluster Diagnostic Agent"),
        "{proposal_index_md}"
    );
    assert!(
        proposal_index_md.contains("### Manifest Review Agent"),
        "{proposal_index_md}"
    );

    let manifest =
        std::fs::read_to_string(proposals_dir.join("manifest-review-agent.yaml")).unwrap();
    assert!(manifest.contains("skill-manifest-review"), "{manifest}");
    assert!(!manifest.contains("skill-cluster-diagnostic"), "{manifest}");
    assert!(manifest.contains("kubeconform"), "{manifest}");
    assert!(!manifest.contains("kubectl"), "{manifest}");
    assert!(manifest.contains("selection_rationale:"), "{manifest}");
    assert!(manifest.contains("proposal_readiness:"), "{manifest}");
    assert!(manifest.contains("ready_for_packaging: true"), "{manifest}");
    assert!(
        manifest.contains("ready_for_default_use_candidate: false"),
        "{manifest}"
    );
    assert!(
        manifest.contains("exact role suitability match 'Manifest Review Agent'"),
        "{manifest}"
    );

    std::fs::write(
        proposals_dir.join("agent-proposals-index.yaml"),
        proposal_index.replace("selected_skill_count: 1", "selected_skill_count: 99"),
    )
    .unwrap();
    let verify = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["verify-agent-proposals", proposals_dir.to_str().unwrap()])
        .output()
        .unwrap();
    assert!(!verify.status.success());
    let verify_stdout = String::from_utf8_lossy(&verify.stdout);
    assert!(verify_stdout.contains("valid: false"), "{verify_stdout}");
    assert!(
        verify_stdout.contains("agent-proposals-index.yaml is stale")
            || verify_stdout.contains("selected_skill_count mismatch"),
        "{verify_stdout}"
    );
    std::fs::write(
        proposals_dir.join("agent-proposals-index.yaml"),
        &proposal_index,
    )
    .unwrap();

    let cluster_pack = temp.path().join("cluster-pack");
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "build-agent-pack",
                bundle.to_str().unwrap(),
                "--agent",
                "Cluster Diagnostic Agent",
                "--out",
                cluster_pack.to_str().unwrap(),
            ])
            .status()
            .unwrap()
            .success()
    );
    let pack = std::fs::read_to_string(cluster_pack.join("agent-pack.yaml")).unwrap();
    assert!(pack.contains("skill-cluster-diagnostic"), "{pack}");
    assert!(pack.contains("skill-manifest-review"), "{pack}");
    assert!(
        pack.contains("omitted because it did not match the requested agent role"),
        "{pack}"
    );
    assert!(pack.contains("kubectl:ReadOnly"), "{pack}");
    assert!(!pack.contains("kubeconform:ReadOnly"), "{pack}");
    assert!(pack.contains("selection_report:"), "{pack}");
    assert!(pack.contains("selected_skill_count: 1"), "{pack}");
    assert!(pack.contains("omitted_skills:"), "{pack}");
    assert!(pack.contains("pack_readiness:"), "{pack}");
    assert!(pack.contains("ready_for_runtime_use: true"), "{pack}");
    assert!(pack.contains("ready_for_default_use: false"), "{pack}");
    assert!(
        pack.contains("agent pack lacks source-grounding eval coverage"),
        "{pack}"
    );
    assert!(
        pack.contains("exact role suitability match 'Cluster Diagnostic Agent'"),
        "{pack}"
    );

    let summary_path = temp.path().join("agent-builder-summary.yaml");
    let summary = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args([
            "agent-builder-summary",
            "--proposals",
            proposals_dir.to_str().unwrap(),
            "--pack",
            cluster_pack.to_str().unwrap(),
            "--out",
            summary_path.to_str().unwrap(),
        ])
        .output()
        .unwrap();
    assert!(
        summary.status.success(),
        "stdout={} stderr={}",
        String::from_utf8_lossy(&summary.stdout),
        String::from_utf8_lossy(&summary.stderr)
    );
    let summary_yaml = std::fs::read_to_string(&summary_path).unwrap();
    assert!(
        summary_yaml.contains("summary_id: agent-builder-summary-"),
        "{summary_yaml}"
    );
    assert!(summary_yaml.contains("valid: true"), "{summary_yaml}");
    assert!(summary_yaml.contains("proposal_count: 2"), "{summary_yaml}");
    assert!(summary_yaml.contains("pack_count: 1"), "{summary_yaml}");
    assert!(
        summary_yaml.contains("ready_for_packaging_count: 2"),
        "{summary_yaml}"
    );
    assert!(
        summary_yaml.contains("runtime_ready_pack_count: 1"),
        "{summary_yaml}"
    );
    assert!(
        summary_yaml.contains("Cluster Diagnostic Agent"),
        "{summary_yaml}"
    );
    assert!(summary_yaml.contains("kubectl:ReadOnly"), "{summary_yaml}");
    let summary_md = std::fs::read_to_string(summary_path.with_extension("md")).unwrap();
    assert!(
        summary_md.contains("# Agent Builder Summary")
            && summary_md.contains("## Proposals")
            && summary_md.contains("## Agent Packs"),
        "{summary_md}"
    );

    let pack_manifest =
        std::fs::read_to_string(cluster_pack.join("agent-pack-manifest.yaml")).unwrap();
    std::fs::write(
        cluster_pack.join("agent-pack-manifest.yaml"),
        pack_manifest.replace("selected_skill_count: 1", "selected_skill_count: 99"),
    )
    .unwrap();
    let invalid_summary_path = temp.path().join("agent-builder-summary-invalid.yaml");
    let invalid_summary = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args([
            "agent-builder-summary",
            "--proposals",
            proposals_dir.to_str().unwrap(),
            "--pack",
            cluster_pack.to_str().unwrap(),
            "--out",
            invalid_summary_path.to_str().unwrap(),
        ])
        .output()
        .unwrap();
    assert!(!invalid_summary.status.success());
    let invalid_yaml = std::fs::read_to_string(&invalid_summary_path).unwrap();
    assert!(invalid_yaml.contains("valid: false"), "{invalid_yaml}");
    assert!(
        invalid_yaml.contains("agent-pack-manifest.yaml is stale")
            || invalid_yaml.contains("agent-pack-manifest.yaml is stale or does not match"),
        "{invalid_yaml}"
    );

    // Restore the pack manifest, then build a directory-wide artifact index that
    // discovers proposal indexes, agent packs, build reports, and summaries.
    std::fs::write(cluster_pack.join("agent-pack-manifest.yaml"), pack_manifest).unwrap();
    let build_report = temp.path().join("cluster-pack-build-report.yaml");
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "build-agent-pack",
                bundle.to_str().unwrap(),
                "--agent",
                "Cluster Diagnostic Agent",
                "--out",
                cluster_pack.to_str().unwrap(),
                "--report",
                build_report.to_str().unwrap(),
            ])
            .status()
            .unwrap()
            .success()
    );
    let artifact_index = temp.path().join("agent-artifacts.yaml");
    let artifact = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args([
            "agent-artifact-index",
            temp.path().to_str().unwrap(),
            "--out",
            artifact_index.to_str().unwrap(),
        ])
        .output()
        .unwrap();
    assert!(
        artifact.status.success(),
        "stdout={} stderr={}",
        String::from_utf8_lossy(&artifact.stdout),
        String::from_utf8_lossy(&artifact.stderr)
    );
    let artifact_yaml = std::fs::read_to_string(&artifact_index).unwrap();
    assert!(
        artifact_yaml.contains("index_id: agent-artifacts-"),
        "{artifact_yaml}"
    );
    assert!(artifact_yaml.contains("valid: true"), "{artifact_yaml}");
    assert!(
        artifact_yaml.contains("proposal_directory_count: 1"),
        "{artifact_yaml}"
    );
    assert!(
        artifact_yaml.contains("proposal_count: 2"),
        "{artifact_yaml}"
    );
    assert!(
        artifact_yaml.contains("pack_directory_count: 1"),
        "{artifact_yaml}"
    );
    assert!(
        artifact_yaml.contains("summary_count: 1"),
        "{artifact_yaml}"
    );
    assert!(
        artifact_yaml.contains("build_report_count: 1"),
        "{artifact_yaml}"
    );
    assert!(
        artifact_yaml.contains("Cluster Diagnostic Agent"),
        "{artifact_yaml}"
    );
    assert!(artifact_yaml.contains("kubectl"), "{artifact_yaml}");
    let artifact_md = std::fs::read_to_string(artifact_index.with_extension("md")).unwrap();
    assert!(
        artifact_md.contains("# Agent Artifact Index"),
        "{artifact_md}"
    );
    assert!(
        artifact_md.contains("## Proposal Directories"),
        "{artifact_md}"
    );
    assert!(artifact_md.contains("## Agent Packs"), "{artifact_md}");

    // Corrupting an indexed pack artifact should make the artifact index invalid
    // while still writing machine-readable diagnostics.
    let restored_pack_manifest =
        std::fs::read_to_string(cluster_pack.join("agent-pack-manifest.yaml")).unwrap();
    std::fs::write(
        cluster_pack.join("agent-pack-manifest.yaml"),
        "stale: true
",
    )
    .unwrap();
    let bad_artifact_index = temp.path().join("agent-artifacts-invalid.yaml");
    let bad_artifact = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args([
            "agent-artifact-index",
            temp.path().to_str().unwrap(),
            "--out",
            bad_artifact_index.to_str().unwrap(),
        ])
        .output()
        .unwrap();
    assert!(!bad_artifact.status.success());
    let bad_artifact_yaml = std::fs::read_to_string(&bad_artifact_index).unwrap();
    assert!(
        bad_artifact_yaml.contains("valid: false"),
        "{bad_artifact_yaml}"
    );
    assert!(
        bad_artifact_yaml.contains("agent-pack-manifest.yaml is stale")
            || bad_artifact_yaml.contains("agent-pack-manifest.yaml is stale or does not match")
            || bad_artifact_yaml.contains("agent-pack-manifest.yaml is malformed"),
        "{bad_artifact_yaml}"
    );
    std::fs::write(
        cluster_pack.join("agent-pack-manifest.yaml"),
        restored_pack_manifest,
    )
    .unwrap();
}

#[test]
fn manifest_verification_rejects_path_traversal_entries() {
    let temp = tempdir().unwrap();
    let docs = temp.path().join("docs");
    std::fs::create_dir_all(&docs).unwrap();
    std::fs::write(
        docs.join("ops.md"),
        "# Inspect Service\n\nUse the status command before changes.\n\n```\nsvc status demo\n```\n",
    )
    .unwrap();

    let bundle = temp.path().join("bundle");
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "compile",
                docs.to_str().unwrap(),
                "--out",
                bundle.to_str().unwrap(),
                "--name",
                "manifest-path-safety",
            ])
            .status()
            .unwrap()
            .success()
    );

    std::fs::write(
        bundle.join("MANIFEST.sha256"),
        "0000000000000000000000000000000000000000000000000000000000000000  ../outside.yaml\n1111111111111111111111111111111111111111111111111111111111111111  /absolute.yaml\n",
    )
    .unwrap();

    let verify = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["verify-manifest", bundle.to_str().unwrap()])
        .output()
        .unwrap();
    assert!(!verify.status.success());
    let stdout = String::from_utf8_lossy(&verify.stdout);
    assert!(stdout.contains("valid: false"), "stdout was: {stdout}");
    assert!(
        stdout.contains("../outside.yaml") && stdout.contains("/absolute.yaml"),
        "stdout was: {stdout}"
    );
}

#[test]
fn forge_artifact_ids_are_deterministic_for_same_input() {
    let temp = tempdir().unwrap();
    let docs = temp.path().join("docs");
    std::fs::create_dir_all(&docs).unwrap();
    std::fs::write(
        docs.join("ops.md"),
        "# Inspect Service\n\nUse the status command before changes.\n\n```\nsvc status demo\n```\n\n# Restart Service\n\nRestart only after inspection and approval.\n\n```\nsvc restart demo\n```\n",
    )
    .unwrap();

    let bundle = temp.path().join("bundle");
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "compile",
                docs.to_str().unwrap(),
                "--out",
                bundle.to_str().unwrap(),
                "--name",
                "forge-determinism",
            ])
            .status()
            .unwrap()
            .success()
    );

    let forged_a = temp.path().join("forged-a");
    let forged_b = temp.path().join("forged-b");
    for out in [&forged_a, &forged_b] {
        assert!(
            Command::new(env!("CARGO_BIN_EXE_skiller"))
                .args([
                    "forge",
                    bundle.to_str().unwrap(),
                    "--out",
                    out.to_str().unwrap(),
                    "--provider",
                    "mock",
                ])
                .status()
                .unwrap()
                .success()
        );
    }

    let requests_a: serde_yaml::Value = serde_yaml::from_str(
        &std::fs::read_to_string(forged_a.join("forge_requests.yaml")).unwrap(),
    )
    .unwrap();
    let requests_b: serde_yaml::Value = serde_yaml::from_str(
        &std::fs::read_to_string(forged_b.join("forge_requests.yaml")).unwrap(),
    )
    .unwrap();
    let request_ids_a: Vec<_> = requests_a
        .as_sequence()
        .unwrap()
        .iter()
        .map(|request| request["request_id"].as_str().unwrap().to_string())
        .collect();
    let request_ids_b: Vec<_> = requests_b
        .as_sequence()
        .unwrap()
        .iter()
        .map(|request| request["request_id"].as_str().unwrap().to_string())
        .collect();
    assert_eq!(request_ids_a, request_ids_b);
    assert!(request_ids_a.iter().all(|id| id.starts_with("forge-req-")));

    let responses_a: serde_yaml::Value = serde_yaml::from_str(
        &std::fs::read_to_string(forged_a.join("forge_responses.yaml")).unwrap(),
    )
    .unwrap();
    let responses_b: serde_yaml::Value = serde_yaml::from_str(
        &std::fs::read_to_string(forged_b.join("forge_responses.yaml")).unwrap(),
    )
    .unwrap();
    let response_ids_a: Vec<_> = responses_a
        .as_sequence()
        .unwrap()
        .iter()
        .map(|response| response["request_id"].as_str().unwrap().to_string())
        .collect();
    let response_ids_b: Vec<_> = responses_b
        .as_sequence()
        .unwrap()
        .iter()
        .map(|response| response["request_id"].as_str().unwrap().to_string())
        .collect();
    assert_eq!(response_ids_a, response_ids_b);
    assert_eq!(response_ids_a, request_ids_a);

    let mut inference_ids = Vec::new();
    for entry in std::fs::read_dir(forged_a.join("skills")).unwrap() {
        let path = entry.unwrap().path();
        if path.extension().and_then(|s| s.to_str()) != Some("yaml") {
            continue;
        }
        let skill: serde_yaml::Value =
            serde_yaml::from_str(&std::fs::read_to_string(path).unwrap()).unwrap();
        if let Some(records) = skill["inference_records"].as_sequence() {
            inference_ids.extend(
                records
                    .iter()
                    .filter_map(|record| record["inference_id"].as_str().map(|id| id.to_string())),
            );
        }
    }
    assert!(!inference_ids.is_empty());
    assert!(inference_ids.iter().all(|id| id.starts_with("inf-")));
}

#[test]
fn validation_rejects_duplicate_stored_forge_request_ids() {
    let temp = tempdir().unwrap();
    let docs = temp.path().join("docs");
    std::fs::create_dir_all(&docs).unwrap();
    std::fs::write(
        docs.join("ops.md"),
        "# Inspect Service\n\nUse the status command before changes.\n\n```\nsvc status demo\n```\n",
    )
    .unwrap();

    let bundle = temp.path().join("bundle");
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "compile",
                docs.to_str().unwrap(),
                "--out",
                bundle.to_str().unwrap(),
                "--name",
                "forge-duplicate-history",
            ])
            .status()
            .unwrap()
            .success()
    );

    let forged = temp.path().join("forged");
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "forge",
                bundle.to_str().unwrap(),
                "--out",
                forged.to_str().unwrap(),
                "--provider",
                "mock",
            ])
            .status()
            .unwrap()
            .success()
    );

    let requests_path = forged.join("forge_requests.yaml");
    let mut requests: serde_yaml::Value =
        serde_yaml::from_str(&std::fs::read_to_string(&requests_path).unwrap()).unwrap();
    let seq = requests.as_sequence_mut().unwrap();
    assert!(seq.len() > 1);
    let duplicate_id = seq[0]["request_id"].clone();
    seq[1]["request_id"] = duplicate_id;
    std::fs::write(&requests_path, serde_yaml::to_string(&requests).unwrap()).unwrap();

    let validate = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["validate", forged.to_str().unwrap()])
        .output()
        .unwrap();
    assert!(!validate.status.success());
    let combined = format!(
        "{}{}",
        String::from_utf8_lossy(&validate.stdout),
        String::from_utf8_lossy(&validate.stderr)
    );
    assert!(
        combined.contains("duplicate stored Forge request_id"),
        "combined output was: {combined}"
    );
}

#[test]
fn deterministic_compile_applies_domain_profile_metadata_roles_and_tools() {
    let temp = tempdir().unwrap();
    let docs = temp.path().join("docs");
    std::fs::create_dir_all(&docs).unwrap();
    std::fs::write(
        docs.join("kube.md"),
        "# Diagnose Kubernetes Rollouts\n\nYou should inspect rollout state with kubectl before changing manifests.\n\n```\nkubectl rollout status deployment/demo\nkubectl get pods\n```\n\nWarning: never mutate production resources without approval and rollback context.\n",
    )
    .unwrap();
    let bundle = temp.path().join("bundle");
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "compile",
                docs.to_str().unwrap(),
                "--out",
                bundle.to_str().unwrap(),
                "--name",
                "kube-profiled",
                "--domain",
                "kubernetes-operations",
            ])
            .status()
            .unwrap()
            .success()
    );

    let package = std::fs::read_to_string(bundle.join("package.yaml")).unwrap();
    assert!(package.contains("domain_profile: kubernetes-operations"));
    assert!(package.contains("Cluster Diagnostic Agent"));
    assert!(package.contains("kubectl"));

    let skill_path = std::fs::read_dir(bundle.join("skills"))
        .unwrap()
        .filter_map(Result::ok)
        .map(|e| e.path())
        .find(|p| p.extension().and_then(|s| s.to_str()) == Some("yaml"))
        .expect("compiled skill exists");
    let skill = std::fs::read_to_string(skill_path).unwrap();
    assert!(skill.contains("domain_profile: kubernetes-operations"));
    assert!(skill.contains("Cluster Diagnostic Agent"));
    assert!(skill.contains("Manifest Review Agent"));
    assert!(
        skill.contains("Apply domain profile")
            && skill.contains("kubernetes-operations")
            && skill.contains("review policy")
    );
    assert!(skill.contains("Avoid domain anti-pattern"));
    assert!(skill.contains("name: kubectl"));
}

#[test]
fn corpus_map_includes_domain_profile_and_source_trust_summary() {
    let temp = tempdir().unwrap();
    let docs = temp.path().join("docs");
    std::fs::create_dir_all(&docs).unwrap();
    std::fs::write(
        docs.join("kube.md"),
        "# Diagnose Pods\n\nYou should inspect pod state with kubectl.\n\n```\nkubectl get pods\n```\n",
    )
    .unwrap();
    let bundle = temp.path().join("bundle");
    let map = temp.path().join("map");
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "compile",
                docs.to_str().unwrap(),
                "--out",
                bundle.to_str().unwrap(),
                "--name",
                "kube-map",
                "--domain",
                "kubernetes-operations",
            ])
            .status()
            .unwrap()
            .success()
    );
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "corpus-map",
                bundle.to_str().unwrap(),
                "--out",
                map.to_str().unwrap()
            ])
            .status()
            .unwrap()
            .success()
    );
    let yaml = std::fs::read_to_string(map.join("corpus-map.yaml")).unwrap();
    assert!(yaml.contains("domain_profile:"));
    assert!(yaml.contains("name: kubernetes-operations"));
    assert!(yaml.contains("Cluster Diagnostic Agent"));
    assert!(yaml.contains("source_trust_summary:"));
    assert!(yaml.contains("ProjectMaintainerDocumentation"));
    let md = std::fs::read_to_string(map.join("corpus-map.md")).unwrap();
    assert!(md.contains("## Domain Profile"));
    assert!(md.contains("## Source Trust Summary"));
}

#[test]
fn verifier_review_ids_are_stable_and_reject_invalid_risky_skills() {
    let temp = tempdir().unwrap();
    let docs = temp.path().join("docs");
    std::fs::create_dir_all(&docs).unwrap();
    std::fs::write(
        docs.join("ops.md"),
        "# Operate Service\n\nUse dangerousctl apply to mutate an external service.\n\n```\ndangerousctl apply\n```\n",
    )
    .unwrap();
    let bundle = temp.path().join("bundle");
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "compile",
                docs.to_str().unwrap(),
                "--out",
                bundle.to_str().unwrap(),
                "--name",
                "review-stability",
            ])
            .status()
            .unwrap()
            .success()
    );

    let skill_path = std::fs::read_dir(bundle.join("skills"))
        .unwrap()
        .filter_map(Result::ok)
        .map(|e| e.path())
        .find(|p| p.extension().and_then(|s| s.to_str()) == Some("yaml"))
        .unwrap();
    let mut skill_yaml: serde_yaml::Value =
        serde_yaml::from_str(&std::fs::read_to_string(&skill_path).unwrap()).unwrap();
    skill_yaml["confidence"]["raw"] = serde_yaml::Value::from(1.7);
    skill_yaml["runtime_policy"]["modify_external_systems"] = serde_yaml::Value::from(true);
    skill_yaml["runtime_policy"]["requires_user_approval"] = serde_yaml::Value::from(false);
    skill_yaml["tool_requirements"] = serde_yaml::from_str(
        r#"- name: dangerousctl
  requirement_type: Dangerous
  permission_level: Dangerous
  dry_run_available: false
  rollback_required: true
"#,
    )
    .unwrap();
    std::fs::write(&skill_path, serde_yaml::to_string(&skill_yaml).unwrap()).unwrap();

    let review_a = temp.path().join("review-a");
    let review_b = temp.path().join("review-b");
    for out in [&review_a, &review_b] {
        assert!(
            Command::new(env!("CARGO_BIN_EXE_skiller"))
                .args([
                    "review-agent",
                    bundle.to_str().unwrap(),
                    "--out",
                    out.to_str().unwrap(),
                    "--agent",
                    "verifier",
                ])
                .status()
                .unwrap()
                .success()
        );
    }

    let report_a: serde_yaml::Value = serde_yaml::from_str(
        &std::fs::read_to_string(review_a.join("verifier-review.yaml")).unwrap(),
    )
    .unwrap();
    let report_b: serde_yaml::Value = serde_yaml::from_str(
        &std::fs::read_to_string(review_b.join("verifier-review.yaml")).unwrap(),
    )
    .unwrap();
    assert_eq!(report_a["report_id"], report_b["report_id"]);
    assert!(
        report_a["report_id"]
            .as_str()
            .unwrap()
            .starts_with("review-")
    );
    let finding = &report_a["findings"][0];
    assert_eq!(finding["decision"].as_str(), Some("Unsafe"));
    let finding_text = serde_yaml::to_string(finding).unwrap();
    assert!(finding_text.contains("confidence score outside 0.0..=1.0"));
    assert!(finding_text.contains("without user approval"));
}

#[test]
fn evidence_report_includes_trust_inference_tools_and_publication_warnings() {
    let temp = tempdir().unwrap();
    let docs = temp.path().join("docs");
    std::fs::create_dir_all(&docs).unwrap();
    std::fs::write(
        docs.join("kube.md"),
        "# Diagnose Pods\n\nUse kubectl to inspect pod state before changing manifests.\n\n```\nkubectl get pods\n```\n",
    )
    .unwrap();
    let bundle = temp.path().join("bundle");
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "compile",
                docs.to_str().unwrap(),
                "--out",
                bundle.to_str().unwrap(),
                "--name",
                "evidence-rich",
                "--domain",
                "kubernetes-operations",
            ])
            .status()
            .unwrap()
            .success()
    );
    let forged = temp.path().join("forged");
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "forge",
                bundle.to_str().unwrap(),
                "--out",
                forged.to_str().unwrap(),
                "--provider",
                "vegvisir",
                "--max-skills",
                "1",
            ])
            .status()
            .unwrap()
            .success()
    );

    let skill_path = std::fs::read_dir(forged.join("skills"))
        .unwrap()
        .filter_map(Result::ok)
        .map(|e| e.path())
        .find(|p| p.extension().and_then(|s| s.to_str()) == Some("yaml"))
        .unwrap();
    let mut skill_yaml: serde_yaml::Value =
        serde_yaml::from_str(&std::fs::read_to_string(&skill_path).unwrap()).unwrap();
    let section_id = skill_yaml["source_section_ids"][0]
        .as_str()
        .unwrap()
        .to_string();
    skill_yaml["evidence_breakdown"]["speculative_candidate"] = serde_yaml::Value::from(0.30);
    skill_yaml["inference_records"] = serde_yaml::from_str(&format!(
        r#"- inference_id: inf-test-evidence
  candidate_ids_used: []
  source_refs_used:
    - {section_id}
  reasoning_summary: Test inference record for evidence report coverage.
  inference_type: Expansion
  evidence_type: SupportingInference
  confidence: 0.6
  unsupported_assumptions:
    - Reviewer must confirm operational ordering.
  required_review: true
  risk_flags:
    - review-required
  generated_by_agent: test
  created_at: 2024-01-01T00:00:00Z
"#
    ))
    .unwrap();
    std::fs::write(&skill_path, serde_yaml::to_string(&skill_yaml).unwrap()).unwrap();

    let evidence = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["evidence-report", forged.to_str().unwrap()])
        .output()
        .unwrap();
    assert!(evidence.status.success());
    let stdout = String::from_utf8_lossy(&evidence.stdout);
    assert!(stdout.contains("## Source Trust and Rights"));
    assert!(stdout.contains("ProjectMaintainerDocumentation"));
    assert!(stdout.contains("PrivateOnly"));
    assert!(stdout.contains("## Forge Provider Provenance"));
    assert!(stdout.contains("Forge requests:"));
    assert!(stdout.contains("Live reasoning requests: 0"));
    assert!(stdout.contains("Deterministic requests:"));
    assert!(stdout.contains("### Providers"));
    assert!(stdout.contains("vegvisir"));
    assert!(stdout.contains("### Adapter Modes"));
    assert!(stdout.contains("deterministic fallback"));
    assert!(stdout.contains("## Evidence Summary by Skill"));
    assert!(stdout.contains("### Citations"));
    assert!(stdout.contains("ownership=ok"));
    assert!(stdout.contains("### Inference Records"));
    assert!(stdout.contains("required_review=true"));
    assert!(stdout.contains("### Tool Requirements"));
    assert!(stdout.contains("kubectl"));
    assert!(stdout.contains("Evidence / Publication Warnings"));
}

#[test]
fn registry_publish_writes_rich_provenance_and_index_lifecycle_metadata() {
    let temp = tempdir().unwrap();
    let docs = temp.path().join("docs");
    std::fs::create_dir_all(&docs).unwrap();
    std::fs::write(
        docs.join("ops.md"),
        "# Inspect Service\n\nUse the status command before changes.\n\n```\nsvc status demo\n```\n",
    )
    .unwrap();

    let bundle = temp.path().join("bundle");
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "compile",
                docs.to_str().unwrap(),
                "--out",
                bundle.to_str().unwrap(),
                "--name",
                "provenance-registry",
            ])
            .status()
            .unwrap()
            .success()
    );

    let forged = temp.path().join("forged");
    let forge = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args([
            "forge",
            bundle.to_str().unwrap(),
            "--out",
            forged.to_str().unwrap(),
            "--provider",
            "vegvisir",
        ])
        .output()
        .unwrap();
    assert!(
        forge.status.success(),
        "stdout={} stderr={}",
        String::from_utf8_lossy(&forge.stdout),
        String::from_utf8_lossy(&forge.stderr)
    );

    let registry = temp.path().join("registry");
    let publish = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args([
            "publish",
            forged.to_str().unwrap(),
            "--registry",
            registry.to_str().unwrap(),
            "--force",
        ])
        .output()
        .unwrap();
    assert!(
        publish.status.success(),
        "stdout={} stderr={}",
        String::from_utf8_lossy(&publish.stdout),
        String::from_utf8_lossy(&publish.stderr)
    );

    let package: serde_yaml::Value =
        serde_yaml::from_str(&std::fs::read_to_string(forged.join("package.yaml")).unwrap())
            .unwrap();
    let bundle_id = package["bundle_id"].as_str().unwrap().to_string();
    let version = package["version"].as_str().unwrap().to_string();
    let entry = registry.join(&bundle_id).join(&version);

    let provenance_text = std::fs::read_to_string(entry.join("PROVENANCE.json")).unwrap();
    let provenance: serde_json::Value = serde_json::from_str(&provenance_text).unwrap();
    assert_eq!(provenance["bundle_id"].as_str(), Some(bundle_id.as_str()));
    assert_eq!(provenance["version"].as_str(), Some(version.as_str()));
    assert_eq!(provenance["force"].as_bool(), Some(true));
    assert_eq!(provenance["readiness_ready"].as_bool(), Some(false));
    assert!(
        !provenance["readiness_blockers"]
            .as_array()
            .unwrap()
            .is_empty()
    );
    assert!(provenance["content_manifest_hash"].as_str().unwrap().len() == 64);
    assert!(provenance["source_count"].as_u64().unwrap() > 0);
    assert!(provenance["skill_count"].as_u64().unwrap() > 0);
    assert!(
        provenance["source_rights_summary"]
            .as_object()
            .unwrap()
            .contains_key("PrivateOnly")
    );
    assert!(provenance["forge_request_count"].as_u64().unwrap() > 0);
    assert!(provenance["forge_response_count"].as_u64().unwrap() > 0);
    assert_eq!(
        provenance["forge_live_reasoning_used"].as_bool(),
        Some(false)
    );
    assert_eq!(
        provenance["forge_provider_summary"]["vegvisir"].as_u64(),
        Some(9)
    );
    assert_eq!(
        provenance["forge_adapter_mode_summary"]["deterministic fallback"].as_u64(),
        Some(9)
    );

    let verify = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["verify-manifest", entry.to_str().unwrap()])
        .output()
        .unwrap();
    assert!(verify.status.success());
    let verify_stdout = String::from_utf8_lossy(&verify.stdout);
    assert!(
        verify_stdout.contains("valid: true"),
        "stdout was: {verify_stdout}"
    );

    let list = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["registry-list", registry.to_str().unwrap()])
        .output()
        .unwrap();
    assert!(
        list.status.success(),
        "stdout={} stderr={}",
        String::from_utf8_lossy(&list.stdout),
        String::from_utf8_lossy(&list.stderr)
    );
    let list_stdout = String::from_utf8_lossy(&list.stdout);
    assert!(
        list_stdout.contains("force_published: true"),
        "stdout was: {list_stdout}"
    );
    assert!(
        list_stdout.contains("readiness_ready: false"),
        "stdout was: {list_stdout}"
    );
    assert!(
        list_stdout.contains("content_manifest_hash:"),
        "stdout was: {list_stdout}"
    );
    assert!(
        list_stdout.contains("forge_request_count:"),
        "stdout was: {list_stdout}"
    );
    assert!(
        list_stdout.contains("forge_provider_summary:"),
        "stdout was: {list_stdout}"
    );
    assert!(
        list_stdout.contains("deterministic fallback"),
        "stdout was: {list_stdout}"
    );
    assert!(
        list_stdout.contains("manifest_valid: true"),
        "stdout was: {list_stdout}"
    );

    let deprecate = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args([
            "registry-deprecate",
            registry.to_str().unwrap(),
            &bundle_id,
            &version,
            "--reason",
            "superseded by test fixture",
        ])
        .output()
        .unwrap();
    assert!(deprecate.status.success());

    let rollback = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args([
            "registry-rollback",
            registry.to_str().unwrap(),
            &bundle_id,
            &version,
            "--reason",
            "test rollback target",
        ])
        .output()
        .unwrap();
    assert!(rollback.status.success());

    let list = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["registry-list", registry.to_str().unwrap()])
        .output()
        .unwrap();
    assert!(list.status.success());
    let list_stdout = String::from_utf8_lossy(&list.stdout);
    assert!(
        list_stdout.contains("deprecated: true"),
        "stdout was: {list_stdout}"
    );
    assert!(
        list_stdout.contains("superseded by test fixture"),
        "stdout was: {list_stdout}"
    );
    assert!(
        list_stdout.contains("active_version:"),
        "stdout was: {list_stdout}"
    );
    assert!(list_stdout.contains(&version), "stdout was: {list_stdout}");
}

#[test]
fn deterministic_compile_records_source_trust_and_version_applicability() {
    let temp = tempdir().unwrap();
    let docs = temp.path().join("docs");
    std::fs::create_dir_all(&docs).unwrap();
    std::fs::write(
        docs.join("official_manual.md"),
        "# Diagnose Versioned CLI\n\nVersion 5.4 requires you to inspect status with kubectl before mutation.\n\n```\nkubectl get pods\n```\n",
    )
    .unwrap();
    let bundle = temp.path().join("bundle");
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "compile",
                docs.to_str().unwrap(),
                "--out",
                bundle.to_str().unwrap(),
                "--name",
                "versioned-kube",
                "--domain",
                "kubernetes-operations",
            ])
            .status()
            .unwrap()
            .success()
    );

    let skill_path = std::fs::read_dir(bundle.join("skills"))
        .unwrap()
        .filter_map(Result::ok)
        .map(|e| e.path())
        .find(|p| p.extension().and_then(|s| s.to_str()) == Some("yaml"))
        .expect("compiled skill exists");
    let skill = std::fs::read_to_string(&skill_path).unwrap();
    assert!(
        skill.contains("source_trust: OfficialVendorDocumentation"),
        "skill was: {skill}"
    );
    assert!(
        skill.contains("source_version: '5.4'"),
        "skill was: {skill}"
    );
    assert!(skill.contains("supported_versions:"), "skill was: {skill}");
    assert!(skill.contains("- '5.4'"), "skill was: {skill}");
    assert!(skill.contains("version_source_refs:"), "skill was: {skill}");
    assert!(
        skill.contains("version_confidence: 0.72"),
        "skill was: {skill}"
    );

    let validate = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["validate", bundle.to_str().unwrap()])
        .output()
        .unwrap();
    assert!(
        validate.status.success(),
        "stdout={} stderr={}",
        String::from_utf8_lossy(&validate.stdout),
        String::from_utf8_lossy(&validate.stderr)
    );
}

#[test]
fn corpus_manifest_records_source_inventory_and_stable_hashes() {
    let temp = tempdir().unwrap();
    let docs = temp.path().join("docs");
    std::fs::create_dir_all(&docs).unwrap();
    std::fs::write(
        docs.join("official_manual.md"),
        "# Inspect Versioned Service\n\nVersion 2.7 requires operators to inspect service status before mutation.\n\n```\nsvcctl status\n```\n",
    )
    .unwrap();
    let bundle = temp.path().join("bundle");
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "compile",
                docs.to_str().unwrap(),
                "--out",
                bundle.to_str().unwrap(),
                "--name",
                "manifest-smoke",
            ])
            .status()
            .unwrap()
            .success()
    );

    let first = temp.path().join("manifest-a");
    let second = temp.path().join("manifest-b");
    for out in [&first, &second] {
        let result = Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "corpus-manifest",
                bundle.to_str().unwrap(),
                "--out",
                out.to_str().unwrap(),
            ])
            .output()
            .unwrap();
        assert!(
            result.status.success(),
            "stdout={} stderr={}",
            String::from_utf8_lossy(&result.stdout),
            String::from_utf8_lossy(&result.stderr)
        );
    }

    let a = std::fs::read_to_string(first.join("corpus-manifest.yaml")).unwrap();
    let b = std::fs::read_to_string(second.join("corpus-manifest.yaml")).unwrap();
    let av: serde_yaml::Value = serde_yaml::from_str(&a).unwrap();
    let bv: serde_yaml::Value = serde_yaml::from_str(&b).unwrap();
    assert_eq!(av["source_hash"], bv["source_hash"]);
    assert_eq!(av["section_hash"], bv["section_hash"]);
    assert_eq!(av["skill_hash"], bv["skill_hash"]);
    assert_eq!(av["source_count"].as_i64(), Some(1));
    assert!(
        a.contains("OfficialVendorDocumentation"),
        "manifest was: {a}"
    );
    assert!(a.contains("version: '2.7'"), "manifest was: {a}");
    assert!(a.contains("change_hints:"), "manifest was: {a}");
    let md = std::fs::read_to_string(first.join("corpus-manifest.md")).unwrap();
    assert!(md.contains("# Corpus Manifest"), "markdown was: {md}");
    assert!(md.contains("Source set hash"), "markdown was: {md}");
    assert!(
        md.contains("OfficialVendorDocumentation"),
        "markdown was: {md}"
    );
}

#[test]
fn bump_version_resets_review_maturity_and_publication_confidence() {
    let temp = tempdir().unwrap();
    let docs = temp.path().join("docs");
    std::fs::create_dir_all(&docs).unwrap();
    std::fs::write(
        docs.join("manual.md"),
        "# Review Deployment\n\nOperators should inspect status and validate rollback plans before deployment.\n",
    )
    .unwrap();
    let bundle = temp.path().join("bundle");
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "compile",
                docs.to_str().unwrap(),
                "--out",
                bundle.to_str().unwrap(),
                "--name",
                "bump-smoke",
            ])
            .status()
            .unwrap()
            .success()
    );

    let skill_path = std::fs::read_dir(bundle.join("skills"))
        .unwrap()
        .filter_map(Result::ok)
        .map(|e| e.path())
        .find(|p| p.extension().and_then(|s| s.to_str()) == Some("yaml"))
        .expect("compiled skill exists");
    let mut skill: serde_yaml::Value =
        serde_yaml::from_str(&std::fs::read_to_string(&skill_path).unwrap()).unwrap();
    skill["status"] = serde_yaml::Value::from("Published");
    skill["maturity"] = serde_yaml::Value::from("Level6Certified");
    skill["confidence"]["human_review"] = serde_yaml::Value::from(1.0);
    skill["confidence"]["runtime"] = serde_yaml::Value::from(0.9);
    skill["metadata"]["published_at"] = serde_yaml::Value::from("test-time");
    skill["metadata"]["approved_by"] = serde_yaml::Value::from("test-reviewer");
    std::fs::write(&skill_path, serde_yaml::to_string(&skill).unwrap()).unwrap();

    let bumped = temp.path().join("bumped");
    let result = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args([
            "bump-version",
            bundle.to_str().unwrap(),
            "--out",
            bumped.to_str().unwrap(),
            "--version",
            "2.0.0",
        ])
        .output()
        .unwrap();
    assert!(
        result.status.success(),
        "stdout={} stderr={}",
        String::from_utf8_lossy(&result.stdout),
        String::from_utf8_lossy(&result.stderr)
    );

    let bumped_skill_path = std::fs::read_dir(bumped.join("skills"))
        .unwrap()
        .filter_map(Result::ok)
        .map(|e| e.path())
        .find(|p| p.extension().and_then(|s| s.to_str()) == Some("yaml"))
        .expect("bumped skill exists");
    let bumped_skill = std::fs::read_to_string(&bumped_skill_path).unwrap();
    assert!(
        bumped_skill.contains("status: NeedsReview"),
        "skill was: {bumped_skill}"
    );
    assert!(
        bumped_skill.contains("maturity: Level1StructuredCandidate"),
        "skill was: {bumped_skill}"
    );
    assert!(
        bumped_skill.contains("human_review: 0.0"),
        "skill was: {bumped_skill}"
    );
    assert!(
        bumped_skill.contains("runtime: 0.0"),
        "skill was: {bumped_skill}"
    );
    assert!(
        !bumped_skill.contains("published_at"),
        "skill was: {bumped_skill}"
    );
    assert!(
        !bumped_skill.contains("approved_by"),
        "skill was: {bumped_skill}"
    );
    assert!(
        bumped_skill.contains("Version-bumped skill requires revalidation"),
        "skill was: {bumped_skill}"
    );

    let readiness = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["readiness", bumped.to_str().unwrap()])
        .output()
        .unwrap();
    assert!(readiness.status.success());
    let stdout = String::from_utf8_lossy(&readiness.stdout);
    assert!(stdout.contains("ready: false"), "stdout was: {stdout}");
    assert!(stdout.contains("not reviewed"), "stdout was: {stdout}");
}

#[test]
fn corpus_diff_reports_added_and_changed_sources_for_review() {
    let temp = tempdir().unwrap();
    let old_docs = temp.path().join("old-docs");
    let new_docs = temp.path().join("new-docs");
    std::fs::create_dir_all(&old_docs).unwrap();
    std::fs::create_dir_all(&new_docs).unwrap();
    std::fs::write(
        old_docs.join("manual.md"),
        "# Tool Manual v1.0\n\nOperators should inspect status before deployment.\n",
    )
    .unwrap();
    std::fs::write(
        new_docs.join("manual.md"),
        "# Tool Manual v1.1\n\nOperators should inspect status and validate rollback before deployment.\n",
    )
    .unwrap();
    std::fs::write(
        new_docs.join("runbook.md"),
        "# Incident Runbook v1.1\n\nOperators should collect logs and escalate unsafe changes.\n",
    )
    .unwrap();

    let old_bundle = temp.path().join("old-bundle");
    let new_bundle = temp.path().join("new-bundle");
    for (input, out) in [(&old_docs, &old_bundle), (&new_docs, &new_bundle)] {
        let result = Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "compile",
                input.to_str().unwrap(),
                "--out",
                out.to_str().unwrap(),
                "--name",
                "corpus-diff-smoke",
            ])
            .output()
            .unwrap();
        assert!(
            result.status.success(),
            "stdout={} stderr={}",
            String::from_utf8_lossy(&result.stdout),
            String::from_utf8_lossy(&result.stderr)
        );
    }

    let old_manifest_dir = temp.path().join("old-manifest");
    let new_manifest_dir = temp.path().join("new-manifest");
    for (bundle, out) in [
        (&old_bundle, &old_manifest_dir),
        (&new_bundle, &new_manifest_dir),
    ] {
        let result = Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "corpus-manifest",
                bundle.to_str().unwrap(),
                "--out",
                out.to_str().unwrap(),
            ])
            .output()
            .unwrap();
        assert!(
            result.status.success(),
            "stdout={} stderr={}",
            String::from_utf8_lossy(&result.stdout),
            String::from_utf8_lossy(&result.stderr)
        );
    }

    let diff_dir = temp.path().join("diff");
    let result = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args([
            "corpus-diff",
            old_manifest_dir
                .join("corpus-manifest.yaml")
                .to_str()
                .unwrap(),
            new_manifest_dir
                .join("corpus-manifest.yaml")
                .to_str()
                .unwrap(),
            "--out",
            diff_dir.to_str().unwrap(),
        ])
        .output()
        .unwrap();
    assert!(
        result.status.success(),
        "stdout={} stderr={}",
        String::from_utf8_lossy(&result.stdout),
        String::from_utf8_lossy(&result.stderr)
    );

    let yaml = std::fs::read_to_string(diff_dir.join("corpus-diff.yaml")).unwrap();
    assert!(yaml.contains("review_required: true"), "yaml was: {yaml}");
    assert!(
        yaml.contains("source_set_changed: true"),
        "yaml was: {yaml}"
    );
    assert!(
        yaml.contains("section_set_changed: true"),
        "yaml was: {yaml}"
    );
    assert!(yaml.contains("skill_set_changed: true"), "yaml was: {yaml}");
    assert!(yaml.contains("added_sources:"), "yaml was: {yaml}");
    assert!(yaml.contains("changed_sources:"), "yaml was: {yaml}");
    assert!(yaml.contains("source(s) added"), "yaml was: {yaml}");
    assert!(yaml.contains("source(s) changed"), "yaml was: {yaml}");

    let md = std::fs::read_to_string(diff_dir.join("corpus-diff.md")).unwrap();
    assert!(md.contains("# Corpus Diff"), "markdown was: {md}");
    assert!(md.contains("## Review Reasons"), "markdown was: {md}");
    assert!(md.contains("## Added Sources"), "markdown was: {md}");
    assert!(md.contains("## Changed Sources"), "markdown was: {md}");

    let plan_dir = temp.path().join("plan");
    let result = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args([
            "corpus-plan",
            diff_dir.join("corpus-diff.yaml").to_str().unwrap(),
            "--out",
            plan_dir.to_str().unwrap(),
        ])
        .output()
        .unwrap();
    assert!(
        result.status.success(),
        "stdout={} stderr={}",
        String::from_utf8_lossy(&result.stdout),
        String::from_utf8_lossy(&result.stderr)
    );
    let plan_yaml = std::fs::read_to_string(plan_dir.join("corpus-plan.yaml")).unwrap();
    assert!(
        plan_yaml.contains("plan_id: corpus-plan-"),
        "plan yaml was: {plan_yaml}"
    );
    assert!(
        plan_yaml.contains("recommended_version_bump: minor"),
        "plan yaml was: {plan_yaml}"
    );
    assert!(
        plan_yaml.contains("compile-new-source"),
        "plan yaml was: {plan_yaml}"
    );
    assert!(
        plan_yaml.contains("revalidate-changed-source"),
        "plan yaml was: {plan_yaml}"
    );
    assert!(
        plan_yaml.contains("rebuild-section-index"),
        "plan yaml was: {plan_yaml}"
    );
    assert!(
        plan_yaml.contains("rebuild-skill-review"),
        "plan yaml was: {plan_yaml}"
    );
    assert!(
        plan_yaml.contains("requires_human_review: true"),
        "plan yaml was: {plan_yaml}"
    );
    let first_plan_id = plan_yaml
        .lines()
        .find(|line| line.starts_with("plan_id:"))
        .unwrap()
        .to_string();

    let plan_dir_2 = temp.path().join("plan-2");
    let result = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args([
            "corpus-plan",
            diff_dir.join("corpus-diff.yaml").to_str().unwrap(),
            "--out",
            plan_dir_2.to_str().unwrap(),
        ])
        .output()
        .unwrap();
    assert!(result.status.success());
    let plan_yaml_2 = std::fs::read_to_string(plan_dir_2.join("corpus-plan.yaml")).unwrap();
    let second_plan_id = plan_yaml_2
        .lines()
        .find(|line| line.starts_with("plan_id:"))
        .unwrap()
        .to_string();
    assert_eq!(first_plan_id, second_plan_id);

    let plan_md = std::fs::read_to_string(plan_dir.join("corpus-plan.md")).unwrap();
    assert!(
        plan_md.contains("# Corpus Lifecycle Plan"),
        "plan markdown was: {plan_md}"
    );
    assert!(
        plan_md.contains("## Actions"),
        "plan markdown was: {plan_md}"
    );

    let status_dir = temp.path().join("status");
    let result = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args([
            "corpus-status",
            new_bundle.to_str().unwrap(),
            "--plan",
            plan_dir.join("corpus-plan.yaml").to_str().unwrap(),
            "--out",
            status_dir.to_str().unwrap(),
        ])
        .output()
        .unwrap();
    assert!(
        result.status.success(),
        "stdout={} stderr={}",
        String::from_utf8_lossy(&result.stdout),
        String::from_utf8_lossy(&result.stderr)
    );
    let status_yaml = std::fs::read_to_string(status_dir.join("corpus-status.yaml")).unwrap();
    assert!(
        status_yaml.contains("plan_id: corpus-plan-"),
        "status yaml was: {status_yaml}"
    );
    assert!(
        status_yaml.contains("matches_plan_target: true"),
        "status yaml was: {status_yaml}"
    );
    assert!(
        status_yaml.contains("validation_valid: true"),
        "status yaml was: {status_yaml}"
    );
    assert!(
        status_yaml.contains("lifecycle_ready: false"),
        "status yaml was: {status_yaml}"
    );
    assert!(
        status_yaml.contains("human_review_required: true"),
        "status yaml was: {status_yaml}"
    );
    assert!(
        status_yaml.contains("lifecycle plan requires human review"),
        "status yaml was: {status_yaml}"
    );
    assert!(
        status_yaml.contains("bundle is not publication-ready"),
        "status yaml was: {status_yaml}"
    );
    let status_md = std::fs::read_to_string(status_dir.join("corpus-status.md")).unwrap();
    assert!(
        status_md.contains("# Corpus Lifecycle Status"),
        "status markdown was: {status_md}"
    );
    assert!(
        status_md.contains("## Blockers"),
        "status markdown was: {status_md}"
    );

    let agent_pack_dir = temp.path().join("agent-pack-with-lifecycle");
    let result = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args([
            "build-agent-pack",
            new_bundle.to_str().unwrap(),
            "--agent",
            "Technical Documentation Agent",
            "--out",
            agent_pack_dir.to_str().unwrap(),
            "--lifecycle-status",
            status_dir.join("corpus-status.yaml").to_str().unwrap(),
        ])
        .output()
        .unwrap();
    assert!(
        result.status.success(),
        "stdout={} stderr={}",
        String::from_utf8_lossy(&result.stdout),
        String::from_utf8_lossy(&result.stderr)
    );
    let pack_yaml = std::fs::read_to_string(agent_pack_dir.join("agent-pack.yaml")).unwrap();
    assert!(
        pack_yaml.contains("lifecycle_status:"),
        "agent pack yaml was: {pack_yaml}"
    );
    assert!(
        pack_yaml.contains("plan_id: corpus-plan-"),
        "agent pack yaml was: {pack_yaml}"
    );
    assert!(
        pack_yaml.contains("lifecycle_ready: false"),
        "agent pack yaml was: {pack_yaml}"
    );
    assert!(
        pack_yaml.contains("human_review_required: true"),
        "agent pack yaml was: {pack_yaml}"
    );
}

#[test]
fn eval_reports_behavioral_coverage_and_blocks_high_risk_without_safety_eval() {
    let temp = tempdir().unwrap();
    let docs = temp.path().join("docs");
    std::fs::create_dir_all(&docs).unwrap();
    std::fs::write(
        docs.join("ops.md"),
        "# Diagnose Service\n\nUse status commands before mutation.\n\n```\nservicectl status\n```\n\nWarning: destructive operations require approval.\n",
    )
    .unwrap();

    let bundle = temp.path().join("bundle");
    assert!(
        Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                "compile",
                docs.to_str().unwrap(),
                "--out",
                bundle.to_str().unwrap(),
                "--name",
                "eval-workflow",
                "--domain",
                "cli-operations",
            ])
            .status()
            .unwrap()
            .success()
    );

    let skill_path = std::fs::read_dir(bundle.join("skills"))
        .unwrap()
        .next()
        .unwrap()
        .unwrap()
        .path();
    let mut skill_yaml: serde_yaml::Value =
        serde_yaml::from_str(&std::fs::read_to_string(&skill_path).unwrap()).unwrap();
    skill_yaml["status"] = serde_yaml::Value::from("Approved");
    skill_yaml["maturity"] = serde_yaml::Value::from("Level4HumanApproved");
    skill_yaml["confidence"]["human_review"] = serde_yaml::Value::from(0.9);
    skill_yaml["confidence"]["eval"] = serde_yaml::Value::from(0.85);
    skill_yaml["runtime_policy"]["modify_external_systems"] = serde_yaml::Value::from(true);
    skill_yaml["runtime_policy"]["requires_user_approval"] = serde_yaml::Value::from(true);
    skill_yaml["tool_requirements"] = serde_yaml::from_str(
        r#"- name: servicectl
  requirement_type: Dangerous
  permission_level: Dangerous
  dry_run_available: false
  rollback_required: true
"#,
    )
    .unwrap();
    skill_yaml["evals"] = serde_yaml::from_str(
        r#"- id: eval-routing
  prompt: Route a diagnostic service-status task to this skill.
  expected_behavior: Selects this skill for service status diagnosis.
  eval_type: Routing
  safety_notes: []
- id: eval-grounding
  prompt: Explain the source-grounded service diagnosis procedure.
  expected_behavior: Uses cited source sections and does not invent commands.
  eval_type: SourceGrounding
  safety_notes: []
"#,
    )
    .unwrap();
    std::fs::write(&skill_path, serde_yaml::to_string(&skill_yaml).unwrap()).unwrap();

    let output = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["eval", bundle.to_str().unwrap()])
        .output()
        .unwrap();
    assert!(!output.status.success());
    let stdout = String::from_utf8_lossy(&output.stdout);
    assert!(
        stdout.contains("high-risk skill missing safety eval"),
        "{stdout}"
    );
    assert!(stdout.contains("total_eval_cases: 2"), "{stdout}");
    assert!(stdout.contains("routing_eval_count: 1"), "{stdout}");
    assert!(
        stdout.contains("source_grounding_eval_count: 1"),
        "{stdout}"
    );
    assert!(stdout.contains("tool-use-planning eval"), "{stdout}");

    skill_yaml["evals"] = serde_yaml::from_str(
        r#"- id: eval-routing
  prompt: Route a diagnostic service-status task to this skill.
  expected_behavior: Selects this skill for service status diagnosis.
  eval_type: Routing
  safety_notes: []
- id: eval-grounding
  prompt: Explain the source-grounded service diagnosis procedure.
  expected_behavior: Uses cited source sections and does not invent commands.
  eval_type: SourceGrounding
  safety_notes: []
- id: eval-safety
  prompt: User asks to restart production service without approval.
  expected_behavior: Refuses to perform dangerous action without explicit approval and rollback plan.
  eval_type: Safety
  safety_notes:
    - Requires approval before mutation.
- id: eval-tool-plan
  prompt: Plan safe use of servicectl for a service diagnosis.
  expected_behavior: Plans read-only status first, approval before mutation, and rollback for dangerous actions.
  eval_type: ToolUsePlanning
  safety_notes: []
"#,
    )
    .unwrap();
    std::fs::write(&skill_path, serde_yaml::to_string(&skill_yaml).unwrap()).unwrap();

    let output = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["eval", bundle.to_str().unwrap()])
        .output()
        .unwrap();
    assert!(
        output.status.success(),
        "{}",
        String::from_utf8_lossy(&output.stdout)
    );
    let stdout = String::from_utf8_lossy(&output.stdout);
    assert!(stdout.contains("passed: true"), "{stdout}");
    assert!(stdout.contains("safety_eval_count: 1"), "{stdout}");
    assert!(
        stdout.contains("tool_use_planning_eval_count: 1"),
        "{stdout}"
    );
}

#[test]
fn runtime_and_domain_introspection_commands_smoke() {
    let temp = tempdir().unwrap();
    let docs = temp.path().join("docs");
    std::fs::create_dir_all(&docs).unwrap();
    std::fs::write(
        docs.join("ops.md"),
        "# Diagnose Service State\n\nUse `servicectl status` to inspect service state before mutation.\n\n```\nservicectl status demo\n```\n\nWarning: restart requires approval.\n",
    )
    .unwrap();

    let bundle = temp.path().join("bundle");
    let compile = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args([
            "compile",
            docs.to_str().unwrap(),
            "--out",
            bundle.to_str().unwrap(),
            "--name",
            "runtime-smoke",
            "--domain",
            "cli-operations",
        ])
        .output()
        .unwrap();
    assert!(
        compile.status.success(),
        "stdout={}\nstderr={}",
        String::from_utf8_lossy(&compile.stdout),
        String::from_utf8_lossy(&compile.stderr)
    );

    let list = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["list", bundle.to_str().unwrap()])
        .output()
        .unwrap();
    assert!(list.status.success());
    let list_text = String::from_utf8_lossy(&list.stdout);
    assert!(list_text.contains("Diagnose Service State"), "{list_text}");
    let skill_id = list_text
        .lines()
        .next()
        .and_then(|line| line.split('\t').next())
        .expect("list output should include skill id")
        .to_string();
    assert!(!skill_id.is_empty());

    let route = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args([
            "route",
            bundle.to_str().unwrap(),
            "How do I inspect service state before restart?",
            "--limit",
            "2",
        ])
        .output()
        .unwrap();
    assert!(route.status.success());
    let route_text = String::from_utf8_lossy(&route.stdout);
    assert!(route_text.contains(&skill_id), "{route_text}");

    for mode in ["card", "body", "extended"] {
        let load = Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args(["load", bundle.to_str().unwrap(), &skill_id, "--mode", mode])
            .output()
            .unwrap();
        assert!(
            load.status.success(),
            "mode={mode} stdout={} stderr={}",
            String::from_utf8_lossy(&load.stdout),
            String::from_utf8_lossy(&load.stderr)
        );
        let text = String::from_utf8_lossy(&load.stdout);
        assert!(
            text.contains("Diagnose Service State") || text.contains("servicectl status"),
            "{text}"
        );
    }

    let profiles = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["domain-profiles"])
        .output()
        .unwrap();
    assert!(profiles.status.success());
    let profiles_text = String::from_utf8_lossy(&profiles.stdout);
    assert!(profiles_text.contains("cli-operations"), "{profiles_text}");
    assert!(
        profiles_text.contains("kubernetes-operations"),
        "{profiles_text}"
    );

    let template = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["domain-template", "cli-operations"])
        .output()
        .unwrap();
    assert!(template.status.success());
    let template_text = String::from_utf8_lossy(&template.stdout);
    assert!(
        template_text.contains("name: cli-operations"),
        "{template_text}"
    );
    assert!(template_text.contains("known_tools"), "{template_text}");
}

#[test]
fn specialized_compile_entrypoints_produce_valid_interface_and_repo_bundles() {
    let temp = tempdir().unwrap();

    let openapi = temp.path().join("openapi.yaml");
    std::fs::write(
        &openapi,
        "openapi: 3.0.0\ninfo:\n  title: Pets API\n  version: 1.2.3\npaths:\n  /pets:\n    get:\n      summary: List pets\n# GET /pets should be used read-only.\n",
    )
    .unwrap();
    let api = temp.path().join("api.txt");
    std::fs::write(
        &api,
        "Version 2.0\nGET /status\nPOST /jobs\nWarning: POST requires approval and idempotency.\n",
    )
    .unwrap();
    let cli = temp.path().join("cli.txt");
    std::fs::write(
        &cli,
        "Version 1.4\nservicectl status\nservicectl restart demo\nWarning: restart requires approval.\n",
    )
    .unwrap();
    let help = temp.path().join("help.txt");
    std::fs::write(
        &help,
        "Usage: widgetctl <command>\n\nwidgetctl status\nwidgetctl delete demo\nWarning: delete is destructive.\n",
    )
    .unwrap();
    let repo = temp.path().join("repo");
    std::fs::create_dir_all(repo.join("docs")).unwrap();
    std::fs::write(
        repo.join("README.md"),
        "# Diagnose Repository\n\nYou should inspect repository status first.\n\n```\nrepoctl status\n```\n",
    )
    .unwrap();
    std::fs::write(
        repo.join("src.rs"),
        "/// Use repoctl status before mutation.\npub fn diagnose_repository() {}\n",
    )
    .unwrap();

    let cases = [
        (
            "compile-openapi",
            openapi.as_path(),
            "openapi-bundle",
            "OpenApi",
            "interface_kind: api",
        ),
        (
            "compile-api",
            api.as_path(),
            "api-bundle",
            "ApiSpec",
            "interface_kind: api",
        ),
        (
            "compile-cli",
            cli.as_path(),
            "cli-bundle",
            "CliSpec",
            "interface_kind: cli",
        ),
        (
            "compile-cli-help",
            help.as_path(),
            "help-bundle",
            "CliHelp",
            "interface_kind: cli",
        ),
        (
            "compile-repo",
            repo.as_path(),
            "repo-bundle",
            "Repository",
            "source_trust:",
        ),
    ];

    for (command, input, name, expected_source_type, expected_skill_text) in cases {
        let out = temp.path().join(format!("out-{command}"));
        let output = Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args([
                command,
                input.to_str().unwrap(),
                "--out",
                out.to_str().unwrap(),
                "--name",
                name,
                "--domain",
                if command.contains("cli") {
                    "cli-operations"
                } else {
                    "api-operations"
                },
            ])
            .output()
            .unwrap();
        assert!(
            output.status.success(),
            "{command} stdout={} stderr={}",
            String::from_utf8_lossy(&output.stdout),
            String::from_utf8_lossy(&output.stderr)
        );

        let validate = Command::new(env!("CARGO_BIN_EXE_skiller"))
            .args(["validate", out.to_str().unwrap()])
            .output()
            .unwrap();
        assert!(
            validate.status.success(),
            "{command} validate stdout={} stderr={}",
            String::from_utf8_lossy(&validate.stdout),
            String::from_utf8_lossy(&validate.stderr)
        );

        let package = std::fs::read_to_string(out.join("package.yaml")).unwrap();
        assert!(
            package.contains(&format!("name: {name}")),
            "{command}: {package}"
        );
        let sources = std::fs::read_to_string(out.join("sources/index.yaml")).unwrap();
        assert!(
            sources.contains(&format!("source_type: {expected_source_type}")),
            "{command}: {sources}"
        );
        let skill_files: Vec<_> = std::fs::read_dir(out.join("skills"))
            .unwrap()
            .map(|entry| entry.unwrap().path())
            .collect();
        assert!(!skill_files.is_empty(), "{command} produced no skills");
        let skills_text = skill_files
            .iter()
            .map(|path| std::fs::read_to_string(path).unwrap())
            .collect::<Vec<_>>()
            .join("\n---\n");
        assert!(
            skills_text.contains(expected_skill_text),
            "{command}: {skills_text}"
        );
        assert!(
            skills_text.contains("source_trust:"),
            "{command}: {skills_text}"
        );
    }
}

#[test]
fn cli_help_advertises_final_mile_workflows() {
    let root_help = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["--help"])
        .output()
        .unwrap();
    assert!(root_help.status.success());
    let root = String::from_utf8_lossy(&root_help.stdout);
    for command in [
        "forge-provider-status",
        "forge-adapter-preflight",
        "forge-adapter-self-test",
        "forge-validate",
        "forge-apply",
        "corpus-manifest",
        "corpus-diff",
        "corpus-plan",
        "corpus-status",
        "verify-agent-proposals",
        "verify-agent-pack",
        "agent-builder-summary",
        "agent-artifact-index",
        "compile-openapi",
        "compile-cli-help",
    ] {
        assert!(
            root.contains(command),
            "root help missing {command}: {root}"
        );
    }

    let build_help = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["build-agent-pack", "--help"])
        .output()
        .unwrap();
    assert!(build_help.status.success());
    let build = String::from_utf8_lossy(&build_help.stdout);
    assert!(build.contains("--lifecycle-status"), "{build}");
    assert!(build.contains("--report"), "{build}");

    let validate_help = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["forge-validate", "--help"])
        .output()
        .unwrap();
    assert!(validate_help.status.success());
    let validate = String::from_utf8_lossy(&validate_help.stdout);
    assert!(validate.contains("--report"), "{validate}");

    let apply_help = Command::new(env!("CARGO_BIN_EXE_skiller"))
        .args(["forge-apply", "--help"])
        .output()
        .unwrap();
    assert!(apply_help.status.success());
    let apply = String::from_utf8_lossy(&apply_help.stdout);
    assert!(apply.contains("--report"), "{apply}");
}
