Commit graph

34 commits

Author SHA1 Message Date
8bd16664c4 docs(tui): note D delete/tombstone gesture (§8.1)
Some checks failed
Build / validate (pull_request) Has been cancelled
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 08:48:49 -07:00
bed2d6a8aa docs(tui): note recurring ↻ marker + inline selected-row detail (§8.1)
All checks were successful
Build / validate (pull_request) Successful in 25m8s
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 08:08:42 -07:00
ffec575249 docs(tui): FTS search done; move-to-project (needs task.set_project) is the last gap
Some checks failed
Build / validate (pull_request) Failing after 11s
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 07:41:12 -07:00
3099034d43 docs(tui): NL quick-add done; T3 remainder is search + move-to-project
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 07:33:37 -07:00
2e0e37f76d docs(tui): mark heph-tui daily-driver core built (§8.1, §14)
§8.1 status → "core built" with the implemented panes/gestures/handoff; §14
adds the heph-tui Done bullet, retargets the resume list to T3 (NL quick-add +
search), and bumps the count to 171 Rust tests. Changelog fragment for the TUI.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 07:23:18 -07:00
a5fc578525 feat(views): filter views (§8.2) — saved agenda slices
Some checks failed
Build / validate (pull_request) Failing after 18m44s
Make the owner's saved filters first-class so the agenda isn't one flat
list. `list` now takes a ListFilter predicate-as-data (heph-core::filter):
attention include/exclude sets, project-id scope, exclude_projects, and an
actionable do-date gate. New Store::view(name) resolves a built-in ViewSpec
— looking project names up to ids and subtree-expanding them through parent
links — then lists.

Five built-ins seeded from the Todoist queries (design §6.2.1): tom, ondeck,
chores, work, tasks (Schedule dropped — time-of-day isn't modeled on
date-grained do-dates). Surfaced as `heph view <name>` (no name lists them),
the `view` RPC + RemoteStore forward, and `:Heph view <name>` in nvim.

The list RPC/RemoteStore/CLI/heph.nvim migrate to the filter wire; legacy
--scope/--attention/--no-blue map onto it (nvim view.lua updated).

Tests: filter unit predicate, a views integration suite (subtree
scope+exclude, actionable gate, unknown-view error, absent-project empties),
a socket list/view dispatch test, two nvim e2e specs. 154 Rust tests + 18
nvim e2e green; clippy/fmt clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 06:39:07 -07:00
eb4a827700 docs: plan the filter-views slice + TUI coherence; note chores rework
Some checks failed
Build / validate (pull_request) Failing after 8m9s
- tech-spec §8.2 (new): filter-views slice — extend `list` to a data-expressed
  predicate (attention set, project-subtree/multi scope, exclude-projects,
  actionable toggle), five built-in views (`heph view <name>`) seeded from the
  owner's verbatim Todoist queries; the TUI's filter pane reuses them
- tech-spec §8.1: TUI filter pane = the §8.2 views; depends on that slice
- tech-spec §14: filter-views is the next slice (before heph-tui); CLI +
  daemon-service marked done
- design §6.2.1: record the verbatim filter queries + the reference-context →
  wiki reclassification, and note future direction: chores as a first-class
  feature (own do-date/recurrence; retire Chores/Camano-Chores projects) and
  dropping the Schedule filter (time-of-day not modeled)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 22:02:08 -07:00
0cfe627055 feat(cli): heph daemon — manage hephd as a launchd/systemd service
Some checks failed
Build / validate (pull_request) Has been cancelled
Surfaces are connect-only; the daemon now runs as an explicit OS service so it
can be shared without any surface owning its lifecycle.

- service.rs: heph daemon start/stop/restart/status/uninstall, idempotent;
  launchd LaunchAgent (macOS) / systemd user service (Linux); resolves hephd
  next to heph else on PATH; pure plist/unit render fns unit-tested
- main.rs: Command::Daemon handled before connecting (like auth)
- hephd: default socket is now a STABLE <data-dir>/heph/hephd.sock when
  XDG_RUNTIME_DIR is unset (was $TMPDIR — fragile for a persistent service;
  macOS prunes /var/folders and the path varied per session)
- tech-spec §14: CLI + daemon-service done entries

Verified live on macOS: start/restart/stop/uninstall + CLI reaches the store.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 21:14:50 -07:00
1315b9ce18 docs: daemon lifecycle becomes an explicit service (connect-only surfaces)
Some checks failed
Build / validate (pull_request) Has been cancelled
A surface-owned, auto-spawned daemon can't be shared once the CLI is also a
first-class client — so drop auto-spawn and manage the daemon as an OS service.

- design §4: daemon lifecycle = explicit OS service; surfaces connect-only
- heph-nvim.md: rewrite the daemon-lifecycle section (connect-only) + history
- new how-to/run-the-daemon.md (heph daemon start/stop/restart/status); indexed
- install-heph.md: post-install is `heph daemon start`; plugin no longer spawns
- tech-spec §14: mark the managed-daemon entry superseded
- changelog fragment

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 21:07:11 -07:00
0b32ed4397 feat(tooling): mise run import-todoist — seed a heph store from Todoist
All checks were successful
Build / validate (pull_request) Successful in 7m1s
Turn the one-off Todoist importer into a documented, repeatable mise task.
Self-contained (spawns its own hephd), dry-run by default into a throwaway
store, `-- --commit` writes into the real store after backing it up. Auth via
TODOIST_TOKEN or TODOIST_OP_REF (op://). Mapping per design §6.2.1:
project hierarchy (+ Inbox→unfiled), priority→attention by meaning, due→do-date,
NL recurrence, descriptions + sub-tasks→canonical-context doc.

- mise-tasks/import-todoist
- docs/how-to/import-todoist.md (+ how-to index, reference mise-tasks table)
- changelog fragment

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 20:15:05 -07:00
0aa7e725a5 docs: revise to a three-surface model (CLI/TUI/nvim) from a Todoist study
Some checks failed
Build / validate (pull_request) Has been cancelled
Study of the owner's live Todoist (387 tasks, 34 hierarchical projects)
grounds a surface-strategy revision: structured task fields suit CLI flags,
large-set triage suits an interactive TUI, and context/KB suits nvim — so v1
adopts three surfaces, each to its strength. Supersedes the earlier
"heph.nvim is the primary surface" framing.

- design.md: new §6.2.1 (Todoist study) + revised §4 (three-surface model)
- tech-spec §1/§8: reframe surface roles; new §8.1 (planned heph-tui agenda)
- tech-spec §14: reorder remaining work (CLI-complete task surface in progress,
  heph-tui next, nvim navigation polish, tags/hierarchy deferred)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 19:21:05 -07:00
7188daeb32 docs: refresh §14 tracker + README/AGENTS for the heph.nvim UX iteration
Some checks failed
Build / validate (pull_request) Has been cancelled
The plugin is built, installed, and well past 11a–11c. Record the post-11c UX
iteration (managed daemon + self-heal, follow-or-create, home/index, dailies
picker, interactive views, dev isolation, fully-Dagger CI) as done, and reset
the "not yet done" backlog to lead with the highest-value next work:
task-scheduling UX (do-date/late-on/recurrence from the editor), then more
surfacing (backlinks/tags/health/log-read), 11d (deferred), and the heph.nvim
repo split.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 12:27:43 -07:00
0462a6e43b heph.nvim: interactive next/list views (add, done, refresh) + key hint
Some checks failed
Build / validate (pull_request) Failing after 4m47s
The Tactical next and Organizational list buffers are now actionable:
- a  add a task from the list (prompt title + attention)
- d  mark the task under the cursor done
- r  refresh
- <CR> open the task's context (as before)

A dimmed key hint renders above the rows as a virtual line (extmark), so it's
discoverable without taking a task row. e2e covers add-from-list and
done-from-list via stubbed vim.ui.input/select.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 11:48:09 -07:00
d0930aa6a3 heph.nvim: :Heph journals — recent-days picker with preview + @create
All checks were successful
Build / validate (pull_request) Successful in 16m0s
A dailies picker (zkd-style): lists the last `journal_days` (default 7) days
newest-first, previews existing journals and shows "@create" for new ones, and
opens the chosen day (creating if new). Journals resolve by their ISO-date
title, so no new RPC is needed. picker.select gains an optional Telescope
preview pane. e2e covers the recent-days list (exists/@create across a month
boundary) and open-on-pick via a stubbed vim.ui.select.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 11:26:45 -07:00
e99c284941 heph.nvim: :Heph home — a base index/landing page for the zk
Some checks failed
Build / validate (pull_request) Failing after 5m3s
A single designated "home" doc (open-or-create by title, configurable via
opts.home, default "Home") — a stable landing page to grow a map of content
around. e2e covers create-on-first-open + idempotent reuse.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 11:14:12 -07:00
9249ca46a1 heph.nvim: follow-or-create wiki links + :Heph doc
Some checks failed
Build / validate (pull_request) Failing after 4m13s
Pressing <CR> on a [[wiki-link]] whose target doesn't exist now creates a doc
with that title and opens it (the zettelkasten gesture), and materializes the
source's backlink: if the source has unsaved edits, saving re-extracts and
links it (and persists the edits); otherwise the wiki link is added directly
(a no-op re-save wouldn't re-extract). Adds :Heph doc <title> to create a
standalone wiki entry. e2e covers both the saved-source and just-typed-source
paths.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 11:07:09 -07:00
e3db2ac550 heph.nvim: plug-and-play managed daemon (autostart, self-heal, client/server guardrail)
The plugin now manages its own hephd by default (autostart = true): if nothing
is serving the socket it spawns a local daemon against the default XDG paths,
kills only what it spawned on VimLeavePre, and self-heals — rpc.call retries
once through a respawn hook when the connection drops (the prior owner releases
the DB lock on exit, so a respawn can claim it).

- daemon.ensure() connects to an already-running daemon (any mode) or spawns one
  we own; stop_spawned()/is_managed() track lifecycle.
- A server/client daemon you started is always respected (spawn only when nothing
  serves the socket). autostart = false → connect-only, warns/errors if down,
  and clears the self-heal hook so it fails loudly.
- config: autostart defaults true; new `db` option; $HEPH_SOCKET / $HEPH_DB
  fallbacks isolate a dev Neovim onto a separate daemon + DB.

e2e: managed_daemon_spec covers autostart spawn, self-heal-after-kill, and
connect-only error. 10 specs green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 09:37:49 -07:00
fef0e82d26 ci: run build entirely through Dagger; drop prek from CI
All checks were successful
Build / validate (pull_request) Successful in 21m0s
The Forgejo k8s job image is a thin Alpine + Dagger orchestrator (no native
Rust/Neovim toolchain, no prek) with a DinD sidecar — so CI was failing on
step 1 (`prek: command not found`) on every run, and a native cargo hook
wouldn't work there either.

- build.yaml now runs `dagger call check` (cargo fmt/clippy/test on
  rust:1-bookworm) + `dagger call test-nvim` (build hephd + headless e2e).
- New Dagger `check` function; CARGO_BUILD_JOBS capped on both functions so
  parallel rustc on heavy crates doesn't OOM the build engine; cargo registry
  + target caches shared across runs.
- prek is intentionally not run in CI — it runs locally via git hooks.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 07:14:15 -07:00
b97c387252 heph.nvim: context-item promotion + Dagger headless-nvim CI (slice 11c)
Some checks failed
Build / validate (pull_request) Failing after 3s
Backend (TDD):
- task.promote {container_id, item_ref, attention?, project?}: mint a committed
  task from the item_ref-th `- [ ]` context item (1-based, document order via a
  new extract::context_item_lines) and rewrite that source line into a [[link]]
  to it. Unit + rpc_socket tests.
- resolve_id now excludes canonical-context docs, so [[Task Title]] resolves to
  the task, not its identically-titled context doc (deterministic; a general fix
  surfaced by promotion's ULID-tiebreak ambiguity).

Plugin: :Heph promote / promote_under_cursor (save-if-dirty → compute item index
with a code-fence-aware scanner mirroring extract.rs → task.promote → reload the
rewritten buffer). e2e spec (f): promote a context line, assert the new task in
next, the source line became a link, and the container backlinks the task.

CI via Dagger: a test_nvim function 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 self-contained shim suite (cargo + target cache volumes);
build.yaml calls `dagger call test-nvim`. run.lua now fails on zero specs (no
false-green). Validated end-to-end: passing suite → exit 0, failing spec →
Dagger exit 1.

117 Rust tests + 7 nvim e2e specs green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 06:08:41 -07:00
7c9a734ebd heph.nvim: task views — next/list/capture/attention/state/log (slice 11b)
Some checks failed
Build / validate (pull_request) Failing after 2s
Backend: enrich `list` to return titled RankedTask rows (title +
canonical_context_id, via a shared ranked_from_row with `next`), so the
Organizational view needs no N+1 node.get. TDD: query_surface test asserts
list rows carry title + context id.

Plugin:
- view.lua: Tactical `next` + Organizational `list` rendered scratch buffers;
  <CR> opens the row's canonical-context doc. Narrowed the node autocmd to
  heph://node/* so view buffers (heph://next, heph://list) don't trip it.
- task.lua: 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).
- picker.lua: vim.ui.select with Telescope auto-upgrade (headless-safe).
- command.lua: :Heph next/list/capture/attention/done/drop/skip/log/search.

e2e: capture→next→open context→add/check checklist→done; recurring
fresh-checklist (complete rolls forward in place, next occurrence all-unchecked
— the §4.4 hard requirement). 6 specs green via `mise run test-nvim`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 21:12:56 -07:00
3997e948ed heph.nvim: run e2e via mise run test-nvim, drop the Makefile
Some checks failed
Build / validate (pull_request) Failing after 3s
Make mise the dev entrypoint for the headless e2e suite, matching the repo's
mise-tasks convention (auto-discovered, shows in `mise tasks`). Dev relies on
system-installed nvim + rustc; CI will provide them via Dagger (slice 11c).
The self-contained shim runner is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 21:02:46 -07:00
ee865e5635 heph.nvim: RPC client + buffer editing + wiki-links + journal (slice 11a)
Some checks failed
Build / validate (pull_request) Failing after 4s
The primary surface begins (tech-spec §8): a Neovim plugin that is a thin
client of the local hephd over its unix-socket JSON-RPC.

- node.resolve {title} → Node|null (heph-core Store + dispatch): exact,
  owner-scoped, non-tombstoned alias-then-title match — the same mapping that
  materializes wiki links, so follow-link jumps to the node the stored link
  points at (never fuzzy search). Unit + rpc_socket integration tests.
- heph.nvim/: vim.uv unix-socket JSON-RPC client (blocking call via vim.wait,
  id-demuxed, partial-line buffered, luanil so JSON null → Lua nil; isolated
  Sessions for tests). Buffer-backed nodes (heph://node/<id>, acwrite;
  BufReadCmd→node.get / BufWriteCmd→node.update, whole-buffer body round-trips
  exactly through the CRDT). [[wiki-link]] follow on <CR>. Daily journal.
  :Heph command surface + completion.
- Headless e2e (§9): a self-contained busted-style runner (tests/e2e/runner.lua)
  — no external plugins, no network, deterministic CI exit codes. Specs: journal
  round-trip, follow-link (+ unresolved no-op), link-two-docs/backlink.
  `make -C heph.nvim test` builds hephd and runs it.

Docs: heph-nvim reference card, §14 tracker (11a done; 11b/11c/11d queued),
changelog fragment.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 20:33:29 -07:00
87c76da659 docs: refresh resume state for next session (AGENTS structure + §14 order)
Some checks failed
Build / validate (pull_request) Failing after 5s
Backend is feature-complete; point the trackers at heph.nvim as the next
slice so a cold-start session resumes correctly.

- AGENTS.md: update the (always-in-context) project-structure block — all
  three daemon modes + sync + auth are done; CLI gained search/journal/auth;
  heph.nvim is the next slice. Point at tech-spec §14 as the live tracker.
- tech-spec §14: reorder "not yet done" so heph.nvim is #1 (the remaining
  build slice), with adoption refinement and the dependency-refresh sweep as
  non-blocking follow-ups.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 19:07:03 -07:00
f4db186234 hephd: OIDC client auth — device-code flow + token attach (auth 10b)
Some checks failed
Build / validate (pull_request) Failing after 9s
Close the auth loop: clients obtain a bearer token and present it to the
hub (tech-spec §13).

- oauth module: DeviceFlow (RFC 8628 — discover, start, poll handling
  authorization_pending/slow_down, refresh) + StoredToken + TokenStore
  (OS keyring via `keyring`, in-memory for tests) + current_bearer (loads
  and refreshes-on-expiry).
- heph auth login/logout: runs the device flow, prints the verification
  URL + user code, caches the token in the keyring.
- sync_once gains a bearer arg; the daemon (Daemon::spawn_sync_loop +
  sync.now) obtains it via current_bearer; RemoteStore attaches it to /rpc.
  --oidc-issuer/--oidc-client-id configure the spoke/client.
- Fix a latent panic: reqwest::blocking spins its own runtime and panics
  inside the daemon's spawn_blocking pool. All blocking auth/proxy HTTP
  (OidcVerifier JWKS, DeviceFlow, RemoteStore) now uses runtime-free `ureq`;
  async reqwest remains only for sync_once. (Caught by the new e2e test.)
- Tests (offline): device flow + refresh + token store vs a mock OAuth
  server; a full spoke->authenticated-hub loop (valid token accepted,
  missing token rejected) signed by a runtime-generated RSA key.

112 tests green; clippy -D warnings + fmt + prek clean. Slice 10 (auth)
complete; next is heph.nvim.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 16:27:36 -07:00
497c62a988 hephd: OIDC hub authentication — verification side (auth 10a)
Some checks failed
Build / validate (pull_request) Failing after 3s
Authenticate op exchange at the network boundary (tech-spec §13). The hub
now requires a valid OIDC bearer token on /sync/* and /rpc; local mode is
unchanged (no auth).

- heph-core: Store::authorize_owner_sub — single-tenant gate that claims the
  owner's oidc_sub on first sight, then authorizes only that sub (403 for any
  other identity). LocalStore impl over users.oidc_sub; RemoteStore stub.
- hephd auth module: TokenVerifier trait (mockable seam) + OidcVerifier
  (jsonwebtoken, rust_crypto). Strict validation: RS256 pinned, exact iss +
  aud, exp/nbf, required sub; JWKS discovered + cached, refetched on unknown
  kid (rotation). Claims/AuthError.
- Hub router takes Option<verifier>; an axum middleware on every route
  extracts the Bearer token, verifies it off the async worker, and runs the
  owner gate — 401 missing/invalid, 403 wrong identity, 503 IdP-unreachable.
  Open (no auth) when unconfigured, for local dev.
- main: --oidc-issuer/--oidc-audience enable the hub verifier (server mode).
- Security tests, all offline: stub-verifier middleware (missing/bad/valid +
  owner gate) and an adversarial battery driving OidcVerifier against an
  in-process mock IdP — rejects expired, wrong iss/aud, unknown kid, tampered
  signature, alg confusion (HS256/none), and missing sub. The RSA key + JWKS
  are generated at runtime (rsa/rand/base64 dev-deps) so no key is committed.
- tech-spec: add an end-of-v1 dependency-refresh pass to the roadmap.

108 tests green; clippy -D warnings + fmt + prek clean. Next: client-side
device-code login + keyring (10b).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 15:58:20 -07:00
5d54e913c2 hephd: client mode + RemoteStore (sync 9b)
Some checks failed
Build / validate (pull_request) Failing after 4s
Add the online-only escape hatch — a no-replica daemon that proxies every
Store call to a server over HTTP (tech-spec §3.1).

- Daemon is now generic over the backing store (Arc<Mutex<dyn Store +
  Send>>), so the same unix-socket surface fronts either a LocalStore
  (local/server) or a RemoteStore (client). sync::router/sync_once and the
  Ctx follow suit.
- New POST /rpc route on the hub router runs the full rpc::dispatch over
  HTTP (result-xor-error body, always 200). dispatch gains task.get and
  links.add so the proxied API is complete.
- RemoteStore (hephd): implements heph_core::Store by forwarding each call
  to /rpc via a blocking reqwest client (Store is sync; the daemon only
  calls it from the blocking pool). Error::Remote for transport failures;
  NOT_FOUND is preserved as Error::NodeNotFound. Sync primitives are
  stubbed (a client keeps no op-log).
- main: --mode client + --server-url; client skips the file lock and opens
  no LocalStore.
- tests/client_mode.rs: a RemoteStore drives node/task/search/list/health
  against a real HTTP server, and not-found maps back correctly.

102 tests green; clippy -D warnings + fmt + prek clean. Next: OIDC auth.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 15:29:28 -07:00
8c25d114c4 hephd: network sync over HTTP — hub + spoke (sync 9a)
Some checks failed
Build / validate (pull_request) Failing after 2s
Wire the existing merge engine over the network so the everyday config
(local + hub_url) syncs through a hub. Transport ratified = axum HTTP/JSON
(tech-spec §6.1, §12).

- heph-core: SyncCursors model + Store::sync_state/record_sync over the
  sync_state table (per-peer push/pull HLC cursors). Incremental, so each
  exchange transfers only the tail.
- hephd::sync: the hub router (POST /sync/push, GET /sync/pull?after=<hlc>)
  served from the shared LocalStore, and sync_once — a spoke's pull-then-
  merge, then push-tail exchange, advancing the cursors. Idempotent: a
  re-pushed op the hub already has is a no-op.
- Daemon carries optional hub config; sync.now/sync.status handled at the
  daemon (they need the hub transport the store can't reach). conflicts.
  list/resolve now reachable over the unix socket too.
- main: --mode local|server, --hub-url, --http-addr. server mode binds the
  hub HTTP endpoint on the same store; a local+hub_url spoke background-
  syncs on a 30s interval.
- tests/sync_http.rs: two spokes converge through a real-HTTP hub on an
  ephemeral port — node propagation and a divergent-scalar conflict.

Unauthenticated/single-owner for now; OIDC + per-user scoping is slice 10,
client mode + RemoteStore is 9b. 100 tests green; clippy -D warnings + fmt
+ prek clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 15:14:20 -07:00
455f172a54 heph-core: body text-CRDT via yrs (sync 8d)
Some checks failed
Build / validate (pull_request) Failing after 4s
Replace last-writer-wins for node bodies with the yrs text CRDT, so
concurrent edits to different regions of a body merge instead of one
clobbering the other (tech-spec §5, §12).

- New crate::crdt module wraps yrs: a device authors under a stable
  client_id derived from its sync origin; a whole-buffer write is diffed
  (common prefix/suffix, char-boundary safe) into the doc and the yrs
  delta is captured; merge is commutative/idempotent.
- nodes::create/update/journal maintain the body_crdt BLOB and put the
  yrs delta in the node.create/node.set op payload (body_crdt field).
  Recurrence's local checklist reset goes through the same path to keep
  body and body_crdt consistent (still records no op, as before).
- apply::node_upsert merges the body delta through the CRDT regardless of
  HLC order and drops body-conflict recording; titles + task scalars stay
  LWW with the conflict queue.
- convergence test now asserts disjoint concurrent body edits both survive
  and enqueue no conflict.

97 tests green; clippy -D warnings + fmt + prek clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 09:06:17 -07:00
662a360da8 docs: Phase 1 progress tracker (design roadmap + tech-spec §14)
Some checks failed
Build / validate (pull_request) Failing after 5s
Record what's built and the resume order for the next context session:
heph-core + hephd local mode + CLI/export + local query surface + the
sync engine (HLC, op-log, converging merge/conflict-queue) are done;
resume at yrs body-CRDT → network sync → OIDC auth → heph.nvim.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 05:23:43 -07:00
c81d45a291 heph-core: real HLC + persistent device origin (sync 8a)
Some checks failed
Build / validate (pull_request) Failing after 4s
First sync slice. Ratifies yrs for body merge in the tech-spec.

- hlc module: Hlc (physical, counter, origin) with a fixed-width encoding
  whose lexical order equals causal order; HlcClock generator (tick/update)
  — clock-injected, strictly monotonic. Unit + 2 proptests.
- meta table (migration v3) holds the stable per-device `origin` and the
  last HLC. next_hlc() does a read-modify-write inside the caller's
  transaction (store is single-writer, so no race), replacing the
  timestamp placeholder. Every node write is now stamped with a real,
  monotonic, causally-ordered HLC.

4 stamping integration tests (monotonic under stalled/regressed clock;
origin shared + persists across reopen; HLC resumes from persisted state).
89 tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 21:13:55 -07:00
2fcc5c0f22 Fold second-pass design review into v1 spec
Some checks failed
Build / validate (push) Failing after 5s
Resolve the open tensions surfaced in the pre-Phase-1 second pass over
design.md and tech-spec.md:

- Context items: Fork A index model — body markdown is the source of
  truth; context items are a locally-derived, non-synced index;
  identity is pinned at promotion. Dissolves the body-CRDT vs.
  extraction convergence problem.
- Recurrence: roll-forward in place; drop task_occurrences and
  is_template; advance to next RRULE instance after now (skip misses).
- Identity: deterministic ids for journal/tag (offline-convergent);
  ULID for content nodes and project.
- Mode/sync: orthogonal hub_url spoke capability; everyday device is
  local + hub_url, not server.
- Auth/owner: nullable oidc_sub, friction-free local user, hub-
  authoritative identity, one-time pre-first-sync adoption rewrite.
- Ranking: do_date is a boolean candidacy filter only; late_on is the
  sole urgency signal (global tier); FIFO tiebreak; order expressed as
  a reorderable named-dimension list.
- Modes are plugin-side compositions; add list() and log.tail().
- Frame v1 as a single deliberate C1; misc cleanups (export, health,
  CI nvim runner, README license).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 18:08:47 -07:00
247a079cd8 Reframe v1 around a targetable storage backend
Some checks failed
Build / validate (push) Failing after 3s
Replace "distributed from day one" with a targetable Store backend that
supports the full spectrum from local-only to distributed by configuration:

- Store trait with LocalStore (direct SQLite, exclusive lock) and
  RemoteStore (RPC to a server)
- three hephd runtime modes: local / server / client
- exclusive-lock handoff so the same SQLite file can pass between local and
  server mode; client mode is thin and online-only
- offline remains a property of local-backed replicas syncing via the hub

Local-only is now a first-class configuration. Build order starts at local
mode. Updates docs/reference/tech-spec.md (§1, §3.1, §11) and
docs/explanation/design.md (§9, §11) to match.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 10:05:15 -07:00
cbf859b2d7 Set up hephaestus from template and add design + tech spec
Some checks failed
Build / validate (push) Failing after 2s
Customize the generated repo (rename Dagger module to hephaestus_ci /
HephaestusCi, set docs baseUrl, add All-Rights-Reserved LICENSE, update
README/AGENTS), and add the project's foundational design documentation:

- docs/explanation/design.md — rationale + decision-history record
- docs/reference/tech-spec.md — implementation-ready technical spec

These define hephaestus as a self-hosted, client/server + offline-first
system unifying a markdown knowledge base with task management: 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 TDD strategy.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 09:37:28 -07:00
28c1f7886a Initial commit 2026-05-31 06:13:36 -07:00