From 9384d67b8e0fef3fdd0d4bb423a9f7b7ac8b2c92 Mon Sep 17 00:00:00 2001 From: sudacode Date: Thu, 19 Feb 2026 00:49:23 -0800 Subject: [PATCH] chore(workflow): sync backlog state and subagent coordination Capture backlog task lifecycle updates, archive TASK-34, and add planning artifacts for recent config work. Update docs sweep scripts and AGENTS guidance to use sharded docs/subagents coordination metadata. --- AGENTS.md | 43 ++++++ ...local-files-and-Jellyfin-ready-metadata.md | 0 ...dly-config-validation-errors-on-startup.md | 54 +++++-- ...ion-validation-and-deprecation-handling.md | 46 ++++-- ...pendent-keybindings-behind-config-flags.md | 25 ++++ config.example.jsonc | 80 ++++------ docs/public/config.example.jsonc | 80 ++++------ docs/subagents/INDEX.md | 7 + docs/subagents/agents/TEMPLATE.md | 27 ++++ docs/subagents/agents/codex-main.md | 64 ++++++++ docs/subagents/archive/.gitkeep | 1 + docs/subagents/collaboration.md | 6 + scripts/docs-sweep-once.sh | 140 +++++++++++++++--- scripts/docs-sweep-watch.sh | 50 +++++-- 14 files changed, 466 insertions(+), 157 deletions(-) rename backlog/{ => archive}/tasks/task-34 - Add-in-app-episode-browser-CtrlE-for-local-files-and-Jellyfin-ready-metadata.md (100%) create mode 100644 backlog/tasks/task-84 - Gate-feature-dependent-keybindings-behind-config-flags.md create mode 100644 docs/subagents/INDEX.md create mode 100644 docs/subagents/agents/TEMPLATE.md create mode 100644 docs/subagents/agents/codex-main.md create mode 100644 docs/subagents/archive/.gitkeep create mode 100644 docs/subagents/collaboration.md diff --git a/AGENTS.md b/AGENTS.md index 1b3f6b5..a15d50f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -27,3 +27,46 @@ You MUST read the overview resource to understand the complete workflow. The inf + +## Subagent Coordination Protocol (`docs/subagents/`) + +Purpose: multi-agent coordination across runs; single-agent continuity during long runs. + +Layout: +- `docs/subagents/INDEX.md` (active agents table) +- `docs/subagents/collaboration.md` (shared notes) +- `docs/subagents/agents/.md` (one file per agent) +- `docs/subagents/archive//` (archived histories) + +Required behavior (all agents): + +1. At run start, read in order: + - `docs/subagents/INDEX.md` + - `docs/subagents/collaboration.md` + - your own file: `docs/subagents/agents/.md` +2. Identify self by stable `agent_id` (runner/env-provided). If missing, create own file from template. +3. Maintain `alias` (short human-readable label) + `mission` (one-line focus). +4. Before coding: + - record intent, planned files, assumptions in your own file. +5. During run: + - update on phase changes (plan -> edit -> test -> handoff), + - heartbeat at least every `HEARTBEAT_MINUTES` (default 5), + - update your own row in `INDEX.md` (`status`, `last_update_utc`), + - append cross-agent notes in `collaboration.md` when needed. +6. Write limits: + - MAY edit own file. + - MAY append to `collaboration.md`. + - MAY edit only own row in `INDEX.md`. + - MUST NOT edit other agent files. +7. At run end: + - record files touched, key decisions, assumptions, blockers, next step for handoff. +8. Conflict handling: + - if another agent touched your target files, add conflict note in `collaboration.md` before continuing. +9. Brevity: + - terse bullets; factual; no long prose. + +Suggested env vars: + +- `AGENT_ID` (required) +- `AGENT_ALIAS` (required) +- `HEARTBEAT_MINUTES` (optional, default 20) diff --git a/backlog/tasks/task-34 - Add-in-app-episode-browser-CtrlE-for-local-files-and-Jellyfin-ready-metadata.md b/backlog/archive/tasks/task-34 - Add-in-app-episode-browser-CtrlE-for-local-files-and-Jellyfin-ready-metadata.md similarity index 100% rename from backlog/tasks/task-34 - Add-in-app-episode-browser-CtrlE-for-local-files-and-Jellyfin-ready-metadata.md rename to backlog/archive/tasks/task-34 - Add-in-app-episode-browser-CtrlE-for-local-files-and-Jellyfin-ready-metadata.md diff --git a/backlog/tasks/task-38 - Add-user-friendly-config-validation-errors-on-startup.md b/backlog/tasks/task-38 - Add-user-friendly-config-validation-errors-on-startup.md index 1b5b123..4f28f92 100644 --- a/backlog/tasks/task-38 - Add-user-friendly-config-validation-errors-on-startup.md +++ b/backlog/tasks/task-38 - Add-user-friendly-config-validation-errors-on-startup.md @@ -1,9 +1,11 @@ --- id: TASK-38 title: Add user-friendly config validation errors on startup -status: To Do -assignee: [] +status: Done +assignee: + - codex-main created_date: '2026-02-14 02:02' +updated_date: '2026-02-19 08:21' labels: - config - developer-experience @@ -15,7 +17,6 @@ priority: medium ## Description - Improve config validation to surface clear, actionable error messages at startup when the user's config file has invalid values, missing required fields, or type mismatches. ## Motivation @@ -39,14 +40,43 @@ The project has a config schema with validation, but invalid configs (wrong type ## Acceptance Criteria - - -- [ ] #1 All config fields are validated against the schema before service initialization. -- [ ] #2 Validation errors show field path, expected type, and actual value. -- [ ] #3 Multiple errors are collected and shown together, not one at a time. -- [ ] #4 Deprecated or renamed fields produce a helpful migration suggestion. -- [ ] #5 Non-critical validation issues allow startup with defaults and a visible warning. -- [ ] #6 Critical validation failures prevent startup with a clear dialog or console message. -- [ ] #7 Config file path is shown in error output. +- [x] #1 All config fields are validated against the schema before service initialization. +- [x] #2 Validation errors show field path, expected type, and actual value. +- [x] #3 Multiple errors are collected and shown together, not one at a time. +- [x] #4 Deprecated or renamed fields produce a helpful migration suggestion. +- [x] #5 Non-critical validation issues allow startup with defaults and a visible warning. +- [x] #6 Critical validation failures prevent startup with a clear dialog or console message. +- [x] #7 Config file path is shown in error output. + +## Implementation Plan + + +1) Extend config warning coverage in `src/config/service.ts` (+ tests in `src/config/config.test.ts`) to include unknown top-level keys and deprecated `ankiConnect.openRouter` migration suggestions. +2) Add startup critical validation in `src/core/services/startup.ts` (+ tests in `src/core/services/app-ready.test.ts`) so invalid Anki field mappings are treated as startup-blocking and aggregated. +3) Wire end-to-end startup UX in `src/main.ts`: strict startup reload, fatal parse/config dialogs with config path, aggregated warning summary for non-critical issues, and visible warning notification. +4) Verify with targeted test commands and update TASK-38 notes/acceptance criteria in Backlog. + + +## Implementation Notes + + +Implemented startup config UX hardening across config resolver + app-ready lifecycle + Electron wiring. + +Added non-fatal validation warnings for unknown top-level keys and deprecated `ankiConnect.openRouter` with migration suggestion to `ankiConnect.ai`. + +Added startup critical validation for Anki field mappings when `ankiConnect.enabled === true`; app-ready now aborts before service initialization and reports aggregated critical errors. + +Startup config reload now uses `reloadConfigStrict()`; parse failures show a clear `dialog.showErrorBox`, include config path, set non-zero exit code, and quit. + +Added aggregated startup warning summary (path + expected/message + actual + fallback) with visible desktop notification while continuing startup with defaults. + +Verification: `bun run build && node --test dist/config/config.test.js dist/core/services/app-ready.test.js` (pass: 39/39). + + +## Final Summary + + +Implemented startup config validation UX end-to-end: strict startup parse validation, aggregated warning/error reporting, migration guidance for deprecated config keys, and startup-blocking critical checks for invalid Anki field mappings. Added targeted test coverage for config warnings and app-ready critical validation flow; verification passed with `bun run build && node --test dist/config/config.test.js dist/core/services/app-ready.test.js`. + diff --git a/backlog/tasks/task-69 - Harden-legacy-config-migration-validation-and-deprecation-handling.md b/backlog/tasks/task-69 - Harden-legacy-config-migration-validation-and-deprecation-handling.md index 5dd6411..a48a492 100644 --- a/backlog/tasks/task-69 - Harden-legacy-config-migration-validation-and-deprecation-handling.md +++ b/backlog/tasks/task-69 - Harden-legacy-config-migration-validation-and-deprecation-handling.md @@ -1,10 +1,11 @@ --- id: TASK-69 title: Harden legacy config migration validation and deprecation handling -status: To Do -assignee: [] +status: Done +assignee: + - codex-main created_date: '2026-02-18 11:35' -updated_date: '2026-02-18 11:35' +updated_date: '2026-02-19 08:27' labels: - config - validation @@ -32,15 +33,40 @@ priority: high ## Acceptance Criteria -- [ ] #1 No legacy key path writes directly to resolved config without type validation -- [ ] #2 Invalid legacy values produce warnings and safe fallback behavior -- [ ] #3 Valid legacy configs still map to equivalent resolved config -- [ ] #4 Config test suite includes legacy-invalid regression coverage +- [x] #1 No legacy key path writes directly to resolved config without type validation +- [x] #2 Invalid legacy values produce warnings and safe fallback behavior +- [x] #3 Valid legacy configs still map to equivalent resolved config +- [x] #4 Config test suite includes legacy-invalid regression coverage +## Implementation Plan + + +Implementation plan saved at `docs/plans/2026-02-19-task-69-legacy-config-migration-hardening.md`. + +Execution breakdown: +1. Add failing regression tests for invalid legacy `ankiConnect` migration values and valid-legacy compatibility behavior. +2. Replace unchecked `mapLegacy` casts in `src/config/service.ts` with typed validators using `asString`/`asBoolean`/`asNumber` plus enum/range checks; warn on invalid legacy input and keep deprecation warnings. +3. Preserve modern key precedence and existing `ankiConnect.openRouter` deprecation behavior. +4. Run `bun run build && bun run test:config:dist` to verify. +5. Update `docs/configuration.md` only if migration/deprecation behavior clarification is needed. +6. Append notes and acceptance progress back into TASK-69. + + +## Implementation Notes + + +Implemented legacy migration hardening in `src/config/service.ts` by filtering top-level legacy keys out of broad `ankiConnect` merge and replacing unchecked `mapLegacy` casts with parser-backed legacy mapping (`asString`/`asBoolean`/range+enum validators). Invalid legacy values now emit warnings and keep safe fallback values; valid legacy values still map when modern key is absent. + +Added regression tests in `src/config/config.test.ts`: `falls back and warns when legacy ankiConnect migration values are invalid` and `maps valid legacy ankiConnect values to equivalent modern config`. + +Updated docs in `docs/configuration.md` to clarify that legacy top-level `ankiConnect` migration keys are compatibility-only, validated, and ignored with warnings when invalid. + +Verification: `bun run build && bun run test:config:dist` passed (32/32). + + ## Definition of Done -- [ ] #1 `bun run test:config:dist` passes -- [ ] #2 Any doc changes for migration/deprecation are committed +- [x] #1 `bun run test:config:dist` passes +- [x] #2 Any doc changes for migration/deprecation are committed - diff --git a/backlog/tasks/task-84 - Gate-feature-dependent-keybindings-behind-config-flags.md b/backlog/tasks/task-84 - Gate-feature-dependent-keybindings-behind-config-flags.md new file mode 100644 index 0000000..9cc5317 --- /dev/null +++ b/backlog/tasks/task-84 - Gate-feature-dependent-keybindings-behind-config-flags.md @@ -0,0 +1,25 @@ +--- +id: TASK-84 +title: Gate feature-dependent keybindings behind config flags +status: To Do +assignee: [] +created_date: '2026-02-19 08:41' +labels: [] +dependencies: [] +priority: medium +--- + +## Description + + +Ensure feature-specific keybindings only work when their related feature is enabled in configuration, so users do not trigger unavailable behavior and the app avoids loading integrations that are disabled (for example Jellyfin). + + +## Acceptance Criteria + +- [ ] #1 Feature-dependent keybindings are effectively disabled when their corresponding feature flag/config is off, with no user-facing error dialogs. +- [ ] #2 When a feature is disabled in config, its related integration code is not loaded or initialized during startup (including Jellyfin as a concrete case). +- [ ] #3 When a feature is enabled, existing keybinding behavior continues to work as expected. +- [ ] #4 Automated tests cover enabled and disabled paths for keybinding gating and disabled-integration loading behavior. +- [ ] #5 User-facing docs/config guidance explain that feature keybindings require the corresponding feature to be enabled. + diff --git a/config.example.jsonc b/config.example.jsonc index 4c2ddaa..abcdeaf 100644 --- a/config.example.jsonc +++ b/config.example.jsonc @@ -5,7 +5,6 @@ * Copy to $XDG_CONFIG_HOME/SubMiner/config.jsonc (or ~/.config/SubMiner/config.jsonc) and edit as needed. */ { - // ========================================== // Overlay Auto-Start // When overlay connects to mpv, automatically show overlay and hide mpv subtitles. @@ -24,7 +23,7 @@ // Control whether browser opens automatically for texthooker. // ========================================== "texthooker": { - "openBrowser": true + "openBrowser": true, }, // ========================================== @@ -34,7 +33,7 @@ // ========================================== "websocket": { "enabled": "auto", - "port": 6677 + "port": 6677, }, // ========================================== @@ -43,7 +42,7 @@ // Set to debug for full runtime diagnostics. // ========================================== "logging": { - "level": "info" + "level": "info", }, // ========================================== @@ -56,15 +55,13 @@ "enabled": false, "url": "http://127.0.0.1:8765", "pollingRate": 3000, - "tags": [ - "SubMiner" - ], + "tags": ["SubMiner"], "fields": { "audio": "ExpressionAudio", "image": "Picture", "sentence": "Sentence", "miscInfo": "MiscInfo", - "translation": "SelectionText" + "translation": "SelectionText", }, "ai": { "enabled": false, @@ -73,7 +70,7 @@ "model": "openai/gpt-4o-mini", "baseUrl": "https://openrouter.ai/api", "targetLanguage": "English", - "systemPrompt": "You are a translation engine. Return only the translated text with no explanations." + "systemPrompt": "You are a translation engine. Return only the translated text with no explanations.", }, "media": { "generateAudio": true, @@ -86,7 +83,7 @@ "animatedCrf": 35, "audioPadding": 0.5, "fallbackDuration": 3, - "maxMediaDuration": 30 + "maxMediaDuration": 30, }, "behavior": { "overwriteAudio": true, @@ -94,7 +91,7 @@ "mediaInsertMode": "append", "highlightWord": true, "notificationType": "osd", - "autoUpdateNewCards": true + "autoUpdateNewCards": true, }, "nPlusOne": { "highlightEnabled": false, @@ -103,22 +100,20 @@ "decks": [], "minSentenceWords": 3, "nPlusOne": "#c6a0f6", - "knownWord": "#a6da95" + "knownWord": "#a6da95", }, "metadata": { - "pattern": "[SubMiner] %f (%t)" + "pattern": "[SubMiner] %f (%t)", }, "isLapis": { "enabled": false, "sentenceCardModel": "Japanese sentences", - "sentenceCardSentenceField": "Sentence", - "sentenceCardAudioField": "SentenceAudio" }, "isKiku": { "enabled": false, "fieldGrouping": "disabled", - "deleteDuplicateInAuto": true - } + "deleteDuplicateInAuto": true, + }, }, // ========================================== @@ -142,7 +137,7 @@ "toggleSecondarySub": "CommandOrControl+Shift+V", "markAudioCard": "CommandOrControl+Shift+A", "openRuntimeOptions": "CommandOrControl+Shift+O", - "openJimaku": "Ctrl+Shift+J" + "openJimaku": "Ctrl+Shift+J", }, // ========================================== @@ -152,7 +147,7 @@ // This edit-mode shortcut is fixed and is not currently configurable. // ========================================== "invisibleOverlay": { - "startupVisibility": "platform-default" + "startupVisibility": "platform-default", }, // ========================================== @@ -183,7 +178,7 @@ "N2": "#f5a97f", "N3": "#f9e2af", "N4": "#a6e3a1", - "N5": "#8aadf4" + "N5": "#8aadf4", }, "frequencyDictionary": { "enabled": false, @@ -191,13 +186,7 @@ "topX": 1000, "mode": "single", "singleColor": "#f5a97f", - "bandedColors": [ - "#ed8796", - "#f5a97f", - "#f9e2af", - "#a6e3a1", - "#8aadf4" - ] + "bandedColors": ["#ed8796", "#f5a97f", "#f9e2af", "#a6e3a1", "#8aadf4"], }, "secondary": { "fontSize": 24, @@ -205,8 +194,8 @@ "backgroundColor": "transparent", "fontWeight": "normal", "fontStyle": "normal", - "fontFamily": "Noto Sans CJK JP Regular, Noto Sans CJK JP, Arial Unicode MS, Arial, sans-serif" - } + "fontFamily": "Noto Sans CJK JP Regular, Noto Sans CJK JP, Arial Unicode MS, Arial, sans-serif", + }, }, // ========================================== @@ -218,7 +207,7 @@ "secondarySub": { "secondarySubLanguages": [], "autoLoadSecondarySub": false, - "defaultMode": "hover" + "defaultMode": "hover", }, // ========================================== @@ -229,7 +218,7 @@ "defaultMode": "auto", "alass_path": "", "ffsubsync_path": "", - "ffmpeg_path": "" + "ffmpeg_path": "", }, // ========================================== @@ -237,7 +226,7 @@ // Initial vertical subtitle position from the bottom. // ========================================== "subtitlePosition": { - "yPercent": 10 + "yPercent": 10, }, // ========================================== @@ -247,7 +236,7 @@ "jimaku": { "apiBaseUrl": "https://jimaku.cc", "languagePreference": "ja", - "maxEntryResults": 10 + "maxEntryResults": 10, }, // ========================================== @@ -258,10 +247,7 @@ "mode": "automatic", "whisperBin": "", "whisperModel": "", - "primarySubLanguages": [ - "ja", - "jpn" - ] + "primarySubLanguages": ["ja", "jpn"], }, // ========================================== @@ -270,7 +256,7 @@ // ========================================== "anilist": { "enabled": false, - "accessToken": "" + "accessToken": "", }, // ========================================== @@ -295,16 +281,8 @@ "pullPictures": false, "iconCacheDir": "/tmp/subminer-jellyfin-icons", "directPlayPreferred": true, - "directPlayContainers": [ - "mkv", - "mp4", - "webm", - "mov", - "flac", - "mp3", - "aac" - ], - "transcodeVideoCodec": "h264" + "directPlayContainers": ["mkv", "mp4", "webm", "mov", "flac", "mp3", "aac"], + "transcodeVideoCodec": "h264", }, // ========================================== @@ -326,7 +304,7 @@ "telemetryDays": 30, "dailyRollupsDays": 365, "monthlyRollupsDays": 1825, - "vacuumIntervalDays": 7 - } - } + "vacuumIntervalDays": 7, + }, + }, } diff --git a/docs/public/config.example.jsonc b/docs/public/config.example.jsonc index 4c2ddaa..abcdeaf 100644 --- a/docs/public/config.example.jsonc +++ b/docs/public/config.example.jsonc @@ -5,7 +5,6 @@ * Copy to $XDG_CONFIG_HOME/SubMiner/config.jsonc (or ~/.config/SubMiner/config.jsonc) and edit as needed. */ { - // ========================================== // Overlay Auto-Start // When overlay connects to mpv, automatically show overlay and hide mpv subtitles. @@ -24,7 +23,7 @@ // Control whether browser opens automatically for texthooker. // ========================================== "texthooker": { - "openBrowser": true + "openBrowser": true, }, // ========================================== @@ -34,7 +33,7 @@ // ========================================== "websocket": { "enabled": "auto", - "port": 6677 + "port": 6677, }, // ========================================== @@ -43,7 +42,7 @@ // Set to debug for full runtime diagnostics. // ========================================== "logging": { - "level": "info" + "level": "info", }, // ========================================== @@ -56,15 +55,13 @@ "enabled": false, "url": "http://127.0.0.1:8765", "pollingRate": 3000, - "tags": [ - "SubMiner" - ], + "tags": ["SubMiner"], "fields": { "audio": "ExpressionAudio", "image": "Picture", "sentence": "Sentence", "miscInfo": "MiscInfo", - "translation": "SelectionText" + "translation": "SelectionText", }, "ai": { "enabled": false, @@ -73,7 +70,7 @@ "model": "openai/gpt-4o-mini", "baseUrl": "https://openrouter.ai/api", "targetLanguage": "English", - "systemPrompt": "You are a translation engine. Return only the translated text with no explanations." + "systemPrompt": "You are a translation engine. Return only the translated text with no explanations.", }, "media": { "generateAudio": true, @@ -86,7 +83,7 @@ "animatedCrf": 35, "audioPadding": 0.5, "fallbackDuration": 3, - "maxMediaDuration": 30 + "maxMediaDuration": 30, }, "behavior": { "overwriteAudio": true, @@ -94,7 +91,7 @@ "mediaInsertMode": "append", "highlightWord": true, "notificationType": "osd", - "autoUpdateNewCards": true + "autoUpdateNewCards": true, }, "nPlusOne": { "highlightEnabled": false, @@ -103,22 +100,20 @@ "decks": [], "minSentenceWords": 3, "nPlusOne": "#c6a0f6", - "knownWord": "#a6da95" + "knownWord": "#a6da95", }, "metadata": { - "pattern": "[SubMiner] %f (%t)" + "pattern": "[SubMiner] %f (%t)", }, "isLapis": { "enabled": false, "sentenceCardModel": "Japanese sentences", - "sentenceCardSentenceField": "Sentence", - "sentenceCardAudioField": "SentenceAudio" }, "isKiku": { "enabled": false, "fieldGrouping": "disabled", - "deleteDuplicateInAuto": true - } + "deleteDuplicateInAuto": true, + }, }, // ========================================== @@ -142,7 +137,7 @@ "toggleSecondarySub": "CommandOrControl+Shift+V", "markAudioCard": "CommandOrControl+Shift+A", "openRuntimeOptions": "CommandOrControl+Shift+O", - "openJimaku": "Ctrl+Shift+J" + "openJimaku": "Ctrl+Shift+J", }, // ========================================== @@ -152,7 +147,7 @@ // This edit-mode shortcut is fixed and is not currently configurable. // ========================================== "invisibleOverlay": { - "startupVisibility": "platform-default" + "startupVisibility": "platform-default", }, // ========================================== @@ -183,7 +178,7 @@ "N2": "#f5a97f", "N3": "#f9e2af", "N4": "#a6e3a1", - "N5": "#8aadf4" + "N5": "#8aadf4", }, "frequencyDictionary": { "enabled": false, @@ -191,13 +186,7 @@ "topX": 1000, "mode": "single", "singleColor": "#f5a97f", - "bandedColors": [ - "#ed8796", - "#f5a97f", - "#f9e2af", - "#a6e3a1", - "#8aadf4" - ] + "bandedColors": ["#ed8796", "#f5a97f", "#f9e2af", "#a6e3a1", "#8aadf4"], }, "secondary": { "fontSize": 24, @@ -205,8 +194,8 @@ "backgroundColor": "transparent", "fontWeight": "normal", "fontStyle": "normal", - "fontFamily": "Noto Sans CJK JP Regular, Noto Sans CJK JP, Arial Unicode MS, Arial, sans-serif" - } + "fontFamily": "Noto Sans CJK JP Regular, Noto Sans CJK JP, Arial Unicode MS, Arial, sans-serif", + }, }, // ========================================== @@ -218,7 +207,7 @@ "secondarySub": { "secondarySubLanguages": [], "autoLoadSecondarySub": false, - "defaultMode": "hover" + "defaultMode": "hover", }, // ========================================== @@ -229,7 +218,7 @@ "defaultMode": "auto", "alass_path": "", "ffsubsync_path": "", - "ffmpeg_path": "" + "ffmpeg_path": "", }, // ========================================== @@ -237,7 +226,7 @@ // Initial vertical subtitle position from the bottom. // ========================================== "subtitlePosition": { - "yPercent": 10 + "yPercent": 10, }, // ========================================== @@ -247,7 +236,7 @@ "jimaku": { "apiBaseUrl": "https://jimaku.cc", "languagePreference": "ja", - "maxEntryResults": 10 + "maxEntryResults": 10, }, // ========================================== @@ -258,10 +247,7 @@ "mode": "automatic", "whisperBin": "", "whisperModel": "", - "primarySubLanguages": [ - "ja", - "jpn" - ] + "primarySubLanguages": ["ja", "jpn"], }, // ========================================== @@ -270,7 +256,7 @@ // ========================================== "anilist": { "enabled": false, - "accessToken": "" + "accessToken": "", }, // ========================================== @@ -295,16 +281,8 @@ "pullPictures": false, "iconCacheDir": "/tmp/subminer-jellyfin-icons", "directPlayPreferred": true, - "directPlayContainers": [ - "mkv", - "mp4", - "webm", - "mov", - "flac", - "mp3", - "aac" - ], - "transcodeVideoCodec": "h264" + "directPlayContainers": ["mkv", "mp4", "webm", "mov", "flac", "mp3", "aac"], + "transcodeVideoCodec": "h264", }, // ========================================== @@ -326,7 +304,7 @@ "telemetryDays": 30, "dailyRollupsDays": 365, "monthlyRollupsDays": 1825, - "vacuumIntervalDays": 7 - } - } + "vacuumIntervalDays": 7, + }, + }, } diff --git a/docs/subagents/INDEX.md b/docs/subagents/INDEX.md new file mode 100644 index 0000000..6ff0044 --- /dev/null +++ b/docs/subagents/INDEX.md @@ -0,0 +1,7 @@ +# Subagents Index + +Read first. Keep concise. + +| agent_id | alias | mission | status | file | last_update_utc | +| ------------ | -------------- | -------------------------------------------- | --------- | ------------------------------------- | ---------------------- | +| `codex-main` | `planner-exec` | `Track config-gated keybinding task request` | `handoff` | `docs/subagents/agents/codex-main.md` | `2026-02-19T08:41:44Z` | diff --git a/docs/subagents/agents/TEMPLATE.md b/docs/subagents/agents/TEMPLATE.md new file mode 100644 index 0000000..918d6fc --- /dev/null +++ b/docs/subagents/agents/TEMPLATE.md @@ -0,0 +1,27 @@ +# Agent: + +- alias: +- mission: +- status: +- branch: +- started_at: +- heartbeat_minutes: + +## Current Work (newest first) +- [YYYY-MM-DDTHH:MM:SSZ] intent: ... +- [YYYY-MM-DDTHH:MM:SSZ] progress: ... +- [YYYY-MM-DDTHH:MM:SSZ] test: ... +- [YYYY-MM-DDTHH:MM:SSZ] handoff: ... + +## Files Touched +- `path/to/file` +- `path/to/another` + +## Assumptions +- ... + +## Open Questions / Blockers +- ... + +## Next Step +- ... diff --git a/docs/subagents/agents/codex-main.md b/docs/subagents/agents/codex-main.md new file mode 100644 index 0000000..da412d4 --- /dev/null +++ b/docs/subagents/agents/codex-main.md @@ -0,0 +1,64 @@ +# Agent: codex-main + +- alias: planner-exec +- mission: Track config-gated keybinding task request +- status: handoff +- branch: main +- started_at: 2026-02-19T08:06:28Z +- heartbeat_minutes: 20 + +## Current Work (newest first) + +- [2026-02-19T08:41:44Z] handoff: created backlog TASK-84 for config-gated keybindings + disabled-feature integration non-loading (Jellyfin example) with tests/docs acceptance criteria. +- [2026-02-19T08:41:22Z] intent: create backlog task for config-gated keybindings so disabled features become no-ops and feature modules (example: Jellyfin) are not loaded when disabled. +- [2026-02-19T08:40:53Z] handoff: completed TASK-83 simplification; removed configurable sentence/audio field overrides for Lapis sentence cards and verified. +- [2026-02-19T08:40:53Z] test: `bun run build && bun run test:config:dist` -> pass (33/33). +- [2026-02-19T08:38:30Z] intent: implement TASK-83 to remove `isLapis.sentenceCardSentenceField` + `isLapis.sentenceCardAudioField` from config/types/docs and force fixed `Sentence`/`SentenceAudio` in integration. +- [2026-02-19T08:27:23Z] handoff: completed TASK-69 implementation + verification; backlog TASK-69 set Done with AC/DoD checked. +- [2026-02-19T08:27:23Z] test: `bun run build && bun run test:config:dist` -> pass (32/32). +- [2026-02-19T08:27:23Z] progress: hardened legacy ankiConnect migration mapping with validators + warnings in `src/config/service.ts`; added invalid/valid legacy regression tests in `src/config/config.test.ts`; updated migration doc note in `docs/configuration.md`. +- [2026-02-19T08:23:30Z] intent: user approved TASK-69 plan; start implementation (tests first, then validated legacy mapping, then verify + backlog updates). +- [2026-02-19T08:21:11Z] handoff: completed TASK-38 implementation + verification; updated Backlog acceptance checks and notes. +- [2026-02-19T08:21:11Z] test: `bun run build && node --test dist/config/config.test.js dist/core/services/app-ready.test.js` -> pass (39/39). +- [2026-02-19T08:21:11Z] progress: wired strict startup parse failure handling + warning summary + critical validation dialog path in `src/main.ts` and threaded new app-ready dependency in `src/main/app-lifecycle.ts`. +- [2026-02-19T08:20:00Z] intent: read TASK-69 details, write end-to-end implementation plan via writing-plans skill, then execute via executing-plans skill with parallel subagents where possible. +- [2026-02-19T08:18:47Z] progress: implemented unknown top-level config warning + `ankiConnect.openRouter` deprecation warning; added coverage in `src/config/config.test.ts`; verification passed (`bun run build && node --test dist/config/config.test.js`). +- [2026-02-19T08:10:40Z] intent: implement TASK-38 plan Task 1 scope (`src/config/service.ts`, `src/config/config.test.ts`) for unknown top-level key warnings + `ankiConnect.openRouter` deprecation warning, then run required build+test command. +- [2026-02-19T08:09:46Z] progress: wrote plan `docs/plans/2026-02-19-task-38-config-validation-startup.md`, set TASK-38 In Progress, and stored plan in Backlog. +- [2026-02-19T08:06:28Z] intent: read backlog/task context, draft implementation plan for TASK-38, then execute with parallel subagents where possible. + +## Files Touched + +- `docs/subagents/INDEX.md` +- `docs/subagents/agents/codex-main.md` +- `backlog/tasks/task-84 - Gate-feature-dependent-keybindings-behind-config-flags.md` +- `src/config/service.ts` +- `src/config/config.test.ts` +- `src/core/services/startup.ts` +- `src/core/services/app-ready.test.ts` +- `src/main.ts` +- `src/main/app-lifecycle.ts` +- `docs/plans/2026-02-19-task-38-config-validation-startup.md` +- `docs/plans/2026-02-19-task-69-legacy-config-migration-hardening.md` +- `docs/configuration.md` +- `src/types.ts` +- `src/config/definitions.ts` +- `src/anki-integration.ts` +- `config.example.jsonc` +- `docs/public/config.example.jsonc` +- `docs/anki-integration.md` +- `docs/mining-workflow.md` +- `backlog/tasks/task-83 - Simplify-isLapis-sentence-card-field-config-to-fixed-field-names.md` +- `backlog/tasks/task-38 - Add-user-friendly-config-validation-errors-on-startup.md` + +## Assumptions + +- AGENT_ID/AGENT_ALIAS env vars are unset; using stable fallback `codex-main` / `planner-exec` for this run. + +## Open Questions / Blockers + +- none + +## Next Step + +- Await user prioritization / implementation request for TASK-84. diff --git a/docs/subagents/archive/.gitkeep b/docs/subagents/archive/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/docs/subagents/archive/.gitkeep @@ -0,0 +1 @@ + diff --git a/docs/subagents/collaboration.md b/docs/subagents/collaboration.md new file mode 100644 index 0000000..7bc0775 --- /dev/null +++ b/docs/subagents/collaboration.md @@ -0,0 +1,6 @@ +# Subagents Collaboration + +Shared notes. Append-only. + +- [YYYY-MM-DDTHH:MM:SSZ] [agent_id|alias] note, question, dependency, conflict, decision. +- [2026-02-19T08:21:11Z] [codex-main|planner-exec] conflict note: `docs/subagents/INDEX.md` and `docs/subagents/agents/codex-main.md` were externally updated to TASK-69 while TASK-38 work was in-flight; reconciled own row/file back to TASK-38 handoff state. diff --git a/scripts/docs-sweep-once.sh b/scripts/docs-sweep-once.sh index 605aefd..9c6fa78 100755 --- a/scripts/docs-sweep-once.sh +++ b/scripts/docs-sweep-once.sh @@ -6,31 +6,26 @@ LOCK_FILE="${LOCK_FILE:-/tmp/subminer-doc-sweep.lock}" STATE_FILE="${STATE_FILE:-/tmp/subminer-doc-sweep.state}" LOG_FILE="${LOG_FILE:-$REPO/.codex-doc-sweep.log}" TIMEOUT_SECONDS="${TIMEOUT_SECONDS:-240}" +SUBAGENT_ROOT="${SUBAGENT_ROOT:-$REPO/docs/subagents}" +SUBAGENT_INDEX_FILE="${SUBAGENT_INDEX_FILE:-$SUBAGENT_ROOT/INDEX.md}" +SUBAGENT_COLLAB_FILE="${SUBAGENT_COLLAB_FILE:-$SUBAGENT_ROOT/collaboration.md}" +SUBAGENT_AGENTS_DIR="${SUBAGENT_AGENTS_DIR:-$SUBAGENT_ROOT/agents}" +LEGACY_SUBAGENT_FILE="${LEGACY_SUBAGENT_FILE:-$REPO/docs/subagent.md}" +AGENT_ID="${AGENT_ID:-docs-sweep}" +AGENT_ALIAS="${AGENT_ALIAS:-Docs Sweep}" +AGENT_MISSION="${AGENT_MISSION:-Docs drift cleanup and coordination updates}" # Non-interactive agent command used to run the prompt. # Example: # AGENT_CMD='codex exec' # AGENT_CMD='opencode run' AGENT_CMD="${AGENT_CMD:-codex exec}" - -read -r -d '' PROMPT << 'EOF' || true -Watch for in-flight refactors. If repo changes introduced drift, update only: -- README.md -- docs/**/*.md -- config.example.jsonc -- docs/public/config.example.jsonc <-- generated automatically with make generate-example-config / bun run generate:config-example -- package.json scripts/config references (only if needed) - -Rules: -- Keep edits minimal and accurate to current code. -- Do not commit. -- Do not push. -- If ambiguous, do not guess; skip and report uncertainty. -- Print concise summary of files changed and why. -EOF +AGENT_ID_SAFE="$(printf '%s' "$AGENT_ID" | tr -c 'A-Za-z0-9._-' '_')" +AGENT_FILE="${SUBAGENT_AGENTS_DIR}/${AGENT_ID_SAFE}.md" mkdir -p "$(dirname "$LOCK_FILE")" mkdir -p "$(dirname "$STATE_FILE")" +mkdir -p "$SUBAGENT_ROOT" "$SUBAGENT_AGENTS_DIR" "$SUBAGENT_ROOT/archive" exec 9> "$LOCK_FILE" if ! flock -n 9; then @@ -51,23 +46,120 @@ fi printf '%s' "$current_state" > "$STATE_FILE" -quoted_prompt="$(printf '%q' "$PROMPT")" - run_started_at="$(date -Is)" -echo "[RUN] [$run_started_at] docs sweep running" -echo "[$run_started_at] state changed; starting docs sweep" >> "$LOG_FILE" +run_started_utc="$(date -u +%Y-%m-%dT%H:%M:%SZ)" +echo "[RUN] [$run_started_at] docs sweep running (agent_id=$AGENT_ID alias=$AGENT_ALIAS)" +echo "[$run_started_at] state changed; starting docs sweep (agent_id=$AGENT_ID alias=$AGENT_ALIAS)" >> "$LOG_FILE" + +if [[ ! -f "$SUBAGENT_INDEX_FILE" ]]; then + cat > "$SUBAGENT_INDEX_FILE" << 'EOF' +# Subagents Index + +Read first. Keep concise. + +| agent_id | alias | mission | status | file | last_update_utc | +| --- | --- | --- | --- | --- | --- | +EOF +fi + +if [[ ! -f "$SUBAGENT_COLLAB_FILE" ]]; then + cat > "$SUBAGENT_COLLAB_FILE" << 'EOF' +# Subagents Collaboration + +Shared notes. Append-only. + +- [YYYY-MM-DDTHH:MM:SSZ] [agent_id|alias] note, question, dependency, conflict, decision. +EOF +fi + +if [[ ! -f "$AGENT_FILE" ]]; then + cat > "$AGENT_FILE" << EOF +# Agent: $AGENT_ID + +- alias: $AGENT_ALIAS +- mission: $AGENT_MISSION +- status: planning +- branch: unknown +- started_at: $run_started_utc +- heartbeat_minutes: 20 + +## Current Work (newest first) +- [$run_started_utc] intent: initialize section + +## Files Touched +- none yet + +## Assumptions +- none yet + +## Open Questions / Blockers +- none + +## Next Step +- continue run +EOF +fi + +if [[ -f "$LEGACY_SUBAGENT_FILE" ]]; then + echo "[WARN] [$run_started_at] legacy file exists; prefer sharded layout: $LEGACY_SUBAGENT_FILE" | tee -a "$LOG_FILE" +fi + +read -r -d '' PROMPT << EOF || true +Watch for in-flight refactors. If repo changes introduced drift, update only: +- README.md +- AGENTS.md +- docs/**/*.md +- config.example.jsonc +- docs/public/config.example.jsonc <-- generated automatically with make generate-example-config / bun run generate:config-example +- package.json scripts/config references (only if needed) + +Coordination protocol: +- Read in order before edits: + 1) \`$SUBAGENT_INDEX_FILE\` + 2) \`$SUBAGENT_COLLAB_FILE\` + 3) \`$AGENT_FILE\` +- Edit scope: + - MAY edit own file: \`$AGENT_FILE\` + - MAY append to collaboration: \`$SUBAGENT_COLLAB_FILE\` + - MAY update own row in index: \`$SUBAGENT_INDEX_FILE\` + - MUST NOT edit other agent files in \`$SUBAGENT_AGENTS_DIR\` +- Ensure own file has updated: alias, mission, status, branch, started_at, heartbeat_minutes. +- Add UTC ISO entries in "Current Work (newest first)" for intent/progress/handoff for this run. +- Keep own file sections current: Files Touched, assumptions, blockers, next step. +- Ensure index row for \`$AGENT_ID\` reflects alias/mission/status/file/last_update_utc. +- If file conflict/dependency seen, append note in collaboration. + +Run metadata: +- run_started_at_utc: $run_started_utc +- repo: $REPO +- agent_id: $AGENT_ID +- agent_alias: $AGENT_ALIAS +- agent_file: $AGENT_FILE + +Rules: +- Keep edits minimal and accurate to current code. +- Do not commit. +- Do not push. +- If ambiguous, do not guess; skip and report uncertainty. +- Print concise summary with: + 1) files changed + why + 2) coordination updates made (\`$SUBAGENT_INDEX_FILE\`, \`$SUBAGENT_COLLAB_FILE\`, \`$AGENT_FILE\`) + 3) open questions/blockers +EOF + +quoted_prompt="$(printf '%q' "$PROMPT")" job_status=0 if timeout "${TIMEOUT_SECONDS}s" bash -lc "$AGENT_CMD $quoted_prompt" >> "$LOG_FILE" 2>&1; then run_finished_at="$(date -Is)" - echo "[OK] [$run_finished_at] docs sweep complete" - echo "[$run_finished_at] docs sweep complete" >> "$LOG_FILE" + echo "[OK] [$run_finished_at] docs sweep complete (agent_id=$AGENT_ID)" + echo "[$run_finished_at] docs sweep complete (agent_id=$AGENT_ID)" >> "$LOG_FILE" else run_failed_at="$(date -Is)" exit_code=$? job_status=$exit_code - echo "[FAIL] [$run_failed_at] docs sweep failed (exit $exit_code)" - echo "[$run_failed_at] docs sweep failed (exit $exit_code)" >> "$LOG_FILE" + echo "[FAIL] [$run_failed_at] docs sweep failed (exit $exit_code, agent_id=$AGENT_ID)" + echo "[$run_failed_at] docs sweep failed (exit $exit_code, agent_id=$AGENT_ID)" >> "$LOG_FILE" fi exit "$job_status" diff --git a/scripts/docs-sweep-watch.sh b/scripts/docs-sweep-watch.sh index ba33575..16b1325 100755 --- a/scripts/docs-sweep-watch.sh +++ b/scripts/docs-sweep-watch.sh @@ -6,6 +6,13 @@ RUN_ONCE_SCRIPT="$SCRIPT_DIR/docs-sweep-once.sh" INTERVAL_SECONDS="${INTERVAL_SECONDS:-300}" REPO="${REPO:-$HOME/projects/japanese/SubMiner}" LOG_FILE="${LOG_FILE:-$REPO/.codex-doc-sweep.log}" +SUBAGENT_ROOT="${SUBAGENT_ROOT:-$REPO/docs/subagents}" +SUBAGENT_INDEX_FILE="${SUBAGENT_INDEX_FILE:-$SUBAGENT_ROOT/INDEX.md}" +SUBAGENT_COLLAB_FILE="${SUBAGENT_COLLAB_FILE:-$SUBAGENT_ROOT/collaboration.md}" +SUBAGENT_AGENTS_DIR="${SUBAGENT_AGENTS_DIR:-$SUBAGENT_ROOT/agents}" +AGENT_ID="${AGENT_ID:-docs-sweep}" +AGENT_ID_SAFE="$(printf '%s' "$AGENT_ID" | tr -c 'A-Za-z0-9._-' '_')" +AGENT_FILE="${AGENT_FILE:-$SUBAGENT_AGENTS_DIR/${AGENT_ID_SAFE}.md}" REPORT_WITH_CODEX=false REPORT_TIMEOUT_SECONDS="${REPORT_TIMEOUT_SECONDS:-120}" REPORT_AGENT_CMD="${REPORT_AGENT_CMD:-codex exec}" @@ -23,6 +30,12 @@ Usage: scripts/docs-sweep-watch.sh [options] Options: -r, --report One-off: summarize current log with Codex and exit. -h, --help Show this help message. + +Environment: + AGENT_ID Stable agent id (default: docs-sweep) + AGENT_ALIAS Human label shown in logs/coordination (default: Docs Sweep) + AGENT_MISSION One-line focus for this run + SUBAGENT_ROOT Coordination root (default: docs/subagents) EOF } @@ -53,28 +66,47 @@ trim_log_runs() { } run_report() { - if [[ ! -f "$LOG_FILE" ]]; then - echo "[REPORT] log file not found: $LOG_FILE" - return + local has_log=false + local has_index=false + local has_collab=false + local has_agent_file=false + if [[ -s "$LOG_FILE" ]]; then + has_log=true fi - - if [[ ! -s "$LOG_FILE" ]]; then - echo "[REPORT] log file empty: $LOG_FILE" + if [[ -s "$SUBAGENT_INDEX_FILE" ]]; then + has_index=true + fi + if [[ -s "$SUBAGENT_COLLAB_FILE" ]]; then + has_collab=true + fi + if [[ -s "$AGENT_FILE" ]]; then + has_agent_file=true + fi + if [[ "$has_log" != "true" && "$has_index" != "true" && "$has_collab" != "true" && "$has_agent_file" != "true" ]]; then + echo "[REPORT] no inputs; missing/empty files:" + echo "[REPORT] - $LOG_FILE" + echo "[REPORT] - $SUBAGENT_INDEX_FILE" + echo "[REPORT] - $SUBAGENT_COLLAB_FILE" + echo "[REPORT] - $AGENT_FILE" return fi local report_prompt read -r -d '' report_prompt << EOF || true -Summarize docs sweep log. Output: +Summarize docs sweep state. Output: - Changes made (short bullets; file-focused when possible) +- Agent coordination updates from sharded docs/subagents files - Open questions / uncertainty - Left undone / follow-up items Constraints: - Be concise. - If uncertain, say uncertain. -Read this file directly: +Read these files directly if present: $LOG_FILE +$SUBAGENT_INDEX_FILE +$SUBAGENT_COLLAB_FILE +$AGENT_FILE EOF echo "[REPORT] codex summary start" @@ -130,7 +162,7 @@ fi stop_requested=false trap 'stop_requested=true' INT TERM -echo "Starting docs sweep watcher (interval: ${INTERVAL_SECONDS}s). Press Ctrl+C to stop." +echo "Starting docs sweep watcher (interval: ${INTERVAL_SECONDS}s, subagent_root: ${SUBAGENT_ROOT}). Press Ctrl+C to stop." while true; do run_started_at="$(date -Is)"