[PROPOSAL] Fool seed rule filed before the beacon; two unresolvable values caught (PENDING-149)

§4 steps 1-4 discharged and pushed ahead of the 2026-08-25T12:00:00Z beacon:
ratified axes recorded in writing, seed derivation rule filed, retirement and
regeneration criteria filed, derivation implemented and self-tested.

Two values in the jurist's §6b block did not resolve, and both are corrected in
the open rather than absorbed:

  - the provenance commit's stated rationale was false. 3b0730d5 (2026-08-06)
    postdates the fool's conception by five days, its subject names the
    PENDING-89 docket, and Constraint 6 is already in it. Steward directed
    4d2ae87 (2026-07-28), where Constraint 6 occurs zero times.

  - the retrieval URL returns HTTP 302 and an empty body, redirecting to an
    HTML page. Filed verbatim, the 25th would have produced no pulse and the
    UNAVAILABILITY clause would have run a 24-hour retry against an address
    that can never return one. Found only because §6b directs a historical
    dry run.

Also measured: outputValue is served UPPERCASE, so the rule's "lowercased
before use" is load-bearing; and curl reaches the beacon where python urllib
times out.

Nothing derived. Target pulse not fetched. CLAUDE.md untouched (PENDING-150).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JQKeKY9T9d95KpvHwwok8T
This commit is contained in:
David F Glidden
2026-08-22 20:52:12 +02:00
co-authored by Claude Opus 5
parent 7b366eb646
commit acfbb9fc0e
9 changed files with 1606 additions and 2 deletions
@@ -0,0 +1,181 @@
---
name: FOOL-SEED-RULE
description: "The filed rule required by PENDING-149 §4 steps 1-4: ratified axes, seed derivation rule (both components), retirement and regeneration criteria. Filed and pushed BEFORE the beacon timestamp 2026-08-25T12:00:00Z. Governs derive_fool.py; where the code and this rule disagree, THIS RULE GOVERNS."
metadata:
node_type: governance-artifact
type: reference
---
# FOOL SEED DERIVATION RULE — filed 2026-08-22
**Filed and pushed before the beacon timestamp, as §4 requires.** Nothing has been
derived. No target pulse has been fetched. This document governs
`derive_fool.py`; where the code and this rule disagree, **the rule governs and the
code is the defect**.
---
## 1 · Perception axes — RATIFIED
**Ratified by the steward in writing, 2026-08-22.** The steward ratified the five
verbally ("Perfect", after §5a was settled) and then directed that the ratification be
recorded explicitly rather than resting on v2's §5 heading — *"a heading asserting
ratification and a deliverable requiring it are two different records."* This section
is that record.
| axis | question |
|---|---|
| **SUCCESSION** | would this be legible to someone arriving cold, with no thread? |
| **ABSENCE** | what is not here, not asked, not yet existing? |
| **AIM** | is this the right question, at the right level? |
| **SCALE** | is the unit right? (item vs block vs programme) |
| **STAKE** | who bears the cost if this is wrong? |
A sixth **PROCEDURE** axis was proposed by the executor and **declined** by the jurist
(v2 §5a) on structural grounds the executor accepts: procedure failures are checkable,
§2 makes gradeable output a design failure, and a PROCEDURE-peaked fool would produce
nothing but gradeable observations. Redirected to a separate `[HARDENING]` extension of
`governance-drift-check.py`.
**Order is fixed as listed** — the derivation permutes over this order, so it is part of
the rule, not presentation.
## 2 · The filed rule
```
FOOL SEED DERIVATION RULE
Filed: 2026-08-22 Governs: PENDING-149 §6
ENTROPY COMPONENT
Source: NIST Randomness Beacon v2.0, https://beacon.nist.gov/beacon/2.0/
Retrieval: GET https://beacon.nist.gov/beacon/2.0/pulse/time/1787659200000
(= 2026-08-25T12:00:00Z in epoch milliseconds)
Field: pulse.outputValue, hex, LOWERCASED before use
Transport: curl. See §5 — python urllib cannot reach the host in this environment.
PROVENANCE COMPONENT
File: CLAUDE.md (repo root) in ~/dotfiles, at commit
4d2ae87a4e5350c4d3bb3aa50f9544b521d9c53d
Value: SHA-256 of the file contents at that commit, hex, lowercase
= 2d6e250a347d25698fb147f80e2dababbb930c4b3b3f9bb822478f360153120d
Note: contributes provenance, NOT unpredictability. Past commit,
named by full hash. Verify with:
git -C ~/dotfiles cat-file -p \
4d2ae87a4e5350c4d3bb3aa50f9544b521d9c53d:CLAUDE.md | shasum -a 256
SEED
seed_string = <provenance-sha256> || <beacon-outputValue-lowercased>
seed = SHA-256(seed_string), hex, lowercase
DERIVATION
seed -> FNV-1a (32-bit) -> Mulberry32 -> stat draws over the five axes of §1.
One peak, one dump, three scattered. No salt from any reference
implementation.
EXECUTION
Run ONCE. The executor does not retry on its own authority.
A re-run against the SAME recorded outputValue is legitimate (broken
implementation). A re-run against a LATER pulse is a new draw, governed
by §4 REGENERATION.
Record outputValue the moment it is fetched, before running anything.
UNAVAILABILITY
If no pulse is returned at or after the stated timestamp, retry the same
request for up to 24 hours. If still unavailable: STOP and report. Do not
substitute a different timestamp, beacon, or source.
TESTING
Dry runs use a fixed historical pulse only. Never the target pulse, never
a near-future pulse.
```
### 2a · ⚠ Two corrections to the v2 §6b block, marked rather than silent
The draft said to commit its block verbatim. **Two values in it do not resolve**, and a
rule that cannot be resolved on the day is not a rule (v2's own standard). Both changes
are recorded here for veto rather than absorbed quietly.
**(a) The provenance commit — changed on the steward's direction, 2026-08-22.**
`3b0730d59336113aa3a500a889a3e154be6a1de7` → `4d2ae87a4e5350c4d3bb3aa50f9544b521d9c53d`.
The draft's stated rationale — *"the constitution as it stood before the fool was
conceived"* — was false of the original: it is dated 2026-08-06, five days after trial
01, its subject line names the **PENDING-89 docket** (the question §11 forbids the fool
from being cited on), and **Constraint 6 is already present in it**. Verified: at
`4d2ae87` (2026-07-28) `Differently biased checkers` occurs **0 times**, and exactly one
`CLAUDE.md` exists at that commit.
**(b) The retrieval URL — corrected on evidence, and this is the executor's change.**
The block's `GET /pulse?timeGE=2026-08-25T12:00:00Z` **returns HTTP 302 with an empty
body**, redirecting to `https://csrc.nist.gov/projects/interoperable-randomness-beacons`
— an HTML page, not JSON. Measured 2026-08-22 against a *historical* timestamp.
`/beacon/2.0/pulse/time/<epoch-ms>` returns 200 and the expected JSON.
⚠ **Had this been filed verbatim, the 25th would have produced no pulse, the
UNAVAILABILITY clause would have run its 24-hour retry against a URL that cannot ever
return one, and the rule would have STOPPED — correctly, and for the wrong reason.**
Found only because §6b's TESTING clause directs a historical dry run.
**This is the same beacon, the same source and the same pulse — only the address form
changes.** The executor judges that correcting an unresolvable address for the named
source is not "substituting a different beacon or source". **If the jurist reads it
otherwise, this is the line to strike, and it must be struck before 2026-08-25.**
## 3 · Draw ranges — EXECUTOR-SPECIFIED, declared
v2 says *"one peak (near max), one dump (near floor), three scattered"* without numbers.
The executor supplies them. **Filed before the beacon value is known**, which is what
makes them non-steering: they set magnitudes, while the permutation — driven entirely by
the entropy component — decides which axis receives which.
| role | range (inclusive) |
|---|---|
| peak | 85–100 |
| dump | 0–15 |
| scattered ×3 | 25–75 |
**No floor is applied to the dump** — it can reach 0. v2 §3 forbids the rarity mechanic
precisely because it would soften the dump.
## 4 · Pre-registered criteria (§4 steps 3 and 4)
⚠ **Naming note:** §4 step 3 calls for an *"abandonment criterion"*; §10 defines
**RETIREMENT**. They are the same criterion under two names; §10 is the referent.
**REGENERATION** — permitted ONLY on a demonstrable implementation error, verified
against this filed rule. **Not because the output is disliked.** A re-run against the
same recorded `outputValue` is legitimate; a re-run against a later pulse is a new draw.
**RETIREMENT (abandonment)** — only on mechanical failure: does not fire; fires
constantly; or produces gradeable in-genre findings despite §9.
**NOT grounds for retirement:** being uncomfortable, being frequently wrong, being
annoying, being ignored. *Those are the specification. Lear ignores his Fool for four
acts and the Fool is not thereby broken.*
## 5 · Implementation and its verification
`derive_fool.py`, same directory. Deterministic, no cache, no reroll path, no salt. It
recomputes the provenance SHA from git on every run and **refuses to proceed** if it
disagrees with this rule.
`--selftest` runs 12 checks with **no network and no live pulse** — synthetic vectors
only — including two positive controls proving the PRNG moves both peak and dump across
all five axes over 200 draws. All 12 pass as of 2026-08-22.
**End-to-end dry run, 2024-01-01T12:00:00Z pulse** (a fixed historical pulse, per
TESTING): pipeline verified from fetch through bones. **That output is not the fool and
is recorded nowhere as bones.**
⚠ **Transport constraint, measured:** `curl` reaches the beacon; **python `urllib`
times out** in this environment. The fetch on the 25th must use curl.
⚠ **`outputValue` is served UPPERCASE** (128 hex chars). The rule's *"lowercased before
use"* is therefore **load-bearing, not cosmetic** — omitting it yields a different seed.
## 6 · What has NOT happened
- The target pulse has **not** been fetched. No near-future pulse has been fetched.
- No bones have been derived. No soul has been generated.
- `~/CLAUDE.md` has **not** been touched (PENDING-150, unbundled).
- Nothing has been implemented of §8, §8a or §9 — the status line is confirmed free but
not built.
+145
View File
@@ -0,0 +1,145 @@
#!/usr/bin/env python3
"""
Fool bones derivation — PENDING-149 §6 / §6b.
Deterministic. No salt. No reroll path. Nothing is cached: the caller supplies
the two seed components and the bones are recomputed from them every time.
This file implements the FILED RULE (FOOL-SEED-RULE.md). Where this code and the
filed rule disagree, THE FILED RULE GOVERNS and this file is the defect.
Usage:
derive_fool.py --beacon <outputValue-hex> # bones from a pulse value
derive_fool.py --selftest # determinism + range checks, no network
"""
import argparse, hashlib, subprocess, sys
# ---- the filed rule's constants. Do not edit without amending the filed rule. ----
PROVENANCE_COMMIT = "4d2ae87a4e5350c4d3bb3aa50f9544b521d9c53d"
PROVENANCE_PATH = "CLAUDE.md"
PROVENANCE_REPO = "/Users/davidglidden/dotfiles"
PROVENANCE_SHA256 = "2d6e250a347d25698fb147f80e2dababbb930c4b3b3f9bb822478f360153120d"
AXES = ["SUCCESSION", "ABSENCE", "AIM", "SCALE", "STAKE"] # §5, ratified order
PEAK_RANGE = (85, 100) # "near max" — executor-specified, filed pre-beacon
DUMP_RANGE = (0, 15) # "near floor" — executor-specified, filed pre-beacon
SCATTER_RANGE = (25, 75) # "scattered" — executor-specified, filed pre-beacon
def provenance_sha() -> str:
"""SHA-256 of CLAUDE.md at the named past commit. Recomputed, never trusted from the constant."""
blob = subprocess.run(
["git", "-C", PROVENANCE_REPO, "cat-file", "-p", f"{PROVENANCE_COMMIT}:{PROVENANCE_PATH}"],
capture_output=True, check=True).stdout
got = hashlib.sha256(blob).hexdigest()
if got != PROVENANCE_SHA256:
raise SystemExit(f"STOP: provenance blob does not match the filed rule.\n"
f" filed: {PROVENANCE_SHA256}\n got: {got}")
return got
def fnv1a_32(data: bytes) -> int:
h = 0x811C9DC5
for b in data:
h ^= b
h = (h * 0x01000193) & 0xFFFFFFFF
return h
def mulberry32(a: int):
"""Reference Mulberry32, 32-bit wrapped to match the JS original exactly:
a = a + 0x6D2B79F5 | 0
t = Math.imul(a ^ a >>> 15, 1 | a)
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t
return ((t ^ t >>> 14) >>> 0) / 4294967296
"""
M = 0xFFFFFFFF
state = a & M
def imul(x, y):
r = (x * y) & M
return r - 0x100000000 if r & 0x80000000 else r
def rnd():
nonlocal state
state = (state + 0x6D2B79F5) & M
a_ = state
t = imul(a_ ^ (a_ >> 15), 1 | a_) & M
t = ((t + imul(t ^ (t >> 7), 61 | t)) & M) ^ t
return ((t ^ (t >> 14)) & M) / 4294967296.0
return rnd
def draw_int(rnd, lo: int, hi: int) -> int:
return lo + int(rnd() * (hi - lo + 1))
def derive(beacon_output_value: str) -> dict:
beacon = beacon_output_value.strip().lower()
if not beacon or any(c not in "0123456789abcdef" for c in beacon):
raise SystemExit("STOP: beacon outputValue must be non-empty lowercase hex.")
prov = provenance_sha()
seed_string = prov + beacon
seed = hashlib.sha256(seed_string.encode()).hexdigest()
rnd = mulberry32(fnv1a_32(seed.encode()))
order = list(range(len(AXES))) # Fisher-Yates over the PRNG
for i in range(len(order) - 1, 0, -1):
j = int(rnd() * (i + 1))
order[i], order[j] = order[j], order[i]
stats = {}
stats[AXES[order[0]]] = draw_int(rnd, *PEAK_RANGE)
stats[AXES[order[1]]] = draw_int(rnd, *DUMP_RANGE)
for k in order[2:]:
stats[AXES[k]] = draw_int(rnd, *SCATTER_RANGE)
return {"provenance_sha256": prov, "beacon_outputValue": beacon,
"seed_string": seed_string, "seed": seed,
"peak": AXES[order[0]], "dump": AXES[order[1]],
"stats": {a: stats[a] for a in AXES}}
def selftest() -> int:
"""No network. Fixed synthetic vectors only — never a live or near-future pulse."""
ok = True
V1 = "0" * 128
V2 = "f" * 128
r1, r1b, r2 = derive(V1), derive(V1), derive(V2)
checks = [
("determinism: same input twice -> identical bones", r1 == r1b),
("sensitivity: different beacon -> different seed", r1["seed"] != r2["seed"]),
("provenance recomputed matches filed rule", r1["provenance_sha256"] == PROVENANCE_SHA256),
("seed_string is prov||beacon, no salt", r1["seed_string"] == PROVENANCE_SHA256 + V1),
("exactly five axes", sorted(r1["stats"]) == sorted(AXES)),
("peak in range", PEAK_RANGE[0] <= r1["stats"][r1["peak"]] <= PEAK_RANGE[1]),
("dump in range", DUMP_RANGE[0] <= r1["stats"][r1["dump"]] <= DUMP_RANGE[1]),
("peak is not dump", r1["peak"] != r1["dump"]),
("three scattered in range", all(SCATTER_RANGE[0] <= v <= SCATTER_RANGE[1]
for a, v in r1["stats"].items()
if a not in (r1["peak"], r1["dump"]))),
("no floor: dump can reach the bottom of its range",
min(derive(f"{i:0128x}")["stats"][derive(f"{i:0128x}")["dump"]] for i in range(200)) <= DUMP_RANGE[0] + 1),
]
# positive control: the PRNG must actually move both peak and dump around the axes
peaks = {derive(f"{i:0128x}")["peak"] for i in range(200)}
dumps = {derive(f"{i:0128x}")["dump"] for i in range(200)}
checks.append(("POSITIVE CONTROL: peak lands on all five axes over 200 draws", peaks == set(AXES)))
checks.append(("POSITIVE CONTROL: dump lands on all five axes over 200 draws", dumps == set(AXES)))
for name, passed in checks:
print(f" {'PASS' if passed else 'FAIL'} {name}")
ok &= passed
print(f"\n{'SELFTEST PASSED' if ok else 'SELFTEST FAILED — do not run against a live pulse'}")
return 0 if ok else 1
if __name__ == "__main__":
p = argparse.ArgumentParser()
p.add_argument("--beacon"); p.add_argument("--selftest", action="store_true")
a = p.parse_args()
if a.selftest:
sys.exit(selftest())
if not a.beacon:
p.error("--beacon <outputValue-hex> required (or --selftest)")
import json; print(json.dumps(derive(a.beacon), indent=2))