/* #p8v2_e64_viewer_goal_20260805 @viz_standard @root @hutter_leaderboard_goal_20260701 GOAL: an e64 settling instrument -- step f, drive the rules, see the axes BUILT 2026-08-05: /hutter/pprog/p8v2-e64/, built by docs/pprog/build-p8v2-e64 from docs/pprog/p8v2-e64.tpl.html. Nine of eleven variants reproduced exactly. Corrections from #hutter_e64_viewer_prereq_20260805 are folded in below and marked CORRECTED. A separate published page, /hutter/pprog/p8v2-e64/, that runs f over the 64-byte enwik9 prefix one time step at a time, under live controls for the settling rules, so that (1) settling can be explored rather than only reported, and (2) the axes that move nothing in the archive are visible where they do move. Specified 2026-08-05 with MJC in interview. This block is the spec; there is no companion section in tests/pprog/p8v2-viz-spec.md, because rendering and publication are ../hutter's per #hutter_publication_handoff and that file is cmpr-src's. ## Vocabulary, which is not negotiable on this page A TIME STEP IN f. Not a "sweep". One time step is ONE APPLICATION OF THE TOTAL (VISIBLE) PATTERN -- f is the update function, t_{n+1} = p x t_n, so an iteration is P applied once to T. That it visits every position at once is not an extra fact, it is what the equation says. A PATTERN APPLICATION. Not a "message". One time step is built from pattern applications: one LPP's contribution to one position, computed from one neighbouring source position -- three per unclamped position, forward k=1, backward k=1 and the k=2 token. Each entry is min(w_p, w_s), which IS the inner min of the max-min forward pass f_p(t)_j = max_i min(t_i, p_ij), with the source restricted to its argmax event by the admitted optimization; under A1 the forward and backward applications are then combined with lsa_max, the outer max. The machinery is the forward pass written out longhand, and "message" was belief-propagation vocabulary for something the UM already names. The implementation's own cost counter has always agreed: settle_apps counts "one for a forward message, one for a backward message, one for a token message that fires", i.e. one per pattern application. Renamed on the page 2026-08-06 at MJC's direction; #pp_settle's prose is cmpr-src's and is left alone. Two departures the UM vocabulary makes visible, and both matter elsewhere in this design: - A k=2 token application ASSIGNS rather than joining the max. #f-p8 argues that is correct because k=2 is a subset of k=1 and they are fully correlated. It is also exactly why axis D can remove nothing (Q4): the settled argmax at a candidate is then the causal composite prediction, which is wrong there by construction. - A2 and A3 leave max-min altogether, replacing the outer max with LSA addition. Under "message" that reads as bookkeeping; in UM terms it is visibly a different f. VISIBLE means the part of P inside the window of indeterminacy. The memory chain's left-shift patterns are outside it -- they are just output -- and are not drawn, not stepped and not counted. As P grows we will likely want to look at other parts of it; the page should not make that hard, and should not try to design it now. The implementation's word is "sweep" and it survives in cmpr-src's TSV columns (mean_sweeps, settle_apps) and in the .pos `conv` field description. Those are the GENERATING side's names and are theirs. Everything WE publish says time step in f. #um_optimizations already requires reporting to stay in UM terms; this is that rule applied to vocabulary. ## Why e64, specifically - The whole sample is inside ONE window, so there is no boundary to explain and no tiling, and every position is on screen at every step. CORRECTED by #hutter_e64_viewer_prereq_20260805: W IS 64 AT e64, NOT 128. settle_window takes W = we - ws, #pp_dump settles in non-overlapping windows of 128 over the first min(M, cap) positions, so at e64 there is one window ws=0 we=64. 128 is the architecture; 64 is what the dumps record, f runs 64 time steps, and CONV = 64 IS THE DID-NOT-CONVERGE SENTINEL -- v004 and v005 report exactly that. - IT IS WHERE AXES A, B AND C ARE VISIBLE AT ALL. In the archive they move nothing: under D1 the trace is the causal composite pass and the settled state is never consulted, so v001..v005 have identical class (b) and class (c) at every sample size. They are not identical runs, and at e64 the difference is stark -- v001 settles to the true byte at positions 8-13 with w1 = 1, v002 settles to 97/119/105 with w1 around 250 and a live second candidate, conv 27 against 14. This page is the only place that difference can be seen. - D and F change nothing at e64 and E DOES. CORRECTED by the prereq block: a = 0.2188 for all eleven is true of the class (c) RATE, not of settling, which is what the page draws. v008/v009 still settle 64 of 64 but converge at 59 instead of 27 and carry a DIFFERENT LEARNED k=1 TABLE -- replay relearns k=1 against the k=2 mask -- so E is the one axis at this sample that changes what was LEARNED rather than what settling does with it. D and F genuinely coincide with the baseline. Per MJC the convention is now that EVERY AXIS GETS A CONTROL: one whose alternatives coincide at this sample is still a control, and the page says they coincide HERE rather than omitting them, which is also what makes the same page reusable at e1k. ## Register AN INSTRUMENT, densely labelled. Built for whoever picks the axes, not for a cold reader of the field: everything on screen at once, minimal narration, optimised for finding the position where two rules disagree. It still obeys #viz_standard -- every term this experiment invented is defined ON the page -- but it does not narrate the UM, which is assumed. Layout, as agreed: [A1|A2|A3] [B1|B2] [C1|C2] [min|sub] steps:128 preset: v001 v002 v003 ... v011 [RUN + conformance state] step in f [|<][<]( 14/128 )[>][>|] [run] pos 0 1 2 3 4 5 6 7 8 9 ... byte < m e d i a w i k i _ x argm < m e d i a w i k i _ x w1 255 1 1 1 1 1 1 1 1 1 1 diff ^ ^ ^ ^ pos 8 'k': fwd i->n w2 | bwd i->k w1 | tok (w,i)->k w1 A1: k=2 assigns -> 'k' w1=1 conv 27 Stepping f is the only TIME control. Selecting a position is for DETAIL -- its LSA vector, the candidates and weights, which message supplied each, which pattern fired, what the axis-A rule then did with them. All 64 positions stay on screen at every step, so the window is watched relaxing and one cell is interrogated without losing the whole. ## The engine, and how it is kept honest The settling runs LIVE, in JS, so that rules can be driven. That buys exploration and costs fidelity, and the cost is paid as follows. ON LOAD, THE VIEWER REPLAYS ALL ELEVEN RECORDED e64 RUNS and asserts that its own f reproduces, for every position, the recorded `settled`, `s1`, `w1`, `s2`, `w2` and `conv`. The result is displayed on the page, per variant, not hidden in a console. - PASSING pins the JS engine to the C at the fixed point and at the convergence step, which is most of what could silently drift. - IT DOES NOT PIN THE INTERMEDIATE STEPS. The dumps carry only the endpoint and `conv`, so every step the viewer draws between 0 and convergence is the viewer's own. The page says this in plain words, once, near the step control. Do not let it be discovered later. - IT IS REPORTED PER VARIANT, not as one verdict. CORRECTED by the prereq block: LSA is stochastic and lsa_add / lsa_sum256 draw from ONE global xorshift32 stream that is shared with learning, so by the time settling starts the C stream sits wherever learning left it and the draw count is data-dependent. A1 uses only lsa_max and assignment and is deterministic, so the nine A1 variants are checkable and v002 (A2) and v003 (A3) ARE OUT OF REACH BY CONSTRUCTION. Marking the whole engine UNVERIFIED because of those two would be wrong. On a genuine failure the page still renders and names the failing variants; a viewer that hides a failed check is worse than no viewer. - MEASURED: nine of eleven conform exactly, endpoint and conv, including the hard cases -- v004's stored backward LPP with conv = 64 never-converged, v005's C2 gate, and v008/v009's relearned k=1. The two that differ are exactly v002 and v003. ## Controls Live, all of them, and all of them settling rules: AXIS A, the update rule -- A1 assignment / A2 addition against decay at 2f / A3 addition with per-position renormalisation to constant sum. This IS f; the page cannot omit it. AXES B and C, what the messages are -- B1 backward k=1 reconstructed from the forward table vs B2 read from a stored full LPP; C1 min(w_a, w_b) vs C2 sum with threshold at the AND gate. These change what a step delivers without changing how it is combined. THE APPLICATION RULE (Q5), called the message rule where the blocks still say so -- min(w_p, w_s) vs the original w_p - (255 - w_s). Q5 calls this the decision made on MJC's behalf that is most worth overruling. The old rule annihilates every non-absolute message: one hop off a clamped position takes it to 0 and the window collapses to byte 0. WATCHING THAT COLLAPSE HAPPEN IS THE POINT -- it is an argument no table makes, and it is the single most valuable thing this page can show. The rule is implemented in NO variant, so there is no dump of it and every frame under it including the endpoint is unverified; the page therefore shows a LIVE agreement readout computed from what is on screen rather than quoting a number. Driven on the baseline it gives 10 of 64 against min's 64 of 64, which is where Q5's figures come from and is now reproducible rather than unsourced. THE NUMBER OF TIME STEPS f RUNS FOR (Q7) -- W, which is 64 at e64. An application travels one position per step, so stopping early leaves the far end of the window on the k=0 background. Making this a control makes the stopping rule visible, and it is also the entire settling cost and the reason D2/D3 are quadratic in M. The eleven built variants are PRESETS that snap the controls; the controls are otherwise free. NOT controls: D, E and F. They change nothing at e64 and the page says so once instead. ## Fencing: what is a measurement and what is not A combination that WAS RUN (the eleven) is marked RUN and carries its recorded endpoint alongside the viewer's. ANY OTHER COMBINATION is drawn in the warning / dashed treatment #viz_standard already uses for provisional material, and labelled, so that a screenshot cannot be mistaken for a result. The whole page carries the standing line from #hutter_publication_handoff: no picks, no chart point, and nothing here is a compression number. ## The settling shell, frozen -- the viewer implements exactly this From tests/pprog/p8v2-words.md, section "Axis A", which all three alternatives share: - a window of W positions, W = 128; - the positions the trace RECORDS are clamped absolute: 255 on their own event, 0 elsewhere. They never change; - every other position is initialised to the k=0 background, the 256-value byte-frequency model learned by omega; - each time step computes, FOR EVERY UNCLAMPED POSITION, a forward k=1 message, a backward k=1 message and a k=2 token message, and then applies the axis-A update. Synchronous: every position updates from the previous step's state; - a position's SETTLED STATE is its LSA vector after the last step; the SETTLED ARGMAX is that vector's largest entry; - `conv` is the step after which the window's argmax sequence stops changing; conv = W means it never did. ADMITTED OPTIMIZATION, which the viewer must reproduce and must unfold: a message is computed from the SOURCE POSITION'S ARGMAX EVENT ONLY, not from its whole distribution. Exact when the source is absolute, an approximation elsewhere. The detail pane unfolds it by NAMING THE SOURCE EVENT that produced each message -- which is exactly what the query layer does, and is why the detail pane is not decoration. STALENESS TO FIX FIRST, in cmpr-src: that same section still says "then 24 synchronous sweeps". The runs do not do 24 -- v001 at e64 reports conv = 27, which is impossible under 24 steps -- because Q7 changed it to W steps. The prose and the runs disagree. THE VIEWER FOLLOWS THE RUNS. ## Prerequisites -- RESOLVED 2026-08-05, see #hutter_e64_viewer_prereq_20260805 1. RETAIN THE MODELS: DONE, and better than asked. Every model is kept under the same DUMP_MAX gate that keeps the .pos dump, laid out by program version, dataset and prefix length: tests/pprog/models/p8v2/enwik9/{64,1000,10000}/wordsv2-vNNN.m, with fallback samples under models/p8v2/fallback-not-enwik9/ so nothing there can be read as enwik9. 2. k=0: WITHDRAWN, AND THE QUERY LAYER WAS NOT AT FAULT. This goal asked for the background to be "nameable by --patterns" and called the omission a query-layer defect. That was wrong. There is no k=0 pattern: it is not in #wordsv2's P header and contributes zero atomic patterns to the structural line, so --patterns has no ES pair to file it under and is right not to invent one. The programmer's ruling is that k=0 was a throwaway remark about the k indexing and the 256-byte table the implementation reads directly IS AN ERROR that will go away; the correct form is a pattern from an always-present "true" event onto each atomic event, fired by f like any other. MEANWHILE the viewer reads the 256 bytes at model offset 48 because it must, and BUILDS NO CONTROL, LABEL OR PANEL AROUND "the k=0 background" AS AN OBJECT, because there is no such object and the page would need rewriting when the true-event patterns land. Note it is read during settling too, not only at initialisation: under B1 it is the fallback when a byte has no predecessor at all. Also fixed on their side: p8v2-words.md's stale "24 synchronous sweeps" now says W sweeps and says what W is; and --patterns-from/--patterns-to now accept `byte` as well as `input`. ## What we build docs/pprog/p8v2-e64/ the page. One self-contained HTML file: no external stylesheet, no CDN script, no image file, nothing fetched -- the same self-containment contract the viz spec's section 0 sets. docs/pprog/build-p8v2-e64 the builder. Reads the eleven e64 models through the query layer, bakes the SN and the .pos endpoints into the page, and writes it. Runs NO compression. Same shape as docs/pprog/build-p8v2-gen1. data symlinked from ../cmpr-src/tests/pprog/, never copied. Linked from /hutter/pprog/p8v2-gen1/ (which is where someone looking at the generation will be) and from docs/index.html. ## A finding: Q7's evidence is confounded Turned up by driving the built page, and it is a correction to #pprog_p8v2_gen1_report_20260804. Q7 cites "measured: at 24 sweeps over 64 positions, 10 of 64 correct" as the evidence for running W time steps. 10 of 64 is what the OLD APPLICATION RULE gives -- at every step count from 1 to 64, because under w_p - (255 - w_s) the window collapses immediately and the step count is irrelevant. Under min(w_p, w_s) the baseline goes 11, 12, 14, 27, 49, 62, 64 at 1, 2, 4, 8, 16, 24 and 32 steps: fully settled by 32, and stopping at 24 costs TWO positions, not fifty-four. So Q7's measurement was taken before the application rule was fixed and does not bear on its own conclusion. The conclusion may still be right -- W is the safe bound, and 32 of 64 is over half the window -- but its stated evidence is evidence for Q5, not for Q7. Both numbers reproduce on the page, on an engine that conforms on A1, so it is checkable rather than asserted. Not our block to edit; raised for cmpr-src. ## Two things the build had to get right that this block did not anticipate - B2 BORROWS A PATTERN AND MUST REFUSE TO. The stored 65536-byte backward LPP exists only in v004. At e64 it is valid for every variant that shares v004's learned model -- verified at build time, nine of the eleven -- but NOT for v008/v009, which relearned k=1 under axis E. The page refuses B2 there rather than silently settling one model with another's pattern. - D, E AND F ARE MODEL SELECTORS, NOT SETTLING RULES. They change what was learned and recorded, so each picks a different model file rather than a different computation. Only single-axis variants were run, so a combination that moves two of them has no model and is refused rather than approximated. ## Out of scope - Other samples. e64 alone. e1k and e10k have dumps and would work, but 64 positions on screen at once is the whole reason the layout is legible, and nothing forces the choice now. - Controls for axes D, E and F. - Any pick, any ranking, any chart point, and any number presented as a compression result. - Re-rendering the generation panel, which is section 15's job and already exists. - The selectable-scope idea for the visible part of P as it grows: keep it possible, do not build it. ## Done when The page runs f over e64 one time step at a time under all four control groups; the eleven variants are presets; the conformance check runs on load and its result is on the page; unrun combinations are fenced; a position can be selected and its messages unfolded by source event; the page states that intermediate steps are the viewer's own, that D/E/F change nothing here, and that nothing on it is a result; it is rebuilt by its builder rather than hand-edited; and it is self-contained. Wants: "We want settling explorable, not only reported" 255. "We want the viewer's f pinned to the compressor's by a check that shows on the page" 255. "We want the application rule's failure visible by driving it" 255. "We want settling described as pattern applications, not as messages" 255. "We want axes A, B and C visible somewhere, since the archive hides them" 200. "We want unrun rule combinations fenced as not measurements" 255. "We want the k=0 background nameable by the query layer" 200. */