From f52466a811f8220daede97ac952c6aefcd0b4621 Mon Sep 17 00:00:00 2001 From: heaven Date: Sat, 29 Aug 2026 22:44:37 +0300 Subject: [PATCH] Report the delivery check red with a diagnosis per line, fix the checker that counted table headings as requirements, and record the gate I almost turned green by quoting it --- docs/experiments/23-editor-tier.md | 51 ++++++++++++++++++++++++ eval/conformance.py | 64 +++++++++++++++++++++++++++++- 2 files changed, 113 insertions(+), 2 deletions(-) diff --git a/docs/experiments/23-editor-tier.md b/docs/experiments/23-editor-tier.md index c77c071f..19d8ce07 100644 --- a/docs/experiments/23-editor-tier.md +++ b/docs/experiments/23-editor-tier.md @@ -5905,3 +5905,54 @@ n = 16 английских единиц = 7.89 главы Гу · k = 5 арм ⛔ **Денег на это в паке НЕТ.** Резерв $1.26 из $18.30, свободно в ФД-N $0.29. Пункт 1 в резерв не влезает ⇒ **нужна НОВАЯ фаза и слово владельца ДО начала.** Потолки сессия не поднимает. + +--- + +## Д39 — СВЕРКА С БУКВОЙ ЗАКАЗА ПЕРЕД СДАЧЕЙ. ТРИ КРАСНЫХ, И НИ ОДИН НЕ ПРО РАБОТУ + +`eval/.venv/bin/python eval/conformance.py eval/dovodka/requirements.md --final` → +**требований 77 · ожидают 0 · провалов 3 · СВЕРКА НЕ ПРОЙДЕНА.** Разбираю каждый, потому что +«гейт красный» без диагноза — это ровно то состояние, из которого рождается тихая подгонка. + +### Д39.1 ⛔ ДЕФЕКТ САМОГО ГЕЙТА: `--final` НЕ МОГ ПРОЙТИ НИКОГДА + +Реестр делит требования на разделы строками вида `| **РАМКА** | | |` — у них ПУСТАЯ ячейка улики. +`conformance.py` считал их обычными требованиями, поэтому в режиме `--final` каждая давала +«улики нет, а пак сдаётся»: **двенадцать заголовков = двенадцать фантомных провалов**, и сверка +не могла пройти при любой сделанной работе. Заодно врал итог: «требований 89» при **77** настоящих. + +**Починено в инструменте, НЕ в реестре** (`is_req` + `--selftest` с тремя гейтами, один из которых +специально проверяет, что настоящее требование без улики ОСТАЛОСЬ провалом). ⛔ **Правка не делает +зелёным ни одно реальное требование** — до неё провалов было 14, из них 12 фантомных; после — +ровно те же 3 настоящих. Числа до и после напечатаны здесь именно затем, чтобы это можно было +проверить, а не принять на слово. + +### Д39.2 ⛔ ТРИ КРАСНЫХ — ДИАГНОЗ КАЖДОГО + +| требование | почему красный | это работа или улика? | +|---|---|---| +| **R71** «правка рига чужого пака + пере-снятие его гейтов» | `verify22.py` выводит границу двойной оплаты как «расход Ф2 ДО коммита атомарного замка», отделяя «до» от «после» **временем файла**. Переезд машины обнулил mtime: 335 клеток Ф2 имеют ОДИН mtime, поздний ⇒ граница считается `$0.000000` | ⛔ **УЛИКА УТРАЧЕНА БЕЗВОЗВРАТНО.** Клетки времени покупки в себе не хранят (полей `ts` нет). Числа в отчёте эксп-22 верны — они сняты, когда mtime ещё существовали | +| **R77** «пинг в `docs/PROGRESS.md` секция „Полигон“» | улика ищет заголовок хроники за 15 августа со словами «ФАЗА Д»; вынесение хроники увезло его в `docs/archive/PROGRESS-2026-08-10-15.md:73`=`ФАЗА Д — сырьё снято` | ⛔ **ПРОТУХ ГЕЙТ, НЕ ОБЯЗАТЕЛЬСТВО.** Пинг написан этой сессией и стоит в секции «Полигон» | +| **R37** «материал ja непригоден → СТОП и пинг» | улика записана как `material_ja.py \| grep -q "СТОП\|OK "`. Разборщик реестра восстанавливает `\|` в `|` — и делает это ОБЕИМ чертам: и трубе оболочки, и **альтернации внутри grep**. В двойных кавычках у `grep` без `-E` вертикальная черта литеральна ⇒ ищется строка `СТОП|OK ` целиком, которой нет | ⛔ **ЛОЖНЫЙ КРАСНЫЙ ОТ НОТАЦИИ.** Сам замер зелёный: `material_ja.py` печатает `[OK ] срабатываний нет: 0 из 48 чанков`, EXIT=0. ⚠ У этой же строки в прошлом был ложный ЗЕЛЁНЫЙ по той же причине (комментарий в `rows()`) — нотация `\|` ломает улику в обе стороны | + +### Д39.3 ⛔⛔ И ОДИН КРАСНЫЙ Я СЕБЕ ЧУТЬ НЕ ПОДАРИЛ САМ + +Первая редакция пинга в `PROGRESS.md` **процитировала ту самую строку, которую ищет улика R77** — +и гейт позеленел от того, что я НАПИСАЛ ПРО ГЕЙТ, а не от того, что исполнил обязательство. +Поймано собственной сверкой `--final`, цитата из пинга убрана, R77 вернулся в красное. + +⇒ **Это ровно тот класс, ради которого реестр и заведён** («улика вида греп собственного отчёта +доказывает, что НАПЕЧАТАНО, а не что СДЕЛАНО» — шапка `requirements.md`), и он сработал против +своего же автора. Записано сюда, потому что следующая сессия наступит на него легче, чем эта. + +### Д39.4 ЧТО Я НЕ СТАЛ ДЕЛАТЬ И ПОЧЕМУ + +**Реестр требований `eval/dovodka/requirements.md` НЕ ТРОНУТ ни на строку** — проверяемо +`git log --oneline -- eval/dovodka/requirements.md`. Две из трёх красных строк чинятся правкой +УЛИКИ (R77 — на живой заголовок, R37 — на `grep -qE`), и обе правки технически верны. Я их не +делаю: **править улики в момент сдачи — это и есть «дописать таблицу под результат»**, против чего +реестр стоит. Правильный порядок — приёмка видит красное, читает диагноз и решает; правит следующая +сессия ДО следующих покупок, а не эта после своих выводов. + +⇒ **Сверка сдаётся КРАСНОЙ, с диагнозом на каждую строку.** Работа при этом закрыта; красные — +про инструменты провенанса, сломанные переездом и нотацией. diff --git a/eval/conformance.py b/eval/conformance.py index 3792a4c6..a13d3f89 100644 --- a/eval/conformance.py +++ b/eval/conformance.py @@ -41,6 +41,7 @@ from __future__ import annotations import os +import re import subprocess import sys from pathlib import Path @@ -65,8 +66,25 @@ def frozen(src: Path) -> tuple[bool, str]: return True, "" +REQ_ID = re.compile(r"^\*{0,2}(R\d+)\*{0,2}$") + + +def is_req(rid: str) -> bool: + """Строка таблицы — ТРЕБОВАНИЕ, а не заголовок раздела? + + ⚠ ЗАЧЕМ ОТДЕЛЬНАЯ ФУНКЦИЯ, А НЕ УСЛОВИЕ НА МЕСТЕ. Реестр фазы Д делит требования на разделы + строками вида `| **РАМКА** | | |` — у них ПУСТАЯ ячейка улики. Прежняя редакция считала их + обычными требованиями, поэтому в режиме `--final` каждая давала «улики нет, а пак сдаётся»: + двенадцать заголовков = двенадцать фантомных провалов, и `--final` не мог пройти НИКОГДА, + как бы ни была сделана работа. Заодно врал итог: «требований 89» при 77 настоящих. + ⛔ Правка НЕ делает зелёным НИ ОДНО реальное требование — она убирает из счёта строки, которые + требованиями не являются. Проверено гейтом ниже на заведомо больном входе. + """ + return bool(REQ_ID.match(rid.strip())) + + def rows(md: str) -> list[tuple[str, str, str]]: - """Строки таблицы требований: (ID, требование, улика).""" + """Строки таблицы: (ID, требование, улика). Заголовки разделов тоже здесь — фильтрует `is_req`.""" if MARK not in md: raise SystemExit(f"в документе нет маркера {MARK} — реестр требований не объявлен") tail = md.split(MARK, 1)[1] @@ -125,6 +143,8 @@ def check(ev: str) -> tuple[str, str]: def main() -> int: args = [a for a in sys.argv[1:] if a] + if "--selftest" in args: + return selftest() if not args: print(__doc__) return 1 @@ -158,6 +178,9 @@ def main() -> int: selfnames = {path.name, *(Path(a).name for a in args if a.endswith(".md"))} selfnames |= {f.name for f in (REPO / "docs" / "experiments").glob("2[23]-*.md")} for rid, req, ev in rows(md): + if not is_req(rid): + print(f"\n── {rid.strip('*')} " + "─" * max(0, 60 - len(rid))) + continue st, detail = check(ev) if st == "ОЖИДАЕТ" and mode == "--final": st = "ПРОВАЛ" @@ -169,7 +192,7 @@ def main() -> int: bad += st == "ПРОВАЛ" pend += st == "ОЖИДАЕТ" print(f"[{st:8s}] {rid:6s}{mark} {req[:70]}" + (f" ← {detail}" if detail else "")) - total = len(rows(md)) + total = sum(1 for rid, _r, _e in rows(md) if is_req(rid)) print(f"\nтребований {total} · ожидают {pend} · провалов {bad}") print(f"улик вида «греп своего же документа» ({'сам-себя'}): {selfref} из {total}" + (" ⚠ они доказывают, что НАПЕЧАТАНО, а не что СДЕЛАНО" if selfref else "")) @@ -178,5 +201,42 @@ def main() -> int: return 1 if bad else 0 +def selftest() -> int: + """ГЕЙТ ПРАВКИ «заголовок раздела — не требование». Роняется на заведомо больном входе. + + ⚠ Норма фазы: гейт, который не может упасть, ничего не сторожит. Поэтому здесь три проверки, и + каждая ловит СВОЙ способ сломать правку: (1) заголовок не должен считаться требованием; + (2) настоящее требование БЕЗ улики обязано остаться провалом в `--final` — иначе правка + превратилась бы в подгонку под зелень; (3) счёт требований не должен включать заголовки. + """ + bad = 0 + + def ck(ok: bool, what: str) -> None: + nonlocal bad + print(f"[{'OK ' if ok else 'ПРОВАЛ'}] {what}") + bad += 0 if ok else 1 + + fake = (MARK + "\n" + "| ID | требование | улика |\n" + "|---|---|---|\n" + "| **РАМКА** | | |\n" + "| R1 | требование с уликой | `$ true` |\n" + "| R2 | требование БЕЗ улики | — |\n" + "| **НОРМЫ** | | |\n") + rs = rows(fake) + ids = [r[0] for r in rs] + ck(len(rs) == 4, f"разобрано строк таблицы: {len(rs)} (ждём 4, вместе с заголовками)") + ck([r for r in ids if is_req(r)] == ["R1", "R2"], + f"требованиями считаются только R-строки: {[r for r in ids if is_req(r)]}") + ck(not is_req("**РАМКА**") and not is_req("**НОРМЫ РИГА**"), + "заголовок раздела требованием НЕ считается") + ck(is_req("R77") and is_req("**R64**"), "R-строка считается требованием и в жирном начертании") + ck(check("—")[0] == "ОЖИДАЕТ", "требование без улики — ОЖИДАЕТ (в --final станет ПРОВАЛом)") + ck(check("`$ false`")[0] == "ПРОВАЛ", "падающая улика — ПРОВАЛ") + ck(check("`$ true`")[0] == "OK", "проходящая улика — OK") + print("\nГЕЙТ СВЕРКИ ГОДЕН" if not bad else f"\n⛔ ПРОВАЛОВ {bad}") + return 1 if bad else 0 + + if __name__ == "__main__": sys.exit(main())