Buyer problem
reconstruct() already computes the winning edge's per-channel scores (temporal, secondary_key, text, optional llm), but the production persistence contract deliberately collapses each edge to (parent_post_id, child_post_id, fused_score). /api/lineage and the Buyer DAG therefore expose only the fused score.
A buyer can see that two posts were linked but cannot answer:
- which independent signals supported the edge;
- whether the optional LLM channel participated;
- which signal dominated the decision;
- whether an edge was created from weak agreement across channels or one strong signal;
- how to audit a later reconstruction after model or weight changes.
This is a distinct follow-up to the #264 ontology relationship-path repair. Do not mix the two: #264 explains typed Knowledge Graph paths; this issue explains reconstructed post-to-post lineage evidence.
Verified causal boundary
lineageweave.models.Edge retains channel_scores.
lineageweave.reconstruct._best_parent returns the winner's channel scores.
lineageweave.lineage_persistence documents a three-column persistence projection.
backend.app.lineage_ingestion.persist_lineage_edges writes only fused_score.
post_lineage_edge stores only fused_score.
visible_lineage_graph and frontend/src/api.ts::LineageGraphEdge return only fused_score.
Required design
3NF persistence
Add an effective schema migration with a child table such as:
post_lineage_edge_signal
- parent_post_id
- child_post_id
- signal_code
- signal_score
- signal_weight
- signal_contribution
Requirements:
- composite FK to
post_lineage_edge with ON DELETE CASCADE;
- one row per edge and active signal;
- controlled lookup values for
temporal, secondary_key, text, and llm;
- exact decimal constraints for score/weight/contribution;
- no JSONB for billable/auditable numeric facts;
- no synthetic
llm row when the LLM channel is unavailable;
- weights must be the normalized active weights actually used for that reconstruction;
- contribution must reconcile with the persisted fused score within an explicit numerical tolerance.
API and Buyer surface
Return an additive, typed channel_evidence collection on each visible lineage edge. Each item must include:
signal_code
signal_label
score
weight
contribution
rank
The Buyer surface must provide:
- an accessible edge-detail disclosure;
- exact values without hover-only interaction;
- a plain-language explanation that the relation is inferred, not causal;
- a clear indication when no LLM channel participated;
- deterministic ordering by contribution, then controlled signal order;
- print/export preservation of the same values.
Provenance and versioning
Persist or reference the reconstruction version, active weight profile, and generated-at time so a later rebuild cannot silently rewrite the meaning of historic evidence. PostgreSQL remains authoritative; PROV-O/RDF export is a projection, not the source of truth.
RED → GREEN acceptance tests
- A reconstructed edge with four channels round-trips all four scores and normalized weights through PostgreSQL and
/api/lineage.
- A no-LLM reconstruction persists exactly three channels and never fabricates an LLM score.
- Signal contributions reconcile to
fused_score within the documented tolerance.
- Rebuilding replaces edge and signal rows atomically; no orphan or stale signal rows remain.
- Duplicate rebuilds are idempotent.
- ABAC filtering never reveals evidence for an invisible endpoint.
- The UI renders exact values with keyboard and screen-reader access.
- The UI labels the edge as inferred and does not state causality.
- Migration clean-install and upgrade rehearsal pass.
- Production statement/branch coverage and public API docstrings remain 100%.
Non-goals
- Do not re-score or reinterpret old edges without an explicit reconstruction run.
- Do not make one channel authoritative.
- Do not expose prompts, responses, credentials, or private source text in the evidence payload.
- Do not fold this into the current ontology-path repair or the GNB navigation behavior.
Buyer problem
reconstruct()already computes the winning edge's per-channel scores (temporal,secondary_key,text, optionalllm), but the production persistence contract deliberately collapses each edge to(parent_post_id, child_post_id, fused_score)./api/lineageand the Buyer DAG therefore expose only the fused score.A buyer can see that two posts were linked but cannot answer:
This is a distinct follow-up to the #264 ontology relationship-path repair. Do not mix the two: #264 explains typed Knowledge Graph paths; this issue explains reconstructed post-to-post lineage evidence.
Verified causal boundary
lineageweave.models.Edgeretainschannel_scores.lineageweave.reconstruct._best_parentreturns the winner's channel scores.lineageweave.lineage_persistencedocuments a three-column persistence projection.backend.app.lineage_ingestion.persist_lineage_edgeswrites onlyfused_score.post_lineage_edgestores onlyfused_score.visible_lineage_graphandfrontend/src/api.ts::LineageGraphEdgereturn onlyfused_score.Required design
3NF persistence
Add an effective schema migration with a child table such as:
Requirements:
post_lineage_edgewithON DELETE CASCADE;temporal,secondary_key,text, andllm;llmrow when the LLM channel is unavailable;API and Buyer surface
Return an additive, typed
channel_evidencecollection on each visible lineage edge. Each item must include:The Buyer surface must provide:
Provenance and versioning
Persist or reference the reconstruction version, active weight profile, and generated-at time so a later rebuild cannot silently rewrite the meaning of historic evidence. PostgreSQL remains authoritative; PROV-O/RDF export is a projection, not the source of truth.
RED → GREEN acceptance tests
/api/lineage.fused_scorewithin the documented tolerance.Non-goals