mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-04-09 16:19:25 -07:00
Compare commits
3 Commits
v0.10.0
...
de111dbf8d
| Author | SHA1 | Date | |
|---|---|---|---|
|
de111dbf8d
|
|||
|
ea86f4e504
|
|||
|
bb54898747
|
@@ -1,20 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "subminer-local",
|
|
||||||
"interface": {
|
|
||||||
"displayName": "SubMiner Local"
|
|
||||||
},
|
|
||||||
"plugins": [
|
|
||||||
{
|
|
||||||
"name": "subminer-workflow",
|
|
||||||
"source": {
|
|
||||||
"source": "local",
|
|
||||||
"path": "./plugins/subminer-workflow"
|
|
||||||
},
|
|
||||||
"policy": {
|
|
||||||
"installation": "AVAILABLE",
|
|
||||||
"authentication": "ON_INSTALL"
|
|
||||||
},
|
|
||||||
"category": "Productivity"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,22 +1,127 @@
|
|||||||
---
|
---
|
||||||
name: 'subminer-change-verification'
|
name: "subminer-change-verification"
|
||||||
description: 'Compatibility shim. Canonical SubMiner change verification workflow now lives in the repo-local subminer-workflow plugin.'
|
description: "Use when working in the SubMiner repo and you need to verify code changes actually work. Covers targeted regression checks during debugging and pre-handoff verification, with cheap-first lane selection for config, docs, launcher/plugin, runtime-compat, and optional real-runtime escalation."
|
||||||
---
|
---
|
||||||
|
|
||||||
# Compatibility Shim
|
# SubMiner Change Verification
|
||||||
|
|
||||||
Canonical source:
|
Use this skill for SubMiner code changes. Default to cheap, repo-native verification first. Escalate only when the changed behavior actually depends on Electron, mpv, overlay/window tracking, or other GUI-sensitive runtime behavior.
|
||||||
|
|
||||||
- `plugins/subminer-workflow/skills/subminer-change-verification/SKILL.md`
|
## Scripts
|
||||||
|
|
||||||
Canonical helper scripts:
|
- `scripts/classify_subminer_diff.sh`
|
||||||
|
- Emits suggested lanes and flags from explicit paths or current git changes.
|
||||||
|
- `scripts/verify_subminer_change.sh`
|
||||||
|
- Runs selected lanes, captures artifacts, and writes a compact summary.
|
||||||
|
|
||||||
- `plugins/subminer-workflow/skills/subminer-change-verification/scripts/classify_subminer_diff.sh`
|
If you need an explicit installed path, use the directory that contains this `SKILL.md`. The helper scripts live under:
|
||||||
- `plugins/subminer-workflow/skills/subminer-change-verification/scripts/verify_subminer_change.sh`
|
|
||||||
|
|
||||||
When this shim is invoked:
|
```bash
|
||||||
|
export SUBMINER_VERIFY_SKILL="<path-to-skill>"
|
||||||
|
```
|
||||||
|
|
||||||
1. Read the canonical plugin-owned skill.
|
## Default workflow
|
||||||
2. Follow the plugin-owned skill as the source of truth.
|
|
||||||
3. Use the wrapper scripts in this shim directory only for compatibility with existing commands, docs, and backlog history.
|
1. Inspect the changed files or user-requested area.
|
||||||
4. Do not duplicate workflow changes here; update the plugin-owned skill and scripts instead.
|
2. Run the classifier unless you already know the right lane.
|
||||||
|
3. Run the verifier with the cheapest sufficient lane set.
|
||||||
|
4. If the classifier emits `flag:real-runtime-candidate`, do not jump straight to runtime verification. First run the non-runtime lanes.
|
||||||
|
5. Escalate to explicit `--lane real-runtime --allow-real-runtime` only when cheaper lanes cannot validate the behavior claim.
|
||||||
|
6. Return:
|
||||||
|
- verification summary
|
||||||
|
- exact commands run
|
||||||
|
- artifact paths
|
||||||
|
- skipped lanes and blockers
|
||||||
|
|
||||||
|
## Quick start
|
||||||
|
|
||||||
|
Repo-source quick start:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash .agents/skills/subminer-change-verification/scripts/classify_subminer_diff.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Installed-skill quick start:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash "$SUBMINER_VERIFY_SKILL/scripts/classify_subminer_diff.sh"
|
||||||
|
```
|
||||||
|
|
||||||
|
Classify explicit files:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash .agents/skills/subminer-change-verification/scripts/classify_subminer_diff.sh \
|
||||||
|
launcher/main.ts \
|
||||||
|
plugin/subminer/lifecycle.lua \
|
||||||
|
src/main/runtime/mpv-client-runtime-service.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
Run automatic lane selection:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash .agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Installed-skill form:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash "$SUBMINER_VERIFY_SKILL/scripts/verify_subminer_change.sh"
|
||||||
|
```
|
||||||
|
|
||||||
|
Run targeted lanes:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash .agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh \
|
||||||
|
--lane launcher-plugin \
|
||||||
|
--lane runtime-compat
|
||||||
|
```
|
||||||
|
|
||||||
|
Dry-run to inspect planned commands and artifact layout:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash .agents/skills/subminer-change-verification/scripts/verify_subminer_change.sh \
|
||||||
|
--dry-run \
|
||||||
|
launcher/main.ts \
|
||||||
|
src/main.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
## Lane guidance
|
||||||
|
|
||||||
|
- `docs`
|
||||||
|
- For `docs-site/`, `docs/`, and doc-only edits.
|
||||||
|
- `config`
|
||||||
|
- For `src/config/` and config-template-sensitive edits.
|
||||||
|
- `core`
|
||||||
|
- For general source changes where `typecheck` + `test:fast` is the best cheap signal.
|
||||||
|
- `launcher-plugin`
|
||||||
|
- For `launcher/`, `plugin/subminer/`, plugin gating scripts, and wrapper/mpv routing work.
|
||||||
|
- `runtime-compat`
|
||||||
|
- For `src/main*`, runtime/composer wiring, mpv/overlay services, window trackers, and dist-sensitive behavior.
|
||||||
|
- `real-runtime`
|
||||||
|
- Only after deliberate escalation.
|
||||||
|
|
||||||
|
## Real Runtime Escalation
|
||||||
|
|
||||||
|
Escalate only when the change claim depends on actual runtime behavior, for example:
|
||||||
|
|
||||||
|
- overlay appears, hides, or tracks a real mpv window
|
||||||
|
- mpv launch flags or pause-until-ready behavior
|
||||||
|
- plugin/socket/auto-start handshake under a real player
|
||||||
|
- macOS/window-tracker/focus-sensitive behavior
|
||||||
|
|
||||||
|
If the environment cannot support authoritative runtime verification, report the blocker explicitly. Do not silently downgrade a runtime-required claim to a pass.
|
||||||
|
|
||||||
|
## Artifact contract
|
||||||
|
|
||||||
|
The verifier writes under `.tmp/skill-verification/<timestamp>/`:
|
||||||
|
|
||||||
|
- `summary.json`
|
||||||
|
- `summary.txt`
|
||||||
|
- `classification.txt`
|
||||||
|
- `env.txt`
|
||||||
|
- `lanes.txt`
|
||||||
|
- `steps.tsv`
|
||||||
|
- `steps/*.stdout.log`
|
||||||
|
- `steps/*.stderr.log`
|
||||||
|
|
||||||
|
On failure, quote the exact failing command and point at the artifact directory.
|
||||||
|
|||||||
@@ -1,13 +1,163 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
usage() {
|
||||||
REPO_ROOT=$(cd "$SCRIPT_DIR/../../../.." && pwd)
|
cat <<'EOF'
|
||||||
TARGET="$REPO_ROOT/plugins/subminer-workflow/skills/subminer-change-verification/scripts/classify_subminer_diff.sh"
|
Usage: classify_subminer_diff.sh [path ...]
|
||||||
|
|
||||||
if [[ ! -x "$TARGET" ]]; then
|
Emit suggested verification lanes for explicit paths or current local git changes.
|
||||||
echo "Missing canonical script: $TARGET" >&2
|
|
||||||
exit 1
|
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
|
fi
|
||||||
|
|
||||||
exec "$TARGET" "$@"
|
declare -a PATHS=()
|
||||||
|
declare -a LANES=()
|
||||||
|
declare -a FLAGS=()
|
||||||
|
declare -a REASONS=()
|
||||||
|
|
||||||
|
if [[ $# -gt 0 ]]; then
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
PATHS+=("$1")
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
else
|
||||||
|
while IFS= read -r line; do
|
||||||
|
[[ -n "$line" ]] && PATHS+=("$line")
|
||||||
|
done < <(collect_git_paths)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ${#PATHS[@]} -eq 0 ]]; then
|
||||||
|
add_lane "core"
|
||||||
|
add_reason "no changed paths detected -> default to core"
|
||||||
|
fi
|
||||||
|
|
||||||
|
for path in "${PATHS[@]}"; do
|
||||||
|
specialized=0
|
||||||
|
|
||||||
|
case "$path" in
|
||||||
|
docs-site/*|docs/*|changes/*|README.md)
|
||||||
|
add_lane "docs"
|
||||||
|
add_reason "$path -> docs"
|
||||||
|
specialized=1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
case "$path" in
|
||||||
|
src/config/*|src/generate-config-example.ts|src/verify-config-example.ts|docs-site/public/config.example.jsonc|config.example.jsonc)
|
||||||
|
add_lane "config"
|
||||||
|
add_reason "$path -> config"
|
||||||
|
specialized=1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
case "$path" in
|
||||||
|
launcher/*|plugin/subminer/*|plugin/subminer.conf|scripts/test-plugin-*|scripts/get-mpv-window-*|scripts/configure-plugin-binary-path.mjs)
|
||||||
|
add_lane "launcher-plugin"
|
||||||
|
add_reason "$path -> launcher-plugin"
|
||||||
|
add_flag "real-runtime-candidate"
|
||||||
|
add_reason "$path -> real-runtime-candidate"
|
||||||
|
specialized=1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
case "$path" in
|
||||||
|
src/main.ts|src/main-entry.ts|src/preload.ts|src/main/*|src/core/services/mpv*|src/core/services/overlay*|src/renderer/*|src/window-trackers/*|scripts/prepare-build-assets.mjs)
|
||||||
|
add_lane "runtime-compat"
|
||||||
|
add_reason "$path -> runtime-compat"
|
||||||
|
add_flag "real-runtime-candidate"
|
||||||
|
add_reason "$path -> real-runtime-candidate"
|
||||||
|
specialized=1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [[ "$specialized" == "0" ]]; then
|
||||||
|
case "$path" in
|
||||||
|
src/*|package.json|tsconfig*.json|scripts/*|Makefile)
|
||||||
|
add_lane "core"
|
||||||
|
add_reason "$path -> core"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$path" in
|
||||||
|
package.json|src/main.ts|src/main-entry.ts|src/preload.ts)
|
||||||
|
add_flag "broad-impact"
|
||||||
|
add_reason "$path -> broad-impact"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ ${#LANES[@]} -eq 0 ]]; then
|
||||||
|
add_lane "core"
|
||||||
|
add_reason "no lane-specific matches -> default to core"
|
||||||
|
fi
|
||||||
|
|
||||||
|
for lane in "${LANES[@]}"; do
|
||||||
|
printf 'lane:%s\n' "$lane"
|
||||||
|
done
|
||||||
|
|
||||||
|
for flag in "${FLAGS[@]}"; do
|
||||||
|
printf 'flag:%s\n' "$flag"
|
||||||
|
done
|
||||||
|
|
||||||
|
for reason in "${REASONS[@]}"; do
|
||||||
|
printf 'reason:%s\n' "$reason"
|
||||||
|
done
|
||||||
|
|||||||
@@ -1,13 +1,566 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
usage() {
|
||||||
REPO_ROOT=$(cd "$SCRIPT_DIR/../../../.." && pwd)
|
cat <<'EOF'
|
||||||
TARGET="$REPO_ROOT/plugins/subminer-workflow/skills/subminer-change-verification/scripts/verify_subminer_change.sh"
|
Usage: verify_subminer_change.sh [options] [path ...]
|
||||||
|
|
||||||
if [[ ! -x "$TARGET" ]]; then
|
Options:
|
||||||
echo "Missing canonical script: $TARGET" >&2
|
--lane <name> Force a verification lane. Repeatable.
|
||||||
exit 1
|
--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
|
fi
|
||||||
|
|
||||||
exec "$TARGET" "$@"
|
if [[ -z "$ARTIFACT_DIR" ]]; then
|
||||||
|
mkdir -p "$REPO_ROOT/.tmp/skill-verification"
|
||||||
|
ARTIFACT_DIR="$REPO_ROOT/.tmp/skill-verification/$SESSION_ID"
|
||||||
|
fi
|
||||||
|
|
||||||
|
SESSION_HOME="$ARTIFACT_DIR/home"
|
||||||
|
SESSION_XDG_CONFIG_HOME="$ARTIFACT_DIR/xdg"
|
||||||
|
SESSION_MPV_DIR="$ARTIFACT_DIR/mpv"
|
||||||
|
SESSION_LOGS_DIR="$ARTIFACT_DIR/logs"
|
||||||
|
SESSION_MPV_LOG="$SESSION_LOGS_DIR/mpv.log"
|
||||||
|
|
||||||
|
mkdir -p "$ARTIFACT_DIR/steps" "$ARTIFACT_DIR/reports" "$SESSION_HOME" "$SESSION_XDG_CONFIG_HOME" "$SESSION_MPV_DIR" "$SESSION_LOGS_DIR"
|
||||||
|
STEPS_TSV="$ARTIFACT_DIR/steps.tsv"
|
||||||
|
: >"$STEPS_TSV"
|
||||||
|
|
||||||
|
trap cleanup EXIT
|
||||||
|
STARTED_AT=$(timestamp_iso)
|
||||||
|
|
||||||
|
if [[ ${#EXPLICIT_LANES[@]} -gt 0 ]]; then
|
||||||
|
local_lane=""
|
||||||
|
for local_lane in "${EXPLICIT_LANES[@]}"; do
|
||||||
|
add_lane "$local_lane"
|
||||||
|
done
|
||||||
|
printf 'reason:explicit lanes supplied\n' >"$ARTIFACT_DIR/classification.txt"
|
||||||
|
else
|
||||||
|
if [[ ${#PATH_ARGS[@]} -gt 0 ]]; then
|
||||||
|
CLASSIFIER_OUTPUT=$(bash "$SCRIPT_DIR/classify_subminer_diff.sh" "${PATH_ARGS[@]}")
|
||||||
|
else
|
||||||
|
CLASSIFIER_OUTPUT=$(bash "$SCRIPT_DIR/classify_subminer_diff.sh")
|
||||||
|
fi
|
||||||
|
printf '%s\n' "$CLASSIFIER_OUTPUT" >"$ARTIFACT_DIR/classification.txt"
|
||||||
|
while IFS= read -r line; do
|
||||||
|
case "$line" in
|
||||||
|
lane:*)
|
||||||
|
add_lane "${line#lane:}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done <<<"$CLASSIFIER_OUTPUT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
record_env
|
||||||
|
|
||||||
|
printf 'artifact_dir=%s\n' "$ARTIFACT_DIR"
|
||||||
|
printf 'selected_lanes=%s\n' "$(IFS=,; echo "${SELECTED_LANES[*]}")"
|
||||||
|
|
||||||
|
for lane in "${SELECTED_LANES[@]}"; do
|
||||||
|
case "$lane" in
|
||||||
|
docs)
|
||||||
|
run_step "$lane" "docs-test" "bun run docs:test" || break
|
||||||
|
[[ "$FAILED" == "1" ]] && break
|
||||||
|
run_step "$lane" "docs-build" "bun run docs:build" || break
|
||||||
|
;;
|
||||||
|
config)
|
||||||
|
run_step "$lane" "test-config" "bun run test:config" || break
|
||||||
|
;;
|
||||||
|
core)
|
||||||
|
run_step "$lane" "typecheck" "bun run typecheck" || break
|
||||||
|
[[ "$FAILED" == "1" ]] && break
|
||||||
|
run_step "$lane" "test-fast" "bun run test:fast" || break
|
||||||
|
;;
|
||||||
|
launcher-plugin)
|
||||||
|
run_step "$lane" "launcher-smoke-src" "bun run test:launcher:smoke:src" || break
|
||||||
|
[[ "$FAILED" == "1" ]] && break
|
||||||
|
run_step "$lane" "plugin-src" "bun run test:plugin:src" || break
|
||||||
|
;;
|
||||||
|
runtime-compat)
|
||||||
|
run_step "$lane" "build" "bun run build" || break
|
||||||
|
[[ "$FAILED" == "1" ]] && break
|
||||||
|
run_step "$lane" "test-runtime-compat" "bun run test:runtime:compat" || break
|
||||||
|
[[ "$FAILED" == "1" ]] && break
|
||||||
|
run_step "$lane" "test-smoke-dist" "bun run test:smoke:dist" || break
|
||||||
|
;;
|
||||||
|
real-runtime)
|
||||||
|
if [[ "$PATH_SELECTION_MODE" != "explicit" ]]; then
|
||||||
|
record_blocked_step \
|
||||||
|
"$lane" \
|
||||||
|
"real-runtime-guard" \
|
||||||
|
"real-runtime lane requires explicit paths; inferred local git changes are non-authoritative"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$ALLOW_REAL_RUNTIME" != "1" ]]; then
|
||||||
|
record_blocked_step \
|
||||||
|
"$lane" \
|
||||||
|
"real-runtime-guard" \
|
||||||
|
"real-runtime lane requested but --allow-real-runtime was not supplied"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! acquire_real_runtime_lease; then
|
||||||
|
record_blocked_step \
|
||||||
|
"$lane" \
|
||||||
|
"real-runtime-lease" \
|
||||||
|
"real-runtime lease already held; rerun after the active runtime verification finishes"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! REAL_RUNTIME_HELPER=$(find_real_runtime_helper); then
|
||||||
|
record_blocked_step \
|
||||||
|
"$lane" \
|
||||||
|
"real-runtime-helper" \
|
||||||
|
"real-runtime helper not implemented yet"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf -v REAL_RUNTIME_COMMAND \
|
||||||
|
'SESSION_ID=%q HOME=%q XDG_CONFIG_HOME=%q SUBMINER_MPV_LOG=%q bash %q' \
|
||||||
|
"$SESSION_ID" \
|
||||||
|
"$SESSION_HOME" \
|
||||||
|
"$SESSION_XDG_CONFIG_HOME" \
|
||||||
|
"$SESSION_MPV_LOG" \
|
||||||
|
"$REAL_RUNTIME_HELPER"
|
||||||
|
|
||||||
|
run_step "$lane" "real-runtime-smoke" "$REAL_RUNTIME_COMMAND" || break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
record_failed_step "$lane" "lane-validation" "unknown lane: $lane"
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [[ "$FAILED" == "1" || "$BLOCKED" == "1" ]]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
FINISHED_AT=$(timestamp_iso)
|
||||||
|
compute_final_status
|
||||||
|
write_summary_files
|
||||||
|
|
||||||
|
printf 'status=%s\n' "$FINAL_STATUS"
|
||||||
|
printf 'artifact_dir=%s\n' "$ARTIFACT_DIR"
|
||||||
|
|
||||||
|
case "$FINAL_STATUS" in
|
||||||
|
failed)
|
||||||
|
printf 'result=failed\n'
|
||||||
|
printf 'failure_command=%s\n' "$FAILURE_COMMAND"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
blocked)
|
||||||
|
printf 'result=blocked\n'
|
||||||
|
exit 2
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
printf 'result=ok\n'
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|||||||
@@ -1,18 +1,146 @@
|
|||||||
---
|
---
|
||||||
name: 'subminer-scrum-master'
|
name: "subminer-scrum-master"
|
||||||
description: 'Compatibility shim. Canonical SubMiner scrum-master workflow now lives in the repo-local subminer-workflow plugin.'
|
description: "Use in the SubMiner repo when a request should be turned into planned work and driven through execution. Assesses whether backlog tracking is warranted, creates or updates tasks when needed, records a plan, dispatches one or more subagents, and requires verification before handoff."
|
||||||
---
|
---
|
||||||
|
|
||||||
# Compatibility Shim
|
# SubMiner Scrum Master
|
||||||
|
|
||||||
Canonical source:
|
Own workflow, not code by default.
|
||||||
|
|
||||||
- `plugins/subminer-workflow/skills/subminer-scrum-master/SKILL.md`
|
Use this skill when the user gives a feature request, bug report, issue, refactor, or implementation ask and the agent should manage intake, planning, backlog hygiene, worker dispatch, and verification through completion.
|
||||||
|
|
||||||
When this shim is invoked:
|
## Core Rules
|
||||||
|
|
||||||
1. Read the canonical plugin-owned skill.
|
1. Decide first whether backlog tracking is warranted.
|
||||||
2. Follow the plugin-owned skill as the source of truth.
|
2. If backlog is needed, search first. Update existing work when it clearly matches.
|
||||||
3. Do not duplicate workflow changes here; update the plugin-owned skill instead.
|
3. If backlog is not needed, keep the process light. Do not invent ticket ceremony.
|
||||||
|
4. Record a plan before dispatching coding work.
|
||||||
|
5. Use parent + subtasks for multi-part work when backlog is used.
|
||||||
|
6. Dispatch conservatively. Parallelize only disjoint write scopes.
|
||||||
|
7. Require verification before handoff, typically via `subminer-change-verification`.
|
||||||
|
8. Report backlog actions, dispatched workers, verification, blockers, and remaining risks.
|
||||||
|
|
||||||
This shim exists so existing repo references and prompts keep resolving during the migration to the repo-local plugin workflow.
|
## Backlog Decision
|
||||||
|
|
||||||
|
Skip backlog when the request is:
|
||||||
|
- question only
|
||||||
|
- obvious mechanical edit
|
||||||
|
- tiny isolated change with no real planning
|
||||||
|
|
||||||
|
Use backlog when the work:
|
||||||
|
- needs planning or scope decisions
|
||||||
|
- spans multiple phases or subsystems
|
||||||
|
- is likely to need subagent dispatch
|
||||||
|
- should remain traceable for handoff/resume
|
||||||
|
|
||||||
|
If backlog is used:
|
||||||
|
- search existing tasks first
|
||||||
|
- create/update a standalone task for one focused deliverable
|
||||||
|
- create/update a parent task plus subtasks for multi-part work
|
||||||
|
- record the implementation plan in the task before implementation begins
|
||||||
|
|
||||||
|
## Intake Workflow
|
||||||
|
|
||||||
|
1. Parse the request.
|
||||||
|
Classify it as question, mechanical edit, bugfix, feature, refactor, investigation, or follow-up.
|
||||||
|
2. Decide whether backlog is needed.
|
||||||
|
3. If backlog is needed:
|
||||||
|
- search first
|
||||||
|
- update existing task if clearly relevant
|
||||||
|
- otherwise create the right structure
|
||||||
|
- write the implementation plan before dispatch
|
||||||
|
4. If backlog is skipped:
|
||||||
|
- write a short working plan in-thread
|
||||||
|
- proceed without fake ticketing
|
||||||
|
5. Choose execution mode:
|
||||||
|
- no subagents for trivial work
|
||||||
|
- one worker for focused work
|
||||||
|
- parallel workers only for disjoint scopes
|
||||||
|
6. Run verification before handoff.
|
||||||
|
|
||||||
|
## Dispatch Rules
|
||||||
|
|
||||||
|
The scrum master orchestrates. Workers implement.
|
||||||
|
|
||||||
|
- Do not become the default implementer unless delegation is unnecessary.
|
||||||
|
- Do not parallelize overlapping files or tightly coupled runtime work.
|
||||||
|
- Give every worker explicit ownership of files/modules.
|
||||||
|
- Tell every worker other agents may be active and they must not revert unrelated edits.
|
||||||
|
- Require each worker to report:
|
||||||
|
- changed files
|
||||||
|
- tests run
|
||||||
|
- blockers
|
||||||
|
|
||||||
|
Use worker agents for implementation and explorer agents only for bounded codebase questions.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
Every nontrivial code task gets verification.
|
||||||
|
|
||||||
|
Preferred flow:
|
||||||
|
1. use `subminer-change-verification`
|
||||||
|
2. start with the cheapest sufficient lane
|
||||||
|
3. escalate only when needed
|
||||||
|
4. if worker verification is sufficient, accept it or run one final consolidating pass
|
||||||
|
|
||||||
|
Never hand off nontrivial work without stating what was verified and what was skipped.
|
||||||
|
|
||||||
|
## Pre-Handoff Policy Checks (Required)
|
||||||
|
|
||||||
|
Before handoff, always ask and answer both of these questions explicitly:
|
||||||
|
|
||||||
|
1. **Docs update required?**
|
||||||
|
2. **Changelog fragment required?**
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
- Do not assume silence implies "no." Record an explicit yes/no decision for each item.
|
||||||
|
- If the answer is yes, either complete the update or report the blocker before handoff.
|
||||||
|
- Include the final answers in the handoff summary even when both answers are "no."
|
||||||
|
|
||||||
|
## Failure / Scope Handling
|
||||||
|
|
||||||
|
- If a worker hits ambiguity, pause and ask the user.
|
||||||
|
- If verification fails, either:
|
||||||
|
- send the worker back with exact failure context, or
|
||||||
|
- fix it directly if it is tiny and clearly in scope
|
||||||
|
- If new scope appears, revisit backlog structure before silently expanding work.
|
||||||
|
|
||||||
|
## Representative Flows
|
||||||
|
|
||||||
|
### Trivial no-ticket work
|
||||||
|
|
||||||
|
- decide backlog is unnecessary
|
||||||
|
- keep a short plan
|
||||||
|
- implement directly or with one worker if helpful
|
||||||
|
- run targeted verification
|
||||||
|
- report outcome concisely
|
||||||
|
|
||||||
|
### Single-task implementation
|
||||||
|
|
||||||
|
- search/create/update one task
|
||||||
|
- record plan
|
||||||
|
- dispatch one worker
|
||||||
|
- integrate
|
||||||
|
- verify
|
||||||
|
- update task and report outcome
|
||||||
|
|
||||||
|
### Parent + subtasks execution
|
||||||
|
|
||||||
|
- search/create/update parent task
|
||||||
|
- create subtasks for distinct deliverables/phases
|
||||||
|
- record sequencing in the plan
|
||||||
|
- dispatch workers only where scopes are disjoint
|
||||||
|
- integrate
|
||||||
|
- run consolidated verification
|
||||||
|
- update task state and report outcome
|
||||||
|
|
||||||
|
## Output Expectations
|
||||||
|
|
||||||
|
At the end, report:
|
||||||
|
- whether backlog was used and what changed
|
||||||
|
- which workers were dispatched and what they owned
|
||||||
|
- what verification ran
|
||||||
|
- explicit answers to:
|
||||||
|
- docs update required?
|
||||||
|
- changelog fragment required?
|
||||||
|
- blockers, skips, and risks
|
||||||
|
|||||||
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
@@ -61,16 +61,6 @@ jobs:
|
|||||||
- name: Test suite (source)
|
- name: Test suite (source)
|
||||||
run: bun run test:fast
|
run: bun run test:fast
|
||||||
|
|
||||||
- name: Coverage suite (maintained source lane)
|
|
||||||
run: bun run test:coverage:src
|
|
||||||
|
|
||||||
- name: Upload coverage artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: coverage-test-src
|
|
||||||
path: coverage/test-src/lcov.info
|
|
||||||
if-no-files-found: error
|
|
||||||
|
|
||||||
- name: Launcher smoke suite (source)
|
- name: Launcher smoke suite (source)
|
||||||
run: bun run test:launcher:smoke:src
|
run: bun run test:launcher:smoke:src
|
||||||
|
|
||||||
|
|||||||
10
.github/workflows/release.yml
vendored
10
.github/workflows/release.yml
vendored
@@ -49,16 +49,6 @@ jobs:
|
|||||||
- name: Test suite (source)
|
- name: Test suite (source)
|
||||||
run: bun run test:fast
|
run: bun run test:fast
|
||||||
|
|
||||||
- name: Coverage suite (maintained source lane)
|
|
||||||
run: bun run test:coverage:src
|
|
||||||
|
|
||||||
- name: Upload coverage artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: coverage-test-src
|
|
||||||
path: coverage/test-src/lcov.info
|
|
||||||
if-no-files-found: error
|
|
||||||
|
|
||||||
- name: Launcher smoke suite (source)
|
- name: Launcher smoke suite (source)
|
||||||
run: bun run test:launcher:smoke:src
|
run: bun run test:launcher:smoke:src
|
||||||
|
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,7 +9,6 @@ out/
|
|||||||
dist/
|
dist/
|
||||||
release/
|
release/
|
||||||
build/yomitan/
|
build/yomitan/
|
||||||
coverage/
|
|
||||||
|
|
||||||
# Launcher build artifact (produced by make build-launcher)
|
# Launcher build artifact (produced by make build-launcher)
|
||||||
/subminer
|
/subminer
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ This project uses Backlog.md MCP for all task and project management activities.
|
|||||||
- **When to read it**: BEFORE creating tasks, or when you're unsure whether to track work
|
- **When to read it**: BEFORE creating tasks, or when you're unsure whether to track work
|
||||||
|
|
||||||
These guides cover:
|
These guides cover:
|
||||||
|
|
||||||
- Decision framework for when to create tasks
|
- Decision framework for when to create tasks
|
||||||
- Search-first workflow to avoid duplicates
|
- Search-first workflow to avoid duplicates
|
||||||
- Links to detailed guides for task creation, execution, and finalization
|
- Links to detailed guides for task creation, execution, and finalization
|
||||||
|
|||||||
266
Backlog.md
266
Backlog.md
@@ -1,266 +0,0 @@
|
|||||||
# Backlog
|
|
||||||
|
|
||||||
Purpose: lightweight repo-local task board. Seeded with current testing / coverage work.
|
|
||||||
|
|
||||||
Status keys:
|
|
||||||
|
|
||||||
- `todo`: not started
|
|
||||||
- `doing`: in progress
|
|
||||||
- `blocked`: waiting
|
|
||||||
- `done`: shipped
|
|
||||||
|
|
||||||
Priority keys:
|
|
||||||
|
|
||||||
- `P0`: urgent / release-risk
|
|
||||||
- `P1`: high value
|
|
||||||
- `P2`: useful cleanup
|
|
||||||
- `P3`: nice-to-have
|
|
||||||
|
|
||||||
## Active
|
|
||||||
|
|
||||||
| ID | Pri | Status | Area | Title |
|
|
||||||
| ------ | --- | ------ | -------------- | --------------------------------------------------- |
|
|
||||||
| SM-013 | P1 | done | review-followup | Address PR #36 CodeRabbit action items |
|
|
||||||
|
|
||||||
## Ready
|
|
||||||
|
|
||||||
| ID | Pri | Status | Area | Title |
|
|
||||||
| ------ | --- | ------ | ----------------- | ---------------------------------------------------------------- |
|
|
||||||
| SM-001 | P1 | todo | launcher | Add tests for CLI parser and args normalizer |
|
|
||||||
| SM-002 | P1 | todo | immersion-tracker | Backfill tests for uncovered query exports |
|
|
||||||
| SM-003 | P1 | todo | anki | Add focused field-grouping service + merge edge-case tests |
|
|
||||||
| SM-004 | P2 | todo | tests | Extract shared test utils for deps factories and polling helpers |
|
|
||||||
| SM-005 | P2 | todo | tests | Strengthen weak assertions in app-ready and IPC tests |
|
|
||||||
| SM-006 | P2 | todo | tests | Break up monolithic youtube-flow and subtitle-sidebar tests |
|
|
||||||
| SM-007 | P2 | todo | anilist | Add tests for AniList rate limiter |
|
|
||||||
| SM-008 | P3 | todo | subtitles | Add core subtitle-position persistence/path tests |
|
|
||||||
| SM-009 | P3 | todo | tokenizer | Add tests for JLPT token filter |
|
|
||||||
| SM-010 | P1 | todo | immersion-tracker | Refactor storage + immersion-tracker service into focused modules |
|
|
||||||
| SM-011 | P1 | done | tests | Add coverage reporting for maintained test lanes |
|
|
||||||
| SM-012 | P2 | done | config/runtime | Replace JSON serialize-clone helpers with structured cloning |
|
|
||||||
|
|
||||||
## Icebox
|
|
||||||
|
|
||||||
None.
|
|
||||||
|
|
||||||
## Ticket Details
|
|
||||||
|
|
||||||
### SM-001
|
|
||||||
|
|
||||||
Title: Add tests for CLI parser and args normalizer
|
|
||||||
Priority: P1
|
|
||||||
Status: done
|
|
||||||
Scope:
|
|
||||||
|
|
||||||
- `launcher/config/cli-parser-builder.ts`
|
|
||||||
- `launcher/config/args-normalizer.ts`
|
|
||||||
Acceptance:
|
|
||||||
- root options parsing covered
|
|
||||||
- subcommand routing covered
|
|
||||||
- invalid action / invalid log level / invalid backend cases covered
|
|
||||||
- target classification covered: file, directory, URL, invalid
|
|
||||||
|
|
||||||
### SM-002
|
|
||||||
|
|
||||||
Title: Backfill tests for uncovered query exports
|
|
||||||
Priority: P1
|
|
||||||
Status: todo
|
|
||||||
Scope:
|
|
||||||
|
|
||||||
- `src/core/services/immersion-tracker/query-*.ts`
|
|
||||||
Targets:
|
|
||||||
- headword helpers
|
|
||||||
- anime/media detail helpers not covered by existing wrapper tests
|
|
||||||
- lexical detail / appearance helpers
|
|
||||||
- maintenance helpers beyond `deleteSession` and `upsertCoverArt`
|
|
||||||
Acceptance:
|
|
||||||
- every exported query helper either directly tested or explicitly justified as covered elsewhere
|
|
||||||
- at least one focused regression per complex SQL branch / aggregation branch
|
|
||||||
|
|
||||||
### SM-003
|
|
||||||
|
|
||||||
Title: Add focused field-grouping service + merge edge-case tests
|
|
||||||
Priority: P1
|
|
||||||
Status: todo
|
|
||||||
Scope:
|
|
||||||
|
|
||||||
- `src/anki-integration/field-grouping.ts`
|
|
||||||
- `src/anki-integration/field-grouping-merge.ts`
|
|
||||||
Acceptance:
|
|
||||||
- auto/manual/disabled flow branches covered
|
|
||||||
- duplicate-card preview failure path covered
|
|
||||||
- merge edge cases covered: empty fields, generated media fallback, strict grouped spans, audio synchronization
|
|
||||||
|
|
||||||
### SM-004
|
|
||||||
|
|
||||||
Title: Extract shared test utils for deps factories and polling helpers
|
|
||||||
Priority: P2
|
|
||||||
Status: todo
|
|
||||||
Scope:
|
|
||||||
|
|
||||||
- common `makeDeps` / `createDeps` helpers
|
|
||||||
- common `waitForCondition`
|
|
||||||
Acceptance:
|
|
||||||
- shared helper module added
|
|
||||||
- at least 3 duplicated polling helpers removed
|
|
||||||
- at least 5 duplicated deps factories consolidated or clearly prepared for follow-up migration
|
|
||||||
|
|
||||||
### SM-005
|
|
||||||
|
|
||||||
Title: Strengthen weak assertions in app-ready and IPC tests
|
|
||||||
Priority: P2
|
|
||||||
Status: todo
|
|
||||||
Scope:
|
|
||||||
|
|
||||||
- `src/core/services/app-ready.test.ts`
|
|
||||||
- `src/core/services/ipc.test.ts`
|
|
||||||
Acceptance:
|
|
||||||
- replace broad `assert.ok(...)` presence checks with exact value / order assertions where expected value known
|
|
||||||
- handler registration tests assert channel-specific behavior, not only existence
|
|
||||||
|
|
||||||
### SM-006
|
|
||||||
|
|
||||||
Title: Break up monolithic youtube-flow and subtitle-sidebar tests
|
|
||||||
Priority: P2
|
|
||||||
Status: todo
|
|
||||||
Scope:
|
|
||||||
|
|
||||||
- `src/main/runtime/youtube-flow.test.ts`
|
|
||||||
- `src/renderer/modals/subtitle-sidebar.test.ts`
|
|
||||||
Acceptance:
|
|
||||||
- reduce single-test breadth
|
|
||||||
- split largest tests into focused cases by behavior
|
|
||||||
- keep semantics unchanged
|
|
||||||
|
|
||||||
### SM-007
|
|
||||||
|
|
||||||
Title: Add tests for AniList rate limiter
|
|
||||||
Priority: P2
|
|
||||||
Status: todo
|
|
||||||
Scope:
|
|
||||||
|
|
||||||
- `src/core/services/anilist/rate-limiter.ts`
|
|
||||||
Acceptance:
|
|
||||||
- capacity-window wait behavior covered
|
|
||||||
- `x-ratelimit-remaining` + reset handling covered
|
|
||||||
- `retry-after` handling covered
|
|
||||||
|
|
||||||
### SM-008
|
|
||||||
|
|
||||||
Title: Add core subtitle-position persistence/path tests
|
|
||||||
Priority: P3
|
|
||||||
Status: todo
|
|
||||||
Scope:
|
|
||||||
|
|
||||||
- `src/core/services/subtitle-position.ts`
|
|
||||||
Acceptance:
|
|
||||||
- save/load persistence covered
|
|
||||||
- fallback behavior covered
|
|
||||||
- path normalization behavior covered for URL vs local target
|
|
||||||
|
|
||||||
### SM-009
|
|
||||||
|
|
||||||
Title: Add tests for JLPT token filter
|
|
||||||
Priority: P3
|
|
||||||
Status: todo
|
|
||||||
Scope:
|
|
||||||
|
|
||||||
- `src/core/services/jlpt-token-filter.ts`
|
|
||||||
Acceptance:
|
|
||||||
- excluded term membership covered
|
|
||||||
- ignored POS1 membership covered
|
|
||||||
- exported list / entry consistency covered
|
|
||||||
|
|
||||||
### SM-010
|
|
||||||
|
|
||||||
Title: Refactor storage + immersion-tracker service into focused layers without API changes
|
|
||||||
Priority: P1
|
|
||||||
Status: todo
|
|
||||||
Scope:
|
|
||||||
|
|
||||||
- `src/core/database/storage/storage.ts`
|
|
||||||
- `src/core/database/storage/schema.ts`
|
|
||||||
- `src/core/database/storage/cover-blob.ts`
|
|
||||||
- `src/core/database/storage/records.ts`
|
|
||||||
- `src/core/database/storage/write-path.ts`
|
|
||||||
- `src/core/services/immersion-tracker/youtube.ts`
|
|
||||||
- `src/core/services/immersion-tracker/youtube-manager.ts`
|
|
||||||
- `src/core/services/immersion-tracker/write-queue.ts`
|
|
||||||
- `src/core/services/immersion-tracker/immersion-tracker-service.ts`
|
|
||||||
|
|
||||||
Acceptance:
|
|
||||||
|
|
||||||
- behavior and public API remain unchanged for all callers
|
|
||||||
- `storage.ts` responsibilities split into DDL/migrations, cover blob helpers, record CRUD, and write-path execution
|
|
||||||
- `immersion-tracker-service.ts` reduces to session state, media change orchestration, query proxies, and lifecycle
|
|
||||||
- YouTube code split into pure utilities, a stateful manager (`YouTubeManager`), and a dedicated write queue (`WriteQueue`)
|
|
||||||
- removed `storage.ts` is replaced with focused modules and updated imports
|
|
||||||
- no API or migration regressions; existing tests for trackers/storage coverage remain green or receive focused updates
|
|
||||||
|
|
||||||
### SM-011
|
|
||||||
|
|
||||||
Title: Add coverage reporting for maintained test lanes
|
|
||||||
Priority: P1
|
|
||||||
Status: done
|
|
||||||
Scope:
|
|
||||||
|
|
||||||
- `package.json`
|
|
||||||
- CI workflow files under `.github/`
|
|
||||||
- `docs/workflow/verification.md`
|
|
||||||
Acceptance:
|
|
||||||
- at least one maintained test lane emits machine-readable coverage output
|
|
||||||
- CI surfaces coverage as an artifact, summary, or check output
|
|
||||||
- local contributor path for coverage is documented
|
|
||||||
- chosen coverage path works with Bun/TypeScript lanes already maintained by the repo
|
|
||||||
Implementation note:
|
|
||||||
- Added `bun run test:coverage:src` for the maintained source lane via a sharded coverage runner, with merged LCOV output at `coverage/test-src/lcov.info` and CI/release artifact upload as `coverage-test-src`.
|
|
||||||
|
|
||||||
### SM-012
|
|
||||||
|
|
||||||
Title: Replace JSON serialize-clone helpers with structured cloning
|
|
||||||
Priority: P2
|
|
||||||
Status: todo
|
|
||||||
Scope:
|
|
||||||
|
|
||||||
- `src/runtime-options.ts`
|
|
||||||
- `src/config/definitions.ts`
|
|
||||||
- `src/config/service.ts`
|
|
||||||
- `src/main/controller-config-update.ts`
|
|
||||||
Acceptance:
|
|
||||||
- runtime/config clone helpers stop using `JSON.parse(JSON.stringify(...))`
|
|
||||||
- replacement preserves current behavior for plain config/runtime objects
|
|
||||||
- focused tests cover clone/merge behavior that could regress during the swap
|
|
||||||
- no new clone helper is introduced in these paths without a documented reason
|
|
||||||
|
|
||||||
Done:
|
|
||||||
|
|
||||||
- replaced JSON serialize-clone call sites in runtime/config/controller update paths with `structuredClone`
|
|
||||||
- updated focused tests and fixtures to cover detached clone behavior and guard against regressions
|
|
||||||
|
|
||||||
### SM-013
|
|
||||||
|
|
||||||
Title: Address PR #36 CodeRabbit action items
|
|
||||||
Priority: P1
|
|
||||||
Status: done
|
|
||||||
Scope:
|
|
||||||
|
|
||||||
- `plugins/subminer-workflow/skills/subminer-change-verification/scripts/verify_subminer_change.sh`
|
|
||||||
- `scripts/subminer-change-verification.test.ts`
|
|
||||||
- `src/core/services/immersion-tracker/query-sessions.ts`
|
|
||||||
- `src/core/services/immersion-tracker/query-trends.ts`
|
|
||||||
- `src/core/services/immersion-tracker/maintenance.ts`
|
|
||||||
- `src/main/boot/services.ts`
|
|
||||||
- `src/main/character-dictionary-runtime/zip.test.ts`
|
|
||||||
Acceptance:
|
|
||||||
- fix valid open CodeRabbit findings on PR #36
|
|
||||||
- add focused regression coverage for behavior changes where practical
|
|
||||||
- verify touched tests plus typecheck stay green
|
|
||||||
|
|
||||||
Done:
|
|
||||||
|
|
||||||
- hardened `--artifact-dir` validation in the verification script
|
|
||||||
- fixed trend aggregation rounding and monthly ratio bucketing
|
|
||||||
- preserved unwatched anime episodes in episode queries
|
|
||||||
- restored seconds-based aggregate timestamps in shared maintenance
|
|
||||||
- fixed the startup refactor compile break by making the predicates local at the call site
|
|
||||||
- verified with `bun test src/core/services/immersion-tracker/__tests__/query.test.ts src/core/services/immersion-tracker/__tests__/query-split-modules.test.ts` and `bun run typecheck`
|
|
||||||
91
CHANGELOG.md
91
CHANGELOG.md
@@ -1,96 +1,5 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## v0.10.0 (2026-03-29)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- Integrations: Replaced the deprecated Discord Rich Presence wrapper with the maintained `@xhayper/discord-rpc` package.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Stats: Fixed stats startup so the immersion tracker can run when `Bun.serve` is unavailable.
|
|
||||||
- Stats: Stats server now falls back to a Node `http` listener in Electron/runtime paths that do not expose Bun.
|
|
||||||
- Overlay: Fixed the macOS visible-overlay toggle path so manual hides stay hidden and the plugin uses the explicit visible-overlay toggle command.
|
|
||||||
- Subtitle Sidebar: Restored macOS mpv passthrough while the overlay subtitle sidebar is open so clicks outside the sidebar can refocus mpv and keep native keybindings working.
|
|
||||||
|
|
||||||
### Internal
|
|
||||||
- Release: Added a maintained source coverage lane that shards Bun coverage one test file at a time and merges LCOV output into `coverage/test-src/lcov.info`.
|
|
||||||
- Release: CI and release quality-gate now upload the merged source-lane LCOV artifact for inspection.
|
|
||||||
- Runtime: Extracted remaining inline runtime logic from `src/main.ts` into dedicated runtime modules and composer helpers.
|
|
||||||
- Runtime: Added focused regression tests for the extracted runtime/composer boundaries.
|
|
||||||
- Runtime: Updated task tracking notes to mark TASK-238.6 complete and confirm follow-on boot-phase split can be deferred.
|
|
||||||
- Runtime: Split `src/main.ts` boot wiring into dedicated `src/main/boot/services.ts`, `src/main/boot/runtimes.ts`, and `src/main/boot/handlers.ts` modules.
|
|
||||||
- Runtime: Added focused tests for the new boot-phase seams and kept the startup/typecheck/build verification lanes green.
|
|
||||||
- Runtime: Updated internal architecture/task docs to record the boot-phase split and new ownership boundary.
|
|
||||||
|
|
||||||
## v0.9.3 (2026-03-25)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- Launcher: Moved YouTube primary subtitle language defaults to `youtube.primarySubLanguages`.
|
|
||||||
- Launcher: Removed the placeholder YouTube subtitle retime step and now uses downloaded primary subtitle tracks directly, so there is no fake path rewrite before playback/sidebar loading.
|
|
||||||
- YouTube: Removed the `src/core/services/youtube/retime` helper and its tests after retiring the internal retime strategy.
|
|
||||||
- Docs: Clarified optional `alass` / `ffsubsync` subtitle-sync requirements and setup steps, including fallback behavior when sync tools are absent.
|
|
||||||
- Launcher: Removed the old `youtubeSubgen.primarySubLanguages` config path from the generated config and docs.
|
|
||||||
|
|
||||||
## v0.9.2 (2026-03-25)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Overlay: Fixed overlay pointer tracking so Windows click-through toggles immediately when the cursor enters or leaves subtitle regions, without waiting for a later hover resync.
|
|
||||||
- Overlay: Fixed Windows overlay window tracking on scaled displays by converting native tracked window bounds to Electron DIP coordinates before applying overlay bounds.
|
|
||||||
- Launcher: Fixed Windows direct `--youtube-play` startup so MPV boots reliably, stays paused until the app-owned subtitle flow is ready, and reuses an already-running SubMiner instance when available.
|
|
||||||
- Launcher: Fixed standalone Windows `--youtube-play` sessions so closing MPV fully exits SubMiner instead of leaving hidden overlay windows or a background process behind.
|
|
||||||
- Overlay: Fixed `subminer <youtube-url>` on Linux so the YouTube playback flow waits for Yomitan to load before creating the overlay window, avoiding the broken lookup popup state that previously required a manual overlay refresh.
|
|
||||||
|
|
||||||
## v0.9.1 (2026-03-24)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- Release: Reduced packaged release size by excluding duplicate `extraResources` payload and pruning docs, tests, sourcemaps, and other source-only files from Electron bundles.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Overlay: Restored controller navigation and lookup/mining controls while the subtitle sidebar is open, while keeping true modal dialogs blocking controller actions.
|
|
||||||
- Tokenizer: Fixed subtitle annotation clearing so explanatory contrast endings like `んですけど` are excluded consistently across the shared tokenizer filter and annotation stage.
|
|
||||||
|
|
||||||
## v0.9.0 (2026-03-23)
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- Docs: Added a new WebSocket / Texthooker API and integration guide covering WebSocket payloads, custom client patterns, mpv plugin automation, and webhook-style relay examples. Linked from configuration and mining workflow docs for easier discovery.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- Launcher: Added an app-owned YouTube subtitle flow that pauses mpv, uses absPlayer-style YouTube timedtext parsing/conversion to download subtitle tracks, and injects them as external files before playback resumes.
|
|
||||||
- Launcher: Changed YouTube subtitle startup to auto-load the best-available primary and secondary subtitle tracks at launch instead of forcing the picker modal first. Secondary subtitle failures no longer block playback resume.
|
|
||||||
- Launcher: Added `Ctrl+Alt+C` as the default keybinding to manually open the YouTube subtitle picker during active YouTube playback.
|
|
||||||
- Launcher: Added yt-dlp metadata probing so YouTube playback and immersion tracking record canonical video title and channel metadata.
|
|
||||||
- Launcher: Stopped forcing `--ytdl-raw-options=` before user-provided mpv options so existing YouTube cookie integrations in user `--args` are no longer clobbered.
|
|
||||||
- Launcher: Disabled mpv native YouTube subtitle auto-loading for the app-owned flow so injected external subtitle files remain authoritative.
|
|
||||||
- Launcher: Added OSD status messages for YouTube playback startup, subtitle acquisition, and subtitle loading so the flow stays visible before and during the picker.
|
|
||||||
- Subtitle Sidebar: Added startup-auto-open controls and resume positioning improvements so the sidebar jumps directly to the first resolved active cue.
|
|
||||||
- Subtitle Sidebar: Improved subtitle prefetch and embedded overlay passthrough sync so sidebar and overlay subtitle states stay consistent across media transitions.
|
|
||||||
- Subtitle Sidebar: Updated scroll handling, embedded layout styling, and active-cue visual behavior.
|
|
||||||
- Stats: Stats Library tab now displays YouTube video title, channel name, and channel thumbnail for YouTube media entries, with retry logic to fill in metadata that arrives after initial load.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Launcher: Fixed Anki media mining for mpv YouTube streams by unwrapping the stream URL so audio and screenshot capture work correctly for YouTube playback sessions.
|
|
||||||
- Immersion: Fixed YouTube media path handling in the immersion runtime and tracking so YouTube sessions record correct media references, AniList guessing skips YouTube URLs, and post-watch state transitions do not fire for YouTube media.
|
|
||||||
- Launcher: Fixed startup-launched YouTube playback so primary subtitle overlay updates continue after auto-load completes.
|
|
||||||
- Launcher: Fixed auto-loaded YouTube primary subtitles so parsed cues appear in the subtitle sidebar without needing a manual picker retry.
|
|
||||||
- Launcher: Fixed the YouTube picker to guard against duplicate subtitle submissions and tightened YouTube URL detection so follow-up runtime flows only treat real YouTube hosts as YouTube playback.
|
|
||||||
- Launcher: Fixed primary subtitle failure notifications being shown while app-owned YouTube subtitle probing and downloads are still in flight.
|
|
||||||
- Launcher: Preserved existing authoritative YouTube subtitle tracks when available; downloaded tracks are used only to fill missing sides, and native mpv secondary subtitle rendering is hidden so the overlay remains the sole secondary display.
|
|
||||||
|
|
||||||
## v0.8.0 (2026-03-22)
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- Overlay: Added the subtitle sidebar feature with a new `subtitleSidebar` configuration surface and rendered sidebar modal with cue list rendering, click-to-seek, active-cue highlighting, and embedded layout support.
|
|
||||||
- IPC: Added sidebar snapshot plumbing between renderer and main process for overlay/sidebar synchronization.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- Config: Added hot-reloadable sidebar options for enablement, layout, visibility, typography, opacity, sizing, and interaction behavior (`autoOpen`, `pauseOnHover`, `autoScroll`, toggle key).
|
|
||||||
- Docs: Added full `subtitleSidebar` documentation coverage, including sample config, option table, and toggle shortcut notes.
|
|
||||||
- Runtime: Improved subtitle prefetch/rendering flow so sidebar and overlay subtitle states stay in sync across media transitions.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Overlay: Kept sidebar cue tracking stable across playback transitions and timing edge cases.
|
|
||||||
- Overlay: Improved sidebar resume/start behavior to jump directly to the first resolved active cue.
|
|
||||||
- Overlay: Stopped stale subtitle refreshes from regressing active-cue and text state.
|
|
||||||
|
|
||||||
## v0.7.0 (2026-03-19)
|
## v0.7.0 (2026-03-19)
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
236
README.md
236
README.md
@@ -1,168 +1,60 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
|
<img src="assets/SubMiner.png" width="140" alt="SubMiner logo">
|
||||||
<img src="assets/SubMiner.png" width="160" alt="SubMiner logo">
|
|
||||||
|
|
||||||
# SubMiner
|
# SubMiner
|
||||||
|
|
||||||
## Turn mpv into a sentence-mining workstation.
|
**Sentence-mine from mpv — look up words, one-key Anki export, immersion tracking.**
|
||||||
|
|
||||||
Look up words with Yomitan, export to Anki in one key, track your immersion — all without leaving mpv.
|
[](https://www.gnu.org/licenses/gpl-3.0)
|
||||||
|
[](https://github.com/ksyasuda/SubMiner)
|
||||||
[](https://www.gnu.org/licenses/gpl-3.0)
|
[](https://docs.subminer.moe)
|
||||||
[](https://github.com/ksyasuda/SubMiner)
|
[](https://aur.archlinux.org/packages/subminer-bin)
|
||||||
[](https://docs.subminer.moe)
|
|
||||||
[](https://aur.archlinux.org/packages/subminer-bin)
|
|
||||||
|
|
||||||
[](./assets/minecard.mp4)
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
## How It Works
|
|
||||||
|
|
||||||
SubMiner runs as an invisible Electron overlay on top of mpv. Subtitles render as an interactive layer. Move your cursor over any word and trigger a [Yomitan](https://github.com/yomidevs/yomitan) lookup. Press one key to snapshot the sentence, audio, and screenshot into Anki via AnkiConnect.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
### Dictionary Lookups
|
|
||||||
|
|
||||||
Yomitan runs inside the overlay. Trigger a lookup on any word for full dictionary popups — definitions, pitch accent, frequency data — without ever leaving mpv.
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
<img src="docs-site/public/screenshots/yomitan-lookup.png" width="800" alt="Yomitan dictionary popup over annotated subtitles in mpv">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
### Instant Anki Mining
|
|
||||||
|
|
||||||
Create an Anki card with the sentence, audio clip, screenshot, and machine translation from the exact playback moment with one key press, click, or controller input.
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
<img src="docs-site/public/screenshots/one-key-mining.png" width="800" alt="Anki card created from SubMiner with sentence, audio, and screenshot">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
### Reading Annotations
|
|
||||||
|
|
||||||
Real-time subtitle annotations with frequency highlighting, JLPT tags, N+1 targeting, and a character name dictionary. Known words fade back; new words stand out. Grammar-only tokens render as plain text so you focus on what matters.
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
<img src="docs-site/public/screenshots/annotations.png" width="800" alt="Annotated subtitles with frequency coloring, JLPT underlines, and N+1 targets">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
### Immersion Dashboard
|
|
||||||
|
|
||||||
Local stats dashboard — watch time, anime library, vocabulary growth, mining throughput, session history, and trends. All stored locally, no third-party tracking.
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
<img src="docs-site/public/screenshots/stats-overview.png" width="800" alt="Stats dashboard showing watch time, cards mined, streaks, and tracking data">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
### Integrations
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<td><b>YouTube</b></td>
|
|
||||||
<td>Auto-loaded yt-dlp subtitle tracks at startup with a manual overlay picker on demand (<code>Ctrl+Alt+C</code>)</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><b>AniList</b></td>
|
|
||||||
<td>Automatic episode tracking and progress sync</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><b>Jellyfin</b></td>
|
|
||||||
<td>Browse and launch media from your Jellyfin server</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><b>Jimaku</b></td>
|
|
||||||
<td>Search and download Japanese subtitles</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><b>alass / ffsubsync</b></td>
|
|
||||||
<td>Automatic subtitle retiming — requires <code>alass</code> or <code>ffsubsync</code> on your <code>PATH</code> (optional; subtitle syncing is disabled without them)</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><b>WebSocket</b></td>
|
|
||||||
<td>Annotated subtitle feed for external clients (texthooker pages, custom tools)</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
<img src="docs-site/public/screenshots/texthooker.png" width="800" alt="Texthooker page receiving annotated subtitle lines via WebSocket">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Requirements
|
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.
|
||||||
|
|
||||||
| | Required | Optional |
|
<div align="center">
|
||||||
| -------------- | --------------------------------------- | -------------------------------------- |
|
|
||||||
| **Player** | [`mpv`](https://mpv.io) with IPC socket | — |
|
|
||||||
| **Processing** | `ffmpeg`, `mecab` + `mecab-ipadic` | `guessit` (AniSkip), `alass` / `ffsubsync` (subtitle sync) |
|
|
||||||
| **Media** | — | `yt-dlp`, `chafa`, `ffmpegthumbnailer` |
|
|
||||||
| **Selection** | — | `fzf` / `rofi` |
|
|
||||||
|
|
||||||
> [!NOTE]
|
[](./assets/minecard.mp4)
|
||||||
> [`bun`](https://bun.sh) is required if building from source or using the CLI wrapper: `subminer`. Pre-built releases (AppImage, DMG, installer) do not require it.
|
|
||||||
|
|
||||||
**Platform-specific:**
|
</div>
|
||||||
|
|
||||||
| Linux | macOS | Windows |
|
## Features
|
||||||
| ----------------------------------- | ------------------------ | ------------- |
|
|
||||||
| `hyprctl` or `xdotool` + `xwininfo` | Accessibility permission | No extra deps |
|
|
||||||
|
|
||||||
<details>
|
**Dictionary lookups** — Yomitan runs inside the overlay. Hover or navigate to any word for full dictionary popups without leaving mpv.
|
||||||
<summary><b>Arch Linux</b></summary>
|
|
||||||
|
|
||||||
```bash
|
**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.
|
||||||
paru -S --needed mpv ffmpeg mecab-git mecab-ipadic
|
|
||||||
# Optional
|
|
||||||
paru -S --needed yt-dlp fzf rofi chafa ffmpegthumbnailer xdotool xorg-xwininfo
|
|
||||||
# Optional: subtitle sync (install at least one for subtitle syncing to work)
|
|
||||||
paru -S --needed alass python-ffsubsync
|
|
||||||
# X11 / XWAYLAND
|
|
||||||
paru -S --needed xdotool xorg-xwininfo
|
|
||||||
```
|
|
||||||
|
|
||||||
</details>
|
<div align="center">
|
||||||
|
<img src="docs-site/public/screenshots/yomitan-lookup.png" width="800" alt="Yomitan popup with dictionary entry and mine button over annotated subtitles in mpv">
|
||||||
|
</div>
|
||||||
|
|
||||||
<details>
|
**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.
|
||||||
<summary><b>macOS</b></summary>
|
|
||||||
|
|
||||||
```bash
|
<div align="center">
|
||||||
brew install mpv ffmpeg mecab mecab-ipadic
|
<img src="docs-site/public/screenshots/annotations.png" width="800" alt="Annotated subtitles with frequency highlighting, JLPT underlines, known words, and N+1 targets">
|
||||||
# Optional
|
</div>
|
||||||
brew install yt-dlp fzf rofi chafa ffmpegthumbnailer
|
|
||||||
# Optional: subtitle sync (install at least one for subtitle syncing to work)
|
|
||||||
brew install alass
|
|
||||||
pip install ffsubsync
|
|
||||||
```
|
|
||||||
|
|
||||||
Grant Accessibility permission to SubMiner in **System Settings > Privacy & Security > Accessibility**.
|
**Immersion dashboard** — Local stats dashboard with watch time, anime progress, vocabulary growth, mining throughput, and session history.
|
||||||
|
|
||||||
</details>
|
<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>
|
||||||
|
|
||||||
<details>
|
**Integrations** — AniList episode tracking, Jellyfin remote playback, Jimaku subtitle downloads, alass/ffsubsync, and an annotated websocket feed for external clients.
|
||||||
<summary><b>Windows</b></summary>
|
|
||||||
|
|
||||||
Install [`mpv`](https://mpv.io/installation/) and [`ffmpeg`](https://ffmpeg.org/download.html) and ensure both are on your `PATH`.
|
<div align="center">
|
||||||
|
<img src="docs-site/public/screenshots/texthooker.png" width="800" alt="Texthooker page with annotated subtitle lines and frequency highlighting">
|
||||||
For MeCab, install [MeCab for Windows](https://taku910.github.io/mecab/#download) with the UTF-8 dictionary.
|
</div>
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
### 1. Install
|
### Install
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><b>Arch Linux (AUR)</b></summary>
|
<summary><b>Arch Linux (AUR)</b></summary>
|
||||||
@@ -196,63 +88,53 @@ wget https://github.com/ksyasuda/SubMiner/releases/latest/download/subminer -O ~
|
|||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><b>macOS</b></summary>
|
<summary><b>macOS / Windows / From source</b></summary>
|
||||||
|
|
||||||
Download the latest DMG or ZIP from [GitHub Releases](https://github.com/ksyasuda/SubMiner/releases/latest) and drag `SubMiner.app` into `/Applications`.
|
**macOS** — Download the latest DMG/ZIP from [GitHub Releases](https://github.com/ksyasuda/SubMiner/releases/latest) and drag `SubMiner.app` into `/Applications`.
|
||||||
|
|
||||||
|
**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>
|
</details>
|
||||||
|
|
||||||
<details>
|
### First Launch
|
||||||
<summary><b>Windows</b></summary>
|
|
||||||
|
|
||||||
Download the latest installer or portable `.zip` from [GitHub Releases](https://github.com/ksyasuda/SubMiner/releases/latest). Make sure `mpv` is on your `PATH`.
|
Run `SubMiner.AppImage` (Linux), `SubMiner.app` (macOS), or `SubMiner.exe` (Windows). On first launch, SubMiner starts in the tray, creates a default config, and opens a setup popup where you can install the mpv plugin and configure Yomitan dictionaries.
|
||||||
|
|
||||||
</details>
|
### Mine
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary><b>From source</b></summary>
|
|
||||||
|
|
||||||
See the [build-from-source guide](https://docs.subminer.moe/installation#from-source).
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
### 2. First Launch
|
|
||||||
|
|
||||||
Run the app. On first launch SubMiner starts in the system tray, creates a default config, and opens a setup popup to install the mpv plugin and configure Yomitan dictionaries.
|
|
||||||
|
|
||||||
### 3. Mine
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
subminer video.mkv # play video with overlay
|
subminer video.mkv # auto-starts overlay + resumes playback
|
||||||
subminer --start video.mkv # explicit overlay start
|
subminer --start video.mkv # explicit overlay start (if plugin auto_start=no)
|
||||||
subminer stats # open immersion dashboard
|
subminer stats # open the immersion dashboard
|
||||||
subminer stats -b # stats daemon in background
|
subminer stats -b # keep the stats daemon running in background
|
||||||
subminer stats -s # stop background stats daemon
|
subminer stats -s # stop the dedicated stats daemon
|
||||||
|
subminer stats cleanup # repair/prune stored stats vocabulary rows
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
| Required | Optional |
|
||||||
|
| ------------------------------------------------------ | ----------------------------- |
|
||||||
|
| [`mpv`](https://mpv.io) with IPC socket | `yt-dlp` |
|
||||||
|
| `ffmpeg` | `guessit` (AniSkip detection) |
|
||||||
|
| `mecab` + `mecab-ipadic` | `fzf` / `rofi` |
|
||||||
|
| [`bun`](https://bun.sh) (source builds, Linux wrapper) | `chafa`, `ffmpegthumbnailer` |
|
||||||
|
| Linux: `hyprctl` or `xdotool` + `xwininfo` | |
|
||||||
|
| macOS: Accessibility permission | |
|
||||||
|
|
||||||
|
Windows uses native window tracking and does not need the Linux compositor tools.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
Full guides on configuration, Anki setup, Jellyfin, immersion tracking, and more: **[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
|
||||||
|
|
||||||
SubMiner builds on the work of these open-source projects:
|
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).
|
||||||
|
|
||||||
| Project | Role |
|
|
||||||
| ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
|
|
||||||
| [Anacreon-Script](https://github.com/friedrich-de/Anacreon-Script) | Inspiration for the mining workflow |
|
|
||||||
| [asbplayer](https://github.com/killergerbah/asbplayer) | Inspiration for subtitle sidebar and logic for YouTube subtitle parsing |
|
|
||||||
| [Bee's Character Dictionary](https://github.com/bee-san/Japanese_Character_Name_Dictionary) | Character name recognition in subtitles |
|
|
||||||
| [GameSentenceMiner](https://github.com/bpwhelan/GameSentenceMiner) | Inspiration for Electron overlay with Yomitan integration |
|
|
||||||
| [jellyfin-mpv-shim](https://github.com/jellyfin/jellyfin-mpv-shim) | Jellyfin integration |
|
|
||||||
| [Jimaku.cc](https://jimaku.cc) | Japanese subtitle search and downloads |
|
|
||||||
| [Renji's Texthooker Page](https://github.com/Renji-XD/texthooker-ui) | Base for the WebSocket texthooker integration |
|
|
||||||
| [Yomitan](https://github.com/yomidevs/yomitan) | Dictionary engine powering all lookups and the morphological parser |
|
|
||||||
| [yomitan-jlpt-vocab](https://github.com/stephenmk/yomitan-jlpt-vocab) | JLPT level tags for vocabulary |
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
---
|
|
||||||
id: TASK-243
|
|
||||||
title: 'Assess and address PR #36 latest CodeRabbit review round'
|
|
||||||
status: Done
|
|
||||||
assignee: []
|
|
||||||
created_date: '2026-03-29 07:39'
|
|
||||||
updated_date: '2026-03-29 07:41'
|
|
||||||
labels:
|
|
||||||
- code-review
|
|
||||||
- pr-36
|
|
||||||
dependencies: []
|
|
||||||
references:
|
|
||||||
- 'https://github.com/ksyasuda/SubMiner/pull/36'
|
|
||||||
priority: high
|
|
||||||
ordinal: 3600
|
|
||||||
---
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
|
||||||
Inspect the latest CodeRabbit review round on PR #36, verify each actionable comment against the current branch, implement the confirmed fixes, and verify the touched paths.
|
|
||||||
<!-- SECTION:DESCRIPTION:END -->
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
<!-- AC:BEGIN -->
|
|
||||||
- [ ] #1 Confirmed review comments are implemented or explicitly deferred with rationale.
|
|
||||||
- [ ] #2 Touched paths are verified with the smallest sufficient test/build lane.
|
|
||||||
- [ ] #3 Current PR feedback is reduced to resolved or intentionally deferred suggestions.
|
|
||||||
<!-- AC:END -->
|
|
||||||
|
|
||||||
## Final Summary
|
|
||||||
|
|
||||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
|
||||||
Addressed the confirmed latest CodeRabbit review items on PR #36. `scripts/run-coverage-lane.ts` now uses the Bun-style `import.meta.main` entrypoint check with a local ts-ignore to preserve the repo's CommonJS typecheck settings. `src/core/services/immersion-tracker/maintenance.ts` no longer shadows the imported `nowMs` helper in retention functions. `src/main.ts` now centralizes the startup-mode predicates behind a shared helper and releases `resolvedSource.cleanup` on the cached-subtitle fast path so materialized sources do not leak.
|
|
||||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
---
|
|
||||||
id: TASK-244
|
|
||||||
title: 'Assess and address PR #36 latest CodeRabbit review round 2'
|
|
||||||
status: Done
|
|
||||||
assignee: []
|
|
||||||
created_date: '2026-03-29 08:09'
|
|
||||||
updated_date: '2026-03-29 08:10'
|
|
||||||
labels:
|
|
||||||
- code-review
|
|
||||||
- pr-36
|
|
||||||
dependencies: []
|
|
||||||
references:
|
|
||||||
- 'https://github.com/ksyasuda/SubMiner/pull/36'
|
|
||||||
priority: high
|
|
||||||
ordinal: 3610
|
|
||||||
---
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
|
||||||
Inspect the newest CodeRabbit review round on PR #36, verify the actionable comment against the current branch, implement the confirmed fix, and verify the touched path.
|
|
||||||
<!-- SECTION:DESCRIPTION:END -->
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
<!-- AC:BEGIN -->
|
|
||||||
- [ ] #1 The actionable review comment is implemented or explicitly deferred with rationale.
|
|
||||||
- [ ] #2 Touched path is verified with the smallest sufficient test lane.
|
|
||||||
- [ ] #3 Current PR feedback is reduced to resolved or intentionally deferred suggestions.
|
|
||||||
<!-- AC:END -->
|
|
||||||
|
|
||||||
## Final Summary
|
|
||||||
|
|
||||||
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
|
||||||
Addressed the actionable latest CodeRabbit comment on PR #36. `src/core/services/immersion-tracker/maintenance.ts` now skips retention deletions when a window is disabled with `Infinity`, so `toDbMs(...)` is only called for finite retention values. Added a regression test in `maintenance.test.ts` that verifies disabled retention windows preserve session events, telemetry, and sessions while returning zero deletions.
|
|
||||||
<!-- SECTION:FINAL_SUMMARY:END -->
|
|
||||||
@@ -1,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'
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
---
|
|
||||||
id: m-2
|
|
||||||
title: 'Mining Workflow Upgrades'
|
|
||||||
---
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
Future user-facing workflow improvements that directly improve discoverability, previewability, and mining control without depending on speculative platform integrations like OCR, marketplace infrastructure, or cloud sync.
|
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
---
|
---
|
||||||
id: TASK-143
|
id: TASK-143
|
||||||
title: Keep character dictionary auto-sync non-blocking during startup
|
title: Keep character dictionary auto-sync non-blocking during startup
|
||||||
status: Done
|
status: In Progress
|
||||||
assignee:
|
assignee:
|
||||||
- codex
|
- codex
|
||||||
created_date: '2026-03-09 01:45'
|
created_date: '2026-03-09 01:45'
|
||||||
updated_date: '2026-03-23 03:22'
|
updated_date: '2026-03-20 09:22'
|
||||||
labels:
|
labels:
|
||||||
- dictionary
|
- dictionary
|
||||||
- startup
|
- startup
|
||||||
@@ -18,7 +18,7 @@ references:
|
|||||||
- >-
|
- >-
|
||||||
/home/sudacode/projects/japanese/SubMiner/src/main/runtime/current-media-tokenization-gate.ts
|
/home/sudacode/projects/japanese/SubMiner/src/main/runtime/current-media-tokenization-gate.ts
|
||||||
priority: high
|
priority: high
|
||||||
ordinal: 144500
|
ordinal: 38500
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ status: Done
|
|||||||
assignee:
|
assignee:
|
||||||
- '@codex'
|
- '@codex'
|
||||||
created_date: '2026-03-19 17:46'
|
created_date: '2026-03-19 17:46'
|
||||||
updated_date: '2026-03-23 03:22'
|
updated_date: '2026-03-19 17:54'
|
||||||
labels:
|
labels:
|
||||||
- stats
|
- stats
|
||||||
- immersion-tracking
|
- immersion-tracking
|
||||||
@@ -19,7 +19,6 @@ references:
|
|||||||
- src/core/services/stats-server.ts
|
- src/core/services/stats-server.ts
|
||||||
parent_task_id: TASK-177
|
parent_task_id: TASK-177
|
||||||
priority: medium
|
priority: medium
|
||||||
ordinal: 132500
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ status: Done
|
|||||||
assignee:
|
assignee:
|
||||||
- '@codex'
|
- '@codex'
|
||||||
created_date: '2026-03-19 19:38'
|
created_date: '2026-03-19 19:38'
|
||||||
updated_date: '2026-03-23 03:22'
|
updated_date: '2026-03-19 19:40'
|
||||||
labels:
|
labels:
|
||||||
- stats
|
- stats
|
||||||
- immersion-tracking
|
- immersion-tracking
|
||||||
@@ -17,7 +17,6 @@ references:
|
|||||||
- stats/src/lib/dashboard-data.ts
|
- stats/src/lib/dashboard-data.ts
|
||||||
parent_task_id: TASK-177
|
parent_task_id: TASK-177
|
||||||
priority: medium
|
priority: medium
|
||||||
ordinal: 130500
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ status: Done
|
|||||||
assignee:
|
assignee:
|
||||||
- '@codex'
|
- '@codex'
|
||||||
created_date: '2026-03-19 20:15'
|
created_date: '2026-03-19 20:15'
|
||||||
updated_date: '2026-03-23 03:22'
|
updated_date: '2026-03-19 20:17'
|
||||||
labels:
|
labels:
|
||||||
- launcher
|
- launcher
|
||||||
- stats
|
- stats
|
||||||
@@ -19,7 +19,6 @@ references:
|
|||||||
- src/main/runtime/stats-cli-command.test.ts
|
- src/main/runtime/stats-cli-command.test.ts
|
||||||
parent_task_id: TASK-177
|
parent_task_id: TASK-177
|
||||||
priority: medium
|
priority: medium
|
||||||
ordinal: 129500
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ status: Done
|
|||||||
assignee:
|
assignee:
|
||||||
- codex
|
- codex
|
||||||
created_date: '2026-03-19 20:31'
|
created_date: '2026-03-19 20:31'
|
||||||
updated_date: '2026-03-23 03:22'
|
updated_date: '2026-03-19 20:52'
|
||||||
labels:
|
labels:
|
||||||
- bug
|
- bug
|
||||||
- stats
|
- stats
|
||||||
@@ -17,7 +17,6 @@ references:
|
|||||||
- >-
|
- >-
|
||||||
/Users/sudacode/projects/japanese/SubMiner/stats/src/lib/session-detail.test.tsx
|
/Users/sudacode/projects/japanese/SubMiner/stats/src/lib/session-detail.test.tsx
|
||||||
parent_task_id: TASK-182
|
parent_task_id: TASK-182
|
||||||
ordinal: 128500
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user