Put the carrier check in the tree instead of a scratchpad, and let the pack name the instrument rather than a remembered number

This commit is contained in:
heaven 2026-09-06 17:20:19 +03:00
parent 508384bb48
commit ac37c8ac59
2 changed files with 99 additions and 4 deletions

View file

@ -65,10 +65,17 @@
внутри — **реестр из 36 сплошно нумерованных живых решений**. Ошибка была в том, что судили по ЗАГОЛОВКУ, не
прочитав содержимое. ⇒ **в журнале сделай тот же тест ПРЕЖДЕ выноса.**
Машинный проход по выносимому (он уже написан контролёром, воспроизводится за секунду): какие `PD-*` и «строки N»
упомянуты и все ли разрешаются; и главное — **какие ⛔/⚠⚠-утверждения не ссылаются ни на ряд регистра, ни на строку
бэклога, ни на D-ноту**. Замер 06.09 по двум новейшим секциям: **69 таких**, в основном «найдено и починено до
сдачи». По норме регистра починенное тоже получает ряд ⇒ **это список на чтение, а не на вынос**.
**Прибор — `docs/scripts/carriers.py`** (написан 06.09 ровно под этот заказ; в дереве, не в чьём-то скрэтчпаде):
`python3 docs/scripts/carriers.py platform/docs/platform-PROGRESS.md --from N --to M`. Он находит ⛔/⚠⚠-утверждения,
не ссылающиеся ни на ряд регистра, ни на строку бэклога, ни на D-ноту, ни на эрратту, и печатает рядом с числом
**сколько строк осмотрено** — иначе ноль неотличим от «искал не там».
**Число называй ПРИБОРОМ, а не памятью.** На двух новейших секциях (32007) он даёт **55**; ручной проход
контролёра давал **69**. Расхождение — в определении носителя, не в дереве: это ровно тот случай, из-за которого
норма требует называть популяцию. Считай ЭТИМ скриптом и скажи, какой диапазон брал.
**И помни, что прибор отвечает на свой вопрос:** он не отличает правило от хроники и не судит о важности.
Ноль означает «всё сослалось», а не «выносить безопасно» — читать всё равно надо.
**Приёмка §4.0:** проход печатает **ноль** утверждений без носителя · вынесено только то, что при чтении оказалось
настоящей хроникой · всё, что оказалось правилом или незакрытой находкой, получило ряд регистра или строку бэклога

88
docs/scripts/carriers.py Normal file
View file

@ -0,0 +1,88 @@
#!/usr/bin/env python3
"""Находки без НОСИТЕЛЯ: утверждения, которые исчезнут при выносе секции в архив.
Зачем. Смена 06.09 пять раз наткнулась на один класс: находка живёт только в
отчёте или в переписке, ряда в регистре и строки в бэклоге у неё нет и после
выноса секции в архив её больше не найти. Отдельно проверено, что и обратное
бывает: секция, ВЫГЛЯДЯЩАЯ хроникой по заголовку, оказалась реестром живых
решений (`platform/docs/STACK_DECISIONS.md`, решения 136). Поэтому перед любым
выносом сначала прогоняют это, а решают глазами.
Что считается носителем: ссылка на ряд регистра (`PD-123`), на строку бэклога
(«строка 281», «строки 49 и 161»), на ноту решения (`D39.202`, `D22.6`) или на
эрратту («эррата 06.09-в»).
Прибор отвечает на СВОЙ вопрос: он не судит, важно ли утверждение, и не умеет
отличать правило от рассказа. Ноль здесь означает «всё сослалось», а не «выносить
безопасно» читать всё равно надо.
Использование:
python3 docs/scripts/carriers.py <файл> [--from N] [--to M] [--quiet]
Код возврата: 0 находок без носителя нет; 1 есть (число в последней строке).
"""
import argparse
import re
import sys
# Утверждение, которое автор пометил как важное: именно они переживают вынос
# хуже всего, потому что читаются как знание, а не как хроника.
MARKED = re.compile(r"⛔|⚠⚠")
# Носители. `D\d` покрывает и `D22.6`, и `D39.202`; эррата ловится отдельно,
# потому что пишется датой, а не номером.
CARRIER = re.compile(
r"PD-\d+"
r"|строк[аиеу]?\s*\d+"
r"|строки\s*\d+"
r"|\bD\d+(?:\.\d+)*\b"
r"|эррат[аеы]\s*\d\d\.\d\d"
)
# Строки, которые несут разметку, а не утверждение.
SKIP = re.compile(r"^\s*(\||#{1,6}\s|```|>\s*$)")
def scan(path: str, lo: int, hi: int):
out = []
with open(path, encoding="utf-8") as fh:
for n, raw in enumerate(fh, 1):
if n < lo or (hi and n > hi):
continue
line = raw.rstrip("\n")
if SKIP.match(line) or len(line.strip()) < 40:
continue
if MARKED.search(line) and not CARRIER.search(line):
out.append((n, line.strip()))
return out
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("path")
ap.add_argument("--from", dest="lo", type=int, default=1)
ap.add_argument("--to", dest="hi", type=int, default=0)
ap.add_argument("--quiet", action="store_true")
a = ap.parse_args()
with open(a.path, encoding="utf-8") as fh:
total = sum(1 for _ in fh)
hi = a.hi or total
found = scan(a.path, a.lo, a.hi)
if not a.quiet:
for n, line in found:
print(f"{a.path}:{n}: {line[:150]}")
# ⚠ Контрольная величина рядом с нулём — норма CLAUDE.md: «0 находок» и
# «искал не там» в выводе неразличимы, поэтому осмотренное печатается всегда.
print(f"\ncarriers: {len(found)} помеченных утверждений без носителя")
print(f" осмотрено строк: {hi - a.lo + 1} (с {a.lo} по {hi} из {total})")
print(" носителем считается: ряд PD · строка бэклога · D-нота · эррата")
print("НЕ проверяется: важность утверждения · правило это или хроника —")
print(" ноль означает «всё сослалось», а не «выносить безопасно».")
return 1 if found else 0
if __name__ == "__main__":
sys.exit(main())