"""The frequency-sorted review queue — the pipeline's actual product. Ordering is the product decision here, and it targets the bottleneck directly: the expert is the scarce resource, so they should hit the terms whose definition propagates furthest first. Conflicts are promoted above frequency regardless, because a contradiction is a decision only they can make. Each row carries page, section and the verbatim span so review is a matter of checking a quote against a page, not a claim against memory. That is what makes the queue finishable. v2 dropped `extraction_status`, `diff_status`, `definition_conflict` and `conflict_variants` from the row: all four are already expressed by `review_reason`, which is the field a reviewer actually reads, and they remain on the entry for a consumer that wants them. """ from __future__ import annotations def build_queue(entries: list[dict], domain: dict | None = None) -> list[dict]: """Rows an expert works through, most-decision-worthy first. **The queue is no longer glossary-only.** Every row carries `row_kind`, and a consumer must switch on it rather than assume `term`: an unresolved key parameter has a surface and no term, no definition and no mention count. """ def sort_key(entry: dict): conflicting = ( entry.get("diff_status") == "conflicting" or entry.get("definition_conflict") is True ) return (0 if conflicting else 2, -int(entry.get("mention_count", 0) or 0)) queue = [] for entry in sorted(entries, key=sort_key): provenance = entry.get("provenance") or {} queue.append( { "row_kind": "term", # What an approve/edit/reject decision attaches to. Without it a # re-run loses which rows the expert already cleared. "term_id": entry.get("term_id"), "term": entry.get("term"), "definition": entry.get("definition"), "source_wording": entry.get("source_wording"), "mention_count": entry.get("mention_count", 0), "page": provenance.get("page"), "section_no": provenance.get("section_no"), "span": provenance.get("span"), "review_reason": _reason(entry), "_sort": sort_key(entry), } ) queue.extend(_key_parameter_rows(domain)) # Ranked once, over both kinds. Unresolved parameters sit between conflicts # and routine confirmations: a contradiction is still the more urgent call, # but "the model named a parameter nothing in the document corroborates" is # a judgement only the expert can make, and it should not sink beneath 60 # routine rows where nobody reaches it. queue.sort(key=lambda row: row["_sort"]) for rank, row in enumerate(queue, start=1): row["rank"] = rank del row["_sort"] return queue def _key_parameter_rows(domain: dict | None) -> list[dict]: """One row per key parameter that resolved to no extracted term. Resolved parameters produce no row — they are corroborated and need nothing from a human. Only the dangles are worth an expert's attention, and they are worth it precisely because this branch has no span check: an unresolvable surface is the one hallucination signal it can produce. """ provenance = (domain or {}).get("provenance") or {} rows = [] for parameter in (domain or {}).get("key_parameters") or []: if parameter.get("term_id"): continue rows.append( { "row_kind": "key_parameter", # The decision attaches to the domain entry plus the surface — # there is no term_id, which is the entire point of the row. "term_id": None, "domain_id": (domain or {}).get("domain_id"), "term": parameter.get("surface"), "definition": None, "source_wording": None, "mention_count": 0, "page": provenance.get("page"), "section_no": provenance.get("section_no"), "span": provenance.get("span"), "review_reason": ( "named as a key parameter but no extracted term corroborates it " "— confirm it is real" ), "_sort": (1, 0), } ) return rows def _reason(entry: dict) -> str: if entry.get("definition_conflict") or entry.get("diff_status") == "conflicting": return "conflicting definitions — expert decision required" if _wording_differs(entry): return "source wording differs from the expanded name — confirm which is correct" if entry.get("extraction_status") == "no_definition_found": return "term found but no definition in document" if not entry.get("definition"): return "definition rejected by span check or absent" return "routine confirmation" def _wording_differs(entry: dict) -> bool: """The document says "Physical of Availability"; the expansion says "Physical Availability". Surfacing that to the expert is a locked requirement, so it earns its own review reason.""" source = (entry.get("source_wording") or "").strip().casefold() full = (entry.get("full_name") or "").strip().casefold() return bool(source and full) and full not in source