| name | sdd:audit |
| workflow_step | 8 |
| description | Аудит репозитория: 12-фазный пайплайн на стейт-машине. Проходит по логам, спекам, рулам, существующим ченджам; ищет внутренние противоречия; валидирует каждую норму спеки против реального состояния репозитория (фаза 6); синтезирует и создаёт новые ченджи; предлагает кандидатов на удаление. Никакие источники (спеки, рулы, существующие ченджи) не модифицируются. Запускать вручную, без аргументов.
|
sdd:audit — аудит инвентаря
12 фаз на стейт-машине. Каждая фаза: читает входы → LLM-анализ → пишет лог в .logs/_audit-aggregate/ → state_manager.py --step <name>. При падении повторный запуск продолжает с последней незавершённой фазы.
Preflight
-
Проверь openspec через Bash tool:
which openspec > /dev/null 2>&1 || echo "NOTFOUND"
Если NOTFOUND — остановись с сообщением openspec not found. Install: npm install -g @openspec/cli.
-
Подготовь окружение:
TS=$(date +%Y%m%dT%H%M%S)
mkdir -p ".logs/_audit-aggregate"
mkdir -p ".logs/audit"
Сохрани TS — используется во всех именах лог-файлов этого прогона.
-
Проверь стейт скилла. Скилл хранит свой собственный .sdd-state.yaml в .logs/audit/.sdd-state.yaml. Если файла нет — создай через state.py update:
python3 "${CLAUDE_SKILL_DIR}/../scripts/state.py" update ".logs/audit/.sdd-state.yaml" stage proposed
python3 "${CLAUDE_SKILL_DIR}/../scripts/state.py" update ".logs/audit/.sdd-state.yaml" owner "$(python3 ${CLAUDE_SKILL_DIR}/../scripts/identity.py)"
Если файл есть — прочитай текущий stage:
python3 "${CLAUDE_SKILL_DIR}/../scripts/state.py" read ".logs/audit/.sdd-state.yaml"
Если stage audit-done — выведи «previous audit completed; resetting to proposed» и сбрось stage в proposed через state.py update. Иначе — продолжай с фазы, соответствующей текущему stage:
log-aggregating → выполняй фазу 1 (она прервалась)
spec-grouping → выполняй фазу 2
spec-contradictions → выполняй фазу 3
rule-grouping → выполняй фазу 4
rule-contradictions → выполняй фазу 5
spec-reality-checking → выполняй фазу 6 (она прервалась)
change-analyzing → выполняй фазу 7
synthesizing → выполняй фазу 8
change-merging → выполняй фазу 9
change-mapping → выполняй фазу 10
change-retiring → выполняй фазу 11
reporting → выполняй фазу 12
proposed или contradiction-ok → начни с фазы 1
Правило логирования ошибок фаз
При ошибке любой фазы (скрипт вернул exit ≠ 0, файл не найден, неожиданное состояние) — перед остановкой или переходом дописать в лог-файл текущей фазы секцию ## Errors:
## Errors
- **step:** <имя фазы и шага, где произошла ошибка>
**what:** <текст ошибки или описание отклонения>
**why:** <ожидалось / получено>
**reproduce:** <точная Bash-команда или входные данные>
Если фаза ещё не создала лог-файл — создать минимальный файл с frontmatter и секцией ## Errors по пути .logs/_audit-aggregate/phase-<N>-error-${TS}.md. Запись ошибки выполняется до любых других действий остановки.
Фаза 1 — лог-агрегация
Stage: proposed | contradiction-ok → log-aggregating
-
Перейди в stage:
python3 "${CLAUDE_SKILL_DIR}/../scripts/state_manager.py" --ns sdd --skill audit --step log-aggregate --state-file ".logs/audit/.sdd-state.yaml"
-
Собери список лог-файлов:
find .logs -mindepth 2 -type f -name "*.md" | grep -v "^\.logs/_audit-aggregate/" | grep -v "^\.logs/audit/"
-
Если список пуст — пиши пустой агрегат и переходи к следующей фазе:
echo "# Aggregate (empty — no source logs)" > ".logs/_audit-aggregate/aggregate-${TS}.md"
-
Иначе — прочитай каждый файл через Read tool. Извлеки:
- Требования (Requirement-блоки, MUST/SHALL/SHOULD-формулировки);
- Наблюдения (выявленные проблемы, сделанные решения, decision-gates).
Запиши в .logs/_audit-aggregate/aggregate-${TS}.md:
---
phase: 1
timestamp: <TS>
sources_count: <N>
---
## Requirements extracted
- [<source-file>] <requirement>
- ...
## Observations
- [<source-file>] <observation>
- ...
-
Удали обработанные исходные логи (только после успешной записи агрегата):
rm "<source-file>"
Фаза 2 — группировка спек
Stage: log-aggregating → spec-grouping
-
Перейди в stage:
python3 "${CLAUDE_SKILL_DIR}/../scripts/state_manager.py" --ns sdd --skill audit --step spec-group --state-file ".logs/audit/.sdd-state.yaml"
-
Собери список спек:
find openspec/specs -mindepth 2 -type f -name "spec.md" | grep -v "^openspec/specs/archive/"
-
Read-only прочитай каждую спеку. Спеки не изменять.
-
Сгруппируй по тематическим кластерам. Критерии группировки:
- Общая capability-область (например, всё про state-machine);
- Общий артефакт (всё про
manifest.yaml);
- Общий процесс (всё про SDD workflow).
Каждая спека попадает в одну группу.
-
Запиши результат в .logs/_audit-aggregate/spec-groups-${TS}.md:
---
phase: 2
timestamp: <TS>
total_specs: <N>
total_groups: <K>
---
## Group: <theme>
- <spec-path-1>
- <spec-path-2>
## Group: <theme>
- ...
Фаза 3 — противоречия в спеках
Stage: spec-grouping → spec-contradictions
-
Перейди в stage:
python3 "${CLAUDE_SKILL_DIR}/../scripts/state_manager.py" --ns sdd --skill audit --step spec-contradict --state-file ".logs/audit/.sdd-state.yaml"
-
Прочитай результат фазы 2: .logs/_audit-aggregate/spec-groups-${TS}.md.
-
Для каждой группы — попарно сравни спеки на противоречия:
- Конфликтующее требование: одна спека требует X, другая —
not X для одного subject;
- Дублирующая норма: две спеки описывают одну и ту же норму с расхождением;
- Взаимоисключающие правила: правила обеих спек не могут одновременно соблюдаться.
Спеки не изменять.
-
Запиши в .logs/_audit-aggregate/spec-contradictions-${TS}.md:
---
phase: 3
timestamp: <TS>
pairs_compared: <P>
contradictions_found: <C>
---
## Contradictions
- **Group:** <theme>
- **Pair:** <spec-a> ↔ <spec-b>
- **Type:** conflicting-requirement | duplicate-norm | mutually-exclusive-rules
- **Subject:** <quoted subject>
- **A claims:** <claim>
- **B claims:** <claim>
Если противоречий нет — секция ## Contradictions пустая, файл всё равно создаётся.
Фаза 4 — группировка рулов
Stage: spec-contradictions → rule-grouping
-
Перейди в stage:
python3 "${CLAUDE_SKILL_DIR}/../scripts/state_manager.py" --ns sdd --skill audit --step rule-group --state-file ".logs/audit/.sdd-state.yaml"
-
Собери список рулов:
find .claude/rules -type f -name "*.md" | grep -v "^.claude/rules/index.md"
-
Read-only прочитай каждый рул. Рулы не изменять.
-
Сгруппируй по тематическим кластерам (аналогично фазе 2): по теме (frontend, git, testing, …), по типу нормы (формат коммитов, code style, archetype).
-
Запиши в .logs/_audit-aggregate/rule-groups-${TS}.md в том же формате что и spec-groups.
Фаза 5 — противоречия в рулах
Stage: rule-grouping → rule-contradictions
-
Перейди в stage:
python3 "${CLAUDE_SKILL_DIR}/../scripts/state_manager.py" --ns sdd --skill audit --step rule-contradict --state-file ".logs/audit/.sdd-state.yaml"
-
Попарно сравни рулы внутри каждой группы (аналогично фазе 3).
-
Дополнительно — проверь rules-registry. Прочитай .claude/rules/index.md, найди YAML-блок:
python3 -c "
import re, yaml, os, glob
txt = open('.claude/rules/index.md').read()
m = re.search(r'\`\`\`yaml\n(.*?)\`\`\`', txt, re.DOTALL)
if m:
data = yaml.safe_load(m.group(1)) or {}
rules = data.get('rules', {})
registered = {r['file'] for r in rules.get('always', []) + rules.get('path_scoped', [])}
missing = [f for f in registered if not os.path.exists(f)]
on_disk = set(glob.glob('.claude/rules/**/*.md', recursive=True)) - {'.claude/rules/index.md'}
unregistered = on_disk - registered
print('missing:', missing)
print('unregistered:', sorted(unregistered))
else:
print('no YAML block in index.md')
"
-
Запиши в .logs/_audit-aggregate/rule-contradictions-${TS}.md:
---
phase: 5
timestamp: <TS>
pairs_compared: <P>
contradictions_found: <C>
registry_missing: <N>
registry_unregistered: <M>
---
## Contradictions
- <как в фазе 3>
## Registry issues
- **Missing files** (зарегистрированы в index.md, но нет на диске): <list>
- **Unregistered files** (есть на диске, но нет в index.md): <list>
Фаза 6 — spec-reality-check
Stage: rule-contradictions → spec-reality-checking
-
Перейди в stage:
python3 "${CLAUDE_SKILL_DIR}/../scripts/state_manager.py" --ns sdd --skill audit --step spec-reality-check --state-file ".logs/audit/.sdd-state.yaml"
-
Собери список спек:
find openspec/specs -mindepth 2 -type f -name "spec.md" | grep -v "^openspec/specs/archive/"
Если список пуст — запиши пустой результат и переходи к фазе 7.
-
Для каждой спеки прочитай через Read tool и выполни LLM-extraction: извлеки чек-лист валидации по следующей схеме:
Ты — анализатор OpenSpec-спек. Для каждого блока Requirement и Scenario извлеки
проверяемые сущности (файлы, команды, YAML-ключи) и сформируй JSON-объект:
{
"spec": "<путь к spec.md>",
"checks": [
{
"id": "<req-slug>-<n>",
"type": "file_exists | string_contains | regex_match | yaml_keys_in_glob | yaml_value_equals",
"source": "<Requirement или Scenario title>",
// file_exists: "path": "<путь>"
// string_contains: "path": "<путь>", "needle": "<подстрока>"
// regex_match: "path": "<путь>", "pattern": "<regex>"
// yaml_keys_in_glob: "glob": "<glob-паттерн>", "keys": ["key1","key2"]
// yaml_value_equals: "glob": "<glob>", "key": "<key>", "value": <value>
}
],
"unverifiable": [
{"id": "<id>", "reason": "<почему static assert невозможен>"}
]
}
Примеры из sdd-state-archived-in-sdd/spec.md:
Requirement «_sdd_yaml.py merge-state command exists» →
{"id":"req1-file","type":"file_exists","path":"namespace/sdd/skills/scripts/_sdd_yaml.py","source":"Requirement: _sdd_yaml.py merge-state command exists"}
{"id":"req1-cmd","type":"string_contains","path":"namespace/sdd/skills/scripts/_sdd_yaml.py","needle":"merge-state","source":"..."}
Requirement «archived .sdd.yaml contains stage and last_step_at» →
{"id":"req2-fields","type":"yaml_keys_in_glob","glob":"openspec/changes/archive/**/.sdd.yaml","keys":["stage","last_step_at"],"source":"..."}
Scenario про runtime-поведение «WHEN вызывается команда THEN файл создан» →
unverifiable: {"id":"scen-runtime","reason":"scenario describes runtime behavior, no static post-state to assert"}
-
Накопи все JSON-объекты в массив и запиши в .logs/_audit-aggregate/spec-reality-checklist-${TS}.json.
-
Запусти скрипт-валидатор:
python3 "${CLAUDE_SKILL_DIR}/../scripts/audit_spec_reality.py" validate \
".logs/_audit-aggregate/spec-reality-checklist-${TS}.json" \
--output ".logs/_audit-aggregate/spec-reality-${TS}.md"
Если скрипт вернул exit ≠ 0 — остановись с сообщением ошибки; stage не сдвигается.
-
Запиши в .logs/_audit-aggregate/spec-reality-${TS}.md создаётся скриптом автоматически. Сохрани вывод stdout скрипта (строки passed=, failed=, unverifiable=) для сводки фазы 12.
Фаза 7 — анализ существующих ченджей
Stage: spec-reality-checking → change-analyzing
-
Перейди в stage:
python3 "${CLAUDE_SKILL_DIR}/../scripts/state_manager.py" --ns sdd --skill audit --step change-analyze --state-file ".logs/audit/.sdd-state.yaml"
-
Собери активные ченджи:
find openspec/changes -mindepth 1 -maxdepth 1 -type d | grep -v "^openspec/changes/archive$"
-
Read-only прочитай артефакты каждого ченджа: proposal.md, design.md, tasks.md, test-plan.md, specs/**/spec.md (если есть). Ченджи не изменять.
-
Для каждого ченджа проанализируй:
- Внутренние противоречия: расхождения между proposal/design/tasks (как в
sdd:contradiction);
- Пробелы: задачи без покрытия в спеках, capabilities без задач;
- Дубликаты: ченджи, тематически перекрывающие другой активный чендж.
-
Запиши в .logs/_audit-aggregate/change-analysis-${TS}.md:
---
phase: 7
timestamp: <TS>
changes_analyzed: <N>
issues_found: <I>
---
## Per-change findings
### <change-name>
- **Internal contradictions:** <list or "none">
- **Coverage gaps:** <list or "none">
- **Overlaps with:** <list of other change-names or "none">
Фаза 8 — синтез
Stage: change-analyzing → synthesizing
-
Перейди в stage:
python3 "${CLAUDE_SKILL_DIR}/../scripts/state_manager.py" --ns sdd --skill audit --step synthesize --state-file ".logs/audit/.sdd-state.yaml"
-
Прочитай все накопленные логи:
.logs/_audit-aggregate/aggregate-${TS}.md
.logs/_audit-aggregate/spec-contradictions-${TS}.md
.logs/_audit-aggregate/rule-contradictions-${TS}.md
.logs/_audit-aggregate/change-analysis-${TS}.md
.logs/_audit-aggregate/spec-reality-${TS}.md
-
Сформируй список предлагаемых ченджей. Для каждого источника проблем (противоречие в спеках, противоречие в рулах, gap в существующем чендже, требование из агрегата без покрытия, failed-норма из phase 6):
- Имя ченджа в kebab-case;
- Краткое описание проблемы;
- Какие артефакты затрагивает (specs / rules / changes / skills).
-
Запиши в .logs/_audit-aggregate/proposed-changes-${TS}.md:
---
phase: 8
timestamp: <TS>
proposed_count: <N>
---
## Proposed changes
### <change-name>
- **Source:** spec-contradiction | rule-contradiction | change-gap | requirement-orphan | spec-vs-reality
- **Problem:** <one sentence>
- **Artifacts affected:** <list>
Фаза 9 — объединение и разделение
Stage: synthesizing → change-merging
-
Перейди в stage:
python3 "${CLAUDE_SKILL_DIR}/../scripts/state_manager.py" --ns sdd --skill audit --step change-merge --state-file ".logs/audit/.sdd-state.yaml"
-
Прочитай proposed-changes-${TS}.md.
-
Объединяй ченджи, попадающие в одну тематическую группу (из фаз 2/4) или затрагивающие один и тот же артефакт. Разделяй ченджи, затрагивающие несвязанные области.
-
Запиши в .logs/_audit-aggregate/change-merge-split-${TS}.md:
---
phase: 9
timestamp: <TS>
merged: <M>
split: <S>
final_count: <F>
---
## Merges
- <new-name> ← <name-a>, <name-b>
## Splits
- <original-name> → <new-name-1>, <new-name-2>
## Final list
- <name>: <problem>
Фаза 10 — сопоставление и создание
Stage: change-merging → change-mapping
-
Перейди в stage:
python3 "${CLAUDE_SKILL_DIR}/../scripts/state_manager.py" --ns sdd --skill audit --step change-map --state-file ".logs/audit/.sdd-state.yaml"
-
Прочитай change-merge-split-${TS}.md (финальный список) и openspec/changes/ (read-only).
-
Для каждого предложенного ченджа определи:
covered — уже покрыт существующим ченджем (тематика и название совпадают);
extend — нужно дополнить существующий чендж;
new — создать новый.
-
Создай новые ченджи только для категории new:
openspec new change "<kebab-name>"
После создания: вручную не заполняй proposal.md и т.д. — это работа sdd:propose. Аудит только создаёт каркас и оставляет TODO-маркеры в proposal.md.
-
Запиши в .logs/_audit-aggregate/change-mapping-${TS}.md:
---
phase: 10
timestamp: <TS>
covered: <C>
extend: <E>
new: <N>
---
## Coverage
### <proposed-name> → covered by <existing-change>
### <proposed-name> → extend <existing-change>
### <proposed-name> → new (created)
Фаза 11 — ревизия существующих ченджей
Stage: change-mapping → change-retiring
-
Перейди в stage:
python3 "${CLAUDE_SKILL_DIR}/../scripts/state_manager.py" --ns sdd --skill audit --step change-retire --state-file ".logs/audit/.sdd-state.yaml"
-
Прочитай список существующих ченджей и результаты фаз 3, 5, 10.
-
Для каждого существующего ченджа оцени, попадает ли он в одну из категорий:
- Устаревший: противоречит текущим спекам или рулам;
- Дублирующий: тематически перекрывает другой активный чендж;
- Потерял смысл: проблема, которую он решал, теперь покрыта одним из новых ченджей фазы 9.
-
Запиши в .logs/_audit-aggregate/change-retire-${TS}.md:
---
phase: 11
timestamp: <TS>
retire_candidates: <N>
---
## Retire candidates (recommendation only)
### <change-name>
- **Reason:** stale | duplicate | obsolete-after-phase-9
- **Detail:** <one sentence>
Ченджи не удаляются автоматически. Это только список рекомендаций.
Фаза 12 — отчёт
Stage: change-retiring → reporting → audit-done
-
Перейди в reporting:
python3 "${CLAUDE_SKILL_DIR}/../scripts/state_manager.py" --ns sdd --skill audit --step report --state-file ".logs/audit/.sdd-state.yaml"
-
Запиши технический лог .logs/audit/audit-${TS}.md:
---
skill: sdd:audit
timestamp: <TS>
---
## Phase summaries
- Phase 1: logs aggregated, <N> sources processed
- Phase 2: <K> spec groups
- Phase 3: <C> spec contradictions
- Phase 4: <K> rule groups
- Phase 5: <C> rule contradictions; registry missing=<M>, unregistered=<U>
- Phase 6: spec-reality — passed=<P>, failed=<F>, unverifiable=<U>
- Phase 7: <N> changes analyzed, <I> issues found
- Phase 8: <P> changes proposed
- Phase 9: merged=<M>, split=<S>, final=<F>
- Phase 10: covered=<C>, extend=<E>, new=<N>
- Phase 11: <R> retire candidates
## All phase logs
- .logs/_audit-aggregate/aggregate-<TS>.md
- .logs/_audit-aggregate/spec-groups-<TS>.md
- .logs/_audit-aggregate/spec-contradictions-<TS>.md
- .logs/_audit-aggregate/rule-groups-<TS>.md
- .logs/_audit-aggregate/rule-contradictions-<TS>.md
- .logs/_audit-aggregate/spec-reality-checklist-<TS>.json
- .logs/_audit-aggregate/spec-reality-<TS>.md
- .logs/_audit-aggregate/change-analysis-<TS>.md
- .logs/_audit-aggregate/proposed-changes-<TS>.md
- .logs/_audit-aggregate/change-merge-split-<TS>.md
- .logs/_audit-aggregate/change-mapping-<TS>.md
- .logs/_audit-aggregate/change-retire-<TS>.md
-
Выведи в консоль краткую сводку:
sdd:audit — done
logs processed: <N>
spec contradictions: <C-spec>
rule contradictions: <C-rule>
spec-reality: passed=<P>, failed=<F>, unverifiable=<U>
changes proposed: <P>, merged into final: <F>
new changes created: <N>
retire candidates: <R>
→ Подробный отчёт: .logs/audit/audit-<TS>.md
-
Заверши стейт:
python3 "${CLAUDE_SKILL_DIR}/../scripts/state_manager.py" --ns sdd --skill audit --step done --state-file ".logs/audit/.sdd-state.yaml"
Что скилл НЕ делает
- НЕ изменяет файлы спек, рулов или существующих ченджей.
- НЕ удаляет ченджи автоматически (фаза 11 — только рекомендация).
- НЕ заполняет содержимое новых ченджей (фаза 10 — только
openspec new change).
- НЕ запускается автоматически по триггеру — только ручной вызов.
- НЕ исполняет код и не симулирует Scenario-цепочки (фаза 6 spec-reality-check) — только статический assert по файлам, строкам и YAML-ключам на диске.