diff --git a/apps/website/src/data/blog/bodies/the-control-that-could-not-fail.ts b/apps/website/src/data/blog/bodies/the-control-that-could-not-fail.ts new file mode 100644 index 0000000000..1797f4329b --- /dev/null +++ b/apps/website/src/data/blog/bodies/the-control-that-could-not-fail.ts @@ -0,0 +1,263 @@ +import type { Block } from '../types' + +export const body: Block[] = [ + { + "kind": "p", + "text": "Four gates in a repository had never been seen to fail. Not \"had failed and been fixed\" — never once observed red, by anyone, in their lifetimes. Green on the happy path was the whole of the evidence that they worked, which is the same evidence a gate that cannot fail produces." + }, + { + "kind": "p", + "text": "So each got a negative control: plant a deliberate fault, run the gate, demand it goes red and names the right branch. Thirty-one mutants across the four, all killed. Then I checked the controls themselves, and one of them was the exact defect the batch was written to remove." + }, + { + "kind": "h", + "text": "The measurement" + }, + { + "kind": "p", + "text": "The catalog-integrity gate checks that every row in a format catalog still has its spec file on disk. Its control plants seven faults — a dangling reference, a deleted neighbour, a collapsed family — and asserts each is reported by its own message, with its neighbours' markers absent." + }, + { + "kind": "p", + "text": "I changed one line in the gate: the `return 1` that turns a non-empty problem list into a failing exit code became `return 0`." + }, + { + "kind": "code", + "text": "main(): return 1 -> return 0\n\n gate, on a catalog with a dangling source=\n \"OK: 109 catalog rows, every source= resolves...\" exit 0\n\n --self-check\n \"self-check: all branches proven red\" exit 0" + }, + { + "kind": "p", + "text": "The gate was completely dead. It printed a clean bill of health on a broken catalog. And all seven of its cases still reported success, because every one of them called the checking function directly and inspected the list it returned. The function was covered. The wiring from that function to the process exit code was not — and the exit code is the only thing continuous integration reads." + }, + { + "kind": "h", + "text": "The in-process design was not the mistake" + }, + { + "kind": "p", + "text": "It would be easy to conclude that controls should always spawn the real program. The comment above this one explains why it doesn't, and the reasoning is sound: the module resolves its repository root from its own file path, so a control that runs it from the wrong working directory gets a root of `/`, scans nothing, and reports zero problems for entirely the wrong reason." + }, + { + "kind": "p", + "text": "That is not hypothetical. It had already happened once in this campaign — a control run from a temporary directory \"killed\" three mutants and the conclusion was nearly reversed before someone re-ran it from the right place, where it killed none. Calling the function directly makes that class of error impossible." + }, + { + "kind": "p", + "text": "So the fix adds a layer instead of replacing one. Two end-to-end runs now spawn the whole program against a planted tree, with the script **copied into** that tree — so the root resolves there by the ordinary parent-of-parent rule. No `--root` flag, no environment variable. Nothing new that could aim a live gate at somewhere harmless." + }, + { + "kind": "h", + "text": "My first attempt to measure this was itself broken" + }, + { + "kind": "p", + "text": "To check all four controls at once I neutered each gate with a single regular expression: every `return 1` through `return 4` became `return 0`. Two controls then \"passed vacuously\" — the gate was dead and they said nothing." + }, + { + "kind": "p", + "text": "They hadn't. The regex had also rewritten the returns inside the control functions. Both had detected the mutant correctly and merely lost the ability to report it. Reading the printed output rather than the exit code is what showed the difference; rerunning with one variable changed is what proved it." + }, + { + "kind": "p", + "text": "This is the broken-ruler error, applied to the experiment rather than the system: I had modified the instrument and the subject in the same step, then read the instrument. It is worth noticing that the discipline against this was already written down, in a document I wrote, and I did it anyway — the rule is easy to hold when diagnosing someone else's system and easy to drop when the system is your own tooling." + }, + { + "kind": "h", + "text": "A label is not a property" + }, + { + "kind": "p", + "text": "The command that found the four uncontrolled gates reports a column called `control`. It answers: does a negative control exist? After this batch it reads zero missing, twelve of twelve covered." + }, + { + "kind": "p", + "text": "That number is a label. The property is: can the control fail? The catalog-integrity gate would have been counted as controlled the entire time its control was incapable of noticing a dead gate. Trusting a self-declared label over a measured property is one of the ten ways a gate lies in the taxonomy this same audit produced — and it was sitting in the tool built to find it." + }, + { + "kind": "p", + "text": "So the sweep got a sibling. It flips each verdict-returning line to `return 0`, one site at a time, runs the control, and calls the mutant killed only if the control goes non-zero. Survivors are printed by line number. Where `sweep` asks whether a control exists, this asks whether it works." + }, + { + "kind": "h", + "text": "Nine survivors, and what they are not" + }, + { + "kind": "code", + "text": "gate mutants verdict\ncheck_catalog_count.py 3/3 all killed\ncheck_catalog_integrity.py 1/1 all killed\ncheck_elab_ratchet.py 3/5 SURVIVED at lines 346, 390\ncheck_seal_coverage.py 0/3 SURVIVED at lines 288, 321, 339\ncheck_withdrawn_live.py 0/2 SURVIVED at lines 177, 192" + }, + { + "kind": "p", + "text": "Nine of twelve gates had at least one surviving mutant. The tempting headline is that nine gates are broken. It would have been wrong, and checking took ten minutes." + }, + { + "kind": "p", + "text": "Six of the nine keep a baseline file — a ledger of known, accepted problems. I moved each baseline aside and ran each gate. All six went red, correctly, with the right message. They work today. A surviving mutant does not say a gate is wrong now; it says nothing in the repository proves it will stay right." + }, + { + "kind": "p", + "text": "That distinction is the difference between a report someone acts on and a report someone learns to discount." + }, + { + "kind": "h", + "text": "Why the survivors cluster" + }, + { + "kind": "p", + "text": "They are **preconditions**, not verdicts. A control builds a well-formed world and then breaks one fact inside it: a row that points nowhere, a file that lost its data, a count that drifted. It never breaks the world's *existence* — the baseline that isn't there, the tool that wouldn't run, the directory that wouldn't open." + }, + { + "kind": "code", + "text": "check_elab_ratchet.py:390 -> return 0\n\n gate, with its baseline moved aside\n \"no baseline; run --update-baseline once\" exit 0\n\n its control exit 0" + }, + { + "kind": "p", + "text": "The gate announces that it has nothing to check, and passes. Everything downstream reads green. This is the vacuous-pass class, one layer further out than the audit had looked — the audit asked whether a gate could pass without doing its work, and answered it for the data path only." + }, + { + "kind": "p", + "text": "Six of the nine share that exact shape, which means one control pattern closes the class rather than six bespoke cases." + }, + { + "kind": "h", + "text": "The check worth running on your own repository" + }, + { + "kind": "p", + "text": "Take any gate you rely on and break its failure path — change the line that returns the failing status, nothing else. Then run whatever proves that gate works. If it still passes, you have learned something specific: not that the gate is bad, but that your evidence for it doesn't reach as far as you thought." + }, + { + "kind": "p", + "text": "And do it one line at a time. Breaking several things at once and reading the result is how a working control gets mistaken for a broken one, which is how a real defect two files over goes unexamined for another week." + } +] + +export const ruBody: Block[] = [ + { + "kind": "p", + "text": "Четыре гейта в репозитории никогда не видели красными. Не «падали и были починены» — ни разу, ни кем, за всё время их жизни. Зелёный на счастливом пути был всем свидетельством их работы, а это ровно то же свидетельство, которое выдаёт гейт, неспособный упасть." + }, + { + "kind": "p", + "text": "Каждому написали негативный контроль: подсадить намеренную поломку, прогнать гейт, потребовать, чтобы он покраснел и назвал нужную ветку. Тридцать один мутант на четверых, все убиты. Потом я проверил сами контроли — и один из них оказался ровно тем дефектом, ради устранения которого партия и писалась." + }, + { + "kind": "h", + "text": "Измерение" + }, + { + "kind": "p", + "text": "Гейт целостности каталога проверяет, что у каждой строки каталога форматов её файл-спека всё ещё лежит на диске. Контроль подсаживает семь поломок — висящую ссылку, удалённого соседа, схлопнувшееся семейство — и требует, чтобы каждая была названа своим сообщением, а маркеры соседних веток отсутствовали." + }, + { + "kind": "p", + "text": "Я поменял в гейте одну строку: `return 1`, превращающий непустой список проблем в падающий код возврата, стал `return 0`." + }, + { + "kind": "code", + "text": "main(): return 1 -> return 0\n\n гейт на каталоге со сломанной source=\n «OK: 109 catalog rows, every source= resolves...» код 0\n\n --self-check\n «self-check: all branches proven red» код 0" + }, + { + "kind": "p", + "text": "Гейт был мёртв полностью. Он печатал справку о здоровье на сломанном каталоге. И все семь его случаев по-прежнему рапортовали об успехе — потому что каждый вызывал проверяющую функцию напрямую и смотрел в возвращённый ею список. Функция была покрыта. Проводка от функции к коду возврата процесса — нет, а код возврата это единственное, что читает непрерывная интеграция." + }, + { + "kind": "h", + "text": "Ошибкой был не вызов внутри процесса" + }, + { + "kind": "p", + "text": "Легко заключить, что контроль обязан всегда запускать настоящую программу. Комментарий над этим кодом объясняет, почему он так не делает, и рассуждение верное: модуль выводит корень репозитория из пути собственного файла — и контроль, запустивший его из неверного рабочего каталога, получит корнем `/`, не просканирует ничего и отрапортует ноль проблем по совершенно постороннему поводу." + }, + { + "kind": "p", + "text": "Это не гипотеза. В этой кампании такое уже случилось: контроль, запущенный из временного каталога, «убил» трёх мутантов, и вывод чуть не развернулся на противоположный, пока его не перезапустили из правильного места — где он не убил ни одного. Прямой вызов функции делает этот класс ошибок невозможным." + }, + { + "kind": "p", + "text": "Поэтому починка **добавляет** слой, а не заменяет. Два сквозных прогона запускают всю программу целиком на подсаженном дереве, а скрипт **копируется** в это дерево — и корень разрешается туда обычным правилом «родитель родителя». Ни флага `--root`, ни переменной окружения. Ничего нового, чем можно было бы увести живой гейт в безобидное место." + }, + { + "kind": "h", + "text": "Моя первая попытка это измерить сама была сломана" + }, + { + "kind": "p", + "text": "Чтобы проверить все четыре контроля разом, я обезвредил каждый гейт одной регуляркой: любой `return 1`…`return 4` стал `return 0`. Два контроля тогда «прошли вакуумно» — гейт мёртв, а они молчат." + }, + { + "kind": "p", + "text": "Ничего подобного. Регулярка переписала и возвраты внутри самих контрольных функций. Оба мутанта заметили правильно и лишь потеряли возможность об этом сообщить. Разницу показало чтение печати вместо кода возврата; доказал — повтор с изменением одной переменной." + }, + { + "kind": "p", + "text": "Это ошибка сломанной линейки, применённая к эксперименту, а не к системе: я изменил прибор и предмет одним действием, а потом прочитал прибор. Стоит заметить, что правило против этого уже было записано — в документе, который писал я, — и я всё равно так сделал. Правило легко держать, когда диагностируешь чужую систему, и легко выронить, когда система — твой собственный инструментарий." + }, + { + "kind": "h", + "text": "Ярлык — не свойство" + }, + { + "kind": "p", + "text": "Команда, нашедшая четыре гейта без контроля, печатает колонку `control`. Она отвечает на вопрос: существует ли негативный контроль? После этой партии там ноль отсутствующих, двенадцать из двенадцати покрыты." + }, + { + "kind": "p", + "text": "Это число — ярлык. Свойство звучит иначе: способен ли контроль упасть? Гейт целостности каталога считался бы контролируемым всё то время, пока его контроль был неспособен заметить мёртвый гейт. Доверие самопровозглашённому ярлыку вместо измеренного свойства — один из десяти способов гейту соврать в таксономии, которую произвёл этот же аудит. И он сидел в инструменте, построенном его искать." + }, + { + "kind": "p", + "text": "Поэтому у свода появился напарник. Он переворачивает каждую строку, возвращающую вердикт, в `return 0` — по одному сайту за раз, — прогоняет контроль и засчитывает мутанта убитым, только если контроль ушёл в ненулевой код. Выжившие печатаются номерами строк. Свод спрашивает, существует ли контроль; этот — работает ли он." + }, + { + "kind": "h", + "text": "Девять выживших, и чем они не являются" + }, + { + "kind": "code", + "text": "gate mutants verdict\ncheck_catalog_count.py 3/3 all killed\ncheck_catalog_integrity.py 1/1 all killed\ncheck_elab_ratchet.py 3/5 SURVIVED at lines 346, 390\ncheck_seal_coverage.py 0/3 SURVIVED at lines 288, 321, 339\ncheck_withdrawn_live.py 0/2 SURVIVED at lines 177, 192" + }, + { + "kind": "p", + "text": "У девяти гейтов из двенадцати нашёлся хотя бы один выживший мутант. Соблазнительный заголовок — «девять гейтов сломаны». Он был бы неверен, а проверка заняла десять минут." + }, + { + "kind": "p", + "text": "Шесть из девяти держат baseline — реестр известных и принятых проблем. Я отодвинул каждый baseline и прогнал каждый гейт. Все шесть покраснели, корректно, с верным сообщением. Сегодня они работают. Выживший мутант не говорит, что гейт неверен сейчас; он говорит, что ничто в репозитории не доказывает, что он останется верным." + }, + { + "kind": "p", + "text": "Это различие — граница между отчётом, по которому действуют, и отчётом, который учатся не читать." + }, + { + "kind": "h", + "text": "Почему выжившие кучкуются" + }, + { + "kind": "p", + "text": "Это **предусловия**, а не вердикты. Контроль строит правильный мир и портит один факт внутри него: строку, которая никуда не ведёт, файл, потерявший данные, съехавший счёт. Он никогда не ломает *существование* мира — отсутствующий baseline, инструмент, который не запустился, каталог, который не открылся." + }, + { + "kind": "code", + "text": "check_elab_ratchet.py:390 -> return 0\n\n гейт с отодвинутым baseline\n «no baseline; run --update-baseline once» код 0\n\n его контроль код 0" + }, + { + "kind": "p", + "text": "Гейт объявляет, что проверять ему нечего, и проходит. Всё, что ниже по течению, читает зелёный. Это класс вакуумного прохода, на слой дальше, чем смотрел аудит: аудит спрашивал, может ли гейт пройти, не сделав работы, и отвечал на это только для пути данных." + }, + { + "kind": "p", + "text": "Шесть из девяти имеют ровно эту форму — значит один образец контроля закрывает весь класс, а не шесть отдельных случаев." + }, + { + "kind": "h", + "text": "Проверка, которую стоит провести у себя" + }, + { + "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 7fa7ed7a59..10d119b0b6 100644 --- a/apps/website/src/data/blog/index.ts +++ b/apps/website/src/data/blog/index.ts @@ -2,6 +2,36 @@ import type { PostMeta } from './types' /** Индекс блога: список и метаданные без тяжёлых тел публикаций. */ export const postsIndex: PostMeta[] = [ + { + slug: 'the-control-that-could-not-fail', + title: 'The control that could not fail', + summary: 'Four gates had never been seen red. Writing their negative controls produced one that reported every branch red while the gate it guarded printed OK on a broken catalog.', + date: '2026-08-23', + readingMinutes: 9, + tags: ['CI', 'Negative controls', 'Mutation testing', 'Self-critique'], + receipts: [ + { label: 'gHashTag/t27 #2467 — the four controls, and the end-to-end layer the first draft lacked', href: 'https://github.com/gHashTag/t27/pull/2467' }, + { label: 'gHashTag/t27 #2465 — four gates with no negative control, and a claim that was not reproducible', href: 'https://github.com/gHashTag/t27/issues/2465' }, + { label: 'gHashTag/t27 #2468 — nine gates whose controls cover the verdict path but not the precondition path', href: 'https://github.com/gHashTag/t27/issues/2468' }, + ], + openQuestions: [ + 'Whether the nine gates with surviving mutants stay correct is not established here: the six baseline-backed ones were checked by hand and go red correctly today, and that is a dated observation, not a standing property.', + 'The shared control pattern for the precondition class is proposed and not written; whether one pattern really covers all six is untested.', + 'The mutation operator is a single one — a verdict-returning line forced to zero. A gate can be broken in ways this operator never produces, so "all killed" bounds one failure mode, not all of them.', + 'A surviving mutant can mean the site is unreachable rather than uncovered, and this run does not separate the two.', + ], + published: true, + ru: { + title: 'Контроль, который не мог упасть', + summary: 'Четыре гейта никогда не видели красными. Написание негативных контролей дало один, который рапортовал «все ветки красные», пока охраняемый им гейт печатал OK на сломанном каталоге.', + openQuestions: [ + 'Останутся ли верными девять гейтов с выжившими мутантами — здесь не установлено: шесть baseline-гейтов проверены руками и краснеют корректно сегодня, а это датированное наблюдение, а не постоянное свойство.', + 'Общий образец контроля для класса предусловий предложен, но не написан; покроет ли один образец все шесть — не проверено.', + 'Мутационный оператор здесь один — строка вердикта, принудительно обнулённая. Гейт можно сломать способами, которых этот оператор не порождает: «все убиты» ограничивает одну форму отказа, а не все.', + 'Выживший мутант может означать недостижимый сайт, а не непокрытый, и этот прогон их не разделяет.', + ], + }, + }, { slug: 'ternary-won-the-wire-not-the-gate', title: 'Ternary won the wire; it did not win the gate', diff --git a/apps/website/src/data/blog/posts.ts b/apps/website/src/data/blog/posts.ts index ac9228833e..7d2c595327 100644 --- a/apps/website/src/data/blog/posts.ts +++ b/apps/website/src/data/blog/posts.ts @@ -35,7 +35,10 @@ import { body as body_the_ratchet_counted_a_total_as_an_error, ruBody as ruBody_ import { body as body_four_hundred_and_twelve_tests_that_were_sentences, ruBody as ruBody_four_hundred_and_twelve_tests_that_were_sentences } from './bodies/four-hundred-and-twelve-tests-that-were-sentences' import { body as body_ternary_won_the_wire_not_the_gate, ruBody as ruBody_ternary_won_the_wire_not_the_gate } from './bodies/ternary-won-the-wire-not-the-gate' +import { body as body_the_control_that_could_not_fail, ruBody as ruBody_the_control_that_could_not_fail } from './bodies/the-control-that-could-not-fail' + const bodies: Record = { + 'the-control-that-could-not-fail': { body: body_the_control_that_could_not_fail, ruBody: ruBody_the_control_that_could_not_fail }, 'ternary-won-the-wire-not-the-gate': { body: body_ternary_won_the_wire_not_the_gate, ruBody: ruBody_ternary_won_the_wire_not_the_gate }, 'the-required-check-was-an-echo': { body: body_the_required_check_was_an_echo, ruBody: ruBody_the_required_check_was_an_echo }, 'the-gate-was-right-and-nothing-stopped': { body: body_the_gate_was_right_and_nothing_stopped, ruBody: ruBody_the_gate_was_right_and_nothing_stopped }, diff --git a/apps/website/typecheck-baseline.json b/apps/website/typecheck-baseline.json index 7262b234cc..61dd8aeae9 100644 --- a/apps/website/typecheck-baseline.json +++ b/apps/website/typecheck-baseline.json @@ -1,6 +1,6 @@ { "note": "Pre-existing tsc errors per file. This file may only shrink. Run `npm run typecheck:update` after fixing some.", - "total": 184, + "total": 179, "files": { "src/components/AgentMuDashboard.tsx": 1, "src/components/AgentMuWidget.tsx": 1, @@ -20,7 +20,6 @@ "src/components/sections/MarketplaceSection.tsx": 11, "src/components/sections/NFTMarketplaceSection.tsx": 8, "src/components/sections/QuantumGravitySection.tsx": 12, - "src/components/sections/SacredChemistryWidget.tsx": 5, "src/components/sections/SacredEconomySection.tsx": 4, "src/components/sections/SelfImproverSection.tsx": 5, "src/data/neuralData.ts": 1,