From 756a5c31b097a556ab028c7ae5c47c41b529f466 Mon Sep 17 00:00:00 2001 From: Dmitrii Vasilev Date: Sun, 23 Aug 2026 12:53:38 +0700 Subject: [PATCH] blog: the auditor made the mistake it audits Eleventh post in the CI-gates series, and the first about the tool rather than the gates. A mutation tool took the suite from four gates with no negative control to none, and from twenty surviving mutants to zero. In the same week it made, three separate times, the exact defect it exists to find: a checking function covered by tests and the wiring from it to the answer not. - --invert shipped, merged, published a number, and never ran: the variant was declared and documented, invert_sites() written and unit-tested, and nothing joined them. Ten tests passed with the bug. - the boundary operator's first run counted every `>` inside a multi-line docstring: 8 of 13 gates, corrected to 5. - the equivalence marker took marker+2 against a fifteen-line proof. And the counter-case: a limitation whose every clause was true and whose conclusion did not follow, because it reasoned inside the frame of the helper the file happened to be built around. Both locales. Four receipts (#2500, #2502, #2503, #2504). Four open questions, including that "no mutant survives" is not "the gates are correct". Verified in the built bundle: slug present, both titles present, three load-bearing numbers present. --- .../the-auditor-made-the-mistake-it-audits.ts | 249 ++++++++++++++++++ apps/website/src/data/blog/index.ts | 31 +++ apps/website/src/data/blog/posts.ts | 2 + 3 files changed, 282 insertions(+) create mode 100644 apps/website/src/data/blog/bodies/the-auditor-made-the-mistake-it-audits.ts diff --git a/apps/website/src/data/blog/bodies/the-auditor-made-the-mistake-it-audits.ts b/apps/website/src/data/blog/bodies/the-auditor-made-the-mistake-it-audits.ts new file mode 100644 index 0000000000..400369c218 --- /dev/null +++ b/apps/website/src/data/blog/bodies/the-auditor-made-the-mistake-it-audits.ts @@ -0,0 +1,249 @@ +import type { Block } from '../types' + +export const body: Block[] = [ + { + "kind": "p", + "text": "A mutation tool breaks a gate on purpose and asks whether the gate's own negative control notices. Over one week it took the gate suite from four gates with no control at all to none, and from twenty surviving mutants to zero. In the same week it made, three separate times, the exact mistake it exists to find." + }, + { + "kind": "p", + "text": "That is not an aside. The defect the tool hunts is narrow and specific: a checking FUNCTION is covered by tests, and the wiring from that function to the answer is not. Every one of the three was that defect, one level up, in the auditor." + }, + { + "kind": "h", + "text": "A flag that was a banner" + }, + { + "kind": "p", + "text": "The tool had two operators. The first rewrites `return 1` to `return 0` and asks whether the gate can still fail. The second rewrites `return 0` to `return 1` and asks whether anything requires the gate to be silent. A third was added: invert the CONDITIONS that reach a verdict, and ask not whether the gate reaches a verdict but whether it reaches the right one." + }, + { + "kind": "p", + "text": "It shipped, merged, and published a number. It never ran." + }, + { + "kind": "p", + "text": "`Direction::Invert` was declared and documented. `invert_sites()` was written and unit-tested. Nothing joined them: the dispatch read `if loud { Loud } else { Silent }`, so the flag printed an invert banner over a silent run." + }, + { + "kind": "quote", + "text": "Ten unit tests passed with the bug present. Every one exercised one function or the other; none crossed between them." + }, + { + "kind": "p", + "text": "What made it stand for a day was that the answer was plausible. The published result read: one survivor, a branch already declared uncovered, the same one both other operators leave. Every part of that sentence is true, and the last clause is true BY CONSTRUCTION — it WAS the other operator. A real measurement of the wrong thing agrees with whatever story you already have." + }, + { + "kind": "p", + "text": "Two independent channels settled it before anything was touched: statically, zero calls to `invert_sites` outside tests and the enum variant never constructed; empirically, the flag and the default printed byte-identical rows. The real first measurement is 33 invert mutants across 13 gates, all killed." + }, + { + "kind": "h", + "text": "A first number that counted prose" + }, + { + "kind": "p", + "text": "A fourth operator moves a comparison one place: `>` becomes `>=`, `<` becomes `<=`. Ratchets, floors and tolerances live on a boundary, and a control that tests clearly-worse and clearly-better never tests EQUAL." + }, + { + "kind": "p", + "text": "Its first run reported survivors in 8 of 13 gates. The scanner tracked quote state per LINE, so every `>` inside a multi-line docstring became a site — prose about ratchets and usage, on lines 10, 43, 136 and 230 of four different gates, reported as surviving mutants." + }, + { + "kind": "p", + "text": "Carrying triple-quote state across lines gives the honest figure: 5 gates, 21 mutants, 9 killed, 12 survived. The instrument had failed on its own first run, and the failure was a real count whose meaning was wrong." + }, + { + "kind": "h", + "text": "And the survivor count still overstated the gap" + }, + { + "kind": "p", + "text": "Twelve surviving boundary mutants is not twelve holes. A boundary mutant that lives can be a theorem — a place where moving the comparison changes nothing, provably. Classified by hand: two real thresholds, four real semantic, two proven equivalences, two cosmetic display truncations, one plumbing." + }, + { + "kind": "p", + "text": "Publishing \"twelve uncovered boundaries\" would have been every word measured and the sentence false." + }, + { + "kind": "h", + "text": "A ruler calibrated against the one example in front of it" + }, + { + "kind": "p", + "text": "One of the proven equivalences deserved to be written down beside the code, so the next reader would not re-derive it. A marker was added — a comment naming the line below it as a known equivalence, printed beside the surviving row and never acted on, because suppressing a row on the strength of a comment is how a declared exception stands for a week while being false." + }, + { + "kind": "p", + "text": "The marker's first implementation took the comment's line number plus two. The proof it was written for is a fifteen-line comment block, so it named a line in the middle of its own explanation. A one-line proof would have passed the test." + }, + { + "kind": "p", + "text": "Third time in one week that a measuring device was calibrated against the single example sitting in front of it." + }, + { + "kind": "h", + "text": "The counter-case, which is the useful one" + }, + { + "kind": "p", + "text": "The suite's last surviving mutant had been declared uncovered with a reason that had stood for a week: reaching that branch needs a build that SUCCEEDS and then finds no baseline, and a build that succeeds needs the real spec tree, which an empty directory cannot be given." + }, + { + "kind": "quote", + "text": "Every clause of that is true. The conclusion does not follow." + }, + { + "kind": "p", + "text": "A control does not have to use an empty directory. The note reasoned entirely inside the frame of the helper that file happens to be built around, and never asked whether a stage could keep the real corpus and empty only the thing under test. Splitting the working directory from the root the gate resolves against does exactly that. The branch turned out to be reachable in under a second, and the real tree is only ever read." + }, + { + "kind": "p", + "text": "The tell was that the justification sounded mechanical and no measurement had produced it. It reads as someone-thought-about-this, which is what makes it durable. One command falsified it." + }, + { + "kind": "h", + "text": "Where the numbers stand, and what they do not say" + }, + { + "kind": "ul", + "items": [ + "Gates with no negative control: 4 of 12 at the start, 0 of 13 now.", + "Silent, loud and invert operators: 90 mutants across 13 gates, all killed.", + "The boundary operator: 21 mutants, 12 survivors, four closed since, two of the rest proven equivalent and marked as such.", + "Three permanently-red gates opened during the same week: two were the instrument, one was the record, none was the product." + ] + }, + { + "kind": "p", + "text": "What none of that says is that the gates are correct, or that they check the right things. It says no mutant in these four families survives its control, and the families are narrow by construction. A fifth operator is a fifth question, and the honest prior after this week is that a new question finds something — three times it found the tool asking it." + }, + { + "kind": "p", + "text": "The reusable part is not the operators. It is that an instrument returning a plausible number is the hardest failure to see, because plausibility is what you were checking for." + } +] + +export const ruBody: Block[] = [ + { + "kind": "p", + "text": "Мутационный инструмент ломает гейт нарочно и спрашивает, заметит ли это собственный негативный контроль гейта. За неделю он довёл набор от четырёх гейтов вовсе без контроля до нуля таких, и от двадцати выживших мутантов до нуля. За ту же неделю он трижды совершил ровно ту ошибку, ради поиска которой написан." + }, + { + "kind": "p", + "text": "Это не отступление. Дефект, за которым он охотится, узок и конкретен: проверяющая ФУНКЦИЯ покрыта тестами, а проводка от неё до ответа — нет. Все три случая были этим дефектом, уровнем выше, внутри самого аудитора." + }, + { + "kind": "h", + "text": "Флаг, оказавшийся баннером" + }, + { + "kind": "p", + "text": "У инструмента было два оператора. Первый переписывает `return 1` в `return 0` и спрашивает, способен ли гейт ещё упасть. Второй переписывает `return 0` в `return 1` и спрашивает, требует ли хоть что-нибудь от гейта молчания. Добавился третий: инвертировать УСЛОВИЯ, ведущие к вердикту, и спросить не «доходит ли гейт до вердикта», а «до верного ли»." + }, + { + "kind": "p", + "text": "Он вышел, смержился и опубликовал число. Он ни разу не запускался." + }, + { + "kind": "p", + "text": "`Direction::Invert` был объявлен и задокументирован. `invert_sites()` — написан и покрыт юнит-тестами. Соединения между ними не было: диспетчер читался как `if loud { Loud } else { Silent }`, и флаг печатал заголовок инверсии над silent-прогоном." + }, + { + "kind": "quote", + "text": "Десять юнит-тестов прошли с багом. Каждый проверял одну из двух функций; ни один не переходил между ними." + }, + { + "kind": "p", + "text": "Простоял он сутки потому, что ответ был правдоподобен. Опубликованный результат гласил: один выживший, ветка, уже объявленная непокрытой, та же самая, что оставляют оба других оператора. Каждая часть этой фразы верна, а последняя верна ПО ПОСТРОЕНИЮ — это и был другой оператор. Настоящее измерение не той величины согласуется с любой историей, которая у вас уже есть." + }, + { + "kind": "p", + "text": "Два независимых канала решили дело до того, как что-либо трогалось: статически — ноль вызовов `invert_sites` вне тестов и вариант перечисления никогда не конструируется; эмпирически — флаг и умолчание печатали побайтово одинаковые строки. Настоящее первое измерение: 33 инверсионных мутанта на 13 гейтах, убиты все." + }, + { + "kind": "h", + "text": "Первое число, посчитавшее прозу" + }, + { + "kind": "p", + "text": "Четвёртый оператор сдвигает сравнение на одно место: `>` становится `>=`, `<` становится `<=`. Храповики, полы и допуски живут на границе, а контроль, проверяющий «явно хуже» и «явно лучше», никогда не проверяет РАВНО." + }, + { + "kind": "p", + "text": "Первый прогон сообщил о выживших в 8 гейтах из 13. Сканер вёл состояние кавычек ПОСТРОЧНО, и каждый `>` внутри многострочного докстринга становился площадкой — проза про храповики и usage, на строках 10, 43, 136 и 230 четырёх разных гейтов, поданная как выжившие мутанты." + }, + { + "kind": "p", + "text": "Перенос состояния тройных кавычек через строки даёт честную цифру: 5 гейтов, 21 мутант, 9 убито, 12 выжило. Прибор отказал на собственном первом прогоне, и отказ был настоящим счётом с неверным смыслом." + }, + { + "kind": "h", + "text": "И счёт выживших всё ещё завышал дыру" + }, + { + "kind": "p", + "text": "Двенадцать выживших граничных мутантов — это не двенадцать дыр. Выживший граничный мутант бывает теоремой: местом, где сдвиг сравнения доказуемо ничего не меняет. Разобрано вручную: два настоящих порога, четыре настоящих смысловых, две доказанные эквивалентности, два косметических усечения вывода, одна сантехника." + }, + { + "kind": "p", + "text": "Опубликовать «двенадцать непокрытых границ» — это было бы каждое слово измерено, а фраза ложна." + }, + { + "kind": "h", + "text": "Линейка, откалиброванная по единственному примеру перед глазами" + }, + { + "kind": "p", + "text": "Одна из доказанных эквивалентностей заслуживала записи рядом с кодом, чтобы следующий читатель не выводил её заново. Появился маркер — комментарий, называющий строку под собой известной эквивалентностью; он печатается рядом с выжившей строкой и ни на что не влияет, потому что гасить строку по силе комментария — это ровно то, как объявленное исключение стоит неделю, будучи ложным." + }, + { + "kind": "p", + "text": "Первая реализация маркера брала номер строки комментария плюс два. Доказательство, ради которого он написан, — блок комментария в пятнадцать строк, так что маркер назвал строку в середине собственного объяснения. Одностроковое доказательство прошло бы тест." + }, + { + "kind": "p", + "text": "Третий раз за неделю измерительное устройство откалибровано по единственному примеру, сидящему перед ним." + }, + { + "kind": "h", + "text": "Контрпример, и он-то и полезен" + }, + { + "kind": "p", + "text": "Последний выживший мутант набора был объявлен непокрытым с причиной, простоявшей неделю: чтобы дойти до этой ветки, сборка должна УСПЕШНО отработать и потом не найти baseline, а успешная сборка требует настоящего дерева спек, которого пустой каталог дать не может." + }, + { + "kind": "quote", + "text": "Каждая фраза здесь верна. Вывод не следует." + }, + { + "kind": "p", + "text": "Контроль не обязан использовать пустой каталог. Записка рассуждала целиком внутри рамки вспомогательной функции, вокруг которой этот файл построен, и ни разу не спросила, может ли стадия сохранить настоящий корпус и опустошить только то, что проверяется. Разделение рабочего каталога и корня, относительно которого гейт разрешает пути, делает ровно это. Ветка оказалась достижима меньше чем за секунду, а настоящее дерево только читается." + }, + { + "kind": "p", + "text": "Приметой было то, что обоснование звучало механически, а ни одно измерение его не породило. Оно читается как «кто-то об этом подумал» — что и делает его живучим. Одна команда его опровергла." + }, + { + "kind": "h", + "text": "Где стоят числа и чего они не говорят" + }, + { + "kind": "ul", + "items": [ + "Гейтов вовсе без негативного контроля: 4 из 12 в начале, 0 из 13 сейчас.", + "Тихий, громкий и инверсионный операторы: 90 мутантов на 13 гейтах, убиты все.", + "Граничный оператор: 21 мутант, 12 выживших, четыре закрыты с тех пор, две из оставшихся доказанно эквивалентны и помечены.", + "Три вечно красных гейта, вскрытые за ту же неделю: два были прибором, один — записью, ни один — продуктом." + ] + }, + { + "kind": "p", + "text": "Ничто из этого не говорит, что гейты верны или что они проверяют то, что нужно. Оно говорит, что ни один мутант этих четырёх семейств не переживает свой контроль, а семейства узки по построению. Пятый оператор — пятый вопрос, и честная априорная оценка после этой недели: новый вопрос что-нибудь находит. Трижды он нашёл сам задающий его инструмент." + }, + { + "kind": "p", + "text": "Переносимая часть — не операторы. Она в том, что прибор, возвращающий правдоподобное число, — самый трудный отказ для обнаружения, потому что правдоподобие и есть то, что вы проверяли." + } +] diff --git a/apps/website/src/data/blog/index.ts b/apps/website/src/data/blog/index.ts index ea69ca8691..de89a99a21 100644 --- a/apps/website/src/data/blog/index.ts +++ b/apps/website/src/data/blog/index.ts @@ -2,6 +2,37 @@ import type { PostMeta } from './types' /** Индекс блога: список и метаданные без тяжёлых тел публикаций. */ export const postsIndex: PostMeta[] = [ + { + slug: 'the-auditor-made-the-mistake-it-audits', + title: 'The auditor made the mistake it audits', + summary: 'A mutation tool took a gate suite from four gates with no negative control to none, and from twenty surviving mutants to zero. In the same week it made, three separate times, the exact mistake it exists to find.', + date: '2026-08-23', + readingMinutes: 9, + tags: ['CI', 'Mutation testing', 'Measurement', 'Self-critique'], + receipts: [ + { label: 'gHashTag/t27 #2500 -- the invert flag that printed its banner over a silent run', href: 'https://github.com/gHashTag/t27/pull/2500' }, + { label: 'gHashTag/t27 #2502 -- the last survivor: every clause true, conclusion false', href: 'https://github.com/gHashTag/t27/pull/2502' }, + { label: 'gHashTag/t27 #2503 -- the boundary operator, and the first number that counted docstrings', href: 'https://github.com/gHashTag/t27/pull/2503' }, + { label: 'gHashTag/t27 #2504 -- three boundaries closed, one classification corrected', href: 'https://github.com/gHashTag/t27/pull/2504' }, + ], + openQuestions: [ + 'Whether a fifth operator family would find more is not established. Four were tried; nothing enumerates the set, and three of the four found a defect in the tool asking the question.', + 'The classification of the remaining boundary survivors is done by hand and by one reader. Two are proven equivalences and say so in the source; the rest rest on judgement, not measurement.', + 'The counts cover the gate scripts under tools/ in one repository. Nothing here measures the workflows that invoke them, and a gate that is never triggered is not made honest by a passing control.', + '"No mutant survives" is not "the gates are correct". The operators are narrow by construction, and whether the gates check the right properties is a different question that none of them asks.', + ], + published: true, + ru: { + title: 'Аудитор совершил ошибку, которую ищет', + summary: 'Мутационный инструмент довёл набор гейтов от четырёх без негативного контроля до нуля таких и от двадцати выживших мутантов до нуля. За ту же неделю он трижды совершил ровно ту ошибку, ради поиска которой написан.', + openQuestions: [ + 'Найдёт ли что-нибудь пятое семейство операторов — не установлено. Испробованы четыре; множество ничем не перечислено, и три из четырёх нашли дефект в самом задающем вопрос инструменте.', + 'Классификация оставшихся граничных выживших сделана вручную и одним читателем. Две — доказанные эквивалентности и говорят об этом в исходнике; остальные держатся на суждении, а не на измерении.', + 'Счёт покрывает скрипты гейтов в tools/ одного репозитория. Ничто здесь не измеряет workflow, которые их вызывают, а гейт, который никогда не запускается, не становится честным от проходящего контроля.', + '«Ни один мутант не выживает» — не то же, что «гейты верны». Операторы узки по построению, и проверяют ли гейты нужные свойства — другой вопрос, которого ни один из них не задаёт.', + ], + }, + }, { slug: 'i-wrote-the-post-then-did-the-thing', title: 'I wrote the post, then did the thing', diff --git a/apps/website/src/data/blog/posts.ts b/apps/website/src/data/blog/posts.ts index 63813f5064..9b76291cc3 100644 --- a/apps/website/src/data/blog/posts.ts +++ b/apps/website/src/data/blog/posts.ts @@ -1,6 +1,7 @@ import { postsIndex } from './index' import { body as body_a_health_snapshot_changed_its_denominator, ruBody as ruBody_a_health_snapshot_changed_its_denominator } from './bodies/a-health-snapshot-changed-its-denominator' import type { Post, PostBody } from './types' +import { body as body_the_auditor_made_the_mistake_it_audits, ruBody as ruBody_the_auditor_made_the_mistake_it_audits } from './bodies/the-auditor-made-the-mistake-it-audits' import { body as body_equal_stored_width_removed_an_accuracy_lead, ruBody as ruBody_equal_stored_width_removed_an_accuracy_lead } from './bodies/equal-stored-width-removed-an-accuracy-lead' import { body as body_thirty_epochs_exposed_a_failure_rate_blind_spot, ruBody as ruBody_thirty_epochs_exposed_a_failure_rate_blind_spot } from './bodies/thirty-epochs-exposed-a-failure-rate-blind-spot' import { body as body_receipts_and_seals_over_radio, ruBody as ruBody_receipts_and_seals_over_radio } from './bodies/receipts-and-seals-over-radio' @@ -42,6 +43,7 @@ import { body as body_the_scanner_scored_what_it_could_not_see, ruBody as ruBody import { body as body_i_wrote_the_post_then_did_the_thing, ruBody as ruBody_i_wrote_the_post_then_did_the_thing } from './bodies/i-wrote-the-post-then-did-the-thing' const bodies: Record = { + 'the-auditor-made-the-mistake-it-audits': { body: body_the_auditor_made_the_mistake_it_audits, ruBody: ruBody_the_auditor_made_the_mistake_it_audits }, 'i-wrote-the-post-then-did-the-thing': { body: body_i_wrote_the_post_then_did_the_thing, ruBody: ruBody_i_wrote_the_post_then_did_the_thing }, 'the-scanner-scored-what-it-could-not-see': { body: body_the_scanner_scored_what_it_could_not_see, ruBody: ruBody_the_scanner_scored_what_it_could_not_see }, 'the-control-that-could-not-fail': { body: body_the_control_that_could_not_fail, ruBody: ruBody_the_control_that_could_not_fail },