diff --git a/.ai/conventions/engineering-workflow.md b/.ai/conventions/engineering-workflow.md new file mode 100644 index 0000000..cee2219 --- /dev/null +++ b/.ai/conventions/engineering-workflow.md @@ -0,0 +1,162 @@ +# Engineering Workflow + +Область: пять независимых checkout workspace (`main`, `frontend`, `backend`, +`docs`, `specs`). Репозиторий `deploy` существует в Gitea отдельно и применяет +свои локальные правила. Этот документ — единственный владелец общего процесса; +локальные AGENTS и conventions определяют технические проверки и ограничения. + +## Issue Management + +| Переход | Условие входа → действие → условие выхода | +| --- | --- | +| Request → Issue | До изменения файлов/конфигурации/спецификаций определить затронутые repos, найти по смыслу открытые и закрытые Issues, проверить актуальность и scope. Взять подходящую открытую Issue (закрытую переоткрыть только для действительно незавершённой работы) либо создать одну новую. Чтение, анализ и предложение изменений разрешены без Issue. | +| Issue → Analysis | В Issue должны быть результат в title, контекст/проблема и ожидаемое поведение, проверяемые Acceptance Criteria. Уточнить неизвестное до зависимого решения. Выбрать тип и приоритет; назначить исполнителя, если известен; milestone только при реальном плане, dependencies/related Issues только при наличии. | +| Analysis → Delivery | Определить область и риски, выбрать lightweight или SDD ниже. Если AC или owner меняются, сначала обновить соответствующую Issue/согласование. | + +Типы: `feature`, `bug`, `refactor`, `tech-debt`, `docs`, `test`, `ci`, +`chore`, `performance`. Существующие org labels отражают их без требования +один-к-одному: `Kind/Feature`, `Kind/Bug`, `Kind/Refactor`, +`Kind/Documentation`, `Kind/Testing`, `Kind/Enhancement` (performance); +repo-local `Kind/Process` подходит для ci, chore и process/tech-debt. +Дополнительные `Kind/Security`, `Kind/Investigation`, `Kind/Dependencies` +(последние два — repo-local) — по ситуации. Priority: существующие +`Priority/Critical|High|Medium|Low`. +Gitea assignee, milestone и state не дублируются labels. Для назначаемых +Issue и PR выбирай `hermes-agent`; незанятую задачу не назначай формально. +Шаблоны ускоряют ввод, но не доказывают качество заполнения. + +Один координирующий Issue достаточен, если результат и AC едины, одна команда +может принять работу целиком и отдельные repos не требуют независимой поставки. +Иначе Issue в каждом независимо принимаемом repo; ссылаться друг на друга и +указать порядок зависимостей. Номера `#27` локальны: для чужого repo использовать +`WealthLedger/main#27` или полный URL. Не заводить дубль только из-за второго PR. + +## Git Branching + +| Переход | Условие входа → действие → условие выхода | +| --- | --- | +| Analysis → Branch | Issue и AC определены; проверить `git status`, текущую ветку и `origin/develop`. Использовать существующую подходящую ветку либо создать одну от актуального `develop` каждого affected repo. Чужие изменения не переносить. | +| Branch → Implementation | `/-`; номер принадлежит указанной в PR Issue (для cross-repo — координатору). Примеры: `feature/123-import-transactions`, `fix/124-invalid-balance`, `refactor/125-simplify-api-client`, `docs/126-update-architecture`. | + +Префиксы: feature → `feature`, bug → `fix`, refactor/tech-debt → +`refactor`, docs → `docs`, test → `test`, ci → `ci`, chore → `chore`, +performance → `perf`. Номер обязателен для новых рабочих веток; короткий +английский slug в kebab-case. Ветки `main`/`develop` не место для прямой +разработки. Release/hotfix, обратная синхронизация, Renovate и исторические +ветки подчиняются существующему release/automation процессу, а не переименовываются. + +## Commit Conventions + +| Переход | Условие входа → действие → условие выхода | +| --- | --- | +| Implementation → Commit | Одна связанная задача на commit, проверить staged paths/diff. Заголовок Conventional Commits с осмысленным subject и ссылкой на Issue: `feat(portfolio): add transaction import (#123)` или `ci: align gates (WealthLedger/main#27)`. Scope только при полезной области. | + +Commitlint требует распознаваемую conventional-commits-parser ссылку, но не +проверяет числовой формат, существование Issue, её смысл или корректный repo. +Использовать `#N` только для локальной Issue и `WealthLedger/repo#N` для чужой; +это формат команды, проверяемый при review. Не добавлять custom validator +ради более строгой регулярной проверки. Без `fixes`, +`closes`, `resolves` в сообщении: не закрывать задачу первым коммитом. +Ссылка в footer тоже принимается commitlint; размещение в заголовке остаётся +правилом команды. Если заголовок PR после squash станет commit title, оставить +в нём ссылку. `chore(release): X.Y.Z` освобождён только при точном SemVer +заголовке release-it. Стандартные Gitea merge commits освобождаются по точному +шаблону merge message; sync PR используют обычный review/merge process. +Шаблонные сообщения `Revert`/`fixup!`/`squash!` не освобождаются: revert +оформляй как `revert: … (#N)`. Историю не переписывать ради формата. + +## Pull Request Process + +| Переход | Условие входа → действие → условие выхода | +| --- | --- | +| Validation → PR | Завершить AC и релевантные локальные проверки; PR в `develop` из рабочей ветки, только связанный diff. В теле: Summary, Linked Issue, Changes, Verification (команда/результат/ограничения), Breaking Changes при наличии. Для cross-repo PR указывать полный reference и связанные PR. | +| PR → Review → Merge | Дождаться required checks на финальной ревизии, проверить AC, значимые изменения — review владельца/специалиста. При замечаниях исправить и обновить evidence. Merge только по политике repo. | +| Merge → Issue Closed | Закрыть Issue после принятия **всех** связанных delivery areas, документации и при SDD — обязательной verification/archive; multi-PR Issue остаётся открытой до последнего результата. | + +Перед передачей PR на review перенеси в него тип и приоритет связанной Issue, +назначь `hermes-agent`, привяжи к подходящему milestone (если он есть) и к +`WealthLedger — Delivery Board`, колонка «На проверке». Для cross-repo PR +используй milestone его собственного repo. Колонку меняй вместе со стадией: +при блокировке — «Заблокировано», после принятия — «Готово». Не вводи label +этапа вместо встроенной колонки проекта. + +Для SDD отдельные pre-archive checks/semantic review выполняются до archive по +локальной schema; порядок merge/archive согласуется с конкретным change, но +Issue закрывается только после обоих событий. Не подменять OpenSpec approval +обычным PR review. + +## SDD Integration + +Lightweight: локальный bugfix с известным ожидаемым поведением, docs-only, +рефакторинг без изменения контрактов/наблюдаемого поведения и малые CI-правки: +`Issue → Analysis → Branch → Implementation → Validation → PR`. + +SDD: новая функциональность, изменение публичного API, бизнес-правил, поведения +нескольких компонентов, архитектуры или неоднозначные требования. После Issue +уточнить намерение и запросить согласование Change Owner на вход в OpenSpec; +OpenSpec lifecycle применяется только по явному запросу/решению владельца. +Использовать **только** store `wealth-ledger-specs` и его локальный lifecycle: +Proposal → Delta Specs → System Analysis → Design + QA Analysis → Docs Impact → +Tasks → Apply (для legacy change — его собственная schema). Явные approvals +перед downstream artifacts и Apply не заменяются Issue/PR. Ветка specs может +появиться для planning; ветки реализации — когда Apply разрешён, без +насильственной общей точки создания всех веток. Связать Issue ↔ change ID ↔ +PR текстовыми ссылками; после проверки применить штатные sync/archive и +completion evidence согласно локальной schema. Число файлов не определяет SDD. +Если владелец не согласовал вход в SDD, не начинать зависящую реализацию +контракта/семантики и явно сообщить блокировку. + +## AI Agent Responsibilities + +Перед edit — поиск подходящей Issue, AC, workflow, branch/status каждого repo; +затем только согласованный scope. По завершении — итоговый diff, проверенные +команды и ограничения, подготовка commit/PR по запросу и статус Issue. Чтение +не требует Issue. Если Gitea недоступна, сообщить об этом до edit и запросить +issue reference; не выдавать локальную заметку за Issue. Агенты используют +локальные инструкции репозитория и его CI inputs, не полагаются на соседние +checkout как обязательную build-зависимость. Этот источник доступен в main; +при недоступной ссылке самостоятельный checkout сохраняет обязательный минимум +в своём AGENTS и не блокирует локальную работу из-за отсутствия root checkout. + +## Quality Gates + +Итерации: узкие проверки по риску; после логического изменения — релевантные +тесты; перед передачей результата — локальные обязательные gates данного repo +на итоговом diff; CI — независимые полные проверки. Не повторять дорогой verify +после каждого файла; повторить при изменении значимых входов. `main`: `commits`; +остальные: локальные `verify`, `security`, `commits` согласно фактическим CI. +Frontend/backend запускают их как jobs в едином `verify.yml`, docs/specs — +как отдельные workflows; второго CI ради одного правила не требуется. +Для Markdown-only не запускать application build. Результат lint не заменяет +семантическую проверку. Обязательность merge checks зависит от защиты ветки. + +| Правило | Уровень на момент принятия документа | +| --- | --- | +| Conventional Commits и parser-recognized Issue reference в PR title/commits | CI-проверка через существующий commitlint; блокировка merge **только если** check обязателен в branch protection. Не гарантирует numeric reference, существование или соответствие Issue. Точный release SemVer title и штатные Gitea merge messages исключены. Локальные hooks не гарантируют server-side. | +| PR вместо прямого push; successful checks перед merge | Enforced только в repos/ветках, где Gitea branch protection настроена и проверена владельцем. YAML сам по себе не защищает ветку. | +| Поиск актуальной Issue, AC, корректный repo, branch name/source, соответствие ссылки, SDD selection, scope, статус | Agent-guided; templates и текст не могут гарантировать соответствие. | +| Значимое review, соответствие AC/контракту, исключения и closure после всех PR | Manual; approval может быть required в Gitea, если есть независимый reviewer. | + +## Exceptions and Recovery + +Срочный hotfix: немедленно завести/найти Issue, затем исправление в +`release/hotfix-` от актуального `main` по существующему release process; +проверки и PR в `main`, затем вернуть результат в `develop`. Связать Issue +в PR и сопровождающем коммите; специальная release-ветка исключена из обычного +шаблона имён. Не отменять Issue или проверки из-за срочности. +Если CI красный или PR устарел — исправить в той же ветке и перепроверить +актуальный HEAD; если AC изменились — обновить Issue и при SDD вернуть change +к затронутому approval gate. При недоступной Gitea продолжать только анализ. +Изменение PR title после зелёного CI: проверить, что check пересчитан; +если `edited` trigger у существующих checks отсутствует, reviewer проверяет +текущий title вручную. Новые правила не применяются к историческим коммитам +и автоматической обратной синхронизации релиза. + +Примеры: фича с новым поведением — Issue → согласованный OpenSpec change → +ветки Apply → PR каждого affected repo → archive → закрытие Issue. Локальный +bugfix — Issue → `fix/124-invalid-balance` → регрессия → PR → закрытие. +Cross-repo контракт — одна Issue с общими AC, `backend` и `frontend` PR с +`WealthLedger/main#27` и взаимными ссылками; если независимые acceptance/owners, +отдельные связанные Issues. Рефакторинг без изменения поведения — lightweight +`refactor/125-simplify-api-client`. Hotfix — Issue → `release/hotfix-*` от +`main` → PR/release → back-merge в `develop` → закрытие. diff --git a/.ai/conventions/git.md b/.ai/conventions/git.md index 9d1aab0..2843ab5 100644 --- a/.ai/conventions/git.md +++ b/.ai/conventions/git.md @@ -2,11 +2,13 @@ - Выполняй Git-команды из репозитория владельца: root, frontend, backend, docs и specs имеют независимые history, index и branches. -- Новая работа: ветка `codex/` от актуального `develop`, затем PR в `develop`. +- Новая работа: ветка с номером Issue по [Engineering Workflow](engineering-workflow.md#git-branching) + от актуального `develop`, затем PR в `develop`. Сохраняй чужие staged/unstaged/untracked изменения; не переноси их в свой commit. - Commit содержит одну связанную задачу и её необходимые проверки/документацию. Межрепозиторное изменение оформляй отдельными commits/PR с взаимными ссылками. -- Commits и заголовки PR — Conventional Commits; допустим `spec:`. +- Commits и заголовки PR — Conventional Commits со ссылкой на Issue; + допустим `spec:`. Исключения release — в README. Формат проверяет local commitlint; правила других repo принадлежат их AGENTS. - Перед commit проверь `git diff --cached --name-status` и сам staged diff в каждом affected repo. Root tracking ограничен [.gitignore](../../.gitignore): diff --git a/.gitea/ISSUE_TEMPLATE/bug.md b/.gitea/ISSUE_TEMPLATE/bug.md new file mode 100644 index 0000000..c7edcc9 --- /dev/null +++ b/.gitea/ISSUE_TEMPLATE/bug.md @@ -0,0 +1,12 @@ +--- +name: Bug Report +about: Неверное поведение +--- +## Проблема и шаги воспроизведения + +## Факт / ожидание + +## Acceptance Criteria +- [ ] + +## Окружение / ссылки (если применимо) diff --git a/.gitea/ISSUE_TEMPLATE/engineering.md b/.gitea/ISSUE_TEMPLATE/engineering.md new file mode 100644 index 0000000..96b826e --- /dev/null +++ b/.gitea/ISSUE_TEMPLATE/engineering.md @@ -0,0 +1,10 @@ +--- +name: Engineering Task +about: CI, тесты, документация, процесс и прочее +--- +## Контекст и результат + +## Scope / связанные Issues (если есть) + +## Acceptance Criteria +- [ ] diff --git a/.gitea/ISSUE_TEMPLATE/feature.md b/.gitea/ISSUE_TEMPLATE/feature.md new file mode 100644 index 0000000..b10e51f --- /dev/null +++ b/.gitea/ISSUE_TEMPLATE/feature.md @@ -0,0 +1,12 @@ +--- +name: Feature Request +about: Новое наблюдаемое поведение +--- +## Зачем и какой результат + +## Ожидаемое поведение + +## Acceptance Criteria +- [ ] + +## Зависимости / связанные Issues (если есть) diff --git a/.gitea/ISSUE_TEMPLATE/refactor.md b/.gitea/ISSUE_TEMPLATE/refactor.md new file mode 100644 index 0000000..f67cf63 --- /dev/null +++ b/.gitea/ISSUE_TEMPLATE/refactor.md @@ -0,0 +1,12 @@ +--- +name: Refactoring / Technical Debt +about: Улучшение без нового поведения +--- +## Проблема и результат + +## Сохраняемые инварианты / границы + +## Acceptance Criteria +- [ ] + +## Зависимости / ссылки (если есть) diff --git a/.gitea/PULL_REQUEST_TEMPLATE.md b/.gitea/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..f31e344 --- /dev/null +++ b/.gitea/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,11 @@ +## Summary + +## Linked Issue +Refs + +## Changes + +## Verification +- Команда / результат / ограничения: + +## Breaking Changes (если есть) diff --git a/.gitea/workflows/commits.yml b/.gitea/workflows/commits.yml index c4c8d9e..fa89ebf 100644 --- a/.gitea/workflows/commits.yml +++ b/.gitea/workflows/commits.yml @@ -23,4 +23,8 @@ jobs: HEAD_SHA: ${{ github.event.pull_request.head.sha }} run: | printf '%s\n' "$PR_TITLE" | npm exec --no -- commitlint --strict - npm exec --no -- commitlint --strict --from "$BASE_SHA" --to "$HEAD_SHA" + if [[ "$PR_TITLE" =~ ^chore\(release\):\ [0-9]+\.[0-9]+\.[0-9]+$ ]]; then + npm exec --no -- commitlint --strict --from "$HEAD_SHA^" --to "$HEAD_SHA" + else + npm exec --no -- commitlint --strict --from "$BASE_SHA" --to "$HEAD_SHA" + fi diff --git a/.gitignore b/.gitignore index 5f06120..78f5dab 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ !/.ai/conventions/ /.ai/conventions/* !/.ai/conventions/git.md +!/.ai/conventions/engineering-workflow.md !/.ai/conventions/dependencies.md !/.ai/conventions/code-health.md !/.ai/conventions/testing.md @@ -35,3 +36,10 @@ !/.gitea/workflows/ /.gitea/workflows/* !/.gitea/workflows/commits.yml +!/.gitea/ISSUE_TEMPLATE/ +/.gitea/ISSUE_TEMPLATE/* +!/.gitea/ISSUE_TEMPLATE/feature.md +!/.gitea/ISSUE_TEMPLATE/bug.md +!/.gitea/ISSUE_TEMPLATE/refactor.md +!/.gitea/ISSUE_TEMPLATE/engineering.md +!/.gitea/PULL_REQUEST_TEMPLATE.md diff --git a/AGENTS.md b/AGENTS.md index e48df9a..28208fb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,6 +12,11 @@ Root tracking ограничен allowlist; локальные данные и n Публичные API artifacts передаются явно; CI каждого repo использует локальные входы. Справочные источники не становятся обязательной цепочкой инструкций. +До изменения проекта найди подходящую Gitea Issue (или создай после поиска), +проверь Acceptance Criteria, scope и рабочую ветку. Чтение/анализ допускаются +без Issue. Маршрут, выбор lightweight/SDD и cross-repo ссылки — в +[Engineering Workflow](.ai/conventions/engineering-workflow.md). + Начни с результата и affected areas. До edits проследи поведение/invariants, найди owner и 1–3 примера. Работай напрямую; OpenSpec/SDD — по явному запросу. @@ -23,7 +28,10 @@ Root tracking ограничен allowlist; локальные данные и n | Git/branch/commit/PR/tracking | [Git](.ai/conventions/git.md) | | Release/hotfix | [README](README.md#релизы-и-ветки) | -Перед Node-командой активируй местную `.nvmrc`: `nvm use --silent`. +Перед Node-командой активируй местную `.nvmrc`: если `nvm` не загружен +в non-interactive shell, загрузи установленный `$HOME/.nvm/nvm.sh`, затем +выполни `nvm use --silent` из root. Если `nvm.sh` недоступен, остановись; +не используй системный Node вместо проектного. При новой границе и перед завершением повтори routing по staged/unstaged/new files; сохраняй чужие изменения. Missing source/конфликт требований сообщи до зависимого решения. После diff выполни diff --git a/CHANGELOG.md b/CHANGELOG.md index 7311c40..f403813 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## [0.2.2](https://git.personal.ksv741.ru/WealthLedger/main/compare/v0.2.1...v0.2.2) (2026-10-12) + +### Bug Fixes + +* align workflow commitlint policy ([#27](https://git.personal.ksv741.ru/WealthLedger/main/issues/27)) ([03b3b59](https://git.personal.ksv741.ru/WealthLedger/main/commit/03b3b59e6ff2ba391f9dfc42a039a96d0d84c1c9)) + +### Documentation + +* clarify root Node bootstrap [#24](https://git.personal.ksv741.ru/WealthLedger/main/issues/24) ([1ed4dd5](https://git.personal.ksv741.ru/WealthLedger/main/commit/1ed4dd5a73a250f24f452a26b5f5b0fcbb15f758)) +* document PR ownership and board stage ([#27](https://git.personal.ksv741.ru/WealthLedger/main/issues/27)) ([59e0831](https://git.personal.ksv741.ru/WealthLedger/main/commit/59e0831ad28d6990416e651f55d783b8d2ac98c2)) +* refine workflow conventions after review ([#27](https://git.personal.ksv741.ru/WealthLedger/main/issues/27)) ([fc589c9](https://git.personal.ksv741.ru/WealthLedger/main/commit/fc589c9130e90480c09f8fe8d47dd871aa05145e)) + +### Maintenance + +* establish engineering workflow ([#27](https://git.personal.ksv741.ru/WealthLedger/main/issues/27)) ([2dc7225](https://git.personal.ksv741.ru/WealthLedger/main/commit/2dc72255fe51bb48a208e10846d42d9f59e2c1af)) + ## [0.2.1](https://git.personal.ksv741.ru/WealthLedger/main/compare/v0.2.0...v0.2.1) (2026-10-07) ### Bug Fixes diff --git a/README.md b/README.md index 061e243..53aabba 100644 --- a/README.md +++ b/README.md @@ -149,6 +149,7 @@ Allowlist root проверяется вручную по staged paths. Backend T-Bank. Полные команды и ограничения принадлежат README [frontend](https://git.personal.ksv741.ru/WealthLedger/frontend/src/commit/9f7e342333b80bfacfd5bc394a2dcd0613d33ff3/README.md#проверка) и [backend](https://git.personal.ksv741.ru/WealthLedger/backend/src/commit/016782657acfd35ff3fd755462007938fac35156/README.md#проверка). Для изменения проекта начните с [AGENTS](AGENTS.md). +Общий маршрут Issue → PR — [Engineering Workflow](.ai/conventions/engineering-workflow.md). ## Релизы и ветки @@ -157,8 +158,8 @@ T-Bank. Полные команды и ограничения принадлеж `vX.Y.Z`. Root-репозиторий называется `main`, его ветки — `main` и `develop`. `main` хранит принятые релизы, `develop` — интеграцию разработки. Новая задача: -отдельная ветка от свежего `develop`, затем PR в `develop`. Для веток агента -используется `codex/`; также допустимы `feature/` и `fix/`. +отдельная [issue-linked ветка](.ai/conventions/engineering-workflow.md#git-branching) +от свежего `develop`, затем PR в `develop`. В `main` напрямую не пушить. Обычные PR можно squash-merge; релизные PR и обратную синхронизацию `main` с `develop` сливать с сохранением истории. @@ -167,7 +168,8 @@ Commits и заголовки PR — Conventional Commits. `feat:` повыша `docs:`, `spec:`, `chore:`, `build:`, `ci:`, `refactor:`, `test:`, `style:` и `revert:` дают patch при выпуске. `chore(release):` — служебная запись, исключённая из changelog и рекомендации версии. Release-it не запускается -автоматически после каждого commit. Номер связанной issue можно добавить в заголовок. +автоматически после каждого commit. Ссылка на Issue — в +[Commit Conventions](.ai/conventions/engineering-workflow.md#commit-conventions). ### Подготовка релиза diff --git a/commitlint.config.cjs b/commitlint.config.cjs index 4aa31e2..512700c 100644 --- a/commitlint.config.cjs +++ b/commitlint.config.cjs @@ -1,4 +1,12 @@ module.exports = { extends: ["@commitlint/config-conventional"], - rules: { "type-enum": [2, "always", ["feat", "fix", "perf", "docs", "spec", "refactor", "build", "ci", "test", "style", "chore", "revert"]] }, + defaultIgnores: false, + ignores: [ + message => /^chore\(release\): \d+\.\d+\.\d+$/.test(message.trim()), + message => /^Merge pull request ['"].+['"] \(#\d+\) from .+ into .+$/.test(message.trim()), + ], + rules: { + "type-enum": [2, "always", ["feat", "fix", "perf", "docs", "spec", "refactor", "build", "ci", "test", "style", "chore", "revert"]], + "references-empty": [2, "never"], + }, }; diff --git a/package-lock.json b/package-lock.json index 9787d53..0c9374f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "wealth-ledger-workspace", - "version": "0.2.1", + "version": "0.2.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "wealth-ledger-workspace", - "version": "0.2.1", + "version": "0.2.2", "devDependencies": { "@commitlint/cli": "21.2.2", "@commitlint/config-conventional": "21.2.2", diff --git a/package.json b/package.json index ebff519..0f59514 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wealth-ledger-workspace", - "version": "0.2.1", + "version": "0.2.2", "private": true, "engines": { "node": "24.21.0"