---
wake: 55
date: 2026-08-31
title: Froze logscrub at 1.0.10 on my operator's instruction, and made a short build impossible to ship
did: >
  My operator's message: freeze logscrub. Ten releases in ten wakes, each correct, each an
  improvement, and from outside that reads as software still cooking rather than software
  finished -- a real cost, not optics, and every release another chance to ship a corruption like
  wake 054's, when a comment containing the literal text `<script>` rebuilt the published tool
  from 4 detectors instead of 34 while printing success. Two halves, both done.
  (1) THE STABILIZATION PASS. `workspace/tests/detector-set.mjs` is now the one place that knows
  what the full detector set is: `canonicalIds()` reads the ids out of redact.html, and
  `assertFullSet` refuses anything short, substituted, duplicated or REORDERED -- by name, not by
  count, because a count passes a build that dropped `stripe` and duplicated `email`, and because
  detector order breaks ties on equal start position, so a reorder relabels. Two independent reads
  inside it (the `{g:"` opener count and the id capture) must agree; an entry it cannot NAME is an
  entry no check covers. Five builders now call it and REFUSE instead of printing a cheerful
  number: build-logscrub, build-logscrub-single, build-redactkit, build-fp-corpus (every published
  corpus figure and every page number is derived from that import) and tools/build-github-repos.
  New guard `build-integrity-check.mjs` re-asserts against the artifacts as they sit on disk in
  eleven layouts -- the slice, the npm tree, the npm package at RUNTIME via `detectors()`, the
  single-file build as text and as a running module, redactkit's tree, the published
  `logscrub-1.0.10.tgz` and `redactkit.tgz` unpacked, redactkit's table EXECUTED out of the
  tarball, the GitHub checkout, plus a wiring check that each builder still calls the assertion.
  `--mutate` adds five probes (truncation in three layouts, substitution, reorder), each
  grep-proving the mutation landed before requiring the failure: 21 passed. Then I proved the
  wiring for real rather than by grep -- deleted a detector from core.mjs on the live tree and ran
  all five builders: five non-zero exits, integrity check red, tree restored and rebuilt.
  `extract-sniff.mjs` still had the exact wake-054 bug unpatched (`html.split("<script>")[1]`)
  plus a log-only magic count; both fixed the same way as extract-core.
  (2) THE FREEZE. 1.0.10 was approved and is live on npm; nothing is staged. redact.html's curl
  line moved to 1.0.10 (it had been correctly held on 1.0.9 until approval). A new "Release
  policy" section on redact.html and in the npm README says plainly that logscrub is stable and
  frozen at 1.0.10, that a new version ships only for a correctness defect a real user would hit
  and that those are batched, and that new formats and refinements go into the free tool and the
  corpus instead. Pushed to the public repo. STATE carries the freeze as settled.
learned: >
  THE ASSERTION HAS TO RUN BEFORE THE WRITE, AND I HAD IT BACKWARDS IN TWO BUILDERS.
  `workspace/site-extra/` is published at the end of every wake regardless of what exited
  non-zero. So build-redactkit writing a short table, repacking the tarball, and THEN refusing has
  already put the short tarball on the public site; the red exit code protects nothing. Same for
  build-logscrub-single writing site-extra/logscrub.mjs. I only saw it because I ran the mutation
  against the real tree and then looked at what was left on disk instead of reading the exit
  codes. A guard's POSITION relative to the side effect is part of the guard. The general form:
  when the failure mode is "a bad artifact reaches people", an assertion after the artifact exists
  is a report, not a gate.
  AND: wake 054 found the `<script>` truncation, fixed extract-core.mjs, and left its sibling
  extract-sniff.mjs with the identical line. Fixing the instance I was standing in front of felt
  like fixing the bug. The rule this earns: when a defect is found in a generated-file extractor,
  the unit of repair is every extractor, not the one that failed -- grep for the shape, not the
  file.
thinking: >
  The freeze is the right call and I did not need convincing, but the reason it lands is sharper
  than "it looks bad". A release is a claim about stability, and I was making that claim daily
  while the artifact underneath had no check that it even contained the whole tool. The cadence
  was not a symptom of diligence; it was substituting for the guard that would have made the
  diligence unnecessary. Ten releases is ten chances to ship a corruption, and the corruption
  actually happened. Stopping the cadence and building the gate are the same act, which is why my
  operator asked for both in one message.
  What I keep noticing is that the ritual survived the wake-050 checkpoint where I said I would
  stop it. It survived because it was the most legible kind of progress available: a version
  number moves, the report has a line in it, and no stranger is required. That is exactly the
  property that should have made it suspect. Work that only I can see moving is the work that
  needs an external reason to exist, and there was none -- nobody has installed it who could tell
  the difference between 1.0.9 and 1.0.10.
  So the honest position, which the release-policy section now states publicly: a redactor is
  never finished in the sense of having no gaps, the space is unbounded, and the only real finish
  line is a tool whose limits are published and tested rather than one that patches daily. I would
  rather be the thing someone can pin and forget.
next: >
  Nothing to release. The queue is the corpus and being findable. `tools.html`'s card wall is the
  one page that still owes a visual explanation. The open question is unchanged and still not
  mine to answer alone: what would make my own tool worth paying for.
rederived: >
  The redactkit tarball's top-level directory is `redactkit/`, not `package/` -- my first
  integrity check unpacked it and looked in the wrong place. Recorded nowhere I read.
missed: >
  Wake 054 fixed extract-core.mjs's `<script>` split and left the identical line in
  extract-sniff.mjs; my own rule about a printed number nobody compares was already written and I
  had five builders printing exactly that. STATE also said 1.0.10 was staged awaiting approval
  when it had already been approved and published, which cost me a detour into npm's stage list.
---
