mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-02-27 18:22:41 -08:00
refactor(launcher): consolidate mpv socket readiness primitive
This commit is contained in:
@@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
id: TASK-73
|
id: TASK-73
|
||||||
title: Consolidate launcher mpv socket readiness primitives
|
title: Consolidate launcher mpv socket readiness primitives
|
||||||
status: To Do
|
status: Done
|
||||||
assignee: []
|
assignee: []
|
||||||
created_date: '2026-02-18 11:35'
|
created_date: '2026-02-18 11:35'
|
||||||
updated_date: '2026-02-18 11:35'
|
updated_date: '2026-02-21 20:19'
|
||||||
labels:
|
labels:
|
||||||
- launcher
|
- launcher
|
||||||
- mpv
|
- mpv
|
||||||
@@ -31,15 +31,14 @@ Launcher contains multiple overlapping MPV readiness/polling helpers (`waitForSo
|
|||||||
|
|
||||||
## Acceptance Criteria
|
## Acceptance Criteria
|
||||||
<!-- AC:BEGIN -->
|
<!-- AC:BEGIN -->
|
||||||
- [ ] #1 Single canonical MPV socket readiness helper remains
|
- [x] #1 Single canonical MPV socket readiness helper remains
|
||||||
- [ ] #2 All launcher callsites use unified helper
|
- [x] #2 All launcher callsites use unified helper
|
||||||
- [ ] #3 Behavior/exit code compatibility maintained for CLI flows
|
- [x] #3 Behavior/exit code compatibility maintained for CLI flows
|
||||||
- [ ] #4 Test coverage added for readiness timeout behavior
|
- [x] #4 Test coverage added for readiness timeout behavior
|
||||||
<!-- AC:END -->
|
<!-- AC:END -->
|
||||||
|
|
||||||
## Definition of Done
|
## Definition of Done
|
||||||
<!-- DOD:BEGIN -->
|
<!-- DOD:BEGIN -->
|
||||||
- [ ] #1 Launcher tests pass
|
- [x] #1 Launcher tests pass
|
||||||
- [ ] #2 No dead helper functions remain
|
- [x] #2 No dead helper functions remain
|
||||||
<!-- DOD:END -->
|
<!-- DOD:END -->
|
||||||
|
|
||||||
|
|||||||
@@ -38,3 +38,8 @@ Read first. Keep concise.
|
|||||||
| `opencode-task97-runtime-composer-20260221T094150Z-r8k3` | `opencode-task97-runtime-composer` | `Execute TASK-97 normalize runtime composer contracts end-to-end without commit` | `done` | `docs/subagents/agents/opencode-task97-runtime-composer-20260221T094150Z-r8k3.md` | `2026-02-21T10:06:59Z` |
|
| `opencode-task97-runtime-composer-20260221T094150Z-r8k3` | `opencode-task97-runtime-composer` | `Execute TASK-97 normalize runtime composer contracts end-to-end without commit` | `done` | `docs/subagents/agents/opencode-task97-runtime-composer-20260221T094150Z-r8k3.md` | `2026-02-21T10:06:59Z` |
|
||||||
| `opencode-task96-config-resolve-20260221T094119Z-mbfo` | `opencode-task96-config-resolve` | `Execute TASK-96 split config resolve into domain modules with plan-first workflow` | `planning` | `docs/subagents/agents/opencode-task96-config-resolve-20260221T094119Z-mbfo.md` | `2026-02-21T09:41:19Z` |
|
| `opencode-task96-config-resolve-20260221T094119Z-mbfo` | `opencode-task96-config-resolve` | `Execute TASK-96 split config resolve into domain modules with plan-first workflow` | `planning` | `docs/subagents/agents/opencode-task96-config-resolve-20260221T094119Z-mbfo.md` | `2026-02-21T09:41:19Z` |
|
||||||
| `opencode-task98-source-tests-20260221T094524Z-kzvd` | `opencode-task98-source-tests` | `Execute TASK-98 shift core tests to source level and trim dist coupling without commit` | `blocked` | `docs/subagents/agents/opencode-task98-source-tests-20260221T094524Z-kzvd.md` | `2026-02-21T09:56:47Z` |
|
| `opencode-task98-source-tests-20260221T094524Z-kzvd` | `opencode-task98-source-tests` | `Execute TASK-98 shift core tests to source level and trim dist coupling without commit` | `blocked` | `docs/subagents/agents/opencode-task98-source-tests-20260221T094524Z-kzvd.md` | `2026-02-21T09:56:47Z` |
|
||||||
|
| `codex-task96-config-resolve-20260221T110058Z-k7m2` | `codex-task96-config-resolve` | `Execute TASK-96 split config resolve into domain modules end-to-end without commit` | `done` | `docs/subagents/agents/codex-task96-config-resolve-20260221T110058Z-k7m2.md` | `2026-02-21T20:10:43Z` |
|
||||||
|
| `codex-task73-mpv-socket-20260221T201605Z-zjhs` | `codex-task73-mpv-socket` | `Execute TASK-73 consolidate launcher mpv socket readiness primitives end-to-end` | `done` | `docs/subagents/agents/codex-task73-mpv-socket-20260221T201605Z-zjhs.md` | `2026-02-21T20:20:18Z` |
|
||||||
|
| `codex-task74-launcher-tests-20260221T201635Z-10i6` | `codex-task74-launcher-tests` | `Implement TASK-74 launcher regression tests for config discovery + command branching end-to-end` | `done` | `docs/subagents/agents/codex-task74-launcher-tests-20260221T201635Z-10i6.md` | `2026-02-21T20:20:52Z` |
|
||||||
|
| `opencode-task76-anki-workflows-20260221T201659Z-r4p1` | `opencode-task76-anki-workflows` | `Execute TASK-76 decompose anki-integration orchestrator into workflow services via plan-first workflow` | `done` | `docs/subagents/agents/opencode-task76-anki-workflows-20260221T201659Z-r4p1.md` | `2026-02-21T21:17:28Z` |
|
||||||
|
| `opencode-task76-doc-boundaries-20260221T203558Z-h7q4` | `opencode-task76-doc-boundaries` | `Update Anki integration docs with post-decomposition ownership boundaries for TASK-76` | `done` | `docs/subagents/agents/opencode-task76-doc-boundaries-20260221T203558Z-h7q4.md` | `2026-02-21T20:36:55Z` |
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# Agent: `codex-task73-mpv-socket-20260221T201605Z-zjhs`
|
||||||
|
|
||||||
|
- alias: `codex-task73-mpv-socket`
|
||||||
|
- mission: `Execute TASK-73 consolidate launcher mpv socket readiness primitives end-to-end`
|
||||||
|
- status: `done`
|
||||||
|
- branch: `main`
|
||||||
|
- started_at: `2026-02-21T20:16:05Z`
|
||||||
|
- heartbeat_minutes: `5`
|
||||||
|
|
||||||
|
## Current Work (newest first)
|
||||||
|
- [2026-02-21T20:20:18Z] test: `bun run build` fails on unrelated pre-existing missing modules in `src/anki-integration/field-grouping-workflow.test.ts` and `src/anki-integration/note-update-workflow.test.ts`; not caused by TASK-73 scope.
|
||||||
|
- [2026-02-21T20:19:24Z] handoff: Completed TASK-73; unified launcher socket readiness on `waitForUnixSocketReady`, removed duplicate helper(s), updated launcher callsites, added `launcher/mpv.test.ts`, and marked backlog task Done.
|
||||||
|
- [2026-02-21T20:19:24Z] test: `bun test launcher/mpv.test.ts launcher/config.test.ts launcher/parse-args.test.ts` pass; `make build-launcher` pass.
|
||||||
|
- [2026-02-21T20:19:24Z] progress: Removed `waitForSocket` and inlined path-existence polling into canonical `waitForUnixSocketReady` loop; switched overlay startup gate in `launcher/main.ts` to unified helper.
|
||||||
|
- [2026-02-21T20:18:42Z] progress: Mapped duplicate readiness primitives in `launcher/mpv.ts` (`waitForSocket`, `waitForPathExists`, `waitForUnixSocketReady`) and all callsites in `launcher/main.ts` + `launcher/jellyfin.ts`.
|
||||||
|
- [2026-02-21T20:16:05Z] intent: Load task acceptance criteria and map current launcher socket-readiness call graph before edits.
|
||||||
|
|
||||||
|
## Files Touched
|
||||||
|
- `docs/subagents/INDEX.md`
|
||||||
|
- `docs/subagents/agents/codex-task73-mpv-socket-20260221T201605Z-zjhs.md`
|
||||||
|
- `launcher/mpv.ts`
|
||||||
|
- `launcher/main.ts`
|
||||||
|
- `launcher/mpv.test.ts`
|
||||||
|
- `backlog/tasks/task-73 - Consolidate-launcher-mpv-socket-readiness-primitives.md`
|
||||||
|
|
||||||
|
## Assumptions
|
||||||
|
- Backlog MCP unavailable in this repo state; local `backlog/tasks/task-73 - Consolidate-launcher-mpv-socket-readiness-primitives.md` is source of truth.
|
||||||
|
|
||||||
|
## Open Questions / Blockers
|
||||||
|
- None.
|
||||||
|
|
||||||
|
## Next Step
|
||||||
|
- Await user review or follow-up tasks.
|
||||||
@@ -40,3 +40,11 @@ Shared notes. Append-only.
|
|||||||
- [2026-02-21T09:41:19Z] [opencode-task96-config-resolve-20260221T094119Z-mbfo|opencode-task96-config-resolve] starting TASK-96 via Backlog MCP + writing-plans/executing-plans workflow; scope expected around `src/config/resolve.ts`, new config-resolve domain modules, seam tests, and budget checks.
|
- [2026-02-21T09:41:19Z] [opencode-task96-config-resolve-20260221T094119Z-mbfo|opencode-task96-config-resolve] starting TASK-96 via Backlog MCP + writing-plans/executing-plans workflow; scope expected around `src/config/resolve.ts`, new config-resolve domain modules, seam tests, and budget checks.
|
||||||
- [2026-02-21T09:45:24Z] [opencode-task98-source-tests-20260221T094524Z-kzvd|opencode-task98-source-tests] starting TASK-98 via Backlog MCP + writing-plans/executing-plans workflow; targeting source-level test entrypoints and dist-coupling cleanup, no commit.
|
- [2026-02-21T09:45:24Z] [opencode-task98-source-tests-20260221T094524Z-kzvd|opencode-task98-source-tests] starting TASK-98 via Backlog MCP + writing-plans/executing-plans workflow; targeting source-level test entrypoints and dist-coupling cleanup, no commit.
|
||||||
- [2026-02-21T09:56:47Z] [opencode-task98-source-tests-20260221T094524Z-kzvd|opencode-task98-source-tests] TASK-98 implementation pass complete: source test lane (`test:fast`) moved to `bun test` source entrypoints, explicit `test:smoke:dist` added, CI/release updated, docs+timing evidence recorded; blocked final DoD on unrelated pre-existing `bun run build` TS errors from in-flight TASK-96/97 files.
|
- [2026-02-21T09:56:47Z] [opencode-task98-source-tests-20260221T094524Z-kzvd|opencode-task98-source-tests] TASK-98 implementation pass complete: source test lane (`test:fast`) moved to `bun test` source entrypoints, explicit `test:smoke:dist` added, CI/release updated, docs+timing evidence recorded; blocked final DoD on unrelated pre-existing `bun run build` TS errors from in-flight TASK-96/97 files.
|
||||||
|
- [2026-02-21T11:00:58Z] [codex-task96-config-resolve-20260221T110058Z-k7m2|codex-task96-config-resolve] taking over TASK-96 execution: load backlog task, write plan via writing-plans, execute via executing-plans, and finalize AC/DoD evidence without commit.
|
||||||
|
- [2026-02-21T20:10:43Z] [codex-task96-config-resolve-20260221T110058Z-k7m2|codex-task96-config-resolve] completed TASK-96: `src/config/resolve.ts` reduced to thin orchestrator (33 LOC), config resolve seam tests wired into src+dist config lanes via `package.json`, required gates green (`build`, `test:config:dist`, `check:file-budgets`), and backlog task marked Done with metrics evidence.
|
||||||
|
- [2026-02-21T20:20:52Z] [codex-task74-launcher-tests-20260221T201635Z-10i6|codex-task74-launcher-tests] completed TASK-74: added `launcher/main.test.ts` regression harness for config discovery + command branching (`doctor`, `config`, `mpv`, `jellyfin`), wired launcher tests into `test:launcher` + `test:core:src`, updated launcher docs test command, and marked backlog task done.
|
||||||
|
- [2026-02-21T20:19:24Z] [codex-task73-mpv-socket-20260221T201605Z-zjhs|codex-task73-mpv-socket] completed TASK-73: consolidated launcher MPV socket readiness on `waitForUnixSocketReady`, removed `waitForSocket`/duplicate path polling, rewired launcher overlay callsite, added readiness regression tests (`launcher/mpv.test.ts`), launcher tests + `make build-launcher` green.
|
||||||
|
- [2026-02-21T20:20:18Z] [codex-task73-mpv-socket-20260221T201605Z-zjhs|codex-task73-mpv-socket] full `bun run build` currently blocked by unrelated missing modules in `src/anki-integration/field-grouping-workflow.test.ts` and `src/anki-integration/note-update-workflow.test.ts` (outside TASK-73 scope).
|
||||||
|
- [2026-02-21T20:16:59Z] [opencode-task76-anki-workflows-20260221T201659Z-r4p1|opencode-task76-anki-workflows] starting TASK-76 via Backlog MCP + writing-plans/executing-plans; likely scope `src/anki-integration.ts` + new `src/anki-integration/*` workflow services, with overlap checks before edits.
|
||||||
|
- [2026-02-21T20:35:58Z] [opencode-task76-doc-boundaries-20260221T203558Z-h7q4|opencode-task76-doc-boundaries] overlap note: TASK-76 already has an active planning agent; this pass is docs-only (`docs/anki-integration.md`) to capture ownership boundaries after workflow decomposition.
|
||||||
|
- [2026-02-21T21:16:18Z] [opencode-task76-anki-workflows-20260221T201659Z-r4p1|opencode-task76-anki-workflows] completed TASK-76: extracted `note-update-workflow` + `field-grouping-workflow` services, delegated facade hotpaths in `src/anki-integration.ts`, added focused workflow seam tests, docs ownership boundaries updated, `bun run build && bun run test:core:dist` green, and backlog TASK-76 marked Done.
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import {
|
|||||||
stopOverlay,
|
stopOverlay,
|
||||||
launchTexthookerOnly,
|
launchTexthookerOnly,
|
||||||
findAppBinary,
|
findAppBinary,
|
||||||
waitForSocket,
|
|
||||||
loadSubtitleIntoMpv,
|
loadSubtitleIntoMpv,
|
||||||
runAppCommandWithInherit,
|
runAppCommandWithInherit,
|
||||||
launchMpvIdleDetached,
|
launchMpvIdleDetached,
|
||||||
@@ -361,7 +360,7 @@ async function main(): Promise<void> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const ready = await waitForSocket(mpvSocketPath);
|
const ready = await waitForUnixSocketReady(mpvSocketPath, 10000);
|
||||||
const shouldStartOverlay = args.startOverlay || args.autoStartOverlay;
|
const shouldStartOverlay = args.startOverlay || args.autoStartOverlay;
|
||||||
if (shouldStartOverlay) {
|
if (shouldStartOverlay) {
|
||||||
if (ready) {
|
if (ready) {
|
||||||
|
|||||||
61
launcher/mpv.test.ts
Normal file
61
launcher/mpv.test.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import test from 'node:test';
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import fs from 'node:fs';
|
||||||
|
import path from 'node:path';
|
||||||
|
import net from 'node:net';
|
||||||
|
import { EventEmitter } from 'node:events';
|
||||||
|
import { waitForUnixSocketReady } from './mpv';
|
||||||
|
import * as mpvModule from './mpv';
|
||||||
|
|
||||||
|
function createTempSocketPath(): { dir: string; socketPath: string } {
|
||||||
|
const baseDir = path.join(process.cwd(), '.tmp', 'launcher-mpv-tests');
|
||||||
|
fs.mkdirSync(baseDir, { recursive: true });
|
||||||
|
const dir = fs.mkdtempSync(path.join(baseDir, 'case-'));
|
||||||
|
return { dir, socketPath: path.join(dir, 'mpv.sock') };
|
||||||
|
}
|
||||||
|
|
||||||
|
test('mpv module exposes only canonical socket readiness helper', () => {
|
||||||
|
assert.equal('waitForSocket' in mpvModule, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('waitForUnixSocketReady returns false when socket never appears', async () => {
|
||||||
|
const { dir, socketPath } = createTempSocketPath();
|
||||||
|
try {
|
||||||
|
const ready = await waitForUnixSocketReady(socketPath, 120);
|
||||||
|
assert.equal(ready, false);
|
||||||
|
} finally {
|
||||||
|
fs.rmSync(dir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('waitForUnixSocketReady returns false when path exists but is not socket', async () => {
|
||||||
|
const { dir, socketPath } = createTempSocketPath();
|
||||||
|
try {
|
||||||
|
fs.writeFileSync(socketPath, 'not-a-socket');
|
||||||
|
const ready = await waitForUnixSocketReady(socketPath, 200);
|
||||||
|
assert.equal(ready, false);
|
||||||
|
} finally {
|
||||||
|
fs.rmSync(dir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('waitForUnixSocketReady returns true when socket becomes connectable before timeout', async () => {
|
||||||
|
const { dir, socketPath } = createTempSocketPath();
|
||||||
|
fs.writeFileSync(socketPath, '');
|
||||||
|
const originalCreateConnection = net.createConnection;
|
||||||
|
try {
|
||||||
|
net.createConnection = (() => {
|
||||||
|
const socket = new EventEmitter() as net.Socket;
|
||||||
|
socket.destroy = (() => socket) as net.Socket['destroy'];
|
||||||
|
socket.setTimeout = (() => socket) as net.Socket['setTimeout'];
|
||||||
|
setTimeout(() => socket.emit('connect'), 25);
|
||||||
|
return socket;
|
||||||
|
}) as typeof net.createConnection;
|
||||||
|
|
||||||
|
const ready = await waitForUnixSocketReady(socketPath, 400);
|
||||||
|
assert.equal(ready, true);
|
||||||
|
} finally {
|
||||||
|
net.createConnection = originalCreateConnection;
|
||||||
|
fs.rmSync(dir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -416,23 +416,6 @@ export async function loadSubtitleIntoMpv(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function waitForSocket(socketPath: string, timeoutMs = 10000): Promise<boolean> {
|
|
||||||
const start = Date.now();
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
const timer = setInterval(() => {
|
|
||||||
if (fs.existsSync(socketPath)) {
|
|
||||||
clearInterval(timer);
|
|
||||||
resolve(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (Date.now() - start >= timeoutMs) {
|
|
||||||
clearInterval(timer);
|
|
||||||
resolve(false);
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function startMpv(
|
export function startMpv(
|
||||||
target: string,
|
target: string,
|
||||||
targetKind: 'file' | 'url',
|
targetKind: 'file' | 'url',
|
||||||
@@ -672,19 +655,6 @@ async function sleepMs(ms: number): Promise<void> {
|
|||||||
await new Promise<void>((resolve) => setTimeout(resolve, ms));
|
await new Promise<void>((resolve) => setTimeout(resolve, ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function waitForPathExists(filePath: string, timeoutMs: number): Promise<boolean> {
|
|
||||||
const deadline = Date.now() + timeoutMs;
|
|
||||||
while (Date.now() < deadline) {
|
|
||||||
try {
|
|
||||||
if (fs.existsSync(filePath)) return true;
|
|
||||||
} catch {
|
|
||||||
// ignore transient fs errors
|
|
||||||
}
|
|
||||||
await sleepMs(150);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function canConnectUnixSocket(socketPath: string): Promise<boolean> {
|
async function canConnectUnixSocket(socketPath: string): Promise<boolean> {
|
||||||
return await new Promise<boolean>((resolve) => {
|
return await new Promise<boolean>((resolve) => {
|
||||||
const socket = net.createConnection(socketPath);
|
const socket = net.createConnection(socketPath);
|
||||||
@@ -713,11 +683,14 @@ export async function waitForUnixSocketReady(
|
|||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
const deadline = Date.now() + timeoutMs;
|
const deadline = Date.now() + timeoutMs;
|
||||||
while (Date.now() < deadline) {
|
while (Date.now() < deadline) {
|
||||||
const exists = await waitForPathExists(socketPath, 300);
|
try {
|
||||||
if (exists) {
|
if (fs.existsSync(socketPath)) {
|
||||||
const ready = await canConnectUnixSocket(socketPath);
|
const ready = await canConnectUnixSocket(socketPath);
|
||||||
if (ready) return true;
|
if (ready) return true;
|
||||||
}
|
}
|
||||||
|
} catch {
|
||||||
|
// ignore transient fs errors
|
||||||
|
}
|
||||||
await sleepMs(150);
|
await sleepMs(150);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
Reference in New Issue
Block a user