From 60d9c06b7f4676c04686f7250dc09e4a426f6064 Mon Sep 17 00:00:00 2001 From: Amolith Date: Wed, 29 Oct 2025 14:56:34 -0600 Subject: [PATCH] docs(schema): refine event payloads and keys Co-authored-by: Crush --- docs/Potential schema.md | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/docs/Potential schema.md b/docs/Potential schema.md index b33a44082f1dfbcaed647c93fd099040e6e48023..8423eb6e89866a054e884b4c504f3b1a60a973a9 100644 --- a/docs/Potential schema.md +++ b/docs/Potential schema.md @@ -39,7 +39,7 @@ SPDX-License-Identifier: CC0-1.0 #### Archived sessions (queryable by time and by dir) - idx/archived/{ts_be}/{sid} -> {dir_hash} // for global archive lists in chronological order -- dir/{dir_hash}/archived/{ts_be} -> {sid} // for "archive history" per working directory +- dir/{dir_hash}/archived/{ts_be}/{sid} -> "" // for "archive history" per working directory - {ts_be} = 8-byte big-endian Unix nanos, hex-encoded lowercase, zero-padded to 16 hex chars; sorts chronologically #### Per-session namespace @@ -56,7 +56,13 @@ SPDX-License-Identifier: CC0-1.0 - s/{sid}/evt/{seq_be} -> JSON event record - {seq_be} = 8-byte big-endian u64 counter, hex-encoded lowercase, zero-padded to 16 hex chars; assures correct chronological iteration - { seq, at, type, reason: null|string, cmd: "np ...", payload: {...} } - - Types you'll likely use: session_started, goal_set, goal_updated, task_added, task_updated, task_status_changed, session_archived, note + - Event payloads capture immutable snapshots so subscribers can render changes without extra lookups: + - type=goal_set → `{"goal":{"title":"…","description":"…","updated_at":"RFC3339"}}` + - type=goal_updated → `{"goal_before":{"title":"…","description":"…","updated_at":"RFC3339"},"goal_after":{"title":"…","description":"…","updated_at":"RFC3339"}}` + - type=task_added → `{"task":{"id":"a1b2c3","title":"…","description":"…","status":"pending","created_at":"RFC3339","updated_at":"RFC3339","created_seq":1}}` + - type=task_updated → `{"task_id":"a1b2c3","before":{"title":"…","description":"…","updated_at":"RFC3339"},"after":{"title":"…","description":"…","updated_at":"RFC3339"}}` + - type=task_status_changed → `{"task_id":"a1b2c3","title":"…","status_before":"pending","status_after":"in_progress","updated_at":"RFC3339"}` + - Supported event types: goal_set, goal_updated, task_added, task_updated, task_status_changed ### Core operations (transactional) @@ -67,11 +73,10 @@ SPDX-License-Identifier: CC0-1.0 - If it exists, read the active {sid} and print: "Session {sid} is already active for this directory. There's already an active session for this directory; ask your operator whether they want to resume or archive it." - Return 0 (idempotent operation). 4) If no active session exists, begin txn: - - Generate new sid = ULID (time-ordered) or blake3(rand) hex; keep as short as practical (e.g., 26 char ULID). + - Generate new sid = ULID (canonical 26-character string for uniqueness and natural sort order). - Put s/{sid}/meta (state=active), s/{sid}/meta/evt_seq=0 (do NOT create goal yet; goal is created when first set). - Put dir/{dir_hash}/active -> {sid}. - Put idx/active/{sid} -> {dir_hash}. - - Append event s/{sid}/evt/{0000000000000001} type=session_started. 5) Commit. #### Set goal (np g s …) @@ -82,7 +87,7 @@ SPDX-License-Identifier: CC0-1.0 3) txn: - Create s/{sid}/goal JSON. - Update s/{sid}/meta.last_updated_at. - - Increment s/{sid}/meta/evt_seq and write s/{sid}/evt/{seq} with type=goal_set (no reason required). + - Increment s/{sid}/meta/evt_seq (read current value, add 1, persist) before writing the corresponding event, then write s/{sid}/evt/{seq} with type=goal_set (no reason required). #### Update goal (np g u …) 1) Lookup sid via dir/{dir_hash}/active. @@ -93,7 +98,7 @@ SPDX-License-Identifier: CC0-1.0 4) txn: - Update s/{sid}/goal JSON. - Update s/{sid}/meta.last_updated_at. - - Increment s/{sid}/meta/evt_seq and write s/{sid}/evt/{seq} with type=goal_updated, reason in payload. + - Increment s/{sid}/meta/evt_seq (read, add 1, persist) before writing the event, then write s/{sid}/evt/{seq} with type=goal_updated, reason in payload. #### Add tasks (np t a …) 1) For each task: @@ -103,11 +108,11 @@ SPDX-License-Identifier: CC0-1.0 - Treat adds as idempotent: if the same task (same title+description) is re-added, it will resolve to the same id and be a no-op. - If a user wants to retry a cancelled task with the exact same title and description, they should update the existing task's status rather than adding a new one, or modify the title/description slightly to differentiate it. - txn: - - If s/{sid}/task/{id} absent, increment evt_seq and create task with status=pending, created_at=now, created_seq=evt_seq. + - If s/{sid}/task/{id} absent, read and increment evt_seq (persisting the new value) and create the task with status=pending, created_at=now, created_seq=evt_seq. - Put s/{sid}/idx/status/pending/{id}. - Update meta.last_updated_at. - Append event task_added with task payload. - - Note: When adding multiple tasks in a single np t a invocation, use a single transaction and increment evt_seq for each task to preserve the order they were provided on the command line. Tasks should be displayed sorted by created_seq (not created_at) to maintain stable, predictable ordering. + - Note: When adding multiple tasks in a single np t a invocation, use a single transaction (one task, or batch of sequenced tasks, per transaction) and increment evt_seq for each task to preserve the order they were provided on the command line. Tasks should be displayed sorted by created_seq (not created_at) to maintain stable, predictable ordering. #### Update task status/title/description (np t u …) 1) Determine if reason is required: @@ -119,7 +124,7 @@ SPDX-License-Identifier: CC0-1.0 - If status changes: delete old s/{sid}/idx/status/{old}/{id}, put new s/{sid}/idx/status/{new}/{id}. - Update task JSON. - Update meta.last_updated_at. - - Increment evt_seq, append event task_updated or task_status_changed with reason (if provided). + - Increment evt_seq (read, add 1, persist) and append event task_updated or task_status_changed with reason (if provided). #### Archive session (np a) 1) Read s/{sid}/meta: @@ -129,8 +134,7 @@ SPDX-License-Identifier: CC0-1.0 - Delete idx/active/{sid}. - Update s/{sid}/meta.state="archived", archived_at=now, last_updated_at. - Put idx/archived/{ts_be}/{sid} -> {dir_hash}. - - Put dir/{dir_hash}/archived/{ts_be} -> {sid}. - - Increment evt_seq, append session_archived event. + - Put dir/{dir_hash}/archived/{ts_be}/{sid} -> "". ### Query patterns @@ -245,7 +249,7 @@ Note on `reason` field: - Enforce one active session per dir: - np s checks if dir/{dir_hash}/active exists before creating a new session (idempotent behavior documented above). - Deterministic task IDs: - - Use blake3(normalized(title)+"|"+normalized(description)+"|"+sid), take first 8 hex. +- Use blake3(normalized(title)+"|"+normalized(description)+"|"+sid), take first 6 hex. - Never recompute on updates; the id is "creation id," not a content hash of current state. - Re-adding the same task (same title+description) resolves to the same id and is treated as a no-op.