feat(synthesis): Lead/Provenance model + grounded leads + conjecture generator
- codex/synthesis.py: Lead/Provenance dataclasses, find_connections/gaps/improvements, propose_conjectures (status=unverified, quarantined in leads/conjectures/), write_leads (grounded→leads/grounded/, conjectures→leads/conjectures/ HARD invariant) - codex/cli.py: synthesis leads/conjectures/report command group - codex/config.py: F-13 settings (leads_dir, synthesis_llm_*, synthesis_top_k, synthesis_min_grounded_ratio, synthesis_gap_min_coverage) - tests/synthesis/: 42 tests covering model, grounding, conjecture invariant, quarantine, CLI - spike/: F-13 spike script + output (live run attempted) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
87
tests/synthesis/test_model.py
Normal file
87
tests/synthesis/test_model.py
Normal file
@@ -0,0 +1,87 @@
|
||||
"""Tests for the Lead / Provenance datamodel (F-13)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import get_args
|
||||
|
||||
import pytest
|
||||
|
||||
from codex.synthesis import Lead, LeadKind, LeadStatus, Provenance
|
||||
|
||||
|
||||
def test_provenance_requires_bibkey_and_locator() -> None:
|
||||
"""Provenance has two required positional fields."""
|
||||
p = Provenance(bibkey="Smith2020", locator="chunk 7")
|
||||
assert p.bibkey == "Smith2020"
|
||||
assert p.locator == "chunk 7"
|
||||
|
||||
|
||||
def test_provenance_missing_field_raises() -> None:
|
||||
"""Provenance without locator must fail to construct (TypeError)."""
|
||||
with pytest.raises(TypeError):
|
||||
Provenance(bibkey="Smith2020") # type: ignore[call-arg]
|
||||
|
||||
|
||||
def test_lead_required_fields() -> None:
|
||||
"""Lead requires id, kind, title, body, provenance, confidence."""
|
||||
lead = Lead(
|
||||
id="L-0001",
|
||||
kind="connection",
|
||||
title="A connection",
|
||||
body="Some body text.",
|
||||
provenance=[Provenance(bibkey="X2020", locator="chunk 1")],
|
||||
confidence=0.8,
|
||||
)
|
||||
assert lead.id == "L-0001"
|
||||
assert lead.kind == "connection"
|
||||
assert lead.status == "unverified" # default
|
||||
assert lead.suggested_validation == "" # default
|
||||
assert lead.claims == [] # default factory
|
||||
|
||||
|
||||
def test_lead_kind_literals_exact() -> None:
|
||||
"""Lead kinds are exactly: connection, gap, improvement, conjecture."""
|
||||
assert set(get_args(LeadKind)) == {"connection", "gap", "improvement", "conjecture"}
|
||||
|
||||
|
||||
def test_lead_status_literals_exact() -> None:
|
||||
"""Lead statuses are exactly: unverified, spiking, go, no-go."""
|
||||
assert set(get_args(LeadStatus)) == {"unverified", "spiking", "go", "no-go"}
|
||||
|
||||
|
||||
def test_lead_provenance_can_be_multiple() -> None:
|
||||
"""A Lead may carry multiple provenance pointers."""
|
||||
provs = [
|
||||
Provenance(bibkey="A2020", locator="chunk 1"),
|
||||
Provenance(bibkey="B2021", locator="chunk 7"),
|
||||
]
|
||||
lead = Lead(
|
||||
id="L-0002",
|
||||
kind="gap",
|
||||
title="t",
|
||||
body="b",
|
||||
provenance=provs,
|
||||
confidence=0.5,
|
||||
)
|
||||
assert len(lead.provenance) == 2
|
||||
|
||||
|
||||
def test_lead_missing_provenance_field_raises() -> None:
|
||||
"""Constructing Lead without provenance is a TypeError."""
|
||||
with pytest.raises(TypeError):
|
||||
Lead( # type: ignore[call-arg]
|
||||
id="L-0003",
|
||||
kind="connection",
|
||||
title="x",
|
||||
body="y",
|
||||
confidence=0.5,
|
||||
)
|
||||
|
||||
|
||||
def test_lead_id_format_helper() -> None:
|
||||
"""The internal _make_lead_id helper produces zero-padded L-XXXX strings."""
|
||||
from codex.synthesis import _make_lead_id
|
||||
|
||||
assert _make_lead_id(1) == "L-0001"
|
||||
assert _make_lead_id(42) == "L-0042"
|
||||
assert _make_lead_id(9999) == "L-9999"
|
||||
Reference in New Issue
Block a user