CB-WP-0019 T03/T04: the cost rule written down, and the lifecycle
Some checks failed
ci / check (push) Has been cancelled

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 <noreply@anthropic.com>
This commit is contained in:
tegwick 2026-08-03 19:25:18 +02:00
parent 0b6f7c5bc8
commit 69c1658d20
6 changed files with 483 additions and 25 deletions

View file

@ -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** (U1U10) 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.

View file

@ -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"

View file

@ -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 3045%,
> 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

View file

@ -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.

View file

@ -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:

View file

@ -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.**