diff --git a/.opencode/opencode.json b/.opencode/opencode.json index 464b5c4..44965d1 100644 --- a/.opencode/opencode.json +++ b/.opencode/opencode.json @@ -177,6 +177,7 @@ "git diff*": "allow", "git log*": "allow", "git pull*": "allow", + "git restore --staged *": "allow", "git -C * add *": "allow", "git -C * commit *": "allow", "git -C * push *": "allow", @@ -185,6 +186,7 @@ "git -C * status*": "allow", "git -C * diff*": "allow", "git -C * log*": "allow", + "git -C * restore --staged *": "allow", "git -C * reset *": "deny", "git -C * clean *": "deny", "git -C * branch -D *": "ask", diff --git a/.opencode/skills/add-skill/SKILL.md b/.opencode/skills/add-skill/SKILL.md index 8c707c0..5da6299 100644 --- a/.opencode/skills/add-skill/SKILL.md +++ b/.opencode/skills/add-skill/SKILL.md @@ -69,8 +69,14 @@ Implementing agent (subagent, которому делегировано созд ```bash cd "$(git rev-parse --show-toplevel)" git add .opencode/skills//SKILL.md +git status # проверь staged set — только SKILL.md, без лишнего ``` +> `commit` tool НЕ делает `git add` — коммитит только уже staged файлы. Если +> в индексе лишнее (например `memory_save` stage'нул всё через `git add -A`) +> — не коммить: сначала `git restore --staged ` или не stage'и его +> изначально. Используй `git add <конкретные-пути>`, НЕ `git add -A`. + Затем через `commit` tool (НЕ raw `git commit` — заблокирован deny): ``` diff --git a/.opencode/skills/configure-opencode/SKILL.md b/.opencode/skills/configure-opencode/SKILL.md index 6559e59..a5b9e78 100644 --- a/.opencode/skills/configure-opencode/SKILL.md +++ b/.opencode/skills/configure-opencode/SKILL.md @@ -19,6 +19,11 @@ description: Use when adding, changing, or removing MCP servers, providers, perm ## 2. Применение изменений - `commit` + `push` в репо `slaid098/opencode-config` (через `commit` tool). +- Перед каждым `commit` — `git status` для проверки staged set. `commit` tool + НЕ делает `git add` — коммитит только уже staged файлы. Используй + `git add <конкретные-пути>`, НЕ `git add -A` (иначе лишние файлы уйдут в + коммит). Если в индексе лишнее (например `memory_save` stage'нул всё через + `git add -A`) — сначала `git restore --staged `, потом коммить. - В клонах: `git pull` + рестарт opencode (MCP-серверы, skills, agents грузятся при старте — см. `add-skill/SKILL.md`). До рестарта правки не видны. - Для Docker-сетапа: `git pull` на хосте + `docker compose restart opencode` (или эквивалент) — MCP/skills/agents грузятся при старте контейнера. @@ -106,7 +111,7 @@ description: Use when adding, changing, or removing MCP servers, providers, perm ## 8. Commit message - Формат: `feat(config): ...` / `chore(config): ...` / `fix(config): ...` (conventional commits, English, ≤72 chars). -- Перед commit — использовать `commit` tool (валидация формата встроена), проверить `git log --oneline -20`, match existing style. +- Перед commit — `git status` для проверки staged set (`commit` tool НЕ делает `git add` — коммитит только уже staged файлы; НЕ `git add -A`, иначе лишние файлы уйдут в коммит). Затем использовать `commit` tool (валидация формата встроена), проверить `git log --oneline -20`, match existing style. - Примеры: `feat(config): add integrations MCP server`, `fix(config): correct timeout for integrations discover tool`. ## 9. Не дублировать блоки между репо diff --git a/.opencode/skills/release/SKILL.md b/.opencode/skills/release/SKILL.md index d5a2716..3c41e91 100644 --- a/.opencode/skills/release/SKILL.md +++ b/.opencode/skills/release/SKILL.md @@ -41,8 +41,14 @@ description: Выполняет релиз после мерджа PR — обн ```bash git add CHANGELOG.md +git status # проверь staged set — только CHANGELOG.md, без лишнего ``` +> `commit` tool НЕ делает `git add` — коммитит только уже staged файлы. Если +> в индексе лишнее (например `memory_save` stage'нул всё через `git add -A`) +> — не коммить: сначала `git restore --staged ` или не stage'и его +> изначально. Используй `git add <конкретные-пути>`, НЕ `git add -A`. + Затем через `commit` tool (НЕ raw `git commit` — заблокирован deny): ``` diff --git a/.opencode/skills/repo-init/SKILL.md b/.opencode/skills/repo-init/SKILL.md index 2e10a58..7cd9a74 100644 --- a/.opencode/skills/repo-init/SKILL.md +++ b/.opencode/skills/repo-init/SKILL.md @@ -38,8 +38,14 @@ description: Sequential checklist: create GitHub remote → configure settings/b git init echo "# " > README.md git add README.md +git status # проверь staged set — только README.md, без лишнего ``` +> `commit` tool НЕ делает `git add` — коммитит только уже staged файлы. Если +> в индексе лишнее (например `memory_save` stage'нул всё через `git add -A`) +> — не коммить: сначала `git restore --staged ` или не stage'и его +> изначально. Используй `git add <конкретные-пути>`, НЕ `git add -A`. + Затем через `commit` tool (НЕ raw `git commit` — заблокирован deny): ``` diff --git a/.opencode/skills/run-pipeline/SKILL.md b/.opencode/skills/run-pipeline/SKILL.md index 171fe52..5675671 100644 --- a/.opencode/skills/run-pipeline/SKILL.md +++ b/.opencode/skills/run-pipeline/SKILL.md @@ -51,6 +51,11 @@ description: Автономный исполнитель PR-пайплайна. 4. Коммиты через `commit({ message: "type(scope): description" })` tool (НЕ raw `git commit` — заблокирован deny). Формат: ≤72 chars, English, no period, no body unless necessary. Минимум 3-4 логических коммита. + Перед КАЖДЫМ `commit` — `git status` для проверки staged set. `commit` + tool НЕ делает `git add` — коммитит только уже staged файлы. Используй + `git add <конкретные-пути>`, НЕ `git add -A` (иначе лишние файлы уйдут в + коммит). Если в индексе лишнее (например `memory_save` stage'нул всё + через `git add -A`) — не коммить: сначала `git restore --staged `. 5. Push ветку (`git push -u origin HEAD`), затем создай PR через tool: `create-pr({ title: "type(scope): description", body: "## Что сделано\n...\n\n## Почему\n...\n\nCloses #N", issue_number: N })`. 6. После получения PR номера — исправь placeholder `` в handoff @@ -72,8 +77,11 @@ Review PR#M в текущем репо (pre-merge, режим docs). Решение, Альтернативы). 6. Если криво — почини (edit: allow). 7. `git add docs/project-map/ docs/handoff/ docs/decisions/` (add — НЕ - заблокирован), затем `commit({ message: "docs: update project map + handoff + ADR" })` - tool, и `git push`. + заблокирован). Перед `commit` — `git status` для проверки staged set + (`commit` tool НЕ делает `git add` — коммитит только staged; НЕ + `git add -A`, иначе лишние файлы уйдут в коммит). Затем + `commit({ message: "docs: update project map + handoff + ADR" })` tool, + и `git push`. 8. **ВСЕГДА** оставь PR comment (даже если structural changes нет) — это детерминированный marker для `check_docs` в pipeline-status.py. Без comment pipeline блокируется на DOCS phase. Используй tool `post-docs-review` (НЕ @@ -116,7 +124,10 @@ Log: `gh run view --log-failed` output: 1. `gh pr checkout M`. 2. Проанализируй log, найди причину. 3. Исправь (минимальные изменения, whitespace/formatting/logic fix). -4. Коммит через `commit({ message: "fix(ci): " })` tool (НЕ raw +4. Перед коммитом — `git status` для проверки staged set (`commit` tool НЕ + делает `git add` — коммитит только staged; используй + `git add <конкретные-пути>`, НЕ `git add -A`). Коммит через + `commit({ message: "fix(ci): " })` tool (НЕ raw `git commit`), push. 5. Не трогай логику unrelated файлов. ``` diff --git a/docs/decisions/035-pr-80-commit-tool-staging-truth-restore-allow.md b/docs/decisions/035-pr-80-commit-tool-staging-truth-restore-allow.md new file mode 100644 index 0000000..ab5165d --- /dev/null +++ b/docs/decisions/035-pr-80-commit-tool-staging-truth-restore-allow.md @@ -0,0 +1,41 @@ +# ADR-035: Commit tool staging truth + git restore --staged allow + +## Статус +Accepted (2026-07-26) + +## Контекст +Агент ругался что `commit` tool коммитит лишние файлы. Расследование показало: `commit.ts` НЕ делает `git add` — коммитит только уже staged файлы (`git diff --cached --name-only` → `git commit -m`). Подтверждено: ADR-015, handoff pr-38, тесты test_commit_tool.py, код commit.ts L42-56. + +Root cause попадания лишних файлов в коммит — НЕ tool: +1. Агент сделал `git add -A` / `git add .` (allow) и забыл проверить staged set. +2. `memory_save` tool (плагин @mathew-cf/opencode-memory) stage'ит ВСЁ через `git add -A` под капотом — задокументированный gotcha (memory `technical/memory-save-git-add-all-gotcha.md`, инцидент PR#30 с SSH-ключами). +3. Агент не запустил `git status` перед `commit`. + +Self-correction был заблокирован: `git reset *` = deny, `git restore --staged` = catch-all `ask` (требовал user approval). Агент не мог unstage лишнее без пользователя. + +Дополнительно: handoff pr-73 L28/L32 содержал ЛОЖЬ — утверждал что `commit` tool делает `git add -A` под капотом. Автор перепутал с `memory_save` tool. Ложь активно вредила: будущие агенты могли верить, что staging вне их контроля, и не проверять `git status`. Memory `technical/commit-tool-staging-truth.md` зафиксировала этот баг. + +## Решение +1. Исправить handoff pr-73 L28/L32 — заменить ложь про `git add -A` под капотом `commit` tool на корректное описание (tool коммитит только staged, staging ответственность агента). + +2. Усилить skills: добавить явный `git status` шаг перед `commit` в 5 SKILL.md (add-skill, release, repo-init, run-pipeline ×3 Template A/B/D, configure-opencode ×2). Формулировка: «`commit` tool НЕ делает `git add` — коммитит только уже staged. Используй `git add <конкретные-пути>`, НЕ `git add -A`. Если лишнее stage'нулось — `git restore --staged `.» + +3. Расширить allow-list в opencode.json: + - `"git restore --staged *": "allow"` (L180) + - `"git -C * restore --staged *": "allow"` (L189) + - Даёт агенту self-correction путь для unstage лишнего без user approval. + +4. `git reset *` остаётся deny (L245) — `restore --staged` безопаснее (не трогает working tree, только индекс). `reset` может перемотать коммиты — поэтому deny. + +5. `commit` tool API НЕ меняется (параметр `paths` не добавляется) — это отдельное решение через ADR + тесты, если проблема повторится. + +## Альтернативы +1. Добавить параметр `paths?: string[]` в `commit` tool — отклонено (меняет tool API, требует тестов + обновления всех skills, отдельный PR. Backward-compatible, но не решает случай когда агент уже stage'нул лишнее через raw `git add -A`). + +2. Запретить `git add -A` / `git add .` в permissions — отклонено (opencode glob-паттерны плохо различают `git add -A` от `git add src/foo.ts` — оба матчат `git add *`. Ненадёжно). + +3. Добавить `git reset HEAD ` в allow — отклонено (`git reset *` deny защищает от перемотки коммитов, `restore --staged` безопаснее — только индекс, не трогает working tree и коммиты). + +4. Оставить как есть (верить агенту на слово) — отклонено (агент уже ругался, проблема реальная, ложь в handoff pr-73 активно вредила). + +5. Не править handoff pr-73 (исторический документ) — отклонено (handoff pr-73 содержит активную ложь про `commit` tool, которая сбивает будущих агентов. Исторические docs сохраняют оригинальное написание, но ложь про поведение tool — это documentation drift, не историческая конвенция). \ No newline at end of file diff --git a/docs/handoff/pr-73-base-image-trixie-slim.md b/docs/handoff/pr-73-base-image-trixie-slim.md index a7ccfb5..194c975 100644 --- a/docs/handoff/pr-73-base-image-trixie-slim.md +++ b/docs/handoff/pr-73-base-image-trixie-slim.md @@ -25,8 +25,8 @@ Debian 13 trixie НЕ использует snap (в отличие от Ubuntu n - memory update `technical/opencode-log-subagent-stumbles-analysis.md` (noble-slim не существует → trixie-slim) — на фазе MEMORY через memory-syncer subagent, если pipeline дойдёт ## Watch out -- **`commit` tool делает `git add -A` под капотом** — staged-группировка НЕ сохраняется. Если в индексе есть лишние файлы (например `package-lock.json`), они уйдут в коммит вместе с целевыми. Урок: проверять `git status` перед `commit` и `git reset` лишнее заранее. Но `git reset *` заблокирован deny в `opencode.json:311-314` — нельзя перемотать неудачный коммит через raw bash. Workaround: новый `commit --amend` тоже не доступен (tool не принимает `--amend`), только новый коммит поверх. +- **`commit` tool НЕ делает `git add`** — коммитит только уже staged файлы (`git diff --cached --name-only` → `git commit -m`). Staging ответственность агента: `git add ` явно перед `commit`. Если в индексе случайно оказались лишние файлы (например `package-lock.json` через `git add -A`), они уйдут в коммит. Урок: проверять `git status` перед `commit`. Для unstage лишнего: `git restore --staged ` (added to allow-list в PR#80, ADR-035) — `git reset *` заблокирован deny (`opencode.json:311-314`). См. ADR-035, memory `technical/commit-tool-staging-truth.md`. - **Урок PR#71: проверять существование Docker тега через Docker Hub API перед коммитом.** `curl -s "https://hub.docker.com/v2/repositories/library/node/tags/noble-slim/" | jq .` — `name: null` = тег не существует. `trixie-slim` возвращает `name: "trixie-slim"`. Также `curl -s "https://hub.docker.com/v2/repositories/library/node/tags?page_size=100&name=trixie"` — список реальных trixie тегов. - **scaffold-handoff.sh создаёт и ADR, и handoff** — для этого PR ADR уже существовал (030-pr-71-...), но паттерн поиска `*-pr--.md` использует PR=73 → не нашёл 030-pr-71 → создал пустой файл `031-pr-73-*.md`. Лишний файл `031-pr-73` удалён вручную (`rm`, не `git rm` — файл ещё не в индексе). Если scaffold запускается после переименования ADR с другим PR-номером — нужно либо переименовать ADR ДО scaffold под целевой PR, либо удалять дубликат после. - **pipeline-status.py ищет handoff по PR-номеру, НЕ issue-номеру** — конвенция репо: `pr--.md`. PR#71 handoff назван по исходному PR#71 (который этот PR исправляет), поэтому pipeline-status для PR#73 не находил его → нужен отдельный `pr-73-*.md`. См. memory `technical/video_uniq-pr51-channel-profile-discoveries.md` §2. -- **`.opencode/package-lock.json` untracked** — не относится к этому PR (зафиксировано в pr-69 handoff), не коммитить. `git status` показывает его как untracked — нужно игнорировать при `commit` (tool сделает `git add -A`, если файл не в `.gitignore` → уйдёт в коммит). \ No newline at end of file +- **`.opencode/package-lock.json` untracked** — не относится к этому PR (зафиксировано в pr-69 handoff), не коммитить. `git status` показывает его как untracked — нужно игнорировать при `commit` (агент должен `git add` только целевые файлы, НЕ `git add -A`; `commit` tool коммитит только staged, не делая `git add` сам — см. Watch out выше). \ No newline at end of file diff --git a/docs/handoff/pr-80-commit-tool-staging-truth-restore-allow.md b/docs/handoff/pr-80-commit-tool-staging-truth-restore-allow.md new file mode 100644 index 0000000..1cd02fb --- /dev/null +++ b/docs/handoff/pr-80-commit-tool-staging-truth-restore-allow.md @@ -0,0 +1,51 @@ +--- +pr: 80 +title: fix(docs): commit tool staging truth + allow git restore --staged +--- + +## Что сделано +3 направления правок, 7 файлов изменено (+50/-6 строк примерно). + +1. Исправлена ложь в handoff pr-73 (L28, L32) про `commit` tool. + - `docs/handoff/pr-73-base-image-trixie-slim.md` L28: было «`commit` tool делает `git add -A` под капотом» — ЛОЖЬ. Стало: «`commit` tool НЕ делает `git add` — коммитит только уже staged файлы (`git diff --cached --name-only` → `git commit -m`). Staging ответственность агента: `git add ` явно перед `commit`.» + - L32: аналогично — было «tool сделает `git add -A`, если файл не в .gitignore → уйдёт в коммит», стало «агент должен `git add` только целевые файлы, НЕ `git add -A`; `commit` tool коммитит только staged, не делая `git add` сам». + - Причина лжи: автор handoff pr-73 перепутал `commit` tool с `memory_save` tool (плагин @mathew-cf/opencode-memory), который ДЕЙСТВИТЕЛЬНО делает `git add -A` под капотом (задокументированный gotcha, memory `technical/memory-save-git-add-all-gotcha.md`, инцидент PR#30 с SSH-ключами). См. ADR-035, memory `technical/commit-tool-staging-truth.md`. + +2. Усилены skills: добавлен явный `git status` шаг перед `commit` в 5 SKILL.md. + - `.opencode/skills/add-skill/SKILL.md` — `git status` в bash-блок + blockquote с правилом + - `.opencode/skills/release/SKILL.md` — `git status` в bash-блок + blockquote с правилом + - `.opencode/skills/repo-init/SKILL.md` — `git status` в bash-блок + blockquote с правилом + - `.opencode/skills/run-pipeline/SKILL.md` — 3 вставки: Template A (общее правило перед КАЖДЫМ commit: `git status` + `git add <конкретные-пути>` НЕ `git add -A`), Template B (`git status` между add/commit), Template D (`git status` перед commit) + - `.opencode/skills/configure-opencode/SKILL.md` — 2 вставки: секция 2 (prose bullet) + секция 8 (усиление существующей строки) + - Формулировки единообразны: «`commit` tool НЕ делает `git add` — коммитит только уже staged файлы. Используй `git add <конкретные-пути>`, НЕ `git add -A`. Если лишнее stage'нулось — `git restore --staged ` (теперь в allow-list).» + +3. Расширен allow-list в opencode.json для self-correction staging. + - `.opencode/opencode.json` — добавлены 2 правила в allow: + - `"git restore --staged *": "allow"` (L180) + - `"git -C * restore --staged *": "allow"` (L189) + - Даёт агенту возможность unstage случайно добавленные файлы без user approval. Раньше `git restore --staged` был в catch-all `ask` (требовал подтверждения пользователя), а `git reset *` — deny (не давал unstage вообще). См. ADR-035. + - Проверено: `git restore --staged *` не матчится ни одним deny-rule (`git reset *` L245, `git clean *` L246 — другие команды, не matчат restore). + +## Почему +Агент ругался что `commit` tool коммитит лишние файлы. Root cause НЕ в tool — `commit.ts` коммитит только уже staged файлы (`git diff --cached --name-only` → `git commit -m`), НЕ делая `git add` (подтверждено ADR-015, handoff pr-38, тестами test_commit_tool.py, кодом commit.ts L42-56). + +Причины попадания лишних файлов в коммит: +1. Агент сделал `git add -A` / `git add .` (allow) и забыл/не заметил, что staged лишнее. +2. `memory_save` tool (плагин @mathew-cf/opencode-memory) stage'ит ВСЁ через `git add -A` под капотом — задокументированный gotcha (memory `technical/memory-save-git-add-all-gotcha.md`, инцидент PR#30 с SSH-ключами). +3. Агент не запустил `git status` перед `commit` для проверки staged set. + +Self-correction был заблокирован: `git reset *` = deny (L245), `git restore --staged` = catch-all `ask` (требовал user approval). Если случайно stage'нул лишнее — агент не мог unstage без пользователя. + +Handoff pr-73 L28/L32 содержал ложь про `git add -A` под капотом `commit` tool — активно вредил: будущие агенты могли верить, что staging вне их контроля, и не проверять `git status`. + +Решение: (1) исправить ложь в handoff pr-73, (2) усилить skills явным `git status` шагом перед `commit`, (3) добавить `git restore --staged` в allow-list для self-correction. См. ADR-035. + +## Pending +— + +## Watch out +- `memory_save` tool (MCP plugin @mathew-cf/opencode-memory) ДЕЙСТВИТЕЛЬНО делает `git add -A` под капотом — это НЕ исправлено (плагин сторонний, так задумано). Это отдельный gotcha (memory `technical/memory-save-git-add-all-gotcha.md`). Новый `git restore --staged` allow-rule даёт агенту путь self-correction после `memory_save`. +- `git reset *` остаётся deny (L245) — `restore --staged` безопаснее (не трогает working tree, только индекс). `reset` может перемотать коммиты — поэтому остаётся deny. +- Handoff pr-73 исправлен, но историческая ложь осталась в memory `technical/commit-tool-staging-truth.md` (создан в предыдущей сессии) — там зафиксировано что handoff pr-73 L28/L32 WRONG. Memory обновлять не нужно (она уже корректна). +- `commit` tool не имеет параметра `paths` (только `message`) — агент должен `git add ` явно перед `commit`. Добавление параметра `paths` в tool API — отдельное решение (через ADR + тесты), не в этом PR. +- Check-permissions.py guard script прогон обязателелен в CI (permissions-check.yml) — проверяет что новые allow-паттерны не опасны. В этом PR `git restore --staged *` — безопасный (только индекс, не working tree, не коммиты). \ No newline at end of file