Skip to content

feat: v0.8.0 — code split, background sync, resume, env vars, search prefixes, fuzz CI#39

Merged
zpenka merged 22 commits into
mainfrom
feat/v0.8
May 9, 2026
Merged

feat: v0.8.0 — code split, background sync, resume, env vars, search prefixes, fuzz CI#39
zpenka merged 22 commits into
mainfrom
feat/v0.8

Conversation

@zpenka
Copy link
Copy Markdown
Owner

@zpenka zpenka commented May 9, 2026

Summary

  • Code split (T1): model.go and render.go split into per-mode files (keys_*.go, render_*.go); nav() helper DRYs all cursor movement; ensureIndex() extracted as a method. Red: 3c46c70 / 59d47b0, Green: 27a91f7 / c676458
  • nav() helper (T7): 24-case table test drives the helper; all cursor handlers (including stats d/u) use it. Red: 649c862, Green: 771d70a
  • ensureIndex() (part of T1/T2): idempotent FTS5 open; skips if already open or indexing. Red: a769d96, Green: be5e8fb
  • LORE_CACHE_DIR env var (T4): resolveCacheDir() in lore.go; index.go and bookmark.go both use it. Red: 3da7c34, Green: 627de11
  • Resume R key (T3): resumeClaude(id, cwd) via tea.ExecProcess; R in list and detail modes; help overlay and footers updated. Red: 07684bf, Green: 82da093
  • LORE_PRICING_FILE env override (T6): go:embed pricing.json; sync.Once loader with env override path. Red: c72219f, Green: 842912d
  • Background FTS5 sync (T2): Init() returns tea.Batch(loadSessionsCmd, syncIndexCmd); list header shows indexing… while sync runs. Red: 27142b1, Green: b2b4030
  • Search prefix syntax (T5): parseSearchQuery() splits project:<name> / branch:<name> from free text; FTS5 results post-filtered; linear-scan pre-filtered. Red: 67121d7, Green: 61d4230
  • Fuzz targets + CI (T8): FuzzParseSessionMetadata (5 seeds) and FuzzParseTurnsFromJSONL (6 seeds); non-blocking fuzz CI job runs each 30s. Red+Green: 3a60459
  • Regression nets (T9): internal_split_test.go, internal_render_split_test.go, nav_test.go guard against dispatch regressions.
  • Docs + version bump (T10): CLAUDE.md phasing table, README nav/config sections, Version = "0.8.0". Commit: 5b3c86b

Test plan

  • go test -race -cover ./... passes with ≥80% coverage per package (currently 86.2%)
  • go vet ./... clean
  • gofmt -l . clean
  • CI test job passes (required)
  • CI fuzz job runs (non-blocking, continue-on-error: true)
  • R key in list mode resumes the selected session via claude --resume <id>
  • R key in detail mode resumes the current session
  • List header shows indexing… briefly at startup while FTS5 sync runs
  • Search query project:lore refresh token returns only lore-project hits
  • LORE_CACHE_DIR=/tmp/test-lore ./lore writes index and bookmarks to that dir
  • LORE_PRICING_FILE=custom.json ./loreS panel uses custom rates

🤖 Generated with Claude Code

Zack Penka and others added 22 commits May 8, 2026 19:07
Adds TestModelDispatch_AllModes which exercises every mode's key
dispatch path through model.Update. As a refactor these tests pass
now and act as a safety net — any breakage during the handler split
will surface here.
Moves all seven handle*Key functions out of model.go into dedicated
files (keys_list.go, keys_detail.go, keys_search.go, keys_project.go,
keys_rerun.go, keys_stats.go, keys_timeline.go). model.go shrinks from
962 to 354 lines. No behavior changes; all tests pass at 85.9% coverage.
Adds TestRenderDispatch_AllModes which calls View() for every mode and
checks for non-empty, non-panicking output. Acts as a safety net for
the upcoming Task 2 render.go split.
…ate files

Moves all per-mode render*View/Header/Footer/*BodyLines triples out of
render.go into dedicated files (render_list.go, render_detail.go,
render_search.go, render_rerun.go, render_stats.go, render_timeline.go).
render.go shrinks from 1029 to 308 lines. project.go already owned
project-mode rendering. No behavior changes; all tests pass at 86%.
Tests nav() for all standard list keys (j/k/d/u/g/G/down/up) covering
empty lists, single items, boundary clamping, half-page overshoot, and
unknown keys as no-ops. Fails because nav is not yet defined.
…ocks

Implements nav(key, cursor, count, halfPage) in nav.go and collapses
the duplicated six-case navigation switch in keys_list, keys_detail,
keys_search, keys_project, and keys_stats to a single nav() call each.
Timeline handler left unchanged (different shape). Coverage 88.2%.
Tests that ensureIndex() is a no-op when m.index is already set, and
that it does not panic when projectsDir is empty. Fails because
ensureIndex is not yet defined.
Adds ensureIndex() to model which opens the FTS5 index on first use
(no-op when already set or projectsDir empty). handleSearchEntryKey
collapses to a single m.ensureIndex() call. Sets up Task 8 background
sync. Coverage 88.1%.
Tests LORE_CACHE_DIR env var set/unset/empty, and that the resolved
directory is created on first call. Fails because resolveCacheDir is
not yet defined.
Adds resolveCacheDir() in lore.go (LORE_CACHE_DIR > os.UserCacheDir()/lore)
mirroring resolveProjectsDir. index.go and bookmark.go now call
resolveCacheDir() instead of os.UserCacheDir() directly. Directory is
created on first use. Documented in CLAUDE.md and README.
…ail modes

Tests that pressing R in list mode and detail mode calls resumeFn with
the correct session ID and CWD. Fails because resumeFn field and R
binding do not exist yet.
Adds resumeClaude(sessionID, cwd) in rerun.go using claude --resume.
New resumeFn hook on model (injectable; defaulted to resumeClaude).
R bound in list and detail modes; help overlay and footers updated.
Reuses rerunDoneMsg for the return-to-list flow. Coverage 87.7%.
… rates

Tests that setting LORE_PRICING_FILE to a temp JSON file makes
estimateCost use the custom per-token rates. Fails because pricing
file loading and resetPricingOnce are not yet implemented.
Replaces the hardcoded pricingTable slice with an embedded pricing.json
loaded via go:embed. Honors LORE_PRICING_FILE env var for custom rates.
sync.Once guards the table so it is parsed once per process. Existing
cost calculations unchanged; new tests verify the override path.
Tests that Init() returns a non-nil batched cmd, that indexReadyMsg
populates m.index and clears m.indexing, that errors clear the flag
without setting the index, and that the list header shows 'indexing'
only while the flag is set. Fails because indexReadyMsg and indexing
do not exist.
Init() now returns tea.Batch(loadSessionsCmd, syncIndexCmd). New
indexReadyMsg{idx, err} populates m.index and clears m.indexing when
the background sync finishes. List header shows 'indexing...' while
in flight. ensureIndex() is a no-op while indexing=true. Coverage 86.7%.
Table tests for parseSearchQuery covering no prefix, project:, branch:,
both prefixes, prefix at end, and only prefixes. Also tests that
searchSessionsFiltered correctly restricts results to matching sessions.
Fails because parseSearchQuery and searchSessionsFiltered are undefined.
Adds parseSearchQuery() that splits free text from project:/branch:
prefixes. searchSessionsFiltered() runs linear-scan then post-filters
by project/branch. FTS5 index hits are post-filtered the same way.
Help overlay updated with syntax hint. Existing prefix-free queries
are unchanged. Coverage 86.0%.
…FromJSONL

Adds FuzzParseSessionMetadata in session_test.go and FuzzParseTurnsFromJSONL
in detail_test.go, each seeded with valid and malformed JSONL inputs.
5-second local runs found no panics. CI fuzz job added to ci.yml as a
non-blocking job (30s per target); promote to required after a week of
green runs.
Adds v0.8 rows to the CLAUDE.md phasing table covering all 10
tasks (code split, background FTS5 sync, resume R key, LORE_CACHE_DIR,
search prefixes, LORE_PRICING_FILE, nav() helper, fuzz CI, regression
nets, and this docs commit). Bumps Version to "0.8.0".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Deletes ephemeral planning files that should not live in the repo.
Updates DESIGN.md status banner to v0.8.0 and adds v0.8 rows to the
phasing table; notes LORE_CACHE_DIR in the Phase 5a section.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…test -fuzz

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@zpenka zpenka merged commit a9f7fa0 into main May 9, 2026
2 checks passed
@zpenka zpenka deleted the feat/v0.8 branch May 9, 2026 01:35
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.

1 participant