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:
Tarik Moussa
2026-06-14 08:37:42 +02:00
parent 967e6baa59
commit 8cf0cc7e01
14 changed files with 2139 additions and 0 deletions

View File

@@ -134,6 +134,59 @@ class Settings(BaseSettings):
),
)
# ------------------------------------------------------------------
# F-13 Synthesis / Lead-Engine
# ------------------------------------------------------------------
leads_dir: str = Field(
default="leads/",
description=(
"Directory where generated leads are written. "
"Grounded leads → leads/grounded/, conjectures → leads/conjectures/. "
"Relative paths are resolved from the current working directory."
),
)
synthesis_llm_model: str = Field(
default="qwen2.5:7b",
description=(
"Ollama model name used for synthesis. "
"Must be available at the configured Ollama endpoint."
),
)
synthesis_llm_url: str | None = Field(
default=None,
description=(
"Ollama base URL for synthesis. "
"When None, falls back to OLLAMA_BASE_URL."
),
)
synthesis_top_k: int = Field(
default=20,
gt=0,
description="Number of top chunks retrieved per topic for synthesis.",
)
synthesis_min_grounded_ratio: float = Field(
default=0.5,
ge=0.0,
le=1.0,
description=(
"Minimum fraction of claims that must be grounded for a lead to be "
"emitted as grounded (not marked ⚠)."
),
)
synthesis_gap_min_coverage: int = Field(
default=2,
gt=0,
description=(
"Minimum number of papers covering a topic before it is NOT flagged as a gap. "
"Topics with fewer papers → gap lead generated."
),
)
# ------------------------------------------------------------------
# F-09 Rich Parsing
# ------------------------------------------------------------------