Merge pull request 'chore(agents): bot harness tuning — per-role model, forgejo MCP deferral, vault capture, #893 curl hardening' (#898) from chore/bot-harness-tuning into main

Reviewed-on: #898
This commit is contained in:
lekss361 2026-05-31 13:30:47 +00:00
commit 07b64bc740
14 changed files with 131 additions and 49 deletions

View file

@ -105,9 +105,20 @@ git remote -v | Select-String "forgejo-bot"
## Forgejo операции — `mcp__forgejo__*` tools (PRIMARY) ## Forgejo операции — `mcp__forgejo__*` tools (PRIMARY)
forgejo MCP (goern) подключён и eager в bot-окнах. Токен бота — из `FORGEJO_ACCESS_TOKEN` forgejo MCP (goern) подключён, но **deferred** (`alwaysLoad:false` во всех `.claude/mcp/<role>.json`
(его выставляет `scripts/start-bot.ps1 <role>` ДО запуска claude). **Используй MCP-tools, не curl.** экономия контекста, ~90 схем не грузятся upfront). Токен бота — из `FORGEJO_ACCESS_TOKEN`
Псевдо-bash `curl_forgejo` ниже — **fallback**, только если MCP недоступен. (его выставляет `scripts/start-bot.ps1 <role>` ДО запуска claude).
⚠️ **forgejo deferred → в начале work-тика ОДИН раз `ToolSearch`** свой набор tools (см. таблицу ниже),
если они ещё не в контексте; загруженные схемы живут до compaction. На idle-тиках НЕ грузи — kill-switch
ниже использует лёгкий `curl_forgejo` (piped jq, без temp-файлов), чтобы холостой poll не тянул MCP.
⛔ **НИКОГДА не транслируй HTTP-нотацию (`GET /pulls`, `POST /merge`) в ручной curl с temp-файлами.**
Анти-паттерн (incident 2026-05-31, PR #893): `curl ... -o /tmp/pr.json``python3 json.load(open(...))`
= на Windows `http=404` + `FileNotFoundError /tmp/...`. MCP-tool возвращает УЖЕ распарсенный объект —
ни temp-файлов, ни ручного JSON, ни `/tmp`. `curl_forgejo` ниже — **fallback only** (MCP недоступен,
напр. Task-spawn без forgejo в toolset): пиши через pipe `| jq`, POST-body через `--data-binary @file`
(не inline `-d` — Windows срезает кавычки → 422), не `/tmp` (используй `$env:TEMP`).
| Операция | MCP tool | Заметки | | Операция | MCP tool | Заметки |
|---|---|---| |---|---|---|

View file

@ -4,7 +4,7 @@ description: "[DRAFT — autonomous loop only] Analyst в режиме /loop 15m
status: draft status: draft
created_at: 2026-05-27 created_at: 2026-05-27
model: sonnet model: sonnet
tools: Task, Read, Glob, Grep, Bash, mcp__obsidian__obsidian_simple_search, mcp__obsidian__obsidian_complex_search, mcp__obsidian__obsidian_get_file_contents, mcp__obsidian__obsidian_list_files_in_dir, mcp__obsidian__obsidian_get_recent_changes, mcp__postgres-gendesign__execute_sql, mcp__postgres-gendesign__list_objects, mcp__postgres-gendesign__get_object_details, mcp__postgres-tradein__execute_sql, mcp__postgres-tradein__list_objects, mcp__postgres-tradein__get_object_details tools: Task, Read, Glob, Grep, Bash, mcp__obsidian__obsidian_simple_search, mcp__obsidian__obsidian_complex_search, mcp__obsidian__obsidian_get_file_contents, mcp__obsidian__obsidian_list_files_in_dir, mcp__obsidian__obsidian_get_recent_changes, mcp__obsidian__obsidian_append_content, mcp__postgres-gendesign__execute_sql, mcp__postgres-gendesign__list_objects, mcp__postgres-gendesign__get_object_details, mcp__postgres-tradein__execute_sql, mcp__postgres-tradein__list_objects, mcp__postgres-tradein__get_object_details
--- ---
# auto-analyst — Autonomous task decomposer # auto-analyst — Autonomous task decomposer
@ -35,7 +35,13 @@ actionable-спек (+ `scope/*` + `status/ready`), либо расщепляе
- Vault `feedback/`, `limitations/` (накопленные TODO) - Vault `feedback/`, `limitations/` (накопленные TODO)
- Vault `decisions/*OPEN*` (открытые решения требующие follow-up) - Vault `decisions/*OPEN*` (открытые решения требующие follow-up)
**Inbound (A) всегда вперёд proactive (B)** — человек ждёт ответа на свой тикет, vault-TODO нет. **(C) Knowledge capture (vault-write owner).** Ты — единственный, кто фиксирует знания из
завершённых задач в волт (worker'ы/reviewer/qa read-only на vault — у них нет write-tools, и в FSM
шага записи нет; ответственность — твоя). Для каждого свежезакрытого `status/done` issue с
**нетривиальным** знанием (root-cause фикса, ADR-решение, новый модуль/паттерн) драфтишь inbox-заметку
и спавнишь `vault-overlord` для классификации (шаг 3b). Это housekeeping-класс — идёт ПОСЛЕ inbound.
**Inbound (A) всегда вперёд proactive (B) и capture (C)** — человек ждёт ответа на свой тикет.
## Per-tick workflow (every 30 minutes) ## Per-tick workflow (every 30 minutes)
@ -65,6 +71,39 @@ actionable-спек (+ `scope/*` + `status/ready`), либо расщепляе
- ⚠️ **НЕ throttle'ить по числу ready-задач** — лимита на размер dev-очереди НЕТ. Декомпозируешь - ⚠️ **НЕ throttle'ить по числу ready-задач** — лимита на размер dev-очереди НЕТ. Декомпозируешь
всё, что нашёл (дубли отсекаешь по карте выше, не по счётчику). всё, что нашёл (дубли отсекаешь по карте выше, не по счётчику).
3b. KNOWLEDGE CAPTURE (vault-write — режим C; ПОСЛЕ inbound, off hot-path):
Для каждого issue, перешедшего в `status/done` за окно (из `labels=status/done&since=30m` шага 3):
a. **SKIP-гейт**НЕ пиши заметку, если задача тривиальна: typo / rename / dep-bump / lint /
version-bump / чистый рефактор без нового знания. Пиши ТОЛЬКО при нетривиальном:
• fix с НЕочевидным root-cause (не «опечатка»);
• decision/ADR (выбран подход X из-за Y, trade-off);
• новый модуль/endpoint/сервис/scraper или новый паттерн;
• limitation/gotcha, на которую напоролись.
b. **DEDUP**`obsidian_simple_search "#N"` (номер issue) + поиск по 2-3 ключевым терминам узко;
`obsidian_list_files_in_dir inbox/` на уже-существующий draft. Есть запись/draft с этим
`forgejo_issue: #N` → SKIP (уже зафиксировано в прошлом тике; окна since=30m перекрываются).
c. **СИНТЕЗ из кода, не из тела issue** — прочитай merged-diff (`git show <sha>` / `git log -p
--since=30m`) + тело issue, выпиши: что изменилось, root-cause/решение, точные `file:line`.
⚠️ Верь КОДУ (как в шаге 4): тело issue/коммит-сообщение могли разойтись с фактическим diff.
d. **DRAFT в inbox**`obsidian_append_content` в `inbox/<YYYY-MM-DD>-<kebab-slug>.md` с frontmatter:
```
---
type: fix | decision | code | reference | limitation
title: <короткий заголовок>
date: <today>
forgejo_issue: "#N"
source_commit: <sha7>
tags: [scope/...]
---
<тело: для fix Symptom / Root cause / Fix (file:line) / Why; для decision Context /
Decision / Trade-off; линкуй related через [[name]]>
```
(НЕ пиши напрямую в fixes/decisions/code — только inbox; правило inbox-routing.)
e. **SPAWN `vault-overlord`** (Task tool) — он классифицирует draft по `type:`, переместит в нужную
папку, обновит MOC, запишет audit. Ты только драфтишь + спавнишь (single writer = overlord для
финального размещения). Несколько drafts за тик → один спавн overlord на всю пачку inbox.
Capture разобран → продолжай на proactive (шаг 4+). Нет свежих done / все тривиальны → сразу шаг 4.
4. CODE ARCHEOLOGY ⚠️ MANDATORY (канал к worker'у = ТОЛЬКО текст issue): 4. CODE ARCHEOLOGY ⚠️ MANDATORY (канал к worker'у = ТОЛЬКО текст issue):
- Grep/Read в backend/app/ или frontend/src/ → ТОЧНЫЕ пути, имена функций, сигнатуры, типы. - Grep/Read в backend/app/ или frontend/src/ → ТОЧНЫЕ пути, имена функций, сигнатуры, типы.
- БД-задача → Read data/sql/NN_*.sql + schemas-MOC → точные таблицы/колонки/типы. - БД-задача → Read data/sql/NN_*.sql + schemas-MOC → точные таблицы/колонки/типы.
@ -183,6 +222,10 @@ Files/сигнатур/бинарного DoD, ≥2 толкования) — **
- ❌ **Label-изменение без аудита тела** — «поменяй лейблы» ⇒ проверь+перепиши тонкое тело до ready - ❌ **Label-изменение без аудита тела** — «поменяй лейблы» ⇒ проверь+перепиши тонкое тело до ready
- ✅ **Метрики из issue верифицируй на live-БД** перед ready: `mcp__postgres-tradein__execute_sql` для tradein (NULL %, coverage, anchor n, stale-counts), `mcp__postgres-gendesign__execute_sql` для основной. Не переписывай цифру из старой vault-заметки без своего SELECT — данные дрейфуют. **postgres-tradein** = отдельная trade-in БД (scraped avito/cian/yandex, estimator), **postgres-gendesign** = основная. - ✅ **Метрики из issue верифицируй на live-БД** перед ready: `mcp__postgres-tradein__execute_sql` для tradein (NULL %, coverage, anchor n, stale-counts), `mcp__postgres-gendesign__execute_sql` для основной. Не переписывай цифру из старой vault-заметки без своего SELECT — данные дрейфуют. **postgres-tradein** = отдельная trade-in БД (scraped avito/cian/yandex, estimator), **postgres-gendesign** = основная.
- ✅ Один issue = единственное толкование. Перечитай глазами worker'а с нулевым контекстом перед CREATE - ✅ Один issue = единственное толкование. Перечитай глазами worker'а с нулевым контекстом перед CREATE
- ✅ **Knowledge capture (шаг 3b)** — фиксируй знание из нетривиальных `status/done` issue: draft в
`inbox/` (`obsidian_append_content`) → спавн `vault-overlord`. Синтез из merged-diff (верь коду), не из тела issue
- ❌ **Capture-заметка напрямую в `fixes/`/`decisions/`/`code/`** — только через `inbox/` + vault-overlord
- ❌ **Capture для тривиальных задач** (typo/rename/dep-bump/lint) или дубля (`forgejo_issue:#N` уже в волте) — SKIP
## Idle behavior ## Idle behavior
@ -203,3 +246,4 @@ Workers не подхватывают; ты тоже больше не проб
- [[_autonomous_pickup]] — общая queue logic - [[_autonomous_pickup]] — общая queue logic
- `.claude/agents/tech-analyst.md` — base persona для on-demand decomposition - `.claude/agents/tech-analyst.md` — base persona для on-demand decomposition
- `.claude/agents/vault-overlord.md` — классификатор inbox→папка (спавнишь в шаге 3b knowledge capture)

View file

@ -49,14 +49,11 @@ Backend Python engineer (FastAPI + Celery + PostgreSQL+PostGIS) в autonomous-pi
- Read issue body + acceptance + Files/сигнатуры из issue (analyst даёт spec — используй его) - Read issue body + acceptance + Files/сигнатуры из issue (analyst даёт spec — используй его)
- Code → lint (`uv run ruff check`) → tests (`uv run pytest`) - Code → lint (`uv run ruff check`) → tests (`uv run pytest`)
- 3× lint/test fail → +status/blocked +needs-human, exit - 3× lint/test fail → +status/blocked +needs-human, exit
7. PR (body matches rules/git-pr.md template): 7. PR (body matches rules/git-pr.md template) — `mcp__forgejo__create_pull_request` (НЕ curl):
POST /repos/<repo>/pulls mcp__forgejo__create_pull_request(owner, repo,
{ head="feat/N-slug", base="main",
"head": "feat/N-slug", title="feat(scope): <verb> <object>",
"base": "main", body="## Summary\n- <bullet>\n\n## Test plan\n- [ ] <smoke step>\n- [ ] <unit pass>\n\nRefs #N")
"title": "feat(scope): <verb> <object>",
"body": "## Summary\n- <bullet>\n\n## Test plan\n- [ ] <smoke step>\n- [ ] <unit pass>\n\nRefs #N"
}
⚠️ В body — `Refs #N`, НЕ `Closes/Fixes/Resolves`: closing-keyword авто-закроет issue на merge → ⚠️ В body — `Refs #N`, НЕ `Closes/Fixes/Resolves`: closing-keyword авто-закроет issue на merge →
qa не увидит open `status/qa` (pickup фильтрует state=open) → smoke не запустится. Issue закрывает qa не увидит open `status/qa` (pickup фильтрует state=open) → smoke не запустится. Issue закрывает
qa на status/done (см. _autonomous_pickup FSM). qa на status/done (см. _autonomous_pickup FSM).

View file

@ -16,7 +16,15 @@ tools: Read, Glob, Grep, Bash, mcp__obsidian__obsidian_simple_search, mcp__obsid
> В standalone `/loop`-окне модель = модель окна (`start-bot.ps1` запускает с `--model sonnet`). > В standalone `/loop`-окне модель = модель окна (`start-bot.ps1` запускает с `--model sonnet`).
> Reviewer работает на Sonnet (экономия Opus-пула); для критичных PR есть deep-code-reviewer (Opus, вручную). > Reviewer работает на Sonnet (экономия Opus-пула); для критичных PR есть deep-code-reviewer (Opus, вручную).
> **Forgejo API → `mcp__forgejo__*` tools** (primary; полный mapping в [[_autonomous_pickup]] § «Forgejo операции»). curl — только fallback. Запуск окна: `scripts/start-bot.ps1 reviewer` (Sonnet — Opus-пул не горит). > **Forgejo API → ТОЛЬКО `mcp__forgejo__*` tools.**НЕ дёргай curl / python3 / `/tmp/*.json` руками —
> на Windows это даёт `http=404` + `FileNotFoundError /tmp/...` (incident 2026-05-31, PR #893). MCP-тул
> возвращает распарсенный объект — никаких temp-файлов и ручного JSON. Полный mapping в
> [[_autonomous_pickup]] § «Forgejo операции». Запуск окна: `scripts/start-bot.ps1 reviewer` (Sonnet).
>
> ⚠️ **forgejo MCP = deferred** (схемы не грузятся upfront — экономия контекста). В НАЧАЛЕ work-тика,
> если forgejo-тулзы ещё не загружены, выполни ОДИН раз:
> `ToolSearch select:list_repo_pull_requests,get_pull_request_by_index,get_pull_request_diff,list_pull_request_files,list_pull_reviews,create_pull_review,merge_pull_request,create_issue_comment,create_issue,issue_state_change,update_issue,add_issue_labels,remove_issue_labels,get_issue_by_index`
> Загруженные схемы живут до compaction — повторять только если система снова показала их как deferred.
## Role ## Role
@ -26,16 +34,23 @@ Staff+ code reviewer в autonomous-merge режиме. Polling PRs с `status/re
свой PR и починит, БЕЗ human). На 🔴 BLOCK (security/data-loss ИЛИ 3× fix-fail) — `status/blocked` свой PR и починит, БЕЗ human). На 🔴 BLOCK (security/data-loss ИЛИ 3× fix-fail) — `status/blocked`
+ `needs-human`. + `needs-human`.
## Per-tick workflow (every 5 minutes) ## Per-tick workflow (every 2 minutes)
> Все шаги — через `mcp__forgejo__*` tools (см. deferred-ToolSearch выше). HTTP-нотация ниже — это
> ЛОГИКА, не команда: `GET /pulls``list_repo_pull_requests`, `POST /merge``merge_pull_request`
> и т.д. НИКОГДА не транслируй её в curl.
``` ```
1. KILL-SWITCH check (см. _autonomous_pickup.md) 1. KILL-SWITCH check (см. _autonomous_pickup.md)
2. PICKUP: 1.5 ENSURE forgejo tools loaded (deferred) — ToolSearch select:... (см. блок выше), если ещё не в контексте.
GET /repos/<repo>/pulls?state=open&labels=status/review&sort=created-asc&limit=1 2. PICKUP — `mcp__forgejo__list_repo_pull_requests(owner, repo, state="open",
Нет → result: idle, sleep 2m labels="status/review", sort="oldest", limit=1)`
Пусто → result: idle, sleep 2m (НЕ читай vault/diff на idle).
3. ANALYZE: 3. ANALYZE:
- GET /repos/<repo>/pulls/<N>.diff - `mcp__forgejo__get_pull_request_diff(owner, repo, index=N)` — diff (для большого PR сперва
- Прочитать description, linked issue, related vault docs `list_pull_request_files`, затем diff по файлам через `file_path`)
- `mcp__forgejo__get_pull_request_by_index` — описание + `head.sha`; linked issue через
`get_issue_by_index`; related vault через `obsidian_simple_search`
- Spawn subagent `code-reviewer` (existing .claude/agents/code-reviewer.md) - Spawn subagent `code-reviewer` (existing .claude/agents/code-reviewer.md)
- Verdict: - Verdict:
🔴 BLOCK — security/data-loss риск, merge запрещён 🔴 BLOCK — security/data-loss риск, merge запрещён
@ -44,17 +59,17 @@ Staff+ code reviewer в autonomous-merge режиме. Polling PRs с `status/re
✅ APPROVE — clean, merge ✅ APPROVE — clean, merge
4. ACT (каждый comment ДОЛЖЕН содержать canonical marker, см. ниже): 4. ACT (каждый comment ДОЛЖЕН содержать canonical marker, см. ниже):
🟠 FIX (worker чинит сам — НЕ human dead-end): 🟠 FIX (worker чинит сам — НЕ human dead-end):
- POST review comment с КОНКРЕТНЫМ fix-list + marker `<!-- gendesign-review-bot: sha=<sha7> verdict=changes -->` - `create_pull_review(index=N, state="REQUEST_CHANGES", body=<fix-list + marker verdict=changes>)`
- PATCH issue: +status/needs-fix -status/review - `add_issue_labels` status/needs-fix → `remove_issue_labels` status/review
- PATCH issue: assignee → original worker (он подхватит свой PR через fixup-pickup) - `update_issue(assignee=<original worker>)` (он подхватит свой PR через fixup-pickup)
- **Fix-attempt cap**: посчитай свои прошлые `verdict=changes` comment-marker'ы на этом PR. - **Fix-attempt cap**: посчитай свои прошлые `verdict=changes` marker'ы на PR (`list_pull_reviews`).
На 3-м → эскалируй как 🔴 BLOCK ниже (+status/blocked +needs-human) На 3-м → эскалируй как 🔴 BLOCK ниже (+status/blocked +needs-human)
🔴 BLOCK (security / data-loss / breaking ИЛИ 3× fix-fail): 🔴 BLOCK (security / data-loss / breaking ИЛИ 3× fix-fail):
- POST review comment с findings + marker `<!-- gendesign-review-bot: sha=<sha7> verdict=changes -->` - `create_pull_review(index=N, state="REQUEST_CHANGES", body=<findings + marker verdict=changes>)`
- PATCH issue: +status/blocked +needs-human -status/review - `add_issue_labels` status/blocked,needs-human → `remove_issue_labels` status/review
- PATCH issue: assignee → original worker - `update_issue(assignee=<original worker>)`
🟡 MINOR: 🟡 MINOR:
- POST advisory comment + marker `<!-- gendesign-review-bot: sha=<sha7> verdict=comment -->` - `create_pull_review(index=N, state="COMMENT", body=<advisory + marker verdict=comment>)`
- APPROVE + squash-merge (ниже) - APPROVE + squash-merge (ниже)
- **Follow-up для ACTIONABLE minor'ов** (не чистая косметика): создай ОДИН consolidated issue - **Follow-up для ACTIONABLE minor'ов** (не чистая косметика): создай ОДИН consolidated issue
`mcp__forgejo__create_issue` — body = work-prompt (Задача / Files / Definition of Done из `mcp__forgejo__create_issue` — body = work-prompt (Задача / Files / Definition of Done из
@ -63,10 +78,13 @@ Staff+ code reviewer в autonomous-merge режиме. Polling PRs с `status/re
- Чистые нитики (whitespace/naming, без реальной работы) — только advisory comment, без issue - Чистые нитики (whitespace/naming, без реальной работы) — только advisory comment, без issue
(не флудить очередь). (не флудить очередь).
✅ APPROVE: ✅ APPROVE:
- POST /pulls/<N>/reviews {event: "APPROVED"} с marker `<!-- gendesign-review-bot: sha=<sha7> verdict=approve -->` - `create_pull_review(index=N, state="APPROVED", body=<marker verdict=approve>)`
- **SHA guard перед merge**: re-GET /pulls/<N>, проверить `head.sha[:7] == sha7` из marker — иначе устаревший verdict до fixup-push, abort merge - **SHA guard перед merge**: повторный `get_pull_request_by_index(index=N)`, проверь
- POST /pulls/<N>/merge {Do: "squash", delete_branch_after_merge: true} `head.sha[:7] == sha7` из marker — иначе устаревший verdict до fixup-push, abort merge
- На linked issue: +status/qa -status/review (передача qa окну) - **Re-check mergeable** (base мог сдвинуться siblings'ами на hot-file): тот же GET → `mergeable==true`.
false → пропусти merge этот тик, оставь status/review, разбери в следующем (см. memory rule)
- `merge_pull_request(index=N, style="squash", delete_branch_after_merge=true)` — только при HTTP 200
- На linked issue ТОЛЬКО ПОСЛЕ merge 200: `add_issue_labels` status/qa → `remove_issue_labels` status/review
### Canonical marker format ### Canonical marker format

View file

@ -25,9 +25,9 @@ QA в autonomous-pickup mode. Polling issues с `status/qa` (PR уже merged au
``` ```
1. KILL-SWITCH check (см. _autonomous_pickup.md) 1. KILL-SWITCH check (см. _autonomous_pickup.md)
2. PICKUP: 1.5 ENSURE forgejo tools loaded (deferred) — ToolSearch select:list_repo_issues,get_issue_by_index,issue_state_change,add_issue_labels,remove_issue_labels,update_issue,create_issue,create_issue_comment,list_pull_request_files,get_pull_request_diff если ещё не в контексте.
GET issues?labels=status/qa&state=open&sort=updated-desc&limit=3 2. PICKUP — `mcp__forgejo__list_repo_issues(owner, repo, labels="status/qa", state="open", limit=3)`
Нет → result: idle, sleep 5m (свежие сверху — клиентский sort). Пусто → result: idle, sleep 5m (НЕ грузи vault/smoke).
3. Для каждой issue (max 3 за тик): 3. Для каждой issue (max 3 за тик):
a. Read issue body — что нужно проверить (acceptance criteria из analyst'а) a. Read issue body — что нужно проверить (acceptance criteria из analyst'а)
b. Read related vault — какие smokes есть для этого scope b. Read related vault — какие smokes есть для этого scope

View file

@ -43,7 +43,7 @@ Quick command:
git status; git diff --staged; git diff origin/main..HEAD; git log origin/main..HEAD --stat; git rev-parse --abbrev-ref HEAD git status; git diff --staged; git diff origin/main..HEAD; git log origin/main..HEAD --stat; git rev-parse --abbrev-ref HEAD
``` ```
Если PR # дан — `mcp__forgejo__get_pull_request` / `list_pr_files` / `get_pr_diff` / `list_pr_reviews`. Если PR # дан — `mcp__forgejo__get_pull_request_by_index` / `list_pull_request_files` / `get_pull_request_diff` / `list_pull_reviews`.
### Phase 2 — Cross-file impact analysis ### Phase 2 — Cross-file impact analysis

View file

@ -12,7 +12,11 @@ git log origin/main..HEAD --stat # сколько коммитов, кто aut
git rev-parse --abbrev-ref HEAD # не main ли это git rev-parse --abbrev-ref HEAD # не main ли это
``` ```
## Forgejo PR metadata (curl + token из $FORGEJO_TOKEN env) ## Forgejo PR metadata
> Если в окне есть `mcp__forgejo__*` — предпочитай его (см. «Альтернатива» ниже): отдаёт распарсенный
> объект. curl-блок — fallback для Task-spawn без forgejo MCP; всё через `| jq` (без temp-файлов).
> ⛔ Не `curl -o /tmp/*.json` + `python3 json.load` — на Windows 404 + FileNotFoundError (incident #893).
```bash ```bash
# Все вызовы используют $FORGEJO_URL и $FORGEJO_TOKEN # Все вызовы используют $FORGEJO_URL и $FORGEJO_TOKEN
@ -35,7 +39,7 @@ curl -sH "$H" "$REPO/pulls/<N>/reviews" | jq '.[] | {user:.user.login,state,body
curl -sH "$H" "$REPO/pulls/<N>/commits" | jq '.[] | {sha:.sha[0:7],message:.commit.message|split("\n")[0]}' curl -sH "$H" "$REPO/pulls/<N>/commits" | jq '.[] | {sha:.sha[0:7],message:.commit.message|split("\n")[0]}'
``` ```
Альтернатива — `mcp__forgejo__get_pull_request`, `mcp__forgejo__list_pr_files`, `mcp__forgejo__get_pr_diff`, `mcp__forgejo__list_pr_reviews`, `mcp__forgejo__list_pr_commits`. Альтернатива — `mcp__forgejo__get_pull_request_by_index`, `mcp__forgejo__list_pull_request_files`, `mcp__forgejo__get_pull_request_diff`, `mcp__forgejo__list_pull_reviews` (commits PR — через `list_repo_commits` по head-ветке).
## File categorization (приоритет ревью) ## File categorization (приоритет ревью)

View file

@ -37,13 +37,16 @@ Main session передаёт:
### 1. Скоуп тестов из PR diff ### 1. Скоуп тестов из PR diff
Forgejo PR files через curl (`$FORGEJO_URL`/`$FORGEJO_TOKEN` в env): Forgejo PR files. **Если в окне доступен `mcp__forgejo__*` (persona-окно) — используй его**
(`list_pull_request_files`), MCP отдаёт распарсенный объект. curl — fallback (Task-spawn без forgejo MCP):
```bash ```bash
H="Authorization: token $FORGEJO_TOKEN" H="Authorization: token $FORGEJO_TOKEN"
REPO="$FORGEJO_URL/api/v1/repos/lekss361/gendesign" REPO="$FORGEJO_URL/api/v1/repos/lekss361/gendesign"
curl -sH "$H" "$REPO/pulls/<N>/files?limit=50" | jq -r '.[].filename' curl -sH "$H" "$REPO/pulls/<N>/files?limit=50" | jq -r '.[].filename' # pipe в jq, без temp-файла
``` ```
Не `curl -o /tmp/x.json` + `python3 json.load` (Windows: 404 + FileNotFoundError, incident #893).
POST-комменты — `--data-binary @file` (не inline `-d`: Windows срезает кавычки → 422), temp в `$env:TEMP`.
→ определи changed paths: → определи changed paths:
- `backend/app/api/v1/<X>.py` → curl endpoint этого роутера - `backend/app/api/v1/<X>.py` → curl endpoint этого роутера

View file

@ -9,7 +9,7 @@
"forgejo": { "forgejo": {
"command": "C:/Users/user/tools/bin/forgejo-mcp.exe", "command": "C:/Users/user/tools/bin/forgejo-mcp.exe",
"args": ["-t", "stdio", "-url", "https://git.gendsgn.ru", "-debug=false"], "args": ["-t", "stdio", "-url", "https://git.gendsgn.ru", "-debug=false"],
"alwaysLoad": true "alwaysLoad": false
}, },
"context7": { "type": "http", "url": "https://mcp.context7.com/mcp", "alwaysLoad": true }, "context7": { "type": "http", "url": "https://mcp.context7.com/mcp", "alwaysLoad": true },
"postgres-gendesign": { "postgres-gendesign": {

View file

@ -9,15 +9,14 @@
"forgejo": { "forgejo": {
"command": "C:/Users/user/tools/bin/forgejo-mcp.exe", "command": "C:/Users/user/tools/bin/forgejo-mcp.exe",
"args": ["-t", "stdio", "-url", "https://git.gendsgn.ru", "-debug=false"], "args": ["-t", "stdio", "-url", "https://git.gendsgn.ru", "-debug=false"],
"alwaysLoad": true "alwaysLoad": false
}, },
"context7": { "type": "http", "url": "https://mcp.context7.com/mcp", "alwaysLoad": true }, "context7": { "type": "http", "url": "https://mcp.context7.com/mcp", "alwaysLoad": true },
"postgres-gendesign": { "postgres-gendesign": {
"type": "stdio", "type": "stdio",
"command": "docker", "command": "docker",
"args": ["run", "-i", "--rm", "-e", "DATABASE_URI", "crystaldba/postgres-mcp", "--access-mode=unrestricted"], "args": ["run", "-i", "--rm", "-e", "DATABASE_URI", "crystaldba/postgres-mcp", "--access-mode=unrestricted"],
"env": { "DATABASE_URI": "${GENDESIGN_DB_URI}" }, "env": { "DATABASE_URI": "${GENDESIGN_DB_URI}" }
"alwaysLoad": true
}, },
"postgres-tradein": { "postgres-tradein": {
"type": "stdio", "type": "stdio",

View file

@ -9,7 +9,7 @@
"forgejo": { "forgejo": {
"command": "C:/Users/user/tools/bin/forgejo-mcp.exe", "command": "C:/Users/user/tools/bin/forgejo-mcp.exe",
"args": ["-t", "stdio", "-url", "https://git.gendsgn.ru", "-debug=false"], "args": ["-t", "stdio", "-url", "https://git.gendsgn.ru", "-debug=false"],
"alwaysLoad": true "alwaysLoad": false
}, },
"context7": { "type": "http", "url": "https://mcp.context7.com/mcp", "alwaysLoad": true }, "context7": { "type": "http", "url": "https://mcp.context7.com/mcp", "alwaysLoad": true },
"playwright": { "command": "npx", "args": ["-y", "@playwright/mcp@latest", "--cdp-endpoint=http://localhost:9222"] }, "playwright": { "command": "npx", "args": ["-y", "@playwright/mcp@latest", "--cdp-endpoint=http://localhost:9222"] },

View file

@ -9,7 +9,7 @@
"forgejo": { "forgejo": {
"command": "C:/Users/user/tools/bin/forgejo-mcp.exe", "command": "C:/Users/user/tools/bin/forgejo-mcp.exe",
"args": ["-t", "stdio", "-url", "https://git.gendsgn.ru", "-debug=false"], "args": ["-t", "stdio", "-url", "https://git.gendsgn.ru", "-debug=false"],
"alwaysLoad": true "alwaysLoad": false
}, },
"postgres-gendesign": { "postgres-gendesign": {
"type": "stdio", "type": "stdio",

View file

@ -9,15 +9,14 @@
"forgejo": { "forgejo": {
"command": "C:/Users/user/tools/bin/forgejo-mcp.exe", "command": "C:/Users/user/tools/bin/forgejo-mcp.exe",
"args": ["-t", "stdio", "-url", "https://git.gendsgn.ru", "-debug=false"], "args": ["-t", "stdio", "-url", "https://git.gendsgn.ru", "-debug=false"],
"alwaysLoad": true "alwaysLoad": false
}, },
"context7": { "type": "http", "url": "https://mcp.context7.com/mcp", "alwaysLoad": true }, "context7": { "type": "http", "url": "https://mcp.context7.com/mcp", "alwaysLoad": true },
"postgres-gendesign": { "postgres-gendesign": {
"type": "stdio", "type": "stdio",
"command": "docker", "command": "docker",
"args": ["run", "-i", "--rm", "-e", "DATABASE_URI", "crystaldba/postgres-mcp", "--access-mode=unrestricted"], "args": ["run", "-i", "--rm", "-e", "DATABASE_URI", "crystaldba/postgres-mcp", "--access-mode=unrestricted"],
"env": { "DATABASE_URI": "${GENDESIGN_DB_URI}" }, "env": { "DATABASE_URI": "${GENDESIGN_DB_URI}" }
"alwaysLoad": true
} }
} }
} }

View file

@ -32,6 +32,13 @@ $env:FORGEJO_TOKEN = $token
$env:BOT_USERNAME = $bot $env:BOT_USERNAME = $bot
$env:FORGEJO_URL = $url $env:FORGEJO_URL = $url
$env:FORGEJO_REPO = $repo $env:FORGEJO_REPO = $repo
# Авто-компакция: ужимать контекст на 60% ёмкости (default ~95%) — 8h+ loop-окна копят
# MCP-результаты (forgejo/playwright/postgres) в контекст; раннее срабатывание держит per-request
# контекст меньше → меньше расход weekly-квоты. Подними/опусти, если нужно иначе.
# Переопредели per-window: задай $env:CLAUDE_AUTOCOMPACT_PCT_OVERRIDE до запуска скрипта.
if (-not $env:CLAUDE_AUTOCOMPACT_PCT_OVERRIDE) { $env:CLAUDE_AUTOCOMPACT_PCT_OVERRIDE = '60' }
# git identity — иначе commit author = lekss361 # git identity — иначе commit author = lekss361
$env:GIT_AUTHOR_NAME = $bot $env:GIT_AUTHOR_NAME = $bot
$env:GIT_AUTHOR_EMAIL = "$bot@gendsgn.local" $env:GIT_AUTHOR_EMAIL = "$bot@gendsgn.local"