Routines accepted before Vikunja #366 carry accepted_ts NULL and a live
reminder row. The tick loop reads accepted_ts to decide when a routine is
next due, so those rows have been silent since the fix landed, while the
reminder they still point at keeps firing on its own schedule.
Migration #19 cancels that reminder first, then dates the acceptance from
created_ts and lets the reminder id go. Order matters: the second update
clears the id the first one needs.
safeKey kept ASCII only, so "Встреча с Аней" and "Обед с мамой" both
reduced to "--" and shared one key on one day. The second event of the
day overwrote the first, silently, and his calendar is Russian.
Letters and digits in any script now pass. Migration #18 deletes the rows
written under the old rule instead of rewriting them: a calendar fact is
derived, the next poll writes the day again, and a stale row reads as an
extra meeting.
Add a 'scope' TEXT column (default 'homelab') to the tools table so tools
can be namespaced by scope (e.g. "homelab:restart", "datacenter:reboot").
Backward-compat: bare name defaults to "homelab" scope.
Changes:
- Migration #1: ALTER TABLE tools ADD COLUMN scope
- store.Tool: add Scope field, update all SQL and scanTool()
- ipc.Tool DTO and request types: add Scope field
- CoreAPI interface: pass scope in ProposeTool/EnableTool
- storeAPI adapters: forward scope
- cmd/mavend/voice: pass scope (empty → homelab)
- cmd/mavweb/tools: show scope column in UI tables, hidden fields
- All tests updated for scope field
- Migration test made dynamic (startVer = len(migrations))
Two spine infra items (feature-ranking #1, part of the migration prereq):
- migrations.go: PRAGMA user_version runner, empty (no-op) migration slice,
one tx per step, fail-closed. Mechanism in place before any real schema
change needs it.
- crypt.go: file-level at-rest encryption. On-disk file is always AES-256-GCM
ciphertext; decrypted to a tmpfs working copy modernc sqlite operates on;
re-encrypted atomically on Close, plaintext wiped, key zeroed. Pure stdlib,
CGO stays off. Fails closed on wrong key/tamper, never falls back to
plaintext. Key is a 32-byte seam (config db_key_b64/db_key_env today; the
passkey-derived L3 cold-start key plugs into the same seam later).
Chosen over cgo SQLCipher (would force libsqlcipher + CGO across the project)
and over the ncruces page-level VFS (swaps the driver project-wide); noted as
the upgrade path in a ponytail: comment. Threat model is disk-at-rest only.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>