diff --git a/PENDING.md b/PENDING.md index 56b4001..bb5f617 100644 --- a/PENDING.md +++ b/PENDING.md @@ -3926,3 +3926,30 @@ Hooks already wired, for §13.6 planning: `SessionStart` ×2 (wake digest lives **§15b — action owed:** say explicitly in PENDING-89 that the buddy contributes zero by construction, and open the v1 Chamber archive read as its own `[PROPOSAL]`. **Awaiting:** (1) §6b provenance-commit disposition — **time-critical**; (2) ``; (3) unambiguous §5 ratification. + +### AMENDMENT 2 — 2026-08-22 — §4 steps 1–4 filed and pushed; the jurist's normalization condition discharged + +**Commit `acfbb9f`, pushed to both remotes (github + gitea), three days ahead of the beacon.** + +**⚖ JURIST RULING on §2a(b), 2026-08-22: NO VETO — the URL correction stands.** Reasons recorded verbatim in `seed/FOOL-SEED-RULE.md` §2a: the UNAVAILABILITY clause forbids substituting a different *timestamp, beacon, or source*, and none of the three changed — only the address at which the identical object is retrieved. The settling test: **could the correction have moved the outcome?** No; the pulse does not exist yet and does not depend on the URL. A substitution rule exists to prevent redraws, and a correction that cannot affect the draw is not one. + +**⚠ The uppercase finding is ruled the more serious of the two, and the assessment is adopted.** A silent seed divergence would have run clean, produced bones, and left nobody able to say afterwards which normalization had been applied. The URL failure at least announced itself. **Both were caught by the TESTING clause's historical dry run — the clause justified itself twice on its first use**, and that is now recorded in the rule rather than left to inference. + +### ✅ The pre-25th condition — DISCHARGED, and checking it found a defect in my own test + +Jurist: *"Confirm that lowercasing is applied at exactly one point in the code and is unit-tested against a known uppercase input. A normalization rule stated in prose and applied in two places is how the two diverge later."* + +**Single point confirmed:** `derive_fool.py:79`, the only `.lower()` / `.upper()` / `casefold` in the file. **Unit-tested**, four new checks including a **negative control** proving the test can fail. Selftest now **16/16**, no network, synthetic vectors plus one known uppercase vector. + +⚠ **The check found that the 2026-08-22 dry run had bypassed the step it was meant to verify.** The run lowercased outside the code and passed the value in already normalized, so the single normalization point was **never exercised on uppercase input in the only end-to-end run**. The test's subject was *the pipeline*; it silently excluded *the step under scrutiny*. Same wrong-subject shape as the `find`-vs-`glob` error and the three-store negative — **third instance this week, and the first found by another party naming the condition rather than by the executor re-checking.** + +**Re-run with the RAW uppercase value through the real path** (2024 pulse): seed `d8e5e74def52c7cd…`, identical to the pre-lowercased run. **Binding procedure added to the rule:** on the 25th the fetched `outputValue` is passed to `derive_fool.py` exactly as served — never normalized by any wrapper, shell step or hand edit before it reaches `derive()`. + +### Owed after the 25th, non-blocking, both accepted + +- **`[FIX]` 'abandonment' → 'retirement'** throughout the fool's doctrine — one word, one meaning; *abandonment* stays owned by §6 of the trial design. Deliberately deferred past the beacon so no edit touches the filed rule before it fires. +- **The mumble-hook answer** — clock-governed, event-checked, residual burst sensitivity declared rather than claimed away; the daemon alternative **costed, not dismissed**. Build decision, not governance. + +**Still NOT done:** target pulse not fetched, no bones, no soul, `~/CLAUDE.md` untouched (PENDING-150), nothing built of §8/§8a/§9. + +**Awaiting:** nothing blocking. The next act is the beacon at 2026-08-25T12:00:00Z. diff --git a/claude/governance/fool/seed/FOOL-SEED-RULE.md b/claude/governance/fool/seed/FOOL-SEED-RULE.md index 904d10b..9b5239e 100644 --- a/claude/governance/fool/seed/FOOL-SEED-RULE.md +++ b/claude/governance/fool/seed/FOOL-SEED-RULE.md @@ -89,7 +89,25 @@ TESTING a near-future pulse. ``` -### 2a · ⚠ Two corrections to the v2 §6b block, marked rather than silent +### 2a · ⚠ Two corrections to the v2 §6b block — RULED, no veto + +⚠ **JURIST RULING, 2026-08-22: no veto; the correction stands.** Recorded with its +reasons, since it will be read later. + +> The UNAVAILABILITY clause forbids substituting a different **timestamp, beacon, or +> source**. None of the three changed. Same beacon (NIST v2.0), same pulse +> (2026-08-25T12:00:00Z, epoch-ms 1787659200000), same field. What changed is the +> address at which the identical object is retrieved — the difference between a wrong +> phone number and a different person. +> +> The test that settles it: **could this correction have moved the outcome?** No. The +> pulse's value does not exist yet and does not depend on the URL used to fetch it. A +> substitution rule exists to prevent redraws; a correction that cannot affect the draw +> is not one. Read otherwise, the clause would forbid fixing a typo in a field name, and +> would have guaranteed a stop on the 25th for a reason unrelated to entropy — the +> opposite of what it protects. + +**The two corrections, 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 @@ -152,6 +170,16 @@ constantly; or produces gradeable in-genre findings despite §9. annoying, being ignored. *Those are the specification. Lear ignores his Fool for four acts and the Fool is not thereby broken.* +## 4a · ⚠ The uppercase finding is the more serious of the two — jurist's assessment, adopted + +> `outputValue` served uppercase against a rule specifying lowercase is a **silent seed +> divergence** — the pipeline would have run clean, produced bones, and nobody could have +> said afterwards which normalization had been applied. That is worse than the URL +> failure, which at least announced itself. + +**Both were caught by the TESTING clause's historical dry run. The clause justified +itself twice on its first use**, and that is recorded here rather than left to inference. + ## 5 · Implementation and its verification `derive_fool.py`, same directory. Deterministic, no cache, no reroll path, no salt. It @@ -172,6 +200,52 @@ 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. +### 5a · Normalization — the jurist's pre-25th condition, DISCHARGED + +**Confirmed: lowercasing is applied at exactly ONE point** — `derive_fool.py:79`, +`beacon_output_value.strip().lower()`, inside `derive()`. It is the only `.lower()`, +`.upper()` or `casefold` in the file. Every downstream use, including the recorded +`beacon_outputValue` field, reads from that single normalized value. + +**Unit-tested against a known uppercase input**, four checks, including one that proves +the test can fail: + +| check | | +|---|---| +| UPPERCASE input normalizes: bones identical to lowercase | PASS | +| UPPERCASE input matches an **independently computed** seed (not read back from `derive()`) | PASS | +| the recorded beacon field is stored lowercased | PASS | +| **NEGATIVE CONTROL:** un-normalized input *would* give a different seed | PASS | + +⚠ **Checking this found that the 2026-08-22 dry run had bypassed the step it was meant to +verify.** The run lowercased the value *outside* the code (`ov.lower()` into a temp file) +and passed it in already normalized, so the single normalization point was never +exercised on an uppercase input in the only end-to-end run. **The test's subject was the +pipeline; it silently excluded the step under scrutiny** — the same wrong-subject shape +the record has been tracking all week. + +**Re-run with the RAW uppercase value through the real path**, 2024-01-01 pulse: +seed `d8e5e74def52c7cd…`, identical to the pre-lowercased run. Normalization verified in +the path that will actually be used. + +⚠ **PROCEDURE FOR THE 25th, binding:** the fetched `outputValue` is passed to +`derive_fool.py` **exactly as served**. It is never lowercased, trimmed or otherwise +normalized by any wrapper, shell step or hand edit before it reaches `derive()`. One +normalization point, and it is in the code. + +## 5b · Owed after the 25th, non-blocking + +**`[FIX]` — 'abandonment' → 'retirement' throughout the fool's doctrine.** The jurist +owns the mismatch (§4 step 3 says *abandonment*, §10 defines *RETIREMENT*) and rules that +the fool's own doctrine should read **retirement**, one word with one meaning — +*abandonment* is the word §6 of the trial design owns, with a specific sense about the +jester form. Naming rather than silently harmonizing was correct; the harmonization is a +`[FIX]` **after** the beacon, so no edit touches this rule before it fires. + +**The mumble-hook answer** (v2 §8) — clock-governed, event-checked, residual burst +sensitivity declared rather than claimed away. The daemon alternative to be **costed, not +dismissed**. A build decision, not a governance one. Also after the 25th. + ## 6 · What has NOT happened - The target pulse has **not** been fetched. No near-future pulse has been fetched. diff --git a/claude/governance/fool/seed/derive_fool.py b/claude/governance/fool/seed/derive_fool.py index ed66c2c..3b204ac 100755 --- a/claude/governance/fool/seed/derive_fool.py +++ b/claude/governance/fool/seed/derive_fool.py @@ -122,6 +122,22 @@ def selftest() -> int: ("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), ] + # --- normalization: the jurist's pre-25th condition. outputValue is served UPPERCASE. --- + UP = "A1B2C3D4E5F6" * 10 + "ABCDEFAB" # 128 chars, uppercase hex + LOW = UP.lower() + r_up, r_low = derive(UP), derive(LOW) + # independent expectation, computed here rather than read back from derive() + import hashlib as _h + expected_seed = _h.sha256((PROVENANCE_SHA256 + LOW).encode()).hexdigest() + wrong_seed = _h.sha256((PROVENANCE_SHA256 + UP ).encode()).hexdigest() + checks += [ + ("UPPERCASE input normalizes: bones identical to lowercase", r_up == r_low), + ("UPPERCASE input matches independently computed seed", r_up["seed"] == expected_seed), + ("recorded beacon field is stored lowercased", r_up["beacon_outputValue"] == LOW), + ("NEGATIVE CONTROL: un-normalized input WOULD give a different seed " + "(so the check above can fail)", expected_seed != wrong_seed), + ] + # 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)}