Skip to content

[Bug] create-wp.sh расходится с WP-434 и создаёт archive stub #431

Description

@den317

Description

В жизненном цикле создания и закрытия рабочего продукта (РП) одновременно действуют несколько несовместимых контрактов:

  1. .claude/skills/wp-new/SKILL.md объявляет, что новый РП атомарно записывается в четыре локальных места.
  2. Этот же скилл задаёт папочный контекст inbox/WP-{N}/WP-{N}.md.
  3. Скилл прямо утверждает, что архивная заготовка больше не создаётся при регистрации.
  4. scripts/create-wp.sh продолжает создавать padded-путь WP-009 и отдельный archive stub.
  5. memory/protocol-close.md требует перемещать исходную папку контекста из inbox/ в archive/wp-contexts/.
  6. scripts/close-wp.sh вместо перемещения исходной папки создаёт ещё один плоский архивный файл.

В результате создание и закрытие РП могут породить несколько представлений одного контекста, а create-wp.sh может сообщить об успешной атомарной регистрации, даже если WeekPlan или active-wp.md фактически не обновлены.

Проблема состоит не только в archive stub. Нужно согласовать и проверить весь контракт:

create
  → context
  → Registry
  → WeekPlan
  → active-wp
  → close
  → archive

Contract Drift

1. Документированный context path не совпадает с фактическим

/wp-new объявляет:

inbox/WP-{N}/WP-{N}.md

При следующем номере 9 ожидаемый по буквальному контракту путь:

inbox/WP-9/WP-9.md

Но create-wp.sh использует:

WP_ID=$(printf '%03d' "$WP_NUM")

и создаёт:

inbox/WP-009/WP-009.md

При этом разные представления одного идентификатора используют разные форматы:

  • frontmatter: wp: 9;
  • consent file: wp-consent-9;
  • Registry/WeekPlan: обычно 9 или WP-9;
  • путь и H1: WP-009.

Само дополнение нулями может быть допустимым архитектурным решением, но оно должно быть единым и явно закреплённым во всех потребителях. Сейчас документация, пути и downstream-скрипты используют разные формы.

2. create-wp.sh создаёт archive stub, который документация объявляет удалённым

/wp-new говорит:

Archive stub больше не создаётся при регистрации. close-wp.sh создаёт архивный файл при закрытии, чтобы не конфликтовать с каноническим git mv.

Фактический create-wp.sh продолжает выполнять отдельный шаг:

2/6 archive stub

и создаёт:

archive/wp-contexts/WP-009-<slug>.md

до того, как РП завершён.

Таким образом, сразу после регистрации существуют:

inbox/WP-009/WP-009.md
archive/wp-contexts/WP-009-<slug>.md

Оба файла относятся к одному РП, но имеют разные роли, статусы и lifecycle.

3. close-wp.sh создаёт ещё один архивный объект вместо переноса исходного контекста

memory/protocol-close.md задаёт каноническое закрытие:

git mv inbox/WP-N archive/wp-contexts/WP-N

То есть исходная папка с полным контекстом должна быть перенесена в архив.

Но close-wp.sh создаёт новый плоский файл:

archive/wp-contexts/WP-N-<slug>.md

и дописывает в него секцию закрытия.

При этом исходный папочный контекст может:

  • остаться в inbox/;
  • быть отдельно перенесён другим протоколом;
  • получить статус, отличный от плоского archive-файла;
  • существовать одновременно с archive stub, созданным при регистрации.

Возможный итог:

inbox/WP-9/WP-9.md
archive/wp-contexts/WP-009-<slug>.md
archive/wp-contexts/WP-9-<другой-slug>.md
archive/wp-contexts/WP-9/WP-9.md

Даже если не все четыре объекта возникают в каждом запуске, текущие контракты допускают их появление.

4. Ошибка записи WeekPlan не всегда нарушает успешный результат создания

create-wp.sh позиционируется как атомарный writer.

Но если скрипт не распознаёт таблицу WeekPlan, внутренний Python-блок печатает предупреждение:

WeekPlan: таблица недели не найдена — добавить вручную

и завершается с успешным кодом.

Shell-обёртка воспринимает шаг как успешно выполненный и продолжает создание РП.

В результате возможен фактический итог:

  • context file создан;
  • Registry обновлён;
  • WeekPlan не обновлён;
  • скрипт сообщает WP создан.

Это не атомарная запись в четыре места.

Проблема воспроизводится на легитимной пользовательской схеме WeekPlan, например:

| Приоритет | Режим ТВС | РП / направление | Бюджет | Hard cap | Срок | Результат |

Такая таблица содержит семантику РП, но не содержит буквальную колонку Статус, которую требует текущий writer.

5. Ошибка пересборки active-wp.md не блокирует успешное создание

Если build-active-wp.py:

  • отсутствует;
  • завершился с ошибкой;
  • не смог распознать Registry;

create-wp.sh печатает предупреждение, но продолжает и сообщает успешное создание РП.

Однако /wp-new включает пересборку active-wp.md в обязательные четыре локальные записи.

Следовательно, нужно выбрать один контракт:

  1. active-wp.md — обязательная часть атомарной транзакции, тогда ошибка должна приводить к rollback;
  2. active-wp.md — производный необязательный индекс, тогда документация не должна называть его обязательным локальным местом атомарной регистрации.

Сейчас документация и реализация расходятся.

6. create-wp.sh может самовольно менять схему WP Registry

На legacy Registry без колонок P, Репо или Бюджет writer может автоматически расширить таблицу:

  • добавить новые колонки;
  • дописать во все существующие строки;
  • создать большой несвязанный diff.

Это изменение схемы не является частью создания конкретного РП и может затронуть весь исторический реестр.

Даже если миграция нужна, она должна быть:

  • отдельной явной операцией;
  • покрыта тестом;
  • описана в migration contract;
  • либо включаться только после явного schema version check.

Создание одного РП не должно молча переписывать структуру всего Registry.

7. Тесты закрепляют конфликтующие контракты

Часть тестов ожидает:

  • padded-пути WP-009;
  • archive stub уже после регистрации;
  • плоский архивный файл.

При этом /wp-new и protocol-close.md описывают:

  • WP-{N};
  • отсутствие archive stub;
  • перенос папки при закрытии.

Получается, что тесты доказывают корректность поведения, которое документация одновременно объявляет устаревшим.

Impact

Несколько источников истины

Контекст выполнения может остаться в inbox/, а сведения о закрытии — попасть в отдельный archive-файл. Непонятно, какой файл должен читать следующий агент.

Потеря полного контекста при закрытии

Плоский файл, созданный close-wp.sh, может содержать только краткую секцию закрытия, тогда как полный журнал фаз, решений и handoff остаётся в исходной папке.

Ложная атомарность

Агент получает сообщение об успешном создании РП, хотя WeekPlan или active-wp.md не обновлены.

Ошибки downstream-аудитов

Детекторы могут искать только одну из схем:

archive/wp-contexts/WP-N.md
archive/wp-contexts/WP-N-<slug>.md
archive/wp-contexts/WP-N/WP-N.md

и переставать видеть РП после его закрытия.

Шумный Registry diff

Создание одного РП может неожиданно изменить десятки исторических строк из-за автоматической миграции колонок.

Нестабильный WP-ID

Один РП фигурирует как 9, WP-9 и WP-009, что усложняет точный поиск, связывание с внешними системами и предотвращение дубликатов.

Expected Behavior

Нужен один явный lifecycle-контракт.

Рекомендуемый вариант:

Регистрация

После успешного создания существуют ровно четыре согласованных локальных представления:

inbox/WP-N/WP-N.md
docs/WP-REGISTRY.md
current/WeekPlan ...
current/active-wp.md

Архивного контекста до закрытия нет.

Закрытие

При закрытии:

  1. context получает содержательный факт приёмки;

  2. статус меняется на terminal;

  3. папка целиком перемещается:

    git mv inbox/WP-N archive/wp-contexts/WP-N
  4. Registry и WeekPlan обновляются;

  5. active-wp.md пересобирается;

  6. после закрытия существует ровно один канонический архивный контекст:

    archive/wp-contexts/WP-N/WP-N.md
    

Legacy-плоские файлы можно продолжать читать, но новые РП не должны их создавать.

Atomicity

Создание должно закончиться одним из двух результатов:

SUCCESS: все обязательные локальные представления обновлены
FAIL: все частичные изменения откачены

Предупреждение без rollback допустимо только для явно необязательного пост-шага, например внешнего трекера.

Open Design Decision: WP-ID Format

Перед исправлением нужно явно решить, какой формат является каноническим.

Вариант A — без дополнения нулями

WP-9
WP-34
WP-123

Преимущества:

  • совпадает с пользовательским названием РП;
  • совпадает с wp: 9;
  • совпадает с большинством текстовых ссылок;
  • соответствует буквальному WP-{N} в документации.

Недостаток:

  • лексикографическая сортировка файлов отличается от числовой.

Вариант B — дополнение нулями только в файловом ID

WP-009
WP-034
WP-123

Если выбирается этот вариант, необходимо явно обновить:

  • /wp-new;
  • WP-434 / INBOX-CONVENTION;
  • protocol-open.md;
  • protocol-close.md;
  • close-wp.sh;
  • archive-done-wp.sh;
  • Registry readers;
  • WeekPlan readers;
  • внешние tracker adapters;
  • тестовые фикстуры.

Недопустимо оставлять формат неявным и одновременно поддерживать разные формы без нормализующего слоя.

Steps to Reproduce

Reproduction A — padded path и archive stub

  1. Подготовить установку, где максимальный номер в Registry меньше 9.

  2. Запустить:

    bash scripts/create-wp.sh \
      --title "Lifecycle contract test" \
      --budget 2h \
      --priority P3 \
      --no-consent-check
  3. Проверить:

    find inbox archive/wp-contexts \
      -maxdepth 2 \
      \( -name 'WP-009*' -o -name 'WP-9*' \) \
      -print
  4. Наблюдаемый результат:

    inbox/WP-009/WP-009.md
    archive/wp-contexts/WP-009-lifecycle-contract-test.md
    
  5. Сравнить с /wp-new, который обещает inbox/WP-{N}/WP-{N}.md и отсутствие stub.

Reproduction B — WeekPlan не обновлён, но создание успешно

  1. Использовать WeekPlan с легитимной таблицей:

    | Приоритет | Режим ТВС | РП / направление | Бюджет | Hard cap | Срок | Результат |
    |---|---|---|---:|---:|---|---|
  2. Запустить create-wp.sh.

  3. Получить предупреждение:

    WeekPlan: таблица недели не найдена — добавить вручную
    
  4. Проверить код завершения и итоговое сообщение.

  5. Наблюдаемый результат: создание считается успешным, хотя обязательная запись WeekPlan отсутствует.

Reproduction C — закрытие создаёт второй архивный объект

  1. Создать РП.

  2. Закрыть его через close-wp.sh.

  3. Затем выполнить канонический git mv из protocol-close.md.

  4. Проверить:

    find archive/wp-contexts -maxdepth 2 -name 'WP-9*' -print
  5. Возможный результат: плоский closure-файл и папочный полный контекст существуют одновременно.

Suggested Fix

Phase 1 — зафиксировать контракт

  1. Выбрать канонический формат WP-ID.
  2. Выбрать единственную новую archive-схему.
  3. Зафиксировать, является ли active-wp.md обязательной частью атомарной записи.
  4. Зафиксировать, может ли создание РП выполнять schema migration Registry.

Phase 2 — исправить создание

  1. Привести create-wp.sh к выбранному WP-ID.
  2. Удалить создание archive stub.
  3. Считать отсутствие записи WeekPlan ошибкой обязательного шага.
  4. Либо успешно пересобирать active-wp.md, либо выполнять rollback.
  5. Не мигрировать Registry молча в ходе обычного create.
  6. Сохранять внешний tracker как отдельный необязательный пост-шаг после локальной транзакции.

Phase 3 — исправить закрытие

  1. Использовать исходный context как единственный объект закрытия.
  2. Добавлять секцию ## Закрытие в исходный файл.
  3. Обновлять terminal status и дату.
  4. Перемещать исходную папку в archive/wp-contexts/WP-N/.
  5. Не создавать параллельный плоский closure-файл для новых РП.
  6. Сохранить read-only совместимость с legacy-плоскими архивами.

Phase 4 — обновить consumers

Проверить и согласовать:

  • build-active-wp.py;
  • close-wp.sh;
  • archive-done-wp.sh;
  • memory-drift-scan.py;
  • Day Open;
  • Day Close;
  • Week Close;
  • WP sync bundle;
  • session guard;
  • внешние tracker adapters;
  • навигационные и индексные скрипты.

Phase 5 — добавить тесты

Минимальный набор:

Create contract

create → context + Registry + WeekPlan + active-wp

No premature archive

create → archive/wp-contexts не изменился

Atomic rollback

ошибка WeekPlan → нет context, Registry row и других частичных записей

Active index failure

ошибка build-active-wp → поведение соответствует объявленному контракту

Close contract

create → close → один context перемещён в archive

No duplicate archive

после close нет второго плоского closure-файла

Legacy compatibility

старые плоские архивы читаются, но новые не создаются

ID normalization

9 / WP-9 / WP-009 однозначно приводятся к выбранной канонической форме

Custom WeekPlan schema

легитимная таблица «РП / направление» обрабатывается либо явно блокируется до любых записей

Registry schema

создание одного РП не меняет схему всего Registry без явной миграции

Acceptance Criteria

  • Документация и код используют один канонический WP-ID.
  • После регистрации нет archive stub.
  • После регистрации обновлены все обязательные локальные представления.
  • Ошибка любого обязательного шага приводит к полному rollback.
  • Создание одного РП не мигрирует Registry неявно.
  • Закрытие использует исходный context, а не создаёт конкурирующий объект.
  • После закрытия существует ровно один новый канонический архивный контекст.
  • Legacy-архивы остаются читаемыми.
  • Все downstream consumers поддерживают выбранную схему.
  • End-to-end тест create → close → archive проходит.
  • Тест подтверждает отсутствие дубликатов и осиротевших stub-файлов.
  • Поведение active-wp.md совпадает с заявленной атомарностью.

Environment

  • OS: Linux
  • Surface: Codex Cloud
  • Setup mode: core/cloud
  • Install level: T1
  • Checked against: upstream main, 2026-08-13

Evidence

Upstream /wp-new объявляет:

inbox/WP-{N}/WP-{N}.md

и утверждает, что archive stub больше не создаётся.

Upstream create-wp.sh одновременно содержит:

WP_ID=$(printf '%03d' "$WP_NUM")
ARCHIVE_STUB="$ARCHIVE_DIR/WP-${WP_ID}-${SLUG}.md"

и выполняет отдельный шаг:

2/6 archive stub

Upstream close-wp.sh утверждает, что stub уже удалён из create, но создаёт новый плоский archive-файл.

Upstream protocol-close.md требует вместо этого перемещать папку исходного context.

Таким образом, расхождение подтверждается непосредственно текущими платформенными файлами и не зависит от конкретной пользовательской установки.

Related

  • Follow-up architecture proposal: подключаемый адаптер внешнего трекера.
  • Этот bug следует исправлять отдельно от выбора Linear, GitHub Issues или другого внешнего трекера: проблема находится в базовом lifecycle РП.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions