DRAFT-файлы для autonomous /loop окон (5 ролей: analyst/backend/frontend/ code-reviewer/qa-tester). НЕ для invoke через Task tool — только как --append-system-prompt для standalone Claude Code windows с /loop. Все файлы помечены status:draft. Не активны до сборки .claude/commands/ work-as-* wrappers + Forgejo labels + bot-PATs (см. runbook multi_agent_autonomous_workflow.md в vault). Также fix .gitignore: .claude/ → .claude/* чтобы whitelist'ы !.claude/agents/** работали (git не allow re-include children of ignored directory). Refs: runbooks/multi_agent_autonomous_workflow.md (vault)
115 lines
4.5 KiB
Markdown
115 lines
4.5 KiB
Markdown
---
|
||
name: _autonomous_pickup
|
||
description: "[SHARED SNIPPET — do not invoke directly] Forgejo queue pickup logic, импортируется во все auto-*.md агенты. Содержит claim/state-transition contract."
|
||
status: draft
|
||
created_at: 2026-05-27
|
||
---
|
||
|
||
# Autonomous queue pickup — shared contract
|
||
|
||
> **NOT a standalone agent.** Этот файл — общая инструкция, которую копи-пастят
|
||
> внутрь каждого `auto-*.md`. Содержит claim/lifecycle/kill-switch логику.
|
||
|
||
## Forgejo API endpoints (на основе bot-PAT в окружении)
|
||
|
||
```bash
|
||
# Базовый URL и auth — приходят через env vars при запуске окна:
|
||
# FORGEJO_BASE_URL=https://git.gendsgn.ru/api/v1
|
||
# FORGEJO_PAT=<your bot's PAT>
|
||
# FORGEJO_REPO=lekss361/gendesign
|
||
|
||
curl_forgejo() {
|
||
curl -sS -H "Authorization: token $FORGEJO_PAT" \
|
||
-H "Content-Type: application/json" \
|
||
"$FORGEJO_BASE_URL/$1" "${@:2}"
|
||
}
|
||
```
|
||
|
||
## Kill-switch check (выполняй ПЕРВЫМ делом в каждом /loop tick)
|
||
|
||
```bash
|
||
# Проверка через 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
|
||
|
||
```bash
|
||
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
|
||
|
||
```bash
|
||
ISSUE=$(echo "$NEXT" | cut -f1)
|
||
|
||
# 1. Assign self
|
||
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 не перехвачен
|
||
ASSIGNEE=$(curl_forgejo "repos/$FORGEJO_REPO/issues/$ISSUE" | jq -r '.assignees[0].login // ""')
|
||
if [[ "$ASSIGNEE" != "$BOT_USERNAME" ]]; then
|
||
echo "result: lost race for #$ISSUE (claimed by $ASSIGNEE)"
|
||
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 |
|
||
|
||
## Stale-claim cleanup (cron на VPS — отдельная задача)
|
||
|
||
```bash
|
||
# Каждые 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
|
||
|
||
1. **3 итерации подряд idle** → `sleep = min(current * 1.5, 60m)`
|
||
2. **3 итерации подряд success** → `sleep = max(current * 0.8, 5m)`
|
||
3. **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 |
|