diff --git a/.github/workflows/withdrawn-live-gate.yml b/.github/workflows/withdrawn-live-gate.yml new file mode 100644 index 0000000000..4eb549713e --- /dev/null +++ b/.github/workflows/withdrawn-live-gate.yml @@ -0,0 +1,42 @@ +name: Withdrawn Number Gate + +# Kills the bug class where a correction reaches the notes and never reaches the +# reader. `323 MHz` was established on 2026-08-05 to be a ring-oscillator toggle +# rate on a netlist containing none of the circuit being claimed. Three documents +# recorded the withdrawal. The arXiv draft and its .tex kept the number in the +# title, the abstract, the results table and a sentence asserting it came "from +# actual FPGA hardware runs" -- for ten days, through an intervening honesty pass +# over the same file that was looking at a different sentence. +# +# The registry is tools/withdrawn.txt: one regex per withdrawn number. Add the row +# when the number is withdrawn, not when the paper is finally fixed -- those two +# events were ten days apart, which is the whole reason this gate exists. +# +# Dated history under docs/reports/ is excluded on purpose: a record of what was +# believed on a date is not a live claim. So is the claims registry, which has to +# be able to state what it withdrew. + +on: + pull_request: + branches: [master] + push: + branches: [master] + workflow_dispatch: + +jobs: + withdrawn-live: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + # Negative control first. A gate nobody has seen fail is not a gate: + # this plants a withdrawn number in a temp tree and proves the scan fires + # on it and stays silent on a clean file. + - name: Negative control + run: python3 tools/check_withdrawn_live.py --self-check + + - name: No withdrawn number in a live document + run: python3 tools/check_withdrawn_live.py diff --git a/docs/NOW.md b/docs/NOW.md index 1d3be3f13f..dcc4358de2 100644 --- a/docs/NOW.md +++ b/docs/NOW.md @@ -1,3 +1,18 @@ +# NOW -- 323 MHz was a ring oscillator, and a gate now says so (2026-08-18) + +Last updated: 2026-08-18 + +## docs: retract 323 MHz and everything derived from it, then gate it (Closes #2179) + +- **The number timed a probe, not the design.** `fpga/vivado/gf16_matmul4x4_top.v:22` is the design's only sequential statement -- `always @(posedge osc)` on a 23-bit counter, where `osc = chain[19]` is the output of a 20-stage LUT1 ring oscillator the wrapper instantiates. `grep -c posedge` over `gf16_{mul,add,dot4,matmul4x4}.v` returns 0, 0, 0, 0: the arithmetic is combinational and has no synchronous path to time +- **The netlist that produced the bitstream contains no GF16 at all.** Its design module holds 55 logic cells -- LUT1 19, FDRE 23, CARRY4 6, BUFG 1, INV 4, OBUF 2 -- which is exactly the ring, the counter, its carry chain and the LEDs. Zero DSP48E1 against 64 claimed. The wrapper feeds the DUT literal constants, so the arithmetic is constant-folded out. When re-checking, count the *design* module: the same JSON lists `DSP48E1: 18 cells` for the Xilinx cell-library model, which is timing metadata +- **`create_clock` is absent from the XDC**, so "PASS at 100 MHz" and "0 timing violations" describe a default target on an auto-inferred domain holding only the counter +- **A tell needed none of that.** Three designs whose claimed sizes differ by 62x reported 330 / 322 / 323 MHz -- a 2.5 % spread. A real critical path cannot be invariant to a 62x change in size +- Corrected in the three documents that state it live -- the arXiv draft, its `.tex`, `NUMERIC_FORMATS_SSOT.md` -- across title, abstract, resource table, timing section, throughput table and the "from actual FPGA hardware runs" sentence. **No replacement frequency**, because none was measured. The four dated `docs/reports/WAVE_LOOP_*` are left alone: a record of what was believed on a date is not a live claim +- **Why a gate and not only an edit.** The withdrawal was in research notes on 2026-08-05 and 2026-08-08 and did not reach the papers for ten days, through an intervening honesty pass over the same file that was looking at a different sentence. `tools/check_withdrawn_live.py` now fails CI if a withdrawn number appears in a live document; the withdrawn list is data (`tools/withdrawn.txt`) so a row is added the moment a number is withdrawn, not when the paper is finally fixed. `--self-check` is a negative control that plants a hit and proves the scan fires +- **It earned its keep immediately**: it caught two places in the `.tex` that the first pass of this very change had missed +- Unrelated and worth stating: `docs/SILICON_TRAINING_METHODOLOGY.md` was audited for the same defect class and is **clean**. It distinguishes a loose from a tight constraint, uses `create_clock -period 50`, attributes the 21 -> 29 MHz change to a specific design edit, and keeps twelve ruled-out hypotheses. The papers were the problem; the engineering notes were not + # NOW -- BNF: the control that measures what ternary is worth (2026-08-09) Last updated: 2026-08-09 diff --git a/docs/NUMERIC_FORMATS_SSOT.md b/docs/NUMERIC_FORMATS_SSOT.md index 2aca14daf9..efccb000ca 100644 --- a/docs/NUMERIC_FORMATS_SSOT.md +++ b/docs/NUMERIC_FORMATS_SSOT.md @@ -222,7 +222,7 @@ The whitepaper's latest family table matches the canonical splits above. - **GF16 — VERIFIED + FROZEN SILICON.** Production Rust (`trios-trainer-igla`) + C codegen ([`../gen/c/numeric/gf16.c`](../gen/c/numeric/gf16.c)) + RTL ([`tt-trinity-gamma/src/gf16_v2_mul.v`](../../tt-trinity-gamma/src/gf16_v2_mul.v) - and `gf16_v2_add.v`); 35/35 FPGA testbench @ 323 MHz Artix-7; benchmarked + and `gf16_v2_add.v`); 35/35 FPGA testbench on Artix-7 (no operating frequency claimed — the 323 MHz figure is withdrawn, see docs/nona-03-manifest/RESEARCH_CLAIMS.md); benchmarked (97.67% MNIST MLP, 0.00% accuracy gap vs f32); fabricated in TTSKY26b TT4913 Gamma. - **GF4/8/12/20/24/32 — Verified.** Spec + Verilog RTL in `tt-trinity-gamma/src/`. - **GF64 — Verified.** Spec ([`gf64.t27`](../specs/numeric/gf64.t27), #916) + diff --git a/docs/arxiv-submission/trinity-gf16.tex b/docs/arxiv-submission/trinity-gf16.tex index 18307a2fd2..87079581b0 100644 --- a/docs/arxiv-submission/trinity-gf16.tex +++ b/docs/arxiv-submission/trinity-gf16.tex @@ -101,14 +101,14 @@ \begin{document} \section{Trinity GF16: A phi-Anchored 16-bit Float with FPGA -Implementation at 323 -MHz}\label{trinity-gf16-a-phi-anchored-16-bit-float-with-fpga-implementation-at-323-mhz} +Synthesised with an Open-Source FPGA +Toolchain}\label{trinity-gf16-a-phi-anchored-16-bit-float-synthesised-with-an-open-source-fpga-toolchain} \subsection{Abstract}\label{abstract} We introduce Golden Float 16 (GF16), a 16-bit floating-point format with a phi-anchored exponent bias of 31. GF16 uses a 1/6/9 bit layout -(sign/exponent/mantissa) and achieves 323 MHz combinational throughput +(sign/exponent/mantissa) and is synthesised (no operating frequency is claimed; see Timing) on a Xilinx Artix-7 XC7A100T FPGA using the open-source openXC7 toolchain (Yosys + nextpnr). We present a complete dot-product (N=4) and 4x4 matrix multiplication accelerator verified in FPGA synthesis and @@ -316,7 +316,7 @@ \subsubsection{4.2 Resource Utilization}\label{resource-utilization} \endlastfoot GF16 mul & \textasciitilde650 & 1 & 330 MHz & 13/13 \\ GF16 dot4 & 2,605 & 4 & 322 MHz & 6/6 \\ -GF16 matmul 4x4 & 40,350 & 64 & 323 MHz & 35/35 \\ +GF16 matmul 4x4 & 40,350 & 64 & --- & 35/35 \\ \end{longtable} } @@ -325,7 +325,7 @@ \subsubsection{4.3 Timing}\label{timing} All designs pass timing at 100 MHz with positive slack: \begin{verbatim} -Max frequency for clock 'chain[19]': 323.31 MHz (PASS at 100.00 MHz) +[withdrawn: this clock is a ring-oscillator probe, not a GF16 path] \end{verbatim} \subsubsection{4.4 Latch Elimination}\label{latch-elimination} @@ -354,10 +354,8 @@ \subsubsection{4.6 Throughput}\label{throughput} \endhead \bottomrule\noalign{} \endlastfoot -Dot4 throughput & 322M dot4/sec (combinational, 1-cycle) \\ -Matmul4x4 throughput & 322M matmuls/sec (fully parallel) \\ -GF16 ops/sec (matmul) & 41.2 GOPS @ 323 MHz \\ -GF16 ops/sec @ 100 MHz & 12.8 GOPS \\ +GF16 ops per matmul4x4 & 128 (64 mul + 64 add) \\ +Throughput at a clock of $f$ & $128f$ ops/sec, fully parallel, 1 cycle \\ \end{longtable} } @@ -433,9 +431,13 @@ \subsection{7. Conclusion}\label{conclusion} exponent/mantissa split offers 65x wider dynamic range than float16 with better precision than bfloat16. -All verified numbers (323 MHz, 40,350 LUTs, 64 DSP48E1, 35/35 tests, 0 -latches, 0 timing violations) are from actual FPGA hardware runs -(Artix-7 XC7A100T), not ASIC silicon nor simulation estimates. +What this work establishes, and no more: the design synthesises for the +Artix-7 XC7A100T through an entirely open-source flow, its testbench passes +35/35, and Yosys infers no latches. The resource figures are synthesis +statistics on the DUT. \textbf{No operating frequency, and therefore no +throughput, is claimed} --- the figure previously reported was a +ring-oscillator probe and is withdrawn (see Timing). No ASIC silicon has been +measured. \subsection{References}\label{references} diff --git a/docs/arxiv-trinity-gf16-draft.md b/docs/arxiv-trinity-gf16-draft.md index 5bc9f49e00..ecd7452544 100644 --- a/docs/arxiv-trinity-gf16-draft.md +++ b/docs/arxiv-trinity-gf16-draft.md @@ -1,8 +1,8 @@ -# Trinity GF16: A phi-Anchored 16-bit Float with FPGA Implementation at 323 MHz +# Trinity GF16: A phi-Anchored 16-bit Float, Synthesised with an Open-Source FPGA Toolchain ## Abstract -We introduce Golden Float 16 (GF16), a 16-bit floating-point format with a phi-anchored exponent bias of 31. GF16 uses a 1/6/9 bit layout (sign/exponent/mantissa) and achieves 323 MHz combinational throughput on a Xilinx Artix-7 XC7A100T FPGA using the open-source openXC7 toolchain (Yosys + nextpnr). We present a complete dot-product (N=4) and 4x4 matrix multiplication accelerator verified in FPGA synthesis and RTL simulation, with 35/35 tests passing and 0 timing violations at 100 MHz. The design has been submitted for ASIC fabrication on Sky130 via the TinyTapeout TTSKY26b TT4913 Gamma shuttle (submission closed May 2026); silicon has not yet been returned (expected late 2026), so no on-chip measurement is claimed. +We introduce Golden Float 16 (GF16), a 16-bit floating-point format with a phi-anchored exponent bias of 31. GF16 uses a 1/6/9 bit layout (sign/exponent/mantissa) and is synthesised for a Xilinx Artix-7 XC7A100T FPGA entirely with the open-source openXC7 toolchain (Yosys + nextpnr), with no vendor licence. No operating frequency is claimed: see §4.3. We present a complete dot-product (N=4) and 4x4 matrix multiplication accelerator verified in FPGA synthesis and RTL simulation, with 35/35 tests passing. The design has been submitted for ASIC fabrication on Sky130 via the TinyTapeout TTSKY26b TT4913 Gamma shuttle (submission closed May 2026); silicon has not yet been returned (expected late 2026), so no on-chip measurement is claimed. ## 1. Introduction @@ -120,19 +120,37 @@ C[i][j] = dot4(A[i][0:3], B[0:3][j]) ### 4.2 Resource Utilization -| Design | LUTs | DSP48E1 | Max Freq | Tests | -|--------|------|---------|----------|-------| -| GF16 mul | ~650 | 1 | 330 MHz | 13/13 | -| GF16 dot4 | 2,605 | 4 | 322 MHz | 6/6 | -| GF16 matmul 4x4 | 40,350 | 64 | 323 MHz | 35/35 | +| Design | LUTs | DSP48E1 | Tests | +|--------|------|---------|-------| +| GF16 mul | ~650 | 1 | 13/13 | +| GF16 dot4 | 2,605 | 4 | 6/6 | +| GF16 matmul 4x4 | 40,350 | 64 | 35/35 | + +Resource figures are `yosys stat` on the DUT. They are **not** from the placed +wrapper that produced the bitstream: that wrapper feeds the DUT literal constants, +so the arithmetic is constant-folded out and its synthesised design module holds +55 cells and no DSP48E1. The two must not be presented as one measurement. +A `Max Freq` column stood here and is withdrawn; see §4.3. ### 4.3 Timing -All designs pass timing at 100 MHz with positive slack: +**No operating frequency is claimed for GF16, and the figure previously reported +here is withdrawn.** -``` -Max frequency for clock 'chain[19]': 323.31 MHz (PASS at 100.00 MHz) -``` +The reported clock, `chain[19]`, is the output of a 20-stage LUT1 ring oscillator +that the test wrapper instantiates; it clocks a 23-bit counter and nothing else. +The GF16 arithmetic is purely combinational -- `grep -c posedge` over +`gf16_{mul,add,dot4,matmul4x4}.v` returns 0, 0, 0, 0 -- so it has no synchronous +path to time, and the constraint file contains no `create_clock`, which makes +"PASS at 100 MHz" a default target on an auto-inferred domain holding only the +counter. The tell was visible without any of that: three designs whose sizes +differ by 62x reported 330 / 322 / 323 MHz, a 2.5 % spread, and a real critical +path cannot be invariant to a 62x change in size. + +Establishing a frequency for this design requires registering the datapath and +constraining it. That work has not been done, so no number is offered in its +place. Full analysis: `docs/nona-03-manifest/RESEARCH_CLAIMS.md`, Retraction +2026-08-18. ### 4.4 Latch Elimination @@ -151,10 +169,13 @@ All designs verified on FPGA via XVC programming: | Metric | Value | |--------|-------| -| Dot4 throughput | 322M dot4/sec (combinational, 1-cycle) | -| Matmul4x4 throughput | 322M matmuls/sec (fully parallel) | -| GF16 ops/sec (matmul) | 41.2 GOPS @ 323 MHz | -| GF16 ops/sec @ 100 MHz | 12.8 GOPS | +| GF16 ops per matmul4x4 | 128 (64 multiplies + 64 adds) | +| Throughput at a clock of *f* | 128 x *f* ops/sec, fully parallel, 1 cycle | + +Absolute throughput figures previously stood here -- 322M matmuls/sec and +41.2 GOPS -- and are withdrawn with the frequency they were computed from +(§4.3). The op count per matmul is a property of the design and stands; the +rate is not known until a frequency is measured. ## 5. ASIC Path (TinyTapeout TTSKY26b TT4913 Gamma) @@ -194,7 +215,7 @@ A complete Python reference (encode/decode/mul/add/dot4) is provided in `conform We have demonstrated a complete implementation of the Trinity GF16 floating-point format, from specification through FPGA verification to ASIC submission. The phi-anchored bias=31 provides a natural centering for ML and scientific computation values, while the 6/9 exponent/mantissa split offers 65x wider dynamic range than float16 with better precision than bfloat16. -All verified numbers (323 MHz, 40,350 LUTs, 64 DSP48E1, 35/35 tests, 0 latches, 0 timing violations) are from actual FPGA hardware runs (Artix-7 XC7A100T), not ASIC silicon nor simulation estimates. +What this work establishes, and no more: the design synthesises for the Artix-7 XC7A100T through an entirely open-source flow, its testbench passes 35/35, and Yosys infers no latches. The resource figures are synthesis statistics on the DUT. **No operating frequency, and therefore no throughput, is claimed** -- the figure previously reported was a ring-oscillator probe and is withdrawn (§4.3). No ASIC silicon has been measured. ## References diff --git a/docs/nona-03-manifest/RESEARCH_CLAIMS.md b/docs/nona-03-manifest/RESEARCH_CLAIMS.md index 73de55e659..60100beee1 100644 --- a/docs/nona-03-manifest/RESEARCH_CLAIMS.md +++ b/docs/nona-03-manifest/RESEARCH_CLAIMS.md @@ -140,6 +140,45 @@ claim (`FALSIFIED_AS_EXACT`; no Standard Model derivation; ~65.7 deg vs PMNS spec. (Note: `specs/physics/formula_discovery.t27` uses a distinct expression 9*phi^-2, which is a separate empirical fit, not this retracted claim.) +**Retraction (2026-08-18).** The GF16 figure **323 MHz on Artix-7** is withdrawn +(`FALSIFIED_AS_EXACT`), together with everything derived from it: **41.2 GOPS**, +**322M matmuls/sec**, the **40,350 LUT / 64 DSP48E1** row it shares a table with, +and the sentence asserting all of them came "from actual FPGA hardware runs". + +The number is the toggle rate of a **20-stage LUT1 ring oscillator** the test +wrapper instantiates, not a path through any GF16 arithmetic. Four independent +checks, each sufficient on its own: + +1. `fpga/vivado/gf16_matmul4x4_top.v:22` is the design's **only** sequential + statement -- `always @(posedge osc)` on a 23-bit counter, where + `osc = chain[19]` is the ring output. +2. `grep -c posedge` over `gf16_{mul,add,dot4,matmul4x4}.v` returns **0, 0, 0, 0**. + The arithmetic contains no clocked logic, so it has no synchronous path to time. +3. `gf16_matmul4x4_top.xdc` contains **no `create_clock`**. "PASS at 100 MHz" and + "0 timing violations" describe a default target on an auto-inferred domain + holding only the counter. +4. The synthesised design module in `target/gf16-build/gf16_matmul4x4_top.json` + holds **55 logic cells** -- LUT1 19, FDRE 23, CARRY4 6, BUFG 1, INV 4, OBUF 2 -- + which is exactly the ring, the counter, its carry chain and the LEDs. + **GF16 contributes zero cells and zero DSP48E1**, because the wrapper feeds the + DUT literal constants and the arithmetic is constant-folded away. + (When re-checking this: count the *design* module. The same JSON contains + `DSP48E1: 18 cells` for the Xilinx *cell-library model*, which is timing + metadata, not instances.) + +A tell was visible without any of the above: three designs whose claimed sizes +differ by **62x** report 330 / 322 / 323 MHz, a 2.5 % spread. A real critical path +cannot be invariant to a 62x change in design size. + +It must not be cited as evidence in any t27 first-party document or spec. No +replacement frequency is offered, because none was measured; what the build does +establish is that the design synthesises and that its testbench passes. + +The withdrawal was recorded in research notes on 2026-08-05 and 2026-08-08 and +**never reached the papers** -- they still carried it ten days later, through an +intervening honesty pass over the same file. Hence the gate: `tools/check_withdrawn_live.py` +fails CI if a withdrawn number reappears in a live document. + --- ## 6. Maintenance rules diff --git a/tools/check_withdrawn_live.py b/tools/check_withdrawn_live.py new file mode 100755 index 0000000000..232309a0ec --- /dev/null +++ b/tools/check_withdrawn_live.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python3 +"""A withdrawn number must not survive in a live document. + +Written because one did, for ten days, in the two files that carry claims to +readers. `323 MHz` was established on 2026-08-05 to be the toggle rate of a +20-stage ring oscillator clocking a 23-bit counter, on a netlist whose design +module holds 55 cells and none of the GF16 arithmetic being claimed. Three +separate documents recorded the withdrawal. The arXiv draft and its .tex kept +the number in the title, the abstract, the results table and a sentence +asserting it came "from actual FPGA hardware runs" -- through an intervening +honesty pass over the same file, which was looking at a different sentence. + +The lesson generalises past this one number: a correction recorded in notes does +not propagate to documents, and nothing was checking. So this checks. + + live document any tracked .md / .tex / .rst, minus the exclusions below + exclusions the claims registry itself (it must state what it withdrew), + dated history under docs/reports/ (a record of what was + believed then is not a live claim), and this gate's own data + baseline tools/withdrawn_live_baseline.txt -- occurrences that are + text ABOUT the withdrawal, keyed by path + pattern + a hash + of the LINE. Keying on the line and not just the file matters + for append-only documents such as docs/NOW.md: baselining the + file wholesale would let a future genuine claim through. + +Usage: + tools/check_withdrawn_live.py gate; exits non-zero on any new hit + tools/check_withdrawn_live.py --self-check negative control: plant a hit in a + temp tree and prove the gate fires + tools/check_withdrawn_live.py --update-baseline + +Exits non-zero on any failure. +""" +import hashlib +import pathlib +import re +import subprocess +import sys + +ROOT = pathlib.Path(__file__).resolve().parent.parent +REGISTRY = ROOT / "tools/withdrawn.txt" +BASELINE = ROOT / "tools/withdrawn_live_baseline.txt" +SUFFIXES = {".md", ".tex", ".rst"} +EXCLUDE_PREFIXES = ( + "docs/nona-03-manifest/RESEARCH_CLAIMS.md", # must state what it withdrew + "docs/reports/", # dated history, not live claims + "tools/withdrawn.txt", + "tools/withdrawn_live_baseline.txt", + "tools/check_withdrawn_live.py", +) + + +def rules(registry=None): + """(compiled regex, reason, pointer) for every row of the registry.""" + registry = registry or REGISTRY + out = [] + for line in registry.read_text(encoding="utf-8").splitlines(): + line = line.strip() + if not line or line.startswith("#"): + continue + parts = [p.strip() for p in line.split("|")] + if len(parts) != 3: + sys.exit(f"malformed row in {registry.name}: {line!r}") + out.append((re.compile(parts[0], re.I), parts[1], parts[2])) + if not out: + sys.exit(f"{registry.name} lists no withdrawn numbers -- the gate would pass vacuously") + return out + + +def live_documents(root=ROOT): + """Tracked documents that carry claims to a reader.""" + try: + listed = subprocess.run(["git", "ls-files"], cwd=root, capture_output=True, + text=True, check=True).stdout.split("\n") + except Exception: + listed = [str(p.relative_to(root)) for p in root.rglob("*") if p.is_file()] + for rel in listed: + if not rel or pathlib.PurePosixPath(rel).suffix not in SUFFIXES: + continue + if any(rel.startswith(x) for x in EXCLUDE_PREFIXES): + continue + yield rel + + +def scan(root=ROOT, registry=None): + hits = [] + for pat, reason, where in rules(registry): + for rel in live_documents(root): + try: + text = (root / rel).read_text(encoding="utf-8", errors="replace") + except OSError: + continue + for i, line in enumerate(text.splitlines(), 1): + if pat.search(line): + key = hashlib.sha1(" ".join(line.split()).encode()).hexdigest()[:12] + hits.append((rel, i, pat.pattern, reason, where, + line.strip()[:110], key)) + return hits + + +def baseline(): + if not BASELINE.exists(): + return set() + return {l.strip() for l in BASELINE.read_text(encoding="utf-8").splitlines() + if l.strip() and not l.startswith("#")} + + +def self_check(): + """Negative control. A gate nobody has seen fail is not a gate.""" + import tempfile, shutil + with tempfile.TemporaryDirectory() as td: + t = pathlib.Path(td) + (t / "tools").mkdir() + (t / "docs").mkdir() + shutil.copy(REGISTRY, t / "tools/withdrawn.txt") + (t / "docs/planted.md").write_text("The design reaches 323 MHz on Artix-7.\n") + (t / "docs/clean.md").write_text("The design synthesises and its testbench passes.\n") + hits = scan(t, t / "tools/withdrawn.txt") + planted = [h for h in hits if h[0] == "docs/planted.md"] + clean = [h for h in hits if h[0] == "docs/clean.md"] + ok = len(planted) == 1 and not clean + print(f" self-check: planted hit found = {len(planted) == 1}, " + f"clean file silent = {not clean}") + return 0 if ok else 1 + + +def main(): + if "--self-check" in sys.argv: + return self_check() + hits = scan() + if "--update-baseline" in sys.argv: + BASELINE.write_text( + "# Occurrences that are text ABOUT a withdrawal, not a live claim.\n" + "# Keyed path | pattern | sha1(line)[:12] -- editing the line re-opens the gate,\n" + "# which is what we want for append-only documents like docs/NOW.md.\n" + + "".join(f"{rel} | {pat} | {key}\n" + for rel, pat, key in sorted({(h[0], h[2], h[6]) for h in hits})), + encoding="utf-8") + print(f" baseline written: {len({(h[0], h[2], h[6]) for h in hits})} entries") + return 0 + known = baseline() + new = [h for h in hits if f"{h[0]} | {h[2]} | {h[6]}" not in known] + if not new: + print(f"OK: no withdrawn number is stated in a live document " + f"({len(list(live_documents()))} documents scanned)") + return 0 + print(f"FAIL: {len(new)} withdrawn number(s) stated in a live document\n") + for rel, line, pat, reason, where, text, _key in new: + print(f" {rel}:{line}") + print(f" matches /{pat}/ -- {reason}") + print(f" see {where}") + print(f" > {text}") + print("\n Fix the document. If the line is text ABOUT the withdrawal, add it to") + print(" tools/withdrawn_live_baseline.txt with --update-baseline.") + return 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/withdrawn.txt b/tools/withdrawn.txt new file mode 100644 index 0000000000..04064be336 --- /dev/null +++ b/tools/withdrawn.txt @@ -0,0 +1,11 @@ +# Withdrawn numbers. A live document that states one of these fails CI. +# +# Format: regex | short reason | where the full analysis lives +# Lines starting with # are comments. The regex is matched case-insensitively. +# +# Add a row the moment a number is withdrawn -- not after the paper is fixed. +# The whole point is that the two events were ten days apart last time. + +323(\.[0-9]+)?\s*MHz | ring-oscillator toggle rate, not a GF16 path | RESEARCH_CLAIMS.md Retraction 2026-08-18 +41\.2\s*GOPS | derived from the withdrawn 323 MHz | RESEARCH_CLAIMS.md Retraction 2026-08-18 +322M\s*matmuls/sec | derived from the withdrawn 323 MHz | RESEARCH_CLAIMS.md Retraction 2026-08-18 diff --git a/tools/withdrawn_live_baseline.txt b/tools/withdrawn_live_baseline.txt new file mode 100644 index 0000000000..a5a4694561 --- /dev/null +++ b/tools/withdrawn_live_baseline.txt @@ -0,0 +1,10 @@ +# Occurrences that are text ABOUT a withdrawal, not a live claim. +# Keyed path | pattern | sha1(line)[:12] -- editing the line re-opens the gate, +# which is what we want for append-only documents like docs/NOW.md. +docs/NOW.md | 323(\.[0-9]+)?\s*MHz | 18f57dc20b45 +docs/NOW.md | 323(\.[0-9]+)?\s*MHz | 700c6d6aca11 +docs/NOW.md | 323(\.[0-9]+)?\s*MHz | ce84017e739b +docs/NUMERIC_FORMATS_SSOT.md | 323(\.[0-9]+)?\s*MHz | bbc2adf030a9 +docs/arxiv-trinity-gf16-draft.md | 322M\s*matmuls/sec | 1026a341042f +docs/arxiv-trinity-gf16-draft.md | 323(\.[0-9]+)?\s*MHz | 6daf60f68b57 +docs/arxiv-trinity-gf16-draft.md | 41\.2\s*GOPS | 546e04c73288