Slice 5 (tech-spec §4.4). Completing a recurring task rolls it forward in
place instead of marking it done — the Todoist-corner-avoiding model.
Pure recurrence module:
- next_occurrence(rrule, anchor, after): lazy RRULE expansion (rrule +
chrono/UTC) returning the next instance strictly after `after`,
skipping missed occurrences; None when a finite series is exhausted.
- reset_checkboxes(body): the fresh-checklist transform — unchecks every
`- [x]`, idempotent, preserves indentation/bullet/line-endings.
Storage roll-forward (one transaction, on set_state(done) of a recurring
task): reset the canonical context doc's checklist, append the completed
occurrence to the task's log, advance do_date to the next instance after
now (skipping misses); finite series finally goes done. `skip` advances
the same way without logging. Non-recurring done is unchanged.
Per-task append-only log (`log-of` doc): log_append / log_tail — the
resumption breadcrumb + recurring-completion narrative ([[design]] §6.4).
Tests: 7 recurrence unit + 2 proptests (no checked marker survives reset;
reset idempotent for any body) + 6 end-to-end incl. five-occurrence
no-carry-forward and missed-collapse-to-one. 53 tests green. This
completes the heph-core library layer.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Slice 4 — the flagship Tactical blank-slate engine. Pure and
clock-injected, two stages:
- Candidacy filter: committed ∧ outstanding ∧ ¬tombstoned ∧ ≠blue ∧
actionable (do_date NULL or ≤ now) ∧ in scope. do_date is used ONLY
here — a boolean "can I do this now?" gate, never urgency.
- Order: an ordered list of named Dimensions applied lexicographically
(PastLateOn → LateOverdueAmount → Attention band → CreatedAt FIFO),
with node_id as final tiebreak for a total order. Reorder RANKING in
one place to retune. late_on is the sole urgency signal (global tier);
age never becomes urgency. blue hidden; red always shown past limit.
Storage `Store::next` loads candidates via a SQL join (project +
canonical-context links) and runs the pure engine with the store clock.
13 table-driven unit cases + 3 proptests (antisymmetry, sorted output
fully ordered, equality ⇒ identity) + 2 end-to-end. 38 tests green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Slice 2 (tech-spec §5). Pure, deterministic derivation from a body:
- `[[wiki-links]]` → wiki-link targets, in first-seen order, deduped,
honoring `[[target|display]]`. Scans the raw body (CommonMark mangles
`[[ ]]` brackets in inline parsing) and excludes matches inside code,
whose byte ranges come from pulldown-cmark's offset iterator.
- GFM `- [ ]` / `- [x]` task items → the local context-item index
(Fork A): label keeps raw markdown (for promotion) + checked state.
- Code blocks are correctly skipped for both.
10 extraction unit tests incl. idempotency; 14 total green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Kick off Phase 1 (v1 prototype) per tech-spec §11.1. Sets up the Cargo
workspace and the first TDD slice of heph-core:
- Migration runner + §4.5 SQLite schema (nodes, tasks, links, aliases,
users, oplog, sync_state, conflicts), versioned via PRAGMA user_version.
- Clock-injected `Clock` trait (no ambient wall-clock reads; §2).
- `Store` trait + `LocalStore` SQLite backend with node create/get,
bootstrapping the single local user (oidc_sub NULL, §13).
- Node model (kinds: doc/task/project/tag/journal).
Repo housekeeping: fill AGENTS.md Project Structure (last template TODO),
ignore /target, add self-bootstrapping .forgejo/scripts/build that runs
cargo fmt/clippy/test in CI (§9), changelog fragment.
Tests green: 4 unit tests (migration version, local-user idempotency,
create/get round-trip, missing-node None).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>