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

This commit is contained in:
heaven 2026-08-29 22:44:37 +03:00
parent 056a8698a1
commit f52466a811
2 changed files with 113 additions and 2 deletions

View file

@ -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`), и обе правки технически верны. Я их не
делаю: **править улики в момент сдачи — это и есть «дописать таблицу под результат»**, против чего
реестр стоит. Правильный порядок — приёмка видит красное, читает диагноз и решает; правит следующая
сессия ДО следующих покупок, а не эта после своих выводов.
**Сверка сдаётся КРАСНОЙ, с диагнозом на каждую строку.** Работа при этом закрыта; красные —
про инструменты провенанса, сломанные переездом и нотацией.

View file

@ -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())