perf(hunt): the source producer walks and reads the product tree once per change, not once per row

facts_for walked the whole product tree and re-read every product file for each candidate row; on a Laravel
application one change of 33 rows took four minutes, 96 percent of it there. The producer context already
holds the product file list and the texts it read for entry points, so facts_for and callers_of now accept
them. Same facts, tested by a call whose tree walk raises and whose file on disk is emptied after reading.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
Marius Ciepluch
2026-10-14 09:35:16 +02:00
co-authored by Claude Fable 5.1
parent 4063fd1562
commit 86d70831c8
3 changed files with 42 additions and 9 deletions
+3 -2
View File
@@ -250,12 +250,13 @@ def _roles(function: Any, facts: Any) -> dict[str, str]:
return roles
def _obligations(path: str, ctx: Context) -> list[dict[str, object]]:
def _obligations(path: str, ctx: Context) -> list[dict[str, object]] | None:
ir, text = ctx.ir(path), ctx.read(path)
facts, flow = load_obligation_facts(), load_facts()
scoped = facts.for_language(ir.language)
if not scoped.operations and not scoped.dischargers:
raise _Absent("obligations_language_unsupported")
# A missing fact table is not a failed parse or a checked empty result.
return None
dominance = OrderDominance({path: text})
result = check_obligations(
irs={path: (ir, text)},