From 7a374d37d24832bff208592194655a6ae8c3edb1 Mon Sep 17 00:00:00 2001 From: tegwick Date: Fri, 31 Jul 2026 10:27:09 +0200 Subject: [PATCH] =?UTF-8?q?CB-WP-0004=20T05:=20control=20loop=20=E2=80=94?= =?UTF-8?q?=206=20points=20recovered,=20not=2025-30?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cb-cost gains --since, so the baseline (--pin 578dcbe, 662 responses, $135.60) and this pass (--since 578dcbe, 83 responses, $7.82) are two disjoint windows over the same transcripts. Every verdict is on share of pass, since the windows differ 17x in size. Test 1 — did mechanical turns disappear? Partly. Mechanical share fell 38.2% -> 32.4%. Six points against a predicted 25-30; reported unmet, and no target was moved in this commit. environment setup 11.5% -> 0.6% (85 turns -> 1) met, decisively hub + workplan 8.5% -> 0.0% (46 turns -> 0) met text patching 10.4% -> 9.6% not met orientation 5.1% -> 21.2% not met, worse The confound is stated before any defence of the numbers: this is the pass that built the tools, and the two categories that missed are exactly the two whose tools were under construction. The clean test is the next pass, and it is carried forward rather than waived. Test 2 — did the work relocate? Not into prose. Output tokens per response fell 896 -> 681. Output's rising share of cost is a shrinking denominator, not more writing. Cost per response halved and the evidence refuses to claim it: that is compaction (mean context 232,982 -> 117,822), and attributing it to tooling would repeat CB-WP-0002's original error in a new direction. Test 3 — did quality hold? Yes, recorded as explicit judgment. make all green with two gates that did not exist before, and four findings surfaced this pass, three caught by controls written this pass — one of them a 5.2x attribution error that would have reached the hub as a measured number. Co-Authored-By: Claude Opus 5 --- evidence/CB-EV-0003-mechanical-work.md | 150 ++++++++++++++++++++++ tools/__pycache__/cb-cost.cpython-312.pyc | Bin 36204 -> 37064 bytes tools/cb-cost.py | 20 ++- workplans/CB-WP-0004-mechanical-work.md | 35 ++++- 4 files changed, 202 insertions(+), 3 deletions(-) create mode 100644 evidence/CB-EV-0003-mechanical-work.md diff --git a/evidence/CB-EV-0003-mechanical-work.md b/evidence/CB-EV-0003-mechanical-work.md new file mode 100644 index 0000000..c56ea18 --- /dev/null +++ b/evidence/CB-EV-0003-mechanical-work.md @@ -0,0 +1,150 @@ +# CB-EV-0003: did converting mechanical turns to tooling recover anything? + +research: [CB-RES-0003](../research/CB-RES-0003-agent-vs-deterministic.md) +workplan: [CB-WP-0004](../workplans/CB-WP-0004-mechanical-work.md) +instrument: `make cost-mix` (`tools/cb-cost.py --composition`) +commit: measured on the tree at CB-WP-0004 T05 + +**Two windows over the same transcripts**, cut at `578dcbe` — the commit +that published the baseline: + +| window | command | responses | pass cost | +|---|---|---|---| +| **baseline** (CB-WP-0001…0003 + the review) | `cb-cost --pin 578dcbe` | 662 | **$135.60** | +| **this pass** (CB-WP-0004 T01–T04) | `cb-cost --since 578dcbe` | 83 | **$7.82** | + +The windows differ 17× in size, so **every verdict below is on share of +pass**, per the normalization this task required. Absolute dollars are +shown but decide nothing. + +--- + +## Test 1 — did the mechanical turns disappear? + +| category | baseline $ | baseline share | this pass $ | this share | predicted | verdict | +|---|---|---|---|---|---|---| +| environment setup | $15.58 (85 turns) | **11.5%** | $0.05 (1 turn) | **0.6%** | <10 turns | **met, decisively** | +| hub task status + workplan edit | $11.52 (46 turns) | **8.5%** | $0.00 (0 turns) | **0.0%** | ~6 turns | **met** | +| ad-hoc text patching | $14.11 (76 turns) | **10.4%** | $0.75 (8 turns) | **9.6%** | $6–9 saved | **not met** | +| orientation / inspect | $6.87 (49 turns) | **5.1%** | $1.66 (16 turns) | **21.2%** | ~1 turn/session | **not met — worse** | +| ad-hoc transcript analysis | $4.56 (39 turns) | 3.4% | $0.08 (1 turn) | 1.0% | $2 saved | met | +| **mechanical total** | **$51.76 (292)** | **38.2%** | **$2.53 (26)** | **32.4%** | **25–30% recovered** | **not met** | + +**Mechanical share fell 38.2% → 32.4%. That is 6 points, not the 25–30 +points the review predicted.** The prediction is reported unmet. Per +InnerLoop §Step 4 no target was moved in the commit that measured it. + +### The two that worked + +Environment setup went from the single largest category to effectively +zero — 85 turns to 1, an 18× drop in share. `make env-test` runs in +`make all`, so it cannot regress silently. + +Hub and workplan closes went to **exactly zero** hand-written turns. Four +tasks were closed this pass, each with one `make task-done`, and each hub +event carries measured tokens rather than a typed estimate — the first +time that has been true in this repo. + +### The two that did not + +**Text patching barely moved (10.4% → 9.6%).** `facts-check` gates +drifted copies but nothing removed the act of patching markdown: the +Makefile edits and the fact-tagging in T04 were both done with the same +heredocs the task was meant to retire. The gate closed the error class; +it did not close the cost category. Those are different claims and the +review conflated them. + +**Orientation got worse — 5.1% → 21.2% of pass.** This is the largest +single miss and it deserves the plain reading first: `make status` was +built this pass and then barely used. 16 turns of `grep`/`sed`/`ls` +still went to reading the repo. + +## The confound, stated before any defence of the numbers + +**This pass is the pass that built the tools.** Writing `make status` +requires inspecting exactly the artifacts `make status` summarizes; +writing `facts-check` requires grepping for every duplicated number in +the repo. The two categories that missed are precisely the two whose +tools were under construction. + +So this measurement **understates** the effect, and no part of it should +be read as the settled answer. The clean test is the *next* pass, which +uses the tools without building them. That test is not optional: a +prediction that can only be confirmed by a differently-shaped future run +is not yet confirmed, and CB-WP-0005 should carry it. + +What this pass does establish is the half that is not confounded: +environment setup and task closes are gone, they were 20% of baseline +pass cost, and their tools have gates that keep them gone. + +## Test 2 — did the work relocate? + +The named failure mode: an agent that can no longer write heredocs simply +writes more prose, and the pass costs the same. + +| signal | baseline | this pass | reading | +|---|---|---|---| +| output tokens per response | **896** | **681** | fell 24% — no prose inflation | +| output share of cost | 13.2% | 18.1% | rose, but see below | +| cost per response | $0.205 | $0.094 | fell 54% — **not attributable to this work** | +| non-mechanical share | 61.8% | 67.6% | rose 6 pts, mirroring the mechanical fall | + +**Relocation into prose is not supported.** Output per response *fell*. +The output share of cost rose only because cache-read share fell +(64.6% → 62.5%) as context shrank — the denominator moved, not the +numerator. + +**The halved cost per response is a compaction effect, not a T01–T04 +effect,** and claiming it would be the most tempting error available +here. `specs/SessionShape.md` measures mean context at 232,982 tokens in +the baseline window against **117,822** in this one, because this session +was compacted twice. Attributing that to tooling would repeat CB-WP-0002's +original mistake in a new direction. + +The 6-point rise in non-mechanical share is arithmetic, not relocation: +if mechanical work leaves and the same judgment work remains, judgment's +share rises by construction. + +## Test 3 — did quality hold? + +Recorded as explicit judgment, because the loop has no metric for this +and a cost number does not settle it. + +- `make all` green, now including two gates that did not exist: + `env-test` and `facts-check`. +- **Findings kept surfacing at the same rate.** This pass produced four, + each caught by a control rather than by review: + 1. `loop-lint` failed on `repo.py` — a new tool with a positive control + but no `--self-test` entry point. + 2. `task-done`'s self-test reported **$12.10** for "T01" where the + qualified figure is **$2.33** — a bare `T\d\d` attribution key + collided across three workplans. That 5.2× overstatement would have + been pushed to the hub *as a measured number*. Fourth instance of + trusted arithmetic. + 3. `status.py`'s heading regex returned twenty paragraphs of the + preceding task's prose as a "task heading". + 4. `facts-check` failed on a live fact tag inside its own documentation + example in `specs/InnerLoop.md`. +- No scenario, benchmark or coverage number regressed. The pinned + benchmark is **$93.15**, unchanged by the attribution-key fix, which is + the evidence that historical attribution was not disturbed. + +**Verdict: quality held.** Three of the four findings were caught by +controls written this pass, which is the pattern the loop is trying to +buy. + +## Verdict + +| claim | status | +|---|---| +| environment setup eliminated | **confirmed** | +| task closes eliminated, hub on measured numbers | **confirmed** | +| DFD has an executable gate | **confirmed** (falsified against a real artifact) | +| text patching reduced | **not met** | +| orientation reduced | **not met — worse this pass** | +| 25–30% of pass recovered | **not met — 6 points, on a confounded window** | +| work did not relocate into prose | **confirmed** (output/response fell 24%) | + +The workplan said a saving that cannot be demonstrated in this table did +not happen. Two of five categories are demonstrated; the aggregate +prediction is not. Both are recorded as measured. diff --git a/tools/__pycache__/cb-cost.cpython-312.pyc b/tools/__pycache__/cb-cost.cpython-312.pyc index 21da165bb72e3e4fff7ca5ba472e308f61a5d417..a5d92f2722efbd3ea57ad0ba0ce09974307a2cb7 100644 GIT binary patch delta 5618 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 Hb=$uIDHYy7 delta 4619 zcmaJ^YitzP6~1?7XT5LlYrMO*_u)4iW5C8H1q`;aG2!6{F)tHrvhlTzjbE7=$6(E1 z2b@3x0VkIzsDRQ`N-GsNC0bQcE45XVG-=frDM}Mrk=jU!)TU{(BEN`IrRUD>tQVwa zrM>r@@0@$ix#ynynBTs_eg0j}^?Rq&hT!)_@7U-m`E}Pz*jhoN3;6JAQb}LKZ{Vur z5a+Yv9dy?IPu!U#jtv~{NnUkP4j)af^)&N%DmmqsIeaGhNx>Nozmz-{`ilksF}b<4 z7UTDm<7NNC_~m48#Z5E*ENNT)Dvv$%=GvcOe{ytP7sk%y&udSaaCvg$hG8BzCSTdu z%HwYOY2z*WUPxT`wH2Y4kb+r^>~|yGA&pb`42e{HBO}*%>fS2mnN*YPQu62*fuQ#^ z9!8p=n%Yob7$ME93iSD7emc68(i9mVF05GQ4>-HIjH0r<_V`b4O%rBnpY9w zuk!|X8Y}r(9nL_tJT*_UuUJy^49RJ%3Wp%1VhG8icvgn07Geqk3%!u(&8)v+O6vfg z*9hc=hzTjaGzQv&B4%lx&NsDUE8W!`aS_V`GJI+_nrf<|Z#6~f%_bjqfI@BD3E6R} z9I-MdTD~f@p+(#WS!z#EZEsk7?>Q_aoZi~aJHMf?#wSXE>svrpcr#cwu zorQg>1(oV79Hj2nurENwt0q!#pL#V8(y`VNPoWlKOWL<3y`ZBtbnm#2UTX6=04xKr zNDZk)G}01q7OF+rP%U&*TUitaUa|~u!)So(i!%et+N^U+*3<)Ymgl(()h47BgGM%L zQnUbN*_Pd~#)@g-5k!iW^;t`TA)%Orm4+-HGVmO%FpI71$l_oYTyY)&=f$*pd&w{l zX6-o}IM-ci4xZiEpkJm2peLdcRjSP>fO^Q8cquZ#kFc!0 zmMw*dDyot6euCbv*LEabOXfiZt z&!Bii=WJ+VGNwq033)&onNl2bY-nnwHbm%l8gSNYqco~3iRWyHL(i7#^Eo21*gtD`p6kP-#6d> zkxg72zHN)lx6;ksyOOVVALHFM4)ceWz_sxAPTmgHruZ5f-nVuRz?2(V^A}Ai52L-v z>N+QrkEgbu-_B}h9{yD>xd z0OGk^zNp^zmZv&&s1u;#Yx{1MuD>0wPj%^7H@&mJd~UB!9{{ z=TMDczI(@t9gTOtT;(sa-MzDp6KyiP2>(#Ire}@m&vo0@_pGuhs?n0S07yR7NGUZ@hZA*qOTti?KVL44NM$# z@?gmB1m30Z+ccjKynbnb{EdU*dXG`-7#Wfi7#>WBcr{+hJU|eF19>;pz6o@EA^P_~ zph7hfzp;xoVdV`(M359*Wtst9a}Dr8Lz9HyJ}6WLQUrZ539EvBmGSJVBN4!&^qE6Z z9HaQKpP4A3zdlqFEnPrMWrhiOC1s{y1WIvlc$28+k#aCl@omPhXWuyRMyPk-X&R-@cOcKAIJ;U{kKEQJStm7F6sb-Y) zT#n_{E4gPV{nddyTmv%?j`!btvtW=I?&~?i@*#WIpXw)s-5&^d=pl6SiL=QtQA7Lmt z0yF8$wQ&-~`{}z!hWxYcSk7f3$6_%F(yH)Wnpl9N;#?Dq*9QL3NaOvJpGs~H8FmY zSusx~WOsI>BQ^(-ReevlM!A2}gI%TyJ3vnztZ}4>S z>8KUHph&*KdEAN_TgL(6UD*ScQ}Jkw-a03WY&rFx zQese}rR-xX&XK~UB4dP6_ZXcFD;0*O%Is9q+I&AZx{5wN@7q#*BXj35#WJJ>4b^fa zt%B<&>y89e9k1#EZ;JH|JnBX4j|*4lmO$B1T@n=mXL_l%@{ij9UL2~Q3hp&Nk(PZ zJI86)`KX%}vedvDb~DCBdg^?9&JFysZ7LzjgGUq6uq?3xkl3203u$Gw^*VTpc}*7s zQyF6odtON@6TpiCNo+Idn@0B)H!X9Csb!OHyPk8gx z(r+#XMSYI?1Xj|Z(bbnq@j2Rc=^T9eeRydib_!$jm)lH@xE0@N<7A8r?~L&=?7Y*C Rp-r{Zp~V>9P0ud2{0~%#?}q>Y diff --git a/tools/cb-cost.py b/tools/cb-cost.py index e0af2bf..503eeb1 100644 --- a/tools/cb-cost.py +++ b/tools/cb-cost.py @@ -64,6 +64,8 @@ def task_label(subject): return f"{m.group(1)}-{m.group(2)}" m = TASK_RE.search(subject) return m.group(0) if m else None + + UNATTRIBUTED = "UNATTRIBUTED" OPEN_REMAINDER = "OPEN (uncommitted)" @@ -362,9 +364,17 @@ def attribute(responses, commits): # ----------------------------------------------------------------- report -def collect(slug, pin_ref=None): +def collect(slug, pin_ref=None, since_ref=None): + """Report over (since_ref, pin_ref]. Both ends optional. + + `since` exists for CB-WP-0004 T05: comparing a pass against the pass + that measured it needs two disjoint windows over the same transcripts, + not two whole-corpus totals. Attribution and reconciliation are + unchanged — the window only selects which responses are counted. + """ prices = load_prices() pin = resolve_pin(pin_ref) + since = resolve_pin(since_ref) paths = transcript_paths(slug) if not paths: raise Abort(f"no transcripts found for {slug}") @@ -372,6 +382,8 @@ def collect(slug, pin_ref=None): responses = [] for p in paths: responses.extend(read_responses(p, pin)) + if since: + responses = [r for r in responses if r["timestamp"] > since] if not responses: # Positive control: a run that measured nothing must not report $0.00 # as though it were an answer. @@ -453,6 +465,7 @@ def collect(slug, pin_ref=None): }, "slug": slug, "pin": pin, + "since": since, "responses": len(responses), "transcripts": len(paths), "total": total, @@ -494,6 +507,8 @@ def collect(slug, pin_ref=None): def render(rep, by_task=False, composition=False): print(f"M-D2-CST cost report — {rep['slug']}") print(f" pin {rep['pin'] or '(none — live file, not reproducible)'}") + if rep.get("since"): + print(f" since {rep['since']} (window is exclusive of this instant)") print(f" transcripts {rep['transcripts']} responses {rep['responses']}") print(f" main ${rep['main_total']:>10,.2f}") print(f" subagent tree ${rep['subagent_total']:>10,.2f}") @@ -714,6 +729,7 @@ def main(): ap = argparse.ArgumentParser() ap.add_argument("--slug", default="-home-worsch-clay-borg") ap.add_argument("--pin", help="commit-ish or ISO Z instant (CA-07)") + ap.add_argument("--since", help="window start, exclusive: commit-ish or ISO Z") ap.add_argument("--by-task", action="store_true") ap.add_argument("--composition", action="store_true") ap.add_argument("--session-shape", action="store_true", @@ -733,7 +749,7 @@ def main(): return budget(args.slug, args.soft, args.hard) try: - rep = collect(args.slug, args.pin) + rep = collect(args.slug, args.pin, args.since) except Abort as e: print(f"ABORT — {e}", file=sys.stderr) return 1 diff --git a/workplans/CB-WP-0004-mechanical-work.md b/workplans/CB-WP-0004-mechanical-work.md index 03a9eac..04898b4 100644 --- a/workplans/CB-WP-0004-mechanical-work.md +++ b/workplans/CB-WP-0004-mechanical-work.md @@ -178,7 +178,7 @@ as the "task heading". ```task id: CB-WP-0004-T04 -status: todo +status: done priority: high state_hub_task_id: "b5dfbf1e-619b-4e64-8463-49ff2ce38b48" ``` @@ -276,6 +276,39 @@ Normalize per unit of work, not per session: passes differ in size, so report **mechanical share of pass cost** (baseline: 38%) alongside absolute dollars. +**Measured — [CB-EV-0003](../evidence/CB-EV-0003-mechanical-work.md).** +`cb-cost` gained `--since`, so the baseline window (`--pin 578dcbe`, 662 +responses, $135.60) and this pass (`--since 578dcbe`, 83 responses, +$7.82) are two disjoint windows over the same transcripts. + +**Mechanical share fell 38.2% → 32.4%. Six points, against a predicted +25–30. The aggregate prediction is reported unmet.** + +Two categories met their prediction and two did not: + +- environment setup **11.5% → 0.6%** of pass (85 turns → 1) — met +- hub + workplan closes **8.5% → 0.0%** (46 turns → 0) — met +- text patching **10.4% → 9.6%** — not met +- orientation **5.1% → 21.2%** — not met, *worse* + +**The confound is stated in the evidence before any defence:** this is +the pass that *built* the tools, and the two categories that missed are +precisely the two whose tools were under construction. Writing +`make status` requires inspecting what it summarizes. The clean test is +the next pass, which uses the tools without building them — that test is +carried forward, not waived. + +**Relocation: not supported.** Output tokens per response *fell* 896 → +681. The rise in output's share of cost is a shrinking denominator +(cache-read fell as context shrank), not more prose. Cost per response +halved, and the evidence explicitly refuses to claim it: that is +compaction (mean context 232,982 → 117,822), not tooling. + +**Quality held.** `make all` green with two new gates, and four findings +surfaced this pass — three of them caught by controls written this pass, +including a 5.2× attribution error that would have reached the hub as a +measured number. + ## Task: Retrospective ```task