Skip to content

Commit c0bf00b

Browse files
committed
Quality pass (2.9.0): verified every UI button/handler is wired (whole-app static scan, now enforced in CI so a broken build can't publish). Confirmed Ollama Cloud (ollama.com/api, gpt-oss:20b), Meeting Ask-AI presets, and Clips folders are all correctly hooked up. Restart button + simplified single sign-in retained. No new features - correctness + coherence.
1 parent 665e486 commit c0bf00b

4 files changed

Lines changed: 63 additions & 2 deletions

File tree

‎.github/workflows/build.yml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ jobs:
2828
python -m pip install --upgrade pip
2929
pip install -r requirements.txt pyinstaller
3030
31+
- name: Verify UI wiring (no undefined button handlers)
32+
run: python scripts/check_wiring.py
33+
3134
- name: Generate icon
3235
run: python scripts/gen_icon.py
3336

‎echoquill/__init__.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""EchoQuill — free, open-source, local-first voice dictation."""
22

3-
__version__ = "1.19.3"
3+
__version__ = "1.20.0"

‎installer.iss‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
; building dist\EchoQuill.exe with build_exe.bat.
66

77
#define AppName "EchoQuill"
8-
#define AppVersion "1.19.3"
8+
#define AppVersion "1.20.0"
99
#define AppExe "EchoQuill.exe"
1010

1111
[Setup]

‎scripts/check_wiring.py‎

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
"""Guardrail: flag any self.X used but never defined as a method, a class-level
2+
attribute, or an instance attribute (self.X = ...). Skips classes that inherit
3+
from an external base (their attrs can't be known statically)."""
4+
import ast, sys, glob
5+
6+
def class_defines(cls):
7+
names = set()
8+
# methods
9+
for n in cls.body:
10+
if isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef)):
11+
names.add(n.name)
12+
# class-level constants: NAME = ... / NAME: T = ...
13+
elif isinstance(n, ast.Assign):
14+
for t in n.targets:
15+
if isinstance(t, ast.Name):
16+
names.add(t.id)
17+
elif isinstance(t, (ast.Tuple, ast.List)):
18+
for e in t.elts:
19+
if isinstance(e, ast.Name):
20+
names.add(e.id)
21+
elif isinstance(n, ast.AnnAssign) and isinstance(n.target, ast.Name):
22+
names.add(n.target.id)
23+
# instance attrs assigned anywhere in the class
24+
for node in ast.walk(cls):
25+
if isinstance(node, ast.Attribute) and isinstance(node.value, ast.Name) \
26+
and node.value.id == "self" and isinstance(node.ctx, ast.Store):
27+
names.add(node.attr)
28+
return names
29+
30+
def has_external_base(cls):
31+
for b in cls.bases:
32+
if isinstance(b, ast.Name) and b.id == "object":
33+
continue
34+
return True # any real base -> may inherit attrs
35+
return False
36+
37+
def check_file(path):
38+
tree = ast.parse(open(path, encoding="utf-8").read(), path)
39+
out = []
40+
for cls in [n for n in ast.walk(tree) if isinstance(n, ast.ClassDef)]:
41+
if has_external_base(cls):
42+
continue
43+
defined = class_defines(cls)
44+
for node in ast.walk(cls):
45+
if isinstance(node, ast.Attribute) and isinstance(node.value, ast.Name) \
46+
and node.value.id == "self" and isinstance(node.ctx, ast.Load):
47+
a = node.attr
48+
if a in defined or (a.startswith("__") and a.endswith("__")):
49+
continue
50+
out.append(f"{path}:{node.lineno} {cls.name}.{a}")
51+
return out
52+
53+
allp = []
54+
for f in sorted(glob.glob("echoquill/*.py")):
55+
allp += check_file(f)
56+
if allp:
57+
print("WIRING PROBLEMS:"); print("\n".join(allp)); sys.exit(1)
58+
print("WIRING OK - every self.X reference resolves to a method/attr.")

0 commit comments

Comments
 (0)