From f086b6690684d9e597beea31d83bc063ec933980 Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Sun, 11 Oct 2026 21:37:11 +0400 Subject: [PATCH] =?UTF-8?q?docs(plan):=20=D1=80=D0=BE=D0=BB=D0=B8=20=D0=B8?= =?UTF-8?q?=20=D0=BF=D0=B0=D0=BC=D1=8F=D1=82=D1=8C,=20=D1=87=D0=B0=D1=81?= =?UTF-8?q?=D1=82=D1=8C=201=20=E2=80=94=20=D0=BE=D0=B1=D0=B2=D1=8F=D0=B7?= =?UTF-8?q?=D0=BA=D0=B0=20(10=20=D0=B7=D0=B0=D0=B4=D0=B0=D1=87)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs #2 Co-Authored-By: Claude Fable 5.1 --- .../plans/2026-10-11-roles-and-memory.md | 1573 +++++++++++++++++ 1 file changed, 1573 insertions(+) create mode 100644 docs/superpowers/plans/2026-10-11-roles-and-memory.md diff --git a/docs/superpowers/plans/2026-10-11-roles-and-memory.md b/docs/superpowers/plans/2026-10-11-roles-and-memory.md new file mode 100644 index 0000000..bd9ffec --- /dev/null +++ b/docs/superpowers/plans/2026-10-11-roles-and-memory.md @@ -0,0 +1,1573 @@ +# Роли и память (часть 1, обвязка) — план реализации + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Обвязка claude-harness получает слой ролей: общие файлы ролей, skill `/role`, агент `briefing`, хук `role-hint`, скрипт `link-roles.sh`, шаблоны workspace и репозитория направления, установка и диагностика, глобальный CLAUDE.md ≤ 3 КБ. + +**Architecture:** Всё новое — bash и markdown в пакете, ставится в `~/.claude` через `install.sh`, как уже стоят хуки и `harness-doctor`. Файлы ролей не загружаются автоматически: их читает skill `/role` (общий слой из `~/.claude/roles/`, проектный из `/.claude/roles/`). Хук `role-hint.sh` только подсказывает роль по `session_title`, ничего не блокирует. Новый набор тестов `tests/run.sh` (bash, без зависимостей) проверяет источники в репозитории; `doctor.sh` проверяет установленное состояние. + +**Tech Stack:** bash (`set -uo pipefail`), встроенный `python3` для JSON, markdown. Claude Code 2.1.296: SessionStart-хуки получают `session_title`; агенты — frontmatter `name/description/model/tools/memory`; skills — `argument-hint`, `user-invocable`, `$ARGUMENTS`/`$0`. + +**Spec:** `docs/superpowers/specs/2026-10-11-roles-and-memory-design.md` (утверждена владельцем 11.10.2026). Этот план покрывает раздел 8 спеки и проверки 2 и 5 раздела 9, плюс проверку 1 (память через симлинк) как первую задачу. Разделы 3.3, 6.2, 6.3, 7.3 и остальные проверки раздела 9 — это работа в workspace `awp-ca`, отдельный план там после merge этой ветки. + +**Ветка:** `feat/2-roles-and-memory`, worktree `/Users/andrey.epifancev/projects/cc-skills/claude-harness/.wt/roles`. Issue Gitea #2. Все пути в шагах — относительно корня worktree, если не сказано иное. Коммиты с trailer `Co-Authored-By: Claude Fable 5.1 `; ревьюер задачи проверяет trailer. + +## Global Constraints + +- Хуки `guard-main.sh`, `require-plan.sh`, `harness-check.sh` не меняются (спека, разделы 1 и 8). +- Глобальный `global/CLAUDE.md` ≤ 3 КБ (3072 байта); шаблон CLAUDE.md workspace ≤ 5 КБ (5120 байт); индекс auto-memory ≤ 3 КБ — последнее проверяет только `doctor.sh`, чистка памяти в части 2. +- Ни одно правило из нынешнего `global/CLAUDE.md` (9155 байт) не теряется: у каждого абзаца есть новый адрес, таблица адресов — в `docs/retro/2026-10-11-claude-md-addresses.md` и в описании PR. +- Формат файла роли одинаков для обоих слоёв (спека 3.1): строка 1 `# Роль: — <описание>`, строка 2 `Эта роль заменяет предыдущую роль сессии целиком.`, затем ровно четыре раздела второго уровня в порядке `## Процесс`, `## Читать на старте`, `## Не делать`, `## Делегировать`. Внутри разделов допускаются `###`. +- Имена ролей: `dev`, `admin`, `analyst`, `qa`. Других нет. +- Агенты: модель указывается явно (`model: sonnet`). +- Все новые скрипты: `#!/usr/bin/env bash`, `set -uo pipefail` (хук) или `set -euo pipefail` (скрипты), русские сообщения, идемпотентность, печать того, что сделано. +- Хук `role-hint.sh` никогда не блокирует: любой сбой разбора → `exit 0` без вывода. +- Язык текстов — русский. Caveman не включать. +- Установленные копии в `~/.claude` по месту не правятся; тесты в `tests/` работают с источниками, ставя `install.sh` во временный `CLAUDE_CONFIG_DIR`. + +## Review Focus + +1. Заголовок сессии с пробелами в начале, в верхнем регистре или с префиксом алиаса (`" Admin: спринт"`, `"cc-qa прогон"`) — владелец ждёт подсказку роли, а не тишину. Тест в Task 2. +2. `link-roles.sh` повторно и при уже существующем обычном файле на месте ссылки (`.claude/roles/admin.md` — файл, не симлинк) — второй запуск ничего не ломает, чужой файл не затирается, печатается предупреждение. Тест в Task 6. +3. `role-hint.sh` на пустом stdin, не-JSON и JSON без `session_title` — молчит и `exit 0`, сессия стартует. Тест в Task 2. +4. `install.sh` дважды подряд — `role-hint.sh` зарегистрирован в `settings.json` ровно один раз, `harness-check.sh` не задвоился. Тест в Task 9. +5. Таблица репозиториев в CLAUDE.md workspace с лишними пробелами, каталогом без завершающего `/`, строкой без роли (`source/`, роль «—») — `link-roles.sh` берёт только строки с ролью `admin|analyst|qa`, не падает на остальных. Тест в Task 6. + +--- + +### Task 1: Проверка памяти агента через симлинк (spike, результат — запись в спеке) + +Открытый вопрос спеки (раздел 11, проверка 1 раздела 9): пишет ли агент с `memory: project` через симлинк каталога `.claude/agent-memory/` в репозиторий направления. Ответ определяет, нужен ли запасной вариант 6.5 в части 2. Код не остаётся; остаётся запись в спеке. + +**Files:** +- Modify: `docs/superpowers/specs/2026-10-11-roles-and-memory-design.md` (раздел 11 «Открытые вопросы», первый пункт) + +**Interfaces:** +- Consumes: ничего. +- Produces: факт «симлинк работает / не работает» и путь, куда реально легла память. + +- [ ] **Step 1: Собрать песочницу в scratchpad (не в репозитории)** + +```bash +SB=$(mktemp -d /tmp/memprobe.XXXX); echo "$SB" +mkdir -p "$SB/ws/.claude/agents" "$SB/ws/.claude/agent-memory" "$SB/dir/.claude/agents" "$SB/dir/.claude/agent-memory/probe" +cat > "$SB/dir/.claude/agents/probe.md" <<'EOF' +--- +name: probe +description: Проверка памяти через симлинк. Использовать только по явной просьбе. +model: haiku +tools: Read, Write, Edit +memory: project +--- +Ты проверяешь память. Выполни задание из промпта буквально и ответь одной строкой. +EOF +ln -s ../../../dir/.claude/agents/probe.md "$SB/ws/.claude/agents/probe.md" +ln -s ../../../dir/.claude/agent-memory/probe "$SB/ws/.claude/agent-memory/probe" +ls -la "$SB/ws/.claude/agents" "$SB/ws/.claude/agent-memory" +``` + +Ожидается: два симлинка, оба разрешаются (`readlink -f` даёт путь внутри `$SB/dir`). + +- [ ] **Step 2: Запустить агента из корня песочницы и попросить записать память** + +```bash +cd "$SB/ws" && claude -p --agent probe "Запиши в свою память (MEMORY.md) строку «probe-ok 2026-10-11» и ответь «готово»." 2>&1 | tail -5 +``` + +Если `--agent probe` не находит агента (сообщение «unknown agent»), повторить с `claude -p "Используй агента probe: запиши в его память строку «probe-ok 2026-10-11»"`. + +- [ ] **Step 3: Проверить, куда легла память** + +```bash +echo '--- через симлинк'; find "$SB/dir/.claude/agent-memory" -type f -exec sh -c 'echo "$1"; cat "$1"' _ {} \; +echo '--- мимо симлинка'; find "$SB/ws/.claude" -path '*agent-memory*' -type f -not -path "$SB/ws/.claude/agent-memory/probe/*" +echo '--- auto-memory проекта'; ls -d "$HOME/.claude/projects/"*memprobe* 2>/dev/null +``` + +Ожидается один из трёх исходов: +- A. Файл `MEMORY.md` с `probe-ok` лежит в `$SB/dir/.claude/agent-memory/probe/` — симлинк работает. +- B. Файл лежит в `$SB/ws/.claude/agent-memory/probe/` как обычный каталог (симлинк заменён) или в другом месте — симлинк не работает, нужен запасной вариант 6.5. +- C. Агент не записал ничего (память выключена или агент не вызвался) — повторить Step 2 с `/memory` в интерактивной сессии; если и так ничего, зафиксировать как «не проверено, причина …». + +- [ ] **Step 4: Записать результат в спеку** + +В `docs/superpowers/specs/2026-10-11-roles-and-memory-design.md` заменить первый пункт раздела 11: + +```markdown +- Работает ли `memory: project` через симлинк каталога (проверка 1). Пока не проверено, в плане первой задачей. +``` + +на (подставить исход): + +```markdown +- Работает ли `memory: project` через симлинк каталога — проверено 2026-10-11 (план, Task 1): , в части 2 применяется запасной вариант 6.5; / C: не проверено: <причина>>. +``` + +- [ ] **Step 5: Убрать песочницу и закоммитить** + +```bash +rm -rf "$SB" +git add docs/superpowers/specs/2026-10-11-roles-and-memory-design.md +git commit -m "docs(spec): результат проверки памяти агента через симлинк + +Refs #2 + +Co-Authored-By: Claude Fable 5.1 " +``` + +--- + +### Task 2: Тестовый прогон `tests/run.sh` и хук `role-hint.sh` + +**Files:** +- Create: `tests/run.sh` +- Create: `tests/test-role-hint.sh` +- Create: `hooks/role-hint.sh` +- Modify: `hooks/settings.hooks.json` + +**Interfaces:** +- Produces для всех следующих задач: `tests/run.sh [подстрока-имени]` запускает все `tests/test-*.sh` (или один) и возвращает 0 только если все `ok`. Хелперы, доступные внутри тестов: `ok "<имя>"`, `fail "<имя>" "<почему>"`, `assert_eq "<имя>" "<получено>" "<ожидалось>"`, `assert_file "<имя>" <путь>`, `assert_grep "<имя>" "<регэксп>" <файл>`, `assert_max_bytes "<имя>" <файл> <байт>`, `assert_symlink "<имя>" <путь>`; переменная `ROOT` — корень репозитория, `TMP` — свежий временный каталог на каждый тест-файл (удаляется после). +- Produces: `hooks/role-hint.sh` — SessionStart-хук; stdin JSON; stdout одна строка `[claude-harness] Роль сессии: . Вызови /role до первого ответа.` или ничего; всегда `exit 0`. + +- [ ] **Step 1: Написать прогон тестов** + +`tests/run.sh`: + +```bash +#!/usr/bin/env bash +# Тесты обвязки: bash, без зависимостей, работают с источниками в репозитории. +# Запуск: bash tests/run.sh [подстрока имени файла теста] +set -uo pipefail +HERE="$(cd "$(dirname "$0")" && pwd)"; ROOT="$(dirname "$HERE")" +pass=0; fail=0 +ok(){ pass=$((pass+1)); printf ' ok %s\n' "$1"; } +fail(){ fail=$((fail+1)); printf ' FAIL %s\n %s\n' "$1" "$2"; } +assert_eq(){ [ "$2" = "$3" ] && ok "$1" || fail "$1" "получено: '$2', ожидалось: '$3'"; } +assert_file(){ [ -e "$2" ] && ok "$1" || fail "$1" "нет файла $2"; } +assert_grep(){ grep -qE -- "$2" "$3" 2>/dev/null && ok "$1" || fail "$1" "в $3 нет /$2/"; } +assert_max_bytes(){ local n; n=$(wc -c < "$2" | tr -d ' '); [ "$n" -le "$3" ] && ok "$1 ($n Б)" || fail "$1" "$n Б > $3"; } +assert_symlink(){ [ -L "$2" ] && [ -e "$2" ] && ok "$1" || fail "$1" "$2 не симлинк или битый"; } +for t in "$HERE"/test-*.sh; do + [ -n "${1:-}" ] && [[ "$(basename "$t")" != *"$1"* ]] && continue + echo "## $(basename "$t")" + TMP=$(mktemp -d); export TMP ROOT + # shellcheck disable=SC1090 + . "$t" + rm -rf "$TMP" +done +echo "passed $pass, failed $fail" +[ "$fail" = 0 ] +``` + +- [ ] **Step 2: Написать падающий тест хука** + +`tests/test-role-hint.sh`: + +```bash +# role-hint.sh: подсказка роли по session_title, никогда не блокирует. +H="$ROOT/hooks/role-hint.sh" +assert_file "role-hint.sh существует" "$H" +run(){ printf '%s' "$1" | bash "$H" 2>/dev/null; echo "rc=$?"; } +assert_eq "admin: … → подсказка" "$(run '{"session_title":"admin: спринт 3","source":"startup"}')" \ + "[claude-harness] Роль сессии: admin. Вызови /role admin до первого ответа. +rc=0" +assert_eq "qa без двоеточия → подсказка" "$(run '{"session_title":"qa прогон Q1"}')" \ + "[claude-harness] Роль сессии: qa. Вызови /role qa до первого ответа. +rc=0" +assert_eq "регистр и пробелы в начале" "$(run '{"session_title":" Admin — Jira"}')" \ + "[claude-harness] Роль сессии: admin. Вызови /role admin до первого ответа. +rc=0" +assert_eq "префикс алиаса cc-" "$(run '{"session_title":"cc-analyst ТЗ v4"}')" \ + "[claude-harness] Роль сессии: analyst. Вызови /role analyst до первого ответа. +rc=0" +assert_eq "чужое слово → тишина" "$(run '{"session_title":"harness: роли"}')" "rc=0" +assert_eq "слово с продолжением (devops) → тишина" "$(run '{"session_title":"devops"}')" "rc=0" +assert_eq "без заголовка → тишина" "$(run '{"cwd":"/tmp","source":"resume"}')" "rc=0" +assert_eq "пустой stdin → тишина, exit 0" "$(run '')" "rc=0" +assert_eq "не JSON → тишина, exit 0" "$(run 'not json at all')" "rc=0" +assert_grep "settings.hooks.json регистрирует role-hint" 'role-hint\.sh' "$ROOT/hooks/settings.hooks.json" +``` + +- [ ] **Step 3: Запустить, убедиться, что падает** + +Run: `bash tests/run.sh role-hint` +Expected: `FAIL role-hint.sh существует` и далее FAIL по всем случаям; итог `failed 11`. + +- [ ] **Step 4: Написать хук** + +`hooks/role-hint.sh`: + +```bash +#!/usr/bin/env bash +# SessionStart hook (все источники): если заголовок сессии начинается с имени роли, напомнить вызвать /role. +# Ничего не блокирует: любой сбой → тихий exit 0. Заголовок приходит в поле session_title (claude -n "<роль> …", /rename). +set -uo pipefail +input=$(cat 2>/dev/null || true) +[ -n "$input" ] || exit 0 +title=$(printf '%s' "$input" | python3 -c 'import sys,json +try: print((json.load(sys.stdin) or {}).get("session_title") or "") +except Exception: print("")' 2>/dev/null) || exit 0 +[ -n "$title" ] || exit 0 +# первое слово: нижний регистр, без ведущих пробелов и префикса алиаса cc-; только латинские буквы. +# LC_ALL=C — чтобы BSD tr/sed работали побайтово и не спотыкались о кириллицу в заголовке. +first=$(printf '%s' "$title" | LC_ALL=C tr '[:upper:]' '[:lower:]' | LC_ALL=C sed -E 's/^[[:space:]]*(cc-)?([a-z]*).*$/\2/') +case "$first" in + dev|admin|analyst|qa) echo "[claude-harness] Роль сессии: $first. Вызови /role $first до первого ответа." ;; +esac +exit 0 +``` + +```bash +chmod +x hooks/role-hint.sh +``` + +- [ ] **Step 5: Зарегистрировать хук в блоке настроек** + +В `hooks/settings.hooks.json` заменить массив `SessionStart`: + +```json + "SessionStart": [ + { + "matcher": "startup", + "hooks": [ + { + "type": "command", + "command": "\"$HOME/.claude/hooks/harness-check.sh\"" + } + ] + }, + { + "hooks": [ + { + "type": "command", + "command": "\"$HOME/.claude/hooks/role-hint.sh\"" + } + ] + } + ] +``` + +Запись без `matcher` срабатывает на всех источниках (startup, resume, clear, compact, fork) — так документировано для хуков Claude Code. + +- [ ] **Step 6: Запустить тест, убедиться, что проходит** + +Run: `bash tests/run.sh role-hint` +Expected: 11 `ok`, `failed 0`. Также `python3 -m json.tool hooks/settings.hooks.json >/dev/null` без ошибок. + +- [ ] **Step 7: Коммит** + +```bash +git add tests/run.sh tests/test-role-hint.sh hooks/role-hint.sh hooks/settings.hooks.json +git commit -m "feat(hooks): role-hint.sh — подсказка роли по session_title; tests/run.sh + +Refs #2 + +Co-Authored-By: Claude Fable 5.1 " +``` + +--- + +### Task 3: Общие файлы ролей `roles/{dev,admin,analyst,qa}.md` + +**Files:** +- Create: `tests/test-roles.sh` +- Create: `roles/dev.md`, `roles/admin.md`, `roles/analyst.md`, `roles/qa.md` + +**Interfaces:** +- Produces: четыре файла формата 3.1 (см. Global Constraints). `roles/dev.md` — единственное место, где теперь живёт процесс разработки (маршрутизация, исполнение, завершение, маркировка, стиль), перенесённый из `global/CLAUDE.md`. Task 4 сжимает `global/CLAUDE.md`, опираясь на то, что всё остальное лежит здесь. Task 5 (`/role`) читает эти файлы по пути `~/.claude/roles/.md`. Строки `brief:` в «Читать на старте» — вход агента `briefing` (Task 5). + +- [ ] **Step 1: Написать падающий тест формата** + +`tests/test-roles.sh`: + +```bash +# Формат файлов ролей (спека 3.1), одинаковый для общего и проектного слоя. +check_role_file(){ # $1 = путь, $2 = ожидаемое имя роли + local f="$1" r="$2" h + assert_file "$f существует" "$f" || return + assert_eq "$f: строка 1" "$(sed -n '1p' "$f" | sed -E 's/^(# Роль: [a-z]+) — .*/\1/')" "# Роль: $r" + assert_eq "$f: строка 2" "$(sed -n '2p' "$f")" "Эта роль заменяет предыдущую роль сессии целиком." + h=$(grep -E '^## ' "$f" | tr '\n' '|') + assert_eq "$f: четыре раздела по порядку" "$h" "## Процесс|## Читать на старте|## Не делать|## Делегировать|" +} +for r in dev admin analyst qa; do check_role_file "$ROOT/roles/$r.md" "$r"; done +assert_grep "dev: чеклист закрытия перенесён" 'openspec archive' "$ROOT/roles/dev.md" +assert_grep "dev: архив всегда отдельным PR" 'всегда отдельным PR' "$ROOT/roles/dev.md" +assert_grep "dev: не cd при фоновом контроллере" 'cd' "$ROOT/roles/dev.md" +assert_grep "dev: bounded обновляет спеку при смене API" 'спека обновляется в том же PR' "$ROOT/roles/dev.md" +assert_grep "dev: маркировка R-n" 'R-n' "$ROOT/roles/dev.md" +assert_grep "admin: Jira вместо Gitea" 'Jira' "$ROOT/roles/admin.md" +assert_grep "analyst: код не пишется" 'не пиш' "$ROOT/roles/analyst.md" +assert_grep "qa: код не меняется" 'не меня' "$ROOT/roles/qa.md" +for r in dev admin analyst qa; do assert_grep "$r: есть строка brief:" '^- brief:' "$ROOT/roles/$r.md"; done +``` + +- [ ] **Step 2: Запустить, убедиться, что падает** + +Run: `bash tests/run.sh roles` +Expected: FAIL «roles/dev.md существует» и остальные; `failed` > 0. + +- [ ] **Step 3: Написать `roles/dev.md`** + +Содержание — нынешний `global/CLAUDE.md` целиком по смыслу, переложенный в четыре раздела, с правками по решениям владельца 11.10.2026 (архив отдельным PR; п. 4 через allow-правило) и уроками ретро. Текст файла: + +````markdown +# Роль: dev — разработчик +Эта роль заменяет предыдущую роль сессии целиком. + +## Процесс + +### Инструменты и их роли +- superpowers ведёт процесс внутри одной фичи: brainstorming → writing-plans → исполнение → ревью → finishing-a-development-branch. +- Gitea (CLI `tea`) хранит задачи между сессиями. Одна фича = одна issue = одна ветка = один PR. Issue заводит агент (`tea issues create`) из формулировки цели, до brainstorming. Порядок: сначала worktree на временной ветке (хук не даёт писать на main), затем issue, затем ветка переименовывается в `feat/-`. +- OpenSpec хранит требования. `openspec/specs/` — текущее поведение системы, `openspec/changes//` — дельта текущей фичи. Спека — единственный источник истины при рулингах. +- Ledger superpowers (`.superpowers/sdd/`) — прогресс только внутри текущего плана. Задачи плана в Gitea не дублировать. +- Карта кода — codegraph (MCP). Сервер отвечает по индексу корня основного checkout, код ветки в worktree он не видит: после создания worktree выполнить в нём `codegraph init`, во всех вызовах `codegraph_explore` передавать `projectPath` = абсолютный путь worktree. Implementer перед изменением вызывает `codegraph_explore` по своей области вместо grep/Read. Пустой ответ — один раз отметить в ledger и дальше использовать Read. codegraph обязателен на период замера (решение владельца 11.10.2026); число вызовов считает `/harness-retro`. + +### Маршрутизация +- Сообщение `[claude-harness]` в начале сессии — обвязка не подключена: действовать по его инструкции до всего остального. Скрипт `~/.claude/harness/adopt-project.sh` — единственная разрешённая запись на main. +- Bounded-задача (флаг, фикс, мелкая правка): без OpenSpec change и без плана. Дизайн в чате, затем файл `.bounded` в корне worktree (разрешает правки в коде без плана; в `.gitignore`), TDD, коммит, PR. Если меняется поведение API или схемы — спека в `openspec/specs/` обновляется в том же PR. Перед PR — verification-before-completion. +- Architectural-задача: brainstorming пишет спеку не в `docs/superpowers/specs/`, а в `openspec/changes//`. В поле **Spec** плана указывается этот путь. Перед architectural brainstorming прочитать релевантные файлы в `openspec/specs/` и ARCHITECTURE.md, если есть. + +### Исполнение +- Работа только в worktree. Ветка называется по issue: `feat/-`. Пути worktree в планах и брифах — абсолютные. +- Пока работает фоновый SDD-контроллер, в главной сессии не делать `cd` в основной checkout: cwd оболочки общий, контроллер и implementer-ы получат `BLOCKED: shell write on main` и остановятся. +- Режим по умолчанию: subagent-driven-development. Контроллер SDD запускается фоновым сабагентом **на модели sonnet** (не на модели сессии); сессия только ждёт его отчёт. Для планов до 5 задач — executing-plans (Native). Режим не спрашивать, он задан здесь. +- Модели исполнителей по SDD Model Selection: задача с готовым кодом в плане — haiku, интеграция — sonnet, ревью всей ветки — opus. Модель в dispatch указывать всегда. +- Ревьюер задачи проверяет trailer коммита (`Co-Authored-By`, как указано в плане); чужой trailer — находка `T.`, переписывать историю нельзя (force-push запрещён). +- План содержит раздел «Предусловия окружения» (стенд погашен, порты свободны, ключи не в окружении), контроллер проверяет его один раз перед первой задачей. + +### Завершение +- finishing-a-development-branch создаёт PR в Gitea (`tea pulls create`) со ссылкой на issue. Комментарии к PR — через receiving-code-review: читать из Gitea, править в той же ветке, отвечать в PR. Merge — только по явной команде человека. +- После merge по команде «закрывай» выполняется весь чеклист без остановок: + 1. `openspec archive ` — всегда отдельным PR (ветка `chore/archive-` из main, `tea pulls create`, merge по команде человека). Прямых коммитов в main нет, исключений нет (решение владельца 11.10.2026). + 2. Issue закрывается (`tea issues close `, комментарий со ссылкой на PR) только если все её пункты выполнены. Если PR закрывает часть пунктов — issue остаётся открытой, в неё пишется комментарий: какие маркеры закрыты, каким PR и коммитом. + 3. Удалить SDD workspace (`.superpowers/sdd//`). + 4. Удалить ветку фичи на сервере и локально: `git push origin --delete <ветка>` выполняется из worktree на ветке фичи (не из main-checkout), разрешён allow-правилом проекта (`Bash(git push origin --delete:*)` в `.claude/settings.json`, строку даёт `adopt-project.sh`); затем `git branch -D`, `git worktree remove`. Остальные worktree не трогать. Обходы через API Gitea запрещены. + 5. Обновить основной checkout: дерево чистое — `git pull --ff-only`; есть незакоммиченные изменения — не трогать, перечислить их в итоговом сообщении. + 6. Все находки со статусом `deferred` собрать в одну issue `Follow-ups from #`: маркер, одна строка, откуда взялось. Нет отложенного — issue не создавать. + 7. Файл auto-memory задачи, если был, свернуть в строку `registry-closed.md` (issue, PR, follow-up, дата); уроки из него перенести в `notes/` направления. + 8. Итоговое сообщение: что сделано по пунктам 1–7, номер follow-up issue, что осталось человеку. + Неприменимые к bounded-задаче пункты (archive, SDD workspace) пропускаются с пометкой. +- Список «Rulings I made» из финального сообщения копируется в описание PR. +- Любое решение, принятое без обсуждения с человеком — на этапе спеки, плана или исполнения — дописывается в `openspec/changes//design.md`, раздел `## Decisions made without discussion`: дата, решение, обоснование, цена ошибки. Ledger удаляется, чат теряется; design.md архивируется со спекой. + +### Маркировка находок и решений +- Каждая находка и каждое решение в ledger, отчётах, PR и design.md несёт маркер источника; маркер присваивается один раз и не меняется при переносе. + - `T.` — k-я minor-находка ревьюера задачи n плана. + - `C-n` / `I-n` / `M-n` — critical / important / minor из финального ревью ветки. + - `R-n` — рулинг контроллера, сквозная нумерация в пределах фичи; рядом номер решения в design.md: `R-5 (design #33)`. + - `O-n` — наблюдение контроллера вне ревью (неточные отчёты, инструменты, окружение). + - `H-n` — замечание человека из PR, обработанное через receiving-code-review. + - `RT-n` — находка ретроспективы обвязки. +- Каждая находка заканчивается статусом `fixed `, `deferred` или `rejected <причина>`. Находка без статуса в финальном отчёте — ошибка контроллера. +- Финальный отчёт контроллера содержит таблицу всех находок с маркером и статусом; `deferred` попадают в описание PR и в follow-up issue (чеклист, п. 6). + +### Стиль +- Отчёты сабагентов, рулинги и ledger не сжимать. Caveman — только по явной команде. + +## Читать на старте +- `tea issues ls` — открытые задачи и follow-up. +- `openspec list` — активные change. +- `docs/superpowers/plans/` в worktree — незавершённый план, если есть; ledger `.superpowers/sdd/`. +- brief: `git log --oneline -15` по каждому репозиторию из таблицы workspace с ролью dev. +- brief: `tea issues ls --state open` — активные задачи и follow-up issues. + +## Не делать +- Не писать на main и на detached HEAD; не обходить хуки (`echo >`, API Gitea вместо `tea`, правка `settings.json` ради «чтобы прошло»). Если хук мешает — сказать владельцу. +- Не делать merge PR и force-push. +- Не вести задачи Jira и документы заказчика — это роль admin (`/role admin`). +- Не прогонять сценарии на стенде со сверкой макетов — это роль qa. +- Не хранить состояние задачи в auto-memory: оно в issue и PR. + +## Делегировать +- SDD-контроллер (superpowers, фоновый сабагент на sonnet) → исполнение плана; implementer-ы haiku/sonnet, финальное ревью opus. +- `briefing` → выжимка строк `brief:` при `/role dev`. +```` + +- [ ] **Step 4: Написать `roles/admin.md`** + +````markdown +# Роль: admin — руководитель/администратор проекта +Эта роль заменяет предыдущую роль сессии целиком. + +## Процесс +- Задачи ведутся в трекере заказчика (Jira) или в репозитории направления (`admin/`). Gitea-issue и OpenSpec change для задач этой роли не заводятся. +- Вход в задачу — superpowers:brainstorming; по умолчанию задачи bounded: дизайн в чате, затем работа. Писать планы только для многошаговых работ со скриптами. +- Код (скрипты выгрузок, генераторы CSV, просмотрщики) — только в репозитории направления, TDD, коммит на ветке, PR по правилам репозитория. Код в репозиториях разработки не трогать. +- Тексты для внешних людей (заказчик, команда, Jira) самодостаточны: без внутренних маркеров (`T1.1`, `R-5`), номеров PR, путей к файлам и упоминаний агентов. Артефакты для передачи кладутся в `deliverables/` репозитория направления. +- Уроки, решения владельца и рецепты роли — в `admin/notes/`, коммит в той же ветке, что и работа. Состояние задач — в Jira или в CSV репозитория, не в auto-memory. + +## Читать на старте +- `CLAUDE.md` workspace — таблица репозиториев и источники истины. +- `admin/README.md` — что лежит в репозитории направления и как им пользоваться. +- brief: `admin/notes/` — уроки и решения владельца. +- brief: последний каталог `admin/jira-sync/runs/<дата>/` — что делалось в прошлый раз. + +## Не делать +- Не менять код репозиториев разработки и не заводить им issue — передать владельцу или `/role dev`. +- Не выполнять записывающие операции против реальной Jira, если проектный файл роли это не разрешает явно. +- Не подключать процесс superpowers→Gitea→OpenSpec к задачам этой роли. +- Не писать в auto-memory состояние спринта, задач и выгрузок. + +## Делегировать +- `briefing` → выжимка строк `brief:` при `/role admin`. +- `jira-admin` (проектный агент, если есть) → сухие прогоны discover/plan, перегенерация CSV, сверка Ганта, черновики описаний задач. +```` + +- [ ] **Step 5: Написать `roles/analyst.md`** + +````markdown +# Роль: analyst — аналитик требований +Эта роль заменяет предыдущую роль сессии целиком. + +## Процесс +- Работа с требованиями: спеки, открытые вопросы (OQ), решения владельца, сверка ТЗ с кодом и с тем, что показывает стенд. +- Код не пишется и не правится. Если сверка показала расхождение кода с ТЗ — это находка для владельца, не правка. +- Публикация в витрину `docs/` — через PR в репозиторий витрины; черновики и протоколы живут в репозитории направления (`analysis/`). +- Противоречия между документами не решаются молча: фиксируются как открытый вопрос с вариантами, решение принимает владелец и записывается в `analysis/notes/decisions.md` с датой. +- Вход в задачу — superpowers:brainstorming; задачи по умолчанию bounded. + +## Читать на старте +- `CLAUDE.md` workspace — таблица репозиториев и источники истины. +- Проектный файл роли — какие документы считаются источниками истины и как оформляются OQ. +- brief: `analysis/notes/decisions.md` — решения владельца. +- brief: список открытых OQ (путь в проектном файле роли). + +## Не делать +- Не писать и не править код, миграции, тесты. +- Не менять DBML, модель данных и доменные правила без решения владельца, записанного в `decisions.md`. +- Не переписывать исходные документы заказчика (`docs/source/`), только ссылаться на них. + +## Делегировать +- `briefing` → выжимка строк `brief:` при `/role analyst`. +```` + +- [ ] **Step 6: Написать `roles/qa.md`** + +````markdown +# Роль: qa — тестирование +Эта роль заменяет предыдущую роль сессии целиком. + +## Процесс +- Стенд, прогоны сценариев, сверка каждого экрана с макетом, гайды и инструкции пользователя. +- Код репозиториев разработки не меняется. Запись только в репозиторий направления (`qa/`): сценарии, журналы прогонов `qa/runs/<дата>/`, скриншоты, находки. +- Находки — списком со скриншотами и шагами воспроизведения, по одной на расхождение; issue в кодовый репозиторий заводит dev по решению владельца. +- Перед прогоном проверить предусловия стенда из проектного файла роли (порты, токен, данные); если стенд занят другой сессией — не гасить, сказать владельцу. +- Уроки и ловушки стенда — в `qa/notes/`, коммит в той же ветке, что и работа. + +## Читать на старте +- `CLAUDE.md` workspace — таблица репозиториев. +- Проектный файл роли — стенд, Playwright, макеты, чек-лист UI. +- brief: `qa/notes/` — ловушки стенда и рецепты. +- brief: последний каталог `qa/runs/<дата>/` — что проверялось в прошлый раз и контрольные цифры. + +## Не делать +- Не менять код, миграции, конфиги стенда. +- Не гасить и не перезапускать стенд и его сервисы — это делает владелец. +- Не заводить issue в кодовые репозитории без решения владельца. + +## Делегировать +- `briefing` → выжимка строк `brief:` при `/role qa`. +- `qa-runner` (проектный агент, если есть) → прогон сценария на стенде, сверка экранов с PNG макета, список расхождений в `qa/runs/<дата>/`. +```` + +- [ ] **Step 7: Запустить тест** + +Run: `bash tests/run.sh roles` +Expected: все `ok`, `failed 0`. + +- [ ] **Step 8: Коммит** + +```bash +git add tests/test-roles.sh roles/ +git commit -m "feat(roles): общие файлы ролей dev/admin/analyst/qa (формат 3.1) + +Refs #2 + +Co-Authored-By: Claude Fable 5.1 " +``` + +--- + +### Task 4: Глобальный `global/CLAUDE.md` ≤ 3 КБ и таблица адресов + +**Files:** +- Modify: `global/CLAUDE.md` (полная замена) +- Create: `docs/retro/2026-10-11-claude-md-addresses.md` +- Create: `tests/test-sizes.sh` + +**Interfaces:** +- Consumes: `roles/dev.md` из Task 3 — туда переехал процесс. +- Produces: глобальные правила, которые остаются в каждой сессии любой роли: язык, хуки, worktree, маркировка (кратко), политика памяти (спека 7.1), правило выбора роли (спека 4), «процесс роли — в /role». `doctor.sh` (Task 8) проверяет в нём строки `R-n`, `projectPath` и `/role`. + +- [ ] **Step 1: Написать падающий тест размеров и содержания** + +`tests/test-sizes.sh`: + +```bash +# Размеры и обязательные строки глобального CLAUDE.md (спека 1, 4, 7.1, 8). +G="$ROOT/global/CLAUDE.md" +assert_max_bytes "global/CLAUDE.md ≤ 3 КБ" "$G" 3072 +assert_grep "правило выбора роли" 'Роль: admin; если не так' "$G" +assert_grep "/role упомянут" '/role' "$G" +assert_grep "политика памяти: трекер" 'Состояние задачи живёт в трекере' "$G" +assert_grep "политика памяти: notes/" 'notes/' "$G" +assert_grep "политика памяти: auto-memory" 'auto-memory' "$G" +assert_grep "маркировка R-n" 'R-n' "$G" +assert_grep "codegraph projectPath" 'projectPath' "$G" +assert_grep "хук [claude-harness]" '\[claude-harness\]' "$G" +assert_grep "worktree" 'worktree' "$G" +assert_file "таблица адресов" "$ROOT/docs/retro/2026-10-11-claude-md-addresses.md" +``` + +- [ ] **Step 2: Запустить, убедиться, что падает** + +Run: `bash tests/run.sh sizes` +Expected: FAIL «global/CLAUDE.md ≤ 3 КБ» (сейчас 9155 Б) и FAIL по строкам роли/памяти. + +- [ ] **Step 3: Переписать `global/CLAUDE.md`** + +```markdown +# Правила сессии + +## Роль +- Сессия работает в одной из ролей: `dev`, `admin`, `analyst`, `qa`. Правила роли загружает `/role `; новая роль заменяет предыдущую целиком. +- Подсказка `[claude-harness] Роль сессии: ` от хука при старте — вызвать `/role ` до первого ответа. +- Без подсказки: если роль очевидна из первого сообщения (Jira, Гант, спринт → admin; стенд, прогон, скриншот, макет → qa; ТЗ, OQ, спека, методика → analyst; срез, issue, PR, тест, миграция → dev) — объявить одной строкой «Роль: admin; если не так, скажи» и вызвать `/role`. Иначе — один вопрос с четырьмя вариантами. +- Процесс и чеклисты роли — в её файле, не здесь. + +## Хуки и ветки +- guard-main блокирует запись на main/master и detached HEAD: работа только в worktree на ветке. require-plan: правки в коде только при плане для ветки или файле `.bounded`. +- Сообщение `[claude-harness]` при старте — инструкция обвязки, выполняется до всего остального. +- Хуки не обходить (`echo >`, API вместо `tea`). Если хук мешает — сказать владельцу. +- codegraph: после создания worktree — `codegraph init` в нём; в `codegraph_explore` — `projectPath` = абсолютный путь worktree. + +## Память +- Состояние задачи живёт в трекере (Gitea issue, Jira), не в памяти. +- В репозиторий направления (`/notes/`, память агента) пишутся уроки, решения владельца, рецепты. Коммит в той же ветке, что и работа. +- В локальную auto-memory — только ловушки этой машины и обвязки и индекс со ссылками на `notes/` направлений. + +## Маркировка +- Каждая находка и каждое решение несёт маркер источника (`T.`, `C-n`/`I-n`/`M-n`, `R-n`, `O-n`, `H-n`, `RT-n`) и статус `fixed ` / `deferred` / `rejected <причина>`. Маркер присваивается один раз. Подробности — в роли dev. + +## Стиль +- Отвечать по-русски. Отчёты сабагентов, рулинги и ledger не сжимать; caveman — только по явной команде. +``` + +- [ ] **Step 4: Написать таблицу адресов** + +`docs/retro/2026-10-11-claude-md-addresses.md` — каждый абзац старого `global/CLAUDE.md` (версия 0.2.0, коммит d2496aa и раньше) и его новый адрес: + +```markdown +# Куда переехал глобальный CLAUDE.md (0.2.0 → 0.3.0) + +Проверка критерия спеки «ни одно правило не потеряно». Старый файл — `global/CLAUDE.md` до коммита этой ветки (9155 Б). + +| Старый абзац (раздел → пункт) | Новый адрес | +|---|---| +| Роли инструментов → superpowers ведёт процесс | `roles/dev.md` → Процесс → Инструменты и их роли | +| Роли инструментов → Gitea, одна фича = одна issue, порядок старта | `roles/dev.md` → Инструменты и их роли | +| Роли инструментов → OpenSpec | `roles/dev.md` → Инструменты и их роли | +| Роли инструментов → Ledger superpowers | `roles/dev.md` → Инструменты и их роли | +| Маршрутизация → сообщение `[claude-harness]`, adopt-project.sh | `global/CLAUDE.md` → Хуки и ветки (сообщение); `roles/dev.md` → Маршрутизация (скрипт) | +| Маршрутизация → bounded-задача | `roles/dev.md` → Маршрутизация (дополнено: спека при смене API, verification-before-completion) | +| Маршрутизация → architectural-задача, спека в openspec/changes | `roles/dev.md` → Маршрутизация | +| Маршрутизация → читать openspec/specs и ARCHITECTURE.md | `roles/dev.md` → Маршрутизация | +| Маршрутизация → codegraph, projectPath, пустой ответ | `global/CLAUDE.md` → Хуки и ветки (init, projectPath); `roles/dev.md` → Инструменты (implementer, пустой ответ, замер) | +| Исполнение → только в worktree, имя ветки | `roles/dev.md` → Исполнение | +| Исполнение → SDD по умолчанию, контроллер на sonnet, executing-plans до 5 задач | `roles/dev.md` → Исполнение (дополнено: режим не спрашивать) | +| Исполнение → модели исполнителей | `roles/dev.md` → Исполнение | +| Завершение → finishing-a-development-branch, receiving-code-review, merge человеком | `roles/dev.md` → Завершение | +| Завершение → чеклист закрытия п. 1 (архив в main) | `roles/dev.md` → Завершение п. 1 — изменено: архив всегда отдельным PR (решение владельца 11.10.2026) | +| Завершение → чеклист п. 2–3 | `roles/dev.md` → Завершение п. 2–3 | +| Завершение → чеклист п. 4 (удаление веток) | `roles/dev.md` → Завершение п. 4 — дополнено: из worktree, allow-правило | +| Завершение → чеклист п. 5–7 | `roles/dev.md` → Завершение п. 5, 6, 8 (новый п. 7 — память, спека 7.1) | +| Завершение → неприменимое к bounded пропускается | `roles/dev.md` → Завершение | +| Завершение → любой другой push в main только через PR | `roles/dev.md` → Завершение п. 1 (без исключений) | +| Завершение → «Rulings I made» в PR | `roles/dev.md` → Завершение | +| Завершение → Decisions made without discussion | `roles/dev.md` → Завершение | +| Маркировка → список маркеров | `global/CLAUDE.md` → Маркировка (кратко); `roles/dev.md` → Маркировка (полностью, + `RT-n`) | +| Маркировка → статусы находок | `global/CLAUDE.md` → Маркировка; `roles/dev.md` → Маркировка | +| Маркировка → таблица в финальном отчёте, deferred в PR и follow-up | `roles/dev.md` → Маркировка | +| Стиль → не сжимать, caveman по команде | `global/CLAUDE.md` → Стиль | + +Новое, чего не было: раздел «Роль» (спека 4), раздел «Память» (спека 7.1), запрет обхода хуков (ретро, `feedback-no-harness-bypass`), `cd` при фоновом контроллере (RT-2), абсолютные пути worktree, trailer коммита (RT-9), предусловия окружения (RT-8). +``` + +- [ ] **Step 5: Запустить тесты** + +Run: `bash tests/run.sh` +Expected: все файлы тестов `ok`, `failed 0`. Вывести `wc -c global/CLAUDE.md` — число ≤ 3072. + +- [ ] **Step 6: Коммит** + +```bash +git add global/CLAUDE.md docs/retro/2026-10-11-claude-md-addresses.md tests/test-sizes.sh +git commit -m "feat(global): CLAUDE.md ≤ 3 КБ — роль, хуки, память, маркировка; таблица адресов + +Refs #2 + +Co-Authored-By: Claude Fable 5.1 " +``` + +--- + +### Task 5: Skill `/role` и агент `briefing` + +**Files:** +- Create: `skills/role/SKILL.md` +- Create: `agents/briefing.md` +- Create: `tests/test-skill-agent.sh` + +**Interfaces:** +- Consumes: формат файлов ролей (Task 3), пути `~/.claude/roles/.md` и `/.claude/roles/.md`. +- Produces: skill `role` (ставится в `~/.claude/skills/role/`), агент `briefing` (ставится в `~/.claude/agents/briefing.md`). Install (Task 9) копирует их по этим путям. + +- [ ] **Step 1: Написать падающий тест frontmatter** + +`tests/test-skill-agent.sh`: + +```bash +# Skill /role и агент briefing: frontmatter и обязательные шаги (спека 5, 6.1). +S="$ROOT/skills/role/SKILL.md"; A="$ROOT/agents/briefing.md" +assert_file "SKILL.md существует" "$S" +assert_eq "skill: первая строка ---" "$(sed -n '1p' "$S")" "---" +assert_grep "skill: name: role" '^name: role$' "$S" +assert_grep "skill: argument-hint" '^argument-hint: dev[|]admin[|]analyst[|]qa$' "$S" +assert_grep "skill: user-invocable" '^user-invocable: true$' "$S" +assert_grep "skill: читает общий слой" '~/.claude/roles/' "$S" +assert_grep "skill: читает проектный слой" '\.claude/roles/' "$S" +assert_grep "skill: собирает brief:" 'brief:' "$S" +assert_grep "skill: запускает briefing в фоне" 'subagent_type: briefing' "$S" +assert_grep "skill: без аргумента — список ролей" 'без аргумента' "$S" +assert_grep "skill: использует \$0" '\$0' "$S" +assert_file "briefing.md существует" "$A" +assert_grep "agent: name" '^name: briefing$' "$A" +assert_grep "agent: model sonnet" '^model: sonnet$' "$A" +assert_grep "agent: tools" '^tools: Read, Glob, Grep, Bash$' "$A" +assert_eq "agent: без memory" "$(grep -c '^memory:' "$A")" "0" +for h in 'Активные задачи' 'Решения за последние дни' 'Открытые вопросы' 'Ловушки'; do assert_grep "agent: раздел $h" "$h" "$A"; done +assert_grep "agent: до 60 строк" '60 строк' "$A" +``` + +- [ ] **Step 2: Запустить, убедиться, что падает** + +Run: `bash tests/run.sh skill-agent` +Expected: FAIL по всем пунктам. + +- [ ] **Step 3: Написать `skills/role/SKILL.md`** + +````markdown +--- +name: role +description: Переключить сессию в роль dev | admin | analyst | qa — загрузить общий и проектный файлы роли, её память и выжимку briefing. Вызывать по подсказке хука «Роль сессии», по правилу выбора роли из глобального CLAUDE.md или по просьбе владельца. +argument-hint: dev|admin|analyst|qa +user-invocable: true +--- + +# /role — роль сессии + +Аргумент: `$0` — имя роли. Workspace — текущий каталог сессии (cwd), не git-корень. + +## Без аргумента +Если `$0` пуст: напечатать текущую роль (та, чей файл читался последним в этой сессии; если ни одного — «роль не выбрана») +и список доступных: для каждой из `dev admin analyst qa` — есть ли `~/.claude/roles/.md` и `./.claude/roles/.md`. +Остановиться. + +## С аргументом +Если `$0` не из `dev admin analyst qa` — одной строкой сказать, какие роли есть, и остановиться. + +1. Прочитать `~/.claude/roles/$0.md` (общий слой). Прочитать `./.claude/roles/$0.md` (проектный слой); если файла нет — + сказать одной строкой «проектного файла роли $0 нет, работаю по общему» и продолжить. При конфликте правил побеждает проектный слой. + Новая роль заменяет предыдущую целиком: правила прежней роли больше не действуют. +2. Открыть индекс auto-memory (путь указан в системном промпте, раздел Memory, файл `MEMORY.md`). Найти раздел роли + (`## Разработка` для dev, `## Администрирование` для admin, `## Аналитика` для analyst, `## QA` для qa) и прочитать файлы, + перечисленные в нём. Раздел `## Общее` читается всегда. Если раздела нет — сказать одной строкой и продолжить. +3. Из раздела «Читать на старте» обоих слоёв собрать строки. Строки с префиксом `brief:` — список для агента `briefing`; + остальные — прочитать в контекст сейчас (путь → Read, команда → Bash, только чтение). + Если строк `brief:` больше нуля — запустить агента в фоне: + `Agent`, `subagent_type: briefing`, `run_in_background: true`, промпт: «Роль: $0. Workspace: . Источники:» и список + строк `brief:` без префикса, по одной на строку. Не ждать результата. +4. Ответить ровно пятью строками: + - Роль: `$0` — <описание из первой строки файла роли>. + - Процесс: <одна строка из раздела «Процесс» — что действует по умолчанию>. + - Прочитано: <перечень файлов обоих слоёв и файлов памяти, через запятую>. + - Не делать: <раздел «Не делать» одной строкой>. + - Ждёт решения владельца: <из памяти роли; если нет — «ничего»>. + Когда придёт выжимка `briefing` — добавить её одним сообщением как есть, без пересказа и без предложений действий. + +## Не делать +- Не читать файлы других ролей и их разделы памяти. +- Не выполнять записывающие команды из «Читать на старте». +- Не спрашивать подтверждения роли, если она пришла из подсказки хука или аргумента. +```` + +- [ ] **Step 4: Написать `agents/briefing.md`** + +````markdown +--- +name: briefing +description: Выжимка состояния для роли при /role — активные задачи, решения за последние дни, открытые вопросы, ловушки. Только чтение. Запускается skill-ом /role в фоне, не вызывать для другой работы. +model: sonnet +tools: Read, Glob, Grep, Bash +--- + +Ты готовишь выжимку для сессии в роли, названной в промпте. Тебе дан workspace (cwd) и список источников: пути +(файлы или каталоги, относительно workspace) и команды. + +Правила: +- Выполняй только читающие команды: `tea issues ls`, `tea pulls ls`, `openspec list`, `git log`, `git status`, `ls`, `cat`, + `find`, `grep`. Любую другую команду из списка пропусти и назови в разделе «Ловушки» как «не выполнено: <команда>». +- Каталог читай так: `MEMORY.md`/`README.md`/`decisions.md`, если есть, затем 5 самых свежих файлов по дате изменения. +- Не редактируй файлы, не предлагай действий, не оценивай. + +Ответ — до 60 строк, ровно в такой форме, без вступления: + +**Активные задачи** — открытые issue/PR/срезы с номерами и состоянием, по строке на задачу. +**Решения за последние дни** — решения владельца за 14 дней с датами (из notes/, decisions.md, памяти). +**Открытые вопросы** — что ждёт решения владельца, с источником. +**Ловушки** — предупреждения из уроков и памяти, актуальные для этой роли, по строке. + +Если раздел пуст — строка «— нет». Если источник не найден — строка «не найдено: <путь>» в «Ловушках». +```` + +- [ ] **Step 5: Запустить тест** + +Run: `bash tests/run.sh skill-agent` +Expected: все `ok`. + +- [ ] **Step 6: Коммит** + +```bash +git add skills/role/SKILL.md agents/briefing.md tests/test-skill-agent.sh +git commit -m "feat: skill /role и агент briefing + +Refs #2 + +Co-Authored-By: Claude Fable 5.1 " +``` + +--- + +### Task 6: `link-roles.sh` + +**Files:** +- Create: `link-roles.sh` +- Create: `tests/test-link-roles.sh` + +**Interfaces:** +- Consumes: формат таблицы репозиториев в CLAUDE.md workspace (вводится здесь, используется шаблоном в Task 7 и `doctor.sh` в Task 8): + + ```markdown + | Каталог | Репозиторий | Роль | + |---|---|---| + | arm-core/ | RSHB/arm-core | dev | + | admin/ | RSHB/arm-ka-admin | admin | + | docs/ | RSHB/arm-ka-docs | — | + ``` + + Строка учитывается, если третья ячейка — `admin`, `analyst` или `qa`; первая ячейка — каталог относительно workspace, завершающий `/` необязателен. Строки с `dev`, `—` и любым другим значением пропускаются. +- Produces: `link-roles.sh [workspace] [--map = ...]`. В `/.claude/` создаёт симлинки: `roles/.md → ../..//.claude/roles/.md`; `agents/.md → ../..//.claude/agents/.md` для каждого файла агента в репозитории направления; `agent-memory/ → ../..//.claude/agent-memory/` для каждого агента (целевой каталог создаётся, если его нет). Идемпотентен; печатает `создан`/`есть`/`ПРОПУЩЕН` по каждой ссылке; выход 0, если нет ошибок разбора. `--map` добавляет пары к таблице или заменяет её, если CLAUDE.md нет. + +- [ ] **Step 1: Написать падающий тест** + +`tests/test-link-roles.sh`: + +```bash +# link-roles.sh: симлинки ролей, агентов и памяти из workspace в репозитории направлений. +L="$ROOT/link-roles.sh" +assert_file "link-roles.sh существует" "$L" +W="$TMP/ws"; mkdir -p "$W/admin/.claude/roles" "$W/admin/.claude/agents" "$W/qa/.claude/roles" "$W/docs" "$W/arm-core" +echo '# Роль: admin — проектный слой' > "$W/admin/.claude/roles/admin.md" +printf -- '---\nname: jira-admin\nmodel: sonnet\n---\n' > "$W/admin/.claude/agents/jira-admin.md" +echo '# Роль: qa — проектный слой' > "$W/qa/.claude/roles/qa.md" +cat > "$W/CLAUDE.md" <<'EOF' +# Проект + +| Каталог | Репозиторий | Роль | +|---|---|---| +| arm-core/ | RSHB/arm-core | dev | +| admin/ | RSHB/arm-ka-admin | admin | +| qa | RSHB/arm-ka-qa | qa | +| docs/ | RSHB/arm-ka-docs | — | +| source/ | — | | +EOF +out=$(bash "$L" "$W" 2>&1); rc=$? +assert_eq "первый запуск: exit 0" "$rc" "0" +assert_symlink "roles/admin.md → admin/" "$W/.claude/roles/admin.md" +assert_symlink "roles/qa.md → qa/ (без завершающего /)" "$W/.claude/roles/qa.md" +assert_eq "roles/admin.md относительный" "$(readlink "$W/.claude/roles/admin.md")" "../../admin/.claude/roles/admin.md" +assert_symlink "agents/jira-admin.md" "$W/.claude/agents/jira-admin.md" +assert_symlink "agent-memory/jira-admin" "$W/.claude/agent-memory/jira-admin" +assert_file "целевой каталог памяти создан" "$W/admin/.claude/agent-memory/jira-admin" +assert_eq "dev не линкуется" "$(ls "$W/.claude/roles" | grep -c dev)" "0" +assert_eq "docs/source пропущены" "$(ls "$W/.claude/roles" | wc -l | tr -d ' ')" "2" +assert_grep "печатает 'создан'" 'создан' <(printf '%s' "$out") +out2=$(bash "$L" "$W" 2>&1); rc=$? +assert_eq "второй запуск: exit 0" "$rc" "0" +assert_eq "второй запуск: ничего не создано" "$(printf '%s' "$out2" | grep -c 'создан')" "0" +# чужой обычный файл на месте ссылки не затирается +rm "$W/.claude/roles/qa.md"; echo 'мой файл' > "$W/.claude/roles/qa.md" +out3=$(bash "$L" "$W" 2>&1) +assert_eq "обычный файл сохранён" "$(cat "$W/.claude/roles/qa.md")" "мой файл" +assert_grep "обычный файл → ПРОПУЩЕН" 'ПРОПУЩЕН' <(printf '%s' "$out3") +# --map без CLAUDE.md +W2="$TMP/ws2"; mkdir -p "$W2/an/.claude/roles"; echo x > "$W2/an/.claude/roles/analyst.md" +bash "$L" "$W2" --map an=analyst >/dev/null 2>&1 +assert_symlink "--map an=analyst" "$W2/.claude/roles/analyst.md" +# источник роли отсутствует → предупреждение, не ошибка +W3="$TMP/ws3"; mkdir -p "$W3/qa" +printf '| Каталог | Репозиторий | Роль |\n|---|---|---|\n| qa/ | x | qa |\n' > "$W3/CLAUDE.md" +out4=$(bash "$L" "$W3" 2>&1); rc=$? +assert_eq "нет файла роли в репозитории: exit 0" "$rc" "0" +assert_grep "нет файла роли: WARN" 'WARN' <(printf '%s' "$out4") +``` + +- [ ] **Step 2: Запустить, убедиться, что падает** + +Run: `bash tests/run.sh link-roles` +Expected: FAIL «link-roles.sh существует» и остальные. + +- [ ] **Step 3: Написать `link-roles.sh`** + +```bash +#!/usr/bin/env bash +# claude-harness: симлинки из /.claude/ в репозитории направлений (роли, агенты, память агентов). +# Запуск: bash link-roles.sh [workspace] [--map = ...] +# Источник пар «каталог → роль»: таблица | Каталог | Репозиторий | Роль | в /CLAUDE.md (роли admin|analyst|qa) +# и/или пары --map. Идемпотентно. Обычные файлы на месте ссылок не трогает (ПРОПУЩЕН). +set -euo pipefail +WS="."; MAPS=() +while [ $# -gt 0 ]; do + case "$1" in + --map) MAPS+=("$2"); shift 2 ;; + *) WS="$1"; shift ;; + esac +done +cd "$WS"; WS="$(pwd)" +pairs=() +if [ -f CLAUDE.md ]; then + while IFS= read -r line; do pairs+=("$line"); done < <( + python3 - CLAUDE.md <<'PY' +import re, sys +for line in open(sys.argv[1], encoding="utf-8"): + if not line.startswith("|"): continue + cells = [c.strip() for c in line.strip().strip("|").split("|")] + if len(cells) < 3: continue + d, role = cells[0].rstrip("/"), cells[2] + if role in ("admin", "analyst", "qa") and d and not d.startswith("-"): + print(f"{d}={role}") +PY + ) +fi +pairs+=("${MAPS[@]+"${MAPS[@]}"}") +[ ${#pairs[@]} -gt 0 ] || { echo "link-roles: нет пар каталог=роль (таблица в CLAUDE.md или --map)"; exit 0; } +mkdir -p .claude/roles .claude/agents .claude/agent-memory + +link() { # $1 = путь ссылки (относительно WS), $2 = цель (относительно каталога ссылки) + local l="$1" t="$2" + if [ -L "$l" ]; then + [ "$(readlink "$l")" = "$t" ] && { echo " есть $l"; return; } + rm "$l" + elif [ -e "$l" ]; then + echo " ПРОПУЩЕН $l — обычный файл/каталог, не симлинк; убери его вручную, если нужна ссылка"; return + fi + ln -s "$t" "$l"; echo " создан $l → $t" +} + +for p in "${pairs[@]}"; do + d=${p%%=*}; r=${p#*=}; d=${d%/} + echo "$d ($r)" + [ -d "$d" ] || { echo " WARN каталог $d не найден"; continue; } + if [ -f "$d/.claude/roles/$r.md" ]; then + link ".claude/roles/$r.md" "../../$d/.claude/roles/$r.md" + else + echo " WARN нет $d/.claude/roles/$r.md — проектный файл роли не слинкован" + fi + for a in "$d"/.claude/agents/*.md; do + [ -f "$a" ] || continue + n=$(basename "$a" .md) + link ".claude/agents/$n.md" "../../$d/.claude/agents/$n.md" + mkdir -p "$d/.claude/agent-memory/$n" + link ".claude/agent-memory/$n" "../../$d/.claude/agent-memory/$n" + done +done +``` + +```bash +chmod +x link-roles.sh +``` + +- [ ] **Step 4: Запустить тест** + +Run: `bash tests/run.sh link-roles` +Expected: все `ok`, `failed 0`. + +- [ ] **Step 5: Коммит** + +```bash +git add link-roles.sh tests/test-link-roles.sh +git commit -m "feat: link-roles.sh — симлинки ролей, агентов и памяти из workspace + +Refs #2 + +Co-Authored-By: Claude Fable 5.1 " +``` + +--- + +### Task 7: Шаблоны workspace и репозитория направления, режим `--workspace` + +**Files:** +- Create: `project-template/workspace/CLAUDE.md` +- Create: `project-template/workspace/roles/dev.md` +- Create: `project-template/role-repo/CLAUDE.md` +- Create: `project-template/role-repo/ROLE.md` +- Create: `project-template/role-repo/notes/README.md` +- Modify: `adopt-project.sh` (добавить ветку `--workspace` до проверки git) +- Modify: `new-project.sh` (добавить режим `--workspace [--role ...]`) +- Create: `tests/test-workspace.sh` + +**Interfaces:** +- Consumes: `link-roles.sh` (Task 6), формат таблицы репозиториев, формат файла роли. +- Produces: `adopt-project.sh --workspace [dir]` — для каталога, который не git: создаёт `CLAUDE.md` из шаблона workspace (если нет), `.claude/roles/dev.md` (если нет), печатает строку allow-правила для удаления веток, вызывает `link-roles.sh`. `new-project.sh --workspace [--role admin --role qa ...]` — создаёт workspace и по каталогу-репозиторию на роль из `role-repo/` (`//`, `git init -b main`, коммит), затем `link-roles.sh`. Существующие режимы обоих скриптов не меняются. + +- [ ] **Step 1: Написать падающий тест** + +`tests/test-workspace.sh`: + +```bash +# Шаблоны workspace/role-repo и режим --workspace в adopt-project.sh / new-project.sh. +T="$ROOT/project-template" +assert_file "workspace/CLAUDE.md" "$T/workspace/CLAUDE.md" +assert_max_bytes "workspace/CLAUDE.md ≤ 5 КБ" "$T/workspace/CLAUDE.md" 5120 +assert_grep "workspace/CLAUDE.md: таблица репозиториев" '^\| Каталог \| Репозиторий \| Роль \|' "$T/workspace/CLAUDE.md" +assert_grep "workspace/CLAUDE.md: роли → /role" '/role' "$T/workspace/CLAUDE.md" +assert_file "workspace/roles/dev.md" "$T/workspace/roles/dev.md" +assert_grep "workspace/roles/dev.md: строка 1" '^# Роль: dev ' "$T/workspace/roles/dev.md" +assert_file "role-repo/CLAUDE.md" "$T/role-repo/CLAUDE.md" +assert_file "role-repo/ROLE.md" "$T/role-repo/ROLE.md" +assert_file "role-repo/notes/README.md" "$T/role-repo/notes/README.md" +# new-project.sh --workspace +W="$TMP/ws" +out=$(bash "$ROOT/new-project.sh" --workspace "$W" --role admin --role qa 2>&1); rc=$? +assert_eq "new-project --workspace: exit 0" "$rc" "0" +assert_file "ws CLAUDE.md" "$W/CLAUDE.md" +assert_grep "ws CLAUDE.md: имя подставлено" "$(basename "$W")" "$W/CLAUDE.md" +assert_grep "ws CLAUDE.md: строка admin в таблице" '^\| admin/ \| .* \| admin \|' "$W/CLAUDE.md" +assert_file "ws .claude/roles/dev.md" "$W/.claude/roles/dev.md" +assert_eq "admin/ — git на main" "$(git -C "$W/admin" branch --show-current)" "main" +assert_eq "admin/.claude/role" "$(cat "$W/admin/.claude/role")" "admin" +assert_grep "admin/.claude/roles/admin.md: строка 1" '^# Роль: admin ' "$W/admin/.claude/roles/admin.md" +assert_file "admin/notes/README.md" "$W/admin/notes/README.md" +assert_eq "admin: коммит есть" "$(git -C "$W/admin" log --oneline | wc -l | tr -d ' ')" "1" +assert_symlink "ws .claude/roles/admin.md слинкован" "$W/.claude/roles/admin.md" +assert_symlink "ws .claude/roles/qa.md слинкован" "$W/.claude/roles/qa.md" +assert_eq "workspace сам не git" "$([ -d "$W/.git" ] && echo git || echo no)" "no" +# adopt-project.sh --workspace на существующем каталоге без CLAUDE.md +W2="$TMP/ws2"; mkdir -p "$W2/qa/.claude/roles"; echo '# Роль: qa — x' > "$W2/qa/.claude/roles/qa.md" +out=$(bash "$ROOT/adopt-project.sh" --workspace "$W2" 2>&1); rc=$? +assert_eq "adopt --workspace: exit 0" "$rc" "0" +assert_file "adopt: CLAUDE.md создан" "$W2/CLAUDE.md" +assert_file "adopt: .claude/roles/dev.md создан" "$W2/.claude/roles/dev.md" +assert_grep "adopt: печатает allow-правило" 'git push origin --delete' <(printf '%s' "$out") +echo 'моё' > "$W2/.claude/roles/dev.md" +bash "$ROOT/adopt-project.sh" --workspace "$W2" >/dev/null 2>&1 +assert_eq "adopt повторно: dev.md не затёрт" "$(cat "$W2/.claude/roles/dev.md")" "моё" +``` + +- [ ] **Step 2: Запустить, убедиться, что падает** + +Run: `bash tests/run.sh workspace` +Expected: FAIL по шаблонам и режимам. + +- [ ] **Step 3: Написать шаблоны** + +`project-template/workspace/CLAUDE.md`: + +```markdown +# {{PROJECT_NAME}} + +## Что это +{{Одним абзацем: что за проект, для кого, что сейчас делается.}} + +## Репозитории +Сессии запускаются из этого каталога. Роль сессии выбирается через `/role ` (подсказка хука по заголовку сессии `claude -n "<роль> …"`); правила роли — в `~/.claude/roles/.md` (общие) и `.claude/roles/.md` (проектные). Ссылки на проектные файлы ролей, агентов и их память создаёт `~/.claude/harness/link-roles.sh` по этой таблице. + +| Каталог | Репозиторий | Роль | +|---|---|---| +| {{code-repo}}/ | {{owner/repo}} | dev | +| admin/ | {{owner/repo-admin}} | admin | +| analysis/ | {{owner/repo-analysis}} | analyst | +| qa/ | {{owner/repo-qa}} | qa | +| docs/ | {{owner/repo-docs}} | — | + +Правило: `docs/` публикует, репозиторий направления работает. Роль-владелец разделов `docs/`: {{context/, model/ — analyst; dev/, contracts/ — dev; user/ — qa; source/ — никто}}. + +## Источники истины +- {{Модель данных, доменные правила, ТЗ — пути.}} +- Задачи: {{Gitea для кода; Jira <проект> для заказчика}}. + +## Ограничения для всех ролей +- Хуки guard-main и require-plan действуют в каждом git-репозитории здесь; workspace сам не git. +- {{Стенд, порты, что нельзя трогать.}} +``` + +`project-template/workspace/roles/dev.md`: + +```markdown +# Роль: dev — разработчик (проектный слой {{PROJECT_NAME}}) +Эта роль заменяет предыдущую роль сессии целиком. + +## Процесс +- {{Репозитории кода и их CLAUDE.md; worktree `.wt/<задача>/` и что в него линкуется; `.bounded`; venv/сборка; стенд.}} +- {{Источники истины для рулингов: модель данных, доменные правила.}} + +## Читать на старте +- {{путь к инвариантам / ARCHITECTURE.md}} +- brief: `tea issues ls --state open` + +## Не делать +- {{Инварианты проекта: что не мутируется, кто единственный писатель, чего не хранить.}} + +## Делегировать +- SDD-контроллер → исполнение плана. +``` + +`project-template/role-repo/CLAUDE.md`: + +```markdown +# {{DIR}} — репозиторий направления {{ROLE}} + +Кухня роли `{{ROLE}}` проекта {{PROJECT_NAME}}: рабочие материалы, `notes/` (уроки, решения владельца, рецепты), +`.claude/roles/{{ROLE}}.md` (проектный слой роли), `.claude/agents/` (агенты роли), `.claude/agent-memory/` (их память, коммитится). +Сессии запускаются из корня workspace; этот файл загружается при чтении файлов отсюда. Публикация в витрину `docs/` — через PR. +``` + +`project-template/role-repo/ROLE.md` (ставится как `.claude/roles/.md`): + +```markdown +# Роль: {{ROLE}} — проектный слой {{PROJECT_NAME}} +Эта роль заменяет предыдущую роль сессии целиком. + +## Процесс +- {{Инструменты роли в этом проекте, трекер, где лежат артефакты.}} + +## Читать на старте +- {{DIR}}/README.md +- brief: {{DIR}}/notes/ + +## Не делать +- {{Операционные ограничения проекта для этой роли.}} + +## Делегировать +- {{агент → для чего, если есть}} +``` + +`project-template/role-repo/notes/README.md`: + +```markdown +# notes/ + +Уроки, решения владельца (с датой) и рецепты роли. Коммитятся в той же ветке, что и работа. Состояние задач сюда не пишется — оно в трекере. +``` + +- [ ] **Step 4: Добавить режим `--workspace` в `adopt-project.sh`** + +Вставить после строки `HERE="$(cd "$(dirname "$0")" && pwd)"` (до разбора аргументов): + +```bash +# Режим workspace (каталог не git, рядом репозитории направлений): bash adopt-project.sh --workspace [папка] +if [ "${1:-}" = "--workspace" ]; then + cd "${2:-.}"; NAME="$(basename "$(pwd)")" + if [ -f CLAUDE.md ]; then echo "CLAUDE.md: есть, не трогаю" + else sed "s/{{PROJECT_NAME}}/$NAME/g" "$HERE/project-template/workspace/CLAUDE.md" > CLAUDE.md; echo "CLAUDE.md: создан из шаблона workspace — заполни {{...}} и таблицу репозиториев"; fi + mkdir -p .claude/roles + if [ -e .claude/roles/dev.md ]; then echo ".claude/roles/dev.md: есть, не трогаю" + else sed "s/{{PROJECT_NAME}}/$NAME/g" "$HERE/project-template/workspace/roles/dev.md" > .claude/roles/dev.md; echo ".claude/roles/dev.md: заготовка"; fi + bash "$HERE/link-roles.sh" "$(pwd)" + cat < [--role admin] [--role qa] [--role analyst] +if [ "${1:-}" = "--workspace" ]; then + DIR="${2:?usage: new-project.sh --workspace [--role ...]}"; shift 2 + ROLES=() + while [ $# -gt 0 ]; do case "$1" in --role) ROLES+=("$2"); shift 2 ;; *) echo "ERR: неизвестный аргумент $1"; exit 1 ;; esac; done + mkdir -p "$DIR" && cd "$DIR"; NAME="$(basename "$(pwd)")" + [ -f CLAUDE.md ] || sed "s/{{PROJECT_NAME}}/$NAME/g" "$HERE/project-template/workspace/CLAUDE.md" > CLAUDE.md + mkdir -p .claude/roles + [ -e .claude/roles/dev.md ] || sed "s/{{PROJECT_NAME}}/$NAME/g" "$HERE/project-template/workspace/roles/dev.md" > .claude/roles/dev.md + for r in "${ROLES[@]+"${ROLES[@]}"}"; do + case "$r" in admin|analyst|qa) ;; *) echo "ERR: роль $r не из admin|analyst|qa"; exit 1 ;; esac + d="$r"; [ "$r" = analyst ] && d="analysis" + if [ -d "$d/.git" ]; then echo "$d/: уже git, не трогаю"; continue; fi + mkdir -p "$d/.claude/roles" "$d/notes" + sed -e "s/{{PROJECT_NAME}}/$NAME/g" -e "s/{{ROLE}}/$r/g" -e "s#{{DIR}}#$d#g" "$HERE/project-template/role-repo/CLAUDE.md" > "$d/CLAUDE.md" + sed -e "s/{{PROJECT_NAME}}/$NAME/g" -e "s/{{ROLE}}/$r/g" -e "s#{{DIR}}#$d#g" "$HERE/project-template/role-repo/ROLE.md" > "$d/.claude/roles/$r.md" + cp "$HERE/project-template/role-repo/notes/README.md" "$d/notes/README.md" + echo "$r" > "$d/.claude/role" + git -C "$d" init -q -b main && git -C "$d" add -A && git -C "$d" commit -qm "chore: репозиторий направления $r (claude-harness)" + # строка таблицы: заменить заготовку {{owner/repo-}} на имя каталога, если заготовка есть + sed -i.bak "s#| $d/ | {{owner/repo-[a-z]*}} | $r |#| $d/ | /$NAME-$d | $r |#" CLAUDE.md && rm -f CLAUDE.md.bak + echo "$d/: репозиторий направления $r создан" + done + bash "$HERE/link-roles.sh" "$(pwd)" + cat <.md. + 2. Заведи репозитории направлений в Gitea и добавь remote (tea repos create …; git remote add origin …). + 3. cd $(pwd) && claude -n "dev …" +MSG + exit 0 +fi +``` + +Примечание для implementer: строка таблицы для `analysis/` в шаблоне — `| analysis/ | {{owner/repo-analysis}} | analyst |`, `sed` выше подставляет её по `$d` и `$r`; для `admin` и `qa` имена каталога и роли совпадают. + +- [ ] **Step 6: Запустить тест** + +Run: `bash tests/run.sh workspace` +Expected: все `ok`. Затем `bash tests/run.sh` — всё зелёное. + +- [ ] **Step 7: Коммит** + +```bash +git add project-template/workspace project-template/role-repo adopt-project.sh new-project.sh tests/test-workspace.sh +git commit -m "feat: шаблоны workspace и role-repo, режим --workspace в adopt/new-project + +Refs #2 + +Co-Authored-By: Claude Fable 5.1 " +``` + +--- + +### Task 8: `doctor.sh` — роли, role-hint, workspace, размеры + +**Files:** +- Modify: `skills/harness-doctor/scripts/doctor.sh` +- Modify: `skills/harness-doctor/SKILL.md` (чеклист прочтения — новые строки) +- Create: `tests/test-doctor.sh` + +**Interfaces:** +- Consumes: установленные пути из Task 9 (`$CC/roles/`, `$CC/skills/role/`, `$CC/agents/briefing.md`, `$CC/hooks/role-hint.sh`, `$CC/harness/link-roles.sh`); формат таблицы репозиториев. +- Produces: в разделе «Машина» — строки `ok`/`MISS` по новым файлам и `WARN глобальный CLAUDE.md > 3 КБ`; в «Самотест хуков» — две проверки `role-hint`; в «Проект» для не-git каталога с таблицей репозиториев — режим workspace: симлинки `.claude/roles|agents|agent-memory` целы/битые, размер CLAUDE.md ≤ 5 КБ, размер индекса памяти `MEMORY.md` ≤ 3 КБ. + +- [ ] **Step 1: Написать падающий тест** + +`tests/test-doctor.sh` — запускает `doctor.sh` с `CLAUDE_CONFIG_DIR`, указывающим на временную установку из источников (без настоящего `install.sh`, чтобы не зависеть от Task 9): + +```bash +# doctor.sh: новые проверки — файлы ролей, самотест role-hint, режим workspace, размеры. +D="$ROOT/skills/harness-doctor/scripts/doctor.sh" +CC="$TMP/cc"; mkdir -p "$CC/hooks" "$CC/roles" "$CC/skills/role" "$CC/agents" "$CC/harness" "$CC/projects" +cp "$ROOT"/hooks/*.sh "$CC/hooks/"; chmod +x "$CC/hooks/"*.sh +cp "$ROOT"/roles/*.md "$CC/roles/"; cp "$ROOT/skills/role/SKILL.md" "$CC/skills/role/"; cp "$ROOT/agents/briefing.md" "$CC/agents/" +cp "$ROOT/link-roles.sh" "$ROOT/adopt-project.sh" "$CC/harness/"; cp "$ROOT/global/CLAUDE.md" "$CC/CLAUDE.md" +printf '{"hooks":{"SessionStart":[{"hooks":[{"type":"command","command":"\\"$HOME/.claude/hooks/role-hint.sh\\""}]}]}}' > "$CC/settings.json" +# workspace с одной целой и одной битой ссылкой +W="$TMP/ws"; mkdir -p "$W/admin/.claude/roles" "$W/.claude/roles" "$W/.claude/agents" +echo '# Роль: admin — x' > "$W/admin/.claude/roles/admin.md" +printf '# p\n\n| Каталог | Репозиторий | Роль |\n|---|---|---|\n| admin/ | o/a | admin |\n' > "$W/CLAUDE.md" +ln -s ../../admin/.claude/roles/admin.md "$W/.claude/roles/admin.md" +ln -s ../../qa/.claude/agents/none.md "$W/.claude/agents/none.md" +slug=$(printf '%s' "$W" | sed 's#[^A-Za-z0-9]#-#g'); mkdir -p "$CC/projects/$slug/memory"; head -c 4000 /dev/zero | tr '\0' 'x' > "$CC/projects/$slug/memory/MEMORY.md" +out=$(CLAUDE_CONFIG_DIR="$CC" bash "$D" "$W" 2>&1) +assert_grep "машина: roles" 'ok +roles/ \(dev admin analyst qa\)' <(printf '%s' "$out") +assert_grep "машина: skill role" 'ok +skill role' <(printf '%s' "$out") +assert_grep "машина: агент briefing" 'ok +agent briefing' <(printf '%s' "$out") +assert_grep "машина: link-roles.sh" 'ok +.*link-roles.sh' <(printf '%s' "$out") +assert_grep "самотест: role-hint admin" 'ok +role-hint: заголовок admin' <(printf '%s' "$out") +assert_grep "самотест: role-hint тишина" 'ok +role-hint: без заголовка' <(printf '%s' "$out") +assert_grep "проект: режим workspace" 'workspace' <(printf '%s' "$out") +assert_grep "проект: целая ссылка" 'ok +roles/admin.md' <(printf '%s' "$out") +assert_grep "проект: битая ссылка" 'WARN +agents/none.md' <(printf '%s' "$out") +assert_grep "проект: индекс памяти > 3 КБ" 'WARN +индекс auto-memory' <(printf '%s' "$out") +rm -f "$CC/roles/qa.md" +out=$(CLAUDE_CONFIG_DIR="$CC" bash "$D" "$W" 2>&1) +assert_grep "машина: MISS roles/qa.md" 'MISS +roles/qa.md' <(printf '%s' "$out") +``` + +- [ ] **Step 2: Запустить, убедиться, что падает** + +Run: `bash tests/run.sh doctor` +Expected: FAIL по всем новым строкам (старые строки doctor печатаются, но новых нет). + +- [ ] **Step 3: Дополнить раздел «Машина»** + +В `doctor.sh` после строки с `[ -x "$CC/harness/adopt-project.sh" ] && ok ...` добавить: + +```bash +[ -x "$CC/harness/link-roles.sh" ] && ok "~/.claude/harness/link-roles.sh" || miss "~/.claude/harness/link-roles.sh" +rmiss=(); for r in dev admin analyst qa; do [ -f "$CC/roles/$r.md" ] || rmiss+=("$r"); done +if [ ${#rmiss[@]} -eq 0 ]; then ok "roles/ (dev admin analyst qa)"; else for r in "${rmiss[@]}"; do miss "roles/$r.md"; done; fi +[ -f "$CC/skills/role/SKILL.md" ] && ok "skill role" || miss "skill role (~/.claude/skills/role/SKILL.md)" +[ -f "$CC/agents/briefing.md" ] && ok "agent briefing" || miss "agent briefing (~/.claude/agents/briefing.md)" +if [ -f "$CC/CLAUDE.md" ]; then n=$(wc -c < "$CC/CLAUDE.md" | tr -d ' '); [ "$n" -le 3072 ] && ok "глобальный CLAUDE.md $n Б (≤ 3 КБ)" || warn "глобальный CLAUDE.md $n Б > 3 КБ"; fi +``` + +И в цикле `for h in guard-main require-plan harness-check; do` добавить `role-hint`: `for h in guard-main require-plan harness-check role-hint; do`. + +- [ ] **Step 4: Дополнить самотест хуков** + +Перед `rm -rf "$T"` в блоке самотеста добавить: + +```bash +if [ -x "$CC/hooks/role-hint.sh" ]; then + o=$(printf '{"session_title":"admin: спринт"}' | bash "$CC/hooks/role-hint.sh" 2>/dev/null) + case "$o" in *"Роль сессии: admin"*) ok "role-hint: заголовок admin → подсказка" ;; *) warn "role-hint: заголовок admin → '$o' (ожидалась подсказка)" ;; esac + o=$(printf '{"cwd":"/tmp","source":"resume"}' | bash "$CC/hooks/role-hint.sh" 2>/dev/null) + [ -z "$o" ] && ok "role-hint: без заголовка → тишина" || warn "role-hint: без заголовка → '$o' (ожидалась тишина)" +fi +``` + +- [ ] **Step 5: Добавить режим workspace в раздел «Проект»** + +Заменить последнюю строку блока `else info "не git-репозиторий"; fi` на: + +```bash +elif [ -f "$DIR/CLAUDE.md" ] && grep -q '^| Каталог | Репозиторий | Роль |' "$DIR/CLAUDE.md"; then + ws=$(cd "$DIR" && pwd) + info "workspace (не git): $ws" + n=$(wc -c < "$ws/CLAUDE.md" | tr -d ' '); [ "$n" -le 5120 ] && ok "CLAUDE.md workspace $n Б (≤ 5 КБ)" || warn "CLAUDE.md workspace $n Б > 5 КБ" + for kind in roles agents agent-memory; do + for l in "$ws/.claude/$kind"/*; do + [ -e "$l" ] || [ -L "$l" ] || continue + name="$kind/$(basename "$l")" + if [ -L "$l" ]; then [ -e "$l" ] && ok "$name → $(readlink "$l")" || warn "$name — битая ссылка на $(readlink "$l")" + else info "$name — обычный файл (не ссылка)"; fi + done + done + grep -E '^\|' "$ws/CLAUDE.md" | awk -F'|' '{gsub(/ /,"",$4); gsub(/^ +| +$/,"",$2); if ($4=="admin"||$4=="analyst"||$4=="qa") print $2, $4}' | while read -r d r; do + [ -f "$ws/${d%/}/.claude/roles/$r.md" ] || warn "нет ${d%/}/.claude/roles/$r.md — запусти link-roles.sh после создания" + done + # slug каталога памяти Claude Code: все не-алфавитно-цифровые символы пути → «-» (например, /Users/a.b/x → -Users-a-b-x) + slug=$(printf '%s' "$ws" | sed 's#[^A-Za-z0-9]#-#g'); mem="$CC/projects/$slug/memory/MEMORY.md" + if [ -f "$mem" ]; then n=$(wc -c < "$mem" | tr -d ' '); [ "$n" -le 3072 ] && ok "индекс auto-memory $n Б (≤ 3 КБ)" || warn "индекс auto-memory $n Б > 3 КБ ($mem)"; else info "индекс auto-memory: нет ($mem)"; fi + [ -x "$CC/harness/link-roles.sh" ] && info "обновить ссылки: bash $CC/harness/link-roles.sh $ws" +else info "не git-репозиторий и не workspace (нет таблицы репозиториев в CLAUDE.md)"; fi +``` + +- [ ] **Step 6: Дополнить `SKILL.md` чеклист прочтения** + +В раздел «**Машина**» добавить: + +```markdown +- `MISS roles/.md`, `MISS skill role`, `MISS agent briefing`, `MISS link-roles.sh` → `bash <пакет>/install.sh` (версия ≥ 0.3.0). +- `WARN глобальный CLAUDE.md > 3 КБ` → в `~/.claude/CLAUDE.md` дописано лишнее; процесс роли живёт в `~/.claude/roles/.md`, перенести туда. +``` + +В раздел «**Проект**» добавить: + +```markdown +- «workspace (не git)» → режим нескольких репозиториев. `WARN … битая ссылка` → репозиторий направления переименован или файл удалён: `bash ~/.claude/harness/link-roles.sh `. `WARN нет /.claude/roles/.md` → создать проектный файл роли из `~/.claude/harness/project-template/role-repo/ROLE.md`. +- `WARN индекс auto-memory > 3 КБ` → индекс снова стал журналом состояния; свернуть закрытое в `registry-closed.md`, уроки — в `notes/` направления (политика памяти в глобальном CLAUDE.md). +``` + +В «Чинится само» добавить `link-roles.sh` в workspace. + +- [ ] **Step 7: Запустить тесты** + +Run: `bash tests/run.sh doctor` затем `bash tests/run.sh` +Expected: всё `ok`. Дополнительно вручную: `bash skills/harness-doctor/scripts/doctor.sh .` в worktree печатает старые разделы без регрессий (строки `ok hook …`, самотест guard-main/require-plan). + +- [ ] **Step 8: Коммит** + +```bash +git add skills/harness-doctor tests/test-doctor.sh +git commit -m "feat(doctor): проверки ролей, самотест role-hint, режим workspace, размеры + +Refs #2 + +Co-Authored-By: Claude Fable 5.1 " +``` + +--- + +### Task 9: `install.sh` ставит роли, skill, агента, хук, link-roles + +**Files:** +- Modify: `install.sh` +- Create: `tests/test-install.sh` + +**Interfaces:** +- Consumes: все артефакты Task 2–8. +- Produces: установленное состояние в `$CC`: `roles/*.md`, `skills/role/SKILL.md`, `agents/briefing.md`, `hooks/role-hint.sh`, `harness/link-roles.sh`; `settings.json` с одной записью `role-hint.sh` в `SessionStart` после любого числа запусков; `--check` печатает `ok`/`MISS` по этим файлам. + +- [ ] **Step 1: Написать падающий тест** + +`tests/test-install.sh`: + +```bash +# install.sh: новые файлы ставятся, повторный запуск не дублирует хуки, --check их видит. +export HOME="$TMP/home"; export CLAUDE_CONFIG_DIR="$TMP/home/.claude"; mkdir -p "$CLAUDE_CONFIG_DIR" +echo '{"hooks":{"SessionStart":[{"matcher":"startup","hooks":[{"type":"command","command":"\"$HOME/.claude/hooks/harness-check.sh\""}]}]}}' > "$CLAUDE_CONFIG_DIR/settings.json" +bash "$ROOT/install.sh" >/dev/null 2>&1; bash "$ROOT/install.sh" >/dev/null 2>&1 +CC="$CLAUDE_CONFIG_DIR" +for r in dev admin analyst qa; do assert_file "roles/$r.md установлен" "$CC/roles/$r.md"; done +assert_file "skills/role/SKILL.md установлен" "$CC/skills/role/SKILL.md" +assert_file "agents/briefing.md установлен" "$CC/agents/briefing.md" +assert_file "hooks/role-hint.sh установлен" "$CC/hooks/role-hint.sh" +assert_eq "hooks/role-hint.sh исполняемый" "$([ -x "$CC/hooks/role-hint.sh" ] && echo x)" "x" +assert_file "harness/link-roles.sh установлен" "$CC/harness/link-roles.sh" +assert_file "harness/project-template/workspace" "$CC/harness/project-template/workspace/CLAUDE.md" +assert_file "harness/project-template/role-repo" "$CC/harness/project-template/role-repo/ROLE.md" +cnt=$(python3 -c "import json,sys; s=json.load(open(sys.argv[1])); print(sum(1 for e in s['hooks']['SessionStart'] for h in e.get('hooks',[]) if 'role-hint.sh' in h['command']))" "$CC/settings.json") +assert_eq "role-hint.sh в settings.json один раз после двух установок" "$cnt" "1" +cnt=$(python3 -c "import json,sys; s=json.load(open(sys.argv[1])); print(sum(1 for e in s['hooks']['SessionStart'] for h in e.get('hooks',[]) if 'harness-check.sh' in h['command']))" "$CC/settings.json") +assert_eq "harness-check.sh в settings.json один раз" "$cnt" "1" +out=$(bash "$ROOT/install.sh" --check 2>&1) +assert_grep "--check: roles" 'roles/' <(printf '%s' "$out") +assert_grep "--check: skill role" 'skills/role' <(printf '%s' "$out") +assert_grep "--check: agent briefing" 'agents/briefing' <(printf '%s' "$out") +rm "$CC/agents/briefing.md" +out=$(bash "$ROOT/install.sh" --check 2>&1) +assert_grep "--check: MISS briefing" 'MISS +.*briefing' <(printf '%s' "$out") +``` + +- [ ] **Step 2: Запустить, убедиться, что падает** + +Run: `bash tests/run.sh install` +Expected: FAIL по `roles/`, `skills/role`, `agents/briefing.md`, `role-hint.sh`, `link-roles.sh`, `--check`. + +- [ ] **Step 3: Дополнить `install.sh`** + +В `check()` после цикла по бинарникам добавить: + +```bash + echo "== Установленная часть обвязки ($CC)" + for f in CLAUDE.md hooks/guard-main.sh hooks/require-plan.sh hooks/harness-check.sh hooks/role-hint.sh \ + roles/dev.md roles/admin.md roles/analyst.md roles/qa.md skills/role/SKILL.md agents/briefing.md \ + harness/adopt-project.sh harness/new-project.sh harness/link-roles.sh skills/harness-doctor/SKILL.md; do + [ -f "$CC/$f" ] && echo " ok $f" || { echo " MISS $f (bash install.sh)"; ok=1; } + done +``` + +В разделе установки заменить строку `cp "$HERE/hooks/guard-main.sh" "$HERE/hooks/require-plan.sh" "$HERE/hooks/harness-check.sh" "$CC/hooks/"` на: + +```bash +cp "$HERE/hooks/guard-main.sh" "$HERE/hooks/require-plan.sh" "$HERE/hooks/harness-check.sh" "$HERE/hooks/role-hint.sh" "$CC/hooks/" +``` + +Строку `cp -R "$HERE/adopt-project.sh" "$HERE/new-project.sh" "$HERE/project-template" "$CC/harness/"` заменить на: + +```bash +rm -rf "$CC/harness/project-template" +cp -R "$HERE/adopt-project.sh" "$HERE/new-project.sh" "$HERE/link-roles.sh" "$HERE/project-template" "$CC/harness/" +``` + +После блока «2b. Skill диагностики» добавить: + +```bash +# 2c. Роли, skill /role, агент briefing +mkdir -p "$CC/roles" "$CC/skills/role" "$CC/agents" +cp "$HERE/roles/"*.md "$CC/roles/" +cp "$HERE/skills/role/SKILL.md" "$CC/skills/role/SKILL.md" +cp "$HERE/agents/briefing.md" "$CC/agents/briefing.md" +``` + +В Python-блоке слияния `settings.json` заменить строку + +```python +ss[:] = [e for e in ss if not any("harness-check.sh" in x.get("command","") for x in e.get("hooks",[]))] +``` + +на + +```python +ours_ss = ("harness-check.sh", "role-hint.sh") +ss[:] = [e for e in ss if not any(any(n in x.get("command","") for n in ours_ss) for x in e.get("hooks",[]))] +``` + +В финальном `echo "done: ..."` добавить `$CC/roles/, $CC/skills/role, $CC/agents/briefing.md, $CC/hooks/role-hint.sh`. + +- [ ] **Step 4: Запустить тест** + +Run: `bash tests/run.sh install` затем `bash tests/run.sh` +Expected: всё `ok`, `failed 0`. + +- [ ] **Step 5: Коммит** + +```bash +git add install.sh tests/test-install.sh +git commit -m "feat(install): роли, skill /role, briefing, role-hint, link-roles; --check видит их + +Refs #2 + +Co-Authored-By: Claude Fable 5.1 " +``` + +--- + +### Task 10: Документация — HARNESS.md «Роли», README, CHANGELOG 0.3.0, CLAUDE.md репозитория + +**Files:** +- Modify: `docs/HARNESS.md` (новый раздел «Роли» после «Дисциплина между сессиями») +- Modify: `README.md` (таблица «Состав», «Файлы», «Установка», новый раздел «Роли») +- Modify: `CHANGELOG.md` (запись 0.3.0) +- Modify: `CLAUDE.md` (разделы «Архитектура», «Команды», «Тесты» — новые файлы и `tests/run.sh`) +- Modify: `docs/superpowers/specs/2026-10-11-roles-and-memory-design.md` (раздел «Decisions made without discussion» — решения плана) + +**Interfaces:** +- Consumes: всё выше. +- Produces: документация, по которой владелец переносит роли в awp-ca (часть 2). + +- [ ] **Step 1: HARNESS.md — раздел «Роли»** + +Вставить перед `## Стоимость`: + +```markdown +## Роли + +Одна сессия — одна роль: `dev`, `admin`, `analyst`, `qa`. Роль даёт сессии только свои правила, свои файлы на старте и свой раздел памяти; процесс разработки (superpowers → Gitea → OpenSpec, SDD, чеклист закрытия) живёт в роли dev и не загружается в сессии admin. + +Два слоя, оба в git: общий — `~/.claude/roles/.md` (из пакета, `install.sh`), проектный — `/.claude/roles/.md` (для dev — файл, для остальных — симлинк в репозиторий направления `admin/`, `qa/`, `analysis/`). Формат одинаковый: четыре раздела «Процесс», «Читать на старте», «Не делать», «Делегировать»; при конфликте побеждает проектный слой. + +Выбор роли, от автоматического к ручному: заголовок сессии (`claude -n "admin …"`; хук `role-hint.sh` печатает подсказку `[claude-harness] Роль сессии: admin`), вывод из первого сообщения (правило в глобальном CLAUDE.md: объявить «Роль: admin; если не так, скажи»), явно — `/role `. Skill `/role` читает оба слоя, раздел памяти роли, строки «Читать на старте», а строки с префиксом `brief:` отдаёт фоновому агенту `briefing`, который возвращает выжимку: активные задачи, решения, открытые вопросы, ловушки. + +Workspace из нескольких репозиториев (не git, cwd всех сессий) описывается таблицей `| Каталог | Репозиторий | Роль |` в его CLAUDE.md; `link-roles.sh` по ней создаёт симлинки на проектные файлы ролей, агентов направлений и их память (`memory: project` → `/.claude/agent-memory//`, коммитится). `adopt-project.sh --workspace` и `new-project.sh --workspace --role …` собирают это из шаблонов; `doctor.sh` проверяет ссылки и размеры. + +Политика памяти (глобальный CLAUDE.md): состояние задачи — в трекере; уроки, решения владельца и рецепты — в `notes/` направления и памяти агентов (в git); в локальной auto-memory — только ловушки машины и индекс со ссылками. При закрытии файл auto-memory задачи сворачивается в строку `registry-closed.md`. +``` + +Также в таблице раздела «Хуки» добавить строку: + +```markdown +| Подсказка роли по заголовку сессии | `role-hint.sh`, SessionStart (все источники) | читает `session_title`; если начинается с `dev|admin|analyst|qa` — одна строка-подсказка вызвать `/role`; ничего не блокирует | +``` + +И в «Дисциплина между сессиями» первое предложение дополнить: «Новая сессия начинается с роли (`/role`), затем `tea issues ls` и `openspec list`…». + +- [ ] **Step 2: README.md** + +В таблицу «Состав» добавить строку: + +```markdown +| Роль сессии | `roles/`, skill `/role`, агент `briefing`, хук `role-hint.sh` | правила, файлы и память своей роли | этот пакет | +``` + +В блок «Файлы» добавить строки: + +``` +roles/.md общий слой ролей dev/admin/analyst/qa → ~/.claude/roles/ +skills/role/SKILL.md /role : загрузить роль, память, запустить briefing → ~/.claude/skills/role/ +agents/briefing.md выжимка состояния для роли (sonnet, только чтение) → ~/.claude/agents/ +hooks/role-hint.sh подсказка роли по заголовку сессии (SessionStart) +link-roles.sh симлинки ролей/агентов/памяти из workspace в репозитории направлений → ~/.claude/harness/ +project-template/workspace/ CLAUDE.md workspace с таблицей репозиториев, roles/dev.md +project-template/role-repo/ репозиторий направления: CLAUDE.md, ROLE.md, notes/README.md +tests/run.sh тесты пакета (bash): bash tests/run.sh +``` + +Новый раздел после «Существующий проект»: + +```markdown +## Workspace из нескольких репозиториев и роли + +``` +bash ~/.claude/harness/adopt-project.sh --workspace ~/projects/foo # есть каталог с репозиториями +bash ~/.claude/harness/new-project.sh --workspace ~/projects/foo --role admin --role qa # с нуля +``` + +Заполните таблицу репозиториев в `CLAUDE.md` workspace и перезапустите `link-roles.sh`. Сессии запускайте из корня workspace с ролью в заголовке: `claude -n "admin спринт 3"`. Удобно завести алиасы в профиле shell (обвязка их не ставит): + +```bash +alias cc-dev='claude -n "dev"'; alias cc-admin='claude -n "admin"'; alias cc-qa='claude -n "qa"'; alias cc-analyst='claude -n "analyst"' +``` + +В сессии: `/role` — текущая роль и список; `/role admin` — переключить. Подробности в `docs/HARNESS.md`, раздел «Роли». +``` + +В «Установка» шаг 4 дополнить: «`bash tests/run.sh` — тесты пакета должны быть зелёными». + +- [ ] **Step 3: CHANGELOG.md** + +В начало после `# Changelog`: + +```markdown +## 0.3.0 — 2026-10-11 +- Роли сессии `dev|admin|analyst|qa`: общий слой `roles/.md` (`~/.claude/roles/`), проектный слой `/.claude/roles/.md`; формат из четырёх разделов (спека `docs/superpowers/specs/2026-10-11-roles-and-memory-design.md`). +- Skill `/role `: читает оба слоя, раздел памяти роли, «Читать на старте»; строки `brief:` — фоновому агенту `briefing` (sonnet, только чтение). +- SessionStart-хук `role-hint.sh`: подсказка роли по заголовку сессии (`claude -n "admin …"`), регистр и префикс `cc-` не важны; ничего не блокирует. +- `link-roles.sh`: симлинки ролей, агентов и их памяти из workspace в репозитории направлений по таблице `| Каталог | Репозиторий | Роль |`. +- Шаблоны `project-template/workspace/` и `project-template/role-repo/`; режим `--workspace` в `adopt-project.sh` и `new-project.sh`. +- Глобальный CLAUDE.md сжат до ≤ 3 КБ (роль, хуки, память, маркировка, стиль); процесс разработки переехал в `roles/dev.md` — таблица адресов `docs/retro/2026-10-11-claude-md-addresses.md`. Изменения правил: архив OpenSpec всегда отдельным PR; п. 4 чеклиста закрытия — из worktree по allow-правилу; bounded обновляет спеку при смене API; новый п. 7 — память задачи при закрытии. +- Политика памяти: состояние задачи в трекере, уроки в `notes/` направления и памяти агентов, в auto-memory — только ловушки машины и индекс. +- `doctor.sh`: файлы ролей, самотест `role-hint`, режим workspace (симлинки, размеры CLAUDE.md и индекса памяти). +- Тесты пакета `tests/run.sh` (bash, без зависимостей): хук, роли, размеры, skill/agent, link-roles, workspace, doctor, install. +- Ретроспектива `docs/retro/2026-10-11.md` и скрипты `docs/retro/scripts/`. +``` + +- [ ] **Step 4: CLAUDE.md репозитория** + +В «Архитектура» добавить пункты: + +```markdown +- `roles/` — общий слой ролей (формат: четыре раздела, см. спеку ролей), `skills/role/` — skill `/role`, `agents/briefing.md` — агент выжимки, `link-roles.sh` — симлинки workspace → репозитории направлений. Ставятся `install.sh` в `~/.claude/{roles,skills/role,agents,harness}`. +- `tests/` — тесты пакета на bash: `run.sh` + `test-*.sh`, работают с источниками, `install.sh` ставят во временный `CLAUDE_CONFIG_DIR`. +``` + +В «Команды» добавить первой строкой: `` - `bash tests/run.sh [имя]` — весь набор тестов. Зелёным считается только весь набор. `` +В «Тесты» заменить первый пункт на: «`tests/run.sh` — автоматические тесты источников (хуки через фиктивный JSON, скрипты во временных каталогах, размеры). Самотест хуков в `doctor.sh` проверяет установленные копии; перед ним `bash install.sh`.» +В «Архитектура» строку про `hooks/` дополнить: `role-hint.sh` (SessionStart, все источники): подсказка роли по `session_title`. + +- [ ] **Step 5: Спека — решения плана** + +В раздел `## Decisions made without discussion` спеки добавить: + +```markdown +- 11.10.2026 (план). Тесты пакета — `tests/run.sh` на bash в репозитории, в дополнение к самотесту `doctor.sh` (спека, раздел 8, упоминала только doctor). Обоснование: TDD по задачам плана невозможен против установленных копий. Цена ошибки: лишний каталог. +- 11.10.2026 (план). Хук `role-hint.sh` регистрируется записью без `matcher` (документация: отсутствующий matcher = все источники), а не перечислением `startup|resume|clear|compact`. Цена ошибки: одна строка в `settings.hooks.json`. +- 11.10.2026 (план). Формат таблицы репозиториев workspace зафиксирован: `| Каталог | Репозиторий | Роль |`; `link-roles.sh` и `doctor.sh` узнают workspace по заголовку этой таблицы. Цена ошибки: правка двух regex. +- 11.10.2026 (план). `role-hint.sh` понимает заголовок без учёта регистра и с префиксом `cc-` (алиасы из README). Цена ошибки: нет. +- 11.10.2026 (план). Часть 2 (awp-ca: проектные файлы ролей, агенты jira-admin/qa-runner, репозитории направлений, чистка памяти, песочница, перенос) — отдельный план в workspace awp-ca после merge этой ветки; каталог для роли analyst — `analysis/`. Цена ошибки: нет. +- 11.10.2026 (план). Чеклист закрытия получил п. 7 (память задачи, спека 7.1) и новый п. 8 (итоговое сообщение); нумерация старых п. 1–6 сохранена. +``` + +- [ ] **Step 6: Проверка** + +Run: `bash tests/run.sh`; `grep -c '{{' global/CLAUDE.md roles/*.md` → все 0; `python3 -m json.tool hooks/settings.hooks.json >/dev/null`. +Expected: тесты зелёные, плейсхолдеров в не-шаблонных файлах нет. + +- [ ] **Step 7: Коммит** + +```bash +git add docs/HARNESS.md README.md CHANGELOG.md CLAUDE.md docs/superpowers/specs/2026-10-11-roles-and-memory-design.md +git commit -m "docs: роли в HARNESS.md и README, CHANGELOG 0.3.0, решения плана в спеке + +Refs #2 + +Co-Authored-By: Claude Fable 5.1 " +``` + +--- + +## Предусловия окружения + +- Worktree `/Users/andrey.epifancev/projects/cc-skills/claude-harness/.wt/roles` на ветке `feat/2-roles-and-memory`, `codegraph init` выполнен. +- Task 1 запускает `claude -p` (платный вызов, модель haiku); остальные задачи — только bash и python3. +- Тесты пишут только во временные каталоги (`mktemp -d`) и в `CLAUDE_CONFIG_DIR`/`HOME` внутри них; настоящий `~/.claude` не трогается до `bash install.sh` по команде владельца после merge. +- В главной сессии не делать `cd` в основной checkout, пока работает контроллер. + +## Что остаётся владельцу после merge + +1. `bash install.sh` из main — ставит роли, skill, агента, хук. +2. Проверка 3 и 4 раздела 9 спеки (четыре сессии с `/context`, `/role admin` в dev-сессии) — в песочнице awp-ca, часть 2. +3. Решение по открытым вопросам 2 и 3 спеки (имена репозиториев в Gitea, алиасы в профиле).