diff --git a/.agents/harness.lock.json b/.agents/harness.lock.json new file mode 100644 index 00000000..94108a35 --- /dev/null +++ b/.agents/harness.lock.json @@ -0,0 +1,108 @@ +{ + "files": { + ".agents/skills/techcon-alert-export/SKILL.md": "7ea5336cdaa143a1f76748aed8bcc173ac81cdaeaff4bb22ed52d338e124b03b", + ".agents/skills/techcon-architecture-boundaries-template/SKILL.md": "0475acba542d3c3f77d9a3d29315716ad27d0e093cc81944bc56c5fb2d560f05", + ".agents/skills/techcon-billing-protocol/SKILL.md": "560987820ac7b44d1c6c95d65397a981e378de182c50bf1703bfab10ab9f3a05", + ".agents/skills/techcon-business-document-system/SKILL.md": "54e92f54ecf86bf7c58d7d2bd04d01a53dd7c78b3d002374da1743c8cfe2dcb3", + ".agents/skills/techcon-claude-code-review-intake/SKILL.md": "a9f5904775a0a148647b2ca73ac256aa3239ab806cb006c82890fe4fecd7db33", + ".agents/skills/techcon-claude-code-review-request/SKILL.md": "c20d8b8a7695bcd9e4ccf26a93571b4d84e7be305e237d3e03838ed7e24d9ede", + ".agents/skills/techcon-claude-handoff-preparation/SKILL.md": "29bb44c2c8c0ea7f0fa2ad3d9f0c50cf358d38770516ba1f6ae6b08c09f2da49", + ".agents/skills/techcon-contract-auditor/SKILL.md": "21fe9319a29cf3b7b5aa5a6ab74d92f6276b48e2c10d305bc097c5224f71e53f", + ".agents/skills/techcon-cross-service-tester/SKILL.md": "1a99c129b9ee5e0a1c28bf636ddfc6eb17ccec9a2ea0354e1ac77f29fe4070e3", + ".agents/skills/techcon-external-report-authoring/SKILL.md": "40d83c0ea6e04e81d09472ad002c7e66dc84af9c37703dbf61c63fa9a5466def", + ".agents/skills/techcon-fact-verification/SKILL.md": "d6bb3f62a669f39c03224ae45f3cd31eb83900f77481501fe5b7c192e3e7f8f5", + ".agents/skills/techcon-grafana-monitoring-access/SKILL.md": "1d68aaf42e77522303aaec6919216a9cecb85aa3f593f546017a4b30853a8093", + ".agents/skills/techcon-infra-access-map/SKILL.md": "832582499d1e6f45d870e67ba182cb72fc8e66669bd69871b15e731fbd4234fb", + ".agents/skills/techcon-infra-access/SKILL.md": "01584ece79b2cfe10c4183c9f4d45e5e6ff34eba6ee71443ee8a8088c36c84bb", + ".agents/skills/techcon-knowledge-system/SKILL.md": "15c237223298544876bdcf97b71623b0bdc799fa6c118d63c9fd0b09fe39f2ab", + ".agents/skills/techcon-large-session/SKILL.md": "2109efd5edb02ea4f1ca9fa9b89c533060c2b674bdbfed1d8d02e59d675cdc82", + ".agents/skills/techcon-linear-governance/SKILL.md": "20740546a4d3aed5f3cda18871139050127b5013256ee55384da1687f9a162f3", + ".agents/skills/techcon-markdown-docx-export/SKILL.md": "47e23677374a97f578f815371a743351bd031007b5b2c8caf1fd801d7b6aca09", + ".agents/skills/techcon-pre-implementation-contracts/SKILL.md": "d4476580f43741238c0e6226dffcfbb0929794f1888f5c1b88ed23728d481981", + ".agents/skills/techcon-production-safety/SKILL.md": "57c4f72c749a2a7bff6b53bf50afb0ba1672cf52fb30624098db964d8c11f8af", + ".agents/skills/techcon-report-synthesis-merge/SKILL.md": "4dd35228484577a00d4c7696ec82ba8f9e0f83632d3bf090ee82d3ac9cda76d9", + ".agents/skills/techcon-research-first/SKILL.md": "1731a44966a55567b70cae0bd3863bd9340be3c4618a0e3daa936a096df8a968", + ".agents/skills/techcon-session-handoff/SKILL.md": "055864a05b77940765108f8eef5477d2776821dc999af785ddfab2eb8c74f6c3", + ".agents/skills/techcon-tz-authoring/SKILL.md": "c89ca405f9d802de49b4e9b1e7dde0ac9d79b38cc50180a053573fb45ff2971a", + ".agents/skills/techcon-web-research-safety/SKILL.md": "6c2660a66bac456d6cc2642b9e3f597becd412b1834733c2969319b69e048499", + ".agents/skills/techcon-yc-cli/SKILL.md": "ffedde087e29cf03482b3c6345ca9b950e4c15d132f8f2a402bc2be3339122f5", + ".claude/agents/techcon-builder.md": "029787fa71dfa92908efb9214c8a9f228b11130f096dc0b553e237a98d50690a", + ".claude/agents/techcon-fixer.md": "e70bf54460528c552962a599d9999183f6050111ec29f9fe6d782d0375529023", + ".claude/agents/techcon-planner-reviewer.md": "4b73012442f83f2ef77ae2742f57808a3d6406c57361cab5fb88265dd8b9ba17", + ".claude/agents/techcon-scout.md": "1af6534c2685880ad193dbc0c2782ec763b7c25035b429507d2894a17afccad1", + ".claude/agents/techcon-solver.md": "876f2b6fa287d9be5c661c89922c49c52dac2f4046cc96582a09c075dabb641b", + ".claude/skills/techcon-alert-export/SKILL.md": "7ea5336cdaa143a1f76748aed8bcc173ac81cdaeaff4bb22ed52d338e124b03b", + ".claude/skills/techcon-architecture-boundaries-template/SKILL.md": "0475acba542d3c3f77d9a3d29315716ad27d0e093cc81944bc56c5fb2d560f05", + ".claude/skills/techcon-billing-protocol/SKILL.md": "560987820ac7b44d1c6c95d65397a981e378de182c50bf1703bfab10ab9f3a05", + ".claude/skills/techcon-business-document-system/SKILL.md": "54e92f54ecf86bf7c58d7d2bd04d01a53dd7c78b3d002374da1743c8cfe2dcb3", + ".claude/skills/techcon-claude-code-review-intake/SKILL.md": "a9f5904775a0a148647b2ca73ac256aa3239ab806cb006c82890fe4fecd7db33", + ".claude/skills/techcon-claude-code-review-request/SKILL.md": "c20d8b8a7695bcd9e4ccf26a93571b4d84e7be305e237d3e03838ed7e24d9ede", + ".claude/skills/techcon-claude-handoff-preparation/SKILL.md": "29bb44c2c8c0ea7f0fa2ad3d9f0c50cf358d38770516ba1f6ae6b08c09f2da49", + ".claude/skills/techcon-contract-auditor/SKILL.md": "21fe9319a29cf3b7b5aa5a6ab74d92f6276b48e2c10d305bc097c5224f71e53f", + ".claude/skills/techcon-cross-service-tester/SKILL.md": "1a99c129b9ee5e0a1c28bf636ddfc6eb17ccec9a2ea0354e1ac77f29fe4070e3", + ".claude/skills/techcon-external-report-authoring/SKILL.md": "40d83c0ea6e04e81d09472ad002c7e66dc84af9c37703dbf61c63fa9a5466def", + ".claude/skills/techcon-fact-verification/SKILL.md": "d6bb3f62a669f39c03224ae45f3cd31eb83900f77481501fe5b7c192e3e7f8f5", + ".claude/skills/techcon-grafana-monitoring-access/SKILL.md": "1d68aaf42e77522303aaec6919216a9cecb85aa3f593f546017a4b30853a8093", + ".claude/skills/techcon-infra-access-map/SKILL.md": "832582499d1e6f45d870e67ba182cb72fc8e66669bd69871b15e731fbd4234fb", + ".claude/skills/techcon-infra-access/SKILL.md": "01584ece79b2cfe10c4183c9f4d45e5e6ff34eba6ee71443ee8a8088c36c84bb", + ".claude/skills/techcon-knowledge-system/SKILL.md": "15c237223298544876bdcf97b71623b0bdc799fa6c118d63c9fd0b09fe39f2ab", + ".claude/skills/techcon-large-session/SKILL.md": "2109efd5edb02ea4f1ca9fa9b89c533060c2b674bdbfed1d8d02e59d675cdc82", + ".claude/skills/techcon-linear-governance/SKILL.md": "20740546a4d3aed5f3cda18871139050127b5013256ee55384da1687f9a162f3", + ".claude/skills/techcon-markdown-docx-export/SKILL.md": "47e23677374a97f578f815371a743351bd031007b5b2c8caf1fd801d7b6aca09", + ".claude/skills/techcon-pre-implementation-contracts/SKILL.md": "d4476580f43741238c0e6226dffcfbb0929794f1888f5c1b88ed23728d481981", + ".claude/skills/techcon-production-safety/SKILL.md": "57c4f72c749a2a7bff6b53bf50afb0ba1672cf52fb30624098db964d8c11f8af", + ".claude/skills/techcon-report-synthesis-merge/SKILL.md": "4dd35228484577a00d4c7696ec82ba8f9e0f83632d3bf090ee82d3ac9cda76d9", + ".claude/skills/techcon-research-first/SKILL.md": "1731a44966a55567b70cae0bd3863bd9340be3c4618a0e3daa936a096df8a968", + ".claude/skills/techcon-session-handoff/SKILL.md": "055864a05b77940765108f8eef5477d2776821dc999af785ddfab2eb8c74f6c3", + ".claude/skills/techcon-tz-authoring/SKILL.md": "c89ca405f9d802de49b4e9b1e7dde0ac9d79b38cc50180a053573fb45ff2971a", + ".claude/skills/techcon-web-research-safety/SKILL.md": "6c2660a66bac456d6cc2642b9e3f597becd412b1834733c2969319b69e048499", + ".claude/skills/techcon-yc-cli/SKILL.md": "ffedde087e29cf03482b3c6345ca9b950e4c15d132f8f2a402bc2be3339122f5", + ".gitignore": "90aa4dd5ed329ce5d43cbd5f018b4a4d43719a0736c1def8f277082192ee5bd7", + "AGENTS.md": "bae4abd61c0247184e804d5f1c82ee2f2b3f5a34d555f4f3457091ca70eb2ebf", + "CLAUDE.md": "336cc4fbf19beaada7ccf9986414fa91851a8d7a07dfb3ccbe800a69eed0ab49" + }, + "harness_sha": "56a6fcf9f3cd846d7238a9233b40f00eed7f22b0", + "repo": "techcon_activity_graph", + "schema_version": "1.0.0", + "skills": { + "techcon-alert-export": "1751cb80b7aa85ff2b08089c8e704b126481f5fe7a1d32e516b0d960786dd9c2", + "techcon-architecture-boundaries-template": "26c520c662b3a1f50d1be441f4ad53ddfad150fe9ec0c80ab45d39f8bcd58405", + "techcon-billing-protocol": "220ad7b73ab689f87c1cce6788f156d38fdd65e47f600ce238afa5742d3dccd4", + "techcon-business-document-system": "5312641abda6936d2a366bce989eb985e962ade4d6580570350e2c599389c91a", + "techcon-claude-code-review-intake": "26a2e8f8b0a0a057efee0baa2ad472cc4dde63a29fd842db7dccf24908ff7e9a", + "techcon-claude-code-review-request": "1f8f0ce447c8a57aa2b6efb1da9e4efa33f9ae1d88875e686c0834e860638ffb", + "techcon-claude-handoff-preparation": "55d7cfc812c5ba2ded68830feb710cd4c63c1464bca9fb8acb6528786a3e7976", + "techcon-contract-auditor": "d8d8eadf0448065a5476971044f6738b1cd64f167e53167748ce5ee9d9801c08", + "techcon-cross-service-tester": "d9910ee55820c254405e65496740520898a5f5a74250e8e4e76e66e046af4a69", + "techcon-external-report-authoring": "bc99b1d241faaac7cf3ee39a5c858d5f600bba3b03eed3eb13eff57192756855", + "techcon-fact-verification": "a60d6bd491e38d5f23701263219f0eed8e864674a75fd5d768e3a3c2c365c0e8", + "techcon-grafana-monitoring-access": "db0250155ea7af2e6be9ba19fa4c15a9dd889af4ca6b0d2edbb9f45233ec3c2b", + "techcon-infra-access": "5a11bdbca6c7abb250224bb25ce1ef2e93ddb26e8679401d5c58bcf57f48c533", + "techcon-infra-access-map": "80a9bf08b2731174b552d4efc0d1e9d0bfcd2efa3153e1588b47a0133c6bc037", + "techcon-knowledge-system": "94c565b778a70b2106d1df632853c349a6ec8df74a2548c0725c6364ae51941e", + "techcon-large-session": "311b72d06bd9371216327b16b35469233f470bb738d13168227f515b3ccb90ba", + "techcon-linear-governance": "07ee5516077f3621aff42897bebe9ddabbcd56cf2bae7bc92c9915ae98638e5d", + "techcon-markdown-docx-export": "e7ce41b3f916acc91aca00274ebd5c1f28da09abef2a3e52de5aa073c5242f69", + "techcon-pre-implementation-contracts": "8a04631191754f6d8abe4de83c446967bf6a043a3d31d08f13c9af4ccfba780e", + "techcon-production-safety": "bc1a6130855894fcc5ed17734d29bce610537f470547b2184e8269b4b85062a4", + "techcon-report-synthesis-merge": "f4229c48764f0baa87d2cc53da87dc24dd52ba5a7a40c31064cac3ac7e877fd9", + "techcon-research-first": "f3573c35a98d39b15e187cec3b38177da872c64e2d681144a80cf7e372e109e6", + "techcon-session-handoff": "63f2632765abe8ca1a6b8f3c416e13e1aa8cd3ec0d7743eacf32ac2038eee611", + "techcon-tz-authoring": "767fde98e2028be0196466b81ee6a2aa0303868c2f27c70f600cacaf140f38e1", + "techcon-web-research-safety": "1ec49c66d46ad5c8ca9898027402c21cedb1ee8ecdb2c697b877725a1a5f05a7", + "techcon-yc-cli": "f0235bd60b93d6001a731dd2d3c1873f6d9e24a88d1485df46178dcf57321dfe" + }, + "sources": { + "models/registry.yaml": "e064fe2b21e4484b64451094565e66263bf7efd77da12105471dba1859059fd1", + "policy/ask.yaml": "46af1e1ee51be02c61c725a3aaef703855ce59a598cecda86961b27f2c3388a8", + "policy/deny.yaml": "3226d91dba6960b35ef3660690efdf92e612fedd1e03b330a2492cb5af52e316", + "policy/exec-profile.yaml": "509be446ef5a521068f7ba46f9a6d1f9763e8dee2cd3d8a27d0f86bd56f7cd48", + "policy/prompt.yaml": "6281ecd4837a3e2e8f843230b51d5b30ebe7119a50f560d2a4de38ec2b2e0832", + "policy/sandbox.yaml": "2bc653b92d341db06c726be56640815aa373de839efdcd8d3cf6ecff940338b5", + "roles/builder.yaml": "6de9aab8f20c1f3f3049760d43d86dd90191bf8944ddaaf5034d6a1e7803ae14", + "roles/fixer.yaml": "c6215a1ef4f5ee5b27e39be7f908f82126f1b2a1b77c3a01943a121ead559cee", + "roles/planner-reviewer.yaml": "da3d2a74f97dde94b14aa05117fac0705f7ce5942ba6c430d411f998e939fb37", + "roles/scout.yaml": "2063392b871b3b953008a712f8e61089fe27373345644a213a2c3f5ca927b7bf", + "roles/solver.yaml": "73b798322846835fdfd593eac25e543618b636f526066b31498d46325fffd401" + } +} diff --git a/.agents/skills/techcon-alert-export/SKILL.md b/.agents/skills/techcon-alert-export/SKILL.md new file mode 100644 index 00000000..0e85cef9 --- /dev/null +++ b/.agents/skills/techcon-alert-export/SKILL.md @@ -0,0 +1,50 @@ +--- +name: techcon-alert-export +description: Используй для выгрузки последних алертов мониторинга TechCon (статус, время, детали) — диагностика инцидента, сводка за период, проверка «замолчал ли алерт». +harness: org@56a6fcf9f3cd846d7238a9233b40f00eed7f22b0 +--- +# Выгрузка алертов мониторинга + +Используй этот скилл, когда нужно получить последние N алертов экосистемы TechCon — +для диагностики инцидента, сводки за период или проверки, замолчал ли алерт после фикса. +Применим из любого репозитория TechCon, не привязан к конкретному репозиторию вызова. + +## Источник истины + +Алерты доставляются на почту (alertmanager → e-mail). Почтовый ящик — первичный источник. +Правила алертов (пороги) живут в репозитории/сервисе мониторинга целевой экосистемы — +конкретное расположение уточняй в текущем проекте, не подставляй запомненное значение. + +| Источник | Назначение | +|---------|-----------| +| Почта (alertmanager) | фактические срабатывания со временем | +| Правила алертов (мониторинг-стек: Prometheus/Alertmanager/Grafana) | пороги, что мониторится | + +## Шаги + +1. **Подключись к почтовому источнику** (read-only). Доступ/креды бери из переменных + окружения или менеджера секретов проекта — никогда не хардкодь в команде. +2. **Выгрузи последние N** писем-алертов (по умолчанию N=20), отсортированные по времени + (новые сверху). Фильтр по отправителю/теме alertmanager. +3. **Нормализуй каждый алерт** в структуру: + - `time` — время срабатывания (ISO, локаль оператора) + - `status` — firing / resolved + - `alertname` — имя правила + - `target` — сервис/ВМ/инстанс + - `severity` — critical / warning / info + - `details` — краткое тело (что превышено, значение vs порог) +4. **Сгруппируй по `alertname`+`target`** — покажи firing↔resolved пары (сколько раз + срабатывал, замолчал ли). Открытые (firing без resolved) — выделить. + +## Минимальный вывод + +Таблица последних N алертов: `time | status | alertname | target | severity | details`. +Плюс краткая сводка: сколько открытых (firing), сколько резолвилось, какие повторяются. +Если источник пуст/недоступен — зафиксируй явно («ящик недоступен: {причина}»), не выдумывай. + +## Анти-паттерны + +- Пересказ «алертов много» без конкретных строк со временем/статусом. +- Игнор resolved-событий — без них не видно, замолчал ли алерт после фикса. +- Хардкод кредов почты в команде (shell history) — только через переменные окружения/секрет-менеджер. +- Подмена почтового источника догадкой по логам сервиса (это другой сигнал). diff --git a/.agents/skills/techcon-architecture-boundaries-template/SKILL.md b/.agents/skills/techcon-architecture-boundaries-template/SKILL.md new file mode 100644 index 00000000..c2aa191c --- /dev/null +++ b/.agents/skills/techcon-architecture-boundaries-template/SKILL.md @@ -0,0 +1,78 @@ +--- +name: techcon-architecture-boundaries-template +description: Настрой arch-lint для репозитория со слоистой структурой (domain/infra, core/adapters): детект слоёв, запрет обратных зависимостей и циклов. Применяй при онбординге нового репозитория или по прямой просьбе проверить архитектурные границы. +harness: org@56a6fcf9f3cd846d7238a9233b40f00eed7f22b0 +--- +# Шаблон архитектурных границ + +## Когда применять + +- При онбординге нового репозитория без существующего arch-lint конфига. +- По прямому запросу пользователя: «настрой проверку архитектурных границ», «запрети domain + импортировать infra», «проверь граф зависимостей между модулями». + +**Не применяется:** +- К репозиторию БЕЗ выраженной слоистой структуры (простой скрипт, монолитный `main.py`) — + предлагать конфиг с пустым `forbidden`-списком создаёт cargo-cult файл без пользы. +- К репозиторию, где arch-lint конфиг уже существует (`.dependency-cruiser.cjs` / `.importlinter` + / `.go-arch-lint.yml` / `scripts/check-crate-boundaries.sh`) — не предлагать замену, не + автопатчить существующий файл. + +## Процесс + +1. **Детекция стека** — прочитай `package.json`/`pyproject.toml`/`go.mod`/`Cargo.toml` в корне + репозитория, чтобы определить язык и пакетный менеджер. +2. **Детекция слоёв** — найди пары директорий по типовым DDD/hexagonal соглашениям: + - `src/domain` + `src/infra` (TS/Python/Go) + - `core/` + `adapters/` + - `application/` + `infrastructure/` + - `internal/domain` + `internal/infra` (Go) + - Multi-crate workspace: `crates/*/Cargo.toml` с ≥2 крейтами (Rust) +3. **Если слои не найдены** — не предлагай ничего, не упоминай в итоговом отчёте (молчаливый + skip, не «нашёл, но не подходит» — если триггера нет, skill не оставляет след в диалоге). +4. **Если слои найдены и конфиг не существует** — покажи предложение пользователю (формат ниже), + жди `Y/n`. +5. **При подтверждении:** + - Возьми соответствующий шаблон под язык проекта (`dependency-cruiser.config.cjs` для TS, + `.importlinter` для Python, `go-arch-lint.yml` для Go, `check-crate-boundaries.sh` для Rust) + — из внутренней библиотеки шаблонов инструмента или напиши минимальный конфиг с нуля по + документации самого инструмента, если готового шаблона нет под рукой. + - Подставь РЕАЛЬНЫЕ найденные пути слоёв вместо примерных `src/domain`/`src/infra` из шаблона. + - Запиши файл в корень проекта (`.dependency-cruiser.cjs`, `.importlinter`, `.go-arch-lint.yml`) + или `scripts/check-crate-boundaries.sh` (Rust, `chmod +x`). + - Предложи (НЕ автопатчь) добавление CI-job, проверяющего границы, в существующий CI-workflow + репозитория — покажи diff, спроси подтверждение отдельно от конфига. +6. **При отказе** — не создавай файл, зафиксируй решение одной строкой в заметках репозитория + (например, в его собственном README или журнале решений): «Arch-lint предложен, отклонён + пользователем {дата}» — чтобы повторный онбординг не предлагал то же самое снова. + +## Правила + +- **Не угадывай конкретные запрещённые пары** сверх обнаруженной пары слоёв — предлагай ровно то, + что нашёл (`{найденный_layer_A} → {найденный_layer_B}` запрещено), не расширяй список правил + фантазией об архитектуре, которую не видел в коде. +- **Никогда не патчь существующий CI-workflow автоматически** — только предложение с diff, + подтверждение пользователя обязательно: изменение CI влияет на все последующие PR всех + контрибьюторов репозитория. +- **Rust — честная оговорка в самом диалоге с пользователем**, не молчать про специфику: + ``` + Rust: настоящая граница — это Cargo workspace (компилятор физически не даст domain-крейту + использовать infra, если тот не в Cargo.toml). Скрипт scripts/check-crate-boundaries.sh — + дополнительная защита ТОЛЬКО от случая, если кто-то явно пропишет infra в зависимости domain + в Cargo.toml (компилятор это разрешит, скрипт поймает). + ``` +- **Языковая локализация** — предложение и объяснение пользователю на русском; сами + конфиги/YAML/имена файлов — на английском (стандарт инструментов). + +## Output contract + +При успешном создании — одна строка в итоговом отчёте: +``` ++ .dependency-cruiser.cjs — запрет {layer_A}→{layer_B}, запрет циклов +``` +(конкретный инструмент и путь — по факту сработавшей ветки; для Rust — путь +`scripts/check-crate-boundaries.sh`, не конфиг-файл). + +Если пользователь отклонил — ничего не пишется в отчёт, только одна строка в заметках +репозитория (см. Процесс, шаг 6). + diff --git a/.agents/skills/techcon-billing-protocol/SKILL.md b/.agents/skills/techcon-billing-protocol/SKILL.md new file mode 100644 index 00000000..7ac6898a --- /dev/null +++ b/.agents/skills/techcon-billing-protocol/SKILL.md @@ -0,0 +1,107 @@ +--- +name: techcon-billing-protocol +description: Используй для воспроизводимого снятия затрат Yandex Cloud любого репозитория TechCon — baseline-расчёт, прогноз бюджета, сверка свежести или сводка расходов. +harness: org@56a6fcf9f3cd846d7238a9233b40f00eed7f22b0 +--- +# Протокол снятия затрат TechCon + +Используй этот скилл, когда нужно посчитать затраты Yandex Cloud экосистемы TechCon — +baseline-расчёт, прогноз бюджета, сверка свежести или сводка расходов. Протокол +**воспроизводимый**: один и тот же ввод → один и тот же вывод, без эвристик «на глаз». +Применим из любого репозитория TechCon — не завязан на конкретный репозиторий вызова. + +Два слоя: +1. **Расчёт** — детерминированная обёртка над `cost_control.py` (Decimal-точность, сценарии). +2. **Свежесть** — независимая сверка с актуальными данными биллинга через REST. + +## Источник истины + +| Источник | Где | Назначение | +|---------|-----|-----------| +| Калькулятор | `~/Repos/techcon_hub/scripts/cost_control.py` | детерминированный расчёт из billing workbook (xlsx) | +| Тесты калькулятора | `~/Repos/techcon_hub/tests/test_cost_control.py` | контракт расчёта (запускать в репозитории, где лежит скрипт) | +| Billing account | `dn26qcivkuh5q8ov1028` | единственный billing-аккаунт TechCon | +| Свой периметр (folder) | `b1g04ejboauabchemv0f` | folder-allowlist — ТОЛЬКО эти затраты наши | +| VM флот, cloud/folder | `~/Repos/techcon_infra_yac/INFRA.yaml` | сверка какие инстансы в нашем folder | + +## 0. ОБЯЗАТЕЛЬНАЯ сегментация своё/чужое (folder-allowlist) + +Billing-аккаунт `dn26qcivkuh5q8ov1028` может агрегировать **несколько облаков/folder-ов**, в т.ч. +чужие. Считать «всё что в аккаунте» — ошибка: завысишь бюджет чужими ресурсами. + +**Правило:** наши затраты = ТОЛЬКО ресурсы в folder-allowlist `[b1g04ejboauabchemv0f]`. +Любой другой folder/cloud — **чужой**, исключается из расчёта явно (не молча). + +Перед любым роллапом: + +1. **Получи список наших инстансов** (джойн периметра): + ```bash + yc compute instance list --folder-id b1g04ejboauabchemv0f --format json + ``` + Это набор ресурсов, затраты по которым считаем нашими. Всё вне этого набора — чужое. + +2. **При разбивке биллинга по folder** оставляй ТОЛЬКО `b1g04ejboauabchemv0f`. Чужие folder-ы + перечисли явно в выводе как исключённые (`excluded_folders: [...]`), а не отбрасывай тихо. + +3. **Сверь с INFRA.yaml** — все инстансы из allowlist-folder должны присутствовать в INFRA.yaml; + расхождение (инстанс в биллинге, но не в INFRA.yaml) — фиксируй как аномалию, не сглаживай. + +## 1. Расчёт (обёртка cost_control.py) + +`cost_control.py` — детерминированный калькулятор из billing workbook (xlsx). Считает baseline, +always-on базу, активный runtime и сценарии (baseline/growth/peak/idle) в Decimal-точности. +Внешний billing-компонент (если есть) подаётся отдельным sidecar-JSON и при отсутствии держится +явным нулём с пометкой (без тихой экстраполяции). + +```bash +python3 ~/Repos/techcon_hub/scripts/cost_control.py \ + --input-xlsx {billing_workbook.xlsx} \ + --external-billing-json {external_billing.json} \ + --period-start YYYY-MM-DD \ + --period-end YYYY-MM-DD \ + --timezone Europe/Moscow \ + --output {out.json} +``` + +Контракт обязывает: явный период должен совпадать с периодом в workbook summary (иначе ошибка), +shared-затраты атрибутируются только нашим ресурсам, отсутствующие источники = явный ноль + assumption. +Результат (`out.json`) — `scenario_contract`, `yc_breakdown`, `workbook_observations`. Это +**воспроизводимая** часть: не пересказывай, прикладывай числа из JSON. + +## 2. Свежесть через REST billing API (НЕ `yc billing`) + +⚠ `yc billing` CLI-группы НЕТ в текущем профиле — не используй её. Свежесть берём через REST: + +``` +GET https://billing.api.cloud.yandex.net/billing/v1/billingAccounts/dn26qcivkuh5q8ov1028 +Authorization: Bearer $(yc iam create-token) +``` + +Эндпоинты `billing.api.cloud.yandex.net/billing/v1` (billingAccount/service/sku) дают актуальное +состояние аккаунта и помогают сверить: не устарел ли workbook, не появились ли новые SKU/folder-ы. +IAM-токен бери через `yc iam create-token` в подстановке — **не печатай токен и не логируй заголовок +Authorization** (см. §Анти-паттерны). + +**Живой роллап затрат пер-folder/пер-SKU через REST = HANDOFF.** Детальный usage-роллап требует +сервис-аккаунта с ролью `billing.accounts.viewer` на billing-аккаунте — у обычного профиля её нет +(403). Не пытайся обойти. Оформи HANDOFF оператору: запрос SA с `billing.accounts.viewer` + +критерий приёмки (GET по аккаунту отдаёт 200, usage-export доступен). До выдачи роли — свежесть +ограничена метаданными аккаунта, основной расчёт остаётся на `cost_control.py` из workbook. + +## Минимальный вывод при использовании навыка + +- billing account (`dn26qcivkuh5q8ov1028`) + folder-allowlist (`b1g04ejboauabchemv0f`) +- сегментация: список наших инстансов (из `--folder-id`), явный список исключённых чужих folder-ов +- расчётные числа из `out.json` `cost_control.py` (baseline/always-on/runtime/сценарии), не пересказ +- свежесть: статус сверки с REST billing (метаданные аккаунта) ИЛИ HANDOFF-пометка, если нужен SA +- расхождения биллинг↔INFRA.yaml, если есть + +## Анти-паттерны + +- Считать «всё в billing-аккаунте» без folder-allowlist — завышение бюджета чужими облаками. +- Тихо отбрасывать чужие folder-ы — их надо перечислить явно как `excluded`. +- `yc billing ...` — группы нет в профиле; свежесть только через REST `billing.api.cloud.yandex.net`. +- Печать/лог IAM-токена или заголовка `Authorization` (секрет в shell history/выводе). +- Личный «живой роллап» usage без SA `billing.accounts.viewer` — это 403, оформляй HANDOFF. +- Пересказ «затраты примерно столько-то» вместо детерминированных чисел из `cost_control.py`. +- Доверие памяти вместо `out.json`/INFRA.yaml — id folder-ов и SKU дрейфуют. diff --git a/.agents/skills/techcon-business-document-system/SKILL.md b/.agents/skills/techcon-business-document-system/SKILL.md new file mode 100644 index 00000000..9c5a7ca5 --- /dev/null +++ b/.agents/skills/techcon-business-document-system/SKILL.md @@ -0,0 +1,60 @@ +--- +name: techcon-business-document-system +description: Используй при создании, правке или синхронизации бизнес-документов (стратегии, отчёты для стейкхолдеров, презентации), чтобы не смешивать их с инженерной документацией репозитория и не терять источник правды. +harness: org@56a6fcf9f3cd846d7238a9233b40f00eed7f22b0 +--- +# Business document system + +## Цель + +Вести базу бизнес-документов так, чтобы инженерный контекст репозитория оставался +чистым, а рабочий процесс с бизнес-документами — предсказуемым и отделённым от кода. + +## Порядок чтения + +1. Прочитать `docs/BUSINESS_DOCUMENT_SYSTEM.md` в целевом репозитории, если он есть — + там описаны конкретные пути и договорённости конкретного репозитория. +2. Если задача техническая — сначала читать актуальные технические документы + репозитория, к бизнес-документам обращаться только за точечным продуктовым + контекстом, который нужен именно для этой задачи. +3. Если задача — авторство или правка бизнес-документа, работать в локальном + рабочем каталоге репозитория (например `.local/business-docs/`), не в общей + технической документации. + +## Правила владения + +- Внешнее хранилище документов (Shared Drive, Google Docs, Confluence — то, что + использует конкретная команда) — целевая человеко-читаемая база бизнес-документов. +- Репозиторий остаётся источником правды об инженерном состоянии, не о бизнес-решениях. +- Доски для визуального планирования (Miro и подобные) не являются постоянным + хранилищем знания — использовать экспорт из них только как разовый источник + входных данных при написании документа, не как то, на что можно сослаться позже. +- Не копировать бизнес-документы целиком в markdown репозитория. + +## Рабочие правила + +- Авторский/правленый исходник бизнес-документа — в markdown, в локальном рабочем + каталоге репозитория. +- Итоговый документ для отправки во внешнее хранилище (`.docx` или формат, + принятый командой) генерировать из этого markdown-исходника, не писать вручную + параллельно с ним. +- На одно семейство документов — один стабильный корень имени файла, чтобы версии + было легко сопоставлять. +- Если бизнес-решение меняет инженерную работу — обновить соответствующий + технический документ репозитория коротким резюме влияния, не полным пересказом + бизнес-документа. + +## Обязательный отчёт по итогам использования скилла + +Явно указать: +- путь к исходному markdown; +- путь к сгенерированному итоговому документу; +- пришлось ли из-за этого менять инженерное резюме в репозитории; +- использовался ли как вход экспорт из доски визуального планирования (если да — откуда). + +## Антипаттерны + +- Считать доску визуального планирования постоянным источником знания +- Вручную править сгенерированный `.docx`/итоговый файл, когда есть markdown-исходник +- Копировать целые стратегические документы в репозиторий +- Затягивать бизнес-контекст в техническую задачу, когда он там не нужен diff --git a/.agents/skills/techcon-claude-code-review-intake/SKILL.md b/.agents/skills/techcon-claude-code-review-intake/SKILL.md new file mode 100644 index 00000000..aa2437f6 --- /dev/null +++ b/.agents/skills/techcon-claude-code-review-intake/SKILL.md @@ -0,0 +1,127 @@ +--- +name: techcon-claude-code-review-intake +description: Читает файл-результат ревью (frontmatter c полем verdict) после того как ревью помечено готовым, и продолжает работу по типу verdict — approved/changes-requested/needs-discussion/critique. +allowed-tools: Read, Edit, Grep, Glob +harness: org@56a6fcf9f3cd846d7238a9233b40f00eed7f22b0 +--- +# Intake результата ревью + +## Когда применять + +Сразу после того как получен сигнал «ревью готово» — независимо от того, кто +его написал (другая модель, человек-ревьюер, отдельный шаг пайплайна). +Работает с любым файлом ревью, у которого есть YAML-frontmatter с полем +`verdict` (по умолчанию ищи `review.md` в директории текущей задачи, но имя +файла может быть любым — бери из контекста задачи). + +## Шаги + +### 1. Найди и прочитай файл ревью + +Если файл не найден: +> "Файл ревью не найден. Убедись, что ревью действительно завершено, и +> проверь путь, который был указан при запросе ревью." + +### 2. Извлеки verdict + +Значения из YAML-фронтматтера: +- `approved` → реализация разрешена; +- `changes-requested` → нужны правки; +- `needs-discussion` → открытые вопросы требуют решения пользователя; +- `critique` → конструктивная критика (не блокер). + +Дополнительно проверь поле вида `notVerified` (или аналог) — сколько пунктов +ревьюер не смог проверить. + +### 3. Действуй по verdict + +#### approved + +``` +✓ Ревью пройдено. Продолжаем реализацию. + +Одобренные аспекты: +[из секции "Одобренные аспекты"] + +Замечания к учёту (non-blocking): +[рекомендации, если есть] + +Не проверено ({notVerified}): +[если есть — прими к сведению, при необходимости проверь сам] +``` + +Переходи к реализации (следующий шаг задачи). + +#### changes-requested + +``` +✗ Ревью требует правок. + +Блокеры ({N}): +[список блокирующих замечаний] + +Следующий шаг: обновить описание задачи/спецификацию → запросить повторное ревью. +``` + +Обнови описание задачи с учётом блокеров, затем повтори запрос ревью. + +#### needs-discussion + +``` +⚠ Открытые вопросы требуют решения. + +Вопросы от ревьюера: +[список открытых вопросов] +``` + +Задай вопросы пользователю. После ответов зафиксируй решения в описании задачи. + +#### critique + +``` +📋 Критика получена. Блокировки нет. + +Слабые места ({N}): +[список слабых мест] + +Не проверено: {notVerified} +``` + +Для каждого слабого места прими решение: учесть его в задаче или +аргументированно отклонить. Реализация продолжается — `critique` не блокирует. + +### 4. Обработай "не проверено" + +Если непроверенных пунктов больше нуля — реши, критично ли это для текущей +задачи: +- критично → получи недостающие доступы/данные, запроси повторное ревью + только для непроверенных пунктов; +- некритично → зафиксируй как known-unknown в описании задачи и продолжай. + +### 5. Обнови статус задачи + +Отметь в трекере задачи (issue/тикет/файл статуса), что ревью получено и +обработано — например, переведи статус `pending review` → `reviewed`. + +### 6. Залогируй результат + +Зафиксируй итог ревью там, где в проекте принято вести журнал работы по +задаче (лог изменений, комментарий в тикете, файл истории): + +```markdown +## {ISO-8601} — Code Review Intake + +- Verdict: {verdict} +- Blockers: {N} +- Critique items: {N} +- Not verified: {N} +- Review source: {путь или ссылка на файл ревью} +``` + +## Output contract + +- Verdict применён; +- `approved/critique`: работа над задачей продолжена; +- `changes-requested`: описание задачи обновлено с учётом блокеров; +- `needs-discussion`: пользователь получил открытые вопросы; +- статус задачи обновлён на «ревью обработано». diff --git a/.agents/skills/techcon-claude-code-review-request/SKILL.md b/.agents/skills/techcon-claude-code-review-request/SKILL.md new file mode 100644 index 00000000..95b2c679 --- /dev/null +++ b/.agents/skills/techcon-claude-code-review-request/SKILL.md @@ -0,0 +1,167 @@ +--- +name: techcon-claude-code-review-request +description: Запрос активного ревью у другого агента/ревьюера — создаёт brief с файлами, endpoint'ами, командами и требованиями к доступу. Применять перед архитектурными изменениями, миграциями или по прямому запросу критики. +allowed-tools: Read, Write, Grep, Glob +harness: org@56a6fcf9f3cd846d7238a9233b40f00eed7f22b0 +--- +# Запрос активного ревью + +Этот скилл готовит brief для внешнего ревью и запускает активную +диагностику — не просто чтение файлов, а реальная проверка состояния +системы (endpoint'ы, команды, доступы). + +## Когда применять + +- Перед реализацией для задач с архитектурными решениями +- Когда спецификация задачи включает изменения API, новые зависимости, миграции +- Для периодического reality-check документации репозитория +- По команде пользователя ("сделай критику", "проверь реальность", "найди пробелы") + +## Когда НЕ применять + +- Мелкий баг-фикс (одна строка) +- Чисто текстовые задачи без архитектурных решений + +--- + +## Шаги + +### 1. Определи тип ревью + +По контексту задачи: +- `architecture` — новые компоненты, рефакторинг структуры +- `security` — auth, данные пользователей, внешние вызовы +- `critique` — пользователь хочет критику без блокировки +- `reality-check` — проверить соответствие документации реальности +- `knowledge-gap` — найти пробелы в документации/базе знаний +- `full` — комплексный ревью + +### 2. Собери контекст задачи + +Прочитай проектную документацию репозитория (README, архитектурные заметки, +описание ограничений) и спецификацию задачи, если она оформлена отдельным +документом. + +Найди файлы кода, которые затрагивает задача. + +### 3. Собери что можно проверить + +**Endpoints:** найди в документации, README, docker-compose, nginx-конфигах +все URL сервисов, которые затрагивает задача. + +**Команды:** определи что полезно выполнить для диагностики (`git log`, +grep по коду, проверки конфигов, статус сервисов). + +**Доступы:** что может понадобиться ревьюеру (SSH-ключи, env vars, +credentials) — перечисли явно, не подразумевай. + +### 4. Создай brief + +Запиши в `{taskDir}/review-brief.md` (или иное согласованное в проекте +место для артефактов задачи): + +```markdown +--- +taskId: {taskId} +title: {название задачи} +reviewType: {тип} +status: pending +outputPath: {taskDir}/review-result.md +createdAt: {ISO-8601} +--- + +# Review Brief — {taskId}: {title} + +## Objective + +{1-2 предложения: что проверяем и почему важно до реализации} + +## Files to read (абсолютные пути) + +| Файл | Назначение | +|---|---| +| `/abs/path/README.md` | Архитектура проекта | +| `/abs/path/к/спецификации/задачи` | Спецификация | +| `/abs/path/к/затронутому/файлу` | {описание} | + +## Endpoints to verify + +| URL | Что проверить | +|---|---| +| `https://{host}/api/health` | Доступность, версия | +| `https://{host}/api/{endpoint}` | Контракт ответа | + +(если нет endpoint'ов — оставь таблицу пустой, ревьюер найдёт сам) + +## Commands to run + +\`\`\`bash +git log --oneline -10 +git diff --stat HEAD~5 +grep -r "{ключевое слово}" src/ --include="*.py" | head -20 +\`\`\` + +## Access requirements + +- **SSH к {host}** — для проверки {что именно} +- **{ENV_VAR}** — для аутентификации к {сервис} +- (оставь пустым если доступ не нужен) + +## Cross-repo context (если задача затрагивает несколько репозиториев) + +| Файл | Репозиторий | +|---|---| +| `/abs/path/к/артефакту/в/другом/репо` | {repo} | + +(удали секцию если не нужна) + +## Questions for review + +1. {Конкретный вопрос с контекстом} +2. {Вопрос о рисках} +3. {Вопрос о граничных случаях} + +## Already decided (не пересматривать) + +- **{Решение}**: {обоснование} +``` + +### 4b. Ограничь ревьюера рамками ревью + +Ревьюер должен только писать в `outputPath` и не трогать код или +спецификацию задачи — иначе роль ревьюера дрейфует в роль допиливателя, +и итог ревью перестаёт быть независимым. Зафиксируй это ограничение прямо +в brief (секция Objective или отдельной строкой) и, если инструмент +ревьюера поддерживает hook/guard на запись файлов, ограничь запись +единственным путём `outputPath`. + +### 5. Выведи инструкцию пользователю + +Выведи ДОСЛОВНО: + +--- +**Ревью готово к запуску.** + +Передай ревьюеру: +``` +Прочитай и выполни протокол ревью: +{абсолютный путь к review-brief.md} +``` + +Ревьюер проверит endpoint'ы, выполнит команды и запросит доступы если нужно. +После того как он напишет ревью — скажи мне "ревью готово". + +--- + +### 6. Жди сигнала + +После "ревью готово" — продолжи работу приёмом результата ревью +(извлеки verdict из результата и действуй по нему: approved / changes-requested +/ needs-discussion / critique). + +## Output contract + +- Файл `{taskDir}/review-brief.md` создан с абсолютными путями +- Секции `Endpoints`, `Commands`, `Access requirements` заполнены +- Ревьюер ограничен записью только в `outputPath` +- Пользователь получил инструкцию с точным путём к brief diff --git a/.agents/skills/techcon-claude-handoff-preparation/SKILL.md b/.agents/skills/techcon-claude-handoff-preparation/SKILL.md new file mode 100644 index 00000000..2feb0349 --- /dev/null +++ b/.agents/skills/techcon-claude-handoff-preparation/SKILL.md @@ -0,0 +1,162 @@ +--- +name: techcon-claude-handoff-preparation +description: Используй, когда задача лучше подходит для Claude: фронтенд, дизайн, HTML, презентации, дашборды, упаковка идей, бизнес- и продуктовое планирование. +harness: org@56a6fcf9f3cd846d7238a9233b40f00eed7f22b0 +--- +# Подготовка пакета передачи в Claude + +## Когда это нужно + +Этот навык нужен, когда текущая задача относится к одному или нескольким классам: +- фронтенд; +- интерфейсы; +- HTML; +- презентации / PowerPoint / PPTX; +- дашборды; +- визуальная упаковка; +- раскрутка идеи до полноценной концепции; +- бизнес-планирование; +- продуктовое планирование; +- упаковка MVP; +- клиентские материалы и одностраничники. + +## Главный принцип + +Не пытайся силой делать в текущем агенте то, что лучше делается в Claude. + +Вместо этого: +1. собери чёткое техническое задание; +2. собери компактный, но достаточный пакет контекста; +3. зафиксируй контракт возврата результата; +4. сложи всё в заранее созданную папку; +5. подготовь материалы так, чтобы их можно было одним фрагментом или малым набором файлов загрузить в Claude. + +## Обязательная структура пакета + +В целевой папке должны появиться минимум такие файлы: + +1. `STATUS.md` + - краткий статус пакета; + - что уже собрано; + - что ещё нужно доложить; + - куда Claude должен вернуть результат. + +2. `01-zadanie-dlya-claude.md` + - что нужно сделать; + - для кого результат; + - в каком формате нужен выход; + - какие ограничения обязательны; + - какие качества считаются успехом. + +3. `02-kontekst-i-ogranicheniya.md` + - контекст проекта; + - контекст пользователя; + - что уже известно; + - что уже пробовали; + - какие есть ограничения по рынку, продукту, сроку, стилю, безопасности; + - что нельзя потерять. + +4. `03-materialy-i-istochniki.md` + - список приложенных файлов; + - список ключевых документов; + - список внешних или внутренних опорных знаний; + - что является фактом, что гипотезой. + +5. `04-kontrakt-vozvrata.md` + - какие файлы Claude должен вернуть; + - где именно их положить; + - в каком формате; + - как подписывать версии; + - какие разделы обязательны. + +6. `input/` + - входные данные; + - заметки; + - копии исходных документов; + - изображения / схемы / скриншоты. + +7. `output/` + - пустая или заранее созданная папка, куда должен быть сложен результат после работы в Claude. + +## Что обязательно включать в ТЗ + +В `01-zadanie-dlya-claude.md` всегда фиксируй: +- конечную цель; +- целевую аудиторию; +- желаемый формат результата; +- что уже есть; +- чего не хватает; +- стиль подачи; +- что важнее всего: глубина, наглядность, дизайн, полнота, убедительность, скорость, аккуратность формулировок; +- что считается провалом. + +## Что обязательно включать в контекст + +В `02-kontekst-i-ogranicheniya.md` всегда фиксируй: +- краткое описание проекта; +- текущее состояние; +- бизнесовый или продуктовый контур; +- технические ограничения; +- рыночные ограничения; +- требования к русскоязычной подаче; +- запрет на лишние англицизмы, если это важно; +- что уже обсуждалось и к каким выводам пришли. + +## Как собирать контекст + +- Копируй только нужные файлы, а не весь репозиторий. +- Давай Claude достаточную картину, но не мусор. +- Если есть большой набор исходников, сначала делай индекс и пояснение, а не сваливай всё без структуры. +- Если в задаче есть визуальная или презентационная часть, по возможности приложи: + - примеры хорошего уровня; + - текущие черновики; + - скриншоты; + - структуру будущего артефакта. + +## Как формулировать контракт возврата + +В `04-kontrakt-vozvrata.md` всегда указывай: +- какие именно файлы нужно вернуть; +- какие названия файлов использовать; +- какие форматы допустимы (`.md`, `.html`, `.pptx`, `.docx`, `.png`, и т.д.); +- нужен ли промежуточный отчёт; +- что должно лежать в `output/`; +- что нужно оставить в виде пояснения рядом с результатом. + +## Обязательные режимы качества + +Если задача касается: + +### Фронтенда / HTML / интерфейсов +Нужно отдельно потребовать: +- структуру экранов; +- пользовательские сценарии; +- визуальную иерархию; +- продуманную композицию; +- аккуратные тексты; +- отсутствие случайной технической грязи в интерфейсе. + +### Презентаций / PPTX / ванпейджеров +Нужно отдельно потребовать: +- чёткую логику повествования; +- плотную, но не перегруженную подачу; +- понятную структуру слайдов/блоков; +- визуальное единообразие; +- деловой русский язык. + +### Бизнес- и продуктового планирования +Нужно отдельно потребовать: +- разделение фактов, гипотез и решений; +- список неизвестных; +- явные допущения; +- логику рынка и покупателя; +- варианты монетизации; +- риски целевого рынка, если это релевантно. + +## Важная оговорка + +Если пользователь явно сказал не делать пакет передачи и работать здесь напрямую, этому приоритету нужно подчиниться. + +Иначе режим по умолчанию такой: +- сначала качественно подготовить пакет для Claude; +- потом уже, при необходимости, продолжать локальную работу. diff --git a/.agents/skills/techcon-contract-auditor/SKILL.md b/.agents/skills/techcon-contract-auditor/SKILL.md new file mode 100644 index 00000000..4c9f4f15 --- /dev/null +++ b/.agents/skills/techcon-contract-auditor/SKILL.md @@ -0,0 +1,162 @@ +--- +name: techcon-contract-auditor +description: Найди рассинхроны контрактов между сервисами TechCon — polling-роуты, envelope ответа, коды ошибок, имена очередей/стримов, auth, /health. Применяй при аудите интеграции 2+ сервисов или подозрении на несовместимость. Выдаёт diff-таблицу с severity. +harness: org@56a6fcf9f3cd846d7238a9233b40f00eed7f22b0 +--- +# Аудитор межсервисных контрактов + +Используй для обнаружения нетривиальных рассинхронов между сервисами +экосистемы TechCon, которые не видны из одного репозитория. + +## Когда применять + +- Аудит интеграции двух и более сервисов (один вызывает другой по HTTP, + либо оба читают/пишут общую очередь). +- Подозрение на несовместимость: разные форматы ответа, разные имена + очередей у producer и consumer, health-check не совпадает с тем, что + реально проверяет мониторинг. +- Перед объединением или рефакторингом общего протокола между сервисами. + +**Не применяется** к одиночному сервису без внешних интеграций — там +нечего сверять. + +## Что проверять + +### 1. Polling-роуты между сервисами + +Если один сервис опрашивает другой по task-id, сверь: +- URL pattern (`/api/v1/task/{id}` vs `/result/{id}` vs `/status/{task_id}` + — разные паттерны для одной и той же операции — рассинхрон); +- модель ответа (поля, типы, обязательность) — сравнить Pydantic/schema + классы или `/openapi.json` обеих сторон. + +```bash +# Прочитать openapi или схемы роутов у каждого сервиса +grep -rn "class.*Response\|@router\.\(get\|post\)" /**/routes/ 2>/dev/null +curl -s /openapi.json | jq '.paths' +``` + +### 2. Envelope ответа (общий формат JSON) + +Если экосистема или конкретная пара сервисов договорились об общем +формате (например `{"ok": bool, "data": ..., "error": null | {...}}`), +проверь: +- у каждого FastAPI/HTTP-сервиса — есть ли middleware/dependency, + принудительно оборачивающий ответ в этот формат; +- нет ли эндпоинтов, отдающих «голый» JSON, HTML или файл там, где + ожидается envelope (это не всегда баг — веб-страницы и экспорт файлов + законно не оборачиваются, но зафиксируй как явное исключение, не как + молчаливое расхождение). + +```bash +grep -rn "class.*Response(BaseModel)\|ok.*bool\|middleware" / 2>/dev/null | grep -v __pycache__ +``` + +### 3. Коды ошибок + +Проверь единообразие между сервисами: +- формат `detail`/`error` в `HTTPException` — одна и та же структура + (`{"code": "SNAKE_CASE", "message": "..."}`) или у каждого сервиса своя; +- нет ли кастомных кодов ошибок, известных только одному сервису, хотя + вызывающая сторона их не различает. + +```bash +grep -rn "HTTPException\|raise.*Error" / 2>/dev/null | grep -v __pycache__ +``` + +### 4. Имена очередей/стримов между producer и consumer + +Классический источник тихой поломки: сервис A пишет в очередь `X`, сервис +B слушает очередь `Y`, оба считают, что интеграция работает, пока не +проверишь явно. + +```bash +# Найти реальные имена stream/queue/topic на стороне producer и consumer +grep -rn "xadd\|publish\|enqueue\|XREAD\|consumer_group\|topic=" / / 2>/dev/null | grep -v __pycache__ +``` + +Если имена не совпадают буквально — это либо незавершённая миграция, либо +два параллельных протокола. Не чини сам: зафиксируй находку и уточни у +владельца сервиса, какой контур канонический, прежде чем менять код. + +### 5. Аутентификация между сервисами + +- Есть ли флаг вида `DISABLE_AUTH`/`SKIP_AUTH`, и не остаётся ли он + включённым в прод-конфигурации (сравнить dev/prod compose или env-файлы); +- fail-open vs fail-closed: если токен/секрет не задан в окружении, + падает ли сервис в 401/403 или молча пропускает запрос; +- не передаётся ли токен через query-параметр (`?token=...`) — попадает в + логи, историю браузера и `Referer`; корректно — только заголовок + `Authorization: Bearer`. + +```bash +grep -rn "DISABLE_AUTH\|SKIP_AUTH\|bypass\|bearer\|query_params.*token" / 2>/dev/null | grep -v __pycache__ +``` + +### 6. `/health` vs то, что реально проверяет мониторинг + +Сравни, что ожидает watchdog/пробa мониторинга (формат JSON, поля, +ожидаемый статус-код) с тем, что реально возвращает эндпоинт `/health` +или `/healthz` сервиса. Расхождение даёт ложные срабатывания или, хуже, +тишину при реальном отказе. + +```bash +grep -A5 "check\|health\|status" /watchdog* 2>/dev/null +grep -A10 "def health\|/health\|/healthz" /**/main.py 2>/dev/null +``` + +### 7. Общий middleware + +Есть ли переиспользуемый пакет middleware (envelope, error mapping, +rate-limit, CORS) между сервисами, или каждый реализует независимо? Если +независимо — рассинхрон п.2–3 будет повторяться при каждом новом сервисе. + +## Формат вывода + +```yaml +contract_audit: + timestamp: "ISO-8601" + severity_counts: + critical: N + high: N + medium: N + findings: + - id: "" + severity: critical | high | medium + services: ["", ""] + description: "Что именно разошлось" + files: + - "/path/to/file.py" + - "/path/to/file.py" + recommendation: "Что сделать (зафиксировать канон, привести к общему виду)" +``` + +## Как фиксировать закрытые находки + +При повторном аудите, если находка из прошлого отчёта устранена — не +удаляй её, добавь поле статуса: + +```yaml +- id: "" + severity: critical + status: "resolved" + resolved_by: "" + verified: "" +``` + +Никогда не удаляй старые findings — это создаёт историческую запись. + +## Правила работы + +- Читать только исходники, не исполнять и не изменять код сервисов. +- Приоритизация: critical > high > medium. + - critical = ломает интеграцию прямо сейчас или является риском + безопасности в проде; + - high = ломает при следующем изменении смежного сервиса, или высокий + security risk без немедленного эффекта; + - medium = техдолг, но работает сейчас. +- При каждом аудите верифицируй командами (grep/curl/openapi), не полагайся + на данные из прошлого отчёта без перепроверки. +- Если контракт неоднозначен (два параллельных протокола, неясно какой + канонический) — не чини сам, зафиксируй находку и предложи уточнить у + владельца сервиса. diff --git a/.agents/skills/techcon-cross-service-tester/SKILL.md b/.agents/skills/techcon-cross-service-tester/SKILL.md new file mode 100644 index 00000000..c634af56 --- /dev/null +++ b/.agents/skills/techcon-cross-service-tester/SKILL.md @@ -0,0 +1,135 @@ +--- +name: techcon-cross-service-tester +description: Пишет и валидирует кросс-сервисные E2E интеграционные тесты между сервисами TechCon-экосистемы (upload -> classify -> detect -> search -> render). Применяй, когда нужно найти конкретный сломанный контракт между двумя сервисами, а не просто зафиксировать что тест упал. +allowed-tools: Bash(pytest:*), Bash(httpx:*) +harness: org@56a6fcf9f3cd846d7238a9233b40f00eed7f22b0 +--- +# Кросс-сервисный тестировщик TechCon + +## Архитектура интеграционного тестирования + +Типовой E2E flow в экосистеме TechCon: `upload (сервис A)` → `classify (сервис B)` → +`detect (сервис C)` → `search (сервис D)` → `render (сервис E)`. + +Каждый переход между сервисами — потенциальная точка рассинхрона контракта: разные +форматы polling-роутов, разные envelope-структуры ответа, разные коды ошибок. Задача +скилла — не просто зафиксировать «упал тест», а назвать конкретный сервис и конкретное +поле/роут, где контракт разошёлся с соседним сервисом. + +## Что генерировать + +### Минимальный интеграционный тест (pytest) + +```python +# tests/integration/test_cross_service_contract.py +# Зависимости: pip install httpx pytest +""" +Кросс-сервисный контракт: сервис A (polling) -> сервис B (polling) -> сервис C +""" +import pytest +import httpx +import os + +SERVICE_A_URL = os.getenv("SERVICE_A_URL", "http://localhost:8001") +SERVICE_B_URL = os.getenv("SERVICE_B_URL", "http://localhost:8002") +SERVICE_C_URL = os.getenv("SERVICE_C_URL", "http://localhost:8003") + + +class TestPollingRouteContracts: + """Проверяет единообразие polling endpoints между сервисами.""" + + def test_service_a_polling_route_format(self): + """сервис A: GET /api/v1/task/{id} возвращает TaskResponse с полями task_id, status""" + r = httpx.get(f"{SERVICE_A_URL}/api/v1/task/nonexistent-id-12345", timeout=5.0) + assert r.status_code in (200, 404), f"Unexpected status: {r.status_code}" + if r.status_code == 200: + body = r.json() + assert "task_id" in body or "id" in body, "TaskResponse missing task_id/id field" + assert "status" in body, "TaskResponse missing status field" + + def test_service_b_polling_route_format(self): + """сервис B: polling endpoint должен совпадать по форме с сервисом A, не отличаться""" + r_canonical = httpx.get(f"{SERVICE_B_URL}/api/v1/task/nonexistent-id-12345", timeout=5.0) + + if r_canonical.status_code not in (200, 404): + # Канонический роут не существует — проверить legacy-маршрут + r_legacy = httpx.get(f"{SERVICE_B_URL}/result/nonexistent-id-12345", timeout=5.0) + if r_legacy.status_code in (200, 404): + # Legacy-роут существует — это контрактный рассинхрон, не skip + pytest.fail( + "Сервис B использует /result/{id} вместо /api/v1/task/{id}. " + "Контрактный рассинхрон с сервисом A. " + f"canonical: {r_canonical.status_code}, legacy: {r_legacy.status_code}" + ) + else: + pytest.skip( + f"Ни /api/v1/task, ни /result не отвечают " + f"({r_canonical.status_code}/{r_legacy.status_code})" + ) + # Если canonical роут ответил 200 или 404 — это правильно, рассинхрона нет + + +class TestAPIEnvelopeContracts: + """Проверяет единообразие envelope формата {"ok": bool, "data": ..., "error": ...}.""" + + def test_health_endpoints_all_services(self): + """Все сервисы отвечают на /health с JSON содержащим поле 'status'.""" + services = [ + ("service_a", SERVICE_A_URL), + ("service_b", SERVICE_B_URL), + ("service_c", SERVICE_C_URL), + ] + failures = [] + for name, url in services: + try: + r = httpx.get(f"{url}/health", timeout=5.0) + if r.status_code != 200: + failures.append(f"{name}: /health -> {r.status_code}") + continue + body = r.json() + if "status" not in body: + failures.append(f"{name}: /health ответ не содержит поле 'status', got: {list(body.keys())}") + except Exception as e: + failures.append(f"{name}: недоступен ({type(e).__name__}: {e})") + assert not failures, "Проблемы health endpoints:\n" + "\n".join(failures) +``` + +## Правила написания тестов + +1. **Конкретный failure message** — тест должен объяснять ЧТО сломано и ПОЧЕМУ это рассинхрон +2. **Один assert = один контракт** — не смешивать несколько проверок в один assert +3. **Skip > xfail для отсутствующих данных** — если нет тестовых данных, skip с объяснением +4. **Environment variables для URL** — не хардкодить адреса, брать из env +5. **Timeout** — всегда `timeout=5.0` для httpx, не висеть бесконечно + +## Приоритеты покрытия + +| Priority | Что тестировать | Почему | +|----------|----------------|--------| +| P0 | Все `/health` endpoints | Базовая доступность | +| P0 | Polling route формат между соседними сервисами | Частый источник рассинхрона | +| P1 | API envelope `{ok, data, error}` | Ломает integration-виджеты | +| P1 | Сигналы готовности через общее хранилище состояния (Redis и подобные) | Асинхронный старт может тихо не стартовать | +| P2 | Error code формат 4xx ответов | Стандартизация | +| P2 | CORS headers (если cross-origin) | Фронт может сломаться | + +## Как запускать + +```bash +# Локально с docker-compose +SERVICE_A_URL=http://localhost:8001 \ +SERVICE_B_URL=http://localhost:8002 \ +SERVICE_C_URL=http://localhost:8003 \ +pytest tests/integration/ -v --tb=short + +# Против staging +SERVICE_A_URL=https://service-a.staging.example.com \ +pytest tests/integration/test_cross_service_contract.py -v +``` + +## Что делать при падении теста + +1. Сформулировать: «Сервис X ожидал Y, получил Z» +2. Указать файл, в котором нужно исправление +3. Предложить конкретное изменение (не общее описание) +4. Проверить, нет ли аналогичного рассинхрона в других парах сервисов diff --git a/.agents/skills/techcon-external-report-authoring/SKILL.md b/.agents/skills/techcon-external-report-authoring/SKILL.md new file mode 100644 index 00000000..f9eeb246 --- /dev/null +++ b/.agents/skills/techcon-external-report-authoring/SKILL.md @@ -0,0 +1,285 @@ +--- +name: techcon-external-report-authoring +description: Используй при подготовке внешнего отчёта — Markdown для менеджмента или HTML-дашборд для руководства — по организации, проекту или группе проектов. Числа берутся только из готового файла с посчитанными фактами, никогда не придумываются агентом. +harness: org@56a6fcf9f3cd846d7238a9233b40f00eed7f22b0 +--- +# Авторство внешних отчётов (менеджмент/руководство) + +Портируемая директива, не программа. Исполняется обычными командами оболочки +и чтением файлов, без вызовов, специфичных для конкретного агентного +harness'а. Применима к любому репозиторию организации. + +## Goal + +Два разных документа для двух разных аудиторий — Markdown-отчёт для +менеджмента и HTML-дашборд для руководства — так, чтобы КАЖДОЕ ЧИСЛО было +проверяемым фактом, а ПОДАЧА (нарратив, структура, визуализация) была живой +для конкретного периода/проекта, не заполнением слотов одного шаблона. + +## Область действия + +**Делает:** Markdown-отчёт для менеджмента (при необходимости — в `.docx`; +Markdown сам по себе не финальная форма сдачи, а трассируемый источник +текста); HTML-дашборд/кейс для руководства. Оба — по организации, по одному +проекту или по группе проектов. + +**НЕ делает:** не считает числа сам (работа отдельного детерминированного +скрипта сбора метрик), не решает, нужен ли отчёт вообще (решение человека), +**не додумывает смысл за пользователя** — см. Фазу 0. + +## Фаза 0 — Уточнение области и контекста (ОБЯЗАТЕЛЬНА, до Фазы 1) + +Живая находка: агент написал в отчёте «X не закрыт — открытый вопрос», +взяв формулировку дословно из внутреннего трекера состояния. Владелец +поправил: это было оставлено НАМЕРЕННО, по прямой просьбе — код и документ +этого не говорят, потому что не обязаны нести историю решения, только его +следствие. **Данные показывают ЧТО произошло, не всегда ПОЧЕМУ и не всегда +что из этого действительно важно показать читателю.** До письма — не после +— агент обязан закрыть такие разрывы вопросом, а не догадкой. + +Спросить пользователя (с конкретными вариантами ответа, если они есть) +ОБЯЗАТЕЛЬНО, если: +- **период не назван** — с основания проекта, за квартал, за спринт? + Предложить варианты, не выбирать самому; +- **масштаб неоднозначен** — один проект, группа проектов, организация; +- **у наблюдаемого состояния может быть скрытая причина** — выглядит как + «не сделано»/«баг», но источники не говорят, было ли это решение + осознанным. Не приписывать причину без подтверждения — либо спросить, + либо описать наблюдение без причины; +- **раздел «куда движемся» просится в отчёт, но нет источника, ЧТО там + значимого** — git-история показывает прошлые эксперименты, не обязательно + текущий фокус. Спросить прямо, не заполнять универсальными фразами; +- **нужна метрика без чистого вычислимого определения** (например «число + экспериментов») — спросить, что считать единицей и откуда брать, не + изобретать подсчёт молча. + +Если источники дают уверенный, непротиворечивый ответ — не спрашивать ради +формы; вопрос — когда РЕАЛЬНО неопределённо. Каждый заданный и отвеченный +вопрос — часть Minimum output, чтобы следующий прогон не переспрашивал молча. + +## Read order + +1. Самый свежий файл с посчитанными числами (`figures-*.yaml` или + эквивалент) для нужного масштаба — единственный источник чисел. Если + файла нет или он старше данных — сначала пересчитать (Фаза 1). +2. Предыдущий отчёт ТОГО ЖЕ типа, если есть — чтобы не повторить структуру и + формулировки слово в слово. Копирование прошлой прозы — антипаттерн. +3. Действующий брендбук репозитория/организации — для HTML: единственный + источник цвета/шрифта; использовать буквально, не «улучшать» по памяти. +4. Файл дизайн-токенов и шрифтовые ассеты, если есть — встраивать шрифт как + base64 в `@font-face`, не ссылаться на путь на личном диске автора. +5. Правила цензуры утечек для внешних документов, если такой список есть. +6. Для per-project кейса — сам репозиторий: `git log`, `README.md`, файл + текущего состояния проекта, если есть. + +## Фаза 0б — Глубина исследования для нового/полноисторийного кейса + +Живая находка: поверхностный однопроходный `git log` дал 3 вехи на +репозиторий с 852 коммитами за 288 дней, хотя реальных крупных переработок +было минимум 12-18. Один проход по «хвосту» истории систематически +недооценивает объём работы за пределами последнего всплеска. + +Если кейс покрывает **весь период с основания проекта** или несколько +месяцев истории — **параллельное исследование по нескольким фронтам +ОБЯЗАТЕЛЬНО**, не на усмотрение агента: +- вехи месяц за месяцем по всей истории, порог ~1 веха на 4-6 недель для + активного проекта; явно отмечать месяцы без вех (это факт, не пробел); +- для инфраструктурных кейсов — отдельный фронт по истории затрат/ + оптимизаций, отдельный по структуре системы на сегодня; +- параллельные потоки, не последовательные ручные проходы — иначе цена + глубины непропорционально велика; +- числа, которые владелец сообщает напрямую (без следа в git) — + использовать с атрибуцией «подтверждено владельцем», не отбрасывать + из-за отсутствия корпоративных доказательств. + +Для короткого явно ограниченного периода (спринт, релиз) это правило не +действует — истории мало по определению. + +Второй живой урок: исследование дважды упускало крупный пласт продукта — +(1) фильтр вех по conventional-commit префиксу не ловит веху, названную +иначе (`Stage 2a: ... (#99)`) — прогонять поиск и без якоря на префикс, и +отдельно топ коммитов по объёму diff; (2) заранее данный список файлов +(README, «паспорт проекта») — только отправная точка, не исчерпывающий +список: обязателен независимый шаг — перечислить ВСЮ документацию и хотя бы +бегло увидеть даты каждого файла; дата старше половины истории репозитория +— сигнал перепроверить актуальность, не цитировать как текущее состояние. + +## Фаза 1 — Факты (код, не агент) + +Числа считает отдельный детерминированный скрипт сбора метрик репозитория +(org-масштаб и per-project вариант с параметром слага), не агент по памяти. + +Если числа нет ни в файле с фактами, ни в выводе скрипта — задача НЕ +«придумать по смыслу», а решить: (а) число не нужно отчёту, (б) скрипт +стоит расширить новым полем (отдельная правка кода), (в) число получается +только ручным замером — тогда с явной пометкой «оценка»/«ручной замер» и +командой проверки в сноске, не наравне с прочими фактами. + +## Фаза 2 — Нарратив + +Не заполнение слотов шаблона. Перед письмом — что здесь ГЛАВНОЕ в этом +периоде/проекте, структура строится вокруг этого, не вокруг фиксированного +списка секций. SCQA (Ситуация → Осложнение → вопрос → Ответ) — рабочий +каркас, не обязательная буква. Честно, не смягчая формулировки, если +источник сам называет вопрос открытым. + +Правило: качественная формулировка (не число) свободна; число — только из +файла с фактами, дословно или как округлённый процент. + +## Фаза 3а — Markdown-отчёт (менеджмент) + +Обязательные смысловые блоки (без фиксации порядка/формулировок): ситуация/ +контекст → что сделали (вехами, без хирургически точных дат, если не +попросили иначе) → результат на сегодня числами → что не решено → куда +движемся дальше (по факту Фазы 0) → всё. Полные смысловые названия, не +технические id. Никакой методологической риторики о происхождении чисел в +теле («оценка», сноски проверки) — вся сверка делается в Фазе 4, не +демонстрируется читателю. Конкретные метрики обязательны, не только число +коммитов. Тон — про эффективность и качество работы команды, не «вау- +эффект» (это роль HTML). Перед сдачей — проверить себя по правилам цензуры +утечек (внутренние хосты, IP, пути, хеши недопустимы). + +## Фаза 3б — HTML-дашборд/кейс (руководство) + +Файл дизайн-токенов — источник цвета/шрифта, НЕ каркас вёрстки: layout и +состав визуализаций агент решает заново под контент. Тема — та, что +зафиксирована брендбуком для этой аудитории. Числовых/графических элементов +должно быть заметно больше, чем прозы: таймлайн, хотя бы одна диаграмма по +реальным данным, крупные цифры-бейджи. Для графиков по умолчанию — простой +ручной SVG; библиотека (проверенный CDN, версия закреплена) — только если +данных действительно много. Один файл, self-contained: брендовый шрифт — +base64 в `@font-face`, системные шрифты — через официальный CDN шрифтов, +никаких прочих внешних подключений. Никакой технической/операционной +механики (CI/PR-числа, разбивка коммитов, внутренние хосты) — здесь посыл +и архитектура простым языком. Раздел «куда движемся» — по факту Фазы 0, +без внутренних тикетов. Отделять подтверждённое замером от заявленного +свойства без живого кадра — псевдо-скриншот не рисовать, заменять честной +схемой без пояснений о происхождении. + +**Паттерны/антипаттерны для HTML-кейса**, накопленные по опыту нескольких +прогонов: +- заголовок утилитарный, не метафора; жаргонный термин — оставлять, рядом + простое объяснение в 1 фразе; +- собственную работу — через выбор и добавленную ценность, не + самоуничижение («ансамблевая модель из лучшего», не «просто дообучили»); +- для зрелых направлений — акцент на исследовательскую ценность (гипотезы, + эксперименты, включая провалившиеся); для молодых — на инженерную + скорость и первый результат; +- прогресс — явное «было / стало» с числами по обе стороны; +- карточки — сеткой/вертикально, никогда горизонтальным скроллом; + насыщенные темы дробить на несколько карточек; +- ограничения внешних систем — явно отдельно от возможностей продукта, не + сливать в одно утверждение; +- шаблонные «нейроночные» обороты («не X, а Y» кластерами, пустые звучные + институциональные фразы) — под запретом; один точечный контраст — норма, + серия подряд — признак генерируемого текста; +- домен/предметную область — проверять с владельцем буквально, не + наследовать из черновика без сверки; +- техническую метрику качества (0–1) не подписывать как процент точности. + +**Три правила владельца, главнее списка выше при конфликте:** +1. **Не стрелять себе в ногу.** Находка — заголовком идёт построенная + способность обнаруживать/предотвращать («разработали механизм X, + который нашёл Y»), не признание промаха. Цифру эффекта не искажать — + искажается только фокус подачи. +2. **Без текстового нейрослопа.** Плюс: абстрактно-институциональные + конструкции без субъекта («оценка владельца») — команда говорит от + своего лица («по нашим расчётам»). +3. **Технологичность — раскрывать широко, простым языком**, не + перечислением названий API; каждый архитектурный выбор — с явным + «зачем», не только описанием структуры. + +Острее применяют правило 1, не новые правила: раздел из одной самокритики +не спасает позитивные заголовки карточек — разбавлять находками ВОВНЕ, не +только внутри своего проекта; простои/красный CI не входят в хронологию +вех (веха = прогресс); провальная гипотеза, о которой команда просит +молчать, — вырезается целиком, не переформулируется позитивно; название- +эвфемизм для внутреннего механизма — тот же нейрослоп, заменять прямым +описанием сути; иноязычные заимствования — грепать и убирать; график +«улучшается со временем» — только на реально верифицированной динамике, не +подменять запрошенную метрику другой молча. + +## Фаза 4 — Самокритика до показа человеку + +- Каждое число трассируется на конкретный `id` в файле с фактами (дословно + или `round(value*100)%`). Число без id — удалить или получить отдельно. +- Нарратив не повторяет прошлый отчёт того же типа слово в слово. +- Открытые вопросы источника поданы так же прямо, как в источнике. +- Проверка утечек для внешних документов пройдена вручную. +- Каждый CSS-класс в разметке HTML реально определён — в дизайн-ките или в + `