verstak/AGENTS.md

152 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Verstak project rules
## Project identity
Verstak is a local-first workbench for clients, projects, notes, files, tasks, activity and sync.
It must remain practical, simple, and filesystem-aware.
## Stack
- Backend: Go
- Storage: SQLite
- GUI: Wails v2
- Frontend: Svelte 4
- Build tooling: Vite 5
- Do not migrate to Wails v3, Svelte 5, or Vite 8 unless explicitly asked.
## Architecture rules
- Keep local-first behavior.
- Do not turn the project into SaaS.
- Do not replace SQLite with another database.
- Do not introduce cloud storage assumptions.
- Preserve recursive folder import semantics.
- Preserve stable node IDs.
- Do not duplicate nodes when moving items.
- Do not create parallel state systems for the same entity.
## UI rules
- Fix GUI behavior at root cause.
- Do not redesign the whole interface unless explicitly asked.
- Preserve active tab state correctly.
- Context menus must open near the cursor.
- Drag-and-drop must show clear visual target feedback.
- Moving nodes must never duplicate the same ID in two places.
- Nested selection must not collapse the parent unexpectedly.
## Files
- File view is not a tree.
- Sidebar shows logical hierarchy.
- Vault filesystem layout must remain human-readable without the app.
- Drag-and-drop folders must perform real recursive copy/move into the vault.
- Do not fake folder support with external links.
## Sync
- Sync settings belong in Settings.
- Main UI may keep only manual sync/status controls.
- Existing URL + login/password device registration flow should be preserved unless explicitly changed.
- Secrets must not be logged.
## Verification
For backend changes:
- Run `go test ./...` if possible.
For frontend changes:
- Run the relevant frontend build/check command if available.
- If unsure, inspect package scripts first.
For GUI bugs:
- Add targeted tests only where practical.
- If manual GUI clicking is required and unavailable, state exact manual verification steps for the user.
# Session summary
## Bugs fixed (this session)
1. **webkit2_41 build tag** — binary wouldn't start without it. Added to build instructions.
2. **Sidebar refresh**`reloadTreePreservingExpanded` patches children in-place so expand/collapse state stays intact.
3. **Context menu off-screen** — changed to `position: fixed` with cursor coordinates.
4. **"Show in explorer" only for folder types** — `OpenFolder` in backend falls back to file record path for `TypeFile` nodes.
5. **Context menu not closing on action**`handleShowInFolder` calls `closeMenu()`.
6. **Wrong folder when opening file's parent folder**`OpenFolder` checks `n.FsPath == ""` for TypeFile and uses first file record path.
7. **Tab highlight not updating visually** — was using `class={tabClass(tab.id)}` which didn't trigger reactive class updates in Svelte. Switched to `class="tab" class:active={activeTab === tab.id}`.
8. **Journal table expand/collapse** — added explicit ▸/▾ toggle column so it's clear rows are expandable.
9. **Per-node worklog entries** — made entries expandable with ▸/▾, showing details + billable/approximate tags.
10. **Manual worklog entry form** — converted inline form to modal dialog ("+ Добавить запись") with all fields: date, summary, minutes, details, billable, approximate.
11. **"С подзадачами" → "Учитывать вложенные дела"** — renamed, now hidden when no node selected.
12. **Filter/export layout** — split into separate "Фильтры" and "Экспорт отчёта" sections with headings.
13. **Suggestion events** — added "Показать в проводнике" button for file-type events in suggestion detail.
14. **Removed duplicate i18n keys** in `ru.js` (worklog.suggestions, worklog.apply).
15. **Removed unused CSS** (`.journal-filters`, `.wl-meta`, `.worklog-form`).
16. **Added `openNodeFolder(nodeOrId)`** — accepts both string ID and node object.
17. **Added `resetJournalFilters()`** — resets all filters and reloads.
18. **Source field** — added `worklog_entries.source` column (migration 014). Values: manual, suggestion. Old entries default to 'unknown'.
19. **Suggestions now use worklog_entry_events** instead of `HasTodayEntries` — only events already linked to worklog entries are excluded. Repeated activity on the same node today now produces new suggestions.
20. **Activity target navigation** — clicking activity events for notes opens the note tab and loads the specific note. File events open the files tab.
21. **Source display** — detail sections now show accurate source: "Ручная запись", "Из предложения", "Из предложения, но связанные события отсутствуют", or "Источник неизвестен".
22. **Wails `[]string` marshalling bug** — Wails v2.12.0 silently drops `[]string` positional args from JS→Go. **Fix**: pass all string arrays as `JSON.stringify()``string``json.Unmarshal` on Go side.
23. **Event link validation**`AcceptSuggestionWith` pre-checks each eventID against `activity_events`, uses plain `INSERT` (not `INSERT OR IGNORE`), and verifies with JOIN `COUNT(*)` after commit.
24. **GetWorklogEntryEvents column fix** — query used `e.details_json` but the column is `e.metadata`. Fixed to `COALESCE(e.metadata,'')`.
25. **"Посмотреть" button** — `openActivityTarget(ev)` navigates to the specific target: note tab + open note for `targetType=note`, files tab + `OpenFolder(targetPath)` for `file/folder`.
26. **End-to-end test**`TestAcceptSuggestionWithEndToEnd` creates node, 3 activity events, accepts suggestion, verifies all 3 linked via `worklog_entry_events` + JOIN.
27. **WriteDebugLog binding**`bindings_debug.go` writes frontend logs to `<vault>/.verstak/debug.log` for production GUI debugging.
28. **Journal regression tests**`TestJournalFullRegression`, `TestSuggestionOnRepeatedActivity`, `TestManualWorklogEntry`.
29. **resolveActivityTarget helper** — pure function returning `{ nodeId, tab, noteId/fileId/targetPath }`, used by `openActivityTarget`.
30. **First-run flow** — no auto-vault creation. New `GetStartupStatus` binding returns `first_run`/`recovery`/`ready`. Frontend shows FirstRun.svelte or VaultRecovery.svelte accordingly.
31. **Global config.json** — moved vault path, sync settings, templates, theme, language from implicit CLI args to `~/.config/verstak/config.json` (`AppConfig` struct).
32. **Sync settings in Settings** — extracted sync modal into Settings → Sync section. Removed all inline sync form fields from `App.svelte`. Added `SyncStatus.svelte` widget replacing navbar sync button.
33. **Settings window** — modal with sidebar (8 sections: General, Workspace, Templates, Plugins, Files, Activity, Sync, Backup). ESC to close. Lazy-loaded content panels.
34. **Template enable/disable**`AllTemplates` + `SetTemplateEnabled` bindings propagate to `appCfg.EnabledTemplates`. `initVault` applies filter to registry.
35. **Vault recovery screen** — when vault path exists but vault is missing, shows VaultRecovery.svelte with choose/create/quit options.
## Bugs fixed (this session)
1. **Trash preview "trash file not found" for TypeFile nodes**`resolveTrashPath` only searched `<nodeID>_*` in trash dir, but TypeFile node files are moved by file record ID (`<recordID>_<filename>`). Added file record fallback via `ListTrashedByNode` + `ReadTrashFile(trashFsPath)` binding.
2. **Trash restore creates empty files**`DeleteToTrash` permanently `DELETE`'d file records from DB and `restoreTrashPath` silently `return nil` for `FsPath=""` nodes. Changed `deleteFileRecords` to `trashRecord` (sets `missing=1`, keeps record). `restoreTrashPath` now restores file records: moves file back from trash, sets `missing=0`.
3. **`resolveTrashPath` inner loop corrupts `anc` variable** — `anc = child` inside the inner loop caused wrong path computation for nesting depth > 2. Replaced with direct `chain[0].FsPath` prefix computation.
4. **`ListTrash` missing `TrashFsPath` for TypeFile nodes** — Phase 1 only checked `<nodeID>_*` entries. Added `ListTrashedByNode` fallback to set `TrashFsPath` for TypeFile nodes.
5. **`ListByNode` returning trashed records** — Added `AND missing != 1` filter to exclude trashed file records from active node file listings.
## Key patterns (this session)
- **TypeFile node trash**: files are moved by file record ID (`<recordID>_<filename>`), NOT by node ID. Never search by `<nodeID>_*` alone — always fall back to file records via `ListTrashedByNode`.
- **File record soft-trash**: use `UPDATE files SET missing=1` instead of `DELETE` to keep records restorable. Restore via `UPDATE ... SET missing=0` + `os.Rename` from trash.
- `ReadTrashFile(trashFsPath)` is preferred over `ReadTrashFileContent(nodeID)` — frontend has `trashFsPath` precomputed by `ListTrash`.
- **`restoreTrashPath` for TypeFile nodes**: when `fsPath == ""`, find file records with `missing=1` and restore each one.
- **Full test coverage**: `TestTrashTypeFilePreviewAndRestore`, `TestTrashTypeFileInsideFolderRestorePreservesContent`, `TestTrashTypeFileMultipleRecords`.
## Key patterns
- Always use explicit toggle icons (▸/▾) on expandable rows.
- `CreateWorklogFull` supports all fields: nodeID, summary, details, date, minutes, approximate, billable.
- `openNodeFolder(id)` accepts a string ID or a node object.
- `GetSuggestions` filters out only events already in `worklog_entry_events`, not entire nodes.
- New worklog entries get `source=manual` via `Add`/`AddWithDate`; suggestion entries get `source=suggestion` via `AcceptSuggestionWith`.
- **NEVER pass `[]string` through Wails v2 bindings** — always JSON-serialize to `string` first. Wails v2.12.0 silently drops slice arguments.
- **Always wrap create-entry + link-events in a transaction** with pre-validation and post-commit verification to prevent orphan entries.
- Frontend debug logs in production: use `wailsCall('WriteDebugLog', msg)` → writes to `<vault>/.verstak/debug.log`.
- `AppConfig` stores all global settings in `~/.config/verstak/config.json`. Vault-specific config stays in `.verstak/config.yml`.
- Use `GetStartupStatus` to determine first-run vs recovery vs normal startup.
- Settings window uses a sidebar with 8 sections; each section is a separate Svelte component imported lazily.
- Template enable/disable state is stored in `appCfg.EnabledTemplates` and applied to the registry during `initVault`.
# Build instructions
## GUI binary (Wails v2)
```bash
# From project root:
cp -r frontend/dist/* cmd/verstak-gui/frontend-dist/
go build -tags "webkit2_41 desktop production" -ldflags="-s -w" -o build/verstak-gui-linux-amd64 ./cmd/verstak-gui/
```
## Server binary
```bash
go build -ldflags="-s -w" -o build/verstak-server-linux-amd64 ./cmd/verstak-server/
```