Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions codec.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,11 +280,11 @@ def close_session():
with open(SESSION_ALIVE) as f: pid = int(f.read().strip())
os.kill(pid, 15)
print(f"[CODEC] Session process {pid} terminated")
except: pass
except Exception: pass
try: os.unlink(SESSION_ALIVE)
except: pass
except Exception: pass
try: os.unlink(TASK_QUEUE_FILE)
except: pass
except Exception: pass
subprocess.Popen(["osascript", "-e",
'tell application "Terminal" to close (every window whose name contains "python3.13 /var/folders")'],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
Expand Down Expand Up @@ -633,7 +633,7 @@ def do_stop_voice():
if rec_duration < 0.5:
print(f"[CODEC] Recording too short ({rec_duration:.1f}s) — ignored")
try: os.unlink(audio)
except: pass
except Exception: pass
return
if os.path.getsize(audio) < 1000:
try: os.unlink(audio)
Expand Down Expand Up @@ -861,7 +861,7 @@ def on_release(key):
ovl = state.get("overlay_proc")
if ovl:
try: ovl.terminate()
except: pass
except Exception: pass
state["overlay_proc"] = None
threading.Thread(target=lambda: subprocess.run(
['afplay', '/System/Library/Sounds/Pop.aiff'],
Expand Down
2 changes: 1 addition & 1 deletion codec_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ def speak_text(text):
tmp.close()
subprocess.run(["afplay", tmp.name], timeout=30)
try: os.unlink(tmp.name)
except: pass
except Exception: pass
finally:
tts_playing = False
tts_finished_at = time.time()
Expand Down
32 changes: 16 additions & 16 deletions codec_dictate.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ def hide_overlay():
try:
overlay_proc.terminate()
overlay_proc = None
except:
except Exception:
pass

# ── SHOW PROCESSING OVERLAY ───────────────────────────────────────────────────
Expand Down Expand Up @@ -151,7 +151,7 @@ def show_processing():
stderr=subprocess.DEVNULL
)
return p
except:
except Exception:
return None

# ── LIVE DICTATION (hands-free, double-tap Option) ──────────────────────────
Expand Down Expand Up @@ -213,21 +213,21 @@ def _producer():
)
if live_stop_event.is_set():
try: os.unlink(tmp.name)
except: pass
except Exception: pass
break
if os.path.exists(tmp.name) and os.path.getsize(tmp.name) >= 1000:
try:
q.put(tmp.name, timeout=1)
except queue.Full:
try: os.unlink(tmp.name)
except: pass
except Exception: pass
else:
try: os.unlink(tmp.name)
except: pass
except Exception: pass
except Exception as e:
print(f"[DICTATE] Producer error: {e}")
try: os.unlink(tmp.name)
except: pass
except Exception: pass

prod = threading.Thread(target=_producer, daemon=True)
prod.start()
Expand All @@ -247,7 +247,7 @@ def _producer():
wf.close()
if _np.abs(data).mean() < 150:
continue
except:
except Exception:
pass
with open(path, "rb") as f:
r = requests.post(WHISPER_SERVER,
Expand All @@ -271,7 +271,7 @@ def _producer():
print(f"[DICTATE] Live chunk error: {e}")
finally:
try: os.unlink(path)
except: pass
except Exception: pass

prod.join(timeout=3)
return full_text.strip()
Expand Down Expand Up @@ -312,11 +312,11 @@ def stop_live_dictation():
# Kill overlay — tkinter mainloop sometimes ignores SIGTERM, so SIGKILL it
if live_overlay:
try: live_overlay.terminate()
except: pass
except Exception: pass
try: live_overlay.wait(timeout=0.5)
except Exception:
try: live_overlay.kill()
except: pass
except Exception: pass
live_overlay = None
# Wait for thread
if live_thread:
Expand Down Expand Up @@ -380,7 +380,7 @@ def transcribe_and_type(audio_path):
if proc_overlay:
try:
proc_overlay.terminate()
except:
except Exception:
pass

if not text or is_hallucination(text):
Expand Down Expand Up @@ -441,12 +441,12 @@ def transcribe_and_type(audio_path):
if proc_overlay:
try:
proc_overlay.terminate()
except:
except Exception:
pass
finally:
try:
os.unlink(audio_path)
except:
except Exception:
pass

# ── KEYBOARD LISTENER ─────────────────────────────────────────────────────────
Expand All @@ -469,7 +469,7 @@ def on_press(key):
try:
recording_proc.terminate()
recording_proc.wait(timeout=2)
except: pass
except Exception: pass
recording_proc = None
recording_path = None
hide_overlay()
Expand Down Expand Up @@ -549,14 +549,14 @@ def _cleanup():
global recording_proc
if recording_proc:
try: recording_proc.terminate(); recording_proc.wait(timeout=2)
except: pass
except Exception: pass
recording_proc = None
hide_overlay()
if live_active:
stop_live_dictation()
for f in _glob.glob(os.path.join(tempfile.gettempdir(), "dictate_*.wav")):
try: os.unlink(f)
except: pass
except Exception: pass
atexit.register(_cleanup)
import signal
signal.signal(signal.SIGTERM, lambda *a: (print("[DICTATE] SIGTERM received"), _cleanup(), sys.exit(0)))
Expand Down
6 changes: 3 additions & 3 deletions codec_textassist.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
def get_config():
try:
with open(os.path.expanduser("~/.codec/config.json")) as f: return json.load(f)
except: return {}
except Exception: return {}

def call_qwen(text, mode):
cfg = get_config()
Expand Down Expand Up @@ -109,7 +109,7 @@ def overlay(text, color, duration):
# Kill processing overlay now that we have the result
if _proc_overlay:
try: _proc_overlay.terminate()
except: pass
except Exception: pass
if MODE in ("explain", "translate"):
# Show result in a styled floating window (no Terminal)
title = "CODEC Explain" if MODE == "explain" else "CODEC Translate"
Expand Down Expand Up @@ -182,5 +182,5 @@ def overlay(text, color, duration):
except Exception:
if _proc_overlay:
try: _proc_overlay.terminate()
except: pass
except Exception: pass
overlay("Error - check terminal", "#ff3333", 3000)
2 changes: 2 additions & 0 deletions docs/audits/PHASE-1-CODE-QUALITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,8 @@ Both scan `SKILLS_DIR` independently, so a skill file is loaded twice in differe
**Location:** `codec_dashboard.py:1114-1115`, `codec_dashboard.py:2998`, several others.

> **Partially closed by PR-3B** (branch `fix/pr3b-silent-except-cleanup`). The 20 A-3 sites — A-22's named HIGH-confidence subset — are all fixed (see A-3 closure). The clearest standalone HIDING_BUG the audit named — the post-LLM `[SKILL:name:query]` tag resolution (audit's old line 2998, now `codec_dashboard.py:3033`) — was a bare `except Exception: pass` that let a raw `[SKILL:...]` tag leak into the user's chat with zero footprint; now it logs `log.warning` + emits a `post_llm_skill_tag_failed` audit (behavior unchanged — tag stays, chat still returns). **Deliberately deferred (PR-3B-2):** the audit's "~50 HIDING_BUG" was an *estimate*, not an enumerated list, and the other named sites turned out to be **legitimate graceful-degradation paths** on inspection — e.g. `codec_voice.py:692` (Gemini vision → local Qwen fallback, already prints + falls back), `:750` (optional observer injection, explicitly "non-fatal"), `:773` (TTS returns None, caller handles it). The audit's named `codec_dashboard.py:1114` vision-DB-save site no longer exists in that form (code evolved). Aggressively narrowing working fallback paths risks regressions for no clear bug, so the residual A-22 sweep is split to a focused per-site survey (PR-3B-2) rather than rushed — protecting the "never break working code" invariant.

> **Survey completed + closed by PR-3B-2** (branch `fix/pr3b2-silent-except-survey`). Per-site survey of every remaining genuinely-silent `except: pass` in production (22 one-liners + their multi-line kin). **Finding: none are bug-hiders** — they're all legitimate resource teardown (`proc.terminate()`/`.kill()`/`.wait()`, `os.unlink()` temp cleanup) or best-effort stat gathering (`skills/system_info.py`). The genuine silent bug-hiders were already fixed in PR-3B (A-3 subset + the post-LLM tag). So no control-flow changes were made to those (correct — adding logging/audit to legitimate cleanup would be noise, and `codec_audit._write` must stay silent by design). **The one concrete anti-pattern the survey DID surface + fix: 36 BARE `except:`** (no exception type) across 12 production files (`codec_dictate.py` ×16, `codec.py` ×5, `skills/system_info.py` ×4, `codec_textassist.py` ×3, `codec_core.py`, `setup_codec.py`, + 6 skills) — a bare except also swallows `KeyboardInterrupt`/`SystemExit`/`GeneratorExit`, so Ctrl-C and clean shutdown could be silently eaten. All 36 converted to `except Exception:` (string-template `except:` inside the deprecated `build_session_script` generator left untouched — they're code-gen strings, not real handlers). Pinned by an AST-based regression guard (`tests/test_no_bare_except.py`) so bare excepts can't return. Full suite 1365 passing, zero new failures; manifest regenerated (7 skill files touched). **A-22 fully closed.**
**Description:** Categorization survey of 814 `except Exception` instances + 30 bare `except:` outside generated code:
- **LEGITIMATE_NONCRITICAL** (majority, ~80%): teardown of subprocess proc, tempfile cleanup, optional-import probing, "config file might not exist" loads, `log.debug` already in the handler. Examples: `codec.py:850-867` (recording-process cleanup with `log.debug`), `codec_core.py:191-194` (process-alive check), `codec_audit.py:_write` (audit must never raise by design).
- **HIDING_BUG** (minority, ~50 instances): line 1114 (`save vision response to DB`) catches `Exception` and just logs a warning, but image-message persistence failure means the chat panel will be silently missing the image. Line 2998 (post-LLM skill routing) silently swallows `Exception` while resolving a `[SKILL:name:query]` tag — if the skill blows up, the user gets a raw tag in their chat. Several `codec_voice.py` exception-handlers also fall in this bucket (line 691, 712, 750, 772, 921, etc.).
Expand Down
1 change: 1 addition & 0 deletions docs/audits/PHASE-1-CONSOLIDATED-TRIAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ Mirror the Intake Phase 3 wave pattern. 7 waves planned; sizes are PR-counts, NO
**PR count estimate:** 4-5
- PR-3A: A-1 + A-2 — delete the ~733 LOC of dead `build_session_script` + `skills/codec.py` fork. **High-leverage**: investors and contributors will read these files first. ✅ (branch `fix/pr3a-delete-dead-build-session-script`; verify-first dead-code trace confirmed both unreachable before deletion; codec.py orphan removed 1170→894 LOC + skills/codec.py fork deleted = ~734 LOC; `codec_core.build_session_script` deprecated copy KEPT per A-1; 1325 passing, fixed a pre-existing test, zero new failures)
- PR-3B: A-3 + A-22 — rewrite the silent-except sites with proper narrow-except + `log_event` audit ✅ A-3 fully closed (20 sites: keyboard/dashboard/skills, narrowed + re-leveled + better messages); A-22 partially closed (the named post-LLM `[SKILL:]` tag-resolution bug fixed). **Residual A-22 sweep → PR-3B-2**: the "~50" was an estimate; the other named sites are legitimate graceful-degradation fallback paths (codec_voice Gemini→local, observer injection, TTS) — narrowing them risks breaking working code, so a focused per-site survey is split out rather than rushed. (branch `fix/pr3b-silent-except-cleanup`)
- PR-3B-2: A-22 survey + bare-except fix ✅ (branch `fix/pr3b2-silent-except-survey`). Per-site survey concluded the remaining silent `except: pass` are all legitimate cleanup/best-effort (no bug-hiders left — those were fixed in PR-3B). Concrete fix: converted all **36 bare `except:` → `except Exception:`** across 12 production files (they swallowed KeyboardInterrupt/SystemExit). AST regression guard added (`tests/test_no_bare_except.py`). Full suite 1365 passing, zero new. **A-22 fully closed.**
- PR-3C: A-16 + A-17 + A-21 — wire `WAKE_PHRASES` (deduped homophone keywords + length-guarded phrase match in a testable `_is_wake_utterance`) + wire `draft_keywords` into `codec_core.is_draft` + remove dead `AGENT_NAME` constant ✅ (branch `fix/pr3c-wire-config-knobs`; 13 tests; zero net-new ruff; full suite 1338 passing). **A-4 (skill-loader unification) deliberately split out → its own PR**: it refactors the LIVE multi-file skill-dispatch path (`codec_core.load_skills` is called from codec.py + dashboard ×2 + voice + agent_runner), needs a careful voice-path test pass, and doesn't belong bundled with these contained config-wiring fixes.
- PR-3D: A-5 + A-6 + A-7 — extract helpers from the 3 monolithic functions (`_dispatch_inner`, `chat_completion`, `Agent.run`)
- PR-3E: A-11 + A-12 — unify vision + 51-site `chat/completions` through `codec_llm_proxy`
Expand Down
2 changes: 1 addition & 1 deletion setup_codec.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ def check_port(port):
s.connect(("localhost", port))
s.close()
return True
except:
except Exception:
return False

# ══════════════════════════════════════════════════════════════════════════════
Expand Down
14 changes: 7 additions & 7 deletions skills/.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"skills": {
"active_window.py": "31fdd25c071a29dfb0174d3b6c042c20370d5e88f4109c20fa5dc687bc25981c",
"ai_news_digest.py": "2f5b58d95cd74ea95eb43bc21b65ef4b3cf9ca38c055b0a88e4be213665c700a",
"app_switch.py": "aaba996619786edee552fd35b3a90bcacc5e30dfc3b6a4a3b479ff1ed97ffe58",
"app_switch.py": "c42155935805466ad6455d5a6ea47c61565edc6779b8d214063af466a4b2649f",
"ask_user.py": "5bb8009a2dc133dafafc2847b7d0ad7c5b31db91b5d687b1c0d90ecd9a7830bc",
"audit_report.py": "1ed0be5e6f49bb5f05826f69a81343a76f42d067e308de459191006b585dfa93",
"audit_verify.py": "887c68f340adc2d93b3ec307dbf149f1bb231478f31db817c5032bc04798eb0e",
Expand All @@ -13,7 +13,7 @@
"backup_status.py": "79caed39a5d64e7a8f9606a4b8a6c683908ff763394e18b035727716904eacbc",
"bitcoin_price.py": "892e646d36a2e41eb0a71b468f60a6377fe3c04768100dac94abe5225632011f",
"brightness.py": "a8e7b93a0b4f4c90baa1565ed73235e9f34eea622e0699afd11ae79dfab87eb3",
"calculator.py": "1023241909682c992ca0829c4b93b3cded9f2e539becef1d17aeaa10201dde5b",
"calculator.py": "f93d047ece1da993796fa33be77d114da459f766833e121f391820fed4b7061e",
"chrome_automate.py": "89f8ea61180bad1136582e0ed51ee72fbe57c62a4a501ff518c421f363fd67d9",
"chrome_click_cdp.py": "6453e5a08a8a3596f688a8b3815812d366c622cc6d9402d0ae4c572cf00a9e0f",
"chrome_close.py": "9c62736a5030ba1c511f6a0b198e2e7cf1c7b64714cf5b4df198cc20b5043b75",
Expand Down Expand Up @@ -42,7 +42,7 @@
"health_check.py": "ee295d393b1e1424dc07002d72a06a070e324737e4ed6b6deb5a0e60863471aa",
"imessage_send.py": "ba8a2577d78b5dc93f4c2c3882e5447661bb192d596182b001de4b35068ab18c",
"json_formatter.py": "995ad15bff5405cfde9ff9dd312998e7bac4337b4ad48e60c27342c7d27e54b2",
"lucy.py": "696d21c81ea37e156d254b0a7bba2ea769e37cd906452e48b9dc2891edc9c40f",
"lucy.py": "7c595d5605cd9913a8afa331ae0013861d6996f378f8a59aca0249f1b2f3a474",
"memory_entities.py": "5865b8a0bf7c29e2c2e9ee4b3bc1d1c20c90ef19df833bd3d4fce3e30b4da36d",
"memory_history.py": "acd89c56c1d17b8bde3b47e2e40ae60186e14c8f3ede22484dde55346b6821ce",
"memory_save.py": "92fef3ef623e74110988a94835e0936851f99bcbc6abf95fdc4cae55b5e9806a",
Expand All @@ -52,12 +52,12 @@
"network_info.py": "d255584410a412b9f76f4ea2ddebc0e9a5523f046aa5b752316bf2e22e221461",
"notes.py": "f0bab6652eafad881b40cddd354619ed82c2f2fb04e58c15a77ae04812b1fb02",
"notification_reader.py": "7fbff9f9063c39b0c4c0ea2568ecaab352c1fc4911382f3dd347f601b4a75e59",
"password_generator.py": "17f8e23d876d74cb100a652507d4dbb88a92d81c954e7728df24375f143ea21d",
"password_generator.py": "8affc3dcaaa57301fb808fd1600fc6b57a7d579c2b2fffb9513732e7db436c94",
"philips_hue.py": "bcc2ef1b7e6c0e5e5f0165aae397fd6a3fd27367a3f100b949cd0efddac43496",
"pilot.py": "33fe0a8cb1f3f577f3ddf801ad6d4e1cb3c9ceae8e6b748edd29ef33149ec1c7",
"plugin_approve.py": "c93861353be2a9ebe08df212ca167bea646962dbeafe704b1864cd78a8cf57cc",
"pm2_control.py": "1f67e3158920987c1f0d2452fcadd7405624b28a799297483a2d11f5d5c90d17",
"pomodoro.py": "ab6816d25c16e7132f899b4584d7fa6d5082d0c50811ffbd79d087e34c8861a5",
"pomodoro.py": "6c438ca3ce37afd10e9e96d3bc848e9e9407298761b06543b0aa00010317092d",
"process_manager.py": "742126eb470b89f91231b58d6e30da6a6683fdb435a2b05f6b070954853a19f9",
"python_exec.py": "e3e5bc4fe55b260858d7b510cd6ccd7dcb43958c2828200130af9e51db0fa7e8",
"qr_generator.py": "c5a6c451bb52f1b37d3a59bcc6db467c2c9c3e8b37f8ac74fd54315ca520ddcb",
Expand All @@ -68,14 +68,14 @@
"shift_report.py": "f11cd03f2a3a860564623a04a053f527cfff1f53be659754e201eb31bb9f8bb3",
"skill_forge.py": "07876ce6fda960c56b3d7f9a9fb1c028dfacd00393a3c96fdc209f8a5fb972d8",
"stuck.py": "dfddfe4dfa1a9d5c017f53e53033a7eb33114a517cd6fa937d9d44e65083f1c9",
"system_info.py": "02d8bbf6d69fd08895bf24ee7daccc002bca6f19b3bc161c1035cb01badefa1f",
"system_info.py": "4cc32e0ad9cb81f34309734ef3388f7cde63407de9b23bb0cd589571a4c43ecb",
"terminal.py": "e156caa2208acbb3802345f7b4f8746a5a14b2b0eac8e428c772e5b099ba2903",
"time_date.py": "b5a8f9341d8d4954833607eac41b5eb99e65e3bd30d0514a8b034245a7ca08dc",
"timer.py": "e7e77b82c5f3b3d60612455c57669ed91d2ffd71c7f084ee16317259785ba7f7",
"translate.py": "8ed19b54a1ab0bf83a640219cecafc28d7e5089bfffab11e9a1cebde36637407",
"tts_say.py": "b20b9ac44740cd4fc1896191d42b4491f6e99a7587a1b41cf87ff7dca5dd875f",
"volume.py": "2ca223e5048db83055dd1ccac21fbeef76d4c44cda047ab852f4eaf318193633",
"weather.py": "d52115291ad124c4671f8e3d7e337f11c824f7cb593e55c85c8e8f99070625fb",
"weather.py": "b787288d8fa7f42541435c016a36d09283b31035f414e33ab48817c7d16dd134",
"web_fetch.py": "e305aab7590a48b37a6f7fc170e76f471851dc69a043620725674758fed72f2a",
"web_search.py": "fff37c6bc28b7cfce7d682454a4127107d66998c72f95e976a95821aa5584748"
}
Expand Down
2 changes: 1 addition & 1 deletion skills/app_switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,5 +80,5 @@ def run(task, app="", ctx=""):
# Try open -a as fallback
subprocess.run(["open", "-a", app_name], capture_output=True, timeout=5)
return f"Opening {app_name}."
except:
except Exception:
return f"Couldn't find {app_name}."
2 changes: 1 addition & 1 deletion skills/calculator.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@ def run(task, app="", ctx=""):
if isinstance(result, float) and result == int(result):
result = int(result)
return f"{safe} = {result}"
except:
except Exception:
return None
2 changes: 1 addition & 1 deletion skills/lucy.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def run(task, app="", ctx=""):
if isinstance(data, dict) and data.get("output"):
return data["output"]
return "CODEC workflow responded but no output parsed"
except:
except Exception:
return r.text[:500] if r.text else "CODEC workflow processed but no response"
else:
return f"CODEC workflow error (status {r.status_code})"
Expand Down
Loading
Loading