Summary
convergence_payload_matches_filters is the post-serialization payload_filter used to apply a ?message_id= filter to convergence runs (there is no SQL-level message_id filter in filtered_convergence_runs). One of its match branches reads a key that the payload it inspects never contains, so the branch is dead — it can never return True.
Location
# forensics/views.py:1914-1916 (convergence_payload_matches_filters)
for ref in payload.get("evidence_refs", []):
if message_id == ref.get("message_id"):
return True
Every element of payload["evidence_refs"] is produced by evidence_ref_payload:
# forensics/views.py:2285-2296 (evidence_ref_payload)
def evidence_ref_payload(event: AuditEvent) -> dict:
return {
"event_id": event.id,
"audit_file_id": event.audit_file_id,
"line_number": event.line_number,
"line_hash": event.line_hash,
"schema_version": event.schema_version,
"audit_data_mode": event.audit_data_mode,
"event_type": event.event_type,
"wall_time_ms": event.wall_time_ms,
"api_path": reverse("api-event-evidence", kwargs={"event_id": event.id}),
}
There is no message_id key. convergence_run_payload sets "evidence_refs": evidence_refs from evidence_refs_payload → evidence_ref_payload (views.py:2776, 2796, 2299-2300), so ref.get("message_id") is unconditionally None. The function only runs with a non-empty message_id (it early-returns True at views.py:1903-1904 when blank), so message_id == None is always False. The loop can never match.
Failure scenario / impact (LOW)
filtered_convergence_runs (views.py:1870-1898) has no SQL message_id filter, so message-scoped convergence filtering relies entirely on this predicate (wired as payload_filter at views.py:989, 1063, 1337 whenever message_id is set). A convergence run is therefore matched for ?message_id=<m> only when <m> appears as selected_branch_id, a losing_branch_id, a candidate branch_id, or a candidate commit_id — the evidence-ref branch contributes nothing.
The practical impact is small because branch/commit ids usually carry the same identifiers, so results rarely change. But this is a real latent gap: the branch was evidently intended to also match a run through its evidence events' message ids, and it silently does nothing.
Suggested fix
Decide which of these the branch was meant to be, and do it explicitly:
- If evidence-ref message matching was intended: add a
message_id to evidence_ref_payload (or match against the convergence evidence events' normalized msg_id/outbound_msg_id), and ideally push it into a SQL filter in filtered_convergence_runs for consistency with the other projection sections.
- If not: delete the dead loop (
views.py:1914-1916) so the matching contract is clear.
Why this is not a duplicate
The filed message_id-filter issues are #239 (Evidence surface matches only msg_id/outbound_msg_id), #250 (mode-changes ignore message_id/epoch), and #247 (action message_id applied after truncation). None concern convergence runs or the evidence_ref_payload schema mismatch; #200/#212/#156 are about scan cost / integer overflow, unrelated to this matching logic.
Summary
convergence_payload_matches_filtersis the post-serializationpayload_filterused to apply a?message_id=filter to convergence runs (there is no SQL-levelmessage_idfilter infiltered_convergence_runs). One of its match branches reads a key that the payload it inspects never contains, so the branch is dead — it can never returnTrue.Location
Every element of
payload["evidence_refs"]is produced byevidence_ref_payload:There is no
message_idkey.convergence_run_payloadsets"evidence_refs": evidence_refsfromevidence_refs_payload→evidence_ref_payload(views.py:2776,2796,2299-2300), soref.get("message_id")is unconditionallyNone. The function only runs with a non-emptymessage_id(it early-returnsTrueatviews.py:1903-1904when blank), somessage_id == Noneis alwaysFalse. The loop can never match.Failure scenario / impact (LOW)
filtered_convergence_runs(views.py:1870-1898) has no SQLmessage_idfilter, so message-scoped convergence filtering relies entirely on this predicate (wired aspayload_filteratviews.py:989,1063,1337whenevermessage_idis set). A convergence run is therefore matched for?message_id=<m>only when<m>appears asselected_branch_id, alosing_branch_id, a candidatebranch_id, or a candidatecommit_id— the evidence-ref branch contributes nothing.The practical impact is small because branch/commit ids usually carry the same identifiers, so results rarely change. But this is a real latent gap: the branch was evidently intended to also match a run through its evidence events' message ids, and it silently does nothing.
Suggested fix
Decide which of these the branch was meant to be, and do it explicitly:
message_idtoevidence_ref_payload(or match against the convergence evidence events' normalizedmsg_id/outbound_msg_id), and ideally push it into a SQL filter infiltered_convergence_runsfor consistency with the other projection sections.views.py:1914-1916) so the matching contract is clear.Why this is not a duplicate
The filed
message_id-filter issues are #239 (Evidence surface matches onlymsg_id/outbound_msg_id), #250 (mode-changes ignoremessage_id/epoch), and #247 (actionmessage_idapplied after truncation). None concern convergence runs or theevidence_ref_payloadschema mismatch; #200/#212/#156 are about scan cost / integer overflow, unrelated to this matching logic.