docs: update docs, add backlog tasks and change notes

Update configuration, immersion tracking, and mining workflow docs.
Add backlog tasks for upcoming work items and change notes for recent
features and fixes.
This commit is contained in:
2026-03-17 19:54:55 -07:00
parent 5698121996
commit f2b3af17d7
24 changed files with 950 additions and 6 deletions

View File

@@ -0,0 +1,72 @@
---
id: TASK-177
title: Track intentional Yomitan lookups in stats
status: Done
assignee:
- codex
created_date: '2026-03-17 09:15'
updated_date: '2026-03-17 09:41'
labels:
- stats
- immersion-tracking
- yomitan
dependencies: []
references:
- vendor/subminer-yomitan/ext/js/app/frontend.js
- src/core/services/immersion-tracker-service.ts
- src/core/services/immersion-tracker/query.ts
- src/core/services/ipc.ts
- src/preload.ts
- stats/src/components/sessions/SessionDetail.tsx
- stats/src/components/library/MediaHeader.tsx
- stats/src/components/anime/AnimeDetailView.tsx
documentation:
- docs/plans/2026-03-17-yomitan-lookup-stats-design.md
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Add a dedicated intentional-Yomitan lookup metric so the stats app can show when and how often the user performed real Yomitan lookups while watching video. Keep existing annotation/known-word lookup counters unchanged. Surface the new metric in session detail, episode/media detail, and anime detail, including lookup rate based on words seen.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Successful Yomitan searches while watching create a dedicated Yomitan lookup event and aggregate counter without changing existing lookupCount or lookupHits behavior
- [x] #2 Session detail shows Yomitan lookup timeline markers plus lookup count and lookup rate using words seen
- [x] #3 Episode/media detail shows aggregated Yomitan lookup count and lookup rate using episode totals
- [x] #4 Anime detail shows aggregated Yomitan lookup count and lookup rate using anime totals
- [x] #5 Automated tests cover the new lookup event path, aggregate queries, and affected stats UI surfaces
- [x] #6 Internal docs/plans reflect the approved design and implementation approach
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Add a SubMiner-specific Yomitan lookup signal emitted from vendored Yomitan on searchSuccess and bridge it through renderer, preload, and main IPC to a tracker hook.
2. Extend immersion tracking with a dedicated Yomitan lookup event type and yomitanLookupCount aggregate, preserving existing lookupCount and lookupHits semantics.
3. Update session, media, anime, and anime-episode queries plus shared stats types to expose the new aggregate count.
4. Update stats UI to show Yomitan lookup markers in session detail and lookup count/rate at session, episode/media, and anime levels using lookups per 100 words copy.
5. Verify with focused unit tests first, then repo typecheck/test/build lanes, and finalize TASK-177 with implementation notes and acceptance-criteria checks.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Approved design recorded in docs/plans/2026-03-17-yomitan-lookup-stats-design.md.
Observed pre-existing local changes in tracker/query/session stats files; implementation plan must preserve those edits while layering Yomitan lookup tracking on top.
Implemented a dedicated Yomitan lookup signal on vendored searchSuccess, bridged it through renderer/preload/main IPC, and persisted YOMITAN_LOOKUP events plus yomitanLookupCount without changing existing annotation lookup counters.
Extended stats queries/types for session, media, anime, and episode aggregates; updated session detail, media header, episode list, and anime overview to show Yomitan lookup counts and lookup rate copy as lookups per 100 words.
Focused verification passed for IPC, tracker service/query, and stats UI tests. stats typecheck still has pre-existing unrelated failures in stats/src/components/anime/AnilistSelector.tsx and stats/src/lib/reading-utils.ts.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Added intentional Yomitan lookup tracking end-to-end: vendored Yomitan searchSuccess now emits a SubMiner event, the app records dedicated YOMITAN_LOOKUP events and yomitanLookupCount aggregates, and the stats UI surfaces lookup counts/rates for sessions, episodes/media, and anime. Focused regression tests pass for the IPC bridge, tracker persistence/querying, and new stats UI helpers/components. Full `bun run typecheck:stats` remains blocked by unrelated existing errors in `stats/src/components/anime/AnilistSelector.tsx` and `stats/src/lib/reading-utils.ts`.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,67 @@
---
id: TASK-178
title: 'Address PR #19 Codex review feedback on immersion session deletion'
status: Done
assignee:
- codex
created_date: '2026-03-17 14:59'
updated_date: '2026-03-17 15:13'
labels:
- pr-review
- immersion-tracker
- stats
dependencies: []
references:
- >-
/Users/sudacode/projects/japanese/SubMiner/src/core/services/immersion-tracker-service.ts
- >-
/Users/sudacode/projects/japanese/SubMiner/src/core/services/immersion-tracker/query.ts
- >-
/Users/sudacode/projects/japanese/SubMiner/src/core/services/immersion-tracker-service.test.ts
- >-
/Users/sudacode/projects/japanese/SubMiner/src/core/services/immersion-tracker/__tests__/query.test.ts
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Assess the open Codex review items on PR #19 and fix verified deletion-path regressions in immersion tracking so dashboard deletes cannot corrupt tracker state or leave stale aggregate stats.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Deleting the active immersion session is rejected safely and does not leave the tracker in a flush-failure loop
- [x] #2 Deleting sessions rebuilds or updates vocabulary and kanji aggregates so stats no longer include removed session data
- [x] #3 Regression tests cover the active-session deletion guard and aggregate cleanup after session deletion
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Add failing regression tests for deleting the active session through ImmersionTrackerService and for deleteSession/deleteSessions keeping imm_words and imm_kanji aggregates in sync after rows are removed.
2. Verify the failures are caused by the current deletion path, then patch the service guard and query-layer aggregate maintenance with the smallest safe change.
3. Re-run focused tests for the touched files, then run SubMiner verification lanes appropriate for core/runtime-compat changes and record results.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Verified Codex PR #19 findings against current code: active-session/video deletes could orphan the live tracker session, and deleteSession/deleteSessions/deleteVideo left imm_words/imm_kanji aggregates stale after subtitle-line removal.
Implemented service guards that ignore deletes targeting the active session or active video and log a warning instead of deleting live tracker rows.
Updated query-layer delete helpers to capture affected word/kanji ids before deletion, remove session/video rows in a transaction, then recompute surviving imm_words/imm_kanji frequency and first/last-seen values from remaining subtitle-line occurrences, deleting orphan aggregate rows when no occurrences remain.
Focused verification passed: bun test src/core/services/immersion-tracker-service.test.ts and bun test src/core/services/immersion-tracker/__tests__/query.test.ts.
SubMiner verifier: classify_subminer_diff.sh selected lane core; verify_subminer_change.sh passed typecheck and failed on unrelated existing launcher test `stats command tolerates slower dashboard startup before timing out` in launcher/main.test.ts (timeout waiting for dashboard startup response).
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Assessed the open Codex PR #19 review items on immersion deletion paths and fixed the confirmed regressions. ImmersionTrackerService now ignores delete requests that target the currently active session or its active video, preventing the dashboard from deleting the live parent rows that subsequent telemetry/event flushes still depend on. On the query side, session/video deletion now captures affected vocabulary and kanji aggregate ids before removing subtitle/session rows, then recomputes imm_words and imm_kanji frequency plus first/last seen timestamps from surviving line occurrences inside the same transaction, deleting orphan aggregate rows when no occurrences remain.
Regression coverage was added for active-session delete protection, active-video delete protection, and aggregate rebuild after session deletion. Focused verification passed with `bun test src/core/services/immersion-tracker-service.test.ts` and `bun test src/core/services/immersion-tracker/__tests__/query.test.ts`. Repo-native verification selected the `core` lane; `bun run typecheck` passed, while `bun run test:fast` failed in an unrelated launcher test (`launcher/main.test.ts`: `stats command tolerates slower dashboard startup before timing out`) that times out waiting for dashboard startup response.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,57 @@
---
id: TASK-179
title: Tune immersion tracker SQLite pragmas and maintenance defaults
status: Done
assignee:
- codex
created_date: '2026-03-17 15:15'
updated_date: '2026-03-17 15:19'
labels:
- sqlite
- immersion-tracking
- performance
dependencies: []
documentation:
- >-
/Users/sudacode/projects/japanese/SubMiner/docs/plans/2026-03-17-sqlite-tuning.md
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Apply low-risk SQLite tuning improvements for the immersion tracker: add modern recommended maintenance/tuning pragmas where appropriate, cover them with regression tests, and update user-facing docs to reflect the actual tuning policy. Scope limited to low-risk local-DB changes already discussed: keep WAL + synchronous=NORMAL, add optimize path, consider WAL growth control, and document workload-dependent knobs left at defaults.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Immersion tracker applies the agreed low-risk SQLite tuning changes without regressing current behavior
- [x] #2 Regression tests cover the new pragma/maintenance behavior
- [x] #3 Immersion tracking docs describe the tuning policy and notable defaults left unchanged
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Add regression coverage for connection pragmas and verify the new WAL growth cap fails before implementation.
2. Add regression coverage for maintenance-time PRAGMA optimize and verify the test fails before implementation.
3. Implement the minimal SQLite tuning changes.
4. Update immersion-tracking docs for the new tuning policy.
5. Run targeted SQLite verification lanes and record results.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Verification: `bun test src/core/services/immersion-tracker/storage-session.test.ts src/core/services/immersion-tracker/maintenance.test.ts` passed (15 tests).
Verification: `bun run test:immersion:sqlite:src` passed (37 tests).
Verification: `bun run typecheck`, `bun run docs:test`, `bun run docs:build`, `bun run test:fast`, `bun run test:env`, and `bun run build` all passed.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Added low-risk SQLite tuning improvements for the immersion tracker: `journal_size_limit` now bounds WAL growth, periodic maintenance runs `PRAGMA optimize`, regression tests cover both behaviors, and the immersion-tracking docs explain the maintained pragmas plus workload-dependent defaults left unchanged.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,60 @@
---
id: TASK-180
title: Fix launcher stats command timeout for slow dashboard startup
status: Done
assignee:
- codex
created_date: '2026-03-17 15:16'
updated_date: '2026-03-17 15:18'
labels:
- launcher
- stats
- tests
dependencies: []
references:
- >-
/Users/sudacode/projects/japanese/SubMiner/launcher/commands/stats-command.ts
- /Users/sudacode/projects/japanese/SubMiner/launcher/main.test.ts
- >-
/Users/sudacode/projects/japanese/SubMiner/launcher/commands/command-modules.test.ts
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Address the failing launcher stats startup path so the CLI tolerates the intended slow dashboard startup window instead of timing out early.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 The launcher stats command no longer times out before the intended slow-start window used by tests
- [x] #2 Regression coverage verifies the slower stats startup path succeeds
- [x] #3 The failing launcher stats startup test passes locally
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Add a focused launcher command regression that simulates a stats response arriving after the current timeout boundary and expects success.
2. Adjust the stats startup wait timeout in launcher/commands/stats-command.ts to match the intended slow-start tolerance.
3. Re-run the targeted command test, the previously failing launcher/main.test.ts case, and then the full launcher/main.test.ts file.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Verified the failing launcher path: launcher/main.test.ts timed out because launcher/commands/stats-command.ts only waited 8000ms for the stats startup response while the supported slow-start test writes the response after 9s.
Raised the stats startup response timeout to 12000ms so attached stats startup tolerates the existing slow cold-start window without changing command flow.
Verification passed: bun test launcher/commands/command-modules.test.ts --test-name-pattern "stats command launches attached app command with response path|stats command returns after startup response even if app process stays running|stats command throws when stats response reports an error"; bun test launcher/main.test.ts --test-name-pattern "stats command tolerates slower dashboard startup before timing out"; bun test launcher/main.test.ts; bun run test:fast.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Fixed the launcher stats startup timeout by extending the response-file wait window in launcher/commands/stats-command.ts from 8s to 12s. The command flow was left unchanged; the launcher now simply gives the stats dashboard enough time to report readiness during slower cold starts, which matches the existing supported behavior exercised by launcher/main.test.ts.
Verification passed with the targeted launcher command tests, the previously failing slow-start launcher/main.test.ts case, the full launcher/main.test.ts file, and the full `bun run test:fast` gate.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,59 @@
---
id: TASK-181
title: Add background-managed stats server lifecycle commands
status: Done
assignee:
- codex
created_date: '2026-03-17 15:31'
updated_date: '2026-03-17 15:55'
labels:
- cli
- launcher
- stats
dependencies: []
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Add a dedicated background stats server mode that can be started and stopped from the launcher without blocking normal SubMiner instances. Launcher UX: `subminer stats -b` starts the stats server in the background, `subminer stats -s` stops the background stats server only, and plain `subminer stats` preserves the existing foreground/open-browser flow.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 `subminer stats -b` starts a background stats server without blocking other SubMiner instances.
- [x] #2 `subminer stats -s` stops only the background stats server and succeeds cleanly when state is stale.
- [x] #3 Plain `subminer stats` preserves current dashboard-open behavior.
- [x] #4 Automated tests cover launcher parsing/dispatch and app-side start-stop lifecycle behavior.
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Extend launcher stats parsing so `subminer stats -b` maps to background-start and `subminer stats -s` maps to stop-only while preserving existing cleanup/rebuild parsing.
2. Add launcher execution branches: detached background start with startup acknowledgement wait, stop command forwarding with response wait, and preserve existing attached foreground behavior for plain `stats` and cleanup flows.
3. Extend app CLI args and stats command handler for background start/stop lifecycle responses, including already-running and stale-state handling.
4. Add a dedicated stats-daemon runtime/state-file path in the app and bypass the normal single-instance lock only for that mode.
5. Verify with focused tests first, then launcher/env lane, and update task acceptance criteria/final summary before handoff.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
User approved option 2 design: dedicated app-side stats daemon, `subminer stats -b` to start, `subminer stats -s` to stop server only.
Implemented launcher `stats -b` and `stats -s` flows plus app-side `--stats-background` / `--stats-stop` handling.
Added background stats daemon state-file management and remote-daemon reuse so normal SubMiner instances do not try to bind a second stats server when the daemon is already running.
Verification: `bun test launcher/main.test.ts launcher/commands/command-modules.test.ts launcher/parse-args.test.ts src/main/runtime/stats-cli-command.test.ts src/main/early-single-instance.test.ts`, `bun run typecheck`, `bun run test:env`, `bun run test:fast`, `bun run build`, `bun run test:smoke:dist`, `bun run docs:test`, `bun run docs:build`, `bun run changelog:lint`.
Non-blocking note: `bun run test:launcher` still showed unrelated existing failures in `launcher/picker.test.ts` and an intermittent `launcher/smoke.e2e.test.ts` mpv-status check on this machine; the narrowed launcher suites covering the changed stats paths passed.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Added a dedicated background stats-daemon lifecycle for the launcher and app runtime. `subminer stats -b` now starts or reuses a detached stats server and returns after startup acknowledgement, while `subminer stats -s` stops that daemon without touching browser tabs. On the app side, new stats background/stop CLI flags bypass the normal single-instance lock only for daemon helper processes, write/read a daemon state file under user data, and reuse an already-running daemon instead of attempting a second local stats bind when another SubMiner instance needs stats access. Updated docs-site stats docs, added a changelog fragment, and covered the new flows with launcher parse/dispatch tests, app stats CLI handler tests, and single-instance bypass tests. Verification run: `bun run typecheck`, `bun run test:env`, `bun run test:fast`, `bun run build`, `bun run test:smoke:dist`, `bun run docs:test`, `bun run docs:build`, `bun run changelog:lint`, plus narrowed changed-path launcher/app test bundles.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,58 @@
---
id: TASK-182
title: Fix session stats chart known-word totals exceeding total words
status: Done
assignee:
- codex
created_date: '2026-03-17 16:07'
updated_date: '2026-03-17 16:19'
labels: []
dependencies: []
references:
- /Users/sudacode/projects/japanese/SubMiner/src/core/services/stats-server.ts
- >-
/Users/sudacode/projects/japanese/SubMiner/stats/src/components/sessions/SessionDetail.tsx
- >-
/Users/sudacode/projects/japanese/SubMiner/src/core/services/__tests__/stats-server.test.ts
- /Users/sudacode/projects/japanese/SubMiner/stats/src/hooks/useSessions.ts
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Fix the session detail stats display so the known-word series cannot exceed the total-word series for the same sample. Ground the fix in the actual immersion-tracker metrics used by the stats UI and cover the regression with automated tests.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Session detail data uses a consistent cumulative word metric so known-word counts do not exceed total words for a sample
- [x] #2 Automated tests cover the session known-word timeline contract and reproduce the regression scenario
- [x] #3 Session stats UI still renders the timeline and tooltip values correctly after the fix
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Add a regression test for `/api/stats/sessions/:id/known-words-timeline` covering a Japanese-style session where telemetry word counts can be lower than token-derived known-word counts.
2. Update the stats known-word timeline contract/server implementation so the series is expressed in the same cumulative unit used for total words in the session detail view.
3. Adjust the session detail UI/types to consume the corrected series and keep tooltip/legend copy coherent.
4. Run targeted tests for stats server and stats UI transforms, then summarize any wider verification skipped.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Implemented server-side known-word timeline fix to preserve stored line positions and accumulate known-word occurrences rather than compressed unique-headword counts.
Updated session-facing stats views to prefer `tokensSeen` over `wordsSeen` when available so displayed session word totals align with the session chart and lookup-rate denominator.
Verification: `bun test src/core/services/__tests__/stats-server.test.ts`, `bun test stats/src/lib/yomitan-lookup.test.tsx`, `bun test src/core/services/immersion-tracker/__tests__/query.test.ts`, `bun run typecheck` all passed.
Verification skipped/blocker: `bun run typecheck:stats` still fails in pre-existing unrelated files `stats/src/components/anime/AnilistSelector.tsx`, `stats/src/lib/reading-utils.test.ts`, and `stats/src/lib/reading-utils.ts`.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Fixed the session stats mismatch that let known words outrun total words. The stats server now preserves actual subtitle-line positions and accumulates known-word occurrences for the session timeline, while session-facing stats views prefer token-based word totals when available. Added regression coverage for the known-word timeline API and for session-row word-count rendering, plus a user-visible changelog fragment.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,62 @@
---
id: TASK-182.1
title: Remove misleading session new-word metric from session detail chart
status: Done
assignee:
- '@codex'
created_date: '2026-03-18 01:41'
updated_date: '2026-03-18 01:45'
labels:
- bug
- stats
- ui
dependencies: []
references:
- >-
/Users/sudacode/projects/japanese/SubMiner/stats/src/components/sessions/SessionDetail.tsx
- >-
/Users/sudacode/projects/japanese/SubMiner/stats/src/components/sessions/SessionsTab.tsx
- >-
/Users/sudacode/projects/japanese/SubMiner/stats/src/lib/media-session-list.test.tsx
parent_task_id: TASK-182
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Remove the misleading `New words` series from the session detail chart so the stats UI no longer presents a fabricated metric that mirrors total words. Keep the session chart focused on the real cumulative totals already backed by tracker data.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Expanded session detail chart no longer renders or labels a `New words` metric in the graph, tooltip, or legend
- [x] #2 Session detail still renders total-word and known-word series correctly after the metric removal
- [x] #3 Automated frontend coverage prevents the `New words` label from reappearing in expanded session detail
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Add a focused stats frontend regression test that renders expanded session detail and asserts the misleading `New words` label is absent while `Total words` remains.
2. Remove the fabricated `New words` area series, tooltip mapping, legend chip, and now-unused left-axis chart plumbing from `stats/src/components/sessions/SessionDetail.tsx`.
3. Add a user-visible changelog fragment describing the session chart cleanup.
4. Run targeted frontend tests plus cheap verification and record any blockers.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Added a focused server-render regression test for SessionDetail copy to ensure the misleading `New words` label stays removed.
Removed the fabricated `New words` chart series and its legend/tooltip plumbing from the expanded session detail view.
Verification: `bun test stats/src/lib/session-detail.test.tsx stats/src/lib/media-session-list.test.tsx` passed. `bash .agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh --lane core stats/src/components/sessions/SessionDetail.tsx stats/src/lib/session-detail.test.tsx changes/2026-03-18-remove-session-new-words-series.md` passed and wrote artifacts under `.tmp/skill-verification/subminer-verify-20260317-184440-1aMWkM`.
Manual spot-check note: `bun test stats/src/lib/yomitan-lookup.test.tsx` is currently red on a pre-existing `AnimeOverviewStats` lookup-rate assertion unrelated to this session-detail change.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Removed the misleading `New words` metric from expanded session charts. Session detail now shows only the real total-word and known-word lines, backed by existing tracker data, with regression coverage that prevents the `New words` label from reappearing.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,56 @@
---
id: TASK-183
title: Fix blank stats vocabulary page regression
status: Done
assignee:
- codex
created_date: '2026-03-17 16:23'
updated_date: '2026-03-17 16:29'
labels: []
dependencies: []
references:
- >-
/Users/sudacode/projects/japanese/SubMiner/stats/src/components/vocabulary/VocabularyTab.tsx
- /Users/sudacode/projects/japanese/SubMiner/stats/src/App.tsx
- /Users/sudacode/projects/japanese/SubMiner/stats/src/lib/api-client.ts
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Diagnose and fix the stats dashboard regression where the Vocabulary tab renders blank at runtime. Capture the frontend failure with browser debugging, add regression coverage, and restore the vocabulary view.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Vocabulary tab renders without a blank-screen failure in the stats dashboard
- [x] #2 Automated test coverage reproduces the failing code path and passes with the fix
- [x] #3 Targeted verification covers the affected stats UI/runtime path
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Reproduce the blank Vocabulary tab locally with a browser-visible stats UI instance and capture console/network failure details.
2. Add a focused regression test for the failing Vocabulary tab code path before editing production code.
3. Implement the minimal fix in the stats UI/runtime path.
4. Re-run targeted browser and automated verification, then record any skipped broader checks.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Identified the runtime failure in the browser console: React reported a hook-order change in `VocabularyTab` after the tab moved from loading to loaded state (`Rendered more hooks than during the previous render`).
Fixed `stats/src/components/vocabulary/VocabularyTab.tsx` by removing the late `useMemo` hook and computing `knownWordCount` as a plain derived value after the loading/error guards.
Added regression coverage in `stats/src/lib/vocabulary-tab.test.ts` to assert that `VocabularyTab` declares all hooks before the loading/error early returns.
Verification: `bun test stats/src/lib/vocabulary-tab.test.ts`, `bun test stats/src/lib/yomitan-lookup.test.tsx`, `bun run build:stats`, and a live Playwright check against the Vite app with stubbed stats API data all passed.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Fixed the blank Vocabulary tab regression in the stats UI. The root cause was a late `useMemo` hook declared after the loading/error early returns in `VocabularyTab`, which caused React to crash once vocabulary data finished loading. Removed that late hook, added a regression test guarding hook placement, verified the stats bundle builds, and confirmed in a live browser that the Vocabulary tab now renders loaded content instead of white-screening.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,56 @@
---
id: TASK-184
title: Stabilize branch verification gate
status: Done
assignee:
- Codex
created_date: '2026-03-17 19:28'
updated_date: '2026-03-17 19:31'
labels:
- stabilization
- ci
dependencies: []
references:
- package.json
- docs/workflow/verification.md
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Bring the current PR branch back to a green verification state by fixing any failing lint/format or test checks required for local handoff.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Repo source formatting checks pass for the current branch.
- [x] #2 Required local verification checks for this branch pass without introducing new failures.
- [x] #3 Any code or test adjustments stay scoped to the failing checks and preserve existing branch behavior.
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Fix the current source-formatting failures reported by `bun run format:check:src` using the minimal repo-standard Prettier output.
2. Re-run `bun run format:check:src` to confirm the lint/format gate is green.
3. Re-run the default handoff gate from `docs/workflow/verification.md`: `bun run typecheck`, `bun run test:fast`, `bun run test:env`, `bun run build`, and `bun run test:smoke:dist`.
4. Because `docs-site/` is modified on this branch, also run `bun run docs:test` and `bun run docs:build`.
5. If any verification step fails after formatting, fix only the blocking issue and re-run the relevant lane until green.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Initial gate snapshot before edits: `typecheck`, `test:fast`, `test:env`, `build`, and `test:smoke:dist` passed; `format:check:src` failed on 15 files.
Applied repo-standard Prettier formatting to the 15 files reported by `bun run format:check:src`; no additional logic changes were introduced in this stabilization pass.
Verification after formatting: `bun run format:check:src` passed; `bash .agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh --lane core --lane runtime-compat --lane docs` passed with artifacts under `.tmp/skill-verification/subminer-verify-20260317-122947-hEInF0`; `bun run test:env` passed separately.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Branch verification gate is green again. Fixed the only failing local gate by applying Prettier formatting to the 15 flagged source files, then re-ran the required verification lanes: source format check, core lane (`typecheck` + `test:fast`), runtime-compat lane (`build`, `test:runtime:compat`, `test:smoke:dist`), docs lane (`docs:test`, `docs:build`), and `test:env`. All passed.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,65 @@
---
id: TASK-185
title: Clarify library stats word-count labels
status: Done
assignee:
- codex
created_date: '2026-03-17 22:58'
updated_date: '2026-03-17 23:00'
labels:
- bug
- stats
- ui
dependencies: []
references:
- >-
/Users/sudacode/projects/japanese/SubMiner/stats/src/components/library/MediaHeader.tsx
- /Users/sudacode/projects/japanese/SubMiner/src/core/services/stats-server.ts
- >-
/Users/sudacode/projects/japanese/SubMiner/src/core/services/immersion-tracker/query.ts
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Fix the library/media detail stats header so occurrence-based subtitle counts are not presented as unique-word vocabulary totals. The UI should clearly distinguish subtitle word occurrences from unique known-word headword coverage to avoid misleading comparisons.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Library/media detail view labels subtitle occurrence totals with wording that does not imply unique vocabulary counts
- [x] #2 Known-words summary in the same view explicitly communicates that its denominator is unique words/headwords
- [x] #3 Frontend tests cover the updated copy so the mismatch does not regress
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Add a focused stats frontend test for MediaHeader copy that distinguishes occurrence totals from unique known-word coverage.
2. Update MediaHeader labels so occurrence-based totals no longer imply unique vocabulary counts.
3. Update the known-words label copy to explicitly state it is based on unique words/headwords.
4. Run targeted stats tests and record results.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Root cause confirmed: library header compares occurrence-based totalWordsSeen against unique-headword known-words summary. Awaiting plan approval before code changes.
Updated library header copy to label totalWordsSeen as word occurrences and known-word coverage as known unique words. Added an optional initialKnownWordsSummary prop to support deterministic server-render tests without changing runtime behavior.
Verification: `bun test stats/src/lib/yomitan-lookup.test.tsx` passes. `bun run typecheck:stats` remains blocked by preexisting unrelated errors in stats/src/components/anime/AnilistSelector.tsx, stats/src/lib/reading-utils.ts, stats/src/lib/reading-utils.test.ts, and stats/src/lib/vocabulary-tab.test.ts.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Clarified the library/media header so occurrence-based subtitle counts are no longer presented as if they were unique vocabulary totals. The header now labels `totalWordsSeen` as `word occurrences`, and the known-words summary explicitly says `known unique words`, which matches the backend's DISTINCT headword calculation.
For regression coverage, added a focused MediaHeader render test that exercises the exact mismatch case (30 occurrences vs 34 unique words) and verifies the new copy. Also updated one stale AnimeOverviewStats assertion in the same targeted test file so the focused stats test lane is green.
Tests run:
- `bun test stats/src/lib/yomitan-lookup.test.tsx`
- `bun run typecheck:stats` ⚠️ blocked by preexisting unrelated errors in AnilistSelector and reading-utils/vocabulary-tab stats files.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,75 @@
---
id: TASK-186
title: Remove stats Library tab and add episode detail navigation from anime page
status: Done
assignee:
- codex
created_date: '2026-03-17 23:19'
updated_date: '2026-03-17 23:29'
labels:
- stats
- ui
dependencies: []
references:
- /Users/sudacode/projects/japanese/SubMiner/stats/src/App.tsx
- >-
/Users/sudacode/projects/japanese/SubMiner/stats/src/components/layout/TabBar.tsx
- >-
/Users/sudacode/projects/japanese/SubMiner/stats/src/components/anime/AnimeDetailView.tsx
- >-
/Users/sudacode/projects/japanese/SubMiner/stats/src/components/anime/EpisodeList.tsx
- >-
/Users/sudacode/projects/japanese/SubMiner/stats/src/components/library/MediaDetailView.tsx
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Update the stats UI so watched-file detail is no longer exposed as a top-level Library tab. Users should open dedicated episode detail pages from the anime detail page while preserving inline quick-peek session expansion.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Stats navigation no longer shows a top-level Library tab.
- [x] #2 Anime episode rows keep inline quick-peek expansion and also expose an explicit control to open the dedicated episode detail page.
- [x] #3 Dedicated episode detail navigation lands on the existing watched-file detail view with a back action that returns to the originating anime detail page.
- [x] #4 Relevant stats component tests cover the new navigation flow and removed tab behavior.
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Add app-level stats navigation state for dedicated media detail so anime flows can open watched-file detail without a Library tab.
2. Remove the Library tab from the tab bar and top-level tab panels while preserving existing Overview/Anime/Trends/Vocabulary/Sessions behavior.
3. Update anime detail episode list to keep row expansion for quick peek and add an explicit button that opens the dedicated detail view for the selected episode.
4. Reuse MediaDetailView for episode detail and adjust its back action to return to the originating anime detail page.
5. Add or update stats component tests to cover the removed Library tab and the new anime-to-episode-detail navigation flow.
6. Run targeted stats tests, then targeted SubMiner verification lanes if needed for touched files.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Implemented app-level stats navigation state for dedicated media detail and removed the Library tab from the tab bar and top-level panels.
Anime episode rows now keep inline quick-peek expansion and expose a visible Details button that opens the dedicated watched-file detail view.
Reused MediaDetailView for anime-origin episode navigation with a Back to Anime label and app-level return path.
Verification: bun test stats/src/lib/stats-navigation.test.ts stats/src/lib/stats-ui-navigation.test.tsx; bun run build:stats; bash .agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh --lane core ... => passed.
Observed unrelated existing stats workspace issues outside this task when running bun run typecheck:stats, including AnilistSelector/reading-utils/vocabulary-tab and an outdated AnimeOverviewStats test signature.
Reopened for bugfix: episode Details button is a no-op when anime detail is open from within AnimeTab because app-level selectedAnimeId is not retained there. Follow-up fix will pass animeId explicitly through the callback chain instead of depending on App route state.
Bugfix: the Details button now passes animeId explicitly from AnimeTab/AnimeDetailView into app-level media-detail navigation, so dedicated episode navigation works even when the anime page was opened from within the tab rather than seeded by App state.
Bugfix verification: bun test stats/src/lib/stats-navigation.test.ts stats/src/lib/stats-ui-navigation.test.tsx; bun run build:stats => passed.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Removed the stats Library tab and replaced that navigation path with app-level dedicated media-detail routing from the anime page. Episode rows still support inline quick peek, and now also provide a Details button that opens the dedicated episode view and returns cleanly to the anime detail page. Added navigation-focused tests for the removed tab and anime-origin media-detail flow, and verified the change with targeted tests, stats bundle build, and the repo core verification lane.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,79 @@
---
id: TASK-187
title: Replace episode detail session history with expandable inline session details
status: Done
assignee:
- codex
created_date: '2026-03-17 23:42'
updated_date: '2026-03-17 23:57'
labels:
- stats
- ui
dependencies: []
references:
- >-
/Users/sudacode/projects/japanese/SubMiner/stats/src/components/library/MediaDetailView.tsx
- >-
/Users/sudacode/projects/japanese/SubMiner/stats/src/components/library/MediaSessionList.tsx
- >-
/Users/sudacode/projects/japanese/SubMiner/stats/src/components/sessions/SessionRow.tsx
- >-
/Users/sudacode/projects/japanese/SubMiner/stats/src/components/sessions/SessionDetail.tsx
- >-
/Users/sudacode/projects/japanese/SubMiner/stats/src/components/sessions/SessionsTab.tsx
- >-
/Users/sudacode/projects/japanese/SubMiner/stats/src/components/overview/OverviewTab.tsx
- >-
/Users/sudacode/projects/japanese/SubMiner/stats/src/components/overview/RecentSessions.tsx
documentation:
- >-
/Users/sudacode/projects/japanese/SubMiner/docs/plans/2026-03-17-episode-detail-session-accordion-design.md
- >-
/Users/sudacode/projects/japanese/SubMiner/docs/plans/2026-03-17-episode-detail-session-accordion.md
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Update the dedicated episode detail page so its session history uses the same expandable session-row behavior as the Sessions page, including inline timeline details and session deletion, instead of navigating away to the Sessions tab. Also update home-page session navigation so recent session links open the associated episode detail page rather than the Sessions tab.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Dedicated episode detail session history uses expandable inline rows styled like the Sessions page instead of linking to the Sessions tab.
- [x] #2 Expanding a session on the episode detail page shows the full existing session detail panel, including the timeline chart and stats.
- [x] #3 Episode detail session rows retain a session delete control with the same behavior and safeguards as the Sessions page.
- [x] #4 Home-page recent session navigation opens the associated episode detail page when a session is tied to a video, instead of routing to the Sessions tab.
- [x] #5 Relevant stats tests cover the inline session expansion/delete behavior and the updated home-page navigation path.
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Add failing tests for media-detail session accordion structure and for overview-to-media-detail navigation, keeping orphan-session fallback coverage.
2. Rework MediaSessionList to reuse SessionRow and SessionDetail with local expansion state and delete affordance matching the Sessions page.
3. Move media-detail session mutation/delete ownership into MediaDetailView so deletes update the current episode page immediately.
4. Add app-level direct media-detail navigation from overview/home-page session rows when videoId exists; keep Sessions-tab fallback for sessions without videoId.
5. Run targeted tests, stats build, and the SubMiner core verification lane; then update TASK-187 with results.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Added approved design/plan docs at docs/plans/2026-03-17-episode-detail-session-accordion-design.md and docs/plans/2026-03-17-episode-detail-session-accordion.md before implementation.
MediaDetailView now owns local session state and delete handling, derives displayed media aggregates from the current session list, and renders MediaSessionList as an inline accordion instead of a session-page link list.
MediaSessionList now reuses SessionRow and full SessionDetail so episode-level session history matches Sessions-page dropdown behavior and keeps the same delete affordance.
Overview/home-page recent session navigation now prefers dedicated media detail when session.videoId exists and falls back to the Sessions tab only for orphan sessions without videoId.
Verification passed: bun test stats/src/lib/stats-navigation.test.ts stats/src/lib/stats-ui-navigation.test.tsx stats/src/lib/media-session-list.test.tsx; bun run build:stats; bash .agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh --lane core stats/src/App.tsx stats/src/components/overview/OverviewTab.tsx stats/src/components/overview/RecentSessions.tsx stats/src/components/library/MediaDetailView.tsx stats/src/components/library/MediaSessionList.tsx stats/src/lib/stats-navigation.ts stats/src/lib/stats-navigation.test.ts stats/src/lib/stats-ui-navigation.test.tsx stats/src/lib/media-session-list.test.tsx => passed.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Dedicated episode detail pages now show inline expandable session rows using the same shared SessionRow + SessionDetail UI as the Sessions page, including per-session delete controls. Overview/home-page recent session clicks now open the episode detail page whenever a backing video exists, with Sessions-tab fallback only for sessions missing videoId. Added navigation and media-session-list tests plus design/implementation docs, and verified the change with targeted tests, stats bundle build, and the SubMiner core verification lane.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,54 @@
---
id: TASK-187.1
title: Auto-expand targeted session when opening media detail
status: In Progress
assignee:
- codex
created_date: '2026-03-18 01:32'
updated_date: '2026-03-18 01:36'
labels:
- stats
- ui
dependencies: []
references:
- stats/src/lib/stats-navigation.ts
- stats/src/App.tsx
- stats/src/components/overview/RecentSessions.tsx
- stats/src/components/library/MediaDetailView.tsx
- stats/src/components/library/MediaSessionList.tsx
- stats/src/lib/stats-navigation.test.ts
parent_task_id: TASK-187
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
When a navigation path opens episode/media detail with a known session ID, the matching session row in media detail should auto-expand so the user lands directly on the intended session details instead of only the episode history page.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 Media detail navigation state can carry an optional target session ID alongside the selected video.
- [ ] #2 Any navigation path that opens media detail with a known session ID causes that session row to auto-expand when the episode history loads.
- [ ] #3 Session-tab fallback for orphan sessions without a video still behaves as it does now.
- [ ] #4 Media detail auto-expansion clears or stabilizes its one-shot navigation state so normal manual expand/collapse behavior still works after landing.
- [ ] #5 Relevant navigation/component tests cover the targeted media-detail auto-expand behavior.
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Extend media-detail navigation state to optionally carry a target session ID while preserving the existing orphan-session fallback to the Sessions tab.
2. Update app-level navigation helpers and overview recent-session click handling to pass session IDs into media-detail navigation whenever both video and session are known.
3. Thread the one-shot target session ID into MediaDetailView and MediaSessionList so the matching accordion row auto-expands on load, then clear/stabilize that state so manual toggling still behaves normally.
4. Update targeted stats navigation/component tests to cover media-detail auto-expansion and fallback behavior.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Extended media-detail navigation state to carry an optional `initialSessionId`, updated overview/app navigation to pass session IDs into media detail whenever a video-backed session is clicked, and wired `MediaDetailView` + `MediaSessionList` to auto-expand and then consume that one-shot session target.
Updated `stats-navigation.test.ts` to cover the new navigation-state shape. Validation not run in this pass, so acceptance criteria remain unchecked pending verification.
<!-- SECTION:NOTES:END -->

View File

@@ -0,0 +1,59 @@
---
id: TASK-188
title: Refactor stats chart data pipeline to use backend-aggregated series
status: In Progress
assignee:
- codex
created_date: '2026-03-18 00:29'
updated_date: '2026-03-18 00:55'
labels:
- stats
- performance
- refactor
dependencies: []
references:
- src/core/services/immersion-tracker/query.ts
- src/core/services/immersion-tracker-service.ts
- src/core/services/stats-server.ts
- stats/src/hooks/useTrends.ts
- stats/src/components/trends/TrendsTab.tsx
- stats/src/lib/api-client.ts
- stats/src/types/stats.ts
- stats/src/lib/dashboard-data.ts
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Reduce long-term dashboard performance debt by moving chart aggregation out of the stats UI and into the tracker/stats API layer. The trends dashboard should consume chart-ready series from backend rollups instead of reconstructing multiple datasets from raw session lists in the browser.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 Stats API exposes chart-oriented aggregated trend data needed by the trends dashboard without requiring raw session lists for those charts.
- [ ] #2 The trends dashboard consumes the new aggregated API responses and no longer rebuilds its main chart datasets from raw sessions in the render path.
- [ ] #3 Time-range and grouping behavior remain correct for recent and all-time views, with explicit handling that keeps older history performant.
- [ ] #4 Existing overview and anime detail charts continue to behave correctly, or are migrated to the shared aggregation path where it reduces debt.
- [ ] #5 Tests cover backend aggregation/query behavior and frontend consumption of the new response shapes.
- [ ] #6 Internal docs are updated to describe the new stats chart data flow and scaling rationale.
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Add a chart-oriented trends dashboard API response on the stats server that returns pre-aggregated series by range/grouping instead of requiring raw session lists in the UI.
2. Implement tracker/query-layer helpers that aggregate trend series on the backend, preferring rollups for scalable time-series data and centralizing chart shaping there.
3. Update stats client types and `useTrends` to consume the new response shape and stop fetching raw sessions for main chart construction.
4. Simplify `TrendsTab` and related chart components so they render backend-provided series with only lightweight UI-level filtering/state.
5. Keep overview/anime detail chart behavior intact, and reuse shared aggregation paths where it meaningfully reduces debt without widening scope.
6. Add/adjust backend and frontend tests plus internal docs to describe the new chart-data flow and performance rationale.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Implemented a new `/api/stats/trends/dashboard` server route backed by tracker/query-layer aggregation, updated the stats client and `useTrends` to consume the new chart-ready payload, simplified `TrendsTab` to render backend-provided series, added route/query/api-client tests, and documented the new trends data flow in `docs/architecture/stats-trends-data-flow.md`.
Did not run validation commands in this pass; acceptance criteria remain unchecked pending requested verification.
<!-- SECTION:NOTES:END -->

View File

@@ -0,0 +1,6 @@
type: added
area: stats
- Added `subminer stats -b` to start or reuse a dedicated background stats server without blocking normal SubMiner instances.
- Added `subminer stats -s` to stop the dedicated background stats server without closing browser tabs.
- Stats server startup now reuses a running background stats daemon instead of trying to bind a second local server in another SubMiner instance.

View File

@@ -0,0 +1,5 @@
type: fixed
area: stats
- Fixed session stats so known-word counts track real known-word occurrences without collapsing subtitle-line gaps.
- Fixed session word totals in session-facing stats views to prefer token counts when available, preventing known words from exceeding total words in the session chart.

View File

@@ -0,0 +1,4 @@
type: changed
area: immersion
- Renamed the stats dashboard's Anime tab to Library so the media browser label matches non-anime sources like YouTube and other yt-dlp-backed content.

View File

@@ -0,0 +1,4 @@
type: fixed
area: stats
- Fixed the stats Vocabulary tab blank-screen regression caused by a hook-order crash after vocabulary data finished loading.

View File

@@ -0,0 +1,5 @@
type: changed
area: anilist
- Standardized episode completion threshold by introducing `DEFAULT_MIN_WATCH_RATIO` and using it for both local watched state transitions and AniList post-watch progress updates.
- Episode auto-marking now uses the same threshold as AniList (`85%`), removing divergent completion behavior.

View File

@@ -0,0 +1,4 @@
type: fixed
area: stats
- Removed the misleading `New words` series from expanded session charts; session detail now shows only the real total-word and known-word lines.

View File

@@ -1014,7 +1014,7 @@ Character dictionary sync behavior:
Current post-watch behavior:
- SubMiner attempts an update near episode completion (`>=85%` watched and at least `10` minutes watched).
- SubMiner attempts an update near episode completion using the shared default minimum watch ratio (`0.85`, or `>=85%`) from `src/shared/watch-threshold.ts`, and requires at least `10` minutes watched. The same ratio is also used by local episode watched state transitions.
- Episode/title detection is `guessit`-first with fallback to SubMiner's filename parser.
- If `guessit` is unavailable, updates still work via fallback parsing but title matching can be less accurate.
- If embedded AniList auth UI fails to render, SubMiner opens the authorize URL in your default browser and shows fallback instructions in-app.
@@ -1256,7 +1256,7 @@ Usage notes:
- The browser UI is served at `http://127.0.0.1:<serverPort>`.
- The overlay toggle is local to the focused visible overlay window; it is not registered as a global OS shortcut.
- The dashboard reads from the same immersion-tracking database, so keep `immersionTracking.enabled` on if you want data to appear.
- The UI includes Overview, Anime, Trends, Vocabulary, and Sessions tabs.
- The UI includes Overview, Library, Trends, Vocabulary, and Sessions tabs.
### YouTube Subtitle Generation

View File

@@ -4,6 +4,8 @@ SubMiner can log your watching and mining activity to a local SQLite database, t
When enabled, SubMiner records per-session statistics (watch time, subtitle lines seen, words encountered, cards mined) and maintains exact lifetime summary tables plus daily/monthly rollups. You can view that data in SubMiner's stats UI or query the database directly with any SQLite tool.
Episode completion for local `watched` state uses the shared `DEFAULT_MIN_WATCH_RATIO` (`85%`) value from `src/shared/watch-threshold.ts`.
## Enabling
```jsonc
@@ -24,13 +26,14 @@ The same immersion data powers the stats dashboard.
- In-app overlay: focus the visible overlay, then press the key from `stats.toggleKey` (default: `` ` `` / `Backquote`).
- Launcher command: run `subminer stats` to start the local stats server on demand and open the dashboard in your browser.
- Background server: run `subminer stats -b` to start or reuse a dedicated background stats server without keeping the launcher attached, and `subminer stats -s` to stop that background server.
- Maintenance command: run `subminer stats cleanup` or `subminer stats cleanup -v` to backfill/repair vocabulary metadata (`headword`, `reading`, POS) and purge stale or excluded rows from `imm_words` on demand.
- Browser page: open `http://127.0.0.1:5175` directly if the local stats server is already running.
Dashboard tabs:
- Overview: recent sessions, streak calendar, watch-time history, and a tracking snapshot with completed episodes/anime totals
- Anime: cover-art library, per-series progress, episode drill-down, and direct links into mined cards
- Library: cover-art library, per-series progress, episode drill-down, and direct links into mined cards
- Trends: watch time, sessions, words seen, and per-anime progress/pattern charts
- Sessions: expandable session history with new-word activity, cumulative totals, and pause/seek/card markers
- Vocabulary: top repeated words (click a bar to open the word), new-word timeline, frequency rank table with full readings, kanji breakdown, word exclusion list, and click-through occurrence drilldown with Mine Word / Mine Sentence / Mine Audio buttons
@@ -50,9 +53,11 @@ Stats server config lives under `stats`:
- `toggleKey` is overlay-local, not a system-wide shortcut.
- `serverPort` controls the localhost dashboard URL.
- `autoStartServer` starts the local stats HTTP server on launch once immersion tracking is active.
- `autoStartServer` starts the local stats HTTP server on launch once immersion tracking is active, or reuses the dedicated background stats server when one is already running.
- `autoOpenBrowser` controls whether `subminer stats` launches the dashboard URL in your browser after ensuring the server is running.
- `subminer stats` forces the dashboard server to start even when `autoStartServer` is `false`.
- `subminer stats -b` starts or reuses the dedicated background stats server and exits after startup acknowledgement.
- `subminer stats -s` stops the dedicated background stats server without closing any browser tabs.
- `subminer stats` fails with an error when `immersionTracking.enabled` is `false`.
- `subminer stats cleanup` defaults to vocabulary cleanup, repairs stale `headword`, `reading`, and `part_of_speech` values, attempts best-effort MeCab backfill for legacy rows, and removes rows that still fail vocab filtering.
@@ -232,10 +237,12 @@ LIMIT ?;
- Write path is asynchronous and queue-backed. Hot paths (subtitle parsing, render, token flows) enqueue telemetry and never await SQLite writes.
- Queue overflow policy: drop oldest queued writes, keep newest.
- SQLite pragmas: `journal_mode=WAL`, `synchronous=NORMAL`, `foreign_keys=ON`, `busy_timeout=2500`.
- SQLite tunings: `journal_mode=WAL`, `synchronous=NORMAL`, `foreign_keys=ON`, `busy_timeout=2500`, bounded WAL growth via `journal_size_limit`.
- Maintenance executes `PRAGMA optimize` after periodic cleanup.
- Rollups run incrementally from the last processed telemetry sample; startup performs a one-time bootstrap pass.
- Cover-art blobs are deduplicated into `imm_cover_art_blobs` and referenced from `imm_media_art`.
- Large-table reads are index-backed for `sample_ms`, session time windows, frequency-ranked words/kanji, and cover-art identity lookups.
- Workload-dependent tuning knobs remain at defaults unless you change them: `cache_size`, `mmap_size`, `temp_store`, `auto_vacuum`.
### Schema (v12)

View File

@@ -206,7 +206,7 @@ Enable it in your config:
}
```
Open the dashboard in the overlay with `stats.toggleKey` (default: `` ` ``), launch it in a browser with `subminer stats`, or visit `http://127.0.0.1:5175` directly if the local stats server is already running. The dashboard covers overview totals, anime progress, session detail, and vocabulary drill-down from the same local immersion database.
Open the dashboard in the overlay with `stats.toggleKey` (default: `` ` ``), launch it in a browser with `subminer stats`, keep a dedicated background server alive with `subminer stats -b`, stop that background server with `subminer stats -s`, or visit `http://127.0.0.1:5175` directly if the local stats server is already running. The dashboard covers overview totals, anime progress, session detail, and vocabulary drill-down from the same local immersion database.
See [Immersion Tracking](/immersion-tracking) for dashboard details, schema, and retention settings.

View File

@@ -0,0 +1,30 @@
# Stats Trends Data Flow
read_when: touching stats trend charts, changing stats API payloads, or debugging dashboard performance
## Summary
Trend charts now consume one chart-oriented backend payload from `/api/stats/trends/dashboard`.
## Why
- remove repeated client-side dataset rebuilding in `TrendsTab`
- collapse multiple network round-trips into one request
- keep heavy chart shaping close to tracker/query logic
## Data Sources
- rollup-backed:
- activity charts
- cumulative watch/cards/words/sessions trends
- per-anime watch/cards/words/episodes series
- session-metric-backed:
- lookup trends
- lookup rate trends
- watch-time by day-of-week/hour
- vocabulary-backed:
- new-words trend
## Contract
The stats UI should treat the trends payload as chart-ready data. Presentation-only work in the client is fine, but rebuilding the main trend datasets from raw sessions should stay out of the render path.