From ee79e33a2a5085745c7d545a4e739236077e86ee Mon Sep 17 00:00:00 2001 From: Scott Idem Date: Tue, 2 Jun 2026 13:50:15 -0400 Subject: [PATCH] fix(idb-sort): correct tmp_sort_* comparator direction in journals, IDAA recovery meetings, and BB post comments build_tmp_sort() encodes priority=true as '0' for ascending sort. JS comparators were using b.localeCompare(a) (descending), inverting the encoding so priority=false items sorted first. Fixed to a.localeCompare(b) in ae_journals_search_helpers.ts (3 sites in recovery_meetings +page.svelte and wrapper component). Also fixes a Dexie anti-pattern in bb/[post_id]: .reverse() before .sortBy() is a no-op in Dexie; moved array .reverse() to after the await. Documents the encoding rule and legacy inverted-encoding modules in GUIDE__SvelteKit2_Svelte5_DexieJS.md and adds mistake #15 to BOOTSTRAP quickstart. Co-Authored-By: Claude Sonnet 4.6 --- documentation/BOOTSTRAP__AI_Agent_Quickstart.md | 14 ++++++++++++++ .../GUIDE__SvelteKit2_Svelte5_DexieJS.md | 15 +++++++++++++-- src/lib/ae_journals/ae_journals_search_helpers.ts | 4 +++- src/routes/idaa/(idaa)/bb/[post_id]/+page.svelte | 6 ++++-- .../idaa/(idaa)/recovery_meetings/+page.svelte | 6 ++++-- .../ae_idaa_comp__event_obj_li_wrapper.svelte | 4 +++- 6 files changed, 41 insertions(+), 8 deletions(-) diff --git a/documentation/BOOTSTRAP__AI_Agent_Quickstart.md b/documentation/BOOTSTRAP__AI_Agent_Quickstart.md index b3ea4bb4..ba9db776 100644 --- a/documentation/BOOTSTRAP__AI_Agent_Quickstart.md +++ b/documentation/BOOTSTRAP__AI_Agent_Quickstart.md @@ -446,6 +446,20 @@ These are real incidents — know them before you start. gate. See `GUIDE__SvelteKit2_Svelte5_DexieJS.md` → "Bootstrap Race" for the Dexie-side context. +15. **`tmp_sort_*` comparators written descending instead of ascending** — `build_tmp_sort()` encodes `priority=true` as `'0'` and `priority=false` as `'1'`, designed for **ascending** sort so priority items appear first. Writing a JS `.sort()` comparator as `b.localeCompare(a)` (descending) inverts the encoding and sends priority items to the bottom. + + Found in journals (2026-06), IDAA recovery meetings fast-path and API re-sort (2026-06), and as a Dexie anti-pattern in BB post comments. + + ```ts + // ❌ Wrong — descending puts priority=false ('1') before priority=true ('0') + list.sort((a, b) => (b.tmp_sort_1 ?? '').localeCompare(a.tmp_sort_1 ?? '')); + + // ✅ Correct — ascending matches build_tmp_sort encoding + list.sort((a, b) => (a.tmp_sort_1 ?? '').localeCompare(b.tmp_sort_1 ?? '')); + ``` + + **Companion Dexie trap:** `collection.reverse().sortBy('tmp_sort_*')` — Dexie ignores a collection-level `.reverse()` when `.sortBy()` is called. The sort is always ascending. To reverse the result, call `.reverse()` on the returned array after `await`. See `GUIDE__SvelteKit2_Svelte5_DexieJS.md` → `build_tmp_sort` section. + --- ## 8. Source Layout (Quick Reference) diff --git a/documentation/GUIDE__SvelteKit2_Svelte5_DexieJS.md b/documentation/GUIDE__SvelteKit2_Svelte5_DexieJS.md index d8c5da06..4681c3c8 100644 --- a/documentation/GUIDE__SvelteKit2_Svelte5_DexieJS.md +++ b/documentation/GUIDE__SvelteKit2_Svelte5_DexieJS.md @@ -155,14 +155,25 @@ obj.tmp_sort_3 = tmp_sort_3; **Sort chain convention:** `group → priority DESC → sort ASC → [module-specific] → name` -**Priority encoding:** `priority ? '0' : '1'` — inverted so that `priority=true` sorts first in ascending order. This means **never use `.reverse()`** on a list sorted by `tmp_sort_*` — `.reverse()` would flip priority-true to sort last. +**Priority encoding:** `priority ? '0' : '1'` — inverted so that `priority=true` sorts first in ascending order. This means: +- **Dexie `.sortBy('tmp_sort_*')`** — always call without `.reverse()` before it (Dexie ignores collection-level `.reverse()` when using `.sortBy()`). If descending is needed for non-tmp_sort fields, call `.reverse()` on the resulting array after `await`. +- **JS `.sort()` comparators** — use **ascending** `a.localeCompare(b)`, NOT `b.localeCompare(a)`. Using descending flips the priority encoding and puts `priority=false` items first. + +```ts +// ✅ Correct — ascending; priority=true ('0') sorts before priority=false ('1') +list.sort((a, b) => (a.tmp_sort_1 ?? '').localeCompare(b.tmp_sort_1 ?? '')); + +// ❌ Wrong — descending inverts the encoding; priority=false ('1') sorts first +list.sort((a, b) => (b.tmp_sort_1 ?? '').localeCompare(a.tmp_sort_1 ?? '')); +``` **Modules using `build_tmp_sort`:** - `ae_events__event_presentation.ts` — `tmp_sort_1/2`: group → priority → sort → start_datetime → code → name +- `ae_events__event.ts` — `tmp_sort_1/2/3`: group → priority → sort → name → updated_on (used by IDAA recovery meetings) - `ae_journals__journal.ts` — `tmp_sort_1/2/3`: group → priority → sort → name → updated_on - `ae_journals__journal_entry.ts` — same chain as journal -Remaining modules (sessions, presenters, locations, posts, core) scheduled for rollout; see `TODO__Agents.md`. +**Legacy encoding (not yet migrated to `build_tmp_sort`):** `ae_posts__post.ts`, `ae_posts__post_comment.ts`, `ae_archives__archive.ts`, `ae_archives__archive_content.ts`, `ae_sponsorships_functions.ts` use the opposite encoding (`priority ? '1' : '0'`, designed for descending sort). Their current route consumers sort by date/name so there is no visible priority bug today, but they must be migrated before any route starts sorting by `tmp_sort_*`. See `TODO__Agents.md`. --- diff --git a/src/lib/ae_journals/ae_journals_search_helpers.ts b/src/lib/ae_journals/ae_journals_search_helpers.ts index bfe6de4f..eeb120e2 100644 --- a/src/lib/ae_journals/ae_journals_search_helpers.ts +++ b/src/lib/ae_journals/ae_journals_search_helpers.ts @@ -75,8 +75,10 @@ export function journal_entry_matches_search( } export function journal_entry_compare_for_list(a: any, b: any): number { + // tmp_sort_1 is built by build_tmp_sort() for ascending comparison: + // priority=true encodes as '0', priority=false as '1', so ASC puts priority first. return ( - (b?.tmp_sort_1 ?? '').localeCompare(a?.tmp_sort_1 ?? '') || + (a?.tmp_sort_1 ?? '').localeCompare(b?.tmp_sort_1 ?? '') || (b?.updated_on ?? '').localeCompare(a?.updated_on ?? '') || (b?.journal_entry_id ?? '').localeCompare(a?.journal_entry_id ?? '') ); diff --git a/src/routes/idaa/(idaa)/bb/[post_id]/+page.svelte b/src/routes/idaa/(idaa)/bb/[post_id]/+page.svelte index 0cec5b01..20f14b06 100644 --- a/src/routes/idaa/(idaa)/bb/[post_id]/+page.svelte +++ b/src/routes/idaa/(idaa)/bb/[post_id]/+page.svelte @@ -73,12 +73,14 @@ let lq__post_comment_obj_li = $derived.by(() => { return liveQuery(async () => { if (!post_id) return []; - return await db_posts.comment + // .reverse() before .sortBy() is a Dexie no-op — reverse the array after instead. + // tmp_sort_1 here uses legacy encoding (priority=true→'1') designed for DESC order. + const comments = await db_posts.comment .where('post_id') .equals(post_id) - .reverse() .limit(limit) .sortBy('tmp_sort_1'); + return comments.reverse(); }); }); diff --git a/src/routes/idaa/(idaa)/recovery_meetings/+page.svelte b/src/routes/idaa/(idaa)/recovery_meetings/+page.svelte index c1d78948..9ccb59bf 100644 --- a/src/routes/idaa/(idaa)/recovery_meetings/+page.svelte +++ b/src/routes/idaa/(idaa)/recovery_meetings/+page.svelte @@ -255,8 +255,9 @@ async function handle_search_refresh(qry_key: string) { } else { // Robust Chronological Sort using pre-computed tmp_sort_1 // Handles Priority, Manual Sort, and the updated_on/created_on fallback + // tmp_sort_1 built by build_tmp_sort(): priority=true→'0', so ASC puts priority first. local_results.sort((a, b) => - (b.tmp_sort_1 ?? '').localeCompare(a.tmp_sort_1 ?? '') + (a.tmp_sort_1 ?? '').localeCompare(b.tmp_sort_1 ?? '') ); } @@ -335,8 +336,9 @@ async function handle_search_refresh(qry_key: string) { (b.name ?? '').localeCompare(a.name ?? '') ); } else { + // tmp_sort_1 built by build_tmp_sort(): priority=true→'0', so ASC puts priority first. api_results.sort((a, b) => - (b.tmp_sort_1 ?? '').localeCompare(a.tmp_sort_1 ?? '') + (a.tmp_sort_1 ?? '').localeCompare(b.tmp_sort_1 ?? '') ); } diff --git a/src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_li_wrapper.svelte b/src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_li_wrapper.svelte index 69e8c5e7..57dafd5e 100644 --- a/src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_li_wrapper.svelte +++ b/src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_li_wrapper.svelte @@ -95,8 +95,10 @@ let lq__event_obj_li = $derived.by(() => { } else { // Robust Chronological Sort using pre-computed tmp_sort_1 (Refactored 2026-02-16) // This handles Group > Priority > Manual Sort > Date (with updated_on fallback) + // tmp_sort_1 is built by build_tmp_sort() for ascending comparison: + // priority=true encodes as '0', priority=false as '1', so ASC puts priority first. results.sort((a, b) => - (b.tmp_sort_1 ?? '').localeCompare(a.tmp_sort_1 ?? '') + (a.tmp_sort_1 ?? '').localeCompare(b.tmp_sort_1 ?? '') ); } return results;