Fix 9 seam-review findings

- Pinned target's profitability flag now overrides the extractor's bucket guess
- Extractor/target periods canonicalized so ledger forecast chaining matches
- autoRunOnDrop no longer error-loops on ungradeable inbox content; failed
  batches count as seen
- Ship 2 default graders (pipeline requires >=2 valid reports per deck)
- UI styles 'failed' deck chips as errors; .markdown discoverable
- Unknown adjudicator model disables adjudication loudly instead of silently
- Reserved rids extractor/adjudicator; teardown also clears bm-grader-* containers

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jonathan Kirkwood
2026-07-06 14:26:41 -05:00
co-authored by Claude Fable 5
parent b1d7aed9f4
commit 1d1074b625
9 changed files with 54 additions and 15 deletions
+8 -2
View File
@@ -87,7 +87,8 @@ def _resolve_target(kpi: dict, pinned_targets: list[dict], prior_targets: list[d
cand, via = match_kpi(canon, pinned_cands, aliases)
if cand is not None:
p = cand["_src"]
return {"target": p.get("target"), "direction": p.get("direction") or kpi.get("direction")}, "pinned", via
return {"target": p.get("target"), "direction": p.get("direction") or kpi.get("direction"),
"profitability": p.get("profitability")}, "pinned", via
cand, via = match_kpi(canon, prior_targets or [], aliases)
if cand is not None:
return {"target": cand.get("target"),
@@ -166,10 +167,15 @@ def score_deck(extraction: dict, grades: list[dict], pinned_targets: list[dict],
target = float(tgt["target"])
credit = _credit(float(k.get("actual", 0)), target,
tgt.get("direction") or k.get("direction") or "gte", floor)
# The operator's pinned classification beats the extractor's guess —
# a pinned profitability KPI must score in the profitability bucket.
profitability = bool(k.get("profitability"))
if tgt is not None and tgt.get("profitability") is not None:
profitability = bool(tgt["profitability"])
kpi_results.append({
"canonical_name": k.get("canonical_name"), "name": k.get("name"),
"actual": k.get("actual"), "unit": k.get("unit") or "",
"direction": k.get("direction"), "profitability": bool(k.get("profitability")),
"direction": k.get("direction"), "profitability": profitability,
"target": target, "target_source": source, "matched_via": via,
"credit": None if credit is None else round(credit, 4),
})