From 774770ce3903db0ba475f6e605c2c5b74309a2a8 Mon Sep 17 00:00:00 2001 From: Forgejo Actions Date: Thu, 4 Jun 2026 08:23:35 -0700 Subject: [PATCH] Update changelog for v1.0.0 [skip ci] --- CHANGELOG.md | 76 +++++++++++++++++++ docs/changelog.d/+clippy-prek-hook.infra.md | 1 - .../+fix-clippy-single-match.bugfix.md | 1 - docs/changelog.d/+keyring-slim.infra.md | 1 - docs/changelog.d/+pin-quartz-v4.bugfix.md | 1 - docs/changelog.d/+project-design.doc.md | 1 - docs/changelog.d/+template-setup.infra.md | 1 - docs/changelog.d/+v1-spec-second-pass.doc.md | 1 - docs/changelog.d/extract-heph-nvim.infra.md | 1 - docs/changelog.d/pre-v1-cleanup.infra.md | 1 - docs/changelog.d/v1-hub-prep.doc.md | 1 - docs/changelog.d/v1-hub-prep.infra.md | 1 - docs/changelog.d/v1-inbox-view.feature.md | 1 - docs/changelog.d/v1-list-project.feature.md | 1 - docs/changelog.d/v1-prek-cargo-fmt.infra.md | 1 - docs/changelog.d/v1-prototype.doc.md | 2 - docs/changelog.d/v1-prototype.feature.md | 35 --------- docs/changelog.d/v1-prototype.infra.md | 2 - docs/changelog.d/v1-quickadd.feature.md | 2 - docs/changelog.d/v1-selfhost-roadmap.doc.md | 1 - .../changelog.d/v1-tui-move-picker.feature.md | 1 - .../changelog.d/v1-tui-undo-panels.feature.md | 5 -- 22 files changed, 76 insertions(+), 62 deletions(-) delete mode 100644 docs/changelog.d/+clippy-prek-hook.infra.md delete mode 100644 docs/changelog.d/+fix-clippy-single-match.bugfix.md delete mode 100644 docs/changelog.d/+keyring-slim.infra.md delete mode 100644 docs/changelog.d/+pin-quartz-v4.bugfix.md delete mode 100644 docs/changelog.d/+project-design.doc.md delete mode 100644 docs/changelog.d/+template-setup.infra.md delete mode 100644 docs/changelog.d/+v1-spec-second-pass.doc.md delete mode 100644 docs/changelog.d/extract-heph-nvim.infra.md delete mode 100644 docs/changelog.d/pre-v1-cleanup.infra.md delete mode 100644 docs/changelog.d/v1-hub-prep.doc.md delete mode 100644 docs/changelog.d/v1-hub-prep.infra.md delete mode 100644 docs/changelog.d/v1-inbox-view.feature.md delete mode 100644 docs/changelog.d/v1-list-project.feature.md delete mode 100644 docs/changelog.d/v1-prek-cargo-fmt.infra.md delete mode 100644 docs/changelog.d/v1-prototype.doc.md delete mode 100644 docs/changelog.d/v1-prototype.feature.md delete mode 100644 docs/changelog.d/v1-prototype.infra.md delete mode 100644 docs/changelog.d/v1-quickadd.feature.md delete mode 100644 docs/changelog.d/v1-selfhost-roadmap.doc.md delete mode 100644 docs/changelog.d/v1-tui-move-picker.feature.md delete mode 100644 docs/changelog.d/v1-tui-undo-panels.feature.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 4771d0f..718d6cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,3 +11,79 @@ All notable changes to this project are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). + +## [v1.0.0] - 2026-06-04 + +### Features + +- - **Inbox view** (§8.2): a sixth built-in filter view, listed below On Deck, showing every outstanding task with **no project** — the capture inbox to triage and file. A new `ListFilter.unfiled` predicate (kept purely in `matches()`) drives it; the `inbox` `ViewSpec` is deliberately un-gated (no attention or do-date filter) so nothing hides from triage. Appears automatically in the `heph-tui` sidebar and `heph view`; `heph view inbox` from the CLI. +- - **`heph list --project ` + `--json`** (§8.2): list a project's outstanding tasks by **name** (subtree-expanded, resolved server-side via a new `project.scope` path that reuses the view machinery — errors loudly on an unknown name), and `--json` prints the raw rows (`node_id`, `canonical_context_id`, attention/state/do_date/late_on/recurrence/project_id) for scripting and agents. This is the canonical "show me a project's outstanding work" command — `AGENTS.md` documents it as how to inspect heph's own roadmap (the `Hephaestus` project), now that heph self-hosts it. +- Begin the v1 prototype (Phase 1, tech-spec §11.1), built in TDD slices: + + - Cargo workspace + `heph-core` crate; migration-run SQLite schema (§4.5); clock-injected `Store` trait + `LocalStore` node create/get; single local-user bootstrap. + - Markdown extraction (§5): `[[wiki-links]]` and GFM `- [ ]` checkbox context-items derived purely and idempotently from a body, skipping code blocks. + - Committed tasks (§4.3, §6): `task.create` auto-creates the canonical context `doc` + `canonical-context` link; attention/do-date/late-on/state/recurrence columns; set-state/set-attention. Links CRUD (outgoing/backlinks). A body update reconciles `wiki` links (diff-based, resolved by alias/title, idempotent). + - "What is next?" ranking (§7): pure, clock-injected, two-stage engine — candidacy filter (do-date as a boolean gate only) then a reorderable list of named dimensions (past-late-on → overdue-amount → attention band → FIFO). `late_on` is the sole urgency signal; blue hidden; red always shown. Proptest-checked total order. `Store::next` surfaces it over SQLite. + - Recurrence — roll-forward in place (§4.4): completing a recurring task resets its checklist to all-unchecked, logs the occurrence, and advances the do-date to the next RRULE instance after now (skipping misses) — completion never carries forward (proptest-checked). Per-task append-only logs (`log-of`) with `log.append`/`log.tail`; `skip` advances without logging. + - `hephd` daemon, local mode (§3, §6): exclusive file lock (handoff-ready), line-delimited JSON-RPC over a unix socket exposing the node/task/next/links/log methods, with DB work on tokio's blocking pool. Synchronous client for surfaces/CLI. Model types are serde-serializable. + - `heph` CLI (§1) — a thin client of the daemon: `next`, `task`, `doc`, `get`, `export`. Export materializes the store to a `/.md` tree with YAML frontmatter + body (§5), one-way, tombstones excluded. + - Sync engine, local-only (§12): real hybrid logical clock + persistent device `origin`; an append-only op-log per mutation; an idempotent, order-independent merge/apply engine — last-writer-wins task scalars (discards surfaced in a `conflicts` queue), OR-set links, monotonic tombstones. Two-replica convergence proven. + - Body text CRDT (§5, §12, slice 8d): node bodies now merge through the `yrs` text CRDT (`body_crdt`) instead of last-writer-wins — whole-buffer writes are diffed into the doc and the yrs delta rides the op, so concurrent edits to different regions both survive and never enqueue a conflict. + - Network sync over HTTP (§6.1, §12, slice 9a): `hephd --mode server` exposes a sync hub (`POST /sync/push`, `GET /sync/pull?after=`, axum) over the same store; `hephd --mode local --hub-url ` becomes a spoke that background-syncs its op-log with that hub (and on demand via the `sync.now`/`sync.status` RPC). Exchange is incremental by HLC cursor (`sync_state`) and idempotent. The merge engine is `heph-core`'s, unchanged. Unauthenticated/single-owner for now (auth lands with OIDC). `conflicts.list`/`conflicts.resolve` are now reachable over the daemon socket. + - Client mode (§3.1, slice 9b): `hephd --mode client --server-url ` runs with no local replica, proxying every store call to a server's `POST /rpc` endpoint (the full daemon API over HTTP). The daemon is now backend-agnostic (`local`/`server` front a `LocalStore`, `client` a `RemoteStore`), so surfaces see the same unix-socket API in every mode. + - Hub authentication (§13, slice 10a): the sync hub now verifies an OIDC bearer token on `/sync/*` and `/rpc` — RS256-pinned JWT validation with exact issuer/audience, expiry, and a required subject; JWKS discovered and cached, refetched on key rotation (`jsonwebtoken`). Enabled with `hephd --mode server --oidc-issuer --oidc-audience ` (open when unset, for local dev). A single-tenant owner gate binds the hub to the first authenticated identity and rejects any other. Verification sits behind a `TokenVerifier` trait, so it's tested entirely offline (stub middleware + an adversarial battery against an in-process mock IdP). + - Client authentication (§13, slice 10b): `heph auth login --hub-url --issuer --client-id ` runs the OAuth 2.0 device-code flow and caches the token in the OS keyring; spokes and `client` mode attach it to hub requests, refreshing on expiry (`--oidc-issuer`/`--oidc-client-id`). Offline-tested against a mock OAuth server and a full spoke-to-authenticated-hub loop. (Auth/proxy HTTP uses the runtime-free `ureq`, since `reqwest::blocking` is unsafe inside the async daemon.) + - CI runs the Rust suite (fmt/clippy/test) via the project build hook. + - `heph.nvim` slice 11a (§8) — the primary surface begins: a Neovim plugin that is a thin client of the local `hephd` over its unix socket. A `vim.uv` JSON-RPC client (blocking `call` via `vim.wait`, id-demuxed, partial-line buffered, JSON `null`→Lua `nil`); buffer-backed nodes (`heph://node/` with `BufReadCmd`→`node.get` / `BufWriteCmd`→`node.update`, whole-buffer body round-tripping exactly through the CRDT); `[[wiki-link]]` follow on `` via a new exact `node.resolve {title}` RPC (alias-then-title, the same mapping that materializes `wiki` links — unresolved links allowed); the daily journal (`:Heph today`); and the `:Heph` command surface. Headless e2e (§9) drives the plugin against a real daemon over a temp socket with a self-contained busted-style runner (no external plugins, no network): journal round-trip, follow-link, and link-two-docs/backlink. + - `heph.nvim` slice 11b (§8) — task views: `list` is enriched to return titled rows (the same shape as `next`, with the canonical-context id) so the Organizational survey needs no per-row `node.get`. The plugin gains the Tactical **`:Heph next`** and Organizational **`:Heph list`** views (`` opens a task's canonical-context doc), task **capture**, **set-attention**, **done/drop**, **skip**, and per-task **`log`** append — each resolving "the current task" from the buffer (a task node, or a context doc via its `canonical-context` backlink). A `vim.ui.select` picker (Telescope auto-upgrade when installed) backs `:Heph search`/`capture`/`attention`. Headless e2e adds the capture→next→context→checklist→done workflow and the recurring fresh-checklist workflow (completing a recurring task rolls it forward and the next occurrence presents an all-unchecked checklist). + - `heph.nvim` slice 11c (§8) — promotion + CI: `task.promote` mints a committed task from a `- [ ]` context-item line (addressed by its 1-based index) and rewrites that line into a `[[link]]` to the new task; `:Heph promote` does this for the line under the cursor. Wiki-link resolution now excludes a task's canonical-context doc, so `[[Task Title]]` resolves to the task itself (not its identically-titled context doc). The headless e2e suite runs in CI via a Dagger function that bakes a pinned, arch-detected Neovim onto a Rust image and runs the same self-contained suite developers run natively with `mise run test-nvim`; the runner fails on a zero-spec discovery so a misconfigured path can't pass silently. + - `heph.nvim` managed daemon — plug-and-play by default: `require("heph").setup({})` spawns and supervises a local `hephd` against the default paths when none is running, kills only the daemon it spawned on exit, and self-heals (respawns + reconnects if the daemon dies mid-session). A daemon you started yourself (a `server`/`client` architecture, or a service) is always respected — the plugin only spawns when nothing is serving the socket; with `autostart = false` it connects only and warns if unreachable. `$HEPH_SOCKET` / `$HEPH_DB` isolate a development Neovim onto a separate daemon + DB. + - `heph.nvim` follow-or-create: pressing `` on a `[[wiki-link]]` whose target doesn't exist yet now **creates** a doc with that title and opens it (the zettelkasten gesture), materializing the source's backlink — so you can link a journal entry to a brand-new note in one keystroke. Plus `:Heph doc ` to create a standalone wiki entry, and `:Heph home` — a single designated landing/index page (open-or-create by title, configurable via `opts.home`) to grow a map of content around. `:Heph journals` opens a recent-days picker (preview existing days, `@create` for new ones; count via `opts.journal_days`, default 7) — the dailies workflow. Pickers (Telescope) now support a preview pane. The `:Heph next`/`list` views are interactive: `<CR>` opens a task's context, `a` adds a task (prompt title + attention), `d` marks the task under the cursor done, `r` refreshes — with a dimmed key hint shown above the list. + - Dev/installed isolation tooling: a `mise run dev` task runs the working-tree `hephd` on isolated `.dev/` paths, and a how-to ([[install-heph]]) covers installing `heph`/`hephd` from the forge (build-from-source), the lazy.nvim plugin setup, and pointing a dev Neovim at the dev daemon via `$HEPH_SOCKET`/`$HEPH_DB` so it never touches the installed store. + - CLI as a complete task surface (§1, §6.2.1): `heph` now implements the entire daemon API and is the task capture/scripting surface. Structured fields are flags with **human dates** (`--do-date tomorrow|+3d|fri|YYYY-MM-DD`, shown back compactly in `next`/`list`) and **recurrence** (`--recur` presets/natural-language like "every 3 days", or a raw `--rrule`). New verbs: `list`, `done`/`drop`/`skip`, `attention`, `edit` (reschedule do-date/late-on/recurrence, re-attention, re-file — backed by the new `task.set_schedule` RPC), `promote`, `show`, `log` (append or tail), `health`, `node update`/`rm`, `resolve`, `links`/`backlinks`, `link add`, `project add [--parent]`, `sync [--status]`, `conflicts [resolve]`. Projects are referenced by name. Date/recurrence parsing is unit-tested; the new verbs have real-socket process tests. + - Daemon lifecycle is now an explicit OS service, and all surfaces are connect-only (no more auto-spawn). `heph daemon start/stop/restart/status/uninstall` idempotently manages a launchd agent (macOS) or systemd user service (Linux) that runs `hephd` on your default store; `heph.nvim` no longer spawns or supervises a daemon — it just connects and points you at `heph daemon start` if none is running. Rationale: once the CLI became a first-class surface, a daemon owned by one surface couldn't be shared (see [[run-the-daemon]], [[design]] §4). + - Filter views (§8.2) — saved agenda slices, so the agenda isn't one flat list. `heph view <name>` runs a built-in view (`tom` Top of Mind, `ondeck` On Deck, `chores`, `work` Work Tasks, `tasks`) seeded from the owner's Todoist filter queries; `heph view` with no name lists them, and `:Heph view <name>` does the same in Neovim. Under the hood, `list` now takes a `ListFilter` predicate-as-data (attention include/exclude sets, project-subtree scope, project exclusions, an actionable do-date gate), and views resolve project names to ids and expand each to its `parent`-link subtree. The Schedule view is intentionally omitted (time-of-day isn't modeled on date-grained do-dates). + - `heph-tui` (§8.1) — a terminal task agenda/triage UI, the primary surface for working a large task set (the §6.2.1 Todoist study showed triage, not single edits, dominates). A `ratatui` app, thin client of the daemon socket. Three panes: a sidebar of the five filter views + your projects, an attention-colored task list with compact human do/late dates, and a preview of the highlighted task's context doc + recent log. Triage from the keyboard: `a` add (guided title → attention → do-date, filed under the selected project), `x` done, `s` skip, `d` drop, `A` cycle attention, `b` push to On Deck, `e` reschedule the do-date; `o` opens the task's context doc in your nvim (live, via heph.nvim) and returns. `j/k` move, `Tab`/`h`/`l` switch panes, `r` refresh, `q` quit. Run it with `heph-tui` (honors `--socket` / `$HEPH_SOCKET`). `a` is a Todoist-style single-line quick-add: `Buy milk tomorrow p2 #Work every week` parses into title + attention (p1–p4) + do-date + recurrence + project (multi-word project names match greedily; an unresolved `#tag` just stays in the title). `/` runs a full-text search whose results overlay the task list; Enter opens a hit (a task at its context doc) in nvim. + - Move-to-project (§8.1): a new `task.set_project` RPC re-files a task under another project (or unfiles it) with OR-set link semantics — the old `in-project` link is tombstoned and a new one added, so a task is never filed under two projects at once. In `heph-tui`, **`m`** opens a list-pick overlay ("(Unfile)" then every project) on the highlighted task. `heph edit <task> --project <name>` now routes through the same RPC (fixing a bug where re-filing piled on a duplicate link), and `--project none` unfiles the task. This closes the last Todoist-parity capture gap. + - `heph-tui` task-list visuals (§8.1): each row now leads with an attention **flag** (`⚑`, colored red/orange/blue; blank for white) and a **project-colored bullet** — the bullet's color is derived stably from the project id (so it survives projects being added/removed), letting you scan a mixed list by project at a glance. The list also grows a **scrollbar** and keeps the selected task scrolled into view when there are more tasks than fit. + - `heph-tui`: **`<Enter>`** opens the selected task's context editor in nvim (from the sidebar it first drills into the task list); the old `o` binding is retired. The view sidebar / `heph view` order is now **Top of Mind, Tasks, Work Tasks, Chores, On Deck**. + - `heph-tui` sort toggle (§8.1): **`s`** flips the task list between two orders — **default** (attention → most-overdue → project → creation) and **by-project** (grouped under dimmed `──── Project ────` separators, then the same sub-order). The view's filter still applies first. (To free `s`, **skip** moved to **`S`**.) + - `heph.nvim` task-view rows (§8): `:Heph next`/`:Heph list` rows now show a compact **do/late date chip** (and a recurrence `↻`), so you can see scheduling at a glance; `<CR>` still jumps to a task's context doc. + - Wiki-links by node id (§8.4): node resolution is now **id-first** (`[[NODEID]]` resolves to its node ahead of any name match, so links can't be shadowed by a like-named node), and heph.nvim grows a **`[[` picker** — type `[[` (or `:Heph link`) to pick a node and insert a canonical `[[NODEID]]` link. With Telescope it's a **live fuzzy filter over your linkable nodes with a preview pane** (the node's body, or a task's context doc) — the list shows first-class targets only (a task appears once; its internal context/log docs and tag nodes are hidden, via the new `node.linkable` query) — type to narrow, Enter to insert, `<C-x>` to create a doc named what you've typed; without Telescope it falls back to a search-then-select prompt. Following such a link (`<CR>`) jumps straight by id. Those id links are kept **readable**: on read a bare `[[NODEID]]` is expanded to `[[NODEID|Current Name]]` (so it follows renames, in both the nvim buffer and the TUI preview), and on save it collapses back to the canonical bare id — a custom `|label` you write is preserved as an override. In the editor the id is **concealed** — a link renders as just its name, styled like a hyperlink, with the raw `[[id|Name]]` revealed on the line your cursor is on. Legacy `[[Name]]` links keep working, and **`heph migrate-links`** rewrites them to the canonical id form in one pass when you're ready (idempotent). + - Frontmatter editing in heph.nvim (§8.3): opening a node now shows an editable **YAML frontmatter** block on top of the body (`id`/`kind`/`title`/`tags`, and for a task or its context doc the task's `state`/`attention`/`do_date`/`late_on`/`recurrence`/`project`). On save, the plugin diffs the block and issues the right RPC per changed field — rename, set-attention, reschedule (dates as `YYYY-MM-DD`), move-to-project (by name), and tag add/remove — then saves the body; the store strips the block so it never persists. A mistyped `state` surfaces a validation error; a buffer with no block changes no metadata (so deleting the block can't wipe your tags). Inline **`#hashtags`** typed in the body are also added as tags on save (a `# heading` doesn't count) and are rendered in **italics** so they stand out. Link-follow and promotion are unaffected (they're content-relative, not line-absolute). + - Frontmatter projection (§8.3): a node can now be fetched with an editable **YAML frontmatter** block prepended — `node.get {frontmatter: true}` renders `id`/`kind`/`title`/`tags`, and for a task (or its context doc) the owning task's `state`/`attention`/`do_date`/`late_on`/`recurrence`/`project` plus a `task:` ref. Dates are local `YYYY-MM-DD`. On write, the store **strips and ignores** any leading frontmatter (conservatively — a real `---` hrule in prose survives) before the CRDT diff, so frontmatter never persists and an unchanged read→write is a no-op; a naive editor can't corrupt metadata. This is the read/write groundwork for editing a node's metadata as frontmatter in heph.nvim (the diff-into-RPCs layer is next). + - Tags (§4, §8.3): nodes can now be **tagged**. A tag is a `tag`-kind node whose id is deterministic in `(owner, name)`, so the same name is **one canonical tag** shared across everything it's applied to (and replicas converge — no duplicate tags). Tagging is an OR-set link, so adding/removing is idempotent and merge-safe. Surfaced as `tag.add`/`tag.remove`/`tag.list` RPCs and `heph tag add|rm|list` (list a node's tags, or every tag with no node). Tag names are trimmed; a canonical case/spelling normalization is deferred to the future zk import. This is the groundwork for the `tags:` line of the upcoming frontmatter edit surface. +- - `heph-quickadd` (§8) — a **global quick-capture popover**, the last piece needed to retire Todoist. A tiny always-warm `eframe`/`egui` agent registers **⌘'** system-wide (`global-hotkey`, via Carbon — no Accessibility permission) and, on press, *toggles an already-created hidden window visible and focuses a single capture field* — never spawning on the keypress, so it's a muscle-reflex ([[design]] §6.2.1). The field reuses the shared `quickadd::parse` (lifted from `heph-tui` into `hephd`'s lib so both surfaces share one parser): one line like `Call dentist fri p1 #Health` parses live into title + attention (`p1`–`p4`) + do-date + recurrence + project, shown as a chip row (⚑ attention · 📁 project · ⏰ do-date · ↻ recurrence) as you type. **Enter** saves **optimistically** — the window hides instantly and `task.create` runs on a background thread, so perceived latency is just the keystroke; a failed RPC re-shows the window with the text restored, so a capture is never lost. **Esc** dismisses. + - Supervision (no second launch agent): **hephd supervises `heph-quickadd`** as a child in local mode on macOS (opt-in via `HEPH_QUICKADD=1`, which the installed launchd plist now sets — dev/test runs that spawn a local daemon never pop a window). hephd already runs as a `gui/$uid` LaunchAgent, so the child inherits the Aqua session the hotkey/GUI need, and the user still installs/manages exactly one service (`heph daemon`). The helper **self-exits when orphaned** (its parent daemon gone) and exits if ⌘' is already held, so killing hephd — even `kill -9` — leaves nothing behind and duplicates never stack. Runs as a macOS **accessory** app (no Dock icon). macOS-only for v1; `global-hotkey` + `egui` also run on Linux/X11, so the Linux port is mostly window-behavior polish. +- - `heph-tui` move-to-project picker (`m`) is now an **fzf-style filter** (§8.1): a prompt line on top narrows the project list by fuzzy subsequence match as you type (↑/↓ or Ctrl-n/p move, Enter selects, Esc cancels) — no more scrolling the full list. When the typed text names no existing project, a **"+ New project"** row is offered: selecting it **creates the project and files the task under it** in one step (via `node.create`), so projects can be created without leaving the TUI. +- - `heph-tui` safety + undo wave (§8.1): + - **Pane-specific keys.** Task-triage gestures (`x`/`d`/`S`/`A`/`b`/`e`/`m`/`D`) now fire **only when the task pane is focused**, so a stray keypress while navigating the sidebar can no longer drop or delete a task. The sidebar gets its own actions; the status-line hints are now focus-aware. + - **Undo / redo.** **`u`** undoes the last triage action (drop, done, skip, attention, move) and **Ctrl-z** redoes it, restoring the task's prior state from a snapshot — multi-level, capped at 200 steps. (Tombstone-delete stays excluded — it keeps its y/N prompt — and an attention of "none" can't be re-cleared.) + - **Delete a project** from the sidebar with **`D`** (y/N confirm); its tasks become unfiled (they move to the Inbox), not deleted. + - **Sidebar auto-refreshes** after creating or deleting a project, so a new project shows up immediately (no more quit-and-reload). + +### Bug Fixes + +- Fix `clippy::single_match` lint in heph-tui sidebar key handling (CI failure on main). +- Pin the Quartz docs build to v4.5.2. The Dagger `build_docs` pipeline cloned Quartz from the default branch unpinned; Quartz v5.0.0 restructured its config layout (`.quartz/plugins`, `../quartz` imports) and broke the docs build against our existing `quartz.config.ts`/`quartz.layout.ts`. + +### Infrastructure + +- Extracted the Neovim plugin into its own forge repo, [eblume/hephaestus.nvim](ssh://forgejo@forge.ops.eblu.me:2222/eblume/hephaestus.nvim.git). Removed `heph.nvim/` from the monorepo along with its build/test wiring: the `test_nvim` Dagger function, the `dagger call test-nvim` CI step, the `mise run test-nvim` task, and the `.stylua.toml` + stylua prek hook (no Lua remains here). The CLI/TUI→nvim integration is unchanged (they shell out to `nvim` expecting the plugin installed). Install now uses a plain lazy.nvim spec pointing at the plugin repo — see [[install-heph]]. +- Pre-v1 dependency-refresh sweep: bumped all external crates to latest stable — `keyring` 3→4 (the new `keyring_core` split + register-the-native-store model), `rusqlite` 0.32→0.40, `ratatui` 0.29→0.30, `rrule` 0.13→0.14, `yrs` 0.26→0.27, plus semver-compatible updates across the rest. Dropped the `fs4` dependency in favor of `std::fs::File::try_lock` (stable since Rust 1.89), raising the workspace MSRV to 1.89. Removed the orphaned `.forgejo/scripts/build` hook (CI calls Dagger directly). Full suite green: 228 Rust tests + 25 heph.nvim headless e2e specs, `clippy -D warnings` + `fmt` clean. +- Hub auth now **resolves an OIDC `sub` to an `owner_id`** (`Store::resolve_owner → Option<owner_id>`) instead of a single-tenant boolean gate (`authorize_owner_sub → bool`). Behavior is unchanged for the single-owner hub (claim-on-first; a stranger's token still 403s), but the contract no longer assumes one global owner — this is the multi-tenancy seam, so serving N owners later is additive rather than a rewrite. See the `Adoption + multi-tenant` task's context for the full decision. +- - **`cargo fmt` is now a prek hook** — `cargo fmt --all` runs in place over the workspace on any staged `.rs` change (grouped with the other in-place formatters), so unformatted Rust can't be committed locally. CI already enforced `cargo fmt --check` via `dagger call check`; this catches it at commit time instead. +- - Lua is now formatted by **stylua** (a `.stylua.toml` + a `stylua-system` prek hook), so heph.nvim's whitespace is enforced by an opinionated formatter rather than by hand. + - `mise run import-todoist` — a one-way importer that seeds a heph store from your Todoist projects + active tasks (project hierarchy, priority→attention, do-dates, natural-language recurrence, descriptions + sub-tasks as context items). Dry-run by default; `-- --commit` writes into your real store after backing it up. See [[import-todoist]]. +- Add a `cargo clippy -D warnings` pre-commit hook so lints are caught locally before CI. +- Customize the repository from the `project-template`: rename the Dagger module to `hephaestus_ci` (`HephaestusCi`), set the docs `baseUrl`, add an All-Rights-Reserved `LICENSE`, and update `README.md`/`AGENTS.md` for the hephaestus project. +- Slimmed the credential-keyring dependency to cut CI compile time. keyring 4's `keyring` meta-crate compiles *every* platform backend for the target — on Linux that pulled the zbus async stack, a redundant libdbus secret-service, the kernel keyutils store, a SQLite/zstd `db-keystore`, and OpenSSL (~290 crates in its subtree). Replaced it with `keyring-core` (the API) plus a single store per OS — macOS Keychain (`apple-native-keyring-store`), Linux Secret Service (`dbus-secret-service-keyring-store`, pure-Rust crypto, vendored libdbus so the build needs no system `libdbus-1-dev`) — registered directly in `oauth.rs`. hephd's Linux dependency graph drops from **401 to 235 crates** (−166), removing the zbus stack and two C builds. Runtime behavior is unchanged. + +### Documentation + +- New how-to: [[set-up-sync-hub]] — stand up the canonical hub and connect an existing local device as an offline-capable spoke, the data-safe way (the hub adopts the device's identity rather than rewriting the device). +- - Added a [[task-lifecycle]] explanation doc: the **two-axis task model** (lifecycle state — outstanding/done/dropped — kept separate from attention white/orange/red/blue), why **On Deck (blue) is not the same as dropped**, what **delete/tombstone** does (and that it keeps the context doc), and where each task shows up across the agenda, full-text search, and export. + - Surface strategy revised to a **three-surface model** ([[design]] §4, tech-spec §1/§8), grounded in a study of the owner's live Todoist usage (387 active tasks, 34 hierarchical projects — [[design]] §6.2.1): the **CLI** is task capture/scripting plus the complete daemon API, a planned **`heph-tui`** terminal UI (tech-spec §8.1) is the primary task agenda/triage surface, and **`heph.nvim`** is the primary context/knowledge-base surface. The study also confirms do-date-not-due-date (zero deadlines used), that task descriptions are already full of unresolved `[[wiki-links]]` (the exact task↔KB fusion heph provides), and surfaces new needs (project hierarchy, natural-language recurrence) while showing tags are negligible. +- - **heph self-hosts its roadmap (the "bootstrap lift").** With v1 at Todoist feature-parity, remaining/future work is now tracked **in heph itself** — as tasks in the `Hephaestus` project (`heph view ondeck`) — rather than in a document. The tech-spec is reframed accordingly: `tech-spec.md` → **`v1-prototype-tech-spec.md`** (all wiki-links + paths updated), retitled and bannered as a **historical** record of the v1 build, with its §14 tracker frozen. `AGENTS.md` gains a *Planning future work* section (capture via `heph task "…" --project Hephaestus`, triage in `heph-tui` On Deck); `README.md`'s status reflects parity + the three daily-driver surfaces. The living **[[design]]** doc remains the rationale of record. +- Add the project design document (`docs/explanation/design.md`, rationale + decision history) and the distilled technical specification (`docs/reference/v1-prototype-tech-spec.md`, the build artifact) defining hephaestus as a unified, self-hosted, **client/server + offline-first** task + knowledge-base system: typed node graph, the lived priority discipline ("what is next?"), recurrence with fresh-per-occurrence checklists, op-log/CRDT sync with conflict resolution, OIDC/Authentik auth, the heph.nvim surface, and a test-driven development strategy. +- Second-pass review of the v1 design + tech spec before Phase 1: resolved context-item storage (Fork A index model), recurrence (roll-forward in place), key-unique node identity (deterministic ids for journal/tag), mode/sync orthogonality (`hub_url` spoke), the local-only owner / OIDC adoption path, the "what is next?" ranking (do-date as candidacy filter only, late-on as the sole urgency signal), and the plugin-side mode model (added `list` + `log.tail`). diff --git a/docs/changelog.d/+clippy-prek-hook.infra.md b/docs/changelog.d/+clippy-prek-hook.infra.md deleted file mode 100644 index a3ed71b..0000000 --- a/docs/changelog.d/+clippy-prek-hook.infra.md +++ /dev/null @@ -1 +0,0 @@ -Add a `cargo clippy -D warnings` pre-commit hook so lints are caught locally before CI. diff --git a/docs/changelog.d/+fix-clippy-single-match.bugfix.md b/docs/changelog.d/+fix-clippy-single-match.bugfix.md deleted file mode 100644 index 0548d68..0000000 --- a/docs/changelog.d/+fix-clippy-single-match.bugfix.md +++ /dev/null @@ -1 +0,0 @@ -Fix `clippy::single_match` lint in heph-tui sidebar key handling (CI failure on main). diff --git a/docs/changelog.d/+keyring-slim.infra.md b/docs/changelog.d/+keyring-slim.infra.md deleted file mode 100644 index 49aabd1..0000000 --- a/docs/changelog.d/+keyring-slim.infra.md +++ /dev/null @@ -1 +0,0 @@ -Slimmed the credential-keyring dependency to cut CI compile time. keyring 4's `keyring` meta-crate compiles *every* platform backend for the target — on Linux that pulled the zbus async stack, a redundant libdbus secret-service, the kernel keyutils store, a SQLite/zstd `db-keystore`, and OpenSSL (~290 crates in its subtree). Replaced it with `keyring-core` (the API) plus a single store per OS — macOS Keychain (`apple-native-keyring-store`), Linux Secret Service (`dbus-secret-service-keyring-store`, pure-Rust crypto, vendored libdbus so the build needs no system `libdbus-1-dev`) — registered directly in `oauth.rs`. hephd's Linux dependency graph drops from **401 to 235 crates** (−166), removing the zbus stack and two C builds. Runtime behavior is unchanged. diff --git a/docs/changelog.d/+pin-quartz-v4.bugfix.md b/docs/changelog.d/+pin-quartz-v4.bugfix.md deleted file mode 100644 index e073bbb..0000000 --- a/docs/changelog.d/+pin-quartz-v4.bugfix.md +++ /dev/null @@ -1 +0,0 @@ -Pin the Quartz docs build to v4.5.2. The Dagger `build_docs` pipeline cloned Quartz from the default branch unpinned; Quartz v5.0.0 restructured its config layout (`.quartz/plugins`, `../quartz` imports) and broke the docs build against our existing `quartz.config.ts`/`quartz.layout.ts`. diff --git a/docs/changelog.d/+project-design.doc.md b/docs/changelog.d/+project-design.doc.md deleted file mode 100644 index b790809..0000000 --- a/docs/changelog.d/+project-design.doc.md +++ /dev/null @@ -1 +0,0 @@ -Add the project design document (`docs/explanation/design.md`, rationale + decision history) and the distilled technical specification (`docs/reference/v1-prototype-tech-spec.md`, the build artifact) defining hephaestus as a unified, self-hosted, **client/server + offline-first** task + knowledge-base system: typed node graph, the lived priority discipline ("what is next?"), recurrence with fresh-per-occurrence checklists, op-log/CRDT sync with conflict resolution, OIDC/Authentik auth, the heph.nvim surface, and a test-driven development strategy. diff --git a/docs/changelog.d/+template-setup.infra.md b/docs/changelog.d/+template-setup.infra.md deleted file mode 100644 index f99c513..0000000 --- a/docs/changelog.d/+template-setup.infra.md +++ /dev/null @@ -1 +0,0 @@ -Customize the repository from the `project-template`: rename the Dagger module to `hephaestus_ci` (`HephaestusCi`), set the docs `baseUrl`, add an All-Rights-Reserved `LICENSE`, and update `README.md`/`AGENTS.md` for the hephaestus project. diff --git a/docs/changelog.d/+v1-spec-second-pass.doc.md b/docs/changelog.d/+v1-spec-second-pass.doc.md deleted file mode 100644 index 2348847..0000000 --- a/docs/changelog.d/+v1-spec-second-pass.doc.md +++ /dev/null @@ -1 +0,0 @@ -Second-pass review of the v1 design + tech spec before Phase 1: resolved context-item storage (Fork A index model), recurrence (roll-forward in place), key-unique node identity (deterministic ids for journal/tag), mode/sync orthogonality (`hub_url` spoke), the local-only owner / OIDC adoption path, the "what is next?" ranking (do-date as candidacy filter only, late-on as the sole urgency signal), and the plugin-side mode model (added `list` + `log.tail`). diff --git a/docs/changelog.d/extract-heph-nvim.infra.md b/docs/changelog.d/extract-heph-nvim.infra.md deleted file mode 100644 index 5c69204..0000000 --- a/docs/changelog.d/extract-heph-nvim.infra.md +++ /dev/null @@ -1 +0,0 @@ -Extracted the Neovim plugin into its own forge repo, [eblume/hephaestus.nvim](ssh://forgejo@forge.ops.eblu.me:2222/eblume/hephaestus.nvim.git). Removed `heph.nvim/` from the monorepo along with its build/test wiring: the `test_nvim` Dagger function, the `dagger call test-nvim` CI step, the `mise run test-nvim` task, and the `.stylua.toml` + stylua prek hook (no Lua remains here). The CLI/TUI→nvim integration is unchanged (they shell out to `nvim` expecting the plugin installed). Install now uses a plain lazy.nvim spec pointing at the plugin repo — see [[install-heph]]. diff --git a/docs/changelog.d/pre-v1-cleanup.infra.md b/docs/changelog.d/pre-v1-cleanup.infra.md deleted file mode 100644 index 4b0b7a2..0000000 --- a/docs/changelog.d/pre-v1-cleanup.infra.md +++ /dev/null @@ -1 +0,0 @@ -Pre-v1 dependency-refresh sweep: bumped all external crates to latest stable — `keyring` 3→4 (the new `keyring_core` split + register-the-native-store model), `rusqlite` 0.32→0.40, `ratatui` 0.29→0.30, `rrule` 0.13→0.14, `yrs` 0.26→0.27, plus semver-compatible updates across the rest. Dropped the `fs4` dependency in favor of `std::fs::File::try_lock` (stable since Rust 1.89), raising the workspace MSRV to 1.89. Removed the orphaned `.forgejo/scripts/build` hook (CI calls Dagger directly). Full suite green: 228 Rust tests + 25 heph.nvim headless e2e specs, `clippy -D warnings` + `fmt` clean. diff --git a/docs/changelog.d/v1-hub-prep.doc.md b/docs/changelog.d/v1-hub-prep.doc.md deleted file mode 100644 index 08c92f0..0000000 --- a/docs/changelog.d/v1-hub-prep.doc.md +++ /dev/null @@ -1 +0,0 @@ -New how-to: [[set-up-sync-hub]] — stand up the canonical hub and connect an existing local device as an offline-capable spoke, the data-safe way (the hub adopts the device's identity rather than rewriting the device). diff --git a/docs/changelog.d/v1-hub-prep.infra.md b/docs/changelog.d/v1-hub-prep.infra.md deleted file mode 100644 index e2a9ad4..0000000 --- a/docs/changelog.d/v1-hub-prep.infra.md +++ /dev/null @@ -1 +0,0 @@ -Hub auth now **resolves an OIDC `sub` to an `owner_id`** (`Store::resolve_owner → Option<owner_id>`) instead of a single-tenant boolean gate (`authorize_owner_sub → bool`). Behavior is unchanged for the single-owner hub (claim-on-first; a stranger's token still 403s), but the contract no longer assumes one global owner — this is the multi-tenancy seam, so serving N owners later is additive rather than a rewrite. See the `Adoption + multi-tenant` task's context for the full decision. diff --git a/docs/changelog.d/v1-inbox-view.feature.md b/docs/changelog.d/v1-inbox-view.feature.md deleted file mode 100644 index 3683314..0000000 --- a/docs/changelog.d/v1-inbox-view.feature.md +++ /dev/null @@ -1 +0,0 @@ -- **Inbox view** (§8.2): a sixth built-in filter view, listed below On Deck, showing every outstanding task with **no project** — the capture inbox to triage and file. A new `ListFilter.unfiled` predicate (kept purely in `matches()`) drives it; the `inbox` `ViewSpec` is deliberately un-gated (no attention or do-date filter) so nothing hides from triage. Appears automatically in the `heph-tui` sidebar and `heph view`; `heph view inbox` from the CLI. diff --git a/docs/changelog.d/v1-list-project.feature.md b/docs/changelog.d/v1-list-project.feature.md deleted file mode 100644 index a8d7f2a..0000000 --- a/docs/changelog.d/v1-list-project.feature.md +++ /dev/null @@ -1 +0,0 @@ -- **`heph list --project <name>` + `--json`** (§8.2): list a project's outstanding tasks by **name** (subtree-expanded, resolved server-side via a new `project.scope` path that reuses the view machinery — errors loudly on an unknown name), and `--json` prints the raw rows (`node_id`, `canonical_context_id`, attention/state/do_date/late_on/recurrence/project_id) for scripting and agents. This is the canonical "show me a project's outstanding work" command — `AGENTS.md` documents it as how to inspect heph's own roadmap (the `Hephaestus` project), now that heph self-hosts it. diff --git a/docs/changelog.d/v1-prek-cargo-fmt.infra.md b/docs/changelog.d/v1-prek-cargo-fmt.infra.md deleted file mode 100644 index bfe9c18..0000000 --- a/docs/changelog.d/v1-prek-cargo-fmt.infra.md +++ /dev/null @@ -1 +0,0 @@ -- **`cargo fmt` is now a prek hook** — `cargo fmt --all` runs in place over the workspace on any staged `.rs` change (grouped with the other in-place formatters), so unformatted Rust can't be committed locally. CI already enforced `cargo fmt --check` via `dagger call check`; this catches it at commit time instead. diff --git a/docs/changelog.d/v1-prototype.doc.md b/docs/changelog.d/v1-prototype.doc.md deleted file mode 100644 index 6ae23cc..0000000 --- a/docs/changelog.d/v1-prototype.doc.md +++ /dev/null @@ -1,2 +0,0 @@ -- Added a [[task-lifecycle]] explanation doc: the **two-axis task model** (lifecycle state — outstanding/done/dropped — kept separate from attention white/orange/red/blue), why **On Deck (blue) is not the same as dropped**, what **delete/tombstone** does (and that it keeps the context doc), and where each task shows up across the agenda, full-text search, and export. -- Surface strategy revised to a **three-surface model** ([[design]] §4, tech-spec §1/§8), grounded in a study of the owner's live Todoist usage (387 active tasks, 34 hierarchical projects — [[design]] §6.2.1): the **CLI** is task capture/scripting plus the complete daemon API, a planned **`heph-tui`** terminal UI (tech-spec §8.1) is the primary task agenda/triage surface, and **`heph.nvim`** is the primary context/knowledge-base surface. The study also confirms do-date-not-due-date (zero deadlines used), that task descriptions are already full of unresolved `[[wiki-links]]` (the exact task↔KB fusion heph provides), and surfaces new needs (project hierarchy, natural-language recurrence) while showing tags are negligible. diff --git a/docs/changelog.d/v1-prototype.feature.md b/docs/changelog.d/v1-prototype.feature.md deleted file mode 100644 index fb2854f..0000000 --- a/docs/changelog.d/v1-prototype.feature.md +++ /dev/null @@ -1,35 +0,0 @@ -Begin the v1 prototype (Phase 1, tech-spec §11.1), built in TDD slices: - -- Cargo workspace + `heph-core` crate; migration-run SQLite schema (§4.5); clock-injected `Store` trait + `LocalStore` node create/get; single local-user bootstrap. -- Markdown extraction (§5): `[[wiki-links]]` and GFM `- [ ]` checkbox context-items derived purely and idempotently from a body, skipping code blocks. -- Committed tasks (§4.3, §6): `task.create` auto-creates the canonical context `doc` + `canonical-context` link; attention/do-date/late-on/state/recurrence columns; set-state/set-attention. Links CRUD (outgoing/backlinks). A body update reconciles `wiki` links (diff-based, resolved by alias/title, idempotent). -- "What is next?" ranking (§7): pure, clock-injected, two-stage engine — candidacy filter (do-date as a boolean gate only) then a reorderable list of named dimensions (past-late-on → overdue-amount → attention band → FIFO). `late_on` is the sole urgency signal; blue hidden; red always shown. Proptest-checked total order. `Store::next` surfaces it over SQLite. -- Recurrence — roll-forward in place (§4.4): completing a recurring task resets its checklist to all-unchecked, logs the occurrence, and advances the do-date to the next RRULE instance after now (skipping misses) — completion never carries forward (proptest-checked). Per-task append-only logs (`log-of`) with `log.append`/`log.tail`; `skip` advances without logging. -- `hephd` daemon, local mode (§3, §6): exclusive file lock (handoff-ready), line-delimited JSON-RPC over a unix socket exposing the node/task/next/links/log methods, with DB work on tokio's blocking pool. Synchronous client for surfaces/CLI. Model types are serde-serializable. -- `heph` CLI (§1) — a thin client of the daemon: `next`, `task`, `doc`, `get`, `export`. Export materializes the store to a `<kind>/<id>.md` tree with YAML frontmatter + body (§5), one-way, tombstones excluded. -- Sync engine, local-only (§12): real hybrid logical clock + persistent device `origin`; an append-only op-log per mutation; an idempotent, order-independent merge/apply engine — last-writer-wins task scalars (discards surfaced in a `conflicts` queue), OR-set links, monotonic tombstones. Two-replica convergence proven. -- Body text CRDT (§5, §12, slice 8d): node bodies now merge through the `yrs` text CRDT (`body_crdt`) instead of last-writer-wins — whole-buffer writes are diffed into the doc and the yrs delta rides the op, so concurrent edits to different regions both survive and never enqueue a conflict. -- Network sync over HTTP (§6.1, §12, slice 9a): `hephd --mode server` exposes a sync hub (`POST /sync/push`, `GET /sync/pull?after=<hlc>`, axum) over the same store; `hephd --mode local --hub-url <url>` becomes a spoke that background-syncs its op-log with that hub (and on demand via the `sync.now`/`sync.status` RPC). Exchange is incremental by HLC cursor (`sync_state`) and idempotent. The merge engine is `heph-core`'s, unchanged. Unauthenticated/single-owner for now (auth lands with OIDC). `conflicts.list`/`conflicts.resolve` are now reachable over the daemon socket. -- Client mode (§3.1, slice 9b): `hephd --mode client --server-url <url>` runs with no local replica, proxying every store call to a server's `POST /rpc` endpoint (the full daemon API over HTTP). The daemon is now backend-agnostic (`local`/`server` front a `LocalStore`, `client` a `RemoteStore`), so surfaces see the same unix-socket API in every mode. -- Hub authentication (§13, slice 10a): the sync hub now verifies an OIDC bearer token on `/sync/*` and `/rpc` — RS256-pinned JWT validation with exact issuer/audience, expiry, and a required subject; JWKS discovered and cached, refetched on key rotation (`jsonwebtoken`). Enabled with `hephd --mode server --oidc-issuer <url> --oidc-audience <client-id>` (open when unset, for local dev). A single-tenant owner gate binds the hub to the first authenticated identity and rejects any other. Verification sits behind a `TokenVerifier` trait, so it's tested entirely offline (stub middleware + an adversarial battery against an in-process mock IdP). -- Client authentication (§13, slice 10b): `heph auth login --hub-url <url> --issuer <url> --client-id <id>` runs the OAuth 2.0 device-code flow and caches the token in the OS keyring; spokes and `client` mode attach it to hub requests, refreshing on expiry (`--oidc-issuer`/`--oidc-client-id`). Offline-tested against a mock OAuth server and a full spoke-to-authenticated-hub loop. (Auth/proxy HTTP uses the runtime-free `ureq`, since `reqwest::blocking` is unsafe inside the async daemon.) -- CI runs the Rust suite (fmt/clippy/test) via the project build hook. -- `heph.nvim` slice 11a (§8) — the primary surface begins: a Neovim plugin that is a thin client of the local `hephd` over its unix socket. A `vim.uv` JSON-RPC client (blocking `call` via `vim.wait`, id-demuxed, partial-line buffered, JSON `null`→Lua `nil`); buffer-backed nodes (`heph://node/<id>` with `BufReadCmd`→`node.get` / `BufWriteCmd`→`node.update`, whole-buffer body round-tripping exactly through the CRDT); `[[wiki-link]]` follow on `<CR>` via a new exact `node.resolve {title}` RPC (alias-then-title, the same mapping that materializes `wiki` links — unresolved links allowed); the daily journal (`:Heph today`); and the `:Heph` command surface. Headless e2e (§9) drives the plugin against a real daemon over a temp socket with a self-contained busted-style runner (no external plugins, no network): journal round-trip, follow-link, and link-two-docs/backlink. -- `heph.nvim` slice 11b (§8) — task views: `list` is enriched to return titled rows (the same shape as `next`, with the canonical-context id) so the Organizational survey needs no per-row `node.get`. The plugin gains the Tactical **`:Heph next`** and Organizational **`:Heph list`** views (`<CR>` opens a task's canonical-context doc), task **capture**, **set-attention**, **done/drop**, **skip**, and per-task **`log`** append — each resolving "the current task" from the buffer (a task node, or a context doc via its `canonical-context` backlink). A `vim.ui.select` picker (Telescope auto-upgrade when installed) backs `:Heph search`/`capture`/`attention`. Headless e2e adds the capture→next→context→checklist→done workflow and the recurring fresh-checklist workflow (completing a recurring task rolls it forward and the next occurrence presents an all-unchecked checklist). -- `heph.nvim` slice 11c (§8) — promotion + CI: `task.promote` mints a committed task from a `- [ ]` context-item line (addressed by its 1-based index) and rewrites that line into a `[[link]]` to the new task; `:Heph promote` does this for the line under the cursor. Wiki-link resolution now excludes a task's canonical-context doc, so `[[Task Title]]` resolves to the task itself (not its identically-titled context doc). The headless e2e suite runs in CI via a Dagger function that bakes a pinned, arch-detected Neovim onto a Rust image and runs the same self-contained suite developers run natively with `mise run test-nvim`; the runner fails on a zero-spec discovery so a misconfigured path can't pass silently. -- `heph.nvim` managed daemon — plug-and-play by default: `require("heph").setup({})` spawns and supervises a local `hephd` against the default paths when none is running, kills only the daemon it spawned on exit, and self-heals (respawns + reconnects if the daemon dies mid-session). A daemon you started yourself (a `server`/`client` architecture, or a service) is always respected — the plugin only spawns when nothing is serving the socket; with `autostart = false` it connects only and warns if unreachable. `$HEPH_SOCKET` / `$HEPH_DB` isolate a development Neovim onto a separate daemon + DB. -- `heph.nvim` follow-or-create: pressing `<CR>` on a `[[wiki-link]]` whose target doesn't exist yet now **creates** a doc with that title and opens it (the zettelkasten gesture), materializing the source's backlink — so you can link a journal entry to a brand-new note in one keystroke. Plus `:Heph doc <title>` to create a standalone wiki entry, and `:Heph home` — a single designated landing/index page (open-or-create by title, configurable via `opts.home`) to grow a map of content around. `:Heph journals` opens a recent-days picker (preview existing days, `@create` for new ones; count via `opts.journal_days`, default 7) — the dailies workflow. Pickers (Telescope) now support a preview pane. The `:Heph next`/`list` views are interactive: `<CR>` opens a task's context, `a` adds a task (prompt title + attention), `d` marks the task under the cursor done, `r` refreshes — with a dimmed key hint shown above the list. -- Dev/installed isolation tooling: a `mise run dev` task runs the working-tree `hephd` on isolated `.dev/` paths, and a how-to ([[install-heph]]) covers installing `heph`/`hephd` from the forge (build-from-source), the lazy.nvim plugin setup, and pointing a dev Neovim at the dev daemon via `$HEPH_SOCKET`/`$HEPH_DB` so it never touches the installed store. -- CLI as a complete task surface (§1, §6.2.1): `heph` now implements the entire daemon API and is the task capture/scripting surface. Structured fields are flags with **human dates** (`--do-date tomorrow|+3d|fri|YYYY-MM-DD`, shown back compactly in `next`/`list`) and **recurrence** (`--recur` presets/natural-language like "every 3 days", or a raw `--rrule`). New verbs: `list`, `done`/`drop`/`skip`, `attention`, `edit` (reschedule do-date/late-on/recurrence, re-attention, re-file — backed by the new `task.set_schedule` RPC), `promote`, `show`, `log` (append or tail), `health`, `node update`/`rm`, `resolve`, `links`/`backlinks`, `link add`, `project add [--parent]`, `sync [--status]`, `conflicts [resolve]`. Projects are referenced by name. Date/recurrence parsing is unit-tested; the new verbs have real-socket process tests. -- Daemon lifecycle is now an explicit OS service, and all surfaces are connect-only (no more auto-spawn). `heph daemon start/stop/restart/status/uninstall` idempotently manages a launchd agent (macOS) or systemd user service (Linux) that runs `hephd` on your default store; `heph.nvim` no longer spawns or supervises a daemon — it just connects and points you at `heph daemon start` if none is running. Rationale: once the CLI became a first-class surface, a daemon owned by one surface couldn't be shared (see [[run-the-daemon]], [[design]] §4). -- Filter views (§8.2) — saved agenda slices, so the agenda isn't one flat list. `heph view <name>` runs a built-in view (`tom` Top of Mind, `ondeck` On Deck, `chores`, `work` Work Tasks, `tasks`) seeded from the owner's Todoist filter queries; `heph view` with no name lists them, and `:Heph view <name>` does the same in Neovim. Under the hood, `list` now takes a `ListFilter` predicate-as-data (attention include/exclude sets, project-subtree scope, project exclusions, an actionable do-date gate), and views resolve project names to ids and expand each to its `parent`-link subtree. The Schedule view is intentionally omitted (time-of-day isn't modeled on date-grained do-dates). -- `heph-tui` (§8.1) — a terminal task agenda/triage UI, the primary surface for working a large task set (the §6.2.1 Todoist study showed triage, not single edits, dominates). A `ratatui` app, thin client of the daemon socket. Three panes: a sidebar of the five filter views + your projects, an attention-colored task list with compact human do/late dates, and a preview of the highlighted task's context doc + recent log. Triage from the keyboard: `a` add (guided title → attention → do-date, filed under the selected project), `x` done, `s` skip, `d` drop, `A` cycle attention, `b` push to On Deck, `e` reschedule the do-date; `o` opens the task's context doc in your nvim (live, via heph.nvim) and returns. `j/k` move, `Tab`/`h`/`l` switch panes, `r` refresh, `q` quit. Run it with `heph-tui` (honors `--socket` / `$HEPH_SOCKET`). `a` is a Todoist-style single-line quick-add: `Buy milk tomorrow p2 #Work every week` parses into title + attention (p1–p4) + do-date + recurrence + project (multi-word project names match greedily; an unresolved `#tag` just stays in the title). `/` runs a full-text search whose results overlay the task list; Enter opens a hit (a task at its context doc) in nvim. -- Move-to-project (§8.1): a new `task.set_project` RPC re-files a task under another project (or unfiles it) with OR-set link semantics — the old `in-project` link is tombstoned and a new one added, so a task is never filed under two projects at once. In `heph-tui`, **`m`** opens a list-pick overlay ("(Unfile)" then every project) on the highlighted task. `heph edit <task> --project <name>` now routes through the same RPC (fixing a bug where re-filing piled on a duplicate link), and `--project none` unfiles the task. This closes the last Todoist-parity capture gap. -- `heph-tui` task-list visuals (§8.1): each row now leads with an attention **flag** (`⚑`, colored red/orange/blue; blank for white) and a **project-colored bullet** — the bullet's color is derived stably from the project id (so it survives projects being added/removed), letting you scan a mixed list by project at a glance. The list also grows a **scrollbar** and keeps the selected task scrolled into view when there are more tasks than fit. -- `heph-tui`: **`<Enter>`** opens the selected task's context editor in nvim (from the sidebar it first drills into the task list); the old `o` binding is retired. The view sidebar / `heph view` order is now **Top of Mind, Tasks, Work Tasks, Chores, On Deck**. -- `heph-tui` sort toggle (§8.1): **`s`** flips the task list between two orders — **default** (attention → most-overdue → project → creation) and **by-project** (grouped under dimmed `──── Project ────` separators, then the same sub-order). The view's filter still applies first. (To free `s`, **skip** moved to **`S`**.) -- `heph.nvim` task-view rows (§8): `:Heph next`/`:Heph list` rows now show a compact **do/late date chip** (and a recurrence `↻`), so you can see scheduling at a glance; `<CR>` still jumps to a task's context doc. -- Wiki-links by node id (§8.4): node resolution is now **id-first** (`[[NODEID]]` resolves to its node ahead of any name match, so links can't be shadowed by a like-named node), and heph.nvim grows a **`[[` picker** — type `[[` (or `:Heph link`) to pick a node and insert a canonical `[[NODEID]]` link. With Telescope it's a **live fuzzy filter over your linkable nodes with a preview pane** (the node's body, or a task's context doc) — the list shows first-class targets only (a task appears once; its internal context/log docs and tag nodes are hidden, via the new `node.linkable` query) — type to narrow, Enter to insert, `<C-x>` to create a doc named what you've typed; without Telescope it falls back to a search-then-select prompt. Following such a link (`<CR>`) jumps straight by id. Those id links are kept **readable**: on read a bare `[[NODEID]]` is expanded to `[[NODEID|Current Name]]` (so it follows renames, in both the nvim buffer and the TUI preview), and on save it collapses back to the canonical bare id — a custom `|label` you write is preserved as an override. In the editor the id is **concealed** — a link renders as just its name, styled like a hyperlink, with the raw `[[id|Name]]` revealed on the line your cursor is on. Legacy `[[Name]]` links keep working, and **`heph migrate-links`** rewrites them to the canonical id form in one pass when you're ready (idempotent). -- Frontmatter editing in heph.nvim (§8.3): opening a node now shows an editable **YAML frontmatter** block on top of the body (`id`/`kind`/`title`/`tags`, and for a task or its context doc the task's `state`/`attention`/`do_date`/`late_on`/`recurrence`/`project`). On save, the plugin diffs the block and issues the right RPC per changed field — rename, set-attention, reschedule (dates as `YYYY-MM-DD`), move-to-project (by name), and tag add/remove — then saves the body; the store strips the block so it never persists. A mistyped `state` surfaces a validation error; a buffer with no block changes no metadata (so deleting the block can't wipe your tags). Inline **`#hashtags`** typed in the body are also added as tags on save (a `# heading` doesn't count) and are rendered in **italics** so they stand out. Link-follow and promotion are unaffected (they're content-relative, not line-absolute). -- Frontmatter projection (§8.3): a node can now be fetched with an editable **YAML frontmatter** block prepended — `node.get {frontmatter: true}` renders `id`/`kind`/`title`/`tags`, and for a task (or its context doc) the owning task's `state`/`attention`/`do_date`/`late_on`/`recurrence`/`project` plus a `task:` ref. Dates are local `YYYY-MM-DD`. On write, the store **strips and ignores** any leading frontmatter (conservatively — a real `---` hrule in prose survives) before the CRDT diff, so frontmatter never persists and an unchanged read→write is a no-op; a naive editor can't corrupt metadata. This is the read/write groundwork for editing a node's metadata as frontmatter in heph.nvim (the diff-into-RPCs layer is next). -- Tags (§4, §8.3): nodes can now be **tagged**. A tag is a `tag`-kind node whose id is deterministic in `(owner, name)`, so the same name is **one canonical tag** shared across everything it's applied to (and replicas converge — no duplicate tags). Tagging is an OR-set link, so adding/removing is idempotent and merge-safe. Surfaced as `tag.add`/`tag.remove`/`tag.list` RPCs and `heph tag add|rm|list` (list a node's tags, or every tag with no node). Tag names are trimmed; a canonical case/spelling normalization is deferred to the future zk import. This is the groundwork for the `tags:` line of the upcoming frontmatter edit surface. diff --git a/docs/changelog.d/v1-prototype.infra.md b/docs/changelog.d/v1-prototype.infra.md deleted file mode 100644 index 33f276c..0000000 --- a/docs/changelog.d/v1-prototype.infra.md +++ /dev/null @@ -1,2 +0,0 @@ -- Lua is now formatted by **stylua** (a `.stylua.toml` + a `stylua-system` prek hook), so heph.nvim's whitespace is enforced by an opinionated formatter rather than by hand. -- `mise run import-todoist` — a one-way importer that seeds a heph store from your Todoist projects + active tasks (project hierarchy, priority→attention, do-dates, natural-language recurrence, descriptions + sub-tasks as context items). Dry-run by default; `-- --commit` writes into your real store after backing it up. See [[import-todoist]]. diff --git a/docs/changelog.d/v1-quickadd.feature.md b/docs/changelog.d/v1-quickadd.feature.md deleted file mode 100644 index 06ad4f9..0000000 --- a/docs/changelog.d/v1-quickadd.feature.md +++ /dev/null @@ -1,2 +0,0 @@ -- `heph-quickadd` (§8) — a **global quick-capture popover**, the last piece needed to retire Todoist. A tiny always-warm `eframe`/`egui` agent registers **⌘'** system-wide (`global-hotkey`, via Carbon — no Accessibility permission) and, on press, *toggles an already-created hidden window visible and focuses a single capture field* — never spawning on the keypress, so it's a muscle-reflex ([[design]] §6.2.1). The field reuses the shared `quickadd::parse` (lifted from `heph-tui` into `hephd`'s lib so both surfaces share one parser): one line like `Call dentist fri p1 #Health` parses live into title + attention (`p1`–`p4`) + do-date + recurrence + project, shown as a chip row (⚑ attention · 📁 project · ⏰ do-date · ↻ recurrence) as you type. **Enter** saves **optimistically** — the window hides instantly and `task.create` runs on a background thread, so perceived latency is just the keystroke; a failed RPC re-shows the window with the text restored, so a capture is never lost. **Esc** dismisses. -- Supervision (no second launch agent): **hephd supervises `heph-quickadd`** as a child in local mode on macOS (opt-in via `HEPH_QUICKADD=1`, which the installed launchd plist now sets — dev/test runs that spawn a local daemon never pop a window). hephd already runs as a `gui/$uid` LaunchAgent, so the child inherits the Aqua session the hotkey/GUI need, and the user still installs/manages exactly one service (`heph daemon`). The helper **self-exits when orphaned** (its parent daemon gone) and exits if ⌘' is already held, so killing hephd — even `kill -9` — leaves nothing behind and duplicates never stack. Runs as a macOS **accessory** app (no Dock icon). macOS-only for v1; `global-hotkey` + `egui` also run on Linux/X11, so the Linux port is mostly window-behavior polish. diff --git a/docs/changelog.d/v1-selfhost-roadmap.doc.md b/docs/changelog.d/v1-selfhost-roadmap.doc.md deleted file mode 100644 index 01d006b..0000000 --- a/docs/changelog.d/v1-selfhost-roadmap.doc.md +++ /dev/null @@ -1 +0,0 @@ -- **heph self-hosts its roadmap (the "bootstrap lift").** With v1 at Todoist feature-parity, remaining/future work is now tracked **in heph itself** — as tasks in the `Hephaestus` project (`heph view ondeck`) — rather than in a document. The tech-spec is reframed accordingly: `tech-spec.md` → **`v1-prototype-tech-spec.md`** (all wiki-links + paths updated), retitled and bannered as a **historical** record of the v1 build, with its §14 tracker frozen. `AGENTS.md` gains a *Planning future work* section (capture via `heph task "…" --project Hephaestus`, triage in `heph-tui` On Deck); `README.md`'s status reflects parity + the three daily-driver surfaces. The living **[[design]]** doc remains the rationale of record. diff --git a/docs/changelog.d/v1-tui-move-picker.feature.md b/docs/changelog.d/v1-tui-move-picker.feature.md deleted file mode 100644 index 9d75e0a..0000000 --- a/docs/changelog.d/v1-tui-move-picker.feature.md +++ /dev/null @@ -1 +0,0 @@ -- `heph-tui` move-to-project picker (`m`) is now an **fzf-style filter** (§8.1): a prompt line on top narrows the project list by fuzzy subsequence match as you type (↑/↓ or Ctrl-n/p move, Enter selects, Esc cancels) — no more scrolling the full list. When the typed text names no existing project, a **"+ New project"** row is offered: selecting it **creates the project and files the task under it** in one step (via `node.create`), so projects can be created without leaving the TUI. diff --git a/docs/changelog.d/v1-tui-undo-panels.feature.md b/docs/changelog.d/v1-tui-undo-panels.feature.md deleted file mode 100644 index 0d6b0e6..0000000 --- a/docs/changelog.d/v1-tui-undo-panels.feature.md +++ /dev/null @@ -1,5 +0,0 @@ -- `heph-tui` safety + undo wave (§8.1): - - **Pane-specific keys.** Task-triage gestures (`x`/`d`/`S`/`A`/`b`/`e`/`m`/`D`) now fire **only when the task pane is focused**, so a stray keypress while navigating the sidebar can no longer drop or delete a task. The sidebar gets its own actions; the status-line hints are now focus-aware. - - **Undo / redo.** **`u`** undoes the last triage action (drop, done, skip, attention, move) and **Ctrl-z** redoes it, restoring the task's prior state from a snapshot — multi-level, capped at 200 steps. (Tombstone-delete stays excluded — it keeps its y/N prompt — and an attention of "none" can't be re-cleared.) - - **Delete a project** from the sidebar with **`D`** (y/N confirm); its tasks become unfiled (they move to the Inbox), not deleted. - - **Sidebar auto-refreshes** after creating or deleting a project, so a new project shows up immediately (no more quit-and-reload).