Explorar el Código

Consolidated troubleshooting file location

Efren Yevale Varela hace 1 mes
padre
commit
4944e4c956

+ 1 - 3
.gitignore

@@ -10,14 +10,12 @@
 .venv/
 __pycache__/
 audio.json
-audit/
 build/
 config.toml
-debug/
 dist/
 models/
 questions.json
 speakers/
 transcripts/
-triage/
+troubleshoot/
 venv/

+ 2 - 2
AGENTS.md

@@ -64,7 +64,7 @@ publish-only.
   `XDG_RUNTIME_DIR` (sockets), `HF_HOME` (Whisper cache, honored by faster-whisper/huggingface).
 - `[triage]` is the display-only signal panel, off by default (`enabled = false`). Its
   `questions_file` (`questions.json` in the cwd) is private user state and is gitignored, like
-  `config.toml`. `dump_io = true` writes one JSON per triage under `triage/` (also gitignored);
+  `config.toml`. `dump_io = true` writes one JSON per triage under `troubleshoot/triage/` (also gitignored);
   a coalesced brief update writes two files. The brief engine is driven by
   `[llm].triage_system_prompt` and returns two plain-text sections (`NEW FACTS:` + `BRIEF:`);
   code owns the append-only `FactLedger` (dedupe/eviction/render, `ledger_max_facts`), so the
@@ -112,7 +112,7 @@ publish-only.
 ## Conventions & gotchas
 
 - Never commit gitignored runtime output: `config.toml`, `transcripts/`, `audio.json`, `audio/`,
-  `audit/`, `triage/`, `brief/`, `debug/`, `speakers/` (biometric), `models/`, `.omo/`.
+  `troubleshoot/` (audit/debug/triage), `brief/`, `speakers/` (biometric), `models/`, `.omo/`.
 - New assistant tools: register in `assistant/tools.py`. Model-supplied arguments are JSON-parsed and
   schema-validated before running; set `side_effect` and `requires_confirmation` for anything
   non-read-only.

+ 1 - 1
README.md

@@ -33,7 +33,7 @@ TranscriptSink (stdout + <session_dir>/<id>/transcripts.jsonl) + EventPublisher
     ↓  publish-only UNIX socket ($XDG_RUNTIME_DIR/stt-daemon.sock)
 stt-assistant  (subscribes; owns modes, command buffer, chat, LLM, tools)
     ↓  control socket ($XDG_RUNTIME_DIR/stt-assistant.sock)
-stt-tui (viewer) / stt-ctl (hotkeys)              audit/YYYY-MM-DD.jsonl ←┘
+stt-tui (viewer) / stt-ctl (hotkeys)     troubleshoot/audit/YYYY-MM-DD.jsonl ←┘
 ```
 
 The daemon never accepts input: it only transcribes and publishes. Everything stateful lives in the assistant, a separate process (see [docs/ASSISTANT.md](docs/ASSISTANT.md)).

+ 2 - 2
config.example.toml

@@ -66,7 +66,7 @@ chat_exit_phrases     = ["end conversation", "that's all", "stop listening"]
 
 [audit]
 enabled = true
-dir     = "audit"
+dir     = "troubleshoot/audit"
 
 [llm]
 enabled                        = true
@@ -140,6 +140,6 @@ write = false
 
 [debug]
 enabled           = false
-dir               = "debug"
+dir               = "troubleshoot/debug"
 heartbeat         = true
 max_bytes_per_day = 50000000

+ 2 - 2
docs/ASSISTANT.md

@@ -99,7 +99,7 @@ red otherwise).
 
 ## Audit
 
-Only **actions** are persisted, to `audit/YYYY-MM-DD.jsonl`:
+Only **actions** are persisted, to `troubleshoot/audit/YYYY-MM-DD.jsonl`:
 
 ```json
 {"ts":"...","mode":"command","text":"what time is it","edited":false,
@@ -194,7 +194,7 @@ its buffer; an explicit `clear` does (it also truncates the durable brief file).
 active conversation folder (gitignored, falling back to `brief/brief.md` when no session is active);
 with the default `write = false` it stays in the TUI only. A new conversation resets the brief, so
 each session folder gets a fresh one. With `dump_io = true` each
-triage also writes its I/O under `triage/` (a coalesced update writes two files; see
+triage also writes its I/O under `troubleshoot/triage/` (a coalesced update writes two files; see
 [TRIAGE.md](TRIAGE.md)).
 
 ## Speakers

+ 12 - 11
docs/CONFIGURATION.md

@@ -89,8 +89,8 @@ require_viewer        = true                # Refuse to execute commands with no
 chat_exit_phrases     = ["end conversation", "that's all", "stop listening"]
 
 [audit]                                     # Executed actions only (commands/chats are volatile)
-enabled = true                              # audit/YYYY-MM-DD.jsonl
-dir     = "audit"
+enabled = true                              # troubleshoot/audit/YYYY-MM-DD.jsonl
+dir     = "troubleshoot/audit"
 
 [llm]                                       # local llama.cpp (OpenAI-compatible)
 enabled                  = true
@@ -141,7 +141,7 @@ fact_max_chars       = 240                  # per-fact character cap; over-long
 device               = ""                   # "" = auto -> None (Laya picks the multilingual checkpoint's default device)
 preload              = ["multilingual"]     # Fixed default: only the multilingual checkpoint is preloaded
 default_checkpoint   = "multilingual"       # Router default; multilingual is pinned for every call
-dump_io              = false                # Opt-in triage I/O dump: one JSON per triage under triage/YYYY-MM-DD/HHMMSS-mmm.json (Laya request/response + LLM request/response; best-effort, gitignored)
+dump_io              = false                # Opt-in triage I/O dump: one JSON per triage under troubleshoot/triage/YYYY-MM-DD/HHMMSS-mmm.json (Laya request/response + LLM request/response; best-effort, gitignored)
 
 [speaker]                                   # Speaker identification (sherpa-onnx embedding lane; off by default)
 enabled           = false                    # Master switch: false = no embedding lane, every record status "none"
@@ -164,7 +164,7 @@ write = false                                # false = brief shown in the TUI on
 
 [debug]                                     # Structured health-signal log (no transcript text; off by default)
 enabled          = false                    # false = no debug lines, zero overhead (DebugSink is None)
-dir              = "debug"                  # Destination directory (cwd-relative); gitignored, one JSONL per day
+dir              = "troubleshoot/debug"    # Destination directory (cwd-relative); gitignored, one JSONL per day
 heartbeat        = true                     # true = ~1 line/s heartbeat signals; false = anomalies only
 max_bytes_per_day = 50000000                # Best-effort per-day byte cap; writes past the cap are skipped
 ```
@@ -227,7 +227,7 @@ question head, leaving ~830 tokens of room for state at the default. `confidence
 confidence values drives the per-item `flagged`/`⚠` marker, with `answer_confidence` (the
 calibrated `max(p)`) as the default. With `enabled = false` (the default) the lane is never built and the system behaves
 exactly as before. `dump_io = true` additionally writes one JSON file per triage under
-`triage/YYYY-MM-DD/HHMMSS-mmm.json` (local time, millisecond precision, same naming scheme as
+`troubleshoot/triage/YYYY-MM-DD/HHMMSS-mmm.json` (local time, millisecond precision, same naming scheme as
 the audio files) containing the Laya request/response and the LLM request/response; it is
 off by default, best-effort (a dump failure never disturbs triage), and gitignored.
 
@@ -264,11 +264,11 @@ display-only: the TUI renders it and nothing is written to disk. With `write = t
 assistant snapshots the current brief as plain text to
 `<session_dir>/<YYYY-MM-DD-HH-MM-SS>/brief.md` (cwd-relative, gitignored): one folder per
 conversation, named by the first transcription's `YYYY-MM-DD-HH-MM-SS`, holding that
-conversation's transcript JSONL, audio, brief, and screenshots, while audit/debug/triage
-remain global. Starting a new conversation resets the brief; clearing the brief truncates
+conversation's transcript JSONL, audio, brief, and screenshots, while the `troubleshoot/`
+diagnostics remain global. Starting a new conversation resets the brief; clearing the brief truncates
 the file.
 
-`[debug]` persists per-component health signals to `debug/YYYY-MM-DD.jsonl` (one JSON
+`[debug]` persists per-component health signals to `troubleshoot/debug/YYYY-MM-DD.jsonl` (one JSON
 object per line, `ensure_ascii=False`) so the next silent freeze is diagnosable from disk.
 Signals only — no transcript text, no audio, no prompt content: RMS/queue/timing fields such
 as `device_ok`, `worker_stall_s`, `queue_depth`, `inference_depth`, plus anomaly lines for
@@ -278,9 +278,10 @@ capture reopens, inference-queue `queue-full` / `eviction-drop`, `llm-timeout`,
 exit lines. With
 `enabled = false` (the default) no sink is built and the suite stays disk-clean; with
 `heartbeat = false` only anomalies are written. `max_bytes_per_day` is a best-effort guard:
-writes past the cap are skipped with one warning (never crash, never rotate). `debug/` lives
-on local storage (a slow/NFS/FUSE mount can block the only capturer) and is gitignored like
-`transcripts/`, `audit/`, and `triage/`; clean it manually with `rm debug/*`. The assistant
+writes past the cap are skipped with one warning (never crash, never rotate). `troubleshoot/debug/`
+lives on local storage (a slow/NFS/FUSE mount can block the only capturer); the whole
+`troubleshoot/` tree is gitignored like `transcripts/`; clean it manually with
+`rm -r troubleshoot/`. The assistant
 constructs its sink via `debug_sink_factory` threaded through `runner.main` (`None` when
 disabled), and every assistant emit runs after its `with self._lock` block — never under the
 RLock — so a slow write cannot stall the ticker + event reader.

+ 1 - 1
docs/DEVELOPMENT.md

@@ -50,7 +50,7 @@ src/stt_daemon/
 ├── daemon.py            - Orchestrator (threads, queues, event emission)
 ├── events.py            - NDJSON event envelope + codec
 ├── bus.py               - Publish-only event bus (socket, ring replay)
-├── debug.py             - Structured health-signal log (`DebugSink`, `debug/YYYY-MM-DD.jsonl`)
+├── debug.py             - Structured health-signal log (`DebugSink`, `troubleshoot/debug/YYYY-MM-DD.jsonl`)
 ├── assistant/
 │   ├── modes.py         - Mode state machine (idle/command_armed/executing/chat)
 │   ├── service.py       - Modes, command buffer, readiness, timeouts

+ 1 - 1
docs/OUTPUT.md

@@ -72,6 +72,6 @@ emitted and a warning is logged.
 > actually need the clips for auditing.
 
 **Triage I/O dump (assistant-side)** — with `[triage].dump_io = true` the assistant writes one
-self-contained JSON per triage under `triage/YYYY-MM-DD/HHMMSS-mmm.json` (the Laya request/response
+self-contained JSON per triage under `troubleshoot/triage/YYYY-MM-DD/HHMMSS-mmm.json` (the Laya request/response
 plus the LLM request/response). It is separate from the daemon transcript output above, off by
 default, and best-effort. See [TRIAGE.md](TRIAGE.md#opt-in-io-dump-dump_io).

+ 3 - 3
docs/TRIAGE.md

@@ -306,7 +306,7 @@ file.
 | `device` | `""` | Empty string means auto, which maps to `None` so Laya picks the checkpoint's default device. Any other value is passed through. |
 | `preload` | `["multilingual"]` | Checkpoints preloaded at lane startup. The fixed default preloads the multilingual checkpoint only. |
 | `default_checkpoint` | `"multilingual"` | Router default and the per-request `model` for every call. Multilingual is pinned. |
-| `dump_io` | `false` | Opt-in I/O dump under `triage/YYYY-MM-DD/HHMMSS-mmm.json`; a coalesced update writes two files (see below). |
+| `dump_io` | `false` | Opt-in I/O dump under `troubleshoot/triage/YYYY-MM-DD/HHMMSS-mmm.json`; a coalesced update writes two files (see below). |
 
 `[llm].triage_system_prompt` drives the brief engine: it is sent as the static system
 message, and the user turn carries the code-owned fact ledger (`CURRENT LEDGER`), the volatile
@@ -329,7 +329,7 @@ each session folder gets a fresh one.
 ## Opt-in I/O dump (`dump_io`)
 
 With `[triage].dump_io = true` (default `false`) each triage writes a self-contained JSON
-file under `triage/YYYY-MM-DD/HHMMSS-mmm.json` — local time with millisecond precision, the
+file under `troubleshoot/triage/YYYY-MM-DD/HHMMSS-mmm.json` — local time with millisecond precision, the
 same naming scheme as the audio files (`<session_dir>/<id>/audio/HHMMSS-mmm-<reason>.wav`). A file
 holds the Laya request, the Laya response, the LLM request (endpoint URL plus the full
 chat-completion body), and the LLM response (raw envelope), keyed by the `ts` carried on the
@@ -355,7 +355,7 @@ record overwrites another.
 When the brief does not run (the master switch is off, the gate fails, or no brief function
 is wired) the file still gets written with only the `laya` half (`llm` is `null`); on a Laya
 error the file records `laya.response = null` with `laya.error = "predict failed"`. The
-directory is fixed as `triage/` (cwd-relative, like the session folders and `audit/`) —
+directory is fixed as `troubleshoot/triage/` (cwd-relative, like the session folders) —
 there is no second config key for it. Files contain conversation text, so they are
 gitignored private debug output. Only the triage Laya predict and the triage brief call are
 dumped (never the loader warmup predict or non-triage LLM calls), and dumping is best-effort:

+ 4 - 4
docs/TROUBLESHOOTING.md

@@ -128,10 +128,10 @@ uv run stt-daemon --model small
 
 With `[debug] enabled = true`, the debug log is the first place to look — it is the only
 structured record (child stdout/stderr is discarded, so there is no raw stderr complement).
-Each line is one JSON object in `debug/YYYY-MM-DD.jsonl`; no transcript text is stored.
+Each line is one JSON object in `troubleshoot/debug/YYYY-MM-DD.jsonl`; no transcript text is stored.
 
 ```bash
-tail -n 100 debug/$(date +%F).jsonl
+tail -n 100 troubleshoot/debug/$(date +%F).jsonl
 ```
 
 Read the tail in this order:
@@ -156,8 +156,8 @@ Read the tail in this order:
    (`gate` / `extract_disabled` / `no_brief_fn`). A run that only ever shows `triage-submit`
    with no `triage-result` means the Laya predict is still loading or stalled.
 
-When the system is healthy again, clear the log manually (`rm debug/*`); there is no
-automatic retention job. Keep `debug/` on local storage — a slow/NFS/FUSE mount can block
+When the system is healthy again, clear the log manually (`rm -r troubleshoot/`); there is no
+automatic retention job. Keep `troubleshoot/` on local storage — a slow/NFS/FUSE mount can block
 the only capturer.
 
 ## Speaker identification

+ 1 - 1
src/stt_daemon/assistant/audit.py

@@ -15,7 +15,7 @@ logger = logging.getLogger(__name__)
 class AuditLog:
     """Appends action records to `dir/YYYY-MM-DD.jsonl`."""
 
-    def __init__(self, enabled: bool = True, directory: str = "audit"):
+    def __init__(self, enabled: bool = True, directory: str = "troubleshoot/audit"):
         """Create the audit log.
 
         Args:

+ 1 - 1
src/stt_daemon/assistant/runner.py

@@ -102,7 +102,7 @@ def main() -> int:
         except Exception:  # noqa: BLE001 - debug stays disabled without the module
             DebugSink = None  # type: ignore[assignment]
         if DebugSink is not None:
-            debug_dir = str(getattr(debug_cfg, "dir", "debug"))
+            debug_dir = str(getattr(debug_cfg, "dir", "troubleshoot/debug"))
 
             def _make_debug_sink():
                 return DebugSink(debug_dir, "assistant")

+ 5 - 5
src/stt_daemon/assistant/triage_dump.py

@@ -2,12 +2,12 @@
 
 Enabled via `[triage].dump_io = true` (off by default). When enabled, each
 triage handled in `_on_triage_result` produces exactly one file under
-`triage/YYYY-MM-DD/HHMMSS-mmm.json` (local time, millisecond precision — the
-same naming scheme as the session audio
+`troubleshoot/triage/YYYY-MM-DD/HHMMSS-mmm.json` (local time, millisecond
+precision — the same naming scheme as the session audio
 `<session_dir>/<id>/audio/HHMMSS-mmm-<reason>.wav`).
 
-The directory `triage/` is fixed (cwd-relative, like `transcripts/`/`audit/`)
-and gitignored. Files contain conversation text (transcript state,
+The directory `troubleshoot/triage/` is fixed (cwd-relative, like
+`transcripts/`) and gitignored. Files contain conversation text (transcript state,
 Laya questions/answers, LLM prompts), so treat them as private debug output.
 
 Best-effort by design: a dump failure is logged and swallowed, never raised,
@@ -35,7 +35,7 @@ logger = logging.getLogger(__name__)
 class TriageDumper:
     """Thread-safe, best-effort per-triage I/O recorder."""
 
-    def __init__(self, enabled: bool, directory: str = "triage") -> None:
+    def __init__(self, enabled: bool, directory: str = "troubleshoot/triage") -> None:
         self._enabled = bool(enabled)
         self._directory = str(directory)
         self._lock = threading.Lock()

+ 2 - 2
src/stt_daemon/config.py

@@ -113,7 +113,7 @@ class AssistantConfig:
 class AuditConfig:
     """Action audit configuration (daily JSONL, like transcripts)."""
     enabled: bool = True
-    dir: str = "audit"
+    dir: str = "troubleshoot/audit"
 
 
 @dataclass
@@ -229,7 +229,7 @@ class BriefConfig:
 class DebugConfig:
     """Debug signal-log configuration (JSONL diagnostics, gitignored)."""
     enabled: bool = False
-    dir: str = "debug"
+    dir: str = "troubleshoot/debug"
     heartbeat: bool = True
     max_bytes_per_day: int = 50000000
 

+ 1 - 1
src/stt_daemon/daemon.py

@@ -340,7 +340,7 @@ class Daemon:
         """Default debug sink builder (lazy import; duck-typed by callers)."""
         from .debug import DebugSink
 
-        return DebugSink(getattr(self._debug_config(), "dir", "debug"), "daemon")
+        return DebugSink(getattr(self._debug_config(), "dir", "troubleshoot/debug"), "daemon")
 
     def _debug_emit(self, type_: str, **fields: Any) -> None:
         """Best-effort debug signal line; never raises, never blocks shutdown."""

+ 1 - 1
src/stt_daemon/supervisor.py

@@ -109,7 +109,7 @@ class Supervisor:
         from .debug import DebugSink
 
         debug_cfg = getattr(self.cfg, "debug", None)
-        sink_dir = getattr(debug_cfg, "dir", "debug")
+        sink_dir = getattr(debug_cfg, "dir", "troubleshoot/debug")
         return DebugSink(sink_dir, "supervisor")
 
     def _flush_debug(self) -> None:

+ 2 - 2
tests/test_config.py

@@ -1036,7 +1036,7 @@ def test_debug_defaults():
             os.chdir(old_cwd)
 
     assert cfg.debug.enabled is False
-    assert cfg.debug.dir == "debug"
+    assert cfg.debug.dir == "troubleshoot/debug"
     assert cfg.debug.heartbeat is True
     assert cfg.debug.max_bytes_per_day == 50000000
 
@@ -1094,7 +1094,7 @@ bogus_key = 42
 
     assert cfg.debug.enabled is True
     assert not hasattr(cfg.debug, "bogus_key")
-    assert cfg.debug.dir == "debug"
+    assert cfg.debug.dir == "troubleshoot/debug"
 
 
 def test_unknown_top_level_still_warns(caplog):