refactor(config): slim resolver facade and expand regression lanes

Collapse src/config/resolve.ts into an orchestrated pipeline over domain modules, wire launcher regression coverage into test scripts, and sync backlog/subagent tracking artifacts for completed TASK-74/TASK-96/TASK-98 follow-up planning.
This commit is contained in:
2026-02-21 13:37:38 -08:00
parent 2b77ab2406
commit 10b94ce889
19 changed files with 827 additions and 1415 deletions

View File

@@ -0,0 +1,50 @@
---
id: TASK-100
title: Run post-refactor dead code prune and cleanup
status: To Do
assignee: []
created_date: '2026-02-21 07:15'
updated_date: '2026-02-21 07:15'
labels:
- cleanup
- maintainability
- refactor
dependencies:
- TASK-96
- TASK-97
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Major refactors likely left unused exports/helpers and stale compatibility code. Perform a deliberate dead-code sweep with regression safety.
<!-- SECTION:DESCRIPTION:END -->
## Action Steps
<!-- SECTION:PLAN:BEGIN -->
1. Run unused-export scans (`ts-prune` or equivalent) and collect candidate list.
2. Manually verify each candidate to avoid false positives from dynamic loading/IPC wiring.
3. Remove or inline confirmed dead code; simplify import surfaces/barrels accordingly.
4. Delete stale helper paths retained only for pre-composer wiring.
5. Add or update regression tests where removal could alter behavior.
6. Run verification gate: `bun run build`, `bun run test:config:dist`, `bun run test:core:dist`.
7. Publish cleanup report: removed files/exports, kept exceptions, and rationale.
<!-- SECTION:PLAN:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 Confirmed dead code removed without behavior regressions.
- [ ] #2 Remaining flagged candidates either resolved or documented with justification.
- [ ] #3 Build and core/config suites pass after cleanup.
- [ ] #4 Import graph complexity reduced (fewer exports/entrypoints where applicable).
<!-- AC:END -->
## Definition of Done
<!-- DOD:BEGIN -->
- [ ] #1 Dead-code report attached in task notes.
- [ ] #2 Regression test updates included for risky removals.
- [ ] #3 Verification gate commands complete successfully.
<!-- DOD:END -->

View File

@@ -0,0 +1,52 @@
---
id: TASK-101
title: Consolidate architecture docs and archive task noise
status: To Do
assignee: []
created_date: '2026-02-21 07:15'
updated_date: '2026-02-21 07:15'
labels:
- documentation
- maintainability
dependencies:
- TASK-96
- TASK-97
- TASK-98
- TASK-99
- TASK-100
priority: low
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Architecture guidance is fragmented across long-lived docs and task notes. Consolidate canonical runtime architecture docs and move stale/noisy task-level detail to archive.
<!-- SECTION:DESCRIPTION:END -->
## Action Steps
<!-- SECTION:PLAN:BEGIN -->
1. Audit architecture and development docs for duplicated runtime composition guidance.
2. Select one canonical architecture section for runtime composition and dependency boundaries.
3. Refactor duplicate sections to brief pointers to the canonical section.
4. Move stale task-evidence prose from persistent docs to backlog/archive where appropriate.
5. Add a compact architecture diagram and update links in contributor docs.
6. Validate docs build and link integrity.
7. Record what moved and why in task notes for traceability.
<!-- SECTION:PLAN:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 Runtime composition guidance exists in one canonical location.
- [ ] #2 Duplicated/stale architecture notes removed from long-lived docs.
- [ ] #3 Task evidence retained in backlog/archive, not lost.
- [ ] #4 Docs build and links pass after consolidation.
<!-- AC:END -->
## Definition of Done
<!-- DOD:BEGIN -->
- [ ] #1 Change log of moved/removed doc sections included in task notes.
- [ ] #2 `bun run docs:build` passes.
- [ ] #3 Contributor-facing entry points link to canonical architecture section.
<!-- DOD:END -->

View File

@@ -1,10 +1,10 @@
---
id: TASK-74
title: Add launcher regression tests for config discovery and command branching
status: To Do
status: Done
assignee: []
created_date: '2026-02-18 11:35'
updated_date: '2026-02-18 11:35'
updated_date: '2026-02-21 20:21'
labels:
- launcher
- tests
@@ -35,14 +35,13 @@ Launcher currently has no direct test coverage for config discovery behavior and
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 Launcher config discovery paths are regression-tested
- [ ] #2 Core launcher command branches are regression-tested
- [ ] #3 Tests run in CI/local test gate without external process dependencies
- [x] #1 Launcher config discovery paths are regression-tested
- [x] #2 Core launcher command branches are regression-tested
- [x] #3 Tests run in CI/local test gate without external process dependencies
<!-- AC:END -->
## Definition of Done
<!-- DOD:BEGIN -->
- [ ] #1 Launcher tests included in standard test workflow
- [ ] #2 Documented how to run launcher tests locally
- [x] #1 Launcher tests included in standard test workflow
- [x] #2 Documented how to run launcher tests locally
<!-- DOD:END -->

View File

@@ -0,0 +1,96 @@
---
id: TASK-96
title: Split config resolve into domain modules
status: Done
assignee:
- '@codex-task96-config-resolve'
created_date: '2026-02-21 07:15'
updated_date: '2026-02-21 20:10'
labels:
- architecture
- refactor
- maintainability
dependencies:
- TASK-85
priority: high
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
`src/config/resolve.ts` remains oversized and mixes unrelated concerns. Split into domain-focused modules while preserving behavior and public API.
<!-- SECTION:DESCRIPTION:END -->
## Action Steps
<!-- SECTION:PLAN:BEGIN -->
1. Capture baseline: LOC, exported symbols, and current test coverage touching config resolution.
2. Create domain modules under `src/config/resolve/`:
- `env-paths.ts`
- `subtitle-style.ts`
- `integrations.ts`
- `validation-errors.ts`
3. Keep `src/config/resolve.ts` as thin orchestrator/barrel that composes domain resolvers.
4. Add seam tests per new module; keep existing config suite green.
5. Verify no call-site changes required outside config service layer.
6. Run verification gate: `bun run build`, `bun run test:config:dist`, `bun run check:file-budgets`.
7. Record before/after LOC and ownership map in task notes.
<!-- SECTION:PLAN:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 `src/config/resolve.ts` reduced to orchestration/barrel role.
- [x] #2 New domain modules exist with clear single-responsibility boundaries.
- [x] #3 Existing config behavior preserved with passing tests.
- [x] #4 New seam tests cover domain-specific resolution logic.
- [x] #5 File-budget report shows measurable reduction in hotspot file size.
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
Execution plan (2026-02-21 codex refresh):
1. Capture baseline metrics (`wc -l src/config/resolve.ts`, `bun run check:file-budgets`) and keep evidence in notes.
2. Ensure seam test coverage exists for extracted domain modules (`src/config/resolve/anki-connect.test.ts`, `src/config/resolve/subtitle-style.test.ts`, `src/config/resolve/jellyfin.test.ts`) and wire config test scripts so these tests run in src + dist lanes.
3. Reduce `src/config/resolve.ts` to orchestration facade by composing extracted modules in stable order:
- `createResolveContext`
- `applyTopLevelConfig`
- `applyCoreDomainConfig`
- `applySubtitleDomainConfig`
- `applyIntegrationConfig`
- `applyImmersionTrackingConfig`
- `applyAnkiConnectResolution`
4. Verify no behavior drift with required gates: `bun run build`, `bun run test:config:dist`, `bun run check:file-budgets`.
5. Record before/after LOC + budget results and finalize acceptance criteria / DoD in task metadata (no commit).
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
[baseline] `wc -l src/config/resolve.ts` => 1414 LOC.
[baseline] `bun run check:file-budgets` reports `src/config/resolve.ts: 1415 LOC` and 17 files over 500 LOC.
[implementation] Reduced `src/config/resolve.ts` to orchestration facade that now composes extracted domain modules in stable order: top-level -> core -> subtitle -> integrations -> immersion-tracking -> anki-connect.
[implementation] Wired config seam tests into official config lanes by updating `package.json` scripts `test:config:src` and `test:config:dist` to include `src/config/resolve/{anki-connect,subtitle-style,jellyfin}.test.ts` (and compiled dist equivalents).
[verification] `bun run build && bun run test:config:dist && bun run check:file-budgets` passed. Config dist lane now runs 48 tests including seam tests; all passing.
[metrics] LOC before/after: `wc -l src/config/resolve.ts` baseline 1414 -> final 33.
[metrics] Budget before/after: baseline report showed 18 files over 500 LOC including `src/config/resolve.ts: 1415 LOC`; final report shows 17 files over 500 LOC and `src/config/resolve.ts` no longer over budget.
<!-- SECTION:NOTES:END -->
## Final Summary
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
Refactored config resolution into domain-focused modules and reduced `src/config/resolve.ts` to a thin orchestration facade while preserving behavior. Added/wired resolver seam tests into src+dist config test lanes and verified with required gates (`build`, `test:config:dist`, `check:file-budgets`) plus before/after LOC and budget evidence.
<!-- SECTION:FINAL_SUMMARY:END -->
## Definition of Done
<!-- DOD:BEGIN -->
- [x] #1 Baseline and final LOC metrics recorded in Implementation Notes.
- [x] #2 `bun run build` and `bun run test:config:dist` pass.
- [x] #3 `bun run check:file-budgets` completed and attached in notes.
<!-- DOD:END -->

View File

@@ -0,0 +1,87 @@
---
id: TASK-98
title: Shift core tests to source level and trim dist coupling
status: In Progress
assignee:
- opencode
created_date: '2026-02-21 07:15'
updated_date: '2026-02-21 09:56'
labels:
- testing
- maintainability
- developer-experience
dependencies:
- TASK-95
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Core test flow is heavily coupled to `dist/` artifacts, increasing cycle time and reducing test clarity. Move the majority of tests to source-level execution while retaining minimal dist smoke coverage.
<!-- SECTION:DESCRIPTION:END -->
## Action Steps
<!-- SECTION:PLAN:BEGIN -->
1. Catalog current test commands and classify tests: source-level candidates vs required dist smoke tests.
2. Introduce/standardize source-level test runners for core suites.
3. Migrate high-volume `dist/core/services/*` tests to source-level equivalents.
4. Keep a small dist smoke suite for packaging/runtime sanity only.
5. Update `package.json` scripts and CI workflow steps to use new split.
6. Capture timing comparison (before/after) for local and CI runs.
7. Run verification gate for both lanes: source-level tests + dist smoke tests.
<!-- SECTION:PLAN:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [x] #1 Majority of core regression tests run from source-level entrypoints.
- [x] #2 Dist suite reduced to explicit smoke scope with documented purpose.
- [x] #3 CI still validates packaged/runtime assumptions via smoke lane.
- [x] #4 Total default test cycle time improves measurably.
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1) Baseline current dist-coupled matrix and capture `time bun run test:fast` before timing.
2) Update `package.json` scripts so default regression lanes (`test:config`, `test:core`, `test:fast`) run source-level tests (`bun test src/...`) instead of `dist` artifacts.
3) Introduce explicit `test:smoke:dist` command for minimal compiled/runtime smoke checks; keep launcher smoke behavior intact.
4) Update `.github/workflows/ci.yml` and `.github/workflows/release.yml` to run source lane as primary + dist smoke lane as secondary.
5) Document command matrix in `docs/development.md`, rerun timing commands, capture before/after delta in task notes, then finalize AC/DoD.
Detailed execution plan: `docs/plans/2026-02-21-task-98-source-tests-dist-smoke-split.md`
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Execution updates (2026-02-21):
- Default regression lane moved to source entrypoints in `package.json`:
- `test:config` -> `test:config:src` (`bun test src/config/config.test.ts src/config/path-resolution.test.ts`)
- `test:core` -> `test:core:src` (`bun test` over curated core list)
- `test:fast` -> source config + source core
- Dist coupling trimmed to explicit smoke lane:
- `test:smoke:dist` -> `test:config:smoke:dist` + `test:core:smoke:dist`
- Smoke scope validates compiled/runtime assumptions across config, IPC/electron interop (`overlay-manager`, `anilist-token-store`), startup, renderer, main URL guard, and x11 tracker.
- CI/release wiring updated:
- `.github/workflows/ci.yml`: `test:fast` (source) before build; `test:smoke:dist` after build.
- `.github/workflows/release.yml` quality-gate: source tests, build, dist smoke.
- Docs updated in `docs/development.md` with source-vs-dist command matrix and smoke rationale.
- Timing evidence (repro commands):
- Before: `time bun run test:fast` (dist-coupled) => `1.438 total`
- After: `time bun run test:fast` (source lane) => `1.017 total`
- Delta: ~`0.421s` faster (~29.3% reduction).
- Dist smoke timing: `time bun run test:smoke:dist` => `0.206 total`.
- Verification:
- PASS: `bun run test:config:src && bun run test:core:src`
- PASS: `bun run test:smoke:dist`
- BLOCKED (pre-existing unrelated workspace errors): `bun run build` currently fails in `src/main.ts` and `src/main/runtime/composers/mpv-runtime-composer.test.ts` from in-flight TASK-96/97 changes present in working tree; not introduced by TASK-98 edits.
<!-- SECTION:NOTES:END -->
## Definition of Done
<!-- DOD:BEGIN -->
- [x] #1 Test command matrix documented in task notes.
- [ ] #2 CI config updated and passing with new source/dist split.
- [x] #3 Performance delta captured with reproducible timing commands.
<!-- DOD:END -->

View File

@@ -0,0 +1,49 @@
---
id: TASK-99
title: Expand maintainability guardrails and runtime cycle checks
status: To Do
assignee: []
created_date: '2026-02-21 07:15'
updated_date: '2026-02-21 07:15'
labels:
- quality
- architecture
- ci
dependencies:
- TASK-96
- TASK-97
priority: medium
---
## Description
<!-- SECTION:DESCRIPTION:BEGIN -->
Current guardrails cover `main.ts` fan-in and broad file budgets. Add targeted checks for newly extracted hotspots and runtime import-cycle detection.
<!-- SECTION:DESCRIPTION:END -->
## Action Steps
<!-- SECTION:PLAN:BEGIN -->
1. Rebaseline file-size budgets for known hotspots (including post-TASK-96 files).
2. Extend `check:file-budgets` config to enforce thresholds on new modules and key test files.
3. Add runtime dependency cycle detection for `src/main/runtime/**` (script + CI hook).
4. Wire new checks into local gate commands and CI workflow.
5. Add failure guidance output so contributors can remediate quickly.
6. Run gates on current branch and capture pass/fail evidence in task notes.
<!-- SECTION:PLAN:END -->
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 Budget thresholds include new hotspot modules and prevent silent growth.
- [ ] #2 Runtime cycle check detects at least one injected fixture cycle in tests.
- [ ] #3 CI runs both checks and fails fast on violations.
- [ ] #4 Contributor guidance exists for fixing guardrail failures.
<!-- AC:END -->
## Definition of Done
<!-- DOD:BEGIN -->
- [ ] #1 Guardrail scripts and CI wiring merged with passing checks.
- [ ] #2 Documentation updated with local commands and expected outputs.
- [ ] #3 Baseline numbers recorded to justify thresholds.
<!-- DOD:END -->

View File

@@ -95,3 +95,11 @@ Use `subminer <subcommand> -h` for command-specific help.
- Default log level is `info`
- `--background` mode defaults to `warn` unless `--log-level` is explicitly set
- `--dev` / `--debug` control app behavior, not logging verbosity — use `--log-level` for that
## Testing
Run launcher regression tests (config discovery + command branching):
```bash
bun run test:launcher
```

View File

@@ -43,3 +43,4 @@ Read first. Keep concise.
| `codex-task74-launcher-tests-20260221T201635Z-10i6` | `codex-task74-launcher-tests` | `Implement TASK-74 launcher regression tests for config discovery + command branching end-to-end` | `done` | `docs/subagents/agents/codex-task74-launcher-tests-20260221T201635Z-10i6.md` | `2026-02-21T20:20:52Z` |
| `opencode-task76-anki-workflows-20260221T201659Z-r4p1` | `opencode-task76-anki-workflows` | `Execute TASK-76 decompose anki-integration orchestrator into workflow services via plan-first workflow` | `done` | `docs/subagents/agents/opencode-task76-anki-workflows-20260221T201659Z-r4p1.md` | `2026-02-21T21:17:28Z` |
| `opencode-task76-doc-boundaries-20260221T203558Z-h7q4` | `opencode-task76-doc-boundaries` | `Update Anki integration docs with post-decomposition ownership boundaries for TASK-76` | `done` | `docs/subagents/agents/opencode-task76-doc-boundaries-20260221T203558Z-h7q4.md` | `2026-02-21T20:36:55Z` |
| `codex-docs-unpushed-review-20260221T213707Z-lyej` | `codex-docs-unpushed-review` | `Review unpushed commits for docs drift; patch docs to reflect current code/state` | `in_progress` | `docs/subagents/agents/codex-docs-unpushed-review-20260221T213707Z-lyej.md` | `2026-02-21T21:37:07Z` |

View File

@@ -0,0 +1,32 @@
# Agent Session: codex-docs-unpushed-review-20260221T213707Z-lyej
- alias: `codex-docs-unpushed-review`
- mission: `Review unpushed commits for docs drift; patch docs to reflect current code/state`
- status: `in_progress`
- started_utc: `2026-02-21T21:37:07Z`
- heartbeat_minutes: `5`
## Intent
- audit `origin/main..HEAD` commit set
- compare code/runtime/test-script changes against user-facing docs
- patch stale docs only; avoid unrelated code churn
## Planned Files
- `docs/development.md` (verify test lane + build guidance)
- `docs/architecture.md` (verify runtime composer/domain registry narrative)
- `docs/configuration.md` (verify config discovery semantics if needed)
- `docs/launcher-script.md` (verify launcher test coverage + mpv socket behavior)
- `docs/subagents/INDEX.md` (own row heartbeat/status only)
- `docs/subagents/collaboration.md` (cross-agent notes/conflicts)
## Assumptions
- scope = unpushed commits (`origin/main..HEAD`) + doc drift for current working state
- Backlog MCP not initialized (`backlog://init-required`); no enforceable ticket link in this run
- existing dirty workspace includes other agents/user edits; do not revert or normalize unrelated files
## Log
- `2026-02-21T21:37:07Z` start; loaded subagent index/collaboration/backlog state; collecting unpushed commit/doc impact matrix.

View File

@@ -8,6 +8,8 @@
- heartbeat_minutes: `5`
## Current Work (newest first)
- [2026-02-21T07:16:33Z] handoff: added cleanup backlog set TASK-96..TASK-101 with implementation steps, AC, DoD, and dependency chain.
- [2026-02-21T07:15:00Z] intent: user requested backlog build-out; creating follow-on cleanup tasks with implementation steps and completion goals.
- [2026-02-21T06:25:27Z] handoff: review complete; no blocking/important defects found in refactor + launcher-workflow enforcement diff; targeted guardrails/tests passed.
- [2026-02-21T06:25:27Z] test: `bun run check:main-fanin` and `bun run test:core:dist` passed on current tree.
- [2026-02-21T06:24:30Z] progress: audited diffs in `.github/workflows/{ci,release}.yml`, `scripts/verify-generated-launcher.sh`, docs/task updates; validated launcher verifier behavior.
@@ -22,6 +24,18 @@
- `docs/development.md` (reviewed)
- `docs/installation.md` (reviewed)
- `backlog/tasks/task-85 - Refactor-large-files-for-maintainability-and-readability.md` (reviewed)
- `backlog/tasks/task-96 - Split-config-resolve-into-domain-modules.md` (planned)
- `backlog/tasks/task-97 - Normalize-runtime-composer-contracts.md` (planned)
- `backlog/tasks/task-98 - Shift-core-tests-to-source-level-and-trim-dist-coupling.md` (planned)
- `backlog/tasks/task-99 - Expand-maintainability-guardrails-and-runtime-cycle-checks.md` (planned)
- `backlog/tasks/task-100 - Run-post-refactor-dead-code-prune-and-cleanup.md` (planned)
- `backlog/tasks/task-101 - Consolidate-architecture-docs-and-archive-task-noise.md` (planned)
- `backlog/tasks/task-96 - Split-config-resolve-into-domain-modules.md` (added)
- `backlog/tasks/task-97 - Normalize-runtime-composer-contracts.md` (added)
- `backlog/tasks/task-98 - Shift-core-tests-to-source-level-and-trim-dist-coupling.md` (added)
- `backlog/tasks/task-99 - Expand-maintainability-guardrails-and-runtime-cycle-checks.md` (added)
- `backlog/tasks/task-100 - Run-post-refactor-dead-code-prune-and-cleanup.md` (added)
- `backlog/tasks/task-101 - Consolidate-architecture-docs-and-archive-task-noise.md` (added)
## Assumptions
- User asks for review only; no code changes requested.
@@ -31,4 +45,4 @@
- None.
## Next Step
- Await user direction (fixes optional; no required corrections identified).
- Await prioritization/execution order from user.

View File

@@ -0,0 +1,51 @@
# Agent Log: codex-task74-launcher-tests-20260221T201635Z-10i6
- alias: `codex-task74-launcher-tests`
- mission: `Implement TASK-74 launcher regression tests for config discovery + command branching end-to-end`
- status: `done`
- last_update_utc: `2026-02-21T20:20:52Z`
## Intent
- add launcher regression coverage for config discovery matrix + command branching paths
- keep tests deterministic via fs/process/spawn mocks; no external process deps
- wire tests into existing launcher/core test workflow
## Planned Files
- `launcher/main.ts`
- `launcher/main.test.ts` (new)
- `launcher/config.test.ts` (extend if needed)
- `package.json` (only if test command wiring needed)
- `backlog/tasks/task-74 - Add-launcher-regression-tests-for-config-discovery-and-command-branching.md` (status/checklists evidence)
## Assumptions
- existing test runner: `bun test`
- task expects launcher tests in repo test gates (likely `test:launcher`)
- no behavior change needed unless tests expose branch gaps
## Progress
- `2026-02-21T20:16:35Z` started; protocol + task context loaded
- `2026-02-21T20:19:30Z` added `launcher/main.test.ts` regression suite (config discovery + command branching via child-process harness + fake app binary capture)
- `2026-02-21T20:20:02Z` wired launcher tests into scripts (`test:launcher:src`, `test:launcher`) and included launcher tests in `test:core:src`
- `2026-02-21T20:20:20Z` documented launcher test command in `docs/launcher-script.md`
- `2026-02-21T20:20:52Z` verification complete:
- `bun run test:launcher`
- `bun run test:core:src`
- both green
## Files Touched
- `launcher/main.test.ts` (new)
- `package.json`
- `docs/launcher-script.md`
- `backlog/tasks/task-74 - Add-launcher-regression-tests-for-config-discovery-and-command-branching.md`
- `docs/subagents/INDEX.md`
- `docs/subagents/agents/codex-task74-launcher-tests-20260221T201635Z-10i6.md`
## Handoff
- task status moved to `Done` in backlog markdown with AC/DoD checked
- no blockers

View File

@@ -0,0 +1,35 @@
# Agent: `codex-task96-config-resolve-20260221T110058Z-k7m2`
- alias: `codex-task96-config-resolve`
- mission: `Execute TASK-96 by splitting src/config/resolve.ts into domain modules without behavior drift`
- status: `done`
- branch: `main`
- started_at: `2026-02-21T11:00:58Z`
- heartbeat_minutes: `5`
## Current Work (newest first)
- [2026-02-21T20:10:43Z] complete: reduced `src/config/resolve.ts` to 33 LOC orchestration facade over extracted domain modules; updated config test scripts to include resolve seam tests in src+dist lanes; ran required gates (`build`, `test:config:dist`, `check:file-budgets`) all green.
- [2026-02-21T20:10:43Z] backlog: finalized `TASK-96` as Done with AC/DoD checked and metrics evidence (LOC 1414 -> 33; budget over-limit files 18 -> 17).
- [2026-02-21T11:00:58Z] intent: load TASK-96 from Backlog MCP, draft execution plan with writing-plans skill, execute with executing-plans skill, no commit.
- [2026-02-21T11:00:58Z] context: read subagent index/collaboration + prior opencode TASK-96 planning handoff.
## Files Touched
- `docs/subagents/INDEX.md`
- `docs/subagents/collaboration.md`
- `docs/subagents/agents/codex-task96-config-resolve-20260221T110058Z-k7m2.md`
- `src/config/resolve.ts`
- `package.json`
## Assumptions
- User request grants consent to execute TASK-96 on current branch and run required verification gates.
## Open Questions / Blockers
- None.
## Next Step
- Handoff complete; await user review or next task.

View File

@@ -0,0 +1,43 @@
# Agent: `opencode-task76-anki-workflows-20260221T201659Z-r4p1`
- alias: `opencode-task76-anki-workflows`
- mission: `Execute TASK-76 decompose anki-integration orchestrator into workflow services end-to-end without commit`
- status: `done`
- branch: `main`
- started_at: `2026-02-21T20:16:59Z`
- heartbeat_minutes: `5`
## Current Work (newest first)
- [2026-02-21T21:17:28Z] test: reran `bun run build && node --test dist/anki-integration.test.js dist/anki-integration/note-update-workflow.test.js dist/anki-integration/field-grouping-workflow.test.js` and `bun run build && bun run test:core:dist`; all pass.
- [2026-02-21T21:16:18Z] handoff: TASK-76 complete; extracted note-update and field-grouping workflow services, added workflow seam tests, updated Anki ownership docs, verified build + anki suites + core dist gate, and marked backlog task Done.
- [2026-02-21T20:40:00Z] progress: implemented `NoteUpdateWorkflow` and `FieldGroupingWorkflow` extraction; rewired `AnkiIntegration` to delegate `processNewCard` and grouping handlers.
- [2026-02-21T20:30:00Z] progress: created TASK-76 execution plan and moved backlog task to In Progress with baseline metrics.
- [2026-02-21T20:16:59Z] intent: load Backlog TASK-76 context, draft plan via writing-plans skill, execute via executing-plans with parallel subagents when safe.
## Files Touched
- `docs/subagents/INDEX.md`
- `docs/subagents/agents/opencode-task76-anki-workflows-20260221T201659Z-r4p1.md`
- `docs/subagents/collaboration.md`
- `docs/plans/2026-02-21-task-76-anki-workflow-services-plan.md`
- `src/anki-integration.ts`
- `src/anki-integration/note-update-workflow.ts`
- `src/anki-integration/note-update-workflow.test.ts`
- `src/anki-integration/field-grouping-workflow.ts`
- `src/anki-integration/field-grouping-workflow.test.ts`
- `docs/anki-integration.md`
- `backlog/tasks/task-76 - Decompose-anki-integration-orchestrator-into-workflow-services.md` (via MCP edits)
## Assumptions
- Backlog MCP is initialized and TASK-76 exists.
- Existing in-flight changes in repo are unrelated unless overlap found in `src/anki-integration.ts` and `src/anki-integration/*`.
## Open Questions / Blockers
- None.
## Next Step
- Await user review or follow-up requests (no commit performed).

View File

@@ -0,0 +1,34 @@
# Agent: `opencode-task76-doc-boundaries-20260221T203558Z-h7q4`
- alias: `opencode-task76-doc-boundaries`
- mission: `Update Anki integration docs with post-decomposition ownership boundaries for TASK-76`
- status: `done`
- branch: `main`
- started_at: `2026-02-21T20:35:58Z`
- heartbeat_minutes: `5`
## Current Work (newest first)
- [2026-02-21T20:35:58Z] intent: update `docs/anki-integration.md` with concise ownership boundaries for facade/workflows/collaborators after TASK-76 decomposition.
- [2026-02-21T20:36:55Z] progress: added ownership-boundary section in `docs/anki-integration.md` covering facade, note-update workflow, field-grouping workflow, and existing collaborators.
- [2026-02-21T20:36:55Z] handoff: docs-only change complete; no tests run per request.
## Files Touched
- `docs/subagents/INDEX.md`
- `docs/subagents/collaboration.md`
- `docs/subagents/agents/opencode-task76-doc-boundaries-20260221T203558Z-h7q4.md`
- `docs/anki-integration.md`
## Assumptions
- TASK-76 remains the source of truth for this documentation update.
- Requested scope is docs-only; no source-code behavior changes.
## Open Questions / Blockers
- None.
## Next Step
- Await user review; adjust wording if they want tighter or more code-facing phrasing.

View File

@@ -0,0 +1,30 @@
# Agent: `opencode-task96-config-resolve-20260221T094119Z-mbfo`
- alias: `opencode-task96-config-resolve`
- mission: `Execute TASK-96 by splitting src/config/resolve.ts into domain modules without behavior drift`
- status: `planning`
- branch: `main`
- started_at: `2026-02-21T09:41:19Z`
- heartbeat_minutes: `5`
## Current Work (newest first)
- [2026-02-21T09:41:19Z] intent: load TASK-96 from Backlog MCP, draft implementation plan via writing-plans skill, then execute via executing-plans skill (no commit).
- [2026-02-21T09:41:19Z] progress: read workflow overview + subagent protocol docs; creating session record and index row before code edits.
## Files Touched
- `docs/subagents/INDEX.md`
- `docs/subagents/agents/opencode-task96-config-resolve-20260221T094119Z-mbfo.md`
## Assumptions
- User request to execute TASK-96 implies consent to edit current branch state and run required verification commands.
## Open Questions / Blockers
- None.
## Next Step
- Load TASK-96 code context and write plan file under `docs/plans/`.

View File

@@ -48,3 +48,4 @@ Shared notes. Append-only.
- [2026-02-21T20:16:59Z] [opencode-task76-anki-workflows-20260221T201659Z-r4p1|opencode-task76-anki-workflows] starting TASK-76 via Backlog MCP + writing-plans/executing-plans; likely scope `src/anki-integration.ts` + new `src/anki-integration/*` workflow services, with overlap checks before edits.
- [2026-02-21T20:35:58Z] [opencode-task76-doc-boundaries-20260221T203558Z-h7q4|opencode-task76-doc-boundaries] overlap note: TASK-76 already has an active planning agent; this pass is docs-only (`docs/anki-integration.md`) to capture ownership boundaries after workflow decomposition.
- [2026-02-21T21:16:18Z] [opencode-task76-anki-workflows-20260221T201659Z-r4p1|opencode-task76-anki-workflows] completed TASK-76: extracted `note-update-workflow` + `field-grouping-workflow` services, delegated facade hotpaths in `src/anki-integration.ts`, added focused workflow seam tests, docs ownership boundaries updated, `bun run build && bun run test:core:dist` green, and backlog TASK-76 marked Done.
- [2026-02-21T21:37:07Z] [codex-docs-unpushed-review-20260221T213707Z-lyej|codex-docs-unpushed-review] starting docs-drift audit for unpushed commits (`origin/main..HEAD`); scope docs alignment only, no behavior/code rewrites.

209
launcher/main.test.ts Normal file
View File

@@ -0,0 +1,209 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { spawnSync } from 'node:child_process';
import { resolveConfigFilePath } from '../src/config/path-resolution.js';
type RunResult = {
status: number | null;
stdout: string;
stderr: string;
};
function withTempDir<T>(fn: (dir: string) => T): T {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'subminer-launcher-test-'));
try {
return fn(dir);
} finally {
fs.rmSync(dir, { recursive: true, force: true });
}
}
function runLauncher(argv: string[], env: NodeJS.ProcessEnv): RunResult {
const result = spawnSync(process.execPath, ['run', path.join(process.cwd(), 'launcher/main.ts'), ...argv], {
env,
encoding: 'utf8',
});
return {
status: result.status,
stdout: result.stdout || '',
stderr: result.stderr || '',
};
}
function makeTestEnv(homeDir: string, xdgConfigHome: string): NodeJS.ProcessEnv {
return {
...process.env,
HOME: homeDir,
XDG_CONFIG_HOME: xdgConfigHome,
};
}
test('config path uses XDG_CONFIG_HOME override', () => {
withTempDir((root) => {
const xdgConfigHome = path.join(root, 'xdg');
const homeDir = path.join(root, 'home');
fs.mkdirSync(path.join(xdgConfigHome, 'SubMiner'), { recursive: true });
fs.writeFileSync(path.join(xdgConfigHome, 'SubMiner', 'config.json'), '{"source":"xdg"}');
const result = runLauncher(['config', 'path'], makeTestEnv(homeDir, xdgConfigHome));
assert.equal(result.status, 0);
assert.equal(result.stdout.trim(), path.join(xdgConfigHome, 'SubMiner', 'config.json'));
});
});
test('config discovery ignores lowercase subminer candidate', () => {
const homeDir = '/home/tester';
const xdgConfigHome = '/tmp/xdg-config';
const expected = path.join(xdgConfigHome, 'SubMiner', 'config.jsonc');
const foundPaths = new Set([path.join(xdgConfigHome, 'subminer', 'config.json')]);
const resolved = resolveConfigFilePath({
xdgConfigHome,
homeDir,
existsSync: (candidate) => foundPaths.has(path.normalize(candidate)),
});
assert.equal(resolved, expected);
});
test('config path prefers jsonc over json for same directory', () => {
withTempDir((root) => {
const homeDir = path.join(root, 'home');
const xdgConfigHome = path.join(root, 'xdg');
fs.mkdirSync(path.join(xdgConfigHome, 'SubMiner'), { recursive: true });
fs.writeFileSync(path.join(xdgConfigHome, 'SubMiner', 'config.json'), '{"format":"json"}');
fs.writeFileSync(path.join(xdgConfigHome, 'SubMiner', 'config.jsonc'), '{"format":"jsonc"}');
const result = runLauncher(['config', 'path'], makeTestEnv(homeDir, xdgConfigHome));
assert.equal(result.status, 0);
assert.equal(result.stdout.trim(), path.join(xdgConfigHome, 'SubMiner', 'config.jsonc'));
});
});
test('config show prints config body and appends trailing newline', () => {
withTempDir((root) => {
const homeDir = path.join(root, 'home');
const xdgConfigHome = path.join(root, 'xdg');
fs.mkdirSync(path.join(xdgConfigHome, 'SubMiner'), { recursive: true });
fs.writeFileSync(path.join(xdgConfigHome, 'SubMiner', 'config.jsonc'), '{"logLevel":"debug"}');
const result = runLauncher(['config', 'show'], makeTestEnv(homeDir, xdgConfigHome));
assert.equal(result.status, 0);
assert.equal(result.stdout, '{"logLevel":"debug"}\n');
});
});
test('mpv socket command returns socket path from plugin runtime config', () => {
withTempDir((root) => {
const homeDir = path.join(root, 'home');
const xdgConfigHome = path.join(root, 'xdg');
const expectedSocket = path.join(root, 'custom', 'subminer.sock');
fs.mkdirSync(path.join(xdgConfigHome, 'mpv', 'script-opts'), { recursive: true });
fs.writeFileSync(
path.join(xdgConfigHome, 'mpv', 'script-opts', 'subminer.conf'),
`socket_path=${expectedSocket}\n`,
);
const result = runLauncher(['mpv', 'socket'], makeTestEnv(homeDir, xdgConfigHome));
assert.equal(result.status, 0);
assert.equal(result.stdout.trim(), expectedSocket);
});
});
test('mpv status exits non-zero when socket is not ready', () => {
withTempDir((root) => {
const homeDir = path.join(root, 'home');
const xdgConfigHome = path.join(root, 'xdg');
const result = runLauncher(['mpv', 'status'], makeTestEnv(homeDir, xdgConfigHome));
assert.equal(result.status, 1);
assert.match(result.stdout, /socket not ready/i);
});
});
test('doctor reports checks and exits non-zero without hard dependencies', () => {
withTempDir((root) => {
const homeDir = path.join(root, 'home');
const xdgConfigHome = path.join(root, 'xdg');
const env = {
...makeTestEnv(homeDir, xdgConfigHome),
PATH: '',
};
const result = runLauncher(['doctor'], env);
assert.equal(result.status, 1);
assert.match(result.stdout, /\[doctor\] app binary:/);
assert.match(result.stdout, /\[doctor\] mpv:/);
assert.match(result.stdout, /\[doctor\] config:/);
});
});
test('jellyfin discovery routes to app --start with log-level forwarding', () => {
withTempDir((root) => {
const homeDir = path.join(root, 'home');
const xdgConfigHome = path.join(root, 'xdg');
const appPath = path.join(root, 'fake-subminer.sh');
const capturePath = path.join(root, 'captured-args.txt');
fs.writeFileSync(
appPath,
'#!/bin/sh\nif [ -n "$SUBMINER_TEST_CAPTURE" ]; then printf "%s\\n" "$@" > "$SUBMINER_TEST_CAPTURE"; fi\nexit 0\n',
);
fs.chmodSync(appPath, 0o755);
const env = {
...makeTestEnv(homeDir, xdgConfigHome),
SUBMINER_APPIMAGE_PATH: appPath,
SUBMINER_TEST_CAPTURE: capturePath,
};
const result = runLauncher(['jellyfin', 'discovery', '--log-level', 'debug'], env);
assert.equal(result.status, 0);
assert.equal(fs.readFileSync(capturePath, 'utf8'), '--start\n--log-level\ndebug\n');
});
});
test('jellyfin login routes credentials to app command', () => {
withTempDir((root) => {
const homeDir = path.join(root, 'home');
const xdgConfigHome = path.join(root, 'xdg');
const appPath = path.join(root, 'fake-subminer.sh');
const capturePath = path.join(root, 'captured-args.txt');
fs.writeFileSync(
appPath,
'#!/bin/sh\nif [ -n "$SUBMINER_TEST_CAPTURE" ]; then printf "%s\\n" "$@" > "$SUBMINER_TEST_CAPTURE"; fi\nexit 0\n',
);
fs.chmodSync(appPath, 0o755);
const env = {
...makeTestEnv(homeDir, xdgConfigHome),
SUBMINER_APPIMAGE_PATH: appPath,
SUBMINER_TEST_CAPTURE: capturePath,
};
const result = runLauncher(
[
'jellyfin',
'login',
'--server',
'https://jf.example.test',
'--username',
'alice',
'--password',
'secret',
],
env,
);
assert.equal(result.status, 0);
assert.equal(
fs.readFileSync(capturePath, 'utf8'),
'--jellyfin-login\n--jellyfin-server\nhttps://jf.example.test\n--jellyfin-username\nalice\n--jellyfin-password\nsecret\n',
);
});
});

View File

@@ -20,16 +20,18 @@
"check:file-budgets:strict": "bun run scripts/check-file-budgets.ts --strict",
"check:main-fanin": "bun run scripts/check-main-runtime-fanin.ts",
"check:main-fanin:strict": "bun run scripts/check-main-runtime-fanin.ts --strict",
"test:config:src": "bun test src/config/config.test.ts src/config/path-resolution.test.ts",
"test:config:dist": "node --test dist/config/config.test.js dist/config/path-resolution.test.js",
"test:config:src": "bun test src/config/config.test.ts src/config/path-resolution.test.ts src/config/resolve/anki-connect.test.ts src/config/resolve/subtitle-style.test.ts src/config/resolve/jellyfin.test.ts",
"test:config:dist": "node --test dist/config/config.test.js dist/config/path-resolution.test.js dist/config/resolve/anki-connect.test.js dist/config/resolve/subtitle-style.test.js dist/config/resolve/jellyfin.test.js",
"test:config:smoke:dist": "node --test dist/config/path-resolution.test.js",
"test:core:src": "bun test src/cli/args.test.ts src/cli/help.test.ts src/core/services/cli-command.test.ts src/core/services/field-grouping-overlay.test.ts src/core/services/numeric-shortcut-session.test.ts src/core/services/secondary-subtitle.test.ts src/core/services/mpv-render-metrics.test.ts src/core/services/overlay-content-measurement.test.ts src/core/services/mpv-control.test.ts src/core/services/mpv.test.ts src/core/services/runtime-options-ipc.test.ts src/core/services/runtime-config.test.ts src/core/services/config-hot-reload.test.ts src/core/services/tokenizer.test.ts src/core/services/subsync.test.ts src/core/services/overlay-bridge.test.ts src/core/services/overlay-shortcut-handler.test.ts src/core/services/mining.test.ts src/core/services/anki-jimaku.test.ts src/core/services/jellyfin.test.ts src/core/services/jellyfin-remote.test.ts src/core/services/immersion-tracker-service.test.ts src/core/services/app-ready.test.ts src/core/services/startup-bootstrap.test.ts src/core/services/subtitle-processing-controller.test.ts src/core/services/anilist/anilist-update-queue.test.ts src/renderer/error-recovery.test.ts src/subsync/utils.test.ts src/main/anilist-url-guard.test.ts src/window-trackers/x11-tracker.test.ts",
"test:launcher:src": "bun test launcher/config.test.ts launcher/parse-args.test.ts launcher/main.test.ts",
"test:core:src": "bun test src/cli/args.test.ts src/cli/help.test.ts src/core/services/cli-command.test.ts src/core/services/field-grouping-overlay.test.ts src/core/services/numeric-shortcut-session.test.ts src/core/services/secondary-subtitle.test.ts src/core/services/mpv-render-metrics.test.ts src/core/services/overlay-content-measurement.test.ts src/core/services/mpv-control.test.ts src/core/services/mpv.test.ts src/core/services/runtime-options-ipc.test.ts src/core/services/runtime-config.test.ts src/core/services/config-hot-reload.test.ts src/core/services/tokenizer.test.ts src/core/services/subsync.test.ts src/core/services/overlay-bridge.test.ts src/core/services/overlay-shortcut-handler.test.ts src/core/services/mining.test.ts src/core/services/anki-jimaku.test.ts src/core/services/jellyfin.test.ts src/core/services/jellyfin-remote.test.ts src/core/services/immersion-tracker-service.test.ts src/core/services/app-ready.test.ts src/core/services/startup-bootstrap.test.ts src/core/services/subtitle-processing-controller.test.ts src/core/services/anilist/anilist-update-queue.test.ts src/renderer/error-recovery.test.ts src/subsync/utils.test.ts src/main/anilist-url-guard.test.ts src/window-trackers/x11-tracker.test.ts launcher/config.test.ts launcher/parse-args.test.ts launcher/main.test.ts",
"test:core:dist": "node --test dist/cli/args.test.js dist/cli/help.test.js dist/core/services/cli-command.test.js dist/core/services/ipc.test.js dist/core/services/field-grouping-overlay.test.js dist/core/services/numeric-shortcut-session.test.js dist/core/services/secondary-subtitle.test.js dist/core/services/mpv-render-metrics.test.js dist/core/services/overlay-content-measurement.test.js dist/core/services/mpv-control.test.js dist/core/services/mpv.test.js dist/core/services/runtime-options-ipc.test.js dist/core/services/runtime-config.test.js dist/core/services/config-hot-reload.test.js dist/core/services/tokenizer.test.js dist/core/services/subsync.test.js dist/core/services/overlay-bridge.test.js dist/core/services/overlay-manager.test.js dist/core/services/overlay-shortcut-handler.test.js dist/core/services/mining.test.js dist/core/services/anki-jimaku.test.js dist/core/services/jellyfin.test.js dist/core/services/jellyfin-remote.test.js dist/core/services/immersion-tracker-service.test.js dist/core/services/app-ready.test.js dist/core/services/startup-bootstrap.test.js dist/core/services/subtitle-processing-controller.test.js dist/core/services/anilist/anilist-token-store.test.js dist/core/services/anilist/anilist-update-queue.test.js dist/renderer/error-recovery.test.js dist/subsync/utils.test.js dist/main/anilist-url-guard.test.js dist/window-trackers/x11-tracker.test.js",
"test:core:smoke:dist": "node --test dist/cli/help.test.js dist/core/services/runtime-config.test.js dist/core/services/ipc.test.js dist/core/services/overlay-manager.test.js dist/core/services/anilist/anilist-token-store.test.js dist/core/services/startup-bootstrap.test.js dist/renderer/error-recovery.test.js dist/main/anilist-url-guard.test.js dist/window-trackers/x11-tracker.test.js",
"test:smoke:dist": "bun run test:config:smoke:dist && bun run test:core:smoke:dist",
"test:subtitle:dist": "echo \"Subtitle tests are currently not configured\"",
"test": "bun run test:config && bun run test:core",
"test:config": "bun run test:config:src",
"test:launcher": "bun run test:launcher:src",
"test:core": "bun run test:core:src",
"test:subtitle": "bun run build && bun run test:subtitle:dist",
"test:fast": "bun run test:config:src && bun run test:core:src",

File diff suppressed because it is too large Load Diff