Skip to content

Timeline UX + multi-file memory viewer + captain-hook v2.1.92 expansion#55

Merged
JayantDevkar merged 15 commits intomainfrom
enhance/timeline-updates
Apr 12, 2026
Merged

Timeline UX + multi-file memory viewer + captain-hook v2.1.92 expansion#55
JayantDevkar merged 15 commits intomainfrom
enhance/timeline-updates

Conversation

@the-non-expert
Copy link
Copy Markdown
Collaborator

@the-non-expert the-non-expert commented Mar 22, 2026

Summary

This PR started as timeline UX polish and grew into four themed chunks, kept bundled per the 2026-04-07 scope decision. All 13 commits pass CI locally (ruff, pytest, svelte-check, eslint, vitest).

What's in the PR

1. Timeline UX (original scope) — 4 commits

  • Granular copy buttons on markdown: every H1/H2 + H3 when prose ≥150 chars + every code block (markdownCopyButtons.ts).
  • Write tool content panel: timeline card now shows full file content with line count and scroll container.
  • TaskUpdate subject mapping: Pass 1b pre-scan parses TaskCreate tool results into a taskId → subject map; TaskUpdate events display the task description instead of just Task #N.
  • Scroll position restore on back navigation for /sessions and /projects/[slug] (sessionStorage + beforeNavigate + await tick() + rAF).
  • Last-opened session highlight: previously-clicked card glows with a 2px accent ring that fades over 1.8s.
  • Pagination state restore on back navigation (URL sync for projects, sessionStorage + reload for sessions).
  • Skill route matcher to prevent file paths from matching the [skill_name] segment.

2. Code-review fix commit (13cd5bf)

Addresses 5 HIGH + 4 MEDIUM + 1 LOW findings from PR review — summary inline in the commit and the earlier PR comment. Key fixes: Write-tool content panel un-skipped, last-opened highlight drift between live/canonical slugs, H1 selector + code-block gating, TaskUpdate rename delta, Task #(\d+) regex tightening, deepcopy dropped from jsonl_utils merge.

3. JSONL [Image #N] marker fix (272f506)

Claude Code v2.1.83+ serializes attached images as [Image #N] placeholders. JSONL merge now recognizes and preserves the pattern. +1 regression test.

4. Captain-hook v2.1.92 expansion (cf7eb26)

Eleven new hook models covering Claude Code v2.1.83–v2.1.92:

  • Filesystem/context: InstructionsLoaded, CwdChanged, FileChanged
  • User interaction: PermissionDenied, Elicitation, ElicitationResult
  • Agent Teams (experimental): TaskCreated, TaskCompleted, TeammateIdle
  • Worktree lifecycle: WorktreeCreate, WorktreeRemove

Plus "defer" added to PreToolUseOutput.permissionDecision (v2.1.89) and a new PermissionDeniedOutput class with retry: bool. HOOK_TYPE_MAP grows from 13 → 24. Adds 3 domain modules (fs_hooks.py, team_hooks.py, worktree_hooks.py) and 11 reference docs. +107 tests (238 total, up from 131).

5. Audit remediation plan (e122b3c)

docs/features/2026-04-06-claude-code-audit-action-items.md — 874-line planning doc tracking 14 action items across Blockers / High Priority / Experimental tiers for the v2.1.81→v2.1.92 remediation sprint. Docs-only change.

6. Multi-file project memory (5 commits)

  • API (1b4634f): new endpoints on routers/projects.py for list/read/write/delete of .claude/CLAUDE.md siblings. +448-line test suite in test_memory.py.
  • Frontend viewer (4f13259): MemoryViewer splits into MemoryIndex, MemoryFilePanel, MemoryOrphanList, MemoryHoverCard, plus a rewriteMemoryLinks.ts action that intercepts cross-file links and routes them through the viewer without a page load. +8 vitest suites.
  • Stale-fetch fix (d770c15): sequence-gate on MemoryFilePanel fetch resolution to prevent a late response from clobbering a newer selection. +1 test.
  • Dedup refactor (d5a9652): memoryTypeBadge.ts extracted so MemoryIndex, MemoryFilePanel, MemoryHoverCard, MemoryOrphanList share a single source for type badges and the markdown pipeline.
  • Design doc (a3e9b54): docs/features/2026-04-07-multi-file-memory-ui.md architecture spec.

7. Ruff lint fix (d51e389)

Removes unused pytest import from test_conversation_endpoints.py so Python Lint CI goes green.

Verification

Suite Result
api/ pytest 1474 passed, 6 skipped
captain-hook/ pytest 238 passed
api/ ruff check ✅ clean
frontend/ svelte-check ✅ 0 errors (2 pre-existing warnings)
frontend/ eslint ✅ 0 errors (11 pre-existing warnings)
frontend/ vitest 261 passed across 8 suites

Manual test plan

  • Write tool timeline card shows file content with line count and scrolls
  • Click into a live session on /projects/[slug], press back, confirm the card you came from glows with the ring
  • H1 copy button on a Claude response that starts with #
  • TaskUpdate rename shows "Rename to" row when input.subject is present
  • Pagination restore on /sessions lands on the right page and scroll position
  • Multi-file memory viewer: click a hover link, confirm it opens the target file in-place without a page reload
  • MemoryFilePanel rapid-click across files — no stale content flash

Rollback strategy

Each commit is independently revertable. Ordered from lowest-risk (lint fix, [Image #N] pattern match) to highest-risk-of-subjective-disagreement (audit plan, multi-file memory UX). Revert from top down if needed.

🤖 Generated with Claude Code

@the-non-expert
Copy link
Copy Markdown
Collaborator Author

  1. Fixing Project scoped skills path mismatch - By intercepting navigation with onclick when projectEncodedName is present, allows users to drill down into project folders statelessly from the perspective of the router. gracefully falls back to real URL links (/skills?path=...) when on the global dashboard.

Five HIGH-severity issues plus supporting cleanups and tests.

HIGH
- ToolCallDetail: remove 'content' from skipKeys — the Write tool
  content preview was filtered out before the render path could read it,
  so feature #2 of PR #55 silently did not work. Kept 'task_subject' in
  skipKeys since it's read directly from event.metadata.
- SessionCard + projects/[project_slug]: extract getSessionUrlIdentifier
  helper so the last-opened-highlight comparison uses the same slug/uuid
  resolution as SessionCard's href, fixing highlight loss on sessions
  where liveSession.slug differs from session.slug.
- markdownCopyButtons: query h1/h2/h3 (was h2/h3); h1 and h2 always get
  a button, h3 keeps the 150-char gate. Matches the PR description.
  Removed stale h2-vs-h3 visual-distinction comment.
- ToolCallDetail TaskUpdate: restore input.subject to hasChanges and add
  a "Rename to" row inside the Changes panel so a subject-only rename
  is visible as an explicit delta (header still shows inherited subject).
- conversation_endpoints: tighten regex from r"Task #(\w+)" to r"Task #(\d+)"
  and move `import re` to module top. Added 3 regression tests in a new
  test_conversation_endpoints.py covering happy path, no matching TaskCreate,
  and unparseable result content.

MEDIUM
- markdownCopyButtons: delete dead `cleanupFns` array — it was populated
  but never invoked; DOM removal handles listener cleanup via GC.
- jsonl_utils: replace copy.deepcopy(base) with {**base} shallow copy.
  The merge function only mutates the `message` key which is already
  rebuilt as a fresh dict, so deepcopy was wasted allocation for
  base64-image payloads. Drop `import copy`.
- test_jsonl_utils: add TestMessageMerging with 8 tests — 4 direct
  unit tests of _merge_user_message_dicts (image-source drop, real-extra
  preservation, empty extra, legacy content key) and 4 integration tests
  via iter_messages_from_jsonl (same-timestamp merge, different-timestamp
  no-merge, cross-type no-merge, 3-way merge).
- sessions/+page.svelte: make restoreScroll async and await tick() before
  requestAnimationFrame so scroll restore lands on fully-rendered DOM.

LOW
- ToolCallDetail: strip trailing newline before counting lines in the
  Write content preview so "a\nb\n" reads as 2 lines not 3.

Verification
- api: pytest tests/test_jsonl_utils.py tests/test_conversation_endpoints.py
  tests/test_session.py tests/test_agent.py — 149 passed.
- frontend: svelte-check clean on all touched files (pre-existing errors
  only in skills/[skill_name=skill_name]/* from the PR's own route rename).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@JayantDevkar
Copy link
Copy Markdown
Owner

@the-non-expert — pushed code-review fixes in 13cd5bf on this PR branch. Please pull and verify before merging. Summary of what changed:

🔴 HIGH (5 fixes)

  1. ToolCallDetail.svelte — Write tool content panel was broken. 'content' was in the skipKeys array so it was filtered out before the render path at line 320 could read input.content. I removed it from skipKeys. Kept 'task_subject' since that one is read directly from event.metadata by the TaskUpdate header. Please manually test the Write tool modal to confirm the content panel actually renders now.

  2. Last-opened highlight was broken for live sessions. The comparison in projects/[project_slug]/+page.svelte was session.uuid.slice(0, 8) === lastOpenedSessionId || session.slug === lastOpenedSessionId, but SessionCard.svelte navigates via liveSession?.slug ?? session.slug. When liveSession.slug differed from session.slug, the highlight missed. Extracted a helper frontend/src/lib/utils/sessionIdentifier.ts and use it in both SessionCard's navigation and the projects-page comparison so they can't drift. GlobalSessionCard + /sessions stay uuid-only (intentional — the global list uses uuid-only navigation).

  3. markdownCopyButtons.ts — H1 was missing and code-only sections had no button. The PR description promised "every H1 and H2" but the selector only queried h2, h3, and collectSection excluded <pre> from charCount so code-heavy h2 sections failed the 150-char gate. Changed to h1, h2, h3; h1 and h2 now always get a button, h3 keeps the gate. Removed the stale "h2 always / h3 hover-reveal" comment that didn't match any CSS rule.

  4. TaskUpdate rename became invisible. You removed subject from hasChanges, so a subject-only rename TaskUpdate shows the new subject in the header but has no delta indicator. Added subject back to hasChanges and added a "Rename to" row inside the Changes panel (distinct from the inherited taskSubject in the header).

  5. conversation_endpoints.py — regex tightened and tests added. Task #(\w+)Task #(\d+), and import re moved to the module top. Added api/tests/test_conversation_endpoints.py with 3 regression tests covering the happy path, missing TaskCreate, and unparseable result content.

🟡 MEDIUM (4 fixes)

  • markdownCopyButtons.ts — dead cleanupFns array removed. It was populated but never invoked on setup/destroy. DOM removal handles listener cleanup via GC.
  • jsonl_utils.pycopy.deepcopy(base){**base}. The merge function only mutates the message key, which is already rebuilt as a fresh dict. Dropped import copy. Avoids wasted allocation for base64-image payloads.
  • test_jsonl_utils.py — 8 new tests for _merge_user_message_dicts. 4 direct unit tests + 4 integration tests via iter_messages_from_jsonl. Covers image-source drop, real-extra preserve, empty extra, legacy content key, same-timestamp merge, different-timestamp no-merge, cross-type no-merge, 3-way merge.
  • sessions/+page.svelteawait tick() before requestAnimationFrame in restoreScroll. Avoids a race where scroll lands on stale layout after reloadSessions resolves but before Svelte has painted.

🟢 LOW

  • ToolCallDetail.svelte line count now strips trailing newline ("a\nb\n" reads as 2 lines, not 3).

Verification

  • Backend: pytest tests/test_jsonl_utils.py tests/test_conversation_endpoints.py tests/test_session.py tests/test_agent.py149/149 passed.
  • Frontend: svelte-check clean on every file touched. The 5 pre-existing errors in skills/[skill_name=skill_name]/* are from the route rename in your PR and will go away once npm run dev/build regenerates .svelte-kit/types.

Manual test plan — please verify before merging

  • Write tool content panel renders with line count and scrolls (H13 — was broken before this commit)
  • Click into a live session from /projects/[slug], press back, confirm the card you came from glows with the ring (H4)
  • H1 copy button on a Claude response that starts with # (H6)
  • TaskUpdate rename shows "Rename to" row when input.subject is present (H10)
  • Pagination restore on /sessions still works (H3 — added await tick(), behavior should be identical but DOM-safer)

Full diff: https://github.com/JayantDevkar/claude-code-karma/pull/55/files

JayantDevkar and others added 3 commits April 6, 2026 21:46
Post-audit regression fix for the merge logic added in the previous
commit. Claude Code v2.1.83 changed the image-attachment marker from
`[Image: source: /var/folders/...]` to `[Image #N]`, with a trailing
space added in v2.1.85+. The existing `startswith("[Image: source:")`
check missed the new format, so `_merge_user_message_dicts` would leave
the redundant marker text in the merged content for any session created
by a Claude Code version released after 2026-03-25.

Changes
- Extract a `_is_image_marker_text` helper that matches both prefixes.
- Update the docstring to call out the v2.1.83 + v2.1.85 format variants
  so the next schema change is easier to spot.
- Add `test_merge_drops_image_hash_number_marker` covering a 2-image
  attachment with both `[Image #1]` and `[Image #2] ` (trailing space)
  marker variants in the extra message.

Verification: pytest tests/test_jsonl_utils.py → 18 passed.

Cite: Claude Code changelog v2.1.83 (2026-03-25) and v2.1.85 (2026-03-26).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
….1.92

Expand the captain-hook Pydantic library with 11 new hook event types
introduced between Claude Code v2.1.83 and v2.1.92, bringing the total
from 13 to 24 supported hooks.

New hook classes:

  Context:
    - InstructionsLoadedHook  (CLAUDE.md / rules file loaded)

  User interaction:
    - PermissionDeniedHook    (auto mode denied a tool call; can request retry)
    - ElicitationHook         (MCP server requests structured input)
    - ElicitationResultHook   (user response to MCP elicitation)

  Filesystem (new module fs_hooks.py):
    - CwdChangedHook          (working directory changed)
    - FileChangedHook         (external file change detected)

  Agent Teams (new module team_hooks.py, experimental):
    - TaskCreatedHook
    - TaskCompletedHook
    - TeammateIdleHook

  Worktree (new module worktree_hooks.py):
    - WorktreeCreateHook      (HTTP hook can override worktreePath)
    - WorktreeRemoveHook

Output model changes:

  - PreToolUseOutput.permission_decision now accepts "allow", "deny",
    "ask", and "defer" (the new "defer" value supports the headless
    `-p --resume` pause/resume flow).
  - New PermissionDeniedOutput with a single retry: bool field for
    requesting that Claude retry a denied tool call.

Other changes:

  - HookEventName Literal in base.py extended with all 11 new event names.
  - HOOK_TYPE_MAP, HookEvent union, and __all__ in src/captain_hook/__init__.py
    extended with the new classes.
  - Backward-compat shim models.py mirrors the new exports.
  - 107 new tests added (fixtures, parser dispatch, round-trips, output
    models): 131 -> 238 total tests, all passing.
  - README.md and CLAUDE.md updated to reflect the 24-hook count and
    the new module layout.
  - 11 new docs/hooks/*-info-available.md files following the existing
    template.

No existing hook classes or test cases were modified.
14 action items across 3 sprints tracking dashboard updates needed
for Claude Code releases between 2026-03-20 and 2026-04-06.

- Tier 1 (Blockers): JSONL merge regression (shipped), captain-hook expansion (11 new types), Teams discovery, bare mode detection
- Tier 2 (High Priority): PowerShell/Teams tool recognition, frontmatter parsing (initialPrompt, shell, paths), managed-settings fragment merging, MCP metadata preservation, 14 new settings fields
- Tier 3 (Experimental): Extended thinking visibility, hook conditional if support, session resumption hardening

Cite: claude-code-guide audit (2026-04-06)
@JayantDevkar
Copy link
Copy Markdown
Owner

@the-non-expert — two more commits pushed on top of the earlier review fixes. The scope of this PR grew beyond timeline UX because I ran a Claude Code release audit (v2.1.81→v2.1.92) in the same session and chose to keep all follow-up work together per @JayantDevkar's preference rather than splitting into 3 PRs.

Current commit stack on enhance/timeline-updates

e122b3c  docs(features): Claude Code v2.1.81-v2.1.92 audit remediation plan
cf7eb26  feat(captain-hook): add 11 new hook types from Claude Code v2.1.83-v2.1.92
272f506  fix: handle [Image #N] marker in JSONL merge (v2.1.83+ format)
13cd5bf  fix: address code review findings on PR #55
2158feb  fix: add skill_name route matcher...                    ← your original
ca89fac  mapping update task tabs on timeline with actual task texts  ← your original
2e3f508  scroll position restore on navigation and last opened highlight  ← your original
12c28bd  adding modular copy options...                           ← your original

What's new beyond the code review fixes

cf7eb26 — captain-hook expansion (+107 tests)
Eleven new hook types from the Claude Code v2.1.83–v2.1.92 release window that the library didn't model yet:

  • InstructionsLoaded, CwdChanged, FileChanged — filesystem / context lifecycle
  • PermissionDenied, Elicitation, ElicitationResult — user-interaction hooks
  • TaskCreated, TaskCompleted, TeammateIdle — Agent Teams (experimental, gated on CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1)
  • WorktreeCreate, WorktreeRemove — git worktree lifecycle

Plus: \"defer\" added to PreToolUseOutput.permissionDecision Literal (new in v2.1.89 for headless -p --resume), and a new PermissionDeniedOutput class with a retry: bool field. Three new domain-file modules (fs_hooks.py, team_hooks.py, worktree_hooks.py) following the existing naming convention. 11 new reference docs in docs/hooks/. HOOK_TYPE_MAP grows from 13 to 24 entries; HookEvent union and __all__ updated in lockstep.

e122b3c — audit remediation plan (874 lines)
New planning doc at docs/features/2026-04-06-claude-code-audit-action-items.md tracking the full remediation sprint across three tiers (Blockers / High Priority / Experimental) with 14 action items, file change index for 46 affected files, and three verification matrices (53 checkboxes). Follows the same convention as the existing docs/features/ plan docs (Section 1-10, tables everywhere, no frontmatter).

Three sub-sections in the plan are marked {needs clarification} where I'd need official Claude Code schema docs to finalize implementation details — they're flagged for lookup in Sprint 1 execution.

Verification

Suite Count Result
pytest api/ 1444 passed, 6 skipped
pytest captain-hook/ 238 passed (baseline was 131)
Svelte-check on touched files clean

What I'd like you to focus on in review

  • Timeline UX changes (your original work) — already reviewed by me in the earlier comment
  • H13/H4/H10/H6/H1 review-fix commit (13cd5bf) — needs manual test on the Write content panel and the last-opened session highlight
  • [Image #N] regression (272f506) — purely additive pattern match, backed by a new test
  • captain-hook expansion (cf7eb26) — please sanity-check the new hook models against the official Claude Code hook docs. The schemas are my best-effort interpretation of the audit report, and any field that was ambiguous I defaulted to Optional[str] to be permissive. The 3 sub-sections marked {needs clarification} in the audit plan correspond to exactly these ambiguities.
  • audit plan doc (e122b3c) — content review only, it's a planning doc not code

Rollback strategy if anything is wrong

Each commit is independently revertable. The 4 new commits on top of your original 4 are ordered from lowest-risk (review fixes) to highest-risk-of-subjective-disagreement (audit plan). Revert from the top down if needed.

Scratch branches cleaned up

The work landed here via cherry-pick from two scratch branches (feat/captain-hook-v2.1.92-events and worktree-agent-a380b18f). Both are being deleted locally now that they're merged here. No remote scratch branches were pushed.

JayantDevkar and others added 6 commits April 7, 2026 09:37
Design for reworking MemoryViewer to handle the new auto-memory layout
(MEMORY.md index + topical child files with YAML frontmatter). Reader-
first UX: hover previews (Wikipedia-style) for link metadata, side panel
drawer for full-content reading, collapsible orphan section for files
not referenced by the index. Covers API changes (2 endpoints), component
split, backwards compatibility, and test strategy.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the single-file /projects/{name}/memory response with a
{index, files} shape that enumerates every *.md in the project's
memory/ directory. Add /projects/{name}/memory/files/{filename}
for fetching individual child file content on demand.

- New schemas: MemoryIndexEntry, MemoryFileMeta, ProjectMemoryFileResponse
- Hand-rolled YAML frontmatter parser (no new dependency) with graceful
  fallback on malformed blocks
- Markdown link extraction computes linked_from_index for each child
- Strict path validation on the per-file endpoint: regex allowlist,
  reject path separators / .. / leading dot / null byte, plus a
  resolve() + is_relative_to() containment check for symlink escapes
- Backwards compatible: projects with only MEMORY.md return files=[]
- 30 new tests covering all spec error cases and 12 path-traversal variants

Design: docs/features/2026-04-07-multi-file-memory-ui.md

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rework the project memory UI to handle the new auto-memory layout
(MEMORY.md index plus topical child files with YAML frontmatter).

Reader-first UX: the index narrative is the page. Links within the
index are rewritten to in-app interactive elements — hover shows a
Wikipedia-style popover with the target file's metadata (type, word
count, modified time), click opens the file in a right-side drawer.
Orphan files (present on disk but not referenced from MEMORY.md)
surface in a collapsible section at the bottom.

- MemoryViewer.svelte: refactored into a shell that owns state,
  fetches the new {index, files} response, and debounces hover 150ms
- MemoryIndex.svelte: renders MEMORY.md through the existing marked +
  DOMPurify pipeline, applies the rewriteMemoryLinks action
- MemoryHoverCard.svelte: floating popover with manual viewport flip
- MemoryFilePanel.svelte: bits-ui Dialog styled as a right-side sheet,
  fetches /memory/files/{filename} on open, handles 400/403/404/network
  errors with a Retry button, swaps content when filename changes
- MemoryOrphanList.svelte: collapsible "Other memory files" section
- rewriteMemoryLinks.ts: Svelte 5 action that post-processes the
  rendered DOM, matches a[href$=".md"] anchors against known files,
  attaches hover/click listeners, and marks broken links visually
- api-types.ts: replaced ProjectMemory with the new {index, files}
  shape, added MemoryFileMeta, ProjectMemoryFile, MemoryFileType
- vite.config.ts: added svelteTesting() plugin (VITEST-gated, no-op
  in production) to enable Svelte 5 component tests
- 37 new tests across 4 files covering loading/error/orphan states,
  DOM rewriting lifecycle, hover card rendering, and panel refetch

Integration point at +page.svelte:1725 unchanged.

Design: docs/features/2026-04-07-multi-file-memory-ui.md

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
MemoryFilePanel.fetchFile() mutated shared state (fileData, bodyFading,
loading, error) without tracking which invocation was current. This
created three reachable bugs with a single root cause — no fetch
lifecycle management:

1. Out-of-order resolution: click A → click B → A's fetch resolves
   after B's → fileData ends up as A (wrong file shown permanently).
2. bodyFading race: A's 80ms setTimeout could fire while B was still
   loading, briefly showing A without the fade-out covering it.
3. Close-during-fetch leak: closing the panel mid-fetch cleared
   state, but the in-flight fetch still wrote fileData on resolve,
   leaking stale content into the next open.

Fix: add a generation counter (fetchGen). Each fetchFile captures
++fetchGen at entry and checks `myGen !== fetchGen` after every await
and in the finally block. Superseded invocations drop their state
writes silently. Panel close also bumps fetchGen to invalidate any
in-flight fetch.

Chose a gen counter over AbortController for simplicity — the
bandwidth saved by actual cancellation is negligible for a localhost
API, and the code stays in one mental model (no try/catch on
AbortError, no extra imports).

Adds a regression test that resolves an older fetch AFTER a newer one
and asserts the newer file's content remains visible. Test fails
against the pre-fix code at the exact expected assertion.

Verification: 261 frontend tests pass (up from 260), svelte-check
clean, production build succeeds.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… components

Addresses two HIGH quality findings from the code review of the
multi-file memory UI feature. No behavioral change — verified by the
existing 261-test suite including the race regression test.

1. Reuse existing renderMarkdownEffect helper

   MemoryIndex.svelte and MemoryFilePanel.svelte both reimplemented
   the marked + DOMPurify pipeline inline (the same ~12-line block
   each), when utils.ts already exports renderMarkdownEffect — a
   helper used by 4 other routes (agents, about, commands, skills).
   Two copies of a security-critical sanitization pipeline drift
   over time; centralize on the existing helper.

2. Extract shared type badge module

   TYPE_BADGE_CLASSES, TYPE_LABELS, badgeClass, and badgeLabel were
   duplicated byte-for-byte across MemoryHoverCard, MemoryFilePanel,
   and MemoryOrphanList (~25 lines each × 3 = ~75 lines). Extract
   to a single memoryTypeBadge.ts module.

   MemoryOrphanList's badge needed `shrink-0` because it lives
   inside a flex row — accept optional extra classes via a second
   parameter to badgeClass() rather than forcing per-call string
   concatenation at every site.

Net: -84 lines (+18 / -102) across 4 edited files + 1 new module.

Verification: 261 tests pass, svelte-check clean (0 errors), build
succeeds. The fetch-race regression test from the previous commit
continues to pass, confirming the refactor did not reintroduce any
of the bugs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fixes ruff F401 CI failure on PR #55. `pytest` was imported but never
referenced — the tests use plain assert statements without fixtures.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@JayantDevkar JayantDevkar changed the title Enhance/timeline updates Timeline UX + multi-file memory viewer + captain-hook v2.1.92 expansion Apr 12, 2026
…utils tests

Fixes Python Lint CI failure on PR #55. `ruff format --check` flagged two
test files with whitespace drift; applying the formatter is idempotent
and matches the repo's existing ruff 0.15.9 config.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Owner

@JayantDevkar JayantDevkar left a comment

Choose a reason for hiding this comment

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

Self-approving as repo owner. Local verification: 1474 api + 238 captain-hook + 261 vitest + svelte-check + ruff all green. CI green on dbf5d8f after the formatter fix.

Copy link
Copy Markdown
Owner

@JayantDevkar JayantDevkar left a comment

Choose a reason for hiding this comment

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

Self-approving as repo owner.

@JayantDevkar JayantDevkar merged commit 38321f5 into main Apr 12, 2026
20 checks passed
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