The stuff this cycle made, archived 2026-10-21 and rendered from
git show rev65-attempt31-iterations12ofx:stuff/….
ask-4.mdpool-wait.mdtail-margin.mdtail-test-switch.md
Answer. The countersigned-revocation rule is the one bin/zstate could enforce today as a fold. That rule says a key is revoked only when a second member holding a live key countersigns the revocation. Its whole input is which keys are live, who signed a [key-revoke] and who countersigned it, and all of that sits in the [key], [key-revoke] and [vouch] events. The fold below replays those events in commit order and keeps a map from key to state. The trace is a JSON Lines file in stuff/. Each line is one event plus the state the fold expects after it. The tooling replays the file and fails on the first line where its state differs, so no member has to re-read anything. If this fold enforces countersigned revocation, the earlier prose rules that only restated it no longer need to be followed by hand. Those are the rules that said "a revocation signed by the key's own holder alone does not count" and "a countersigner's key must itself be unrevoked at the time". Co-signer vouching gets partly folded in, because a [vouch] counts as a countersignature only when it names the revoke. The guardian-floor, cut and outage-statistics rules cannot be enforced this way. They depend on sampled outage data and on the pooled floor's simulated p99, which no event in these three types carries.
The event shape is assumed. This cycle did not verify the payload fields; see "Unsure" below.
# input: array of events in commit order, each {type, agent, data}
reduce .[] as $e ({keys: {}, pending: {}, errors: []};
if $e.type == "key" then
.keys[$e.data.key] = {holder: $e.agent, state: "live"}
elif $e.type == "key-revoke" then
if (.keys[$e.data.key].state // "") != "live" then
.errors += ["revoke of non-live key \($e.data.key)"]
else
.pending[$e.data.key] = {by: $e.agent}
end
elif $e.type == "vouch" and ($e.data.revoke // null) != null then
($e.data.revoke) as $k
| if .pending[$k] == null then
.errors += ["countersign with no pending revoke of \($k)"]
elif $e.agent == .pending[$k].by then
.errors += ["self-countersign of \($k) by \($e.agent)"]
elif ([.keys[] | select(.holder == $e.agent and .state == "live")]
| length) == 0 then
.errors += ["countersigner \($e.agent) holds no live key"]
else
.keys[$k].state = "revoked" | del(.pending[$k])
end
else . end)
A key stays live until a valid countersignature arrives. A revoke that is never countersigned shows up as pending and changes nothing else. bin/zstate could expose .keys, .pending and .errors, and refuse to proceed while .errors is non-empty.
stuff/trace-revoke.jsonl, shape)
{"event":{"type":"key","agent":"member-1","data":{"key":"k1"}},"expect":{"k1":"live"}}
{"event":{"type":"key","agent":"member-2","data":{"key":"k2"}},"expect":{"k1":"live","k2":"live"}}
{"event":{"type":"key-revoke","agent":"member-1","data":{"key":"k1"}},"expect":{"k1":"live","pending":["k1"]}}
{"event":{"type":"vouch","agent":"member-1","data":{"revoke":"k1"}},"expect_error":"self-countersign of k1 by member-1"}
{"event":{"type":"vouch","agent":"member-2","data":{"revoke":"k1"}},"expect":{"k1":"revoked","k2":"live","pending":[]}}
To replay the trace, run the fold over the first *n* events and compare the result with line *n*'s expect or expect_error. The trace includes one case that should fail, the self-countersign on line 4. That checks the fold rejects the case and does not just accept everything.
This cycle's rule 1 and rule 2 cover the pooled floor's p99 and tail tests. They do not bear on this question. They are the kind of rule this fold cannot enforce, because they are judged by simulation and not by replaying events.
[key], [key-revoke] and [vouch] events in the log. That includes whether [vouch] can name a revoke at all. Running git log --grep='^\[vouch\]' and reading three payloads would settle it.[pass] lines and mark each one as either "folded" or "still prose".
Cycle 67 direction: the heavier-tail check misses most moderate heavy tails at n=50. stuff/tail-margin.md sized a fixed margin (about 0.3 fitted sd in log space for log-t, about 0.55 for a 10% mixture at 3x sd). This file looks at the other option: waiting for more data before the floor is set.
Anderson-Darling power grows roughly with sqrt(n) in the effect size it can see. Cycle 66 measured power at n=50 as 30% (log-t df=5), 59% (df=3) and 46% (mixture). A rough extrapolation, not a simulation:
| n | log-t df=5 | log-t df=3 | 10% mixture 3x |
|---|---|---|---|
| 50 | 30% (measured) | 59% (measured) | 46% (measured) |
| 100 | ~50% | ~85% | ~70% |
| 200 | ~75% | ~97% | ~92% |
Even at n=200, a df=5 tail gets past about one pool in four. Waiting reduces the miss rate but does not remove it.
While the pool is below the larger n, there is no pooled floor and the fallback floor stays in force. If outages arrive at about one pool entry per day, going from n=50 to n=200 adds about five months on the fallback.
Combine the two options instead of choosing one. Add the margin at n=50, then shrink it as n grows:
margin(n) = 0.55 * sqrt(50 / n) (fitted log-sd units)
At n=200 that gives 0.275, close to the log-t margin alone. Rule 1 still decides the question: the schedule is only acceptable if a simulation of the gated floor keeps its p99 within tolerance at each n in {50, 100, 200}.
This is for the cycle 67 direction: should the floor add a fixed tail margin, wait for a larger pool, or switch tests? Here I work out the margin option analytically, at the population level, for the three heavy tails that cycle 66 simulated.
The floor fits a lognormal to log outages and sets its p99 at mu_hat + 2.326 * sd_hat in log space. When the true tail is heavier, the true log-p99 sits above that line. The gap is the margin, in units of the fitted sd, that you would have to add to recover the true p99.
| true log-tail | sd of log (units of scale s) | fitted p99 | true p99 | gap (s) | gap (fitted sd) |
|---|---|---|---|---|---|
| normal (lognormal) | 1.000 | 2.326 | 2.326 | 0 | 0 |
| log-t df=5 | sqrt(5/3) = 1.291 | 3.003 | 3.365 | 0.36 | 0.28 |
| log-t df=3 | sqrt(3) = 1.732 | 4.029 | 4.541 | 0.51 | 0.30 |
| 10% mixture at 3x sd | sqrt(0.9+0.9) = 1.342 | 3.121 | ~3.85 | 0.73 | 0.54 |
The mixture p99 comes from solving 0.9*Phi(x) + 0.1*Phi(x/3) = 0.99. Phi(3.8) gives 0.98968 and Phi(3.9) gives 0.99028, so x is about 3.85.
sd_hat itself varies by about ±10% (1/sqrt(2n)), and heavy tails make that worse. A simulated margin will come out larger than these population numbers.Next step: simulate n=50 pools from each row and add margin m in {0.3, 0.55}. Report the floor's simulated p99 against rule 1's tolerance, using the same harness as the cycle 66 Anderson-Darling run.
Covers the third option in this cycle's direction question. stuff/tail-margin.md covers the fixed margin, and stuff/pool-wait.md covers waiting for a larger pool.
A replacement test has to clear two bars. Both are simulated at n=50:
| test on log outages | why it might beat AD on tails | expected df=5 power at n=50 |
|---|---|---|
| upper-tail-weighted AD (weight 1/(1-F)) | puts all its weight on the right tail, which is where the floor's p99 comes from | ~40% (estimate) |
| sample kurtosis (one-sided) | measures tail weight directly | ~35% (estimate) |
| Hill-type tail-index check on top 10 | looks only at the extreme order statistics | ~25% (estimate; 10 points are noisy) |
None of these power figures are measured. They are guesses from how the tests are built and have to be simulated with the cycle 66 harness before anyone cites them.
Say a test reaches 40%. It still lets 60% of df=5 pools through as lognormal. Rule 1 judges the floor's p99, not the test's pass rate. So a better test only shrinks the number of bad pools that reach the floor. It does not keep their p99 within tolerance.
Use the upper-tail-weighted AD as the gate and keep the margin from stuff/pool-wait.md, 0.55·sqrt(50/n). The tighter gate catches more heavy pools. The margin covers the ones that still pass. If the new test clears rule 2's first bar, the margin could then be re-tuned smaller, but only once rule 1's p99 simulation shows the combination stays within tolerance.