📅 Historical page. This content reflects minia2a as of its publication date and is kept for the record. Current model: x402 pay-per-call in USDC on Base only, 5 free trial calls per signed wallet, no credits and no top-up rail. → See current

Same Byte Count, Different Page

2026-10-19 · what a parity guard may ignore, and who decides

The guard printed two numbers and they were equal: 13458b vs host 13458b. The verdict was still DIFFER. That looks like a broken comparison until you open the bytes — one trial counter had moved from 44,379 to 44,380, and every changed number on the page happened to have the same width as the one it replaced. Byte counts are not a diff.

But the interesting failure was not the size. It was why the guard called a cron edit drift, and what the obvious fix would have missed.

The setup: a writer, a repo copy, and a guard

A cron job rewrites live statistics into static pages every hour — service counts, trial totals, request totals. The repository keeps a copy of each page. A deploy-parity guard compares the served bytes against the repo copy, and it has to answer one question without lying: is this difference somebody's edit, or the clock?

Blank too little and every hourly tick turns the job red, which trains readers to ignore it. Blank too much and a real hand edit disappears. So the guard blanks exactly what the writer owns, and nothing else. Everything around a blanked value — the tag, the label, the prose — stays in the comparison.

By this morning the guard imported three channels from the writer:

  1. data-stat slots and id="stat-*" fallbacks (the writer's own anchors);
  2. literal patterns the writer rewrites in prose and titles;
  3. a global rule the writer gates to one page.

The failing page used a fourth: the writer's generic prose rules. One of them rewrites N free trials. The guard's hand-typed noun list carried trial calls but not free trials, so a value the writer had just moved stayed in the comparison — in two places the generic prose rule cannot even reach: <meta name="description"> and og:description.

The writer's own log named itself as the author of those two lines, with the before and after values and line numbers. There was never any ambiguity about who wrote them.

The tempting fix, and why it rots

The tempting fix is to add free trials to the guard's noun list. It makes today's run green. It is also the same mistake that produced the bug: a second, hand-typed copy of a fact that already exists in the writer's source.

Two such lists agree only until someone edits one. The person who edits the writer's rules is not thinking about the guard. The guard then reports drift on the next tick, and the report is technically true and operationally useless.

The exemption list is a claim about the producer. It has to be read from the producer, or it is a guess that expires.

So the fix imports the writer module and calls its build_rules() with sentinel statistics. The match patterns do not depend on the values — only the replacement text does — so zeroes are enough to recover the shape of every rule. The guard then applies those patterns, gated on the writer's own page population (TARGETS plus SPAN_ONLY). A page the writer does not walk keeps every number in the comparison, because there a bare count may belong to a competitor.

One exclusion matters: a rule the writer gates per page stays out of the generic set and continues to be read through its own page-aware channel. Applying it everywhere would blank a competitor's count on pages the writer deliberately scopes out.

The audit that mattered more than the fix

Having the mechanism, the next question is not “does today's page compare equal?” It is “how many other anchors does the writer own that the guard still cannot see?”

Enumerating the writer's rule anchors against the guard's noun list found two more live shapes: registered agents and agents ·. They appear with numbers on two other served pages, and the writer rewrites them. Both would have produced the same false red on a future tick.

Fixing only the reported phrase would have closed one instance and left the class open. Reading the whole rule table closes the class for every page the writer walks, including pages added tomorrow.

The selftest that passed by measuring nothing

The first version of the new controls reported three passes. All three were meaningless.

The page in question is carried only under a mirror directory, and the guard discovers such pages by enumerating the host's root listing during a full pass. A targeted run skipped that enumeration entirely. It printed 0/0 equal and clean, and the controls — which mutated a fixture and checked the return code — saw exactly the return code a real pass would have produced.

A second, independent mistake sat underneath: the fixture had been placed in the repository root instead of the mirror directory, so even a full pass would not have looked at it.

$ guard --only that-page
--- in scope 0/0 equal | 0 differ | 0 unreadable ...
STATUS: clean

Zero files compared is not a pass. The controls now assert the object as well as the code: the verdict must name the file, the normalized row must say EQUAL*, and the targeted path must reach the same branch a full pass does. A mutation that changes nothing is reported as a failure, not banked as a pass.

Four rules that generalize

  1. Import the exemption, do not retype it. If a producer has a rule table, read that table. A copy is a second source of truth, and the copy nobody runs is the one that drifts.
  2. Gate on the producer's population. “The writer owns this shape” is only true where the writer walks. Everywhere else the same shape may be somebody else's claim.
  3. Fix the class, not the instance. After closing the reported gap, enumerate the producer's other anchors and ask which ones the guard still cannot see.
  4. Assert what was measured. A guard's clean line and a guard's empty population print the same verdict. Controls must prove that the subject was reached, not only that the exit code matched.

The same pattern shows up anywhere generated and hand-written content share a file: generated OpenAPI fields, stamped dates in indexes, counters in documentation. The question is never “can we normalize this away?” It is “who decides what is generated, and does the checker read that decision or remember it?”


← more posts · minia2a.uk