Enhance AniList character dictionary sync and subtitle features (#15)

This commit is contained in:
2026-03-07 18:30:59 -08:00
committed by GitHub
parent 2f07c3407a
commit e18985fb14
696 changed files with 14297 additions and 173564 deletions

View File

@@ -1,11 +1,11 @@
project_name: 'SubMiner'
default_status: 'To Do'
statuses: ['To Do', 'In Progress', 'Done']
project_name: "SubMiner"
default_status: "To Do"
statuses: ["To Do", "In Progress", "Done"]
labels: []
definition_of_done: []
date_format: yyyy-mm-dd
max_column_width: 20
default_editor: 'nvim'
default_editor: "nvim"
auto_open_browser: false
default_port: 6420
remote_operations: true
@@ -13,4 +13,4 @@ auto_commit: false
bypass_git_hooks: false
check_active_branches: true
active_branch_days: 30
task_prefix: 'task'
task_prefix: "task"

View File

@@ -0,0 +1,49 @@
---
id: TASK-100
title: 'Add configurable texthooker startup launch'
status: Done
assignee: []
created_date: '2026-03-06 23:30'
updated_date: '2026-03-07 01:59'
labels: []
dependencies: []
priority: medium
ordinal: 10000
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Add a config option under `texthooker` to launch the built-in texthooker server automatically when SubMiner starts.
Scope:
- Add `texthooker.launchAtStartup`.
- Default to `true`.
- Start the existing texthooker server during normal app startup when enabled.
- Keep `texthooker.openBrowser` as separate behavior.
- Add regression coverage and update generated config docs/example.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Default config enables automatic texthooker startup.
- [x] #2 Config parser accepts valid boolean values and warns on invalid values.
- [x] #3 App-ready startup launches texthooker when enabled.
- [x] #4 Generated config template/example documents the new option.
<!-- AC:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Added `texthooker.launchAtStartup` with a default of `true`, wired it through config defaults/validation/template generation, and started the existing texthooker server during app-ready startup without coupling it to browser auto-open behavior.
Also added regression coverage for config parsing/template output and app-ready dependency wiring, then regenerated the checked-in config example artifacts.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,39 @@
---
id: TASK-101
title: Index AniList character alternative names in the character dictionary
status: Done
assignee: []
created_date: '2026-03-07 00:00'
updated_date: '2026-03-08 00:11'
labels:
- dictionary
- anilist
dependencies: []
references:
- src/main/character-dictionary-runtime.ts
- src/main/character-dictionary-runtime.test.ts
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Index AniList character alternative names in generated character dictionaries so aliases like Shadow resolve during subtitle lookup instead of falling through to unrelated generic dictionary entries.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Character fetch reads AniList alternative character names needed for lookup coverage
- [x] #2 Generated term banks include alias-derived terms for subtitle lookups like シャドウ
- [x] #3 Regression coverage proves alternative-name indexing works end to end
<!-- AC:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Character dictionary generation now requests AniList `name.alternative`, indexes those aliases as term candidates, and expands mixed aliases like `Minoru Kagenou (影野ミノル)` into usable outer/inner variants. Also extended kana alias synthesis so the AniList alias `Shadow` emits `シャドウ`, which matches the subtitle token the user hit in The Eminence in Shadow.
Bumped the character-dictionary snapshot format to invalidate stale cached snapshots, and updated merged-dictionary rebuilds to refresh invalid snapshots before composing the ZIP so old cache files do not hard-fail the merge path.
Verified with `bun test src/main/character-dictionary-runtime.test.ts` and `bun run tsc --noEmit`.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,65 @@
---
id: TASK-102
title: Quiet default AppImage startup and implicit background launch
status: Done
assignee:
- codex
created_date: '2026-03-06 21:20'
updated_date: '2026-03-06 21:33'
labels: []
dependencies: []
references:
- /home/sudacode/projects/japanese/SubMiner/src/main-entry-runtime.ts
- /home/sudacode/projects/japanese/SubMiner/src/core/services/cli-command.ts
- /home/sudacode/projects/japanese/SubMiner/src/main.ts
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Make the packaged Linux no-arg launch path behave like a quiet background start instead of surfacing startup-only noise.
Scope:
- Treat default background entry launches as implicit `--start --background`.
- Keep the `--password-store` diagnostic out of normal startup output.
- Suppress known startup-only `node:sqlite` and `lsfg-vk` warnings for the entry/background launch path.
- Avoid noisy protocol-registration warnings during normal startup when registration is unsupported.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Initial background launch reaches the start path without logging `No running instance. Use --start to launch the app.`
- [x] #2 Default startup no longer emits the `Applied --password-store gnome-libsecret` line at normal log levels.
- [x] #3 Entry/background launch sanitization suppresses the observed `ExperimentalWarning: SQLite...` and `lsfg-vk ... unsupported configuration version` startup noise.
- [x] #4 Regression coverage documents the new startup behavior.
<!-- AC:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Normalized no-arg/password-store-only entry launches to append implicit `--start --background`, and upgraded `--background`-only entry launches to include `--start`.
Applied shared entry env sanitization before loading the main process so default startup strips the `lsfg-vk` Vulkan layer and sets `NODE_NO_WARNINGS=1`; background children keep the same sanitized env.
Downgraded startup-only protocol-registration failure logging to debug, and routed the Linux password-store diagnostic through the scoped debug logger instead of raw console output.
Verification:
- `bun test src/main-entry-runtime.test.ts src/main/runtime/anilist-setup-protocol.test.ts src/main/runtime/anilist-setup-protocol-main-deps.test.ts`
- `bun run test:fast`
Note: the final `node --experimental-sqlite --test dist/main/runtime/registry.test.js` step in `bun run test:fast` still prints Node's own experimental SQLite warning because that test command explicitly enables the feature flag outside the app entrypoint.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Default packaged startup is now quiet and behaves like an implicit `--start --background` launch.
- No-arg AppImage entry launches now append `--start --background`, and `--background`-only launches append the missing `--start`.
- Entry/background startup sanitization now suppresses the observed `lsfg-vk` and `node:sqlite` warnings on the app launch path.
- Linux password-store and unsupported protocol-registration diagnostics now stay at debug level instead of normal startup output.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,38 @@
---
id: TASK-103
title: Add dedicated annotation websocket for texthooker
status: Done
assignee:
- codex
created_date: '2026-03-07 02:20'
updated_date: '2026-03-07 02:20'
labels:
- texthooker
- websocket
- subtitle
dependencies: []
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Add a separate annotated subtitle websocket for bundled texthooker so token/JLPT/frequency markup is available on a stable dedicated port even when the regular websocket is in `auto` mode and skipped because `mpv_websocket` is installed.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Regular `websocket.enabled: "auto"` behavior remains unchanged and still skips the regular websocket when `mpv_websocket` is installed.
- [x] #2 A separate `annotationWebsocket` config controls an independent annotated websocket with default port `6678`.
- [x] #3 Bundled texthooker is pointed at the annotation websocket when it is enabled.
- [x] #4 Focused regression tests cover config parsing, startup wiring, and texthooker bootstrap injection.
<!-- AC:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Added `annotationWebsocket.enabled`/`annotationWebsocket.port` with defaults of `true`/`6678`, started that websocket independently from the regular auto-managed websocket, and injected the bundled texthooker websocket URL so it connects to the annotation feed by default.
Also added focused regression coverage and regenerated the checked-in config examples.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,43 @@
---
id: TASK-104
title: Mirror overlay annotation hover behavior in vendored texthooker
status: Done
assignee:
- codex
created_date: '2026-03-06 21:45'
updated_date: '2026-03-06 21:45'
labels:
- texthooker
- subtitle
- websocket
dependencies:
- TASK-103
references:
- /home/sudacode/projects/japanese/SubMiner/src/core/services/subtitle-ws.ts
- /home/sudacode/projects/japanese/SubMiner/vendor/texthooker-ui/src/components/App.svelte
- /home/sudacode/projects/japanese/SubMiner/vendor/texthooker-ui/src/line-markup.ts
- /home/sudacode/projects/japanese/SubMiner/vendor/texthooker-ui/src/app.css
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Bring bundled texthooker annotation rendering closer to the visible overlay. Keep the lightweight texthooker UX, but preserve token metadata for hover, match overlay color-precedence rules across known/N+1/name/frequency/JLPT, expose name-match highlighting as a toggle, and emit a structured annotation payload on the dedicated websocket so non-SubMiner clients can treat it as an API.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Annotation websocket payload includes both rendered `sentence` HTML and structured token metadata for generic clients.
- [x] #2 Vendored texthooker preserves annotation metadata attrs needed for hover labels and uses overlay-matching color precedence rules.
- [x] #3 Vendored texthooker supports character-name highlighting with a user-facing toggle and standalone-web note.
- [x] #4 Hovering annotated texthooker tokens reveals JLPT/frequency metadata without adding the full overlay popup workflow.
- [x] #5 Focused serializer, texthooker markup, socket parsing, CSS, and build verification pass.
<!-- AC:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Extended the dedicated annotation websocket payload to ship `version`, plain `text`, rendered `sentence`, and structured `tokens` metadata while keeping backward-compatible `sentence` consumers working. Updated the vendored texthooker to preserve hover metadata attrs, follow overlay color precedence for known/N+1/name/frequency/JLPT annotations, add a character-name highlight toggle plus standalone-web dictionary note, and render lightweight hover labels for frequency/JLPT metadata. Added focused regression coverage and rebuilt both the vendored texthooker bundle and SubMiner.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,47 @@
---
id: TASK-105
title: Stop local docs artifact writes after docs repo split
status: Done
assignee: []
created_date: '2026-03-07 00:00'
updated_date: '2026-03-07 00:20'
labels: []
dependencies: []
priority: medium
ordinal: 10500
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Now that user-facing docs live in `../subminer-docs`, first-party scripts in this repo should not keep writing generated artifacts into the local `docs/` tree.
Scope:
- Audit first-party scripts/automation for writes to `docs/`.
- Keep repo-local outputs only where they are still intentionally owned by this repo.
- Repoint generated docs artifacts to `../subminer-docs` when that is the maintained source of truth.
- Add regression coverage for the config-example generation path contract.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 The config-example generator no longer writes to `docs/public/config.example.jsonc` inside this repo.
- [x] #2 When `../subminer-docs` exists, the generator updates `../subminer-docs/public/config.example.jsonc`.
- [x] #3 Automated coverage guards the output-path contract so local docs writes do not regress.
<!-- AC:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Removed the first-party local `docs/public` config-example write path from `src/generate-config-example.ts` and replaced it with sibling-docs-repo detection that targets `../subminer-docs/public/config.example.jsonc` only when that repo exists.
Added a project-local regression suite for output-path resolution and artifact writing, wired that suite into the maintained config test lane, and removed the stale generated `docs/public/config.example.jsonc` artifact from the working tree.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,69 @@
---
id: TASK-106
title: Add first-run setup gate and auto-install flow
status: Done
assignee:
- codex
created_date: '2026-03-07 06:10'
updated_date: '2026-03-07 06:20'
labels: []
dependencies: []
references:
- /home/sudacode/projects/japanese/SubMiner/src/main.ts
- /home/sudacode/projects/japanese/SubMiner/src/shared/setup-state.ts
- /home/sudacode/projects/japanese/SubMiner/src/main/runtime/first-run-setup-service.ts
- /home/sudacode/projects/japanese/SubMiner/src/main/runtime/first-run-setup-window.ts
- /home/sudacode/projects/japanese/SubMiner/launcher/commands/playback-command.ts
priority: high
ordinal: 10600
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Replace the current manual install flow with a first-run setup gate:
- bootstrap the default config dir/config file automatically
- detect legacy installs and mark them complete when config + Yomitan dictionaries are already present
- open a compact Catppuccin Macchiato setup popup for incomplete installs
- optionally install the mpv plugin into the default mpv location
- block launcher playback until setup completes, then resume the original playback flow
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 First app launch seeds the default config dir/config file without manual copy steps.
- [x] #2 Existing installs with config plus at least one Yomitan dictionary are auto-detected as already complete.
- [x] #3 Incomplete installs get a first-run setup popup with mpv plugin install, Yomitan settings, refresh, skip, and finish actions.
- [x] #4 Launcher playback waits for setup completion and does not start mpv while setup is incomplete.
- [x] #5 Plugin assets are packaged into the Electron bundle and regression tests cover the new flow.
<!-- AC:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Added shared setup-state/config/mpv path helpers so Electron and launcher read the same onboarding state file.
Introduced a first-run setup service plus compact BrowserWindow popup using Catppuccin Macchiato styling. The popup supports optional mpv plugin install, opening Yomitan settings, status refresh, skip-plugin, and gated finish once at least one Yomitan dictionary is installed.
Electron startup now bootstraps a default config file, auto-detects legacy-complete installs, adds `--setup` CLI support, exposes a tray `Complete Setup` action while incomplete, and avoids reopening setup once completion is recorded.
Launcher playback now checks the shared setup-state file before starting mpv. If setup is incomplete, it launches the app with `--background --setup`, waits for completion, and only then proceeds.
Verification:
- `bun run typecheck`
- `bun run test:fast`
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
SubMiner now supports a download-and-launch install flow.
- First launch auto-creates config and opens setup only when needed.
- Existing users with working installs are silently migrated to completed setup.
- The setup popup handles optional mpv plugin install and Yomitan dictionary readiness.
- Launcher playback is gated on setup completion and resumes automatically afterward.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,42 @@
---
id: TASK-107
title: 'Fix Yomitan scan-token fallback fragmentation on exact-source misses'
status: Done
assignee: []
created_date: '2026-03-07 01:10'
updated_date: '2026-03-07 01:12'
labels: []
dependencies: []
priority: high
ordinal: 9007
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Left-to-right Yomitan scanning can emit bogus fallback tokens when `termsFind` returns entries but none of their headwords carries an exact primary source for the consumed substring. Repro: `だが それでも届かぬ高みがあった` currently yields trailing fragments like `があ` / `た`, which blocks the real `あった` token from receiving frequency highlighting.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Scanner skips `termsFind` fallback entries that are not backed by an exact primary source for the consumed substring.
- [x] #2 Repro line no longer yields bogus trailing fragments such as `があ`.
- [x] #3 Regression coverage added for the scan-token path.
<!-- AC:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Removed the scan-token helper fallback that previously emitted a token from the first returned headword even when Yomitan did not report an exact primary source for the consumed substring. Added a focused regression test covering `だが それでも届かぬ高みがあった`, ensuring bogus `があ` fragmentation is skipped so the later `あった` exact match can still be tokenized and highlighted.
Verification:
- `bun test src/core/services/tokenizer/yomitan-parser-runtime.test.ts src/core/services/tokenizer.test.ts --timeout 20000`
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,43 @@
---
id: TASK-108
title: 'Exclude single kana tokens from frequency highlighting'
status: Done
assignee: []
created_date: '2026-03-07 01:18'
updated_date: '2026-03-07 01:22'
labels: []
dependencies: []
priority: medium
ordinal: 9008
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Suppress frequency highlighting for single-character hiragana or katakana tokens. Scope is frequency-only: known/N+1/JLPT behavior stays unchanged.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Single-character hiragana tokens do not retain `frequencyRank`.
- [x] #2 Single-character katakana tokens do not retain `frequencyRank`.
- [x] #3 Regression coverage exists at annotation-stage and tokenizer levels.
<!-- AC:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Added a frequency-only suppression rule for single-character kana tokens based on token `surface`, so bogus merged fragments like `た` and standalone one-character kana no longer keep `frequencyRank`. Regression coverage now exists both in the annotation stage and in the tokenizer path, while multi-character tokens and N+1/JLPT behavior remain unchanged.
Verification:
- `bun test src/core/services/tokenizer/annotation-stage.test.ts --timeout 20000`
- `bun test src/core/services/tokenizer.test.ts --timeout 20000`
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,51 @@
---
id: TASK-110
title: Replace vendored Yomitan with submodule-built Chrome artifact workflow
status: Done
assignee: []
created_date: '2026-03-07 11:05'
updated_date: '2026-03-07 11:22'
labels:
- yomitan
- build
- release
dependencies: []
priority: high
ordinal: 9010
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Replace the checked-in `vendor/yomitan` release tree with a `subminer-yomitan` git submodule. Build Yomitan from source, extract the Chromium zip artifact into a stable local build directory, and make SubMiner dev/runtime/tests/release packaging load that extracted extension instead of the source tree or vendored files.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Repo tracks Yomitan as a git submodule instead of committed extension files under `vendor/yomitan`.
- [x] #2 SubMiner has a reproducible build/extract step that produces a local Chromium extension directory from `subminer-yomitan`.
- [x] #3 Dev/runtime/tests resolve the extracted build output as the default Yomitan extension path.
- [x] #4 Release packaging includes the extracted Chromium extension files instead of the old vendored tree.
- [x] #5 Docs and verification commands reflect the new workflow.
<!-- AC:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Replaced the checked-in `vendor/yomitan` extension tree with a `vendor/subminer-yomitan` git submodule and added a reproducible `bun run build:yomitan` workflow that builds `yomitan-chrome.zip`, extracts it into `build/yomitan`, and reuses a source-state stamp to skip redundant rebuilds. Runtime path resolution, helper CLIs, Yomitan integration tests, packaging, CI cache keys, and README source-build notes now all target that generated artifact instead of the old vendored files.
Verification:
- `bun run build:yomitan`
- `bun test src/core/services/yomitan-extension-paths.test.ts src/core/services/yomitan-structured-content-generator.test.ts src/yomitan-translator-sort.test.ts`
- `bun run typecheck`
- `bun run build`
- `bun run test:core:src`
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,71 @@
---
id: TASK-111
title: Fix subtitle-cycle OSD labels for J keybindings
status: Done
assignee:
- Codex
created_date: '2026-03-07 23:45'
updated_date: '2026-03-08 00:06'
labels: []
dependencies: []
references:
- /Users/sudacode/projects/japanese/SubMiner/src/core/services/ipc-command.ts
- /Users/sudacode/projects/japanese/SubMiner/src/core/services/mpv.ts
- >-
/Users/sudacode/projects/japanese/SubMiner/src/core/services/ipc-command.test.ts
- >-
/Users/sudacode/projects/japanese/SubMiner/src/core/services/mpv-control.test.ts
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
When cycling subtitle tracks with the default J/Shift+J keybindings, the mpv OSD currently shows raw template text like `${sid}` instead of a resolved subtitle label. Update the keybinding OSD behavior so users see the active subtitle selection clearly when cycling tracks, and ensure placeholder-based OSD messages sent through the mpv client API render correctly.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Pressing the primary subtitle cycle keybinding shows a resolved subtitle label on the OSD instead of a raw `${sid}` placeholder.
- [x] #2 Pressing the secondary subtitle cycle keybinding shows a resolved subtitle label on the OSD instead of a raw `${secondary-sid}` placeholder.
- [x] #3 Proxy OSD messages that rely on mpv property expansion render resolved values when sent through the mpv client API.
- [x] #4 Regression tests cover the subtitle-cycle OSD behavior and the placeholder-expansion OSD path.
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Add focused failing tests for subtitle-cycle OSD labels and mpv placeholder-expansion behavior.
2. Update the IPC mpv command handler to resolve primary and secondary subtitle track labels from mpv `track-list` data after cycling subtitle tracks.
3. Update the mpv OSD runtime path so placeholder-based `show-text` messages sent through the client API opt into property expansion.
4. Run focused tests, then the relevant core test lane, and record results in the task notes.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Initial triage: `ipc-command.ts` emits raw `${sid}`/`${secondary-sid}` placeholder strings, and `showMpvOsdRuntime` sends `show-text` via mpv client API without enabling property expansion.
User approved implementation plan on 2026-03-07.
Implementation: proxy mpv command OSD now supports an async resolver so subtitle track cycling can show human-readable labels instead of raw `${sid}` placeholders.
Implementation: `showMpvOsdRuntime` now prefixes placeholder-based messages with mpv client-api `expand-properties`, which fixes raw `${...}` OSD output for subtitle delay/position messages.
Testing: `bun test src/core/services/ipc-command.test.ts src/core/services/mpv-control.test.ts src/main/runtime/mpv-proxy-osd.test.ts src/main/runtime/ipc-mpv-command-main-deps.test.ts src/main/runtime/ipc-bridge-actions.test.ts src/main/runtime/ipc-bridge-actions-main-deps.test.ts src/main/runtime/composers/ipc-runtime-composer.test.ts` passed.
Testing: `bun x tsc --noEmit` passed.
Testing: `bun run test:core:src` passed (423 pass, 6 skip, 0 fail).
Docs: no update required because no checked-in docs or help text describe the J/Shift+J OSD output behavior.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Fixed subtitle-cycle OSD handling for the default J/Shift+J keybindings. The IPC mpv command path now supports resolving proxy OSD text asynchronously, and the main-runtime resolver reads mpv `track-list` state so primary and secondary subtitle cycling show human-readable track labels instead of raw `${sid}` / `${secondary-sid}` placeholders.
Also fixed the lower-level mpv OSD transport so placeholder-based `show-text` messages sent through the client API opt into `expand-properties`. That preserves existing template-based OSD messages like subtitle delay and subtitle position without leaking the raw `${...}` syntax.
Added regression coverage for the async proxy OSD path, the placeholder-expansion `showMpvOsdRuntime` path, and the runtime subtitle-track label resolver. Verification run: `bun x tsc --noEmit`; focused mpv/IPC tests; and the maintained `bun run test:core:src` lane (423 pass, 6 skip, 0 fail).
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,61 @@
---
id: TASK-112
title: Address Claude review items on PR 15
status: Done
assignee:
- codex
created_date: '2026-03-08 00:11'
updated_date: '2026-03-08 00:12'
labels:
- pr-review
- ci
dependencies: []
references:
- .github/workflows/release.yml
- .github/workflows/ci.yml
- .gitmodules
- >-
backlog/tasks/task-101 -
Index-AniList-character-alternative-names-in-the-character-dictionary.md
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Review Claude's PR feedback on PR #15, implement only the technically valid fixes on the current branch, and document which comments are non-actionable or already acceptable.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Validated Claude's concrete PR review items against current branch state and repo conventions
- [x] #2 Implemented the accepted fixes with regression coverage or verification where applicable
- [x] #3 Documented which review items are non-blocking or intentionally left unchanged
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Validate each Claude review item against current branch files and repo workflow.
2. Patch release quality-gate to match CI ordering and add explicit typecheck.
3. Remove duplicate .gitmodules stanza and normalize the TASK-101 reference path through Backlog MCP.
4. Run relevant verification for workflow/config metadata changes and record which review items remain non-actionable.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
User asked to address Claude PR comments on PR #15 and assess whether any action items remain. Treat review suggestions skeptically; only fix validated defects.
Validated Claude's five review items. Fixed release workflow ordering/typecheck, removed the duplicate .gitmodules entry, and normalized TASK-101 references to repo-relative paths via Backlog MCP.
Left the vendor/subminer-yomitan branch-pin suggestion unchanged. The committed submodule SHA already controls reproducibility; adding a branch would only affect update ergonomics and was not required to address a concrete defect.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Validated Claude's PR #15 review summary against the current branch and applied the actionable fixes. In `.github/workflows/release.yml`, the release `quality-gate` job now restores the dependency cache before installation, no longer installs twice, and runs `bun run typecheck` before the fast test suite to match CI expectations. In `.gitmodules`, removed the duplicate `vendor/yomitan-jlpt-vocab` stanza with the conflicting duplicate path. Through Backlog MCP, updated `TASK-101` references from an absolute local path to repo-relative paths so the task metadata is portable across contributors.
Verification: `git diff --check`, `git config -f .gitmodules --get-regexp '^submodule\..*\.path$'`, `bun run typecheck`, and `bun run test:fast` all passed. `bun run format:check` still fails on many pre-existing unrelated files already present on the branch, including multiple backlog task files and existing source/docs files; this review patch did not attempt a repo-wide formatting sweep.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,59 @@
---
id: TASK-113
title: Scope make pretty to maintained source files
status: Done
assignee:
- codex
created_date: '2026-03-08 00:20'
updated_date: '2026-03-08 00:22'
labels:
- tooling
- formatting
dependencies: []
references:
- Makefile
- package.json
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Change the `make pretty` workflow so it formats only the maintained source/config files we intentionally keep under Prettier, instead of sweeping backlog/docs/generated content across the whole repository.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 `make pretty` formats only the approved maintained source/config paths
- [x] #2 The allowlist is reusable for check/write flows instead of duplicating path logic
- [x] #3 Verification shows the scoped formatting command targets the intended files without touching backlog or vendored content
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Inspect current Prettier config/ignore behavior and keep the broad repo-wide format command unchanged.
2. Add a reusable scoped Prettier script that targets maintained source/config paths only.
3. Update `make pretty` to call the scoped script.
4. Verify the scoped command resolves only intended files and does not traverse backlog or vendor paths.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
User approved the allowlist approach: keep repo-wide `format` intact, make `make pretty` use a maintained-path formatter scope.
Added `scripts/prettier-scope.sh` as the single allowlist for scoped Prettier paths and wired `format:src` / `format:check:src` to it.
Updated `make pretty` to call `bun run format:src`. Verified with `make -n pretty` and shell tracing that the helper only targets the maintained allowlist and does not traverse `backlog/` or `vendor/`.
Excluded `Makefile` and `.prettierignore` from the allowlist after verification showed Prettier cannot infer parsers for them.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Scoped the repo's day-to-day formatting entrypoint without changing the existing broad repo-wide Prettier scripts. Added `scripts/prettier-scope.sh` as the shared allowlist for maintained source/config paths (`.github`, `build`, `launcher`, `scripts`, `src`, plus selected root JSON config files), added `format:src` and `format:check:src` in `package.json`, and updated `make pretty` to run the scoped formatter.
Verification: `make -n pretty` now resolves to `bun run format:src`. `bash -n scripts/prettier-scope.sh` passed, and shell-traced `bash -x scripts/prettier-scope.sh --check` confirmed the exact allowlist passed to Prettier. `bun run format:check:src` fails only because existing files inside the allowed source scope are not currently formatted; it no longer touches `backlog/` or `vendor/`.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,62 @@
---
id: TASK-114
title: Fix failing CI checks on PR 15
status: Done
assignee:
- codex
created_date: '2026-03-08 00:34'
updated_date: '2026-03-08 00:37'
labels:
- ci
- test
dependencies: []
references:
- src/renderer/subtitle-render.test.ts
- src/renderer/style.css
- .github/workflows/ci.yml
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Investigate the failing GitHub Actions CI run for PR #15 on branch `yomitan-fork`, fix the underlying test or code regression, and verify the affected local test/CI lane passes.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Identified the concrete failing CI job and captured the relevant failure context
- [x] #2 Implemented the minimal code or test change needed to resolve the CI failure
- [x] #3 Verified the affected local test target and the broader fast CI test lane pass
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Inspect the failing GitHub Actions run and confirm the exact failing test/assertion.
2. Reproduce the failing renderer stylesheet test locally and compare the assertion against current CSS.
3. Apply the minimal test or stylesheet fix needed to restore the intended hover/selection behavior.
4. Re-run the targeted renderer test, then re-run `bun run test` to verify the fast CI lane is green.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
GitHub Actions run 22810400921 failed in job build-test-audit, step `Test suite (source)`, with a single failing test: `JLPT CSS rules use underline-only styling in renderer stylesheet` in src/renderer/subtitle-render.test.ts.
Reproduced the failing test locally with `bun test src/renderer/subtitle-render.test.ts`. The failure was a brittle stylesheet assertion, not a renderer behavior regression.
Updated the renderer stylesheet test helper to split selectors safely across `:is(...)` commas and normalize multiline selector whitespace, then switched the failing hover/JLPT assertions to inspect extracted rule blocks instead of matching the entire CSS file text.
Verification passed with `bun test src/renderer/subtitle-render.test.ts` and `bun run test`.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Investigated GitHub Actions CI run `22810400921` for PR #15 and confirmed the only failing job was `build-test-audit`, step `Test suite (source)`, with a single failure in `src/renderer/subtitle-render.test.ts` (`JLPT CSS rules use underline-only styling in renderer stylesheet`).
The renderer CSS itself was still correct; the regression was in the test helper. `extractClassBlock` was splitting selector lists on every comma, which breaks selectors containing `:is(...)`, and the affected assertions fell back to brittle whole-file regex matching against a multiline selector. Fixed the test by teaching the helper to split selectors only at top-level commas, normalizing selector whitespace around multiline `:not(...)` / `:is(...)` clauses, and asserting on extracted rule blocks for the plain-word hover and JLPT-only hover/selection rules.
Verification: `bun test src/renderer/subtitle-render.test.ts` passed, and `bun run test` passed end to end (the same fast lane that failed in CI).
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,59 @@
---
id: TASK-115
title: Refresh subminer-docs contributor docs for current repo workflow
status: Done
assignee:
- codex
created_date: '2026-03-08 00:40'
updated_date: '2026-03-08 00:42'
labels:
- docs
dependencies: []
references:
- ../subminer-docs/development.md
- ../subminer-docs/README.md
- Makefile
- package.json
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Update the sibling `subminer-docs` repo so contributor/development docs match the current SubMiner repo workflow after the docs split and recent tooling changes, including removing stale in-repo docs build steps and documenting the scoped formatting command.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Contributor docs in `subminer-docs` no longer reference stale in-repo docs build commands for the app repo
- [x] #2 Contributor docs mention the current scoped formatting workflow (`make pretty` / `format:src`) where relevant
- [x] #3 Removed stale or no-longer-needed instructions that no longer match the current repo layout
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Inspect `subminer-docs` for contributor/development instructions that drifted after the docs repo split and recent tooling changes.
2. Update contributor docs to remove stale app-repo docs commands and document the current scoped formatting workflow.
3. Verify the modified docs page and build the docs site from the sibling docs repo when local dependencies are available.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Detected concrete doc drift in `subminer-docs/development.md`: stale in-repo docs build commands and no mention of the scoped `make pretty` formatter.
Updated `../subminer-docs/development.md` to remove stale app-repo docs build steps from the local gate, document `make pretty` / `format:check:src`, and point docs-site work to the sibling docs repo explicitly.
Installed docs repo dependencies locally with `bun install` and verified the docs site with `bun run docs:build` in `../subminer-docs`.
Did not change `../subminer-docs/README.md`; it was already accurate for the docs repo itself.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Refreshed the contributor/development docs in the sibling `subminer-docs` repo to match the current SubMiner workflow. In `development.md`, removed the stale app-repo `bun run docs:build` step from the local CI-equivalent gate, added an explicit note to run docs builds from `../subminer-docs` when docs change, documented the scoped formatting workflow (`make pretty` and `bun run format:check:src`), and replaced the old in-repo `make docs*` instructions with the correct sibling-repo `bun run docs:*` commands. Also updated the Makefile reference to include `make pretty` and removed the obsolete `make docs-dev` entry.
Verification: installed docs repo dependencies with `bun install` in `../subminer-docs` and ran `bun run docs:build` successfully. Left `README.md` unchanged because it was already accurate for the standalone docs repo.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,53 @@
---
id: TASK-116
title: Audit branch commits for remaining subminer-docs updates
status: Done
assignee:
- codex
created_date: '2026-03-08 00:46'
updated_date: '2026-03-08 00:48'
labels:
- docs
dependencies: []
references:
- ../subminer-docs/installation.md
- ../subminer-docs/troubleshooting.md
- src/core/services/yomitan-extension-paths.ts
- scripts/build-yomitan.mjs
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Review recent `yomitan-fork` commits against the sibling `subminer-docs` repo, identify any concrete documentation drift that remains after the earlier contributor-doc updates, and patch the docs for behavior/tooling changes that are now outdated or misleading.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Reviewed recent branch commits for user-facing or contributor-facing changes that may require docs updates
- [x] #2 Updated `subminer-docs` pages where branch changes introduced concrete doc drift
- [x] #3 Verified the docs site still builds after the updates
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Review branch commit themes against `subminer-docs` and identify only concrete drift introduced by recent workflow/runtime changes.
2. Patch docs for the Yomitan submodule build workflow, updated source-build prerequisites, and current runtime Yomitan search paths/manual fallback path.
3. Rebuild the docs site to verify the updated pages render cleanly.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Concrete remaining drift after commit audit: installation/development docs still understate the Node/npm + submodule requirements for the Yomitan build flow, and troubleshooting still points at obsolete `vendor/yomitan` / `extensions/yomitan` paths.
Audited branch commits against subminer-docs coverage. Existing docs already cover first-run setup, texthooker startup/annotated websocket config, AniList merged character dictionaries, configurable collapsible sections, and subtitle name highlighting. Patched remaining drift around source-build prerequisites and Yomitan build/install paths in installation.md, development.md, and troubleshooting.md. Verified with `bun run docs:build` in ../subminer-docs.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Audited branch commits for missing documentation updates in ../subminer-docs. Updated installation, development, and troubleshooting docs to match the current Yomitan submodule build flow, source-build prerequisites, and runtime extension search/manual fallback paths. Confirmed other recent branch features were already documented and rebuilt the docs site successfully.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,50 @@
---
id: TASK-85
title: >-
Add launcher dictionary subcommand and initial AniList character dictionary
zip generation
status: Done
assignee: []
created_date: '2026-03-03 08:47'
updated_date: '2026-03-03 08:57'
labels: []
dependencies: []
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Implement initial character dictionary flow: launcher `dictionary` subcommand, app `--dictionary` command, AniList media resolution from current playback, Yomitan zip generation to local file, and local cache to avoid repeated API fetches for same AniList id. Manual Yomitan import path only in this phase.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Launcher supports `dictionary` (and alias) and forwards to app command path.
- [x] #2 App CLI accepts `--dictionary` and dispatches to dictionary runtime command.
- [x] #3 Dictionary command resolves current anime to AniList id, generates Yomitan-compatible zip, and logs output path for manual load.
- [x] #4 Generated dictionaries are cached by AniList id so repeated commands reuse existing zip when available.
- [x] #5 Backlog task is updated with implementation notes and completion summary for this phase.
<!-- AC:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Implemented launcher `dictionary`/`dict` subcommand parsing and normalized args flow (`launcher/config/cli-parser-builder.ts`, `launcher/config/args-normalizer.ts`, `launcher/types.ts`).
Added launcher command dispatch (`launcher/commands/dictionary-command.ts`) and wired `launcher/main.ts` to forward `--dictionary` (plus non-default `--log-level`) to app binary.
Added app CLI flag `--dictionary` in parser/help and startup routing (`src/cli/args.ts`, `src/cli/help.ts`).
Added dictionary runtime service (`src/main/character-dictionary-runtime.ts`) that resolves AniList media id from current playback guess, fetches AniList character edges, builds Yomitan-compatible banks/index, writes zip, and caches by AniList id in user data.
Threaded dictionary generation dependency through CLI runtime/context builders and `src/main.ts` context composition so command executes from launcher/app entrypoints.
Added/updated tests for parser, command modules, launcher main forwarding, CLI command dispatch, and context/deps wiring. Updated docs for launcher/usage command lists to include dictionary subcommand.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Initial phase shipped: `subminer dictionary` now routes to `SubMiner.AppImage --dictionary`, generates a Yomitan-importable character dictionary zip for the current anime (AniList-based), logs zip output path for manual import, and reuses cached zips by AniList id to avoid repeated API fetches.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,64 @@
---
id: TASK-85.1
title: 'Address PR #14 character dictionary review follow-ups'
status: Done
assignee:
- codex
created_date: '2026-03-06 07:48'
updated_date: '2026-03-06 07:56'
labels: []
dependencies: []
references:
- >-
/home/sudacode/projects/japanese/SubMiner/launcher/commands/dictionary-command.ts
- >-
/home/sudacode/projects/japanese/SubMiner/src/main/character-dictionary-runtime.ts
- /home/sudacode/projects/japanese/SubMiner/launcher/types.ts
documentation:
- 'https://docs.anilist.co/guide/rate-limiting'
parent_task_id: TASK-85
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Apply the accepted follow-up fixes from Claude's PR review for the AniList character dictionary work: remove dead launcher code, deduplicate video extension handling where practical, and add explicit pacing for AniList character-page requests / character image downloads so the integration stays within AniList rate-limiting expectations.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Launcher dictionary command no longer contains unreachable dead code after the app handoff.
- [x] #2 Character dictionary runtime no longer maintains a separate ad hoc video extension list when existing shared extension data can be reused safely.
- [x] #3 Character dictionary generation spaces outbound AniList-related requests with explicit named delays, and tests cover the pacing behavior and unchanged command forwarding behavior.
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Add failing tests for dictionary command handoff semantics and dictionary runtime request pacing.
2. Remove unreachable boolean return path from the launcher dictionary command while preserving call sites.
3. Reuse the shared launcher video extension set inside the character dictionary runtime with extname normalization, then add named AniList pacing constants for page fetches and character image downloads.
4. Run targeted tests, then broader relevant test slices, and update acceptance criteria / notes with the validated result.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Added a shared `src/shared/video-extensions.ts` source and rewired both launcher/runtime consumers to remove the duplicated runtime extension list.
Replaced the hardcoded AniList page sleep with a per-generation AniList request pacer (2000ms between API requests) plus 250ms spacing between character image download attempts, including failed image fetches.
Hardened `runDictionaryCommand` so an unexpected return from the `never`-typed app handoff throws immediately instead of silently falling through.
Validated with targeted and adjacent test slices plus `bun run tsc --noEmit`.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Removed the dead post-handoff return from the launcher dictionary command and replaced it with an explicit invariant error if the `never`-typed app handoff ever returns unexpectedly. Extracted video extension data into `src/shared/video-extensions.ts` so the launcher and character dictionary runtime share one source of truth.
Adjusted character dictionary generation to use a per-run AniList request pacer with a conservative 2000ms delay between AniList API calls, and added 250ms spacing between character image download attempts so repeated image fetches are not bursty even when an image URL fails. Added regression coverage for the pacing behavior and the launcher handoff invariant.
Validation: `bun test src/main/character-dictionary-runtime.test.ts`, `bun test launcher/commands/command-modules.test.ts`, `bun test launcher/main.test.ts launcher/parse-args.test.ts src/cli/args.test.ts src/core/services/cli-command.test.ts src/main/runtime/character-dictionary-auto-sync.test.ts`, `bun run tsc --noEmit`.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,54 @@
---
id: TASK-86
title: >-
Require target path for launcher dictionary command and forward dictionary
target to app runtime
status: Done
assignee: []
created_date: '2026-03-03 09:22'
updated_date: '2026-03-03 09:53'
labels: []
dependencies: []
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Change dictionary flow so launcher uses `subminer dictionary <file-or-directory>` and forwards target to app without playback launch. Keep direct app `--dictionary` behavior for in-session/mpv-triggered use, adding optional `--dictionary-target` path override.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Launcher `dictionary`/`dict` requires a target path argument and parses optional log level.
- [x] #2 Launcher forwards target to app as `--dictionary-target <path>` together with `--dictionary`.
- [x] #3 App CLI parses optional `--dictionary-target` and dictionary command passes it into dictionary runtime.
- [x] #4 Dictionary runtime resolves explicit file/directory target for AniList guess flow; falls back to current playback when target is absent.
- [x] #5 Docs/tests are updated for new launcher invocation syntax.
<!-- AC:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Launcher dictionary subcommand now requires a positional target path (`subminer dictionary <path>` / `dict <path>`) via commander argument wiring in `launcher/config/cli-parser-builder.ts`.
Added `dictionaryTarget` flow in launcher normalization/types and path validation (must be existing local file or directory) in `launcher/config/args-normalizer.ts`.
Launcher dictionary command now forwards `--dictionary --dictionary-target <resolved-path>` to app binary in `launcher/commands/dictionary-command.ts`.
App CLI now parses optional `--dictionary-target` in `src/cli/args.ts`; dictionary handler passes target through to runtime in `src/core/services/cli-command.ts`.
Dictionary runtime now resolves explicit target inputs in `src/main/character-dictionary-runtime.ts`: file target uses file path/title; directory target selects first video file recursively (fallback to directory name), and still falls back to current playback when target is absent.
Updated context/dependency pass-through for dictionary target argument (`src/main.ts`, `src/main/runtime/cli-command-context-main-deps.ts`).
Updated tests/docs for new syntax and forwarding behavior (`launcher/main.test.ts`, `launcher/parse-args.test.ts`, `launcher/commands/command-modules.test.ts`, `src/cli/args.test.ts`, `src/cli/help.test.ts`, `docs/usage.md`, `docs/launcher-script.md`).
Follow-up noise reduction: dictionary commands now opt into lightweight startup path by extending `shouldSkipHeavyStartup` in `src/main.ts` to include `initialArgs.dictionary`. This skips heavy app-ready initialization (mpv client creation/background warmups/overlay bootstrap) for dictionary CLI runs.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Launcher dictionary flow now uses explicit targets: run `subminer dictionary <file-or-directory>`. It forwards target to app and performs dictionary generation without depending on currently playing media. Direct app `--dictionary` remains available for in-session/mpv-triggered workflows, with optional `--dictionary-target` override support.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -3,10 +3,10 @@ id: TASK-87
title: >-
Codebase health: harden verification and retire dead architecture identified
in the March 2026 review
status: To Do
status: In Progress
assignee: []
created_date: '2026-03-06 03:19'
updated_date: '2026-03-06 03:20'
updated_date: '2026-03-06 11:11'
labels:
- tech-debt
- tests
@@ -19,9 +19,10 @@ references:
- src/main.ts
- src/anki-integration.ts
- src/core/services/immersion-tracker-service.test.ts
- src/translators/index.ts
- src/subsync/engines.ts
- src/subtitle/pipeline.ts
- src/translators/index.ts
- src/subsync/engines.ts
- src/subtitle/pipeline.ts
- backlog/tasks/task-87.5 - Dead-architecture-cleanup-delete-unused-registry-and-pipeline-modules-that-are-off-the-live-path.md
documentation:
- docs/reports/2026-02-22-task-100-dead-code-report.md
priority: high
@@ -69,3 +70,10 @@ Shared review context to restate in child tasks:
- src/main.ts trips many noUnusedLocals/noUnusedParameters diagnostics.
- src/translators/index.ts, src/subsync/engines.ts, src/subtitle/pipeline.ts, src/tokenizers/index.ts, and src/token-mergers/index.ts appeared unreferenced during review and must be re-verified before deletion.
<!-- SECTION:PLAN:END -->
## Progress Notes
- `TASK-87.5` is complete. The isolated dead registry/pipeline modules were re-verified as off the maintained runtime path and removed.
- Live subtitle tokenization now owns the zero-width separator normalization that previously only existed in the dead subtitle pipeline path, so the cleanup did not drop that behavior.
- Verification completed for the cleanup slice with `bun test src/core/services/tokenizer.test.ts`, `bun test src/dead-architecture-cleanup.test.ts`, `bun test src/core/services/subsync.test.ts src/subsync/utils.test.ts`, `bun run tsc`, and `bun run test:src`.
- Remaining parent-task scope still includes the broader verification hardening, `src/main.ts` dead-symbol cleanup, and `src/anki-integration.ts` decomposition work tracked by the other child tasks.

View File

@@ -3,10 +3,11 @@ id: TASK-87.1
title: >-
Testing workflow: make standard test commands reflect the maintained test
surface
status: To Do
assignee: []
status: Done
assignee:
- OpenCode
created_date: '2026-03-06 03:19'
updated_date: '2026-03-06 03:21'
updated_date: '2026-03-06 08:52'
labels:
- tests
- maintainability
@@ -27,27 +28,41 @@ priority: high
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
The current package scripts hand-enumerate a small subset of test files, which leaves the standard green signal misleading. A local audit found 241 test/type-test files under src/ and launcher/, but only 53 unique files referenced by the standard package.json test scripts. This task should redesign the runnable test matrix so maintained tests are either executed by the standard commands or intentionally excluded through a documented rule, instead of silently drifting out of coverage.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 The repository has a documented and reproducible test matrix for standard development commands, including which suites belong in the default lane versus slower or environment-specific lanes.
- [ ] #2 The standard test entrypoints stop relying on a brittle hand-maintained allowlist for the currently covered unit and integration suites, or an explicit documented mechanism exists that prevents silent omission of new tests.
- [ ] #3 Representative tests that were previously outside the standard lane from src/main/runtime, src/anki-integration, and entry/runtime surfaces are executed by an automated command and included in the documented matrix.
- [ ] #4 Documentation for contributors explains which command to run for fast verification, full verification, and environment-specific verification.
- [x] #1 The repository has a documented and reproducible test matrix for standard development commands, including which suites belong in the default lane versus slower or environment-specific lanes.
- [x] #2 The standard test entrypoints stop relying on a brittle hand-maintained allowlist for the currently covered unit and integration suites, or an explicit documented mechanism exists that prevents silent omission of new tests.
- [x] #3 Representative tests that were previously outside the standard lane from src/main/runtime, src/anki-integration, and entry/runtime surfaces are executed by an automated command and included in the documented matrix.
- [x] #4 Documentation for contributors explains which command to run for fast verification, full verification, and environment-specific verification.
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Inventory the current test surface under src/ and launcher/ and compare it to package.json scripts to classify fast, full, slow, and environment-specific suites.
2. Replace or reduce the brittle hand-maintained allowlist so new maintained tests do not silently miss the standard matrix.
3. Update contributor docs with the intended fast/full/environment-specific commands.
4. Verify the new matrix by running the relevant commands and by demonstrating at least one previously omitted runtime/Anki/entry test now belongs to an automated lane.
1. Update `package.json` to replace the current file-by-file test allowlists with a documented lane matrix: keep `test`/`test:fast` as the quick default lane, add `test:full` for the maintained source test surface, and add `test:env` for slower or environment-specific checks.
2. Use directory-based discovery for maintained suites so new tests under stable surfaces such as `src/main`, `src/anki-integration`, and `launcher` are not silently omitted by default script maintenance.
3. Split environment-specific verification into explicit commands for checks such as launcher smoke/plugin coverage and sqlite-gated tests, instead of leaving them undocumented or mixed into the default signal.
4. Update `README.md` with a contributor-facing testing matrix that explains fast, full, and environment-specific verification, including any prerequisites or expected skip behavior.
5. Verify the matrix by running representative targeted tests plus the documented lane commands, demonstrating that previously omitted entry/runtime, Anki integration, and main runtime tests now belong to automated commands.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Reviewed task context via Backlog MCP plus repo audit. Current package.json test scripts still rely on hand-maintained file allowlists and omit large maintained areas including src/main/runtime, src/anki-integration, and src/main-entry-runtime.test.ts. Preparing an implementation plan and contributor-facing test matrix update before code changes.
Saved detailed implementation plan to docs/plans/2026-03-06-testing-workflow-test-matrix.md and recorded the approved direction in the Backlog task before implementation.
Implemented a lane-based test matrix. Added `scripts/run-test-lane.mjs` so Bun-managed `src/**` and launcher unit lanes discover files automatically while excluding a small explicit Node-only set instead of relying on large hand-maintained allowlists. Added `test:node:compat` for `ipc`, `anki-jimaku-ipc`, `overlay-manager`, `config-validation`, `startup-config`, and `registry` suites, kept `test:env` for launcher smoke/plugin plus SQLite-backed immersion checks, and updated `README.md` with the contributor-facing matrix and exclusions.
Validated the new matrix with `bun run test:fast`, `bun run test:full`, `bun run test:env`, `bun run test:src`, `bun run test:launcher:unit:src`, `bun run test:node:compat`, and targeted `bun test src/core/services/anilist/anilist-updater.test.ts`. Representative previously omitted surfaces now run through automated commands: `src/main-entry-runtime.test.ts` via `test:fast`, `src/anki-integration/anki-connect-proxy.test.ts` via `test:fast`/`test:src`, and `src/main/runtime/registry.test.ts` via `test:node:compat`/`test:full`.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Reworked the repository test matrix so standard commands reflect the maintained test surface without relying on brittle file allowlists. Added automated Bun discovery lanes for Bun-compatible `src/**` and launcher unit suites, a documented Node compatibility lane for Electron/sqlite-sensitive tests, and updated the contributor docs with fast/full/environment-specific guidance plus explicit exclusions. Verified with `bun run test:fast`, `bun run test:full`, and `bun run test:env`, along with the component lanes and targeted regression coverage for the updated AniList guessit test seam.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -3,10 +3,11 @@ id: TASK-87.2
title: >-
Subtitle sync verification: replace the no-op subtitle lane with real
automated coverage
status: To Do
assignee: []
status: Done
assignee:
- Kyle Yasuda
created_date: '2026-03-06 03:19'
updated_date: '2026-03-06 03:21'
updated_date: '2026-03-06 08:06'
labels:
- tests
- subsync
@@ -27,27 +28,57 @@ priority: high
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
SubMiner advertises subtitle syncing with alass and ffsubsync, but the dedicated test:subtitle command currently does not run any tests. There is already lower-level coverage in src/core/services/subsync.test.ts, but the test matrix and contributor-facing commands do not reflect that reality. This task should replace the no-op lane with real verification, align scripts with the existing subsync test surface, and make the user-facing docs honest about how subtitle sync is verified.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 The test:subtitle entrypoint runs real automated verification instead of echoing a placeholder message.
- [ ] #2 The subtitle verification lane covers both alass and ffsubsync behavior, including at least one non-happy-path scenario relevant to current functionality.
- [ ] #3 Contributor-facing documentation points to the real subtitle verification command and no longer implies a dedicated test lane exists when it does not.
- [ ] #4 The resulting verification strategy integrates cleanly with the repository-wide test matrix without duplicating or hiding existing subsync coverage.
- [x] #1 The test:subtitle entrypoint runs real automated verification instead of echoing a placeholder message.
- [x] #2 The subtitle verification lane covers both alass and ffsubsync behavior, including at least one non-happy-path scenario relevant to current functionality.
- [x] #3 Contributor-facing documentation points to the real subtitle verification command and no longer implies a dedicated test lane exists when it does not.
- [x] #4 The resulting verification strategy integrates cleanly with the repository-wide test matrix without duplicating or hiding existing subsync coverage.
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
Plan of record:
1. Audit the existing subtitle-sync test surface, especially src/core/services/subsync.test.ts, and decide whether test:subtitle should reuse or regroup that coverage.
2. Replace the placeholder script with a real automated command and keep the matrix legible alongside TASK-87.1 work.
3. Update README or related docs so the advertised subtitle verification path matches reality.
4. Verify both alass and ffsubsync behavior remain covered by the resulting lane.
1. Replace the placeholder package-script lane with a real `test:subtitle:src` command that runs the maintained subtitle-sync tests directly (`src/core/services/subsync.test.ts` and `src/subsync/utils.test.ts`), and point `test:subtitle` at that lane instead of build+echo behavior.
2. Add one focused ffsubsync non-happy-path test in `src/core/services/subsync.test.ts` so the dedicated lane explicitly covers both engines plus failure propagation relevant to current functionality.
3. Update `README.md` contributor guidance to name `bun run test:subtitle` as the subtitle verification command and explain that it reuses the maintained subsync tests already included in broader core coverage.
4. Verify the final strategy by running `bun run test:subtitle` and `bun run test:core:src` so the dedicated lane stays aligned with the repository-wide matrix instead of creating a divergent hidden suite.
Detailed execution plan saved at `docs/plans/2026-03-06-subtitle-sync-verification.md`.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Reviewed task references and current subtitle verification surface. Existing coverage already lives in `src/core/services/subsync.test.ts` and `src/subsync/utils.test.ts`; `test:subtitle` is still a placeholder build+echo wrapper. The referenced report `docs/reports/2026-02-22-task-100-dead-code-report.md` is not present in the workspace, so planning used the task body plus repository state instead.
Implementation plan written and saved to `docs/plans/2026-03-06-subtitle-sync-verification.md`. Proceeding with execution per the task request.
Replaced the placeholder subtitle lane with `test:subtitle:src` in `package.json`, pointing `test:subtitle` directly at `src/core/services/subsync.test.ts` and `src/subsync/utils.test.ts` instead of build+echo behavior.
Added explicit ffsubsync failure-path coverage in `src/core/services/subsync.test.ts`, asserting non-zero command failures surface detailed `ffsubsync synchronization failed` messaging alongside existing alass coverage.
Updated `README.md` verification guidance to point contributors at `bun run test:subtitle` and explain that the lane reuses the maintained subsync tests already included in `bun run test:core`.
Verification: `bun run test:subtitle` passed (15 tests across 2 files). `bun run test:core:src` also passed (373 pass, 6 skip, 0 fail), confirming the dedicated subtitle lane stays aligned with the broader matrix.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Implemented a real subtitle verification lane by replacing the placeholder `test:subtitle` build+echo flow with a source-level `test:subtitle:src` command that runs the maintained subtitle-sync tests directly from `src/core/services/subsync.test.ts` and `src/subsync/utils.test.ts`. This keeps subtitle verification explicit for contributors while still reusing the same maintained test surface already covered by `test:core`.
Expanded subtitle-sync coverage with an explicit ffsubsync failure-path test so the dedicated lane now exercises both engines plus a user-visible non-happy path. Updated `README.md` to document `bun run test:subtitle` as the contributor-facing subtitle verification command and to explain its relationship to the broader core suite.
Verification run:
- `bun run test:subtitle`
- `bun run test:core:src`
Notes:
- The task reference `docs/reports/2026-02-22-task-100-dead-code-report.md` was not present in the workspace during execution, so implementation used the task body and live repository state as the source of truth.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -3,10 +3,11 @@ id: TASK-87.3
title: >-
Immersion tracking verification: make SQLite-backed persistence tests visible
and reproducible
status: To Do
assignee: []
status: Done
assignee:
- Kyle Yasuda
created_date: '2026-03-06 03:19'
updated_date: '2026-03-06 03:21'
updated_date: '2026-03-06 08:20'
labels:
- tests
- immersion-tracking
@@ -26,27 +27,49 @@ priority: medium
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
The immersion tracker is persistence-heavy, but its SQLite-backed tests are conditionally skipped in the standard Bun run when node:sqlite support is unavailable. That creates a blind spot around session finalization, telemetry persistence, and retention behavior. This task should establish a reliable automated verification path for the database-backed cases and make the prerequisite/runtime behavior explicit to contributors and CI.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 Database-backed immersion tracking tests run in at least one documented automated command that is practical for contributors or CI to execute.
- [ ] #2 If the current runtime cannot execute the SQLite-backed tests, the repository exposes that limitation clearly instead of silently reporting a misleading green result.
- [ ] #3 Contributor-facing documentation explains how to run the immersion tracker verification lane and any environment prerequisites it depends on.
- [ ] #4 The resulting verification covers session persistence or finalization behavior that is not exercised by the pure seam tests alone.
- [x] #1 Database-backed immersion tracking tests run in at least one documented automated command that is practical for contributors or CI to execute.
- [x] #2 If the current runtime cannot execute the SQLite-backed tests, the repository exposes that limitation clearly instead of silently reporting a misleading green result.
- [x] #3 Contributor-facing documentation explains how to run the immersion tracker verification lane and any environment prerequisites it depends on.
- [x] #4 The resulting verification covers session persistence or finalization behavior that is not exercised by the pure seam tests alone.
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
Implementation plan recorded in `docs/plans/2026-03-06-immersion-sqlite-verification.md`.
1. Confirm which SQLite-backed immersion tests are currently skipped and why in the standard Bun environment.
2. Establish a reproducible command or lane for the DB-backed cases, or make the unsupported-runtime limitation explicit and actionable.
3. Document prerequisites and expected behavior for contributors and CI.
4. Verify at least one persistence/finalization path beyond the seam tests is exercised by the new lane.
1. Update `src/core/services/immersion-tracker-service.test.ts` and `src/core/services/immersion-tracker/storage-session.test.ts` so unsupported `node:sqlite` runtimes emit an explicit skip reason instead of a silent top-level skip alias.
2. Add a dedicated `package.json` SQLite verification lane that runs both immersion persistence suites together under a runtime with `node:sqlite` support, likely via built `dist/**` tests executed by Node.
3. Wire that lane into `.github/workflows/ci.yml` and `.github/workflows/release.yml` so automated verification includes a real DB-backed persistence/finalization check.
4. Document the new command, prerequisites, and coverage in `README.md`, including the distinction between Bun's default lane and the reproducible SQLite lane.
5. Validate the final lane by running the dedicated command and confirming it exercises persistence/finalization behavior beyond the seam-only tests.
Execution adjustment: the reproducible lane uses `node --experimental-sqlite --test ...` because Node 22 exposes `node:sqlite` behind the experimental flag. Running that lane also exposed placeholder-count mismatches in `src/core/services/immersion-tracker/storage.ts`, so the final implementation includes a small SQL placeholder fix required for the new cross-runtime verification path.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Confirmed Bun 1.3.5 lacks `node:test` `t.skip()` support, so explicit unsupported-runtime messaging is surfaced with file-level warnings while the SQLite-backed tests remain conditionally skipped.
Added `test:immersion:sqlite:src`, `test:immersion:sqlite:dist`, and `test:immersion:sqlite` scripts; the source lane now prints explicit warnings when `node:sqlite` is unavailable, and the dist lane runs both SQLite-backed immersion suites under Node with `--experimental-sqlite`.
Wired the dist SQLite lane into `.github/workflows/ci.yml` and `.github/workflows/release.yml` after the bundle build, with explicit `actions/setup-node@v4` provisioning for Node 22.12.0.
Fixed SQL prepared-statement placeholder counts in `src/core/services/immersion-tracker/storage.ts`, which the new Node-backed SQLite lane surfaced immediately.
Verification: `bun run test:immersion:sqlite:src` -> pass with explicit unsupported-runtime warnings and 10 skips under Bun 1.3.5; `bun run test:immersion:sqlite` -> pass with 14/14 tests under Node 22.12.0 + `--experimental-sqlite`.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Added an explicit SQLite-backed immersion verification lane and documented it so persistence-heavy coverage is no longer hidden behind Bun-only skips. `package.json` now exposes source and dist SQLite scripts, the source test files print actionable warnings when `node:sqlite` is unavailable, and `README.md` explains the dedicated contributor command plus its Node 22 `--experimental-sqlite` prerequisite.
Automated verification now includes the new dist lane in both `.github/workflows/ci.yml` and `.github/workflows/release.yml` after build output is available. While wiring the reproducible Node lane, it exposed placeholder-count mismatches in `src/core/services/immersion-tracker/storage.ts`; fixing those placeholders makes the SQLite-backed persistence/finalization tests pass cross-runtime, covering session finalization, telemetry persistence, and storage-session write paths.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -3,10 +3,10 @@ id: TASK-87.4
title: >-
Runtime composition root: remove dead symbols and tighten module boundaries in
src/main.ts
status: To Do
status: Done
assignee: []
created_date: '2026-03-06 03:19'
updated_date: '2026-03-06 03:21'
updated_date: '2026-03-06 18:10'
labels:
- tech-debt
- runtime
@@ -36,10 +36,10 @@ A noUnusedLocals/noUnusedParameters compile pass reports a large concentration o
<!-- AC:BEGIN -->
- [ ] #1 src/main.ts no longer emits dead-symbol diagnostics under a noUnusedLocals/noUnusedParameters compile pass for the areas touched by this cleanup.
- [ ] #2 Unused imports, destructured values, and stale locals identified in the current composition root are removed or relocated without behavior changes.
- [ ] #3 The resulting composition root has clearer ownership boundaries for at least one runtime slice that is currently buried in the monolith.
- [ ] #4 Relevant runtime and startup verification commands pass after the cleanup, and any command changes are documented if needed.
- [x] #1 src/main.ts no longer emits dead-symbol diagnostics under a noUnusedLocals/noUnusedParameters compile pass for the areas touched by this cleanup.
- [x] #2 Unused imports, destructured values, and stale locals identified in the current composition root are removed or relocated without behavior changes.
- [x] #3 The resulting composition root has clearer ownership boundaries for at least one runtime slice that is currently buried in the monolith.
- [x] #4 Relevant runtime and startup verification commands pass after the cleanup, and any command changes are documented if needed.
<!-- AC:END -->
## Implementation Plan
@@ -51,3 +51,13 @@ A noUnusedLocals/noUnusedParameters compile pass reports a large concentration o
3. Keep changes behavior-preserving and avoid mixing unrelated cleanup outside src/main.ts unless required to compile.
4. Verify with the updated runtime/startup test commands from TASK-87.1 plus a noUnused compile pass.
<!-- SECTION:PLAN:END -->
## Completion Notes
- Removed the dead import/destructure backlog from `src/main.ts` and deleted stale wrapper seams that no longer owned runtime behavior after the composer/runtime extractions.
- Tightened module boundaries so the composition root depends on the composed/public runtime surfaces it actually uses instead of retaining unused lower-level domain factory symbols.
- Cleared the remaining strict `noUnusedLocals`/`noUnusedParameters` failures in nearby touched files required for a clean repo-wide pass: `launcher/commands/playback-command.ts`, `src/anki-integration.ts`, `src/anki-integration/field-grouping-workflow.ts`, `src/core/services/tokenizer/yomitan-parser-runtime.test.ts`, and `src/main/runtime/composers/composer-contracts.type-test.ts`.
- Verification:
- `bunx tsc --noEmit -p tsconfig.typecheck.json --noUnusedLocals --noUnusedParameters --pretty false`
- `bun run test:fast`
- Commit: `e659b5d` (`refactor(runtime): remove dead symbols from composition roots`)

View File

@@ -3,10 +3,10 @@ id: TASK-87.5
title: >-
Dead architecture cleanup: delete unused registry and pipeline modules that
are off the live path
status: To Do
status: Done
assignee: []
created_date: '2026-03-06 03:20'
updated_date: '2026-03-06 03:21'
updated_date: '2026-03-06 11:05'
labels:
- tech-debt
- dead-code
@@ -40,10 +40,10 @@ The review found several modules that appear self-contained but unused from the
<!-- AC:BEGIN -->
- [ ] #1 Each candidate module identified in the review is either removed as dead code or justified and reconnected to a real supported execution path.
- [ ] #2 Any stale exports, imports, or tests associated with the removed or consolidated modules are cleaned up so the codebase has a single obvious path for the affected behavior.
- [ ] #3 The cleanup does not regress live tokenization or subtitle sync behavior and the relevant verification commands remain green.
- [ ] #4 Contributor-facing documentation or internal notes no longer imply that removed duplicate architecture is part of the current design.
- [x] #1 Each candidate module identified in the review is either removed as dead code or justified and reconnected to a real supported execution path.
- [x] #2 Any stale exports, imports, or tests associated with the removed or consolidated modules are cleaned up so the codebase has a single obvious path for the affected behavior.
- [x] #3 The cleanup does not regress live tokenization or subtitle sync behavior and the relevant verification commands remain green.
- [x] #4 Contributor-facing documentation or internal notes no longer imply that removed duplicate architecture is part of the current design.
<!-- AC:END -->
## Implementation Plan
@@ -55,3 +55,10 @@ The review found several modules that appear self-contained but unused from the
3. Pay special attention to subtitle sync and tokenization surfaces, since duplicate architecture exists near active code.
4. Verify the relevant tokenization and subsync commands/tests still pass and update any stale docs or notes.
<!-- SECTION:PLAN:END -->
## Implementation Notes
- Traced imports from `src/main.ts`, `src/main/runtime/**`, `src/core/services/subsync-runner.ts`, and `src/core/services/tokenizer.ts`; confirmed the candidate registry/pipeline modules were isolated from the maintained runtime path.
- Deleted dead modules: `src/translators/index.ts`, `src/subsync/engines.ts`, `src/subtitle/pipeline.ts`, `src/subtitle/stages/{merge,normalize,tokenize}.ts`, `src/subtitle/stages/normalize.test.ts`, `src/tokenizers/index.ts`, and `src/token-mergers/index.ts`.
- Moved the useful zero-width separator normalization into the live tokenizer path in `src/core/services/tokenizer.ts` and added regression coverage plus a repository-level dead-architecture guard in `src/dead-architecture-cleanup.test.ts`.
- Verified with `bun test src/core/services/tokenizer.test.ts`, `bun test src/dead-architecture-cleanup.test.ts`, `bun test src/core/services/subsync.test.ts src/subsync/utils.test.ts`, `bun run tsc`, and `bun run test:src`.

View File

@@ -3,10 +3,10 @@ id: TASK-87.6
title: >-
Anki integration maintainability: continue decomposing the oversized
orchestration layer
status: To Do
status: Done
assignee: []
created_date: '2026-03-06 03:20'
updated_date: '2026-03-06 03:21'
updated_date: '2026-03-06 09:23'
labels:
- tech-debt
- anki
@@ -40,10 +40,10 @@ src/anki-integration.ts remains an oversized orchestration file even after earli
<!-- AC:BEGIN -->
- [ ] #1 The responsibilities currently concentrated in src/anki-integration.ts are split into clearer modules or services with narrow ownership boundaries.
- [ ] #2 The resulting orchestration surface is materially smaller and easier to review, with at least one mixed-responsibility cluster extracted behind a well-named interface.
- [ ] #3 Existing Anki integration behavior remains covered by automated verification, including note update, field grouping, and proxy-related flows that the refactor touches.
- [ ] #4 Any developer-facing docs or notes needed to understand the new structure are updated in the same task.
- [x] #1 The responsibilities currently concentrated in src/anki-integration.ts are split into clearer modules or services with narrow ownership boundaries.
- [x] #2 The resulting orchestration surface is materially smaller and easier to review, with at least one mixed-responsibility cluster extracted behind a well-named interface.
- [x] #3 Existing Anki integration behavior remains covered by automated verification, including note update, field grouping, and proxy-related flows that the refactor touches.
- [x] #4 Any developer-facing docs or notes needed to understand the new structure are updated in the same task.
<!-- AC:END -->
## Implementation Plan

View File

@@ -0,0 +1,68 @@
---
id: TASK-88
title: >-
Fix second-instance --start handling when overlay runtime is already
initialized
status: Done
assignee:
- codex
created_date: '2026-03-06 07:30'
updated_date: '2026-03-06 07:31'
labels: []
dependencies: []
references:
- /home/sudacode/projects/japanese/SubMiner/src/core/services/cli-command.ts
- >-
/home/sudacode/projects/japanese/SubMiner/src/core/services/cli-command.test.ts
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Restore the CLI command guard so a second-instance `--start` request does not reconnect or reinitialize overlay work when the overlay runtime is already active, while preserving other second-instance commands.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Second-instance `--start` logs that the app is already running when the overlay runtime is initialized.
- [x] #2 Second-instance `--start` does not reconnect the MPV client when the overlay runtime is already initialized.
- [x] #3 Second-instance commands that include non-start actions still execute those actions.
- [x] #4 Regression coverage documents the guarded second-instance behavior.
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Reproduce the failing `handleCliCommand` second-instance `--start` regression in `src/core/services/cli-command.test.ts`.
2. Update `src/core/services/cli-command.ts` so second-instance `--start` is ignored when the overlay runtime is already initialized, while still allowing non-start actions in the same invocation.
3. Run focused CLI command tests, then rerun the core test target if practical, and record acceptance criteria/results.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Reproduced the failing second-instance `--start` regression in `src/core/services/cli-command.test.ts` before editing.
Restored a guard in `src/core/services/cli-command.ts` that ignores second-instance `--start` when the overlay runtime is already initialized, but still allows other flags in the same invocation to run.
Verification: `bun test src/core/services/cli-command.test.ts`, `bun run test:core:src`, and `bun run test` all pass; the six immersion tracker tests remain skipped as before.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Restored the missing second-instance `--start` guard in `src/core/services/cli-command.ts`.
- Added an `ignoreSecondInstanceStart` check so `handleCliCommand` logs `Ignoring --start because SubMiner is already running.` when a second-instance `--start` arrives after the overlay runtime is already initialized.
- Updated start gating so pure duplicate `--start` requests no longer reconnect the MPV client, while combined commands such as `--start --toggle-visible-overlay` still execute their non-start behavior and can connect through those paths.
- Verified the regression with existing CLI command coverage and reran the broader test targets.
Tests run:
- `bun test src/core/services/cli-command.test.ts`
- `bun run test:core:src`
- `bun run test`
Notes:
- The six skipped immersion tracker tests are unchanged from the pre-fix baseline.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,57 @@
---
id: TASK-89
title: Replace per-anime Yomitan imports with merged usage-based character dictionary
status: Done
assignee:
- '@codex'
created_date: '2026-03-06 07:59'
updated_date: '2026-03-06 08:09'
labels:
- character-dictionary
- yomitan
- anilist
dependencies: []
references:
- >-
/home/sudacode/projects/japanese/SubMiner/src/main/character-dictionary-runtime.ts
- >-
/home/sudacode/projects/japanese/SubMiner/src/main/runtime/character-dictionary-auto-sync.ts
- >-
/home/sudacode/projects/japanese/SubMiner/src/config/definitions/defaults-integrations.ts
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Replace TTL-based per-anime character dictionary imports with a single merged Yomitan dictionary built from locally stored per-media metadata snapshots. Retain only most-recently-used anime up to configured maxLoaded, rebuild merged import when retained set membership/order changes, and avoid rebuilding on revisits that do not change the retained set.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Character dictionary retention becomes usage-based rather than TTL-based.
- [x] #2 Only one Yomitan character dictionary import is maintained and updated as a merged dictionary.
- [x] #3 Local storage keeps only metadata/snapshots needed to rebuild the merged dictionary; per-anime source zip cache is removed.
- [x] #4 Merged dictionary rebuild occurs when retained-set membership or order changes, not on unchanged revisits.
- [x] #5 Tests cover merged rebuild, MRU eviction, and no-op revisits.
<!-- AC:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Replaced per-media auto-sync imports with one merged Yomitan dictionary. Added snapshot persistence in `src/main/character-dictionary-runtime.ts` so auto-sync stores normalized per-media term/image metadata locally under `character-dictionaries/snapshots/` and rebuilds `merged.zip` from the MRU retained media ids.
Updated `src/main/runtime/character-dictionary-auto-sync.ts` to keep only MRU `activeMediaIds` plus merged revision/title state, rebuild/import the merged dictionary only when retained-set membership/order changes or the merged import is missing/stale, and skip rebuild on unchanged revisits.
Kept manual `generateForCurrentMedia` support by generating a one-off per-media zip from the stored snapshot, but removed the old per-media zip cache path from auto-sync state.
Updated config/help text to describe usage-based merged retention and mark legacy TTL/eviction knobs as ignored.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Implemented MRU-based merged character dictionary sync. Auto-sync now stores per-media normalized snapshots locally, rebuilds a single merged Yomitan dictionary when the retained anime set/order changes, and keeps `maxLoaded` as the cap on most-recently-used anime included in that merged import. Unchanged revisits no longer rebuild/import the dictionary.
Validation: `bun test src/main/runtime/character-dictionary-auto-sync.test.ts src/main/character-dictionary-runtime.test.ts`, `bun run tsc --noEmit`.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,38 @@
---
id: TASK-90
title: Expand TypeScript typecheck coverage beyond src
status: Done
assignee: []
created_date: '2026-03-06 08:18'
updated_date: '2026-03-06 08:23'
labels:
- tooling
- typescript
dependencies: []
references:
- /home/sudacode/projects/japanese/SubMiner/tsconfig.json
- /home/sudacode/projects/japanese/SubMiner/package.json
- /home/sudacode/projects/japanese/SubMiner/launcher
- /home/sudacode/projects/japanese/SubMiner/scripts
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Bring all repository TypeScript entrypoints outside src/ into the enforced typecheck gate so CI and local checks cover launcher/ and script files, then resolve any surfaced diagnostics.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 TypeScript typecheck covers repository TypeScript entrypoints outside src/ that should be maintained in this repo, including launcher/ and script files.
- [x] #2 The enforced typecheck command used by CI and local development passes with the expanded coverage.
- [x] #3 Any diagnostics surfaced by the expanded coverage are fixed without weakening existing strictness for src/.
- [x] #4 Relevant documentation or command wiring is updated if the typecheck entrypoint changes.
<!-- AC:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Added a dedicated repo-wide typecheck config at tsconfig.typecheck.json and wired package.json/CI to use `bun run typecheck` for launcher and scripts coverage without changing the existing src build config. Fixed the strict-null/indexing diagnostics surfaced in launcher/* and scripts/*, keeping src strictness intact. Verified with `bun run typecheck`, `bun run tsc --noEmit`, and `bun run test:launcher:src` (47 passing, plugin start gate OK).
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,35 @@
---
id: TASK-91
title: >-
Keep unsupported subtitle characters visible while excluding them from token
hover
status: Done
assignee: []
created_date: '2026-03-06 08:29'
updated_date: '2026-03-06 08:32'
labels:
- bug
- tokenizer
- renderer
dependencies: []
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Tokenizer/rendering bug: symbols and other unsupported characters with no lookup result are removed from the rendered subtitle line after tokenization, causing the displayed line to diverge from the source subtitle text. Update rendering so unsupported spans remain visible as plain text but are not tokenized/hoverable, and add regression coverage.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Subtitle rendering preserves unsupported symbols and special characters from the original line.
- [x] #2 Unsupported symbols and special characters do not create interactive token hover targets.
- [x] #3 Regression tests cover a mixed line containing tokenizable text plus unsupported characters.
<!-- AC:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Updated tokenized subtitle rendering to preserve unsupported punctuation and symbol spans as plain text while keeping only matched tokens interactive. Added renderer and alignment regression coverage for mixed lines so hover offsets stay correct after non-tokenizable characters remain visible.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,34 @@
---
id: TASK-92
title: Fix merged Yomitan headword selection for katakana subtitle tokens
status: Done
assignee: []
created_date: '2026-03-06 08:43'
updated_date: '2026-03-06 08:43'
labels:
- bug
- tokenizer
- yomitan
dependencies: []
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Tokenizer/parser-selection bug: when a scanning-parser line is merged from multiple segments, the merged token currently keeps the first segment headword even if a later segment provides the full dictionary-backed term. This truncates katakana names such as バニール to バニ in the lookup payload and prevents correct dictionary matching. Also align kana classification so the prolonged sound mark is treated as kana in tokenizer heuristics.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Merged scanning-parser tokens prefer a full cross-segment headword when one segment expands to the full term.
- [x] #2 Standalone later segment headwords do not override the primary token headword in normal content-word + auxiliary merges.
- [x] #3 Katakana prolonged sound mark is treated as kana in tokenizer heuristics.
- [x] #4 Regression tests cover the merged katakana headword case.
<!-- AC:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Adjusted merged scanning-parser headword selection so later segments only override the first headword when they provide an expanded cross-segment dictionary term, which fixes truncated katakana lookups like バニール -> バニ. Also updated kana classification to include the katakana prolonged sound mark and added regression coverage for both the expanded-headword case and the normal content-word-plus-auxiliary case.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,34 @@
---
id: TASK-93
title: Replace subtitle tokenizer with left-to-right Yomitan scanning parser
status: Done
assignee: []
created_date: '2026-03-06 09:02'
updated_date: '2026-03-06 09:14'
labels:
- tokenizer
- yomitan
- refactor
dependencies: []
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Replace the current parseText candidate-selection tokenizer with a GSM-style left-to-right Yomitan scanning tokenizer for all subtitles. Preserve downstream token contracts for rendering, JLPT/frequency/N+1 annotation, and MeCab enrichment while improving full-term matching for names and katakana compounds.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Subtitle tokenization uses a left-to-right Yomitan scanning strategy instead of parseText candidate selection.
- [x] #2 Token surfaces, readings, headwords, and offsets remain compatible with existing renderer and annotation stages.
- [x] #3 Known problematic name cases such as カズマ and バニール resolve to full-token dictionary matches when Yomitan can match them.
- [x] #4 Regression tests cover left-to-right exact-match scanning, unmatched text handling, and downstream tokenizeSubtitle integration.
<!-- AC:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Replaced the live subtitle tokenization path with a left-to-right Yomitan `termsFind` scanner that greedily advances through the normalized subtitle text, preserving downstream `MergedToken` contracts for renderer, MeCab enrichment, JLPT, frequency, and N+1 annotation. Added runtime and integration coverage for exact-match scanning plus name cases like カズマ and kept compatibility fallback handling for older mocked parseText-style test payloads.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,40 @@
---
id: TASK-94
title: Add kana aliases for AniList character dictionary entries
status: Done
assignee: []
created_date: '2026-03-06 09:20'
updated_date: '2026-03-06 09:23'
labels:
- dictionary
- tokenizer
- anilist
dependencies: []
references:
- >-
/home/sudacode/projects/japanese/SubMiner/src/main/character-dictionary-runtime.ts
- >-
/home/sudacode/projects/japanese/SubMiner/src/main/character-dictionary-runtime.test.ts
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Generate katakana/hiragana-friendly aliases from AniList romanized character names so subtitle katakana names like カズマ match character dictionary entries even when AniList native name is kanji.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 AniList character dictionary generation adds kana aliases for romanized names when native name is not already kana-only
- [x] #2 Generated dictionary entries allow katakana subtitle names like カズマ to resolve against a kanji-native AniList character entry
- [x] #3 Regression tests cover alias generation and resulting term bank output
<!-- AC:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Added katakana aliases synthesized from AniList romanized character names during character dictionary generation, so kanji-native entries such as 佐藤和真 / Satou Kazuma now also emit terms like カズマ and サトウカズマ with hiragana readings. Added regression coverage verifying generated term-bank output for the Konosuba case.
Verified with `bun test src/main/character-dictionary-runtime.test.ts` and `bun run tsc --noEmit`.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,39 @@
---
id: TASK-95
title: Invalidate old character dictionary snapshots after kana alias schema change
status: Done
assignee: []
created_date: '2026-03-06 09:25'
updated_date: '2026-03-06 09:28'
labels:
- dictionary
- cache
dependencies: []
references:
- >-
/home/sudacode/projects/japanese/SubMiner/src/main/character-dictionary-runtime.ts
- >-
/home/sudacode/projects/japanese/SubMiner/src/main/character-dictionary-runtime.test.ts
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Bump character dictionary snapshot format/version so cached AniList snapshots created before kana alias generation are rebuilt automatically on next auto-sync or generation run.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Old cached character dictionary snapshots are treated as invalid after the schema/version bump
- [x] #2 Current snapshot generation tests cover rebuild behavior across version mismatch
- [x] #3 No manual cache deletion is required for users to pick up kana alias term generation
<!-- AC:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Bumped the character dictionary snapshot format version so cached AniList snapshots created before kana alias generation are automatically treated as stale and rebuilt. Added regression coverage that seeds an older-format snapshot and verifies `getOrCreateCurrentSnapshot` fetches fresh data and overwrites the stale cache.
Verified with `bun test src/main/character-dictionary-runtime.test.ts` and `bun run tsc --noEmit`.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,47 @@
---
id: TASK-96
title: Add launcher/app log progress for anime dictionary generate/update flow
status: Done
assignee: []
created_date: '2026-03-06 09:30'
updated_date: '2026-03-06 09:33'
labels:
- logging
- dictionary
- launcher
dependencies: []
references:
- >-
/home/sudacode/projects/japanese/SubMiner/src/main/character-dictionary-runtime.ts
- /home/sudacode/projects/japanese/SubMiner/src/core/services/cli-command.ts
- >-
/home/sudacode/projects/japanese/SubMiner/launcher/commands/playback-command.ts
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Surface user-visible log progress while the anime character dictionary is being generated or refreshed so launcher/app output no longer appears hung before mpv launches.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Dictionary generation logs a start/progress message before the first AniList/network/cache work begins.
- [x] #2 Dictionary refresh/update path logs progress messages during the wait before completion.
- [x] #3 Regression coverage verifies the new progress logging behavior.
<!-- AC:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Added progress logging to character dictionary generation at anime resolution, AniList match, snapshot miss, character-page fetch, image download start, and ZIP build stages.
Added auto-sync progress logging at snapshot sync start, active AniList set selection, merged rebuild, Yomitan import, and settings application stages.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Character dictionary generation/update no longer appears hung before mpv resumes. Added runtime progress logs for anime resolution, AniList lookup, snapshot rebuild, image-download phase, ZIP build, and auto-sync merged-dictionary import/settings stages. Added regression coverage in the runtime and auto-sync test suites and verified with focused Bun tests.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,39 @@
---
id: TASK-97
title: Add configurable character-name token highlighting
status: Done
assignee: []
created_date: '2026-03-06 10:15'
updated_date: '2026-03-06 10:15'
labels:
- subtitle
- dictionary
- renderer
dependencies: []
references:
- /home/sudacode/projects/japanese/SubMiner/src/core/services/tokenizer.ts
- >-
/home/sudacode/projects/japanese/SubMiner/src/core/services/tokenizer/yomitan-parser-runtime.ts
- /home/sudacode/projects/japanese/SubMiner/src/renderer/subtitle-render.ts
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Color subtitle tokens that match entries from the SubMiner character dictionary, with a configurable default color and a config toggle that disables both rendering and name-match detection work.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Tokens matched from the SubMiner character dictionary receive dedicated renderer styling.
- [x] #2 `subtitleStyle.nameMatchEnabled` disables name-match detection work when false.
- [x] #3 `subtitleStyle.nameMatchColor` overrides the default `#f5bde6`.
- [x] #4 Regression coverage verifies config parsing, tokenizer propagation, scanner gating, and renderer class/CSS behavior.
<!-- AC:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Added configurable character-name token highlighting with default color `#f5bde6` and config gate `subtitleStyle.nameMatchEnabled`. When enabled, left-to-right Yomitan scanning tags tokens whose winning dictionary entry comes from the SubMiner character dictionary; when disabled, the tokenizer skips that metadata work and the renderer suppresses name-match styling. Added focused regression tests for config parsing, main-deps wiring, Yomitan scan gating, token propagation, renderer classes, and CSS behavior.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,60 @@
---
id: TASK-98
title: Gate subtitle character-name highlighting on character dictionary enablement
status: Done
assignee:
- codex
created_date: '2026-03-07 00:54'
updated_date: '2026-03-07 00:56'
labels:
- subtitle
- character-dictionary
dependencies: []
references:
- /Users/sudacode/projects/japanese/SubMiner/src/main.ts
- /Users/sudacode/projects/japanese/SubMiner/src/core/services/tokenizer.ts
- >-
/Users/sudacode/projects/japanese/SubMiner/src/config/definitions/defaults-subtitle.ts
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Ensure subtitle tokenization and other annotations continue to work, but character-name lookup/highlighting is disabled whenever the AniList character dictionary feature is disabled. This avoids unnecessary name-match processing when the backing dictionary is unavailable.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 When anilist.characterDictionary.enabled is false, subtitle tokenization does not request character-name match metadata or highlight character names.
- [x] #2 When anilist.characterDictionary.enabled is true and subtitleStyle.nameMatchEnabled is true, existing character-name matching behavior remains enabled.
- [x] #3 Subtitle tokenization, JLPT, frequency, and other non-name annotation behavior remain unchanged when character dictionaries are disabled.
- [x] #4 Automated tests cover the runtime gating behavior.
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Add a failing test in `src/main/runtime/subtitle-tokenization-main-deps.test.ts` proving name-match enablement resolves to false when `anilist.characterDictionary.enabled` is false even if `subtitleStyle.nameMatchEnabled` is true.
2. Update `src/main/runtime/subtitle-tokenization-main-deps.ts` and `src/main.ts` so subtitle tokenization only enables name matching when both the subtitle setting and the character dictionary setting are enabled.
3. Run focused Bun tests for the updated runtime deps and subtitle processing seams.
4. If verification stays green, check off acceptance criteria and record the result.
Implementation plan saved in `docs/plans/2026-03-06-character-name-gating.md`.
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Created plan doc `docs/plans/2026-03-06-character-name-gating.md` after user approved the narrow runtime-gating approach. Proceeding with TDD from the subtitle tokenization main-deps seam.
Implemented the gate at the subtitle tokenization runtime-deps boundary so `getNameMatchEnabled` is false unless both `subtitleStyle.nameMatchEnabled` and `anilist.characterDictionary.enabled` are true.
Verification: `bun test src/main/runtime/subtitle-tokenization-main-deps.test.ts`, `bun test src/core/services/subtitle-processing-controller.test.ts`, `bun run typecheck`.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Character-name lookup/highlighting is now suppressed when the AniList character dictionary is disabled, while subtitle tokenization and other annotation paths remain active. Added focused runtime-deps coverage and wired the main runtime to pass the character-dictionary enabled flag into subtitle tokenization.
<!-- SECTION:FINAL_SUMMARY:END -->

View File

@@ -0,0 +1,38 @@
---
id: TASK-99
title: Add configurable character dictionary collapsible section open states
status: Done
assignee: []
created_date: '2026-03-07 00:00'
updated_date: '2026-03-07 00:00'
labels:
- dictionary
- config
references:
- /home/sudacode/projects/japanese/SubMiner/src/main/character-dictionary-runtime.ts
- /home/sudacode/projects/japanese/SubMiner/src/config/resolve/integrations.ts
- /home/sudacode/projects/japanese/SubMiner/src/config/definitions/defaults-integrations.ts
priority: medium
dependencies: []
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Add per-section config for character dictionary collapsible glossary sections so Description, Character Information, and Voiced by can each default open or closed independently. Default all sections closed.
<!-- SECTION:DESCRIPTION:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Config supports `anilist.characterDictionary.collapsibleSections.description`.
- [x] #2 Config supports `anilist.characterDictionary.collapsibleSections.characterInformation`.
- [x] #3 Config supports `anilist.characterDictionary.collapsibleSections.voicedBy`.
- [x] #4 Default config keeps all generated character dictionary collapsible sections closed.
- [x] #5 Regression coverage verifies config parsing/warnings and generated glossary `details.open` behavior.
<!-- AC:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Added per-section open-state config under `anilist.characterDictionary.collapsibleSections` for `description`, `characterInformation`, and `voicedBy`, all defaulting to `false`. Wired the glossary generator to read those settings so generated `details.open` matches config, and added regression coverage for defaults, parsing/warnings, registry exposure, and runtime glossary output.
<!-- SECTION:FINAL_SUMMARY:END -->