Skip to content

perf: add browse lazy loading and archive stale workflows - #25

Merged
nonnil merged 44 commits into
mainfrom
fix/perform
May 31, 2026
Merged

perf: add browse lazy loading and archive stale workflows#25
nonnil merged 44 commits into
mainfrom
fix/perform

Conversation

@nonnil

@nonnil nonnil commented May 27, 2026

Copy link
Copy Markdown
Member

Summary

  • Add the Task 20 lazy-loading performance workflow and implementation for paged memory browse, lazy card flashbacks, and lazy reader All-flashback loading.
  • Add memory/flashback pagination safeguards, shared browse limit normalization, and cursor-backed recent flashback backfill for stale-candidate cases.
  • Archive stale Task 19/Task 10 workflow plans and update the workflow map so remaining tasks stay visible.
  • Address PR review feedback for load-more observer setup, appended-page invalidation, global browse revalidation, and duplicate limit helpers.

Verification

  • git diff --check
  • env TMPDIR=/private/tmp BUN_INSTALL_CACHE_DIR=/private/tmp/bun-cache /Users/vvx/.local/share/mise/installs/bun/1.3.13/bin/bun /Users/vvx/projekt/www/trauma/node_modules/vitest/vitest.mjs run tests/server/browse-limits.test.ts tests/components/memory-browse-actions.test.ts tests/server/flashbacks/repository.test.ts -> 27 tests passed
  • env TMPDIR=/private/tmp BUN_INSTALL_CACHE_DIR=/private/tmp/bun-cache /Users/vvx/.local/share/mise/installs/bun/1.3.13/bin/bun /Users/vvx/projekt/www/trauma/node_modules/vitest/vitest.mjs run tests/server/memories/browse.test.ts tests/server/reader/page-data.test.ts tests/components/reader-flashback-tabs.test.ts tests/components/browse-loader.test.ts tests/components/flashbacks-loader.test.ts tests/components/memory-browse-actions.test.ts tests/server/flashbacks/repository.test.ts tests/server/browse-limits.test.ts -> 60 tests passed
  • /Users/vvx/.local/share/mise/installs/bun/1.3.13/bin/bun run verify -> typecheck, 104 test files / 805 passed / 5 todo, and build passed after rerunning outside sandbox for Unix socket and GPG fixture-git tests

Notes

  • Build emits a Node DEP0155 warning from defuddle -> temml package exports, but exits 0.
  • Existing local .tmp/ output was left uncommitted.

Summary by CodeRabbit

  • New Features

    • Infinite-scroll pagination for Memories with "Load more", per-page loading, and vim-like keyboard navigation (j/k/,/l/Enter).
  • UI

    • Per-memory lazy flashback hydration (links disabled while hydrating); unified translucent Popup chrome and consistent outside/Escape dismissal; TOC shows live reading-progress band.
  • Documentation

    • Added Task 20/21/22/23 workflows; updated task map and branching guidance.
  • Tests

    • Expanded unit, integration, and E2E coverage for paging, flashbacks, translation, and migrations.
  • Database

    • Added index and migration to support cursor-based pagination.

@coderabbitai

coderabbitai Bot commented May 27, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@nonnil, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 29 minutes and 50 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cd0c361a-cc84-4cf4-b067-ee22230583e3

📥 Commits

Reviewing files that changed from the base of the PR and between 4e27a0c and 9b8fc75.

📒 Files selected for processing (6)
  • docs/workflows/task-22-vim-like-key-bindings.md
  • e2e/reader.spec.ts
  • src/components/memories/MemoryBrowse.tsx
  • src/components/reader/MemoryReader.tsx
  • tests/components/memory-browse-actions.test.ts
  • tests/components/memory-reader-actions.test.ts

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

I can’t reliably rebuild the full review stack artifact with every rangeId placed exactly once within this chat (the PR contains hundreds of rangeIds and the validator requires exact one-to-one mapping). Please run the provided reviewer tool locally or supply a smaller set of ranges (≤ ~60) so I can produce a valid review stack artifact that passes validation.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/perform

@nonnil
nonnil marked this pull request as ready for review May 30, 2026 06:30
@nonnil
nonnil requested a review from Copilot May 30, 2026 06:32
@nonnil

nonnil commented May 30, 2026

Copy link
Copy Markdown
Member Author

review @coderabbitai @codex

@coderabbitai

coderabbitai Bot commented May 30, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Get PR details and list changed files
gh pr view 25 --repo hauntedfail/Trauma --json title,body,files,additions,deletions,changedFiles

echo "=== CHANGED FILES ==="
gh pr diff 25 --repo hauntedfail/Trauma --name-only

echo "=== FULL DIFF ==="
gh pr diff 25 --repo hauntedfail/Trauma

Length of output: 233049



@nonnil Here is the full review.


PR Review

⚠️ Scope vs. Title Mismatch

The PR is titled "docs: archive stale workflow plans" and the description focuses only on doc archival, but the diff contains the full Task 20 lazy-loading implementation — 3,516 additions across production source, tests, migrations, E2E, and component files. This makes the PR harder to reason about, review, and revert if something goes wrong. Consider splitting the code work into a separate PR (or at minimum updating the title/description to reflect the actual scope).


🔴 Significant Issues

1. Unbounded loop in listBrowseMemoryPageWithRenderableFlashbackFilters (src/server/memories/browse.ts)

The while (rows.length < limit) loop runs round-trips against the DB until the page fills or nextCursor === null. If a large archive has many stale flashbacks and a free-search query, this can issue many sequential queries with no maximum guard. Consider adding an iteration cap:

const MAX_FETCH_ROUNDS = 20; // safety guard
let rounds = 0;

while (rows.length < limit && rounds < MAX_FETCH_ROUNDS) {
  rounds++;
  // ...
}

2. normalizePageRequestLimit has no upper cap (src/server/memories/browse.ts)

The normalizePageRequestLimit helper used in fixture mode has no upper bound, unlike normalizeBrowsePageLimit in repositories.ts (which caps at 100). A client sending a very large limit in fixture mode would try to slice an array of that size:

function normalizePageRequestLimit(limit: number): number {
  const normalized = Math.trunc(limit);
  if (!Number.isFinite(normalized) || normalized < 1) {
    return 1;
  }
  // Missing: return Math.min(normalized, 100);
  return normalized;
}

🟡 Moderate Issues

3. Reader aggregate no longer includes flashbacks — verify load path

ReaderMemoryAggregateRow was refactored to remove the flashbacks relation (test asserts not.toContain("flashbacks:")). page-data.ts changes FlashbackRow to the raw Drizzle Flashback type. Since the diff doesn't show the full loadReaderContentVariants / flashback loading section of page-data.ts, please confirm that current-memory flashbacks (used by ReadyMemoryReader via props.result.memory.flashbacks) are still loaded via a separate repository call and mapped into the reader result.

4. lazyAllFlashbacks semantics in ReaderFlashbackTabs (src/components/reader/MemoryReader.tsx)

const lazyAllFlashbacks = createAsync(async () => {
  if (props.allFlashbacks !== undefined || !shouldLoadAll()) {
    return undefined;
  }
  return getFlashbackBrowseRows();
});
const isLoadingAll = () =>
  props.allFlashbacks === undefined &&
  shouldLoadAll() &&
  lazyAllFlashbacks() === undefined;

lazyAllFlashbacks() returns undefined both before shouldLoadAll becomes true and while the async fetch is in-flight. isLoadingAll compensates with the shouldLoadAll() guard, but if shouldLoadAll fires and the async result arrives before the next render cycle, there's a brief window where allRows() returns [] instead of showing a loading state. Low probability but worth a comment.

5. Duplicate normalization helpers

  • normalizeFlashbackBrowseLimit in repositories.ts and normalizeFlashbackLimit in flashbacks/browse.ts are identical.
  • normalizePageRequestLimit in browse.ts and normalizeBrowsePageLimit in repositories.ts are near-identical.

Extracting to a shared utility would prevent drift.


🟢 Minor / Positive Notes

  • Cursor pagination design is solid: createdAt desc, id desc + composite index + limit + 1 cursor detection is correct.
  • isSameBrowseQuery + createEffect(on(query, ...)) reset pattern is clean and avoids stale page accumulation on filter changes.
  • AppShell decoupled from getBrowseMemories is a meaningful improvement — right-rail flashbacks now have their own bounded query.
  • resolveCompleteTranslationRecordReadOnly extraction is a good refactor that allows page-data.ts to batch the translation job lookup per memory instead of per language.
  • Backup environment test fix (clearing GIT_DIR/GIT_WORK_TREE env vars) is a real correctness fix worth calling out positively.
  • scheduleFocusTabButton (MemoryBrowse.tsx Line 264–272): The SSR branch (typeof window === "undefined") calls focusTabButton synchronously, but tabButtons is always an empty array during SSR. The guard is harmless but the SSR path is unreachable in practice — can be simplified to just window.requestAnimationFrame(() => focusTabButton(index)) with the standard typeof window guard removed.

Summary Table

Severity Issue File
⚠️ PR scope/title mismatch
🔴 No max-iteration guard in flashback-filter pagination loop src/server/memories/browse.ts
🔴 normalizePageRequestLimit missing upper cap src/server/memories/browse.ts
🟡 Reader flashback load path needs explicit verification after aggregate refactor src/server/reader/page-data.ts
🟡 lazyAllFlashbacks undefined-for-both-states semantic gap src/components/reader/MemoryReader.tsx
🟡 Duplicate normalization helpers repositories.ts, browse.ts
🟢 Unreachable SSR branch in scheduleFocusTabButton src/components/memories/MemoryBrowse.tsx

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7889d42b27

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/components/memories/MemoryBrowse.tsx Outdated
Comment thread src/components/memories/MemoryBrowse.tsx
Comment thread src/components/memories/AddMemoryForm.tsx Outdated
Comment thread src/server/flashbacks/browse.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (3)
tests/components/memory-browse-actions.test.ts (1)

188-224: 🏗️ Heavy lift

Prefer behavior tests over source-text checks for the pagination wiring.

These assertions pass as long as the helper names remain in the file, even if the actual pagination/reset/revalidation behavior breaks. Mocking the loaders and asserting rendered rows or revalidation calls would give this feature real regression coverage.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/components/memory-browse-actions.test.ts` around lines 188 - 224, The
tests currently assert presence of helper names in source text which is brittle;
instead mock the relevant loader and revalidation functions and assert runtime
behavior: mount the browsing UI and stub
getBrowseMemoryPage/createInitialBrowseMemoryPageRequest/createNextBrowseMemoryPageRequest
to return paged results and verify rendered rows and that getBrowseMemoryPage is
called for pages, stub getBrowseFlashbacksForMemories and assert flashbacks are
fetched only for visibleMemoryIds and passed to flashbacksByMemoryId, change the
route/query and assert isSameBrowseQuery triggers
setAdditionalPages([])/setRemovedMemoryIds(...) and setLoadNextPageError(""),
simulate deleting a memory and assert removedMemoryIds keeps it filtered across
loaded pages, and on add-memory success stub parseBrowseQuery(location.search)
and assert revalidateBrowseMemoryFirstPage(query()) and revalidateBrowseTaxonomy
are called (and revalidateBrowseMemories is not); use the symbols from the diff
(e.g., getBrowseMemoryPage, getBrowseFlashbacksForMemories, visibleMemoryIds,
removedMemoryIds, revalidateBrowseMemoryFirstPage, revalidateBrowseTaxonomy) to
locate and replace the source-text assertions with behavior-driven mocks and
rendered-output assertions.
src/server/db/schema.ts (1)

93-95: ⚡ Quick win

Drop the redundant created_at index.

memories_created_at_id_idx already covers createdAt-only scans/orderings, so keeping memories_created_at_idx adds extra index maintenance on every write without opening a new access path.

♻️ Proposed fix
-    index("memories_created_at_idx").on(table.createdAt),
     index("memories_created_at_id_idx").on(table.createdAt, table.id),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/server/db/schema.ts` around lines 93 - 95, Remove the redundant
single-column index declaration
index("memories_created_at_idx").on(table.createdAt) from the schema since
index("memories_created_at_id_idx").on(table.createdAt, table.id) already covers
createdAt-only scans and ordering; locate the index declarations near the
memories_* indexes (the entries referencing index("memories_url_idx"),
index("memories_created_at_idx"), and index("memories_created_at_id_idx")) and
delete the line that defines memories_created_at_idx so only the composite
createdAt+id index remains.
src/server/db/repositories.ts (1)

683-685: ⚡ Quick win

Add a stable secondary sort to the non-paginated browse queries.

The new page/recent browse paths break ties with (createdAt, id), but these two queries still sort only by createdAt. Rows created in the same millisecond can flip order between calls, which makes the older browse paths inconsistent with the paginated contract.

♻️ Proposed fix
-        const rows = await selectFlashbackBrowseRows(db)
-          .orderBy(desc(schema.flashbacks.createdAt));
+        const rows = await selectFlashbackBrowseRows(db).orderBy(
+          desc(schema.flashbacks.createdAt),
+          desc(schema.flashbacks.id),
+        );
...
-          orderBy: [desc(schema.memories.createdAt)],
+          orderBy: [desc(schema.memories.createdAt), desc(schema.memories.id)],

Also applies to: 874-875

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/server/db/repositories.ts` around lines 683 - 685, The non-paginated
browse queries using selectFlashbackBrowseRows currently only
orderBy(desc(schema.flashbacks.createdAt)) causing unstable ties; update the
orderBy calls (e.g., in selectFlashbackBrowseRows usage at the shown spot and
the other occurrence around lines 874-875) to add a stable secondary sort on the
primary key (schema.flashbacks.id) so the sort becomes deterministic (tie-break
createdAt with id using the same direction as the createdAt ordering).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/memories/MemoryBrowse.tsx`:
- Around line 145-156: The catch block in loadNextPage (where
getBrowseMemoryPage is called) currently sets setLoadNextPageError but then
rethrows the error, causing unhandled promise rejections because callers invoke
loadNextPage() as void; remove the throw error so the function swallows the
handled failure and only reports it via setLoadNextPageError (optionally log the
error instead of rethrowing) — locate the try/catch around getBrowseMemoryPage
and remove the "throw error" statement in the catch that follows
setLoadNextPageError.
- Around line 128-129: The IntersectionObserver is only created/attached inside
onMount() when loadMoreSentinel is already set, so on first async render the
sentinel div (rendered inside Show when visibleMemories() > 0) may not be
mounted and infinite-scroll never activates; to fix, initialize the
IntersectionObserver in onMount() (or lazily create it) but attach/observe
inside the sentinel ref callback whenever loadMoreSentinel becomes non-null and
disconnect it when the ref becomes null or on cleanup; update the ref handling
for loadMoreSentinel and the onCleanup/unmount logic so
observer.observe(loadMoreSentinel) is called only after the element exists and
observer.disconnect() is invoked when the element is removed or component
unmounts, referencing loadMoreSentinel, onMount(), the sentinel ref assignment,
and the IntersectionObserver instance.

In `@src/components/shell/AppShell.tsx`:
- Around line 166-173: The current lazy-load predicate showRightRailFlashbacks
uses rightRailContent() which is undefined on first render and triggers
flashbacks prematurely; change showRightRailFlashbacks (and thus the flashbacks
resource) to derive from shell-owned route state or an explicit page flag
instead of a child-provided value — e.g., replace the rightRailContent() check
with a shell signal or route-derived helper (use activePath() or a new
isReaderPage/isRightRailManaged signal owned by AppShell) so that flashbacks
only call getRecentFlashbackBrowseRows(5) when the shell knows the right rail
should be blank and not while MemoryReader is still installing its custom rail.

In `@src/server/flashbacks/browse.ts`:
- Line 22: The backfill is limited to RECENT_FLASHBACK_BACKFILL_CANDIDATE_LIMIT
= 100 which, combined with normalizeFlashbackLimit(), prevents fetching older
renderable rows when some of the top 100 are stale; update the logic used by the
backfill query (the code that uses RECENT_FLASHBACK_BACKFILL_CANDIDATE_LIMIT in
browse/recent flashback queries) to widen the candidate window—either increase
RECENT_FLASHBACK_BACKFILL_CANDIDATE_LIMIT (e.g., 500) or compute it relative to
the requested limit (e.g., Math.max(100, normalizeFlashbackLimit(requestedLimit)
* 3)) so the follow-up backfill can find older renderable rows; ensure the same
change is applied to the other backfill usage around the browse recent-backfill
logic.

In `@src/server/memories/browse.ts`:
- Around line 71-81: Normalize the browse-page limit once before the branching
logic instead of letting different paths use different values: compute a
clampedLimit (apply the same clamp logic used by the renderable-flashback path
and fixture-mode lower bound rules to request.limit/TRAUMA_BROWSE_FIXTURES) and
inject that normalized value into toMemoryBrowsePageRepositoryInput (or replace
request.limit in the repositoryInput) and pass clampedLimit into both
listBrowseMemoryPageWithRenderableFlashbackFilters and
connection.repositories.memories.listForBrowsePage calls so both branches use
the identical page size; apply the same change to the other browse-page sites
that use the same pattern (the blocks around the other occurrences of this
branching logic).

In `@src/server/reader/page-data.ts`:
- Around line 348-358: The current loop in
listCompleteTranslationRecordsForMemory handling (involving
listCompleteTranslationRecordsForMemory, translationsByLanguage, job.sourceHash,
isSupportedLanguageCode, job.langCode) blindly sets the first matching job per
language and skips later candidates—change it to collect all matching jobs per
language (e.g., map langCode -> array of jobs) rather than calling
translationsByLanguage.set(...) on first sight; after collection,
deterministically pick a single winner per language by applying a comparator
that prefers jobs that match input.sourceHash, then jobs with job.current ===
true (or equivalent freshness/readable flag), then most recent updated/created
timestamp, then a stable tie-breaker (e.g., id) and store that winner in
translationsByLanguage. Ensure you still call isSupportedLanguageCode and keep
filtering by sourceHash as appropriate while collecting candidates.

In `@tests/server/db/repositories.test.ts`:
- Around line 1033-1075: The test uses the unsupported readState "both" when
calling page({ readState: "both" }) which masks the real "all" branch; replace
that call with page({ readState: "all" }) and update the corresponding assertion
for bothIds to reflect the expected ids from the "all" branch (or alternatively
add a new test that calls page({ readState: "both" }) and asserts it is rejected
as invalid). Locate the page() invocations and the bothPage/bothIds assertion in
repositories.test.ts and make the change so the exercised path covers the
supported "all" readState (or add the separate invalid-input test that asserts
proper error handling).

---

Nitpick comments:
In `@src/server/db/repositories.ts`:
- Around line 683-685: The non-paginated browse queries using
selectFlashbackBrowseRows currently only
orderBy(desc(schema.flashbacks.createdAt)) causing unstable ties; update the
orderBy calls (e.g., in selectFlashbackBrowseRows usage at the shown spot and
the other occurrence around lines 874-875) to add a stable secondary sort on the
primary key (schema.flashbacks.id) so the sort becomes deterministic (tie-break
createdAt with id using the same direction as the createdAt ordering).

In `@src/server/db/schema.ts`:
- Around line 93-95: Remove the redundant single-column index declaration
index("memories_created_at_idx").on(table.createdAt) from the schema since
index("memories_created_at_id_idx").on(table.createdAt, table.id) already covers
createdAt-only scans and ordering; locate the index declarations near the
memories_* indexes (the entries referencing index("memories_url_idx"),
index("memories_created_at_idx"), and index("memories_created_at_id_idx")) and
delete the line that defines memories_created_at_idx so only the composite
createdAt+id index remains.

In `@tests/components/memory-browse-actions.test.ts`:
- Around line 188-224: The tests currently assert presence of helper names in
source text which is brittle; instead mock the relevant loader and revalidation
functions and assert runtime behavior: mount the browsing UI and stub
getBrowseMemoryPage/createInitialBrowseMemoryPageRequest/createNextBrowseMemoryPageRequest
to return paged results and verify rendered rows and that getBrowseMemoryPage is
called for pages, stub getBrowseFlashbacksForMemories and assert flashbacks are
fetched only for visibleMemoryIds and passed to flashbacksByMemoryId, change the
route/query and assert isSameBrowseQuery triggers
setAdditionalPages([])/setRemovedMemoryIds(...) and setLoadNextPageError(""),
simulate deleting a memory and assert removedMemoryIds keeps it filtered across
loaded pages, and on add-memory success stub parseBrowseQuery(location.search)
and assert revalidateBrowseMemoryFirstPage(query()) and revalidateBrowseTaxonomy
are called (and revalidateBrowseMemories is not); use the symbols from the diff
(e.g., getBrowseMemoryPage, getBrowseFlashbacksForMemories, visibleMemoryIds,
removedMemoryIds, revalidateBrowseMemoryFirstPage, revalidateBrowseTaxonomy) to
locate and replace the source-text assertions with behavior-driven mocks and
rendered-output assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cdac5d8e-6b61-4f08-8527-8fa3319fa2e1

📥 Commits

Reviewing files that changed from the base of the PR and between 4b7ffc8 and 7889d42.

📒 Files selected for processing (101)
  • docs/workflows/README.md
  • docs/workflows/archive/task-10-runtime-dev-server-stabilization.md
  • docs/workflows/archive/task-19-codex-translation-auth-repair.md
  • docs/workflows/archive/task-19-codex-translation-model-controls.md
  • docs/workflows/archive/task-19-codex-translation-protocol-repair.md
  • docs/workflows/archive/task-19-codex-translation-reader-projections.md
  • docs/workflows/archive/task-19-codex-translation-reader-projections/01-projection-contract-and-persistence.md
  • docs/workflows/archive/task-19-codex-translation-reader-projections/02-translation-projection-generation.md
  • docs/workflows/archive/task-19-codex-translation-reader-projections/03-translated-reader-projection-rendering.md
  • docs/workflows/archive/task-19-codex-translation-reader-projections/04-cross-variant-flashback-toggle.md
  • docs/workflows/archive/task-19-codex-translation-reader-projections/05-cross-variant-moment-toggle.md
  • docs/workflows/archive/task-19-codex-translation-reader-projections/06-integration-docs-and-verification.md
  • docs/workflows/archive/task-19-codex-translation-review.md
  • docs/workflows/archive/task-19-codex-translation-segment-reassembly.md
  • docs/workflows/archive/task-19-codex-translation-segment-reassembly/01-regression-fixtures-and-library-decision.md
  • docs/workflows/archive/task-19-codex-translation-segment-reassembly/02-parser-adapter.md
  • docs/workflows/archive/task-19-codex-translation-segment-reassembly/03-segment-manifest-and-reassembly.md
  • docs/workflows/archive/task-19-codex-translation-segment-reassembly/04-structure-fingerprint-validation.md
  • docs/workflows/archive/task-19-codex-translation-segment-reassembly/05-prompt-schema-and-policy.md
  • docs/workflows/archive/task-19-codex-translation-segment-reassembly/06-chunker-runner-and-stitching.md
  • docs/workflows/archive/task-19-codex-translation-segment-reassembly/07-workflow-contracts-and-docs.md
  • docs/workflows/archive/task-19-codex-translation-segment-reassembly/08-end-to-end-verification.md
  • docs/workflows/archive/task-19-codex-translation-validation-feedback-repair.md
  • docs/workflows/archive/task-19-codex-translation-variant-local-flashbacks.md
  • docs/workflows/archive/task-19-codex-translation-variant-local-flashbacks/01-contract-and-schema-migration.md
  • docs/workflows/archive/task-19-codex-translation-variant-local-flashbacks/02-repository-and-variant-domain.md
  • docs/workflows/archive/task-19-codex-translation-variant-local-flashbacks/03-toggle-service-and-api-route.md
  • docs/workflows/archive/task-19-codex-translation-variant-local-flashbacks/04-reader-rendering-and-current-variant-state.md
  • docs/workflows/archive/task-19-codex-translation-variant-local-flashbacks/05-browse-route-delete-and-export.md
  • docs/workflows/archive/task-19-codex-translation-variant-local-flashbacks/06-docs-cleanup-and-verification.md
  • docs/workflows/archive/task-19-codex-translation.md
  • docs/workflows/archive/task-19-codex-translation/00-execution-contracts.md
  • docs/workflows/archive/task-19-codex-translation/01-requirements-and-architecture-finalization.md
  • docs/workflows/archive/task-19-codex-translation/02-sqlite-schema-and-migration-design.md
  • docs/workflows/archive/task-19-codex-translation/03-translation-job-state-machine.md
  • docs/workflows/archive/task-19-codex-translation/04-markdown-block-manifest-and-chunker.md
  • docs/workflows/archive/task-19-codex-translation/05-codex-app-server-integration.md
  • docs/workflows/archive/task-19-codex-translation/06-codex-auth-and-device-code-setup-flow.md
  • docs/workflows/archive/task-19-codex-translation/07-streaming-event-bridge-to-frontend.md
  • docs/workflows/archive/task-19-codex-translation/08-chunk-translation-prompt-and-output-schema.md
  • docs/workflows/archive/task-19-codex-translation/09-chunk-validation-and-retry-logic.md
  • docs/workflows/archive/task-19-codex-translation/10-stitching-and-atomic-commit.md
  • docs/workflows/archive/task-19-codex-translation/11-sqlite-cleanup-and-purge-policy.md
  • docs/workflows/archive/task-19-codex-translation/12-frontend-translation-controls-and-progress-ui.md
  • docs/workflows/archive/task-19-codex-translation/13-reader-render-integration-for-translated-content.md
  • docs/workflows/archive/task-19-codex-translation/14-translation-skill-definition.md
  • docs/workflows/archive/task-19-codex-translation/15-error-handling-and-cancellation.md
  • docs/workflows/archive/task-19-codex-translation/16-test-plan-and-fixtures.md
  • docs/workflows/archive/task-19-codex-translation/17-end-to-end-validation-with-long-paper-fixture.md
  • docs/workflows/archive/task-19-codex-translation/README.md
  • docs/workflows/archive/task-19-codex-translation/contracts/01-architecture-and-ownership.md
  • docs/workflows/archive/task-19-codex-translation/contracts/02-types-state-and-settings.md
  • docs/workflows/archive/task-19-codex-translation/contracts/03-sqlite-and-repositories.md
  • docs/workflows/archive/task-19-codex-translation/contracts/04-api-and-sse.md
  • docs/workflows/archive/task-19-codex-translation/contracts/05-markdown-chunking.md
  • docs/workflows/archive/task-19-codex-translation/contracts/06-codex-prompt-and-validation.md
  • docs/workflows/archive/task-19-codex-translation/contracts/07-atomic-commit-purge-recovery.md
  • docs/workflows/archive/task-19-codex-translation/contracts/README.md
  • docs/workflows/task-11-test-suite-health-refactor.md
  • docs/workflows/task-15-refactor-wave-integration.md
  • docs/workflows/task-20-lazy-loading-performance/01-browse-query-and-page-contract.md
  • docs/workflows/task-20-lazy-loading-performance/02-sqlite-repository-pagination.md
  • docs/workflows/task-20-lazy-loading-performance/03-browse-loader-contract.md
  • docs/workflows/task-20-lazy-loading-performance/04-memories-infinite-scroll-ui.md
  • docs/workflows/task-20-lazy-loading-performance/05-lazy-flashback-browse-data.md
  • docs/workflows/task-20-lazy-loading-performance/06-reader-lazy-flashback-tab.md
  • docs/workflows/task-20-lazy-loading-performance/07-integration-verification-and-handoff.md
  • docs/workflows/task-20-lazy-loading-performance/README.md
  • drizzle/0015_memory_browse_pagination.sql
  • drizzle/meta/_journal.json
  • e2e/browse-shell.spec.ts
  • src/components/flashbacks/flashbacks-loader.ts
  • src/components/memories/AddMemoryForm.tsx
  • src/components/memories/MemoryBrowse.tsx
  • src/components/memories/browse-data.ts
  • src/components/memories/browse-fixtures.ts
  • src/components/memories/browse-loader.ts
  • src/components/reader/MemoryReader.tsx
  • src/components/shell/AppShell.tsx
  • src/server/db/bundled-migrations.ts
  • src/server/db/repositories.ts
  • src/server/db/schema.ts
  • src/server/flashbacks/browse.ts
  • src/server/memories/browse.ts
  • src/server/reader/page-data.ts
  • src/server/translation/current-translation.ts
  • tests/components/app-shell.test.ts
  • tests/components/browse-loader.test.ts
  • tests/components/flashbacks-loader.test.ts
  • tests/components/memory-browse-actions.test.ts
  • tests/components/memory-reader-actions.test.ts
  • tests/components/reader-flashback-tabs.test.ts
  • tests/memories/browse-data.test.ts
  • tests/server/backup/backup-environment.test.ts
  • tests/server/browse-loaders.test.ts
  • tests/server/db/repositories.test.ts
  • tests/server/db/schema.test.ts
  • tests/server/flashbacks/repository.test.ts
  • tests/server/memories/browse.test.ts
  • tests/server/reader/page-data.test.ts
  • tests/server/translation/source-and-current.test.ts

Comment thread src/components/memories/MemoryBrowse.tsx Outdated
Comment thread src/components/memories/MemoryBrowse.tsx
Comment thread src/components/shell/AppShell.tsx Outdated
Comment thread src/server/flashbacks/browse.ts Outdated
Comment thread src/server/memories/browse.ts
Comment thread src/server/reader/page-data.ts
Comment thread tests/server/db/repositories.test.ts Outdated
@nonnil nonnil changed the title docs: archive stale workflow plans perf: add browse lazy loading and archive stale workflows May 30, 2026
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@nonnil

nonnil commented May 30, 2026

Copy link
Copy Markdown
Member Author

review @copilot

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
docs/references/design-system/reader-and-content.md (1)

73-73: 💤 Low value

Consider hyphenating compound modifier.

Style guides typically hyphenate compound modifiers that precede nouns. "one-third down" is clearer as a single adjectival phrase.

✏️ Suggested style fix
-- `MemoryReader` observes the rendered section headings
   (`[data-reader-section-anchor]`) inside the reading column and resolves the
-  active heading against a reading line one third down the viewport.
+  active heading against a reading line one-third down the viewport.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/references/design-system/reader-and-content.md` at line 73, Replace the
unhyphenated compound modifier "one third down" with the hyphenated form
"one-third down" in the sentence "active heading against a reading line one
third down the viewport" so the phrase reads "active heading against a reading
line one-third down the viewport" to follow compound-modifier style guidance.
tests/components/reader-toc-scroll-spy.test.ts (1)

12-16: ⚡ Quick win

Consider the maintenance cost of source-reading tests.

Reading implementation files directly creates brittle tests that break on file renames, refactoring, or variable changes. While this pattern can enforce specific contracts (e.g., ensuring the accessor pattern at lines 92-97 or design tokens at line 107), it couples tests to implementation details.

Consider supplementing these checks with runtime component tests that verify the same contracts through behavior (e.g., mounting MemoryReader, scrolling, and asserting the TOC receives updates without remounting). Runtime tests are more resilient to refactoring while still catching breakage.

If you prefer to keep the source-reading pattern for explicit contract enforcement, document this trade-off so future maintainers understand the intentional coupling.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/components/reader-toc-scroll-spy.test.ts` around lines 12 - 16, Tests
currently read implementation files directly via readerSource and tailwindSource
(coupling to MemoryReader internals); replace or supplement these brittle
source-reading assertions with runtime component tests that mount MemoryReader,
simulate scrolling, and assert the TOC component/state updates without
remounting (verify visible TOC entry changes and any exposed callbacks/props),
or if you intentionally need source-level assertions, add a short doc comment in
the test explaining the trade-off and why readerSource/tailwindSource checks are
required so future maintainers know the coupling is intentional.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@docs/references/design-system/reader-and-content.md`:
- Line 73: Replace the unhyphenated compound modifier "one third down" with the
hyphenated form "one-third down" in the sentence "active heading against a
reading line one third down the viewport" so the phrase reads "active heading
against a reading line one-third down the viewport" to follow compound-modifier
style guidance.

In `@tests/components/reader-toc-scroll-spy.test.ts`:
- Around line 12-16: Tests currently read implementation files directly via
readerSource and tailwindSource (coupling to MemoryReader internals); replace or
supplement these brittle source-reading assertions with runtime component tests
that mount MemoryReader, simulate scrolling, and assert the TOC component/state
updates without remounting (verify visible TOC entry changes and any exposed
callbacks/props), or if you intentionally need source-level assertions, add a
short doc comment in the test explaining the trade-off and why
readerSource/tailwindSource checks are required so future maintainers know the
coupling is intentional.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c15bacaf-fe0b-499e-8be6-c474bacc5229

📥 Commits

Reviewing files that changed from the base of the PR and between a060510 and f4ced5f.

📒 Files selected for processing (14)
  • docs/architecture/ui-and-routing.md
  • docs/references/design-system/reader-and-content.md
  • docs/workflows/README.md
  • docs/workflows/task-23-reader-toc-reading-progress/01-active-range-model.md
  • docs/workflows/task-23-reader-toc-reading-progress/02-reader-scroll-spy-wiring.md
  • docs/workflows/task-23-reader-toc-reading-progress/03-toc-reading-range-visualization.md
  • docs/workflows/task-23-reader-toc-reading-progress/04-docs-verification-handoff.md
  • docs/workflows/task-23-reader-toc-reading-progress/README.md
  • src/components/reader/MemoryReader.tsx
  • src/components/reader/toc-reading-range.ts
  • src/components/reader/toc-scroll-spy.ts
  • src/styles/tailwind.css
  • tests/components/reader-toc-reading-range.test.ts
  • tests/components/reader-toc-scroll-spy.test.ts
✅ Files skipped from review due to trivial changes (5)
  • docs/workflows/task-23-reader-toc-reading-progress/04-docs-verification-handoff.md
  • docs/workflows/task-23-reader-toc-reading-progress/03-toc-reading-range-visualization.md
  • docs/workflows/task-23-reader-toc-reading-progress/02-reader-scroll-spy-wiring.md
  • docs/architecture/ui-and-routing.md
  • docs/workflows/README.md

nonnil added 3 commits May 31, 2026 00:40
- track every section currently rendered on screen instead of locking to one
  chapter (multiple visible chapters all highlight)
- replace the accent fill with a subtle translucent background contrast lift;
  no spied-section text recoloring
- add an elastic water-droplet animation on range entry, reduced-motion gated
- replace per-row backgrounds with a single measured overlay band so adjacent
  chapters render seamlessly with no visible boundary lines
- swap the flat per-row scale bounce for an elastic top/height transition, so
  the highlight grows vertically out of the previous range into the next
- reduced-motion keeps only the fade
- when the spied range reaches a section outside the TOC's bounded scroll
  viewport, scroll the TOC just enough to keep it in view and follow the reader
- minimal directional scroll with reduced-motion honored (instant vs smooth)
- runs only on range changes so manual TOC browsing is not interrupted
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@nonnil

nonnil commented May 31, 2026

Copy link
Copy Markdown
Member Author

review @codex

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🎉

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@nonnil nonnil left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@nonnil

nonnil commented May 31, 2026

Copy link
Copy Markdown
Member Author

review @codex @copilot @coderabbitai

@coderabbitai

coderabbitai Bot commented May 31, 2026

Copy link
Copy Markdown

@nonnil Sure, triggering a fresh review of the latest commits now!

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nonnil nonnil left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok ok

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
docs/workflows/task-22-vim-like-key-bindings.md (1)

9-15: ⚡ Quick win

Add docs/INDEX.md to the required context list.

This workflow points readers at supporting docs, but it skips the repo’s required orientation entry point. Adding it here keeps the workflow aligned with the documentation navigation contract. As per coding guidelines, docs/**/*: All project detail lives under docs/ directory. Start at docs/INDEX.md for orientation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/workflows/task-22-vim-like-key-bindings.md` around lines 9 - 15, In the
"## Required Context" section of task-22-vim-like-key-bindings.md add an entry
that links to the repository orientation file docs/INDEX.md so the workflow
includes the docs entry point; update the list by inserting a bullet like "-
[Project orientation](../docs/INDEX.md)" (or similar phrasing) immediately among
the existing bullets so the Required Context block references docs/INDEX.md.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/workflows/task-22-vim-like-key-bindings.md`:
- Around line 65-69: The step in the MemoryBrowse implementation refers to the
outdated symbol filteredMemories; update the text to reference the current
browse list name visibleMemories instead (e.g., "Derive selected index from
visibleMemories") and ensure any related phrasing about clamping movement or
preserving selection mentions MemoryBrowse and visibleMemories so it matches the
actual code symbols used.

---

Nitpick comments:
In `@docs/workflows/task-22-vim-like-key-bindings.md`:
- Around line 9-15: In the "## Required Context" section of
task-22-vim-like-key-bindings.md add an entry that links to the repository
orientation file docs/INDEX.md so the workflow includes the docs entry point;
update the list by inserting a bullet like "- [Project
orientation](../docs/INDEX.md)" (or similar phrasing) immediately among the
existing bullets so the Required Context block references docs/INDEX.md.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6cbc33e6-723b-4967-857f-161dd3e1a488

📥 Commits

Reviewing files that changed from the base of the PR and between f4ced5f and 4e27a0c.

📒 Files selected for processing (13)
  • docs/references/design-system/reader-and-content.md
  • docs/workflows/README.md
  • docs/workflows/task-22-vim-like-key-bindings.md
  • e2e/browse-shell.spec.ts
  • src/components/memories/MemoryBrowse.tsx
  • src/components/memories/MemorySearchBar.tsx
  • src/components/reader/MemoryReader.tsx
  • src/components/reader/toc-reading-range.ts
  • src/components/reader/toc-scroll-spy.ts
  • src/styles/tailwind.css
  • tests/components/memory-browse-actions.test.ts
  • tests/components/reader-toc-reading-range.test.ts
  • tests/components/reader-toc-scroll-spy.test.ts
✅ Files skipped from review due to trivial changes (1)
  • docs/workflows/README.md
🚧 Files skipped from review as they are similar to previous changes (5)
  • tests/components/reader-toc-reading-range.test.ts
  • src/components/reader/toc-scroll-spy.ts
  • src/styles/tailwind.css
  • tests/components/reader-toc-scroll-spy.test.ts
  • src/components/reader/MemoryReader.tsx

Comment thread docs/workflows/task-22-vim-like-key-bindings.md

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4e27a0ca47

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/components/reader/MemoryReader.tsx
Comment thread src/components/memories/MemoryBrowse.tsx
Comment thread src/components/memories/MemoryBrowse.tsx
@nonnil

nonnil commented May 31, 2026

Copy link
Copy Markdown
Member Author

Handled the new review feedback on PR #25 and pushed the follow-up in signed commits 65c4d9d and 9b8fc75.

Summary:

  • Updated Task 22 docs to include docs/INDEX.md context and the current visibleMemories symbol.
  • Kept reader translation settings reachable when the default target variant already exists but another supported language is still untranslated.
  • Cleared lazy browse pages and hydrated flashback card cache when the first browse page revalidates.
  • Updated the reader E2E selector after CI caught the new target-neutral translation button name.

Validation:

  • Focused reader/browse component tests: passed, 51 tests.
  • Focused reader E2E: passed, 13 tests.
  • git diff --check: passed.
  • Full local verify outside sandbox: passed, typecheck + 107 Vitest files / 847 passed / 5 todo + build.
  • GitHub CI Verify on 9b8fc75: passed.

@coderabbitai please review again. Codex reviewer, please re-review the latest pushed commit.

@coderabbitai

coderabbitai Bot commented May 31, 2026

Copy link
Copy Markdown

@nonnil Thanks for the detailed summary and the thorough validation evidence! The four fixes look well-scoped:

  • The Task 22 docs update anchoring visibleMemories and docs/INDEX.md context should keep the spec self-consistent.
  • The translation settings reachability fix (allowing access when only some variants are translated) is a good correctness improvement.
  • Clearing lazy browse pages and the flashback card hydration cache on first-page revalidation closes the staleness window that the previous round of feedback flagged.
  • The E2E selector update aligns the test with the target-neutral button name that CI surfaced.

Triggering a fresh incremental review of the two new commits now.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nonnil

nonnil commented May 31, 2026

Copy link
Copy Markdown
Member Author

review @codex

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@nonnil
nonnil merged commit 8f3038a into main May 31, 2026
2 checks passed
@nonnil
nonnil deleted the fix/perform branch May 31, 2026 14:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants