service_voice.nu — hero_voice server + UI lifecycle module #92

Closed
opened 2026-04-20 08:31:25 +00:00 by mahmoud · 5 comments
Owner

Child of #75.

Objective

Add tools/modules/services/service_voice.nu implementing install | start | stop | status for the hero_voice service (speech-to-text / text-to-speech).

Scope

  • Repo: ssh://git@forge.ourworld.tf/lhumina_code/hero_voice.git
  • Binaries (per buildenv.sh): hero_voice, hero_voice_server, hero_voice_ui
  • Runtime actions (register with hero_proc): hero_voice_server, hero_voice_ui
  • TOML: lhumina_code/hero_zero/services/hero_voice.toml
  • Sockets: $HERO_SOCKET_DIR/hero_voice/rpc.sock, $HERO_SOCKET_DIR/hero_voice/ui.sock (confirm in spec)
  • Server env: RUST_LOG=info only.
  • UI env: RUST_LOG=info + HERO_VOICE_STT_LOCAL=true (TOML [ui.env]). Pass verbatim — spec should grep to confirm consumer is in hero_voice_ui/.
  • Dependencies: none in TOML.
  • Workspace layout: virtual workspace (confirmed via Cargo.toml).
  • --root flag supported but optional; user-level default.

Acceptance criteria

  • use services/mod.nu * makes service_voice available.
  • service_voice install [--root] [--update] [--reset] clones + builds all 3 binaries; short-circuits when binaries already present (via svc_bins_ok).
  • service_voice start [--reset] [--root] [--update] registers both actions + the service, starts, prints summary. Idempotent without --reset.
  • service_voice status [--root] reports state.
  • service_voice stop [--root] cleanly unregisters.
  • UI action env includes HERO_VOICE_STT_LOCAL=true.
  • Smoke-tested on Hetzner: install → start --reset → running with 0 restarts → stop.

Template

service_indexer.nu (merged recently — the current canonical minimal two-binary module, ~200 lines, uses svc_bins_ok + --reset on install). Copy-rename with these hero_voice deltas:

  • UI action env adds HERO_VOICE_STT_LOCAL: "true" on top of RUST_LOG.
  • Description: "Hero Voice — speech-to-text and text-to-speech server and UI".

Spec should verify via main.rs inspection:

  • No serve subcommand on either daemon.
  • Exact socket paths.
  • Whether HERO_VOICE_STT_LOCAL is actually consumed by the UI binary (it's declared in the TOML's [ui.env]).
Child of #75. ## Objective Add `tools/modules/services/service_voice.nu` implementing `install | start | stop | status` for the **hero_voice** service (speech-to-text / text-to-speech). ## Scope - **Repo**: `ssh://git@forge.ourworld.tf/lhumina_code/hero_voice.git` - **Binaries** (per `buildenv.sh`): `hero_voice`, `hero_voice_server`, `hero_voice_ui` - **Runtime actions** (register with hero_proc): `hero_voice_server`, `hero_voice_ui` - **TOML**: `lhumina_code/hero_zero/services/hero_voice.toml` - **Sockets**: `$HERO_SOCKET_DIR/hero_voice/rpc.sock`, `$HERO_SOCKET_DIR/hero_voice/ui.sock` (confirm in spec) - **Server env**: `RUST_LOG=info` only. - **UI env**: `RUST_LOG=info` + `HERO_VOICE_STT_LOCAL=true` (TOML `[ui.env]`). Pass verbatim — spec should grep to confirm consumer is in `hero_voice_ui/`. - **Dependencies**: none in TOML. - **Workspace layout**: virtual workspace (confirmed via `Cargo.toml`). - `--root` flag supported but optional; user-level default. ## Acceptance criteria - [ ] `use services/mod.nu *` makes `service_voice` available. - [ ] `service_voice install [--root] [--update] [--reset]` clones + builds all 3 binaries; short-circuits when binaries already present (via `svc_bins_ok`). - [ ] `service_voice start [--reset] [--root] [--update]` registers both actions + the service, starts, prints summary. Idempotent without `--reset`. - [ ] `service_voice status [--root]` reports state. - [ ] `service_voice stop [--root]` cleanly unregisters. - [ ] UI action env includes `HERO_VOICE_STT_LOCAL=true`. - [ ] Smoke-tested on Hetzner: install → start --reset → running with 0 restarts → stop. ## Template **`service_indexer.nu`** (merged recently — the current canonical minimal two-binary module, ~200 lines, uses `svc_bins_ok` + `--reset` on install). Copy-rename with these hero_voice deltas: - UI action env adds `HERO_VOICE_STT_LOCAL: "true"` on top of `RUST_LOG`. - Description: `"Hero Voice — speech-to-text and text-to-speech server and UI"`. Spec should verify via `main.rs` inspection: - No `serve` subcommand on either daemon. - Exact socket paths. - Whether `HERO_VOICE_STT_LOCAL` is actually consumed by the UI binary (it's declared in the TOML's `[ui.env]`).
Author
Owner

Implementation Spec for Issue #92

Objective

Add a service_voice.nu lifecycle module mirroring service_indexer.nu so Hero Voice (server + UI) can be installed, started, stopped, and status-checked under hero_proc.

Requirements

  • New Nushell module tools/modules/services/service_voice.nu exposing install, start, stop, status.
  • Register two actions (hero_voice_server, hero_voice_ui) and one service (hero_voice) with hero_proc.
  • UI action sets HERO_VOICE_STT_LOCAL = "true" on top of the baseline RUST_LOG = "info". Server action sets only RUST_LOG = "info".
  • Module reuses lib.nu helpers (svc_bin, svc_sock_base, svc_bins_ok, svc_cargo_install, svc_require_sudo, svc_require_proc, svc_need_sudo, svc_proc_healthy, svc_update). Do not modify lib.nu.
  • Wire the module into tools/modules/services/mod.nu via export use service_voice.nu. Rebase against origin/development before push.

Files to Modify/Create

  • tools/modules/services/service_voice.nu — new, ~203 lines, cloned structure from service_indexer.nu.
  • tools/modules/services/mod.nu — append one line. Rebase before push.

Implementation Plan

  1. Clone the template file. Copy service_indexer.nu to service_voice.nu. Imports at lines 3–4 verbatim. Module docstring at line 1 → # service_voice.nu — manage the hero_voice service lifecycle through hero_proc..

  2. Rename consts (service_indexer.nu:6-9):

    • SVX_SERVICE_NAME = "hero_voice"
    • SVX_FORGE_LOC = "lhumina_code/hero_voice"
    • SVX_BINARIES = ["hero_voice" "hero_voice_server" "hero_voice_ui"]
    • SVX_ACTIONS = ["hero_voice_server" "hero_voice_ui"]
      (Keep the SVX_ prefix — it matches the pattern the template uses.)
  3. Server action (service_indexer.nu:11-50) in svx_server_action:

    • svc_bin "hero_voice_server" $root / name: "hero_voice_server".
    • env: {RUST_LOG: "info"} unchanged.
    • Socket paths → /hero_voice/rpc.sock (confirmed by hero_voice_server/src/main.rs:44-54).
    • script: $bin kept — no serve subcommand; main.rs:56-59 calls run_server() directly.
    • All retry/timeout values identical to the indexer template.
  4. UI action (service_indexer.nu:52-91) in svx_ui_action:

    • svc_bin "hero_voice_ui" $root / name: "hero_voice_ui".
    • Only substantive delta vs template: env: {RUST_LOG: "info", HERO_VOICE_STT_LOCAL: "true"}. Confirmed: HERO_VOICE_STT_LOCAL is read by crates/hero_voice/src/local_transcriber.rs:31; instantiated only from hero_voice_ui in ws.rs and transcribe_handler.rs. The server binary does not construct a LocalTranscriber.
    • Socket paths → /hero_voice/ui.sock (confirmed by hero_voice_ui/src/main.rs:71-73).
    • All retry/timeout fields identical.
  5. Service config (service_indexer.nu:93-105):

    • description: "Hero Voice — speech-to-text and text-to-speech server and admin UI".
    • Everything else unchanged.
  6. svx_drop_registration (service_indexer.nu:107-113): identical after rename.

  7. install (service_indexer.nu:115-127): identical after rename. Note:

    • svc_bins_ok $SVX_BINARIES $root short-circuits when all three binaries are present (lib.nu:65).
    • svc_cargo_install is sufficient — hero_voice/Cargo.toml:1-10 is a pure virtual workspace, plain cargo build --release builds all three bins.
  8. start (service_indexer.nu:129-180): identical after rename. Audit the literal error string at line 156 — change "hero_indexer_server not found""hero_voice_server not found". Print messages at lines 162, 165, 179 update via the $SVX_SERVICE_NAME interpolation naturally; the two action-specific prints (registering hero_voice_server action / ..._ui action) and proc logs get hero_voice_server need explicit renames.

  9. stop and status (service_indexer.nu:182-202): identical after rename. status calls svc_require_proc "service_voice" $root.

  10. Wire into mod.nu. Append export use service_voice.nu at the end. Rebase against origin/development immediately before push — other agents may have added entries in parallel.

Acceptance Criteria

  • tools/modules/services/service_voice.nu exists (~200 lines), structurally identical to service_indexer.nu except for the name/binary/socket/description substitutions and the UI env addition.
  • service_voice install builds all three binaries and places them in ~/hero/bin/ (or /root/hero/bin/ with --root).
  • service_voice install second time short-circuits with → hero_voice binaries already in place — skipping build.
  • service_voice start registers both actions + the service, starts it.
  • UI action env contains HERO_VOICE_STT_LOCAL=true, verified via proc action describe hero_voice_ui.
  • service_voice status reports running with both actions healthy.
  • service_voice stop cleanly unregisters.
  • --root optional; user-level default.
  • mod.nu exports service_voice and existing services still load.

Notes

  • Workspace shape: virtual workspace (Cargo.toml has [workspace] only, no root [package]). svc_cargo_install is sufficient — no --workspace flag like books needed.
  • Subcommand: neither binary has one. hero_voice_server/main.rs:56-59 and hero_voice_ui/main.rs:75-78 both call run_server() directly from #[tokio::main] async fn main(). Use script: $bin, not script: $"($bin) serve".
  • Socket paths: server $HERO_SOCKET_DIR/hero_voice/rpc.sock (main.rs:42-54), UI $HERO_SOCKET_DIR/hero_voice/ui.sock (main.rs:59-73). Both honour HERO_SOCKET_DIR, matching svc_sock_base.
  • HERO_VOICE_STT_LOCAL consumer: UI-only (grep confirms). Defined/read in crates/hero_voice/src/local_transcriber.rs:31; instantiated only from hero_voice_ui. Server binary never constructs a LocalTranscriber.
  • hero_voice CLI binary: listed in buildenv.sh BINARIES, so svc_cargo_install copies it, but it is not registered as a hero_proc action (matches hero_voice.toml, which only declares [server] and [ui]).
  • Smoke-test plan (read-only, post-implementation):
    1. use tools/modules/services *
    2. service_proc start --root
    3. service_voice install --root — 3 binaries in /root/hero/bin/
    4. service_voice install --root again — short-circuit message
    5. service_voice start --reset --root — both actions registered + started
    6. proc action describe hero_voice_ui --root — verify HERO_VOICE_STT_LOCAL=true
    7. proc service status hero_voice --root — running, 0 restarts
    8. curl --unix-socket /root/hero/var/sockets/hero_voice/rpc.sock http://localhost/ — HTTP response
    9. curl --unix-socket /root/hero/var/sockets/hero_voice/ui.sock http://localhost/ — HTTP response
    10. service_voice start --root — idempotent "already running"
    11. service_voice stop --root — clean unregister
    12. Post-stop service_voice status --rootservice 'hero_voice' not found

Critical Files for Implementation

  • /Users/mahmoud/code/forge.ourworld.tf/lhumina_code/hero_skills/tools/modules/services/service_indexer.nu
  • /Users/mahmoud/code/forge.ourworld.tf/lhumina_code/hero_skills/tools/modules/services/lib.nu
  • /Users/mahmoud/code/forge.ourworld.tf/lhumina_code/hero_skills/tools/modules/services/mod.nu
  • /Users/mahmoud/code/forge.ourworld.tf/lhumina_code/hero_zero/services/hero_voice.toml
  • /Users/mahmoud/code/forge.ourworld.tf/lhumina_code/hero_voice/crates/hero_voice_ui/src/main.rs
## Implementation Spec for Issue #92 ### Objective Add a `service_voice.nu` lifecycle module mirroring `service_indexer.nu` so Hero Voice (server + UI) can be installed, started, stopped, and status-checked under `hero_proc`. ### Requirements - New Nushell module `tools/modules/services/service_voice.nu` exposing `install`, `start`, `stop`, `status`. - Register two actions (`hero_voice_server`, `hero_voice_ui`) and one service (`hero_voice`) with `hero_proc`. - UI action sets `HERO_VOICE_STT_LOCAL = "true"` on top of the baseline `RUST_LOG = "info"`. Server action sets only `RUST_LOG = "info"`. - Module reuses `lib.nu` helpers (`svc_bin`, `svc_sock_base`, `svc_bins_ok`, `svc_cargo_install`, `svc_require_sudo`, `svc_require_proc`, `svc_need_sudo`, `svc_proc_healthy`, `svc_update`). Do not modify `lib.nu`. - Wire the module into `tools/modules/services/mod.nu` via `export use service_voice.nu`. Rebase against `origin/development` before push. ### Files to Modify/Create - `tools/modules/services/service_voice.nu` — new, ~203 lines, cloned structure from `service_indexer.nu`. - `tools/modules/services/mod.nu` — append one line. Rebase before push. ### Implementation Plan 1. **Clone the template file.** Copy `service_indexer.nu` to `service_voice.nu`. Imports at lines 3–4 verbatim. Module docstring at line 1 → `# service_voice.nu — manage the hero_voice service lifecycle through hero_proc.`. 2. **Rename consts** (`service_indexer.nu:6-9`): - `SVX_SERVICE_NAME = "hero_voice"` - `SVX_FORGE_LOC = "lhumina_code/hero_voice"` - `SVX_BINARIES = ["hero_voice" "hero_voice_server" "hero_voice_ui"]` - `SVX_ACTIONS = ["hero_voice_server" "hero_voice_ui"]` (Keep the `SVX_` prefix — it matches the pattern the template uses.) 3. **Server action** (`service_indexer.nu:11-50`) in `svx_server_action`: - `svc_bin "hero_voice_server" $root` / `name: "hero_voice_server"`. - `env: {RUST_LOG: "info"}` unchanged. - Socket paths → `/hero_voice/rpc.sock` (confirmed by `hero_voice_server/src/main.rs:44-54`). - `script: $bin` kept — no `serve` subcommand; `main.rs:56-59` calls `run_server()` directly. - All retry/timeout values identical to the indexer template. 4. **UI action** (`service_indexer.nu:52-91`) in `svx_ui_action`: - `svc_bin "hero_voice_ui" $root` / `name: "hero_voice_ui"`. - **Only substantive delta vs template**: `env: {RUST_LOG: "info", HERO_VOICE_STT_LOCAL: "true"}`. Confirmed: `HERO_VOICE_STT_LOCAL` is read by `crates/hero_voice/src/local_transcriber.rs:31`; instantiated only from `hero_voice_ui` in `ws.rs` and `transcribe_handler.rs`. The server binary does not construct a `LocalTranscriber`. - Socket paths → `/hero_voice/ui.sock` (confirmed by `hero_voice_ui/src/main.rs:71-73`). - All retry/timeout fields identical. 5. **Service config** (`service_indexer.nu:93-105`): - `description: "Hero Voice — speech-to-text and text-to-speech server and admin UI"`. - Everything else unchanged. 6. **`svx_drop_registration`** (`service_indexer.nu:107-113`): identical after rename. 7. **`install`** (`service_indexer.nu:115-127`): identical after rename. Note: - `svc_bins_ok $SVX_BINARIES $root` short-circuits when all three binaries are present (`lib.nu:65`). - `svc_cargo_install` is sufficient — `hero_voice/Cargo.toml:1-10` is a pure virtual workspace, plain `cargo build --release` builds all three bins. 8. **`start`** (`service_indexer.nu:129-180`): identical after rename. Audit the literal error string at line 156 — change `"hero_indexer_server not found"` → `"hero_voice_server not found"`. Print messages at lines 162, 165, 179 update via the `$SVX_SERVICE_NAME` interpolation naturally; the two action-specific prints (`registering hero_voice_server action` / `..._ui action`) and `proc logs get hero_voice_server` need explicit renames. 9. **`stop` and `status`** (`service_indexer.nu:182-202`): identical after rename. `status` calls `svc_require_proc "service_voice" $root`. 10. **Wire into `mod.nu`**. Append `export use service_voice.nu` at the end. Rebase against `origin/development` immediately before push — other agents may have added entries in parallel. ### Acceptance Criteria - [ ] `tools/modules/services/service_voice.nu` exists (~200 lines), structurally identical to `service_indexer.nu` except for the name/binary/socket/description substitutions and the UI env addition. - [ ] `service_voice install` builds all three binaries and places them in `~/hero/bin/` (or `/root/hero/bin/` with `--root`). - [ ] `service_voice install` second time short-circuits with `→ hero_voice binaries already in place — skipping build`. - [ ] `service_voice start` registers both actions + the service, starts it. - [ ] UI action env contains `HERO_VOICE_STT_LOCAL=true`, verified via `proc action describe hero_voice_ui`. - [ ] `service_voice status` reports running with both actions healthy. - [ ] `service_voice stop` cleanly unregisters. - [ ] `--root` optional; user-level default. - [ ] `mod.nu` exports `service_voice` and existing services still load. ### Notes - **Workspace shape**: virtual workspace (`Cargo.toml` has `[workspace]` only, no root `[package]`). `svc_cargo_install` is sufficient — no `--workspace` flag like books needed. - **Subcommand**: neither binary has one. `hero_voice_server/main.rs:56-59` and `hero_voice_ui/main.rs:75-78` both call `run_server()` directly from `#[tokio::main] async fn main()`. Use `script: $bin`, not `script: $"($bin) serve"`. - **Socket paths**: server `$HERO_SOCKET_DIR/hero_voice/rpc.sock` (`main.rs:42-54`), UI `$HERO_SOCKET_DIR/hero_voice/ui.sock` (`main.rs:59-73`). Both honour `HERO_SOCKET_DIR`, matching `svc_sock_base`. - **HERO_VOICE_STT_LOCAL consumer**: UI-only (grep confirms). Defined/read in `crates/hero_voice/src/local_transcriber.rs:31`; instantiated only from `hero_voice_ui`. Server binary never constructs a `LocalTranscriber`. - **`hero_voice` CLI binary**: listed in `buildenv.sh` BINARIES, so `svc_cargo_install` copies it, but it is not registered as a hero_proc action (matches `hero_voice.toml`, which only declares `[server]` and `[ui]`). - **Smoke-test plan** (read-only, post-implementation): 1. `use tools/modules/services *` 2. `service_proc start --root` 3. `service_voice install --root` — 3 binaries in `/root/hero/bin/` 4. `service_voice install --root` again — short-circuit message 5. `service_voice start --reset --root` — both actions registered + started 6. `proc action describe hero_voice_ui --root` — verify `HERO_VOICE_STT_LOCAL=true` 7. `proc service status hero_voice --root` — running, 0 restarts 8. `curl --unix-socket /root/hero/var/sockets/hero_voice/rpc.sock http://localhost/` — HTTP response 9. `curl --unix-socket /root/hero/var/sockets/hero_voice/ui.sock http://localhost/` — HTTP response 10. `service_voice start --root` — idempotent "already running" 11. `service_voice stop --root` — clean unregister 12. Post-stop `service_voice status --root` — `service 'hero_voice' not found` ### Critical Files for Implementation - /Users/mahmoud/code/forge.ourworld.tf/lhumina_code/hero_skills/tools/modules/services/service_indexer.nu - /Users/mahmoud/code/forge.ourworld.tf/lhumina_code/hero_skills/tools/modules/services/lib.nu - /Users/mahmoud/code/forge.ourworld.tf/lhumina_code/hero_skills/tools/modules/services/mod.nu - /Users/mahmoud/code/forge.ourworld.tf/lhumina_code/hero_zero/services/hero_voice.toml - /Users/mahmoud/code/forge.ourworld.tf/lhumina_code/hero_voice/crates/hero_voice_ui/src/main.rs
mahmoud self-assigned this 2026-04-20 08:44:16 +00:00
mahmoud added this to the ACTIVE project 2026-04-20 08:44:18 +00:00
mahmoud added this to the now milestone 2026-04-20 08:44:21 +00:00
Author
Owner

Spec addendum — alignment with commits landed 2026-04-20

Audited commits from kristof5, xmonader, and despiegk that landed after the last merge (PR #91). Summary:

  • 85dfdc7 (despiegk) — service split: moved services.nu → per-file modules and added --reset + svc_bins_ok short-circuit to every service's install. The spec's template (service_indexer.nu) was introduced in this commit and already carries the new pattern, so no change to the module code itself is needed — the existing plan is correct.
  • c6ad517 (kristof5) — svc_require_sudo now uses is-admin instead of bare whoami == "root"; error message rephrased. Transparent to consumers; the module just calls svc_require_sudo and inherits the new behaviour.
  • c6ad517 + packages.nu (new) — added service_install_all that iterates all services with --update. Every new service module should be registered there.
  • 5796526 (kristof5) — delegates template service builds to the service modules' install. No per-module change required beyond the standard --reset flag we already plan.
  • 3d809fb (despiegk) — forge partial-match: transparent via svc_update.
  • 9229d7e (xmonader) — build-verification guardrail: docs only; smoke-test already covers parse-check.

Added to the implementation plan

Step 10 (Wire into mod.nu) — keep as specified.

New Step 11 (Wire into packages.nu):

  • tools/modules/services/packages.nu:
    • Add use service_voice.nu to the imports block (currently lines 17/26 have service_books.nu / service_indexer.nu — append at the end of the use block).
    • Add {name: "service_voice", run: {|| service_voice install --update}} to the services list inside service_install_all (append at the end, after line 62 service_indexer; order inside the list is only meaningful for services with hard install-time dependencies, which voice does not have).

Renumber: the old Step 11 (syntax check) becomes Step 12; the old Step 12 (smoke test) becomes Step 13.

Acceptance criteria — add one entry:

  • service_install_all iterates service_voice as one of its entries — no separate test required; just grep the file.

Other in-flight / merged work — alignment check

  • Merged service_os / service_books / service_whiteboard / service_collab / service_biz / service_aibroker modules were all auto-patched by 85dfdc7 to pick up the --reset + svc_bins_ok pattern. No manual retrofit needed.
  • The HERO0_BASE_URL brittleness note in service_biz.nu is still accurate; nothing in the recent landings changes it.
  • The serve subcommand carve-out for service_books.nu is still accurate (confirmed by script: $"($bin) serve" at line 79 and 124 of the current merged file).

No other changes to the voice spec. The 10 implementation steps remain otherwise as posted.

## Spec addendum — alignment with commits landed 2026-04-20 Audited commits from kristof5, xmonader, and despiegk that landed after the last merge (PR #91). Summary: - **`85dfdc7` (despiegk) — service split**: moved `services.nu` → per-file modules and added `--reset` + `svc_bins_ok` short-circuit to every service's `install`. The spec's template (`service_indexer.nu`) was introduced in this commit and already carries the new pattern, so no change to the module code itself is needed — the existing plan is correct. - **`c6ad517` (kristof5) — `svc_require_sudo` now uses `is-admin`** instead of bare `whoami == "root"`; error message rephrased. Transparent to consumers; the module just calls `svc_require_sudo` and inherits the new behaviour. - **`c6ad517` + `packages.nu` (new)** — added `service_install_all` that iterates all services with `--update`. Every new service module should be registered there. - **`5796526` (kristof5)** — delegates template service builds to the service modules' `install`. No per-module change required beyond the standard `--reset` flag we already plan. - **`3d809fb` (despiegk) — forge partial-match**: transparent via `svc_update`. - **`9229d7e` (xmonader) — build-verification guardrail**: docs only; smoke-test already covers parse-check. ### Added to the implementation plan **Step 10 (Wire into `mod.nu`)** — keep as specified. **New Step 11 (Wire into `packages.nu`)**: - `tools/modules/services/packages.nu`: - Add `use service_voice.nu` to the imports block (currently lines 17/26 have `service_books.nu` / `service_indexer.nu` — append at the end of the use block). - Add `{name: "service_voice", run: {|| service_voice install --update}}` to the `services` list inside `service_install_all` (append at the end, after line 62 `service_indexer`; order inside the list is only meaningful for services with hard install-time dependencies, which voice does not have). **Renumber**: the old Step 11 (syntax check) becomes Step 12; the old Step 12 (smoke test) becomes Step 13. **Acceptance criteria** — add one entry: - [ ] `service_install_all` iterates `service_voice` as one of its entries — no separate test required; just grep the file. ### Other in-flight / merged work — alignment check - Merged service_os / service_books / service_whiteboard / service_collab / service_biz / service_aibroker modules were all auto-patched by `85dfdc7` to pick up the `--reset` + `svc_bins_ok` pattern. No manual retrofit needed. - The `HERO0_BASE_URL` brittleness note in `service_biz.nu` is still accurate; nothing in the recent landings changes it. - The `serve` subcommand carve-out for `service_books.nu` is still accurate (confirmed by `script: $"($bin) serve"` at line 79 and 124 of the current merged file). No other changes to the voice spec. The 10 implementation steps remain otherwise as posted.
Author
Owner

Spec addendum correction — packages.nu structure changed

Pulled two more commits that landed after my previous addendum:

  • 5e032e5 (kristof5, 10:29) — adds --template flag to service_install_all (mirrors built binaries into /home/template/hero/bin).
  • abd0a1d (kristof5, 10:44) — splits the single services list into services_core (proc, router, mycelium, code, codescalers, lib_rhai, embedder) and services_extra (everything else), adds --core flag.

Correction to Step 11 in the addendum: the services list no longer exists as a single entity. Voice goes into services_extra, not a flat list.

Updated Step 11 (Wire into packages.nu):

  • Add use service_voice.nu to the import block at the top (after use service_lib_rhai.nu or any appropriate position — order in the use block is not significant).
  • Add {name: "service_voice", run: {|| service_voice install --update}} to the services_extra list inside service_install_all (append at the end; order inside services_extra is not dependency-sensitive for voice).
  • Do NOT add voice to services_core — the core list is reserved for bootstrap services (proc/router/mycelium/code/codescalers/lib_rhai/embedder) that must come up first for a minimal install.

Nothing else in the spec changes. The voice module code itself is unaffected by these packages.nu refactors (the module's install contract is what service_install_all calls either way).

Other files touched in the six new commits (abd0a1d, 3e24368, 7864695, f4ec800, ec946fe, 5e032e5) are all installer / multiuser / hero_loader refactors — no impact on voice.

## Spec addendum correction — packages.nu structure changed Pulled two more commits that landed after my previous addendum: - **`5e032e5` (kristof5, 10:29)** — adds `--template` flag to `service_install_all` (mirrors built binaries into `/home/template/hero/bin`). - **`abd0a1d` (kristof5, 10:44)** — splits the single `services` list into `services_core` (proc, router, mycelium, code, codescalers, lib_rhai, embedder) and `services_extra` (everything else), adds `--core` flag. **Correction to Step 11 in the addendum**: the `services` list no longer exists as a single entity. Voice goes into **`services_extra`**, not a flat list. Updated Step 11 (**Wire into `packages.nu`**): - Add `use service_voice.nu` to the import block at the top (after `use service_lib_rhai.nu` or any appropriate position — order in the `use` block is not significant). - Add `{name: "service_voice", run: {|| service_voice install --update}}` to the **`services_extra`** list inside `service_install_all` (append at the end; order inside `services_extra` is not dependency-sensitive for voice). - Do NOT add voice to `services_core` — the core list is reserved for bootstrap services (proc/router/mycelium/code/codescalers/lib_rhai/embedder) that must come up first for a minimal install. Nothing else in the spec changes. The voice module code itself is unaffected by these packages.nu refactors (the module's `install` contract is what `service_install_all` calls either way). Other files touched in the six new commits (`abd0a1d`, `3e24368`, `7864695`, `f4ec800`, `ec946fe`, `5e032e5`) are all installer / multiuser / hero_loader refactors — no impact on voice.
Author
Owner

Implementation summary

Changes

  • Added tools/modules/services/service_voice.nu — ~203 lines, near-verbatim copy of service_indexer.nu with one UI-env addition (HERO_VOICE_STT_LOCAL: "true").
  • Updated tools/modules/services/mod.nu — appended export use service_voice.nu.
  • Updated tools/modules/services/packages.nu — added use service_voice.nu and {name: "service_voice", run: {|| service_voice install --update}} entry in services_extra.

Test results on Hetzner

Module-level assertions: all PASS.

# Assertion Result
Parse-check service_voice.nu standalone PASS
Parse-check packages.nu with use service_voice.nu PASS
use mod.nu * exports the 4 subcommands PASS
1a status hero_proc-down → guidance error PASS
1b stop hero_proc-down → warn and return PASS
1c start hero_proc-down → guidance error before anything PASS
2a service_proc start --root PASS

End-to-end smoke test blocked by upstream build issue.

service_voice install --root fails during the cargo build of whisper-rs-sys (hero_voice's STT dependency):

error: command did not execute successfully
  .../whisper-rs-sys-.../out/whisper.cpp/ggml/src/ggml.c:7603:
  fatal error: opening dependency file CMakeFiles/ggml-base.dir/ggml.c.o.d:
  Permission denied

The failure is in the nested CMake build that whisper-rs-sys's build.rs kicks off. Root of the failure is the interaction between sccache (configured as RUSTC_WRAPPER with server running under root's hero_proc) and whisper-rs-sys's CMake-driven C++ compilation. Direct gcc -MD -MT foo -MF foo.o.d -c foo.c works fine; wrapping through sccache + make + CMake breaks.

Reproduced class of issue:

  • Another user on the same box hit error writing dependencies to /home/<user>/hero/build/cargo/release/deps/herolib_tools-*.d: Permission denied during service_core install. Same "permission denied on a file the user owns" symptom.

Not in PR scope. The voice module is correct:

  • Action specs mirror service_indexer.nu's canonical shape (the latest template landed in 2026-04-20 by despiegk in 85dfdc7).
  • UI env correctly registers HERO_VOICE_STT_LOCAL alongside RUST_LOG — this is the one delta from the baseline.
  • Server action action-env has only RUST_LOG (matches hero_voice.toml's [server.env]).
  • mod.nu and packages.nu wire the module in with the correct ordering (voice → services_extra, not services_core, since it's not a bootstrap prerequisite).

Once the whisper-rs-sys / sccache interaction is resolved upstream or sccache is disabled for this build, the install + full end-to-end cycle will work without any change to this module.

Acceptance criteria

  • Module parses, loads, and exports install, start, stop, status.
  • mod.nu re-exports service_voice.
  • packages.nu services_extra contains service_voice.
  • UI action env wires HERO_VOICE_STT_LOCAL=true on top of RUST_LOG=info.
  • Server action env has only RUST_LOG=info.
  • hero_proc-down error paths produce the clean remediation message (PASS on 1a/1b/1c).
  • service_voice install succeeds end-to-end — blocked by upstream whisper-rs-sys/CMake+sccache interaction on the current Hetzner build environment. Not addressable in this module's scope. Parallels the upstream gating we saw on service_biz.nu (placeholder run_server) and service_os.nu (WASM assets prereq).

Follow-up (out of scope)

The Permission denied on .d / .o.d file writes inside cargo build directories is hitting multiple services and multiple users. Worth a dedicated ticket to either:

  1. Make RUSTC_WRAPPER=sccache opt-in per user instead of default, or
  2. Investigate why sccache's remote-builder / output-writeback path produces mode/owner combos that cargo's next rebuild can't update.

Tracked informally alongside issue #79 (the service_proc.nu:270 bug) — worth splitting into a proper follow-up once a maintainer triages.

## Implementation summary ### Changes - Added `tools/modules/services/service_voice.nu` — ~203 lines, near-verbatim copy of `service_indexer.nu` with one UI-env addition (`HERO_VOICE_STT_LOCAL: "true"`). - Updated `tools/modules/services/mod.nu` — appended `export use service_voice.nu`. - Updated `tools/modules/services/packages.nu` — added `use service_voice.nu` and `{name: "service_voice", run: {|| service_voice install --update}}` entry in `services_extra`. ### Test results on Hetzner **Module-level assertions: all PASS.** | # | Assertion | Result | |---|---|---| | — | Parse-check `service_voice.nu` standalone | PASS | | — | Parse-check `packages.nu` with `use service_voice.nu` | PASS | | — | `use mod.nu *` exports the 4 subcommands | PASS | | 1a | status hero_proc-down → guidance error | PASS | | 1b | stop hero_proc-down → warn and return | PASS | | 1c | start hero_proc-down → guidance error before anything | PASS | | 2a | `service_proc start --root` | PASS | **End-to-end smoke test blocked by upstream build issue.** `service_voice install --root` fails during the cargo build of `whisper-rs-sys` (hero_voice's STT dependency): ``` error: command did not execute successfully .../whisper-rs-sys-.../out/whisper.cpp/ggml/src/ggml.c:7603: fatal error: opening dependency file CMakeFiles/ggml-base.dir/ggml.c.o.d: Permission denied ``` The failure is in the nested CMake build that `whisper-rs-sys`'s `build.rs` kicks off. Root of the failure is the interaction between **sccache** (configured as `RUSTC_WRAPPER` with server running under root's hero_proc) and `whisper-rs-sys`'s CMake-driven C++ compilation. Direct `gcc -MD -MT foo -MF foo.o.d -c foo.c` works fine; wrapping through sccache + make + CMake breaks. Reproduced class of issue: - Another user on the same box hit `error writing dependencies to /home/<user>/hero/build/cargo/release/deps/herolib_tools-*.d: Permission denied` during `service_core install`. Same "permission denied on a file the user owns" symptom. **Not in PR scope.** The voice module is correct: - Action specs mirror `service_indexer.nu`'s canonical shape (the latest template landed in 2026-04-20 by despiegk in `85dfdc7`). - UI env correctly registers `HERO_VOICE_STT_LOCAL` alongside `RUST_LOG` — this is the one delta from the baseline. - Server action action-env has only `RUST_LOG` (matches `hero_voice.toml`'s `[server.env]`). - `mod.nu` and `packages.nu` wire the module in with the correct ordering (voice → `services_extra`, not `services_core`, since it's not a bootstrap prerequisite). Once the whisper-rs-sys / sccache interaction is resolved upstream or sccache is disabled for this build, the install + full end-to-end cycle will work without any change to this module. ### Acceptance criteria - [x] Module parses, loads, and exports `install`, `start`, `stop`, `status`. - [x] `mod.nu` re-exports `service_voice`. - [x] `packages.nu` `services_extra` contains `service_voice`. - [x] UI action env wires `HERO_VOICE_STT_LOCAL=true` on top of `RUST_LOG=info`. - [x] Server action env has only `RUST_LOG=info`. - [x] `hero_proc-down` error paths produce the clean remediation message (PASS on 1a/1b/1c). - [ ] `service_voice install` succeeds end-to-end — **blocked by upstream whisper-rs-sys/CMake+sccache interaction on the current Hetzner build environment**. Not addressable in this module's scope. Parallels the upstream gating we saw on `service_biz.nu` (placeholder `run_server`) and `service_os.nu` (WASM assets prereq). ### Follow-up (out of scope) The `Permission denied` on `.d` / `.o.d` file writes inside cargo build directories is hitting multiple services and multiple users. Worth a dedicated ticket to either: 1. Make `RUSTC_WRAPPER=sccache` opt-in per user instead of default, or 2. Investigate why sccache's remote-builder / output-writeback path produces mode/owner combos that cargo's next rebuild can't update. Tracked informally alongside issue #79 (the `service_proc.nu:270` bug) — worth splitting into a proper follow-up once a maintainer triages.
Author
Owner

PR opened: #93

PR opened: https://forge.ourworld.tf/lhumina_code/hero_skills/pulls/93
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_skills#92
No description provided.