Tasks: port calternal.js's task model (rich task files attached to log entries) #55

Open
opened 2026-09-24 15:42:29 +00:00 by kayg · 8 comments
Owner

Port calternal.js's current task model into calternal (owner decision K1, confirmed; DESIGN §31).

Reference (read-only): /home/kayg/Developer/calternal.js — docs/superpowers/specs/2026-06-26-tasks-system-design.md (fetch origin/dev first), the task code in packages/core/src/tasks/ and the task parts of packages/web/src/lib/db (tasks/, taskAttachment.ts, eventOrder.ts, promotion.ts), and the Forgejo issues kayg/calternal.js#4 (epic, read all comments), #170 (attach new tasks/notes to the latest log entry — the current model), #156, #158, #169.

Build, in calternal-notes-core (pure logic, with the reference tests ported) and the notes plugin (API + single-writer writes):

  • Rich task files Tasks/<YYYYMMDD>-<slug>-<id>.md (capitalized folders per DESIGN §17) with scalar frontmatter: status (6 values), priority (4), due/scheduled/start dates, recurrence (parse/store/round-trip only; roll-forward deferred), tags, links; derived ## Links; byte-stable writers.
  • New tasks attach to the latest log entry by durable creation order as a child bullet - [ ] → [Title](Tasks/….md); with no log entry (or the standalone setting) they are standalone items under their creation day with an Attach action. Legacy tasks/inbox.md and wikilink references stay readable; the app never writes new items to an inbox file.
  • Inline - [ ] checkboxes anywhere in notes are indexed as tasks (read + tick write-back through the exact checkbox contract); Obsidian-Tasks emoji metadata is read, never rewritten.
  • Composer task mode (TickTick-style NLP: dates, priority, recurrence, tags, mode classifier) from calternal.js's composer, using the already-ported calternal-notes-core composer.
  • Index tables for the two queries (by date/role with the dedup rule; standalone/undated) and API: create, update properties, tick, attach/detach, list by day range, list standalone.
  • Crash-safe: the calternal.js promotion/attach journal pattern (target-first, verified, idempotent replay) through calternal-fs; concurrency tests (attach vs concurrent log write).

Context: DESIGN §31 and the calternal.js references listed above. Comment on this issue as you work; never close it.

Port calternal.js's current task model into calternal (owner decision K1, confirmed; DESIGN §31). Reference (read-only): /home/kayg/Developer/calternal.js — docs/superpowers/specs/2026-06-26-tasks-system-design.md (fetch `origin/dev` first), the task code in packages/core/src/tasks/ and the task parts of packages/web/src/lib/db (tasks/, taskAttachment.ts, eventOrder.ts, promotion.ts), and the Forgejo issues kayg/calternal.js#4 (epic, read all comments), #170 (attach new tasks/notes to the latest log entry — the current model), #156, #158, #169. Build, in calternal-notes-core (pure logic, with the reference tests ported) and the notes plugin (API + single-writer writes): - Rich task files `Tasks/<YYYYMMDD>-<slug>-<id>.md` (capitalized folders per DESIGN §17) with scalar frontmatter: status (6 values), priority (4), due/scheduled/start dates, recurrence (parse/store/round-trip only; roll-forward deferred), tags, links; derived `## Links`; byte-stable writers. - New tasks attach to the **latest log entry by durable creation order** as a child bullet `- [ ] → [Title](Tasks/….md)`; with no log entry (or the standalone setting) they are standalone items under their creation day with an Attach action. Legacy `tasks/inbox.md` and wikilink references stay readable; the app never writes new items to an inbox file. - Inline `- [ ]` checkboxes anywhere in notes are indexed as tasks (read + tick write-back through the exact checkbox contract); Obsidian-Tasks emoji metadata is read, never rewritten. - Composer task mode (TickTick-style NLP: dates, priority, recurrence, tags, mode classifier) from calternal.js's composer, using the already-ported calternal-notes-core composer. - Index tables for the two queries (by date/role with the dedup rule; standalone/undated) and API: create, update properties, tick, attach/detach, list by day range, list standalone. - Crash-safe: the calternal.js promotion/attach journal pattern (target-first, verified, idempotent replay) through calternal-fs; concurrency tests (attach vs concurrent log write). Context: DESIGN §31 and the calternal.js references listed above. Comment on this issue as you work; never close it.
Author
Owner

Started tasks port on branch job/tasks-port at base 63f3bdd6cb. I read the issue and repository contract. I am tracing the calternal.js task model and the existing notes/core and notes API before implementing the owned slices.

Started tasks port on branch job/tasks-port at base 63f3bdd6cb299d8619d8ed1b7ff83a40be5e4c3f. I read the issue and repository contract. I am tracing the calternal.js task model and the existing notes/core and notes API before implementing the owned slices.
Author
Owner

Finding: the existing Notes plugin has no task index or durable log-entry creation order. The calternal.js #170 implementation uses immutable creation metadata because start time and a local pointer select the wrong latest entry after edits or replication. I ported the reference Rust task parser/writers into calternal-notes-core, adapting them to capitalized Tasks/ and the existing shared Note link scanner. Commit e285720 passes 446 core library tests. Next I am adding a durable log-entry order and server-side task projection/writes.

Finding: the existing Notes plugin has no task index or durable log-entry creation order. The calternal.js #170 implementation uses immutable creation metadata because start time and a local pointer select the wrong latest entry after edits or replication. I ported the reference Rust task parser/writers into calternal-notes-core, adapting them to capitalized Tasks/ and the existing shared Note link scanner. Commit e285720 passes 446 core library tests. Next I am adding a durable log-entry order and server-side task projection/writes.
Author
Owner

Finding: the existing Log API wrote entries without block IDs, so a Task reference could not safely target one entry after another Log write. New Log entries now carry a sortable block ID stored in the Daily note; the Index projects that order. A Task file is written before its reference, then a persisted attach intent is replayed idempotently. The notes plugin tests currently cover create, attach, replay, tick, detach, and an attach/concurrent-Log interleaving. I am extending the range query and adversarial probe before the full gates.

Finding: the existing Log API wrote entries without block IDs, so a Task reference could not safely target one entry after another Log write. New Log entries now carry a sortable block ID stored in the Daily note; the Index projects that order. A Task file is written before its reference, then a persisted attach intent is replayed idempotently. The notes plugin tests currently cover create, attach, replay, tick, detach, and an attach/concurrent-Log interleaving. I am extending the range query and adversarial probe before the full gates.
Author
Owner

Finding: a legacy Daily note can have valid Log entries with no block ID, so the first Task attach could otherwise create a standalone Task despite an existing Log. The attach API now scans the Daily note files on explicit attach, chooses a deterministic latest-file/last-entry fallback only when no durable Log order exists, inserts a sortable block ID into the selected line without changing its other bytes, then writes the Task reference. The core regression preserves a CRLF line with an en-dash separator and trailing spaces. Focused results: 451 core library tests, 11 vector parity tests, and 12 notes plugin tests passed. Server OpenAPI generation and the TypeScript client generation completed; contracts are committed at 4a45166300.

Finding: a legacy Daily note can have valid Log entries with no block ID, so the first Task attach could otherwise create a standalone Task despite an existing Log. The attach API now scans the Daily note files on explicit attach, chooses a deterministic latest-file/last-entry fallback only when no durable Log order exists, inserts a sortable block ID into the selected line without changing its other bytes, then writes the Task reference. The core regression preserves a CRLF line with an en-dash separator and trailing spaces. Focused results: 451 core library tests, 11 vector parity tests, and 12 notes plugin tests passed. Server OpenAPI generation and the TypeScript client generation completed; contracts are committed at 4a451663001fe9fe771c7679448b2615129d0373.
Author
Owner

Implemented and committed the Task port on job/tasks-port at 4a451663001fe9fe771c7679448b2615129d0373 (base 63f3bdd6cb299d8619d8ed1b7ff83a40be5e4c3f). No push or merge.

Built: rich Tasks/ parser and byte-stable writers with reference vector tests; composer parsing; inline and legacy Task indexing; by-day and standalone projections; create, properties, tick, attach and detach API; target-first attachment journal and replay; durable Log entry order and legacy Log ID stamping; OpenAPI and generated TypeScript; live adversarial probes. Files are under crates/calternal-notes-core/**, crates/plugins/notes/**, contracts/openapi.json, packages/api-client/src/generated.ts, tests/adversarial/attack.py, and Cargo.lock.

Gate output (exact output lines):

cargo fmt --check exit=0
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 1m 22s
cargo clippy --all-targets -- -D warnings exit=0
test result: ok. 451 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.53s
test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.03s
test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.67s
cargo test exit=0
✨ openapi-typescript 7.13.0
🚀 ../../contracts/openapi.json → src/generated.ts [1.4s]
bash packages/api-client/check-generated.sh exit=0
server alive at end: True

==== FINDINGS 0
bash tests/adversarial/run.sh exit=0
     Removed 14557 files, 6.3GiB total
cargo clean exit=0

Decisions and limits for owner review: historical Log entries without IDs have no recoverable creation order. Explicit attachment chooses the latest dated Log and last entry, then stamps a durable ID; new Log entries use durable order. Updating a Task title changes the Task file and API projection while an existing Markdown attachment label keeps its original text. Recurrence is parsed and preserved but roll-forward is deferred per the issue. No blocking gaps found in the live attack round.

Implemented and committed the Task port on `job/tasks-port` at `4a451663001fe9fe771c7679448b2615129d0373` (base `63f3bdd6cb299d8619d8ed1b7ff83a40be5e4c3f`). No push or merge. Built: rich `Tasks/` parser and byte-stable writers with reference vector tests; composer parsing; inline and legacy Task indexing; by-day and standalone projections; create, properties, tick, attach and detach API; target-first attachment journal and replay; durable Log entry order and legacy Log ID stamping; OpenAPI and generated TypeScript; live adversarial probes. Files are under `crates/calternal-notes-core/**`, `crates/plugins/notes/**`, `contracts/openapi.json`, `packages/api-client/src/generated.ts`, `tests/adversarial/attack.py`, and `Cargo.lock`. Gate output (exact output lines): ``` cargo fmt --check exit=0 Finished `dev` profile [unoptimized + debuginfo] target(s) in 1m 22s cargo clippy --all-targets -- -D warnings exit=0 test result: ok. 451 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.53s test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.03s test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.67s cargo test exit=0 ✨ openapi-typescript 7.13.0 🚀 ../../contracts/openapi.json → src/generated.ts [1.4s] bash packages/api-client/check-generated.sh exit=0 server alive at end: True ==== FINDINGS 0 bash tests/adversarial/run.sh exit=0 Removed 14557 files, 6.3GiB total cargo clean exit=0 ``` Decisions and limits for owner review: historical Log entries without IDs have no recoverable creation order. Explicit attachment chooses the latest dated Log and last entry, then stamps a durable ID; new Log entries use durable order. Updating a Task title changes the Task file and API projection while an existing Markdown attachment label keeps its original text. Recurrence is parsed and preserved but roll-forward is deferred per the issue. No blocking gaps found in the live attack round.
Author
Owner

Deep-link audit evidence: apps/web/src/routes/t/[id]/+page.svelte:10-13 states that a Task ID is its Markdown location. Renaming the Task file changes the ID and breaks an existing /t/<task-id> link. This conflicts with the CLAUDE.md Deep links rule that links use stable identities and survive renames. Regression idea: copy a dated and undated Task link, rename each Task file, then open both old links and verify the same Tasks are selected.

Deep-link audit evidence: `apps/web/src/routes/t/[id]/+page.svelte:10-13` states that a Task ID is its Markdown location. Renaming the Task file changes the ID and breaks an existing `/t/<task-id>` link. This conflicts with the CLAUDE.md Deep links rule that links use stable identities and survive renames. Regression idea: copy a dated and undated Task link, rename each Task file, then open both old links and verify the same Tasks are selected.
Author
Owner

Superseded storage model: this issue writes Tasks into a separate Tasks/ folder. DESIGN §40 says it replaces the earlier folder rules in §31; tasks are Notes with task frontmatter, stored flat in Notes/, and there is no Tasks folder. Recommend updating this issue's path and collection requirements to match §40 while keeping task behavior that still matches the current design. Do not close the issue in this audit.

Superseded storage model: this issue writes Tasks into a separate Tasks/ folder. DESIGN §40 says it replaces the earlier folder rules in §31; tasks are Notes with task frontmatter, stored flat in Notes/, and there is no Tasks folder. Recommend updating this issue's path and collection requirements to match §40 while keeping task behavior that still matches the current design. Do not close the issue in this audit.
Author
Owner

DESIGN §40 supersedes the old Tasks/ path: Task Notes live flat in Notes/. The Task model and its remaining behavior still need tracking, so this issue stays open with the path decision updated.

DESIGN §40 supersedes the old `Tasks/` path: Task Notes live flat in `Notes/`. The Task model and its remaining behavior still need tracking, so this issue stays open with the path decision updated.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#55
No description provided.