You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
CENSUS-RATCHET: entity-bound contracts never drop and rise every release #4709
Operator C303, verbatim: "you need to include a ratchet for every release to close the - pv census: counts contracts. Only 48 of 1,889 are tied to a real thing so far, which is the biggest gap."
Parent: epic #4678 (0.70.2). Cross-link: ONT epic #3269.
The gate (scripts/check_census_ratchet.sh, measured by pv census, no Python, no stored limit)
Every PR (never-worse). It runs head vs base, with the same pv binary in the same run (base = scripts/lib/resolve_base.sh, extracted with git archive). RED if bound(head) < bound(base), or if unbound(head)/n(head) > unbound(base)/n(base). The shares are compared by integer cross-multiplication. guard-tree runs it on every PR (it lives under scripts/check_*.sh, so it needs no workflow edit).
Every release. It runs --release <version> --commit <sha>, called from autopilot.sh step readiness before any tag exists. It requires bound(release) ≥ bound(previous tag) + STEP, where STEP = min(100, unbound(previous tag)). 0.70.2 is the baseline and pays no step; the step starts at 0.71.0.
It never passes vacuously. pv missing or without census, a non-zero exit, unparseable JSON, 0 contracts, parse errors, or anchoring counts that do not sum to n_parsed: each one gives NOT_MEASURED and a FAIL.
Red/green proof.--self-test runs the real pv on fixture repos. Unbinding one contract → RED. Adding an unbound contract → RED. Binding one → GREEN. A release short of the step → RED. A stub pv that prints garbage or 0 contracts → NOT_MEASURED RED.
Projection
unbound = 1841 at baseline. At 100 per train from 0.71: 0.71 → 148 bound, 0.72 → 248, …, 0.88 → 1848, and 0.89 binds the last 41 (1889/1889). That is 19 trains (0.71–0.89), provided the never-worse share rule holds, so no new unbound debt arrives.
Step owners
The binding work (~100 contracts → entity: per train) goes to the look-ahead lanes. Proposed first owner: L1 0.71 la-71.
Done when
The guard is on main, its self-test is green, and the mutation RED is shown in the PR
So 0.71.0 owed bound ≥ 148 under the rule as written and shipped 48. Between v0.70.1 and main, n grew by 24 and bound by 2, so 22 of the 24 new contracts arrived unbound. The share barely moved (97.46% → 97.39%) only because 2 of the 24 were bound. That is the debt the never-worse share rule exists to stop.
What is already there
scripts/check_ont_ratchet.sh already measures base and head with one instrument (resolve_base + git archive) and refuses a fall in contracts_anchored. But it counts entity: lines, not pv census anchoring, and it has no share rule, no release step, and no CI runner (only make ont-ratchet). The new guard reuses its base resolution and leaves its counters alone.
Build: scripts/census_ratchet.sh (deliberately NOT check_*.sh at first)
As specified above: head vs base with the same pv in one run; bound never falls; the unbound share never rises (integer cross-multiplication); --release <ver> --commit <sha> requires bound ≥ bound(previous tag) + min(100, unbound(previous tag)); no stored limit. Any of the following is NOT_MEASURED → FAIL, never a pass:
pv missing or without census
a non-zero exit
unparseable JSON
0 contracts
parse errors
anchoring counts that do not sum to n_parsed
--self-test runs the real pv on fixture trees, with these rows:
unbind one → RED
add an unbound contract → RED
bind one → GREEN
a release short of the step → RED
a stub pv printing garbage or 0 contracts → NOT_MEASURED RED
A planted mutant shows that each row can go RED.
Why not check_*.sh on day one (C324)
guard-tree runs every git ls-files 'scripts/check_*.sh' on every PR. So naming it check_census_ratchet.sh would make it a merge GATE on landing. Under C324 a new gate needs the yes ruling, three green nights on main first, and one gate retired. Order:
PR 1: the script plus its self-test, run nightly as a LAB step in guards-nightly. A red there opens or updates this issue, and nothing else. The baseline row goes in the PR body.
After three green nights: a promotion round asks for the yes ruling and names the gate it retires. Promotion is a rename to check_census_ratchet.sh, which puts it in guard-tree, and an autopilot readiness call with --release.
The release step needs a decision before it can arm
The step was meant to start at 0.71.0 and was never measured, so today --release 0.71.3 would demand 148 against 50, and 0.72.0 would demand at least 150. Proposal: the step starts at the first release cut after promotion, from the bound of the tag before it, with no back-debt for 0.71.x. That ruling goes to the quorum with the promotion round. The binding work (~100 entity: bindings per train) needs owners per train. That is coordinated with the pv P0 lane, because empty contracts change n and the share.
Timing
PR 1 waits on an a3 slot word (open PRs are near the cap). The self-test and the planted RED are built and run before then.
Evidence (la-0.71), the "Done when" items checked against main 8f601ca. I am not closing the issue.
Measured on a fleet host, worktree detached at 8f601ca, with pv 0.71.3 built from that tree (cargo build --release -p aprender-contracts-cli --bin pv, rc 0).
1. The guard is on main, its self-test is green, and a mutation RED is shown in the PR: MET, as a LAB check.
scripts/census_ratchet.sh landed in feat(contracts): pv census ratchet, LAB nightly (#4709) #5359 (merged 2026-10-14 22:51Z as 091e68d). It runs from guards-nightly.yml only (schedule + workflow_dispatch), never on a PR. C324 replaced the "every PR, guard-tree" form in this issue's body, which is why the script is not named check_*.sh.
census_ratchet.sh --self-test at 8f601ca gives SELF-TEST PASSED, rc 0, with 23/23 rows ok. All 7 real-pv rows now pass: unchanged→GREEN, unbind→RED, add unbound→RED, bind→GREEN, add bound→GREEN, drop falsification_tests→RED, bound fell→RED.
Nightly mode at 8f601ca against base a77040a: base and head are both n=1963, bound=50, unbound=1913, missing_ft=803, giving GREEN, rc 0.
2. autopilot readiness calls --release: NOT MET, and not built.
scripts/release/autopilot.sh at 8f601ca has no census call. Under the standing policy (AP_POLICY, line 172), readiness (R8) is not run at all; CRUX smoke is the release gate.
Wiring it in would add a release GATE. C324 item 7 allows that only with the operator's yes, three green nights on main first, and one gate retired for each one added (C333 caps release-stopping checks at 20). None of those three exists yet, so I built nothing for this item.
Release mode measured today: --release 0.72.0 --commit 8f601caff2 against v0.71.3 gives RED bound 50 < 150 (prev 50 + step min(100, 1909)), rc 1. As a gate it would stop 0.72.0. The ~100 contracts→entity: binding step for this train has not been done.
3. The baseline row is recorded in the PR: MET, with a newer measurement.
Activity
fix(contracts): five aprender binding rows named the wrong function
chore(contracts): regenerate contracts.nt for the qualified binding r…
contracts: bind 21 method contracts to their type-qualified symbol
docs(lookahead): #4709 census step 2 at 117 of 148, and the routes fo…
contracts: bind continuous-batching and quantization-ordering
docs(lookahead): #4709 census step 3 at 119 of 148
contracts: bind 31 same-stem twins by their own directory's binding rows
contracts: keep same-stem twins byte-identical where main has them id…
docs(lookahead): #4709 census step 4 at 153, release rule green
docs(lookahead): #4709 three proposed pv census changes
5 remaining items
noahgift commentedon Oct 7, 2026
slipped_from: 0.70.2 -- carried to backlog at the 0.70.2 cut by the release autopilot (not must-carry, and not listed by the 0.71.0 epic)
docs(reports): 0.71 counts from a gate-graph walk; census check liste…
noahgift commentedon Oct 14, 2026
Plan (la-0.72, owner from 2026-10-14)
Baseline, re-measured
I ran
pv census --format json contracts(pv 0.71.2 f0a7b07) ongit archive <ref> contracts. bound = class + instance.So 0.71.0 owed bound ≥ 148 under the rule as written and shipped 48. Between v0.70.1 and main, n grew by 24 and bound by 2, so 22 of the 24 new contracts arrived unbound. The share barely moved (97.46% → 97.39%) only because 2 of the 24 were bound. That is the debt the never-worse share rule exists to stop.
What is already there
scripts/check_ont_ratchet.shalready measures base and head with one instrument (resolve_base +git archive) and refuses a fall incontracts_anchored. But it countsentity:lines, not pv census anchoring, and it has no share rule, no release step, and no CI runner (onlymake ont-ratchet). The new guard reuses its base resolution and leaves its counters alone.Build:
scripts/census_ratchet.sh(deliberately NOTcheck_*.shat first)As specified above: head vs base with the same pv in one run; bound never falls; the unbound share never rises (integer cross-multiplication);
--release <ver> --commit <sha>requires bound ≥ bound(previous tag) + min(100, unbound(previous tag)); no stored limit. Any of the following is NOT_MEASURED → FAIL, never a pass:census--self-testruns the real pv on fixture trees, with these rows:A planted mutant shows that each row can go RED.
Why not
check_*.shon day one (C324)guard-tree runs every
git ls-files 'scripts/check_*.sh'on every PR. So naming itcheck_census_ratchet.shwould make it a merge GATE on landing. Under C324 a new gate needs the yes ruling, three green nights on main first, and one gate retired. Order:check_census_ratchet.sh, which puts it in guard-tree, and an autopilotreadinesscall with--release.The release step needs a decision before it can arm
The step was meant to start at 0.71.0 and was never measured, so today
--release 0.71.3would demand 148 against 50, and 0.72.0 would demand at least 150. Proposal: the step starts at the first release cut after promotion, from the bound of the tag before it, with no back-debt for 0.71.x. That ruling goes to the quorum with the promotion round. The binding work (~100entity:bindings per train) needs owners per train. That is coordinated with the pv P0 lane, because empty contracts change n and the share.Timing
PR 1 waits on an a3 slot word (open PRs are near the cap). The self-test and the planted RED are built and run before then.
noahgift commentedon Oct 15, 2026
Evidence, night 1 (NOT_MEASURED). The census ratchet's first scheduled night ran.
no_falsification_testsuntil fix(pv): refuse empty or missing falsification tests; no-test debt only shrinks, capped per release (Refs #5291) #5295 lands (it is still open), so the census cannot be measured. This is the expected LAB red, tracked on LAB: nightly pv census ratchet red (#4709) #5366 (backlog).This is not a measured night. #4709 stays open until the first night after #5295 measures GREEN or RED; a second evidence comment follows that run.
noahgift commentedon Oct 15, 2026
Evidence (la-0.71), the "Done when" items checked against main 8f601ca. I am not closing the issue.
Measured on a fleet host, worktree detached at 8f601ca, with
pv 0.71.3built from that tree (cargo build --release -p aprender-contracts-cli --bin pv, rc 0).1. The guard is on main, its self-test is green, and a mutation RED is shown in the PR: MET, as a LAB check.
scripts/census_ratchet.shlanded in feat(contracts): pv census ratchet, LAB nightly (#4709) #5359 (merged 2026-10-14 22:51Z as 091e68d). It runs fromguards-nightly.ymlonly (schedule + workflow_dispatch), never on a PR. C324 replaced the "every PR, guard-tree" form in this issue's body, which is why the script is not namedcheck_*.sh.census_ratchet.sh --self-testat 8f601ca givesSELF-TEST PASSED, rc 0, with 23/23 rows ok. All 7 real-pv rows now pass: unchanged→GREEN, unbind→RED, add unbound→RED, bind→GREEN, add bound→GREEN, drop falsification_tests→RED, bound fell→RED.no_falsification_teststopv census. LAB ticket LAB: nightly pv census ratchet red (#4709) #5366 holds that red. The next scheduled nightly runs at a sha that includes fix(pv): refuse empty or missing falsification tests; walk kaizen; no-test debt 803 only shrinks, capped per release (Refs #5291) #5362.resolve_pvfix is reverted.GREEN, rc 0.2. autopilot readiness calls
--release: NOT MET, and not built.scripts/release/autopilot.shat 8f601ca has no census call. Under the standing policy (AP_POLICY, line 172), readiness (R8) is not run at all; CRUX smoke is the release gate.--release 0.72.0 --commit 8f601caff2against v0.71.3 givesRED bound 50 < 150 (prev 50 + step min(100, 1909)), rc 1. As a gate it would stop 0.72.0. The ~100 contracts→entity:binding step for this train has not been done.3. The baseline row is recorded in the PR: MET, with a newer measurement.
What is left (an operator ruling, not a lane's call):
--releasea release GATE, after three green nights, with one gate retired. Or the release-mode check stays LAB.noahgift commentedon Oct 15, 2026
Folded in (cop batch 10:31Z): #5366 — each is now a checklist row of this master.