diff --git a/AGENTS.md b/AGENTS.md index caec98b..6841801 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -41,7 +41,7 @@ See [[agent-change-process]] for the full methodology. ## Project Structure -A Cargo workspace (`Cargo.toml` at the root) plus the Neovim plugin and repo tooling. The build follows the tech-spec §11.1 slice order; the **Rust backend is feature-complete** (all three runtime modes + sync + OIDC auth) — the remaining v1 work is `heph.nvim`. The live progress tracker is **[[tech-spec]] §14**. +A Cargo workspace (`Cargo.toml` at the root) plus the Neovim plugin and repo tooling. The build follows the tech-spec §11.1 slice order; the **Rust backend is feature-complete** (all three runtime modes + sync + OIDC auth) and **`heph.nvim` is built and installed** (knowledge base + tasks + a plug-and-play managed daemon). Remaining v1 work is task-scheduling UX + polish (the live tracker is **[[tech-spec]] §14**). ``` ./Cargo.toml # workspace manifest (shared deps + members) @@ -49,7 +49,7 @@ A Cargo workspace (`Cargo.toml` at the root) plus the Neovim plugin and repo too # recurrence, "what is next?" ranking, op-log/HLC/CRDT (yrs) sync ./crates/hephd/ # daemon: local/server/client modes — unix-socket RPC + HTTP sync/rpc + OIDC auth ./crates/heph/ # CLI (thin client of hephd): next/task/doc/get/export/search/journal/auth -./heph.nvim/ # Neovim plugin (planned, next slice): primary surface; replaces obsidian.nvim +./heph.nvim/ # Neovim plugin: primary surface; replaces obsidian.nvim (Lua + headless e2e) ./docs/ # Diataxis docs (incl. [[design]] + [[tech-spec]]), Quartz config, release content ./docs/changelog.d/ # towncrier fragments for noteworthy changes ./.dagger/ # Dagger module (src/hephaestus_ci/) backing docs builds and releases diff --git a/README.md b/README.md index fe8e7e3..3357640 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ See **[docs/explanation/design.md](docs/explanation/design.md)** for the vision ## Status -**Phase 1 (v1 prototype) — nearly feature-complete** on branch `feature/v1-prototype`. **All three runtime modes work, replicas sync through a hub over HTTP, and op exchange is authenticated end-to-end with OIDC** (Authentik): the hub verifies bearer tokens (JWKS/RS256) and enforces single-tenant ownership, and `heph auth login` runs the device-code flow, caching tokens in the OS keyring. The offline-first everyday config (`local` + `hub_url`) converges with a `yrs` text-CRDT merging bodies. Remaining: the Neovim plugin (the primary surface). Built test-first (112 tests at last update). The canonical tracker is **tech-spec §14**. +**Phase 1 (v1 prototype) — nearly feature-complete** on branch `feature/v1-prototype`. **All three runtime modes work, replicas sync through a hub over HTTP, and op exchange is authenticated end-to-end with OIDC** (Authentik): the hub verifies bearer tokens (JWKS/RS256) and enforces single-tenant ownership, and `heph auth login` runs the device-code flow, caching tokens in the OS keyring. The offline-first everyday config (`local` + `hub_url`) converges with a `yrs` text-CRDT merging bodies. The **`heph.nvim` plugin is built and installed on the dev machine** — knowledge base (journals, wiki-links with follow-or-create, a home/index page, a dailies picker), tasks (capture, the "what is next?" view, interactive list, promotion), and a plug-and-play self-healing daemon. CI is green, fully through Dagger. Built test-first (117 Rust + 17 nvim e2e). The canonical tracker is **tech-spec §14**. | Area | State | |---|---| @@ -27,6 +27,7 @@ See **[docs/explanation/design.md](docs/explanation/design.md)** for the vision | `heph.nvim` (primary surface) — RPC client, buffer-backed editing, wiki-link follow, journal (slice 11a) | ✅ done | | `heph.nvim` — Tactical/Organizational task views, capture, attention, done/drop, log (slice 11b) | ✅ done | | `heph.nvim` — context-item promotion + Dagger headless-nvim CI (slice 11c) | ✅ done | +| `heph.nvim` — managed daemon (plug-and-play, self-heal), follow-or-create, home/index, dailies picker, interactive views; installed + dev-isolated | ✅ done | ## Architecture @@ -35,7 +36,7 @@ A Cargo workspace, layered so the same core runs from a laptop to a hub: - **`crates/heph-core`** — the library: data model, the `Store` trait + SQLite store, markdown parsing/extraction, recurrence, the "what is next?" engine, and the sync engine (op-log, hybrid logical clocks, CRDT/LWW merge, conflict detection). Synchronous and clock-injected (no ambient wall-clock reads) so ranking and merge are deterministic. - **`crates/hephd`** — the per-device daemon. One binary, three modes — **`local`** (own SQLite replica; a syncing spoke when given `--hub-url`), **`server`** (also the sync hub: an HTTP endpoint others sync against), **`client`** (thin, remote, no replica — proxies to a `--server-url`) — selected by configuration via a targetable `Store` backend. Surfaces connect to it over a unix socket; it owns the DB handle and background sync. - **`crates/heph`** — the CLI: a thin client of the daemon (no direct DB access). -- **`heph.nvim/`** *(planned)* — the Neovim plugin, the primary editing/agenda surface. +- **`heph.nvim/`** — the Neovim plugin, the primary editing/agenda surface (a thin client of the daemon; see [docs/reference/heph-nvim.md](docs/reference/heph-nvim.md) and [docs/how-to/install-heph.md](docs/how-to/install-heph.md)). **Storage:** SQLite is the source of truth; a node's body is markdown; `export` materializes the whole store as a directory of `.md` files. **Sync:** each device holds a full replica + an append-only op-log; devices reconcile through a hub with automatic merge (text-CRDT bodies, last-writer-wins scalars, OR-set links) and a conflict queue for the ambiguous remainder. **Auth:** the hub verifies an OIDC bearer token (Authentik) on every op exchange — RS256/JWKS verification + a single-tenant owner gate — and clients obtain tokens via the OAuth 2.0 device-code flow (`heph auth login`), cached in the OS keyring. Local-only instances need no auth. @@ -88,7 +89,7 @@ mise run ai-docs # docs AI agents read firs ./crates/heph-core/ # core library: model, store, extraction, recurrence, ranking, sync ./crates/hephd/ # daemon: local/server/client modes — unix-socket RPC + HTTP sync/rpc ./crates/heph/ # CLI: thin client of the daemon -./heph.nvim/ # Neovim plugin (planned) +./heph.nvim/ # Neovim plugin (primary surface) — Lua, headless e2e harness ./docs/ # Diataxis docs (design, tech-spec, how-to), Quartz config ./.forgejo/ # CI build + release workflows and hooks ./.dagger/ # Dagger module backing docs builds/releases diff --git a/docs/reference/tech-spec.md b/docs/reference/tech-spec.md index 4f1536a..3d8f94a 100644 --- a/docs/reference/tech-spec.md +++ b/docs/reference/tech-spec.md @@ -327,7 +327,7 @@ See [[design]] §5–§7 for the constraints later phases impose on present choi ## 14. Implementation status (Phase 1 tracker) -> Cross-session resume tracker for the Phase 1 C1 (branch `feature/v1-prototype`, PR #1). Updated 2026-06-02 — **117 Rust tests green** (`cargo test --all`) + the heph.nvim headless e2e suite (`mise run test-nvim`, 7 specs; also runs in CI via `dagger call test-nvim`), `clippy -D warnings` + `fmt` + `prek` clean. Workspace: `crates/heph-core`, `crates/hephd`, `crates/heph`, plus `heph.nvim/` (slices 11a–11c). +> Cross-session resume tracker for the Phase 1 C1 (branch `feature/v1-prototype`, PR #1). Updated 2026-06-02 — **117 Rust tests** (`cargo test --all`) + **17 heph.nvim headless e2e specs** (`mise run test-nvim`; also runs in CI via `dagger call test-nvim`), `clippy -D warnings` + `fmt` + `prek` clean. Workspace: `crates/heph-core`, `crates/hephd`, `crates/heph`, plus `heph.nvim/` (slices 11a–11c **+ a UX iteration**, below). **The plugin is installed and running on the dev machine** (built from the forge; see [[install-heph]]). **Done** @@ -348,14 +348,23 @@ See [[design]] §5–§7 for the constraints later phases impose on present choi - ✅ **`heph.nvim` slice 11a (§8) — the primary surface begins:** the Lua plugin (`heph.nvim/`) as a thin client of the `hephd` unix socket. **RPC client** over a `vim.uv` pipe (blocking `call` via `vim.wait`; id-demuxed; partial-line buffered; `luanil` so JSON `null`→`nil`; isolated `Session`s for tests). **Buffer-backed nodes** — `heph://node/` buffers (`buftype=acwrite`), `BufReadCmd`→`node.get` / `BufWriteCmd`→`node.update` (whole-buffer body, CRDT-diffed; exact round-trip). **`[[wiki-link]]` follow** on `` via a new **`node.resolve {title}`** RPC (exact alias-then-title match, the same mapping that materializes `wiki` links — never fuzzy `search`; unresolved links allowed). **Daily journal** (`:Heph today`/`journal `, idempotent). `:Heph` command surface + completion. **Headless e2e (§9):** drives the plugin in `nvim --headless` against a real daemon over a temp socket via a **self-contained busted-style runner** (`tests/e2e/runner.lua` — no external plugins/network, deterministic exit codes); specs cover journal round-trip, follow-link (+ unresolved no-op), and link-two-docs/backlink. `mise run test-nvim` builds the daemon and runs the suite (dev: system nvim/rustc; CI: a Dagger container provides them — slice 11c). - ✅ **`heph.nvim` slice 11b (§8) — task views:** **`list` enriched** to titled [`RankedTask`] rows (title + `canonical_context_id`, shared `ranked_from_row` with `next`) so the Organizational view needs no N+1 `node.get`. Plugin: **Tactical `next`** + **Organizational `list`** views (rendered scratch buffers, `` opens a row's canonical-context doc — the node autocmd narrowed to `heph://node/*` so view buffers don't trip it); **task capture**, **set-attention**, **done/drop**, **skip**, **per-task `log` append** — all resolving "the current task" from the buffer (a `task` node, or a context doc via its `canonical-context` backlink); **`vim.ui.select` picker** (`picker.lua`) with Telescope auto-upgrade; `:Heph next/list/capture/attention/done/drop/skip/log/search` subcommands. e2e specs: **capture→next→open context→add/check checklist→done**, and **recurring fresh-checklist** (complete rolls forward in place; the next occurrence is all-unchecked — the §4.4 hard requirement). - ✅ **`heph.nvim` slice 11c (§8) — promotion + Dagger CI:** backend **`task.promote {container_id, item_ref, attention?, project?}`** — mints a committed task from the `item_ref`-th `- [ ]` context item (1-based, document order via a new `extract::context_item_lines`) and rewrites that source line into a `[[link]]` to it. **Wiki-link resolution now excludes canonical-context docs** (`resolve_id`), so `[[Task Title]]` deterministically resolves to the task, not its identically-titled context doc — a general fix surfaced by promotion. Plugin: `:Heph promote`, `promote_under_cursor` (save-if-dirty → `util.context_item_index_at_cursor` mirrors extract's fence rules → `task.promote` → reload). e2e spec (f). **CI via Dagger:** a `test_nvim` function in `.dagger/` bakes a **pinned, arch-detected Neovim** (`v0.11.2`; Debian's is too old for `vim.uv`) onto `rust:1-bookworm`, builds `hephd`, and runs the shim suite (cargo + cargo-target cache volumes); `build.yaml` calls `dagger call test-nvim`. `run.lua` fails on zero-specs (no false-green) — validated end-to-end (failing spec → Dagger exit 1). +- ✅ **`heph.nvim` UX iteration + install (§8) — post-11c, makes the plugin a daily driver:** + - **Plug-and-play managed daemon:** `setup({})` spawns + supervises its own `hephd` (default XDG paths), kills only what it spawned on exit, and **self-heals** (`rpc.call` respawns + retries once on a dropped connection). A daemon you run yourself is respected (spawn only when nothing serves the socket); `autostart=false` ⇒ connect-only. **Bugfix:** `daemon.wait_ready` must not call the rpc probe inside a `vim.wait` predicate (nested `vim.wait` deadlocks Neovim) — bit on the 2nd launch via the prior daemon's stale socket; now a plain-loop probe + socket-unlink on exit, with a regression test. + - **Knowledge-base UX:** **follow-or-create** (`` on an unresolved `[[link]]` mints the doc + materializes the source backlink), **`:Heph doc`**, **`:Heph home`** (an open-or-create index/landing page), **`:Heph journals`** (recent-days dailies picker with Telescope preview + `@create`). + - **Interactive task views:** `:Heph next`/`list` buffers gained `a` add / `d` done / `r` refresh (+ `` open), with a dimmed key-hint **header line** (virt-lines-above the first row render off-screen — use a real header). + - **Dev/installed isolation:** installed `heph`/`hephd` own the default paths; `mise run dev` runs the working-tree daemon on `.dev/` paths; `$HEPH_SOCKET`/`$HEPH_DB` point a dev Neovim at it. + - **CI is now fully Dagger** (`build.yaml`: `dagger call check` + `test-nvim`; **prek dropped from CI** — the Alpine job image has no Rust/nvim/prek, only Dagger + DinD). First-ever green CI. **Not yet done (resume order)** > The Rust backend is feature-complete; `heph.nvim` slices 11a–11c are done — the v1 surface (knowledge base + task views + promotion) works end-to-end with CI. The remainder is the deferred reconcile slice plus non-blocking polish + an end-of-v1 sweep (§11). -1. ⏳ **`heph.nvim` slice 11d (§6/§8) — DEFERRED, post-parity:** daemon **server-push** notification framing (no-`id` lines on the socket; the client read-loop already demuxes them) + the **dirty-buffer reconcile** (the §8 "known-hard" case) + the "update-arrives-while-open" e2e (§9). -3. ⏳ **Adoption refinement + multi-tenant (§13) — non-blocking:** local→authed **adoption** currently rewrites `owner_id` (`adopt_owner`) but not yet the owner-embedded deterministic ids (journal/tag) + their links; and the hub is single-tenant (one owner per store) — owner-per-token storage is a future extension. -4. ⏳ **Dependency-refresh pass (§11) — before declaring v1 done:** sweep all external deps to latest stable (e.g. `keyring` 3→4, which restructured to `keyring_core`), re-run the full suite. +1. ⏳ **Task-scheduling UX (§7, §8) — the highest-value next gap:** the plugin can only set a task's **title + attention** today. The engine fully supports **do-date / late-on / recurrence / project**, but nothing in the editor sets them — so the ranking has no urgency signals to work with from nvim. Surface these (capture-with-fields and an edit affordance), and show do/late in the `next`/`list` rows. +2. ⏳ **More surfacing of existing engine features (§6, §8):** a **backlinks/links** picker (navigate the graph back), **tags**, a **`health`** working-set view (orange-vs-6, active-vs-~30), and **log read** (`log.tail`) — the resumption breadcrumb on return. +3. ⏳ **`heph.nvim` slice 11d (§6/§8) — DEFERRED, post-parity:** daemon **server-push** notification framing (no-`id` lines on the socket; the client read-loop already demuxes them) + the **dirty-buffer reconcile** (the §8 "known-hard" case) + the "update-arrives-while-open" e2e (§9). +4. ⏳ **Split `heph.nvim` to its own forge repo (§8) — UX polish:** generated from this monorepo (subtree-split in CI) so the lazy spec becomes `{ "eblume/heph.nvim" }` instead of a local-clone `dir` (see [[install-heph]]). +5. ⏳ **Adoption refinement + multi-tenant (§13) — non-blocking:** local→authed **adoption** currently rewrites `owner_id` (`adopt_owner`) but not yet the owner-embedded deterministic ids (journal/tag) + their links; and the hub is single-tenant (one owner per store) — owner-per-token storage is a future extension. +6. ⏳ **Dependency-refresh pass (§11) — before declaring v1 done:** sweep all external deps to latest stable (e.g. `keyring` 3→4, which restructured to `keyring_core`), re-run the full suite. ## Related