name: applied-quality description: Оценка ПРИКЛАДНОГО качества Unity-кода (пакета/системы/подсистемы) по канону «Сценарная прочность» — трассировка логических нитей вход→выход, их прочность, ступенчатость, спутанность и рекурсия. Дополняет code-quality: тот меряет ФОРМУ кода в покое (метрики/формулы), этот — ПОВЕДЕНИЕ в динамике (нить от входа к выходу, что рвётся при повторном входе/сбое/гонке). Триггеры — как у code-quality, но БЕЗ слова «качество»: вместо него корректность/логичность/адекватность/правильность. Примеры: «оцени корректность пакета», «проверь систему на логичность», «оцени адекватность кода», «прогони на правильность», «оцени корректность/логичность/адекватность/правильность системы X». Слово «качество» → это code-quality, не этот скилл. Развилка по LOC: моно (≤2000), шардинг с реконструкцией графа (2000–10000), частичный (>10000).

Прикладное качество кода — «Сценарная прочность» (Unity)

Скилл самодостаточен: канон, оси, рубрики, формула, формат — всё ниже.

На вход — целевой пакет/система/подсистема (или конкретная фича/дифф). На выходе — оценка прочности логических нитей: балл по осям + множитель спутанности + потолок катастроф, с доказательствами в виде сценариев отказа, а не абстрактных счётчиков.

Граница с code-quality (не дублировать)

code-quality applied-quality (этот)
Меряет форму: метрики, структуру, паттерны пригодность в работе: поведение нити вход→выход
Единица доказательства посчитанное нарушение (grep/формула) сценарий отказа (прогон в голове)
Метод детерминированный семантический (чтение + рассуждение о рантайме)
Пример Fat Interface, DIT, аллокации dangling-ссылки, гонки, утечки на re-entry, over-fetch, ступенчатость

Пересекающиеся термины разводятся: «атомарность» здесь — НЕ размер модели (это code-quality 2.2), а over-fetch по нити; «однонаправленность» здесь — реальная спутанность нитей в рантайме, а не статичный счётчик зависимостей.


0. Триггер и проверка контекста

⚠️ Подтверждение направления (ПЕРЕД запуском, обязательно)

Этот скилл меряет ПРИКЛАДНУЮ ЛОГИКУ / поведение: корректность логических нитей вход→выход, спутанность, ступенчатость, рекурсию — код «в движении», что рвётся на re-entry/сбое/гонке.

Рядом — скилл-близнец code-quality: он меряет ФОРМУ / архитектуру (метрики, структуру, паттерны, связность) — код «в покое».

Триггеры близнецов похожи, поэтому до анализа убедиться, что пользователь хочет именно прикладную логику/корректность. Если формулировка неоднозначна (или могла относиться к архитектуре/форме) — задать ОДИН вопрос и дождаться ответа:

«Оценить прикладную корректность (нити, спутанность, что порвётся в рантайме — applied-quality), или архитектуру/форму (метрики, паттерны, связность — code-quality)?»

  • Явно «корректность / логичность / адекватность / правильность / спутанность / что рвётся» → продолжать без вопроса.
  • Явно «качество / архитектура / метрики / паттерны» → предложить переключиться на code-quality.
  • Просят и то, и другое («качество и корректность») → НЕ пинг-понг между близнецами: выполнить applied-quality и предложить вторым проходом прогнать code-quality (или наоборот), уточнив объём.

Запускается, когда просят оценить прикладное качество / прочность / спутанность конкретного пакета.

Если цель не указана — уточнить:

«Какой пакет/система/фича оценивается? Канон точен на одном пакете ≤2000 LOC; больше — шардинг с предупреждением о деградации.»

Без цели скилл НЕ выполняется.


1. Шаг 0 — измерение размера (всегда)

Измерить LOC всех релевантных .cs (исключить *.Designer.cs, // <auto-generated>, *Tests*, .meta). wc -l достаточно для гейтинга.

bash:

find <target> -name "*.cs" -not -name "*.Designer.cs" -not -path "*/Tests/*" -not -path "*Tests*" -exec wc -l {} + | tail -1

Делегирование: подсчёт LOC + инвентарь (файлы, типы, точки входа/выхода-кандидаты) — через Agent subagent_type=Explore или general-purpose model=haiku. Основной контекст не занимать инвентаризацией.


2. Развилка по LOC

Пороги ниже, чем в code-quality: трассировка нитей дороже формул.

Ветка LOC Протокол
A — моно ≤ 2000 весь граф нитей в одном контексте → раздел 8
A+ — шардинг с реконструкцией графа 2000–10000 2–4 шарда по естественным швам (asmdef/системы), каждый ≤ ~2500 LOC → раздел 9
B — частичный (предупреждение) >10000 или деление неестественное по-шардовый анализ + швы выборочно, дисклеймер → раздел 10

Границы шардов — по швам asmdef/систем, чтобы внутришардовые нити были самодостаточны, а межшардовые рёбра — редкими и явными (проходят через шов). Соотношение размеров max/min ≤ 3, не более 4 групп.


3. Модель анализа: сценарная нить

Единица — логическая нить: путь от точки входа до точки выхода.

Точки входа (нить начинается):

  • ввод: AddActionUser(, performed +=, Unity-input;
  • события/шины: On[A-Z]\w+ +=, OnUpdateData +=, OnGameStateChanged;
  • публичный API, вызываемый другими: методы контроллера, Run(, Execute команд;
  • Unity-lifecycle с логикой: OnEnable/Awake/Update/spine-events.

Точки выхода (нить заканчивается):

  • представление: switcher.Set(, ChangeVisibility(, SetActive(, запуск анимации/spine, image.sprite=;
  • мутация общего состояния: реактив .Set(, запись модели, смена game-state;
  • персист: SaveGlobal, запись сейва;
  • отдача наружу: ?.Invoke(), LoadAndPlay, PlaySound, return.

Протокол выделения:

  1. Инвентарь кандидатов вход/выход — делегируется Haiku (grep сигнатур).
  2. Для каждого входа — трассировка вперёд до выхода(ов): трансформации, ветвления, await, поднятые события, тронутое общее состояние, пересечения границ. Не делегируется — семантика.
  3. Таблица нитей: {id, вход→выход, длина(хопы), ветвление, S, пересечения}.

Единица доказательства — нить, не счётчик: «нить #2 от Run() до LoadAndPlay рвётся на fake-null», а не «1 нарушение».


4. Оси (рубрика 0–3)

Балл по каждой оси — с доказательством файл:метод / id нити.

Ось 1 — Атомарность носителя

Меряет НЕ размер модели (толщину игнорировать — это code-quality 2.2). God-модель, замкнутая в своём домене, — норма. Меряет:

  • Over-fetch: нить получает полновесный агрегат (GetData<FatModel>), а читает/пишет малую часть → минус. Метрика: потреблено_полей / получено_полей. Низкое отношение при толстом носителе = over-fetch (ещё и ложная связность: нить привязана к полям, которых не касается).
  • Right-fetch: нить, которой по существу нужна вся модель (сборщик/сериализатор/оркестратор), тянет её целиком — норма, даже если модель God-типа.
  • Доменная замкнутость: агрегат не течёт за границу своего домена. Вынос в чужой домен = минус (пересекается с Ось 4 boundary — учитывать один раз).

| 3 | аппетит ≈ потреблению у всех нитей; толстые модели тянут только законные потребители; ничего не течёт за домен | | 2 | 1–2 нити over-fetch | | 1 | систематический over-fetch: толстая модель раздаётся как «шина», потребители берут крохи | | 0 | агрегат протекает за домен / все тянут всё ради всего |

Ось 2 — Чистота паттернов в динамике

Держат ли паттерны при повторном входе (replay / close→open / reload):

| 3 | relink, owner-lock реактива, сброс кэша/полей, штатные примитивы Vortex; ничего не течёт на re-entry | | 2 | чисто в покое, 1–2 протечки на re-entry | | 1 | протекают: «сбросили одно поле, забыли соседнее»; relink-кэш не обнуляется; утечки ресурсов (спрайты/подписки/токены/таймеры/IDisposable); велосипеды вместо примитивов | | 0 | паттерн-фасад, в динамике разваливается |

Частые маркеры: DeInit/OnDisable обнуляет не все кэши; relink-кэш источника не сброшен в Unsubscribe; статик-состояние без сброса; Dispose() IDisposable не зовётся; новый ресурс присвоен без Dispose() старого.

Ось 3 — Прочность нити (вход→выход)

| 3 | все нити доходят до корректного выхода на всех входах (границы, null, идемпотентность); деградация явная; путь трассируем | | 2 | границы покрыты, но лог/трассировка местами теряются | | 1 | часть нитей рвётся на штатных входах (null/пусто/replay), сбой глотается молча / тихая порча | | 0 | рвутся на штатных входах, тихая порча общего состояния |

Маркеры: NRE на misconfig-путях (незащищённые OnDisable/GetData), деление на ноль, IndexOutOfRange после инкремента, тихий return вместо fail-loud, метод всегда возвращает одно значение (мёртвая ветвь результата).

Ложная точка расширения в динамике (мутация интерфейса в concrete). Нить получает/декларирует абстракцию (IX — параметр, GetData<IX>(), GetService<IX>(), поле-интерфейс), но в теле приводит её к конкретному типу (as Concrete, (Concrete)x, is Concrete c) и зовёт concrete-only логику. Сценарий отказа: подставили валидную альтернативную реализацию IX (mockup / вариант актёра / тестовый двойник) → as→null→NRE ниже, (Concrete)InvalidCastException, либо тихая потеря поведения. Нить рвётся на штатном «входе» — а входом здесь является какая реализация интерфейса подключена.

  • Отличие от code-quality: там downcast штрафуется структурно (сам факт as Concrete). Здесь — разрыв только при реальном пути подмены реализации. Pre-check (как в code-quality): есть ли у IX иные реализации / mockup / точка инъекции? Нет подмены → латентный смелл, не разрыв (пометить, балл Ось 3 не ронять). Есть → нить рвётся: Ось 3 низко.
  • ДВА ОРТОГОНАЛЬНЫХ ПОД-ЧЕКА (не путать):
    • (a) Тихий разрыв — нить молча возвращает null/no-op при неверном типе (as … ?.). Это Ось 3, и звонкий гард (if(x==null) throw) его СМЯГЧАЕТ (громкий отказ вместо тихого).
    • (b) Ложная точка расширения — компонент загружен через абстракцию (GetService<IX>, AddUI()→IManagedUI, конфиг-префаб, IA-seam), затем as Concrete, и Concrete реализует IB → декларированная расширяемость фиктивна (заместитель обязан БЫТЬ этим concrete). Это Ось 4 (скрытое ×0.6) и гард на неё НЕ влияет: throw лишь меняет режим отказа, а шов остаётся ложным.
  • Не давать «эталон» за звонкий гард. Гард закрывает только (a). Дискриминатор (b) из code-quality: «Есть ли у B интерфейс IB в системе, и загружен ли B через абстракцию?» Да+да → ложная точка (флаг, ×0.6). У B нет интерфейса и это endpoint (Image/Button) → честная связка (ОК).

Ось 5 — Экономность нитей

| 3 | число нитей/ветвлений = задаче; ничего лишнего | | 2 | небольшой избыток (пара спекулятивных веток / мёртвых полей) | | 1 | заметный over/under-engineering нитей; спекулятивные/дублирующие НИТИ (одна логика раскопирована на несколько входов); мёртвые ветви результата на пути нити; поллинг-Update без dirty-check | | 0 | клубок спекулятивных путей / «умные» решения без нужды |

Граница с формой: чисто статическое — закомментированные файлы, мёртвый код, дублирование типов/структур — это форма → code-quality, здесь НЕ считать. Ось 5 меряет избыточность в ДИНАМИКЕ: лишние нити/ветки/поллинг, а не мёртвый текст в покое.


5. Ступенчатость нити (S — счётная характеристика)

Идеал — нить считает в памяти и фиксирует результат один раз на выходе (single-commit). Минус — промежуточные фиксации/коррекции/ожидания на пути.

Подтип Вес Признак
Ожидание-ответа 0 если вынужденное (внешний round-trip: LoadAsync/сеть/ввод); +1 если самонаведённое (ждём свой же отложенный шаг / поллинг вместо готового события)
Промежуточная фиксация +1 пишет наблюдаемый результат (реактив/состояние/вью/сейв) до финального выхода
Коррекция +2 позже перезаписывает/отменяет свой же результат (set A → set !A, откат)

S_i = взвешенная сумма самонаведённых точек нити.

Заряжается через две оси (без нового члена формулы):

  1. Ось 3 (рубрика): S=0→возможно 3; самонаведённое ожидание/фиксация (S≥1)→не выше 2; коррекция→не выше 1.
  2. Ось 4 (эскалация): промежуточная фиксация, чей half-state наблюдаем другими нитями до коррекции, — точка пересечения (tangle); если связь не видна из сигнатур — скрытая (×0.6).

Частый маркер: замена события завершения (AnimationState.Complete, OnStop) на самонаведённый таймер/NextFrame-перепроверку.

Пример двойного заряда (одна точка → две оси, НЕ дубль): нить OnPointerDown пишет State.Set(Pressed) [промежуточная фиксация, +1], а отложенная NextFrame-перепроверка при драге откатывает State.Set(Free) [коррекция, +2]. Заряд идёт в две оси разного смысла:

  • Ось 3 (база) — своя нить негладкая: S≥1 + коррекция → потолок оси 1;
  • Ось 4 (множитель) — если half-state Pressed успевает прочитать нить визуала ДО отката, это наблюдаемая утечка → +1 tangle (скрытый ×0.6, если связь не видна из сигнатур).

Это не двойной счёт: Ось 3 меряет прочность СВОЕЙ нити, Ось 4 — сцепку с ЧУЖОЙ (разный вред). Запрет двойного счёта действует только ВНУТРИ множителя — ту же точку не заряжать в Ось 4 дважды.


6. Ось 4 — Спутанность (множитель)

Насколько нити переплетены. Три канала пересечения:

Канал Признак
Общее состояние/событие нить пишет поле / поднимает событие, которое читает/слушает другая
Время/порядок/async корректность нити B зависит от того, что A отработала раньше (init-order, one-frame, порядок await)
Границы пакета/слоя нить лезет в чужой concrete/внутренности мимо контракта (as Concrete, GetService<Concrete>, прямой static-доступ в обход инжектируемого контракта); сюда же мутация интерфейса в concrete — downcast СВОЕЙ декларированной абстракции (IXConcrete), когда за ним реальный путь подмены реализации
Ложная точка расширения компонент загружен через абстракцию (AddUI()→IManagedUI, GetService<IX>, конфиг-префаб, IA-seam), затем as Concrete (где Concrete реализует IB) → заявленная сменяемость фиктивна. Всегда скрытое ×0.6, гард не смягчает (в code-quality это arch-маркер ×0.5). Отсеивать по дискриминатору: нет IB у B / B — endpoint движка → честная связка, не флаг

Разделение по опасности:

  • скрытое пересечение — не видно из сигнатур входа/выхода обеих нитей (через статик/глобал/общее событие). Тяжёлый класс → ×0.6.
  • явное пересечение — видимая зависимость (переданная ссылка, задокументированный порядок). Лёгкий класс → ×0.9.

НЕ штрафовать как спутанность: чистый fan-out события (один продюсер → N независимых потребителей без взаимного порядка) — это штатная событийная развязка. Owner-locked реактив (поле под owner-ключом: единственный писатель — владелец-контроллер, N независимых читателей) — тот же fan-out, а НЕ общий мутабельный пивот: писать может только владелец, гонки за поле нет → не спутанность. Это санкционированный примитив Vortex, не штрафуется. Штраф Ось 4 — за НЕ-owner-locked общую мутацию: несколько писателей одного поля / гонка за него / порядок записи. Правило: избыточно-но-безопасно → Ось 5; окно рассинхрона / не-owner-locked общий пивот → Ось 4.

Метрики (диагностические, в формулу НЕ входят): N нитей · L длина · B ветвление · X пересечений; сигнальный индекс X/N. Балл двигают счётчики tangle_hidden/tangle_visible (§11), а X/N лишь ориентирует, где клубок:

X/N Клубок
0–0.5 нити почти независимы
0.5–1.5 умеренная связность
>1.5 спутанный клубок — правка одной нити рвёт другие

7. Ярус катастроф — рекурсия (hard-cap)

Класс дефектов, обрушивающих живучесть (hang / stack-overflow / event-storm / фриз кадра). Не множитель, а потолок.

Первый член — возможная неконтролируемая рекурсия / цикл нитей: нить через свой выход/промежуточную фиксацию снова запускает свой вход (прямо или по цепочке A→B→A) без гарантированного стопа.

Подтипы: реактивная петля (X.Set в обработчике X.OnUpdateData), событийная (обработчик поднимает то же событие), осцилляция пары (форс-гейт + реопен), спин-луп (while(cond) await без изменения cond).

НЕ катастрофа (цикл + гарантированный стоп): dedup реактива; re-entry guard (if(_flag)return); монотонный прогресс к терминалу; обычная рекурсия с base-case. Помечать «контролируемо».

Проверка ЛОГИКИ рекурсии (не только наличия base-case). Рекурсия часто — штатный ИНСТРУМЕНТ: обход графа/дерева с прогоном до терминала («до тупика»). Проверять не факт рекурсии, а её логику — на обратном ребре должно быть одно из:

  1. монотонный прогресс к терминалу — спуск к листьям / сжатие фронтира / убывающая мера (глубина, размер остатка);
  2. visited/dedup-гард, завершающий на back-edge (в т.ч. циклические данные).

Есть надёжный (1) или (2) → корректный инструмент, помечать «контролируемо, traversal».

Канонический пример корректной рекурсииSerializeController.SerializeClass (рекурсивный спуск по графу модели): терминал — лист (IsSimpleType → return GetSimple, тот самый «прогон до тупика»); цикл в данных (back-edge) ловит visited-сет (VisitedObjects.Add(model) == false) и завершает fail-loud («Serialization failed from cycled model data»). Оба стопа надёжны → чисто, не катастрофа.

Катастрофа — рекурсия, чья логика НЕ даёт ни монотонного прогресса, ни visited-гарда: обход без пометки посещённого по потенциально циклическим данным, взаимная A→B→A без убывающей меры, спуск без гарантии достижения листа.

Правило: цикл без гарантии стопа → катастрофа; цикл с хрупким стопом → тяжёлый скрытый tangle (×0.6); цикл с надёжным стопом → чисто.

Проверка переиспользует граф нитей: найти циклы → на обратном ребре проверить гарантию стопа (семантика, основной контекст). Особое внимание: spine-event→анимация→контроллер→состояние→вью, Update-петли.


8. Ветка A — моно (≤2000 LOC)

  1. Выделить нити (раздел 3), собрать таблицу.
  2. По каждой нити — оси 1/2/3/5 (раздел 4) + S (раздел 5).
  3. Построить граф нитей → Ось 4 (раздел 6) + проверка рекурсии (раздел 7).
  4. Формула (раздел 11), формат (раздел 12).

9. Ветка A+ — двухфазный шардинг с реконструкцией графа

Наивно резать граф по LOC нельзя — потеряются cross-shard пересечения и циклы. Пер-нитевые оси шардим и усредняем; граф (спутанность + рекурсия) собираем на швах.

9.1 Предложение разбиения

Вывести структуру шардов с LOC и обоснованием границ (естественные швы), проверкой max/min≤3. Дождаться подтверждения (или запустить, оставив возможность сдвинуть швы, если пользователь уже дал «анализируй»).

9.2 Фаза 1 — по шардам, параллельно

Agent model=sonnet, run_in_background:true, по одному на шард. Каждый агент читает ТОЛЬКО свою зону и возвращает:

  1. Внутренние нити с осями (1/2/3/5) и S.

  2. Внутреннюю спутанность и циклы (с вердиктом контролируемо/катастрофа).

  3. Манифест границы (критично для сшивки) — все полуребра за пределы зоны:

    • экспонированные входы: public API / поднимаемые события / реактив наружу — с идентити;
    • выходы наружу: подписки на чужие события, вызовы чужого API/контроллера/хаба, Set в общий реактив, static/GetService в другие зоны — с идентити и каналом. Формат: {идентити, направление, канал, файл:метод}.

    Что есть «идентити» (по нему матчатся полуребра на шве):

    • событие — ОбъявляющийТип.ИмяСобытия;
    • API/метод — Тип.Метод(сигнатура);
    • реактив — конкретный тип ReactiveValue<T> + владелец/поле (напр. PointerModel.ScreenPosition);
    • static/глобал — полное Namespace.Тип.член.

    Матч ребра — совпадение идентити с обеих сторон шва. Если идентити не резолвится однозначно (перегрузки, дженерики, доступ через рефлексию/строковый ключ, событие через обёртку) → ребро помечается unresolved и трактуется как ветка B для ЭТОГО ребра: в вывод идёт дисклеймер «cross-shard связь по <идентити> не подтверждена», а НЕ молчаливый пропуск. Скрытая cross-shard рекурсия/спутанность не должна теряться тихо.

Промпт агента должен содержать полную модель (разделы 3–7), т.к. у агента нет этого контекста.

9.3 Фаза 2 — сборка (основной контекст, не делегируется)

  1. Сшить полуребра по идентити → cross-shard рёбра и cross-shard нити.
  2. На пересобранном графе: Ось 4 (включая межшардовые пересечения) + рекурсия (циклы через швы).
  3. Оси 1/2/3/5 и S — среднее по шардам, с дедупом маркеров по типу (один тип = 1 единица, но перечислить все проявления).

9.4 Сведение

Величина Как сводим
Оси 1/2/3/5, S среднее по шардам + обязательно разброс min–max (усреднение маскирует «где болит»: ядро может быть 3, вью 1)
Ось 4 спутанность на пересобранном графе (не усреднять)
Рекурсия/катастрофа на пересобранном графе (цикл может замкнуться через шов)

10. Ветка B — частичный (>10000 LOC / деление неестественно)

Вывести предупреждение:

⚠️ Объём превышает порог реконструкции графа. Пер-шардовые оси применимы, но cross-shard спутанность и рекурсия могут быть пропущены. Оценка частичная.

Далее: по-шардовый анализ осей + швы выборочно (только явные пивоты), в выводе маркировать «оценка частичная».


11. Итоговая формула

База  = (A1 + A2 + A3 + A5) / 12 × 10          # каждая ось 0–3
Сырой = База × 0.6^tangle_hidden × 0.9^tangle_visible
Итог  = min(Сырой, CAP)

CAP = 1   если есть цикл БЕЗ гарантии стопа (гарантированный hang/overflow)
CAP = 2   если цикл с хрупким/частичным стопом
CAP = 10  (нет потолка) если катастроф нет

Дизамбигуация: проблема, попавшая и в спутанность, и в другую ось, считается один раз — в более тяжёлой (Ось 4 / потолок). Ступенчатость заряжает Ось 3 (база) и, при утечке half-state, Ось 4 (множитель) — но одну и ту же точку не считать дважды в множителе.

Чувствительность

hidden visible Множитель
0 0 1.000
0 1 0.900
0 2 0.810
1 0 0.600
1 1 0.540
1 2 0.486
2 0 0.360

Скрытая спутанность бьёт резко намеренно: невидимые сцепки не чинятся коммитом.


12. Формат вывода

[applied-quality] <target> · Ветка X · <LOC>
⛔ КАТАСТРОФА: <есть/нет>   ← если есть, баннер первой строкой, CAP применён

## Карта нитей
| id | вход → выход | S | пересечения |

## Манифест швов (только A+)   ← сшитые cross-shard рёбра
| идентити | из→в | канал | скрытое/явное |

## Циклы (проверка рекурсии)
| цикл | гарантия стопа | вердикт |

## Оси (рубрика; для A+ — со столбцами по шардам + min–max)
| Ось | балл | доказательство (id нити / файл:метод) |

## Спутанность (Ось 4)
| пересечение | нити | канал | скрытое/явное |

## Итог
- База / множитель (0.6^h·0.9^v) / CAP / Итог / шкала
- Диагностический сигнал: разрыв База↔Итог

Шкала итога (интервалы полуоткрыты — граница уходит в верхнюю корзину)

  • [8–10] — прочный: нити чистые, распутанные.
  • [6–8) — хороший, minor проблемы жизненного цикла.
  • [4–6) — приемлемый, локальные баги на re-entry/границах.
  • [2–4) — проблемный, сложно поддерживать (спутанность/ступенчатость).
  • [0–2) — критический (или сработал потолок катастроф).

Диагностический сигнал (обязателен)

  • Разрыв База↔Итог большой (Итог < 0.6×База): «Низкий Итог при более высокой Базе — над локальными багами есть скрытая спутанность/рекурсия, которую усреднённые оси не ловят. Смотреть швы прицельно.»
  • Разрыв малый: «Оси и Итог близки — спутанности мало, минусы локальны и чинятся коммитами.»

13. Делегирование

  • LOC + инвентарь входов/выходов → Haiku (Explore/general-purpose).
  • Пер-шардовый анализ нитей + манифест границы (A+) → Sonnet-агенты параллельно (run_in_background).
  • Сшивка графа, Ось 4, рекурсия, применение формулы/CAPосновной контекст, не делегируется (семантика).

14. Жёсткие правила

  • Единица доказательства — сценарий отказа на конкретной нити, не абстрактный счётчик. Каждый минус привязан к файл:метод/id нити, иначе n/a.
  • Размер носителя не оценивается (это code-quality). Оценивается over-fetch по нити + доменная замкнутость.
  • Чистота — в динамике: паттерн, чистый в покое, но текущий на re-entry (replay/close→open) — минус. «Держит при повторном входе» — обязательный вопрос.
  • Рекурсия без гарантии стопа = потолок, не множитель. Гарантированный стоп (dedup/guard/прогресс/base-case) снимает катастрофу.
  • Fan-out события ≠ спутанность. Спутанность — общий мутабельный пивот или окно рассинхрона, не чистая событийная развязка.
  • Проверять downcast в ОБЕ стороны. Не только «нить лезет в чужой concrete», но и «нить приводит СВОЮ декларированную абстракцию (IX) к concrete» — ложная точка расширения в динамике: рвётся при подмене реализации. Разрыв засчитывать только при реальном пути подмены (иначе латентный смелл).
  • Гард ≠ проход по ложной точке расширения. Звонкий throw/null-гард закрывает только ТИХИЙ разрыв (Ось 3). Сам ложный шов (абстракция загружена → низведена в concrete, декларированная расширяемость обнулена) остаётся и идёт в Ось 4 (скрытое ×0.6) НЕЗАВИСИМО от гарда. Никогда не выдавать «эталон/done right» за одно лишь наличие гарда на as Concrete.
  • A+: граф пересобирается на швах. Спутанность и рекурсия считаются глобально, не усредняются. Оси — среднее + разброс min–max.
  • Результаты агентов — гипотезы до верификации. Самые тяжёлые находки (катастрофа, деление на ноль, гонка, always-return) при сомнении доверять чтением перед принятием как факт.
  • Не смягчать балл «потому что фикс дорогой». Объём фикса — отдельно.