install.sh ставит allow-правила цикла фичи, --no-permissions для отказа (#24, 0.5.0) #26
@ -1,5 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## 0.5.0 — 2026-10-23
|
||||
- **`install.sh` ставит allow-правила цикла фичи** (#24; пересмотр решения владельца 2). Список — `global/permissions.json`: `git worktree`, `git branch -m/-D`, `git push -u origin feat/…`/`chore/…`, `git push origin --delete`, `tea issues`, `tea pulls create/list/edit`, `tea comments`. Merge и push в main в список не входят. Правила только дописываются в `permissions.allow` файла `~/.claude/settings.json`, чужие не трогаются; копия списка — `~/.claude/harness/permissions.json`. `--no-permissions` пропускает шаг. `--check` и `doctor.sh` показывают `permissions обвязки: N/M` и недостающие правила.
|
||||
- `adopt-project.sh` и `new-project.sh` печатают строку `push --delete` для установки с `--no-permissions`. WARN проверки в `doctor.sh` подсказывает `bash install.sh`.
|
||||
- `EnterWorktree` в список не входит. Вход в worktree вне `<repo>/.claude/worktrees/` спрашивает подтверждение при любых allow-правилах (Claude Code ≥ 2.1.206); перенос worktree — #25.
|
||||
- harness-doctor: агент чинит `install.sh --no-permissions`, права ставит владелец.
|
||||
|
||||
## 0.4.2 — 2026-10-23
|
||||
- Удаление ветки фичи на сервере (чеклист закрытия, п. 4; B3, RT-3, решение владельца 2): `doctor.sh` в разделе «Проект» ищет allow-правило для `git push origin --delete` в `.claude/settings.json`, `settings.local.json` проекта и в глобальном `settings.json` — `ok` с файлом или `WARN` с готовой строкой `"Bash(git push origin --delete:*)"`. Строку печатают `adopt-project.sh` и `new-project.sh` во всех режимах (раньше — только `adopt --workspace`); файл настроек они не пишут, правило добавляет владелец (#22).
|
||||
- `HARNESS.md`: шаг 10 — чеклист из 8 пунктов в `roles/dev.md` и рецепт п. 4. `roles/dev.md`, п. 4: `git worktree remove` раньше `git branch -D` (ветку, выведенную в worktree, git не удаляет).
|
||||
|
||||
11
CLAUDE.md
11
CLAUDE.md
@ -6,14 +6,14 @@
|
||||
## Стек и ограничения
|
||||
- Bash (`set -euo pipefail`), markdown и `python3` stdlib (3.8+). Логика guard-main и require-plan — `hooks/harness_target.py`, их `.sh` — обёртки; в остальных хуках и скриптах JSON разбирается встроенным `python3`. Других зависимостей времени выполнения нет. Внешние CLI (`tea`, `openspec`, `codegraph`) проверяются, но не требуются для установки.
|
||||
- Хук блокирует вызов только через `exit 2` с сообщением в stderr; `exit 0` — пропустить. Любой сбой разбора входа должен приводить к пропуску, не к блокировке.
|
||||
- `install.sh` идемпотентен: повторный запуск не ломает `settings.json` и делает бэкапы `CLAUDE.md` и `settings.json` с меткой времени.
|
||||
- `install.sh` идемпотентен: повторный запуск не ломает `settings.json` и делает бэкапы `CLAUDE.md` и `settings.json` с меткой времени. В `settings.json` он сливает `hooks` и только дописывает `permissions.allow` из `global/permissions.json`; чужие правила не трогает. Агент запускает его только с `--no-permissions`: права ставит владелец.
|
||||
- Хуки работают из `~/.claude/hooks/`, пути в `settings.hooks.json` через `$HOME`; `CLAUDE_CONFIG_DIR` переопределяет `~/.claude`.
|
||||
- macOS и Linux; `python3` должен быть в PATH сессии Claude Code.
|
||||
|
||||
## Архитектура
|
||||
- `hooks/` — поведение обвязки, единственный «код» с логикой. `guard-main.sh` (PreToolUse на Edit/Write/MultiEdit/Bash): запрет записи в репозиторий на main/master/detached HEAD по цели записи (файл, цели команды Bash, git-мутации), без исключений. `require-plan.sh` (PreToolUse на Edit/Write/MultiEdit): правки в каталогах кода репозитория файла только при плане для ветки или файле `.bounded`. Обе — обёртки над `harness_target.py`: `parse` (команда → цели, без ФС) → `resolve` (путь → корень и ветка) → `decide`; режим `targets` — отладка разбора. `harness-check.sh` (SessionStart): проверка, что проект подключён. `role-hint.sh` (SessionStart, все источники): подсказка роли по `session_title`. `settings.hooks.json` — блок, который `install.sh` сливает в `~/.claude/settings.json`.
|
||||
- `install.sh`, `adopt-project.sh`, `new-project.sh` — установка и бутстрап. `install.sh` копирует `global/`, `hooks/`, `project-template/`, `adopt-project.sh`, `new-project.sh` и `skills/harness-doctor` в `~/.claude/{CLAUDE.md,hooks,harness,skills}`. `adopt-project.sh` работает на ветке `chore/harness-init` и вливает её ff в текущую ветку; это единственная разрешённая запись на main вне PR.
|
||||
- `global/CLAUDE.md` — глобальные правила процесса, ставятся в `~/.claude/CLAUDE.md`. `project-template/` — `CLAUDE.md`, `CLAUDE.harness-section.md` (секция «OpenSpec и superpowers», по ней `harness-check` узнаёт подключённый проект), `ARCHITECTURE.md`, `gitignore`.
|
||||
- `global/CLAUDE.md` — глобальные правила процесса, ставятся в `~/.claude/CLAUDE.md`. `global/permissions.json` — allow-правила цикла фичи (без merge и push в main), `install.sh` дописывает их в `~/.claude/settings.json` и кладёт копию в `~/.claude/harness/` для `doctor.sh`. `project-template/` — `CLAUDE.md`, `CLAUDE.harness-section.md` (секция «OpenSpec и superpowers», по ней `harness-check` узнаёт подключённый проект), `ARCHITECTURE.md`, `gitignore`.
|
||||
- `skills/harness-doctor/` — skill диагностики: `scripts/doctor.sh` снимает состояние машины и проекта и прогоняет самотест хуков; `SKILL.md` — чеклист прочтения отчёта. `fixtures/hook-cases.jsonl` + `scripts/hook_corpus.py` — регрессионный корпус хуков (реальные команды с ожидаемым вердиктом, прогон в песочнице); лежит в skill, чтобы `doctor.sh` гонял его по установленным копиям.
|
||||
- `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`.
|
||||
@ -23,8 +23,8 @@
|
||||
|
||||
## Команды
|
||||
- `bash tests/run.sh [имя]` — весь набор тестов. Зелёным считается только весь набор.
|
||||
- `bash install.sh --check` — проверка окружения: бинарники, плагины, конфликты.
|
||||
- `bash install.sh` — установка глобальной части, идемпотентно.
|
||||
- `bash install.sh --check` — проверка окружения: бинарники, плагины, конфликты, allow-правила обвязки.
|
||||
- `bash install.sh [--no-permissions]` — установка глобальной части, идемпотентно; `--no-permissions` — без allow-правил.
|
||||
- `bash skills/harness-doctor/scripts/doctor.sh [проект]` — отчёт и самотест хуков. Только чтение.
|
||||
- `bash adopt-project.sh <папка> [--no-merge] [--push]` — подключить существующий проект. Из терминала, не из Claude Code.
|
||||
- `bash new-project.sh <папка> [ssh-url]` — каркас нового проекта.
|
||||
@ -42,7 +42,8 @@
|
||||
- Сделано: B5+B6 часть 1 — слой ролей (PR #4, 0.3.0). Часть 2 (awp-ca) — issue #2, отложенные находки — issue #5.
|
||||
- Сделано: B1+B2 и отмена исключения openspec-only для guard-main — хуки по цели записи и корпус хуков (issue #6, PR #12, 0.4.0), спека — `openspec/specs/hook-guards/`. Отложенные находки — issue #13.
|
||||
- Сделано: B3 — allow-правило `git push origin --delete` печатают adopt/new-project, проверяет doctor (issue #22, 0.4.2). B4 закрыт в #6 (решение владельца 1). B10 — workspace-хук awp-ca, в части 2 issue #2.
|
||||
- Дальше: B12.
|
||||
- Сделано: allow-правила цикла фичи ставит `install.sh` (issue #24, 0.5.0; пересмотр решения владельца 2).
|
||||
- Дальше: worktree в `.claude/worktrees/` (issue #25), затем B12.
|
||||
|
||||
## OpenSpec и superpowers
|
||||
- Вход в любую задачу — superpowers:brainstorming. Skills openspec-propose и openspec-explore сами по себе не запускать.
|
||||
|
||||
@ -27,7 +27,8 @@ hooks/settings.hooks.json блок PreToolUse для ~/.claude/settings.json
|
||||
caveman/config.json defaultMode: off → ~/.config/caveman/config.json
|
||||
project-template/ CLAUDE.md, ARCHITECTURE.md, gitignore для нового проекта
|
||||
examples/ реальный CLAUDE.md проекта-пилота
|
||||
install.sh установка глобальной части, идемпотентно; --check проверяет окружение
|
||||
global/permissions.json allow-правила цикла фичи (worktree, tea, push фичевых веток, удаление веток) → ~/.claude/settings.json
|
||||
install.sh установка глобальной части, идемпотентно; --check проверяет окружение; --no-permissions — без allow-правил
|
||||
new-project.sh каркас нового проекта: git, CLAUDE.md, openspec init, codegraph init, remote
|
||||
roles/<r>.md общий слой ролей dev/admin/analyst/qa → ~/.claude/roles/
|
||||
skills/role/SKILL.md /role <r>: загрузить роль, память, запустить briefing → ~/.claude/skills/role/
|
||||
@ -82,7 +83,9 @@ claude mcp add-json -s user codegraph '{"type":"stdio","command":"codegraph","ar
|
||||
|
||||
# 3. Глобальная часть
|
||||
bash install.sh --check
|
||||
bash install.sh
|
||||
bash install.sh # дописывает в ~/.claude/settings.json allow-правила из global/permissions.json;
|
||||
# чужие правила не трогает. Без них: bash install.sh --no-permissions.
|
||||
# Нет tea pulls merge и push в main: merge — по вашей команде.
|
||||
|
||||
# 4. Проверка хука: в любом репозитории на main запустить claude и попросить создать файл.
|
||||
# Ожидается BLOCKED и предложение создать worktree.
|
||||
|
||||
@ -17,7 +17,7 @@ if [ "${1:-}" = "--workspace" ]; then
|
||||
|
||||
Workspace $NAME подключён. Дальше:
|
||||
1. Заполни таблицу репозиториев в CLAUDE.md и перезапусти: bash $HERE/link-roles.sh $(pwd)
|
||||
2. Разреши агенту удалять ветки на сервере (чеклист закрытия, п. 4) — строка в .claude/settings.json → permissions.allow:
|
||||
2. Если обвязка ставилась с --no-permissions: разреши агенту удалять ветки на сервере (чеклист закрытия, п. 4) — строка в .claude/settings.json → permissions.allow:
|
||||
"Bash(git push origin --delete:*)"
|
||||
3. Сессии: claude -n "dev …" | "admin …" | "qa …" | "analyst …" из этого каталога.
|
||||
MSG
|
||||
@ -73,7 +73,7 @@ fi
|
||||
cat <<MSG
|
||||
|
||||
Проект $NAME подключён к обвязке. Дальше:
|
||||
1. Разреши агенту удалять ветки на сервере (чеклист закрытия, п. 4) — строка в .claude/settings.json → permissions.allow:
|
||||
1. Если обвязка ставилась с --no-permissions: разреши агенту удалять ветки на сервере (чеклист закрытия, п. 4) — строка в .claude/settings.json → permissions.allow:
|
||||
"Bash(git push origin --delete:*)"
|
||||
2. cd $(pwd) && claude
|
||||
«Опиши проект в CLAUDE.md по коду: разделы «Что это», «Стек», «Архитектура», «Команды». Bounded-задача.»
|
||||
|
||||
@ -29,7 +29,9 @@
|
||||
|
||||
Шаги 6–8 идут без остановок. Агент останавливается только на необратимой операции, вопросе безопасности, побочном эффекте вне worktree или плане, который невозможно исполнить.
|
||||
|
||||
**Удаление ветки фичи (п. 4 закрытия).** `git push origin --delete <ветка>` из worktree фичи, затем из основного checkout `git worktree remove <путь>` и `git branch -D <ветка>`. guard-main с 0.4.0 эту команду не блокирует, но её может отклонить классификатор auto mode. Поэтому в проекте нужно allow-правило: строка `"Bash(git push origin --delete:*)"` в `.claude/settings.json` → `permissions.allow`. Строку добавляете вы (решение владельца 2); `adopt-project.sh` и `new-project.sh` её печатают, `doctor.sh` показывает, найдена ли она.
|
||||
**Удаление ветки фичи (п. 4 закрытия).** `git push origin --delete <ветка>` из worktree фичи, затем из основного checkout `git worktree remove <путь>` и `git branch -D <ветка>`. guard-main с 0.4.0 эту команду не блокирует, но её может отклонить классификатор auto mode. Поэтому нужно allow-правило `"Bash(git push origin --delete:*)"`. Его вместе с остальными правилами цикла фичи (`global/permissions.json`) ставит `bash install.sh` в `~/.claude/settings.json`. Запускаете его вы: агент себе права не выдаёт. Если обвязка стоит с `--no-permissions`, строку добавляете в `.claude/settings.json` проекта сами: её печатают `adopt-project.sh` и `new-project.sh`, а `doctor.sh` показывает, найдена ли она.
|
||||
|
||||
**Подтверждения.** В обычном режиме Claude Code спрашивает подтверждение на всё, что не покрыто allow-правилами. В auto mode решает классификатор: он пропускает или запрещает, но вас не спрашивает. Allow-правила проверяются раньше него. Вход в worktree вне `<repo>/.claude/worktrees/` спрашивает подтверждение при любых правилах, перенос worktree — #25.
|
||||
|
||||
**Bounded-задача** (флаг, фикс) идёт короче: дизайн в чате, файл `.bounded` в корне worktree (пропускает хук require-plan), TDD, коммит, PR. Без OpenSpec change и без плана.
|
||||
|
||||
|
||||
@ -173,6 +173,8 @@ requesting-code-review 0 раз. Закрытие и ревью идут по ч
|
||||
«в main только через PR» становится без исключений. (B4 → переформулирован.)
|
||||
2. **Удаление серверных веток — allow-правило в настройках проекта.** Строку правила владелец добавляет сам; обвязка даёт
|
||||
готовую строку в `adopt-project.sh` и в отчёте `doctor.sh`. Чеклист закрытия п. 4 остаётся за агентом. (B3.)
|
||||
*Пересмотрено 23.10.2026 (#24): правило вместе с остальными allow-правилами цикла фичи ставит `install.sh`
|
||||
в `~/.claude/settings.json`; отказ — `--no-permissions`, тогда строку по-прежнему печатают adopt/new-project и doctor.*
|
||||
3. **codegraph — оставить и замерить.** Один срез с обязательным codegraph и подсчётом вызовов `codegraph_explore` против
|
||||
Bash/Read в тех же задачах; решение после замера. Метрика входит в `/harness-retro`. (B8 → замер, не вывод из потока.)
|
||||
4. **Память — политика «состояние задачи в Gitea, в память только уроки, фидбек и знания роли» и одноразовая чистка
|
||||
|
||||
17
global/permissions.json
Normal file
17
global/permissions.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(git worktree:*)",
|
||||
"Bash(git branch -m:*)",
|
||||
"Bash(git branch -D:*)",
|
||||
"Bash(git push -u origin feat/:*)",
|
||||
"Bash(git push -u origin chore/:*)",
|
||||
"Bash(git push origin --delete:*)",
|
||||
"Bash(tea issues:*)",
|
||||
"Bash(tea pulls create:*)",
|
||||
"Bash(tea pulls list:*)",
|
||||
"Bash(tea pulls edit:*)",
|
||||
"Bash(tea comments:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
47
install.sh
47
install.sh
@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
# claude-harness: установка глобальной части (CLAUDE.md, хуки, конфиг caveman, слияние settings.json).
|
||||
# Идемпотентно. Запуск: bash install.sh | проверка окружения: bash install.sh --check
|
||||
# Идемпотентно. Запуск: bash install.sh [--no-permissions] | проверка окружения: bash install.sh --check
|
||||
# --no-permissions — не дописывать allow-правила цикла фичи (global/permissions.json) в settings.json.
|
||||
set -euo pipefail
|
||||
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||
CC="${CLAUDE_CONFIG_DIR:-$HOME/.claude}"
|
||||
@ -24,6 +25,23 @@ check() {
|
||||
if [ -f "$CC/settings.json" ] && grep -q 'codegraph prompt-hook' "$CC/settings.json"; then
|
||||
echo " WARN codegraph prompt-hook в settings.json — рекомендуется отключить на время пилота"
|
||||
fi
|
||||
local pr; pr=$(python3 - "$CC/settings.json" "$HERE/global/permissions.json" <<'PY' 2>/dev/null
|
||||
import json, sys
|
||||
try:
|
||||
s = json.load(open(sys.argv[1], encoding="utf-8"))
|
||||
except Exception:
|
||||
s = {}
|
||||
have = (s.get("permissions") or {}).get("allow") or []
|
||||
need = json.load(open(sys.argv[2], encoding="utf-8"))["permissions"]["allow"]
|
||||
miss = [r for r in need if r not in have]
|
||||
print("%d/%d %s" % (len(need) - len(miss), len(need), " ".join(miss)))
|
||||
PY
|
||||
) || true
|
||||
case "$pr" in
|
||||
"") echo " WARN permissions обвязки: не прочитаны ($CC/settings.json)" ;;
|
||||
*" ") echo " ok permissions обвязки: ${pr% }" ;;
|
||||
*) echo " WARN permissions обвязки: ${pr%% *}, нет: ${pr#* } — bash install.sh (или осознанно --no-permissions)" ;;
|
||||
esac
|
||||
echo "== Установленная часть обвязки ($CC)"
|
||||
for f in CLAUDE.md hooks/guard-main.sh hooks/require-plan.sh hooks/harness_target.py 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 \
|
||||
@ -33,7 +51,14 @@ check() {
|
||||
return $ok
|
||||
}
|
||||
|
||||
if [ "${1:-}" = "--check" ]; then check; exit $?; fi
|
||||
PERMS=1
|
||||
for a in "$@"; do
|
||||
case "$a" in
|
||||
--check) check; exit $? ;;
|
||||
--no-permissions) PERMS=0 ;;
|
||||
*) echo "ERR: неизвестный аргумент $a (есть --check, --no-permissions)"; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
mkdir -p "$CC/hooks" "$HOME/.config/caveman"
|
||||
stamp=$(date +%Y%m%d-%H%M%S)
|
||||
@ -50,7 +75,7 @@ cp "$HERE/hooks/guard-main.sh" "$HERE/hooks/require-plan.sh" "$HERE/hooks/harnes
|
||||
# 2a. Скрипты бутстрапа проекта — агент запускает их из сессии
|
||||
mkdir -p "$CC/harness"
|
||||
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/"
|
||||
cp -R "$HERE/adopt-project.sh" "$HERE/new-project.sh" "$HERE/link-roles.sh" "$HERE/project-template" "$HERE/global/permissions.json" "$CC/harness/"
|
||||
chmod +x "$CC/harness/"*.sh
|
||||
# 2b. Skill диагностики
|
||||
mkdir -p "$CC/skills"
|
||||
@ -94,6 +119,22 @@ json.dump(s, open(sp,"w"), ensure_ascii=False, indent=2)
|
||||
print("settings.json: hooks merged")
|
||||
PY
|
||||
|
||||
# 5. settings.json: allow-правила цикла фичи — только дописываются, чужие не трогаются
|
||||
if [ "$PERMS" = 1 ]; then
|
||||
python3 - "$SETTINGS" "$HERE/global/permissions.json" <<'PY'
|
||||
import json, sys
|
||||
sp, pp = sys.argv[1], sys.argv[2]
|
||||
s = json.load(open(sp)); need = json.load(open(pp))["permissions"]["allow"]
|
||||
allow = s.setdefault("permissions", {}).setdefault("allow", [])
|
||||
new = [r for r in need if r not in allow]
|
||||
allow.extend(new)
|
||||
json.dump(s, open(sp, "w"), ensure_ascii=False, indent=2)
|
||||
print("settings.json: permissions +%d%s" % (len(new), (" (" + ", ".join(new) + ")") if new else " (все на месте)"))
|
||||
PY
|
||||
else
|
||||
echo "settings.json: permissions пропущены (--no-permissions)"
|
||||
fi
|
||||
|
||||
echo "done: $CC/CLAUDE.md, $CC/skills/harness-doctor, $CC/hooks/{guard-main,require-plan,harness-check,role-hint}.sh + harness_target.py, $CC/roles/, $CC/skills/role, $CC/agents/briefing.md, $CC/harness/, ~/.config/caveman/config.json"
|
||||
echo "Плагины Claude Code ставятся вручную: /plugin install superpowers@claude-plugins-official"
|
||||
check || true
|
||||
|
||||
@ -32,7 +32,7 @@ if [ "${1:-}" = "--workspace" ]; then
|
||||
Workspace $NAME готов. Дальше:
|
||||
1. Заполни {{...}} в CLAUDE.md и в .claude/roles/dev.md; в репозиториях направлений — .claude/roles/<r>.md.
|
||||
2. Заведи репозитории направлений в Gitea и добавь remote (tea repos create …; git remote add origin …).
|
||||
3. Разреши агенту удалять ветки на сервере (чеклист закрытия, п. 4) — строка в .claude/settings.json → permissions.allow:
|
||||
3. Если обвязка ставилась с --no-permissions: разреши агенту удалять ветки на сервере (чеклист закрытия, п. 4) — строка в .claude/settings.json → permissions.allow:
|
||||
"Bash(git push origin --delete:*)"
|
||||
4. cd $(pwd) && claude -n "dev …"
|
||||
MSG
|
||||
@ -70,7 +70,7 @@ cat <<MSG
|
||||
Проект $NAME готов. Дальше:
|
||||
1. Заполните {{...}} в CLAUDE.md и ARCHITECTURE.md.
|
||||
2. Положите первые фикстуры в tests/fixtures/.
|
||||
3. Разрешите агенту удалять ветки на сервере (чеклист закрытия, п. 4) — строка в .claude/settings.json → permissions.allow:
|
||||
3. Если обвязка ставилась с --no-permissions: разрешите агенту удалять ветки на сервере (чеклист закрытия, п. 4) — строка в .claude/settings.json → permissions.allow:
|
||||
"Bash(git push origin --delete:*)"
|
||||
4. cd $DIR && claude
|
||||
«Хочу сделать ... Описание и ограничения в CLAUDE.md. Давай начнём.»
|
||||
|
||||
@ -29,7 +29,7 @@
|
||||
1. `openspec archive <id>` — всегда отдельным PR (ветка `chore/archive-<id>` из main, `tea pulls create`, merge по команде человека). Прямых коммитов в main нет, исключений нет (решение владельца 11.10.2026).
|
||||
2. Issue закрывается (`tea issues close <id>`, комментарий со ссылкой на PR) только если все её пункты выполнены. Если PR закрывает часть пунктов — issue остаётся открытой, в неё пишется комментарий: какие маркеры закрыты, каким PR и коммитом.
|
||||
3. Удалить SDD workspace (`.superpowers/sdd/<plan>/`).
|
||||
4. Удалить ветку фичи на сервере и локально: `git push origin --delete <ветка>` выполняется из worktree на ветке фичи (не из main-checkout), разрешён allow-правилом проекта (`Bash(git push origin --delete:*)` в `.claude/settings.json`; строку добавляет владелец, её печатают `adopt-project.sh` и `new-project.sh`, есть ли она — показывает `doctor.sh`); затем из основного checkout `git worktree remove`, потом `git branch -D`. Остальные worktree не трогать. Обходы через API Gitea запрещены.
|
||||
4. Удалить ветку фичи на сервере и локально: `git push origin --delete <ветка>` выполняется из worktree на ветке фичи (не из main-checkout), разрешён allow-правилом `Bash(git push origin --delete:*)` (его ставит `install.sh` владельца; при `--no-permissions` — строка в `.claude/settings.json` проекта, её печатают `adopt-project.sh` и `new-project.sh`, есть ли она — показывает `doctor.sh`; агент права себе не добавляет); затем из основного checkout `git worktree remove`, потом `git branch -D`. Остальные worktree не трогать. Обходы через API Gitea запрещены.
|
||||
5. Обновить основной checkout: дерево чистое — `git pull --ff-only`; есть незакоммиченные изменения — не трогать, перечислить их в итоговом сообщении.
|
||||
6. Все находки со статусом `deferred` собрать в одну issue `Follow-ups from #<issue-id>`: маркер, одна строка, откуда взялось. Нет отложенного — issue не создавать.
|
||||
7. Файл auto-memory задачи, если был, свернуть в строку `registry-closed.md` (issue, PR, follow-up, дата); уроки из него перенести в `notes/` направления.
|
||||
|
||||
@ -27,6 +27,7 @@ description: Диагностика обвязки Claude Code (claude-harness)
|
||||
- `WARN в окружении есть *_API_KEY` → ключ виден всем сабагентам и делает интеграционные тесты платными при каждом `npm test`. Убрать из профиля shell, передавать только в строке запуска. Это находка для пользователя, агент сам профиль не правит.
|
||||
- Глобальный CLAUDE.md без «маркировка: да» или «projectPath: да» → старая версия, `install.sh`.
|
||||
- `MISS roles/<r>.md`, `MISS skill role`, `MISS agent briefing`, `MISS link-roles.sh` → `bash <пакет>/install.sh` (версия ≥ 0.3.0).
|
||||
- `WARN permissions обвязки: N/M, нет: …` или `MISS permissions обвязки` → allow-правила цикла фичи (`global/permissions.json`) не стоят в `~/.claude/settings.json`. Это находка владельцу: `bash <пакет>/install.sh` запускает он сам. Агент себе права не выдаёт: так он обошёл бы отказ классификатора auto mode. Если обвязка ставилась с `--no-permissions` осознанно, WARN ожидаем.
|
||||
- `WARN глобальный CLAUDE.md > 3 КБ` → в `~/.claude/CLAUDE.md` дописано лишнее; процесс роли живёт в `~/.claude/roles/<r>.md`, перенести туда.
|
||||
|
||||
**Самотест хуков** — любое `WARN … (ожидалось N)` означает, что хук установлен, но не делает того, что должен. Причины по убыванию частоты: правлен руками; `python3` не в PATH сессии; settings.json зовёт другой путь. Чинится `install.sh`; если и после него WARN — приложить строку к находке, это дефект пакета.
|
||||
@ -42,13 +43,13 @@ description: Диагностика обвязки Claude Code (claude-harness)
|
||||
- «code-paths: умолчание» при коде вне `src/` → создать `.claude/code-paths`.
|
||||
- «SDD workspace: …» при отсутствии активной фичи → хвост незакрытой фичи; проверить чеклист закрытия.
|
||||
- `WARN .bounded не в .gitignore` → добавить строку, иначе уедет в main.
|
||||
- `WARN нет allow-правила для git push origin --delete` → находка владельцу: готовую строку из WARN он сам добавляет в `.claude/settings.json` проекта (решение владельца 2, чеклист закрытия п. 4); агент `settings.json` не правит. `ok allow …` называет файл, где правило найдено: проектный `settings.json`, `settings.local.json` или глобальный.
|
||||
- `WARN нет allow-правила для git push origin --delete` → находка владельцу (чеклист закрытия п. 4). Правило ставит `bash install.sh`. При `--no-permissions` готовую строку из WARN владелец сам добавляет в `.claude/settings.json` проекта. Агент `settings.json` не правит. `ok allow …` называет файл, где правило найдено: проектный `settings.json`, `settings.local.json` или глобальный.
|
||||
- `WARN tea не отвечает` → `tea logins list`; токен нужен со scopes read:user, write:repository, write:issue.
|
||||
- «workspace (не git)» → режим нескольких репозиториев. `WARN … битая ссылка` → репозиторий направления переименован или файл удалён: `bash ~/.claude/harness/link-roles.sh <workspace>`. `WARN нет <dir>/.claude/roles/<r>.md` → создать проектный файл роли из `~/.claude/harness/project-template/role-repo/ROLE.md`.
|
||||
- `WARN индекс auto-memory > 3 КБ` → индекс снова стал журналом состояния; свернуть закрытое в `registry-closed.md`, уроки — в `notes/` направления (политика памяти в глобальном CLAUDE.md).
|
||||
|
||||
## Чинится само (без вопроса)
|
||||
`install.sh`, `link-roles.sh` в workspace, `codegraph init` в worktree, `.claude/code-paths`, строка в `.gitignore` на ветке. Всё остальное — находка пользователю: профиль shell, плагины, токены, удаление чего-либо.
|
||||
`install.sh --no-permissions` (агент так обновляет хуки и файлы; права ставит владелец своим `bash install.sh`), `link-roles.sh` в workspace, `codegraph init` в worktree, `.claude/code-paths`, строка в `.gitignore` на ветке. Всё остальное — находка пользователю: профиль shell, плагины, токены, права, удаление чего-либо.
|
||||
|
||||
## Чего не делать
|
||||
Не «улучшать» хуки по месту: их источник — пакет claude-harness, правка идёт туда и ставится install.sh. Не отключать хук ради того, чтобы «прошло».
|
||||
|
||||
@ -34,7 +34,7 @@ PY
|
||||
)
|
||||
while IFS= read -r line; do case "$line" in "bad "*) info "${line#bad } — не JSON, allow-правила не прочитаны" ;; esac; done <<< "$r"
|
||||
case "$r" in *"ok "*) ok "allow git push origin --delete (${r##*ok })" ;;
|
||||
*) warn "нет allow-правила для git push origin --delete (закрытие, п. 4) — строка в .claude/settings.json → permissions.allow: \"Bash(git push origin --delete:*)\"" ;; esac
|
||||
*) warn "нет allow-правила для git push origin --delete (закрытие, п. 4) — bash install.sh или строка в .claude/settings.json → permissions.allow: \"Bash(git push origin --delete:*)\"" ;; esac
|
||||
}
|
||||
|
||||
echo "## harness-doctor $(date +%F' '%T)"
|
||||
@ -56,6 +56,25 @@ done
|
||||
[ -f "$CC/hooks/harness_target.py" ] && ok "hooks/harness_target.py (ядро guard-main и require-plan)" || miss "hooks/harness_target.py (bash install.sh)"
|
||||
[ -x "$CC/harness/adopt-project.sh" ] && ok "~/.claude/harness/ (adopt/new-project)" || miss "~/.claude/harness/adopt-project.sh"
|
||||
[ -x "$CC/harness/link-roles.sh" ] && ok "~/.claude/harness/link-roles.sh" || miss "~/.claude/harness/link-roles.sh"
|
||||
if [ -f "$CC/harness/permissions.json" ]; then # allow-правила цикла фичи, которые ставит install.sh
|
||||
pr=$(python3 - "$CC/settings.json" "$CC/harness/permissions.json" <<'PY' 2>/dev/null
|
||||
import json, sys
|
||||
try:
|
||||
s = json.load(open(sys.argv[1], encoding="utf-8"))
|
||||
except Exception:
|
||||
s = {}
|
||||
have = (s.get("permissions") or {}).get("allow") or [] if isinstance(s, dict) else []
|
||||
need = json.load(open(sys.argv[2], encoding="utf-8"))["permissions"]["allow"]
|
||||
miss = [r for r in need if r not in have]
|
||||
print("%d/%d %s" % (len(need) - len(miss), len(need), " ".join(miss)))
|
||||
PY
|
||||
)
|
||||
case "$pr" in
|
||||
"") warn "permissions обвязки: не прочитаны ($CC/harness/permissions.json)" ;;
|
||||
*" ") ok "permissions обвязки: ${pr% }" ;;
|
||||
*) warn "permissions обвязки: ${pr%% *}, нет: ${pr#* } — bash install.sh (или осознанно --no-permissions)" ;;
|
||||
esac
|
||||
else miss "permissions обвязки (harness/permissions.json — bash install.sh)"; fi
|
||||
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)"
|
||||
|
||||
@ -4,7 +4,7 @@ CC="$TMP/cc"; mkdir -p "$CC/hooks" "$CC/roles" "$CC/skills/role" "$CC/agents" "$
|
||||
cp "$ROOT"/hooks/*.sh "$ROOT/hooks/harness_target.py" "$CC/hooks/"; chmod +x "$CC/hooks/"*.sh
|
||||
mkdir -p "$CC/skills"; cp -R "$ROOT/skills/harness-doctor" "$CC/skills/"
|
||||
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"
|
||||
cp "$ROOT/link-roles.sh" "$ROOT/adopt-project.sh" "$ROOT/global/permissions.json" "$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"
|
||||
@ -26,6 +26,7 @@ assert_grep "самотест: Write на main" 'ok +guard-main: Write на main
|
||||
assert_grep "самотест: Write на ветке" 'ok +guard-main: Write на ветке → exit 0' <(printf '%s' "$out")
|
||||
assert_grep "корпус хуков сходится" 'ok +корпус хуков: [0-9]+/[0-9]+' <(printf '%s' "$out")
|
||||
assert_grep "заголовок раздела проекта (M-8)" '^## Проект: /' <(printf '%s' "$out")
|
||||
assert_grep "машина: правил обвязки нет — WARN с подсказкой install.sh (#24)" 'WARN +permissions обвязки: 0/[0-9]+.*bash install\.sh' <(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")
|
||||
@ -53,6 +54,14 @@ printf '{"permissions":{"allow":["Bash(git push origin --delete:*)"]},"hooks":{}
|
||||
out=$(CLAUDE_CONFIG_DIR="$CC" HOME="$TMP/home" bash "$D" "$W" 2>&1)
|
||||
assert_grep "workspace: правило в глобальном settings.json — ok" 'ok +allow git push origin --delete \(.*/cc/settings\.json\)' <(printf '%s' "$out")
|
||||
cp "$TMP/settings.bak" "$CC/settings.json"
|
||||
# правила обвязки (#24): все на месте — ok; нет harness/permissions.json — MISS
|
||||
python3 -c 'import json,sys; s=json.load(open(sys.argv[1])); s["permissions"]=json.load(open(sys.argv[2]))["permissions"]; json.dump(s,open(sys.argv[1],"w"))' "$CC/settings.json" "$ROOT/global/permissions.json"
|
||||
out=$(CLAUDE_CONFIG_DIR="$CC" HOME="$TMP/home" bash "$D" "$W" 2>&1)
|
||||
assert_grep "машина: правила обвязки на месте — ok" 'ok +permissions обвязки: [0-9]+/[0-9]+' <(printf '%s' "$out")
|
||||
rm "$CC/harness/permissions.json"
|
||||
out=$(CLAUDE_CONFIG_DIR="$CC" HOME="$TMP/home" bash "$D" "$W" 2>&1)
|
||||
assert_grep "машина: нет harness/permissions.json — MISS" 'MISS +permissions обвязки' <(printf '%s' "$out")
|
||||
cp "$TMP/settings.bak" "$CC/settings.json"
|
||||
rm -f "$CC/roles/qa.md"
|
||||
out=$(CLAUDE_CONFIG_DIR="$CC" HOME="$TMP/home" bash "$D" "$W" 2>&1)
|
||||
assert_grep "машина: MISS roles/qa.md" 'MISS +roles/qa.md' <(printf '%s' "$out")
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# install.sh: новые файлы ставятся, повторный запуск не дублирует хуки, --check их видит.
|
||||
TH="$TMP/home"; CLAUDE_CONFIG_DIR="$TH/.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"
|
||||
echo '{"permissions":{"allow":["Bash(echo:*)"]},"hooks":{"SessionStart":[{"matcher":"startup","hooks":[{"type":"command","command":"\"$HOME/.claude/hooks/harness-check.sh\""}]}]}}' > "$CLAUDE_CONFIG_DIR/settings.json"
|
||||
HOME="$TH" CLAUDE_CONFIG_DIR="$CLAUDE_CONFIG_DIR" bash "$ROOT/install.sh" >/dev/null 2>&1; HOME="$TH" CLAUDE_CONFIG_DIR="$CLAUDE_CONFIG_DIR" 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
|
||||
@ -26,3 +26,25 @@ assert_grep "--check: harness_target.py" 'ok +hooks/harness_target\.py' <(printf
|
||||
rm "$CC/agents/briefing.md"
|
||||
out=$(HOME="$TH" CLAUDE_CONFIG_DIR="$CLAUDE_CONFIG_DIR" bash "$ROOT/install.sh" --check 2>&1)
|
||||
assert_grep "--check: MISS briefing" 'MISS +.*briefing' <(printf '%s' "$out")
|
||||
# allow-правила цикла фичи (#24): ставятся из global/permissions.json, чужие не трогаются, --no-permissions — отказ
|
||||
P="$ROOT/global/permissions.json"
|
||||
assert_file "global/permissions.json есть" "$P"
|
||||
assert_file "harness/permissions.json установлен" "$CC/harness/permissions.json"
|
||||
perm(){ python3 -c 'import json,sys; s=json.load(open(sys.argv[1])); a=s.get("permissions",{}).get("allow",[]); r=json.load(open(sys.argv[2]))["permissions"]["allow"]; print(sum(a.count(x)==1 for x in r), len(r), a.count("Bash(echo:*)"), len(a)-len(set(a)))' "$1" "$P"; }
|
||||
read -r have total own dup <<< "$(perm "$CC/settings.json")"
|
||||
assert_eq "список правил обвязки прочитан и не пуст" "$([ "${total:-0}" -gt 0 ] 2>/dev/null && echo да)" "да"
|
||||
assert_eq "правила обвязки в settings.json — все, по одному разу после двух установок" "$have" "$total"
|
||||
assert_eq "чужое правило сохранено" "$own" "1"
|
||||
assert_eq "дублей в permissions.allow нет" "$dup" "0"
|
||||
assert_eq "в списке нет merge и push в main" "$(grep -cE 'pulls merge|push origin main|"Bash\(git push:\*\)"|"Bash\(git:\*\)"|"Bash"' "$P")" "0"
|
||||
assert_eq "EnterWorktree в списке нет: allow-правило не снимает запрос (#25)" "$(grep -c 'EnterWorktree' "$P")" "0"
|
||||
out=$(HOME="$TH" CLAUDE_CONFIG_DIR="$CLAUDE_CONFIG_DIR" bash "$ROOT/install.sh" --check 2>&1)
|
||||
assert_grep "--check: правила обвязки на месте" 'ok +permissions обвязки: [0-9]+/[0-9]+' <(printf '%s' "$out")
|
||||
T2="$TMP/home2"; C2="$T2/.claude"; mkdir -p "$C2"; echo '{}' > "$C2/settings.json"
|
||||
out=$(HOME="$T2" CLAUDE_CONFIG_DIR="$C2" bash "$ROOT/install.sh" --no-permissions 2>&1); rc=$?
|
||||
assert_eq "--no-permissions: exit 0" "$rc" "0"
|
||||
assert_grep "--no-permissions: говорит, что правила пропущены" 'permissions.*--no-permissions' <(printf '%s' "$out")
|
||||
read -r have total own dup <<< "$(perm "$C2/settings.json")"
|
||||
assert_eq "--no-permissions: правил обвязки не добавлено" "$have" "0"
|
||||
assert_file "--no-permissions: хуки всё равно ставятся" "$C2/hooks/guard-main.sh"
|
||||
assert_grep "--no-permissions: --check предупреждает" 'WARN +permissions обвязки: 0/' <(printf '%s' "$out")
|
||||
|
||||
@ -49,4 +49,5 @@ A="$TMP/adopt"; git init -q -b main "$A" && git -C "$A" commit -q --allow-empty
|
||||
out=$(bash "$ROOT/adopt-project.sh" "$A" 2>&1); rc=$?
|
||||
assert_eq "adopt (старый режим): git-репозиторий → exit 0" "$rc" "0"
|
||||
assert_grep "adopt (старый режим): печатает allow-правило (B3)" '"Bash\(git push origin --delete:\*\)"' <(printf '%s' "$out")
|
||||
assert_grep "adopt: строка — для установки с --no-permissions (#24)" 'no-permissions' <(printf '%s' "$out")
|
||||
assert_eq "adopt (старый режим): settings.json не создаёт" "$([ -e "$A/.claude/settings.json" ] && echo есть || echo нет)" "нет"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user