411 lines
16 KiB
Markdown
411 lines
16 KiB
Markdown
# Верстак — Пошаговый план разработки
|
||
|
||
## Принципы работы
|
||
|
||
1. После каждого шага — **СТОП**. Влад проверяет, даёт обратную связь.
|
||
2. Следующий шаг начинается **ТОЛЬКО** после одобрения.
|
||
3. Каждый шаг — отдельный git commit.
|
||
4. Периодически сверяться с этим планом и документацией в docs/.
|
||
|
||
## Статус шагов
|
||
|
||
| # | Шаг | Статус |
|
||
|---|-----|--------|
|
||
| 1 | Git init + Skeleton | ✅ выполнен |
|
||
| 2 | Init + SQLite + First Migration | ✅ выполнен |
|
||
| 3 | Nodes Repository + CRUD + CLI Node | ✅ выполнен |
|
||
| 4 | Vault Files: Trash + File Service + CLI File | ✅ выполнен |
|
||
| 5 | Markdown Notes: Create/Read/Save + CLI Note | ✅ выполнен |
|
||
| 6 | GUI (browser prototype): Sidebar + Main Panel | ✅ выполнен |
|
||
| 7 | Actions + Worklog | ✅ выполнен |
|
||
| 8 | FTS5 Search | ✅ выполнен |
|
||
| 9 | Section assignment + Sidebar filtering | ✅ выполнен |
|
||
| 10 | Plugin Manager (discovery + templates) | ✅ выполнен |
|
||
| 11 | **Wails Desktop GUI** | ✅ выполнено (v2, full Svelte UI) |
|
||
| 12 | **Files/Folders full workflow** | ✅ выполнено (copy/link/import/tree) |
|
||
| 13 | **Drag-and-drop** | ✅ выполнено (internal + external drops) |
|
||
| 14 | **MVP stabilization** | ✅ выполнено — atomicity audit, template children as nodes, fs_path validation, descendant move protection, delete atomicity, sync_apply FS-first rewrite, SafeVaultPath, CreateNodeFromTemplate rollback, 24 integration tests pass. Known gaps: Restore from trash not implemented, applyRemoteNodeUpdate/Move uses FS-first (parent_id + title updates still need full txn), ensureTemplateChildren uses `continue` on errors |
|
||
| 15 | Sync Server + Client | 🔒 PAUSED — HTTP API key, push/pull, blob sync |
|
||
| 16 | Activity Suggestions | 🔒 PAUSED — worklog suggestions from activity_events |
|
||
| 17 | File Scanner/Watcher | 🔒 PAUSED — fsnotify, snapshot scanner, missing file detection |
|
||
| 18 | TUI MVP (Bubble Tea) | 🔒 PAUSED — tree/search, add worklog, run action, sync |
|
||
| 19 | Plugins: Lua runtime | 🔒 PAUSED — gopher-lua, hooks, sandbox |
|
||
| 20 | Browser Extension | 🔒 PAUSED — tracking, capture, evidence |
|
||
| 21 | Calendar/Kanban | 🔒 PAUSED — view by date, board view |
|
||
| 22 | Integrity Check + Repair | 🔒 PAUSED — checksums, crash recovery |
|
||
| 23 | New templates/integrations | 🔒 PAUSED — community plugins |
|
||
|
||
> 🔒 = **PAUSED** — не начинать до завершения шага 14 (MVP stabilization). Текущий статус: ✅ **MVP stabilization завершена** — все операции атомарны (DB+FS), template файлы/папки создаются как полноценные ноды с rollback, fs_path валидируется, sync_apply FS-first с SafeVaultPath, delete находит ошибки trash-переноса, 24 integration tests проходят. Известные пробелы: Restore не реализован, ensureTemplateChildren продолжает при ошибках (backward compat), parent_id+title в applyRemoteNodeUpdate без полной транзакции.
|
||
|
||
> **Wails v3 → v2 migration:** Wails v3 alpha.96 показал SIGSEGV на Linux desktop (GTK/X11). Wails v2 stable выбран как GUI base для MVP. Миграция в процессе (ветка `gui/migrate-wails-v2`).
|
||
|
||
**GUI Build (Wails v2):**
|
||
```bash
|
||
cd frontend && npm run build && cd ..
|
||
rm -rf cmd/verstak-gui/frontend-dist && cp -r frontend/dist cmd/verstak-gui/frontend-dist
|
||
go build -tags "gui production webkit2_41" -o verstak-gui ./cmd/verstak-gui
|
||
./verstak-gui
|
||
```
|
||
|
||
Или для dev режима: `wails dev` (требует Wails v2 CLI)
|
||
|
||
---
|
||
|
||
## Текущий этап: MVP Stabilization ✅
|
||
|
||
**Цель:** стабилизация MVP — атомарность операций, template ноды, fs_path инварианты, sync roundtrip тесты.
|
||
|
||
**Прогресс Wails v2 Desktop GUI:**
|
||
- ✅ Wails v2 shell (window opens, no SIGSEGV)
|
||
- ✅ Layout fix (full viewport, dark theme, sidebar+main)
|
||
- ✅ Notes bindings + UI (create/read/save/dirty state)
|
||
- ✅ Tabs (Overview/Notes/Files/Actions/Worklog/Activity)
|
||
- ✅ Node creation + template selection (FromTemplate)
|
||
- ✅ Section filtering
|
||
- ✅ File tree with breadcrumbs, preview, CRUD (rename/delete/duplicate/cut/copy/paste)
|
||
- ✅ Drag-and-drop (internal + external OS file drops)
|
||
- ✅ Actions CRUD (create/list/run/delete)
|
||
- ✅ Worklog entry form
|
||
- ✅ Today dashboard + Activity feed (global + per-case)
|
||
- ✅ Search
|
||
- ✅ Import dialog with safety checks (PreviewImport)
|
||
- ✅ Keyboard shortcuts
|
||
|
||
**Среднесрочные шаги (заморожены до стабилизации MVP):**
|
||
- Sync server/client
|
||
- File Scanner/Watcher
|
||
- TUI (Bubble Tea)
|
||
- Activity suggestions
|
||
- Lua runtime
|
||
- Browser extension
|
||
|
||
---
|
||
|
||
## Выполненные шаги (1-10)
|
||
|
||
### ШАГ 1 — Git Init + Skeleton
|
||
- go module `verstak`, структура cmd/internal/migrations
|
||
- CLI `verstak --version`
|
||
- README.md
|
||
|
||
### ШАГ 2 — Init + SQLite
|
||
- storage.go: DB wrapper, migration runner
|
||
- vault.go: Init() создаёт .verstak/ + index.db
|
||
- config.go: YAML config
|
||
|
||
### ШАГ 3 — Nodes Repository
|
||
- Node struct + CRUD (Create, Get, ListChildren, ListRoots, UpdateTitle, Move, SoftDelete)
|
||
- Meta KV + tests
|
||
|
||
### ШАГ 4 — Files
|
||
- FileService: AddExternal, CopyIntoVault, Get, ListByNode, MarkMissing, DeleteToTrash, Open
|
||
- file_test.go: 5 tests
|
||
|
||
### ШАГ 5 — Notes
|
||
- NoteService: Create, Read, Save (с backup), Delete
|
||
- note_test.go: 3 tests
|
||
|
||
### ШАГ 6 — GUI (browser prototype)
|
||
- Go HTTP SPA на случайном порту
|
||
- Sidebar tree + разделы (Сегодня, Неразобранное, Клиенты, Проекты...)
|
||
- Dashboard дела + вкладки (Обзор, Заметки, Файлы, Действия, Журнал, Активность)
|
||
- Модальное окно "+ Добавить" с выбором типа
|
||
- Поиск по корневым нодам
|
||
- **Это legacy prototype — не развивать как основной GUI**
|
||
|
||
### ШАГ 7 — Actions + Worklog
|
||
- ActionService: Create, Get, ListByNode, Delete, Run (open_url/file/folder, run_command)
|
||
- WorklogService: Add, Update, ListByNode, Delete, SumMinutes, Report
|
||
- CLI: `action add/list/run/delete`, `log add/list/report`
|
||
- GUI вкладки с кнопками действий и журналом работ
|
||
|
||
### ШАГ 8 — FTS5 Search
|
||
- SearchService: Index, Remove, Rebuild, Search (FTS5 MATCH)
|
||
- FTS5 virtual table создаётся лениво (работает с/без FTS5)
|
||
- Fallback на LIKE по заголовкам нод
|
||
- CLI: `verstak index rebuild`
|
||
- GUI search bar
|
||
|
||
### ШАГ 9 — Section assignment
|
||
- Колонка `section` в nodes (clients/projects/recipes/documents/archive/inbox)
|
||
- Фильтрация разделов по `?section=` в API
|
||
- Root-ноды без section → inbox
|
||
|
||
### ШАГ 10 — Plugin Manager
|
||
- Discovery: `.verstak/plugins/<name>/plugin.json`
|
||
- Enable/disable, templates → pre-filled node trees
|
||
- Built-in шаблон "Клиент" (Overview + Документы/Переписка/Скриншоты)
|
||
- Template selector в модалке создания дела
|
||
- POST /api/nodes/from-template
|
||
- CLI: `plugin list/enable/disable/templates`
|
||
- Lua runtime — stub (placeholder)
|
||
|
||
---
|
||
|
||
## Текущий этап: ШАГ 11 — Wails Desktop GUI
|
||
|
||
**Целевой commit:** `gui/wails-file-workflow`
|
||
|
||
Архитектура:
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────┐
|
||
│ Frontend (Wails) │
|
||
│ frontend/src/ │
|
||
│ App.svelte │
|
||
│ components/Sidebar, TopBar, CaseView, ... │
|
||
│ stores/selection, nodes, files, ui │
|
||
│ styles/theme.css │
|
||
└──────────────────┬──────────────────────────────┘
|
||
│ Wails bindings
|
||
┌──────────────────▼──────────────────────────────┐
|
||
│ Go Core (internal/core/) │
|
||
│ nodes, vault, storage, notes, files, │
|
||
│ actions, worklog, search, plugins │
|
||
└──────────────────┬──────────────────────────────┘
|
||
│
|
||
┌──────────────────▼──────────────────────────────┐
|
||
│ Vault filesystem + SQLite │
|
||
└─────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### Действия
|
||
|
||
1. Создать Wails app skeleton (`wails init`)
|
||
2. Структура frontend/ — Svelte/Vue/vanilla TS
|
||
3. Backend bindings — методы Wails над core services
|
||
4. Перенести текущий UI shell из inline HTML в frontend
|
||
5. Оставить текущий `internal/gui/` как legacy (не удалять, но не развивать)
|
||
|
||
### Backend bindings (Wails v2 — реализовано)
|
||
|
||
```go
|
||
// Nodes
|
||
ListSections() []SectionDTO
|
||
ListNodesBySection(section string) ([]NodeDTO, error)
|
||
GetNodeDetail(nodeID string) (*NodeDTO, error)
|
||
ListChildren(parentID string) ([]NodeDTO, error)
|
||
CreateNode(parentID, typ, title, section string) (*NodeDTO, error)
|
||
DeleteNode(id string) error
|
||
MoveNode(nodeID, newParentID string) error
|
||
RenameNode(nodeID, newTitle string) error
|
||
|
||
// Templates
|
||
ListTemplates() []TemplateDTO
|
||
FromTemplate(parentID, typ, title, section, template string) (*NodeDTO, error)
|
||
|
||
// Notes
|
||
ListNotes(nodeID string) ([]NodeDTO, error)
|
||
CreateNote(parentID, title string) (*NodeDTO, error)
|
||
ReadNote(noteID string) (string, error)
|
||
SaveNote(noteID, content string) error
|
||
|
||
// Files
|
||
ListFiles(nodeID string) ([]FileDTO, error)
|
||
ListItems(nodeID string) ([]FileTreeItemDTO, error)
|
||
AddPathCopy(nodeID, sourcePath string) ([]NodeDTO, error)
|
||
AddPathLink(nodeID, sourcePath string) ([]NodeDTO, error)
|
||
DeleteFileOrFolder(nodeID string) error
|
||
CreateEmptyFile(parentID, filename string) (*NodeDTO, error)
|
||
DuplicateNode(nodeID string) (*NodeDTO, error)
|
||
OpenFile(fileID string) error
|
||
OpenFolder(nodeID string) error
|
||
ReadFileText(fileID string) (string, error)
|
||
GetFileBase64(fileID string) (string, error)
|
||
PreviewImport(sourcePath string) (*ImportSummary, error)
|
||
ValidateName(name string) error
|
||
PickFile() (string, error)
|
||
PickFiles() ([]string, error)
|
||
PickDirectory() (string, error)
|
||
|
||
// Actions
|
||
ListActions(nodeID string) ([]ActionDTO, error)
|
||
CreateAction(nodeID, kind, title, data string) (*ActionDTO, error)
|
||
DeleteAction(id string) error
|
||
RunAction(id string) error
|
||
|
||
// Worklog
|
||
ListWorklog(nodeID string) ([]WorklogDTO, error)
|
||
CreateWorklog(nodeID, summary string, minutes int) (*WorklogDTO, error)
|
||
|
||
// Search
|
||
Search(query string) ([]SearchResultDTO, error)
|
||
|
||
// Activity
|
||
ListTodayView() (*TodayDashboardDTO, error)
|
||
ListActivityFeed(limit, offset int) ([]EventDTO, error)
|
||
ListActivityByNode(nodeID string, limit, offset int) ([]EventDTO, error)
|
||
CountActivityByNode(nodeID string) (int, error)
|
||
```
|
||
|
||
---
|
||
|
||
## ШАГ 12 — Files/Folders full workflow
|
||
|
||
### Core service extensions
|
||
|
||
Расширить `files.Service`:
|
||
|
||
```go
|
||
AddPathCopy(nodeID string, sourcePath string) ([]Node, error)
|
||
AddPathLink(nodeID string, sourcePath string) ([]Node, error)
|
||
```
|
||
|
||
Логика:
|
||
- `os.Stat(sourcePath)` → если директория → рекурсивный обход
|
||
- Каждый файл → File node + file record
|
||
- Каждая папка → Folder node
|
||
- Структура сохраняется через parent_id
|
||
|
||
### Folder model
|
||
|
||
Папка = node type `folder` (не file record с mime=directory).
|
||
|
||
При импорте `romashka-docs/`:
|
||
```
|
||
Folder node: romashka-docs (type=folder)
|
||
File node: dogovor.docx (type=file)
|
||
Folder node: screenshots (type=folder)
|
||
File node: error.png (type=file)
|
||
```
|
||
|
||
### Name conflict resolution
|
||
|
||
Если в target уже есть `docs`:
|
||
```
|
||
docs
|
||
docs (2)
|
||
docs (3)
|
||
```
|
||
|
||
### Safety checks
|
||
|
||
При добавлении папки показать summary:
|
||
- количество файлов/папок
|
||
- общий размер
|
||
- предупреждение если > 1000 файлов, > 1 GB, содержит `.git`/`node_modules`/`.cache`
|
||
|
||
### Trash
|
||
|
||
- Soft delete node + children
|
||
- Vault files → `.verstak/trash`
|
||
- External files — только удалить связь
|
||
- Не `rm -rf`
|
||
|
||
### Tests
|
||
|
||
1. Copy single file → vault, record created, source intact
|
||
2. Link single file → no copy, external path saved
|
||
3. Copy folder → tree created, files in vault
|
||
4. Link folder → node created, no content copied
|
||
5. Delete vault file → soft-deleted, file in trash
|
||
6. Delete vault folder → children soft-deleted
|
||
7. Name conflict → no overwrite, safe suffix
|
||
8. Open file → mocked opener (no real app launch)
|
||
|
||
---
|
||
|
||
## ШАГ 13 — Drag-and-drop
|
||
|
||
### External D&D
|
||
|
||
Drop target: активное дело / вкладка Файлы / Неразобранное.
|
||
|
||
После drop → диалог:
|
||
```
|
||
Добавить в "ООО Ромашка / Сайт"
|
||
Файлов: 3, Папок: 1, 240 MB
|
||
[Скопировать] [Переместить] [Привязать] [Отмена]
|
||
```
|
||
|
||
---
|
||
|
||
## ШАГ 14 — MVP stabilization
|
||
|
||
- Smoke tests базовых сценариев
|
||
- Проверка: дело → заметка → файл → папка → trash → перезапуск
|
||
- go test ./... pass
|
||
- Обновление документации
|
||
- Остановка перед следующими фичами
|
||
|
||
---
|
||
|
||
## Структура репозитория
|
||
|
||
```
|
||
verstak/
|
||
go.mod
|
||
README.md
|
||
PLAN.md
|
||
|
||
cmd/
|
||
verstak/ # CLI
|
||
verstak-gui/ # Wails v2 GUI main
|
||
|
||
frontend/ # Wails v2 Svelte frontend
|
||
package.json
|
||
vite.config.js
|
||
src/
|
||
App.svelte
|
||
FileTreeRow.svelte
|
||
lib/
|
||
FileBreadcrumbs.svelte
|
||
FilePreviewModal.svelte
|
||
ConfirmModal.svelte
|
||
FileIcon.svelte
|
||
FileActions.svelte
|
||
fileUtils.js
|
||
api/verstak.js
|
||
|
||
internal/
|
||
core/
|
||
nodes/
|
||
vault/
|
||
storage/
|
||
notes/
|
||
files/
|
||
actions/
|
||
worklog/
|
||
activity/
|
||
search/
|
||
config/
|
||
plugins/ # manager, lua (stub), builtin templates
|
||
|
||
contrib/
|
||
plugins/
|
||
importer-dokuwiki/
|
||
|
||
migrations/
|
||
001_init.sql
|
||
002_add_meta.sql
|
||
003_add_files.sql
|
||
004_add_notes.sql
|
||
005_add_actions.sql
|
||
006_add_worklog.sql
|
||
007_search.sql
|
||
008_sync.sql
|
||
009_section.sql
|
||
```
|
||
|
||
## RAID
|
||
|
||
- **Критично:** Wails требует Node.js для frontend-сборки
|
||
- **Критично:** go-sqlite3 + cgo; gcc уже установлен
|
||
- **Зависимость:** Steps 15+ ждут завершения step 14 (MVP stabilization)
|
||
|
||
---
|
||
|
||
## Phase 4: Template-Driven Architecture
|
||
|
||
Implemented in this commit:
|
||
|
||
- **Template system** — built-in system templates for folder, project, client, document, and recipe types.
|
||
Each template defines default modules, default files (`Overview.md`), and default subfolders.
|
||
- **Vault layout** — human-readable folder structure on disk. Every node gets a folder named after its
|
||
title (sanitized). Nesting reflects parent-child relationships. UUIDs are never exposed in user paths.
|
||
- **`.verstak/` directory** — app-internal data (db, backups, thumbnails, cache, sync, trash, history).
|
||
- **i18n keys** — new locale keys for `nav.*`, `template.*`, `common.archive`, and `migrate.*` namespaces
|
||
added to both `ru.json` and `en.json`.
|
||
- **Documentation** — `docs/VAULT_LAYOUT.md` (vault folder structure, rules, migration) and
|
||
`docs/TEMPLATES.md` (system templates, template structure JSON, UI integration).
|