Приложение задач под Windows: headless-бэкенд на Go + десктоп-клиент на Wails. Любые задачи — от «убраться дома» до цепочек закрепления знаний (spaced repetition). Часть экосистемы activization: владеет данными о задачах, публикует события в Kafka для бота, отдаёт ICS-фид для внешних календарей.
Прототип и источник идей модели — планер в Google Таблицах: planer.md (темы, форматы повторений, перенос невыполненных со сдвигом цепочки).
tasks/
├── backend/ Go-сервис: REST + MCP + генератор вхождений + outbox-релей
│ ├── cmd/tasks/ точка входа
│ ├── internal/ config, handler, service, repository, models, kafka
│ ├── migrations/ SQL-миграции (golang-migrate, встроены в бинарник)
│ └── Dockerfile multi-stage сборка
├── desktop/ Wails v2 приложение (React + TS + Tailwind)
│ ├── frontend/ UI: календарь, дерево тем и задач, темы
│ └── main.go окно + биндинг адреса бэкенда
├── docker-compose.yml postgres:16 (порт 5433) + backend (порт 8081)
└── planer.md UX-прототип (Google Apps Script)
Команды выполняются из каталога tasks/ (WSL наследует текущий каталог). Compose подключается к внешней сети ecosystem — на чистой машине её нужно создать один раз:
wsl -d Ubuntu-22.04 -- docker network create ecosystem # однократно
wsl -d Ubuntu-22.04 -- docker compose up -d --buildПоднимает Postgres на localhost:5433 и бэкенд на http://localhost:8081. Kafka не обязательна: события копятся в outbox и уходят, когда брокер появится (общая Kafka — infra/docker-compose.yml экосистемы).
wsl -d Ubuntu-22.04 -- docker compose up -d postgres
cd backend
go run ./cmd/taskscd desktop
& "$env:USERPROFILE\go\bin\wails.exe" dev # разработка
& "$env:USERPROFILE\go\bin\wails.exe" build # сборка → build\bin\tasks-desktop.exeКлиент ходит на http://localhost:8081 (переопределяется переменной TASKS_API_URL).
.env в каталоге backend/ (или корне tasks/), переменные окружения поверх. Префикс TASKS_:
| Переменная | Default | Смысл |
|---|---|---|
| TASKS_HTTP_PORT | 8081 | порт REST + MCP |
| TASKS_DB_HOST / PORT / USER / PASSWORD / NAME | localhost / 5433 / tasks / tasks / tasks | Postgres |
| TASKS_KAFKA_BROKERS | localhost:9094 | брокеры через запятую |
| TASKS_KAFKA_TOPIC | tasks.events | топик CloudEvents |
| TASKS_TIMEZONE | Europe/Moscow | локальная TZ пользователя |
| TASKS_WINDOW_DAYS | 60 | окно материализации вхождений |
| TASKS_OUTBOX_INTERVAL_SECONDS | 5 | период outbox-релея |
| TASKS_DB_DRIVER | postgres | postgres или sqlite (мобильное) |
| TASKS_DB_PATH | tasks.db | путь к файлу SQLite (при driver=sqlite) |
| TASKS_SYNC_URL | — | URL сервера-источника для синхронизации (пусто = не синхронизировать) |
| TASKS_SYNC_TOKEN | — | bearer-токен эндпоинтов /api/v1/sync |
| TASKS_SYNC_INTERVAL_SECONDS | 60 | период push/pull синхронизации |
Тот же самый бэкенд и тот же самый фронтенд (desktop/frontend), только упакованные под телефон — без переписывания и дублирования UI.
backend/mobile/— gomobile-пакет: весь бэкенд задач на SQLite (ncruces/go-sqlite3— чистый Go/WASM без сисколлов, иначе seccomp Android убивает процесс) слушает127.0.0.1:18081и сам раздаёт собранныйdesktop/frontendкак статику.android/— тонкая Kotlin-обёртка: WebView открывает локальный бэкенд; в меню — настройки синхронизации (URL сервера ПК + токен).api.tsфронта сам выбирает транспорт: в Wails — через биндинг, в мобильном/браузере — same-origin по/healthz, иначеlocalhost:8081.
Синхронизация с ПК (internal/syncer): двусторонняя, REST /api/v1/sync/changes (GET отдаёт изменения с курсора, POST принимает). Конфликты — last-write-wins по updated_at; удаления через sync_tombstones (сервер ретранслирует их другим устройствам); ID вхождений детерминированные (uuid5 от task_id+date), поэтому одно и то же вхождение не двоится на двух устройствах.
Сборка APK (окружение — C:\Users\pavel\android-tools, ничего в систему не ставится):
deploy\build-android.ps1Скрипт собирает фронт → кладёт в backend/mobile/webdist → gomobile bind в android/app/libs/tasks.aar → gradle APK в android/app/build/outputs/apk/.
REST /api/v1 (JSON snake_case, ошибки {"error":{"code","message"}}):
CRUD /topics, /tasks; GET /occurrences?from&to&status; POST /occurrences/{id}/complete|skip; POST /tasks/{id}/reschedule-missed; GET /plans/{date}; PUT /plans/{date}/items; POST /plans/{date}/commit; GET /calendar.ics; GET /healthz.
MCP (streamable HTTP /mcp): list_tasks, create_task (плоские поля повторений + effort_minutes, requires_pomodoro, topic/parent_topic), complete_occurrence, skip_occurrence, list_due, get_day_plan, commit_day_plan, reschedule_missed, create_topic (с родителем — дерево тем), list_topics.
Подробности — в backend/README.md и docs/contracts.md экосистемы. Модель данных — docs/data-model.md.
cd backend
go test ./...Покрыто: генератор повторений (все 9 видов, monthly-прижатие, SR c series_step), перенос невыполненных, план дня, outbox-релей с mock-продюсером, REST-хендлеры (httptest).