--- title: Design Document modified: 2026-05-31 tags: - explanation - design --- # Hephaestus β€” Design Document > **Status:** Living design record. This is the **rationale + decision-history** document. For the clean, implementation-facing spec the next session builds from, see **[[tech-spec]]**. Sections marked **❓ OPEN** are unresolved; **πŸ”’ DECIDED** are settled. ## 1. Purpose & Vision **Hephaestus** is a personal context management system that fuses two systems the owner already relies on into a single, purpose-built, self-hosted application: 1. **The Zettelkasten** (`~/code/personal/zk`) β€” an Obsidian vault of ~600 markdown files acting as a personal wiki, journal, knowledge base, and "task-context" store for long-running projects. Notes use `[[wiki-links]]`, YAML frontmatter (`id`, `aliases`, `tags`), timestamp-based note IDs (`1722897441-MWFE`), and daily notes (`YYYY-MM-DD`). 2. **Todoist** β€” the owner's primary task manager, accessed today via a bespoke Python mise-task in blumeops (`mise-tasks/blumeops-tasks`) that polls the Todoist v1 API (projects β†’ tasks; fields: `content`, `description`, `priority`, `due`) using a token from 1Password. Today these are loosely coupled by fragile cross-links (`todoist://` in notes, `[[note]]` text in Todoist comments). Hephaestus replaces the fragile coupling with **one unified data model and database** where tasks and knowledge are first-class, linkable entities. ### Guiding principles - **Context-aware:** A task like "Fix the roof leak" is coupled to the relevant knowledge β€” the home-repair log, the log of contractor calls, etc. Surfacing that context is a core feature, not an afterthought. - **Workflow-optimized:** Highly optimized, concise answers to recurring questions β€” above all **"What is next?"** β€” are the primary UX. Speed and concision beat feature breadth. - **Owner-built, owner-hosted:** A standalone codebase replacing a loose stack of tools, hosted out of blumeops. ## 2. Goals & Non-Goals ### Goals (v1 / prototype) - πŸ”’ Unified data model linking **notes** and **tasks** as first-class, cross-linkable entities. - πŸ”’ SQLite backend. - πŸ”’ Rust implementation. - πŸ”’ **Distributed dual-mode operation:** useful fully offline, auto-syncs to a central self-hosted instance in blumeops (k3s container) when reachable. The central instance may be unavailable for long periods. - πŸ”’ Surfaces over a shared core (via the `hephd` daemon): **CLI** (everyday driver), **nvim plugin** (primary editing UX), **web UI** (hub-hosted). - πŸ”’ Optimized workflow queries, especially "What is next?", with context surfacing. - πŸ”’ **Auth from the ground up** (OIDC/Authentik), with isolated multi-user accounts β€” sensitive data. ### Non-Goals (v1) / Later - ⏳ iOS app + Apple Watch app for quick voice-dispatched task capture β€” explicitly a **follow-up goal**, not v1. - ⏳ Sharing/collaboration between users β€” accounts are isolated for v1 (multi-user *isolation* is in scope; multi-user *collaboration* is not). - ❌ Obsidian feature parity (canvas, graph view, plugins) β€” clean break. - ❌ **Migration / import / Todoist sync in v1** β€” the prototype is a **clean break**: heph is the system of record from day one. No ZK import, no Todoist bridge. (Migration may return as a later, optional phase.) - ❌ Encryption at rest / E2E in v1 β€” security model is **access restriction (OIDC auth) only**; plain SQLite. May revisit. - ❌ Inferred/semantic context surfacing in v1 β€” explicit links + search only. ## 3. Data Model > πŸ”’ **DECIDED (shape).** SQLite is the canonical store; a node's body is plain markdown text; an `export` mode materializes the whole store as a directory of `.md` files. Clean break from Obsidian-the-app, but markdown stays the portable payload. Tasks are **thin**; rich context lives in documents; everything is connected by typed links. ### Entities - **Node** β€” the base entity. Every first-class thing is a node with a stable, sync-safe ID (see Β§3.1). A node has a `kind` discriminator. Kinds (initial set): - **`doc`** β€” a rich markdown context document (the bulk of the knowledge base; replaces ZK articles, journals, logs). Body = markdown text. Has `aliases`, timestamps. - **`task`** β€” **thin**: ID + state, plus a few scalar attributes (attention-state, do-date, late-on, recurrence) and links to a project/tags/context. **No prose body of its own** β€” context is supplied by linked `doc` nodes (or a doc *is* the work-log for the task). This is the key unification: ZK-articles-with-todos and Todoist-tasks-with-context both reduce to *typed links between thin tasks and rich docs*. The full task-attribute model is in Β§6.2 (it encodes the owner's actual lived prioritization system). - **`project`** β€” a grouping node (maps to Todoist projects / ZK project dirs like `payrix/`). - **`tag`** β€” a label node (so tags are linkable/queryable, not just strings). - **`journal`** β€” a daily-note node keyed by date (`YYYY-MM-DD`). - **Link** β€” a typed, directional edge between two nodes. Types (initial set): `wiki` (a `[[link]]` found in a doc body), `context-of` (doc ↔ task), `blocks`, `parent`/`child`, `tagged`, `in-project`. Wiki-links in markdown bodies are parsed and materialized as `wiki` links so the graph and the prose stay consistent. ### 3.1 Identity - πŸ”’ **DECIDED β€” ULID for content nodes** (`doc`/`task`/`project`), sortable and sync-safe, with a human-facing `slug`/`alias` layer so `[[wiki-links]]` can be written by name. The ZK's timestamp-IDs (`1722897441-MWFE`) are ULID-like already; a future migration can map them. - πŸ”’ **DECIDED β€” deterministic ids for key-unique kinds.** Random ULIDs *diverge* when two offline replicas independently create the *same logical singleton* (today's `journal`, a `tag` by name) β€” duplicates that never merge. So `journal` ids derive deterministically from `(owner, ISO-date)` and `tag` ids from `(owner, normalized-name)`; independent creation then yields the *same* id and the CRDT/op-log coalesce them (two partial daily notes even merge). `project` stays a random ULID (deliberately created, renameable, tiny stable set). Tag rename = retag (no in-place key change); the normalization function is a fixed, versioned constant. See [[tech-spec]] Β§4.5. ### 3.2 Scalar task attributes vs. links **Tags and projects are nodes** (linkable, queryable). **Attention-state, do-date, late-on, recurrence are scalar columns** on the task. See Β§6.2 for the semantics β€” these columns are not arbitrary; they encode a specific, battle-tested model. Due dates are *not* first-class nodes (a task has one project/context; "this Friday" doesn't need its own context). ### 3.3 Recurrence model (recurring tasks & checklists) β€” IN SCOPE for v1 A **recurring task** carries an **RFC-5545 RRULE** and acts as a recurring **definition**. > ❌ **The anti-pattern we must avoid (Todoist's mistake):** modeling a recurring task's checklist sub-items as standalone, non-recurring tasks that **carry their completion state forward** when the parent recurs. This is the corner; designing around it now is *why* recurring checklists are in v1 rather than deferred. **heph model β€” πŸ”’ DECIDED (roll-forward in place):** - The recurring definition's **checklist template** is just the `- [ ]` lines in its canonical context `doc` body (under Fork A, Β§6.3, the body *is* the template β€” no separate template entity). - A recurring task is a **single node**. On completion: (1) append the finished occurrence to the per-task **log** (Β§6.4), (2) reset the body's checkboxes to unchecked (a body-CRDT edit), (3) advance the do-date to **the next RRULE instance strictly after `now`**, *skipping* missed occurrences. So a fresh, all-`outstanding` checklist each occurrence; **completion never carries forward**; a missed daily routine is **one** gently-overdue item, not a pile. - History is narrative (the log), matching "narrative > list" (Β§6.1). RRULE expansion stays **lazy** β€” only "next instance after now" is ever computed, never the series (Β§6.6). **Why not occurrence-instances?** The rejected alternative (a fresh node per occurrence) was attractive for queryable per-occurrence history, but under Fork A each occurrence would need its own body β€” hence its own node β€” turning every daily recurrence into a new node-pair: exactly the explosion Β§6.6 forbids. No v1 query needs occurrence rows (the ranking reads the single node's do-date), and adherence/streak stats can be reconstructed from the log later. See [[tech-spec]] Β§4.4. ## 4. Architecture > πŸ”’ **DECIDED (shape).** Layers, top to bottom: - **Surfaces (thin clients) β€” a three-surface model (revised 2026-06 after the Β§6.2.1 Todoist study; supersedes the earlier "nvim is *the* primary surface" framing).** Tasks and knowledge pull in different interaction directions, so each surface plays to its strength rather than one trying to do everything: - **`heph.nvim` β€” the primary *context / knowledge-base* surface.** The full "org-mode"-style experience (markdown buffers backed by `doc` nodes; wiki-links, journaling, the canonical-context doc, per-task log, checklists). The explicit **successor to obsidian.nvim** (telescope picker, follow `[[wiki-links]]` on ``, dailies, multi-state checkboxes); must reach that parity (Β§6.5). It surfaces tasks for **navigation/reading and context**, not as the primary place to *edit* structured task fields. - **`heph` CLI β€” capture/scripting + the complete daemon API.** Every structured task field is a flag (`-a red --do tomorrow --late fri --recur weekly --project Maintenance`), which is exactly what command-line flags are good at and dissolves the "how do I edit N structured fields" problem. The CLI implements the *entire* API (admin, sync, conflicts, export) so it is also the scripting/automation surface. - **`heph-tui` β€” the primary *task agenda / triage* surface (planned, [[tech-spec]] Β§8.1).** The Β§6.2.1 study shows the dominant task activity is *interactive triage of a large set* (daily orange reconfirm, blue keep/drop review, browse-by-project) β€” work that is awkward as either CLI flags or nvim buffers. A terminal UI owns that, and **launches into nvim** for a task's context (and nvim launches back). Not yet built. - The **web UI** is the occasional hub-served surface. A later iOS/Watch client talks to the hub directly. - πŸ”’ **Single binary, three modes.** One Rust binary runs as `local` / `server` / `client` ([[tech-spec]] Β§3.1); the `heph` CLI shares the same command surface. Mode is two orthogonal axes (backend + inbound listener) plus an optional `hub_url` that makes any `local` instance a syncing **spoke** β€” the everyday device is `local` + `hub_url`, the hub is `server`, `client` is the online-only convenience. - **Per-device daemon (`hephd`):** owns the local SQLite handle, the CRDT/op-log state, and background sync. All local surfaces connect to it over a local socket. This is what makes multi-surface access concurrent and safe on one SQLite file, and gives one place to run background sync. - πŸ”’ **Lifecycle = an explicit OS service; surfaces are connect-only (decided 2026-06).** Since the daemon is shared across surfaces (CLI, TUI, nvim), no single surface owns it β€” so none auto-spawns it (an earlier nvim "managed daemon" that spawned + killed-on-exit was removed: a surface-owned daemon can't be shared, and "when do we stop it?" has no good answer). Instead it runs as a launchd agent (macOS) / systemd user service (Linux), managed by **`heph daemon start/stop/restart/status`** ([[run-the-daemon]]). Surfaces only connect, and tell you to run `heph daemon start` if nothing is serving the socket. - **Core crate (Rust lib):** data model, query engine, markdown parsing + wiki-link extraction, and sync logic. Linked by both `hephd` and the hub server. - **Hub:** `hephd` running in "server" mode on blumeops k3s β€” central SQLite, the sync rendezvous point, and the web UI host. May be offline for long periods; devices keep working and reconcile when it returns. Open points: - ❓ nvim ↔ daemon transport: JSON-RPC over a unix socket (nvim has a native RPC channel; allows the daemon to *push* updates when sync brings in remote changes) vs. the plugin shelling out to the `heph` CLI. Leaning socket RPC. - ❓ Web front-end style: server-rendered (Axum + HTMX/Askama) vs. WASM SPA (Leptos/Dioxus). Leaning **SSR + HTMX** since it's the lowest-traffic surface β€” favor simplicity. ## 5. Sync & Conflict Resolution ### Requirements (gathered from the owner) - πŸ”’ ~99% of interaction today is via the **CLI on Gilbert** (dev laptop). Future split: ~90% Gilbert, ~9% phone/watch, ~1% web. - πŸ”’ Must work **seamlessly across tailnet workstations** (Gilbert, ringtail, others): dispatch a change on one, walk to another, see it arrive "in short order." - πŸ”’ **Genuine offline conflicts will occur** β€” e.g. Gilbert edits offline, then ringtail edits, then Gilbert reconnects. Must resolve **gracefully**: auto-merge where possible; for the unresolvable remainder, a **conflict queue + alert** ("you have N merge conflicts, run `heph conflicts`"). - πŸ”’ **Auth from the ground up** β€” this is extremely sensitive data. Use **Authentik (OIDC)** as IdP, support **multiple isolated user accounts** (in practice just `eblume`). - πŸ”’ A **web UI** is part of the hosted (hub) model. ### Proposed model β€” *for confirmation* A **hub-and-spoke, op-log + CRDT** design: - **Topology:** each device runs `hephd` with a full local SQLite replica; the blumeops **hub** is the rendezvous. Devices push/pull an **append-only operation log** to the hub when reachable. Hub-and-spoke (not P2P mesh) β€” simpler, matches "central self-hosted app," and the hub is normally up on the tailnet so propagation is prompt. When the hub is down, devices keep working and the op-log drains on reconnect. - **Merge semantics (so "graceful" is precise):** - **`doc` bodies (markdown):** a **text CRDT** (e.g. Yrs/Automerge) β†’ concurrent edits *always* merge with no hard conflict. This covers the common case. - **Scalar task fields (completion, due, priority):** **last-writer-wins via Hybrid Logical Clocks**. Deterministic and cheap. - **Links / set membership (tags, projects):** add/remove as a CRDT set (OR-Set) β†’ no conflicts. - **Conflict queue:** because CRDTs auto-merge, true blocking conflicts are rare. We *surface* (not block) the cases that are semantically ambiguous: a discarded LWW value on a meaningful field (e.g. completion flipped both ways, divergent due dates), or concurrent edits to overlapping text regions. These go to `heph conflicts` with an alert; sync never stalls. - **Identity & ordering:** ULIDs for nodes (Β§3.1); HLC timestamps stamp every op for causal ordering across offline devices. - **Auth:** the hub's sync + web endpoints require an **OIDC bearer token from Authentik**. Devices obtain tokens via the **OAuth device-code flow**; refresh token stored in the OS keychain / 1Password. Every node/op is owned by a `user_id`; the hub enforces per-user isolation. Offline devices operate on cached credentials. ### Open points - ❓ **Hub-only vs. P2P fallback:** if the hub is down but Gilbert and ringtail are both on the tailnet, do they sync *directly*, or wait for the hub? Hub-only is simpler; direct P2P is more available. (Leaning hub-only for v1.) - ❓ **Encryption at rest:** given "extremely sensitive," should each device's SQLite (and the hub's) be encrypted at rest (e.g. SQLCipher), and/or should op-log payloads be end-to-end encrypted so the hub stores ciphertext it can't read? (E2E complicates server-side search/web UI.) - ❓ **Todoist's role** during/after migration β€” two-way bridge during transition, import-once, or retire on cutover? - ❓ How "short order" is short β€” is a few seconds of propagation (push-based) needed, or is periodic pull (e.g. every 30–60s) fine? ## 6. Workflow Features > Driven by "highly optimized specific workflows." heph is organized around a small set of **workflows**, each a first-class surface in heph.nvim: > > - **Task / "What is next?"** (Β§6.1–§6.3) β€” the Tactical / Strategic / Organizational task engine. The flagship. > - **Log** (Β§6.4) β€” daily journal + optional append-only per-task work-logs ("narrative > list"). > - **Wiki / KB** (Β§6.5) β€” the whole context corpus as a browsable/queryable personal wiki; the direct **obsidian.nvim replacement**. > - **Calendar** (Β§6.6) β€” entering via the calendar, tied to Strategic mode. *Deferred, but scaffolded carefully.* ### "What is next?" β€” the flagship > ❓ **ACTIVE DEEP-DIVE.** This is the defining workflow and has ~10 years of the owner's prior art and prior tool attempts behind it. Being designed from that prior art rather than from scratch β€” see Β§6.1. The blumeops-tasks ranking (overdue-first, then priority, p3 = backlog) is a known reference point, not the target. ### Context surfacing β€” v1 scope > πŸ”’ **DECIDED for v1.** Explicit links only; no inferred/semantic context yet. - Every **task** automatically gets **one canonical `doc` (context) node**, created and linked on task creation. That canonical doc is the jumping-off point and can wiki-link onward to other docs/cards. - nvim commands provide **telescope/fzf/rg-style search** over context docs, optionally with preloaded filters (e.g. scoped to a project). - Inferred context (shared tags, full-text/semantic similarity) is deferred β€” large surface area, not the v1 focus. ### 6.1 "What is next?" β€” prior art & synthesis > Distilled from ~10 years of the owner's thinking β€” predating the ZK by ~5 years β€” chiefly the owner's direct account (this design conversation), plus `zk/mole/20240202113646.todo.md` (the most developed written framework), daily logs (`zk/home/2023-09-01.log.md`, `2023-10-12.log.md`), and `zk/payrix/friction_log.md`. Prior tool code may exist at `github.com/eblume/{mole,hermes}` (check detached-head branches β€” several restarts); the owner expects little reusable there beyond what's captured here. #### History: Hermes β†’ Mole β†’ heph (and the lessons) - **Hermes β€” the "time accountant" (β‰ˆ earliest).** Goal: a *timeline*-based system fed "blueprints" of what a day should look like, with rules like *"check email twice a day, unless a high-priority email comes in β€” but even then no more than once an hour,"* continuously re-solving the calendar in real time. Built a working prototype using a **SAT solver (OR-tools)**. **Killer feature deduced: a quick, meaningful answer to "what is next" β€” here, the next thing on the calendar.** **Why it failed:** the calendar became a *battle zone*, swinging wildly every few minutes as the solver re-resolved the dependency graph β†’ impossible to plan around. **Lesson + salvage:** there may be fruit here as a future, *opt-in* heph **planning mode**, but real-time auto-rescheduling of a visible calendar is anti-useful. - **Mole β€” "whack-a-mole" rules engine.** A rules engine that constantly re-evaluated rules and materialized Todoist tasks (canonical first test: *"is there an active task 'whack the mole'? if not, make one"*). **"What is next" = "what Todoist says is available."** Worked reasonably. **Why it stalled:** Todoist accumulated **too many tasks**, so *choosing the actual next thing* became the whole problem again. This pushed the owner to design the Todoist project/priority/filter discipline in Β§6.2. **The deeper lesson:** Mole's automation **wasn't adding much value over the project/priority discipline itself** β€” so the owner *turned Mole off and just kept using Todoist*. The manual discipline was the real engine. - **Today.** No automation beyond blumeops' **read-only** `blumeops-tasks` mise poller. Just Todoist + the Β§6.2 discipline β€” but as the direct descendant of all the above. **Implication for heph:** the value is in *embodying and strengthening the discipline* (and finally fixing the weak note↔task link), **not** in clever auto-scheduling or a rules engine. Earn any automation only after the discipline is faithfully supported. #### "What's next?" is three questions, not one The owner's framework splits the question by **mode**: - **Tactical** β€” blank-slate "what do I do *right now*?" Stay in the editor/terminal; capture interruptions and resume in **milliseconds** ("save state and walk away in milliseconds"). This is the mode a single ranked list serves. - **Strategic** β€” "I want to do X β€” what's the *next step*, or *where was I*?" Exploring a goal, generating the sub-tasks to reach it. - **Organizational** β€” "I want to do X β€” help me *plan* the steps." Project-level enumeration of what needs doing, not how. πŸ”’ **REAFFIRMED.** The owner still strongly identifies with this split (the earlier "maybe" was conflating it with the unrelated CLI-first *process* note on the same page). The clean operational test, in the owner's words: **"looking at a project's list of todos = Organizational; deciding which to work on = Strategic; working a todo = Tactical."** Escalation flows upward (project-infra work can become Strategic, then Tactical). **How heph embodies it** β€” as **named modes/views in the nvim plugin**, *not* an inference engine that guesses your mode: - **Tactical (execution view):** you are "tactically engaging" a specific committed task. Loads its canonical context + sub-items into a **goal stack**; millisecond interrupt-capture pushes new context items onto the stack; "smallest amount of contextual guidance and next steps to stay productive." Includes **Chore Mode** (synthesize a goal stack from a query β€” e.g. the `Chores` project β€” and chug through one by one). - **Strategic (planning view):** editor present but the goal is to *explore/decompose*, not edit. "To do X I must also do Y and Z." Produces a fleshed-out task with ready sub-items so Tactical can grab and go. **When Strategic completes, no work has been done** β€” only new/changed tasks, docs, links. - **Organizational (survey view):** project-level enumeration β€” *what* needs doing, not *how*. - **Shared primitive β€” the goal stack** (see Β§6.3) spans Tactical and Strategic; transitions between modes are first-class (Strategic *produces* β†’ Tactical *consumes*). #### The real cost is resumption, not ranking The recurring pain in the logs is **reorienting after a context switch**, not deciding raw priority. Design rules that follow: - **Resumption hint invariant:** when work pauses on a task/todo, it should leave at least one concrete next-step so future-you can re-enter quickly. heph should make capturing/β€œleaving a breadcrumb” frictionless and surface it first on return. - **Narrative > list:** daily logs proved more motivating and contextual than flat task lists ("a log is the way"). Journaling/log nodes are first-class, and "what is next" should be able to lean on recent log narrative, not just task metadata. #### The "weak link" is heph's reason to exist The #1 unsolved problem: the **fragile, clunky coupling between Todoist and notes** (manually typing `nb o 45` into task descriptions). heph's thin-task ↔ auto-created canonical-context-doc model is the direct fix: the link is structural, not a hand-typed string. #### Mapping prior taxonomy β†’ heph entities | Prior (`nb`/Todoist) | Meaning | heph | |---|---|---| | nb-project | grouping; completing all members completes it | `project` node | | nb-todo | planning complex multi-step work; has "a single unified context log" | `task` + its **canonical context `doc`** | | nb-task | immediate, short-lived "might eventually be done"; checking off = "no longer might be done" (not necessarily *completed*) | thin `task` node | | Todoist | "deciding what is next and knowing what needs to be done now" | heph's "what is next?" engine | | Calendar | scheduling / future | due dates (+ later calendar integration) | > πŸ”’ **Preserved.** The nb-task semantic β€” checking off means "no longer outstanding," *not* "accomplished" β€” carries into heph. **Context items are `outstanding` / `not-outstanding`** (see Β§6.3). For **committed tasks**, heph distinguishes end-states: **done** (accomplished) vs. **dropped/dismissed** (let go, e.g. during a Blue review). Both are "not outstanding"; the distinction is kept for honesty and history. #### Process steer (now historical) The owner's old rule β€” "avoid ncurses and interactive UIs; write atomic code and call it manually with subcommands… until a strong pattern emerges" β€” was a *focus device for hand-building tools*. It no longer constrains heph: we go **straight to the nvim org-mode plugin backed by the Rust server** (the patterns are now well understood; see Β§6.2/Β§6.3). The CLI remains as a secondary/utility surface. #### Distilled requirements for heph's "what is next?" β€” *to confirm/refine* 1. **Mode-aware** (Tactical / Strategic / Organizational), not a single global sort. 2. **Sub-millisecond capture** and fast state-save (Tactical interrupt handling). 3. **Resumption-first:** on returning to a task, surface the last breadcrumb/next-step before anything else. 4. **Context-coupled:** every task one keystroke away from its canonical context doc and onward links. 5. **Log-aware:** can factor in / surface recent daily-log narrative. 6. **A sane default ranking** for the Tactical blank-slate case β€” the concrete model is Β§6.2, not the blumeops-tasks placeholder. ### 6.2 The lived priority discipline (the working basis for heph) > πŸ”’ **This is the real model.** The owner has used this Todoist discipline daily for years; Mole was eventually *turned off* because this discipline outvalued the automation. heph should **embody and strengthen** it. Captured verbatim-in-spirit from the owner's account. **Projects = contexts (a task has exactly one).** Examples: `Life` (catch-all), `Work`, `Chores`, `Maintenance`, `Outside`, `Allison` (wife), `Errands`, `Cooking`, `Health`. A project *contextualizes* a task; it is not a plan. **Attention-state (thought of by color, not number):** | State | Meaning | |---|---| | **White** (default) | Able to be done **once its do-date arrives**. (See do-date semantics below.) | | **Orange** | **Top of mind.** Keep **≀ 6**, reassessed *every day*. Goal: you know your top-of-minds *without looking at the tool*. | | **Red** | Top of mind **+ there will be a consequence if not done ASAP**. Red means **the existence of a consequence, NOT its severity/importance.** Flagging by *importance* is a trap β†’ anxiety (everything due feels important). | | **Blue** | **On Deck** (backlog). Doable, but deliberately *cooling off*. The pressure-relief valve that keeps the working set light. | **Do-date, not due-date (key insight):** "due date" means **DO date** β€” *don't do it before this date*. Optionally a separate **"Late On"** marker for when it actually becomes a problem. **Due dates are a terrible planning tool** β€” calendar-style deadlines make everything "yell for attention" constantly. heph must treat the do-date as *earliest actionable*, not a deadline alarm. **Working-set discipline (the healthy tensions β€” a feature, not a bug):** - Keep White+Orange+Red ≀ **~30 total**, or the system collapses into noise. Keep Orange ≀ **6**. - Push freely to Blue to stay light; periodically **review Blue: keep vs. let go** (this is where "drop/dismiss" happens). Target On-Deck **< 100** (owner is at ~149 and hates it). - These tensions **map the owner's emotional state to the task system** and should be *surfaced honestly*. heph must avoid **two failure modes**: (1) **"fake happy"** β€” telling you everything's fine when it isn't; (2) **overwhelm** β€” yelling that you're buried until you freeze and make no progress. Reflect true state; help maintain the tensions. **Filters = saved views (queries over the above):** - **Top of Mind** β€” all Red + Orange. - **Tasks / Work Tasks** β€” Red/Orange/White that are *actionable now* (do-date arrived), scoped by project. - **Chores** β€” like Tasks but from `Chores`, *slightly tuned down* so the (near-universally recurring) chores don't become a nuisance. - **On Deck** β€” all Blue. - **Schedule** β€” recurring checklists from a few projects (e.g. *Morning Routine* β†’ brush teeth, feed birds, coffee, medicine). The owner uses *this particular filter* sparingly, but the underlying **recurring-checklist mechanism is IN SCOPE for v1** (see Β§3.3): the only way to be sure we avoid Todoist's mistake is to model it up front. **Each recurrence gets a fresh checklist instance; completion never carries forward.** **heph's default "what is next?" (Tactical blank-slate)** therefore β‰ˆ Top of Mind (Red first β€” by *consequence*, then Orange) **+** White items whose do-date has arrived, scoped to the current project/context, **with Blue hidden**. Concise, honest, light. > πŸ”’ **DECIDED ranking mechanics ([[tech-spec]] Β§7):** `do_date` is a *boolean candidacy filter only* (null β‡’ "now"), **never** an urgency input; **`late_on` is the sole urgency signal** (a global "now a problem" top tier); within a band, FIFO by `created_at` β€” **age never becomes urgency**. The order is expressed as a reorderable named-dimension list so it can be retuned without touching the engine. ### 6.2.1 Todoist study (2026-06): empirical confirmation + new requirements > πŸ“Š **Snapshot of the owner's live Todoist** (read via the blumeops `blumeops-tasks` API pattern), to ground heph against how the discipline of Β§6.2 *actually* manifests at scale. 387 active tasks, 34 projects. **Confirms the model:** - **Projects = contexts, used heavily and hierarchically.** 34 projects organized into top-level life-areas (`Life`, `Work`, `Coding`, `Camano`, `Culture`) β†’ sub-projects (`Child`, `Blumeops`, `Daily Routine`, `Maintenance`, `Movies`…). The *hierarchy* is new information (Β§6.2 listed projects flat). - **Huge backlog, tiny hot set.** Priority split p1=2, p2=8, p4(default)=229, p3=148 β€” i.e. the genuinely "hot" set is ~10, the rest is white/blue backlog. The dominant *activity* is therefore **triage of a large set**, not editing single tasks β€” a direct argument for an interactive agenda surface (Β§4, [[tech-spec]] Β§8.1). - **Do-date, not due-date β€” validated to the hilt.** **Zero** Todoist *deadlines* are used; only 107/387 carry a due (do) date. `late_on` will be genuinely rare. - **Recurring checklists are real** ("Prep For Day", every day @ 07:00, 6 children) β€” exactly the Β§3.3 reset-each-occurrence mechanism. - **Daily rituals exist as tasks**: "Excess Tasks to On Deck" (= the blue keep/drop review), "Coordinate with Allison", "Check Personal Email" (= orange reconfirm). The Β§6.2 working-set rituals are alive in the data; the TUI should make them first-class filters. **The strongest validation β€” descriptions are already heph.** Real task descriptions contain `see [[review-services]]`, `See [[run-1password-backup]]`, `docs/how-to/plans/migrate-forgejo-from-brew`, plus URLs and phone numbers. The owner is **hand-rolling task↔knowledge-base wiki-links inside Todoist descriptions that cannot resolve.** heph's task β†’ canonical-context-`doc` fusion (Β§6.3) is precisely the thing being worked around. (89/387 tasks carry a description; the task description ≑ heph's canonical context doc body.) **New requirements surfaced:** - **Project hierarchy** β€” a project needs an optional parent project. (Model it with the existing `parent` link; deeper hierarchy-aware `scope` is a later refinement.) - **Natural-language recurrence** β€” 95/107 dated tasks recur, expressed as `every 3 days`, `every 6 months`, `every workday`, `every April 15`, `every other wed`. heph stores RFC-5545 RRULE; capture should accept the common NL forms and compile them (the easy subset; time-of-day like "at 08:00" deferred β€” heph's `do_date` is date-grained for ranking). - **Tags are noise** β€” 7 labels, **5 task-uses across 387**. Confidently **defer** a tag surface; it is not load-bearing. **The saved filters, verbatim (the basis for heph's filter views, [[tech-spec]] Β§8.2):** | Filter | Todoist query | |---|---| | Top of Mind | `(p1\|p2) & (no date \| today \| overdue)` | | Schedule | `(today \| overdue) & !no time` | | Tasks | `!p3 & (no date \| today \| overdue) & (p1 \| (p2 & !#Work) \| !(#Daily Routine \| #Work Routine \| #Chores \| #Camano Chores \| #Work \| ##Culture \| #Camano Info)) & !subtask` | | Work Tasks | `#Work & !p3 & (no date \| today \| overdue) & !subtask` | | Chores | `(today \| overdue \| no date) & (#Chores \| #Camano Chores)` | | On Deck | `p3 & (no date \| overdue \| today)` | These define both the **agenda slices** heph must offer ([[tech-spec]] Β§8.2) and, by what "Tasks" *excludes* (`##Culture` = Movies/Books/Theater; `#Camano Info`), which contexts are **not tasks at all**. **Reference contexts β†’ wiki, not tasks (decided 2026-06).** The `##Culture` subtree (Movies/Books/Theater) and `#Camano Info` are reference lists (films to watch, books to read, contractor phone numbers), deliberately excluded from every task filter. In heph they belong as **`doc` pages**, not committed tasks β€” otherwise they flood "what is next?". On the initial Todoist import these 5 projects (~59 items) were reclassified into one wiki list-doc each and the task/project nodes tombstoned. (A useful general principle: a "task" that never appears in any of your filters is probably a note.) **Future direction (noted 2026-06, not scheduled):** - **Chores as a first-class feature.** Chores want **different do-date / recurrence semantics** from regular tasks (the every-N-days "do it again sometime after" rhythm, tuned-down urgency). Rather than model them as a `#Chores` *project* you scope to, make "chore" a **first-class task property** (kind/flag) with its own scheduling rules β€” which retires the `#Chores` / `#Camano Chores` projects (and the Camano split) entirely. The interim `Chores` filter view ([[tech-spec]] Β§8.2) is project-scoped until then. - **Drop the `Schedule` filter.** Schedule was Todoist's timed-routine view (`!no time`); heph's `do_date` is date-grained, so it is **omitted** (not approximated). It's entangled with the chores rework (timed routines), so reconsider both together if/when time-of-day lands on tasks. ### 6.3 Two kinds of task: commitments vs. context items > πŸ”’ **DECIDED (shape).** A **commitment axis** orthogonal to the Β§6.2 attention-states. - **Committed task** β€” long-lived, strongly tied to context (e.g. "Complete PLAT-1234", "Build a nursery"). **Participates in Β§6.2**: has an attention-state, a project, a do-date; is a candidate for "what is next"; may carry sub-structure and a web of context. - **Context item** β€” ephemeral, created mid-flow ("six things before this is done", "oh crap, another" pushed on the stack). **Does *not* enter the Β§6.2 scheduling system** β€” it is *part of its containing task's context*, with only **outstanding / not-outstanding** state. Never appears in global "what is next." - **Promotion** is first-class: a context item can be **promoted to a committed task** when you decide to actually schedule it (the old "nb-todo β†’ Todoist-task on activation"). - **Ephemeral β‰  deleted:** deletes effectively never happen β€” tombstones + a dedicated cleanup mode only. "Ephemeral" is lifecycle/visibility, not storage. (This also keeps the CRDT sync simple.) - **The goal stack** is a **session-scoped, synthesized construct**: seeded from a committed task (Tactical) or from a query over contexts (Chore Mode β€” "grind on some chores"). It holds committed tasks and/or context items for the duration of a work session. #### Editing surface for context items β€” *deliberately deferred to the prototype* > πŸ”‘ **DECIDED β€” Fork A "index" backend (the affordance is still trialed both ways).** The stored source of truth is the `doc` **body markdown**: context items are `- [ ]` lines in it, and a context item's checked state *is* the `[ ]`/`[x]`. They are **not** synced nodes β€” each replica derives a **local index** from its converged CRDT body, so they converge for free (no divergent ULIDs across offline edits β€” the original sync trap). Identity is pinned only at **promotion**, which mints a real committed `task` node and rewrites the source line into a link to it. This collapses the hardest sync problem into "the body CRDT already converges" and matches this section's "ephemeral, low-stakes identity" intent. **What's still trialed both ways is purely the nvim *affordance*** (Option A checkboxes-in-body vs. Option B command-driven capture) β€” the backend is shared, so ergonomics can be judged in the prototype. See [[tech-spec]] Β§4.3, Β§5. - **Option A β€” checkboxes in the body, derived on save.** Edit the task's canonical context doc as plain markdown; `- [ ]` lines are materialized into context-item state on `:w` (`BufWritePost`), reusing the wiki-link extraction machinery. **No real-time scanning needed** (debounced live updates are optional polish). Remote CRDT edits are *pushed* from the daemon to reconcile an open buffer. Item identity is low-stakes (reword = tombstone-old + add-new) since items are ephemeral; identity is pinned only at **promotion**. Most "personal org-mode"-native; millisecond capture = "type a line." - **Option B β€” command-driven capture, rendered.** Capture via a leader chord + `vim.ui.input` (no raw checkbox typing); the new item is still written as a `- [ ]` line into the body-backed store (Fork A), then **rendered** inline via virtual text/extmarks or a transient **floating window** (avoids the disliked persistent cutaway). Heavier capture; more UI to build; promotion identical. - Lean: **A** for capture flow, but **build the shared node backend first** and trial both affordances in the plugin. > **Prior-art note (multi-state checkboxes):** the owner's obsidian.nvim config uses checkbox states `" ", "x", ">", "~", "!"` (todo / done / forwarded / ~ / important). heph's task/context-item state model should accommodate richer-than-binary states (at least the done/dropped/outstanding set in Β§6.1; possibly forwarded/deferred). ### 6.4 The Log workflow > πŸ”’ **In scope for v1** (daily journal certainly; per-task logs as the model allows). Logs embody the "narrative > list" insight and the resumption-hint invariant from Β§6.1. Two related shapes: - **Daily journal.** Today driven by the `zkd` abbrev β†’ `Obsidian dailies`: a telescope picker to create/open dated journal buffers. heph keeps this as a **first-class** feature: `journal` nodes keyed by date, a picker to jump to today / recent / create. The daily journal is the catch-all narrative stream. - **Per-task log.** Sometimes a task wants an append-only "here's what I was working on" side-commentary. It **half-belongs to the task's context and half doesn't** β€” so model it as a **separate buffer/node from the task's canonical context doc**, optionally present only for tasks that need longer-duration commentary. Properties: - **Append-only** (entries accrete; you don't rewrite history). - **Dispatchable from an isolated context:** you must be able to fire a log entry *without leaving / losing your place in* the context buffer you're navigating β€” i.e. append to the log from elsewhere (a quick-capture into the task's log), while the context buffer stays where it is. (This is the ergonomic crux; likely a quick-capture command targeting "the current task's log".) - The per-task log is a strong candidate to *be* the resumption breadcrumb store (Β§6.1). ❓ Open: is the per-task log a distinct node `kind` (`log`, append-only), or just a `doc` flagged append-only and linked `log-of` β†’ task? Leaning a dedicated append-only `log` kind to make "dispatch an entry" a cheap, well-defined op. ### 6.5 The Wiki / KB workflow > πŸ”’ **First-class in v1.** This is the "view all my contexts as one big personal wiki" surface β€” the direct replacement for obsidian.nvim. The owner estimates ~90% of the value comes from a few well-worn primitives, so heph.nvim should reach **obsidian.nvim feature parity** on day one: - **Follow `[[wiki-links]]` on ``** to jump between context docs (the core navigation gesture). - **Telescope-based** search (`rg`), quick-switch between docs, tag browse, **backlinks**, outgoing **links** β€” mirroring the owner's `o*` verbs (to be rebound under heph's own prefix). - **Logical layout + tagging** of context docs so the corpus is browsable/queryable as a whole. - New-doc-from-query and insert-link from the picker (obsidian.nvim's ``/``). heph.nvim **replaces** obsidian.nvim outright β€” the owner already rarely opens Obsidian itself. This workflow is the knowledge-base analogue of the Organizational task view: surveying and traversing the `doc` graph. ### 6.6 The Calendar workflow > ⏳ **Deferred (later version) β€” but scaffold now, don't paint ourselves into a corner.** Ties closely to **Strategic** mode (planning against time). - The owner uses **Calendar.app** bound to a **Gmail calendar** (almost certainly **CalDAV**; Gmail also exposes ICS/WebCal feeds β€” to confirm when implemented). - Eventual goal: **integration between calendar views and task views** β€” enter the system *via* the calendar, see tasks/do-dates in temporal context. - ⚠️ **Hard-won gotcha:** enabling Todoist's gcal integration **flooded the calendar with a million events** because of recurring rules. heph must **never explode recurrence into individual calendar events**. This echoes the Hermes "battle zone" failure (Β§6.1) β€” calendar integration must be *read-mostly, careful, de-duplicated, and opt-in*, not a firehose. - **Scaffolding guidance (decisions to keep clean now):** keep a crisp separation between *tasks/do-dates* and *calendar events*; model recurrence as RRULEs that are **expanded lazily for display, never materialized as stored events**; design sync so a future **read-only CalDAV ingestion** (calendar β†’ heph) is easy, and treat any heph β†’ calendar export as a later, explicitly bounded, non-exploding feature. ## 7. Hosting & Deployment Reuse the established blumeops patterns (πŸ”’ confirmed by repo conventions): - Containerized service deployed to **k3s** (ringtail cluster) via **ArgoCD app-of-apps** + **Kustomize** raw manifests (no Helm/Flux). - Secrets via **1Password + external-secrets operator** (`ClusterSecretStore onepassword-blumeops`). - Image built via **Dagger + Forgejo Actions**, pushed to **Zot** registry (`registry.ops.eblu.me`). - ❓ Rust build approach for the container: Nix (like kingfisher) vs. Dagger-native `cargo` multi-stage build. - ❓ Persistence for the central instance's SQLite β€” NFS PVC (sifaka) vs. local PV. (SQLite over NFS has known locking caveats.) - ❓ How do clients reach the central instance β€” Tailscale tailnet only, or public via Caddy/Fly proxy? ## 8. Technology Choices (to decide) - ❓ nvim plugin stack: Lua + JSON-RPC over a socket to `hephd` (with daemonβ†’plugin push) vs. CLI shell-out. Telescope as the picker dependency. - ❓ Web framework (Axum + HTMX/Askama? Leptos? Dioxus?). - ❓ SQLite access layer (sqlx, rusqlite, SeaORM, Diesel) and migrations. - ❓ CRDT / sync library (Automerge-rs, Yrs, bespoke op-log). - ❓ Markdown parser/renderer (pulldown-cmark, comrak) and wiki-link / checkbox extraction. ## 9. Open Questions Summary ### Decided - βœ… Source of truth: **SQLite-primary**, markdown as the body payload, with a directory `export` mode. Clean break from Obsidian-the-app. - βœ… Note/task model: **typed node graph** β€” thin `task` nodes, rich `doc` nodes, connected by typed links. - βœ… Front-end: **`hephd` daemon** with thin clients over a local socket; **nvim plugin** (`heph.nvim`) as the primary surface, **CLI** secondary; **web UI** on the hub (later). - βœ… Sync (shape): **hub-and-spoke, op-log + CRDT**, HLC ordering, OIDC/Authentik auth, conflict queue for the ambiguous remainder, over a **targetable backend** (tech-spec Β§3.1). ### Decided since (B/C/D) - βœ… Context surfacing v1: **explicit links only** + auto-created canonical context doc per task + nvim fuzzy search. Inferred/semantic deferred. - βœ… **v1 supports local-only *and* distributed via a targetable backend** (tech-spec Β§3.1): `Store` trait + three modes (`local`/`server`/`client`) + exclusive-lock handoff over a shared SQLite file. **Offline-first sync with automatic merge + conflict queue** + **full OIDC/Authentik auth with per-user isolation** are all IN v1 (tech-spec Β§12/Β§13). Local-only is a first-class config. Web UI and k3s deployment are later (hub binary is built deployable). - βœ… Security v1: **OIDC/Authentik auth + per-user isolation** is the boundary; plain SQLite at rest (no encryption). May revisit. - βœ… Migration v1: **clean break**, no ZK import / no Todoist bridge. heph is system of record day one. - βœ… **License: All Rights Reserved / private.** Open-sourcing considered later. ### Decided (workflows) - βœ… heph organized around first-class workflows (Β§6): **Task** (what-is-next), **Log**, **Wiki/KB**, **Calendar**. - βœ… **Log** (Β§6.4) in scope: daily journal (picker over dated `journal` nodes, Γ  la `zkd`/`Obsidian dailies`) + optional **append-only per-task work-logs** (separate buffer from the canonical context doc; dispatchable without leaving the context buffer). - βœ… **Wiki/KB** (Β§6.5) first-class: heph.nvim reaches **obsidian.nvim feature parity** (follow `[[links]]` on ``, telescope search/quick-switch/tags/backlinks/links) and **replaces** it. - βœ… **Calendar** (Β§6.6) deferred but **scaffolded carefully**: never explode recurrence into stored events (Todoist-gcal-flood / Hermes lesson); keep tasks vs. events separate; lazy RRULE expansion; design for a future read-only CalDAV ingest. ### Decided (workflow model) - βœ… "What is next?" rests on the **lived priority discipline** (Β§6.2): projects-as-contexts; attention-states White/Orange/Red/Blue (Red = *consequence*, not severity); **do-date not due-date**; working-set tensions surfaced honestly (avoid "fake happy" *and* overwhelm). - βœ… **Tactical / Strategic / Organizational** reaffirmed as **named modes/views** in the nvim plugin, sharing a **goal-stack** primitive (Β§6.1). - βœ… **Commitment axis** (Β§6.3): committed tasks (in Β§6.2 system) vs. ephemeral context items (outstanding/not-outstanding, scoped to a container); **promotion** is first-class; **no hard deletes** (tombstones + cleanup mode). - βœ… Task end-states: **done** vs. **dropped/dismissed** (both "not outstanding"). - βœ… Prototype goes **straight to the nvim plugin + Rust server**; CLI is secondary. - βœ… **Rust stack RATIFIED** (Β§11.2): rusqlite + tokio/JSON-RPC + pulldown-cmark + ulid + rrule + clap. - βœ… **Recurrence + recurring checklists IN SCOPE for v1** (Β§3.3): RRULE-driven; **each occurrence gets a fresh checklist instance, completion never carries forward** (avoids Todoist's corner). ### Still open *Prototype-blocking (resolve at kickoff β€” see Β§11 / tech-spec):* 1. **CRDT library** for body merge: `yrs` (leaning) vs `automerge`; bespoke op-log/HLC for scalar fields either way. 2. **Hub network transport** (tech-spec Β§6.1): `axum` HTTP/JSON (leaning) vs gRPC; sync propagation cadence (push vs periodic pull); **device-id provisioning** (how a spoke mints its `origin` id and registers with the hub). *Resolved in the second-pass review (2026-05-31), now folded into [[tech-spec]]:* 3. βœ… **Recurrence β†’ roll-forward in place** (Β§3.3) β€” occurrence-instances rejected (would explode into a node-per-occurrence under Fork A); advance to the next RRULE instance after `now`, skipping misses. 4. βœ… **Context items β†’ Fork A "index" backend** (Β§6.3) β€” body markdown is the source of truth; context items are a local derived index, not synced nodes; identity at promotion. The nvim *affordance* (A vs B) is still trialed in-prototype. 5. βœ… **Identity β†’ deterministic ids for `journal`/`tag`** (Β§3.1); random ULID for content nodes/`project`. 6. βœ… **Mode/sync orthogonality** (Β§4): backend + inbound-listener axes plus optional `hub_url`; everyday device = `local` + `hub_url`. 7. βœ… **Auth/owner** (tech-spec Β§13): nullable `oidc_sub`, local user for local-only, hub-authoritative identity, one-time pre-first-sync adoption rewrite. 8. βœ… **Ranking** (tech-spec Β§7): `do_date` boolean candidacy filter only; `late_on` sole urgency signal (global tier); FIFO tiebreak; order as a reorderable named-dimension list. 9. βœ… **Modes** (Β§6.1) are plugin-side compositions of mode-agnostic daemon primitives; added `list` (Organizational) and `log.tail` (resumption breadcrumb). *Not prototype-blocking (later phases):* 5. Per-task **log** representation: dedicated append-only `log` node kind vs. `doc` flagged append-only (Β§6.4). 6. Calendar protocol confirmation (CalDAV vs ICS/WebCal) and the careful, non-exploding integration (Β§6.6). 7. Web front-end style (SSR + HTMX vs. WASM SPA). 8. k3s deployment specifics: Rust container build (Nix vs Dagger-native cargo); central SQLite persistence (local PV vs NFS); client reachability (tailnet-only vs public). 9. Encryption at rest / E2E (currently out; may revisit). ## 10. Roadmap (provisional) - **Phase 0 β€” Design** (this document + [[tech-spec]]): done enough to build. - **Phase 1 β€” v1 prototype (a single C1 effort, deliberately β€” a one-shot test of delivering a high-complexity prototype from the spec; built in TDD slices):** `heph-core` (model, schema, extraction, recurrence, "what is next", `Store` trait, op-log/HLC/CRDT merge) β†’ `hephd` **local mode** β†’ **server + client modes (+ lock handoff)** β†’ **offline sync + conflict queue** β†’ **OIDC/Authentik auth + per-user isolation** β†’ `heph.nvim` + `heph` CLI. Local-only works standalone; runnable client/server + offline sync on the tailnet. The build order doubles as the cross-session resume tracker (next un-green slice = where to resume). C2/Mikado is *not* used: it sequences prerequisites against existing code under test, and this is greenfield delivery from a complete spec; follow-up C1s or a C2 refactor come later as needed. **Phase 1 progress** (branch `feature/v1-prototype`, PR #1; 102 tests green as of 2026-06-01 β€” see [[tech-spec]] Β§14 for the per-area tracker): - [x] `heph-core` library β€” schema/`Store`/`LocalStore`, extraction, tasks/links/canonical-context + wiki-link materialization, "what is next?" ranking, recurrence roll-forward + per-task logs. - [x] `hephd` **local mode** β€” file lock + JSON-RPC over a unix socket (tokio blocking pool); `heph` CLI + `export`. - [x] Local query surface β€” `list`, `health`, `journal`, `search` (FTS5). - [x] **Sync engine (no network yet)** β€” HLC + device origin; op-log recording; `apply_op` merge (LWW + conflict queue, OR-set links, monotonic tombstones, idempotent); two-replica convergence proven; `adopt_owner` (basic Β§13). - [ ] **Resume here β†’** yrs text-CRDT for bodies (upgrade body merge from LWW); then **server/client modes + network push/pull** (transport over the existing engine); then **OIDC/Authentik auth**; then **`heph.nvim`**. - **Phase 2 β€” k3s deployment:** Daggerβ†’Zot image, ArgoCD app + Kustomize manifests, external-secrets; hub on blumeops. - **Phase 3 β€” Web UI** on the hub. - **Phase 4 (later, optional)** β€” calendar integration (careful CalDAV); migration from ZK / Todoist; iOS / Apple Watch voice capture; Hermes-style planning mode. ## 11. v1 Prototype β€” Scope, Stack, and Kickoff > The handoff target for the next context session. Items here are **proposals to ratify**; once ratified, a fresh session can build directly from this. > **The canonical, implementation-ready scope/stack/schema/API now live in [[tech-spec]].** This section keeps the *intent* and the kickoff process; defer to the spec for details (and update the spec when decisions change). ### 11.1 Scope β€” *local-to-distributed via a targetable backend* (ratified) v1 **supports the full spectrum** β€” pure local-only through full distributed β€” chosen by configuration, not a rebuild. A `Store` abstraction backs three runtime modes (`local` / `server` / `client`) with an exclusive-lock handoff over a shared SQLite file (tech-spec Β§3.1). - **In:** the full data model (Β§3, Β§6) incl. **recurrence + recurring checklists** (Β§3.3); the "what is next?" engine; the **targetable backend + all three modes** with the same-file local↔server lock handoff; **offline-first op-log + CRDT sync with automatic merge and a conflict queue** for local-backed replicas (tech-spec Β§12); **OIDC/Authentik auth with per-user isolation** on the server endpoint (tech-spec Β§13); the `heph.nvim` + `heph` CLI surfaces. - **Out (later, scaffolded):** the **web UI** (hub serves sync only in v1); **actual k3s deployment** to blumeops (hub binary is built deployable; ArgoCD/Kustomize/Dagger is a fast-follow); calendar; iOS/Watch; inferred context; P2P-over-tailnet fallback; persistent goal stack (an in-plugin session stack suffices). **Why this v1:** the backend abstraction, offline/merge behavior, and auth model are *architecturally load-bearing* β€” bolting them on later means reworking the core. The owner wants them designed in from the start, while keeping **local-only a first-class, friction-free configuration**. (Recurrence-with-checklists is in for the same reason β€” see Β§3.3.) Natural build order: `local` mode first (simplest), then `server`/`client`, then sync, then auth. ### 11.2 Stack β€” βœ… RATIFIED Core: `rusqlite` (bundled) + migrations Β· `tokio` + JSON-RPC/unix-socket (local) Β· `ulid` Β· `rrule` Β· `pulldown-cmark` Β· `clap` Β· `anyhow`/`thiserror` Β· `tracing`. Distributed/auth additions (some to confirm at kickoff): text CRDT `yrs` (vs `automerge`) for bodies + bespoke op-log/HLC for fields Β· hub transport `axum`/`reqwest` Β· OIDC via `openidconnect` + `keyring`. Full detail and the SQLite schema: **tech-spec Β§4.5, Β§10, Β§12, Β§13**. ### 11.3 First-session kickoff checklist 1. `mise run ai-docs`; read **[[tech-spec]]** (the build spec) and this doc's Β§3/Β§6 for rationale. 2. **Classify as a single C1** (deliberate β€” a one-shot test of delivering this high-complexity prototype from the spec; C2/Mikado is for sequencing prerequisites against *existing* code, not greenfield delivery). Single long-lived feature branch + early draft PR, docs-first, push as you go ([[agent-change-process]]). The Β§11.1 build order is the resume tracker; the hairy slices (sync/CRDT) may spin off follow-up C1s rather than blocking the prototype. 3. Scaffold the cargo workspace + `heph.nvim` skeleton; **fill the AGENTS.md Project Structure section** (last template TODO). 4. Build outward in testable slices (TDD, tech-spec Β§2/Β§9): `heph-core` (schema, model, extraction, recurrence, "what is next", `Store` trait, **op-log/HLC/CRDT merge**) β†’ `hephd` **local mode** (LocalStore + lock + local RPC) β†’ **server + client modes** (network endpoint, RemoteStore, lock handoff) β†’ **sync + conflict queue** β†’ **OIDC auth** β†’ `heph.nvim` + `heph` CLI. 5. Confirm the kickoff-open picks: CRDT lib (`yrs` vs `automerge`), hub transport (`axum` vs gRPC) + propagation cadence + device-id provisioning. (Recurrence = roll-forward and the context-item backend = Fork A are now decided; only the nvim *affordance* A vs B is trialed in-prototype.) ## Related - [[tech-spec]] β€” clean implementation-facing technical specification distilled from this document - [[ai-assistance-guide]] β€” conventions for AI agents in this repo - [[agent-change-process]] β€” C0/C1/C2 change methodology