Files
command-center/agent/managed-skills/dankvoice-silence-triage/SKILL.md
T

3.6 KiB

name, description
name description
dankvoice-silence-triage Triage silent or late dankvoice auto-speak on the fleet: arming key, /say grammar, endpoint/sink, event granularity, humanizer latency, plus the black-box bats driver.

Triage "dankvoice is silent" on the fleet

Symptom that recurs: auto-speak only speaks when /say <text> is typed explicitly. Work the gates in this order; each has an objective check.

  1. Armed? cat ~/.omp/cache/dankvoice.json | jq .auto — must be true (or DANKVOICE_AUTO=1). Arming is read at startup only and /say on never persists, so a session started before the key was added is silent even though fresh sessions work. /say start is not a subcommand: unknown arguments are spoken verbatim (it read out the word "start"). /say on is the verb; /say status must print auto-speak on.
  2. Endpoint + sink? /say status must show a real endpoint (not NO ENDPOINT) and the saved sink must exist: pw-dump | grep -o 'alsa_output\.[^"]*' | sort -u.
  3. Did it synthesize? pgrep -a pw-play during a turn: one process per segment, --target <sink>, wav under /tmp/dankvoice-*/say-<epochms>.wav. One temp directory per utterance chain — count dirs to count utterances, pids to count segments.
  4. Event granularity (the structural cause). agent_end fires once per prompt when the loop settles and is skipped when the harness maps a non-terminal settle (willContinue) — long runs with background jobs or subagents therefore narrate nothing. Mid-run narration needs turn_end (fires per assistant message + tool results). Verify delivery, don't infer it: skill omp-extension-event-probe.
  5. Latency is not silence. Narration passes through the humanizer (frank-zealot) before synthesis, so an utterance lands several seconds after its turn; under endpoint load that stretches to tens of seconds. /say humanize off = mechanical voice, near-instant. Judge cadence from probe-log timestamps, never from when audio starts.

Design rules the plugin now encodes (dank/dankvoice#2→#3)

  • Narrate turn_end per turn plus agent_end for the settled answer; dedupe identical text per run (reset on agent_start) so the answer speaks once.
  • A turn landing while the player is alive is dropped — no queue, no interrupting; a tool loop would cut every sentence off after a word. Busy test: state.child !== undefined && state.child.exitCode === null.
  • Tool-only turns (no prose, no narration-flagged thinking) stay silent.
  • print/json modes stay silent, so benches never chatter.
  • Settings writes must round-trip the file ({...onDisk, ...owned keys}). Rebuilding from owned keys deleted "auto" on any menu choice and silently un-armed every later session — the classic cause of "it worked yesterday".

Black-box test harness (repo dank/dankvoice)

tests/behavior.bats drives the built artifact through an in-process driver: const say = DV_EXT(ctx, cfg)[0], handlers via const events = new Map(); const pi = { on: (n,h)=>events.set(n,h) }, then events.get("turn_end")?.({ turnIndex:0, message:{role:"assistant",content:[{type:"text",text:"…"}]}, toolResults:[]}, ctx). Assert on CALL/PLAYS/NOTIFY lines and SETTINGS <file contents>; stubs are DV_PLAYER_CMD (logs voice+input, honours DV_HOLD) and DV_REWRITE_URL (404 → falls back to mechanical text). setup() runs bun run build per test → serialize suites on the shared dist/. tests/packaging.bats requires dist/ committed (compares to HEAD), so build → commit → the drift pin goes green.