Files
Drew T c6515a6cdc fix(phase-29): the FLOOR VERDICT was an artifact of snapshot frequency, not yield decay
burndown.py averaged the last 3 INTER-COMMIT deltas. The phase's ROI criterion is
"per-SESSION yield floors out", and historically one snapshot was taken per session
— but I seeded THREE inside this session (9, 9b, 9c). Averaging them drove the mean
to +0.23 and printed "AT THE FLOOR — consider closing P29" for a session that
actually yielded +0.7pp instr (78.0 -> 78.7), comparable to recent sessions.

I nearly closed the phase on it. Same error class as everything else this session:
an instrument answering a DIFFERENT QUESTION than the one asked, where the wrong
answer is indistinguishable from the right one.

- --session-close marks a snapshot as a session boundary; the floor verdict is now
  computed ONLY from those (older records predate the flag, so a label containing
  "close" counts too). Mid-session snapshots still record for tracking.
- honest output now: "0 SESSION-to-SESSION delta(s) logged — need >=3 for a floor
  verdict (1 session-close snapshot on record)".

=> P29 must NOT close on ROI grounds yet: the floor is UNDETERMINED and needs 3
session closes to become computable. The eyeballed "+2.5 -> +1.1 -> +0.6 -> +0.2"
trend is not the instrument's verdict either.
2026-07-22 15:24:59 -06:00

151 lines
7.5 KiB
Python

#!/usr/bin/env python3
"""burndown.py — Phase-29 Task-7/11: the burn-down + velocity tracker (the ROI-close instrument).
The Phase-29 milestone is NOT a checkbox count — it is "close when per-session yield floors out"
(the ROI-gated close Drew scoped at phase start). This tool makes that floor VISIBLE: each run it
snapshots the fleet's 3 headline metrics + the remaining substantial crack-frontier, appends the
snapshot to .run/burndown.jsonl, computes velocity vs the previous snapshot, and flags when recent
yield has dropped below the floor threshold.
INPUTS (read the CURRENT committed digests — regenerate them first for a fresh reading):
- docs/progress.fleet.md (via `make report` / `make tools-health`) -> the 3 metrics
- docs/family-hseq.md (via `tools/family_hseq.py`) -> remaining substantial frontier
Run order for a true reading: make report && .venv/bin/python tools/family_hseq.py && tools/burndown.py
OUTPUT: a burn-down summary (current metrics, remaining frontier, velocity since last snapshot, and a
FLOOR verdict) + an appended snapshot in .run/burndown.jsonl (git HEAD keyed, so re-runs on the same
commit UPDATE rather than duplicate).
The whole-binary byte-gate remains the sole arbiter of a match (G3/P9); this only MEASURES progress.
"""
import json, os, re, sys, subprocess, argparse
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
LOG = os.path.join(REPO, "docs", "burndown.jsonl") # tracked (committed) so velocity persists across sessions
FLEET = os.path.join(REPO, "docs", "progress.fleet.md")
HSEQ = os.path.join(REPO, "docs", "family-hseq.md")
def parse_fleet():
"""(instr_pct, distinct_pct, fn_pct) from progress.fleet.md — the authoritative digest."""
txt = open(FLEET).read()
def pct(label):
m = re.search(rf"FLEET {label}[^\n]*?=\s*([0-9.]+)%", txt)
return float(m.group(1)) if m else None
return {
"instr_pct": pct(r"instr-weighted"),
"distinct_pct": pct(r"distinct-code"),
"fn_pct": pct(r"fn-count byte-ident"),
}
def parse_frontier():
"""(substantial_families, templatable_ins, matched_sib) from family-hseq.md."""
txt = open(HSEQ).read()
m = re.search(r"Substantial:\s*\*\*([\d,]+)\s*families\s*/\s*([\d,]+)\s*templatable ins\*\*,\s*(\d+)\s*with a matched sibling", txt)
if not m:
return {"substantial_families": None, "templatable_ins": None, "matched_sib": None}
return {
"substantial_families": int(m.group(1).replace(",", "")),
"templatable_ins": int(m.group(2).replace(",", "")),
"matched_sib": int(m.group(3)),
}
def git_head():
return subprocess.run(["git", "-C", REPO, "rev-parse", "--short", "HEAD"],
capture_output=True, text=True).stdout.strip() or "unknown"
def load_log():
if not os.path.exists(LOG):
return []
return [json.loads(l) for l in open(LOG) if l.strip()]
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--label", default="", help="optional session label for this snapshot")
ap.add_argument("--session-close", action="store_true",
help="mark this snapshot as a SESSION BOUNDARY. The floor verdict is computed "
"ONLY from these: the ROI criterion is per-SESSION yield, so averaging "
"mid-session snapshots silently drives the mean down and manufactures a "
"floor (byte-witnessed 2026-07-22: three mid-session snapshots of a +0.7pp "
"session read as mean +0.23 = 'AT THE FLOOR').")
ap.add_argument("--floor-pp", type=float, default=0.3,
help="instr-pp/snapshot below which yield is 'at the floor' (default 0.3)")
ap.add_argument("--no-append", action="store_true", help="report only; do not write the snapshot")
a = ap.parse_args()
fleet, front, head = parse_fleet(), parse_frontier(), git_head()
if fleet["instr_pct"] is None:
sys.exit("burndown: could not parse docs/progress.fleet.md — run `make report` first.")
snap = {"head": head, "label": a.label, "session_close": bool(a.session_close), **fleet, **front}
log = load_log()
prev = next((s for s in reversed(log) if s.get("head") != head), None) # last DIFFERENT commit
# velocity vs the previous distinct-commit snapshot
dv = {}
if prev:
for k in ("instr_pct", "distinct_pct", "fn_pct"):
if snap.get(k) is not None and prev.get(k) is not None:
dv[k] = round(snap[k] - prev[k], 2)
# append/update (dedupe on HEAD so re-runs on the same commit overwrite)
if not a.no_append:
log = [s for s in log if s.get("head") != head] + [snap]
os.makedirs(os.path.dirname(LOG), exist_ok=True)
with open(LOG, "w") as f:
for s in log:
f.write(json.dumps(s) + "\n")
# ---- report ----
print("=" * 64)
print(f"BURN-DOWN (HEAD {head}{' ' + a.label if a.label else ''})")
print("=" * 64)
print(f" instr-weighted : {fleet['instr_pct']}%"
+ (f" ({dv['instr_pct']:+.2f} pp since {prev['head']})" if "instr_pct" in dv else ""))
print(f" distinct-code : {fleet['distinct_pct']}%"
+ (f" ({dv['distinct_pct']:+.2f} pp)" if "distinct_pct" in dv else ""))
print(f" fn-count : {fleet['fn_pct']}%"
+ (f" ({dv['fn_pct']:+.2f} pp)" if "fn_pct" in dv else ""))
print("-" * 64)
if front["substantial_families"] is not None:
crackable = front["substantial_families"] - (front["matched_sib"] or 0)
print(f" REMAINING substantial frontier: {front['substantial_families']} families "
f"/ {front['templatable_ins']:,} templatable ins")
print(f" - {front['matched_sib']} matched-sibling (zero-crack sweep) + ~{crackable} needing a crack")
print("-" * 64)
# ---- floor verdict: mean instr-pp over the last N inter-commit deltas ----
# GRANULARITY GUARD (2026-07-22). The phase's ROI criterion is "per-SESSION yield floors out",
# so the deltas averaged here must be SESSION-to-SESSION. Mid-session snapshots are useful for
# tracking but must NOT feed the verdict: three of them inside a single +0.7pp session average
# to +0.23 and read as "AT THE FLOOR" — an artifact of how often someone ran the tool, not a
# measurement of yield decay. Count only snapshots explicitly marked --session-close; older
# records predate the flag, so treat a label containing "close" as one too.
def _is_close(r):
return bool(r.get("session_close")) or ("close" in (r.get("label") or "").lower())
marks = [r for r in log if _is_close(r)]
hist = []
for i in range(1, len(marks)):
a_, b_ = marks[i - 1], marks[i]
if a_.get("head") != b_.get("head") and a_.get("instr_pct") and b_.get("instr_pct"):
hist.append(round(b_["instr_pct"] - a_["instr_pct"], 2))
if len(hist) >= 3:
recent = hist[-3:]
mean = sum(recent) / len(recent)
verdict = "AT THE FLOOR — consider closing P29" if mean < a.floor_pp else "above floor — keep cracking"
print(f" velocity (last 3 snapshots, instr pp): {recent} mean {mean:+.2f}")
print(f" FLOOR VERDICT (threshold {a.floor_pp} pp): {verdict}")
else:
print(f" velocity: {len(hist)} SESSION-to-SESSION delta(s) logged — need >=3 for a floor "
f"verdict ({len(marks)} session-close snapshot(s) on record).")
print(f" (seed more snapshots each session: `make report && tools/family_hseq.py && tools/burndown.py`)")
print("=" * 64)
if __name__ == "__main__":
main()