mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-04-09 16:19:25 -07:00
Compare commits
3 Commits
v0.12.0-be
...
de111dbf8d
| Author | SHA1 | Date | |
|---|---|---|---|
|
de111dbf8d
|
|||
|
ea86f4e504
|
|||
|
bb54898747
|
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"name": "subminer-local",
|
||||
"interface": {
|
||||
"displayName": "SubMiner Local"
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
"name": "subminer-workflow",
|
||||
"source": {
|
||||
"source": "local",
|
||||
"path": "./plugins/subminer-workflow"
|
||||
},
|
||||
"policy": {
|
||||
"installation": "AVAILABLE",
|
||||
"authentication": "ON_INSTALL"
|
||||
},
|
||||
"category": "Productivity"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,22 +1,127 @@
|
||||
---
|
||||
name: 'subminer-change-verification'
|
||||
description: 'Compatibility shim. Canonical SubMiner change verification workflow now lives in the repo-local subminer-workflow plugin.'
|
||||
name: "subminer-change-verification"
|
||||
description: "Use when working in the SubMiner repo and you need to verify code changes actually work. Covers targeted regression checks during debugging and pre-handoff verification, with cheap-first lane selection for config, docs, launcher/plugin, runtime-compat, and optional real-runtime escalation."
|
||||
---
|
||||
|
||||
# Compatibility Shim
|
||||
# SubMiner Change Verification
|
||||
|
||||
Canonical source:
|
||||
Use this skill for SubMiner code changes. Default to cheap, repo-native verification first. Escalate only when the changed behavior actually depends on Electron, mpv, overlay/window tracking, or other GUI-sensitive runtime behavior.
|
||||
|
||||
- `plugins/subminer-workflow/skills/subminer-change-verification/SKILL.md`
|
||||
## Scripts
|
||||
|
||||
Canonical helper scripts:
|
||||
- `scripts/classify_subminer_diff.sh`
|
||||
- Emits suggested lanes and flags from explicit paths or current git changes.
|
||||
- `scripts/verify_subminer_change.sh`
|
||||
- Runs selected lanes, captures artifacts, and writes a compact summary.
|
||||
|
||||
- `plugins/subminer-workflow/skills/subminer-change-verification/scripts/classify_subminer_diff.sh`
|
||||
- `plugins/subminer-workflow/skills/subminer-change-verification/scripts/verify_subminer_change.sh`
|
||||
If you need an explicit installed path, use the directory that contains this `SKILL.md`. The helper scripts live under:
|
||||
|
||||
When this shim is invoked:
|
||||
```bash
|
||||
export SUBMINER_VERIFY_SKILL="<path-to-skill>"
|
||||
```
|
||||
|
||||
1. Read the canonical plugin-owned skill.
|
||||
2. Follow the plugin-owned skill as the source of truth.
|
||||
3. Use the wrapper scripts in this shim directory only for compatibility with existing commands, docs, and backlog history.
|
||||
4. Do not duplicate workflow changes here; update the plugin-owned skill and scripts instead.
|
||||
## Default workflow
|
||||
|
||||
1. Inspect the changed files or user-requested area.
|
||||
2. Run the classifier unless you already know the right lane.
|
||||
3. Run the verifier with the cheapest sufficient lane set.
|
||||
4. If the classifier emits `flag:real-runtime-candidate`, do not jump straight to runtime verification. First run the non-runtime lanes.
|
||||
5. Escalate to explicit `--lane real-runtime --allow-real-runtime` only when cheaper lanes cannot validate the behavior claim.
|
||||
6. Return:
|
||||
- verification summary
|
||||
- exact commands run
|
||||
- artifact paths
|
||||
- skipped lanes and blockers
|
||||
|
||||
## Quick start
|
||||
|
||||
Repo-source quick start:
|
||||
|
||||
```bash
|
||||
bash .agents/skills/subminer-change-verification/scripts/classify_subminer_diff.sh
|
||||
```
|
||||
|
||||
Installed-skill quick start:
|
||||
|
||||
```bash
|
||||
bash "$SUBMINER_VERIFY_SKILL/scripts/classify_subminer_diff.sh"
|
||||
```
|
||||
|
||||
Classify explicit files:
|
||||
|
||||
```bash
|
||||
bash .agents/skills/subminer-change-verification/scripts/classify_subminer_diff.sh \
|
||||
launcher/main.ts \
|
||||
plugin/subminer/lifecycle.lua \
|
||||
src/main/runtime/mpv-client-runtime-service.ts
|
||||
```
|
||||
|
||||
Run automatic lane selection:
|
||||
|
||||
```bash
|
||||
bash .agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh
|
||||
```
|
||||
|
||||
Installed-skill form:
|
||||
|
||||
```bash
|
||||
bash "$SUBMINER_VERIFY_SKILL/scripts/verify_subminer_change.sh"
|
||||
```
|
||||
|
||||
Run targeted lanes:
|
||||
|
||||
```bash
|
||||
bash .agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh \
|
||||
--lane launcher-plugin \
|
||||
--lane runtime-compat
|
||||
```
|
||||
|
||||
Dry-run to inspect planned commands and artifact layout:
|
||||
|
||||
```bash
|
||||
bash .agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh \
|
||||
--dry-run \
|
||||
launcher/main.ts \
|
||||
src/main.ts
|
||||
```
|
||||
|
||||
## Lane guidance
|
||||
|
||||
- `docs`
|
||||
- For `docs-site/`, `docs/`, and doc-only edits.
|
||||
- `config`
|
||||
- For `src/config/` and config-template-sensitive edits.
|
||||
- `core`
|
||||
- For general source changes where `typecheck` + `test:fast` is the best cheap signal.
|
||||
- `launcher-plugin`
|
||||
- For `launcher/`, `plugin/subminer/`, plugin gating scripts, and wrapper/mpv routing work.
|
||||
- `runtime-compat`
|
||||
- For `src/main*`, runtime/composer wiring, mpv/overlay services, window trackers, and dist-sensitive behavior.
|
||||
- `real-runtime`
|
||||
- Only after deliberate escalation.
|
||||
|
||||
## Real Runtime Escalation
|
||||
|
||||
Escalate only when the change claim depends on actual runtime behavior, for example:
|
||||
|
||||
- overlay appears, hides, or tracks a real mpv window
|
||||
- mpv launch flags or pause-until-ready behavior
|
||||
- plugin/socket/auto-start handshake under a real player
|
||||
- macOS/window-tracker/focus-sensitive behavior
|
||||
|
||||
If the environment cannot support authoritative runtime verification, report the blocker explicitly. Do not silently downgrade a runtime-required claim to a pass.
|
||||
|
||||
## Artifact contract
|
||||
|
||||
The verifier writes under `.tmp/skill-verification/<timestamp>/`:
|
||||
|
||||
- `summary.json`
|
||||
- `summary.txt`
|
||||
- `classification.txt`
|
||||
- `env.txt`
|
||||
- `lanes.txt`
|
||||
- `steps.tsv`
|
||||
- `steps/*.stdout.log`
|
||||
- `steps/*.stderr.log`
|
||||
|
||||
On failure, quote the exact failing command and point at the artifact directory.
|
||||
|
||||
@@ -1,13 +1,163 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
REPO_ROOT=$(cd "$SCRIPT_DIR/../../../.." && pwd)
|
||||
TARGET="$REPO_ROOT/plugins/subminer-workflow/skills/subminer-change-verification/scripts/classify_subminer_diff.sh"
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: classify_subminer_diff.sh [path ...]
|
||||
|
||||
if [[ ! -x "$TARGET" ]]; then
|
||||
echo "Missing canonical script: $TARGET" >&2
|
||||
exit 1
|
||||
Emit suggested verification lanes for explicit paths or current local git changes.
|
||||
|
||||
Output format:
|
||||
lane:<name>
|
||||
flag:<name>
|
||||
reason:<text>
|
||||
EOF
|
||||
}
|
||||
|
||||
has_item() {
|
||||
local needle=$1
|
||||
shift || true
|
||||
local item
|
||||
for item in "$@"; do
|
||||
if [[ "$item" == "$needle" ]]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
add_lane() {
|
||||
local lane=$1
|
||||
if ! has_item "$lane" "${LANES[@]:-}"; then
|
||||
LANES+=("$lane")
|
||||
fi
|
||||
}
|
||||
|
||||
add_flag() {
|
||||
local flag=$1
|
||||
if ! has_item "$flag" "${FLAGS[@]:-}"; then
|
||||
FLAGS+=("$flag")
|
||||
fi
|
||||
}
|
||||
|
||||
add_reason() {
|
||||
REASONS+=("$1")
|
||||
}
|
||||
|
||||
collect_git_paths() {
|
||||
local top_level
|
||||
if ! top_level=$(git rev-parse --show-toplevel 2>/dev/null); then
|
||||
return 0
|
||||
fi
|
||||
|
||||
(
|
||||
cd "$top_level"
|
||||
if git rev-parse --verify HEAD >/dev/null 2>&1; then
|
||||
git diff --name-only --relative HEAD --
|
||||
git diff --name-only --relative --cached --
|
||||
else
|
||||
git diff --name-only --relative --
|
||||
git diff --name-only --relative --cached --
|
||||
fi
|
||||
git ls-files --others --exclude-standard
|
||||
) | awk 'NF' | sort -u
|
||||
}
|
||||
|
||||
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
|
||||
usage
|
||||
exit 0
|
||||
fi
|
||||
|
||||
exec "$TARGET" "$@"
|
||||
declare -a PATHS=()
|
||||
declare -a LANES=()
|
||||
declare -a FLAGS=()
|
||||
declare -a REASONS=()
|
||||
|
||||
if [[ $# -gt 0 ]]; then
|
||||
while [[ $# -gt 0 ]]; do
|
||||
PATHS+=("$1")
|
||||
shift
|
||||
done
|
||||
else
|
||||
while IFS= read -r line; do
|
||||
[[ -n "$line" ]] && PATHS+=("$line")
|
||||
done < <(collect_git_paths)
|
||||
fi
|
||||
|
||||
if [[ ${#PATHS[@]} -eq 0 ]]; then
|
||||
add_lane "core"
|
||||
add_reason "no changed paths detected -> default to core"
|
||||
fi
|
||||
|
||||
for path in "${PATHS[@]}"; do
|
||||
specialized=0
|
||||
|
||||
case "$path" in
|
||||
docs-site/*|docs/*|changes/*|README.md)
|
||||
add_lane "docs"
|
||||
add_reason "$path -> docs"
|
||||
specialized=1
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$path" in
|
||||
src/config/*|src/generate-config-example.ts|src/verify-config-example.ts|docs-site/public/config.example.jsonc|config.example.jsonc)
|
||||
add_lane "config"
|
||||
add_reason "$path -> config"
|
||||
specialized=1
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$path" in
|
||||
launcher/*|plugin/subminer/*|plugin/subminer.conf|scripts/test-plugin-*|scripts/get-mpv-window-*|scripts/configure-plugin-binary-path.mjs)
|
||||
add_lane "launcher-plugin"
|
||||
add_reason "$path -> launcher-plugin"
|
||||
add_flag "real-runtime-candidate"
|
||||
add_reason "$path -> real-runtime-candidate"
|
||||
specialized=1
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$path" in
|
||||
src/main.ts|src/main-entry.ts|src/preload.ts|src/main/*|src/core/services/mpv*|src/core/services/overlay*|src/renderer/*|src/window-trackers/*|scripts/prepare-build-assets.mjs)
|
||||
add_lane "runtime-compat"
|
||||
add_reason "$path -> runtime-compat"
|
||||
add_flag "real-runtime-candidate"
|
||||
add_reason "$path -> real-runtime-candidate"
|
||||
specialized=1
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ "$specialized" == "0" ]]; then
|
||||
case "$path" in
|
||||
src/*|package.json|tsconfig*.json|scripts/*|Makefile)
|
||||
add_lane "core"
|
||||
add_reason "$path -> core"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
case "$path" in
|
||||
package.json|src/main.ts|src/main-entry.ts|src/preload.ts)
|
||||
add_flag "broad-impact"
|
||||
add_reason "$path -> broad-impact"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ ${#LANES[@]} -eq 0 ]]; then
|
||||
add_lane "core"
|
||||
add_reason "no lane-specific matches -> default to core"
|
||||
fi
|
||||
|
||||
for lane in "${LANES[@]}"; do
|
||||
printf 'lane:%s\n' "$lane"
|
||||
done
|
||||
|
||||
for flag in "${FLAGS[@]}"; do
|
||||
printf 'flag:%s\n' "$flag"
|
||||
done
|
||||
|
||||
for reason in "${REASONS[@]}"; do
|
||||
printf 'reason:%s\n' "$reason"
|
||||
done
|
||||
|
||||
@@ -1,13 +1,566 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
REPO_ROOT=$(cd "$SCRIPT_DIR/../../../.." && pwd)
|
||||
TARGET="$REPO_ROOT/plugins/subminer-workflow/skills/subminer-change-verification/scripts/verify_subminer_change.sh"
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: verify_subminer_change.sh [options] [path ...]
|
||||
|
||||
if [[ ! -x "$TARGET" ]]; then
|
||||
echo "Missing canonical script: $TARGET" >&2
|
||||
exit 1
|
||||
Options:
|
||||
--lane <name> Force a verification lane. Repeatable.
|
||||
--artifact-dir <dir> Use an explicit artifact directory.
|
||||
--allow-real-runtime Allow explicit real-runtime execution.
|
||||
--allow-real-gui Deprecated alias for --allow-real-runtime.
|
||||
--dry-run Record planned steps without executing commands.
|
||||
--help Show this help text.
|
||||
|
||||
If no lanes are supplied, the script classifies the provided paths. If no paths are
|
||||
provided, it classifies the current local git changes.
|
||||
|
||||
Authoritative real-runtime verification should be requested with explicit path
|
||||
arguments instead of relying on inferred local git changes.
|
||||
EOF
|
||||
}
|
||||
|
||||
timestamp() {
|
||||
date +%Y%m%d-%H%M%S
|
||||
}
|
||||
|
||||
timestamp_iso() {
|
||||
date -u +%Y-%m-%dT%H:%M:%SZ
|
||||
}
|
||||
|
||||
generate_session_id() {
|
||||
local tmp_dir
|
||||
tmp_dir=$(mktemp -d "${TMPDIR:-/tmp}/subminer-verify-$(timestamp)-XXXXXX")
|
||||
basename "$tmp_dir"
|
||||
rmdir "$tmp_dir"
|
||||
}
|
||||
|
||||
has_item() {
|
||||
local needle=$1
|
||||
shift || true
|
||||
local item
|
||||
for item in "$@"; do
|
||||
if [[ "$item" == "$needle" ]]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
normalize_lane_name() {
|
||||
case "$1" in
|
||||
real-gui)
|
||||
printf '%s' "real-runtime"
|
||||
;;
|
||||
*)
|
||||
printf '%s' "$1"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
add_lane() {
|
||||
local lane
|
||||
lane=$(normalize_lane_name "$1")
|
||||
if ! has_item "$lane" "${SELECTED_LANES[@]:-}"; then
|
||||
SELECTED_LANES+=("$lane")
|
||||
fi
|
||||
}
|
||||
|
||||
add_blocker() {
|
||||
BLOCKERS+=("$1")
|
||||
BLOCKED=1
|
||||
}
|
||||
|
||||
append_step_record() {
|
||||
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \
|
||||
"$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" >>"$STEPS_TSV"
|
||||
}
|
||||
|
||||
record_env() {
|
||||
{
|
||||
printf 'repo_root=%s\n' "$REPO_ROOT"
|
||||
printf 'session_id=%s\n' "$SESSION_ID"
|
||||
printf 'artifact_dir=%s\n' "$ARTIFACT_DIR"
|
||||
printf 'path_selection_mode=%s\n' "$PATH_SELECTION_MODE"
|
||||
printf 'dry_run=%s\n' "$DRY_RUN"
|
||||
printf 'allow_real_runtime=%s\n' "$ALLOW_REAL_RUNTIME"
|
||||
printf 'session_home=%s\n' "$SESSION_HOME"
|
||||
printf 'session_xdg_config_home=%s\n' "$SESSION_XDG_CONFIG_HOME"
|
||||
printf 'session_mpv_dir=%s\n' "$SESSION_MPV_DIR"
|
||||
printf 'session_logs_dir=%s\n' "$SESSION_LOGS_DIR"
|
||||
printf 'session_mpv_log=%s\n' "$SESSION_MPV_LOG"
|
||||
printf 'pwd=%s\n' "$(pwd)"
|
||||
git rev-parse --short HEAD 2>/dev/null | sed 's/^/git_head=/' || true
|
||||
git status --short 2>/dev/null || true
|
||||
if [[ ${#PATH_ARGS[@]} -gt 0 ]]; then
|
||||
printf 'requested_paths=\n'
|
||||
printf ' %s\n' "${PATH_ARGS[@]}"
|
||||
fi
|
||||
} >"$ARTIFACT_DIR/env.txt"
|
||||
}
|
||||
|
||||
run_step() {
|
||||
local lane=$1
|
||||
local name=$2
|
||||
local command=$3
|
||||
local note=${4:-}
|
||||
local slug=${name//[^a-zA-Z0-9_-]/-}
|
||||
local stdout_rel="steps/${slug}.stdout.log"
|
||||
local stderr_rel="steps/${slug}.stderr.log"
|
||||
local stdout_path="$ARTIFACT_DIR/$stdout_rel"
|
||||
local stderr_path="$ARTIFACT_DIR/$stderr_rel"
|
||||
local status exit_code
|
||||
|
||||
COMMANDS_RUN+=("$command")
|
||||
printf '%s\n' "$command" >"$ARTIFACT_DIR/steps/${slug}.command.txt"
|
||||
|
||||
if [[ "$DRY_RUN" == "1" ]]; then
|
||||
printf '[dry-run] %s\n' "$command" >"$stdout_path"
|
||||
: >"$stderr_path"
|
||||
status="dry-run"
|
||||
exit_code=0
|
||||
else
|
||||
if bash -lc "cd \"$REPO_ROOT\" && $command" >"$stdout_path" 2>"$stderr_path"; then
|
||||
status="passed"
|
||||
exit_code=0
|
||||
EXECUTED_REAL_STEPS=1
|
||||
else
|
||||
exit_code=$?
|
||||
status="failed"
|
||||
FAILED=1
|
||||
fi
|
||||
fi
|
||||
|
||||
append_step_record "$lane" "$name" "$status" "$exit_code" "$command" "$stdout_rel" "$stderr_rel" "$note"
|
||||
printf '%s\t%s\t%s\n' "$lane" "$name" "$status"
|
||||
|
||||
if [[ "$status" == "failed" ]]; then
|
||||
FAILURE_STEP="$name"
|
||||
FAILURE_COMMAND="$command"
|
||||
FAILURE_STDOUT="$stdout_rel"
|
||||
FAILURE_STDERR="$stderr_rel"
|
||||
return "$exit_code"
|
||||
fi
|
||||
}
|
||||
|
||||
record_nonpassing_step() {
|
||||
local lane=$1
|
||||
local name=$2
|
||||
local status=$3
|
||||
local note=$4
|
||||
local slug=${name//[^a-zA-Z0-9_-]/-}
|
||||
local stdout_rel="steps/${slug}.stdout.log"
|
||||
local stderr_rel="steps/${slug}.stderr.log"
|
||||
printf '%s\n' "$note" >"$ARTIFACT_DIR/$stdout_rel"
|
||||
: >"$ARTIFACT_DIR/$stderr_rel"
|
||||
append_step_record "$lane" "$name" "$status" "0" "" "$stdout_rel" "$stderr_rel" "$note"
|
||||
printf '%s\t%s\t%s\n' "$lane" "$name" "$status"
|
||||
}
|
||||
|
||||
record_skipped_step() {
|
||||
record_nonpassing_step "$1" "$2" "skipped" "$3"
|
||||
}
|
||||
|
||||
record_blocked_step() {
|
||||
add_blocker "$3"
|
||||
record_nonpassing_step "$1" "$2" "blocked" "$3"
|
||||
}
|
||||
|
||||
record_failed_step() {
|
||||
FAILED=1
|
||||
FAILURE_STEP=$2
|
||||
FAILURE_COMMAND=${FAILURE_COMMAND:-"(validation)"}
|
||||
FAILURE_STDOUT="steps/${2//[^a-zA-Z0-9_-]/-}.stdout.log"
|
||||
FAILURE_STDERR="steps/${2//[^a-zA-Z0-9_-]/-}.stderr.log"
|
||||
add_blocker "$3"
|
||||
record_nonpassing_step "$1" "$2" "failed" "$3"
|
||||
}
|
||||
|
||||
find_real_runtime_helper() {
|
||||
local candidate
|
||||
for candidate in \
|
||||
"$SCRIPT_DIR/run_real_runtime_smoke.sh" \
|
||||
"$SCRIPT_DIR/run_real_mpv_smoke.sh"; do
|
||||
if [[ -x "$candidate" ]]; then
|
||||
printf '%s' "$candidate"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
acquire_real_runtime_lease() {
|
||||
local lease_root="$REPO_ROOT/.tmp/skill-verification/locks"
|
||||
local lease_dir="$lease_root/exclusive-real-runtime"
|
||||
mkdir -p "$lease_root"
|
||||
if mkdir "$lease_dir" 2>/dev/null; then
|
||||
REAL_RUNTIME_LEASE_DIR="$lease_dir"
|
||||
printf '%s\n' "$SESSION_ID" >"$lease_dir/session_id"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local owner=""
|
||||
if [[ -f "$lease_dir/session_id" ]]; then
|
||||
owner=$(cat "$lease_dir/session_id")
|
||||
fi
|
||||
add_blocker "real-runtime lease already held${owner:+ by $owner}"
|
||||
return 1
|
||||
}
|
||||
|
||||
release_real_runtime_lease() {
|
||||
if [[ -n "$REAL_RUNTIME_LEASE_DIR" && -d "$REAL_RUNTIME_LEASE_DIR" ]]; then
|
||||
if [[ -f "$REAL_RUNTIME_LEASE_DIR/session_id" ]]; then
|
||||
local owner
|
||||
owner=$(cat "$REAL_RUNTIME_LEASE_DIR/session_id")
|
||||
if [[ "$owner" != "$SESSION_ID" ]]; then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
rm -rf "$REAL_RUNTIME_LEASE_DIR"
|
||||
fi
|
||||
}
|
||||
|
||||
compute_final_status() {
|
||||
if [[ "$FAILED" == "1" ]]; then
|
||||
FINAL_STATUS="failed"
|
||||
elif [[ "$BLOCKED" == "1" ]]; then
|
||||
FINAL_STATUS="blocked"
|
||||
elif [[ "$EXECUTED_REAL_STEPS" == "1" ]]; then
|
||||
FINAL_STATUS="passed"
|
||||
else
|
||||
FINAL_STATUS="skipped"
|
||||
fi
|
||||
}
|
||||
|
||||
write_summary_files() {
|
||||
local lane_lines
|
||||
lane_lines=$(printf '%s\n' "${SELECTED_LANES[@]}")
|
||||
printf '%s\n' "$lane_lines" >"$ARTIFACT_DIR/lanes.txt"
|
||||
printf '%s\n' "${BLOCKERS[@]}" >"$ARTIFACT_DIR/blockers.txt"
|
||||
printf '%s\n' "${PATH_ARGS[@]}" >"$ARTIFACT_DIR/requested-paths.txt"
|
||||
|
||||
ARTIFACT_DIR_ENV="$ARTIFACT_DIR" \
|
||||
SESSION_ID_ENV="$SESSION_ID" \
|
||||
FINAL_STATUS_ENV="$FINAL_STATUS" \
|
||||
PATH_SELECTION_MODE_ENV="$PATH_SELECTION_MODE" \
|
||||
ALLOW_REAL_RUNTIME_ENV="$ALLOW_REAL_RUNTIME" \
|
||||
SESSION_HOME_ENV="$SESSION_HOME" \
|
||||
SESSION_XDG_CONFIG_HOME_ENV="$SESSION_XDG_CONFIG_HOME" \
|
||||
SESSION_MPV_DIR_ENV="$SESSION_MPV_DIR" \
|
||||
SESSION_LOGS_DIR_ENV="$SESSION_LOGS_DIR" \
|
||||
SESSION_MPV_LOG_ENV="$SESSION_MPV_LOG" \
|
||||
STARTED_AT_ENV="$STARTED_AT" \
|
||||
FINISHED_AT_ENV="$FINISHED_AT" \
|
||||
FAILED_ENV="$FAILED" \
|
||||
FAILURE_COMMAND_ENV="${FAILURE_COMMAND:-}" \
|
||||
FAILURE_STDOUT_ENV="${FAILURE_STDOUT:-}" \
|
||||
FAILURE_STDERR_ENV="${FAILURE_STDERR:-}" \
|
||||
bun -e '
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
function readLines(filePath) {
|
||||
if (!fs.existsSync(filePath)) return [];
|
||||
return fs.readFileSync(filePath, "utf8").split(/\r?\n/).filter(Boolean);
|
||||
}
|
||||
|
||||
const artifactDir = process.env.ARTIFACT_DIR_ENV;
|
||||
const reportsDir = path.join(artifactDir, "reports");
|
||||
const lanes = readLines(path.join(artifactDir, "lanes.txt"));
|
||||
const blockers = readLines(path.join(artifactDir, "blockers.txt"));
|
||||
const requestedPaths = readLines(path.join(artifactDir, "requested-paths.txt"));
|
||||
const steps = readLines(path.join(artifactDir, "steps.tsv")).map((line) => {
|
||||
const [lane, name, status, exitCode, command, stdout, stderr, note] = line.split("\t");
|
||||
return {
|
||||
lane,
|
||||
name,
|
||||
status,
|
||||
exitCode: Number(exitCode || 0),
|
||||
command,
|
||||
stdout,
|
||||
stderr,
|
||||
note,
|
||||
};
|
||||
});
|
||||
const summary = {
|
||||
sessionId: process.env.SESSION_ID_ENV || "",
|
||||
artifactDir,
|
||||
reportsDir,
|
||||
status: process.env.FINAL_STATUS_ENV || "failed",
|
||||
selectedLanes: lanes,
|
||||
failed: process.env.FAILED_ENV === "1",
|
||||
failure:
|
||||
process.env.FAILED_ENV === "1"
|
||||
? {
|
||||
command: process.env.FAILURE_COMMAND_ENV || "",
|
||||
stdout: process.env.FAILURE_STDOUT_ENV || "",
|
||||
stderr: process.env.FAILURE_STDERR_ENV || "",
|
||||
}
|
||||
: null,
|
||||
blockers,
|
||||
pathSelectionMode: process.env.PATH_SELECTION_MODE_ENV || "git-inferred",
|
||||
requestedPaths,
|
||||
allowRealRuntime: process.env.ALLOW_REAL_RUNTIME_ENV === "1",
|
||||
startedAt: process.env.STARTED_AT_ENV || "",
|
||||
finishedAt: process.env.FINISHED_AT_ENV || "",
|
||||
env: {
|
||||
home: process.env.SESSION_HOME_ENV || "",
|
||||
xdgConfigHome: process.env.SESSION_XDG_CONFIG_HOME_ENV || "",
|
||||
mpvDir: process.env.SESSION_MPV_DIR_ENV || "",
|
||||
logsDir: process.env.SESSION_LOGS_DIR_ENV || "",
|
||||
mpvLog: process.env.SESSION_MPV_LOG_ENV || "",
|
||||
},
|
||||
steps,
|
||||
};
|
||||
|
||||
const summaryJson = JSON.stringify(summary, null, 2) + "\n";
|
||||
fs.writeFileSync(path.join(artifactDir, "summary.json"), summaryJson);
|
||||
fs.writeFileSync(path.join(reportsDir, "summary.json"), summaryJson);
|
||||
|
||||
const lines = [];
|
||||
lines.push(`session_id: ${summary.sessionId}`);
|
||||
lines.push(`artifact_dir: ${artifactDir}`);
|
||||
lines.push(`selected_lanes: ${lanes.join(", ") || "(none)"}`);
|
||||
lines.push(`status: ${summary.status}`);
|
||||
lines.push(`path_selection_mode: ${summary.pathSelectionMode}`);
|
||||
if (requestedPaths.length > 0) {
|
||||
lines.push(`requested_paths: ${requestedPaths.join(", ")}`);
|
||||
}
|
||||
if (blockers.length > 0) {
|
||||
lines.push(`blockers: ${blockers.join(" | ")}`);
|
||||
}
|
||||
for (const step of steps) {
|
||||
lines.push(`${step.lane}/${step.name}: ${step.status}`);
|
||||
if (step.command) lines.push(` command: ${step.command}`);
|
||||
lines.push(` stdout: ${step.stdout}`);
|
||||
lines.push(` stderr: ${step.stderr}`);
|
||||
if (step.note) lines.push(` note: ${step.note}`);
|
||||
}
|
||||
if (summary.failed) {
|
||||
lines.push(`failure_command: ${process.env.FAILURE_COMMAND_ENV || ""}`);
|
||||
}
|
||||
const summaryText = lines.join("\n") + "\n";
|
||||
fs.writeFileSync(path.join(artifactDir, "summary.txt"), summaryText);
|
||||
fs.writeFileSync(path.join(reportsDir, "summary.txt"), summaryText);
|
||||
'
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
release_real_runtime_lease
|
||||
}
|
||||
|
||||
CLASSIFIER_OUTPUT=""
|
||||
ARTIFACT_DIR=""
|
||||
ALLOW_REAL_RUNTIME=0
|
||||
DRY_RUN=0
|
||||
FAILED=0
|
||||
BLOCKED=0
|
||||
EXECUTED_REAL_STEPS=0
|
||||
FINAL_STATUS=""
|
||||
FAILURE_STEP=""
|
||||
FAILURE_COMMAND=""
|
||||
FAILURE_STDOUT=""
|
||||
FAILURE_STDERR=""
|
||||
REAL_RUNTIME_LEASE_DIR=""
|
||||
STARTED_AT=""
|
||||
FINISHED_AT=""
|
||||
|
||||
declare -a EXPLICIT_LANES=()
|
||||
declare -a SELECTED_LANES=()
|
||||
declare -a PATH_ARGS=()
|
||||
declare -a COMMANDS_RUN=()
|
||||
declare -a BLOCKERS=()
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--lane)
|
||||
EXPLICIT_LANES+=("$(normalize_lane_name "$2")")
|
||||
shift 2
|
||||
;;
|
||||
--artifact-dir)
|
||||
ARTIFACT_DIR=$2
|
||||
shift 2
|
||||
;;
|
||||
--allow-real-runtime|--allow-real-gui)
|
||||
ALLOW_REAL_RUNTIME=1
|
||||
shift
|
||||
;;
|
||||
--dry-run)
|
||||
DRY_RUN=1
|
||||
shift
|
||||
;;
|
||||
--help|-h)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
while [[ $# -gt 0 ]]; do
|
||||
PATH_ARGS+=("$1")
|
||||
shift
|
||||
done
|
||||
;;
|
||||
*)
|
||||
PATH_ARGS+=("$1")
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
|
||||
SESSION_ID=$(generate_session_id)
|
||||
PATH_SELECTION_MODE="git-inferred"
|
||||
if [[ ${#PATH_ARGS[@]} -gt 0 ]]; then
|
||||
PATH_SELECTION_MODE="explicit"
|
||||
fi
|
||||
|
||||
exec "$TARGET" "$@"
|
||||
if [[ -z "$ARTIFACT_DIR" ]]; then
|
||||
mkdir -p "$REPO_ROOT/.tmp/skill-verification"
|
||||
ARTIFACT_DIR="$REPO_ROOT/.tmp/skill-verification/$SESSION_ID"
|
||||
fi
|
||||
|
||||
SESSION_HOME="$ARTIFACT_DIR/home"
|
||||
SESSION_XDG_CONFIG_HOME="$ARTIFACT_DIR/xdg"
|
||||
SESSION_MPV_DIR="$ARTIFACT_DIR/mpv"
|
||||
SESSION_LOGS_DIR="$ARTIFACT_DIR/logs"
|
||||
SESSION_MPV_LOG="$SESSION_LOGS_DIR/mpv.log"
|
||||
|
||||
mkdir -p "$ARTIFACT_DIR/steps" "$ARTIFACT_DIR/reports" "$SESSION_HOME" "$SESSION_XDG_CONFIG_HOME" "$SESSION_MPV_DIR" "$SESSION_LOGS_DIR"
|
||||
STEPS_TSV="$ARTIFACT_DIR/steps.tsv"
|
||||
: >"$STEPS_TSV"
|
||||
|
||||
trap cleanup EXIT
|
||||
STARTED_AT=$(timestamp_iso)
|
||||
|
||||
if [[ ${#EXPLICIT_LANES[@]} -gt 0 ]]; then
|
||||
local_lane=""
|
||||
for local_lane in "${EXPLICIT_LANES[@]}"; do
|
||||
add_lane "$local_lane"
|
||||
done
|
||||
printf 'reason:explicit lanes supplied\n' >"$ARTIFACT_DIR/classification.txt"
|
||||
else
|
||||
if [[ ${#PATH_ARGS[@]} -gt 0 ]]; then
|
||||
CLASSIFIER_OUTPUT=$(bash "$SCRIPT_DIR/classify_subminer_diff.sh" "${PATH_ARGS[@]}")
|
||||
else
|
||||
CLASSIFIER_OUTPUT=$(bash "$SCRIPT_DIR/classify_subminer_diff.sh")
|
||||
fi
|
||||
printf '%s\n' "$CLASSIFIER_OUTPUT" >"$ARTIFACT_DIR/classification.txt"
|
||||
while IFS= read -r line; do
|
||||
case "$line" in
|
||||
lane:*)
|
||||
add_lane "${line#lane:}"
|
||||
;;
|
||||
esac
|
||||
done <<<"$CLASSIFIER_OUTPUT"
|
||||
fi
|
||||
|
||||
record_env
|
||||
|
||||
printf 'artifact_dir=%s\n' "$ARTIFACT_DIR"
|
||||
printf 'selected_lanes=%s\n' "$(IFS=,; echo "${SELECTED_LANES[*]}")"
|
||||
|
||||
for lane in "${SELECTED_LANES[@]}"; do
|
||||
case "$lane" in
|
||||
docs)
|
||||
run_step "$lane" "docs-test" "bun run docs:test" || break
|
||||
[[ "$FAILED" == "1" ]] && break
|
||||
run_step "$lane" "docs-build" "bun run docs:build" || break
|
||||
;;
|
||||
config)
|
||||
run_step "$lane" "test-config" "bun run test:config" || break
|
||||
;;
|
||||
core)
|
||||
run_step "$lane" "typecheck" "bun run typecheck" || break
|
||||
[[ "$FAILED" == "1" ]] && break
|
||||
run_step "$lane" "test-fast" "bun run test:fast" || break
|
||||
;;
|
||||
launcher-plugin)
|
||||
run_step "$lane" "launcher-smoke-src" "bun run test:launcher:smoke:src" || break
|
||||
[[ "$FAILED" == "1" ]] && break
|
||||
run_step "$lane" "plugin-src" "bun run test:plugin:src" || break
|
||||
;;
|
||||
runtime-compat)
|
||||
run_step "$lane" "build" "bun run build" || break
|
||||
[[ "$FAILED" == "1" ]] && break
|
||||
run_step "$lane" "test-runtime-compat" "bun run test:runtime:compat" || break
|
||||
[[ "$FAILED" == "1" ]] && break
|
||||
run_step "$lane" "test-smoke-dist" "bun run test:smoke:dist" || break
|
||||
;;
|
||||
real-runtime)
|
||||
if [[ "$PATH_SELECTION_MODE" != "explicit" ]]; then
|
||||
record_blocked_step \
|
||||
"$lane" \
|
||||
"real-runtime-guard" \
|
||||
"real-runtime lane requires explicit paths; inferred local git changes are non-authoritative"
|
||||
break
|
||||
fi
|
||||
|
||||
if [[ "$ALLOW_REAL_RUNTIME" != "1" ]]; then
|
||||
record_blocked_step \
|
||||
"$lane" \
|
||||
"real-runtime-guard" \
|
||||
"real-runtime lane requested but --allow-real-runtime was not supplied"
|
||||
break
|
||||
fi
|
||||
|
||||
if ! acquire_real_runtime_lease; then
|
||||
record_blocked_step \
|
||||
"$lane" \
|
||||
"real-runtime-lease" \
|
||||
"real-runtime lease already held; rerun after the active runtime verification finishes"
|
||||
break
|
||||
fi
|
||||
|
||||
if ! REAL_RUNTIME_HELPER=$(find_real_runtime_helper); then
|
||||
record_blocked_step \
|
||||
"$lane" \
|
||||
"real-runtime-helper" \
|
||||
"real-runtime helper not implemented yet"
|
||||
break
|
||||
fi
|
||||
|
||||
printf -v REAL_RUNTIME_COMMAND \
|
||||
'SESSION_ID=%q HOME=%q XDG_CONFIG_HOME=%q SUBMINER_MPV_LOG=%q bash %q' \
|
||||
"$SESSION_ID" \
|
||||
"$SESSION_HOME" \
|
||||
"$SESSION_XDG_CONFIG_HOME" \
|
||||
"$SESSION_MPV_LOG" \
|
||||
"$REAL_RUNTIME_HELPER"
|
||||
|
||||
run_step "$lane" "real-runtime-smoke" "$REAL_RUNTIME_COMMAND" || break
|
||||
;;
|
||||
*)
|
||||
record_failed_step "$lane" "lane-validation" "unknown lane: $lane"
|
||||
break
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ "$FAILED" == "1" || "$BLOCKED" == "1" ]]; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
FINISHED_AT=$(timestamp_iso)
|
||||
compute_final_status
|
||||
write_summary_files
|
||||
|
||||
printf 'status=%s\n' "$FINAL_STATUS"
|
||||
printf 'artifact_dir=%s\n' "$ARTIFACT_DIR"
|
||||
|
||||
case "$FINAL_STATUS" in
|
||||
failed)
|
||||
printf 'result=failed\n'
|
||||
printf 'failure_command=%s\n' "$FAILURE_COMMAND"
|
||||
exit 1
|
||||
;;
|
||||
blocked)
|
||||
printf 'result=blocked\n'
|
||||
exit 2
|
||||
;;
|
||||
*)
|
||||
printf 'result=ok\n'
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -1,18 +1,146 @@
|
||||
---
|
||||
name: 'subminer-scrum-master'
|
||||
description: 'Compatibility shim. Canonical SubMiner scrum-master workflow now lives in the repo-local subminer-workflow plugin.'
|
||||
name: "subminer-scrum-master"
|
||||
description: "Use in the SubMiner repo when a request should be turned into planned work and driven through execution. Assesses whether backlog tracking is warranted, creates or updates tasks when needed, records a plan, dispatches one or more subagents, and requires verification before handoff."
|
||||
---
|
||||
|
||||
# Compatibility Shim
|
||||
# SubMiner Scrum Master
|
||||
|
||||
Canonical source:
|
||||
Own workflow, not code by default.
|
||||
|
||||
- `plugins/subminer-workflow/skills/subminer-scrum-master/SKILL.md`
|
||||
Use this skill when the user gives a feature request, bug report, issue, refactor, or implementation ask and the agent should manage intake, planning, backlog hygiene, worker dispatch, and verification through completion.
|
||||
|
||||
When this shim is invoked:
|
||||
## Core Rules
|
||||
|
||||
1. Read the canonical plugin-owned skill.
|
||||
2. Follow the plugin-owned skill as the source of truth.
|
||||
3. Do not duplicate workflow changes here; update the plugin-owned skill instead.
|
||||
1. Decide first whether backlog tracking is warranted.
|
||||
2. If backlog is needed, search first. Update existing work when it clearly matches.
|
||||
3. If backlog is not needed, keep the process light. Do not invent ticket ceremony.
|
||||
4. Record a plan before dispatching coding work.
|
||||
5. Use parent + subtasks for multi-part work when backlog is used.
|
||||
6. Dispatch conservatively. Parallelize only disjoint write scopes.
|
||||
7. Require verification before handoff, typically via `subminer-change-verification`.
|
||||
8. Report backlog actions, dispatched workers, verification, blockers, and remaining risks.
|
||||
|
||||
This shim exists so existing repo references and prompts keep resolving during the migration to the repo-local plugin workflow.
|
||||
## Backlog Decision
|
||||
|
||||
Skip backlog when the request is:
|
||||
- question only
|
||||
- obvious mechanical edit
|
||||
- tiny isolated change with no real planning
|
||||
|
||||
Use backlog when the work:
|
||||
- needs planning or scope decisions
|
||||
- spans multiple phases or subsystems
|
||||
- is likely to need subagent dispatch
|
||||
- should remain traceable for handoff/resume
|
||||
|
||||
If backlog is used:
|
||||
- search existing tasks first
|
||||
- create/update a standalone task for one focused deliverable
|
||||
- create/update a parent task plus subtasks for multi-part work
|
||||
- record the implementation plan in the task before implementation begins
|
||||
|
||||
## Intake Workflow
|
||||
|
||||
1. Parse the request.
|
||||
Classify it as question, mechanical edit, bugfix, feature, refactor, investigation, or follow-up.
|
||||
2. Decide whether backlog is needed.
|
||||
3. If backlog is needed:
|
||||
- search first
|
||||
- update existing task if clearly relevant
|
||||
- otherwise create the right structure
|
||||
- write the implementation plan before dispatch
|
||||
4. If backlog is skipped:
|
||||
- write a short working plan in-thread
|
||||
- proceed without fake ticketing
|
||||
5. Choose execution mode:
|
||||
- no subagents for trivial work
|
||||
- one worker for focused work
|
||||
- parallel workers only for disjoint scopes
|
||||
6. Run verification before handoff.
|
||||
|
||||
## Dispatch Rules
|
||||
|
||||
The scrum master orchestrates. Workers implement.
|
||||
|
||||
- Do not become the default implementer unless delegation is unnecessary.
|
||||
- Do not parallelize overlapping files or tightly coupled runtime work.
|
||||
- Give every worker explicit ownership of files/modules.
|
||||
- Tell every worker other agents may be active and they must not revert unrelated edits.
|
||||
- Require each worker to report:
|
||||
- changed files
|
||||
- tests run
|
||||
- blockers
|
||||
|
||||
Use worker agents for implementation and explorer agents only for bounded codebase questions.
|
||||
|
||||
## Verification
|
||||
|
||||
Every nontrivial code task gets verification.
|
||||
|
||||
Preferred flow:
|
||||
1. use `subminer-change-verification`
|
||||
2. start with the cheapest sufficient lane
|
||||
3. escalate only when needed
|
||||
4. if worker verification is sufficient, accept it or run one final consolidating pass
|
||||
|
||||
Never hand off nontrivial work without stating what was verified and what was skipped.
|
||||
|
||||
## Pre-Handoff Policy Checks (Required)
|
||||
|
||||
Before handoff, always ask and answer both of these questions explicitly:
|
||||
|
||||
1. **Docs update required?**
|
||||
2. **Changelog fragment required?**
|
||||
|
||||
Rules:
|
||||
- Do not assume silence implies "no." Record an explicit yes/no decision for each item.
|
||||
- If the answer is yes, either complete the update or report the blocker before handoff.
|
||||
- Include the final answers in the handoff summary even when both answers are "no."
|
||||
|
||||
## Failure / Scope Handling
|
||||
|
||||
- If a worker hits ambiguity, pause and ask the user.
|
||||
- If verification fails, either:
|
||||
- send the worker back with exact failure context, or
|
||||
- fix it directly if it is tiny and clearly in scope
|
||||
- If new scope appears, revisit backlog structure before silently expanding work.
|
||||
|
||||
## Representative Flows
|
||||
|
||||
### Trivial no-ticket work
|
||||
|
||||
- decide backlog is unnecessary
|
||||
- keep a short plan
|
||||
- implement directly or with one worker if helpful
|
||||
- run targeted verification
|
||||
- report outcome concisely
|
||||
|
||||
### Single-task implementation
|
||||
|
||||
- search/create/update one task
|
||||
- record plan
|
||||
- dispatch one worker
|
||||
- integrate
|
||||
- verify
|
||||
- update task and report outcome
|
||||
|
||||
### Parent + subtasks execution
|
||||
|
||||
- search/create/update parent task
|
||||
- create subtasks for distinct deliverables/phases
|
||||
- record sequencing in the plan
|
||||
- dispatch workers only where scopes are disjoint
|
||||
- integrate
|
||||
- run consolidated verification
|
||||
- update task state and report outcome
|
||||
|
||||
## Output Expectations
|
||||
|
||||
At the end, report:
|
||||
- whether backlog was used and what changed
|
||||
- which workers were dispatched and what they owned
|
||||
- what verification ran
|
||||
- explicit answers to:
|
||||
- docs update required?
|
||||
- changelog fragment required?
|
||||
- blockers, skips, and risks
|
||||
|
||||
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
@@ -61,16 +61,6 @@ jobs:
|
||||
- name: Test suite (source)
|
||||
run: bun run test:fast
|
||||
|
||||
- name: Coverage suite (maintained source lane)
|
||||
run: bun run test:coverage:src
|
||||
|
||||
- name: Upload coverage artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: coverage-test-src
|
||||
path: coverage/test-src/lcov.info
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Launcher smoke suite (source)
|
||||
run: bun run test:launcher:smoke:src
|
||||
|
||||
|
||||
389
.github/workflows/prerelease.yml
vendored
389
.github/workflows/prerelease.yml
vendored
@@ -1,389 +0,0 @@
|
||||
name: Prerelease
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*-beta.*'
|
||||
- 'v*-rc.*'
|
||||
|
||||
concurrency:
|
||||
group: prerelease-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
quality-gate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: 1.3.5
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.bun/install/cache
|
||||
node_modules
|
||||
stats/node_modules
|
||||
vendor/subminer-yomitan/node_modules
|
||||
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'stats/bun.lock', 'vendor/subminer-yomitan/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-bun-
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
bun install --frozen-lockfile
|
||||
cd stats && bun install --frozen-lockfile
|
||||
|
||||
- name: Lint stats (formatting)
|
||||
run: bun run lint:stats
|
||||
|
||||
- name: Build (TypeScript check)
|
||||
run: bun run typecheck
|
||||
|
||||
- name: Test suite (source)
|
||||
run: bun run test:fast
|
||||
|
||||
- name: Coverage suite (maintained source lane)
|
||||
run: bun run test:coverage:src
|
||||
|
||||
- name: Upload coverage artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: coverage-test-src
|
||||
path: coverage/test-src/lcov.info
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Launcher smoke suite (source)
|
||||
run: bun run test:launcher:smoke:src
|
||||
|
||||
- name: Upload launcher smoke artifacts (on failure)
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: launcher-smoke
|
||||
path: .tmp/launcher-smoke/**
|
||||
if-no-files-found: ignore
|
||||
|
||||
- name: Build (bundle)
|
||||
run: bun run build
|
||||
|
||||
- name: Immersion SQLite verification
|
||||
run: bun run test:immersion:sqlite:dist
|
||||
|
||||
- name: Dist smoke suite
|
||||
run: bun run test:smoke:dist
|
||||
|
||||
build-linux:
|
||||
needs: [quality-gate]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: 1.3.5
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.bun/install/cache
|
||||
node_modules
|
||||
stats/node_modules
|
||||
vendor/texthooker-ui/node_modules
|
||||
vendor/subminer-yomitan/node_modules
|
||||
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'stats/bun.lock', 'vendor/texthooker-ui/package.json', 'vendor/subminer-yomitan/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-bun-
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
bun install --frozen-lockfile
|
||||
cd stats && bun install --frozen-lockfile
|
||||
|
||||
- name: Build texthooker-ui
|
||||
run: |
|
||||
cd vendor/texthooker-ui
|
||||
bun install
|
||||
bun run build
|
||||
|
||||
- name: Build AppImage
|
||||
run: bun run build:appimage
|
||||
|
||||
- name: Build unversioned AppImage
|
||||
run: |
|
||||
shopt -s nullglob
|
||||
appimages=(release/SubMiner-*.AppImage)
|
||||
if [ "${#appimages[@]}" -eq 0 ]; then
|
||||
echo "No versioned AppImage found to create unversioned artifact."
|
||||
ls -la release
|
||||
exit 1
|
||||
fi
|
||||
cp "${appimages[0]}" release/SubMiner.AppImage
|
||||
|
||||
- name: Upload AppImage artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: appimage
|
||||
path: release/*.AppImage
|
||||
|
||||
build-macos:
|
||||
needs: [quality-gate]
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: 1.3.5
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.bun/install/cache
|
||||
node_modules
|
||||
stats/node_modules
|
||||
vendor/texthooker-ui/node_modules
|
||||
vendor/subminer-yomitan/node_modules
|
||||
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'stats/bun.lock', 'vendor/texthooker-ui/package.json', 'vendor/subminer-yomitan/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-bun-
|
||||
|
||||
- name: Validate macOS signing/notarization secrets
|
||||
run: |
|
||||
missing=0
|
||||
for name in CSC_LINK CSC_KEY_PASSWORD APPLE_ID APPLE_APP_SPECIFIC_PASSWORD APPLE_TEAM_ID; do
|
||||
if [ -z "${!name}" ]; then
|
||||
echo "Missing required secret: $name"
|
||||
missing=1
|
||||
fi
|
||||
done
|
||||
if [ "$missing" -ne 0 ]; then
|
||||
echo "Set all required macOS signing/notarization secrets and rerun."
|
||||
exit 1
|
||||
fi
|
||||
env:
|
||||
CSC_LINK: ${{ secrets.CSC_LINK }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
bun install --frozen-lockfile
|
||||
cd stats && bun install --frozen-lockfile
|
||||
|
||||
- name: Build texthooker-ui
|
||||
run: |
|
||||
cd vendor/texthooker-ui
|
||||
bun install
|
||||
bun run build
|
||||
|
||||
- name: Build signed + notarized macOS artifacts
|
||||
run: bun run build:mac
|
||||
env:
|
||||
CSC_LINK: ${{ secrets.CSC_LINK }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
|
||||
- name: Upload macOS artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: macos
|
||||
path: |
|
||||
release/*.dmg
|
||||
release/*.zip
|
||||
|
||||
build-windows:
|
||||
needs: [quality-gate]
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: 1.3.5
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.bun/install/cache
|
||||
node_modules
|
||||
stats/node_modules
|
||||
vendor/texthooker-ui/node_modules
|
||||
vendor/subminer-yomitan/node_modules
|
||||
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'stats/bun.lock', 'vendor/texthooker-ui/package.json', 'vendor/subminer-yomitan/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-bun-
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
bun install --frozen-lockfile
|
||||
cd stats && bun install --frozen-lockfile
|
||||
|
||||
- name: Build texthooker-ui
|
||||
shell: powershell
|
||||
run: |
|
||||
Set-Location vendor/texthooker-ui
|
||||
bun install
|
||||
bun run build
|
||||
|
||||
- name: Build unsigned Windows artifacts
|
||||
run: bun run build:win:unsigned
|
||||
|
||||
- name: Upload Windows artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: windows
|
||||
path: |
|
||||
release/*.exe
|
||||
release/*.zip
|
||||
if-no-files-found: error
|
||||
|
||||
release:
|
||||
needs: [build-linux, build-macos, build-windows]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download AppImage
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: appimage
|
||||
path: release
|
||||
|
||||
- name: Download macOS artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: macos
|
||||
path: release
|
||||
|
||||
- name: Download Windows artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: windows
|
||||
path: release
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: 1.3.5
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.bun/install/cache
|
||||
node_modules
|
||||
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-bun-
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install --frozen-lockfile
|
||||
|
||||
- name: Build Bun subminer wrapper
|
||||
run: make build-launcher
|
||||
|
||||
- name: Verify Bun subminer wrapper
|
||||
run: dist/launcher/subminer --help >/dev/null
|
||||
|
||||
- name: Enforce generated launcher workflow
|
||||
run: bash scripts/verify-generated-launcher.sh
|
||||
|
||||
- name: Verify generated config examples
|
||||
run: bun run verify:config-example
|
||||
|
||||
- name: Package optional assets bundle
|
||||
run: |
|
||||
tar -czf "release/subminer-assets.tar.gz" \
|
||||
config.example.jsonc \
|
||||
plugin/subminer \
|
||||
plugin/subminer.conf \
|
||||
assets/themes/subminer.rasi
|
||||
|
||||
- name: Generate checksums
|
||||
run: |
|
||||
shopt -s nullglob
|
||||
files=(release/*.AppImage release/*.dmg release/*.exe release/*.zip release/*.tar.gz dist/launcher/subminer)
|
||||
if [ "${#files[@]}" -eq 0 ]; then
|
||||
echo "No release artifacts found for checksum generation."
|
||||
exit 1
|
||||
fi
|
||||
sha256sum "${files[@]}" > release/SHA256SUMS.txt
|
||||
|
||||
- name: Get version from tag
|
||||
id: version
|
||||
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Generate prerelease notes from pending fragments
|
||||
run: bun run changelog:prerelease-notes --version "${{ steps.version.outputs.VERSION }}"
|
||||
|
||||
- name: Publish Prerelease
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
if gh release view "${{ steps.version.outputs.VERSION }}" >/dev/null 2>&1; then
|
||||
gh release edit "${{ steps.version.outputs.VERSION }}" \
|
||||
--draft=false \
|
||||
--prerelease \
|
||||
--title "${{ steps.version.outputs.VERSION }}" \
|
||||
--notes-file release/prerelease-notes.md
|
||||
else
|
||||
gh release create "${{ steps.version.outputs.VERSION }}" \
|
||||
--latest=false \
|
||||
--prerelease \
|
||||
--title "${{ steps.version.outputs.VERSION }}" \
|
||||
--notes-file release/prerelease-notes.md
|
||||
fi
|
||||
|
||||
shopt -s nullglob
|
||||
artifacts=(
|
||||
release/*.AppImage
|
||||
release/*.dmg
|
||||
release/*.exe
|
||||
release/*.zip
|
||||
release/*.tar.gz
|
||||
release/SHA256SUMS.txt
|
||||
dist/launcher/subminer
|
||||
)
|
||||
|
||||
if [ "${#artifacts[@]}" -eq 0 ]; then
|
||||
echo "No release artifacts found for upload."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for asset in "${artifacts[@]}"; do
|
||||
gh release upload "${{ steps.version.outputs.VERSION }}" "$asset" --clobber
|
||||
done
|
||||
77
.github/workflows/release.yml
vendored
77
.github/workflows/release.yml
vendored
@@ -4,8 +4,6 @@ on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
- '!v*-beta.*'
|
||||
- '!v*-rc.*'
|
||||
|
||||
concurrency:
|
||||
group: release-${{ github.ref }}
|
||||
@@ -51,16 +49,6 @@ jobs:
|
||||
- name: Test suite (source)
|
||||
run: bun run test:fast
|
||||
|
||||
- name: Coverage suite (maintained source lane)
|
||||
run: bun run test:coverage:src
|
||||
|
||||
- name: Upload coverage artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: coverage-test-src
|
||||
path: coverage/test-src/lcov.info
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Launcher smoke suite (source)
|
||||
run: bun run test:launcher:smoke:src
|
||||
|
||||
@@ -411,64 +399,33 @@ jobs:
|
||||
id: version
|
||||
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Check AUR publish prerequisites
|
||||
id: aur_prereqs
|
||||
- name: Validate AUR SSH secret
|
||||
env:
|
||||
AUR_SSH_PRIVATE_KEY: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ -z "${AUR_SSH_PRIVATE_KEY}" ]; then
|
||||
echo "::warning::Missing AUR_SSH_PRIVATE_KEY; skipping automated AUR publish."
|
||||
echo "skip=true" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
echo "Missing required secret: AUR_SSH_PRIVATE_KEY"
|
||||
exit 1
|
||||
fi
|
||||
echo "skip=false" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Configure SSH for AUR
|
||||
id: aur_ssh
|
||||
if: steps.aur_prereqs.outputs.skip != 'true'
|
||||
env:
|
||||
AUR_SSH_PRIVATE_KEY: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if install -dm700 ~/.ssh \
|
||||
&& printf '%s\n' "${AUR_SSH_PRIVATE_KEY}" > ~/.ssh/aur \
|
||||
&& chmod 600 ~/.ssh/aur \
|
||||
&& ssh-keyscan aur.archlinux.org >> ~/.ssh/known_hosts \
|
||||
&& chmod 644 ~/.ssh/known_hosts; then
|
||||
echo "skip=false" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "::warning::Unable to configure SSH for AUR; skipping automated AUR publish."
|
||||
echo "skip=true" >> "$GITHUB_OUTPUT"
|
||||
install -dm700 ~/.ssh
|
||||
printf '%s\n' "${AUR_SSH_PRIVATE_KEY}" > ~/.ssh/aur
|
||||
chmod 600 ~/.ssh/aur
|
||||
ssh-keyscan aur.archlinux.org >> ~/.ssh/known_hosts
|
||||
chmod 644 ~/.ssh/known_hosts
|
||||
|
||||
- name: Clone AUR repo
|
||||
id: aur_clone
|
||||
if: steps.aur_prereqs.outputs.skip != 'true' && steps.aur_ssh.outputs.skip != 'true'
|
||||
env:
|
||||
GIT_SSH_COMMAND: ssh -i ~/.ssh/aur -o IdentitiesOnly=yes
|
||||
run: |
|
||||
set -euo pipefail
|
||||
attempts=3
|
||||
for attempt in $(seq 1 "$attempts"); do
|
||||
if git clone ssh://aur@aur.archlinux.org/subminer-bin.git aur-subminer-bin; then
|
||||
echo "skip=false" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
rm -rf aur-subminer-bin
|
||||
|
||||
if [ "$attempt" -lt "$attempts" ]; then
|
||||
sleep $((attempt * 15))
|
||||
fi
|
||||
done
|
||||
|
||||
echo "::warning::Unable to clone subminer-bin from AUR after ${attempts} attempts; skipping automated AUR publish."
|
||||
echo "skip=true" >> "$GITHUB_OUTPUT"
|
||||
run: git clone ssh://aur@aur.archlinux.org/subminer-bin.git aur-subminer-bin
|
||||
|
||||
- name: Download release assets for AUR
|
||||
if: steps.aur_prereqs.outputs.skip != 'true' && steps.aur_ssh.outputs.skip != 'true' && steps.aur_clone.outputs.skip != 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
@@ -482,7 +439,6 @@ jobs:
|
||||
--pattern "subminer-assets.tar.gz"
|
||||
|
||||
- name: Update AUR packaging metadata
|
||||
if: steps.aur_prereqs.outputs.skip != 'true' && steps.aur_ssh.outputs.skip != 'true' && steps.aur_clone.outputs.skip != 'true'
|
||||
run: |
|
||||
set -euo pipefail
|
||||
version_no_v="${{ steps.version.outputs.VERSION }}"
|
||||
@@ -497,7 +453,6 @@ jobs:
|
||||
--assets ".tmp/aur-release-assets/subminer-assets.tar.gz"
|
||||
|
||||
- name: Commit and push AUR update
|
||||
if: steps.aur_prereqs.outputs.skip != 'true' && steps.aur_ssh.outputs.skip != 'true' && steps.aur_clone.outputs.skip != 'true'
|
||||
working-directory: aur-subminer-bin
|
||||
env:
|
||||
GIT_SSH_COMMAND: ssh -i ~/.ssh/aur -o IdentitiesOnly=yes
|
||||
@@ -511,16 +466,4 @@ jobs:
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git add PKGBUILD .SRCINFO
|
||||
git commit -m "Update to ${{ steps.version.outputs.VERSION }}"
|
||||
|
||||
attempts=3
|
||||
for attempt in $(seq 1 "$attempts"); do
|
||||
if git push origin HEAD:master; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$attempt" -lt "$attempts" ]; then
|
||||
sleep $((attempt * 15))
|
||||
fi
|
||||
done
|
||||
|
||||
echo "::warning::Unable to push the AUR update after ${attempts} attempts; GitHub release is published, but subminer-bin needs manual follow-up."
|
||||
git push origin HEAD:master
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,7 +9,6 @@ out/
|
||||
dist/
|
||||
release/
|
||||
build/yomitan/
|
||||
coverage/
|
||||
|
||||
# Launcher build artifact (produced by make build-launcher)
|
||||
/subminer
|
||||
|
||||
@@ -83,6 +83,7 @@ This project uses Backlog.md MCP for all task and project management activities.
|
||||
- **When to read it**: BEFORE creating tasks, or when you're unsure whether to track work
|
||||
|
||||
These guides cover:
|
||||
|
||||
- Decision framework for when to create tasks
|
||||
- Search-first workflow to avoid duplicates
|
||||
- Links to detailed guides for task creation, execution, and finalization
|
||||
|
||||
266
Backlog.md
266
Backlog.md
@@ -1,266 +0,0 @@
|
||||
# Backlog
|
||||
|
||||
Purpose: lightweight repo-local task board. Seeded with current testing / coverage work.
|
||||
|
||||
Status keys:
|
||||
|
||||
- `todo`: not started
|
||||
- `doing`: in progress
|
||||
- `blocked`: waiting
|
||||
- `done`: shipped
|
||||
|
||||
Priority keys:
|
||||
|
||||
- `P0`: urgent / release-risk
|
||||
- `P1`: high value
|
||||
- `P2`: useful cleanup
|
||||
- `P3`: nice-to-have
|
||||
|
||||
## Active
|
||||
|
||||
| ID | Pri | Status | Area | Title |
|
||||
| ------ | --- | ------ | -------------- | --------------------------------------------------- |
|
||||
| SM-013 | P1 | done | review-followup | Address PR #36 CodeRabbit action items |
|
||||
|
||||
## Ready
|
||||
|
||||
| ID | Pri | Status | Area | Title |
|
||||
| ------ | --- | ------ | ----------------- | ---------------------------------------------------------------- |
|
||||
| SM-001 | P1 | todo | launcher | Add tests for CLI parser and args normalizer |
|
||||
| SM-002 | P1 | todo | immersion-tracker | Backfill tests for uncovered query exports |
|
||||
| SM-003 | P1 | todo | anki | Add focused field-grouping service + merge edge-case tests |
|
||||
| SM-004 | P2 | todo | tests | Extract shared test utils for deps factories and polling helpers |
|
||||
| SM-005 | P2 | todo | tests | Strengthen weak assertions in app-ready and IPC tests |
|
||||
| SM-006 | P2 | todo | tests | Break up monolithic youtube-flow and subtitle-sidebar tests |
|
||||
| SM-007 | P2 | todo | anilist | Add tests for AniList rate limiter |
|
||||
| SM-008 | P3 | todo | subtitles | Add core subtitle-position persistence/path tests |
|
||||
| SM-009 | P3 | todo | tokenizer | Add tests for JLPT token filter |
|
||||
| SM-010 | P1 | todo | immersion-tracker | Refactor storage + immersion-tracker service into focused modules |
|
||||
| SM-011 | P1 | done | tests | Add coverage reporting for maintained test lanes |
|
||||
| SM-012 | P2 | done | config/runtime | Replace JSON serialize-clone helpers with structured cloning |
|
||||
|
||||
## Icebox
|
||||
|
||||
None.
|
||||
|
||||
## Ticket Details
|
||||
|
||||
### SM-001
|
||||
|
||||
Title: Add tests for CLI parser and args normalizer
|
||||
Priority: P1
|
||||
Status: done
|
||||
Scope:
|
||||
|
||||
- `launcher/config/cli-parser-builder.ts`
|
||||
- `launcher/config/args-normalizer.ts`
|
||||
Acceptance:
|
||||
- root options parsing covered
|
||||
- subcommand routing covered
|
||||
- invalid action / invalid log level / invalid backend cases covered
|
||||
- target classification covered: file, directory, URL, invalid
|
||||
|
||||
### SM-002
|
||||
|
||||
Title: Backfill tests for uncovered query exports
|
||||
Priority: P1
|
||||
Status: todo
|
||||
Scope:
|
||||
|
||||
- `src/core/services/immersion-tracker/query-*.ts`
|
||||
Targets:
|
||||
- headword helpers
|
||||
- anime/media detail helpers not covered by existing wrapper tests
|
||||
- lexical detail / appearance helpers
|
||||
- maintenance helpers beyond `deleteSession` and `upsertCoverArt`
|
||||
Acceptance:
|
||||
- every exported query helper either directly tested or explicitly justified as covered elsewhere
|
||||
- at least one focused regression per complex SQL branch / aggregation branch
|
||||
|
||||
### SM-003
|
||||
|
||||
Title: Add focused field-grouping service + merge edge-case tests
|
||||
Priority: P1
|
||||
Status: todo
|
||||
Scope:
|
||||
|
||||
- `src/anki-integration/field-grouping.ts`
|
||||
- `src/anki-integration/field-grouping-merge.ts`
|
||||
Acceptance:
|
||||
- auto/manual/disabled flow branches covered
|
||||
- duplicate-card preview failure path covered
|
||||
- merge edge cases covered: empty fields, generated media fallback, strict grouped spans, audio synchronization
|
||||
|
||||
### SM-004
|
||||
|
||||
Title: Extract shared test utils for deps factories and polling helpers
|
||||
Priority: P2
|
||||
Status: todo
|
||||
Scope:
|
||||
|
||||
- common `makeDeps` / `createDeps` helpers
|
||||
- common `waitForCondition`
|
||||
Acceptance:
|
||||
- shared helper module added
|
||||
- at least 3 duplicated polling helpers removed
|
||||
- at least 5 duplicated deps factories consolidated or clearly prepared for follow-up migration
|
||||
|
||||
### SM-005
|
||||
|
||||
Title: Strengthen weak assertions in app-ready and IPC tests
|
||||
Priority: P2
|
||||
Status: todo
|
||||
Scope:
|
||||
|
||||
- `src/core/services/app-ready.test.ts`
|
||||
- `src/core/services/ipc.test.ts`
|
||||
Acceptance:
|
||||
- replace broad `assert.ok(...)` presence checks with exact value / order assertions where expected value known
|
||||
- handler registration tests assert channel-specific behavior, not only existence
|
||||
|
||||
### SM-006
|
||||
|
||||
Title: Break up monolithic youtube-flow and subtitle-sidebar tests
|
||||
Priority: P2
|
||||
Status: todo
|
||||
Scope:
|
||||
|
||||
- `src/main/runtime/youtube-flow.test.ts`
|
||||
- `src/renderer/modals/subtitle-sidebar.test.ts`
|
||||
Acceptance:
|
||||
- reduce single-test breadth
|
||||
- split largest tests into focused cases by behavior
|
||||
- keep semantics unchanged
|
||||
|
||||
### SM-007
|
||||
|
||||
Title: Add tests for AniList rate limiter
|
||||
Priority: P2
|
||||
Status: todo
|
||||
Scope:
|
||||
|
||||
- `src/core/services/anilist/rate-limiter.ts`
|
||||
Acceptance:
|
||||
- capacity-window wait behavior covered
|
||||
- `x-ratelimit-remaining` + reset handling covered
|
||||
- `retry-after` handling covered
|
||||
|
||||
### SM-008
|
||||
|
||||
Title: Add core subtitle-position persistence/path tests
|
||||
Priority: P3
|
||||
Status: todo
|
||||
Scope:
|
||||
|
||||
- `src/core/services/subtitle-position.ts`
|
||||
Acceptance:
|
||||
- save/load persistence covered
|
||||
- fallback behavior covered
|
||||
- path normalization behavior covered for URL vs local target
|
||||
|
||||
### SM-009
|
||||
|
||||
Title: Add tests for JLPT token filter
|
||||
Priority: P3
|
||||
Status: todo
|
||||
Scope:
|
||||
|
||||
- `src/core/services/jlpt-token-filter.ts`
|
||||
Acceptance:
|
||||
- excluded term membership covered
|
||||
- ignored POS1 membership covered
|
||||
- exported list / entry consistency covered
|
||||
|
||||
### SM-010
|
||||
|
||||
Title: Refactor storage + immersion-tracker service into focused layers without API changes
|
||||
Priority: P1
|
||||
Status: todo
|
||||
Scope:
|
||||
|
||||
- `src/core/database/storage/storage.ts`
|
||||
- `src/core/database/storage/schema.ts`
|
||||
- `src/core/database/storage/cover-blob.ts`
|
||||
- `src/core/database/storage/records.ts`
|
||||
- `src/core/database/storage/write-path.ts`
|
||||
- `src/core/services/immersion-tracker/youtube.ts`
|
||||
- `src/core/services/immersion-tracker/youtube-manager.ts`
|
||||
- `src/core/services/immersion-tracker/write-queue.ts`
|
||||
- `src/core/services/immersion-tracker/immersion-tracker-service.ts`
|
||||
|
||||
Acceptance:
|
||||
|
||||
- behavior and public API remain unchanged for all callers
|
||||
- `storage.ts` responsibilities split into DDL/migrations, cover blob helpers, record CRUD, and write-path execution
|
||||
- `immersion-tracker-service.ts` reduces to session state, media change orchestration, query proxies, and lifecycle
|
||||
- YouTube code split into pure utilities, a stateful manager (`YouTubeManager`), and a dedicated write queue (`WriteQueue`)
|
||||
- removed `storage.ts` is replaced with focused modules and updated imports
|
||||
- no API or migration regressions; existing tests for trackers/storage coverage remain green or receive focused updates
|
||||
|
||||
### SM-011
|
||||
|
||||
Title: Add coverage reporting for maintained test lanes
|
||||
Priority: P1
|
||||
Status: done
|
||||
Scope:
|
||||
|
||||
- `package.json`
|
||||
- CI workflow files under `.github/`
|
||||
- `docs/workflow/verification.md`
|
||||
Acceptance:
|
||||
- at least one maintained test lane emits machine-readable coverage output
|
||||
- CI surfaces coverage as an artifact, summary, or check output
|
||||
- local contributor path for coverage is documented
|
||||
- chosen coverage path works with Bun/TypeScript lanes already maintained by the repo
|
||||
Implementation note:
|
||||
- Added `bun run test:coverage:src` for the maintained source lane via a sharded coverage runner, with merged LCOV output at `coverage/test-src/lcov.info` and CI/release artifact upload as `coverage-test-src`.
|
||||
|
||||
### SM-012
|
||||
|
||||
Title: Replace JSON serialize-clone helpers with structured cloning
|
||||
Priority: P2
|
||||
Status: todo
|
||||
Scope:
|
||||
|
||||
- `src/runtime-options.ts`
|
||||
- `src/config/definitions.ts`
|
||||
- `src/config/service.ts`
|
||||
- `src/main/controller-config-update.ts`
|
||||
Acceptance:
|
||||
- runtime/config clone helpers stop using `JSON.parse(JSON.stringify(...))`
|
||||
- replacement preserves current behavior for plain config/runtime objects
|
||||
- focused tests cover clone/merge behavior that could regress during the swap
|
||||
- no new clone helper is introduced in these paths without a documented reason
|
||||
|
||||
Done:
|
||||
|
||||
- replaced JSON serialize-clone call sites in runtime/config/controller update paths with `structuredClone`
|
||||
- updated focused tests and fixtures to cover detached clone behavior and guard against regressions
|
||||
|
||||
### SM-013
|
||||
|
||||
Title: Address PR #36 CodeRabbit action items
|
||||
Priority: P1
|
||||
Status: done
|
||||
Scope:
|
||||
|
||||
- `plugins/subminer-workflow/skills/subminer-change-verification/scripts/verify_subminer_change.sh`
|
||||
- `scripts/subminer-change-verification.test.ts`
|
||||
- `src/core/services/immersion-tracker/query-sessions.ts`
|
||||
- `src/core/services/immersion-tracker/query-trends.ts`
|
||||
- `src/core/services/immersion-tracker/maintenance.ts`
|
||||
- `src/main/boot/services.ts`
|
||||
- `src/main/character-dictionary-runtime/zip.test.ts`
|
||||
Acceptance:
|
||||
- fix valid open CodeRabbit findings on PR #36
|
||||
- add focused regression coverage for behavior changes where practical
|
||||
- verify touched tests plus typecheck stay green
|
||||
|
||||
Done:
|
||||
|
||||
- hardened `--artifact-dir` validation in the verification script
|
||||
- fixed trend aggregation rounding and monthly ratio bucketing
|
||||
- preserved unwatched anime episodes in episode queries
|
||||
- restored seconds-based aggregate timestamps in shared maintenance
|
||||
- fixed the startup refactor compile break by making the predicates local at the call site
|
||||
- verified with `bun test src/core/services/immersion-tracker/__tests__/query.test.ts src/core/services/immersion-tracker/__tests__/query-split-modules.test.ts` and `bun run typecheck`
|
||||
160
CHANGELOG.md
160
CHANGELOG.md
@@ -1,165 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## v0.11.2 (2026-04-07)
|
||||
|
||||
### Changed
|
||||
- Launcher: Replaced the launcher-only fullscreen toggle with `mpv.launchMode` so SubMiner-managed mpv playback can start in normal, maximized, or fullscreen mode.
|
||||
|
||||
### Fixed
|
||||
- Launcher: Fixed launcher-managed mpv spawning to force an explicit X11 GPU path when Wayland trackers are unavailable.
|
||||
- Launcher: Local playback now promotes a single unlabeled external subtitle sidecar to the primary slot instead of leaving mpv's embedded English auto-selection in place.
|
||||
- Release: Fixed Linux AppImage startup packaging so Chromium child relaunches can resolve the bundled `libffmpeg.so` instead of crash-looping on startup.
|
||||
|
||||
## v0.11.1 (2026-04-04)
|
||||
|
||||
### Fixed
|
||||
- Release: Linux packaged builds now expose the canonical `SubMiner` app identity to Electron's startup metadata so native Wayland compositors stop reporting the window class/app-id as lowercase `subminer`.
|
||||
- Linux: Linux now restores the runtime options, Jimaku, and Subsync shortcuts after the Electron 39 regression by routing those actions through the overlay's mpv/IPC shortcut path.
|
||||
|
||||
## v0.11.0 (2026-04-03)
|
||||
|
||||
### Added
|
||||
- Overlay: Added a playlist browser overlay modal for browsing sibling video files and the live mpv queue during playback.
|
||||
- Overlay: Added the default `Ctrl+Alt+P` keybinding to open the playlist browser and manage queue order without leaving playback.
|
||||
|
||||
### Changed
|
||||
- Setup: Made mpv plugin installation mandatory in the first-run setup flow, removed the skip path, and kept Finish disabled until the plugin is installed.
|
||||
- Setup: Clarified that the mpv plugin requirement applies to setup on every platform, while the optional `SubMiner mpv` shortcut remains the recommended Windows playback entry point.
|
||||
- Launcher: Streamlined Windows setup and config by making the `SubMiner mpv` shortcut self-contained and keeping `mpv.executablePath` as the simple fallback when `mpv.exe` is not on `PATH`.
|
||||
- Overlay: Changed fresh-install default config to keep texthooker and stats from auto-opening browser tabs.
|
||||
- Overlay: Changed fresh-install default config to enable AnkiConnect, Discord Rich Presence, subtitle-sidebar, and Yomitan-popup auto-pause by default, while disabling controller input by default.
|
||||
|
||||
### Fixed
|
||||
- Main: Resolve the YouTube playback socket path lazily so startup honors CLI and config overrides.
|
||||
- Main: Add regression coverage for the lazy socket-path lookup during Windows mpv startup.
|
||||
- Main: Keep integrated `--start --texthooker` launches on the full app-ready startup path so the texthooker page and websocket servers start together during normal playback startup.
|
||||
- Main: Stop the mpv/plugin auto-start flow from spawning a separate standalone texthooker helper during normal `subminer <video>` launches.
|
||||
- Overlay: Keep tracked macOS visible overlays click-through by default so subtitle sidebar passthrough works immediately without requiring a subtitle hover cycle first.
|
||||
- Overlay: Add regression coverage for the macOS visible-overlay passthrough default.
|
||||
- Anilist: Stop AniList post-watch from sending a second progress update when the current episode was already satisfied by a ready retry item in the same watch-completion pass.
|
||||
- Anilist: Add regression coverage for the retry-queue plus live-update duplicate path.
|
||||
- Overlay: Fixed Kiku duplicate grouping to reuse duplicate note IDs from both generic sentence-card creation and Yomitan popup mining instead of running extra duplicate scans after add.
|
||||
- Overlay: Fixed the Yomitan popup mining flow to add cards in the background while keeping the stock popup progress feedback, then pause playback and close the lookup popup before the Kiku merge modal opens.
|
||||
- Overlay: Fixed configured subtitle-jump keybindings so backward and forward subtitle seeks keep playback paused when invoked from a paused state.
|
||||
- Launcher: Fixed the Windows `SubMiner mpv` shortcut and `SubMiner.exe --launch-mpv` flow to launch mpv with SubMiner's required default args directly instead of requiring an `mpv.conf` profile named `subminer`.
|
||||
- Launcher: Clarified the Windows install and usage docs so the shortcut path is documented as self-contained, while the optional `subminer` mpv profile remains available for manual mpv launches.
|
||||
- Launcher: Hardened the first-run setup blocker copy and stale custom-scheme handling so setup messages stay aligned with config, plugin, and dictionary readiness.
|
||||
- Launcher: Fixed the Windows `SubMiner mpv` shortcut idle launch so loading a video after opening the shortcut keeps mpv in the expected SubMiner-managed session, auto-starts the overlay, and re-arms subtitle auto-selection for the newly opened file.
|
||||
- Launcher: Removed the redundant `.` subtitle search path from the Windows shortcut launch args and deduped repeated subtitle source tracks in the manual sync picker so duplicate external subtitle entries no longer appear from the shortcut path.
|
||||
- Playback: Fixed managed local playback so duplicate startup-ready retries no longer unpause media after a later manual pause on the same file.
|
||||
- Playback: Fixed managed local subtitle auto-selection so local files reuse configured primary and secondary subtitle language priorities instead of staying on mpv's initial `sid=auto` guess.
|
||||
- Launcher: Added a blank-by-default `mpv.executablePath` override for Windows playback so users can point SubMiner at `mpv.exe` when it is not on `PATH`.
|
||||
- Launcher: Kept the Windows shortcut and `--launch-mpv` flow simple by preserving PATH auto-discovery as the default and exposing the override in first-run setup.
|
||||
- Launcher: Added `windows` as a recognized launcher backend option and auto-detection target on Windows.
|
||||
- Launcher: Honored `SUBMINER_YTDLP_BIN` consistently across YouTube playback URL resolution, track probing, subtitle downloads, and metadata probing.
|
||||
- Launcher: Kept the first-run setup window from navigating away on unexpected URLs.
|
||||
- Launcher: Made Windows mpv honor an explicitly configured executable path instead of silently falling back to PATH.
|
||||
- Launcher: Hardened `--launch-mpv` parsing and Windows binary resolution so valueless flags do not swallow media targets and symlinked launcher installs do not short-circuit PATH lookup.
|
||||
- Launcher: Fixed first-run setup blocking playback on macOS when the SubMiner mpv plugin was already installed at the canonical `~/.config/mpv` path.
|
||||
- Launcher: Fixed setup gating so stale cancelled setup state no longer prevents playback when the canonical mpv plugin entrypoint already exists.
|
||||
- Playback: Prevented stale async playlist-browser subtitle rearm callbacks from overriding newer subtitle selections during rapid file changes.
|
||||
|
||||
### Docs
|
||||
- Docs Site: Added a dedicated Subtitle Sidebar guide and linked it from the homepage and configuration docs.
|
||||
- Docs Site: Linked Jimaku integration from the homepage to its dedicated docs page.
|
||||
- Docs Site: Refreshed docs-site theme tokens and hover/selection styling for the updated pages.
|
||||
|
||||
### Internal
|
||||
- Release: Retried AUR clone and push operations in the tagged release workflow.
|
||||
- Release: Kept GitHub Releases green when AUR publish flakes and needs manual follow-up.
|
||||
- Release: Updated Electron to 39.8.6 and pinned patched transitive build dependencies to clear the reported high-severity audit findings.
|
||||
|
||||
## v0.10.0 (2026-03-29)
|
||||
|
||||
### Changed
|
||||
- Integrations: Replaced the deprecated Discord Rich Presence wrapper with the maintained `@xhayper/discord-rpc` package.
|
||||
|
||||
### Fixed
|
||||
- Stats: Fixed stats startup so the immersion tracker can run when `Bun.serve` is unavailable.
|
||||
- Stats: Stats server now falls back to a Node `http` listener in Electron/runtime paths that do not expose Bun.
|
||||
- Overlay: Fixed the macOS visible-overlay toggle path so manual hides stay hidden and the plugin uses the explicit visible-overlay toggle command.
|
||||
- Subtitle Sidebar: Restored macOS mpv passthrough while the overlay subtitle sidebar is open so clicks outside the sidebar can refocus mpv and keep native keybindings working.
|
||||
|
||||
### Internal
|
||||
- Release: Added a maintained source coverage lane that shards Bun coverage one test file at a time and merges LCOV output into `coverage/test-src/lcov.info`.
|
||||
- Release: CI and release quality-gate now upload the merged source-lane LCOV artifact for inspection.
|
||||
- Runtime: Extracted remaining inline runtime logic from `src/main.ts` into dedicated runtime modules and composer helpers.
|
||||
- Runtime: Added focused regression tests for the extracted runtime/composer boundaries.
|
||||
- Runtime: Updated task tracking notes to mark TASK-238.6 complete and confirm follow-on boot-phase split can be deferred.
|
||||
- Runtime: Split `src/main.ts` boot wiring into dedicated `src/main/boot/services.ts`, `src/main/boot/runtimes.ts`, and `src/main/boot/handlers.ts` modules.
|
||||
- Runtime: Added focused tests for the new boot-phase seams and kept the startup/typecheck/build verification lanes green.
|
||||
- Runtime: Updated internal architecture/task docs to record the boot-phase split and new ownership boundary.
|
||||
|
||||
## v0.9.3 (2026-03-25)
|
||||
|
||||
### Changed
|
||||
- Launcher: Moved YouTube primary subtitle language defaults to `youtube.primarySubLanguages`.
|
||||
- Launcher: Removed the placeholder YouTube subtitle retime step and now uses downloaded primary subtitle tracks directly, so there is no fake path rewrite before playback/sidebar loading.
|
||||
- YouTube: Removed the `src/core/services/youtube/retime` helper and its tests after retiring the internal retime strategy.
|
||||
- Docs: Clarified optional `alass` / `ffsubsync` subtitle-sync requirements and setup steps, including fallback behavior when sync tools are absent.
|
||||
- Launcher: Removed the old `youtubeSubgen.primarySubLanguages` config path from the generated config and docs.
|
||||
|
||||
## v0.9.2 (2026-03-25)
|
||||
|
||||
### Fixed
|
||||
- Overlay: Fixed overlay pointer tracking so Windows click-through toggles immediately when the cursor enters or leaves subtitle regions, without waiting for a later hover resync.
|
||||
- Overlay: Fixed Windows overlay window tracking on scaled displays by converting native tracked window bounds to Electron DIP coordinates before applying overlay bounds.
|
||||
- Launcher: Fixed Windows direct `--youtube-play` startup so MPV boots reliably, stays paused until the app-owned subtitle flow is ready, and reuses an already-running SubMiner instance when available.
|
||||
- Launcher: Fixed standalone Windows `--youtube-play` sessions so closing MPV fully exits SubMiner instead of leaving hidden overlay windows or a background process behind.
|
||||
- Overlay: Fixed `subminer <youtube-url>` on Linux so the YouTube playback flow waits for Yomitan to load before creating the overlay window, avoiding the broken lookup popup state that previously required a manual overlay refresh.
|
||||
|
||||
## v0.9.1 (2026-03-24)
|
||||
|
||||
### Changed
|
||||
- Release: Reduced packaged release size by excluding duplicate `extraResources` payload and pruning docs, tests, sourcemaps, and other source-only files from Electron bundles.
|
||||
|
||||
### Fixed
|
||||
- Overlay: Restored controller navigation and lookup/mining controls while the subtitle sidebar is open, while keeping true modal dialogs blocking controller actions.
|
||||
- Tokenizer: Fixed subtitle annotation clearing so explanatory contrast endings like `んですけど` are excluded consistently across the shared tokenizer filter and annotation stage.
|
||||
|
||||
## v0.9.0 (2026-03-23)
|
||||
|
||||
### Added
|
||||
- Docs: Added a new WebSocket / Texthooker API and integration guide covering WebSocket payloads, custom client patterns, mpv plugin automation, and webhook-style relay examples. Linked from configuration and mining workflow docs for easier discovery.
|
||||
|
||||
### Changed
|
||||
- Launcher: Added an app-owned YouTube subtitle flow that pauses mpv, uses absPlayer-style YouTube timedtext parsing/conversion to download subtitle tracks, and injects them as external files before playback resumes.
|
||||
- Launcher: Changed YouTube subtitle startup to auto-load the best-available primary and secondary subtitle tracks at launch instead of forcing the picker modal first. Secondary subtitle failures no longer block playback resume.
|
||||
- Launcher: Added `Ctrl+Alt+C` as the default keybinding to manually open the YouTube subtitle picker during active YouTube playback.
|
||||
- Launcher: Added yt-dlp metadata probing so YouTube playback and immersion tracking record canonical video title and channel metadata.
|
||||
- Launcher: Stopped forcing `--ytdl-raw-options=` before user-provided mpv options so existing YouTube cookie integrations in user `--args` are no longer clobbered.
|
||||
- Launcher: Disabled mpv native YouTube subtitle auto-loading for the app-owned flow so injected external subtitle files remain authoritative.
|
||||
- Launcher: Added OSD status messages for YouTube playback startup, subtitle acquisition, and subtitle loading so the flow stays visible before and during the picker.
|
||||
- Subtitle Sidebar: Added startup-auto-open controls and resume positioning improvements so the sidebar jumps directly to the first resolved active cue.
|
||||
- Subtitle Sidebar: Improved subtitle prefetch and embedded overlay passthrough sync so sidebar and overlay subtitle states stay consistent across media transitions.
|
||||
- Subtitle Sidebar: Updated scroll handling, embedded layout styling, and active-cue visual behavior.
|
||||
- Stats: Stats Library tab now displays YouTube video title, channel name, and channel thumbnail for YouTube media entries, with retry logic to fill in metadata that arrives after initial load.
|
||||
|
||||
### Fixed
|
||||
- Launcher: Fixed Anki media mining for mpv YouTube streams by unwrapping the stream URL so audio and screenshot capture work correctly for YouTube playback sessions.
|
||||
- Immersion: Fixed YouTube media path handling in the immersion runtime and tracking so YouTube sessions record correct media references, AniList guessing skips YouTube URLs, and post-watch state transitions do not fire for YouTube media.
|
||||
- Launcher: Fixed startup-launched YouTube playback so primary subtitle overlay updates continue after auto-load completes.
|
||||
- Launcher: Fixed auto-loaded YouTube primary subtitles so parsed cues appear in the subtitle sidebar without needing a manual picker retry.
|
||||
- Launcher: Fixed the YouTube picker to guard against duplicate subtitle submissions and tightened YouTube URL detection so follow-up runtime flows only treat real YouTube hosts as YouTube playback.
|
||||
- Launcher: Fixed primary subtitle failure notifications being shown while app-owned YouTube subtitle probing and downloads are still in flight.
|
||||
- Launcher: Preserved existing authoritative YouTube subtitle tracks when available; downloaded tracks are used only to fill missing sides, and native mpv secondary subtitle rendering is hidden so the overlay remains the sole secondary display.
|
||||
|
||||
## v0.8.0 (2026-03-22)
|
||||
|
||||
### Added
|
||||
- Overlay: Added the subtitle sidebar feature with a new `subtitleSidebar` configuration surface and rendered sidebar modal with cue list rendering, click-to-seek, active-cue highlighting, and embedded layout support.
|
||||
- IPC: Added sidebar snapshot plumbing between renderer and main process for overlay/sidebar synchronization.
|
||||
|
||||
### Changed
|
||||
- Config: Added hot-reloadable sidebar options for enablement, layout, visibility, typography, opacity, sizing, and interaction behavior (`autoOpen`, `pauseOnHover`, `autoScroll`, toggle key).
|
||||
- Docs: Added full `subtitleSidebar` documentation coverage, including sample config, option table, and toggle shortcut notes.
|
||||
- Runtime: Improved subtitle prefetch/rendering flow so sidebar and overlay subtitle states stay in sync across media transitions.
|
||||
|
||||
### Fixed
|
||||
- Overlay: Kept sidebar cue tracking stable across playback transitions and timing edge cases.
|
||||
- Overlay: Improved sidebar resume/start behavior to jump directly to the first resolved active cue.
|
||||
- Overlay: Stopped stale subtitle refreshes from regressing active-cue and text state.
|
||||
|
||||
## v0.7.0 (2026-03-19)
|
||||
|
||||
### Added
|
||||
|
||||
272
README.md
272
README.md
@@ -1,182 +1,60 @@
|
||||
<div align="center">
|
||||
|
||||
<img src="assets/SubMiner.png" width="160" alt="SubMiner logo">
|
||||
<img src="assets/SubMiner.png" width="140" alt="SubMiner logo">
|
||||
|
||||
# SubMiner
|
||||
|
||||
Look up words with Yomitan, export to Anki in one key, track your immersion — all without leaving mpv.
|
||||
**Sentence-mine from mpv — look up words, one-key Anki export, immersion tracking.**
|
||||
|
||||
[Installation](#quick-start) · [Requirements](#requirements) · [Usage](https://docs.subminer.moe/usage) · [Documentation](https://docs.subminer.moe)
|
||||
[](https://www.gnu.org/licenses/gpl-3.0)
|
||||
[](https://github.com/ksyasuda/SubMiner)
|
||||
[](https://docs.subminer.moe)
|
||||
[](https://aur.archlinux.org/packages/subminer-bin)
|
||||
|
||||
[](https://github.com/ksyasuda/SubMiner/releases)
|
||||
[](https://github.com/ksyasuda/SubMiner/releases/latest)
|
||||
[](https://aur.archlinux.org/packages/subminer-bin)
|
||||
[](https://github.com/ksyasuda/SubMiner)
|
||||
[](https://www.gnu.org/licenses/gpl-3.0)
|
||||
[](https://www.typescriptlang.org)
|
||||
</div>
|
||||
|
||||
[](https://github.com/user-attachments/assets/89e61895-e2b7-4b47-8d50-a35afe4132b2)
|
||||
---
|
||||
|
||||
SubMiner is an Electron overlay for [mpv](https://mpv.io) that turns video into a sentence-mining workstation. Look up any word with [Yomitan](https://github.com/yomidevs/yomitan), mine it to Anki with one key, and track your immersion over time.
|
||||
|
||||
<div align="center">
|
||||
|
||||
[](./assets/minecard.mp4)
|
||||
|
||||
</div>
|
||||
|
||||
## Features
|
||||
|
||||
### Dictionary Lookups
|
||||
**Dictionary lookups** — Yomitan runs inside the overlay. Hover or navigate to any word for full dictionary popups without leaving mpv.
|
||||
|
||||
Yomitan runs inside the overlay. Trigger a lookup on any word for full dictionary popups — definitions, pitch accent, frequency data — without ever leaving mpv.
|
||||
**One-key Anki mining** — Press one key to create a card with the sentence, audio clip, screenshot, and machine translation from the exact playback moment.
|
||||
|
||||
<div align="center">
|
||||
<img src="docs-site/public/screenshots/yomitan-lookup.png" width="800" alt="Yomitan dictionary popup over annotated subtitles in mpv">
|
||||
<img src="docs-site/public/screenshots/yomitan-lookup.png" width="800" alt="Yomitan popup with dictionary entry and mine button over annotated subtitles in mpv">
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
### Instant Anki Mining
|
||||
|
||||
Create an Anki card with the sentence, audio clip, screenshot, and machine translation from the exact playback moment with one key press, click, or controller input.
|
||||
**Reading annotations** — Real-time subtitle annotations with N+1 targeting, frequency highlighting, JLPT tags, and a character name dictionary. Grammar-only tokens render as plain text.
|
||||
|
||||
<div align="center">
|
||||
<img src="docs-site/public/screenshots/one-key-mining.png" width="800" alt="Anki card created from SubMiner with sentence, audio, and screenshot">
|
||||
<img src="docs-site/public/screenshots/annotations.png" width="800" alt="Annotated subtitles with frequency highlighting, JLPT underlines, known words, and N+1 targets">
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
### Reading Annotations
|
||||
|
||||
Real-time subtitle annotations with frequency highlighting, JLPT tags, N+1 targeting, and a character name dictionary. Known words fade back; new words stand out. Grammar-only tokens render as plain text so you focus on what matters.
|
||||
**Immersion dashboard** — Local stats dashboard with watch time, anime progress, vocabulary growth, mining throughput, and session history.
|
||||
|
||||
<div align="center">
|
||||
<img src="docs-site/public/screenshots/annotations.png" width="800" alt="Annotated subtitles with frequency coloring, JLPT underlines, and N+1 targets">
|
||||
<img src="docs-site/public/screenshots/stats-overview.png" width="800" alt="Stats dashboard with watch time, cards mined, streaks, and tracking snapshot">
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
### Immersion Dashboard
|
||||
|
||||
Local stats dashboard — watch time, anime library, vocabulary growth, mining throughput, session history, and trends. All stored locally, no third-party tracking.
|
||||
**Integrations** — AniList episode tracking, Jellyfin remote playback, Jimaku subtitle downloads, alass/ffsubsync, and an annotated websocket feed for external clients.
|
||||
|
||||
<div align="center">
|
||||
<img src="docs-site/public/screenshots/stats-overview.png" width="800" alt="Stats dashboard showing watch time, cards mined, streaks, and tracking data">
|
||||
<img src="docs-site/public/screenshots/texthooker.png" width="800" alt="Texthooker page with annotated subtitle lines and frequency highlighting">
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
### Playlist Browser
|
||||
|
||||
Browse sibling episode files and the active mpv queue in one overlay modal. Open it with `Ctrl+Alt+P` to append episodes from the current directory, jump to queued items, remove entries, or reorder the playlist without leaving playback.
|
||||
|
||||
<div align="center">
|
||||
<img src="docs-site/public/screenshots/playlist-browser.png" width="800" alt="Stats dashboard showing watch time, cards mined, streaks, and tracking data">
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
### Integrations
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><b>YouTube</b></td>
|
||||
<td>Auto-loaded yt-dlp subtitle tracks at startup with config-driven primary/secondary language priorities and a manual overlay picker on demand (<code>Ctrl+Alt+C</code>)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>AniList</b></td>
|
||||
<td>Automatic episode tracking and progress sync</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Jellyfin</b></td>
|
||||
<td>Browse and launch media from your Jellyfin server</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Jimaku</b></td>
|
||||
<td>Search and download Japanese subtitles</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>alass / ffsubsync</b></td>
|
||||
<td>Automatic subtitle retiming — requires <code>alass</code> or <code>ffsubsync</code> on your <code>PATH</code> (optional; subtitle syncing is disabled without them)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>WebSocket</b></td>
|
||||
<td>Annotated subtitle feed for external clients (texthooker pages, custom tools)</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div align="center">
|
||||
<img src="docs-site/public/screenshots/texthooker.png" width="800" alt="Texthooker page receiving annotated subtitle lines via WebSocket">
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
---
|
||||
|
||||
## Requirements
|
||||
|
||||
| | Required | Recommended | Optional |
|
||||
| -------------- | --------------------------------------- | ------------------------------------ | --------------------------------------------------------------------------------------------------------------- |
|
||||
| **Player** | [`mpv`](https://mpv.io) with IPC socket | — | — |
|
||||
| **Processing** | — | `ffmpeg` (audio clips & screenshots) | `mecab` + `mecab-ipadic` (annotation POS filtering), `guessit` (AniSkip), `alass` / `ffsubsync` (subtitle sync) |
|
||||
| **Media** | — | — | `yt-dlp`, `chafa`, `ffmpegthumbnailer` |
|
||||
| **Selection** | — | — | `fzf` / `rofi` |
|
||||
|
||||
> [!TIP]
|
||||
> `ffmpeg` is not strictly required to run SubMiner, but without it audio clips and screenshots will not be attached to Anki cards. Most users will want it installed.
|
||||
|
||||
> [!NOTE]
|
||||
> [`bun`](https://bun.sh) is required if building from source or using the CLI wrapper: `subminer`. Pre-built releases (AppImage, DMG, installer) do not require it.
|
||||
|
||||
**Platform-specific:**
|
||||
|
||||
| Linux | macOS | Windows |
|
||||
| ------------------------------------------------------------ | ------------------------ | ------------- |
|
||||
| Hyprland (`hyprctl`) · X11/Xwayland (`xdotool` + `xwininfo`) | Accessibility permission | No extra deps |
|
||||
|
||||
> [!NOTE]
|
||||
> **Wayland support is compositor-specific.** Wayland has no universal API for window positioning and each compositor exposes its own IPC, so SubMiner needs a dedicated backend per compositor. Hyprland is the only native Wayland backend supported currenlty. All other Linux compositors require both mpv and SubMiner to run under X11 or Xwayland. The launcher detects your compositor and configures this automatically.
|
||||
|
||||
<details>
|
||||
<summary><b>Arch Linux</b></summary>
|
||||
|
||||
```bash
|
||||
paru -S --needed mpv ffmpeg
|
||||
# Optional
|
||||
paru -S --needed mecab-git mecab-ipadic yt-dlp fzf rofi chafa ffmpegthumbnailer xdotool xorg-xwininfo
|
||||
# Optional: subtitle sync (install at least one for subtitle syncing to work)
|
||||
paru -S --needed alass python-ffsubsync
|
||||
# X11 / Xwayland (required for non-Hyprland compositors)
|
||||
paru -S --needed xdotool xorg-xwininfo
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>macOS</b></summary>
|
||||
|
||||
```bash
|
||||
brew install mpv ffmpeg
|
||||
# Optional
|
||||
brew install mecab mecab-ipadic yt-dlp fzf rofi chafa ffmpegthumbnailer
|
||||
# Optional: subtitle sync (install at least one for subtitle syncing to work)
|
||||
brew install alass
|
||||
pip install ffsubsync
|
||||
```
|
||||
|
||||
Grant Accessibility permission to SubMiner in **System Settings > Privacy & Security > Accessibility**.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Windows</b></summary>
|
||||
|
||||
Install [`mpv`](https://mpv.io/installation/) and [`ffmpeg`](https://ffmpeg.org/download.html) and ensure both are on your `PATH`.
|
||||
|
||||
Optionally install [MeCab for Windows](https://taku910.github.io/mecab/#download) with the UTF-8 dictionary for additional metadata enrichment.
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Install
|
||||
### Install
|
||||
|
||||
<details>
|
||||
<summary><b>Arch Linux (AUR)</b></summary>
|
||||
@@ -210,93 +88,53 @@ wget https://github.com/ksyasuda/SubMiner/releases/latest/download/subminer -O ~
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>macOS</b></summary>
|
||||
<summary><b>macOS / Windows / From source</b></summary>
|
||||
|
||||
Download the latest DMG or ZIP from [GitHub Releases](https://github.com/ksyasuda/SubMiner/releases/latest) and drag `SubMiner.app` into `/Applications`.
|
||||
**macOS** — Download the latest DMG/ZIP from [GitHub Releases](https://github.com/ksyasuda/SubMiner/releases/latest) and drag `SubMiner.app` into `/Applications`.
|
||||
|
||||
Also download the `subminer` launcher (recommended):
|
||||
**Windows** — Download the latest installer or portable `.zip` from [GitHub Releases](https://github.com/ksyasuda/SubMiner/releases/latest). Keep `mpv` on `PATH`.
|
||||
|
||||
```bash
|
||||
sudo curl -fSL https://github.com/ksyasuda/SubMiner/releases/latest/download/subminer -o /usr/local/bin/subminer \
|
||||
&& sudo chmod +x /usr/local/bin/subminer
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> The `subminer` launcher uses a [Bun](https://bun.sh) shebang. Make sure `bun` is on your `PATH`.
|
||||
**From source** — See [docs.subminer.moe/installation#from-source](https://docs.subminer.moe/installation#from-source).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Windows</b></summary>
|
||||
### First Launch
|
||||
|
||||
Download the latest installer or portable `.zip` from [GitHub Releases](https://github.com/ksyasuda/SubMiner/releases/latest). Make sure `mpv` is on your `PATH`.
|
||||
Run `SubMiner.AppImage` (Linux), `SubMiner.app` (macOS), or `SubMiner.exe` (Windows). On first launch, SubMiner starts in the tray, creates a default config, and opens a setup popup where you can install the mpv plugin and configure Yomitan dictionaries.
|
||||
|
||||
**Windows support is experimental.** Core features such as mining, annotations, and dictionary lookups work, but some functionality may be missing or unstable. Bug reports welcome.
|
||||
|
||||
**Note:** On Windows the `subminer` launcher requires [`bun`](https://bun.sh) and must be invoked with `bun run subminer` instead of running the script directly. The recommended alternative is the **SubMiner mpv** shortcut created during first-run setup — double-click it, drag files onto it, or run `SubMiner.exe --launch-mpv` from a terminal. See the [Windows mpv Shortcut](https://docs.subminer.moe/usage#windows-mpv-shortcut) section for details.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>From source</b></summary>
|
||||
|
||||
See the [build-from-source guide](https://docs.subminer.moe/installation#from-source).
|
||||
|
||||
</details>
|
||||
|
||||
### 2. First Launch
|
||||
### Mine
|
||||
|
||||
```bash
|
||||
subminer app --setup # launch the first-run setup wizard
|
||||
subminer video.mkv # auto-starts overlay + resumes playback
|
||||
subminer --start video.mkv # explicit overlay start (if plugin auto_start=no)
|
||||
subminer stats # open the immersion dashboard
|
||||
subminer stats -b # keep the stats daemon running in background
|
||||
subminer stats -s # stop the dedicated stats daemon
|
||||
subminer stats cleanup # repair/prune stored stats vocabulary rows
|
||||
```
|
||||
|
||||
SubMiner creates a default config, starts in the system tray, and opens a setup popup that walks you through installing the mpv plugin and configuring Yomitan dictionaries. Follow the on-screen steps to complete setup.
|
||||
|
||||
> [!NOTE]
|
||||
> On Windows, run `SubMiner.exe` directly — it opens the setup wizard automatically on first launch.
|
||||
|
||||
### 3. Verify Setup
|
||||
|
||||
```bash
|
||||
subminer doctor # verify mpv, ffmpeg, config, and socket
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> On Windows, use `bun run subminer doctor` or run `SubMiner.exe` directly — first-run setup will guide you through dependency checks.
|
||||
|
||||
### 4. Mine
|
||||
|
||||
```bash
|
||||
subminer video.mkv # play video with overlay
|
||||
subminer --start video.mkv # explicit overlay start
|
||||
subminer stats # open immersion dashboard
|
||||
subminer stats -b # stats daemon in background
|
||||
subminer stats -s # stop background stats daemon
|
||||
```
|
||||
|
||||
On **Windows**, the `subminer` script must be run with `bun run subminer` (e.g. `bun run subminer video.mkv`). The recommended alternative is the **SubMiner mpv** shortcut (created during setup) or `SubMiner.exe --launch-mpv`. Drag a video file onto the shortcut to play it, or double-click it to open mpv with SubMiner's defaults.
|
||||
|
||||
## Documentation
|
||||
|
||||
Full guides on configuration, Anki setup, Jellyfin, immersion tracking, and more: **[docs.subminer.moe](https://docs.subminer.moe)**
|
||||
|
||||
---
|
||||
|
||||
## Requirements
|
||||
|
||||
| Required | Optional |
|
||||
| ------------------------------------------------------ | ----------------------------- |
|
||||
| [`mpv`](https://mpv.io) with IPC socket | `yt-dlp` |
|
||||
| `ffmpeg` | `guessit` (AniSkip detection) |
|
||||
| `mecab` + `mecab-ipadic` | `fzf` / `rofi` |
|
||||
| [`bun`](https://bun.sh) (source builds, Linux wrapper) | `chafa`, `ffmpegthumbnailer` |
|
||||
| Linux: `hyprctl` or `xdotool` + `xwininfo` | |
|
||||
| macOS: Accessibility permission | |
|
||||
|
||||
Windows uses native window tracking and does not need the Linux compositor tools.
|
||||
|
||||
## Documentation
|
||||
|
||||
Full guides on configuration, Anki, Jellyfin, immersion tracking, and more at **[docs.subminer.moe](https://docs.subminer.moe)**.
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
SubMiner builds on the work of these open-source projects:
|
||||
|
||||
| Project | Role |
|
||||
| ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
|
||||
| [Anacreon-Script](https://github.com/friedrich-de/Anacreon-Script) | Inspiration for the mining workflow |
|
||||
| [asbplayer](https://github.com/killergerbah/asbplayer) | Inspiration for subtitle sidebar and logic for YouTube subtitle parsing |
|
||||
| [Bee's Character Dictionary](https://github.com/bee-san/Japanese_Character_Name_Dictionary) | Character name recognition in subtitles |
|
||||
| [GameSentenceMiner](https://github.com/bpwhelan/GameSentenceMiner) | Inspiration for Electron overlay with Yomitan integration |
|
||||
| [jellyfin-mpv-shim](https://github.com/jellyfin/jellyfin-mpv-shim) | Jellyfin integration |
|
||||
| [Jimaku.cc](https://jimaku.cc) | Japanese subtitle search and downloads |
|
||||
| [Renji's Texthooker Page](https://github.com/Renji-XD/texthooker-ui) | Base for the WebSocket texthooker integration |
|
||||
| [Yomitan](https://github.com/yomidevs/yomitan) | Dictionary engine powering all lookups and the morphological parser |
|
||||
| [yomitan-jlpt-vocab](https://github.com/stephenmk/yomitan-jlpt-vocab) | JLPT level tags for vocabulary |
|
||||
Built on [GameSentenceMiner](https://github.com/bpwhelan/GameSentenceMiner), [Renji's Texthooker Page](https://github.com/Renji-XD/texthooker-ui), [Anacreon-Script](https://github.com/friedrich-de/Anacreon-Script), and [Bee's Character Dictionary](https://github.com/bee-san/Japanese_Character_Name_Dictionary). Subtitles from [Jimaku.cc](https://jimaku.cc). Lookups via [Yomitan](https://github.com/yomidevs/yomitan). JLPT tags from [yomitan-jlpt-vocab](https://github.com/stephenmk/yomitan-jlpt-vocab).
|
||||
|
||||
## License
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.5 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 65 KiB |
@@ -1,23 +0,0 @@
|
||||
---
|
||||
id: TASK-279
|
||||
title: Prepare v0.11.2 release notes and verify release gate
|
||||
status: In Progress
|
||||
assignee: []
|
||||
created_date: '2026-04-04 07:38'
|
||||
labels: []
|
||||
dependencies: []
|
||||
priority: high
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Generate release metadata for the pending changelog fragments, review the resulting changelog/release notes output, and run the documented local release gate so the repo is ready for a v0.11.2 cut if checks pass.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [ ] #1 CHANGELOG.md and release/release-notes.md are generated for v0.11.2 using the pending changes fragments.
|
||||
- [ ] #2 The documented local release gate for release prep is run and the pass/fail state is recorded with any blockers.
|
||||
- [ ] #3 Any release-prep file updates needed for the generated notes are left in the worktree for review without tagging or pushing.
|
||||
<!-- AC:END -->
|
||||
@@ -1,35 +0,0 @@
|
||||
---
|
||||
id: TASK-194
|
||||
title: App-owned YouTube subtitle picker flow
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-03-18 07:52'
|
||||
updated_date: '2026-03-23 03:22'
|
||||
labels: []
|
||||
dependencies: []
|
||||
references:
|
||||
- /home/sudacode/projects/japanese/SubMiner/launcher/youtube/orchestrator.ts
|
||||
- /home/sudacode/projects/japanese/SubMiner/launcher/youtube/manual-subs.ts
|
||||
- /home/sudacode/projects/japanese/SubMiner/src/core/services/tokenizer.ts
|
||||
documentation:
|
||||
- /home/sudacode/projects/japanese/SubMiner/youtube.md
|
||||
priority: medium
|
||||
ordinal: 137500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Replace the YouTube subtitle-generation-first flow with an app-owned picker flow that boots mpv paused, opens an overlay track picker, downloads selected subtitles into external subtitle files, and preserves generation as an explicit mode. Keep the existing SubMiner tokenization and annotation pipeline as the downstream consumer of downloaded subtitle files.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Launcher and app expose YouTube subtitle acquisition modes `download` and `generate`, with `download` as the default.
|
||||
- [x] #2 YouTube playback boots mpv paused and presents an overlay selection UI for primary and secondary subtitle choices.
|
||||
- [x] #3 Selected YouTube subtitle tracks are downloaded to external subtitle files and loaded into mpv before playback resumes.
|
||||
- [x] #4 `generate` mode preserves the existing subtitle generation path as an explicit opt-in behavior.
|
||||
- [x] #5 Downloaded YouTube subtitle files integrate with the existing SubMiner subtitle/tokenization/annotation pipeline without regressing current overlay behavior.
|
||||
- [x] #6 Tests cover mode selection, subtitle-track enumeration/selection flow, and the paused bootstrap plus app handoff path.
|
||||
- [x] #7 User-facing config and launcher docs are updated to describe the new modes and default behavior.
|
||||
<!-- AC:END -->
|
||||
@@ -1,40 +0,0 @@
|
||||
---
|
||||
id: TASK-214
|
||||
title: Jump subtitle sidebar directly to resume position on first resolved cue
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-03-21 11:15'
|
||||
updated_date: '2026-03-23 03:22'
|
||||
labels:
|
||||
- bug
|
||||
- ux
|
||||
- overlay
|
||||
- subtitles
|
||||
dependencies: []
|
||||
references:
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/renderer/modals/subtitle-sidebar.ts
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/renderer/modals/subtitle-sidebar.test.ts
|
||||
priority: medium
|
||||
ordinal: 142500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
When playback starts from a resumed timestamp while the subtitle sidebar is open, the sidebar currently smooth-scrolls from the top of the cue list to the resumed cue. Change the first resolved active-cue positioning to jump immediately to the resume location while preserving smooth auto-follow for later playback-driven cue advances.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 The first active cue resolved after open/resume uses an instant jump instead of smooth-scrolling through the list.
|
||||
- [x] #2 Normal subtitle-sidebar auto-follow remains smooth after the first active cue has been positioned.
|
||||
- [x] #3 Regression coverage distinguishes the initial jump behavior from later smooth auto-follow updates.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
2026-03-21: Fixed by treating the first auto-scroll from `previousActiveCueIndex < 0` as `behavior: 'auto'` in the subtitle sidebar scroll helper. Added renderer regression coverage for initial jump plus later smooth follow.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
@@ -1,44 +0,0 @@
|
||||
---
|
||||
id: TASK-215
|
||||
title: Add startup auto-open option for subtitle sidebar
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-03-21 11:35'
|
||||
updated_date: '2026-03-23 03:22'
|
||||
labels:
|
||||
- feature
|
||||
- ux
|
||||
- overlay
|
||||
- subtitles
|
||||
dependencies: []
|
||||
references:
|
||||
- /Users/sudacode/projects/japanese/SubMiner/src/types.ts
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/config/definitions/defaults-subtitle.ts
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/config/resolve/subtitle-domains.ts
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/renderer/modals/subtitle-sidebar.ts
|
||||
- /Users/sudacode/projects/japanese/SubMiner/src/renderer/renderer.ts
|
||||
priority: medium
|
||||
ordinal: 143500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Add a subtitle sidebar config option that auto-opens the sidebar once during overlay startup. The option should default to `false`, only apply when the sidebar feature is enabled, and should not force the sidebar back open later in the same session after manual close or later visibility changes.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 `subtitleSidebar.autoOpen` is available in config with default `false`.
|
||||
- [x] #2 When enabled, overlay startup opens the subtitle sidebar once after initial sidebar config/snapshot load.
|
||||
- [x] #3 Regression coverage covers config resolution and startup-only auto-open behavior.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
2026-03-21: Added `subtitleSidebar.autoOpen` to types/defaults/config registry and resolver. Renderer bootstrap now calls a startup-only subtitle sidebar helper after the initial snapshot refresh. Modal regression coverage verifies startup auto-open requires both `enabled` and `autoOpen`.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
@@ -1,70 +0,0 @@
|
||||
---
|
||||
id: TASK-217
|
||||
title: Fix embedded overlay passthrough sync between subtitle and sidebar
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-03-21 23:16'
|
||||
updated_date: '2026-03-23 03:22'
|
||||
labels:
|
||||
- bug
|
||||
- overlay
|
||||
- macos
|
||||
dependencies: []
|
||||
references:
|
||||
- src/renderer/handlers/mouse.ts
|
||||
- src/renderer/modals/subtitle-sidebar.ts
|
||||
- src/renderer/renderer.ts
|
||||
documentation:
|
||||
- docs/workflow/verification.md
|
||||
priority: high
|
||||
ordinal: 118500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
On macOS, when both the subtitle overlay and embedded subtitle sidebar are visible, mouse passthrough to mpv can remain stale until the user hovers the sidebar. After closing the sidebar, passthrough can likewise remain stale until the user hovers the subtitle again. Fix the overlay input-state synchronization so passthrough reflects the current hover/open state immediately instead of relying on the last hover target.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 When the embedded subtitle sidebar is open and the pointer is not over subtitle or sidebar content, the overlay returns to mouse passthrough immediately without requiring a sidebar hover cycle.
|
||||
- [x] #2 When transitioning between subtitle hover and sidebar hover states on macOS embedded sidebar mode, mouse ignore state stays in sync with the currently interactive region.
|
||||
- [x] #3 Closing the embedded subtitle sidebar restores the correct passthrough state based on remaining subtitle hover/modal state without requiring an additional hover.
|
||||
- [x] #4 Regression tests cover the passthrough synchronization behavior.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Add a shared renderer-side passthrough sync helper that derives whether the overlay should ignore mouse events from subtitle hover, embedded sidebar visibility/hover, popup visibility, and modal state.
|
||||
2. Replace direct embedded-sidebar passthrough toggles in subtitle hover/sidebar handlers with calls to the shared sync helper so state is recomputed on every transition.
|
||||
3. Add regression tests for macOS embedded sidebar mode covering sidebar-open idle passthrough, subtitle-to-sidebar transitions, and sidebar-close restore behavior.
|
||||
4. Run targeted renderer tests for mouse/sidebar passthrough coverage, then summarize any residual risk.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Added shared renderer overlay mouse-ignore recompute so subtitle hover, embedded sidebar hover/open/close, and popup idle transitions all derive passthrough from current state instead of last hover target.
|
||||
|
||||
Added regression coverage for embedded sidebar idle passthrough on subtitle leave and for sidebar-close recompute behavior.
|
||||
|
||||
Verification: `bun run typecheck` passed; `bun test src/renderer/handlers/mouse.test.ts` passed; `bun test src/renderer/modals/subtitle-sidebar.test.ts` passed; core verification wrapper artifact at `.tmp/skill-verification/subminer-verify-20260321-162743-XhSBxw` hit an unrelated `bun run test:fast` failure in `scripts/update-aur-package.test.ts` because macOS system bash lacks `mapfile`.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Fixed stale embedded-sidebar passthrough sync on macOS by introducing a shared renderer mouse-ignore recompute path and tracking sidebar-hover state separately from subtitle hover. Subtitle hover leave, sidebar hover enter/leave, sidebar open, and sidebar close now all recompute passthrough from the current overlay state instead of waiting for a later hover event to repair it. Added regression tests covering subtitle-leave passthrough while the embedded sidebar is open but idle, plus sidebar-close restore behavior based on remaining subtitle hover state.
|
||||
|
||||
Tests run:
|
||||
- `bun run typecheck`
|
||||
- `bun test src/renderer/handlers/mouse.test.ts`
|
||||
- `bun test src/renderer/modals/subtitle-sidebar.test.ts`
|
||||
- `bash .agents/skills/subminer-change-verification/scripts/classify_subminer_diff.sh src/renderer/state.ts src/renderer/overlay-mouse-ignore.ts src/renderer/handlers/mouse.ts src/renderer/handlers/mouse.test.ts src/renderer/modals/subtitle-sidebar.ts src/renderer/modals/subtitle-sidebar.test.ts`
|
||||
- `bash .agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh --lane core src/renderer/state.ts src/renderer/overlay-mouse-ignore.ts src/renderer/handlers/mouse.ts src/renderer/handlers/mouse.test.ts src/renderer/modals/subtitle-sidebar.ts src/renderer/modals/subtitle-sidebar.test.ts` (typecheck passed; `test:fast` blocked by unrelated `scripts/update-aur-package.test.ts` failure on macOS Bash 3.2 lacking `mapfile`)
|
||||
|
||||
Risk: the classifier flagged this as a real-runtime candidate, so actual Electron/mpv macOS pointer behavior was not exercised in a live runtime during this turn.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,69 +0,0 @@
|
||||
---
|
||||
id: TASK-218
|
||||
title: Delete zero-session media from stats library and trends
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-03-22 16:20'
|
||||
updated_date: '2026-03-24 06:41'
|
||||
labels:
|
||||
- stats
|
||||
- immersion-tracker
|
||||
dependencies: []
|
||||
references:
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/core/services/immersion-tracker/query.ts
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/core/services/immersion-tracker/lifetime.ts
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/core/services/immersion-tracker/maintenance.ts
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/core/services/immersion-tracker/__tests__/query.test.ts
|
||||
priority: medium
|
||||
ordinal: 153500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Deleting the last retained session for a video still left stale lifetime media rows and trend rollups behind, so the stats dashboard could continue showing ghost entries in Library and Trends after all sessions were gone.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Deleting the final session for a video removes that media from Library queries and detail reads
|
||||
- [x] #2 Deleting the final session for a video removes stale daily/monthly trend rollups for that media
|
||||
- [x] #3 Regression coverage proves zero-session media disappears from affected stats surfaces after deletion
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Add a failing regression around deleting the only retained session for a video while preexisting lifetime and rollup rows exist.
|
||||
2. Patch the deletion path to rebuild lifetime and rollup state from retained sessions inside the same transaction.
|
||||
3. Run focused immersion-tracker tests plus the repo-native verifier core lane and record results.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Added a query regression that seeds a finished session plus stale lifetime media/anime rows and daily/monthly rollups, deletes that only session, and asserts Library, Anime detail, and Trends all drop the media immediately.
|
||||
|
||||
Refactored lifetime rebuild logic so it can run inside an existing delete transaction, then reused that helper from `deleteSession`, `deleteSessions`, and `deleteVideo`.
|
||||
|
||||
Added a rollup rebuild helper that clears existing daily/monthly rollups and reconstructs them from retained telemetry inside the current transaction so deleted sessions cannot leave ghost trend points behind.
|
||||
|
||||
Verification passed:
|
||||
- `bun test src/core/services/immersion-tracker/__tests__/query.test.ts`
|
||||
- `bun test src/core/services/immersion-tracker-service.test.ts`
|
||||
- `bash .agents/skills/subminer-change-verification/scripts/classify_subminer_diff.sh src/core/services/immersion-tracker/query.ts src/core/services/immersion-tracker/lifetime.ts src/core/services/immersion-tracker/maintenance.ts src/core/services/immersion-tracker/__tests__/query.test.ts`
|
||||
- `bash .agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh --lane core src/core/services/immersion-tracker/query.ts src/core/services/immersion-tracker/lifetime.ts src/core/services/immersion-tracker/maintenance.ts src/core/services/immersion-tracker/__tests__/query.test.ts`
|
||||
|
||||
Verifier artifact dir: `.tmp/skill-verification/subminer-verify-20260322-210718-n6sGL8`
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Delete paths now rebuild lifetime summaries and trend rollups after removing sessions, so when the last session for a video disappears the stats database also drops that media from Library, related detail reads, and chart data. Added a regression proving a video with only stale lifetime/rollup rows vanishes after its final session is deleted, and verified the change with focused immersion-tracker tests plus the SubMiner core verification lane.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,66 +0,0 @@
|
||||
---
|
||||
id: TASK-220
|
||||
title: Restore YouTube overlay mpv keybindings after picker routing
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-03-22 00:00'
|
||||
updated_date: '2026-03-22 23:49'
|
||||
labels:
|
||||
- bug
|
||||
- overlay
|
||||
- youtube
|
||||
- keyboard
|
||||
dependencies: []
|
||||
references:
|
||||
- src/renderer/handlers/keyboard.ts
|
||||
- src/renderer/modals/youtube-track-picker.ts
|
||||
- src/renderer/handlers/keyboard.test.ts
|
||||
- src/renderer/modals/youtube-track-picker.test.ts
|
||||
documentation:
|
||||
- docs/workflow/verification.md
|
||||
priority: high
|
||||
ordinal: 118800
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Regression: after adding the YouTube subtitle picker modal path, visible-overlay keydown handling can stop before reaching the shared mpv keybinding dispatch path. Result: default overlay mpv bindings like `Space` pause/play and `q` quit stop working while the overlay owns focus during YouTube playback.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Unhandled keys while the YouTube track picker state is active still fall through to the shared overlay mpv keybinding dispatcher.
|
||||
- [x] #2 The YouTube picker continues to consume `Enter` and `Escape` for its own actions.
|
||||
- [x] #3 Renderer regression tests cover both the picker modal key contract and the shared keyboard dispatch fallback.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Add a failing renderer keyboard regression test covering YouTube picker state plus shared mpv keybinding fallback.
|
||||
2. Update the global keyboard handler to return early only when the YouTube picker actually handles the key event.
|
||||
3. Update the picker modal handler to return false for unhandled keys while preserving `Enter`/`Escape`.
|
||||
4. Run the cheap renderer verification lane and record results.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Fixed the regression by making the global renderer keyboard handler stop early for the YouTube picker only when the picker actually consumes the key. The picker modal now returns `false` for unrelated keys, so shared overlay mpv bindings like `Space` and `KeyQ` still dispatch while the visible overlay has focus.
|
||||
|
||||
Added regression coverage in the keyboard handler suite for mpv keybinding fallback during YouTube picker state, plus a picker-modal contract test that keeps `Escape` handled but leaves unrelated keys unclaimed.
|
||||
|
||||
Verification:
|
||||
- `bun test src/renderer/handlers/keyboard.test.ts src/renderer/modals/youtube-track-picker.test.ts`
|
||||
- `bash .agents/skills/subminer-change-verification/scripts/classify_subminer_diff.sh src/renderer/handlers/keyboard.ts src/renderer/handlers/keyboard.test.ts src/renderer/modals/youtube-track-picker.ts src/renderer/modals/youtube-track-picker.test.ts`
|
||||
- `bash .agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh --lane core src/renderer/handlers/keyboard.ts src/renderer/handlers/keyboard.test.ts src/renderer/modals/youtube-track-picker.ts src/renderer/modals/youtube-track-picker.test.ts`
|
||||
- verifier artifact: `.tmp/skill-verification/subminer-verify-20260322-234831-b2m6nJ`
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Restored YouTube-session overlay mpv keybindings by removing an unconditional early return added to the renderer keyboard path for the YouTube subtitle picker modal. Unhandled keys now fall through to the shared mpv keybinding dispatcher, while handled picker keys (`Enter`, `Escape`) still stay local to the picker. Added renderer regression tests for both the keyboard fallback path and the picker modal key-consumption contract.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,58 +0,0 @@
|
||||
---
|
||||
id: TASK-221
|
||||
title: 'Assess and address PR #31 latest CodeRabbit review'
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-03-23 07:53'
|
||||
updated_date: '2026-03-24 06:41'
|
||||
labels:
|
||||
- pr-review
|
||||
- coderabbit
|
||||
dependencies: []
|
||||
references:
|
||||
- >-
|
||||
PR #31 feat: add app-owned YouTube subtitle flow with absPlayer-style
|
||||
parsing
|
||||
priority: medium
|
||||
ordinal: 152500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Inspect the latest CodeRabbit review on PR #31, evaluate each actionable comment against the current branch, implement valid fixes, verify the changes, and prepare PR thread updates.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Inspect latest CodeRabbit review on PR #31 and separate valid action items from non-blocking suggestions.
|
||||
2. Add regression coverage for any real bugs before changing production code.
|
||||
3. Implement the minimal fixes for confirmed issues in runtime, renderer modal flow, and test fixtures.
|
||||
4. Run targeted tests plus repo-native verification lanes.
|
||||
5. Update PR threads with fix status and rationale for any comments not actioned yet.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Implemented and pushed commit 207151db to PR #31.
|
||||
|
||||
Replied in-thread to the CodeRabbit comments for YouTube host matching, duplicate picker submissions, and missing MediaDetailView test fixture videoId fields.
|
||||
|
||||
Follow-up scope added: update release-facing docs/changelog for the YouTube subtitle picker work and run a release-readiness gate before handoff.
|
||||
|
||||
Added release-facing docs/changelog updates in commit b7e0026d and pushed them to PR #31.
|
||||
|
||||
Ran the release-readiness gate: changelog:lint, changelog:pr-check, verify:config-example, typecheck, test:fast, test:env, build, test:smoke:dist, docs:test, docs:build.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Assessed the latest CodeRabbit review on PR #31 and applied the confirmed fixes. Tightened `isYoutubeMediaPath()` to match only exact YouTube hosts or subdomains with a regression test for `notyoutube.com`, added an in-flight guard plus temporary control disabling to the YouTube track picker with a duplicate-submit regression test, replaced the picker empty-state `innerHTML` fallback with explicit DOM construction, and added the missing `videoId` fields to the `MediaDetailView` test fixtures. Verified with targeted Bun tests and the `runtime-compat` verification lane (`build`, `test:runtime:compat`, `test:smoke:dist`).
|
||||
|
||||
Updated `README.md`, `docs-site/usage.md`, and `changes/2026-03-23-immersion-youtube.md` so the PR is release-facing and user-visible surfaces describe the YouTube subtitle picker flow plus its latest hardening.
|
||||
|
||||
Release-readiness checks passed locally: `bun run changelog:lint`, `bun run changelog:pr-check`, `bun run verify:config-example`, `bun run typecheck`, `bun run test:fast`, `bun run test:env`, `bun run build`, `bun run test:smoke:dist`, `bun run docs:test`, and `bun run docs:build`.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,56 +0,0 @@
|
||||
---
|
||||
id: TASK-222
|
||||
title: Fix YouTube overlay keybindings in subtitle path
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-03-23 08:32'
|
||||
updated_date: '2026-03-24 06:41'
|
||||
labels:
|
||||
- bug
|
||||
dependencies: []
|
||||
references:
|
||||
- /Users/sudacode/projects/japanese/SubMiner/src/main/runtime
|
||||
- /Users/sudacode/projects/japanese/SubMiner/src/core/services
|
||||
priority: high
|
||||
ordinal: 151500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Users watching video through the YouTube subtitle path cannot use some overlay keyboard controls such as quit and pause/play. Restore expected overlay keybinding behavior for that playback path without regressing other overlay input handling.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Overlay quit and pause/play keybindings work while using the YouTube subtitle path.
|
||||
- [x] #2 Existing overlay keybinding behavior for non-YouTube playback remains unchanged.
|
||||
- [x] #3 Regression coverage exercises the YouTube subtitle path keyboard handling.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Add a regression test around YouTube track-picker close to verify it requests main-process main-window focus restoration before returning overlay focus locally.
|
||||
2. Update the YouTube track-picker close flow to call `window.electronAPI.focusMainWindow()` alongside the existing `window.focus()` and `overlay.focus()` restoration.
|
||||
3. Run targeted tests for the picker/keyboard paths to verify YouTube playback regains overlay keybindings without regressing existing overlay behavior.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Investigated overlay input path. Renderer already maps Space/KeyQ to mpv commands, but YouTube track-picker close only restores DOM focus (`window.focus` + `overlay.focus`) and does not invoke main-process window focus recovery, unlike the keyboard-mode focus reclaim path. Suspected root cause: overlay BrowserWindow focus is not restored after the YouTube picker closes, so playback keybindings stop reaching renderer keydown handlers.
|
||||
|
||||
User approved implementation plan on 2026-03-23. Proceeding with TDD: add failing regression first, then minimal fix, then targeted verification.
|
||||
|
||||
Implemented fix in the YouTube track-picker close path: request main-process `focusMainWindow()` before restoring renderer window/overlay focus so overlay keydown handlers regain input after YouTube subtitle selection.
|
||||
|
||||
Verification: `bun test src/renderer/modals/youtube-track-picker.test.ts` and `bun test src/renderer/handlers/keyboard.test.ts` both pass.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Restored overlay keyboard focus after closing the YouTube subtitle picker by invoking the main-process `focusMainWindow()` recovery path before local window/overlay focus restoration. Added regression coverage to the YouTube picker modal test and verified existing keyboard handler coverage for YouTube picker passthrough keys (`Space`, `KeyQ`) remains green.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,109 +0,0 @@
|
||||
---
|
||||
id: TASK-223
|
||||
title: Fix YouTube overlay Anki initialization regression
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-03-23 08:41'
|
||||
updated_date: '2026-03-24 06:41'
|
||||
labels:
|
||||
- bug
|
||||
- youtube
|
||||
- anki
|
||||
dependencies: []
|
||||
references:
|
||||
- /Users/sudacode/projects/japanese/SubMiner/src/main.ts
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/core/services/overlay-runtime-init.ts
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/main/runtime/cli-command-runtime-handler.ts
|
||||
documentation:
|
||||
- /Users/sudacode/projects/japanese/SubMiner/docs/workflow/verification.md
|
||||
priority: high
|
||||
ordinal: 154500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Restore Anki-backed lookup and known-word behavior during YouTube playback. Recent startup changes appear to let the YouTube flow initialize the overlay before runtime prerequisites exist, leaving the Anki integration unavailable for popup Mine actions and known-word highlighting.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 YouTube playback initializes Anki integration once overlay startup prerequisites are available so lookup can offer card-add actions again
|
||||
- [x] #2 Known-word / N+1 state is available during YouTube playback when the user has Anki-backed known-word highlighting enabled
|
||||
- [x] #3 Regression coverage fails before the fix and passes after it for the YouTube startup path
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Add a regression test covering the YouTube playback command path and assert overlay startup prerequisites are established before the flow runs.
|
||||
2. Reuse the overlay startup prerequisite bootstrap for the YouTube playback path so Anki integration sees subtitle tracker, mpv client, and runtime options manager before initialization.
|
||||
3. Verify with focused runtime/CLI tests, then run the cheapest sufficient verification lane for the touched files.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Identified regression path in CLI command runtime: YouTube playback commands could reach overlay initialization without first materializing overlay startup prerequisites, leaving Anki integration unavailable during the initial startup attempt.
|
||||
|
||||
Added a regression test at src/main/runtime/cli-command-runtime-handler.test.ts covering youtubePlay command dispatch outside texthooker-only mode.
|
||||
|
||||
Verified with bun test src/main/runtime/cli-command-runtime-handler.test.ts src/main/runtime/cli-command-prechecks.test.ts src/main/runtime/cli-command-prechecks-main-deps.test.ts and bun test src/core/services/cli-command.test.ts src/main/runtime/cli-command-context-main-deps.test.ts src/main/runtime/cli-command-prechecks-main-deps.test.ts src/main/runtime/cli-command-runtime-handler.test.ts.
|
||||
|
||||
Removed the YouTube-only ensureOverlayRuntimeReady path from src/main.ts after confirming regular app startup already loads Yomitan and the shared CLI overlay pre-dispatch bootstrap now covers overlay prerequisites.
|
||||
|
||||
Moved overlay bootstrap into the generic initial-args startup path for any initial command that needs overlay runtime, so overlay prerequisites and overlay initialization happen before CLI dispatch instead of inside a YouTube-only or last-moment command path.
|
||||
|
||||
Additional verification passed: bun test src/main/runtime/initial-args-runtime-handler.test.ts src/main/runtime/initial-args-handler.test.ts src/main/runtime/initial-args-main-deps.test.ts, bun run typecheck, bun run test:runtime:compat
|
||||
|
||||
Follow-up regression: subtitle picker was still auto-submitting the default selection during YouTube startup. Investigating renderer-side immediate Enter key bleed-through on picker open.
|
||||
|
||||
Root cause for remaining picker regression: the YouTube track picker accepted Enter immediately on open, so the launch keypress could auto-submit the default track selection before the modal was visible to the user.
|
||||
|
||||
Added renderer regression coverage in src/renderer/modals/youtube-track-picker.test.ts proving immediate Enter after open is ignored and a later Enter still submits normally.
|
||||
|
||||
Implemented a 200ms open-key guard in src/renderer/modals/youtube-track-picker.ts for Enter-based submission only; Escape/click behavior unchanged.
|
||||
|
||||
New follow-up regression report: YouTube subtitle picker can open before the mpv playback window is ready, leaving the picker behind the overlay after geometry snaps into place. Investigating picker-open gating and modal-targeting timing.
|
||||
|
||||
Identified likely cause of picker-behind-overlay regression: YouTube picker open logic mixed overlay targets. First attempt preferred the visible main overlay, timeout retry switched to the dedicated modal window, allowing a late first open to cover the modal.
|
||||
|
||||
Extracted picker-open policy into src/main/runtime/youtube-picker-open.ts and changed YouTube picker startup to always target the dedicated modal window, including retries. This keeps the picker on a single window path and lets overlay-runtime hide/click-through the main overlay while the modal is active.
|
||||
|
||||
Added regression tests in src/main/runtime/youtube-picker-open.test.ts covering dedicated modal first-open, dedicated-modal retry, and failure when no modal target is available.
|
||||
|
||||
User reports overlay flow still feels wrong: YouTube path appears to preload subtitles before mandatory selection and may open the picker before mpv window readiness. Re-evaluating flow design against regular video startup before further implementation.
|
||||
|
||||
New follow-up regression report: duplicate overlay windows appear during YouTube playback and only one window shows subtitles. Investigating main-overlay versus dedicated modal-window handoff/cleanup.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Fixed the YouTube Anki initialization regression by making CLI commands that require overlay runtime bootstrap overlay startup prerequisites before command dispatch when not in texthooker-only mode. This ensures the YouTube playback flow has the mpv client, runtime options manager, and subtitle timing tracker ready before overlay/Anki initialization runs, restoring Mine actions and known-word-backed behavior.
|
||||
|
||||
Added a regression test covering youtubePlay command dispatch in src/main/runtime/cli-command-runtime-handler.test.ts.
|
||||
|
||||
Verification:
|
||||
- bun test src/main/runtime/cli-command-runtime-handler.test.ts src/main/runtime/cli-command-prechecks.test.ts src/main/runtime/cli-command-prechecks-main-deps.test.ts
|
||||
- bun test src/core/services/cli-command.test.ts src/main/runtime/cli-command-context-main-deps.test.ts src/main/runtime/cli-command-prechecks-main-deps.test.ts src/main/runtime/cli-command-runtime-handler.test.ts
|
||||
|
||||
Updated the fix to avoid a YouTube-specific startup path: removed the dedicated ensureOverlayRuntimeReady helper from src/main.ts and relied on the shared CLI overlay prerequisite bootstrap instead.
|
||||
|
||||
Additional verification: bun run typecheck
|
||||
|
||||
Follow-up adjustment: initial overlay-runtime commands now bootstrap overlay prerequisites and initialize overlay during the shared initial-args startup path, rather than waiting for command dispatch. This keeps YouTube on the regular startup path while preserving earlier overlay availability.
|
||||
|
||||
Additional verification: bun test src/main/runtime/initial-args-runtime-handler.test.ts src/main/runtime/initial-args-handler.test.ts src/main/runtime/initial-args-main-deps.test.ts; bun run test:runtime:compat
|
||||
|
||||
Follow-up fix: the YouTube subtitle picker now ignores immediate Enter key bleed-through right after opening, preventing the startup keypress from auto-submitting the default track selection before the modal is visible.
|
||||
|
||||
Added renderer regression coverage for immediate Enter suppression and verified with bun test src/renderer/modals/youtube-track-picker.test.ts plus the runtime-compat verification lane for the touched files.
|
||||
|
||||
Follow-up fix: YouTube subtitle picker startup now uses a dedicated modal-window path consistently instead of mixing main-overlay first-open with modal-window retry. That prevents late overlay opens from covering the interactive picker while mpv/window tracking settles.
|
||||
|
||||
Verified with bun test src/main/runtime/youtube-picker-open.test.ts, bun test src/renderer/modals/youtube-track-picker.test.ts, and the runtime-compat verification lane for src/main.ts plus the touched picker files.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,61 +0,0 @@
|
||||
---
|
||||
id: TASK-224
|
||||
title: >-
|
||||
Auto-load default YouTube subtitles at playback start and make picker
|
||||
manual-only
|
||||
status: Done
|
||||
assignee:
|
||||
- Codex
|
||||
created_date: '2026-03-23 18:51'
|
||||
updated_date: '2026-03-24 06:41'
|
||||
labels:
|
||||
- youtube
|
||||
- mpv
|
||||
- overlay
|
||||
- keybindings
|
||||
dependencies: []
|
||||
references:
|
||||
- /Users/sudacode/projects/japanese/SubMiner/src/main/runtime/youtube-flow.ts
|
||||
- >-
|
||||
/Users/sudacode/projects/japanese/SubMiner/src/renderer/modals/youtube-track-picker.ts
|
||||
- /Users/sudacode/projects/japanese/SubMiner/src/config/definitions/shared.ts
|
||||
priority: high
|
||||
ordinal: 150500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Replace the mandatory YouTube subtitle picker startup flow with automatic default-track loading. On YouTube playback start, attempt to load the default primary subtitle and best-effort secondary subtitle without prompting. Gate playback only on primary subtitle load/tokenization readiness. If primary subtitle probing/download/loading fails, resume playback and report the failure through the configured notification/output path. Keep the YouTube subtitle picker as a regular overlay modal opened by a new default keybinding during active YouTube playback.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Opening a YouTube URL auto-selects and attempts to load the default primary subtitle without opening the picker modal.
|
||||
- [x] #2 Opening a YouTube URL also attempts to load the default secondary subtitle when available, but playback never waits on secondary success.
|
||||
- [x] #3 Playback remains gated only until the primary subtitle is loaded and tokenization is ready; primary failure resumes playback immediately.
|
||||
- [x] #4 Primary auto-load failures report through the existing configured notification/output path and keep playback running.
|
||||
- [x] #5 The YouTube subtitle picker can be opened manually during active YouTube playback via a new default keybinding.
|
||||
- [x] #6 Regression tests cover startup auto-load success, primary failure fallback, and the manual picker keybinding flow.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Add failing tests for YouTube startup auto-load success, primary failure fallback, and manual picker keybinding flow.
|
||||
2. Refactor the YouTube runtime to auto-select default tracks on startup, gate playback only on primary subtitle/tokenization readiness, and route failures through the configured notification/output path.
|
||||
3. Add a new default keybinding and command path to open the YouTube picker manually during active YouTube playback.
|
||||
4. Run targeted tests, then SubMiner verification lanes for launcher/runtime changes; update docs/changelog if required by the final behavior change.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Verification blocker outside this change: `bun run test:fast` still fails at `scripts/update-aur-package.test.ts` on macOS because `scripts/update-aur-package.sh` uses `mapfile`, which is unavailable in the system Bash 3.x environment used here.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Reworked app-owned YouTube playback to auto-load the default primary subtitle plus a best-effort secondary subtitle at startup instead of forcing the picker modal first. Playback now waits only on primary subtitle load/tokenization readiness, routes startup primary-failure messaging through the configured notification output path, and keeps the YouTube subtitle picker available on demand via a new default `Ctrl+Shift+J` keybinding during active YouTube playback. Updated the runtime/IPC/config plumbing, user-facing help/docs, and added regression coverage for startup auto-load, primary-failure fallback, and manual picker invocation.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,41 +0,0 @@
|
||||
---
|
||||
id: TASK-225
|
||||
title: Fix frozen primary YouTube subtitle display after auto-load startup
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-03-23 20:07'
|
||||
updated_date: '2026-03-24 06:41'
|
||||
labels:
|
||||
- bug
|
||||
- youtube
|
||||
- subtitles
|
||||
dependencies:
|
||||
- TASK-224
|
||||
priority: high
|
||||
ordinal: 149500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
After the new YouTube auto-load startup flow, the primary subtitle overlay can stay stuck on an older line while the subtitle sidebar continues advancing. Investigate startup suppression / subtitle refresh timing and restore live primary overlay updates after auto-loaded subtitles are injected.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 When YouTube auto-load succeeds, the visible primary subtitle continues advancing after playback resumes.
|
||||
- [x] #2 Startup suppression does not leave the primary subtitle display stuck on a stale line.
|
||||
- [x] #3 A regression test covers the startup path that previously froze the visible primary subtitle while sidebar timing continued advancing.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Root cause: applyStartupState seeded youtubePlaybackFlowPending from initialArgs.youtubePlay, and runYoutubePlaybackFlowMain restored that preexisting true value after startup auto-load. Result: primary subtitle events stayed suppressed for startup-launched YouTube playback while sidebar timing still advanced.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Stopped pre-seeding youtubePlaybackFlowPending from startup CLI args so only the actual YouTube playback bootstrap window suppresses subtitle events. Added a regression test covering startup YouTube args and re-ran targeted YouTube/runtime subtitle tests plus typecheck.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,42 +0,0 @@
|
||||
---
|
||||
id: TASK-226
|
||||
title: Restore subtitle sidebar cues for auto-loaded YouTube subtitles
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-03-23 20:21'
|
||||
updated_date: '2026-03-24 06:41'
|
||||
labels:
|
||||
- bug
|
||||
- youtube
|
||||
- subtitle-sidebar
|
||||
dependencies:
|
||||
- TASK-224
|
||||
- TASK-225
|
||||
priority: high
|
||||
ordinal: 148500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
After fixing startup subtitle event suppression, the primary subtitle overlay updates for auto-loaded YouTube playback but the subtitle sidebar reports no parsed subtitle cues available. Investigate parsed subtitle source registration / refresh for auto-loaded YouTube subtitle files and restore sidebar cue population.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 When YouTube auto-load succeeds, the subtitle sidebar receives parsed cues for the active primary subtitle source.
|
||||
- [x] #2 Auto-loaded YouTube subtitle source changes refresh the sidebar snapshot without requiring manual picker interaction.
|
||||
- [x] #3 A regression test covers the startup auto-load path where live primary subtitles render but sidebar cues remain empty.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Root cause: successful YouTube auto-load refreshed visible primary subtitle state, but did not explicitly initialize parsed subtitle cues from the resolved downloaded primary subtitle file. Sidebar cue population depended on later mpv source rediscovery, which could leave snapshots empty.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Added a regression test for YouTube auto-load sidebar cue refresh and wired the YouTube subtitle flow to explicitly refresh parsed subtitle cues from the resolved primary subtitle path after a successful load. Verified with targeted YouTube/sidebar/runtime tests plus typecheck.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,65 +0,0 @@
|
||||
---
|
||||
id: TASK-227
|
||||
title: 'Assess and address PR #31 latest CodeRabbit review round'
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-03-24 03:53'
|
||||
updated_date: '2026-03-24 06:41'
|
||||
labels:
|
||||
- pr-review
|
||||
- coderabbit
|
||||
dependencies: []
|
||||
references:
|
||||
- >-
|
||||
PR #31 feat: add app-owned YouTube subtitle flow with absPlayer-style
|
||||
parsing
|
||||
priority: medium
|
||||
ordinal: 147500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Inspect the latest CodeRabbit review round on PR #31, verify each actionable comment against the current branch, implement only the valid fixes, add regression coverage where appropriate, and prepare thread replies for resolved or declined items.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Latest CodeRabbit comments on PR #31 are triaged into valid fixes vs non-actioned suggestions with rationale.
|
||||
- [x] #2 Confirmed issues are fixed with regression coverage where appropriate.
|
||||
- [x] #3 Relevant verification passes for the touched areas.
|
||||
- [x] #4 PR reply notes are ready for each addressed or declined latest-review comment.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Verify the five latest CodeRabbit inline comments against the current branch and separate valid bugs from non-actioned suggestions.
|
||||
2. Add failing regression coverage for confirmed issues in launcher playback tests, CLI YouTube flow error handling, and renderer YouTube picker disabled-state behavior.
|
||||
3. Implement the minimal production fixes for the confirmed issues, plus remove the duplicate overlay Anki initialization if still redundant.
|
||||
4. Inspect the YouTube primary-subtitle failure timer wiring to decide whether a code change is warranted in this round or whether a technical reply declining the comment is more correct.
|
||||
5. Run targeted Bun tests for the touched files and prepare concise PR thread replies for each latest-review comment.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Triaged the latest PR #31 CodeRabbit round: five inline comments were current action items; implemented all five. Strengthened the launcher playback test fixture so YouTube pause coverage no longer piggybacks on generic overlay auto-pause settings.
|
||||
|
||||
Added regression tests for CLI YouTube flow rejection handling, no-track picker disabled-state restoration, and app-owned YouTube notification suppression while subtitle acquisition is still in flight.
|
||||
|
||||
Implemented `runAsyncWithOsd(...)` handling for `args.youtubePlay`, kept no-track picker controls disabled after failed continue attempts, added `setAppOwnedFlowInFlight(...)` to the YouTube primary-subtitle notification runtime with main-process wiring around `runYoutubePlaybackFlowMain(...)`, and removed the duplicate `initializeOverlayAnkiIntegrationCore(...)` call from `initializeOverlayRuntime()`.
|
||||
|
||||
Verification passed: `bun test launcher/commands/playback-command.test.ts src/core/services/cli-command.test.ts src/renderer/modals/youtube-track-picker.test.ts src/main/runtime/youtube-primary-subtitle-notification.test.ts` and `bun run typecheck`.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Assessed the latest CodeRabbit review round on PR #31 and implemented all five current inline action items. Strengthened the launcher playback regression test so app-owned YouTube pause behavior is asserted independently from generic overlay auto-pause settings, wrapped the CLI `youtubePlay` branch in the existing `runAsyncWithOsd(...)` path so probe/download/startup failures surface in logs and OSD, kept the no-track YouTube picker controls disabled after rejected continue attempts, suppressed the generic primary-subtitle failure timer while the app-owned YouTube flow is still probing/downloading and restarted it only after the flow settles, and removed the duplicate overlay Anki initialization from `initializeOverlayRuntime()`.
|
||||
|
||||
Verification passed with `bun test launcher/commands/playback-command.test.ts src/core/services/cli-command.test.ts src/renderer/modals/youtube-track-picker.test.ts src/main/runtime/youtube-primary-subtitle-notification.test.ts` and `bun run typecheck`.
|
||||
|
||||
Prepared thread-reply notes for the five latest inline comments; did not post them because GitHub replies are an external side effect.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,64 +0,0 @@
|
||||
---
|
||||
id: TASK-228
|
||||
title: 'Assess and address PR #31 subsequent CodeRabbit review round'
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-03-24 04:10'
|
||||
updated_date: '2026-03-24 06:41'
|
||||
labels:
|
||||
- pr-review
|
||||
- coderabbit
|
||||
dependencies: []
|
||||
references:
|
||||
- >-
|
||||
PR #31 feat: add app-owned YouTube subtitle flow with absPlayer-style
|
||||
parsing
|
||||
- 'commit cdb12827 fix: address PR #31 latest review follow-ups'
|
||||
priority: medium
|
||||
ordinal: 146500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Inspect the subsequent CodeRabbit review round on PR #31 after commit cdb12827, verify each newly reported issue against the current branch, implement the valid fixes with regression coverage where appropriate, and prepare/update PR thread replies.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 New CodeRabbit comments after cdb12827 are triaged into valid fixes vs declined suggestions with rationale.
|
||||
- [x] #2 Confirmed issues are fixed with regression coverage where appropriate.
|
||||
- [x] #3 Relevant verification passes for the touched areas.
|
||||
- [x] #4 PR threads are updated for the addressed comments.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Verify the new CodeRabbit comments after cdb12827 and separate valid bugs from refactor-only suggestions.
|
||||
2. Add failing regression coverage for the valid runtime issues: `track.selected` fallback in the YouTube primary-subtitle notifier and consistent no-track handling in the picker.
|
||||
3. Inspect existing test seams for the `main.ts` flow-entry guards; if lightweight coverage exists, add it before patching. Otherwise apply the minimal `main.ts` fixes and rely on typecheck plus targeted regression tests around the affected runtime helpers.
|
||||
4. Implement the confirmed fixes: picker re-entry guard, broader `inFlight` cleanup, `track.selected` fallback, and a single canonical `hasTracks` check.
|
||||
5. Run targeted tests/typecheck and update the new PR threads with landed fix refs.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Triaged the post-cdb12827 CodeRabbit round. Implemented the 4 concrete follow-ups: manual picker re-entry guard, broader `setAppOwnedFlowInFlight(...)` cleanup, `track.selected` fallback in the YouTube primary-subtitle notifier, and a single canonical `payloadHasTracks(...)` helper in the picker. Also took the adjacent `replaceChildren()` cleanup while touching the same picker paths.
|
||||
|
||||
Verification passed: `bun test src/main/runtime/youtube-primary-subtitle-notification.test.ts src/renderer/modals/youtube-track-picker.test.ts launcher/commands/playback-command.test.ts src/core/services/cli-command.test.ts` and `bun run typecheck`.
|
||||
|
||||
Updated the new CodeRabbit inline threads with landed fix refs and left a top-level PR comment noting the large refactor suggestions are intentionally out of scope for this bugfix round.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Assessed the subsequent CodeRabbit review round on PR #31 after cdb12827 and applied the valid follow-ups in commit 5f6f93cd. Added a guard in `openYoutubeTrackPickerFromPlayback()` so the manual picker cannot re-enter while another YouTube flow session is active, widened the app-owned in-flight suppression to cover synchronous Windows mpv bootstrap and connect failures, taught the primary-subtitle notifier to honor `track.selected` before `sid` arrives, and unified the picker’s subtitle-availability logic behind `payloadHasTracks(...)` while swapping node clearing to `replaceChildren()`.
|
||||
|
||||
Verification passed with `bun test src/main/runtime/youtube-primary-subtitle-notification.test.ts src/renderer/modals/youtube-track-picker.test.ts launcher/commands/playback-command.test.ts src/core/services/cli-command.test.ts` and `bun run typecheck`.
|
||||
|
||||
Updated the latest inline CodeRabbit threads plus a top-level PR comment summarizing the round and explicitly deferred the large refactor suggestions as non-blocking maintainability nits.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,55 +0,0 @@
|
||||
---
|
||||
id: TASK-229
|
||||
title: 'Address PR #31 final CodeRabbit picker test follow-up'
|
||||
status: Done
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-03-24 04:27'
|
||||
updated_date: '2026-03-24 06:41'
|
||||
labels:
|
||||
- pr-review
|
||||
- coderabbit
|
||||
dependencies: []
|
||||
references:
|
||||
- >-
|
||||
PR #31 feat: add app-owned YouTube subtitle flow with absPlayer-style
|
||||
parsing
|
||||
- >-
|
||||
CodeRabbit comment on src/renderer/modals/youtube-track-picker.test.ts
|
||||
global restoration / harness duplication
|
||||
priority: medium
|
||||
ordinal: 145500
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Fix the remaining CodeRabbit comment on the YouTube picker test file by restoring absent globals correctly and reducing repeated test harness setup so global stubbing is consistent and isolated.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [x] #1 Picker tests restore `window`, `document`, and `CustomEvent` without leaving undefined-valued globals behind.
|
||||
- [x] #2 Repeated picker test setup is consolidated enough to remove the current review complaint.
|
||||
- [x] #3 Relevant picker tests pass and PR thread is updated.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
<!-- SECTION:PLAN:BEGIN -->
|
||||
1. Add a failing regression around global restoration semantics in the YouTube picker test harness.
|
||||
2. Extract shared DOM/environment helpers and restore logic using delete when globals were originally absent.
|
||||
3. Re-run focused tests and typecheck, then commit/push and reply on the PR thread.
|
||||
<!-- SECTION:PLAN:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
Latest CodeRabbit comment targets youtube-track-picker.test.ts harness cleanup and correct restoration of global properties.
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Addressed the last PR #31 CodeRabbit comment by refactoring the YouTube picker test harness to use shared DOM/env helpers, restoring absent globals via delete semantics, adding a regression for cleanup behavior, and pushing commit 039e2f56 with focused picker tests plus typecheck passing.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,57 +0,0 @@
|
||||
---
|
||||
id: TASK-231
|
||||
title: Restore controller input while subtitle sidebar is open
|
||||
status: Done
|
||||
assignee:
|
||||
- '@codex'
|
||||
created_date: '2026-03-24 00:15'
|
||||
updated_date: '2026-03-24 00:15'
|
||||
labels:
|
||||
- bug
|
||||
- controller
|
||||
- subtitle-sidebar
|
||||
- overlay
|
||||
dependencies: []
|
||||
references:
|
||||
- /home/sudacode/projects/japanese/SubMiner/src/renderer/renderer.ts
|
||||
- /home/sudacode/projects/japanese/SubMiner/src/renderer/controller-interaction-blocking.ts
|
||||
- /home/sudacode/projects/japanese/SubMiner/src/renderer/controller-interaction-blocking.test.ts
|
||||
priority: high
|
||||
ordinal: 54900
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
When keyboard-only mode is active, opening the subtitle sidebar should not disable controller navigation and lookup/mining controls. Restore controller input while the sidebar is open, while keeping true modal dialogs blocking controller actions.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 Opening the subtitle sidebar does not block controller input for keyboard-only mode actions.
|
||||
- [x] #2 Controller-select/debug and other true modal dialogs still block controller actions while open.
|
||||
- [x] #3 Focused regression coverage exists for the sidebar-open controller gating rule.
|
||||
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Root cause: renderer gamepad polling used the broad `isAnyModalOpen()` check as its interaction gate, and that list includes `subtitleSidebarModalOpen`. The subtitle sidebar is non-modal for controller usage, so gamepad input was being suppressed whenever the sidebar was visible.
|
||||
|
||||
Fixed by extracting a dedicated controller-interaction blocking helper that excludes the subtitle sidebar but keeps the existing blocking behavior for true modal dialogs.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Restored controller input while the subtitle sidebar is open by switching gamepad polling to a dedicated modal-blocking rule that leaves the sidebar controller-passive. Added a regression test covering the sidebar-open exception and preserving hard blocks for actual modal dialogs.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,66 +0,0 @@
|
||||
---
|
||||
id: TASK-232
|
||||
title: Trim release package size by pruning duplicate and source-only assets
|
||||
status: Done
|
||||
assignee:
|
||||
- '@codex'
|
||||
created_date: '2026-03-24 12:05'
|
||||
updated_date: '2026-03-24 12:30'
|
||||
labels:
|
||||
- release
|
||||
- packaging
|
||||
priority: medium
|
||||
ordinal: 54700
|
||||
dependencies: []
|
||||
references:
|
||||
- /home/sudacode/projects/japanese/SubMiner/package.json
|
||||
- /home/sudacode/projects/japanese/SubMiner/src/release-workflow.test.ts
|
||||
- /home/sudacode/projects/japanese/SubMiner/src/core/services/texthooker.ts
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
|
||||
Reduce packaged release artifact size without changing user-visible functionality by pruning files that are duplicated between `app.asar` and `extraResources`, excluding source/test/doc-only trees from Electron packaging, and trimming obviously non-runtime vendored payload.
|
||||
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- AC:BEGIN -->
|
||||
|
||||
- [x] #1 Electron packaging excludes repo content that is source-only, test-only, docs-only, or duplicated by `extraResources`.
|
||||
- [x] #2 Release packaging tests cover the new exclusion rules.
|
||||
- [x] #3 Verification includes at least targeted release-packaging tests and one packaging-oriented validation step.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
|
||||
Completed scope:
|
||||
- Exclude `assets`, `plugin`, and `vendor/yomitan-jlpt-vocab` from `files` because they are already staged via `extraResources`.
|
||||
- Exclude `dist` sourcemaps/tests, repo docs/tests/packaging metadata, and stats source leftovers from `files`.
|
||||
- Exclude non-runtime `vendor/texthooker-ui` payload such as `public/`, `.vscode/`, and package metadata.
|
||||
- Exclude Linux musl libsql binary from packaged app payload for AppImage-focused savings.
|
||||
|
||||
Verification:
|
||||
- `bun test src/release-workflow.test.ts`
|
||||
- `bun run build`
|
||||
- `node_modules/.bin/electron-builder --linux dir --publish never`
|
||||
- `node_modules/.bin/electron-builder --linux AppImage --publish never`
|
||||
|
||||
Observed result:
|
||||
- `release/linux-unpacked/resources/app.asar` dropped from about `100 MB` to `29 MB`.
|
||||
- `release/SubMiner-0.9.0.AppImage` dropped from about `256 MB` to `194 MB`.
|
||||
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
|
||||
Trimmed Electron packaging so release artifacts no longer bundle duplicated `extraResources`, source/test/doc-only repo content, non-runtime `texthooker-ui` files, or the Linux musl libsql binary. Added release-packaging regression coverage and verified the Linux package shrink with fresh local builds.
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,35 +0,0 @@
|
||||
---
|
||||
id: TASK-243
|
||||
title: 'Assess and address PR #36 latest CodeRabbit review round'
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-03-29 07:39'
|
||||
updated_date: '2026-03-29 07:41'
|
||||
labels:
|
||||
- code-review
|
||||
- pr-36
|
||||
dependencies: []
|
||||
references:
|
||||
- 'https://github.com/ksyasuda/SubMiner/pull/36'
|
||||
priority: high
|
||||
ordinal: 3600
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Inspect the latest CodeRabbit review round on PR #36, verify each actionable comment against the current branch, implement the confirmed fixes, and verify the touched paths.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [ ] #1 Confirmed review comments are implemented or explicitly deferred with rationale.
|
||||
- [ ] #2 Touched paths are verified with the smallest sufficient test/build lane.
|
||||
- [ ] #3 Current PR feedback is reduced to resolved or intentionally deferred suggestions.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Addressed the confirmed latest CodeRabbit review items on PR #36. `scripts/run-coverage-lane.ts` now uses the Bun-style `import.meta.main` entrypoint check with a local ts-ignore to preserve the repo's CommonJS typecheck settings. `src/core/services/immersion-tracker/maintenance.ts` no longer shadows the imported `nowMs` helper in retention functions. `src/main.ts` now centralizes the startup-mode predicates behind a shared helper and releases `resolvedSource.cleanup` on the cached-subtitle fast path so materialized sources do not leak.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,35 +0,0 @@
|
||||
---
|
||||
id: TASK-244
|
||||
title: 'Assess and address PR #36 latest CodeRabbit review round 2'
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-03-29 08:09'
|
||||
updated_date: '2026-03-29 08:10'
|
||||
labels:
|
||||
- code-review
|
||||
- pr-36
|
||||
dependencies: []
|
||||
references:
|
||||
- 'https://github.com/ksyasuda/SubMiner/pull/36'
|
||||
priority: high
|
||||
ordinal: 3610
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Inspect the newest CodeRabbit review round on PR #36, verify the actionable comment against the current branch, implement the confirmed fix, and verify the touched path.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [ ] #1 The actionable review comment is implemented or explicitly deferred with rationale.
|
||||
- [ ] #2 Touched path is verified with the smallest sufficient test lane.
|
||||
- [ ] #3 Current PR feedback is reduced to resolved or intentionally deferred suggestions.
|
||||
<!-- AC:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Addressed the actionable latest CodeRabbit comment on PR #36. `src/core/services/immersion-tracker/maintenance.ts` now skips retention deletions when a window is disabled with `Infinity`, so `toDbMs(...)` is only called for finite retention values. Added a regression test in `maintenance.test.ts` that verifies disabled retention windows preserve session events, telemetry, and sessions while returning zero deletions.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -1,39 +0,0 @@
|
||||
---
|
||||
id: TASK-272
|
||||
title: 'Assess and address PR #40 CodeRabbit review follow-ups'
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-04-03 07:52'
|
||||
updated_date: '2026-04-03 08:04'
|
||||
labels:
|
||||
- coderabbit
|
||||
- review
|
||||
- launcher
|
||||
milestone: 'PR #40'
|
||||
dependencies: []
|
||||
references:
|
||||
- 'https://github.com/ksyasuda/SubMiner/pull/40'
|
||||
priority: medium
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Implement the valid CodeRabbit findings on PR #40 and keep the Windows mpv shortcut / first-run setup flow consistent end to end.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [ ] #1 Windows binary resolution does not return install directories as executable candidates
|
||||
- [ ] #2 Launch-mpv arg parsing preserves space-separated mpv option values and target separation
|
||||
- [ ] #3 Windows mpv launch args keep the final input-ipc-server and script-opts socket path in sync when custom values are supplied
|
||||
- [ ] #4 First-run setup navigation swallows stale or invalid custom-scheme actions without navigating away
|
||||
- [ ] #5 Setup messaging and footer copy reflect configReady, plugin, and dictionary gates consistently
|
||||
- [ ] #6 Regression tests cover the fixed behaviors
|
||||
<!-- AC:END -->
|
||||
|
||||
## Final Summary
|
||||
|
||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||
Addressed CodeRabbit follow-ups for PR #40. Hardened launcher binary discovery on Windows and PATH resolution, fixed launch-mpv argument parsing for value-bearing flags, synced custom Windows mpv IPC socket values into script opts, and tightened first-run setup messaging/navigation to handle stale actions and blocker copy. Verified with `bun test src/main-entry-runtime.test.ts src/main/runtime/windows-mpv-launch.test.ts src/main/runtime/first-run-setup-window.test.ts launcher/mpv.test.ts`.
|
||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||
@@ -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'
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
---
|
||||
id: m-2
|
||||
title: 'Mining Workflow Upgrades'
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Future user-facing workflow improvements that directly improve discoverability, previewability, and mining control without depending on speculative platform integrations like OCR, marketplace infrastructure, or cloud sync.
|
||||
@@ -1,11 +1,11 @@
|
||||
---
|
||||
id: TASK-143
|
||||
title: Keep character dictionary auto-sync non-blocking during startup
|
||||
status: Done
|
||||
status: In Progress
|
||||
assignee:
|
||||
- codex
|
||||
created_date: '2026-03-09 01:45'
|
||||
updated_date: '2026-03-23 03:22'
|
||||
updated_date: '2026-03-20 09:22'
|
||||
labels:
|
||||
- dictionary
|
||||
- startup
|
||||
@@ -18,7 +18,7 @@ references:
|
||||
- >-
|
||||
/home/sudacode/projects/japanese/SubMiner/src/main/runtime/current-media-tokenization-gate.ts
|
||||
priority: high
|
||||
ordinal: 144500
|
||||
ordinal: 38500
|
||||
---
|
||||
|
||||
## Description
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user