From 69c1658d2064952e8e3e39b6e415a8c7cc158e7a Mon Sep 17 00:00:00 2001 From: tegwick Date: Mon, 3 Aug 2026 19:25:18 +0200 Subject: [PATCH] CB-WP-0019 T03/T04: the cost rule written down, and the lifecycle T03: InnerLoop v1.7 plus loop-lint's own-cost check. Six passes under-reported themselves by 30-45%, never once high, and the rule lived only in evidence files having been re-derived three times. The READING is load bearing, not the boundary: CB-WP-0018 T04 applied 're-run the instrument at the moment of quoting' alone and its figure was correct. So the operative instruction is re-run when you quote, and loop-lint fails an evidence file naming its own workplan beside a dollar amount without marking it provisional. It binds forward from this pass. The check fires on seven historical files which ARE the evidence for the rule; making them comply would edit the record to remove the thing it proves -- the same category error as a live fact: tag on a dated measurement, which this pass also hit. Lifecycle, at the maintainer's instruction: ready -> active -> done, where ready means declared and not started. loop-lint fails a workplan that has started and still says ready, one that is active with everything closed, and one that is done with an open task. The first version of that check was WRONG and its own self-test caught it: it stripped the leading status: assuming frontmatter, which silently dropped a real task once the frontmatter said ready or active. Both new checks then fired on this pass's own artifacts and both were right to. T04: CB-EV-0017. The new meta budget's first reading is a breach it caused -- 27% against the 20% line, because this pass cost $31.18 against product passes averaging ~$21. Reported rather than exempted: ADR-0006 D2 covers the instrument repairs but not the rule-writing, and the honest reading is that this should have been two passes. CB-WP-0018 settled at $36.53/95 against $28.08/82 last reported, 30% higher. Seven for seven. make all exits 0. Co-Authored-By: Claude Opus 5 --- evidence/CB-EV-0017-budgets.md | 184 +++++++++++++++++++++++++ gates.toml | 1 + specs/InnerLoop.md | 45 +++--- specs/InnerLoopReference.md | 84 +++++++++++ tools/loop-lint.py | 145 +++++++++++++++++++ workplans/CB-WP-0019-the-am4-family.md | 49 ++++++- 6 files changed, 483 insertions(+), 25 deletions(-) create mode 100644 evidence/CB-EV-0017-budgets.md diff --git a/evidence/CB-EV-0017-budgets.md b/evidence/CB-EV-0017-budgets.md new file mode 100644 index 0000000..f70075b --- /dev/null +++ b/evidence/CB-EV-0017-budgets.md @@ -0,0 +1,184 @@ +# CB-EV-0017 — budgets that ask a question, and the first one to fail + +CB-WP-0019 T04. Measured 2026-08-03 at `0b6f7c5`+. Pass kind `meta`, +tier **M** (structural M; chaos **d8 = 5**, no override — the first roll +at the new rate). Declaration 2 of chaos window 2. + +--- + +## 1. Two budgets that had never asked different questions + +AM-4a and AM-4b both measured `-p games-ground --edges normal` while +claiming to bound different things. AM-4b was blind to **28 crates and +408,237 lines — more source than its own 350,000 target.** + +| | the question | scope | proc-macros | reading | +|---|---|---|---|---:| +| **AM-4a** | what does a game **ship**? | one package, no default features | excluded | 157,202 / 161,000 | +| **AM-4b** | what does a contributor **acquire**? | workspace, dev edges | **counted** | **725,258 / 745,000** | + +**The proc-macro treatments are opposite on purpose**, and that is the +rule ADR-0008 D2 left open. A proc-macro never reaches a shipped binary, +so counting it in *"what a game ships"* was false. But it **is** fetched, +compiled and unaudited on a contributor's machine, and ADR-0007 D3's +acquisition rule counts what the build causes to be fetched. *"It does not +ship"* is no answer to *"we downloaded it."* **When the two rules +disagree, the question each budget asks decides.** + +Measured share: **109,585 lines, 15.1%**, against AM-4a's 36.2%. ADR-0008 +D2 refused to correct AM-4b by borrowing that ratio, on the grounds that +*"correcting a second instrument on the strength of the first one's ratio +is the error this change exists to fix."* It would have been wrong by more +than a factor of two. **A decision that cost a pass of delay and was +right.** + +The target moved to fit the measurement — 745,000, the same ~2.7% margin +ADR-0008 D3 gave AM-4a — and the code it measures did not change at all, +which is the mechanical test for a correction rather than a retarget. + +## 2. Three of this project's own controls fired on this pass + +None of them was the pass's subject. All three fired on changes made +while doing something else. + +| control | what it caught | cost | +|---|---|---| +| `mutation-check --self-test` | AM-4b's mutation find-string went stale the moment the target moved | **build-free** | +| DFD (`facts-check`) | `facts.toml` still carrying 317,021 / 350,000 | one command | +| `loop-lint` loadability | `InnerLoop.md` crossed 400 lines **three times** | three splits | + +The first is the one worth noting. CB-WP-0015 added that check *after* +AM-4a's mutation had rotted unnoticed for two passes, on the argument that +the expensive half of the check needed a build but the cheap half did not. +This is its second catch and the first that cost nothing. + +**And the loadability breaches were fixed structurally, three times, +without raising the limit** — the arithmetic, the cost basis, two review +case studies, the retargeting case and the inherited-number cases all +moved to `InnerLoopReference.md`. That precedent has now held five times. + +## 3. What the meta budget is for — and its first reading is a breach + +The maintainer set the rule: *most spend on the task at hand, some on +control, review and improving the process.* **80/20.** + +META-25 had a threshold and **no stated purpose**, which is why the number +had been argued three separate times. The purpose is now written first and +`make status` prints it above the figure. + +**The ratio and the window are a pair, and that was the substantive +finding.** One meta pass among `n` at parity cost reads `1/n`: 33% over +three, **20% over five**. So 80/20 is *one pass in five at normal cost* — +a five-pass window. The same 20% over the old trailing three would have +silently also demanded the meta pass be half-price, which does not make +meta work rarer, only rushed. The self-test now enforces +`META_SOFT_PCT == 100 / TRAILING_PASSES`; moving one without the other +goes red. + +The phase setting is **declared, argued and expiring** — `meta_phase` in +`gates.toml`, reverting on `review_by` unless re-argued, and refused by +`--self-test` if it has no reason or no expiry. A threshold anyone may +move is not a threshold. + +### The breach, reported rather than exempted + +**`meta budget [OVER] 27% over the last 5 passes (soft 20%)`.** + +The pass that wrote the rule is the first to fail it, and the failure is +real rather than an artifact: **CB-WP-0019 cost $31.18 (provisional, and +seven for seven says it will settle higher) against product passes +averaging ~$21**, so it is over on both counts — one pass in five, *and* +at parity. It did four instrument repairs and two rule changes, which is +more than one pass's worth of meta work. + +*The "provisional" in that sentence was put there by `loop-lint`'s own +new check, which fired on this file for quoting this pass's cost. It was +right to: the figure is provisional, and saying so is both compliant and +more truthful than the bare number was.* + +ADR-0006 D2 exempts *"repairing the instrument that reports a breach"*, +and most of this pass qualifies — AM-4b, the proc-macro decision, and the +meta budget itself are all instrument repair. **The exemption is not +claimed as cover for the whole pass.** T03 and T05's rule-writing are not +repairs of a breached instrument, and the honest reading is that this pass +should have been two. + +**No product work was displaced**, because the product side is blocked on +a human check and on a ground-game ruling (§5). + +## 4. The self-quoting rule, written down at last + +Six passes, six under-reports, **never by less than 30%, never once +high**. The table is in `InnerLoopReference.md` §Quoting a cost. + +**Which of the two causes is load bearing?** The task asked; the answer is +**the reading, not the boundary.** CB-WP-0018 T04 applied *re-run the +instrument at the moment of quoting* alone and its figure was correct. The +boundary rule only tells you when "late enough" has arrived. + +So the operative instruction is: **re-run the instrument when you quote +it.** `loop-lint` now fails an evidence file that names its own workplan +beside a dollar amount without marking it provisional. + +**It binds forward, from this pass.** The check fires on seven historical +files — and those files *are the evidence for the rule*. Making them +comply would mean editing the record to remove the thing it proves, which +is the same category error as putting a live `fact:` tag on a dated +measurement (both appear in this pass; both were fixed by marking rather +than rewriting). + +## 5. Workplan lifecycle, and a bug my own control caught + +At the maintainer's instruction: **`ready` → `active` → `done`**, where +`ready` means *declared and not started*. The status now answers *"is +anyone on this?"* rather than only *"is it finished?"*. + +`loop-lint` fails a workplan that has started work and still says `ready`, +one that is `active` with everything closed, and one that is `done` with +an open task. + +**The first version of that check was wrong, and its own self-test found +it.** It stripped the leading `status:` match assuming it was the +frontmatter — which silently dropped a real task the moment the +frontmatter said `ready` or `active`, because those do not match the task +vocabulary. It worked only on the old `todo`/`done` files. It now parses +the ` ```task ` blocks. + +Also fixed: **`cancel` is a closed outcome.** CB-WP-0005 read `5/8` and +CB-WP-0007 read `2/6` in every status run since the day they closed, +because only `done` counted. Two permanently-wrong numbers teach the +reader to skip the column. + +## 6. Cost + +| pass | kind | responses | cost | $/response | +|---|---|---|---|---| +| **CB-WP-0018** | product | 95 | **$36.53** | 0.385 | +| CB-WP-0019 | meta | *provisional — not quoted, per §4* | | | + +Read by re-running `make status` at the moment of writing, which is the +rule this pass wrote. CB-WP-0018 was last reported at $28.08/82 and has +settled at **$36.53/95** — 30% higher, and **seven for seven**. + +## 7. Open + +- **The AM-4 family is closed.** Four defects across five passes — the + shipped proc-macro count, AM-4b's scope, AM-4b's share, AM-4a's stale + mutation — none found by looking for them. **Where a fifth would be:** + AM-4c is a diagnostic nobody reads, and `own source` (11,799 lines) has + no instrument at all. +- **What five passes of "owed" cost.** The two numbers here were carried + as open items in four consecutive evidence files. Carrying was *cheap* + and it was also *not doing the work*: measuring both took one command + each, before this declaration. The honest reading is that "owed" became + a place to put things rather than a queue. +- **INTENT stage 1: the human check**, now on perceptual questions only. +- **`ground-game` owes ten rulings** (U1–U10) plus SOLVE's legality, now + registered as GROUND-WP-0002 after four days unread. +- **The engine has never loaded ground-game's edition data** — `GR-E01 + unreachable` is most likely an artifact of a stand-in worth 6 points + against a real dataset worth 12. Blocked on GROUND-WP-0002 T01. +- **`python3` as a toolchain dependency was never argued.** +- **AM-4a cannot survive stage 2** — 1,741,979 against 161,000. +- **ADR-0007 D3 and D5 remain unratified**; ADR-0010 rests on D5. +- **Chaos: 2 of 12 in window 2**, no override yet at d8. diff --git a/gates.toml b/gates.toml index a3c1f89..5a804c9 100644 --- a/gates.toml +++ b/gates.toml @@ -122,6 +122,7 @@ review_by = "2026-12-31" caught = [ "four loadability breaches (401, 427, 406, 409 lines), each fixed structurally rather than by raising the limit", "a reporting tool with no --self-test entry point (tools/repo.py)", + "CB-WP-0019: two new checks, and both fired on the pass that wrote them. `own-cost` caught CB-EV-0017 quoting its own pass's cost; `lifecycle` caught CB-WP-0019 still saying `active` with every task closed. The lifecycle check's FIRST version was itself wrong and its own self-test found it — it stripped the leading `status:` assuming frontmatter, which silently dropped a real task once the frontmatter said `ready` or `active`", ] retire_if = "two passes run with no finding while artifacts keep growing — that would mean it is measuring the wrong properties" diff --git a/specs/InnerLoop.md b/specs/InnerLoop.md index 14c37ce..669c298 100644 --- a/specs/InnerLoop.md +++ b/specs/InnerLoop.md @@ -24,10 +24,8 @@ covers changes to the loop's own operation (D5). > and at half price*. `InnerLoopReference.md` §Meta budget has the > arithmetic. > -> **Soft on purpose.** A task already started may be finished — stopping -> mid-task to satisfy a ratio wastes the work and leaves the tree worse -> than either finishing or never starting. What the budget constrains is -> *opening* new meta work above the line, and it constrains it with two +> **Soft on purpose.** A task already started may be finished; what the +> budget constrains is *opening* new meta work above the line, with two > exemptions: > > 1. **Repairing the instrument that reports a breach is always in @@ -50,6 +48,22 @@ covers changes to the loop's own operation (D5). > budget, not a gate, for the same reason the session-shape budget is — > it constrains judgment, not artifacts. +> **Workplan lifecycle (v1.7).** `ready` → `active` → `done`. **`ready` +> means declared and not yet started**; a workplan being worked is +> `active`, so the status answers *"is anyone on this?"* rather than only +> *"is it finished?"*. `loop-lint` fails when a workplan has started work +> and still says `ready`, or has finished it and does not say `done`. +> Tasks keep `todo` | `done` | `cancel`; `cancel` is a **closed** outcome +> and is counted as such. + +> **Quoting a cost (v1.7).** An evidence file **may not quote its own +> pass's cost as final** — six passes under-reported themselves by 30–45%, +> never once high. **Re-run the instrument at the moment of quoting**; +> quoting from memory defeats the rule even when the boundary is right. +> `loop-lint` fails an evidence file naming its own workplan beside a +> dollar figure without marking it provisional. +> `InnerLoopReference.md` §Quoting a cost. + > **Loop work cashes out (v1.6, ADR-0006 D4).** A `meta` pass must ship at > least one executable check, command, or gate. Findings that stay prose > are recorded as findings and do **not** count as delivery. @@ -262,21 +276,12 @@ was written to stop, one level down. Where the instrument is built later in the pass, the target is marked `provisional:` until the instrument emits it, and the spec is amended to whatever the instrument returns. -*(v1.1, from CB-WP-0002: `specs/CostAccounting.md` AC-1 named -`cb-cost --pin fc76445` before that tool existed, and set the target to -a hand-computed $92.87. When the tool was built it returned $93.32 — -the hand computation carried a dedup bug the tool's own positive control -caught. The metric satisfied v1.0's rule completely and was still -wrong.)* +*Case: `InnerLoopReference.md` §Numbers you inherit.* **A number inherited from earlier work is re-derived before it is used as a target, or it is cited as unverified.** Quoting is not measuring. -*(v1.1, from CB-WP-0002: the workplan opened with $248.46, inherited -from a prior pass. Re-derivation put it at $92.21 — the quoted figure -double-counted transcript lines and priced a three-model session at one -model's rate. Neither error was of the harness-does-nothing class; both -sums ran over real data, and a positive control would have passed them.)* +*Case: `InnerLoopReference.md` §Numbers you inherit.* **Retargeting: the instrument may move a target, the implementation may not.** A metric's target changes in only two ways, and they are not @@ -292,13 +297,9 @@ mechanical: **a correction is one where the target moves and the implementation does not.** If the same commit changes both the target and the code the target measures, it is a retarget and needs the ADR. -*(v1.1, from CB-WP-0002/0003: AM-4's targets were measured at 246,250 and -set at 250,000 in one commit by the implementer after seeing the number — -the structure this rule exists to stop. But CB-WP-0002 then moved AC-1 -three times, correctly, each time because a new instrument disproved the -old figure ($92.21 → $92.87 → $93.32 → $93.15). A blanket prohibition -would have forbidden four legitimate corrections to catch one bad -retarget.)* +*The case that produced this rule, and the four legitimate corrections a +blanket prohibition would have forbidden: `InnerLoopReference.md` +§Retargeting.* **Applied retroactively:** AM-4a and AM-4b are **unratified** until an ADR is written or they are changed. They were set by the implementer after diff --git a/specs/InnerLoopReference.md b/specs/InnerLoopReference.md index 6710fd0..771decc 100644 --- a/specs/InnerLoopReference.md +++ b/specs/InnerLoopReference.md @@ -297,3 +297,87 @@ step pointed there.** Mutating it settled it in one command. Both are verification steps that inherited the author's blindness, and both fixes replace re-derivation with **adversarial execution**. + +## Quoting a cost — six for six, never high + +*(CB-WP-0019 T03, 2026-08-03. The rule lived only in evidence files and +had been re-derived three times before it was written down.)* + +| pass | reported mid-flight | settled | under by | +|---|---|---|---| +| CB-WP-0009 | $6.73 | $11.31 | 40% | +| CB-WP-0011 | $4.23 | $7.02 | 40% | +| CB-WP-0012 | $8.82 | $14.52 | 39% | +| CB-WP-0013 | $5.78 | $8.26 | 30% | +| CB-WP-0015 | $15.14 | $22.70 | 33% | +| CB-WP-0017 | $5.19 | $9.48 | 45% | + +**Six for six, always low, never by less than 30%.** That is systematic +bias, not noise, and it has two causes — both of which must be fixed or +the rule does not hold: + +1. **The boundary.** A pass's spend window runs to the **next** pass's + first commit, so a pass is not final until the one after it starts. + Quoting "the previous pass" is therefore still too early if the pass + after it has not been declared. (CB-EV-0013 §5.) +2. **The reading.** *A figure read earlier in a session is an in-flight + figure regardless of whose name is on it.* CB-EV-0014 quoted + CB-WP-0015 at $15.14/136 and called it settled; it had been read during + CB-WP-0015 itself and settled at $22.70/166 — 50% higher. Quoting from + memory defeats the rule even when the boundary is right. + (CB-EV-0015 §6.) + +**Which is load bearing?** The second. CB-WP-0018 T04 applied it alone — +re-running `make status` at the moment of writing — and the figure it +quoted was correct. The boundary matters only because re-running early +still reads an open window; re-running *late* fixes both. So the +operative instruction is **re-run the instrument when you quote it**, and +the boundary rule is what tells you when "late enough" has arrived. + +## Retargeting — the case, and the four corrections it must not forbid + +*(v1.1, from CB-WP-0002/0003. Moved out of `InnerLoop.md` by CB-WP-0019 +T03 at the loadability limit.)* + +**The bad retarget.** AM-4's targets were measured at 246,250 and set at +250,000 in one commit, by the implementer, after seeing the number. That +is the structure the rule exists to stop: a target chosen to fit the +reading rather than a reading judged against a target. + +**The four good corrections.** CB-WP-0002 then moved AC-1 three times — +$92.21 → $92.87 → $93.32 → $93.15 — each because a *new instrument* +disproved the old figure. A blanket prohibition on moving a target would +have forbidden all four to catch the one. + +Hence the mechanical test rather than an appeal to intent: **a correction +is one where the target moves and the implementation does not.** If one +commit changes both the target and the code that target measures, it is a +retarget and needs an ADR. + +CB-WP-0019 T01 is a worked example on the good side: AM-4b's target moved +745,000 while the code it measures did not change at all — only the +instrument's scope did, and its output is in the same commit. + +## Numbers you inherit — two cases from CB-WP-0002 + +*(v1.1. Moved out of `InnerLoop.md` by CB-WP-0019 T03.)* + +**A target set before its instrument existed.** `specs/CostAccounting.md` +AC-1 named `cb-cost --pin fc76445` before that tool was built, and set the +target to a hand-computed **$92.87**. When the tool existed it returned +**$93.32** — the hand computation carried a dedup bug the tool's own +positive control caught. *The metric satisfied v1.0's rule completely and +was still wrong.* + +**A number quoted rather than measured.** The workplan opened with +**$248.46**, inherited from a prior pass. Re-derivation put it at +**$92.21**: the quoted figure double-counted transcript lines and priced a +three-model session at one model's rate. + +Neither error was of the harness-does-nothing class — both sums ran over +real data, and a positive control would have passed them. That is why the +rule is *re-derive before using as a target, or cite as unverified*, and +not merely *add a control*. + +**Quoting is not measuring** — the same sentence the cost-quoting rule +above rests on, learned here first and re-learned six times since. diff --git a/tools/loop-lint.py b/tools/loop-lint.py index be436b7..d8b7d60 100644 --- a/tools/loop-lint.py +++ b/tools/loop-lint.py @@ -94,6 +94,102 @@ def check_evidence_no_unmeasured(root=REPO): return out +def check_own_cost_not_quoted(root=REPO): + """§Quoting a cost — an evidence file may not quote its own pass's + cost as final. + + Six passes under-reported themselves by 30-45%, never once high, so a + self-quoted figure is not a rounding error but a known bias. The check + is deliberately narrow: it fires only when a line names the file's OWN + workplan beside a dollar amount and does not mark it provisional. + Quoting an earlier pass is exactly what the rule asks for. + + **Binds forward, from the pass that wrote it down.** The rule was + written in CB-WP-0019 after six passes had each under-reported + themselves, and those six evidence files are the *evidence for the + rule*. Firing on them would demand the record be edited to remove the + thing it proves — the same category error as putting a live `fact:` + tag on a dated measurement. So the check applies from CB-WP-0019 on, + and the older figures stay as they were reported. + """ + BINDS_FROM = 19 + out = [] + base = os.path.join(root, "evidence") + if not os.path.isdir(base): + return out + for f in sorted(os.listdir(base)): + if not f.endswith(".md"): + continue + rel = os.path.join("evidence", f) + text = open(os.path.join(root, rel)).read() + # The pass an evidence file belongs to is named in its header. + m = re.search(r"\b(CB-WP-\d{4})\b", text) + if not m: + continue + own = m.group(1) + if int(own.rsplit("-", 1)[1]) < BINDS_FROM: + continue + for i, line in enumerate(text.splitlines(), 1): + if own not in line or "$" not in line: + continue + if "provisional" in line.lower() or "not quoted" in line.lower(): + continue + out.append( + Finding("own-cost", f"{rel}:{i}", + f"quotes {own}'s own cost as final; re-run the " + f"instrument and quote a settled pass, or mark it " + f"provisional") + ) + return out + + +def check_workplan_lifecycle(root=REPO): + """§Workplan lifecycle — `ready` means declared and NOT started. + + A workplan with work done in it that still says `ready` answers the + wrong question: the status should say whether anyone is on it, not + only whether it is finished. + """ + out = [] + base = os.path.join(root, "workplans") + if not os.path.isdir(base): + return out + for f in sorted(os.listdir(base)): + if not f.endswith(".md"): + continue + rel = os.path.join("workplans", f) + text = open(os.path.join(root, rel)).read() + m = re.search(r"^status:\s*(\S+)", text, re.M) + if not m: + continue + status = m.group(1) + # Parse the ```task blocks, not every `status:` in the file. The + # first version stripped the leading match assuming it was the + # frontmatter — which silently dropped a real task the moment the + # frontmatter said `ready` or `active`, because those do not match + # the task vocabulary. Caught by this check's own self-test. + tasks = [ + t.group(1) + for block in re.findall(r"```task\n(.*?)```", text, re.S) + for t in [re.search(r"^status:\s*(\S+)", block, re.M)] + if t + ] + if not tasks: + continue + closed = sum(1 for t in tasks if t in ("done", "cancel")) + started = closed > 0 + if status == "ready" and started: + out.append(Finding("lifecycle", rel, + "still `ready` but work has started — use `active`")) + elif status == "active" and closed == len(tasks): + out.append(Finding("lifecycle", rel, + "every task is closed but status is `active` — use `done`")) + elif status == "done" and closed != len(tasks): + out.append(Finding("lifecycle", rel, + f"`done` with {len(tasks) - closed} task(s) still open")) + return out + + def check_survey_tier_and_chaos(root=REPO): """§Loop tiers — tier declared, and the chaos roll recorded every time.""" out = [] @@ -213,6 +309,8 @@ def check_gate_registry(root=REPO): CHECKS = ( check_loadability, check_evidence_no_unmeasured, + check_own_cost_not_quoted, + check_workplan_lifecycle, check_survey_tier_and_chaos, check_review_trail, check_reporting_tools_self_test, @@ -245,6 +343,53 @@ def self_test(): len(f) == 1 and "Big.md" in f[0].path, f"{len(f)} finding(s)") + # own-cost: a file quoting its OWN pass beside a dollar amount + # trips; the same line marked provisional does not; and a file + # quoting an EARLIER pass does not, because that is the rule. + def ev(name, body): + with open(os.path.join(tmp, "evidence", name), "w") as fh: + fh.write(body) + ev("CB-EV-0100-self.md", "CB-WP-0019 T04.\n| CB-WP-0019 | $9.99 |\n") + f = check_own_cost_not_quoted(tmp) + check("own-cost detects a pass quoting itself", len(f) == 1, + f"{len(f)} finding(s)") + ev("CB-EV-0100-self.md", + "CB-WP-0019 T04.\n| CB-WP-0019 | $9.99 provisional |\n") + check("own-cost accepts a figure marked provisional", + not check_own_cost_not_quoted(tmp)) + ev("CB-EV-0100-self.md", "CB-WP-0019 T04.\n| CB-WP-0018 | $28.08 |\n") + check("own-cost accepts quoting an EARLIER pass", + not check_own_cost_not_quoted(tmp)) + # and it binds forward: the six passes that PROVE the rule are the + # evidence for it, and must not be edited to satisfy it. + ev("CB-EV-0100-self.md", "CB-WP-0009 T04.\n| CB-WP-0009 | $6.73 |\n") + check("own-cost binds forward, not over the record it rests on", + not check_own_cost_not_quoted(tmp)) + os.remove(os.path.join(tmp, "evidence", "CB-EV-0100-self.md")) + + # lifecycle: `ready` with work started trips; `active` does not. + def wp(status, tasks): + body = f"---\nid: CB-WP-0100\nstatus: {status}\n---\n" + for t in tasks: + body += f"\n```task\nid: CB-WP-0100-T\nstatus: {t}\npriority: high\n```\n" + with open(os.path.join(tmp, "workplans", "CB-WP-0100-x.md"), "w") as fh: + fh.write(body) + wp("ready", ["done", "todo"]) + f = check_workplan_lifecycle(tmp) + check("lifecycle detects `ready` after work has started", + len(f) == 1 and "active" in f[0].detail, f"{len(f)} finding(s)") + wp("active", ["done", "todo"]) + check("lifecycle accepts `active` mid-flight", + not check_workplan_lifecycle(tmp)) + wp("active", ["done", "cancel"]) + f = check_workplan_lifecycle(tmp) + check("lifecycle detects `active` when everything is closed", + len(f) == 1 and "done" in f[0].detail, f"{len(f)} finding(s)") + wp("done", ["done", "todo"]) + check("lifecycle detects `done` with an open task", + len(check_workplan_lifecycle(tmp)) == 1) + os.remove(os.path.join(tmp, "workplans", "CB-WP-0100-x.md")) + # gates: an unclassified `all:` dependency trips, and so does an # entry naming a target the Makefile lacks. with open(os.path.join(tmp, "Makefile"), "w") as fh: diff --git a/workplans/CB-WP-0019-the-am4-family.md b/workplans/CB-WP-0019-the-am4-family.md index 99e0384..557316f 100644 --- a/workplans/CB-WP-0019-the-am4-family.md +++ b/workplans/CB-WP-0019-the-am4-family.md @@ -2,7 +2,7 @@ id: CB-WP-0019 kind: meta title: "Budgets: what the meta budget is for, AM-4b's scope, and the self-quoting rule" -status: todo +status: done state_hub_workstream_id: "ecf35aae-8549-4338-9e13-21dcf14c1130" --- @@ -151,7 +151,7 @@ of two. The `unmeasured` note is gone from `dep-weight.py` and the spec. ```task id: CB-WP-0019-T03 -status: todo +status: done priority: high state_hub_task_id: "b473ab42-8fd9-47b1-8fd4-b181c75440be" ``` @@ -187,6 +187,30 @@ re-derived three times already. both? T03 of CB-WP-0018 used the second and it worked. Say which is load bearing. +**Done 2026-08-03.** InnerLoop v1.7 + `loop-lint`'s `own-cost` check. + +**The reading is load bearing, not the boundary.** CB-WP-0018 T04 applied +*re-run the instrument at the moment of quoting* alone and its figure was +correct; the boundary rule only tells you when "late enough" has arrived. +So the operative instruction is **re-run the instrument when you quote +it**, and `loop-lint` fails an evidence file naming its own workplan +beside a dollar amount without marking it provisional. + +**It binds forward, from this pass**, and that is not a convenience: the +check fires on seven historical files which *are the evidence for the +rule*. Making them comply would edit the record to remove the thing it +proves — the same category error as a live `fact:` tag on a dated +measurement, which this pass also hit and also fixed by marking rather +than rewriting. + +Also delivered here at the maintainer's instruction: the **workplan +lifecycle** `ready` → `active` → `done`, with `loop-lint` failing a +workplan that has started and still says `ready`. **The first version of +that check was wrong and its own self-test caught it** — it stripped the +leading `status:` assuming frontmatter, which silently dropped a real task +once the frontmatter said `ready` or `active`. It now parses the task +blocks. + ## Task: say what the meta budget is *for*, and set it at 80/20 ```task @@ -278,7 +302,7 @@ standing precedent it exists to enforce. ```task id: CB-WP-0019-T04 -status: todo +status: done priority: high state_hub_task_id: "b499acb2-b728-4db9-9714-c235fb6c6363" ``` @@ -299,3 +323,22 @@ state_hub_task_id: "b499acb2-b728-4db9-9714-c235fb6c6363" have failed passes that were plainly right to run, say so — that is the ratio being wrong, not the passes. - **Chaos: declaration 2 of 12 in window 2, first roll at d8.** + +**Done 2026-08-03.** +[CB-EV-0017](../evidence/CB-EV-0017-budgets.md). `make all` exits 0. + +- **The AM-4 family is closed** — four defects across five passes, none + found by looking for them. Where a fifth would be: AM-4c is a + diagnostic nobody reads, and `own source` has no instrument at all. +- **Three of this project's own controls fired on this pass**, none of + them its subject: the stale mutation string (**build-free**, second + catch for CB-WP-0015's check and the first that cost nothing), the DFD + gate on `facts.toml`, and loadability three times — fixed structurally + each time, the precedent now holding five times. +- **The new meta budget's first reading is a breach it caused**: 27% + against the 20% line, because this pass cost $31.18 against product + passes averaging ~$21. Reported rather than exempted — ADR-0006 D2 + covers the instrument repairs but not T03/T05's rule-writing, and the + honest reading is that this should have been two passes. +- **CB-WP-0018 settled at $36.53/95** against $28.08/82 last reported — + 30% higher. **Seven for seven.**