Implement BDEF v1.1 grading: scoring core, per-deck pipeline, ledger, dashboard, StartOS layer
- Deterministic scoring.py (quant 60 / qual 40 / flags -15, profitability heaviest) - Per-company JSON ledger with forecast-target chaining deck N-1 -> N - Single-shot sandbox agent with guided-JSON fallback ladder (no tool loop) - Portfolio dashboard with sparklines, KPI hit rates, BDEF category bars - 48 unit tests green; endpoints smoke-tested; npm check+build green Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
1dde915540
commit
b1d7aed9f4
@@ -0,0 +1,265 @@
|
||||
"""Markdown renderers: per-deck DECK_REPORT and per-company SCORECARD.
|
||||
|
||||
Pure string builders over the scoring record shape (scoring.score_deck) and
|
||||
ledger deck records — no I/O here; jobs.py decides where the files land.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
_CATEGORIES = "ABCDEFGH"
|
||||
_CATEGORY_TITLES = {
|
||||
"A": "Incentive Alignment & Skin in the Game",
|
||||
"B": "Inversion Discipline & Margin of Safety",
|
||||
"C": "Circle of Competence & Rational Learning",
|
||||
"D": "Capital Allocation Quality",
|
||||
"E": "Moat Durability & Competitive Reality",
|
||||
"F": "Psychological & Cultural Health",
|
||||
"G": "Simplicity, Clarity & Decision Velocity",
|
||||
"H": "Board Value-Add & Governance Quality",
|
||||
}
|
||||
|
||||
|
||||
def _fmt(x, digits: int = 1) -> str:
|
||||
if x is None:
|
||||
return "—"
|
||||
if isinstance(x, bool):
|
||||
return "yes" if x else "no"
|
||||
if isinstance(x, float):
|
||||
return f"{x:.{digits}f}"
|
||||
return str(x)
|
||||
|
||||
|
||||
def _num(x, unit: str = "") -> str:
|
||||
if x is None:
|
||||
return "—"
|
||||
s = f"{x:g}" if isinstance(x, (int, float)) else str(x)
|
||||
return f"{s}{unit}" if unit and unit in ("%",) else (f"{s} {unit}".strip() if unit else s)
|
||||
|
||||
|
||||
def _bucket_row(name: str, b: dict) -> str:
|
||||
if b.get("na"):
|
||||
return f"| {name} | — | — | {b.get('kpi_count', 0)} | NA — weight redistributed |"
|
||||
return (f"| {name} | {_fmt(b.get('weight'))} | {_fmt(b.get('score'))} "
|
||||
f"| {b.get('kpi_count', 0)} | |")
|
||||
|
||||
|
||||
def render_deck_report(record: dict, extraction: dict, adjudication_md: str | None = None) -> str:
|
||||
"""One deck's full markdown report."""
|
||||
lines: list[str] = []
|
||||
company = record.get("company") or "?"
|
||||
period = record.get("period") or "unknown period"
|
||||
lines.append(f"# Deck report — {company} · {period}")
|
||||
lines.append("")
|
||||
lines.append(f"## Composite: **{_fmt(record.get('composite'))} / 100**")
|
||||
lines.append("")
|
||||
q = record.get("quant", {})
|
||||
lines.append(f"Quant {_fmt(q.get('score'))} · Qual {_fmt(record.get('qual', {}).get('score'))}"
|
||||
f" · Penalties −{_fmt(record.get('penalties', {}).get('total'))}"
|
||||
f" · graded {record.get('graded_at') or '?'} (job {record.get('job_id') or '?'})")
|
||||
lines.append("")
|
||||
|
||||
# --- quantitative buckets
|
||||
lines.append("## Quantitative (max 60)")
|
||||
lines.append("")
|
||||
lines.append("| Bucket | Weight | Score | KPIs | Note |")
|
||||
lines.append("|---|---|---|---|---|")
|
||||
lines.append(_bucket_row("Profitability KPIs", q.get("profitability", {})))
|
||||
lines.append(_bucket_row("Other KPIs", q.get("other", {})))
|
||||
lines.append(_bucket_row("Forecast integrity", q.get("forecast_integrity", {})))
|
||||
lines.append("")
|
||||
|
||||
kpi_results = record.get("kpi_results") or []
|
||||
if kpi_results:
|
||||
lines.append("### KPI results")
|
||||
lines.append("")
|
||||
lines.append("| KPI | Actual | Target | Source | Credit |")
|
||||
lines.append("|---|---|---|---|---|")
|
||||
for r in kpi_results:
|
||||
name = r.get("name") or r.get("canonical_name") or "?"
|
||||
if r.get("matched_via") == "fuzzy":
|
||||
name += " (≈ matched via fuzzy)"
|
||||
src = r.get("target_source") or "—"
|
||||
credit = "—" if r.get("credit") is None else _fmt(r.get("credit"), 2)
|
||||
lines.append(f"| {name} | {_num(r.get('actual'), r.get('unit') or '')} "
|
||||
f"| {_num(r.get('target'), r.get('unit') or '')} | {src} | {credit} |")
|
||||
lines.append("")
|
||||
|
||||
fresults = record.get("forecast_results") or []
|
||||
if fresults:
|
||||
lines.append("### Forecast integrity (prior targets vs this period's actuals)")
|
||||
lines.append("")
|
||||
lines.append("| KPI | Prior target | Actual | Accuracy |")
|
||||
lines.append("|---|---|---|---|")
|
||||
for r in fresults:
|
||||
lines.append(f"| {r.get('canonical_name')} | {_num(r.get('target'))} "
|
||||
f"| {_num(r.get('actual'))} | {_fmt(r.get('accuracy'), 2)} |")
|
||||
lines.append("")
|
||||
|
||||
# --- qualitative
|
||||
lines.append("## Qualitative (max 40)")
|
||||
lines.append("")
|
||||
lines.append("| Category | Median | Evidence quality | Adjusted | Points |")
|
||||
lines.append("|---|---|---|---|---|")
|
||||
cats = record.get("qual", {}).get("categories", {})
|
||||
for cid in _CATEGORIES:
|
||||
c = cats.get(cid, {})
|
||||
lines.append(f"| {cid}. {_CATEGORY_TITLES[cid]} | {_fmt(c.get('median'))} "
|
||||
f"| {_fmt(c.get('evidence_quality'), 2)} | {_fmt(c.get('adjusted'), 2)} "
|
||||
f"| {_fmt(c.get('points'), 2)} |")
|
||||
lines.append("")
|
||||
for cid in _CATEGORIES:
|
||||
c = cats.get(cid, {})
|
||||
rats = c.get("rationales") or []
|
||||
if not rats:
|
||||
continue
|
||||
best = max(rats, key=lambda r: sum(len(e.get("quote") or "") for e in r.get("evidence") or []))
|
||||
lines.append(f"### {cid}. {_CATEGORY_TITLES[cid]}")
|
||||
lines.append("")
|
||||
lines.append(f"**{best.get('grader')}**: {best.get('rationale')}")
|
||||
for ev in best.get("evidence") or []:
|
||||
loc = f" — {ev.get('location')}" if ev.get("location") else ""
|
||||
lines.append(f"> \"{ev.get('quote')}\"{loc}")
|
||||
lines.append("")
|
||||
|
||||
# --- red flags
|
||||
flags = record.get("penalties", {}).get("flags") or []
|
||||
lines.append(f"## Red flags (penalty −{_fmt(record.get('penalties', {}).get('total'))})")
|
||||
lines.append("")
|
||||
if flags:
|
||||
lines.append("| Code | Severity | Points | Sources | Description |")
|
||||
lines.append("|---|---|---|---|---|")
|
||||
for f in flags:
|
||||
lines.append(f"| `{f.get('code')}` | {f.get('severity')} | {_fmt(f.get('points'))} "
|
||||
f"| {', '.join(f.get('sources') or [])} | {f.get('description')} |")
|
||||
else:
|
||||
lines.append("None raised.")
|
||||
lines.append("")
|
||||
|
||||
# --- narrative
|
||||
narrative = record.get("narrative") or extraction.get("narrative") or {}
|
||||
if narrative.get("summary"):
|
||||
lines.append("## Narrative")
|
||||
lines.append("")
|
||||
lines.append(narrative["summary"])
|
||||
lines.append("")
|
||||
asks = narrative.get("asks") or []
|
||||
if asks:
|
||||
lines.append("### Asks")
|
||||
lines.append("")
|
||||
for a in asks:
|
||||
lines.append(f"- {a}")
|
||||
lines.append("")
|
||||
|
||||
if adjudication_md:
|
||||
lines.append("## Panel adjudication")
|
||||
lines.append("")
|
||||
lines.append(adjudication_md.strip())
|
||||
lines.append("")
|
||||
return "\n".join(lines).rstrip() + "\n"
|
||||
|
||||
|
||||
def _arrow(delta: float) -> str:
|
||||
if delta > 0:
|
||||
return "↑"
|
||||
if delta < 0:
|
||||
return "↓"
|
||||
return "→"
|
||||
|
||||
|
||||
def render_scorecard(company: dict, records: list[dict]) -> str:
|
||||
"""Company SCORECARD.md across all live deck records (oldest first)."""
|
||||
name = company.get("name") or company.get("slug") or "?"
|
||||
lines: list[str] = [f"# Scorecard — {name}", ""]
|
||||
if not records:
|
||||
lines.append("No graded decks yet.")
|
||||
return "\n".join(lines) + "\n"
|
||||
|
||||
latest = records[-1]
|
||||
prev = records[-2] if len(records) > 1 else None
|
||||
comp = latest.get("composite") or 0.0
|
||||
if prev is not None:
|
||||
delta = round(comp - (prev.get("composite") or 0.0), 1)
|
||||
lines.append(f"## Latest composite: **{_fmt(comp)}** ({latest.get('period')}) "
|
||||
f"{_arrow(delta)} {'+' if delta > 0 else ''}{_fmt(delta)} vs {prev.get('period')}")
|
||||
else:
|
||||
lines.append(f"## Latest composite: **{_fmt(comp)}** ({latest.get('period')}) — first graded deck")
|
||||
lines.append("")
|
||||
|
||||
# --- composite history
|
||||
lines.append("## Composite history")
|
||||
lines.append("")
|
||||
lines.append("| Period | Composite | Quant | Qual | Penalties |")
|
||||
lines.append("|---|---|---|---|---|")
|
||||
for r in records:
|
||||
lines.append(f"| {r.get('period') or '?'} | {_fmt(r.get('composite'))} "
|
||||
f"| {_fmt(r.get('quant', {}).get('score'))} "
|
||||
f"| {_fmt(r.get('qual', {}).get('score'))} "
|
||||
f"| −{_fmt(r.get('penalties', {}).get('total'))} |")
|
||||
lines.append("")
|
||||
|
||||
# --- categories latest vs previous
|
||||
lines.append("## BDEF categories (latest vs previous)")
|
||||
lines.append("")
|
||||
lines.append("| Category | Latest | Previous | Δ |")
|
||||
lines.append("|---|---|---|---|")
|
||||
lcats = latest.get("qual", {}).get("categories", {})
|
||||
pcats = (prev or {}).get("qual", {}).get("categories", {})
|
||||
for cid in _CATEGORIES:
|
||||
lp = lcats.get(cid, {}).get("points")
|
||||
pp = pcats.get(cid, {}).get("points")
|
||||
if lp is not None and pp is not None:
|
||||
d = round(lp - pp, 2)
|
||||
dcol = f"{_arrow(d)} {'+' if d > 0 else ''}{_fmt(d, 2)}"
|
||||
else:
|
||||
dcol = "—"
|
||||
lines.append(f"| {cid}. {_CATEGORY_TITLES[cid]} | {_fmt(lp, 2)} | {_fmt(pp, 2)} | {dcol} |")
|
||||
lines.append("")
|
||||
|
||||
# --- KPI hit-rate across records
|
||||
order: list[str] = []
|
||||
per_kpi: dict[str, list] = {}
|
||||
for r in records:
|
||||
for k in r.get("kpi_results") or []:
|
||||
cn = k.get("canonical_name") or k.get("name") or "?"
|
||||
if cn not in per_kpi:
|
||||
per_kpi[cn] = []
|
||||
order.append(cn)
|
||||
per_kpi[cn].append(k.get("credit"))
|
||||
lines.append("## KPI hit-rate")
|
||||
lines.append("")
|
||||
lines.append("| KPI | Attempts | Hits | Streak | Last credit |")
|
||||
lines.append("|---|---|---|---|---|")
|
||||
for cn in order:
|
||||
credits = [c for c in per_kpi[cn] if c is not None]
|
||||
if not credits:
|
||||
continue
|
||||
hits = sum(1 for c in credits if c >= 1)
|
||||
streak = 0
|
||||
for c in reversed(credits):
|
||||
if c >= 1:
|
||||
streak += 1
|
||||
else:
|
||||
break
|
||||
lines.append(f"| {cn} | {len(credits)} | {hits} | {streak} | {_fmt(credits[-1], 2)} |")
|
||||
lines.append("")
|
||||
|
||||
# --- open flags on the latest deck
|
||||
flags = latest.get("penalties", {}).get("flags") or []
|
||||
lines.append(f"## Open flags ({latest.get('period')})")
|
||||
lines.append("")
|
||||
if flags:
|
||||
lines.append("| Code | Severity | Points | Sources | Description |")
|
||||
lines.append("|---|---|---|---|---|")
|
||||
for f in flags:
|
||||
lines.append(f"| `{f.get('code')}` | {f.get('severity')} | {_fmt(f.get('points'))} "
|
||||
f"| {', '.join(f.get('sources') or [])} | {f.get('description')} |")
|
||||
else:
|
||||
lines.append("None.")
|
||||
lines.append("")
|
||||
|
||||
# --- deck reports
|
||||
lines.append("## Deck reports")
|
||||
lines.append("")
|
||||
for r in records:
|
||||
lines.append(f"- {r.get('period') or '?'} — composite {_fmt(r.get('composite'))} — "
|
||||
f"decks/{r.get('deck_id')}.json / DECK_REPORT.md")
|
||||
return "\n".join(lines).rstrip() + "\n"
|
||||
Reference in New Issue
Block a user