a3s 0.10.1

a3s — A3S coding agent CLI; `a3s code` launches the interactive TUI
Documentation
use super::*;
use a3s::research::{EvidenceRef, InquiryEvent, InquiryLimits, Question, ResearchMethod};

fn observed_workflow_output(sources: &[&str]) -> String {
    let sources = sources
        .iter()
        .enumerate()
        .map(|(index, url)| {
            serde_json::json!({
                "title": format!("Observed source {index}"),
                "url_or_path": url,
                "quote_or_fact": "Observed evidence for the report.",
                "reliability": "Traceable source"
            })
        })
        .collect::<Vec<_>>();
    serde_json::json!({
        "mode": "direct_web",
        "checker": { "decision": "finalize" },
        "research": {
            "status": "success",
            "results": [{
                "success": true,
                "structured": {
                    "summary": "Observed evidence",
                    "sources": sources,
                    "key_evidence": ["Observed evidence for the report."],
                    "contradictions": [],
                    "confidence": "high",
                    "gaps": []
                }
            }]
        }
    })
    .to_string()
}

#[test]
fn completed_artifact_validation_rejects_an_outlining_inquiry() {
    let events = vec![
        InquiryEvent::StrategySelected {
            method: ResearchMethod::Focused,
        },
        InquiryEvent::QuestionsQueued {
            questions: vec![Question::queued(
                "question:one",
                None,
                "What does the evidence establish?",
            )],
        },
        InquiryEvent::EvidenceAccepted {
            evidence: EvidenceRef::new(
                "evidence:one",
                vec!["claim:one".to_string()],
                vec!["source:one".to_string()],
            ),
        },
        InquiryEvent::QuestionAnswered {
            question_id: "question:one".to_string(),
            answer: "The evidence establishes the finding.".to_string(),
            evidence_ids: vec!["evidence:one".to_string()],
        },
    ];
    let state = a3s::research::replay(&events, &InquiryLimits::default()).unwrap();
    let workflow = serde_json::json!({
        "inquiry": {"events": events, "state": state}
    })
    .to_string();

    let error = validate_deep_research_completed_report_content(
        "# Report\n\nA sufficiently long report body that would otherwise continue through the remaining validation gates.",
        "<!doctype html><html><body>report</body></html>",
        "query",
        &workflow,
        None,
    )
    .expect_err("Outlining evidence readiness must not publish a completed artifact");
    assert!(error.contains("current phase is Outlining"), "{error}");
}

#[test]
fn source_display_domains_do_not_become_unobserved_local_citations() {
    let query = "Compare runtime maintenance";
    let crates = "https://crates.io/crates/runtime";
    let docs = "https://docs.rs/runtime";
    let workflow_output = observed_workflow_output(&[crates, docs]);
    let markdown = format!(
        "# Runtime report\n\n\
         ## Findings\n\nThe runtime remains maintained, with explicit limitations in the available evidence.\n\n\
         ## Sources\n\n\
         - Runtime on crates.io — {crates}\n\
         - Runtime documentation on docs.rs — {docs}\n\n\
         ## Limitations\n\nConfidence is bounded by the cited evidence.\n"
    );
    let html = deep_research_completed_report_html(query, &markdown);

    assert!(
        deep_research_report_content_sources_trace_workflow(
            &markdown,
            &html,
            query,
            &workflow_output,
            None,
        ),
        "display-only domain names must not be treated as local source paths"
    );
}

#[test]
fn legitimate_unavailable_finding_is_not_placeholder_content() {
    let markdown = "# Runtime report\n\n## Findings\n\nIndependent adoption data is not yet available, so the report does not claim a precise market share. The available release history still supports a bounded maintenance comparison.\n\n## Sources\n\n- https://example.com/releases\n\n## Limitations\n\nConfidence is limited by the unavailable adoption data.\n";
    let html = deep_research_completed_report_html("Runtime report", markdown);

    assert!(
        has_research_report_substance(markdown, &html),
        "a disclosed evidence gap is a legitimate finding, not placeholder text"
    );
}

#[test]
fn source_trace_diagnostic_names_every_unobserved_citation() {
    let observed = "https://example.com/observed";
    let unobserved = "https://example.com/unobserved";
    let workflow_output = observed_workflow_output(&[observed]);
    let markdown = format!(
        "# Runtime report\n\n## Findings\n\nA substantive comparison with bounded confidence and explicit limitations.\n\n## Sources\n\n- {observed}\n- {unobserved}\n\n## Limitations\n\nConfidence is limited to observed evidence.\n"
    );
    let html = deep_research_completed_report_html("Runtime report", &markdown);

    let diagnostic = deep_research_report_source_trace_diagnostic(
        &markdown,
        &html,
        "Runtime report",
        &workflow_output,
        None,
    )
    .expect_err("an unobserved citation must be rejected with a diagnostic");

    assert!(diagnostic.contains(unobserved), "{diagnostic}");
    assert!(
        diagnostic.contains("not observed in this run"),
        "{diagnostic}"
    );
}

#[test]
fn generated_report_sanitizes_one_unobserved_url_without_discarding_the_report() {
    let observed = "https://example.com/observed";
    let mistyped = "https://example.com/obesrved";
    let workflow_output = observed_workflow_output(&[observed]);
    let markdown = format!(
        "# Runtime report\n\n\
         ## Findings\n\nA supported finding cites [the observed source]({observed}), while the readable label from [a mistyped duplicate]({mistyped}) should remain without an unsafe link.\n\n\
         ## Sources\n\n\
         - [Observed source]({observed})\n\
         - [Mistyped duplicate]({mistyped})\n\n\
         ## Limitations\n\nConfidence is bounded by the one observed source and the unavailable corroboration.\n"
    );

    let cleaned = sanitize_unobserved_markdown_http_citations(
        &markdown,
        "Runtime report",
        &workflow_output,
        None,
    );
    assert!(cleaned.contains(observed), "{cleaned}");
    assert!(!cleaned.contains(mistyped), "{cleaned}");
    assert!(cleaned.contains("a mistyped duplicate"), "{cleaned}");
    assert!(cleaned.contains("- Mistyped duplicate"), "{cleaned}");

    let html = deep_research_completed_report_html("Runtime report", &cleaned);
    deep_research_report_source_trace_diagnostic(
        &cleaned,
        &html,
        "Runtime report",
        &workflow_output,
        None,
    )
    .expect("the remaining report should still pass the complete source trace gate");
}

#[test]
fn generated_report_sanitizes_an_unobserved_autolink_inside_bold_disclosure() {
    let observed = "https://example.com/observed";
    let unavailable = "https://github.com/launchbadge/sqlx/issues/1669";
    let workflow_output = observed_workflow_output(&[observed]);
    let markdown = format!(
        "# Runtime report\n\n\
         > [!CAUTION]\n\
         > **Evidence boundaries**\n\
         >\n\
         > - **Fetch retained no substantive text for {unavailable}.** — The unavailable page remains an explicit evidence gap.\n\n\
         ## Findings\n\nA substantive comparison remains supported by [the observed source]({observed}).\n\n\
         ## Sources\n\n- [Observed source]({observed})\n\n\
         ## Limitations\n\nConfidence is bounded by the unavailable corroboration.\n"
    );

    let cleaned = sanitize_unobserved_markdown_http_citations(
        &markdown,
        "Runtime report",
        &workflow_output,
        None,
    );

    assert!(cleaned.contains(observed), "{cleaned}");
    assert!(!cleaned.contains(unavailable), "{cleaned}");
    assert!(
        cleaned.contains("Fetch retained no substantive text"),
        "{cleaned}"
    );
    let html = deep_research_completed_report_html("Runtime report", &cleaned);
    deep_research_report_source_trace_diagnostic(
        &cleaned,
        &html,
        "Runtime report",
        &workflow_output,
        None,
    )
    .expect("bold evidence-boundary prose must not create an unobserved citation");
}

#[test]
fn internal_status_text_has_a_specific_rejection_diagnostic() {
    let workflow_output = observed_workflow_output(&["https://example.com/observed"]);
    let answer = "# Runtime report\n\n## Findings\n\nDynamicWorkflowRuntime output: an internal transport record that must never be published. This deliberately contains enough additional text to pass the length boundary.\n\n## Sources\n\n- https://example.com/observed\n\n## Limitations\n\nConfidence is bounded.\n";

    let diagnostic = deep_research_report_rejection_diagnostic_from_answer_text(
        "Runtime report",
        answer,
        &workflow_output,
        None,
    )
    .expect("internal status text must have a rejection diagnostic");

    assert!(
        diagnostic.contains("internal workflow or tool-status"),
        "{diagnostic}"
    );
}