fix(synthesis): namespace lead ids by kind to stop collisions (audit C-11)

Each stage (find_connections/gaps/improvements/propose_conjectures) numbered its
leads from seq=1, so connection L-0001, gap L-0001 and improvement L-0001 all
resolved to grounded/L-0001.md. The CLI concatenates them
(connections + gaps + improvements) and write_leads writes in order, so later
kinds silently overwrote earlier ones — survivors = max(per-kind count), not the
sum. Reproduced: 4 distinct leads -> 2 files.

_make_lead_id now takes the kind and emits L-C-/L-G-/L-I-/L-X- prefixes, keeping
same-seq ids distinct across stages. Regression tests cover the format and the
no-collision invariant.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Tarik Moussa
2026-06-15 11:09:24 +02:00
parent 115bb63f2d
commit 3092f1814e
3 changed files with 38 additions and 12 deletions

View File

@@ -76,7 +76,7 @@ def test_finalise_grounded_lead_succeeds_when_grounded(
)
assert lead is not None
assert lead.kind == "connection"
assert lead.id == "L-0001"
assert lead.id == "L-C-0001" # kind-prefixed id (audit C-11)
assert lead.confidence >= 0.5
assert lead.status == "unverified"
assert lead.suggested_validation == ""

View File

@@ -92,9 +92,21 @@ def test_lead_empty_provenance_raises() -> None:
def test_lead_id_format_helper() -> None:
"""The internal _make_lead_id helper produces zero-padded L-XXXX strings."""
"""_make_lead_id produces a kind-prefixed, zero-padded L-<K>-XXXX string."""
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"
assert _make_lead_id(1, "connection") == "L-C-0001"
assert _make_lead_id(42, "gap") == "L-G-0042"
assert _make_lead_id(9999, "improvement") == "L-I-9999"
assert _make_lead_id(1, "conjecture") == "L-X-0001"
def test_lead_id_namespaced_per_kind_no_collision() -> None:
"""Regression for audit C-11: each stage numbers from 1, so without a kind
prefix connection/gap/improvement all collide on L-0001 and overwrite one
another in grounded/. The prefix must keep same-seq ids distinct.
"""
from codex.synthesis import _make_lead_id
ids = {_make_lead_id(1, k) for k in ("connection", "gap", "improvement", "conjecture")}
assert len(ids) == 4, f"same-seq ids must stay distinct across kinds, got {ids}"