С Claude Code 2.1.206 вход в worktree вне .claude/worktrees/ спрашивает подтверждение при любых allow-правилах. EnterWorktree с именем создаёт worktree в <repo>/.claude/worktrees/<slug> на ветке worktree-<slug> от origin/main и входит без вопроса; ветка переименовывается в feat/<id>-<slug>. - roles/dev.md: «Исполнение» — EnterWorktree с именем; п. 1 закрытия — archive-<id>; п. 4 — ExitWorktree (keep) снимает блокировку сессии, затем git worktree remove и branch -D. - .claude/worktrees/ в .gitignore: adopt-project.sh, project-template, этот репозиторий; doctor.sh — WARN, если не игнорируется. - Спека hook-guards: сценарии «worktree внутри репозитория» для guard-main и require-plan; тесты закрепляют уже верное поведение. - HARNESS.md (шаг 4, п. 4, подтверждения), SKILL.md, CHANGELOG 0.5.1, CLAUDE.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
56 lines
4.4 KiB
Bash
56 lines
4.4 KiB
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"
|
||
assert_grep "new-project --workspace: печатает allow-правило (B3)" '"Bash\(git push origin --delete:\*\)"' <(printf '%s' "$out")
|
||
# 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")" "моё"
|
||
# старые режимы не затронуты
|
||
P="$TMP/oldp"
|
||
out=$(bash "$ROOT/new-project.sh" "$P" 2>&1); rc=$?
|
||
assert_eq "new-project (старый режим): exit 0" "$rc" "0"
|
||
assert_eq "new-project (старый режим): git" "$([ -d "$P/.git" ] && echo git || echo no)" "git"
|
||
assert_file "new-project (старый режим): ARCHITECTURE.md" "$P/ARCHITECTURE.md"
|
||
assert_grep "new-project (старый режим): печатает allow-правило (B3)" '"Bash\(git push origin --delete:\*\)"' <(printf '%s' "$out")
|
||
assert_grep "new-project: .claude/worktrees/ в .gitignore (#25)" '^\.claude/worktrees/$' "$P/.gitignore"
|
||
out=$(bash "$ROOT/adopt-project.sh" "$TMP/notgit" 2>&1); rc=$?
|
||
assert_eq "adopt (старый режим): не git → exit 1" "$rc" "1"
|
||
A="$TMP/adopt"; git init -q -b main "$A" && git -C "$A" commit -q --allow-empty -m init
|
||
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 нет)" "нет"
|
||
assert_grep "adopt: .claude/worktrees/ в .gitignore (#25)" '^\.claude/worktrees/$' "$A/.gitignore"
|