diff --git a/.dsh/AGENTS.md b/.dsh/AGENTS.md deleted file mode 100644 index c3d77d6..0000000 --- a/.dsh/AGENTS.md +++ /dev/null @@ -1,37 +0,0 @@ -# Правила работы в репозитории evening_detective_server - -Дополняют глобальный `~/.dsh/AGENTS.md`. Применяются в любой сессии в этом каталоге. - -## Сборка и проверки - -- **Кэш сборки**: системный `GOCACHE` (`/home/vladimir/.cache/go-build`) в этом окружении read-only. Всегда запускай Go-команды с локальным кэшем: - `export GOCACHE=/home/vladimir/projects/evening_detective_server/.gocache` - (иначе `go build/vet/test` падают с `read-only file system`). -- Стандартный набор проверок после изменений: `gofmt -l cmd/ internal/ api/`, `go build ./...`, `go vet ./...`, `go test -count=1 ./...`; при конкурентности — `go test -race ./...`. -- `go mod tidy` может не отработать (read-only module cache): если не хватает только перевода импортируемого пакета в прямые зависимости — перенеси его в первый require-блок вручную (эквивалентно tidy). - -## Прото - -- API описано в `api/main.proto`; сгенерированный код — `proto/` и `cmd/evening_detective_server/main.swagger.json`. -- После правки proto обязателен `make generate` (protoc v35.1, плагины в `~/go/bin`), затем `go build ./...`. -- **Конвенция REST — camelCase** (`accessToken`, `actionsCount`). Осознанное исключение: `file_type` в `Application`/`UploadFileRsp` (явный `json_name = "file_type"`, единый snake_case с внутренним JSON истории). Новые поля с явным json_name — только по согласованию. - -## Известные факты (не переоткрывать в каждой задаче) - -- Файлы хранятся в S3-совместимом RustFS (`file_storage`), URL префиксуются `FILE_PREFIX_DOMAIN` (см. `.env.example`). В БД и JSON истории — относительные имена; префикс добавляется только при чтении (идемпотентно, `prefixDomain`/`mapStory`). -- `string_tools.Transliterate` удаляет ВСЁ кроме `[a-z0-9_]` — включая точку расширения: `Transliterate("photo.png")` → `"photopng"`. Работать с расширением файла — отдельно (split base/ext до транслитерации). -- Улики = `Application { name, image, file_type }` (proto `api/main.proto`, `storytelling.Application`): живут в JSON истории сценария и в таблице `applications` (выданные команде). `file_type`: `image | pdf | audio`, деривируется из расширения через `file_storage.FileType`. -- Загрузка файлов: `POST /api/files/upload` — только роль `Author`, allowlist расширений pdf/jpg/jpeg/png/gif/webp/mp3/ogg/wav, содержимое проверяется sniff'ом (текст/html отклоняется), имя уникализируется `translit(base)_.`. Лимит файла 64 МБ: HTTP `maxFileUploadBody`, gRPC `grpcMsgLimit = maxFileSize + 1MiB`, бизнес-проверка в `file_service`. -- Ошибки размера на HTTP-слое мапятся в 413 через `customErrorHandler` (см. `cmd/evening_detective_server/main.go`): ветка `InvalidArgument` + «request body too large» — единственная реальная защита 413 (сгенерированный gateway-код строкифицирует `*http.MaxBytesError` через `%v`). -- Скачивание `GET /api/files/{filename}` — публичное, с `X-Content-Type-Options: nosniff`. - -## Процесс ревью (скилл task-execution) - -Загружай скилл `task-execution` для нетривиальных задач. Внутри него действуют правила экономии контекста: -- бюджет **3 раунда** на цикл (план/результат), «все замечания одним списком»; -- план/состояние — во внешнем файле (`.dsh/task-plan.md`), промпт ревью — дельта + `git diff`; -- саморевью фактическими проверками до отправки сеньёру (поведение функций на реальных данных, сгенерированный код, арифметика). - -## Улучшения вне скоупа (скилл todo-collect) - -Если в ходе задачи нашлось улучшение/долг, не мешающее текущей работе — **не чини попутно**: запиши его в локальную папку `todo/` по скиллу `todo-collect` (каждая задача — отдельный файл, шаблон в скилле) и продолжай текущую задачу. Папка в `.gitignore` — это локальный журнал, не коммитится. diff --git a/.dsh/skills/create-go-module/SKILL.md b/.dsh/skills/create-go-module/SKILL.md deleted file mode 100644 index b8cf546..0000000 --- a/.dsh/skills/create-go-module/SKILL.md +++ /dev/null @@ -1,94 +0,0 @@ ---- -name: create-go-module -description: Создаёт Go-модуль в internal/modules// по конвенциям репозитория — обязательный interface.go с интерфейсом (имя начинается с I) и всеми публичными типами, в папке как минимум одна реализация интерфейса (неэкспортируемая структура + конструктор New*, возвращающий интерфейс). Загружай при создании нового модуля или заготовки модуля. -whenToUse: Пользователь просит создать модуль/компонент/сервис/заготовку в internal/modules, либо «сделать по конвенциям проекта», либо явно упоминает interface.go или интерфейс с префиксом I. ---- - -# Создание Go-модуля (internal/modules) - -## Контекст - -В этом проекте переиспользуемые компоненты живут в `internal/modules/<имя>/` — по одному модулю на папку. Публичный контракт модуля описывается в **обязательном** файле `interface.go`. Эталонные примеры действующих модулей: `internal/modules/password_generator`, `internal/modules/email_sender`, `internal/modules/cleaner`, `internal/modules/storytelling`, `internal/modules/processor_jwt`. - -## Обязательные правила - -1. **Папка модуля** — `internal/modules//` (например, `email_sender`). Имя Go-пакета равно имени папки. Никаких вложенных подпакетов внутри модуля. -2. **Файл `interface.go` обязателен.** В нём живёт ВЕСЬ публичный контракт модуля: - - интерфейс(ы) модуля; - - публичные типы: структуры-данные (DTO), перечисления, константы, именованные ошибки. - Ничего другого (реализация, приватные хелперы) в этом файле нет. -3. **Имя интерфейса начинается с `I`** — `I`, например `IEmailSender`, `IPasswordGenerator`, `ICleaner`, `IStory`. Интерфейс без префикса `I` — недопустимо. В интерфейсе — только сигнатуры методов, без полей. -4. **Реализация — в отдельном файле** (например, `<короткое_имя>.go` или `service.go`): неэкспортируемая структура (строчная, например `type emailSender struct`), методы которой реализуют интерфейс, и конструктор `func New(...) I`, возвращающий **интерфейс**, а не структуру. -5. **В папке модуля — как минимум одна реализация интерфейса.** Модуль не может состоять из одного `interface.go`: рядом с ним всегда лежит неэкспортируемая структура, реализующая `I` (см. правило 4), и конструктор, возвращающий интерфейс. Если реализаций несколько — каждая в своём файле. -6. **Сигнатуры методов**: операции с внешним миром (сеть, файлы, БД, таймеры) принимают `context.Context` первым аргументом и возвращают `error`; чистые вычисления — без контекста (пример: `Generate() (string, error)` у `password_generator`). -7. **Комментарии — на русском.** Каждый публичный идентификатор имеет doc-комментарий, начинающийся с имени символа. Пояснения сложных мест — тоже по-русски. -8. **Ошибки** оборачиваются через `fmt.Errorf(": ...: %w", err)` — префикс с именем модуля. -9. **JSON-теги** у сериализуемых структур — `json:"..."` (эталон: `storytelling/interface.go`). -10. **Зависимости между модулями**: вместо импорта чужого модуля объявляется минимальный локальный интерфейс в файле `dependency.go` (эталон: `internal/modules/storytelling/dependency.go` объявляет собственный `ICleaner`). Это исключает циклические импорты и связывает модули только через их интерфейсы. -11. **Тесты** — для модуля желателен файл `*_test.go` рядом с реализацией (примеры: `email_sender/sender_test.go`, `cleaner/service_test.go`). - -## Шаги - -1. **Определи имя модуля.** Если имя неоднозначно — уточни у пользователя. Приведи имя к двум формам: - - snake_case — папка и пакет: «генератор паролей» → `password_generator`; - - CamelCase — имена типов и интерфейса: `PasswordGenerator` → `IPasswordGenerator`. -2. **Создай папку** `internal/modules//`. -3. **Напиши `interface.go`** (обязательно): сначала публичные типы/константы, затем интерфейс `I`. -4. **Напиши файл(ы) реализации** (обязательно, минимум один) — неэкспортируемая структура + конструктор `New(...) I`. -5. **При необходимости** добавь `dependency.go` (локальные интерфейсы зависимостей) и `*_test.go`. -6. **Проверь сборку** из корня проекта: `go build ./...` и `go vet ./...`. Ошибки — исправь. - -## Шаблон interface.go - -```go -package - -import "context" - -// — описание публичного типа. -type struct { - Field string `json:"field"` -} - -// I — контракт модуля . -type I interface { - // — описание метода. - (ctx context.Context, in ) error -} -``` - -## Шаблон реализации - -```go -package - -import ( - "context" - "fmt" -) - -// — реализация I. -type struct { - // внутренние поля -} - -// New создаёт реализацию I. -func New(...) I { - return &{} -} - -func (s *) (ctx context.Context, in ) error { - // TODO: реализация - return fmt.Errorf(": не реализовано") -} -``` - -## Критерии готовности - -- [ ] Папка `internal/modules//` создана, имя пакета равно имени папки. -- [ ] `interface.go` существует; все публичные типы, константы и интерфейсы описаны именно в нём. -- [ ] Имя интерфейса начинается с `I`. -- [ ] В папке модуля есть как минимум одна реализация интерфейса (неэкспортируемая структура + конструктор). -- [ ] Реализация неэкспортируемая; конструктор возвращает интерфейс, а не структуру. -- [ ] doc-комментарии на русском у всех публичных идентификаторов. -- [ ] `go build ./...` и `go vet ./...` проходят без ошибок. diff --git a/.dsh/skills/task-execution/SKILL.md b/.dsh/skills/task-execution/SKILL.md deleted file mode 100644 index 6a96458..0000000 --- a/.dsh/skills/task-execution/SKILL.md +++ /dev/null @@ -1,146 +0,0 @@ ---- -name: task-execution -description: Выполняет любую нетривиальную задачу по процессу «план → ревью сеньёром → правки → выполнение → ревью результата → правки». План и результат проверяет субагент в роли senior developer; каждый цикл повторяется, пока не устранены блокирующие замечания. Ниты и уточнения формулировок не блокируют одобрение — они фиксируются и могут остаться неисправленными. При неясностях агент задаёт вопросы пользователю. Загружай для реализации фичи, исправления бага, рефакторинга, написания кода или документации, когда задача не сводится к одному тривиальному действию. -whenToUse: Пользователь просит выполнить задачу (написать код, починить баг, отрефакторить, задокументировать, что-то исследовать и внедрить) и задача не является тривиальным одношаговым действием. Для тривиальных правок скил можно не применять. ---- - -# Выполнение любой задачи: план → сеньёр → выполнение → сеньёр - -## Контекст - -Любая нетривиальная задача выполняется строго по циклу: сначала **план**, затем **ревью плана сеньёром** с правками до одобрения, затем **выполнение** по одобренному плану, затем **ревью результата сеньёром** с правками, пока не устранены блокирующие замечания. Замечания делятся на **блокирующие** (исправляются обязательно) и **ниты** (фиксируются, но не блокируют одобрение и могут остаться неисправленными). На каждом этапе, если что-то неясно, задаются вопросы пользователю. - -## Классификация замечаний - -- **Блокирующее ([BLOCKER])** — влияет на функциональность, корректность, безопасность, целостность данных, совместимость, достижимость шага плана, ломает сборку/тесты, нарушает конвенции проекта, создаёт регрессию. Такие замечания исправляются все, без исключения. -- **Нит ([NIT])** — уточнение формулировки, стиль, типографика, «на будущее», рекомендация без реального риска, альтернатива, которая не меняет поведение. Ниты фиксируются (в файле состояния или `todo/`), но НЕ обязательны к исправлению и не блокируют `APPROVED`. -- Метку ставит сеньёр в каждом замечании. Если метка отсутствует или неоднозначна — трактуй замечание как блокирующее (осторожность в пользу качества); при сомнении можно спросить пользователя. - -## Роли - -- **Исполнитель** — ты (агент). Составляешь план, выполняешь, правишь по блокирующим замечаниям, фиксируешь ниты. -- **Сеньёр (senior developer)** — отдельный субагент, запускаемый через `subagent` **один раз на задачу** и переиспользуемый между раундами через `send_message` (он помнит свои предыдущие замечания и дельты — не противоречит себе и не перечитывает весь файл состояния каждый раунд). Он **не видит наш диалог**, поэтому каждый промпт ревью должен быть самодостаточным — но самодостаточность достигается за счёт внешнего файла состояния, а не дублирования всего плана в промпте (см. правило 9). Новый сеньёр создаётся только при смене задачи или переполнении контекста субагента (тогда — со сводкой уже принятых решений). -- **Пользователь** — источник требований и финальный судья. Ему задаются вопросы при неясностях. - -## Обязательные правила - -1. **Сначала план — потом выполнение.** Не начинай выполнение, пока сеньёр не одобрил план (ответил `APPROVED`). -2. **Ревью плана и результата — только через субагента в роли senior developer** (см. шаблоны промптов ниже). **Создай сеньёра один раз** (первый раунд — `subagent` с `run_in_background: true`), далее каждый раунд — `send_message` тому же субагенту с дельтой. Ответ субагента приходит уведомлением (не синхронно): пока он работает, продолжай независимые шаги, по уведомлению забирай результат. -3. **Блокирующие замечания исправляются все, без выбора.** Нельзя отбрасывать `[BLOCKER]` «потому что не согласен»: если не согласен — верни уточняющий вопрос сеньёру или спроси пользователя. **Ниты (`[NIT]`) исправлять не обязательно**: зафиксируй их в файле состояния или `todo/` и продолжай. -4. **Цикл повторяется, пока сеньёр не вернёт `APPROVED`** — и для плана, и для результата. `APPROVED` достижим при наличии только нитов (сеньёр может вернуть `APPROVED (ниты: ...)`); блокирует цикл только наличие `[BLOCKER]`. -5. **Неясно — спроси.** На любом этапе (задача, шаг плана, замечание сеньёра) при неоднозначности задай вопрос пользователю через `ask_user_question`. Догадки вместо вопросов — ошибка. -6. **Отслеживай прогресс** через `todo_write`: план из шага 2 переносится в todo-список, пункты отмечаются по мере выполнения. -7. **Профильные скилы** (например `create-go-module`) — источник правил «как делать» для конкретной задачи. Этот скил управляет процессом «как вести задачу»; оба применяются вместе. -8. **Бюджет раундов ревью — 3 на цикл** (план и результат отдельно). В шаблонах промптов требуй от сеньёра перечислить **все замечания одним списком за один заход** с метками `[BLOCKER]`/`[NIT]`. После 3 раундов с неисправленными `[BLOCKER]` остановись и спроси пользователя: продолжать цикл или зафиксировать оставшиеся блокеры как известные ограничения (не растягивай полировку на десятки раундов). -9. **Дифф-ревью и внешнее состояние.** План, чек-лист и учёт замечаний держи во внешнем файле состояния (например, `.dsh/task-plan.md`). Промпт ревью содержит: задачу, путь к файлу состояния, **короткую дельту** «что изменилось с прошлого раза» и `git diff` изменённых файлов. Не пересказывай ревьюеру весь план и всю историю замечаний — файл состояния он прочитает сам (это его контекст, а не наш). -10. **Саморевью до отправки.** Перед первым и каждым повторным ревью прогоняй фактические проверки, которые сеньёр всё равно сделает: поведение функций на реальных данных (например, `Transliterate("photo.png")`, `http.DetectContentType`, `filepath.Ext("x.PNG?v=2")`), чтение сгенерированного кода (pb.gw.go и пр.), арифметику лимитов. «Ревью результата» отправляй как `git diff` + выводы проверок, а не как пересказ плана. - -## Шаги - -### 1. Уточни задачу - -Разберись, что именно нужно сделать. Если неясны цель, объём, ограничения или критерии приёмки — задай вопросы пользователю до составления плана. - -### 2. Составь план - -План должен содержать: - -- **Цель** и ожидаемый результат; -- **Шаги** с конкретными файлами, командами и артефактами; -- **Проверки** (сборка, тесты, линтеры, вёрстка и т.п.); -- **Критерии готовности** — что считается «сделано»; -- **Риски и открытые вопросы** (если есть). - -Заведи todo-список через `todo_write` в соответствии с планом. - -### 3. Ревью плана сеньёром - -Сохрани план и критерии в файл состояния (например, `.dsh/task-plan.md`), прогони саморевью (правило 10), затем **создай сеньёра** (`subagent`, первый раунд) с промптом по шаблону **«Ревью плана»** (ниже) и дождись его ответа. Сеньёр возвращает либо `APPROVED`, либо список замечаний с метками. - -### 4. Цикл правок плана - -- Есть блокирующие замечания → исправь их в файле состояния (и todo-список), отправь на ревью **повторно ТОМУ ЖЕ сеньёру** через `send_message`: в промпт добавь только дельту «что изменилось с прошлого раза» (правило 9). -- Ниты → зафиксируй в файле состояния (раздел «Ниты / известные ограничения») или `todo/`, не обязательно исправляй. -- Повторяй, пока не получишь `APPROVED`; после 3 раундов с неисправленными `[BLOCKER]` — спроси пользователя (см. «Ограничение итераций»). - -### 5. Выполнение - -Выполняй строго по одобренному плану, отмечая прогресс в `todo_write`. Если в ходе выполнения стало ясно, что план нужно изменить — не отклоняйся молча: вернись к шагу 3 с обновлённым планом. - -### 6. Ревью результата - -Сначала сам проверь результат (прогони проверки из плана, правило 10), затем отдай его сеньёру по шаблону **«Ревью результата»** через `send_message` (тот же субагент): `git diff` изменённых файлов + выводы проверок. Сеньёр сверяет результат с планом (файл состояния) и критериями, проверяет корректность и соблюдение конвенций проекта. - -### 7. Цикл правок результата - -Исправляй блокирующие замечания, повторяй ревью **тому же сеньёру** через `send_message`, пока не вернёт `APPROVED`. Ниты — фиксируй в `todo/` или файле состояния, не обязательно исправляй. При каждом повторе сообщай сеньёру только дельту — что изменилось с прошлого раза (правило 9). После 3 раундов с неисправленными `[BLOCKER]` — спроси пользователя. - -### 8. Финальный отчёт - -Кратко сообщи пользователю: что сделано, какие файлы затронуты, как проверялось, что план и результат одобрены сеньёром. - -## Шаблон: промпт «Ревью плана» - -План и критерии — в файле состояния (путь ниже); в промпт включай только задачу, путь к файлу, дельту с прошлого раза (если есть) и diff, если правился код. - -``` -Ты — senior developer. Оцени план выполнения задачи. - -Задача: <текст задачи> -План и критерии готовности: <путь к файлу состояния, например .dsh/task-plan.md> (прочитай сам) -Изменения с прошлого раза: <дельту или «первое ревью»> - -Проверь: полноту (нет ли пропущенных шагов), достижимость, корректность -подхода, риски, соответствие конвенциям проекта, отсутствие лишних шагов. - -Классифицируй каждое замечание: -- [BLOCKER] — влияет на функциональность, корректность, безопасность, - целостность данных, достижимость шага, ломает сборку/тесты, нарушает - конвенции проекта, создаёт регрессию. Такие обязательны к исправлению. -- [NIT] — уточнение формулировки, стиль, типографика, «на будущее», - рекомендация без реального риска. Ниты не блокируют APPROVED. - -Перечисли ВСЕ замечания одним списком за один заход — включая мелочи и -ниты; не растягивай на несколько раундов. -Ответь СТРОГО одним из двух вариантов: -- APPROVED (можно с пометкой «ниты: ...») — если блокирующих замечаний нет; -- список замечаний, каждое в формате «[BLOCKER]|[NIT] <что не так> → <как исправить>». -``` - -## Шаблон: промпт «Ревью результата» - -``` -Ты — senior developer. Оцени результат выполнения задачи. - -Задача: <текст задачи> -План и критерии готовности: <путь к файлу состояния> (прочитай сам) -Результат: git diff изменённых файлов + выводы проверок (build/vet/test/race) -Изменения с прошлого раза: <дельту или «первое ревью»> - -Проверь: результат соответствует плану и критериям готовности, код/текст -корректен, соблюдены конвенции проекта, нет регрессий. - -Классифицируй каждое замечание: -- [BLOCKER] — влияет на функциональность, корректность, безопасность, - целостность данных, ломает сборку/тесты, нарушает конвенции, регрессия. - Такие обязательны к исправлению. -- [NIT] — уточнение формулировки, стиль, типографика, «на будущее», - рекомендация без реального риска. Ниты не блокируют APPROVED. - -Перечисли ВСЕ замечания одним списком за один заход — включая мелочи и ниты. -Ответь СТРОГО одним из двух вариантов: -- APPROVED (можно с пометкой «ниты: ...») — если блокирующих замечаний нет; -- список замечаний, каждое в формате «[BLOCKER]|[NIT] <что не так> → <как исправить>». -``` - -## Ограничение итераций - -Если в одном цикле (план или результат) после **3 раундов** остаются неисправленные `[BLOCKER]` — остановись и спроси пользователя: продолжать цикл или зафиксировать оставшиеся блокеры как известные ограничения. Ниты не считаются причиной продолжения цикла: при одних нитах сеньёр возвращает `APPROVED (ниты: ...)`, а исполнитель фиксирует их в файле состояния или `todo/`. - -## Критерии готовности - -- [ ] План составлен и одобрен сеньёром (`APPROVED`) до начала выполнения. -- [ ] Все блокирующие замечания сеньёра по плану устранены; ниты зафиксированы (в плане/`todo/`) и не блокируют одобрение. -- [ ] Результат соответствует плану и критериям готовности. -- [ ] Все блокирующие замечания сеньёра по результату устранены, получен `APPROVED`; ниты зафиксированы. -- [ ] Пользователю дан финальный отчёт. diff --git a/.dsh/skills/todo-collect/SKILL.md b/.dsh/skills/todo-collect/SKILL.md deleted file mode 100644 index 69bdd96..0000000 --- a/.dsh/skills/todo-collect/SKILL.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -name: todo-collect -description: Записывает найденные улучшения «вне скоупа» текущей задачи в локальную папку todo/ — каждую задачу в отдельный файл, без выполнения и без расширения scope. Загружай при работе над любой задачей (реализация, ревью, рефакторинг, исследование), чтобы не терять замечания, не мешающие текущей работе. -whenToUse: В процессе любой задачи обнаружено улучшение, оптимизация, потенциальный баг или долг, которые не блокируют и не относятся к текущей задаче. Триггеры: замечание в коде, «на будущее» из ревью, осознанное ограничение, отложенное решение. ---- - -# Сбор улучшений «вне скоупа» в todo/ - -## Контекст - -В ходе задачи постоянно встречаются места, которые можно улучшить, но чинить их сейчас — значит расширять scope и рисковать текущей работой (правило «не отклоняйся молча»). Такие находки не должны теряться: они записываются в локальную папку `todo/` репозитория (в `.gitignore`, в git не попадает) — **каждая задача в свой отдельный файл**. - -## Правила - -1. **Записывай, не чини.** Нашёл улучшение, не мешающее текущей задаче — запиши в `todo/` и продолжай текущую работу. Не выполняй записанные задачи в текущей сессии, если пользователь явно об этом не попросил. -2. **Одна задача — один файл.** Имя файла — kebab-case, отражающее суть: `todo/.md` (например, `todo/mapimage-idempotent-prefix.md`). Если файл с той же сутью уже существует — дополни его, не создавай дубль. -3. **Записывай сразу.** Не откладывай «на потом»: в конце задачи контекст уже потерян. Короткая запись по шаблону ниже занимает минуту. -4. **Фиксируй только факты и предложения.** Без воды: что не так, где, как исправить (с оценкой риска), кто/когда нашёл. -5. **Не перемещай и не удаляй чужие записи.** Менять статус/содержимое своей записи можно; чужие — только по договорённости. -6. **Папка локальная.** `todo/` в `.gitignore` — не коммить, не синхронизировать. Если задача заслуживает общего трекинга — предложи пользователю перенести её в issue-трекер. - -## Шаблон записи - -```markdown -# <Краткое название задачи> - -- **Где найдено**: <файл/пакет/место в коде; в какой задаче> -- **Дата**: -- **Статус**: todo - -## Проблема - -<что именно не так, почему это проблема, при каких условиях проявится> - -## Предложение - -<как исправить, с указанием конкретных файлов/функций; оценка риска и объёма> - -## Связи - -<опционально: связанные todo-файлы, ссылки на код, замечания ревью> -``` - -## Статусы - -- `todo` — записано, не начато; -- `in_progress` — кто-то взялся (укажи имя/сессию); -- `done` — выполнено (укажи, как и когда; можно удалить файл после подтверждения). - -## Критерии готовности записи - -- [ ] Проблема описана так, что понятна без контекста исходной задачи. -- [ ] Предложено конкретное исправление (или явно сказано, что решение открыто). -- [ ] Файл лежит в `todo/` с kebab-case именем, дублей нет. diff --git a/.gitignore b/.gitignore index 80dc86d..57354b6 100644 --- a/.gitignore +++ b/.gitignore @@ -30,11 +30,8 @@ go.work .gocache/ .gotmp/ -# Локальные данные DSH-агента -.dsh/ - # Локальный журнал улучшений «вне скоупа» текущей задачи -# (каждая запись — отдельный файл, см. скилл todo-collect) +# (каждая запись — отдельный файл) todo/ .VSCodeCounter/