Status: advisory. It states what happened and recommends; it decides nothing, amends no frozen section and changes no task state. Relates to: TFW-60 Phase AC — the consumer run its last acceptance criterion names. Payload taken at: tag
v2.0.0-dirty.5=cab7243onD:/projects/research/steps-framework, derived from the tag the operator named,VERSIONat that commit =2.0.0-dirty.5; source.tfw/clean at pin and at the Step 5 recheck. Baselinev2.0.0-dirty.4frominstalled_from. Result in the receiving project: 34 changed paths, uncommitted at the time of writing (the project's rule: no commit without the owner's word); 0 tasks renamed; 1 phase state file authored by hand on the owner's decision.
Разбор полётов: обновление TFW 2.0.0-dirty.4 → 2.0.0-dirty.5¶
Проект:
helpdesk. Дата: 2026-08-30. Исполнитель: сессия Claude Code от имениsaubakirov, режим CL — владелец присутствовал и ответил на один пакет вопросов. Стены — около 25 минут, из них один round-trip к владельцу. Предыдущие отчёты по этому проекту:…third_external_update(0.8.7 → .3) и…helpdesk_dirty3_to_dirty4.
Одной строкой¶
Пять из восьми болей вчерашнего отчёта закрыты и проверены на том же проекте за сутки — и
ровно в этот день ряд field report'ов TFW-60 столкнулся на общем порядковом счётчике: мой
«четвёртый» отчёт был перезаписан чужим «четвёртым» через минуту после записи. Задача про
conflict-resistant shared workspace получила натурное доказательство собственной темы в
своём же каталоге. Ниже — что процедура .5 сделала правильно, где она стала церемонией, и
два дефекта payload, которые получатель увидит первым делом.
Что произошло, в числах¶
| Измерение | .4 (вчера) | .5 (сегодня) |
|---|---|---|
| Файлов изменено в payload | 16 | 24 (новый 1: templates/briefing.md) |
Файлов, отличных от baseline installed_from |
2 | 2 — knowledge_state.yaml ⚫, project_config.yaml 🟡 |
| Ложных срабатываний | 0 | 0 |
| Пин | тег = HEAD (совпадение) | тег → коммит → VERSION; HEAD не читался |
| Вопросов владельцу до первой записи | 0 | 3 + 1 одним сообщением, один round-trip |
| Из них ответов, изменивших конфиг | — | 0 — все три подтвердили объявленное |
skipped: строк на шаге копирования |
— | 2 (ровно два project-owned файла) |
| Ручных слияний | 1 | 1 (конфиг: version, installed_from — 2 ключа) |
| Адаптеров | 6 копий | 6 копий + .agent/rules/tfw.md + блок TFW:CLAUDE вставлен один раз; Codex/Cursor не установлены — пропущены |
| Хитов ретированной лексики вне allowlist | 6 по букве / 0 по духу | 2 живых — adapters/README.md:31,38 ({version}); остальные 7 — инструкции ретирования, по новой формулировке в allowlist |
| Тесты payload | 177 p + 2 p 1 s | 204 p (framework) + 1 FAILED 2 p 1 s (repository) |
--check tasks |
30 задач | 30 задач + 1 информационная строка (HD-30, 2 фазы) |
--check project |
зелёный | зелёный, 2.0.0-dirty.5 |
build.* |
lint, verify; test пропущен | lint, verify, test — 358 passed |
Фазовых status.md написано руками |
0 | 1 (HD-31/phase-a, решение владельца) |
| Переименовано задач | 0 | 0 |
Чеклист обновления (durable-запись, потому что процедура не называет ей места)¶
- Источник:
D:/projects/research/steps-framework,target_ref=v2.0.0-dirty.5,source_head=cab7243737c68528d0a520d2d01e935ca585b022,VERSION=2.0.0-dirty.5✔ - Baseline:
installed_from: D:/…@v2.0.0-dirty.4→ тег достижим ✔ - Step −1:
update.mdпрочитан из.tfw/.upstream/, не из установленного.4✔ - Step 3, ответы владельца (одно сообщение, 2026-08-30):
- handle →
saubakirov(существующийteam/saubakirov.md; ничего не создано) task_containers→[workspace, tasks](подтверждено)build.*→make lint/make test-unit/gen_index --check tasks(подтверждено, пути существуют)- (вне трёх) HD-31
phase-a/безstatus.mdпод живой задачей → написать из шаблона - Классификация: ⚫ 1, 🟡 1, дрейф/идентичных → перезапись 24 ✔
- Step 5: копия с исключениями, напечатано
skipped:×2; recheck тега ✔ - Step 6: 24 копии
cmp✔,.agent/rules/tfw.md= шаблон ✔,CLAUDE.mdблок = шаблон ✔ (программно) - Step 7:
version: 2.0.0-dirty.5,installed_from: [email protected](символьное имя; путь — в этой строке) ✔ - Step 8:
--check index/tasks/project✔; тесты (см. дефект 1); allowlist (см. дефект 2);/tfw-*12 строк ✔;make lint✔make test-unit358 ✔ - Step 8a: брифинг доставлен владельцу последним сообщением сессии ✔
- Step 9:
.tfw/.upstream/,.tfw/.baseline/удалены ✔
Что сработало — и что из вчерашнего закрылось¶
Step −1 исполнен буквально, и это было не бесплатно. Установленный .4 пинил от HEAD;
целевой .5 — от названного тега. Здесь HEAD == тег, так что старая процедура тоже прошла
бы, — то есть исправление проверено на пути, где оно не могло сработать иначе. Честно: Step −1
подтверждён как привычка, не как спасение. Спасением он был бы у пятого потребителя, у которого
источник ушёл вперёд.
Baseline из installed_from — второй день подряд 0 ложных срабатываний. Вчерашняя боль 4
(«победа привязана к машине») закрыта формой {upstream}@{tag}; я записал
[email protected]. Оборотная сторона — в §Что заболело, п. 4.
Копирование с объявленными исключениями напечатало ровно то, что должно. Две строки
skipped:, и правило «a copy that reports nothing skipped … is a failed step» даёт оператору
критерий, который он может проверить глазами за секунду. Вчера я писал этот цикл сам.
Маркерный блок TFW:CLAUDE — вчерашняя боль 8 закрыта. Наш CLAUDE.md маркеров не имел;
по правилу §9 я вставил блок один раз, вынес проектные секции (Project Context, Conduct, Code
Quality Mandate, Execution Modes) наружу и программно сравнил регион между маркерами с
шаблоном — True. Следующий синк — механический. Одна деталь: блок шаблона не содержит строки
про производный индекс workspace/00-INDEX.md, которую наш .4-рендер нёс в порядке загрузки;
я перенёс её в Project Context. Это правильно (маршрут — проектный), но оператор должен это
заметить, шаблон не подскажет.
Антигравити-правило без {version}. Копия = шаблон, cmp вместо подстановки. Вчерашний
.agent/rules/tfw.md был рендером с вписанным «TFW 0.8.5» два релиза назад — теперь такого
класса дрейфа нет.
Фазовое состояние — гейт сказал ровно то, что нужно, и ровно столько. HD-30 (закрытая, без
собственного status.md) — одна информационная строка, exit 0. HD-31 (ONB, живая) — была бы
failure; владелец решил писать. Шаблон с фазовым абзацем сделал это работой на две минуты, и
фраза «The task-level status.md never summarizes it» отличает файл от задачного без открытия
каталога. Хорошо.
Allowlist по новой формулировке — вчерашняя боль 1 закрыта. update.md:82, init.md:125
и цитата в adapters/claude-code/README.md:71 — текст, назначение которого ретировать термин.
Правило теперь бинарно исполнимо на этих семи хитах. Но не на двух других — дефект 2.
build.test запущен. Вчера я его пропустил и записал это; сегодня make test-unit —
358 passed за 15 секунд. Пропуск был экономией, которая ничего не экономила.
Дефекты payload — по тяжести¶
1. -k repository содержит тест, зашитый на корпус задач самого фреймворка¶
test_the_repository_stateless_phases_are_all_informational
E AssertionError: {'HD-30__tickets_filters_creator_route_bus'}
assert named == {"TFW-42__…", "TFW-46__…", "TFW-47__…", "TFW-52__…", "TFW-53__…", "TFW-55__…"}
Тест утверждает, что информационные строки --check tasks называют ровно шесть задач
репозитория фреймворка. На любом получателе он красный по построению. Это противоречит
двум обещаниям подряд: .4 — «Framework tests and repository-state tests are separable.
Receiving projects can run the payload suite without inheriting checks against this
repository's own task corpus» — и migrations/2.0.0.md .5, который велит получателю после
миграции запустить именно -k repository. Два других repository-теста проходят/скипаются на
получателе корректно — значит, разделение задумано, а этот тест в него не вписан.
Цена для пользователя не в одном красном тесте, а в том, чему он учит: документированный
гейт красный на корректном проекте → оператор учится читать «1 failed» как норму. Это
худшее, что может сделать гейт. Исправление — либо третий маркер (-k "repository and not
framework_corpus"), либо тест читает ожидаемый набор из состояния репозитория, а не из
литерала, либо скип по PROJECT_ROOT != framework root.
2. adapters/README.md — живое употребление ретированной инструкции {version}¶
CHANGELOG .5: «The Antigravity and Cursor rule templates no longer require a {version}
substitution … A test refuses {version} in any adapter template». Шаблоны чисты. Но
.tfw/adapters/README.md:31 показывает # TFW {version} как пример, а :38 велит: «When
creating an adapter from a template, replace {version} with the value from tfw.version».
Это не ретирование — это инструкция сделать то, что релиз отменил. Тест её не видит, потому
что README не шаблон. Оператор, который читает README (а он для этого написан), получит
противоречие с tfw-rules.md.template, где подставлять уже нечего.
3. Порядковые имена field report'ов — общий счётчик в задаче про его отсутствие¶
Хронология по mtime и git:
- 11:27 — эта сессия записала
FIELD-REPORT__TFW-60__fourth_external_update.md(helpdesk .3→.4) и правку-указатель в…third_external_update.md. - 11:28 — сессия проекта
innoforce-ai-firstзаписала свой…fourth_external_update.mdпо тому же пути. Мой текст перезаписан. - Коммит
6f0a600«file the fourth external update's report» — чужой сессии — включил мою правку третьего отчёта, о которой она не знала:git statusкаталога стал чистым, и указатель «см. fourth» теперь вёл на чужой документ. - 11:40 — третья сессия (
kaznpu-ai-lab) записала…fifth_external_update.md.
Ни одна сторона не ошиблась: имя fourth было корректным для каждой в момент записи. Это
ровно та модель отказа, которую conventions.md §4 описывает для идентификаторов задач —
«no participant reads a project-wide maximum to learn which identifier is free» — и которую
ряд отчётов TFW-60 нарушает своим соглашением об именах. Мой отчёт восстановлен из контекста
сессии как …helpdesk_dirty3_to_dirty4.md; указатель в третьем исправлен. Рекомендация —
имена без счётчика: FIELD-REPORT__TFW-60__{project}_{from}_to_{to}.md или, по грамматике
самого фреймворка, {YYYYMMDD-HHMMSS}__{project}. И заметка в TFW-60: три отчёта за одно
утро от трёх операторов одного владельца — это уже не гипотетический сценарий.
Что заболело в процедуре¶
1. Три вопроса на втором обновлении за сутки — церемония, а не решение¶
Все три ответа подтвердили то, что проект уже объявлял: один профиль в team/, вчерашний
[workspace, tasks] с комментарием владельца в конфиге, build.*, проверенный вчера. Я
поставил объявленные значения первыми с пометкой «Recommended», владелец выбрал их за один
проход — процедура это допускает, и round-trip стоил минуту. Но формулировка «asked, never
inferred» не различает проект без объявленного значения (вопрос обязателен) и проект с
объявленным (достаточно подтверждения). Четвёртый вопрос — про HD-31 — был единственным, где
владелец принимал решение, и его процедура не предусматривает: он возник из --check tasks.
Предложение: Step 3 формулировать как «present the declared value and ask for confirmation;
ask open-ended only where none is declared», и явно разрешить добавлять к пакету решения,
которые гейты подняли по ходу read-only шагов.
2. Чеклист — теперь три вещи, которые надо в него записать, и по-прежнему нет места¶
.5 добавил: «record the answers in the checklist», «the checklist records that the briefing
was delivered», «for a local checkout … the path in the checklist». Вчерашняя боль 3 не
закрыта, а усилена: артефакт стал обязательным носителем трёх фактов и не имеет пути.
Я поместил его в этот отчёт (§Чеклист) и в сообщение коммита — оба места проект-специфичны.
Одна строка «the checklist lives at {first-container}/UPDATE-{target}.md» или «…in the
commit that records the update» закроет это окончательно.
3. Брифинг: «no free text» против «one bullet per Changed item»¶
Шаблон briefing.md бинден к CHANGELOG: одна пуля на пулю, без свободного текста. Changed
в .5 — восемь пунктов, из которых шесть про саму процедуру обновления (пин от тега, Step −1,
маркеры, allowlist, форма installed_from, длина update.md). Для владельца helpdesk честный
ответ на «что вы теперь делаете иначе» по этим шести — «ничего: это делает оператор
обновления». Шаблон не разрешает ни пропустить пулю, ни сказать это словами. Я выполнил
правило — каждая пуля есть — и сгруппировал их так, чтобы владелец увидел два пункта, которые
касаются его (аббревиатура = инициалы; фазовый status.md), первыми. Предложение: разрешить
в блоке одну фиксированную фразу «applies to the update operator, not to daily work» как
допустимую «пулю-обёртку» для процедурных изменений.
4. scope_budgets — второй день подряд тихое изменение¶
Вчера маркер FRAMEWORK → PROJECT без строки в CHANGELOG. Сегодня значения по умолчанию
в шаблоне и таблица в conventions.md §5: 30/15/3000/30 → 50/50/5000/50 — и снова ни слова в
.5. Проектные 35/18/3500/26 сохранены (маркер PROJECT теперь на моей стороне), но владелец,
читающий конвенцию, увидит цифры, которых у него нет, и не узнает из CHANGELOG, что они
изменились. Ключ, помеченный «preserve on update», всё равно заслуживает строки в ### Changed,
когда меняется его умолчание.
5. installed_from — символьное имя, которое ни на что не указывает¶
[email protected] переживёт вторую машину — и на ней не разрешится ни во
что: tfw.upstream смотрит на GitHub без dirty-тегов, а символ steps-framework нигде не
определён. Процедура честно велит записать путь «в чеклисте» (см. боль 2). Для следующего
обновления это означает fallback baseline с объявленной неопределённостью — то, что .4
устранил. Не дефект .5; следствие неопубликованной линии тегов. Но пока dirty-теги не в
remote, installed_from в этой форме — обещание, которое проверить может только та машина,
что его дала.
Что не трогалось и почему¶
- Codex (
.agents/) и Cursor (.cursor/) не установлены — строки Step 6 пропущены, каталоги не созданы.AGENTS.mdбез блокаTFW:CODEX— по правилу «report and leave»: отмечено, не тронуто. - Ни одна задача, ни одно событие, ни один мигрированный
status.mdне переименованы и не переписаны. Индекс изменился на одну строку (HD-31 phase-a получила состояние). knowledge_state.yaml,knowledge/,KNOWLEDGE.md,TECH_DEBT.md— не открывались.- HL существующих задач не получили
Title/Abbreviation— поля для новых задач. - Не проверен негативный путь
--check projectнаinstalled_fromсD:/…: я сменил форму до запуска гейта. Утверждение CHANGELOG «reports a drive letter» принято на веру.
Рекомендации — по цене исправления¶
| # | Действие | Цена | Закрывает |
|---|---|---|---|
| 1 | Скипать/перемаркировать test_the_repository_stateless_phases_are_all_informational вне корня фреймворка |
3 строки | Дефект 1 |
| 2 | adapters/README.md: убрать пример # TFW {version} и инструкцию подстановки; расширить тест с шаблонов на README |
2 строки + 1 тест | Дефект 2 |
| 3 | Имена field report'ов без порядкового счётчика; заметка в TFW-60 о коллизии 11:27/11:28 | переименование | Дефект 3 |
| 4 | Step 3: «present declared value, confirm; ask open only where undeclared» + разрешить добавлять решения от гейтов | 3 строки | Боль 1 |
| 5 | Назвать путь чеклиста | 1 строка | Боль 2 (второй день) |
| 6 | briefing.md: допустимая фраза-обёртка для процедурных Changed |
2 строки | Боль 3 |
| 7 | CHANGELOG .5 ### Changed: строка о scope_budgets 30/15/3000/30 → 50/50/5000/50 |
1 строка | Боль 4 |
| 8 | Опубликовать dirty-линию в приватный remote, чтобы installed_from разрешался |
инфраструктура | Боль 5 |
Итог с точки зрения пользователя TFW¶
Удобно: да — вчерашние восемь болей за сутки стали тремя; всё, что процедура обещала
измерить, она измерила и напечатала. Понятно: да, кроме чеклиста, который стал обязательнее и
остался бездомным. Прозрачно: да — skipped:, note:, not checked: дают оператору три
разных слоя честности, и ни один не притворяется другим. Критика: два дефекта payload
получатель увидит в первые пять минут (красный repository-тест, {version} в README), и оба
учат тому, чему фреймворк учить не хочет — читать красное как норму и инструкцию как
устаревшую. И один урок вне payload: ряд отчётов о conflict-resistant workspace потерял отчёт
на конфликте имён.