a3s 0.10.3

a3s — A3S coding agent CLI; `a3s code` launches the interactive TUI
Documentation
use super::test_support::*;
use super::*;

fn one_dimension_fixture(
    outcome: AcquisitionOutcome,
    with_source: bool,
) -> (ResearchContract, SourceCatalog) {
    let spec = spec(
        EvidenceScope::Web,
        vec![dimension("answer", &["answer-target"])],
        vec![named_target(
            "answer-target",
            "answer_family",
            SourceRole::Primary,
            SourceIdentity::Domain("answer.example.test".to_string()),
        )],
        budget(1, 1),
    );
    let plan = plan(
        &spec,
        vec![query(
            "q-answer",
            AcquisitionTransport::Web,
            QueryMode::Exact,
            &["answer"],
            &["answer-target"],
            1,
        )],
        vec![],
    );
    let contract = validate_research_contract(spec, plan).expect("outcome contract");
    let sources = with_source
        .then(|| {
            let chunks = vec![SourceChunk {
                id: "source-answer:chunk:1".to_string(),
                text: "The primary record establishes the answer.".to_string(),
            }];
            SourceRecord {
                id: "source-answer".to_string(),
                title: "Primary answer".to_string(),
                requested_anchor: "https://answer.example.test/record".to_string(),
                canonical_anchor: "https://answer.example.test/record".to_string(),
                captured_at: "2026-07-21T00:00:00Z".to_string(),
                provenance: vec![SourceProvenance {
                    query_id: "q-answer".to_string(),
                    source_target_id: "answer-target".to_string(),
                }],
                content_digest: source_content_digest(&chunks),
                chunks,
            }
        })
        .into_iter()
        .collect();
    let catalog = SourceCatalog {
        spec_digest: research_spec_digest(&contract.spec),
        attempts: vec![AcquisitionAttempt {
            query_id: "q-answer".to_string(),
            source_target_ids: vec!["answer-target".to_string()],
            outcome,
        }],
        sources,
    };
    (contract, catalog)
}

#[test]
fn completed_requires_claims_only_for_every_material_dimension() {
    let (contract, catalog) = one_dimension_fixture(AcquisitionOutcome::Fetched, true);
    let ledger = admit_claim_ledger(
        &contract,
        &catalog,
        ClaimLedgerProposal {
            claims: vec![ClaimProposal {
                id: "answer-fact".to_string(),
                dimension_id: "answer".to_string(),
                placement: ClaimPlacement::DirectAnswer,
                kind: ClaimKind::Fact,
                text: "The primary record establishes the answer.".to_string(),
                evidence_refs: vec![ClaimEvidenceRef {
                    source_id: "source-answer".to_string(),
                    chunk_ids: vec!["source-answer:chunk:1".to_string()],
                }],
                basis_claim_ids: vec![],
                derivation: None,
            }],
            relations: vec![],
            gaps: vec![],
        },
    )
    .expect("completed ledger");
    let document = build_report_document(&contract, &catalog, &ledger).expect("completed document");

    assert_eq!(
        report_structural_outcome(&document),
        StructuralOutcome::Completed
    );
}

#[test]
fn a_useful_claim_with_a_material_gap_is_qualified_not_completed() {
    let replay = super::frozen_fixture::load_frozen_replays()
        .into_iter()
        .find(|replay| replay.id == "F03")
        .expect("F03 replay");
    let ledger = admit_claim_ledger(&replay.contract, &replay.catalog, replay.proposal)
        .expect("qualified ledger");
    let document = build_report_document(&replay.contract, &replay.catalog, &ledger)
        .expect("qualified document");

    assert_eq!(
        report_structural_outcome(&document),
        StructuralOutcome::Qualified
    );
}

#[test]
fn preserved_sources_without_claims_are_source_backed() {
    let (contract, catalog) = one_dimension_fixture(AcquisitionOutcome::Fetched, true);
    let document =
        build_source_backed_document(&contract, &catalog).expect("source-backed document");

    assert_eq!(
        report_structural_outcome(&document),
        StructuralOutcome::SourceBacked
    );
}

#[test]
fn an_honest_gap_without_claims_or_sources_is_degraded() {
    let (contract, catalog) = one_dimension_fixture(AcquisitionOutcome::NoCandidates, false);
    let ledger = admit_claim_ledger(
        &contract,
        &catalog,
        ClaimLedgerProposal {
            claims: vec![],
            relations: vec![],
            gaps: vec![GapProposal {
                id: "answer-gap".to_string(),
                dimension_id: "answer".to_string(),
                text: "The bounded acquisition did not find a usable source.".to_string(),
                attempted_query_ids: vec!["q-answer".to_string()],
                missing_source_target_ids: vec!["answer-target".to_string()],
            }],
        },
    )
    .expect("degraded ledger");
    let document = build_report_document(&contract, &catalog, &ledger).expect("degraded document");

    assert_eq!(
        report_structural_outcome(&document),
        StructuralOutcome::Degraded
    );
}