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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
- The Meetings results list is now scrollable and shows a meeting counter.
- **Packaging now ships all runtime modules.** `ffp_meetings`, `ffp_notifications`, and `ffp_quill` were missing from `pyproject.toml` `py-modules` and the PyInstaller spec `hiddenimports`, so a wheel / frozen installer could omit them and crash on import even though source-tree tests passed. All three are now declared, and a new test (`test_packaging_modules`) asserts `py-modules` and the spec stay in sync with `scripts/*.py`.
- The Telemetry time-of-day chart now renders only **active hours** — zero-activity hours are dropped instead of drawn as empty bars (and an empty history shows "No activity yet").
- **Autostart unified to a single per-user entry.** Three independent autostart registrations had drifted out of sync: the daemon wrote `HKCU\...\Run\FastFlowPrompt` (what the dashboard toggle reads/writes), a source install (`install.ps1`) wrote a *different* value name (`HKCU\...\Run\Flowkey`) the toggle couldn't see, and the packaged installer optionally wrote a third, machine-wide `HKLM\...\Run\Flowkey` entry — enabling the dashboard toggle after either install path could add a redundant entry and launch the app twice at logon. All three now agree on the one per-user `HKCU\...\Run\FastFlowPrompt` entry the daemon owns; the packaged installer no longer offers a machine-wide autostart option, and its uninstaller now removes the per-user entry. Guarded by a new `test_installer_autostart` regression test.

### Internal

Expand Down
7 changes: 4 additions & 3 deletions SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Caveman-encoded (compression, not amputation). Paths / ids / action names / numb
- LLM: FastFlowLM NPU @ `:52625` | Ollama @ `:11434`, OpenAI-compat `POST /v1/chat/completions`
- dashboard: daemon-served `scripts/ui/web/{index.html,app.js,styles.css}`, CSP `default-src 'self'`
- paths: `scripts/paths.py` → USER_ROOT/{config,data,logs}; `_version.py` = version src of truth
- version: current `2.1.0` (held `release/v2.1.0`); public `2.0.0`; repo `agr77one/Fastflow`
- version: `2.1.0` merged to `main`, tag/installer/Release NOT yet cut; public `2.0.0`; repo `agr77one/Fastflow`
- run tree = `flowkey-pub2` (worktree, branch `live`=origin/main). old `FastFlowPrompt_Local_Setup`=1.5.0 stale.

## §I interfaces
Expand Down Expand Up @@ -81,13 +81,13 @@ T6|x|git autosync: `sync.ps1` + daily task + autostart→flowkey-pub2|V21,V22
T7|~|2.1.0 release held on `release/v2.1.0` → land after user test|V18,V19
T8|.|installer clean-VM smoke test|—
T9|.|[AUDIT] dead-code: unused daemon helper + 2 AHK wrappers + stale chat-popup config key + obsolete settings ref in test fixture + deprecated install shims|—
T10|.|[AUDIT-P1] autostart: unify 3 divergent Run keys (daemon/src-installer/pkg-installer) → single HKCU entry; fix UI autostart status reporting|V20
T10|x|[AUDIT-P1] autostart: unify 3 divergent Run keys (daemon/src-installer/pkg-installer) → single HKCU entry|V20,B6
T11|.|[AUDIT] old open_chat default `^+t` still appears in first-run + web config fallback → replace with `^!c`|B5
T12|.|[AUDIT] first-run seed thinner than DEFAULT_CONFIG schema → add seed-vs-schema drift guard (compare keys on first-run copy)|—
T13|.|[AUDIT] installer bootstrap wrapper hardcodes old installer filename → derive from `_version.py`|V18
T14|.|[AUDIT] quality-gate gaps: installer policy drift, autostart reg-name drift, bootstrap output name, README/dashboard tab count|V20
T15|.|[DOCS] dashboard docs: 7 tabs listed, live = 8 (add Benchmark)|—
T16|.|[DOCS] autostart docs conflict: main says no machine-wide entry; installer docs+impl still describe it → align on HKCU-only|
T16|x|[DOCS] autostart docs conflict: main says no machine-wide entry; installer docs+impl still describe it → align on HKCU-only|B6
T17|.|[DOCS] installer layout: build script says flattened, installer.md still shows nested layout|—
T18|.|[DOCS] provider roadmap marks selector/status UX incomplete → update to reflect it exists|—
T19|.|[DOCS] first-run wizard text: "chat popup" + retired hotkey → update to current|B5
Expand All @@ -103,4 +103,5 @@ B2|2026-06|bench history blank row from 0-point result file|skip `rows==[]` in `
B3|2026-06|autostart → stale tree / empty `flowkey-public`|repoint HKCU Run → flowkey-pub2 + bundled AHK
B4|2026-06|install launch: AHK called `.py`, shipped only `.exe`|flatten bundle to {app} + AHK→exe bridge (PR #19)
B5|2026-06|`Ctrl+Shift+T` open_chat collided w/ browser reopen-tab|default → `^!c`; tray label = configured hotkey
B6|2026-07|3 divergent autostart Run keys: daemon HKCU\Run\FastFlowPrompt, `install.ps1` HKCU\Run\Flowkey (different name!), `installer.iss` optional HKLM\Run\Flowkey → toggle blind to other 2, could double-launch|unify on HKCU\Run\FastFlowPrompt everywhere; drop installer.iss HKLM task; uninstall now cleans the HKCU value; guarded by `test_installer_autostart.py`
```
2 changes: 1 addition & 1 deletion installer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ Debug flags:
3. Asks (default = No) whether to wipe per-user data at
`%LOCALAPPDATA%\FastFlowPrompt\`. The user can decline and keep their
notes / config / logs across reinstalls.
4. Removes the HKLM `Run` autostart entry.
4. Removes the per-user `HKCU\...\Run\FastFlowPrompt` autostart entry, if one was set via the dashboard's "Launch Flowkey when I sign in" toggle. The installer itself never sets machine-wide (HKLM) autostart — that's a single per-user entry the daemon owns.

## End-user SmartScreen note

Expand Down
7 changes: 6 additions & 1 deletion installer/install.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,12 @@ $venvDir = Join-Path $scriptsDir ".venv"
$venvPythonw = Join-Path $venvDir "Scripts\pythonw.exe"

$RunKeyPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run"
$RunKeyName = "Flowkey"
# Must match ffp_daemon._AUTOSTART_VALUE_NAME exactly -- that daemon action is
# the single source of truth the dashboard checkbox reads/writes. A source
# install used to register a DIFFERENT value name ("Flowkey") here, so the
# dashboard toggle couldn't see it (and enabling the toggle added a second,
# redundant Run entry -> double launch at logon). See SPEC.md B6 / T10.
$RunKeyName = "FastFlowPrompt"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Remove the legacy Flowkey Run value during upgrades

When a user who installed from source with the previous script reruns installer/install.ps1 to upgrade, their HKCU ...\Run\Flowkey value is still present. This line makes the script write FastFlowPrompt instead, but step 5 only adds the new value and never deletes Flowkey, so Windows will launch both commands at sign-in and the dashboard can only toggle the new one. Please migrate/remove the old value before setting $RunKeyName and in -Uninstall.

Useful? React with 👍 / 👎.


function Info($m) { Write-Host "[FFP] $m" -ForegroundColor Cyan }
function Ok($m) { Write-Host "[ok] $m" -ForegroundColor Green }
Expand Down
22 changes: 13 additions & 9 deletions installer/installer.iss
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,11 @@ MinVersion=10.0.17763
Name: "english"; MessagesFile: "compiler:Default.isl"

[Tasks]
Name: "autostart"; Description: "Launch {#AppName} when Windows starts (all users)"; \
GroupDescription: "Additional options:"
; NOTE: autostart is intentionally NOT an install-time task. The daemon owns a
; single per-user HKCU\...\Run\FastFlowPrompt entry (Dashboard -> Config ->
; "Launch Flowkey when I sign in") -- that's the one source of truth. A prior
; machine-wide HKLM task here used a different value name and could run
; alongside the per-user one, double-launching the app at logon. See T10/B6.
Name: "desktopicon"; Description: "Create a desktop shortcut"; \
GroupDescription: "Additional options:"; Flags: unchecked

Expand Down Expand Up @@ -160,13 +163,6 @@ Name: "{commondesktop}\{#AppName}"; Filename: "{app}\ahk\AutoHotkey64
Parameters: """{app}\scripts\grammarFix.ahk"""; WorkingDir: "{app}"; \
IconFilename: "{app}\ffp-daemon.exe"; Tasks: desktopicon

[Registry]
; --- Autostart (per-machine HKLM Run) — controlled by the autostart task -----
Root: HKLM; Subkey: "Software\Microsoft\Windows\CurrentVersion\Run"; \
ValueType: string; ValueName: "{#AppName}"; \
ValueData: """{app}\ahk\AutoHotkey64.exe"" ""{app}\scripts\grammarFix.ahk"""; \
Flags: uninsdeletevalue; Tasks: autostart

[UninstallRun]
; --- 1. Stop our processes before removing files -----------------------------
; CloseApplications=force handles in-use files but a windowless daemon
Expand All @@ -177,6 +173,14 @@ Filename: "{sys}\taskkill.exe"; \
Parameters: "/F /IM AutoHotkey64.exe /FI ""WINDOWTITLE eq grammarFix*"""; \
RunOnceId: "KillAhk"; Flags: runhidden waituntilterminated

; --- 1b. Remove the per-user autostart entry, if the dashboard toggle or a
; source install set it (installer itself never sets it — see [Tasks]).
; reg.exe exits non-zero when the value is absent; that's fine, Inno
; doesn't treat a nonzero [UninstallRun] exit as fatal.
Filename: "{sys}\reg.exe"; \
Parameters: "delete ""HKCU\Software\Microsoft\Windows\CurrentVersion\Run"" /v ""FastFlowPrompt"" /f"; \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Clean up the legacy HKLM autostart on installer upgrades

For machines that installed a previous package with the old autostart task selected, upgrading to this installer leaves HKLM\...\Run\Flowkey in place because the new script has no install-time migration for that legacy value. This cleanup only deletes the per-user HKCU\...\Run\FastFlowPrompt; after the upgrade, logon still starts the machine-wide entry that the dashboard cannot see, and enabling the per-user toggle double-launches again. Add an install/uninstall migration that deletes the legacy HKLM value.

Useful? React with 👍 / 👎.

RunOnceId: "RemoveAutostart"; Flags: runhidden waituntilterminated

; --- 2. Chain FLM uninstaller — but ONLY if we installed it ------------------
; We tagged it with {app}\.flm_installed_by_us. Pascal helper reads the
; QuietUninstallString out of the registry and runs it silently.
Expand Down
1 change: 0 additions & 1 deletion scripts/first_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,6 @@ def __init__(self) -> None:
self.var_base_url = tk.StringVar(value=str(llm.get("base_url") or self.cfg.get("flm_base_url") or base_url))
self.var_model = tk.StringVar(value=str(llm.get("model") or self.cfg.get("flm_model") or default_model))
self.var_license_accept = tk.BooleanVar(value=False)
self.var_autostart_hint = tk.BooleanVar(value=True) # display-only — installer owns Run key

hk = self.cfg.get("hotkeys") or {}
self.var_hotkeys: dict[str, tk.StringVar] = {
Expand Down
47 changes: 47 additions & 0 deletions tests/test_installer_autostart.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"""Drift guard: autostart must have exactly ONE mechanism — the daemon's
per-user HKCU\\...\\Run entry (ffp_daemon._AUTOSTART_VALUE_NAME), managed by the
dashboard's "Launch Flowkey when I sign in" toggle.

Regression: three independent Run-key registrations had drifted out of sync —
the daemon wrote HKCU\\Run\\FastFlowPrompt, installer/install.ps1 (source
install) wrote a DIFFERENT value HKCU\\Run\\Flowkey, and installer/installer.iss
(packaged installer) optionally wrote a separate machine-wide HKLM\\Run\\Flowkey
via an install-time task. The dashboard toggle only knew about the first, so a
source or packaged install could register autostart the toggle couldn't see or
control, and enabling the toggle afterwards added a second, redundant entry —
launching the app twice at logon.
"""

from __future__ import annotations

import re
import sys
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
SCRIPTS = ROOT / "scripts"

if str(SCRIPTS) not in sys.path:
sys.path.insert(0, str(SCRIPTS))

import ffp_daemon # noqa: E402


def test_install_ps1_uses_the_same_value_name_as_the_daemon():
text = (ROOT / "installer" / "install.ps1").read_text(encoding="utf-8")
m = re.search(r'\$RunKeyName\s*=\s*"([^"]+)"', text)
assert m, "install.ps1 no longer declares $RunKeyName"
assert m.group(1) == ffp_daemon._AUTOSTART_VALUE_NAME


def test_installer_iss_has_no_machine_wide_autostart():
text = (ROOT / "installer" / "installer.iss").read_text(encoding="utf-8")
assert "Tasks: autostart" not in text, "a separate install-time autostart task reappeared"
assert not re.search(r"Root:\s*HKLM.*\n.*CurrentVersion\\Run", text), \
"installer.iss writes an HKLM Run entry — autostart must be per-user (HKCU) only"


def test_installer_iss_uninstall_cleans_the_same_value_name():
text = (ROOT / "installer" / "installer.iss").read_text(encoding="utf-8")
assert f'/v ""{ffp_daemon._AUTOSTART_VALUE_NAME}""' in text, \
"uninstaller doesn't clean up the daemon's actual per-user autostart value"