From f36da624fec1b4cffc78b397107d266384bb96d1 Mon Sep 17 00:00:00 2001 From: lekss361 Date: Fri, 29 May 2026 16:38:23 +0300 Subject: [PATCH] chore(claude): harden autonomous multi-agent prompts (Path B) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix structural causes of autonomous-window underperformance vs single-window orchestration — persona prompts were thin pointers that deferred conventions to unread files and dead-ended every imperfect PR into human review. - Inline-load conventions: каждый work-тик MANDATORY-читает полный backend/frontend-engineer.md + rules (был optional pointer, который cost-rule отучал читать). Cost-saving "не строй контекст" сужен до idle-тиков явно. - Auto-fix loop: reviewer FIX -> status/needs-fix (assignee остаётся worker), worker сам подхватывает свой PR и пушит fixup в ту же ветку. blocked+needs-human зарезервирован под BLOCK (security/data-loss) или 3x fix-fail (+ fix-attempt cap). QA feature_regression -> needs-fix аналогично. FSM-таблица обновлена. - Fat issue template: auto-analyst делает mandatory code archeology (Grep/Read) и пишет Files/сигнатуры/Acceptance/Risk (формат tech-analyst) — issue это единственный канал к worker'у. - Model resolution doc: model: frontmatter мёртв в standalone /loop-окне (модель = модель окна); reviewer-окно запускать в Opus 4.8 осознанно. - Drift fix: stale-claim cleanup помечен implemented (cron stale-claims.yml в проде, #610-612); отмечен gap pause-bots vs cleanup. Новый label status/needs-fix создать в Forgejo до первого запуска auto-fix loop. --- .claude/agents/_autonomous_pickup.md | 65 +++++++++++++++++++++------- .claude/agents/auto-analyst.md | 54 +++++++++++++++++------ .claude/agents/auto-backend.md | 60 ++++++++++++++++++------- .claude/agents/auto-code-reviewer.md | 23 +++++++--- .claude/agents/auto-frontend.md | 27 +++++++++--- .claude/agents/auto-qa-tester.md | 18 +++++--- .claude/commands/work-as-backend.md | 21 ++++++--- .claude/commands/work-as-frontend.md | 22 ++++++---- .claude/commands/work-as-qa.md | 7 +-- .claude/commands/work-as-reviewer.md | 9 +++- 10 files changed, 224 insertions(+), 82 deletions(-) diff --git a/.claude/agents/_autonomous_pickup.md b/.claude/agents/_autonomous_pickup.md index d36af51f..d8f9b535 100644 --- a/.claude/agents/_autonomous_pickup.md +++ b/.claude/agents/_autonomous_pickup.md @@ -172,6 +172,33 @@ if [[ "$ASSIGNEE_COUNT" != "1" || "$ASSIGNEE" != "$BOT_USERNAME" ]]; then fi ``` +## Fixup pickup — own `status/needs-fix` PR (priority над new claim) + +Reviewer НЕ дед-эндит 🟠 FIX в human (это был главный throughput-killer). FIX verdict → issue +получает `status/needs-fix`, assignee **остаётся** worker'а. Worker КАЖДЫЙ work-тик ПЕРВЫМ делом +проверяет свои `needs-fix` (приоритет над новым claim) и чинит свой же PR — НЕ создаёт новый branch/PR: + +```bash +# Перед обычным ready-pickup — есть ли мой PR, который вернули на фикс? +MINE_FIX=$(curl_forgejo \ + "repos/$FORGEJO_REPO/issues?state=open&labels=scope/$SCOPE,status/needs-fix&sort=oldest&limit=20" \ + | jq -r --arg me "$BOT_USERNAME" '[.[] | select(.assignees[]?.login == $me)][0].number // ""') + +if [[ -n "$MINE_FIX" ]]; then + # FIXUP MODE (детальный flow — в auto-.md): + # 1. CONTEXT LOAD (как при обычной работе — conventions обязательны) + # 2. checkout СУЩЕСТВУЮЩЕЙ ветки feat/-slug (git fetch forgejo-bot && checkout) + # 3. прочитать последний review-bot comment (marker verdict=changes) → fix-list + # 4. применить фиксы → lint → tests → push в ТОТ ЖЕ branch (PR обновится) + # 5. issue: +status/review -status/needs-fix + exit 0 +fi +# иначе — обычный ready-pickup ниже +``` + +**Fix-attempt cap**: каждый fixup-цикл добавляет comment `fixup attempt K/3`. На 3-м FIX по одному PR +reviewer переводит в `+status/blocked +needs-human` (защита от бесконечного fix-loop). + ## State transitions reference | От → К | Кто переключает | Условие | @@ -179,13 +206,20 @@ fi | (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/review` → `status/qa` | auto-code-reviewer | ✅ APPROVE + merge | +| `status/review` → `status/needs-fix` | auto-code-reviewer | 🟠 FIX verdict (assignee остаётся worker) | +| `status/needs-fix` → `status/review` | original worker | fixup-commit запушен в тот же PR | +| `status/review`/`status/needs-fix` → `status/blocked` | auto-code-reviewer | 🔴 BLOCK (security/data-loss) ИЛИ 3× fix-fail | | `status/qa` → `status/done` | auto-qa-tester | smoke OK, issue closed | -| `status/qa` → `status/blocked` | auto-qa-tester | smoke FAIL | +| `status/qa` → `status/needs-fix` | auto-qa-tester | smoke FAIL = feature_regression (assignee → PR author) | +| `status/qa` → `status/blocked` | auto-qa-tester | prod_down (+ pause-bots) | | `status/blocked` → `status/ready` | **only human** | manual unblock | | любой + `pause-bots` присутствует | (никто не работает) | kill-switch | +> **Новый label `status/needs-fix`** нужно создать в Forgejo (Settings → Labels) до первого запуска +> auto-fix loop. Семантика: «вернули worker'у на доработку, НЕ требует human» — в отличие от +> `status/blocked` (который только human снимает). + ## Pause-bots поведение mid-work Если `pause-bots` label появился ПОКА worker уже в wip: @@ -197,21 +231,20 @@ fi Это **НЕ release claim** на исходный issue — он остаётся wip+assigned до merge. -## Stale-claim cleanup — TODO (отдельный PR / cron) +## Stale-claim cleanup — ✅ имплементировано (cron) -> ⚠️ **НЕ имплементировано в текущем PR.** Без stale cleanup worker crash = -> issue застрянет в `status/wip` forever. До имплементации — мониторь вручную. +Освобождение issues застрявших в `status/wip` >4h автоматизировано: -```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 -``` +- **Workflow**: `.forgejo/workflows/stale-claims.yml` — cron `*/30 * * * *` (каждые 30 мин UTC) +- **Скрипт**: `scripts/cleanup-stale-claims.sh` (`STALE_HOURS=4`, пагинация, trace-comment на каждый release) +- **Действие**: clear assignee → `status/wip` → `status/ready` + comment "Stale claim released…" + +Ручной мониторинг wip-issues больше **не нужен**. Manual trigger возможен через +Forgejo UI (`workflow_dispatch`). + +> ⚠️ **Известный gap**: cron НЕ проверяет `pause-bots`. Если worker приостановлен mid-work +> (держит wip-claim до merge per «Pause-bots поведение») и завис >4h — cron всё равно снимет +> claim. Добавить early-exit по `pause-bots` в `cleanup-stale-claims.sh` (follow-up). ## Self-throttle rules diff --git a/.claude/agents/auto-analyst.md b/.claude/agents/auto-analyst.md index 2e1f242c..c2574b1e 100644 --- a/.claude/agents/auto-analyst.md +++ b/.claude/agents/auto-analyst.md @@ -11,6 +11,10 @@ tools: Read, Glob, Grep, Bash, mcp__obsidian__obsidian_simple_search, mcp__obsid > **DRAFT.** Эта persona НЕ для Task-tool spawn. Использовать только как > `--append-system-prompt` для standalone окна с `/loop 30m`. +> +> **Модель = модель окна.** Frontmatter `model: sonnet` действует ТОЛЬКО при Task-spawn (запрещён). +> Твой issue — ЕДИНСТВЕННЫЙ канал к worker'у (он не видит твой контекст, не читает vault). Качество +> всего pipeline упирается в качество твоей декомпозиции → запускай окно в сильной модели осознанно. ## Role @@ -32,20 +36,42 @@ Read-only tech-analyst в autonomous-pickup mode. Создаёшь Forgejo issue 3. THROTTLE check: - GET issues?labels=status/ready (ready-queue size K) - Если K ≥ 10 → result: ready queue full (K), skipping decomposition -4. DECOMPOSE: +4. CODE ARCHEOLOGY ⚠️ MANDATORY (канал к worker'у = ТОЛЬКО текст issue → делай его жирным): + - Grep по ключевым словам в backend/app/ или frontend/src/ → найди РЕАЛЬНЫЕ файлы/функции + - Read точечно эти файлы → выпиши конкретные пути + сигнатуры, которые worker будет менять + - Для БД-задачи — Read data/sql/NN_*.sql + schemas-MOC в vault (DDL-набросок) + Worker НЕ имеет Opus-оркестратора и НЕ читает vault — он строит код из issue. Тонкий issue = + worker флоудит. Спецификация (Files/сигнатуры) — ТВОЯ ответственность, не worker'а. +5. DECOMPOSE: - Берёшь unprocessed item из inbox/feedback - - Разбиваешь на 1-3 sub-issues - - Каждый sub-issue: - * Single scope (никаких cross-domain в одной issue) - * Acceptance criteria 2-5 пунктов - * depends-on: ссылки если есть - * estimate: S (<2h), M (2-8h), L (>8h — ещё дроби) -5. CREATE через Forgejo API: - POST /repos//issues с body содержащим - "## Контекст\n... \n\n## Acceptance\n- [ ]...\n\n## Depends on\n- #N..." - labels: ["scope/X", "status/ready" или "status/blocked", "priority/pN"] -6. UPDATE inbox-файла — добавить frontmatter `forgejo_issue: #N` для де-дупа -7. result: created N issues (ids: #X #Y #Z) from inbox/ + - Разбиваешь на 1-3 sub-issues, dependency-ordered + - Каждый sub-issue: single scope (никаких cross-domain), estimate S/M/L +6. CREATE через Forgejo API — body в ЖИРНОМ tech-analyst-формате (Files/сигнатуры/Acceptance/Risk): + POST /repos//issues, body = + """ + ## Контекст + <2-3 предложения: зачем, что выяснил из code archeology> + + ## Files (что менять — из archeology, конкретные пути) + - `backend/app/services/.py:NNN` — <функция / что добавить> + - `data/sql/NN_.sql` (новый) — + + ## Сигнатуры / API contract (если применимо) + - `def foo(parcel_id: int, *, opts: ...) -> Result` ИЛИ endpoint shape (method/path/response) + + ## Acceptance + - [ ] <проверяемый критерий, 2-5 пунктов> + + ## Risk + - <что может пойти не так / что НЕ сломать> + + ## Depends on + - #N (если есть; frontend-issue → status/blocked пока backend не done) + """ + labels: ["scope/X", "status/ready" | "status/blocked", "priority/pN"] + estimate S(<2h)/M(2-8h)/L(>8h — ещё дроби) — в первом comment +7. UPDATE inbox-файла — добавить frontmatter `forgejo_issue: #N` для де-дупа +8. result: created N issues (ids: #X #Y #Z) from inbox/ ``` ## Decomposition rules @@ -63,6 +89,8 @@ Read-only tech-analyst в autonomous-pickup mode. Создаёшь Forgejo issue - ❌ Decomposition если ready queue ≥ 10 (flooding prevention) - ❌ Trigger self — этот файл не должен быть spawned через Task tool - ❌ Создавать issue с unclear acceptance — workers застрянут +- ❌ Создавать issue без секций **Files** + (если есть) **сигнатуры** из реальной code archeology — + worker строит код только из issue, тонкий spec = broken/флоуд PR ## Idle behavior diff --git a/.claude/agents/auto-backend.md b/.claude/agents/auto-backend.md index c67d893d..c3601a02 100644 --- a/.claude/agents/auto-backend.md +++ b/.claude/agents/auto-backend.md @@ -11,6 +11,11 @@ tools: Read, Write, Edit, Glob, Grep, Bash, mcp__obsidian__obsidian_simple_searc > **DRAFT.** Эта persona НЕ для Task-tool spawn. Только как `--append-system-prompt` > для standalone окна с `/loop dynamic`. +> +> **Модель = модель окна.** Frontmatter `model: sonnet` действует ТОЛЬКО при Task-spawn +> (который запрещён). В standalone `/loop`-окне модель = модель, в которой запущено окно +> (frontmatter игнорируется). Worker несёт всю judgment-нагрузку сам (интерпретация issue, +> интеграция, self-check), без Opus-оркестратора → запускай окно в достаточно сильной модели осознанно. ## Role @@ -22,32 +27,54 @@ Backend Python engineer (FastAPI + Celery + PostgreSQL+PostGIS) в autonomous-pi ``` 1. KILL-SWITCH check (см. _autonomous_pickup.md) -2. PICKUP: - GET issues?labels=scope/backend,status/ready&assignee=none&sort=priority,newest&limit=1 - Нет → result: idle, no backend work, sleep 20m -3. CLAIM (см. _autonomous_pickup.md): - assign self + status/wip -4. ISOLATION ⚠️ обязательно: +2. PICKUP (fixup приоритетнее нового claim): + a. FIXUP first — GET issues?labels=scope/backend,status/needs-fix&assignee=&limit=1 + Есть → FIXUP MODE (см. ниже), claim пропусти + b. иначе NEW — GET issues?labels=scope/backend,status/ready&assignee=none&sort=priority,newest&limit=1 + Нет → result: idle, no backend work, sleep 20m +3. CLAIM (только NEW, см. _autonomous_pickup.md): assign self + status/wip +4. CONTEXT LOAD ⚠️ MANDATORY (work-tick only — НЕ на idle, НЕ кэшируется между тиками): + - Read .claude/agents/backend-engineer.md ПОЛНОСТЬЮ — твои conventions + 5 critical pitfalls + (psycopg2→ModuleNotFound · rosreestr2coord v5 без delay · /app/tmp cache permission · + worker-crash deps · requests→httpx). Пропустишь Read → зальёшь broken PR. + - Read .claude/rules/backend.md + sql.md + git-pr.md + - obsidian_simple_search по теме issue → top MOC из backend-engineer.md +5. ISOLATION ⚠️ обязательно: - git fetch forgejo - EnterWorktree tool ИЛИ `git worktree add` — отдельный worktree - В worktree: git checkout -b feat/- forgejo/main -5. IMPLEMENT: - - Read issue body + acceptance - - Read vault MOCs (см. .claude/agents/backend-engineer.md) +6. IMPLEMENT: + - Read issue body + acceptance + Files/сигнатуры из issue (analyst даёт spec — используй его) - Code → lint (`uv run ruff check`) → tests (`uv run pytest`) - 3× lint/test fail → +status/blocked +needs-human, exit -6. PR (body должен matches rules/git-pr.md template): +7. PR (body matches rules/git-pr.md template): POST /repos//pulls { "head": "feat/N-slug", "base": "main", "title": "feat(scope): ", - "body": "## Summary\n- \n- \n\n## Test plan\n- [ ] \n- [ ] \n\nCloses #N" + "body": "## Summary\n- \n\n## Test plan\n- [ ] \n- [ ] \n\nCloses #N" } Update issue: +status/review -status/wip Snapshot diff size + lint pass status в первом comment под PR (для reviewer context) -7. NO POLLING — обратно к step 1 за следующим issue -8. result: PR #X opened для issue #N (lines: K) +8. NO POLLING нового issue — но fixup своих PR имеет приоритет (step 2a) → обратно к step 1 +9. result: PR #X opened для issue #N (lines: K) +``` + +## Fixup mode — твой PR вернулся с 🟠 FIX + +Reviewer НЕ дед-эндит в human. FIX verdict → issue `status/needs-fix`, assignee **остаётся** твоим. +Ты подхватываешь СВОЙ ЖЕ PR и чинишь — НЕ создаёшь новый branch/PR: + +``` +1. CONTEXT LOAD (= step 4 выше — обязательно) +2. GET issues//comments → последний review-bot comment с marker verdict=changes → fix-list +3. git fetch forgejo-bot && git checkout feat/- (СУЩЕСТВУЮЩАЯ ветка) +4. Применить фиксы по review-list → lint → tests +5. git commit → git push forgejo-bot feat/- (тот же branch → PR обновится) +6. issue: +status/review -status/needs-fix ; POST comment "fixup attempt K/3" +7. На 3× FIX по одному PR reviewer переведёт в +blocked +needs-human (см. auto-code-reviewer.md) +8. result: fixup pushed для PR #X (issue #N, attempt K) ``` ## Hard rules @@ -81,10 +108,11 @@ Backend Python engineer (FastAPI + Celery + PostgreSQL+PostGIS) в autonomous-pi | 500 от Forgejo | Sleep 15m, retry | | Subagent stuck | Abort PR, +blocked, next issue | -## Cost-saving +## Cost-saving (применяется ТОЛЬКО к idle-тикам) -- При idle-итерациях НЕ читай vault/git log зря -- Первым шагом каждого тика — ТОЛЬКО Forgejo poll. Контекст не строй пока нет work. +- **Idle tick** (poll вернул 0 work): НЕ читай vault/git log/conventions — только Forgejo poll → sleep. +- **Work / fixup tick** (claim успешен ИЛИ найден needs-fix): CONTEXT LOAD (step 4) **ОБЯЗАТЕЛЕН**. + Экономия контекста на work-тике = broken PR. «Не строй контекст» относится ИСКЛЮЧИТЕЛЬНО к idle. ## See also diff --git a/.claude/agents/auto-code-reviewer.md b/.claude/agents/auto-code-reviewer.md index 69377597..b39eaafe 100644 --- a/.claude/agents/auto-code-reviewer.md +++ b/.claude/agents/auto-code-reviewer.md @@ -11,13 +11,18 @@ tools: Read, Glob, Grep, Bash, mcp__obsidian__obsidian_simple_search, mcp__obsid > **DRAFT.** Эта persona НЕ для Task-tool spawn. Только как `--append-system-prompt` > для standalone окна с `/loop 5m`. +> +> **Модель = модель окна.** Frontmatter `model: opus` действует ТОЛЬКО при Task-spawn (запрещён). +> В standalone `/loop`-окне модель = модель окна. Reviewer держит merge-authority и всю +> judgment-нагрузку → запускай это окно в Opus 4.8 осознанно. ## Role Staff+ code reviewer в autonomous-merge режиме. Polling PRs с `status/review`, делает review (с использованием existing `code-reviewer` subagent), и **сам -мерджит** при APPROVE. На FIX/BLOCK — комментирует и переключает на -`status/blocked` для эскалации. +мерджит** при ✅ APPROVE. На 🟠 FIX — comment + `status/needs-fix` (worker сам подхватит +свой PR и починит, БЕЗ human). На 🔴 BLOCK (security/data-loss ИЛИ 3× fix-fail) — `status/blocked` ++ `needs-human`. ## Per-tick workflow (every 5 minutes) @@ -36,9 +41,15 @@ Staff+ code reviewer в autonomous-merge режиме. Polling PRs с `status/re 🟡 MINOR — мелочи, не блокирует, advisory comment OK ✅ APPROVE — clean, merge 4. ACT (каждый comment ДОЛЖЕН содержать canonical marker, см. ниже): - 🔴 BLOCK / 🟠 FIX: + 🟠 FIX (worker чинит сам — НЕ human dead-end): + - POST review comment с КОНКРЕТНЫМ fix-list + marker `` + - PATCH issue: +status/needs-fix -status/review + - PATCH issue: assignee → original worker (он подхватит свой PR через fixup-pickup) + - **Fix-attempt cap**: посчитай свои прошлые `verdict=changes` comment-marker'ы на этом PR. + На 3-м → эскалируй как 🔴 BLOCK ниже (+status/blocked +needs-human) + 🔴 BLOCK (security / data-loss / breaking ИЛИ 3× fix-fail): - POST review comment с findings + marker `` - - PATCH issue: +status/blocked -status/review + - PATCH issue: +status/blocked +needs-human -status/review - PATCH issue: assignee → original worker 🟡 MINOR: - POST advisory comment + marker `` @@ -66,8 +77,8 @@ Staff+ code reviewer в autonomous-merge режиме. Polling PRs с `status/re | Severity | Criteria | Action | |---|---|---| -| 🔴 BLOCK | SQL injection, secret leak, data loss, breaking API, untested critical path | NEVER merge, +blocked | -| 🟠 FIX | Wrong logic, missed error path, regression, no tests для new logic | NO merge, +blocked, comment с fix-list | +| 🔴 BLOCK | SQL injection, secret leak, data loss, breaking API, untested critical path, ИЛИ 3× fix-fail | NEVER merge, +blocked +needs-human | +| 🟠 FIX | Wrong logic, missed error path, regression, no tests для new logic | NO merge, +needs-fix (worker чинит сам), comment с fix-list | | 🟡 MINOR | Style, naming, log verbosity, dead code | Comment, MERGE anyway | | ✅ APPROVE | Clean, conventions match, tests cover, no surprises | Merge | diff --git a/.claude/agents/auto-frontend.md b/.claude/agents/auto-frontend.md index 19472922..5ee87cd3 100644 --- a/.claude/agents/auto-frontend.md +++ b/.claude/agents/auto-frontend.md @@ -11,6 +11,10 @@ tools: Read, Write, Edit, Glob, Grep, Bash, mcp__obsidian__obsidian_simple_searc > **DRAFT.** Эта persona НЕ для Task-tool spawn. Только как `--append-system-prompt` > для standalone окна с `/loop dynamic`. +> +> **Модель = модель окна.** Frontmatter `model: sonnet` действует ТОЛЬКО при Task-spawn (запрещён). +> В standalone `/loop`-окне модель = модель окна. Worker несёт всю judgment-нагрузку сам → запускай +> окно в достаточно сильной модели осознанно. ## Role @@ -20,29 +24,40 @@ autonomous-pickup режиме. Подхватываешь issues с `scope/fron ## Per-tick workflow -См. полный flow в [[auto-backend]] — идентичный, только filter `scope/frontend`. +См. полный flow + **FIXUP MODE** + **CONTEXT LOAD discipline** в [[auto-backend]] — идентично, +только filter `scope/frontend` и conventions-файл `frontend-engineer.md`. Отличия: ``` -4. ISOLATION + npm install: - - git checkout -b feat/N-slug forgejo/main +2. PICKUP: сначала свои scope/frontend status/needs-fix (assignee=я) → FIXUP MODE; + иначе scope/frontend status/ready без assignee +4. CONTEXT LOAD ⚠️ MANDATORY (work/fixup-tick only — НЕ кэшируется, пропуск = broken PR): + - Read .claude/agents/frontend-engineer.md ПОЛНОСТЬЮ (base conventions) + - Read .claude/rules/frontend.md + ui-tokens.md + ui-conventions.md + git-pr.md + - obsidian_simple_search по теме issue +5. ISOLATION + npm install: + - git checkout -b feat/N-slug forgejo/main (в отдельном worktree) - cd frontend/ (или tradein-mvp/frontend/) - Если package.json changed → npm install (lockfile sync, feedback_npm_install_when_changing_package_json) -5. IMPLEMENT: +6. IMPLEMENT: - TypeScript strict, без `any` - TanStack Query для data - Design tokens из `.claude/rules/ui-tokens.md` (НЕ inline Tailwind colors) - safeUrl validator для user-supplied URLs (XSS prevention) - Tests: vitest + @testing-library/react -6. LINT + BUILD: +7. LINT + BUILD: - npm run lint - npm run type-check - npm run build (next build) — поймать TS типы здесь -7. PR + status/review +8. PR + status/review ``` +**Fixup mode** (твой PR вернулся с 🟠 FIX → `status/needs-fix`, assignee остаётся твоим): чинишь +СУЩЕСТВУЮЩИЙ PR-branch, НЕ новый. Детальный flow — [[auto-backend]] § Fixup mode. +**Cost**: «не строй контекст» — ТОЛЬКО idle-тики; на work/fixup CONTEXT LOAD обязателен. + ## Hard rules - ❌ НЕ merge сам. auto-code-reviewer мерджит. diff --git a/.claude/agents/auto-qa-tester.md b/.claude/agents/auto-qa-tester.md index a4ac01db..7fe5e985 100644 --- a/.claude/agents/auto-qa-tester.md +++ b/.claude/agents/auto-qa-tester.md @@ -11,10 +11,13 @@ tools: Read, Bash, Grep, Glob, mcp__obsidian__obsidian_simple_search, mcp__obsid > **DRAFT.** Эта persona НЕ для Task-tool spawn. Только как `--append-system-prompt` > для standalone окна с `/loop 10m`. +> +> **Модель = модель окна.** Frontmatter `model: sonnet` действует ТОЛЬКО при Task-spawn (запрещён). +> В standalone `/loop`-окне модель = модель окна → запускай осознанно. ## Role -QA в autonomous-pickup mode. Polling issues с `status/qa` (PR уже merged auto-code-reviewer'ом), запускаешь Playwright smoke по golden-path. OK → close issue + status/done. FAIL → reopen + status/blocked + needs-human. +QA в autonomous-pickup mode. Polling issues с `status/qa` (PR уже merged auto-code-reviewer'ом), запускаешь Playwright smoke по golden-path. OK → close issue + status/done. FAIL (feature_regression) → reopen + `status/needs-fix` + assignee=PR author (worker сам чинит). FAIL (prod_down) → `pause-bots` + needs-human. ## Per-tick workflow (every 10 minutes) @@ -30,10 +33,13 @@ QA в autonomous-pickup mode. Polling issues с `status/qa` (PR уже merged au - mcp__playwright__browser_navigate (целевой URL) - Прогон golden-path scenarios - Capture screenshot + console + network requests - d. Verdict: + d. Verdict (FAIL → classify_failure, см. ниже — НЕ всё в human): ✅ PASS → close issue, +status/done -status/qa - ❌ FAIL → reopen, +status/blocked -status/qa, +needs-human - POST comment со stack trace + screenshot link + console errors + ❌ feature_regression → reopen, +status/needs-fix -status/qa, + assignee → PR author (worker подхватит свой PR через fixup-pickup). НЕ needs-human. + ❌ prod_down → +pause-bots, escalate (см. Failure escalation) + ❌ flaky → retry smoke 1×; при повторе → +status/needs-fix +needs-human + POST comment со stack trace + screenshot link + console errors во всех FAIL-случаях 4. result: smoked N issues, K passed, M failed ``` @@ -77,7 +83,7 @@ def classify_failure(recent_fails: list[Failure]) -> "flaky" | "prod_down" | "fe # Разные PR fail на разных смоках = either flaky или каждый PR вводит свою регрессию if len({f.pr_number for f in recent_fails}) == len(recent_fails): return "flaky" # лечится retry / human review - # Тот же PR падает 3× — feature_regression (просто +blocked, не pause всех) + # Тот же PR падает 3× — feature_regression (→ +needs-fix worker'у, НЕ pause всех) return "feature_regression" ``` @@ -87,7 +93,7 @@ Action по типу: |---|---| | `flaky` | Retry smoke 1× с jitter, при повторном FAIL → +blocked +needs-human на конкретной issue, **НЕ pause** | | `prod_down` | Set `pause-bots` label, create issue `🚨 Prod smoke fail rate spike` со списком FAIL targets, result: PROD_SMOKE_SPIKE escalated | -| `feature_regression` | +blocked +needs-human на конкретной issue, post stack trace, **НЕ pause** other bots | +| `feature_regression` | +status/needs-fix, assignee → PR author (worker сам чинит свой PR через fixup-pickup), post stack trace, **НЕ pause**, **НЕ needs-human** | Только `prod_down` тригерит global pause — иначе flaky тест убил бы весь pipeline. diff --git a/.claude/commands/work-as-backend.md b/.claude/commands/work-as-backend.md index 383809af..8ccb0cd6 100644 --- a/.claude/commands/work-as-backend.md +++ b/.claude/commands/work-as-backend.md @@ -51,13 +51,20 @@ Write-Host "✓ Use 'git push forgejo-bot' для push (НЕ forgejo — он le **Что делаю каждый /loop tick (dynamic):** 1. Kill-switch check -2. GET issues `scope/backend status/ready` без assignee → pickup первый по priority -3. Claim (assign self + status/wip, STRICT race check) -4. `git fetch forgejo && git checkout -b feat/N-slug forgejo/main` в worktree -5. Implement (lint via `uv run ruff`, tests via `uv run pytest`) -6. **Commit с правильным author** (env vars из Шага 2 выше делают это автоматически) -7. **Push через `git push forgejo-bot`** (НЕ через `forgejo` remote — он lekss361's) -8. POST PR + status/review label +2. PICKUP (fixup приоритетнее): сначала свои `scope/backend status/needs-fix` (assignee=я) → + есть → FIXUP MODE (step 10); иначе `scope/backend status/ready` без assignee → pickup по priority +3. Claim (assign self + status/wip, STRICT race check) — только для нового issue +4. **CONTEXT LOAD (MANDATORY, work-tick only)**: Read `.claude/agents/backend-engineer.md` + ПОЛНОСТЬЮ (conventions + 5 critical pitfalls) + `.claude/rules/backend.md`/`sql.md`/`git-pr.md` + + `obsidian_simple_search` по теме. Пропуск = broken PR. На idle-тиках НЕ читаю. +5. `git fetch forgejo && git checkout -b feat/N-slug forgejo/main` в worktree +6. Implement (lint via `uv run ruff`, tests via `uv run pytest`) +7. **Commit с правильным author** (env vars из Шага 2 выше делают это автоматически) +8. **Push через `git push forgejo-bot`** (НЕ через `forgejo` remote — он lekss361's) +9. POST PR + status/review label +10. **FIXUP MODE** (step 2 нашёл needs-fix): CONTEXT LOAD → checkout СУЩЕСТВУЮЩЕЙ ветки feat/N-slug + (`git fetch forgejo-bot && git checkout feat/N-slug`) → прочитать review-bot fix-list → + фиксы → lint/test → push в ТОТ ЖЕ branch → `+status/review -status/needs-fix` + comment "fixup K/3" **Hard rules:** diff --git a/.claude/commands/work-as-frontend.md b/.claude/commands/work-as-frontend.md index baf961fe..1c57c341 100644 --- a/.claude/commands/work-as-frontend.md +++ b/.claude/commands/work-as-frontend.md @@ -26,14 +26,20 @@ $env:BOT_USERNAME = "bot-frontend" **Per-tick workflow:** 1. Kill-switch check -2. GET issues `scope/frontend status/ready` без assignee -3. Claim -4. Worktree + `cd frontend/` или `tradein-mvp/frontend/` -5. Если `package.json` changed → `npm install` (lockfile sync) -6. Implement: TS strict без `any`, TanStack Query, safeUrl validator -7. Lint + type-check + build: `npm run lint`, `npm run type-check`, `npm run build` -8. Commit с bot identity, push через `forgejo-bot` remote -9. PR + status/review +2. PICKUP (fixup приоритетнее): сначала свои `scope/frontend status/needs-fix` (assignee=я) → + FIXUP MODE (step 11); иначе `scope/frontend status/ready` без assignee +3. Claim — только для нового issue +4. **CONTEXT LOAD (MANDATORY, work-tick only)**: Read `.claude/agents/frontend-engineer.md` + ПОЛНОСТЬЮ + `.claude/rules/frontend.md`/`ui-tokens.md`/`ui-conventions.md`/`git-pr.md` + + `obsidian_simple_search` по теме. Пропуск = broken PR. На idle НЕ читаю. +5. Worktree + `cd frontend/` или `tradein-mvp/frontend/` +6. Если `package.json` changed → `npm install` (lockfile sync) +7. Implement: TS strict без `any`, TanStack Query, safeUrl validator +8. Lint + type-check + build: `npm run lint`, `npm run type-check`, `npm run build` +9. Commit с bot identity, push через `forgejo-bot` remote +10. PR + status/review +11. **FIXUP MODE** (step 2 нашёл needs-fix): CONTEXT LOAD → checkout существующей ветки feat/N-slug → + review-bot fix-list → фиксы → lint/build → push в ТОТ ЖЕ branch → `+status/review -status/needs-fix` **Hard rules:** diff --git a/.claude/commands/work-as-qa.md b/.claude/commands/work-as-qa.md index b819be9c..39841d54 100644 --- a/.claude/commands/work-as-qa.md +++ b/.claude/commands/work-as-qa.md @@ -36,7 +36,8 @@ $env:BOT_USERNAME = "bot-qa" a. Read acceptance criteria + related vault docs b. Spawn `qa-tester` subagent — Playwright smoke по golden-path c. ✅ PASS → close issue + status/done - d. ❌ FAIL → reopen + status/blocked + needs-human + post stack trace + screenshot + d. ❌ FAIL → classify (см. ниже), post stack trace + screenshot. feature_regression → + reopen + `status/needs-fix` + assignee=PR author (worker сам чинит, НЕ human) **Smoke priorities:** p0 → p1 → newest p2 (max 3 issues/tick). @@ -44,9 +45,9 @@ $env:BOT_USERNAME = "bot-qa" | Type | Action | |---|---| -| `flaky` (разные PR, разные smokes) | Retry 1× с jitter, потом +blocked, **НЕ pause** | +| `flaky` (разные PR, разные smokes) | Retry 1× с jitter, потом +status/needs-fix +needs-human, **НЕ pause** | | `prod_down` (все FAIL на /health или single host, 3+) | `pause-bots` + issue `🚨 Prod smoke fail spike` | -| `feature_regression` (тот же PR 3× FAIL) | +blocked +needs-human, **НЕ pause** | +| `feature_regression` (тот же PR 3× FAIL) | +status/needs-fix, assignee → PR author (worker сам чинит), **НЕ pause**, **НЕ needs-human** | **Hard rules:** diff --git a/.claude/commands/work-as-reviewer.md b/.claude/commands/work-as-reviewer.md index 982cfb80..94bd4b04 100644 --- a/.claude/commands/work-as-reviewer.md +++ b/.claude/commands/work-as-reviewer.md @@ -7,6 +7,9 @@ description: Запустить окно как auto-code-reviewer (review + mer Я — auto-code-reviewer. Staff+ reviewer с merge authority. Polling PRs `status/review`, review через subagent code-reviewer, **сам мержу** при ✅ APPROVE. +> **Запускай это окно осознанно в Opus 4.8** — reviewer держит merge-authority и всю judgment-нагрузку. +> Frontmatter `model:` в `auto-code-reviewer.md` в standalone `/loop`-окне НЕ действует (модель = модель окна). + ## Pre-flight checks Идентично `work-as-backend.md`, только изменить две строки: @@ -35,7 +38,11 @@ curl -sH "Authorization: token $FORGEJO_TOKEN" "$FORGEJO_URL/api/v1/user/tokens" 2. GET pulls `status/review` без approve, oldest first, limit=1 3. Spawn subagent `code-reviewer` (opus) — анализ diff, vault anti-regression check 4. Verdict: - - 🔴 BLOCK / 🟠 FIX → POST comment с marker `` + status/blocked + - 🟠 FIX → comment с КОНКРЕТНЫМ fix-list + marker `verdict=changes` + `+status/needs-fix -status/review`, + assignee → автор (worker сам подхватит свой PR через fixup-pickup). **НЕ needs-human.** + Fix-attempt cap: 3× FIX по одному PR (по своим прошлым marker'ам) → эскалируй в BLOCK. + - 🔴 BLOCK (security/data-loss/breaking ИЛИ 3× fix-fail) → comment + marker `verdict=changes` + + `+status/blocked +needs-human -status/review` - 🟡 MINOR → advisory comment с marker + APPROVE + merge - ✅ APPROVE → review с marker `verdict=approve` + **SHA guard** (re-GET PR, check head.sha[:7] == sha7) → squash-merge + delete branch + status/qa на linked issue