mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-20 12:11:28 -07:00
Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
f6c024d61e
|
|||
| 6749ff843c | |||
|
42abdd1268
|
|||
|
5d914b1547
|
|||
| 50b45cac0b | |||
|
e35aac6ee0
|
|||
|
fe2da22d29
|
|||
|
2045ffbff8
|
|||
| 478869ff28 | |||
| 9eed37420e | |||
| 99f4d2baaf | |||
|
f4e8c3feec
|
|||
|
d0b308f340
|
|||
| 1b56360a24 | |||
|
68833c76c4
|
|||
| 4d7c80f2e4 | |||
|
2f17859b7b
|
|||
|
9c7e02cbf0
|
|||
|
5f320edab5
|
|||
|
f7ce3371a1
|
|||
|
71805dccd7
|
|||
|
a13b7352d7
|
|||
|
ff5ecdaded
|
|||
|
b32c3cf58c
|
|||
|
b791262860
|
|||
|
29b8c6e02a
|
|||
|
618727d8e8
|
|||
|
fed60c265d
|
|||
|
a34a7489db
|
|||
|
e59192bbe1
|
|||
|
e0f82d28f0
|
|||
|
a0521aeeaf
|
|||
|
2127f759ca
|
|||
|
5e787183d0
|
|||
|
81ca31b899
|
127
.agents/skills/subminer-change-verification/SKILL.md
Normal file
127
.agents/skills/subminer-change-verification/SKILL.md
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
---
|
||||||
|
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."
|
||||||
|
---
|
||||||
|
|
||||||
|
# SubMiner Change Verification
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
If you need an explicit installed path, use the directory that contains this `SKILL.md`. The helper scripts live under:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export SUBMINER_VERIFY_SKILL="<path-to-skill>"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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.
|
||||||
163
.agents/skills/subminer-change-verification/scripts/classify_subminer_diff.sh
Executable file
163
.agents/skills/subminer-change-verification/scripts/classify_subminer_diff.sh
Executable file
@@ -0,0 +1,163 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'EOF'
|
||||||
|
Usage: classify_subminer_diff.sh [path ...]
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
566
.agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh
Executable file
566
.agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh
Executable file
@@ -0,0 +1,566 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'EOF'
|
||||||
|
Usage: verify_subminer_change.sh [options] [path ...]
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
146
.agents/skills/subminer-scrum-master/SKILL.md
Normal file
146
.agents/skills/subminer-scrum-master/SKILL.md
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
---
|
||||||
|
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."
|
||||||
|
---
|
||||||
|
|
||||||
|
# SubMiner Scrum Master
|
||||||
|
|
||||||
|
Own workflow, not code by default.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
## Core Rules
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
## 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
|
||||||
16
.github/workflows/ci.yml
vendored
16
.github/workflows/ci.yml
vendored
@@ -27,17 +27,23 @@ jobs:
|
|||||||
path: |
|
path: |
|
||||||
~/.bun/install/cache
|
~/.bun/install/cache
|
||||||
node_modules
|
node_modules
|
||||||
|
stats/node_modules
|
||||||
vendor/subminer-yomitan/node_modules
|
vendor/subminer-yomitan/node_modules
|
||||||
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'vendor/subminer-yomitan/package-lock.json') }}
|
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'stats/bun.lock', 'vendor/subminer-yomitan/package-lock.json') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-bun-
|
${{ runner.os }}-bun-
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: bun install --frozen-lockfile
|
run: |
|
||||||
|
bun install --frozen-lockfile
|
||||||
|
cd stats && bun install --frozen-lockfile
|
||||||
|
|
||||||
- name: Lint changelog fragments
|
- name: Lint changelog fragments
|
||||||
run: bun run changelog:lint
|
run: bun run changelog:lint
|
||||||
|
|
||||||
|
- name: Lint stats (formatting)
|
||||||
|
run: bun run lint:stats
|
||||||
|
|
||||||
- name: Enforce pull request changelog fragments (`skip-changelog` label bypass)
|
- name: Enforce pull request changelog fragments (`skip-changelog` label bypass)
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request'
|
||||||
run: bun run changelog:pr-check --base-ref "origin/${{ github.base_ref }}" --head-ref "HEAD" --labels "${{ join(github.event.pull_request.labels.*.name, ',') }}"
|
run: bun run changelog:pr-check --base-ref "origin/${{ github.base_ref }}" --head-ref "HEAD" --labels "${{ join(github.event.pull_request.labels.*.name, ',') }}"
|
||||||
@@ -46,6 +52,12 @@ jobs:
|
|||||||
# Keep explicit typecheck for fast fail before full build/bundle.
|
# Keep explicit typecheck for fast fail before full build/bundle.
|
||||||
run: bun run typecheck
|
run: bun run typecheck
|
||||||
|
|
||||||
|
- name: Verify generated config examples
|
||||||
|
run: bun run verify:config-example
|
||||||
|
|
||||||
|
- name: Internal docs knowledge-base checks
|
||||||
|
run: bun run test:docs:kb
|
||||||
|
|
||||||
- name: Test suite (source)
|
- name: Test suite (source)
|
||||||
run: bun run test:fast
|
run: bun run test:fast
|
||||||
|
|
||||||
|
|||||||
131
.github/workflows/release.yml
vendored
131
.github/workflows/release.yml
vendored
@@ -9,9 +9,6 @@ concurrency:
|
|||||||
group: release-${{ github.ref }}
|
group: release-${{ github.ref }}
|
||||||
cancel-in-progress: false
|
cancel-in-progress: false
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
quality-gate:
|
quality-gate:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -32,13 +29,19 @@ jobs:
|
|||||||
path: |
|
path: |
|
||||||
~/.bun/install/cache
|
~/.bun/install/cache
|
||||||
node_modules
|
node_modules
|
||||||
|
stats/node_modules
|
||||||
vendor/subminer-yomitan/node_modules
|
vendor/subminer-yomitan/node_modules
|
||||||
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'vendor/subminer-yomitan/package-lock.json') }}
|
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'stats/bun.lock', 'vendor/subminer-yomitan/package-lock.json') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-bun-
|
${{ runner.os }}-bun-
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: bun install --frozen-lockfile
|
run: |
|
||||||
|
bun install --frozen-lockfile
|
||||||
|
cd stats && bun install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Lint stats (formatting)
|
||||||
|
run: bun run lint:stats
|
||||||
|
|
||||||
- name: Build (TypeScript check)
|
- name: Build (TypeScript check)
|
||||||
run: bun run typecheck
|
run: bun run typecheck
|
||||||
@@ -86,14 +89,17 @@ jobs:
|
|||||||
path: |
|
path: |
|
||||||
~/.bun/install/cache
|
~/.bun/install/cache
|
||||||
node_modules
|
node_modules
|
||||||
|
stats/node_modules
|
||||||
vendor/texthooker-ui/node_modules
|
vendor/texthooker-ui/node_modules
|
||||||
vendor/subminer-yomitan/node_modules
|
vendor/subminer-yomitan/node_modules
|
||||||
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'vendor/texthooker-ui/package.json', 'vendor/subminer-yomitan/package-lock.json') }}
|
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'stats/bun.lock', 'vendor/texthooker-ui/package.json', 'vendor/subminer-yomitan/package-lock.json') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-bun-
|
${{ runner.os }}-bun-
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: bun install --frozen-lockfile
|
run: |
|
||||||
|
bun install --frozen-lockfile
|
||||||
|
cd stats && bun install --frozen-lockfile
|
||||||
|
|
||||||
- name: Build texthooker-ui
|
- name: Build texthooker-ui
|
||||||
run: |
|
run: |
|
||||||
@@ -141,9 +147,10 @@ jobs:
|
|||||||
path: |
|
path: |
|
||||||
~/.bun/install/cache
|
~/.bun/install/cache
|
||||||
node_modules
|
node_modules
|
||||||
|
stats/node_modules
|
||||||
vendor/texthooker-ui/node_modules
|
vendor/texthooker-ui/node_modules
|
||||||
vendor/subminer-yomitan/node_modules
|
vendor/subminer-yomitan/node_modules
|
||||||
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'vendor/texthooker-ui/package.json', 'vendor/subminer-yomitan/package-lock.json') }}
|
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'stats/bun.lock', 'vendor/texthooker-ui/package.json', 'vendor/subminer-yomitan/package-lock.json') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-bun-
|
${{ runner.os }}-bun-
|
||||||
|
|
||||||
@@ -168,7 +175,9 @@ jobs:
|
|||||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: bun install --frozen-lockfile
|
run: |
|
||||||
|
bun install --frozen-lockfile
|
||||||
|
cd stats && bun install --frozen-lockfile
|
||||||
|
|
||||||
- name: Build texthooker-ui
|
- name: Build texthooker-ui
|
||||||
run: |
|
run: |
|
||||||
@@ -213,14 +222,17 @@ jobs:
|
|||||||
path: |
|
path: |
|
||||||
~/.bun/install/cache
|
~/.bun/install/cache
|
||||||
node_modules
|
node_modules
|
||||||
|
stats/node_modules
|
||||||
vendor/texthooker-ui/node_modules
|
vendor/texthooker-ui/node_modules
|
||||||
vendor/subminer-yomitan/node_modules
|
vendor/subminer-yomitan/node_modules
|
||||||
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'vendor/texthooker-ui/package.json', 'vendor/subminer-yomitan/package-lock.json') }}
|
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock', 'stats/bun.lock', 'vendor/texthooker-ui/package.json', 'vendor/subminer-yomitan/package-lock.json') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-bun-
|
${{ runner.os }}-bun-
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: bun install --frozen-lockfile
|
run: |
|
||||||
|
bun install --frozen-lockfile
|
||||||
|
cd stats && bun install --frozen-lockfile
|
||||||
|
|
||||||
- name: Build texthooker-ui
|
- name: Build texthooker-ui
|
||||||
shell: powershell
|
shell: powershell
|
||||||
@@ -244,6 +256,8 @@ jobs:
|
|||||||
release:
|
release:
|
||||||
needs: [build-linux, build-macos, build-windows]
|
needs: [build-linux, build-macos, build-windows]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -295,6 +309,9 @@ jobs:
|
|||||||
- name: Enforce generated launcher workflow
|
- name: Enforce generated launcher workflow
|
||||||
run: bash scripts/verify-generated-launcher.sh
|
run: bash scripts/verify-generated-launcher.sh
|
||||||
|
|
||||||
|
- name: Verify generated config examples
|
||||||
|
run: bun run verify:config-example
|
||||||
|
|
||||||
- name: Package optional assets bundle
|
- name: Package optional assets bundle
|
||||||
run: |
|
run: |
|
||||||
tar -czf "release/subminer-assets.tar.gz" \
|
tar -czf "release/subminer-assets.tar.gz" \
|
||||||
@@ -315,7 +332,15 @@ jobs:
|
|||||||
|
|
||||||
- name: Get version from tag
|
- name: Get version from tag
|
||||||
id: version
|
id: version
|
||||||
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Build changelog artifacts for release
|
||||||
|
run: |
|
||||||
|
if find changes -maxdepth 1 -name '*.md' -not -name README.md -print -quit | grep -q .; then
|
||||||
|
bun run changelog:build --version "${{ steps.version.outputs.VERSION }}"
|
||||||
|
else
|
||||||
|
echo "No pending changelog fragments found."
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Verify changelog is ready for tagged release
|
- name: Verify changelog is ready for tagged release
|
||||||
run: bun run changelog:check --version "${{ steps.version.outputs.VERSION }}"
|
run: bun run changelog:check --version "${{ steps.version.outputs.VERSION }}"
|
||||||
@@ -360,3 +385,85 @@ jobs:
|
|||||||
for asset in "${artifacts[@]}"; do
|
for asset in "${artifacts[@]}"; do
|
||||||
gh release upload "${{ steps.version.outputs.VERSION }}" "$asset" --clobber
|
gh release upload "${{ steps.version.outputs.VERSION }}" "$asset" --clobber
|
||||||
done
|
done
|
||||||
|
|
||||||
|
aur-publish:
|
||||||
|
needs: [release]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Get version from tag
|
||||||
|
id: version
|
||||||
|
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- 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 "Missing required secret: AUR_SSH_PRIVATE_KEY"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Configure SSH for AUR
|
||||||
|
env:
|
||||||
|
AUR_SSH_PRIVATE_KEY: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
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
|
||||||
|
env:
|
||||||
|
GIT_SSH_COMMAND: ssh -i ~/.ssh/aur -o IdentitiesOnly=yes
|
||||||
|
run: git clone ssh://aur@aur.archlinux.org/subminer-bin.git aur-subminer-bin
|
||||||
|
|
||||||
|
- name: Download release assets for AUR
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
version="${{ steps.version.outputs.VERSION }}"
|
||||||
|
install -dm755 .tmp/aur-release-assets
|
||||||
|
gh release download "$version" \
|
||||||
|
--dir .tmp/aur-release-assets \
|
||||||
|
--pattern "SubMiner-${version#v}.AppImage" \
|
||||||
|
--pattern "subminer" \
|
||||||
|
--pattern "subminer-assets.tar.gz"
|
||||||
|
|
||||||
|
- name: Update AUR packaging metadata
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
version_no_v="${{ steps.version.outputs.VERSION }}"
|
||||||
|
version_no_v="${version_no_v#v}"
|
||||||
|
cp packaging/aur/subminer-bin/PKGBUILD aur-subminer-bin/PKGBUILD
|
||||||
|
cp packaging/aur/subminer-bin/.SRCINFO aur-subminer-bin/.SRCINFO
|
||||||
|
bash scripts/update-aur-package.sh \
|
||||||
|
--pkg-dir aur-subminer-bin \
|
||||||
|
--version "${{ steps.version.outputs.VERSION }}" \
|
||||||
|
--appimage ".tmp/aur-release-assets/SubMiner-${version_no_v}.AppImage" \
|
||||||
|
--wrapper ".tmp/aur-release-assets/subminer" \
|
||||||
|
--assets ".tmp/aur-release-assets/subminer-assets.tar.gz"
|
||||||
|
|
||||||
|
- name: Commit and push AUR update
|
||||||
|
working-directory: aur-subminer-bin
|
||||||
|
env:
|
||||||
|
GIT_SSH_COMMAND: ssh -i ~/.ssh/aur -o IdentitiesOnly=yes
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
if git diff --quiet -- PKGBUILD .SRCINFO; then
|
||||||
|
echo "AUR packaging already up to date."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
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 }}"
|
||||||
|
git push origin HEAD:master
|
||||||
|
|||||||
20
.gitignore
vendored
20
.gitignore
vendored
@@ -1,6 +1,9 @@
|
|||||||
# Dependencies
|
# Dependencies
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|
||||||
|
# Superpowers brainstorming
|
||||||
|
.superpowers/
|
||||||
|
|
||||||
# Electron build output
|
# Electron build output
|
||||||
out/
|
out/
|
||||||
dist/
|
dist/
|
||||||
@@ -22,9 +25,7 @@ Thumbs.db
|
|||||||
.idea/
|
.idea/
|
||||||
*.swp
|
*.swp
|
||||||
*.swo
|
*.swo
|
||||||
**/CLAUDE.md
|
|
||||||
environment.toml
|
environment.toml
|
||||||
**/CLAUDE.md
|
|
||||||
.env
|
.env
|
||||||
.vscode/*
|
.vscode/*
|
||||||
|
|
||||||
@@ -35,6 +36,21 @@ docs/.vitepress/cache/
|
|||||||
docs/.vitepress/dist/
|
docs/.vitepress/dist/
|
||||||
tests/*
|
tests/*
|
||||||
.worktrees/
|
.worktrees/
|
||||||
|
.tmp/
|
||||||
.codex/*
|
.codex/*
|
||||||
.agents/*
|
.agents/*
|
||||||
|
!.agents/skills/
|
||||||
|
.agents/skills/*
|
||||||
|
!.agents/skills/subminer-change-verification/
|
||||||
|
!.agents/skills/subminer-scrum-master/
|
||||||
|
.agents/skills/subminer-change-verification/*
|
||||||
|
!.agents/skills/subminer-change-verification/SKILL.md
|
||||||
|
!.agents/skills/subminer-change-verification/scripts/
|
||||||
|
.agents/skills/subminer-change-verification/scripts/*
|
||||||
|
!.agents/skills/subminer-change-verification/scripts/classify_subminer_diff.sh
|
||||||
|
!.agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh
|
||||||
|
.agents/skills/subminer-scrum-master/*
|
||||||
|
!.agents/skills/subminer-scrum-master/SKILL.md
|
||||||
favicon.png
|
favicon.png
|
||||||
|
.claude/*
|
||||||
|
!stats/public/favicon.png
|
||||||
|
|||||||
91
AGENTS.md
91
AGENTS.md
@@ -1,60 +1,69 @@
|
|||||||
# AGENTS.MD
|
# AGENTS.MD
|
||||||
|
|
||||||
## PR Feedback
|
## Internal Docs
|
||||||
|
|
||||||
- Active PR: `gh pr view --json number,title,url --jq '"PR #\\(.number): \\(.title)\\n\\(.url)"'`.
|
Start here, then leave this file.
|
||||||
- PR comments: `gh pr view …` + `gh api …/comments --paginate`.
|
|
||||||
- Replies: cite fix + file/line; resolve threads only after fix lands.
|
|
||||||
- When merging a PR: thank the contributor in `CHANGELOG.md`.
|
|
||||||
|
|
||||||
## Changelog
|
- Internal system of record: [`docs/README.md`](./docs/README.md)
|
||||||
|
- Architecture map: [`docs/architecture/README.md`](./docs/architecture/README.md)
|
||||||
|
- Workflow map: [`docs/workflow/README.md`](./docs/workflow/README.md)
|
||||||
|
- Verification lanes: [`docs/workflow/verification.md`](./docs/workflow/verification.md)
|
||||||
|
- Knowledge-base rules: [`docs/knowledge-base/README.md`](./docs/knowledge-base/README.md)
|
||||||
|
- Release guide: [`docs/RELEASING.md`](./docs/RELEASING.md)
|
||||||
|
|
||||||
- User-visible PRs: add one fragment in `changes/*.md`.
|
`docs-site/` is user-facing. Do not treat it as the canonical internal source of truth.
|
||||||
- Fragment format:
|
|
||||||
`type: added|changed|fixed|docs|internal`
|
|
||||||
`area: <short-area>`
|
|
||||||
blank line
|
|
||||||
`- bullet`
|
|
||||||
- `changes/README.md`: instructions only; generator ignores it.
|
|
||||||
- No release-note entry wanted: use PR label `skip-changelog`.
|
|
||||||
- CI runs `bun run changelog:lint` + `bun run changelog:pr-check` on PRs.
|
|
||||||
- Release prep: `bun run changelog:build`, review `CHANGELOG.md` + `release/release-notes.md`, commit generated changelog + fragment deletions, then tag.
|
|
||||||
- Release CI expects committed changelog entry already present; do not rely on tag job to invent notes.
|
|
||||||
|
|
||||||
## Flow & Runtime
|
## Quick Start
|
||||||
|
|
||||||
- Use repo’s package manager/runtime; no swaps w/o approval.
|
- Init workspace: `git submodule update --init --recursive`
|
||||||
- Use Codex background for long jobs; tmux only for interactive/persistent (debugger/server).
|
- Install deps: `make deps` or `bun install` plus `(cd vendor/texthooker-ui && bun install --frozen-lockfile)`
|
||||||
|
- Fast dev loop: `make dev-watch`
|
||||||
|
- Full local run: `bun run dev`
|
||||||
|
- Verbose Electron debug: `electron . --start --dev --log-level debug`
|
||||||
|
|
||||||
## Build / Test
|
## Build / Test
|
||||||
|
|
||||||
- Before handoff: run full gate (lint/typecheck/tests/docs).
|
- Runtime/package manager: Bun (`packageManager: bun@1.3.5`)
|
||||||
- CI red: `gh run list/view`, rerun, fix, push, repeat til green.
|
- Default handoff gate:
|
||||||
- Keep it observable (logs, panes, tails, MCP/browser tools).
|
`bun run typecheck`
|
||||||
- Release: read `docs/RELEASING.md`
|
`bun run test:fast`
|
||||||
|
`bun run test:env`
|
||||||
|
`bun run build`
|
||||||
|
`bun run test:smoke:dist`
|
||||||
|
- If `docs-site/` changed, also run:
|
||||||
|
`bun run docs:test`
|
||||||
|
`bun run docs:build`
|
||||||
|
- Prefer `make pretty` and `bun run format:check:src`
|
||||||
|
|
||||||
## Git
|
## Change-Specific Checks
|
||||||
|
|
||||||
- Safe by default: `git status/diff/log`. Push only when user asks.
|
- Config/schema/defaults: `bun run test:config`; if template/defaults changed, `bun run generate:config-example`
|
||||||
- `git checkout` ok for PR review / explicit request.
|
- Launcher/plugin: `bun run test:launcher` or `bun run test:env`
|
||||||
- Branch changes require user consent.
|
- Runtime-compat / dist-sensitive: `bun run test:runtime:compat`
|
||||||
- Destructive ops forbidden unless explicit (`reset --hard`, `clean`, `restore`, `rm`, …).
|
- Docs-only: `bun run docs:test`, then `bun run docs:build`
|
||||||
- Don’t delete/rename unexpected stuff; stop + ask.
|
|
||||||
- No repo-wide S/R scripts; keep edits small/reviewable.
|
|
||||||
- Avoid manual `git stash`; if Git auto-stashes during pull/rebase, that’s fine (hint, not hard guardrail).
|
|
||||||
- If user types a command (“pull and push”), that’s consent for that command.
|
|
||||||
- No amend unless asked.
|
|
||||||
- Big review: `git --no-pager diff --color=never`.
|
|
||||||
- Multi-agent: check `git status/diff` before edits; ship small commits.
|
|
||||||
|
|
||||||
## Language/Stack Notes
|
## Sensitive Files
|
||||||
|
|
||||||
- Swift: use workspace helper/daemon; validate `swift build` + tests; keep concurrency attrs right.
|
- Launcher source of truth: `launcher/*.ts`
|
||||||
- TypeScript: use repo PM; keep files small; follow existing patterns.
|
- Generated launcher artifact: `dist/launcher/subminer`; never hand-edit it
|
||||||
|
- Repo-root `./subminer` is stale; do not revive it
|
||||||
|
- `bun run build` rebuilds bundled Yomitan from `vendor/subminer-yomitan`
|
||||||
|
- Do not change signing/packaging identifiers unless the task explicitly requires it
|
||||||
|
|
||||||
## macOS Permissions / Signing (TCC)
|
## Release / PR Notes
|
||||||
|
|
||||||
- Never re-sign / ad-hoc sign / change bundle ID as “debug” without explicit ok (can mess TCC).
|
- User-visible PRs need one fragment in `changes/*.md`
|
||||||
|
- CI enforces `bun run changelog:lint` and `bun run changelog:pr-check`
|
||||||
|
- PR review helpers:
|
||||||
|
- `gh pr view --json number,title,url --jq '"PR #\\(.number): \\(.title)\\n\\(.url)"'`
|
||||||
|
- `gh api repos/:owner/:repo/pulls/<num>/comments --paginate`
|
||||||
|
|
||||||
|
## Runtime Notes
|
||||||
|
|
||||||
|
- Use Codex background for long jobs; tmux only when persistence/interaction is required
|
||||||
|
- CI red: `gh run list/view`, rerun, fix, repeat until green
|
||||||
|
- TypeScript: keep files small; follow existing patterns
|
||||||
|
- Swift: use workspace helper/daemon; validate `swift build` + tests
|
||||||
|
|
||||||
<!-- BACKLOG.MD MCP GUIDELINES START -->
|
<!-- BACKLOG.MD MCP GUIDELINES START -->
|
||||||
|
|
||||||
|
|||||||
148
CHANGELOG.md
148
CHANGELOG.md
@@ -1,22 +1,162 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v0.7.0 (2026-03-19)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Immersion: Added Mine Word, Mine Sentence, and Mine Audio buttons to word detail example lines in the stats dashboard.
|
||||||
|
- Immersion: Mine Word creates a full Yomitan card (definition, reading, pitch accent) via the hidden search page bridge, then enriches with sentence audio, screenshot, and metadata extracted from the source video.
|
||||||
|
- Immersion: Mine Sentence and Mine Audio create cards directly with appropriate Lapis/Kiku flags, sentence highlighting, and media from the source file.
|
||||||
|
- Immersion: Media generation (audio + image/AVIF) runs in parallel and respects all AnkiConnect config options.
|
||||||
|
- Immersion: Added word exclusion list to the Vocabulary tab with localStorage persistence and a management modal.
|
||||||
|
- Immersion: Fixed truncated readings in the frequency rank table (e.g. お前 now shows おまえ instead of まえ).
|
||||||
|
- Immersion: Clicking a bar in the Top Repeated Words chart now opens the word detail panel.
|
||||||
|
- Immersion: Secondary subtitle text is now stored alongside primary subtitle lines for use as translation when mining cards from the stats page.
|
||||||
|
- Stats: Added `subminer stats -b` to start or reuse a dedicated background stats server without blocking normal SubMiner instances.
|
||||||
|
- Stats: Added `subminer stats -s` to stop the dedicated background stats server without closing browser tabs.
|
||||||
|
- Stats: Stats server startup now reuses a running background stats daemon instead of trying to bind a second local server in another SubMiner instance.
|
||||||
|
- Launcher: Added launcher passthrough for `-a/--args` so mpv receives raw extra launch flags (`--fs`, `--ytdl-format`, custom audio/video settings, etc.) from the `subminer` command.
|
||||||
|
- Launcher: Added `subminer stats` to launch the local stats dashboard, force-start the stats server on demand, and open the dashboard in your browser.
|
||||||
|
- Launcher: Added `subminer stats cleanup` to backfill vocabulary metadata and prune stale or excluded immersion rows on demand.
|
||||||
|
- Launcher: Added `stats.autoOpenBrowser` so browser launch after `subminer stats` can be enabled or disabled explicitly.
|
||||||
|
- Immersion: Added a local stats dashboard for immersion tracking with Overview, Anime, Trends, Vocabulary, and Sessions views.
|
||||||
|
- Immersion: Added anime progress, episode completion, Anki card links, and occurrence drill-down across the stats dashboard.
|
||||||
|
- Immersion: Added richer session timelines with new-word activity, cumulative totals, and pause/seek/card event markers.
|
||||||
|
- Immersion: Added completed-episodes and completed-anime totals to the Overview tracking snapshot.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Anki: Changed known-word cache settings to live under `ankiConnect.knownWords` instead of mixing them into `ankiConnect.nPlusOne`.
|
||||||
|
- Anki: Kept legacy `ankiConnect.nPlusOne` known-word keys and older `ankiConnect.behavior.nPlusOne*` keys as deprecated compatibility fallbacks.
|
||||||
|
- Stats: Added session deletion to the Sessions tab with the same confirmation prompt used by anime episode/session deletes, and removed all associated session rows from the stats database.
|
||||||
|
- Immersion: Kept immersion tracking history by default while preserving daily/monthly rollup maintenance.
|
||||||
|
- Immersion: Added exact lifetime summary reads for overview/anime/media stats so dashboard totals no longer depend on rescanning raw telemetry.
|
||||||
|
- Immersion: Reduced tracker storage overhead by removing duplicated subtitle text from subtitle-line event payloads.
|
||||||
|
- Immersion: Deduplicated episode cover-art blobs through a shared blob store and updated cover-art reads/writes to resolve shared images correctly.
|
||||||
|
- Immersion: Added indexes for large-history session, telemetry, vocabulary, kanji, and cover-art queries to keep dashboard reads fast as the SQLite database grows.
|
||||||
|
- Immersion: Renamed the stats dashboard's Anime tab to Library so the media browser label matches non-anime sources like YouTube and other yt-dlp-backed content.
|
||||||
|
- Anilist: Standardized episode completion threshold by introducing `DEFAULT_MIN_WATCH_RATIO` and using it for both local watched state transitions and AniList post-watch progress updates.
|
||||||
|
- Anilist: Episode auto-marking now uses the same threshold as AniList (`85%`), removing divergent completion behavior.
|
||||||
|
- Overlay: Excluded interjections and sound-effect tokens from subtitle annotation styling so they no longer inherit misleading lexical highlight treatment while still remaining visible and hoverable as plain subtitle tokens.
|
||||||
|
- Overlay: Expanded subtitle annotation noise filtering to also strip annotation metadata from standalone grammar-only helper tokens such as particles, auxiliaries, adnominals, common explanatory endings like `んです` / `のだ`, and merged trailing quote-particle forms like `...って` while keeping them tokenized for hover lookup.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Launcher: Fixed mpv Lua plugin binary auto-detection on Linux to also search `/usr/bin/subminer` and `/usr/local/bin/subminer` (lowercase), matching the conventional Unix wrapper name used by packaged installs such as the AUR package.
|
||||||
|
- Stats: Fixed the in-app stats overlay so it connects to the configured `stats.serverPort` instead of falling back to the default port.
|
||||||
|
- Overlay: Fixed subtitle frequency tagging for merged lookup-backed tokens like `陰に` by falling back to exact surface-form Yomitan frequencies when the normalized headword lookup misses.
|
||||||
|
- Overlay: Fixed MeCab merged-token position mapping across line breaks so merged content-plus-particle tokens like `陰に` keep their matched Yomitan frequency instead of inheriting shifted POS tags.
|
||||||
|
- Overlay: Fixed grouped frequency parsing in both Yomitan and fallback frequency-dictionary lookups so display values like `118,121` use the leading rank instead of collapsing the rank and occurrence count into `118121`.
|
||||||
|
- Overlay: Fixed frequency-rank ingestion to ignore Yomitan dictionaries explicitly marked `occurrence-based`, so raw occurrence counts are no longer treated as subtitle rank values.
|
||||||
|
- Overlay: Fixed inflected headword frequency tagging to prefer ranks from the selected Yomitan `termsFind` popup entry itself, ordered by configured dictionary priority, so forms like `潜み` use primary-dictionary ranks like `4073` before falling back to lower-priority raw lemma metadata such as `CC100`.
|
||||||
|
- Overlay: Fixed annotation-stage frequency filtering so exact kanji noun tokens like `者` keep their matched rank even when MeCab labels them `名詞/非自立`, instead of dropping the highlight after scan-time frequency lookup succeeds.
|
||||||
|
- Anki: Fixed repeated character-dictionary startup work by scheduling auto-sync only from mpv media-path changes instead of also re-triggering it from connection and media-title events for the same title.
|
||||||
|
- Overlay: Fixed macOS fullscreen overlay stability by keeping the passive visible overlay from stealing focus, re-raising the overlay window when reasserting its macOS topmost level, and tolerating one transient macOS tracker/helper miss before hiding the overlay.
|
||||||
|
- Overlay: Kept subtitle tokenization warmup one-shot for the lifetime of the app so later fullscreen/media churn on macOS does not replay the startup warmup gate after the first file is ready.
|
||||||
|
- Overlay: Added a bounded macOS tracker loss-grace window so fullscreen enter/leave transitions do not immediately hide and reload the overlay when the helper briefly loses the mpv window.
|
||||||
|
- Overlay: Skipped subtitle/tokenization refresh invalidation on character-dictionary auto-sync completion when the dictionary was already current, preventing startup flash/reload loops on unchanged media.
|
||||||
|
- Stats: Fixed session stats so known-word counts track real known-word occurrences without collapsing subtitle-line gaps.
|
||||||
|
- Stats: Fixed session word totals in session-facing stats views to prefer token counts when available, preventing known words from exceeding total words in the session chart.
|
||||||
|
- Stats: Fixed the stats Vocabulary tab blank-screen regression caused by a hook-order crash after vocabulary data finished loading.
|
||||||
|
- Anki: Fixed card-mine OSD feedback so the final mine result stops the Anki spinner first, then shows a single-line `✓`/`x` status without being overwritten by a later spinner tick.
|
||||||
|
- Stats: Removed the misleading `New words` series from expanded session charts; session detail now shows only the real total-word and known-word lines.
|
||||||
|
- Stats: Restored the cross-anime word table behavior in stats vocabulary surfaces so shared vocabulary entries no longer disappear or merge incorrectly across related media.
|
||||||
|
- Stats: `subminer stats -b` now runs as a standalone background stats daemon instead of reusing the main SubMiner app process, so the overlay app can still be launched separately for normal video watching.
|
||||||
|
- Stats: Dashboard word mining still works against the background daemon by using a short-lived hidden helper for the Yomitan add-note flow.
|
||||||
|
- Stats: Load full session timelines by default in stats session detail views so long sessions preserve complete telemetry history instead of being truncated by a fixed sample limit.
|
||||||
|
- Stats: Replaced heuristic stats word counts with Yomitan token counts, so session, media, anime, and trend subtitle totals now come directly from parsed subtitle tokens.
|
||||||
|
- Stats: Updated stats UI labels and lookup-rate copy to refer to tokens instead of words where those counts are shown.
|
||||||
|
- Overlay: Reduced repeated `Overlay loading...` popups on macOS when fullscreen tracker flaps briefly hide and recover the visible overlay.
|
||||||
|
- Stats: Scaled expanded session-detail known-word charts to the session's actual percentage range so small changes no longer render as a nearly flat line.
|
||||||
|
- Jlpt: Reduced JLPT dictionary startup log noise by summarizing duplicate surface-form collisions instead of logging one line per duplicate entry.
|
||||||
|
|
||||||
|
## v0.6.5 (2026-03-15)
|
||||||
|
|
||||||
|
### Internal
|
||||||
|
- Release: Seed the AUR checkout with the repo `.SRCINFO` template before rewriting metadata so tagged releases do not depend on prior AUR state.
|
||||||
|
|
||||||
|
## v0.6.4 (2026-03-15)
|
||||||
|
|
||||||
|
### Internal
|
||||||
|
- Release: Reworked AUR metadata generation to update `.SRCINFO` directly instead of depending on runner `makepkg`, fixing tagged release publishing for `subminer-bin`.
|
||||||
|
|
||||||
|
## v0.6.3 (2026-03-15)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Overlay: Expanded the `Alt+C` controller modal into an inline config/remap flow with preferred-controller saving and per-action learn mode for buttons, triggers, and stick directions.
|
||||||
|
|
||||||
|
### Internal
|
||||||
|
- Workflow: Hardened the `subminer-scrum-master` skill to explicitly answer whether docs updates and changelog fragments are required before handoff.
|
||||||
|
- Release: Automate `subminer-bin` AUR package updates from the tagged release workflow.
|
||||||
|
|
||||||
|
## v0.6.2 (2026-03-12)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Config: Added `yomitan.externalProfilePath` to reuse another Electron app's Yomitan profile in read-only mode.
|
||||||
|
- Config: SubMiner now reuses external Yomitan dictionaries/settings without writing back to that profile.
|
||||||
|
- Config: Launcher-managed playback now respects `yomitan.externalProfilePath` and no longer forces first-run setup when external Yomitan is configured.
|
||||||
|
- Config: SubMiner now seeds `config.jsonc` even when the default config directory already exists.
|
||||||
|
- Config: First-run setup now allows zero internal dictionaries when `yomitan.externalProfilePath` is configured, and falls back to requiring at least one internal dictionary if that external profile is later removed.
|
||||||
|
|
||||||
|
## v0.6.1 (2026-03-12)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Overlay: Added Chrome Gamepad API controller support for keyboard-only overlay mode, including configurable logical bindings for lookup, mining, popup navigation, Yomitan audio, mpv pause, d-pad fallback navigation, and slower smooth popup scrolling.
|
||||||
|
- Overlay: Added `Alt+C` controller selection and `Alt+Shift+C` controller debug modals, with preferred controller persistence and live raw input inspection.
|
||||||
|
- Overlay: Added a transient in-overlay controller-detected indicator when a controller is first found.
|
||||||
|
- Overlay: Fixed stale keyboard-only token highlight cleanup when keyboard-only mode turns off or the Yomitan popup closes.
|
||||||
|
|
||||||
|
### Docs
|
||||||
|
- Install: Added Arch Linux AUR install docs for `subminer-bin` in the README and installation guide.
|
||||||
|
|
||||||
|
### Internal
|
||||||
|
- Config: add an enforced `verify:config-example` gate so checked-in example config artifacts cannot drift silently
|
||||||
|
- Release: Fixed the release workflow token permissions so tagged builds can download `oven-sh/setup-bun` and publish artifacts again.
|
||||||
|
|
||||||
|
## v0.5.6 (2026-03-10)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Dictionary: Persist merged character-dictionary MRU state as soon as a new retained set is built so revisits do not get dropped if later Yomitan import work fails, and skip merged dictionary rebuilds for reorder-only revisits when the retained anime set itself has not changed.
|
||||||
|
- Startup: Fixed early Electron startup writing config and user data under a lowercase `~/.config/subminer` path instead of the canonical `~/.config/SubMiner` directory.
|
||||||
|
- Overlay: Kept JLPT underline colors stable during Yomitan hover and selection states, even when tokens also use known, N+1, name-match, or frequency styling.
|
||||||
|
|
||||||
|
## v0.5.5 (2026-03-09)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Overlay: Added `f` as the default overlay fullscreen toggle and changed the default AniSkip intro-jump key to `Tab`.
|
||||||
|
- Dictionary: Aligned AniList character dictionary generation more closely with the upstream reference by preserving duplicate shared names across characters, skipping characters without native Japanese names, restoring richer character info fields, and using upstream-style role mapping plus hint-aware kanji readings.
|
||||||
|
- Startup: Ordered startup OSD messages so tokenization loads first, annotation loading appears next if still pending, and character dictionary sync progress waits until annotation loading finishes.
|
||||||
|
- Dictionary: Added a visible startup OSD step for merged character-dictionary building so long rebuilds show progress before the later import/upload phase.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Dictionary: Fixed AniList media guessing for character dictionary auto-sync by using filename-only `guessit` input and preserving multi-part guessit titles instead of truncating them to the first segment.
|
||||||
|
- Dictionary: Refresh the current subtitle after character dictionary auto-sync completes so newly imported character names highlight on the active line instead of waiting for the next subtitle change.
|
||||||
|
- Dictionary: Show character dictionary auto-sync progress on the mpv OSD without sending desktop notifications.
|
||||||
|
- Dictionary: Keep character dictionary auto-sync non-blocking during startup by letting snapshot/build work run in parallel and delaying only the Yomitan import/settings phase until current-media tokenization is already ready.
|
||||||
|
- Overlay: Fixed visible overlay keyboard handling so pressing `Tab` still reaches mpv and triggers the default AniSkip skip-intro binding while the overlay has focus.
|
||||||
|
- Plugin: Fix Windows mpv plugin binary override lookup so `SUBMINER_BINARY_PATH` still resolves to `SubMiner.exe` when no AppImage override is set.
|
||||||
|
|
||||||
## v0.5.3 (2026-03-09)
|
## v0.5.3 (2026-03-09)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Release: Publish unsigned Windows `.exe` and `.zip` artifacts directly from release CI instead of routing them through SignPath.
|
- Release: Publish unsigned Windows `.exe` and `.zip` artifacts directly from release CI instead of routing them through SignPath.
|
||||||
- Release: Added `bun run build:win:unsigned` for explicit local unsigned Windows packaging.
|
- Release: Added `bun run build:win:unsigned` for explicit local unsigned Windows packaging.
|
||||||
|
|
||||||
## v0.5.2 (2026-03-09)
|
## v0.5.2 (2026-03-09)
|
||||||
|
|
||||||
### Internal
|
### Internal
|
||||||
|
|
||||||
- Release: Pinned the Windows SignPath submission workflow to an explicit artifact-configuration slug instead of relying on the SignPath project's default configuration.
|
- Release: Pinned the Windows SignPath submission workflow to an explicit artifact-configuration slug instead of relying on the SignPath project's default configuration.
|
||||||
|
|
||||||
## v0.5.1 (2026-03-09)
|
## v0.5.1 (2026-03-09)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Launcher: Removed the YouTube subtitle generation mode switch so YouTube playback always preloads subtitles before mpv starts.
|
- Launcher: Removed the YouTube subtitle generation mode switch so YouTube playback always preloads subtitles before mpv starts.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Launcher: Hardened YouTube AI subtitle fixing so fenced SRT output and text-only one-cue-per-block responses can still be applied without losing original cue timing.
|
- Launcher: Hardened YouTube AI subtitle fixing so fenced SRT output and text-only one-cue-per-block responses can still be applied without losing original cue timing.
|
||||||
- Launcher: Skipped AniSkip lookup during URL playback and YouTube subtitle-preload playback, limiting AniSkip to local file targets where it can actually resolve anime metadata.
|
- Launcher: Skipped AniSkip lookup during URL playback and YouTube subtitle-preload playback, limiting AniSkip to local file targets where it can actually resolve anime metadata.
|
||||||
- Launcher: Keep the background SubMiner process running after a launcher-managed mpv session exits so the next mpv instance can reconnect without restarting the app.
|
- Launcher: Keep the background SubMiner process running after a launcher-managed mpv session exits so the next mpv instance can reconnect without restarting the app.
|
||||||
@@ -24,6 +164,7 @@
|
|||||||
- Windows: Acquire the app single-instance lock earlier so Windows overlay/video launches reuse the running background SubMiner process instead of booting a second full app and repeating startup warmups.
|
- Windows: Acquire the app single-instance lock earlier so Windows overlay/video launches reuse the running background SubMiner process instead of booting a second full app and repeating startup warmups.
|
||||||
|
|
||||||
## v0.3.0 (2026-03-05)
|
## v0.3.0 (2026-03-05)
|
||||||
|
|
||||||
- Added keyboard-driven Yomitan navigation and popup controls, including optional auto-pause.
|
- Added keyboard-driven Yomitan navigation and popup controls, including optional auto-pause.
|
||||||
- Added subtitle/jump keyboard handling fixes for smoother subtitle playback control.
|
- Added subtitle/jump keyboard handling fixes for smoother subtitle playback control.
|
||||||
- Improved Anki/Yomitan reliability with stronger Yomitan proxy syncing and safer extension refresh logic.
|
- Improved Anki/Yomitan reliability with stronger Yomitan proxy syncing and safer extension refresh logic.
|
||||||
@@ -34,6 +175,7 @@
|
|||||||
- Removed docs Plausible integration and cleaned associated tracker settings.
|
- Removed docs Plausible integration and cleaned associated tracker settings.
|
||||||
|
|
||||||
## v0.2.3 (2026-03-02)
|
## v0.2.3 (2026-03-02)
|
||||||
|
|
||||||
- Added performance and tokenization optimizations (faster warmup, persistent MeCab usage, reduced enrichment lookups).
|
- Added performance and tokenization optimizations (faster warmup, persistent MeCab usage, reduced enrichment lookups).
|
||||||
- Added subtitle controls for no-jump delay shifts.
|
- Added subtitle controls for no-jump delay shifts.
|
||||||
- Improved subtitle highlight logic with priority and reliability fixes.
|
- Improved subtitle highlight logic with priority and reliability fixes.
|
||||||
@@ -42,30 +184,36 @@
|
|||||||
- Updated startup flow to load dictionaries asynchronously and unblock first tokenization sooner.
|
- Updated startup flow to load dictionaries asynchronously and unblock first tokenization sooner.
|
||||||
|
|
||||||
## v0.2.2 (2026-03-01)
|
## v0.2.2 (2026-03-01)
|
||||||
|
|
||||||
- Improved subtitle highlighting reliability for frequency modes.
|
- Improved subtitle highlighting reliability for frequency modes.
|
||||||
- Fixed Jellyfin misc info formatting cleanup.
|
- Fixed Jellyfin misc info formatting cleanup.
|
||||||
- Version bump maintenance for 0.2.2.
|
- Version bump maintenance for 0.2.2.
|
||||||
|
|
||||||
## v0.2.1 (2026-03-01)
|
## v0.2.1 (2026-03-01)
|
||||||
|
|
||||||
- Delivered Jellyfin and Subsync fixes from release patch cycle.
|
- Delivered Jellyfin and Subsync fixes from release patch cycle.
|
||||||
- Version bump maintenance for 0.2.1.
|
- Version bump maintenance for 0.2.1.
|
||||||
|
|
||||||
## v0.2.0 (2026-03-01)
|
## v0.2.0 (2026-03-01)
|
||||||
|
|
||||||
- Added task-related release work for the overlay 2.0 cycle.
|
- Added task-related release work for the overlay 2.0 cycle.
|
||||||
- Introduced Overlay 2.0.
|
- Introduced Overlay 2.0.
|
||||||
- Improved release automation reliability.
|
- Improved release automation reliability.
|
||||||
|
|
||||||
## v0.1.2 (2026-02-24)
|
## v0.1.2 (2026-02-24)
|
||||||
|
|
||||||
- Added encrypted AniList token handling and default GNOME keyring support.
|
- Added encrypted AniList token handling and default GNOME keyring support.
|
||||||
- Added launcher passthrough for password-store flows (Jellyfin path).
|
- Added launcher passthrough for password-store flows (Jellyfin path).
|
||||||
- Updated docs for auth and integration behavior.
|
- Updated docs for auth and integration behavior.
|
||||||
- Version bump maintenance for 0.1.2.
|
- Version bump maintenance for 0.1.2.
|
||||||
|
|
||||||
## v0.1.1 (2026-02-23)
|
## v0.1.1 (2026-02-23)
|
||||||
|
|
||||||
- Fixed overlay modal focus handling (`grab input`) behavior.
|
- Fixed overlay modal focus handling (`grab input`) behavior.
|
||||||
- Version bump maintenance for 0.1.1.
|
- Version bump maintenance for 0.1.1.
|
||||||
|
|
||||||
## v0.1.0 (2026-02-23)
|
## v0.1.0 (2026-02-23)
|
||||||
|
|
||||||
- Bootstrapped Electron runtime, services, and composition model.
|
- Bootstrapped Electron runtime, services, and composition model.
|
||||||
- Added runtime asset packaging and dependency vendoring.
|
- Added runtime asset packaging and dependency vendoring.
|
||||||
- Added project docs baseline, setup guides, architecture notes, and submodule/runtime assets.
|
- Added project docs baseline, setup guides, architecture notes, and submodule/runtime assets.
|
||||||
|
|||||||
11
Makefile
11
Makefile
@@ -1,4 +1,4 @@
|
|||||||
.PHONY: help deps build build-launcher install build-linux build-macos build-macos-unsigned clean install-linux install-macos install-windows install-plugin uninstall uninstall-linux uninstall-macos uninstall-windows print-dirs pretty ensure-bun generate-config generate-example-config dev-start dev-start-macos dev-watch dev-watch-macos dev-toggle dev-stop
|
.PHONY: help deps build build-launcher install build-linux build-macos build-macos-unsigned clean install-linux install-macos install-windows install-plugin uninstall uninstall-linux uninstall-macos uninstall-windows print-dirs pretty lint ensure-bun generate-config generate-example-config dev-start dev-start-macos dev-watch dev-watch-macos dev-toggle dev-stop
|
||||||
|
|
||||||
APP_NAME := subminer
|
APP_NAME := subminer
|
||||||
THEME_SOURCE := assets/themes/subminer.rasi
|
THEME_SOURCE := assets/themes/subminer.rasi
|
||||||
@@ -69,11 +69,12 @@ help:
|
|||||||
" generate-config Generate ~/.config/SubMiner/config.jsonc from centralized defaults" \
|
" generate-config Generate ~/.config/SubMiner/config.jsonc from centralized defaults" \
|
||||||
"" \
|
"" \
|
||||||
"Other targets:" \
|
"Other targets:" \
|
||||||
" deps Install JS dependencies (root + texthooker-ui)" \
|
" deps Install JS dependencies (root + stats + texthooker-ui)" \
|
||||||
" uninstall-linux Remove Linux install artifacts" \
|
" uninstall-linux Remove Linux install artifacts" \
|
||||||
" uninstall-macos Remove macOS install artifacts" \
|
" uninstall-macos Remove macOS install artifacts" \
|
||||||
" uninstall-windows Remove Windows mpv plugin artifacts" \
|
" uninstall-windows Remove Windows mpv plugin artifacts" \
|
||||||
" print-dirs Show resolved install locations" \
|
" print-dirs Show resolved install locations" \
|
||||||
|
" lint Lint stats (format check)" \
|
||||||
"" \
|
"" \
|
||||||
"Variables:" \
|
"Variables:" \
|
||||||
" PREFIX=... Override wrapper install prefix (default: $$HOME/.local)" \
|
" PREFIX=... Override wrapper install prefix (default: $$HOME/.local)" \
|
||||||
@@ -104,6 +105,7 @@ print-dirs:
|
|||||||
deps:
|
deps:
|
||||||
@$(MAKE) --no-print-directory ensure-bun
|
@$(MAKE) --no-print-directory ensure-bun
|
||||||
@bun install
|
@bun install
|
||||||
|
@cd stats && bun install --frozen-lockfile
|
||||||
@cd vendor/texthooker-ui && bun install --frozen-lockfile
|
@cd vendor/texthooker-ui && bun install --frozen-lockfile
|
||||||
|
|
||||||
ensure-bun:
|
ensure-bun:
|
||||||
@@ -111,6 +113,10 @@ ensure-bun:
|
|||||||
|
|
||||||
pretty: ensure-bun
|
pretty: ensure-bun
|
||||||
@bun run format:src
|
@bun run format:src
|
||||||
|
@bun run format:stats
|
||||||
|
|
||||||
|
lint: ensure-bun
|
||||||
|
@bun run lint:stats
|
||||||
|
|
||||||
build:
|
build:
|
||||||
@printf '%s\n' "[INFO] Detected platform: $(PLATFORM)"
|
@printf '%s\n' "[INFO] Detected platform: $(PLATFORM)"
|
||||||
@@ -165,7 +171,6 @@ generate-config: ensure-bun
|
|||||||
@bun run electron . --generate-config
|
@bun run electron . --generate-config
|
||||||
|
|
||||||
generate-example-config: ensure-bun
|
generate-example-config: ensure-bun
|
||||||
@bun run build
|
|
||||||
@bun run generate:config-example
|
@bun run generate:config-example
|
||||||
|
|
||||||
dev-start: ensure-bun
|
dev-start: ensure-bun
|
||||||
|
|||||||
164
README.md
164
README.md
@@ -1,16 +1,20 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
<img src="assets/SubMiner.png" width="169" alt="SubMiner logo">
|
<img src="assets/SubMiner.png" width="140" alt="SubMiner logo">
|
||||||
<h1>SubMiner</h1>
|
|
||||||
<strong>Look up words, mine to Anki, and enrich cards with context — without leaving mpv.</strong>
|
# SubMiner
|
||||||
<br /><br />
|
|
||||||
|
**Sentence-mine from mpv — look up words, one-key Anki export, immersion tracking.**
|
||||||
|
|
||||||
[](https://www.gnu.org/licenses/gpl-3.0)
|
[](https://www.gnu.org/licenses/gpl-3.0)
|
||||||
[]()
|
[](https://github.com/ksyasuda/SubMiner)
|
||||||
[](https://docs.subminer.moe)
|
[](https://docs.subminer.moe)
|
||||||
|
[](https://aur.archlinux.org/packages/subminer-bin)
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br />
|
---
|
||||||
|
|
||||||
|
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">
|
<div align="center">
|
||||||
|
|
||||||
@@ -18,103 +22,119 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br />
|
## Features
|
||||||
|
|
||||||
## What it does
|
**Dictionary lookups** — Yomitan runs inside the overlay. Hover or navigate to any word for full dictionary popups without leaving mpv.
|
||||||
|
|
||||||
SubMiner is an Electron overlay that sits on top of mpv. It turns your video player into a full sentence-mining workstation:
|
**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.
|
||||||
|
|
||||||
- **Hover to look up** — Yomitan dictionary popups directly on subtitles
|
<div align="center">
|
||||||
- **Keyboard-driven lookup mode** — Navigate token-by-token, keep lookup open across tokens, and control popup scrolling/audio/mining without leaving the overlay
|
<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">
|
||||||
- **One-key mining** — Creates Anki cards with sentence, audio, screenshot, and translation
|
</div>
|
||||||
- **Instant auto-enrichment** — Optional local AnkiConnect proxy enriches new Yomitan cards immediately
|
|
||||||
- **Reading annotations** — Combines N+1 targeting, frequency-dictionary highlighting, and JLPT underlining while you read
|
|
||||||
- **Hover-aware playback** — By default, hovering subtitle text pauses mpv and resumes on mouse leave (`subtitleStyle.autoPauseVideoOnHover`)
|
|
||||||
- **Subtitle tools** — Download from Jimaku, sync with alass/ffsubsync
|
|
||||||
- **Immersion tracking** — SQLite-powered stats on your watch time and mining activity
|
|
||||||
- **Custom texthooker page** — Built-in custom texthooker page and websocket, no extra setup
|
|
||||||
- **Annotated websocket API** — Dedicated annotation feed can serve bundled texthooker or external clients with rendered `sentence` HTML plus structured `tokens`
|
|
||||||
- **Jellyfin integration** — Remote playback setup, cast device mode, and direct playback launch
|
|
||||||
- **AniList progress** — Track episode completion and push watching progress automatically
|
|
||||||
|
|
||||||
## Quick start
|
**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.
|
||||||
|
|
||||||
### 1. Install
|
<div align="center">
|
||||||
|
<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>
|
||||||
|
|
||||||
**Linux (AppImage):**
|
**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/stats-overview.png" width="800" alt="Stats dashboard with watch time, cards mined, streaks, and tracking snapshot">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
**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/texthooker.png" width="800" alt="Texthooker page with annotated subtitle lines and frequency highlighting">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### Install
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><b>Arch Linux (AUR)</b></summary>
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
wget https://github.com/ksyasuda/SubMiner/releases/latest/download/SubMiner.AppImage -O ~/.local/bin/SubMiner.AppImage
|
paru -S subminer-bin
|
||||||
chmod +x ~/.local/bin/SubMiner.AppImage
|
```
|
||||||
wget https://github.com/ksyasuda/SubMiner/releases/latest/download/subminer -O ~/.local/bin/subminer
|
|
||||||
chmod +x ~/.local/bin/subminer
|
|
||||||
|
|
||||||
|
Or manually:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://aur.archlinux.org/subminer-bin.git && cd subminer-bin && makepkg -si
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><b>Linux (AppImage)</b></summary>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p ~/.local/bin
|
||||||
|
wget https://github.com/ksyasuda/SubMiner/releases/latest/download/SubMiner.AppImage -O ~/.local/bin/SubMiner.AppImage \
|
||||||
|
&& chmod +x ~/.local/bin/SubMiner.AppImage
|
||||||
|
wget https://github.com/ksyasuda/SubMiner/releases/latest/download/subminer -O ~/.local/bin/subminer \
|
||||||
|
&& chmod +x ~/.local/bin/subminer
|
||||||
```
|
```
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> The `subminer` wrapper uses a [Bun](https://bun.sh) shebang. Make sure `bun` is on your `PATH`.
|
> The `subminer` wrapper uses a [Bun](https://bun.sh) shebang. Make sure `bun` is on your `PATH`.
|
||||||
|
|
||||||
**macOS (DMG/ZIP):** download the latest packaged build from [GitHub Releases](https://github.com/ksyasuda/SubMiner/releases/latest) and drag `SubMiner.app` into `/Applications`.
|
</details>
|
||||||
|
|
||||||
**Windows (Installer/ZIP):** download the latest `SubMiner-<version>.exe` installer or portable `.zip` from [GitHub Releases](https://github.com/ksyasuda/SubMiner/releases/latest). Keep `mpv` installed and available on `PATH`.
|
<details>
|
||||||
|
<summary><b>macOS / Windows / From source</b></summary>
|
||||||
|
|
||||||
**From source** — initialize submodules first (`git submodule update --init --recursive`). Bundled Yomitan is built from the `vendor/subminer-yomitan` submodule into `build/yomitan` during `bun run build`, so source builds only need Bun for the JS toolchain. Packaged macOS and Windows installs do not require Bun. Windows installer builds go through `electron-builder`; its bundled `app-builder-lib` NSIS templates already use the third-party `WinShell` plugin for shortcut AppUserModelID assignment, and the `WinShell.dll` binary is supplied by electron-builder's cached `nsis-resources` bundle, so `bun run build:win` does not need a separate repo-local plugin install step. Full install guide: [docs.subminer.moe/installation#from-source](https://docs.subminer.moe/installation#from-source).
|
**macOS** — Download the latest DMG/ZIP from [GitHub Releases](https://github.com/ksyasuda/SubMiner/releases/latest) and drag `SubMiner.app` into `/Applications`.
|
||||||
|
|
||||||
### 2. Launch the app once
|
**Windows** — Download the latest installer or portable `.zip` from [GitHub Releases](https://github.com/ksyasuda/SubMiner/releases/latest). Keep `mpv` on `PATH`.
|
||||||
|
|
||||||
|
**From source** — See [docs.subminer.moe/installation#from-source](https://docs.subminer.moe/installation#from-source).
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
### First Launch
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
### Mine
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Linux
|
subminer video.mkv # auto-starts overlay + resumes playback
|
||||||
SubMiner.AppImage
|
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
|
||||||
```
|
```
|
||||||
|
|
||||||
On macOS, launch `SubMiner.app`. On Windows, launch `SubMiner.exe` from the Start menu or install directory.
|
---
|
||||||
|
|
||||||
On first launch, SubMiner:
|
|
||||||
|
|
||||||
- starts in the tray/background
|
|
||||||
- creates the default config directory and `config.jsonc`
|
|
||||||
- opens a compact setup popup
|
|
||||||
- can install the mpv plugin to the default mpv scripts location for you
|
|
||||||
- links directly to Yomitan settings so you can install dictionaries before finishing setup
|
|
||||||
|
|
||||||
Existing installs that already have a valid config plus at least one Yomitan dictionary are auto-detected as complete and will not be re-prompted.
|
|
||||||
|
|
||||||
### 3. Finish setup
|
|
||||||
|
|
||||||
- click `Install mpv plugin` if you want the default plugin auto-start flow
|
|
||||||
- click `Open Yomitan Settings` and install at least one dictionary
|
|
||||||
- click `Refresh status`
|
|
||||||
- click `Finish setup`
|
|
||||||
|
|
||||||
The mpv plugin step is optional. Yomitan must report at least one installed dictionary before setup can be completed.
|
|
||||||
|
|
||||||
### 4. Mine
|
|
||||||
|
|
||||||
```bash
|
|
||||||
subminer video.mkv # default plugin config auto-starts visible overlay + resumes playback when ready
|
|
||||||
subminer --start video.mkv # optional explicit overlay start when plugin auto_start=no
|
|
||||||
```
|
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
| Required | Optional |
|
| Required | Optional |
|
||||||
| ------------------------------------------ | -------------------------------------------------- |
|
| ------------------------------------------------------ | ----------------------------- |
|
||||||
| `bun` (source builds, Linux `subminer`) | |
|
| [`mpv`](https://mpv.io) with IPC socket | `yt-dlp` |
|
||||||
| `mpv` with IPC socket | `yt-dlp` |
|
| `ffmpeg` | `guessit` (AniSkip detection) |
|
||||||
| `ffmpeg` | `guessit` (better AniSkip title/episode detection) |
|
| `mecab` + `mecab-ipadic` | `fzf` / `rofi` |
|
||||||
| `mecab` + `mecab-ipadic` | `fzf` / `rofi` |
|
| [`bun`](https://bun.sh) (source builds, Linux wrapper) | `chafa`, `ffmpegthumbnailer` |
|
||||||
| Linux: `hyprctl` or `xdotool` + `xwininfo` | `chafa`, `ffmpegthumbnailer` |
|
| Linux: `hyprctl` or `xdotool` + `xwininfo` | |
|
||||||
| macOS: Accessibility permission | |
|
| macOS: Accessibility permission | |
|
||||||
|
|
||||||
Windows builds use native window tracking and do not require the Linux compositor helper tools.
|
Windows uses native window tracking and does not need the Linux compositor tools.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
For full guides on configuration, Anki, Jellyfin, and more, see [docs.subminer.moe](https://docs.subminer.moe).
|
Full guides on configuration, Anki, Jellyfin, immersion tracking, and more at **[docs.subminer.moe](https://docs.subminer.moe)**.
|
||||||
|
|
||||||
## Acknowledgments
|
## Acknowledgments
|
||||||
|
|
||||||
Built on the shoulders of [GameSentenceMiner](https://github.com/bpwhelan/GameSentenceMiner), [Renji's Texthooker Page](https://github.com/Renji-XD/texthooker-ui), [mpvacious](https://github.com/Ajatt-Tools/mpvacious), [Anacreon-Script](https://github.com/friedrich-de/Anacreon-Script), and [Bee's Character Dictionary](https://github.com/bee-san/Japanese_Character_Name_Dictionary). Subtitles powered by [Jimaku.cc](https://jimaku.cc). Dictionary lookups via [Yomitan](https://github.com/yomidevs/yomitan).
|
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
|
## License
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 114 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 21 MiB After Width: | Height: | Size: 3.0 MiB |
@@ -0,0 +1,33 @@
|
|||||||
|
---
|
||||||
|
id: TASK-175
|
||||||
|
title: Address latest PR 19 review comments
|
||||||
|
status: In Progress
|
||||||
|
assignee: []
|
||||||
|
created_date: '2026-03-15 10:25'
|
||||||
|
labels:
|
||||||
|
- pr-review
|
||||||
|
- stats-dashboard
|
||||||
|
dependencies: []
|
||||||
|
references:
|
||||||
|
- src/core/services/ipc.ts
|
||||||
|
- src/core/services/stats-server.ts
|
||||||
|
- src/core/services/immersion-tracker/__tests__/query.test.ts
|
||||||
|
- src/core/services/stats-window-runtime.ts
|
||||||
|
- src/core/services/stats-window.test.ts
|
||||||
|
- src/shared/ipc/contracts.ts
|
||||||
|
- src/main.ts
|
||||||
|
priority: medium
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
Validate the latest automated review comments on PR #19 against the current branch, implement the technically valid fixes, and document any items intentionally left unchanged.
|
||||||
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
<!-- AC:BEGIN -->
|
||||||
|
- [ ] #1 Validated the latest PR #19 review comments against current branch behavior and existing architecture
|
||||||
|
- [ ] #2 Implemented the accepted fixes with regression coverage where it fits
|
||||||
|
- [ ] #3 Documented which latest review items were intentionally not changed because they were already addressed or not technically warranted
|
||||||
|
<!-- AC:END -->
|
||||||
@@ -5,7 +5,7 @@ status: Done
|
|||||||
assignee:
|
assignee:
|
||||||
- codex
|
- codex
|
||||||
created_date: '2026-03-08 15:17'
|
created_date: '2026-03-08 15:17'
|
||||||
updated_date: '2026-03-08 15:17'
|
updated_date: '2026-03-16 05:13'
|
||||||
labels:
|
labels:
|
||||||
- release
|
- release
|
||||||
- docs
|
- docs
|
||||||
@@ -18,6 +18,7 @@ references:
|
|||||||
- ../subminer-docs/usage.md
|
- ../subminer-docs/usage.md
|
||||||
- ../subminer-docs/changelog.md
|
- ../subminer-docs/changelog.md
|
||||||
priority: medium
|
priority: medium
|
||||||
|
ordinal: 53500
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
@@ -24,11 +24,15 @@ references:
|
|||||||
## Description
|
## Description
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
|
||||||
Replace the launcher YouTube subtitle generation flow with a pure TypeScript pipeline that prefers real downloadable YouTube subtitles, never uses YouTube auto-generated subtitles, locally generates missing tracks with whisper.cpp, and can optionally fix generated subtitles via a shared OpenAI-compatible AI provider config. This feature also introduces a breaking config cleanup: move provider settings to a new top-level ai section and reduce ankiConnect.ai to a boolean feature toggle.
|
Replace the launcher YouTube subtitle generation flow with a pure TypeScript pipeline that prefers real downloadable YouTube subtitles, never uses YouTube auto-generated subtitles, locally generates missing tracks with whisper.cpp, and can optionally fix generated subtitles via a shared OpenAI-compatible AI provider config. This feature also introduces a breaking config cleanup: move provider settings to a new top-level ai section and reduce ankiConnect.ai to a boolean feature toggle.
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:END -->
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
## Acceptance Criteria
|
## Acceptance Criteria
|
||||||
|
|
||||||
<!-- AC:BEGIN -->
|
<!-- AC:BEGIN -->
|
||||||
|
|
||||||
- [x] #1 Launcher YouTube subtitle generation prefers downloadable manual YouTube subtitles, never uses YouTube auto-generated subtitles, and locally generates only missing tracks with whisper.cpp.
|
- [x] #1 Launcher YouTube subtitle generation prefers downloadable manual YouTube subtitles, never uses YouTube auto-generated subtitles, and locally generates only missing tracks with whisper.cpp.
|
||||||
- [x] #2 Generated whisper subtitle tracks can optionally be post-processed with an OpenAI-compatible AI provider using shared top-level ai config, with validation and fallback to raw whisper output on failure.
|
- [x] #2 Generated whisper subtitle tracks can optionally be post-processed with an OpenAI-compatible AI provider using shared top-level ai config, with validation and fallback to raw whisper output on failure.
|
||||||
- [x] #3 Configuration is updated so top-level ai is canonical shared provider config, ankiConnect.ai is boolean-only, and youtubeSubgen includes whisperVadModel, whisperThreads, and fixWithAi.
|
- [x] #3 Configuration is updated so top-level ai is canonical shared provider config, ankiConnect.ai is boolean-only, and youtubeSubgen includes whisperVadModel, whisperThreads, and fixWithAi.
|
||||||
@@ -39,6 +43,7 @@ Replace the launcher YouTube subtitle generation flow with a pure TypeScript pip
|
|||||||
## Implementation Plan
|
## Implementation Plan
|
||||||
|
|
||||||
<!-- SECTION:PLAN:BEGIN -->
|
<!-- SECTION:PLAN:BEGIN -->
|
||||||
|
|
||||||
1. Introduce canonical top-level ai config plus youtubeSubgen runtime knobs (whisperVadModel, whisperThreads, fixWithAi) and convert ankiConnect.ai to a boolean-only toggle across types, defaults, validation, option registries, launcher config parsing, and config example/docs.
|
1. Introduce canonical top-level ai config plus youtubeSubgen runtime knobs (whisperVadModel, whisperThreads, fixWithAi) and convert ankiConnect.ai to a boolean-only toggle across types, defaults, validation, option registries, launcher config parsing, and config example/docs.
|
||||||
2. Extract shared OpenAI-compatible AI client helpers from the current Anki translation code, including base URL normalization, API key / apiKeyCommand resolution, timeout handling, and response text extraction.
|
2. Extract shared OpenAI-compatible AI client helpers from the current Anki translation code, including base URL normalization, API key / apiKeyCommand resolution, timeout handling, and response text extraction.
|
||||||
3. Update Anki translation flow and hot-reload/runtime plumbing to consume global ai config while treating ankiConnect.ai as a feature gate only.
|
3. Update Anki translation flow and hot-reload/runtime plumbing to consume global ai config while treating ankiConnect.ai as a feature gate only.
|
||||||
@@ -50,6 +55,7 @@ Replace the launcher YouTube subtitle generation flow with a pure TypeScript pip
|
|||||||
## Implementation Notes
|
## Implementation Notes
|
||||||
|
|
||||||
<!-- SECTION:NOTES:BEGIN -->
|
<!-- SECTION:NOTES:BEGIN -->
|
||||||
|
|
||||||
Implemented pure TypeScript launcher/youtube pipeline modules for manual subtitle fetch, audio extraction, whisper runs, SRT utilities, and optional AI subtitle fixing. Removed yt-dlp auto-subtitle usage from the generation path.
|
Implemented pure TypeScript launcher/youtube pipeline modules for manual subtitle fetch, audio extraction, whisper runs, SRT utilities, and optional AI subtitle fixing. Removed yt-dlp auto-subtitle usage from the generation path.
|
||||||
|
|
||||||
Added shared top-level ai config plus shared AI client helpers; converted ankiConnect.ai to a boolean feature gate and updated Anki runtime wiring to consume global ai config.
|
Added shared top-level ai config plus shared AI client helpers; converted ankiConnect.ai to a boolean feature gate and updated Anki runtime wiring to consume global ai config.
|
||||||
@@ -57,10 +63,13 @@ Added shared top-level ai config plus shared AI client helpers; converted ankiCo
|
|||||||
Updated launcher config parsing, config template sections, and config.example.jsonc for the breaking config shape including youtubeSubgen.whisperVadModel, youtubeSubgen.whisperThreads, and youtubeSubgen.fixWithAi.
|
Updated launcher config parsing, config template sections, and config.example.jsonc for the breaking config shape including youtubeSubgen.whisperVadModel, youtubeSubgen.whisperThreads, and youtubeSubgen.fixWithAi.
|
||||||
|
|
||||||
Verification: bun run test:config:src passed; targeted AI/Anki/runtime tests passed; bun run typecheck passed. bun run test:launcher:unit:src reported one unrelated existing failure in launcher/aniskip-metadata.test.ts (resolveAniSkipMetadataForFile resolves MAL id and intro payload).
|
Verification: bun run test:config:src passed; targeted AI/Anki/runtime tests passed; bun run typecheck passed. bun run test:launcher:unit:src reported one unrelated existing failure in launcher/aniskip-metadata.test.ts (resolveAniSkipMetadataForFile resolves MAL id and intro payload).
|
||||||
|
|
||||||
<!-- SECTION:NOTES:END -->
|
<!-- SECTION:NOTES:END -->
|
||||||
|
|
||||||
## Final Summary
|
## Final Summary
|
||||||
|
|
||||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||||
|
|
||||||
Replaced the launcher YouTube subtitle flow with a modular TypeScript pipeline that prefers manual YouTube subtitles, transcribes only missing tracks with whisper.cpp, and can optionally post-fix whisper output through a shared OpenAI-compatible AI client with strict SRT validation/fallback. Introduced canonical top-level ai config, reduced ankiConnect.ai to a boolean feature gate, updated launcher/config parsing and checked-in config artifacts, and added coverage for YouTube orchestration, whisper args, SRT validation, AI fix behavior, and breaking config validation.
|
Replaced the launcher YouTube subtitle flow with a modular TypeScript pipeline that prefers manual YouTube subtitles, transcribes only missing tracks with whisper.cpp, and can optionally post-fix whisper output through a shared OpenAI-compatible AI client with strict SRT validation/fallback. Introduced canonical top-level ai config, reduced ankiConnect.ai to a boolean feature gate, updated launcher/config parsing and checked-in config artifacts, and added coverage for YouTube orchestration, whisper args, SRT validation, AI fix behavior, and breaking config validation.
|
||||||
|
|
||||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||||
@@ -5,7 +5,7 @@ status: Done
|
|||||||
assignee:
|
assignee:
|
||||||
- codex
|
- codex
|
||||||
created_date: '2026-03-08 15:17'
|
created_date: '2026-03-08 15:17'
|
||||||
updated_date: '2026-03-08 15:17'
|
updated_date: '2026-03-16 05:13'
|
||||||
labels:
|
labels:
|
||||||
- release
|
- release
|
||||||
- windows
|
- windows
|
||||||
@@ -17,6 +17,7 @@ references:
|
|||||||
- build/signpath-windows-artifact-config.xml
|
- build/signpath-windows-artifact-config.xml
|
||||||
- package.json
|
- package.json
|
||||||
priority: high
|
priority: high
|
||||||
|
ordinal: 54500
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
@@ -19,11 +19,15 @@ references:
|
|||||||
## Description
|
## Description
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
|
||||||
The GitHub Actions Release workflow fails during the Publish Release step for tag releases because the gh CLI invocation passes invalid arguments when creating or editing the GitHub release. Restore successful release publication for tagged builds without changing unrelated release packaging behavior.
|
The GitHub Actions Release workflow fails during the Publish Release step for tag releases because the gh CLI invocation passes invalid arguments when creating or editing the GitHub release. Restore successful release publication for tagged builds without changing unrelated release packaging behavior.
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:END -->
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
## Acceptance Criteria
|
## Acceptance Criteria
|
||||||
|
|
||||||
<!-- AC:BEGIN -->
|
<!-- AC:BEGIN -->
|
||||||
|
|
||||||
- [x] #1 Tagged Release workflow completes the Publish Release step without gh CLI argument errors.
|
- [x] #1 Tagged Release workflow completes the Publish Release step without gh CLI argument errors.
|
||||||
- [x] #2 Release workflow still creates or updates the GitHub release as a non-prerelease for normal version tags.
|
- [x] #2 Release workflow still creates or updates the GitHub release as a non-prerelease for normal version tags.
|
||||||
- [x] #3 A regression check covers the publish command shape or workflow behavior that caused this failure.
|
- [x] #3 A regression check covers the publish command shape or workflow behavior that caused this failure.
|
||||||
@@ -33,6 +37,7 @@ The GitHub Actions Release workflow fails during the Publish Release step for ta
|
|||||||
## Implementation Plan
|
## Implementation Plan
|
||||||
|
|
||||||
<!-- SECTION:PLAN:BEGIN -->
|
<!-- SECTION:PLAN:BEGIN -->
|
||||||
|
|
||||||
1. Add a targeted regression test for .github/workflows/release.yml that fails if the publish step passes an argument to the gh --prerelease boolean flag or otherwise omits explicit non-prerelease behavior.
|
1. Add a targeted regression test for .github/workflows/release.yml that fails if the publish step passes an argument to the gh --prerelease boolean flag or otherwise omits explicit non-prerelease behavior.
|
||||||
2. Run the targeted test to confirm the current workflow fails for the expected reason.
|
2. Run the targeted test to confirm the current workflow fails for the expected reason.
|
||||||
3. Patch the Publish Release step in .github/workflows/release.yml to remove the invalid gh CLI usage while preserving non-prerelease release creation/update behavior.
|
3. Patch the Publish Release step in .github/workflows/release.yml to remove the invalid gh CLI usage while preserving non-prerelease release creation/update behavior.
|
||||||
@@ -42,6 +47,7 @@ The GitHub Actions Release workflow fails during the Publish Release step for ta
|
|||||||
## Implementation Notes
|
## Implementation Notes
|
||||||
|
|
||||||
<!-- SECTION:NOTES:BEGIN -->
|
<!-- SECTION:NOTES:BEGIN -->
|
||||||
|
|
||||||
Identified root cause from GitHub Actions run 22812335927: Publish Release failed with `accepts 1 arg(s), received 2` because the workflow passed a value to gh's boolean prerelease flag.
|
Identified root cause from GitHub Actions run 22812335927: Publish Release failed with `accepts 1 arg(s), received 2` because the workflow passed a value to gh's boolean prerelease flag.
|
||||||
|
|
||||||
Added a workflow comment clarifying that omitting the prerelease flag keeps normal releases as non-prerelease releases.
|
Added a workflow comment clarifying that omitting the prerelease flag keeps normal releases as non-prerelease releases.
|
||||||
@@ -51,14 +57,17 @@ Added src/release-workflow.test.ts and wired it into `bun run test:fast` so CI c
|
|||||||
Verification: `bun test src/release-workflow.test.ts`, `bun run typecheck`, and `bun run test:fast` all passed locally.
|
Verification: `bun test src/release-workflow.test.ts`, `bun run typecheck`, and `bun run test:fast` all passed locally.
|
||||||
|
|
||||||
Code-review pass found no issues; remaining caveat is that prerelease tag semantics are still not modeled for tags like `v1.0.0-beta.1`, which is outside this fix scope.
|
Code-review pass found no issues; remaining caveat is that prerelease tag semantics are still not modeled for tags like `v1.0.0-beta.1`, which is outside this fix scope.
|
||||||
|
|
||||||
<!-- SECTION:NOTES:END -->
|
<!-- SECTION:NOTES:END -->
|
||||||
|
|
||||||
## Final Summary
|
## Final Summary
|
||||||
|
|
||||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||||
|
|
||||||
Fixed the GitHub Actions release publish step so tagged releases no longer fail on invalid gh CLI usage. The workflow now omits the prerelease flag when creating or editing normal releases, which preserves existing non-prerelease behavior and avoids the `accepts 1 arg(s), received 2` failure seen in run 22812335927.
|
Fixed the GitHub Actions release publish step so tagged releases no longer fail on invalid gh CLI usage. The workflow now omits the prerelease flag when creating or editing normal releases, which preserves existing non-prerelease behavior and avoids the `accepts 1 arg(s), received 2` failure seen in run 22812335927.
|
||||||
|
|
||||||
Added a small regression test that reads `.github/workflows/release.yml` and asserts the publish step does not set the prerelease flag, then included that test in `bun run test:fast` so the main verification lane catches this class of workflow regression before the next release.
|
Added a small regression test that reads `.github/workflows/release.yml` and asserts the publish step does not set the prerelease flag, then included that test in `bun run test:fast` so the main verification lane catches this class of workflow regression before the next release.
|
||||||
|
|
||||||
Validation run locally: `bun test src/release-workflow.test.ts`, `bun run typecheck`, and `bun run test:fast`. Residual risk: prerelease-tag semantics remain unchanged for tags such as `v1.0.0-beta.1`; this fix is intentionally scoped to restoring normal tagged release publication.
|
Validation run locally: `bun test src/release-workflow.test.ts`, `bun run typecheck`, and `bun run test:fast`. Residual risk: prerelease-tag semantics remain unchanged for tags such as `v1.0.0-beta.1`; this fix is intentionally scoped to restoring normal tagged release publication.
|
||||||
|
|
||||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
---
|
||||||
|
id: TASK-155
|
||||||
|
title: Move user docs site back into main repo
|
||||||
|
status: Done
|
||||||
|
assignee: []
|
||||||
|
created_date: '2026-03-10 19:20'
|
||||||
|
updated_date: '2026-03-10 19:38'
|
||||||
|
labels: []
|
||||||
|
dependencies: []
|
||||||
|
priority: medium
|
||||||
|
ordinal: 15500
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
|
||||||
|
Move the standalone VitePress docs site from the sibling `../subminer-docs` checkout back into the main `SubMiner` repo so docs can be updated alongside code and local tooling can reference one repository.
|
||||||
|
|
||||||
|
Scope:
|
||||||
|
|
||||||
|
- import the tracked docs-site source into a dedicated in-repo subdirectory
|
||||||
|
- update scripts/tests/docs instructions that assume a sibling `../subminer-docs` checkout
|
||||||
|
- preserve Cloudflare Pages deployability from a repo subdirectory
|
||||||
|
- verify the app repo and docs site both still build/test from the new layout
|
||||||
|
|
||||||
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
<!-- AC:BEGIN -->
|
||||||
|
|
||||||
|
- [x] #1 The user-facing VitePress docs source lives inside the `SubMiner` repo in a dedicated subdirectory.
|
||||||
|
- [x] #2 First-party scripts/tests/docs no longer require `../subminer-docs` for normal operation.
|
||||||
|
- [x] #3 In-repo docs instructions include the Cloudflare Pages subdirectory deploy settings.
|
||||||
|
- [x] #4 Verification covers the relocated docs site build/tests plus affected app-repo checks.
|
||||||
|
|
||||||
|
<!-- AC:END -->
|
||||||
|
|
||||||
|
## Final Summary
|
||||||
|
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||||
|
|
||||||
|
Imported the VitePress site into `docs-site/` inside the main repo and updated project instructions, docs contributor guidance, generator logic, and regression tests to treat that in-repo directory as the docs source of truth.
|
||||||
|
|
||||||
|
Added root proxy scripts for `docs:dev`, `docs:build`, `docs:preview`, and `docs:test`, repointed config-example generation to `docs-site/public/config.example.jsonc`, switched docs edit links to the main `SubMiner` repo, and documented the Cloudflare Pages subdirectory settings (`docs-site` root, `.vitepress/dist` output, `docs-site/**` watch path).
|
||||||
|
|
||||||
|
Verified with `bun run format:check:src`, `bun run typecheck`, `bun run docs:test`, `bun run docs:build`, `bun run test:config:src`, and `bun run test:fast`.
|
||||||
|
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||||
@@ -6,10 +6,11 @@ title: >-
|
|||||||
status: Done
|
status: Done
|
||||||
assignee: []
|
assignee: []
|
||||||
created_date: '2026-03-03 08:47'
|
created_date: '2026-03-03 08:47'
|
||||||
updated_date: '2026-03-03 08:57'
|
updated_date: '2026-03-16 05:13'
|
||||||
labels: []
|
labels: []
|
||||||
dependencies: []
|
dependencies: []
|
||||||
priority: high
|
priority: high
|
||||||
|
ordinal: 96500
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
project_name: "SubMiner"
|
project_name: 'SubMiner'
|
||||||
default_status: "To Do"
|
default_status: 'To Do'
|
||||||
statuses: ["To Do", "In Progress", "Done"]
|
statuses: ['To Do', 'In Progress', 'Done']
|
||||||
labels: []
|
labels: []
|
||||||
definition_of_done: []
|
definition_of_done: []
|
||||||
date_format: yyyy-mm-dd
|
date_format: yyyy-mm-dd
|
||||||
max_column_width: 20
|
max_column_width: 20
|
||||||
default_editor: "nvim"
|
default_editor: 'nvim'
|
||||||
auto_open_browser: false
|
auto_open_browser: false
|
||||||
default_port: 6420
|
default_port: 6420
|
||||||
remote_operations: true
|
remote_operations: true
|
||||||
@@ -13,4 +13,4 @@ auto_commit: false
|
|||||||
bypass_git_hooks: false
|
bypass_git_hooks: false
|
||||||
check_active_branches: true
|
check_active_branches: true
|
||||||
active_branch_days: 30
|
active_branch_days: 30
|
||||||
task_prefix: "task"
|
task_prefix: 'task'
|
||||||
|
|||||||
8
backlog/milestones/m-1 - stats-dashboard.md
Normal file
8
backlog/milestones/m-1 - stats-dashboard.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
id: m-1
|
||||||
|
title: "Stats Dashboard"
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
Milestone: Stats Dashboard
|
||||||
@@ -1,20 +1,19 @@
|
|||||||
---
|
---
|
||||||
id: TASK-100
|
id: TASK-100
|
||||||
title: 'Add configurable texthooker startup launch'
|
title: Add configurable texthooker startup launch
|
||||||
status: Done
|
status: Done
|
||||||
assignee: []
|
assignee: []
|
||||||
created_date: '2026-03-06 23:30'
|
created_date: '2026-03-06 23:30'
|
||||||
updated_date: '2026-03-07 01:59'
|
updated_date: '2026-03-16 05:13'
|
||||||
labels: []
|
labels: []
|
||||||
dependencies: []
|
dependencies: []
|
||||||
priority: medium
|
priority: medium
|
||||||
ordinal: 10000
|
ordinal: 11010
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
|
||||||
Add a config option under `texthooker` to launch the built-in texthooker server automatically when SubMiner starts.
|
Add a config option under `texthooker` to launch the built-in texthooker server automatically when SubMiner starts.
|
||||||
|
|
||||||
Scope:
|
Scope:
|
||||||
@@ -24,26 +23,20 @@ Scope:
|
|||||||
- Start the existing texthooker server during normal app startup when enabled.
|
- Start the existing texthooker server during normal app startup when enabled.
|
||||||
- Keep `texthooker.openBrowser` as separate behavior.
|
- Keep `texthooker.openBrowser` as separate behavior.
|
||||||
- Add regression coverage and update generated config docs/example.
|
- Add regression coverage and update generated config docs/example.
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:END -->
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
## Acceptance Criteria
|
## Acceptance Criteria
|
||||||
|
|
||||||
<!-- AC:BEGIN -->
|
<!-- AC:BEGIN -->
|
||||||
|
|
||||||
- [x] #1 Default config enables automatic texthooker startup.
|
- [x] #1 Default config enables automatic texthooker startup.
|
||||||
- [x] #2 Config parser accepts valid boolean values and warns on invalid values.
|
- [x] #2 Config parser accepts valid boolean values and warns on invalid values.
|
||||||
- [x] #3 App-ready startup launches texthooker when enabled.
|
- [x] #3 App-ready startup launches texthooker when enabled.
|
||||||
- [x] #4 Generated config template/example documents the new option.
|
- [x] #4 Generated config template/example documents the new option.
|
||||||
|
|
||||||
<!-- AC:END -->
|
<!-- AC:END -->
|
||||||
|
|
||||||
## Final Summary
|
## Final Summary
|
||||||
|
|
||||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||||
|
|
||||||
Added `texthooker.launchAtStartup` with a default of `true`, wired it through config defaults/validation/template generation, and started the existing texthooker server during app-ready startup without coupling it to browser auto-open behavior.
|
Added `texthooker.launchAtStartup` with a default of `true`, wired it through config defaults/validation/template generation, and started the existing texthooker server during app-ready startup without coupling it to browser auto-open behavior.
|
||||||
|
|
||||||
Also added regression coverage for config parsing/template output and app-ready dependency wiring, then regenerated the checked-in config example artifacts.
|
Also added regression coverage for config parsing/template output and app-ready dependency wiring, then regenerated the checked-in config example artifacts.
|
||||||
|
|
||||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ title: Index AniList character alternative names in the character dictionary
|
|||||||
status: Done
|
status: Done
|
||||||
assignee: []
|
assignee: []
|
||||||
created_date: '2026-03-07 00:00'
|
created_date: '2026-03-07 00:00'
|
||||||
updated_date: '2026-03-08 00:11'
|
updated_date: '2026-03-16 05:13'
|
||||||
labels:
|
labels:
|
||||||
- dictionary
|
- dictionary
|
||||||
- anilist
|
- anilist
|
||||||
@@ -13,6 +13,7 @@ references:
|
|||||||
- src/main/character-dictionary-runtime.ts
|
- src/main/character-dictionary-runtime.ts
|
||||||
- src/main/character-dictionary-runtime.test.ts
|
- src/main/character-dictionary-runtime.test.ts
|
||||||
priority: high
|
priority: high
|
||||||
|
ordinal: 71500
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ status: Done
|
|||||||
assignee:
|
assignee:
|
||||||
- codex
|
- codex
|
||||||
created_date: '2026-03-06 21:20'
|
created_date: '2026-03-06 21:20'
|
||||||
updated_date: '2026-03-06 21:33'
|
updated_date: '2026-03-16 05:13'
|
||||||
labels: []
|
labels: []
|
||||||
dependencies: []
|
dependencies: []
|
||||||
references:
|
references:
|
||||||
@@ -13,6 +13,7 @@ references:
|
|||||||
- /home/sudacode/projects/japanese/SubMiner/src/core/services/cli-command.ts
|
- /home/sudacode/projects/japanese/SubMiner/src/core/services/cli-command.ts
|
||||||
- /home/sudacode/projects/japanese/SubMiner/src/main.ts
|
- /home/sudacode/projects/japanese/SubMiner/src/main.ts
|
||||||
priority: medium
|
priority: medium
|
||||||
|
ordinal: 77500
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
@@ -29,7 +30,6 @@ Scope:
|
|||||||
<!-- SECTION:DESCRIPTION:END -->
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
## Acceptance Criteria
|
## Acceptance Criteria
|
||||||
|
|
||||||
<!-- AC:BEGIN -->
|
<!-- AC:BEGIN -->
|
||||||
- [x] #1 Initial background launch reaches the start path without logging `No running instance. Use --start to launch the app.`
|
- [x] #1 Initial background launch reaches the start path without logging `No running instance. Use --start to launch the app.`
|
||||||
- [x] #2 Default startup no longer emits the `Applied --password-store gnome-libsecret` line at normal log levels.
|
- [x] #2 Default startup no longer emits the `Applied --password-store gnome-libsecret` line at normal log levels.
|
||||||
|
|||||||
@@ -5,13 +5,14 @@ status: Done
|
|||||||
assignee:
|
assignee:
|
||||||
- codex
|
- codex
|
||||||
created_date: '2026-03-07 02:20'
|
created_date: '2026-03-07 02:20'
|
||||||
updated_date: '2026-03-07 02:20'
|
updated_date: '2026-03-16 05:13'
|
||||||
labels:
|
labels:
|
||||||
- texthooker
|
- texthooker
|
||||||
- websocket
|
- websocket
|
||||||
- subtitle
|
- subtitle
|
||||||
dependencies: []
|
dependencies: []
|
||||||
priority: medium
|
priority: medium
|
||||||
|
ordinal: 73500
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
@@ -21,7 +22,6 @@ Add a separate annotated subtitle websocket for bundled texthooker so token/JLPT
|
|||||||
<!-- SECTION:DESCRIPTION:END -->
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
## Acceptance Criteria
|
## Acceptance Criteria
|
||||||
|
|
||||||
<!-- AC:BEGIN -->
|
<!-- AC:BEGIN -->
|
||||||
- [x] #1 Regular `websocket.enabled: "auto"` behavior remains unchanged and still skips the regular websocket when `mpv_websocket` is installed.
|
- [x] #1 Regular `websocket.enabled: "auto"` behavior remains unchanged and still skips the regular websocket when `mpv_websocket` is installed.
|
||||||
- [x] #2 A separate `annotationWebsocket` config controls an independent annotated websocket with default port `6678`.
|
- [x] #2 A separate `annotationWebsocket` config controls an independent annotated websocket with default port `6678`.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ status: Done
|
|||||||
assignee:
|
assignee:
|
||||||
- codex
|
- codex
|
||||||
created_date: '2026-03-06 21:45'
|
created_date: '2026-03-06 21:45'
|
||||||
updated_date: '2026-03-06 21:45'
|
updated_date: '2026-03-16 05:13'
|
||||||
labels:
|
labels:
|
||||||
- texthooker
|
- texthooker
|
||||||
- subtitle
|
- subtitle
|
||||||
@@ -14,10 +14,13 @@ dependencies:
|
|||||||
- TASK-103
|
- TASK-103
|
||||||
references:
|
references:
|
||||||
- /home/sudacode/projects/japanese/SubMiner/src/core/services/subtitle-ws.ts
|
- /home/sudacode/projects/japanese/SubMiner/src/core/services/subtitle-ws.ts
|
||||||
- /home/sudacode/projects/japanese/SubMiner/vendor/texthooker-ui/src/components/App.svelte
|
- >-
|
||||||
- /home/sudacode/projects/japanese/SubMiner/vendor/texthooker-ui/src/line-markup.ts
|
/home/sudacode/projects/japanese/SubMiner/vendor/texthooker-ui/src/components/App.svelte
|
||||||
|
- >-
|
||||||
|
/home/sudacode/projects/japanese/SubMiner/vendor/texthooker-ui/src/line-markup.ts
|
||||||
- /home/sudacode/projects/japanese/SubMiner/vendor/texthooker-ui/src/app.css
|
- /home/sudacode/projects/japanese/SubMiner/vendor/texthooker-ui/src/app.css
|
||||||
priority: medium
|
priority: medium
|
||||||
|
ordinal: 76500
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
@@ -27,7 +30,6 @@ Bring bundled texthooker annotation rendering closer to the visible overlay. Kee
|
|||||||
<!-- SECTION:DESCRIPTION:END -->
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
## Acceptance Criteria
|
## Acceptance Criteria
|
||||||
|
|
||||||
<!-- AC:BEGIN -->
|
<!-- AC:BEGIN -->
|
||||||
- [x] #1 Annotation websocket payload includes both rendered `sentence` HTML and structured token metadata for generic clients.
|
- [x] #1 Annotation websocket payload includes both rendered `sentence` HTML and structured token metadata for generic clients.
|
||||||
- [x] #2 Vendored texthooker preserves annotation metadata attrs needed for hover labels and uses overlay-matching color precedence rules.
|
- [x] #2 Vendored texthooker preserves annotation metadata attrs needed for hover labels and uses overlay-matching color precedence rules.
|
||||||
|
|||||||
@@ -4,17 +4,16 @@ title: Stop local docs artifact writes after docs repo split
|
|||||||
status: Done
|
status: Done
|
||||||
assignee: []
|
assignee: []
|
||||||
created_date: '2026-03-07 00:00'
|
created_date: '2026-03-07 00:00'
|
||||||
updated_date: '2026-03-07 00:20'
|
updated_date: '2026-03-16 05:13'
|
||||||
labels: []
|
labels: []
|
||||||
dependencies: []
|
dependencies: []
|
||||||
priority: medium
|
priority: medium
|
||||||
ordinal: 10500
|
ordinal: 12010
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
|
||||||
Now that user-facing docs live in `../subminer-docs`, first-party scripts in this repo should not keep writing generated artifacts into the local `docs/` tree.
|
Now that user-facing docs live in `../subminer-docs`, first-party scripts in this repo should not keep writing generated artifacts into the local `docs/` tree.
|
||||||
|
|
||||||
Scope:
|
Scope:
|
||||||
@@ -23,25 +22,19 @@ Scope:
|
|||||||
- Keep repo-local outputs only where they are still intentionally owned by this repo.
|
- Keep repo-local outputs only where they are still intentionally owned by this repo.
|
||||||
- Repoint generated docs artifacts to `../subminer-docs` when that is the maintained source of truth.
|
- Repoint generated docs artifacts to `../subminer-docs` when that is the maintained source of truth.
|
||||||
- Add regression coverage for the config-example generation path contract.
|
- Add regression coverage for the config-example generation path contract.
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:END -->
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
## Acceptance Criteria
|
## Acceptance Criteria
|
||||||
|
|
||||||
<!-- AC:BEGIN -->
|
<!-- AC:BEGIN -->
|
||||||
|
|
||||||
- [x] #1 The config-example generator no longer writes to `docs/public/config.example.jsonc` inside this repo.
|
- [x] #1 The config-example generator no longer writes to `docs/public/config.example.jsonc` inside this repo.
|
||||||
- [x] #2 When `../subminer-docs` exists, the generator updates `../subminer-docs/public/config.example.jsonc`.
|
- [x] #2 When `../subminer-docs` exists, the generator updates `../subminer-docs/public/config.example.jsonc`.
|
||||||
- [x] #3 Automated coverage guards the output-path contract so local docs writes do not regress.
|
- [x] #3 Automated coverage guards the output-path contract so local docs writes do not regress.
|
||||||
|
|
||||||
<!-- AC:END -->
|
<!-- AC:END -->
|
||||||
|
|
||||||
## Final Summary
|
## Final Summary
|
||||||
|
|
||||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||||
|
|
||||||
Removed the first-party local `docs/public` config-example write path from `src/generate-config-example.ts` and replaced it with sibling-docs-repo detection that targets `../subminer-docs/public/config.example.jsonc` only when that repo exists.
|
Removed the first-party local `docs/public` config-example write path from `src/generate-config-example.ts` and replaced it with sibling-docs-repo detection that targets `../subminer-docs/public/config.example.jsonc` only when that repo exists.
|
||||||
|
|
||||||
Added a project-local regression suite for output-path resolution and artifact writing, wired that suite into the maintained config test lane, and removed the stale generated `docs/public/config.example.jsonc` artifact from the working tree.
|
Added a project-local regression suite for output-path resolution and artifact writing, wired that suite into the maintained config test lane, and removed the stale generated `docs/public/config.example.jsonc` artifact from the working tree.
|
||||||
|
|
||||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||||
|
|||||||
@@ -5,17 +5,20 @@ status: Done
|
|||||||
assignee:
|
assignee:
|
||||||
- codex
|
- codex
|
||||||
created_date: '2026-03-07 06:10'
|
created_date: '2026-03-07 06:10'
|
||||||
updated_date: '2026-03-07 06:20'
|
updated_date: '2026-03-16 05:13'
|
||||||
labels: []
|
labels: []
|
||||||
dependencies: []
|
dependencies: []
|
||||||
references:
|
references:
|
||||||
- /home/sudacode/projects/japanese/SubMiner/src/main.ts
|
- /home/sudacode/projects/japanese/SubMiner/src/main.ts
|
||||||
- /home/sudacode/projects/japanese/SubMiner/src/shared/setup-state.ts
|
- /home/sudacode/projects/japanese/SubMiner/src/shared/setup-state.ts
|
||||||
- /home/sudacode/projects/japanese/SubMiner/src/main/runtime/first-run-setup-service.ts
|
- >-
|
||||||
- /home/sudacode/projects/japanese/SubMiner/src/main/runtime/first-run-setup-window.ts
|
/home/sudacode/projects/japanese/SubMiner/src/main/runtime/first-run-setup-service.ts
|
||||||
- /home/sudacode/projects/japanese/SubMiner/launcher/commands/playback-command.ts
|
- >-
|
||||||
|
/home/sudacode/projects/japanese/SubMiner/src/main/runtime/first-run-setup-window.ts
|
||||||
|
- >-
|
||||||
|
/home/sudacode/projects/japanese/SubMiner/launcher/commands/playback-command.ts
|
||||||
priority: high
|
priority: high
|
||||||
ordinal: 10600
|
ordinal: 13010
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
@@ -31,7 +34,6 @@ Replace the current manual install flow with a first-run setup gate:
|
|||||||
<!-- SECTION:DESCRIPTION:END -->
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
## Acceptance Criteria
|
## Acceptance Criteria
|
||||||
|
|
||||||
<!-- AC:BEGIN -->
|
<!-- AC:BEGIN -->
|
||||||
- [x] #1 First app launch seeds the default config dir/config file without manual copy steps.
|
- [x] #1 First app launch seeds the default config dir/config file without manual copy steps.
|
||||||
- [x] #2 Existing installs with config plus at least one Yomitan dictionary are auto-detected as already complete.
|
- [x] #2 Existing installs with config plus at least one Yomitan dictionary are auto-detected as already complete.
|
||||||
|
|||||||
@@ -4,40 +4,34 @@ title: Replace vendored Yomitan with submodule-built Chrome artifact workflow
|
|||||||
status: Done
|
status: Done
|
||||||
assignee: []
|
assignee: []
|
||||||
created_date: '2026-03-07 11:05'
|
created_date: '2026-03-07 11:05'
|
||||||
updated_date: '2026-03-07 11:22'
|
updated_date: '2026-03-16 05:13'
|
||||||
labels:
|
labels:
|
||||||
- yomitan
|
- yomitan
|
||||||
- build
|
- build
|
||||||
- release
|
- release
|
||||||
dependencies: []
|
dependencies: []
|
||||||
priority: high
|
priority: high
|
||||||
ordinal: 9010
|
ordinal: 10010
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
|
||||||
Replace the checked-in `vendor/yomitan` release tree with a `subminer-yomitan` git submodule. Build Yomitan from source, extract the Chromium zip artifact into a stable local build directory, and make SubMiner dev/runtime/tests/release packaging load that extracted extension instead of the source tree or vendored files.
|
Replace the checked-in `vendor/yomitan` release tree with a `subminer-yomitan` git submodule. Build Yomitan from source, extract the Chromium zip artifact into a stable local build directory, and make SubMiner dev/runtime/tests/release packaging load that extracted extension instead of the source tree or vendored files.
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:END -->
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
## Acceptance Criteria
|
## Acceptance Criteria
|
||||||
|
|
||||||
<!-- AC:BEGIN -->
|
<!-- AC:BEGIN -->
|
||||||
|
|
||||||
- [x] #1 Repo tracks Yomitan as a git submodule instead of committed extension files under `vendor/yomitan`.
|
- [x] #1 Repo tracks Yomitan as a git submodule instead of committed extension files under `vendor/yomitan`.
|
||||||
- [x] #2 SubMiner has a reproducible build/extract step that produces a local Chromium extension directory from `subminer-yomitan`.
|
- [x] #2 SubMiner has a reproducible build/extract step that produces a local Chromium extension directory from `subminer-yomitan`.
|
||||||
- [x] #3 Dev/runtime/tests resolve the extracted build output as the default Yomitan extension path.
|
- [x] #3 Dev/runtime/tests resolve the extracted build output as the default Yomitan extension path.
|
||||||
- [x] #4 Release packaging includes the extracted Chromium extension files instead of the old vendored tree.
|
- [x] #4 Release packaging includes the extracted Chromium extension files instead of the old vendored tree.
|
||||||
- [x] #5 Docs and verification commands reflect the new workflow.
|
- [x] #5 Docs and verification commands reflect the new workflow.
|
||||||
|
|
||||||
<!-- AC:END -->
|
<!-- AC:END -->
|
||||||
|
|
||||||
## Final Summary
|
## Final Summary
|
||||||
|
|
||||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||||
|
|
||||||
Replaced the checked-in `vendor/yomitan` extension tree with a `vendor/subminer-yomitan` git submodule and added a reproducible `bun run build:yomitan` workflow that builds `yomitan-chrome.zip`, extracts it into `build/yomitan`, and reuses a source-state stamp to skip redundant rebuilds. Runtime path resolution, helper CLIs, Yomitan integration tests, packaging, CI cache keys, and README source-build notes now all target that generated artifact instead of the old vendored files.
|
Replaced the checked-in `vendor/yomitan` extension tree with a `vendor/subminer-yomitan` git submodule and added a reproducible `bun run build:yomitan` workflow that builds `yomitan-chrome.zip`, extracts it into `build/yomitan`, and reuses a source-state stamp to skip redundant rebuilds. Runtime path resolution, helper CLIs, Yomitan integration tests, packaging, CI cache keys, and README source-build notes now all target that generated artifact instead of the old vendored files.
|
||||||
|
|
||||||
Verification:
|
Verification:
|
||||||
@@ -47,5 +41,4 @@ Verification:
|
|||||||
- `bun run typecheck`
|
- `bun run typecheck`
|
||||||
- `bun run build`
|
- `bun run build`
|
||||||
- `bun run test:core:src`
|
- `bun run test:core:src`
|
||||||
|
|
||||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||||
@@ -5,7 +5,7 @@ status: Done
|
|||||||
assignee:
|
assignee:
|
||||||
- Codex
|
- Codex
|
||||||
created_date: '2026-03-07 23:45'
|
created_date: '2026-03-07 23:45'
|
||||||
updated_date: '2026-03-08 00:06'
|
updated_date: '2026-03-16 05:13'
|
||||||
labels: []
|
labels: []
|
||||||
dependencies: []
|
dependencies: []
|
||||||
references:
|
references:
|
||||||
@@ -15,6 +15,7 @@ references:
|
|||||||
/Users/sudacode/projects/japanese/SubMiner/src/core/services/ipc-command.test.ts
|
/Users/sudacode/projects/japanese/SubMiner/src/core/services/ipc-command.test.ts
|
||||||
- >-
|
- >-
|
||||||
/Users/sudacode/projects/japanese/SubMiner/src/core/services/mpv-control.test.ts
|
/Users/sudacode/projects/japanese/SubMiner/src/core/services/mpv-control.test.ts
|
||||||
|
ordinal: 72500
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ status: Done
|
|||||||
assignee:
|
assignee:
|
||||||
- codex
|
- codex
|
||||||
created_date: '2026-03-08 00:11'
|
created_date: '2026-03-08 00:11'
|
||||||
updated_date: '2026-03-08 00:12'
|
updated_date: '2026-03-16 05:13'
|
||||||
labels:
|
labels:
|
||||||
- pr-review
|
- pr-review
|
||||||
- ci
|
- ci
|
||||||
@@ -18,6 +18,7 @@ references:
|
|||||||
backlog/tasks/task-101 -
|
backlog/tasks/task-101 -
|
||||||
Index-AniList-character-alternative-names-in-the-character-dictionary.md
|
Index-AniList-character-alternative-names-in-the-character-dictionary.md
|
||||||
priority: medium
|
priority: medium
|
||||||
|
ordinal: 70500
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ status: Done
|
|||||||
assignee:
|
assignee:
|
||||||
- codex
|
- codex
|
||||||
created_date: '2026-03-08 00:20'
|
created_date: '2026-03-08 00:20'
|
||||||
updated_date: '2026-03-08 00:22'
|
updated_date: '2026-03-16 05:13'
|
||||||
labels:
|
labels:
|
||||||
- tooling
|
- tooling
|
||||||
- formatting
|
- formatting
|
||||||
@@ -14,6 +14,7 @@ references:
|
|||||||
- Makefile
|
- Makefile
|
||||||
- package.json
|
- package.json
|
||||||
priority: medium
|
priority: medium
|
||||||
|
ordinal: 69500
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ status: Done
|
|||||||
assignee:
|
assignee:
|
||||||
- codex
|
- codex
|
||||||
created_date: '2026-03-08 00:34'
|
created_date: '2026-03-08 00:34'
|
||||||
updated_date: '2026-03-08 00:37'
|
updated_date: '2026-03-16 05:13'
|
||||||
labels:
|
labels:
|
||||||
- ci
|
- ci
|
||||||
- test
|
- test
|
||||||
@@ -15,6 +15,7 @@ references:
|
|||||||
- src/renderer/style.css
|
- src/renderer/style.css
|
||||||
- .github/workflows/ci.yml
|
- .github/workflows/ci.yml
|
||||||
priority: high
|
priority: high
|
||||||
|
ordinal: 68500
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ status: Done
|
|||||||
assignee:
|
assignee:
|
||||||
- codex
|
- codex
|
||||||
created_date: '2026-03-08 00:40'
|
created_date: '2026-03-08 00:40'
|
||||||
updated_date: '2026-03-08 00:42'
|
updated_date: '2026-03-16 05:13'
|
||||||
labels:
|
labels:
|
||||||
- docs
|
- docs
|
||||||
dependencies: []
|
dependencies: []
|
||||||
@@ -15,6 +15,7 @@ references:
|
|||||||
- Makefile
|
- Makefile
|
||||||
- package.json
|
- package.json
|
||||||
priority: medium
|
priority: medium
|
||||||
|
ordinal: 67500
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ status: Done
|
|||||||
assignee:
|
assignee:
|
||||||
- codex
|
- codex
|
||||||
created_date: '2026-03-08 00:46'
|
created_date: '2026-03-08 00:46'
|
||||||
updated_date: '2026-03-08 00:48'
|
updated_date: '2026-03-16 05:13'
|
||||||
labels:
|
labels:
|
||||||
- docs
|
- docs
|
||||||
dependencies: []
|
dependencies: []
|
||||||
@@ -15,6 +15,7 @@ references:
|
|||||||
- src/core/services/yomitan-extension-paths.ts
|
- src/core/services/yomitan-extension-paths.ts
|
||||||
- scripts/build-yomitan.mjs
|
- scripts/build-yomitan.mjs
|
||||||
priority: medium
|
priority: medium
|
||||||
|
ordinal: 66500
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ status: Done
|
|||||||
assignee:
|
assignee:
|
||||||
- '@codex'
|
- '@codex'
|
||||||
created_date: '2026-03-08 08:22'
|
created_date: '2026-03-08 08:22'
|
||||||
updated_date: '2026-03-08 08:25'
|
updated_date: '2026-03-16 05:13'
|
||||||
labels: []
|
labels: []
|
||||||
dependencies: []
|
dependencies: []
|
||||||
references:
|
references:
|
||||||
@@ -15,6 +15,7 @@ references:
|
|||||||
- >-
|
- >-
|
||||||
/Users/sudacode/projects/japanese/SubMiner/launcher/youtube/subtitle-fix-ai.test.ts
|
/Users/sudacode/projects/japanese/SubMiner/launcher/youtube/subtitle-fix-ai.test.ts
|
||||||
parent_task_id: TASK-117
|
parent_task_id: TASK-117
|
||||||
|
ordinal: 59500
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
@@ -44,7 +45,7 @@ Prevent optional YouTube AI subtitle post-processing from bailing out whenever t
|
|||||||
<!-- SECTION:NOTES:BEGIN -->
|
<!-- SECTION:NOTES:BEGIN -->
|
||||||
Implemented deterministic AI subtitle-response recovery for fenced SRT, embedded SRT payloads, and text-only 1:1 cue batches while preserving original timing and existing fallback behavior.
|
Implemented deterministic AI subtitle-response recovery for fenced SRT, embedded SRT payloads, and text-only 1:1 cue batches while preserving original timing and existing fallback behavior.
|
||||||
|
|
||||||
Verification: bun test launcher/youtube/*.test.ts passed; bun run typecheck passed; repo-wide format check still reports unrelated pre-existing warnings in launcher/youtube/orchestrator.ts and scripts/build-changelog*.
|
Verification: bun test launcher/youtube/_.test.ts passed; bun run typecheck passed; repo-wide format check still reports unrelated pre-existing warnings in launcher/youtube/orchestrator.ts and scripts/build-changelog_.
|
||||||
<!-- SECTION:NOTES:END -->
|
<!-- SECTION:NOTES:END -->
|
||||||
|
|
||||||
## Final Summary
|
## Final Summary
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ title: Add Jellyfin remote-session subtitle streaming to texthooker
|
|||||||
status: To Do
|
status: To Do
|
||||||
assignee: []
|
assignee: []
|
||||||
created_date: '2026-03-08 03:46'
|
created_date: '2026-03-08 03:46'
|
||||||
|
updated_date: '2026-03-18 05:27'
|
||||||
labels:
|
labels:
|
||||||
- jellyfin
|
- jellyfin
|
||||||
- texthooker
|
- texthooker
|
||||||
@@ -19,6 +20,7 @@ references:
|
|||||||
documentation:
|
documentation:
|
||||||
- 'https://api.jellyfin.org/'
|
- 'https://api.jellyfin.org/'
|
||||||
priority: medium
|
priority: medium
|
||||||
|
ordinal: 1000
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ title: 'Replace node:sqlite with libsql and remove Yomitan Node wrapper'
|
|||||||
status: Done
|
status: Done
|
||||||
assignee: []
|
assignee: []
|
||||||
created_date: '2026-03-08 04:14'
|
created_date: '2026-03-08 04:14'
|
||||||
updated_date: '2026-03-08 04:39'
|
updated_date: '2026-03-16 05:13'
|
||||||
labels:
|
labels:
|
||||||
- runtime
|
- runtime
|
||||||
- bun
|
- bun
|
||||||
@@ -12,6 +12,7 @@ labels:
|
|||||||
- tech-debt
|
- tech-debt
|
||||||
dependencies: []
|
dependencies: []
|
||||||
priority: medium
|
priority: medium
|
||||||
|
ordinal: 65500
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ status: Done
|
|||||||
assignee:
|
assignee:
|
||||||
- '@codex'
|
- '@codex'
|
||||||
created_date: '2026-03-08 05:37'
|
created_date: '2026-03-08 05:37'
|
||||||
updated_date: '2026-03-08 05:42'
|
updated_date: '2026-03-16 05:13'
|
||||||
labels:
|
labels:
|
||||||
- bug
|
- bug
|
||||||
- youtube
|
- youtube
|
||||||
@@ -18,6 +18,7 @@ references:
|
|||||||
- /Users/sudacode/projects/japanese/SubMiner/launcher/youtube/orchestrator.ts
|
- /Users/sudacode/projects/japanese/SubMiner/launcher/youtube/orchestrator.ts
|
||||||
- 'https://www.youtube.com/watch?v=MXzQRLmN9hE'
|
- 'https://www.youtube.com/watch?v=MXzQRLmN9hE'
|
||||||
priority: high
|
priority: high
|
||||||
|
ordinal: 64500
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ status: Done
|
|||||||
assignee:
|
assignee:
|
||||||
- Codex
|
- Codex
|
||||||
created_date: '2026-03-08 06:13'
|
created_date: '2026-03-08 06:13'
|
||||||
updated_date: '2026-03-08 06:28'
|
updated_date: '2026-03-16 05:13'
|
||||||
labels:
|
labels:
|
||||||
- release
|
- release
|
||||||
- changelog
|
- changelog
|
||||||
@@ -19,6 +19,7 @@ references:
|
|||||||
- /Users/sudacode/projects/japanese/SubMiner/docs/RELEASING.md
|
- /Users/sudacode/projects/japanese/SubMiner/docs/RELEASING.md
|
||||||
- /Users/sudacode/projects/japanese/SubMiner/changes/README.md
|
- /Users/sudacode/projects/japanese/SubMiner/changes/README.md
|
||||||
priority: medium
|
priority: medium
|
||||||
|
ordinal: 63500
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ status: Done
|
|||||||
assignee:
|
assignee:
|
||||||
- '@codex'
|
- '@codex'
|
||||||
created_date: '2026-03-08 07:07'
|
created_date: '2026-03-08 07:07'
|
||||||
updated_date: '2026-03-08 07:15'
|
updated_date: '2026-03-16 05:13'
|
||||||
labels:
|
labels:
|
||||||
- ux
|
- ux
|
||||||
- logging
|
- logging
|
||||||
@@ -20,6 +20,7 @@ references:
|
|||||||
- >-
|
- >-
|
||||||
/Users/sudacode/projects/japanese/SubMiner/launcher/youtube/subtitle-fix-ai.ts
|
/Users/sudacode/projects/japanese/SubMiner/launcher/youtube/subtitle-fix-ai.ts
|
||||||
priority: medium
|
priority: medium
|
||||||
|
ordinal: 62500
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ status: Done
|
|||||||
assignee:
|
assignee:
|
||||||
- codex
|
- codex
|
||||||
created_date: '2026-03-08 07:18'
|
created_date: '2026-03-08 07:18'
|
||||||
updated_date: '2026-03-08 07:28'
|
updated_date: '2026-03-16 05:13'
|
||||||
labels:
|
labels:
|
||||||
- launcher
|
- launcher
|
||||||
- youtube
|
- youtube
|
||||||
@@ -27,6 +27,7 @@ references:
|
|||||||
- >-
|
- >-
|
||||||
/Users/sudacode/projects/japanese/SubMiner/src/config/resolve/subtitle-domains.ts
|
/Users/sudacode/projects/japanese/SubMiner/src/config/resolve/subtitle-domains.ts
|
||||||
priority: high
|
priority: high
|
||||||
|
ordinal: 61500
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ title: Add native AI API key secret storage
|
|||||||
status: To Do
|
status: To Do
|
||||||
assignee: []
|
assignee: []
|
||||||
created_date: '2026-03-08 07:25'
|
created_date: '2026-03-08 07:25'
|
||||||
|
updated_date: '2026-03-18 05:27'
|
||||||
labels:
|
labels:
|
||||||
- ai
|
- ai
|
||||||
- config
|
- config
|
||||||
@@ -17,6 +18,7 @@ references:
|
|||||||
/Users/sudacode/projects/japanese/SubMiner/src/core/services/jellyfin-token-store.ts
|
/Users/sudacode/projects/japanese/SubMiner/src/core/services/jellyfin-token-store.ts
|
||||||
- /Users/sudacode/projects/japanese/SubMiner/src/main.ts
|
- /Users/sudacode/projects/japanese/SubMiner/src/main.ts
|
||||||
priority: medium
|
priority: medium
|
||||||
|
ordinal: 2000
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|||||||
@@ -6,13 +6,14 @@ title: >-
|
|||||||
status: Done
|
status: Done
|
||||||
assignee: []
|
assignee: []
|
||||||
created_date: '2026-03-08 07:35'
|
created_date: '2026-03-08 07:35'
|
||||||
updated_date: '2026-03-08 07:40'
|
updated_date: '2026-03-16 05:13'
|
||||||
labels:
|
labels:
|
||||||
- overlay
|
- overlay
|
||||||
- subtitles
|
- subtitles
|
||||||
- ui
|
- ui
|
||||||
dependencies: []
|
dependencies: []
|
||||||
priority: medium
|
priority: medium
|
||||||
|
ordinal: 60500
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ status: Done
|
|||||||
assignee:
|
assignee:
|
||||||
- '@codex'
|
- '@codex'
|
||||||
created_date: '2026-03-08 08:24'
|
created_date: '2026-03-08 08:24'
|
||||||
updated_date: '2026-03-08 10:12'
|
updated_date: '2026-03-16 05:13'
|
||||||
labels:
|
labels:
|
||||||
- bug
|
- bug
|
||||||
- launcher
|
- launcher
|
||||||
@@ -16,6 +16,7 @@ references:
|
|||||||
- >-
|
- >-
|
||||||
/Users/sudacode/projects/japanese/SubMiner/launcher/commands/playback-command.ts
|
/Users/sudacode/projects/japanese/SubMiner/launcher/commands/playback-command.ts
|
||||||
- /Users/sudacode/projects/japanese/SubMiner/launcher/mpv.test.ts
|
- /Users/sudacode/projects/japanese/SubMiner/launcher/mpv.test.ts
|
||||||
|
ordinal: 56500
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|||||||
@@ -6,13 +6,14 @@ title: >-
|
|||||||
status: Done
|
status: Done
|
||||||
assignee: []
|
assignee: []
|
||||||
created_date: '2026-03-08 09:02'
|
created_date: '2026-03-08 09:02'
|
||||||
updated_date: '2026-03-08 09:17'
|
updated_date: '2026-03-16 05:13'
|
||||||
labels:
|
labels:
|
||||||
- bug
|
- bug
|
||||||
- youtube-subgen
|
- youtube-subgen
|
||||||
- ai
|
- ai
|
||||||
dependencies: []
|
dependencies: []
|
||||||
priority: high
|
priority: high
|
||||||
|
ordinal: 58500
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ title: >-
|
|||||||
status: Done
|
status: Done
|
||||||
assignee: []
|
assignee: []
|
||||||
created_date: '2026-03-08 09:40'
|
created_date: '2026-03-08 09:40'
|
||||||
updated_date: '2026-03-08 09:57'
|
updated_date: '2026-03-16 05:13'
|
||||||
labels:
|
labels:
|
||||||
- config
|
- config
|
||||||
- ai
|
- ai
|
||||||
@@ -14,6 +14,7 @@ labels:
|
|||||||
- youtube-subgen
|
- youtube-subgen
|
||||||
dependencies: []
|
dependencies: []
|
||||||
priority: high
|
priority: high
|
||||||
|
ordinal: 57500
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ status: Done
|
|||||||
assignee:
|
assignee:
|
||||||
- codex
|
- codex
|
||||||
created_date: '2026-03-08 10:08'
|
created_date: '2026-03-08 10:08'
|
||||||
updated_date: '2026-03-08 11:00'
|
updated_date: '2026-03-16 05:13'
|
||||||
labels:
|
labels:
|
||||||
- bug
|
- bug
|
||||||
- launcher
|
- launcher
|
||||||
@@ -13,6 +13,7 @@ labels:
|
|||||||
- overlay
|
- overlay
|
||||||
dependencies: []
|
dependencies: []
|
||||||
priority: high
|
priority: high
|
||||||
|
ordinal: 55500
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
@@ -67,6 +68,7 @@ Net effect: background SubMiner can stay alive, keep its warm state, and reconne
|
|||||||
Coverage now includes: launcher playback cleanup (`launcher/mpv.test.ts`), launcher smoke reconnect/keep-alive flow (`launcher/smoke.e2e.test.ts`), mpv plugin shutdown preservation (`scripts/test-plugin-start-gate.lua`), and second-instance start/reconnect behavior (`src/core/services/cli-command.test.ts`).
|
Coverage now includes: launcher playback cleanup (`launcher/mpv.test.ts`), launcher smoke reconnect/keep-alive flow (`launcher/smoke.e2e.test.ts`), mpv plugin shutdown preservation (`scripts/test-plugin-start-gate.lua`), and second-instance start/reconnect behavior (`src/core/services/cli-command.test.ts`).
|
||||||
|
|
||||||
Tests run:
|
Tests run:
|
||||||
|
|
||||||
- `bun test src/core/services/cli-command.test.ts launcher/mpv.test.ts launcher/smoke.e2e.test.ts`
|
- `bun test src/core/services/cli-command.test.ts launcher/mpv.test.ts launcher/smoke.e2e.test.ts`
|
||||||
- `lua scripts/test-plugin-start-gate.lua`
|
- `lua scripts/test-plugin-start-gate.lua`
|
||||||
- `bun run typecheck`
|
- `bun run typecheck`
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
---
|
|
||||||
id: TASK-131
|
|
||||||
title: Avoid duplicate tokenization warmup after background startup
|
|
||||||
status: Done
|
|
||||||
assignee:
|
|
||||||
- codex
|
|
||||||
created_date: '2026-03-08 10:12'
|
|
||||||
updated_date: '2026-03-08 12:00'
|
|
||||||
labels:
|
|
||||||
- bug
|
|
||||||
dependencies: []
|
|
||||||
references:
|
|
||||||
- >-
|
|
||||||
/Users/sudacode/projects/japanese/SubMiner/src/main/runtime/composers/mpv-runtime-composer.ts
|
|
||||||
- >-
|
|
||||||
/Users/sudacode/projects/japanese/SubMiner/src/main/runtime/startup-warmups.ts
|
|
||||||
- >-
|
|
||||||
/Users/sudacode/projects/japanese/SubMiner/src/main/runtime/composers/mpv-runtime-composer.test.ts
|
|
||||||
priority: medium
|
|
||||||
---
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
|
||||||
When SubMiner is already running in the background and mpv is launched from the launcher or mpv plugin, the live app should reuse startup tokenization warmup state instead of re-entering the Yomitan/tokenization/annotation warmup path on first overlay use.
|
|
||||||
<!-- SECTION:DESCRIPTION:END -->
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
<!-- AC:BEGIN -->
|
|
||||||
- [x] #1 Background startup tokenization warmup is recorded in the runtime state used by later mpv/tokenization flows.
|
|
||||||
- [x] #2 Launching mpv from the launcher or plugin against an already-running background app does not re-run duplicate Yomitan/tokenization annotation warmup work in the live process.
|
|
||||||
- [x] #3 Regression tests cover the warmed-background path and protect against re-entering duplicate warmup work.
|
|
||||||
<!-- AC:END -->
|
|
||||||
|
|
||||||
## Implementation Plan
|
|
||||||
|
|
||||||
<!-- SECTION:PLAN:BEGIN -->
|
|
||||||
1. Add a regression test covering the case where background startup warmups already completed and a later tokenize call must not re-enter Yomitan/MeCab/dictionary warmups.
|
|
||||||
2. Update mpv tokenization warmup composition so startup background warmups and on-demand tokenization share the same completion state.
|
|
||||||
3. Run the focused composer/runtime tests and update acceptance criteria/notes with results.
|
|
||||||
<!-- SECTION:PLAN:END -->
|
|
||||||
|
|
||||||
## Implementation Notes
|
|
||||||
|
|
||||||
<!-- SECTION:NOTES:BEGIN -->
|
|
||||||
Root-cause hypothesis: startup background warmups and on-demand tokenization warmups use separate state, so later mpv launch can re-enter warmup bookkeeping even though background startup already warmed dependencies.
|
|
||||||
|
|
||||||
Implemented shared warmup state between startup background warmups and on-demand tokenization warmups by forwarding scheduled Yomitan/tokenization promises into the mpv runtime composer. Added regression coverage for the warmed-background path. Verified with `bun run test:fast` plus focused composer/startup warmup tests.
|
|
||||||
|
|
||||||
Follow-up root cause from live retest: second mpv open could still pause on the startup gate because the runtime only treated full background tokenization warmup completion as reusable readiness. In practice, first-file tokenization could already be ready while slower dictionary prewarm work was still finishing, so reopening a video waited on duplicate warmup completion even though annotations were already usable.
|
|
||||||
|
|
||||||
Adjusted `src/main/runtime/composers/mpv-runtime-composer.ts` so autoplay reuse keys off a separate playback-ready latch. The latch flips true either when background warmups fully cover tokenization or when `onTokenizationReady` fires for a real subtitle line. `src/main.ts` already uses `isTokenizationWarmupReady()` to fast-signal `subminer-autoplay-ready` on a fresh media-path change, so reopened videos can now resume immediately once tokenization has succeeded once in the persistent app.
|
|
||||||
|
|
||||||
Validation update: `bun test src/core/services/cli-command.test.ts src/main/runtime/mpv-main-event-actions.test.ts src/main/runtime/composers/mpv-runtime-composer.test.ts launcher/mpv.test.ts launcher/smoke.e2e.test.ts` passed, `lua scripts/test-plugin-start-gate.lua` passed, and `bun run typecheck` passed.
|
|
||||||
<!-- SECTION:NOTES:END -->
|
|
||||||
|
|
||||||
## Final Summary
|
|
||||||
|
|
||||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
|
||||||
Background startup tokenization warmups now feed the same in-memory warmup state used by later mpv tokenization. When the app is already running and warmed in the background, launcher/plugin-driven mpv startup reuses that state instead of re-entering Yomitan/tokenization annotation warmups. Added a regression test for the warmed-background path and verified with `bun run test:fast`.
|
|
||||||
|
|
||||||
A later follow-up fixed the remaining second-open delay: autoplay reuse no longer waits for the entire background dictionary warmup pipeline to finish. After the persistent app has produced one tokenization-ready event, later mpv reconnects reuse that readiness immediately, so reopening the same or another video does not pause again on duplicate warmup bookkeeping.
|
|
||||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
---
|
||||||
|
id: TASK-131
|
||||||
|
title: >-
|
||||||
|
Make default overlay fullscreen and AniSkip end-jump keybindings easier to
|
||||||
|
reach
|
||||||
|
status: Done
|
||||||
|
assignee:
|
||||||
|
- codex
|
||||||
|
created_date: '2026-03-09 00:00'
|
||||||
|
updated_date: '2026-03-18 05:28'
|
||||||
|
labels:
|
||||||
|
- enhancement
|
||||||
|
- overlay
|
||||||
|
- mpv
|
||||||
|
- aniskip
|
||||||
|
dependencies: []
|
||||||
|
ordinal: 43500
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
Make two default keyboard actions easier to hit during playback: add `f` as the built-in overlay fullscreen toggle, and make AniSkip's default intro-end jump use `Tab`.
|
||||||
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
<!-- AC:BEGIN -->
|
||||||
|
- [x] #1 Default overlay keybindings include `KeyF` mapped to mpv fullscreen toggle.
|
||||||
|
- [x] #2 Default AniSkip hint/button key defaults to `Tab` and the plugin registers that binding.
|
||||||
|
- [x] #3 Automated regression coverage exists for both default bindings.
|
||||||
|
<!-- AC:END -->
|
||||||
|
|
||||||
|
## Implementation Plan
|
||||||
|
|
||||||
|
<!-- SECTION:PLAN:BEGIN -->
|
||||||
|
1. Add a failing TypeScript regression proving default overlay keybindings include fullscreen on `KeyF`.
|
||||||
|
2. Add a failing Lua/plugin regression proving AniSkip defaults to `Tab`, updates the OSD hint text, and registers the expected keybinding.
|
||||||
|
3. Patch the default keybinding/config values with minimal behavior changes and keep fallback binding behavior intentional.
|
||||||
|
4. Run focused tests plus touched verification commands, then record results and a short changelog fragment.
|
||||||
|
<!-- SECTION:PLAN:END -->
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
<!-- SECTION:NOTES:BEGIN -->
|
||||||
|
Added `KeyF -> ['cycle', 'fullscreen']` to the built-in overlay keybindings in `src/config/definitions/shared.ts`.
|
||||||
|
|
||||||
|
Changed the mpv plugin AniSkip default button key from `y-k` to `TAB` in both the runtime default options and the shipped `plugin/subminer.conf`. The AniSkip OSD hint now also falls back to `TAB` when no explicit key is configured.
|
||||||
|
|
||||||
|
Adjusted `plugin/subminer/ui.lua` fallback registration so the legacy `y-k` binding is only added for custom non-default AniSkip bindings, instead of always shadowing the new default.
|
||||||
|
|
||||||
|
Extended regression coverage:
|
||||||
|
|
||||||
|
- `src/config/definitions/domain-registry.test.ts` now asserts the default fullscreen binding on `KeyF`.
|
||||||
|
- `scripts/test-plugin-start-gate.lua` now isolates plugin runs correctly, records keybinding/observer registration, and asserts the default AniSkip keybinding/prompt behavior for `TAB`.
|
||||||
|
|
||||||
|
Verification:
|
||||||
|
|
||||||
|
- `bun test src/config/definitions/domain-registry.test.ts`
|
||||||
|
- `bun run test:config:src`
|
||||||
|
- `lua scripts/test-plugin-start-gate.lua`
|
||||||
|
- `bun run changelog:lint`
|
||||||
|
- `bun run typecheck`
|
||||||
|
|
||||||
|
Known unrelated verification gap:
|
||||||
|
|
||||||
|
- `bun run test:plugin:src` still fails in `scripts/test-plugin-binary-windows.lua` on this Linux host (`windows env override should resolve .exe suffix`), outside the keybinding changes in this task.
|
||||||
|
<!-- SECTION:NOTES:END -->
|
||||||
|
|
||||||
|
## Final Summary
|
||||||
|
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||||
|
Default overlay playback now has an easier fullscreen toggle on `f`, and AniSkip's default intro-end jump now uses `Tab`. The mpv plugin hint text and registration logic were updated to match the new default, while keeping legacy `y-k` fallback behavior limited to custom non-default bindings.
|
||||||
|
|
||||||
|
Regression coverage was added for both defaults, and the plugin test harness now resets plugin bootstrap state between scenarios so keybinding assertions can run reliably.
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||||
@@ -5,7 +5,7 @@ status: Done
|
|||||||
assignee:
|
assignee:
|
||||||
- codex
|
- codex
|
||||||
created_date: '2026-03-08 18:24'
|
created_date: '2026-03-08 18:24'
|
||||||
updated_date: '2026-03-08 18:55'
|
updated_date: '2026-03-18 05:28'
|
||||||
labels:
|
labels:
|
||||||
- bug
|
- bug
|
||||||
- macos
|
- macos
|
||||||
@@ -19,6 +19,7 @@ references:
|
|||||||
- >-
|
- >-
|
||||||
/Users/sudacode/projects/japanese/SubMiner/scripts/get-mpv-window-macos.swift
|
/Users/sudacode/projects/japanese/SubMiner/scripts/get-mpv-window-macos.swift
|
||||||
priority: high
|
priority: high
|
||||||
|
ordinal: 53500
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|||||||
@@ -2,9 +2,10 @@
|
|||||||
id: TASK-133
|
id: TASK-133
|
||||||
title: Improve AniList character dictionary parity with upstream guide
|
title: Improve AniList character dictionary parity with upstream guide
|
||||||
status: To Do
|
status: To Do
|
||||||
assignee: []
|
assignee:
|
||||||
|
- OpenCode
|
||||||
created_date: '2026-03-08 21:06'
|
created_date: '2026-03-08 21:06'
|
||||||
updated_date: '2026-03-08 21:35'
|
updated_date: '2026-03-18 05:27'
|
||||||
labels:
|
labels:
|
||||||
- dictionary
|
- dictionary
|
||||||
- anilist
|
- anilist
|
||||||
@@ -23,6 +24,7 @@ documentation:
|
|||||||
- >-
|
- >-
|
||||||
/Users/sudacode/projects/japanese/SubMiner/docs/plans/2026-03-08-anilist-character-dictionary-parity.md
|
/Users/sudacode/projects/japanese/SubMiner/docs/plans/2026-03-08-anilist-character-dictionary-parity.md
|
||||||
priority: high
|
priority: high
|
||||||
|
ordinal: 3000
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
@@ -40,8 +42,20 @@ Plan and implement guide-faithful parity improvements for the AniList character
|
|||||||
- [ ] #5 AniList requests handle 429 responses with bounded exponential backoff and tests cover retry behavior.
|
- [ ] #5 AniList requests handle 429 responses with bounded exponential backoff and tests cover retry behavior.
|
||||||
<!-- AC:END -->
|
<!-- AC:END -->
|
||||||
|
|
||||||
|
## Implementation Plan
|
||||||
|
|
||||||
|
<!-- SECTION:PLAN:BEGIN -->
|
||||||
|
1. Add targeted regression tests in `src/main/character-dictionary-runtime.test.ts` for AniList 429 retry behavior and thumbnail conversion/fallback image paths before changing runtime code.
|
||||||
|
2. Update `fetchAniList` in `src/main/character-dictionary-runtime.ts` to retry 429 responses with bounded exponential backoff or `Retry-After` delays, while preserving existing request pacing hook behavior.
|
||||||
|
3. Update `downloadCharacterImage` to run downloaded bytes through `convertImageToThumbnail`, returning `.jpg` paths when conversion succeeds and preserving original bytes/extensions on fallback.
|
||||||
|
4. Run focused character-dictionary tests first, then broader required verification commands if the focused lane passes.
|
||||||
|
5. Record progress against acceptance criteria #4 and #5 in TASK-133 notes/final checklist once verified.
|
||||||
|
<!-- SECTION:PLAN:END -->
|
||||||
|
|
||||||
## Implementation Notes
|
## Implementation Notes
|
||||||
|
|
||||||
<!-- SECTION:NOTES:BEGIN -->
|
<!-- SECTION:NOTES:BEGIN -->
|
||||||
Approved design and implementation plan captured on 2026-03-08. Scope stays within current single-media AniList dictionary flow; excludes username-driven CURRENT-list fetching and Yomitan auto-update schema work.
|
Approved design and implementation plan captured on 2026-03-08. Scope stays within current single-media AniList dictionary flow; excludes username-driven CURRENT-list fetching and Yomitan auto-update schema work.
|
||||||
|
|
||||||
|
Resumed TASK-133 to finish remaining AC #4 image thumbnail wiring and AC #5 AniList 429 retry handling. Existing code already contains helper constants/functions; next work is wiring plus regression coverage.
|
||||||
<!-- SECTION:NOTES:END -->
|
<!-- SECTION:NOTES:END -->
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ status: Done
|
|||||||
assignee:
|
assignee:
|
||||||
- codex
|
- codex
|
||||||
created_date: '2026-03-09 00:00'
|
created_date: '2026-03-09 00:00'
|
||||||
updated_date: '2026-03-08 20:23'
|
updated_date: '2026-03-18 05:28'
|
||||||
labels:
|
labels:
|
||||||
- ci
|
- ci
|
||||||
- release
|
- release
|
||||||
@@ -16,8 +16,9 @@ references:
|
|||||||
- .github/workflows/release.yml
|
- .github/workflows/release.yml
|
||||||
- package.json
|
- package.json
|
||||||
- src/release-workflow.test.ts
|
- src/release-workflow.test.ts
|
||||||
- https://github.com/ksyasuda/SubMiner/actions/runs/22836585479
|
- 'https://github.com/ksyasuda/SubMiner/actions/runs/22836585479'
|
||||||
priority: high
|
priority: high
|
||||||
|
ordinal: 52500
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ status: Done
|
|||||||
assignee:
|
assignee:
|
||||||
- codex
|
- codex
|
||||||
created_date: '2026-03-08 20:24'
|
created_date: '2026-03-08 20:24'
|
||||||
updated_date: '2026-03-08 20:28'
|
updated_date: '2026-03-18 05:28'
|
||||||
labels:
|
labels:
|
||||||
- release
|
- release
|
||||||
- patch
|
- patch
|
||||||
@@ -16,6 +16,7 @@ references:
|
|||||||
- CHANGELOG.md
|
- CHANGELOG.md
|
||||||
- release/release-notes.md
|
- release/release-notes.md
|
||||||
priority: high
|
priority: high
|
||||||
|
ordinal: 51500
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ status: Done
|
|||||||
assignee:
|
assignee:
|
||||||
- codex
|
- codex
|
||||||
created_date: '2026-03-08 20:41'
|
created_date: '2026-03-08 20:41'
|
||||||
updated_date: '2026-03-08 20:58'
|
updated_date: '2026-03-18 05:28'
|
||||||
labels:
|
labels:
|
||||||
- ci
|
- ci
|
||||||
- release
|
- release
|
||||||
@@ -18,6 +18,7 @@ references:
|
|||||||
- build/signpath-windows-artifact-config.xml
|
- build/signpath-windows-artifact-config.xml
|
||||||
- src/release-workflow.test.ts
|
- src/release-workflow.test.ts
|
||||||
priority: high
|
priority: high
|
||||||
|
ordinal: 49500
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ status: Done
|
|||||||
assignee:
|
assignee:
|
||||||
- codex
|
- codex
|
||||||
created_date: '2026-03-08 20:44'
|
created_date: '2026-03-08 20:44'
|
||||||
updated_date: '2026-03-08 20:58'
|
updated_date: '2026-03-18 05:28'
|
||||||
labels:
|
labels:
|
||||||
- release
|
- release
|
||||||
- patch
|
- patch
|
||||||
@@ -16,6 +16,7 @@ references:
|
|||||||
- CHANGELOG.md
|
- CHANGELOG.md
|
||||||
- release/release-notes.md
|
- release/release-notes.md
|
||||||
priority: high
|
priority: high
|
||||||
|
ordinal: 50500
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ status: Done
|
|||||||
assignee:
|
assignee:
|
||||||
- codex
|
- codex
|
||||||
created_date: '2026-03-09 00:00'
|
created_date: '2026-03-09 00:00'
|
||||||
updated_date: '2026-03-09 00:00'
|
updated_date: '2026-03-18 05:28'
|
||||||
labels:
|
labels:
|
||||||
- release
|
- release
|
||||||
- windows
|
- windows
|
||||||
@@ -15,6 +15,7 @@ references:
|
|||||||
- package.json
|
- package.json
|
||||||
- src/release-workflow.test.ts
|
- src/release-workflow.test.ts
|
||||||
priority: high
|
priority: high
|
||||||
|
ordinal: 45500
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
@@ -56,6 +57,7 @@ Updated `src/release-workflow.test.ts` to assert the unsigned workflow contract
|
|||||||
Windows release CI now publishes unsigned artifacts directly and no longer depends on SignPath. Local developers also have an explicit `bun run build:win:unsigned` path for unsigned packaging without changing the existing `build:win` command.
|
Windows release CI now publishes unsigned artifacts directly and no longer depends on SignPath. Local developers also have an explicit `bun run build:win:unsigned` path for unsigned packaging without changing the existing `build:win` command.
|
||||||
|
|
||||||
Verification:
|
Verification:
|
||||||
|
|
||||||
- `bun test src/release-workflow.test.ts`
|
- `bun test src/release-workflow.test.ts`
|
||||||
- `bun run typecheck`
|
- `bun run typecheck`
|
||||||
- `node --check scripts/build-win-unsigned.mjs`
|
- `node --check scripts/build-win-unsigned.mjs`
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ status: Done
|
|||||||
assignee:
|
assignee:
|
||||||
- codex
|
- codex
|
||||||
created_date: '2026-03-09 00:00'
|
created_date: '2026-03-09 00:00'
|
||||||
updated_date: '2026-03-09 00:00'
|
updated_date: '2026-03-18 05:28'
|
||||||
labels:
|
labels:
|
||||||
- release
|
- release
|
||||||
- patch
|
- patch
|
||||||
@@ -16,6 +16,7 @@ references:
|
|||||||
- CHANGELOG.md
|
- CHANGELOG.md
|
||||||
- release/release-notes.md
|
- release/release-notes.md
|
||||||
priority: high
|
priority: high
|
||||||
|
ordinal: 46500
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
id: TASK-140
|
||||||
|
title: Fix guessit title parsing for character dictionary sync
|
||||||
|
status: Done
|
||||||
|
assignee: []
|
||||||
|
created_date: '2026-03-09 00:00'
|
||||||
|
updated_date: '2026-03-18 05:28'
|
||||||
|
labels:
|
||||||
|
- dictionary
|
||||||
|
- anilist
|
||||||
|
- bug
|
||||||
|
- guessit
|
||||||
|
dependencies: []
|
||||||
|
references:
|
||||||
|
- >-
|
||||||
|
/home/sudacode/projects/japanese/SubMiner/src/core/services/anilist/anilist-updater.ts
|
||||||
|
- >-
|
||||||
|
/home/sudacode/projects/japanese/SubMiner/src/core/services/anilist/anilist-updater.test.ts
|
||||||
|
priority: high
|
||||||
|
ordinal: 44500
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
Fix AniList character dictionary auto-sync for filenames where `guessit` misparses the full path and our title extraction keeps only the first array segment, causing AniList resolution to match the wrong anime and abort merged dictionary refresh.
|
||||||
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
<!-- AC:BEGIN -->
|
||||||
|
- [x] #1 AniList media guessing passes basename-only targets to `guessit` so parent folder names do not corrupt series title detection.
|
||||||
|
- [x] #2 Guessit title arrays are combined into one usable title instead of truncating to the first segment.
|
||||||
|
- [x] #3 Regression coverage includes the Bunny Girl Senpai filename shape that previously resolved to the wrong AniList entry.
|
||||||
|
- [x] #4 Verification confirms the targeted AniList guessing tests pass.
|
||||||
|
<!-- AC:END -->
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
<!-- SECTION:NOTES:BEGIN -->
|
||||||
|
Root repro: `guessit` parsed the Bunny Girl Senpai full path as `title: ["Rascal", "Does-not-Dream-of-Bunny-Girl-Senapi"]`, and our `firstString` helper kept only `Rascal`, which resolved to AniList 3490 (`rayca`) and produced zero character results. Fixed by sending basename-only input to `guessit` and joining multi-part guessit title arrays.
|
||||||
|
<!-- SECTION:NOTES:END -->
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
id: TASK-141
|
||||||
|
title: Refresh current subtitle after character dictionary sync completes
|
||||||
|
status: Done
|
||||||
|
assignee: []
|
||||||
|
created_date: '2026-03-09 00:00'
|
||||||
|
updated_date: '2026-03-18 05:28'
|
||||||
|
labels:
|
||||||
|
- dictionary
|
||||||
|
- overlay
|
||||||
|
- bug
|
||||||
|
dependencies: []
|
||||||
|
references:
|
||||||
|
- >-
|
||||||
|
/home/sudacode/projects/japanese/SubMiner/src/main/runtime/character-dictionary-auto-sync.ts
|
||||||
|
- /home/sudacode/projects/japanese/SubMiner/src/main.ts
|
||||||
|
priority: high
|
||||||
|
ordinal: 42500
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
When character dictionary auto-sync finishes after startup tokenization, invalidate cached subtitle tokenization and refresh the current subtitle so character-name highlighting catches up without waiting for the next subtitle line.
|
||||||
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
<!-- AC:BEGIN -->
|
||||||
|
- [x] #1 Successful character dictionary sync exposes a completion hook for main runtime follow-up.
|
||||||
|
- [x] #2 Main runtime clears Yomitan parser caches and refreshes the current subtitle after sync completion.
|
||||||
|
- [x] #3 Regression coverage verifies the sync completion callback fires on successful sync.
|
||||||
|
<!-- AC:END -->
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
<!-- SECTION:NOTES:BEGIN -->
|
||||||
|
Observed on Bunny Girl Senpai startup: autoplay/tokenization became ready around 8s, but snapshot/import/state write completed roughly 31s after launch, leaving the current subtitle tokenized without the newly imported character dictionary. Fixed by adding an auto-sync completion hook that clears parser caches and refreshes the current subtitle.
|
||||||
|
<!-- SECTION:NOTES:END -->
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
---
|
||||||
|
id: TASK-142
|
||||||
|
title: Show character dictionary auto-sync progress on OSD
|
||||||
|
status: Done
|
||||||
|
assignee: []
|
||||||
|
created_date: '2026-03-09 01:10'
|
||||||
|
updated_date: '2026-03-18 05:28'
|
||||||
|
labels:
|
||||||
|
- dictionary
|
||||||
|
- overlay
|
||||||
|
- ux
|
||||||
|
dependencies: []
|
||||||
|
references:
|
||||||
|
- >-
|
||||||
|
/home/sudacode/projects/japanese/SubMiner/src/main/runtime/character-dictionary-auto-sync.ts
|
||||||
|
- >-
|
||||||
|
/home/sudacode/projects/japanese/SubMiner/src/main/runtime/character-dictionary-auto-sync-notifications.ts
|
||||||
|
- /home/sudacode/projects/japanese/SubMiner/src/main.ts
|
||||||
|
priority: medium
|
||||||
|
ordinal: 41500
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
When character dictionary auto-sync runs for a newly opened anime, surface progress so users know why character-name lookup/highlighting is temporarily unavailable via the mpv OSD without desktop notification popups.
|
||||||
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
<!-- AC:BEGIN -->
|
||||||
|
- [x] #1 Character dictionary auto-sync emits progress events for syncing, importing, ready, and failure states.
|
||||||
|
- [x] #2 Main runtime routes those progress events through OSD notifications without desktop notifications.
|
||||||
|
- [x] #3 Regression coverage verifies progress events and notification routing behavior.
|
||||||
|
<!-- AC:END -->
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
<!-- SECTION:NOTES:BEGIN -->
|
||||||
|
OSD now shows auto-sync phase changes while the dictionary updates. Desktop notifications were removed for this path to avoid startup popup spam.
|
||||||
|
<!-- SECTION:NOTES:END -->
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
---
|
||||||
|
id: TASK-143
|
||||||
|
title: Keep character dictionary auto-sync non-blocking during startup
|
||||||
|
status: In Progress
|
||||||
|
assignee:
|
||||||
|
- codex
|
||||||
|
created_date: '2026-03-09 01:45'
|
||||||
|
updated_date: '2026-03-20 09:22'
|
||||||
|
labels:
|
||||||
|
- dictionary
|
||||||
|
- startup
|
||||||
|
- performance
|
||||||
|
dependencies: []
|
||||||
|
references:
|
||||||
|
- /home/sudacode/projects/japanese/SubMiner/src/main.ts
|
||||||
|
- >-
|
||||||
|
/home/sudacode/projects/japanese/SubMiner/src/main/runtime/character-dictionary-auto-sync.ts
|
||||||
|
- >-
|
||||||
|
/home/sudacode/projects/japanese/SubMiner/src/main/runtime/current-media-tokenization-gate.ts
|
||||||
|
priority: high
|
||||||
|
ordinal: 38500
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
Keep character dictionary auto-sync running in parallel during startup without delaying playback. Only tokenization readiness should gate playback; character dictionary import/settings updates should wait until tokenization is already ready and then refresh annotations afterward.
|
||||||
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
<!-- AC:BEGIN -->
|
||||||
|
- [x] #1 Character dictionary snapshot/build work can run immediately during startup.
|
||||||
|
- [x] #2 Yomitan dictionary mutation work waits until current-media tokenization is ready.
|
||||||
|
- [x] #3 Regression coverage verifies auto-sync builds before the gate and only mutates Yomitan after the gate resolves.
|
||||||
|
<!-- AC:END -->
|
||||||
|
|
||||||
|
## Implementation Plan
|
||||||
|
|
||||||
|
<!-- SECTION:PLAN:BEGIN -->
|
||||||
|
1. Add a regression test for startup autoplay release surviving delayed mpv readiness or late subtitle refresh after dictionary sync.
|
||||||
|
2. Harden the autoplay-ready release path so paused startup keeps retrying until mpv is actually released or media changes, without resuming user-paused playback later.
|
||||||
|
3. Keep the existing character-dictionary revisit fixes and paused-startup OSD fixes aligned with the autoplay change, then run targeted runtime tests and typecheck.
|
||||||
|
<!-- SECTION:PLAN:END -->
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
<!-- SECTION:NOTES:BEGIN -->
|
||||||
|
Added a small current-media tokenization gate in main runtime. Media changes reset the gate, the first tokenization-ready event marks it ready, and auto-sync now waits on that gate only before Yomitan dictionary inspection/import/settings updates. Snapshot generation and merged ZIP build still run immediately in parallel.
|
||||||
|
|
||||||
|
2026-03-20: User reports startup remains paused after annotations/tokenization are visible and only resumes after character-dictionary generation/import finishes. Investigating autoplay-ready release regression vs dictionary sync completion refresh.
|
||||||
|
|
||||||
|
2026-03-20: Added startup autoplay retry-budget helper so paused startup retries cover the full plugin gate window instead of only ~2.8s. Verification: bun test src/main/runtime/startup-autoplay-release-policy.test.ts src/main/runtime/character-dictionary-auto-sync.test.ts src/main/runtime/startup-osd-sequencer.test.ts src/main/runtime/character-dictionary-auto-sync-completion.test.ts; bun run typecheck; bun run test:fast; bun run test:env; bun run build; bun run test:smoke:dist; runtime-compat verifier passed at .tmp/skill-verification/subminer-verify-20260320-022106-nM28Nk. Pending real installed-app/mpv validation.
|
||||||
|
<!-- SECTION:NOTES:END -->
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
---
|
||||||
|
id: TASK-144
|
||||||
|
title: >-
|
||||||
|
Sequence startup OSD notifications for tokenization, annotations, and
|
||||||
|
character dictionary sync
|
||||||
|
status: Done
|
||||||
|
assignee: []
|
||||||
|
created_date: '2026-03-09 10:40'
|
||||||
|
updated_date: '2026-03-18 05:28'
|
||||||
|
labels:
|
||||||
|
- startup
|
||||||
|
- overlay
|
||||||
|
- ux
|
||||||
|
dependencies: []
|
||||||
|
references:
|
||||||
|
- /home/sudacode/projects/japanese/SubMiner/src/main.ts
|
||||||
|
- >-
|
||||||
|
/home/sudacode/projects/japanese/SubMiner/src/main/runtime/startup-osd-sequencer.ts
|
||||||
|
- >-
|
||||||
|
/home/sudacode/projects/japanese/SubMiner/src/main/runtime/subtitle-tokenization-main-deps.ts
|
||||||
|
- >-
|
||||||
|
/home/sudacode/projects/japanese/SubMiner/src/main/runtime/character-dictionary-auto-sync-notifications.ts
|
||||||
|
priority: medium
|
||||||
|
ordinal: 37500
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
Keep startup OSD progress ordered. While tokenization is still pending, only show the tokenization loading message. After tokenization becomes ready, show annotation loading if annotation warmup still remains. Only surface character dictionary auto-sync progress after annotation loading clears, and only if the dictionary work is still active.
|
||||||
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
<!-- AC:BEGIN -->
|
||||||
|
- [x] #1 Character dictionary progress stays hidden while tokenization startup loading is still active.
|
||||||
|
- [x] #2 Annotation loading OSD appears after tokenization readiness and before any later character dictionary progress.
|
||||||
|
- [x] #3 Regression coverage verifies buffered dictionary progress/failure ordering during startup.
|
||||||
|
<!-- AC:END -->
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
<!-- SECTION:NOTES:BEGIN -->
|
||||||
|
Added a small startup OSD sequencer in main runtime. Annotation warmup OSD now flows through that sequencer, and character dictionary sync notifications buffer until tokenization plus annotation loading clear. Buffered `ready` updates are dropped if dictionary progress finished before it ever became visible, while buffered failures still surface after annotation loading completes.
|
||||||
|
<!-- SECTION:NOTES:END -->
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
---
|
||||||
|
id: TASK-145
|
||||||
|
title: Show checking and generation OSD for character dictionary auto-sync
|
||||||
|
status: Done
|
||||||
|
assignee: []
|
||||||
|
created_date: '2026-03-09 11:20'
|
||||||
|
updated_date: '2026-03-16 05:13'
|
||||||
|
labels:
|
||||||
|
- dictionary
|
||||||
|
- overlay
|
||||||
|
- ux
|
||||||
|
dependencies: []
|
||||||
|
references:
|
||||||
|
- /home/sudacode/projects/japanese/SubMiner/src/main.ts
|
||||||
|
- >-
|
||||||
|
/home/sudacode/projects/japanese/SubMiner/src/main/runtime/character-dictionary-auto-sync.ts
|
||||||
|
- >-
|
||||||
|
/home/sudacode/projects/japanese/SubMiner/src/main/runtime/startup-osd-sequencer.ts
|
||||||
|
- >-
|
||||||
|
/home/sudacode/projects/japanese/SubMiner/src/main/character-dictionary-runtime.ts
|
||||||
|
priority: medium
|
||||||
|
ordinal: 35500
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
Surface an immediate startup OSD that the character dictionary is being checked, and show a distinct generating message only when the current AniList media actually needs a fresh snapshot build instead of reusing a cached one.
|
||||||
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
<!-- AC:BEGIN -->
|
||||||
|
- [x] #1 Auto-sync emits a `checking` progress event before snapshot resolution completes.
|
||||||
|
- [x] #2 Auto-sync emits `generating` only for snapshot cache misses and keeps `updating`/`importing` as later phases.
|
||||||
|
- [x] #3 Startup OSD sequencing still prioritizes tokenization then annotation loading before buffered dictionary progress.
|
||||||
|
<!-- AC:END -->
|
||||||
|
|
||||||
|
## Final Summary
|
||||||
|
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||||
|
Character dictionary auto-sync now emits `Checking character dictionary...` as soon as the AniList media is resolved, then emits `Generating character dictionary...` only when the snapshot layer misses and a real rebuild begins. Cached snapshots skip the generating phase and continue straight into the later update/import flow.
|
||||||
|
|
||||||
|
Wired those progress callbacks through the character-dictionary runtime boundary, updated the startup OSD sequencer to treat checking/generating as dictionary-progress phases with the same tokenization and annotation precedence, and added regression coverage for cache-hit vs cache-miss behavior plus buffered startup ordering.
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
---
|
||||||
|
id: TASK-146
|
||||||
|
title: Forward overlay Tab to mpv for AniSkip
|
||||||
|
status: Done
|
||||||
|
assignee:
|
||||||
|
- codex
|
||||||
|
created_date: '2026-03-09 00:00'
|
||||||
|
updated_date: '2026-03-18 05:28'
|
||||||
|
labels:
|
||||||
|
- bug
|
||||||
|
- overlay
|
||||||
|
- aniskip
|
||||||
|
- linux
|
||||||
|
dependencies: []
|
||||||
|
ordinal: 47500
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
Fix visible-overlay keyboard handling so bare `Tab` is forwarded to mpv instead of being consumed by Electron focus navigation. This restores the default AniSkip `TAB` binding while the overlay has focus, especially on Linux.
|
||||||
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
<!-- AC:BEGIN -->
|
||||||
|
- [x] #1 Visible overlay forwards bare `Tab` to mpv as `keypress TAB`.
|
||||||
|
- [x] #2 Modal overlays keep their existing local `Tab` behavior.
|
||||||
|
- [x] #3 Automated regression coverage exists for the input handler and overlay factory wiring.
|
||||||
|
<!-- AC:END -->
|
||||||
|
|
||||||
|
## Implementation Plan
|
||||||
|
|
||||||
|
<!-- SECTION:PLAN:BEGIN -->
|
||||||
|
1. Add a failing regression around visible-overlay `before-input-event` handling for bare `Tab`.
|
||||||
|
2. Add/extend overlay factory tests so the new mpv-forward callback is wired through runtime construction.
|
||||||
|
3. Patch overlay input handling to intercept visible-overlay `Tab` and send mpv `keypress TAB`.
|
||||||
|
4. Run focused overlay tests, typecheck, and changelog validation.
|
||||||
|
<!-- SECTION:PLAN:END -->
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
<!-- SECTION:NOTES:BEGIN -->
|
||||||
|
Extracted visible-overlay input handling into `src/core/services/overlay-window-input.ts` so the `Tab` forwarding decision can be unit tested without loading Electron window primitives.
|
||||||
|
|
||||||
|
Visible overlay `before-input-event` now intercepts bare `Tab`, prevents the browser default, and forwards mpv `keypress TAB` through the existing mpv runtime command path. Modal overlays remain unchanged.
|
||||||
|
|
||||||
|
Verification:
|
||||||
|
|
||||||
|
- `bun test src/core/services/overlay-window.test.ts src/main/runtime/overlay-window-factory.test.ts src/main/runtime/overlay-window-factory-main-deps.test.ts src/main/runtime/overlay-window-runtime-handlers.test.ts`
|
||||||
|
- `bun x tsc --noEmit`
|
||||||
|
<!-- SECTION:NOTES:END -->
|
||||||
|
|
||||||
|
## Final Summary
|
||||||
|
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||||
|
Visible overlay focus no longer blocks the default AniSkip `Tab` binding. Bare `Tab` is now forwarded straight to mpv while the visible overlay is active, and modal overlays still retain their own normal focus behavior.
|
||||||
|
|
||||||
|
Added regression coverage for both the input-routing decision and the runtime plumbing that carries the new mpv forwarder into overlay window creation.
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
---
|
||||||
|
id: TASK-148
|
||||||
|
title: Fix Windows plugin env binary override resolution
|
||||||
|
status: Done
|
||||||
|
assignee:
|
||||||
|
- codex
|
||||||
|
created_date: '2026-03-09 00:00'
|
||||||
|
updated_date: '2026-03-18 05:28'
|
||||||
|
labels:
|
||||||
|
- windows
|
||||||
|
- plugin
|
||||||
|
- regression
|
||||||
|
dependencies: []
|
||||||
|
priority: medium
|
||||||
|
ordinal: 48500
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
Fix the mpv plugin's Windows binary override lookup so `SUBMINER_BINARY_PATH` still resolves when `SUBMINER_APPIMAGE_PATH` is unset. The current Lua resolver builds an array with a leading `nil`, which causes `ipairs` iteration to stop before the later Windows override candidate.
|
||||||
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
<!-- AC:BEGIN -->
|
||||||
|
- [x] #1 `scripts/test-plugin-binary-windows.lua` passes the env override regression that expects `.exe` suffix resolution from `SUBMINER_BINARY_PATH`.
|
||||||
|
- [x] #2 Existing plugin start/binary test gate stays green after the fix.
|
||||||
|
<!-- AC:END -->
|
||||||
|
|
||||||
|
## Final Summary
|
||||||
|
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||||
|
Updated `plugin/subminer/binary.lua` so env override lookup checks `SUBMINER_APPIMAGE_PATH` and `SUBMINER_BINARY_PATH` sequentially instead of via a Lua array literal that truncates at the first `nil`. This restores Windows `.exe` suffix resolution for `SUBMINER_BINARY_PATH` when the AppImage env var is unset.
|
||||||
|
|
||||||
|
Verification:
|
||||||
|
|
||||||
|
- `lua scripts/test-plugin-binary-windows.lua`
|
||||||
|
- `bun run test:plugin:src`
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
---
|
||||||
|
id: TASK-149
|
||||||
|
title: Cut patch release v0.5.5 for character dictionary updates and release guarding
|
||||||
|
status: Done
|
||||||
|
assignee:
|
||||||
|
- codex
|
||||||
|
created_date: '2026-03-09 01:10'
|
||||||
|
updated_date: '2026-03-18 05:28'
|
||||||
|
labels:
|
||||||
|
- release
|
||||||
|
- patch
|
||||||
|
dependencies:
|
||||||
|
- TASK-140
|
||||||
|
- TASK-141
|
||||||
|
- TASK-142
|
||||||
|
- TASK-143
|
||||||
|
- TASK-144
|
||||||
|
- TASK-145
|
||||||
|
- TASK-146
|
||||||
|
- TASK-148
|
||||||
|
references:
|
||||||
|
- package.json
|
||||||
|
- CHANGELOG.md
|
||||||
|
- scripts/build-changelog.ts
|
||||||
|
- scripts/build-changelog.test.ts
|
||||||
|
- docs/RELEASING.md
|
||||||
|
priority: high
|
||||||
|
ordinal: 39500
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
Prepare and publish patch release `v0.5.5` after the failed `v0.5.4` tag by aligning package version metadata, generating committed changelog output from the pending release fragments, and hardening release validation so a future tag cannot ship with a mismatched `package.json` version.
|
||||||
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
<!-- AC:BEGIN -->
|
||||||
|
- [x] #1 Repository version metadata is updated to `0.5.5`.
|
||||||
|
- [x] #2 `CHANGELOG.md` contains the committed `v0.5.5` section and the consumed fragments are removed.
|
||||||
|
- [x] #3 Release validation rejects a requested release version when it differs from `package.json`.
|
||||||
|
- [x] #4 Release docs capture the required version/changelog prep before tagging.
|
||||||
|
- [x] #5 New `v0.5.5` release-prep commit and tag are pushed to `origin/main`.
|
||||||
|
<!-- AC:END -->
|
||||||
|
|
||||||
|
## Implementation Plan
|
||||||
|
|
||||||
|
<!-- SECTION:PLAN:BEGIN -->
|
||||||
|
1. Add a regression test for tagged-release/package version mismatch.
|
||||||
|
2. Update changelog validation to reject mismatched explicit release versions.
|
||||||
|
3. Bump `package.json`, generate committed `v0.5.5` changelog output, and remove consumed fragments.
|
||||||
|
4. Add a short `docs/RELEASING.md` checklist for the prep flow.
|
||||||
|
5. Run release verification, commit, tag, and push.
|
||||||
|
<!-- SECTION:PLAN:END -->
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
<!-- SECTION:NOTES:BEGIN -->
|
||||||
|
Added a regression test in `scripts/build-changelog.test.ts` that proves `changelog:check --version ...` rejects tag/package mismatches. Updated `scripts/build-changelog.ts` so tagged release validation now compares the explicit requested version against `package.json` before looking for pending fragments or the committed changelog section.
|
||||||
|
|
||||||
|
Bumped `package.json` from `0.5.3` to `0.5.5`, ran `bun run changelog:build --version 0.5.5 --date 2026-03-09`, and committed the generated `CHANGELOG.md` output while removing the consumed task fragments. Added `docs/RELEASING.md` with the required release-prep checklist so version bump + changelog generation happen before tagging.
|
||||||
|
|
||||||
|
Verification: `bun run changelog:lint`, `bun run changelog:check --version 0.5.5`, `bun run typecheck`, `bun run test:fast`, and `bun test scripts/build-changelog.test.ts src/release-workflow.test.ts`. `bun run format:check` still reports many unrelated pre-existing repo-wide Prettier warnings, so touched files were checked/formatted separately with `bunx prettier`.
|
||||||
|
<!-- SECTION:NOTES:END -->
|
||||||
|
|
||||||
|
## Final Summary
|
||||||
|
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||||
|
Prepared patch release `v0.5.5` after the failed `v0.5.4` release attempt. Release metadata now matches the upcoming tag, the pending character-dictionary/overlay/plugin fragments are committed into `CHANGELOG.md`, and release validation now blocks future tag/package mismatches before publish.
|
||||||
|
|
||||||
|
Docs now include a short release checklist in `docs/RELEASING.md`. Validation passed for changelog lint/check, typecheck, targeted workflow tests, and the full fast test suite. Repo-wide Prettier remains noisy from unrelated existing files, but touched release files were formatted and verified.
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
---
|
||||||
|
id: TASK-150
|
||||||
|
title: Restore repo-wide Prettier cleanliness after release prep
|
||||||
|
status: Done
|
||||||
|
assignee:
|
||||||
|
- codex
|
||||||
|
created_date: '2026-03-09 01:11'
|
||||||
|
updated_date: '2026-03-18 05:28'
|
||||||
|
labels:
|
||||||
|
- tooling
|
||||||
|
- formatting
|
||||||
|
dependencies: []
|
||||||
|
references:
|
||||||
|
- backlog/config.yml
|
||||||
|
- backlog/tasks
|
||||||
|
- config.example.jsonc
|
||||||
|
- launcher/types.ts
|
||||||
|
- launcher/util.ts
|
||||||
|
- launcher/youtube/orchestrator.ts
|
||||||
|
- scripts/build-win-unsigned.mjs
|
||||||
|
- src
|
||||||
|
priority: medium
|
||||||
|
ordinal: 40500
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
Bring `bun run format:check` back to green after the `v0.5.5` release-prep work exposed repo-wide Prettier drift across backlog markdown, config files, and maintained TypeScript sources.
|
||||||
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
<!-- AC:BEGIN -->
|
||||||
|
- [x] #1 `bun run format:check` passes.
|
||||||
|
- [x] #2 `bun run changelog:lint` still passes.
|
||||||
|
- [x] #3 Typecheck and fast tests stay green after the formatting-only rewrite.
|
||||||
|
<!-- AC:END -->
|
||||||
|
|
||||||
|
## Implementation Plan
|
||||||
|
|
||||||
|
<!-- SECTION:PLAN:BEGIN -->
|
||||||
|
1. Re-run format and lint checks to confirm failing files.
|
||||||
|
2. Apply Prettier to the warned repo-managed files.
|
||||||
|
3. Re-run formatting, lint, typecheck, and fast tests.
|
||||||
|
4. Commit and push the formatting-only cleanup.
|
||||||
|
<!-- SECTION:PLAN:END -->
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
<!-- SECTION:NOTES:BEGIN -->
|
||||||
|
Ran `bunx prettier --write` across the repo-managed files reported by `bun run format:check`, covering backlog markdown/YAML, `config.example.jsonc`, selected launcher/scripts files, and maintained TypeScript sources under `src/`.
|
||||||
|
|
||||||
|
Verification: `bun run format:check`, `bun run changelog:lint`, `bun run typecheck`, and `bun run test:fast`.
|
||||||
|
<!-- SECTION:NOTES:END -->
|
||||||
|
|
||||||
|
## Final Summary
|
||||||
|
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||||
|
Repo-wide Prettier drift is cleaned up, including backlog task markdown, config/example files, and the maintained code files that `format:check` was flagging. Formatting and lint checks are green again, and typecheck/fast tests stayed green after the formatting-only rewrite.
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
---
|
||||||
|
id: TASK-151
|
||||||
|
title: Keep JLPT underline color stable during Yomitan text selection
|
||||||
|
status: Done
|
||||||
|
assignee:
|
||||||
|
- OpenCode
|
||||||
|
created_date: '2026-03-10 06:42'
|
||||||
|
updated_date: '2026-03-16 05:13'
|
||||||
|
labels: []
|
||||||
|
dependencies: []
|
||||||
|
references:
|
||||||
|
- src/renderer/style.css
|
||||||
|
- src/renderer/subtitle-render.test.ts
|
||||||
|
documentation:
|
||||||
|
- ../subminer-docs/development.md
|
||||||
|
- ../subminer-docs/architecture.md
|
||||||
|
priority: medium
|
||||||
|
ordinal: 33500
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
Fix the subtitle overlay so JLPT underlines keep their JLPT color when Yomitan lookups trigger hover/selection styling on tokens that are also known, N+1, or frequency-highlighted.
|
||||||
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
<!-- AC:BEGIN -->
|
||||||
|
- [x] #1 JLPT-tagged subtitle tokens keep their JLPT underline color during hover/selection states used by Yomitan lookup.
|
||||||
|
- [x] #2 Tokens that combine JLPT with known, N+1, name-match, or frequency styling preserve their primary text color while keeping the JLPT underline color.
|
||||||
|
- [x] #3 Renderer regression coverage verifies the JLPT underline color remains explicit in the stylesheet for combined styling states.
|
||||||
|
<!-- AC:END -->
|
||||||
|
|
||||||
|
## Implementation Plan
|
||||||
|
|
||||||
|
<!-- SECTION:PLAN:BEGIN -->
|
||||||
|
1. Revert the temporary box-shadow JLPT marker experiment and restore native JLPT underline styling in `src/renderer/style.css`.
|
||||||
|
2. Add explicit JLPT hover/selection color rules with higher specificity so Yomitan lookup highlight states cannot repaint the underline to the token hover color.
|
||||||
|
3. Update `src/renderer/subtitle-render.test.ts` to verify JLPT hover/selection rules keep the predefined JLPT underline color, then rerun focused and relevant verification commands.
|
||||||
|
|
||||||
|
4. Add JLPT-specific `text-decoration-color` overrides directly on child `.c` spans and their hover/selection states, since Yomitan lookup likely paints the decorated child text runs rather than only the parent token span.
|
||||||
|
<!-- SECTION:PLAN:END -->
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
<!-- SECTION:NOTES:BEGIN -->
|
||||||
|
Added a renderer regression test that asserts JLPT underline color stays explicit through hover and selection styling.
|
||||||
|
|
||||||
|
Updated `src/renderer/style.css` to route JLPT underline color through `--subtitle-jlpt-underline-color` and re-apply it on word/character hover and selection states.
|
||||||
|
|
||||||
|
Verification: `bun test src/renderer/subtitle-render.test.ts`, `bun run typecheck`, `bun run test:fast`, `bun run build`, `bun run changelog:lint`, `bunx prettier --check src/renderer/style.css src/renderer/subtitle-render.test.ts changes/jlpt-underline-yomitan.md`. `bun run format:check:src` still fails because of unrelated existing formatting issues in `src/main-entry-runtime.test.ts` and `src/main-entry-runtime.ts`.
|
||||||
|
|
||||||
|
User verified the original CSS variable propagation fix did not resolve the live issue; continuing investigation and reopening the task.
|
||||||
|
|
||||||
|
Confirmed Chromium was repainting native `text-decoration` underlines with the selected text color; reproduced it in a temporary browser repro and verified that switching the JLPT marker to an inset box-shadow keeps the JLPT color stable during selection.
|
||||||
|
|
||||||
|
Replaced the previous native underline approach with a fragment-safe JLPT marker using `box-shadow` plus `box-decoration-break`, then updated stylesheet regression coverage to match the new implementation.
|
||||||
|
|
||||||
|
Verification: `bun test src/renderer/subtitle-render.test.ts`, `bunx prettier --check src/renderer/style.css src/renderer/subtitle-render.test.ts`, `bun run typecheck`, `bun run test:fast`, `bun run build`.
|
||||||
|
|
||||||
|
User requested reverting the box-shadow marker experiment. Narrowing the fix to preserve native JLPT underline color during Yomitan hover/selection instead of changing the underline rendering method.
|
||||||
|
|
||||||
|
Reverted the box-shadow experiment per user request. Restored native JLPT underline styling and added explicit JLPT hover/selection rules so Yomitan lookup states keep the predefined JLPT underline color with `!important`.
|
||||||
|
|
||||||
|
Verification after revert/fix: `bun test src/renderer/subtitle-render.test.ts`, `bunx prettier --check src/renderer/style.css src/renderer/subtitle-render.test.ts`, `bun run typecheck`, `bun run build`.
|
||||||
|
|
||||||
|
Issue still reproduces specifically during Shift-held Yomitan lookup. Investigating decoration propagation onto child `.c` spans during lookup-triggered hover/selection states.
|
||||||
|
|
||||||
|
Added JLPT-specific `text-decoration-color` overrides directly to child `.c` spans and their hover/selection states, in addition to the token span itself, so lookup-triggered decoration painting on child runs keeps the JLPT color.
|
||||||
|
|
||||||
|
Verification: `bun test src/renderer/subtitle-render.test.ts`, `bunx prettier --check src/renderer/style.css src/renderer/subtitle-render.test.ts`, `bun run typecheck`, `bun run build`.
|
||||||
|
|
||||||
|
Added a final high-specificity combined-state JLPT override after the annotation hover/selection rules. It targets tokens that are both JLPT-tagged and known/N+1/name-match/frequency-highlighted, forcing `text-decoration-color` back to the JLPT variable after the later annotation state blocks.
|
||||||
|
|
||||||
|
Verification: `bun test src/renderer/subtitle-render.test.ts`, `bunx prettier --check src/renderer/style.css src/renderer/subtitle-render.test.ts`, `bun run build`.
|
||||||
|
|
||||||
|
Switched the JLPT underline from native `text-decoration` to a separate painted underline layer using `background-image` on JLPT tokens. Also changed hover token highlight rules to use `background-color` so the JLPT underline layer survives Shift+Yomitan hover/lookup interactions.
|
||||||
|
|
||||||
|
Verification: `bun test src/renderer/subtitle-render.test.ts`, `bunx prettier --check src/renderer/style.css src/renderer/subtitle-render.test.ts`, `bun run build`.
|
||||||
|
|
||||||
|
User reports the latest background-image JLPT underline change introduced grey-looking token highlights. Investigating the interaction between the new underline layer and hover background styling.
|
||||||
|
|
||||||
|
The background-image underline layer interacts poorly with hover fills and makes highlighted tokens look grey. Switching the separate JLPT underline layer to an inset box-shadow so hover background color and JLPT underline paint can coexist cleanly.
|
||||||
|
|
||||||
|
Adjusted the separate JLPT underline layer from `background-image` to inset `box-shadow`. This keeps the underline decoupled from Yomitan hover color while avoiding the grey-looking underline artifact introduced by the background-image approach.
|
||||||
|
|
||||||
|
User reports the separate underline layer still disappears/greys out during lookup. Switching strategy again: use a dedicated `border-bottom` JLPT marker instead of box-shadow/background so the underline stays independent from Yomitan hover paint without blending artifacts.
|
||||||
|
|
||||||
|
Replaced the separate JLPT underline layer with `border-bottom` plus a small bottom padding. This keeps the underline visually separate from Yomitan hover paint and avoids the disappearing/grey artifact seen with background-image and box-shadow.
|
||||||
|
<!-- SECTION:NOTES:END -->
|
||||||
|
|
||||||
|
## Final Summary
|
||||||
|
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||||
|
Switched the JLPT underline layer in `src/renderer/style.css` to `border-bottom` plus `padding-bottom`, keeping it independent from Yomitan hover/selection repainting while avoiding the disappearing or grey artifact seen with the background-image and box-shadow approaches.
|
||||||
|
|
||||||
|
Updated `src/renderer/subtitle-render.test.ts` to assert the border-bottom-based JLPT underline layer.
|
||||||
|
|
||||||
|
Verification: `bun test src/renderer/subtitle-render.test.ts`, `bunx prettier --check src/renderer/style.css src/renderer/subtitle-render.test.ts`, `bun run build`.
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
---
|
||||||
|
id: TASK-152
|
||||||
|
title: Fix early Electron userData path casing to stay under SubMiner config dir
|
||||||
|
status: Done
|
||||||
|
assignee:
|
||||||
|
- codex
|
||||||
|
created_date: '2026-03-10 06:46'
|
||||||
|
updated_date: '2026-03-16 05:13'
|
||||||
|
labels:
|
||||||
|
- bug
|
||||||
|
- config
|
||||||
|
- electron
|
||||||
|
dependencies: []
|
||||||
|
references:
|
||||||
|
- /home/sudacode/projects/japanese/SubMiner/src/main-entry.ts
|
||||||
|
- /home/sudacode/projects/japanese/SubMiner/src/main.ts
|
||||||
|
- /home/sudacode/projects/japanese/SubMiner/src/config/path-resolution.ts
|
||||||
|
documentation:
|
||||||
|
- /home/sudacode/projects/japanese/subminer-docs/development.md
|
||||||
|
- /home/sudacode/projects/japanese/subminer-docs/architecture.md
|
||||||
|
priority: high
|
||||||
|
ordinal: 34500
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
Electron startup touches the app object before the main-process bootstrap overrides userData, which can create/write the default lowercase ~/.config/subminer directory on Linux/macOS. Ensure early startup pins the app identity and userData path to the canonical SubMiner config directory before any Electron APIs can materialize the default path, and keep regression coverage around the bootstrap path behavior.
|
||||||
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
<!-- AC:BEGIN -->
|
||||||
|
- [x] #1 Electron startup uses the canonical SubMiner config directory as userData before other Electron app calls can create the default lowercase directory.
|
||||||
|
- [x] #2 Regression tests cover the early bootstrap path setup and fail if startup falls back to a lowercase subminer config path.
|
||||||
|
- [x] #3 Existing config path resolution behavior for SubMiner casing remains intact.
|
||||||
|
<!-- AC:END -->
|
||||||
|
|
||||||
|
## Implementation Plan
|
||||||
|
|
||||||
|
<!-- SECTION:PLAN:BEGIN -->
|
||||||
|
1. Add regression coverage for early Electron bootstrap path setup, including a case that would otherwise fall back to lowercase subminer.
|
||||||
|
2. Extract a pure helper that computes and applies the canonical app name/userData path from config resolution.
|
||||||
|
3. Call the helper from main-entry before any Electron app interactions that could materialize the default userData directory.
|
||||||
|
4. Run focused tests for startup/config path behavior, then the relevant fast gate if green.
|
||||||
|
<!-- SECTION:PLAN:END -->
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
<!-- SECTION:NOTES:BEGIN -->
|
||||||
|
Added early Electron bootstrap path setup in src/main-entry so app name and userData are pinned to the canonical SubMiner config dir before single-instance/whenReady handling.
|
||||||
|
|
||||||
|
Added a regression test in src/main-entry-runtime.test.ts covering the lowercase subminer fallback case.
|
||||||
|
|
||||||
|
Validation: bun test src/main-entry-runtime.test.ts src/config/path-resolution.test.ts; bun run typecheck. bun run test:fast still fails on an existing unrelated renderer JLPT CSS test in src/renderer/subtitle-render.test.ts.
|
||||||
|
<!-- SECTION:NOTES:END -->
|
||||||
|
|
||||||
|
## Final Summary
|
||||||
|
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||||
|
Pinned Electron's app identity and userData path during entry bootstrap so startup uses the canonical SubMiner config directory before any other Electron app calls can materialize the default lowercase path. Added a regression test covering the lowercase subminer fallback case and kept existing config-path resolution coverage green.
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
- bun test src/main-entry-runtime.test.ts src/config/path-resolution.test.ts
|
||||||
|
- bun run typecheck
|
||||||
|
- bun run test:fast (fails on existing unrelated JLPT CSS renderer test in src/renderer/subtitle-render.test.ts)
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
---
|
||||||
|
id: TASK-153
|
||||||
|
title: Fix character dictionary MRU eviction after revisits
|
||||||
|
status: Done
|
||||||
|
assignee:
|
||||||
|
- '@codex'
|
||||||
|
created_date: '2026-03-10 07:56'
|
||||||
|
updated_date: '2026-03-16 05:13'
|
||||||
|
labels:
|
||||||
|
- character-dictionary
|
||||||
|
- yomitan
|
||||||
|
- anilist
|
||||||
|
dependencies: []
|
||||||
|
references:
|
||||||
|
- >-
|
||||||
|
/home/sudacode/projects/japanese/SubMiner/src/main/runtime/character-dictionary-auto-sync.ts
|
||||||
|
- >-
|
||||||
|
/home/sudacode/projects/japanese/SubMiner/src/main/runtime/character-dictionary-auto-sync.test.ts
|
||||||
|
- >-
|
||||||
|
/home/sudacode/projects/japanese/SubMiner/src/main/character-dictionary-runtime.ts
|
||||||
|
documentation:
|
||||||
|
- /home/sudacode/projects/japanese/subminer-docs/development.md
|
||||||
|
- /home/sudacode/projects/japanese/subminer-docs/architecture.md
|
||||||
|
priority: high
|
||||||
|
ordinal: 32500
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
Keep merged character dictionary retention truly MRU-based when a currently retained anime is revisited before opening a new title, so the oldest retained title is evicted instead of the revisited one.
|
||||||
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
<!-- AC:BEGIN -->
|
||||||
|
- [x] #1 Revisiting an anime already retained in the merged character dictionary updates MRU ordering before the next new title is added.
|
||||||
|
- [x] #2 When retention exceeds maxLoaded after that revisit-plus-new-title sequence, the least recently used retained anime is evicted rather than the revisited title.
|
||||||
|
- [x] #3 Auto-sync rebuilds or reloads the merged dictionary so an evicted anime becomes available again when reopened.
|
||||||
|
- [x] #4 Regression tests cover the revisit-before-eviction flow.
|
||||||
|
<!-- AC:END -->
|
||||||
|
|
||||||
|
## Implementation Plan
|
||||||
|
|
||||||
|
<!-- SECTION:PLAN:BEGIN -->
|
||||||
|
1. Reproduce the revisit-before-eviction scenario with a focused regression test in `src/main/runtime/character-dictionary-auto-sync.test.ts` using a retained set that revisits an existing anime before introducing a new anime.
|
||||||
|
2. Run the focused test to confirm current behavior fails for the expected MRU ordering / eviction case.
|
||||||
|
3. Adjust `src/main/runtime/character-dictionary-auto-sync.ts` so retained ordering stays MRU-correct across revisits and subsequent additions without regressing unchanged revisit behavior.
|
||||||
|
4. Re-run the focused auto-sync suite, then run the relevant broader checks required for handoff.
|
||||||
|
<!-- SECTION:PLAN:END -->
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
<!-- SECTION:NOTES:BEGIN -->
|
||||||
|
Added a focused regression in `src/main/runtime/character-dictionary-auto-sync.test.ts` for the `1,2,3,1,4,1` revisit-before-eviction flow. The current MRU auto-sync logic passed: merged builds were `[1]`, `[2,1]`, `[3,2,1]`, `[1,3,2]`, `[4,1,3]`, `[1,4,3]`, so I have not reproduced the reported eviction bug in the in-process auto-sync service yet.
|
||||||
|
|
||||||
|
Inspected local cache under `~/.config/SubMiner/character-dictionaries/` and found Little Witch Academia snapshot `anilist-21858.json` already present, so the reported regeneration path was not a snapshot-cache miss. The on-disk `merged.zip` revision (`9251ae23e136`) also differed from `auto-sync-state.json` (`bc16c5b5af17`), indicating a rebuilt merged dictionary artifact could land without the MRU state being persisted.
|
||||||
|
|
||||||
|
Fixed `src/main/runtime/character-dictionary-auto-sync.ts` to persist the rebuilt retained-set state immediately after a merged dictionary revision/title is known, before later Yomitan mutation steps. This keeps MRU eviction state aligned even if import/settings work fails after the rebuild artifact is written.
|
||||||
|
|
||||||
|
Added regression coverage in `src/main/runtime/character-dictionary-auto-sync.test.ts` for the revisit-before-eviction sequence and for post-build import failure preserving the rebuilt MRU state. Verified with `bun test src/main/runtime/character-dictionary-auto-sync.test.ts src/main/character-dictionary-runtime.test.ts`, `bun run typecheck`, `bun run changelog:lint`, and `bun run test:fast`.
|
||||||
|
<!-- SECTION:NOTES:END -->
|
||||||
|
|
||||||
|
## Final Summary
|
||||||
|
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||||
|
Persisted merged character-dictionary MRU state as soon as a rebuilt retained set is known in `src/main/runtime/character-dictionary-auto-sync.ts`, so revisits are not lost if later Yomitan import/settings work fails after `merged.zip` has already been rewritten. Added regression coverage for revisit-before-eviction ordering and import-failure state preservation in `src/main/runtime/character-dictionary-auto-sync.test.ts`, plus a changelog fragment in `changes/character-dictionary-mru-state-recovery.md`.
|
||||||
|
|
||||||
|
Validation: `bun test src/main/runtime/character-dictionary-auto-sync.test.ts src/main/character-dictionary-runtime.test.ts`, `bun run typecheck`, `bun run changelog:lint`, `bun run test:fast`.
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
---
|
||||||
|
id: TASK-154
|
||||||
|
title: Avoid merged dictionary rebuilds on MRU reorder-only revisits
|
||||||
|
status: Done
|
||||||
|
assignee:
|
||||||
|
- '@codex'
|
||||||
|
created_date: '2026-03-10 09:16'
|
||||||
|
updated_date: '2026-03-16 05:13'
|
||||||
|
labels:
|
||||||
|
- character-dictionary
|
||||||
|
- yomitan
|
||||||
|
- anilist
|
||||||
|
dependencies: []
|
||||||
|
references:
|
||||||
|
- >-
|
||||||
|
/home/sudacode/projects/japanese/SubMiner/src/main/runtime/character-dictionary-auto-sync.ts
|
||||||
|
- >-
|
||||||
|
/home/sudacode/projects/japanese/SubMiner/src/main/runtime/character-dictionary-auto-sync.test.ts
|
||||||
|
- >-
|
||||||
|
/home/sudacode/projects/japanese/SubMiner/src/main/character-dictionary-runtime.ts
|
||||||
|
- >-
|
||||||
|
/home/sudacode/projects/japanese/SubMiner/src/main/character-dictionary-runtime.test.ts
|
||||||
|
documentation:
|
||||||
|
- /home/sudacode/projects/japanese/subminer-docs/development.md
|
||||||
|
- /home/sudacode/projects/japanese/subminer-docs/architecture.md
|
||||||
|
priority: high
|
||||||
|
ordinal: 31500
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
Keep per-title MRU retention ordering for eviction decisions, but do not rebuild or reimport the merged character dictionary when the retained set membership is unchanged and only the MRU order changes.
|
||||||
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
<!-- AC:BEGIN -->
|
||||||
|
- [x] #1 Revisiting an anime already inside the retained set updates MRU ordering used for later eviction decisions without rebuilding or reimporting the merged dictionary when retained membership is unchanged.
|
||||||
|
- [x] #2 Merged dictionary revisions and contents are stable for the same retained membership regardless of MRU ordering.
|
||||||
|
- [x] #3 Adding a new anime that changes retained membership still rebuilds and imports the merged dictionary with the correct eviction behavior.
|
||||||
|
- [x] #4 Regression tests cover reorder-only revisits and stable merged revisions for equivalent retained sets.
|
||||||
|
<!-- AC:END -->
|
||||||
|
|
||||||
|
## Implementation Plan
|
||||||
|
|
||||||
|
<!-- SECTION:PLAN:BEGIN -->
|
||||||
|
1. Add a failing auto-sync regression showing that a revisit which only reorders MRU state should update `activeMediaIds` but skip merged rebuild/import.
|
||||||
|
2. Add a runtime-level regression showing `buildMergedDictionary` produces a stable revision for equivalent retained memberships regardless of input order.
|
||||||
|
3. Update merged-dictionary build normalization and auto-sync rebuild gating so rebuilds are driven by retained membership changes (or snapshot changes), not MRU reordering alone.
|
||||||
|
4. Re-run focused dictionary tests plus the local verification lanes impacted by the change.
|
||||||
|
<!-- SECTION:PLAN:END -->
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
<!-- SECTION:NOTES:BEGIN -->
|
||||||
|
Changed `src/main/runtime/character-dictionary-auto-sync.ts` so MRU order still updates in `activeMediaIds`, but merged rebuild/import now keys off retained membership changes rather than order-only reordering. Order-only revisits no longer force a merged ZIP rewrite or Yomitan reimport.
|
||||||
|
|
||||||
|
Changed `src/main/character-dictionary-runtime.ts` to canonicalize merged dictionary media ids before building, which makes merged revisions stable for equivalent retained memberships regardless of MRU ordering.
|
||||||
|
|
||||||
|
Updated regression coverage in `src/main/runtime/character-dictionary-auto-sync.test.ts` for reorder-only revisits and membership-change rebuilds, and extended `src/main/character-dictionary-runtime.test.ts` to assert stable merged revisions for reordered inputs. Verified with `bun test src/main/runtime/character-dictionary-auto-sync.test.ts src/main/character-dictionary-runtime.test.ts`, `bun run typecheck`, `bun run changelog:lint`, and `bun run test:fast`.
|
||||||
|
<!-- SECTION:NOTES:END -->
|
||||||
|
|
||||||
|
## Final Summary
|
||||||
|
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||||
|
Updated merged character-dictionary syncing so MRU ordering is still tracked for eviction in `src/main/runtime/character-dictionary-auto-sync.ts`, but reorder-only revisits no longer rebuild or reimport the merged dictionary unless retained membership or snapshot data changes. Canonicalized merged build input ordering in `src/main/character-dictionary-runtime.ts` so revisions remain stable for the same retained set regardless of MRU order, and added regressions in `src/main/runtime/character-dictionary-auto-sync.test.ts` plus `src/main/character-dictionary-runtime.test.ts`. Also updated `changes/character-dictionary-mru-state-recovery.md` to cover the new behavior.
|
||||||
|
|
||||||
|
Validation: `bun test src/main/runtime/character-dictionary-auto-sync.test.ts src/main/character-dictionary-runtime.test.ts`, `bun run typecheck`, `bun run changelog:lint`, `bun run test:fast`.
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
id: TASK-156
|
||||||
|
title: Fix docs-site Plausible geo attribution through analytics worker
|
||||||
|
status: Done
|
||||||
|
assignee: []
|
||||||
|
created_date: '2026-03-11 02:19'
|
||||||
|
updated_date: '2026-03-16 05:13'
|
||||||
|
labels:
|
||||||
|
- docs-site
|
||||||
|
- analytics
|
||||||
|
dependencies: []
|
||||||
|
priority: medium
|
||||||
|
ordinal: 99500
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
Investigate and fix missing city/region data for docs.subminer.moe visits in Plausible. The docs site already proxies analytics events to worker.subminer.moe, so the remaining work is to verify and correct the worker-side forwarding contract Plausible needs for geolocation.
|
||||||
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
<!-- AC:BEGIN -->
|
||||||
|
- [ ] #1 The analytics worker forwards Plausible event requests in a way that preserves the original client IP information needed for location attribution.
|
||||||
|
- [ ] #2 The docs-site analytics flow remains proxied through worker.subminer.moe after the fix.
|
||||||
|
- [ ] #3 Coverage or documentation records the worker-side header/forwarding requirement for Plausible geo reporting.
|
||||||
|
<!-- AC:END -->
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
<!-- SECTION:NOTES:BEGIN -->
|
||||||
|
Worker implementation lives in `/home/sudacode/projects/blog-proxy`, not in the SubMiner repo.
|
||||||
|
|
||||||
|
Patched `worker.js` to forward client IP via `x-forwarded-for`/`x-real-ip` from `cf-connecting-ip` (fallbacks retained) and added `worker.test.js` regression coverage.
|
||||||
|
|
||||||
|
Local verification in `blog-proxy`: `node --test worker.test.js` passes. Deployment not performed in this session.
|
||||||
|
<!-- SECTION:NOTES:END -->
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
id: TASK-157
|
||||||
|
title: Fix Cloudflare Pages watch path for docs-site
|
||||||
|
status: Done
|
||||||
|
assignee: []
|
||||||
|
created_date: '2026-03-10 20:15'
|
||||||
|
updated_date: '2026-03-16 05:13'
|
||||||
|
labels:
|
||||||
|
- docs-site
|
||||||
|
- cloudflare
|
||||||
|
dependencies: []
|
||||||
|
priority: medium
|
||||||
|
ordinal: 98500
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
Cloudflare Pages skipped a docs-site deployment after the docs repo moved into the main `SubMiner` repository. The documented/configured watch path uses `docs-site/**`, but Pages monorepo watch paths use a single `*` wildcard pattern. Correct the documented setting and leave a regression test so future repo moves or docs rewrites do not reintroduce the bad pattern.
|
||||||
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
<!-- AC:BEGIN -->
|
||||||
|
- [x] #1 Docs contributor guidance points Cloudflare Pages watch paths at `docs-site/*`, not `docs-site/**`.
|
||||||
|
- [x] #2 Regression coverage fails if the docs revert to the incorrect watch-path string.
|
||||||
|
- [x] #3 Implementation notes record that the Cloudflare dashboard setting must be updated manually and the docs deploy retriggered.
|
||||||
|
<!-- AC:END -->
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
<!-- SECTION:NOTES:BEGIN -->
|
||||||
|
Added docs regression coverage in `docs-site/docs-sync.test.ts` for the Pages watch-path string, then corrected the Cloudflare Pages instructions in `docs-site/README.md` and `docs-site/development.md`.
|
||||||
|
|
||||||
|
Manual follow-up still required outside git: update the Cloudflare Pages project include path from `docs-site/**` to `docs-site/*`, then trigger a fresh deployment against `main`.
|
||||||
|
<!-- SECTION:NOTES:END -->
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
---
|
||||||
|
id: TASK-158
|
||||||
|
title: Enforce generated config example drift checks
|
||||||
|
status: Done
|
||||||
|
assignee: []
|
||||||
|
created_date: '2026-03-10 20:35'
|
||||||
|
updated_date: '2026-03-16 05:13'
|
||||||
|
labels:
|
||||||
|
- config
|
||||||
|
- docs-site
|
||||||
|
dependencies: []
|
||||||
|
priority: medium
|
||||||
|
ordinal: 30500
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
The generated `config.example.jsonc` artifact is covered by generation-path tests, but there is no hard gate that fails when the checked-in example drifts from the canonical template. The in-repo docs-site copy can also drift silently.
|
||||||
|
|
||||||
|
Scope:
|
||||||
|
|
||||||
|
- add a first-party verification path that compares generated config-example content against committed artifacts
|
||||||
|
- fail fast when repo-root `config.example.jsonc` is stale or missing
|
||||||
|
- fail fast when `docs-site/public/config.example.jsonc` is stale or missing, when the docs site exists
|
||||||
|
- wire the verification into the normal gate and release flow
|
||||||
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
<!-- AC:BEGIN -->
|
||||||
|
- [x] #1 Automated verification fails when repo-root `config.example.jsonc` is missing or stale.
|
||||||
|
- [x] #2 Automated verification fails when in-repo docs-site `public/config.example.jsonc` is missing or stale, when docs-site exists.
|
||||||
|
- [x] #3 CI/release or equivalent project gates run the verification automatically.
|
||||||
|
<!-- AC:END -->
|
||||||
|
|
||||||
|
## Final Summary
|
||||||
|
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||||
|
Added `src/verify-config-example.ts`, which renders the canonical config template and compares it against the checked-in repo-root `config.example.jsonc` plus `docs-site/public/config.example.jsonc` when the docs site exists.
|
||||||
|
|
||||||
|
Wired the new verification into `package.json` as `bun run verify:config-example`, added regression coverage for missing and stale artifacts, and enforced the new check in both CI and release workflows.
|
||||||
|
|
||||||
|
Regenerated the checked-in config example artifacts so the new gate passes in the repo-local docs-site layout, and documented the release-step expectation in `docs/RELEASING.md`.
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
---
|
||||||
|
id: TASK-159
|
||||||
|
title: Create SubMiner automated testing skill for agents
|
||||||
|
status: Done
|
||||||
|
assignee:
|
||||||
|
- codex
|
||||||
|
created_date: '2026-03-11 05:55'
|
||||||
|
updated_date: '2026-03-16 05:13'
|
||||||
|
labels:
|
||||||
|
- tooling
|
||||||
|
- testing
|
||||||
|
- skills
|
||||||
|
dependencies: []
|
||||||
|
priority: medium
|
||||||
|
ordinal: 29500
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
Design and implement a SubMiner-specific skill that agents can use to verify code changes with automated checks across launcher, mpv, overlay, and related workflows.
|
||||||
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
<!-- AC:BEGIN -->
|
||||||
|
- [x] #1 Define the skill trigger surface and intended use cases for SubMiner change verification.
|
||||||
|
- [x] #2 Implement a SubMiner-specific skill package with concise workflow guidance and any required helper scripts or references.
|
||||||
|
- [x] #3 Document how agents should run automated verification for common SubMiner change types, including launcher/mpv/overlay-sensitive changes.
|
||||||
|
- [x] #4 Verify the skill itself is usable in this repo context.
|
||||||
|
<!-- AC:END -->
|
||||||
|
|
||||||
|
## Implementation Plan
|
||||||
|
|
||||||
|
<!-- SECTION:PLAN:BEGIN -->
|
||||||
|
1. Add a design doc at `docs/plans/2026-03-10-subminer-change-verification-design.md` capturing the approved skill contract, lane selection rules, helper script design, and trigger/workflow guidance.
|
||||||
|
2. Create an in-repo source package for the new SubMiner-specific verification skill with `SKILL.md` plus helper shell scripts for diff classification and coordinated verification/artifact capture.
|
||||||
|
3. Implement cheap-first verification logic: map changed paths to verification lanes, run repo-native commands, capture stdout/stderr and metadata under `.tmp/skill-verification/<timestamp>/`, and report pass/fail/skipped states with artifact paths.
|
||||||
|
4. Encode repo-specific heuristics for docs/config, launcher/plugin, runtime/dist, and optional real-GUI escalation guidance without making GUI launch the default.
|
||||||
|
5. Verify the skill locally by running the classifier/verifier against representative paths and confirming artifact generation and summaries.
|
||||||
|
6. Optionally install the finished skill to `~/.codex/skills/subminer-change-verification/` after user approval because that target is outside the writable sandbox.
|
||||||
|
<!-- SECTION:PLAN:END -->
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
<!-- SECTION:NOTES:BEGIN -->
|
||||||
|
Planned outputs: SKILL.md plus small helper shell scripts for diff classification and coordinated verification/artifact capture. Default to repo-native commands; only escalate to real GUI/mpv verification when affected paths or requested behavior require it.
|
||||||
|
|
||||||
|
2026-03-10: Brainstorming completed with user approval for a SubMiner-specific automated verification skill. Chosen shape: auto-triggering, cheap-first verification workflow with optional real GUI/mpv escalation and artifact capture.
|
||||||
|
|
||||||
|
2026-03-10: Added design doc at docs/plans/2026-03-10-subminer-change-verification-design.md and created the in-repo skill source at tools/skills/subminer-change-verification/.
|
||||||
|
|
||||||
|
2026-03-10: Verified classifier output with representative launcher/runtime/config/docs paths and ran the verifier in dry-run mode plus one real config lane (`bun run test:config`). Artifacts written under .tmp/skill-verification/20260310-231320 and .tmp/skill-verification/20260310-231326.
|
||||||
|
<!-- SECTION:NOTES:END -->
|
||||||
|
|
||||||
|
## Final Summary
|
||||||
|
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||||
|
Implemented a SubMiner-specific change verification skill source with a concise SKILL.md, a diff classifier, and a cheap-first verifier that captures artifacts under `.tmp/skill-verification/`. Verified the flow with representative path classification, a dry-run multi-lane plan, and a passing real config verification run.
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
---
|
||||||
|
id: TASK-160
|
||||||
|
title: Create repo-local scrum master orchestration skill
|
||||||
|
status: Done
|
||||||
|
assignee:
|
||||||
|
- codex
|
||||||
|
created_date: '2026-03-11 06:32'
|
||||||
|
updated_date: '2026-03-16 05:13'
|
||||||
|
labels:
|
||||||
|
- skills
|
||||||
|
- workflow
|
||||||
|
- backlog
|
||||||
|
- subagents
|
||||||
|
- automation
|
||||||
|
dependencies: []
|
||||||
|
priority: high
|
||||||
|
ordinal: 28500
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
Design and implement a repo-local skill that can turn incoming requests/issues into well-scoped Backlog tasks, then coordinate one or more subagents to implement and verify the resulting work using the repo's established skills and verification workflow.
|
||||||
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
<!-- AC:BEGIN -->
|
||||||
|
- [x] #1 Define the trigger surface, responsibilities, and limits of the scrum master skill for request intake, backlog updates, and execution orchestration.
|
||||||
|
- [x] #2 Implement the skill package with concise guidance for intake, task search/create/update, planning, and subagent dispatch.
|
||||||
|
- [x] #3 Specify how the skill coordinates with existing repo workflows, including Backlog.md requirements and SubMiner change verification.
|
||||||
|
- [x] #4 Verify the skill is usable in this repo context for at least one representative request flow.
|
||||||
|
<!-- AC:END -->
|
||||||
|
|
||||||
|
## Implementation Plan
|
||||||
|
|
||||||
|
<!-- SECTION:PLAN:BEGIN -->
|
||||||
|
1. Add a design doc at `docs/plans/2026-03-10-subminer-scrum-master-design.md` capturing the approved contract, backlog decision rules, orchestration policy, and verification handoff.
|
||||||
|
2. Create the repo-local skill at `.agents/skills/subminer-scrum-master/` with a concise `SKILL.md`.
|
||||||
|
3. Keep v1 instruction-heavy: backlog-or-not decision first, search/create/update task structure when needed, require planning before dispatch, use parent + subtasks for multi-part work, dispatch conservatively with explicit ownership, and require `subminer-change-verification` before handoff.
|
||||||
|
4. Include representative flows for trivial no-ticket work, single-task implementation, and multi-part parent+subtask execution.
|
||||||
|
5. Verify the skill in-repo with one representative request flow and update `TASK-160` with results.
|
||||||
|
<!-- SECTION:PLAN:END -->
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
<!-- SECTION:NOTES:BEGIN -->
|
||||||
|
V1 will be instruction-heavy rather than script-heavy. The skill will decide whether backlog is warranted, record plans before dispatch, create parent+subtasks when needed, dispatch conservatively with explicit ownership, and require `subminer-change-verification` before handoff.
|
||||||
|
|
||||||
|
2026-03-10: Added design doc at docs/plans/2026-03-10-subminer-scrum-master-design.md and created the repo-local skill at .agents/skills/subminer-scrum-master/.
|
||||||
|
|
||||||
|
2026-03-10: Verified the skill against a representative request flow ('Fix launcher auto-start pause bug and make sure it is verified') by checking that the instructions cover backlog decisioning, planning-before-dispatch, single-task execution, explicit worker ownership, and verification via subminer-change-verification.
|
||||||
|
<!-- SECTION:NOTES:END -->
|
||||||
|
|
||||||
|
## Final Summary
|
||||||
|
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||||
|
Implemented a repo-local `subminer-scrum-master` skill that assesses whether backlog tracking is needed, manages task structure and planning when appropriate, dispatches subagents conservatively, and requires verification before handoff. Verified the skill in-repo against a representative launcher bug workflow.
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
---
|
||||||
|
id: TASK-161
|
||||||
|
title: Add Arch Linux PKGBUILD and .SRCINFO for SubMiner release artifacts
|
||||||
|
status: Done
|
||||||
|
assignee:
|
||||||
|
- codex
|
||||||
|
created_date: '2026-03-11 07:50'
|
||||||
|
updated_date: '2026-03-16 05:13'
|
||||||
|
labels:
|
||||||
|
- packaging
|
||||||
|
- linux
|
||||||
|
- docs
|
||||||
|
dependencies: []
|
||||||
|
references:
|
||||||
|
- package.json
|
||||||
|
- .github/workflows/release.yml
|
||||||
|
- README.md
|
||||||
|
documentation:
|
||||||
|
- docs-site/development.md
|
||||||
|
- docs-site/installation.md
|
||||||
|
- docs/RELEASING.md
|
||||||
|
priority: medium
|
||||||
|
ordinal: 27500
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
Add repo-maintained Arch packaging metadata so Arch/AUR users can install the packaged SubMiner release artifacts without relying on npm. Cover the binary package flow that matches the current GitHub Releases distribution model and document the install path for Arch users.
|
||||||
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
<!-- AC:BEGIN -->
|
||||||
|
- [x] #1 A dedicated Arch packaging directory exists with a PKGBUILD and matching .SRCINFO for the SubMiner binary release flow.
|
||||||
|
- [x] #2 The package installs the shipped Linux release artifact and wrapper in paths consistent with current runtime auto-detection expectations.
|
||||||
|
- [x] #3 Package metadata declares the required Arch runtime dependencies for the packaged workflow.
|
||||||
|
- [x] #4 The packaging files remain isolated from the main app/release/docs surfaces so they can be moved to a separate repository later.
|
||||||
|
- [x] #5 The packaging metadata is validated with an appropriate local Arch packaging check or an explicitly documented limitation if the tool is unavailable in this environment.
|
||||||
|
<!-- AC:END -->
|
||||||
|
|
||||||
|
## Implementation Plan
|
||||||
|
|
||||||
|
<!-- SECTION:PLAN:BEGIN -->
|
||||||
|
1. Add isolated Arch packaging under packaging/arch/subminer-bin for the binary release flow that matches current GitHub Releases artifacts.
|
||||||
|
2. Install the Linux AppImage to /opt/SubMiner/SubMiner.AppImage, add PATH symlinks for SubMiner.AppImage and subminer, and ship optional plugin/theme/config assets under /usr/share/subminer.
|
||||||
|
3. Generate and commit matching .SRCINFO metadata in the same packaging directory.
|
||||||
|
4. Validate the PKGBUILD metadata locally with shell parsing plus makepkg --printsrcinfo and namcap if available.
|
||||||
|
5. Leave the new files isolated so they can be moved to a separate packaging repository later.
|
||||||
|
<!-- SECTION:PLAN:END -->
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
<!-- SECTION:NOTES:BEGIN -->
|
||||||
|
User requested no commit and no broad repo integration; keep the Arch packaging files isolated in a dedicated directory for later extraction.
|
||||||
|
|
||||||
|
Created isolated Arch packaging under packaging/arch/subminer-bin with PKGBUILD and generated .SRCINFO only; no docs or release workflow changes.
|
||||||
|
|
||||||
|
Validation run: bash -n PKGBUILD, makepkg --printsrcinfo > .SRCINFO, namcap PKGBUILD.
|
||||||
|
|
||||||
|
PKGBUILD uses current v0.5.6 GitHub release assets and recorded SHA-256 values for SubMiner-0.5.6.AppImage, subminer, and subminer-assets.tar.gz.
|
||||||
|
|
||||||
|
Per user follow-up, moved packaging/arch/subminer-bin out of the repo to /home/sudacode/packages/maintaining/subminer-bin for separate maintenance. Repo docs/workflows were still left untouched.
|
||||||
|
<!-- SECTION:NOTES:END -->
|
||||||
|
|
||||||
|
## Final Summary
|
||||||
|
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||||
|
Added an isolated Arch Linux packaging directory at packaging/arch/subminer-bin containing a `subminer-bin` PKGBUILD and generated `.SRCINFO`. The package installs the current GitHub release AppImage to `/opt/SubMiner/SubMiner.AppImage`, adds PATH access for `SubMiner.AppImage` and `subminer`, and ships optional plugin/theme/config assets under `/usr/share/subminer`. Verified locally with `bash -n PKGBUILD`, `makepkg --printsrcinfo`, and `namcap PKGBUILD`.
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user