Tasks: stable task identity so /t/ links survive rename (like note calternal-id) #83

Closed
opened 2026-09-25 04:49:06 +00:00 by kayg · 4 comments
Owner

Found by the deep-link audit (#65): a Task's id is its Markdown file path, so renaming or moving a Task file breaks its /t/<id> deep link, which violates the owner rule that links use stable identities and survive renames (CLAUDE.md, DESIGN §33).

Give Tasks a stable identity like Notes have: a calternal-id in the task file's frontmatter, minted once through the single writer (byte-stable for everything else), indexed, and used by /t/<id>, the Reminders VTODO UID (#48; keep existing UIDs working via a mapping), the log-entry attachment links and the Tasks API. Inline - [ ] tasks in daily notes keep using their ^block-id. Include a migration path for existing task files (mint on first read, like journal block ids), tests for rename/move keeping the link, and an adversarial check that ids can't be forged to reach another user's task.

Found by the deep-link audit (#65): a Task's id is its Markdown file path, so renaming or moving a Task file breaks its `/t/<id>` deep link, which violates the owner rule that links use stable identities and survive renames (CLAUDE.md, DESIGN §33). Give Tasks a stable identity like Notes have: a `calternal-id` in the task file's frontmatter, minted once through the single writer (byte-stable for everything else), indexed, and used by `/t/<id>`, the Reminders VTODO UID (#48; keep existing UIDs working via a mapping), the log-entry attachment links and the Tasks API. Inline `- [ ]` tasks in daily notes keep using their `^block-id`. Include a migration path for existing task files (mint on first read, like journal block ids), tests for rename/move keeping the link, and an adversarial check that ids can't be forged to reach another user's task.
Author
Owner

Starting issue #83 in branch job/task-identity at base 4bbedae355f9ad0af625454b367e55b7a2306865. I am reading the Tasks store/API, note identity helpers, Reminders mapping, and the /t/ deep-link path before implementing stable task identities.

Starting issue #83 in branch `job/task-identity` at base `4bbedae355f9ad0af625454b367e55b7a2306865`. I am reading the Tasks store/API, note identity helpers, Reminders mapping, and the `/t/` deep-link path before implementing stable task identities.
Author
Owner

Finding: file Tasks already carry an optional calternal-id in the projected frontmatter, but TaskIndexEntry.task_id, TaskView.task_id, and tasks_store::by_id still use the Markdown path. The Reminders projection already stores a per-user UID-to-task mapping in reminder_resources. I will keep the path as the internal write target, index the file identity separately, and retain per-user aliases for path links and Reminders UIDs so a move does not change the public Task link.

Finding: file Tasks already carry an optional `calternal-id` in the projected frontmatter, but `TaskIndexEntry.task_id`, `TaskView.task_id`, and `tasks_store::by_id` still use the Markdown path. The Reminders projection already stores a per-user UID-to-task mapping in `reminder_resources`. I will keep the path as the internal write target, index the file identity separately, and retain per-user aliases for path links and Reminders UIDs so a move does not change the public Task link.
Author
Owner

The Task identity slice is committed as e4cbbc3 (after the frontmatter helper commit a640efc). It adds migration 0010, stamps canonical UUIDv4 calternal-id values onto Task files while preserving all other Markdown bytes, retains current and historical paths as User-scoped aliases, and keeps Reminders UIDs resolving through their existing resource mapping. The move coverage also checks Task references attached to a Log entry.

Notes library test output: test result: ok. 53 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 18.71s

The Task identity slice is committed as `e4cbbc3` (after the frontmatter helper commit `a640efc`). It adds migration 0010, stamps canonical UUIDv4 `calternal-id` values onto Task files while preserving all other Markdown bytes, retains current and historical paths as User-scoped aliases, and keeps Reminders UIDs resolving through their existing resource mapping. The move coverage also checks Task references attached to a Log entry. Notes library test output: `test result: ok. 53 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 18.71s`
Author
Owner

Finished kayg/calternal#83 on branch job/task-identity.

Head SHA: 63afb72910fa7390229e571e9f9f16defecc978a

Gate output:

  • cargo fmt --check: exit 0; no output.
  • cargo clippy --workspace --all-targets -- -D warnings: Finished dev profile [unoptimized + debuginfo] target(s) in 5m 37s
  • cargo test --workspace: exit 0; Notes output: test result: ok. 53 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 22.75s
  • bash packages/api-client/check-generated.sh: exit 0; 🚀 ../../contracts/openapi.json → src/generated.ts [699.7ms]
  • bash tests/adversarial/run.sh: ==== FINDINGS 0; ==== ROUND 2 FINDINGS 0. The Task storm also reported SLOW threshold=32.02s, the permitted load-noise finding.
  • Production-build deep-link E2E: deep links e2e: all passed

Decisions: Legacy Task files get canonical UUIDv4 calternal-id values. A User-scoped alias table maps stable IDs, old paths, and current paths to the current projection row. This keeps old links while the Markdown path remains internal to the store.

Finished `kayg/calternal#83` on branch `job/task-identity`. Head SHA: `63afb72910fa7390229e571e9f9f16defecc978a` Gate output: - `cargo fmt --check`: exit 0; no output. - `cargo clippy --workspace --all-targets -- -D warnings`: `Finished `dev` profile [unoptimized + debuginfo] target(s) in 5m 37s` - `cargo test --workspace`: exit 0; Notes output: `test result: ok. 53 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 22.75s` - `bash packages/api-client/check-generated.sh`: exit 0; `🚀 ../../contracts/openapi.json → src/generated.ts [699.7ms]` - `bash tests/adversarial/run.sh`: `==== FINDINGS 0`; `==== ROUND 2 FINDINGS 0`. The Task storm also reported `SLOW threshold=32.02s`, the permitted load-noise finding. - Production-build deep-link E2E: `deep links e2e: all passed` Decisions: Legacy Task files get canonical UUIDv4 `calternal-id` values. A User-scoped alias table maps stable IDs, old paths, and current paths to the current projection row. This keeps old links while the Markdown path remains internal to the store.
kayg closed this issue 2026-09-25 06:27:05 +00:00
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#83
No description provided.