personal memory agent

fix(transcribe): route STT by confidential usability master

- Bug: a stranded journal with services.confidential present and providers.local emptied of its credential made STT callers treat the confidential lane as active from bare block presence. They selected the confidential backend, then deferred forever in confidential.py::_headers; transcription silently stopped while the journal looked healthy. - Add spp.is_confidential_channel_usable(config): is_confidential_enabled(config) and not resolve_local_endpoint_from_config(config).is_bundled. It is config-pure: no network and no attestation probe. - Do not adopt is_confidential_enabled alone. It returns True for a credential-only config with no endpoint_url or served_model_id; that shape fails the forwarder probe with endpoint_invalid and would strand STT identically. - Keep the asymmetry explicit: routing follows channel usability. The egress refusal at observe/transcribe/__init__.py:204 stays on bare block presence because it answers whether raw audio may go remote, and it remains fail-closed and broad. - Changed observe/transcribe/main.py::resolve_default_backend; think/supervisor.py::linux_stt_uses_parakeet_cpp; and think/supervisor.py::_observe_parakeet_provider_truth. - In _parakeet_stt_admission_latch, all four uses now carry usability: payload/fingerprint key value, RAM-read short-circuit, selector call, and ram_blocked. - Changed apps/settings/routes.py to pass usability into apps/settings/transcribe_resource.py, the direct selector caller the original scope map missed. - Add three-way warning copy in main.py so explicit confidential fallback distinguishes disabled confidential audio, no confidential lane, and an incomplete channel that names missing fields. - Widen fixtures without weakening assertions. test_no_implicit_cloud.py uses _add_local_endpoint; both assertions are verbatim. - settings test_transcribe_resource_payload seeds endpoint_url, served_model_id, and credential; assertions are unchanged. - test_supervisor_parakeet.py and test_transcribe_cli.py replace monkeypatch-provenance-alone usable-channel setups with complete usable provider config where that was the intent. - Deliberate AC5 behavior change: stranded low-RAM now exits via SystemExit(1) from resolve_default_backend instead of EXIT_PROVIDER_BLOCKED (69) from deferral. - Data-safety finding: input audio remains on disk, no JSONL is written, _process_one is never reached, resolve_segment_gate reports incomplete, and sense's non-69 path does not delete, move, quarantine, or mark-failed the audio. - No config migration: confidential_lane_active is part of _parakeet_stt_admission_input, so old latch records hash to a different input_sha256 and are recomputed on the first tick. - Red-first evidence: AC1 and AC5 failed with "DID NOT RAISE SystemExit"; AC2 failed selecting confidential instead of parakeet; AC7 failed with not-desired instead of host-blocked and artifact-not-ready. AC6 was green on main by construction. - Reported, not fixed: linux_stt_uses_parakeet_cpp is dead in production; apps/settings/routes.py:209 backend coercion makes the settings needs_setup/notice path unreachable; routes.py:590 uses bool(...) instead of confidential_audio_enabled's true-when-unset default; the confidential_lane_active selector keyword and latch payload key remain a deferred rename. - Validated before commit: focused STT/settings/retention suites passed, then make ci passed with 14738 passed and 16 skipped. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>