# Changelog
All notable changes to devist are documented here.
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/).
## [0.15.0] - 2026-05-03
### Added — periodic, change-gated auto-verify of advice
- New 5th daemon thread `verify` runs every 60s. Per project:
1. Skip if cooldown < 5min since last check.
2. Skip if no `file_changed` in local SQLite since last verify
(change-gate — no tokens spent on quiet projects).
3. Fetch un-acked, **verifiable** advice from Supabase.
4. Read current content of the referenced files (locally, no
extra Supabase round-trips).
5. One Claude call per project: which items are resolved?
Conservative prompt — only resolved=true if the issue is
clearly absent from current file content.
6. PATCH each resolved row with `acked_at=now()`,
`acked_by='verify'`.
- Per-project rate limit: 4 verify calls per hour
(sliding-window `HourlyLimiter`).
- Advice generation now emits `verifiable: true|false` per item +
`paths: [...]`. Subjective advice (`verifiable=false`) is ignored
by the verify thread.
### Added
- Migration `0010_acked_by.sql` adds `worker_events.acked_by` text
column (`'user'` | `'verify'` | `'apply'`). Authenticated users
retain UPDATE permission on it alongside `acked_at`.
- `Db::distinct_projects()` and `Db::project_has_changes_since()`
helpers backing the verify thread's local-SQLite gate.
- `SupabaseClient::list_pending_verifiable_advice(project)` and
`ack_event(id, source)` for the verify ↔ Supabase round-trip.
- `urlencoding` dependency for safe project-name URL encoding.
### Why
File events themselves are too noisy to drive auto-ack. Periodic
batched verification with a change-gate keeps cost bounded
(typical day: ~10-30 Claude calls instead of hundreds), preserves
local SQLite as the source of truth for activity, and reads
Supabase only when there's actually pending work.
## [0.14.0] - 2026-05-03
### Changed — daemon advice locale follows the user automatically
- `auto` advice (file-change burst) used to use the static
`advice_locale` from worker config (default `"ko"`). It now resolves
the user's current language preference at write time:
1. `public.user_locale_pref` view (latest user's
`auth.users.raw_user_meta_data.lang`)
2. fallback to `cfg.advice_locale`
- Same resolution for AI rule-generation jobs when the dashboard
doesn't pass an explicit `input.locale`.
- `SupabaseClient::get_user_locale()` caches the value for 5 minutes
→ ~one extra REST hit per cache window, otherwise zero overhead.
- Failures (HTTP, missing view, no preference) silently fall back to
config — advice generation never blocks on locale resolution.
### Added
- Migration `0009_user_locale_pref_view.sql` — public view exposing
the most-recently-active user's `lang`. Restricted to `service_role`.
### Removed
- Japanese (`ja`) from the dashboard. Two languages only: `en`, `ko`.
Translation of dynamic content turned out to be too much surface
area for too little benefit; `advice_locale` write-at-source is the
canonical answer.
### Notes
- Single-user assumption: the view returns one row (latest user). For
multi-user, replace with a per-(client_id) mapping table.
- Existing English advice rows from earlier daemon runs were
one-shot translated to Korean in the conversation log; not part of
any migration.
## [0.13.0] - 2026-05-03
### Added — locale-aware AI output
- `WorkerConfig.advice_locale` (default `"ko"`). Daemon's advice prompt
appends an explicit instruction to write all natural-language strings
(every `text`, every `explain` field) in this BCP-47 language code.
JSON keys/structure stay English regardless.
- `worker_jobs.input.locale` is honored per-request — the dashboard
passes the user's current UI language so AI-generated rule drafts come
back in that language. Falls back to `advice_locale` from config.
- Dashboard now translates `severity` (info/suggest/warn/block) and
`event_type` (advice/advice_error/file_changed/scan/...) per language.
Filter buttons, EventRow badges, and the "acked" pill all localized
for en/ko/ja.
### Added — Japanese
- Full `ja` dictionary covering Landing / Login / Dashboard / pages.
- `LANGUAGES` array exposes `English / 한국어 / 日本語`.
### Changed
- `LanguageSwitch` uses shadcn/ui `Select` (not native `<select>`).
- I18nProvider now syncs language preference to
`auth.users.user_metadata.lang` so it follows the user across devices.
- Sidebar title is the typographic logo `DEVIST` + small `DASHBOARD`
caption + email.
## [0.12.0] - 2026-05-03
### Changed (breaking)
- Rules are now two layers: **devist core** (built-in, immutable,
ships with the binary) + **user global** (single editable file at
`~/.devist/worker/rules.md`). Per-project rules removed.
- `devist worker rules path|show|init` no longer accept `--project`.
- `Rules::load(monitor, project)` → `Rules::load_global()`.
- Templates no longer ship `.devist/rules.md.tmpl`.
### Added
- `BUILTIN_RULES` constant in `src/worker/rules.rs` with output
discipline, safety, scope, and tone sections that always inject
into the Claude advice prompt regardless of user config.
- Dashboard Rules page redesigned: collapsible read-only "devist core"
panel at top, single editable user textarea below. The AI generation
flow now operates on the user layer only.
### Migrations
- `migrations/0008_drop_project_rules.sql` deletes any
`scope LIKE 'project:%'` rows from `worker_rules`. Idempotent.
### Why
Per-project rules added cognitive load (which scope am I editing?)
without commensurate value. A few well-written global rules + the
project's own `CLAUDE.md` cover the same ground more cleanly.
## [0.11.0] - 2026-05-03
### Changed
- Daemon no longer pushes `file_changed` / `file_changed_continuous`
events to Supabase. They remain in local SQLite (so `worker watch`
CLI and the burst-based advice trigger keep working) but stop
flooding the dashboard. Sync log now reads e.g.
`[sync] pushed 0 events to Supabase (1 local-only)`.
### Migration
- `migrations/0007_drop_file_changed_events.sql` — one-shot DELETE
of historical `file_changed*` rows from the cloud table. Idempotent.
### Why
A typical workday produced thousands of file-change rows that drowned
out the few advice items that mattered. The dashboard's "Recent
events" was essentially noise. Local SQLite is unaffected — full
fidelity stays where forensic detail is useful (the CLI).
## [0.10.0] - 2026-05-03
### Added — Per-thread daemon heartbeat
- Migration `0006_worker_heartbeat.sql` adds the table
`(client_id, thread, last_beat_at)` with composite PK, RLS
authenticated read, joined to supabase_realtime. Daemon writes via
service-role.
- Each daemon thread emits a heartbeat every ~10s during its loop:
`main` (in the watcher poll), `advice` (now uses recv_timeout so it
beats while idle), `rules-sync`, `jobs`. Failure to write a heartbeat
is non-fatal — best-effort.
- Thread spawns are wrapped in `panic::catch_unwind` so a panic
surfaces as `[<thread>] thread panicked` in the log instead of
silently leaving the PID alive while the thread is dead.
- Dashboard sidebar shows a small `DaemonStatus` widget under the
user email: ● green if all threads beat within 30s, yellow within
2 min, red beyond. Click expands a per-thread breakdown with the
age in seconds. Polls every 10s.
### Why
Previously `devist worker status` only checked the PID, so a thread
crashing silently (e.g. `jobs` panic) showed as `running` while no
jobs were processed. The dashboard now surfaces the truth.
## [0.9.0] - 2026-05-03
### Added — Rules editor Phase 2 (AI assistance)
- Migration `0005_worker_jobs.sql` adds the queue table:
`worker_jobs (id, kind, scope, input jsonb, output jsonb, status,
error, client_id, created_at, updated_at)` with auto-touched
`updated_at`, RLS (authenticated read + insert), and realtime.
- Daemon spawns a `jobs` worker thread that polls every 5s for
`status='pending'` rows, atomically claims via
`UPDATE WHERE id=X AND status='pending'`, dispatches by `kind`,
spawns `claude -p` for `generate_rules`, writes back `output` +
`status='done'` (or `'error'` with `error` text).
- Dashboard Rules page gains an AI assistant section:
natural-language intent textarea → "Generate" button → INSERT
`worker_jobs` row → realtime subscription on that row → preview
pane with Claude's draft + explanation → "Use this draft" loads
it into the editor (still editable before save).
### Notes
- `generate_rules` prompt asks Claude to MERGE with current content
rather than replace, so iterating rules with successive prompts is
additive by default.
- Realtime is the primary update path; a 4s polling fallback handles
the case where the user reloads after submitting.
## [0.8.0] - 2026-05-03
### Added
- **Rules editor** (Phase 1: editor + sync, no AI yet).
- Migration `0004_worker_rules.sql` adds the table + auto-touched
`updated_at` trigger + RLS policies (authenticated read+write) +
realtime publication.
- Daemon spawns a `rules_sync` thread that bootstraps any local
`rules.md` content into the table on first run, then polls the
table every 10s and mirrors changed rows to the appropriate local
file (global → `~/.devist/worker/rules.md`, project scope →
`<monitor>/<name>/.devist/rules.md`).
- Dashboard ships a `/dashboard/rules` page with scope picker
(global + per project), textarea, save button, and a
"last updated" relative time.
- Daemon log tags: `[rules-sync] thread up`,
`[rules-sync] bootstrapped <scope> from <path>`,
`[rules-sync] wrote <path> (updated_at=...)`.
### Notes
- Table is canonical: edits made directly to local files after
bootstrap are not pushed back. Use the dashboard or restart the
daemon (with the table row deleted) to re-bootstrap.
- AI-assisted rule generation (Phase 2) lands later: dashboard
describes intent in natural language → daemon spawns claude CLI
via a `worker_jobs` queue → result returned to dashboard.
## [0.7.0] - 2026-05-03
### Added
- `devist project sync <name>` — add-only sync from the project's
template. Walks the latest template files and copies any that don't
already exist in the project. Existing files are never overwritten,
so user edits are safe. Useful for catching old projects up to new
template scaffolds (e.g. the `.claude/`, `_workspace/`, `.devist/`
files added in the recent templates release).
- `--dry-run` flag prints what would change without writing.
- `--var key=value` overrides for rendering newly added templated files
(mirrors `devist init`).
### Notes
- Idempotent: a second sync with no template changes is a no-op.
- This is intentionally a one-way "fast forward" for missing files
only. A full 3-way merge (with lockfile + diff) is a separate
feature that will land later if needed.
## [0.6.0] - 2026-05-03
### Added
- `render.rs` now supports conditional blocks:
- `{{#if VAR}}...{{/if}}` — render when `VAR` is set and non-empty
- `{{#unless VAR}}...{{/unless}}` — render when `VAR` is unset/empty
- Nesting works. Inside a *skipped* branch, unknown variable references
are tolerated (they wouldn't be reachable anyway). Active regions
still error on unknown vars.
- `agent_mode` is a recognized template variable. `devist init` derives
`is_devist` and `is_vibe` boolean-style vars from it (default
`agent_mode = "devist"`). Templates can now ship a single
`CLAUDE.md.tmpl` that conditionally renders devist-flavored or
vibe-flavored content.
- Companion `devist-templates` release pairs every template with
`CLAUDE.md.tmpl` + `AGENT.md.tmpl`, and adds a new `react-vite`
template (no Supabase, pure React + Vite + TS + Tailwind v4 +
shadcn/ui).
### Tests
- 12 render unit tests covering conditional truthy/falsy/nested cases,
skipped-branch tolerance, unmatched/unclosed errors.
## [0.5.0] - 2026-05-03
### Security
- New `src/worker/secrets.rs` module with a 4-layer blocklist
(filenames, prefixes, extensions, path segments). Matched paths are
dropped at the daemon — never recorded in SQLite, never pushed to
Supabase, never read by the advice pipeline. Defense-in-depth check
also runs in `advice.rs::collect_snippets`.
- 7 unit tests cover dotenv family, credential files, key extensions,
`~/.ssh` / `~/.aws` / `secrets/` segments, case-insensitivity, and
Windows separators.
### Changed
- mem0 search query now summarizes the burst's parent dirs, filenames,
and extensions (e.g. `"Project foo: edits in src/auth affecting
login.tsx (tsx, ts)..."`) instead of the prior keyword-style
`"project: foo"` which never matched stored facts.
### Added (web dashboard)
- Realtime subscription via Supabase channel (`useRealtimeWorkerEvents`).
Inbox, Project timeline, and Overview update live on INSERT/UPDATE.
Project timeline subscribes with a server-side `project=eq.X` filter.
- Acknowledge flow: per-row Ack button on advice rows. Acked rows
render muted with a strikethrough title and "ACKED" badge. Inbox and
Project timeline have a "Show acked" toggle.
### Migrations
- `migrations/0003_worker_events_realtime_and_ack.sql` — adds the table
to `supabase_realtime` publication and creates a column-restricted
UPDATE policy (`GRANT UPDATE (acked_at)` + RLS) so authenticated
dashboard users can only mutate `acked_at`, not other columns.
- Fixed `migrations/0002` — replaced unsupported
`CREATE POLICY IF NOT EXISTS` with a `DROP POLICY IF EXISTS` + create
pattern so the file is idempotent.
## [0.4.0] - 2026-05-02
### Added
- Real Supabase L2 push (replaces the prior stub):
- `migrations/0001_worker_events.sql` defines the table, idempotency
index `(client_id, client_event_id)`, and RLS enable.
- `WorkerConfig.client_id` (defaults to hostname) is the per-host
idempotency key.
- Daemon batch-flushes up to 500 unsynced rows every
`sync_interval_secs`, marks them synced on success, retries on the
next interval otherwise.
- Rules system that shapes Claude advice deterministically:
- Global `~/.devist/worker/rules.md` and per-project
`<monitor>/<project>/.devist/rules.md`. Both plain markdown,
concatenated and prepended to the advice prompt.
- `devist worker rules path|show|init [--project N]` to manage them.
- `rules init` writes a starter template (Korean tone, focus areas,
mem0 persistence policy).
### Changed
- Advice prompt now starts with a "User-defined rules (follow these
strictly)" section when rules are present.
## [0.3.0] - 2026-05-02
### Changed (breaking)
- Removed `devist brief`, `devist scan`, `devist explain`, `devist watch`
CLI commands. The underlying scanner/git helpers remain as internal
libraries for the worker.
### Added
- `devist worker` background daemon that observes a parent folder of
projects and records every file event into a local SQLite store
(`~/.devist/worker/worker.db`).
- `worker start` (with first-run setup), `worker stop`, `worker status`,
`worker watch` (live tail), `worker config show|get|set|path`.
- AI advice generation (Phase 2):
- Idle-burst trigger: after `advice_idle_seconds` of quiet on a
project, the daemon spawns the `claude` CLI with the changed
snippets and recent mem0 memories to extract facts + advice.
- mem0 Cloud integration (`api.mem0.ai`) for long-term semantic
memory of facts above `mem0_confidence_threshold`.
- 4-tier guard against runaway calls: `advice_enabled` kill switch,
`advice_min_batch`, per-project `advice_max_per_hour`, global
`mem0_max_writes_per_hour` sliding-window limiters.
- `worker advice [--project N] [--limit N]` lists generated advice.
- `worker memory search <query>` for ad-hoc mem0 lookup.
- `devist doctor` now also checks the `claude` CLI.
### Notes
- The daemon writes its log to `~/.devist/worker/worker.log` and PID to
`worker.pid`. Restart after changing config (`worker stop && start`).
- mem0 + Claude integration is opt-in: without `mem0_api_key` the worker
silently skips advice generation; the SQLite event log still works.
## [0.2.0] - 2026-05-02
### Changed (breaking)
- Renamed CLI command `devist projects` → `devist project`
(e.g. `devist project list`, `devist project forget <name>`).
### Added
- `devist start <name> --dev` now runs the template's `install` command
(e.g. `pnpm install`) automatically when `package.json` exists but
`node_modules` is missing, before launching the dev server.
- Release workflow publishes GitHub Releases directly (no draft step).
## [0.1.1] - 2026-05-02
### Fixed
- Release workflow now downloads SHA files via GitHub API,
supporting draft releases.
- `cargo publish` step is idempotent — re-running on an existing
version is treated as a non-fatal no-op.
## [0.1.0] - 2026-05-02
Initial beta release. Phase 1 through Phase 4 complete.
### Added
- Core CLI with 12 commands: `setup`, `doctor`, `about`, `template` (list/add/sync/remove),
`projects` (list/forget), `init`, `start`, `stop`, `brief`, `scan`, `explain`, `watch`.
- Template system with `devist.toml` manifest, variable substitution via
`*.tmpl` files, automatic discovery of single- and multi-template repos.
- Lazy backend management: only one project's `supabase`/`docker-compose`
stack runs at a time. `start` automatically stops any other active
project's backend.
- Code comprehension commands (`brief`, `scan`, `explain`, `watch`) for
understanding AI-assisted codebases.
- Workspace at `~/.devist/` with `config.toml`, `registry.toml`, `state.toml`,
and template cache.
- 7 official templates: `react-supabase`, `react-native-supabase`,
`flutter-supabase`, `fastapi-postgres`, `nestjs-postgres`, `rust-axum`,
`wordpress`.
### Notes
- Beta — APIs and template manifest schema may change before v1.0.
- Public Homebrew tap and crates.io publish targeted for v1.0.
[Unreleased]: https://github.com/WebchemistCorp/devist/compare/v0.15.0...HEAD
[0.15.0]: https://github.com/WebchemistCorp/devist/releases/tag/v0.15.0
[0.14.0]: https://github.com/WebchemistCorp/devist/releases/tag/v0.14.0
[0.13.0]: https://github.com/WebchemistCorp/devist/releases/tag/v0.13.0
[0.12.0]: https://github.com/WebchemistCorp/devist/releases/tag/v0.12.0
[0.11.0]: https://github.com/WebchemistCorp/devist/releases/tag/v0.11.0
[0.10.0]: https://github.com/WebchemistCorp/devist/releases/tag/v0.10.0
[0.9.0]: https://github.com/WebchemistCorp/devist/releases/tag/v0.9.0
[0.8.0]: https://github.com/WebchemistCorp/devist/releases/tag/v0.8.0
[0.7.0]: https://github.com/WebchemistCorp/devist/releases/tag/v0.7.0
[0.6.0]: https://github.com/WebchemistCorp/devist/releases/tag/v0.6.0
[0.5.0]: https://github.com/WebchemistCorp/devist/releases/tag/v0.5.0
[0.4.0]: https://github.com/WebchemistCorp/devist/releases/tag/v0.4.0
[0.3.0]: https://github.com/WebchemistCorp/devist/releases/tag/v0.3.0
[0.2.0]: https://github.com/WebchemistCorp/devist/releases/tag/v0.2.0
[0.1.1]: https://github.com/WebchemistCorp/devist/releases/tag/v0.1.1
[0.1.0]: https://github.com/WebchemistCorp/devist/releases/tag/v0.1.0