Phase 2 prep для autonomous multi-window workflow (Phase 1 — PR #608). Изменения: 1. `.claude/agents/_autonomous_pickup.md` — добавлена Pre-flight checklist: - Шаг 1: где взять PAT из vault meta/00_credentials.md - Шаг 2: env vars + git identity (GIT_AUTHOR_NAME/EMAIL — критично!) - Шаг 3: bot-remote setup (отдельный remote с bot's PAT для audit log) - Шаг 4: sanity check identity (curl /user → должен вернуть bot-<role>) Без этого commits писались бы под lekss361, не под ботом. 2. `.claude/commands/work-as-*.md` (5 файлов) — slash-commands для запуска окон: - work-as-analyst (sonnet, /loop 30m) - work-as-backend (sonnet, /loop dynamic) - work-as-frontend (sonnet, /loop dynamic) - work-as-reviewer (opus, /loop 5m) - work-as-qa (sonnet, /loop 10m) Каждый command: - Описывает pre-flight для конкретной роли - Ссылается на соответствующий .claude/agents/auto-*.md - Перечисляет hard rules для роли - Объясняет per-tick workflow 3. Forgejo labels (16 шт, созданы через REST API — не в этом diff): scope/{backend,frontend,db,qa,devops} status/{ready,wip,review,qa,done,blocked} priority/{p0,p1,p2} pause-bots, needs-human Что осталось для активации: - 5 Forgejo bot-users + PATs (уже созданы 2026-05-27, см. vault) - ~/.claude/secrets/forgejo-bot-*.token — выгрузка PAT'ов из vault user'ом вручную - Stale-claim cron (отдельный PR) - Optional: multi-window launcher через tmux/wezterm Refs: PR #608 (Phase 1), runbooks/multi_agent_autonomous_workflow.md
9.4 KiB
| name | description | status | created_at |
|---|---|---|---|
| _autonomous_pickup | [SHARED SNIPPET — do not invoke directly] Forgejo queue pickup logic, импортируется во все auto-*.md агенты. Содержит claim/state-transition contract. | draft | 2026-05-27 |
Autonomous queue pickup — shared contract
NOT a standalone agent. Этот файл — общая инструкция, которую копи-пастят внутрь каждого
auto-*.md. Содержит claim/lifecycle/kill-switch логику.
Pre-flight checklist (ОБЯЗАТЕЛЬНО до /loop запуска)
Это критично. Без правильной настройки git identity → commits будут писаться под user'ом (lekss361), не под ботом. Audit trail сломается.
Шаг 1 — Получить PAT из vault
PAT'ы для всех 5 ботов хранятся в meta/00_credentials.md (vault, sensitive,
не в git). Один раз выгрузи их в локальные secrets:
# Локация — никогда не коммитится (gitignored через ~/.gitignore_global)
New-Item -ItemType Directory -Force ~/.claude/secrets | Out-Null
# Для каждого бота — скопируй PAT из vault в файл (открой meta/00_credentials.md,
# секция "Forgejo bot-users", найди PAT для bot-<role>, вставь сюда):
# ~/.claude/secrets/forgejo-bot-analyst.token
# ~/.claude/secrets/forgejo-bot-backend.token
# ~/.claude/secrets/forgejo-bot-frontend.token
# ~/.claude/secrets/forgejo-bot-reviewer.token
# ~/.claude/secrets/forgejo-bot-qa.token
#
# Пример (с placeholder — НЕ commit'ить настоящий token):
# "<PAT-FROM-VAULT>" | Set-Content ~/.claude/secrets/forgejo-bot-<role>.token -NoNewline
⚠️ Проверь что ~/.claude/secrets/ есть в ~/.gitignore_global (или эквивалентном).
Шаг 2 — Env vars + git identity (per окно)
Замени <role> на свою роль (analyst/backend/frontend/reviewer/qa):
$ROLE = "backend" # ← ИЗМЕНИ ПЕРЕД ЗАПУСКОМ
$BOT = "bot-$ROLE"
# Forgejo API credentials
$env:FORGEJO_TOKEN = (Get-Content ~/.claude/secrets/forgejo-$BOT.token -Raw).Trim()
$env:BOT_USERNAME = $BOT
$env:FORGEJO_URL = "https://git.gendsgn.ru"
$env:FORGEJO_REPO = "lekss361/gendesign"
# Git identity — КРИТИЧНО, иначе commit author будет user'а (lekss361)
$env:GIT_AUTHOR_NAME = $BOT
$env:GIT_AUTHOR_EMAIL = "$BOT@gendsgn.local"
$env:GIT_COMMITTER_NAME = $BOT
$env:GIT_COMMITTER_EMAIL = "$BOT@gendsgn.local"
Шаг 3 — Bot-remote (для git push audit-log)
Существующий forgejo remote использует lekss361's PAT — push через него
запишется в Forgejo audit log как lekss361. Создай отдельный bot-remote:
git remote remove forgejo-bot 2>$null
git remote add forgejo-bot "https://$($env:BOT_USERNAME):$($env:FORGEJO_TOKEN)@git.gendsgn.ru/lekss361/gendesign.git"
# Везде в workflow:
# git push forgejo-bot feat/X (НЕ git push forgejo)
Шаг 4 — Sanity check (verify identity)
# 4a. PAT принадлежит правильному боту
$me = curl -sS -H "Authorization: token $env:FORGEJO_TOKEN" "$env:FORGEJO_URL/api/v1/user" | ConvertFrom-Json
if ($me.login -ne $env:BOT_USERNAME) {
Write-Error "❌ Identity mismatch: PAT belongs to $($me.login), expected $env:BOT_USERNAME"
exit 1
}
Write-Host "✓ PAT belongs to $($me.login)"
# 4b. Git identity (на сессию)
Write-Host "✓ Commits will be authored as: $env:GIT_AUTHOR_NAME <$env:GIT_AUTHOR_EMAIL>"
# 4c. Bot-remote configured
git remote -v | Select-String "forgejo-bot"
Только после 4a/4b/4c ✓ — запускай /loop.
Forgejo API endpoints (использует env vars из Шага 2)
curl_forgejo() {
curl -sS -H "Authorization: token $FORGEJO_TOKEN" \
-H "Content-Type: application/json" \
"$FORGEJO_URL/api/v1/$1" "${@:2}"
}
Kill-switch check (выполняй ПЕРВЫМ делом в каждом /loop tick)
# Проверка через meta-issue с label "pause-bots"
if curl_forgejo "repos/$FORGEJO_REPO/issues?labels=pause-bots&state=open&limit=1" \
| jq -e 'length > 0' > /dev/null; then
echo "result: paused (pause-bots active)"
exit 0 # /loop спит до следующего тика
fi
Pickup query — по scope
SCOPE="backend" # ∈ {backend, frontend, db, qa, devops}
NEXT=$(curl_forgejo \
"repos/$FORGEJO_REPO/issues?state=open&labels=scope/$SCOPE,status/ready&assigned_to=none&sort=newest&limit=1" \
| jq -r '.[0] | if . then [.number, .title] | @tsv else "" end')
if [[ -z "$NEXT" ]]; then
echo "result: idle, no work for scope/$SCOPE"
exit 0
fi
Claim — atomic-ish через label transition
Race window note: Forgejo не поддерживает conditional-update (ETag/If-Match для issue PATCH). Между шагом 1 и 3 другой worker теоретически может тоже claim'нуть. Verify checks
length == 1 AND .assignees[0] == me— иначе откатываемся.
ISSUE=$(echo "$NEXT" | cut -f1)
# 1. Assign self (atomic на стороне Forgejo для самого set-assignees, но не для transition)
curl_forgejo "repos/$FORGEJO_REPO/issues/$ISSUE" -X PATCH \
-d "{\"assignees\": [\"$BOT_USERNAME\"]}"
# 2. Transition ready → wip
curl_forgejo "repos/$FORGEJO_REPO/issues/$ISSUE/labels" -X POST \
-d '{"labels": ["status/wip"]}'
curl_forgejo "repos/$FORGEJO_REPO/issues/$ISSUE/labels/status/ready" -X DELETE
# 3. Verify claim не перехвачен — STRICT check
ISSUE_JSON=$(curl_forgejo "repos/$FORGEJO_REPO/issues/$ISSUE")
ASSIGNEE_COUNT=$(echo "$ISSUE_JSON" | jq '.assignees | length')
ASSIGNEE=$(echo "$ISSUE_JSON" | jq -r '.assignees[0].login // ""')
if [[ "$ASSIGNEE_COUNT" != "1" || "$ASSIGNEE" != "$BOT_USERNAME" ]]; then
echo "result: lost race for #$ISSUE (assignees=$ASSIGNEE_COUNT, first=$ASSIGNEE) — releasing"
# Best-effort rollback — снять wip, вернуть ready (не критично если не получится)
curl_forgejo "repos/$FORGEJO_REPO/issues/$ISSUE/labels" -X POST \
-d '{"labels": ["status/ready"]}'
curl_forgejo "repos/$FORGEJO_REPO/issues/$ISSUE/labels/status/wip" -X DELETE
exit 0
fi
State transitions reference
| От → К | Кто переключает | Условие |
|---|---|---|
(new) → status/ready |
auto-analyst | issue декомпозирован, deps удовлетворены |
status/ready → status/wip |
auto-backend / auto-frontend | claim успешный |
status/wip → status/review |
worker | PR открыт |
status/review → status/qa |
auto-code-reviewer | APPROVE + merge |
status/review → status/blocked |
auto-code-reviewer | BLOCK/FIX verdict |
status/qa → status/done |
auto-qa-tester | smoke OK, issue closed |
status/qa → status/blocked |
auto-qa-tester | smoke FAIL |
status/blocked → status/ready |
only human | manual unblock |
любой + pause-bots присутствует |
(никто не работает) | kill-switch |
Pause-bots поведение mid-work
Если pause-bots label появился ПОКА worker уже в wip:
- НЕ abort — finish текущий commit + push (минимизирует потерю работы)
- Open PR как обычно → PR попадёт в queue
status/review(но reviewer тоже paused → PR не merge'нётся) - result: PR #N opened, then paused due to kill-switch
- После un-pause — reviewer подхватит PR
Это НЕ release claim на исходный issue — он остаётся wip+assigned до merge.
Stale-claim cleanup — TODO (отдельный PR / cron)
⚠️ НЕ имплементировано в текущем PR. Без stale cleanup worker crash = issue застрянет в
status/wipforever. До имплементации — мониторь вручную.
# Каждые 30 минут — освобождать issues застрявшие в wip >4h
curl_forgejo "repos/$FORGEJO_REPO/issues?labels=status/wip&state=open" \
| jq -r '.[] | select(.updated_at < (now - 4*3600 | todate)) | .number' \
| while read i; do
curl_forgejo "repos/$FORGEJO_REPO/issues/$i" -X PATCH -d '{"assignees": []}'
curl_forgejo "repos/$FORGEJO_REPO/issues/$i/labels" -X POST -d '{"labels":["status/ready"]}'
curl_forgejo "repos/$FORGEJO_REPO/issues/$i/labels/status/wip" -X DELETE
done
Self-throttle rules
- 3 итерации подряд idle →
sleep = min(current * 1.5, 60m) - 3 итерации подряд success →
sleep = max(current * 0.8, 5m) - 24h ничего не закрыл → result: idle 24h, эскалация (label
needs-human)
Error escalation
| Ошибка | Действие |
|---|---|
| HTTP 401/403 от Forgejo | PAT истёк / отозван → result: AUTH_ERROR, остановка окна |
| HTTP 500 от Forgejo | result: forgejo down, sleep 30m |
| Subagent error 3× на одной issue | +status/blocked +needs-human, отпустить assignee, next issue |