progress: only track KPIs that ever carried a target

Tested against the live Strike ledger (9 decks): its decks mint ad-hoc KPI
names every quarter, so ~70 one-off unscored mentions flooded the watch list,
trajectory table, and dropped-KPIs gap. A KPI now only counts as trackable/
vanished if it was held to a target at least once; the rest collapse to a
one-line count. Scoring-derived flags (no_profitability_visibility,
no_quantitative_kpis, kpi_dropped) no longer duplicate their dedicated gaps
as recurring-flag requests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jonathan Kirkwood
2026-07-31 20:53:14 -05:00
co-authored by Claude Fable 5
parent 9b9c7e58c1
commit f13d044a70
2 changed files with 50 additions and 11 deletions
+32 -11
View File
@@ -172,6 +172,10 @@ def _kpi_trends(records: list[dict]) -> dict:
latest_period = records[-1].get("period") if records else None
for s in out.values():
credits = [p["credit"] for p in s["series"] if p["credit"] is not None]
# Accountable = the KPI was actually held to a target at least once.
# One-off unscored mentions (ad-hoc deck metrics) are extraction noise,
# not a trackable KPI — they'd otherwise flood the review.
s["accountable"] = bool(credits)
if s["last_period"] != latest_period:
s["status"] = "not-reported" # vanished from the latest deck
elif not credits:
@@ -266,13 +270,16 @@ def _gaps(company: dict, records: list[dict], categories: dict,
"Keep KPI names identical period to period (or record aliases in "
"Configure Companies so renamed KPIs still chain).")
dropped = [s["name"] for s in kpis.values() if s["status"] == "not-reported"]
dropped = sorted(s["name"] for s in kpis.values()
if s["status"] == "not-reported" and s["accountable"])
if dropped:
shown = ", ".join(dropped[:12]) + \
(f", … and {len(dropped) - 12} more" if len(dropped) > 12 else "")
gap("dropped_kpis", 3,
f"Previously reported KPIs vanished ({len(dropped)})",
f"No longer reported as of {latest.get('period')}: "
f"{', '.join(sorted(dropped))}. Silently dropped KPIs are a classic "
"way bad news leaves a deck.",
f"Previously targeted KPIs vanished ({len(dropped)})",
f"KPIs that were held to a target in an earlier deck are no longer "
f"reported as of {latest.get('period')}: {shown}. Silently dropped "
"KPIs are a classic way bad news leaves a deck.",
"Restore these KPIs — or retire them explicitly with one line on why.")
thin = [(cid, c) for cid, c in categories.items()
@@ -297,10 +304,13 @@ def _gaps(company: dict, records: list[dict], categories: dict,
# Recurring flags only demand action while still open in the latest deck —
# a flag that recurred historically but is clear now shows under "resolved".
# Scoring-derived codes already have a dedicated gap above; repeating them
# here would duplicate the request.
covered = {"no_profitability_visibility", "no_quantitative_kpis", "kpi_dropped"}
latest_codes = {f.get("code") for f in
(latest.get("penalties") or {}).get("flags") or []}
for f in flags.get("recurring") or []:
if f["code"] in latest_codes:
if f["code"] in latest_codes and f["code"] not in covered:
gap(f"recurring_{f['code']}", min(3, f["severity"]),
f"Recurring flag: {f['code']}",
f"Raised in {len(f['periods'])} decks ({', '.join(str(p) for p in f['periods'])}) "
@@ -404,8 +414,10 @@ def render_progress_md(analysis: dict) -> str:
kpis = a.get("kpis") or {}
up = [(cid, c) for cid, c in cats.items() if c.get("direction") == "improving"]
down = [(cid, c) for cid, c in cats.items() if c.get("direction") == "declining"]
kpi_good = [s for s in kpis.values() if s["status"] in ("on-track", "recovered")]
kpi_bad = [s for s in kpis.values()
tracked = [s for s in kpis.values() if s["accountable"]]
untracked = len(kpis) - len(tracked)
kpi_good = [s for s in tracked if s["status"] in ("on-track", "recovered")]
kpi_bad = [s for s in tracked
if s["status"] in ("slipped", "missing-repeatedly", "missed-latest",
"not-reported")]
lines.append("## What's moving")
@@ -435,13 +447,13 @@ def render_progress_md(analysis: dict) -> str:
lines.append("- Nothing declining.")
lines.append("")
# --- KPI trajectory
if kpis:
# --- KPI trajectory (only KPIs that were ever held to a target)
if tracked:
lines.append("## KPI trajectory")
lines.append("")
lines.append("| KPI | Profit | Trend (oldest → latest) | Hits | Status |")
lines.append("|---|---|---|---|---|")
ordered = sorted(kpis.values(),
ordered = sorted(tracked,
key=lambda s: (not s["profitability"], s["name"].lower()))
for s in ordered:
marks = []
@@ -453,6 +465,15 @@ def render_progress_md(analysis: dict) -> str:
f"| {_STATUS_LABEL[s['status']]} |")
lines.append("")
lines.append("✓ target hit · ✗ missed · `·` no target that period")
if untracked:
lines.append(f"({untracked} more KPI(s) were mentioned in decks without "
"ever carrying a target — not tracked here.)")
lines.append("")
elif untracked:
lines.append("## KPI trajectory")
lines.append("")
lines.append(f"No KPI has ever carried a target — {untracked} KPI(s) were "
"mentioned across the decks but none can be held to account.")
lines.append("")
# --- flags
+18
View File
@@ -135,6 +135,24 @@ class TestGapEngine(unittest.TestCase):
a = progress.analyze(_company(), recs)
self.assertIn("dropped_kpis", self.codes(a))
def test_unaccountable_kpi_mention_is_not_dropped(self):
# A KPI mentioned once without ever carrying a target is extraction
# noise, not a "vanished KPI".
recs = [_record("2026-Q1", 50, kpis=[_kpi("ships launched", None)]),
_record("2026-Q2", 51, kpis=[])]
a = progress.analyze(_company(), recs)
self.assertNotIn("dropped_kpis", self.codes(a))
self.assertFalse(a["kpis"]["ships launched"]["accountable"])
def test_scoring_flags_not_duplicated_as_recurring(self):
f = {"code": "no_profitability_visibility", "description": "none",
"severity": 3, "points": 3.0, "sources": ["scoring"]}
recs = [_record("2026-Q1", 50, flags=[f], prof_na=True),
_record("2026-Q2", 51, flags=[f], prof_na=True)]
a = progress.analyze(_company(), recs)
self.assertIn("no_profitability_kpis", self.codes(a))
self.assertNotIn("recurring_no_profitability_visibility", self.codes(a))
def test_thin_evidence_gap_names_material(self):
a = progress.analyze(_company(),
[_record("2026-Q2", 50, cats={"E": _cat(3.0, 0.05)})])