Skip to content

Commit 66bb892

Browse files
agr77oneagcycleclaude
authored
2.5.0: Notes workspace, vision board, and Quill/notes reliability fixes (#39)
* T31: add Notes v2 repository and board storage Cites V7, V48, V49, V50, V51, V52, V54. * T32: expose Notes v2 daemon actions Cites V1, V2, V3, V7, V48, V50, V53, V54. * T33: move Notes settings into Config Cites V3 and V47. * T34: build living Notes workspace and vision board Cites V5, V47, V48, V50, V52, V54, V55, V56. * T35: open Notes composer from capture hotkey Cites V53. * T36: release Notes workspace as 2.5.0 Cites V18, V20, V47, V48, V49, V50, V51, V52, V53, V54, V55. * T37: repair Quill transcript digest pipeline * T38: add guarded smart note categories * T39: redesign compact Config workspace * B43: isolate Notes daemon tests from real vault * T38: sort note categories in config snapshots * B44-B46: pre-release fixes for Notes v2 and Quill error handling - Serialize note schema migration writes under _NOTES_LOCK and fall back to an unmigrated read instead of silently dropping a note when the migration write fails (V61). - Restore ffp_quill's documented fail-soft contract: get_minutes/ get_transcript/search_meetings/list_recent_meetings catch QuillToolError instead of letting it propagate, which was silently no-oping scheduled meeting batches and breaking the permanent-skip path for dead meetings (V62). - Add the missing optimistic-concurrency revision check to trash_note, and guard the two remaining bare int() revision parses in the daemon. Found via code-review skill pass across 8 angles before shipping 2.5.0. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: agrechenkov <arseniy.grechenkov@cyclelabs.io> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 0125a50 commit 66bb892

35 files changed

Lines changed: 4215 additions & 407 deletions

‎CHANGELOG.md‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,33 @@
22

33
## Unreleased
44

5+
## 2.5.0
6+
7+
**Notes now works like a notepad, sticky-note wall, and vision board instead of a file browser.** Capture first, shape the note in an editor, and organize it without leaving the Notes tab.
8+
9+
### Added
10+
11+
- **Living Notes workspace.** Notes render as responsive sticky cards with dedicated types for notes, tasks, ideas, links, and read-later items. The composer/editor supports title, body, category, tags, color, status, due date, source link, pinning, Archive, and Trash.
12+
- **Smart organization.** Search, type views, Pinned/Archive/Trash views, category facets, and tag facets work over an indexed vault feed.
13+
- **Vision Board.** Editable sections and drag-and-drop card ordering are persisted separately in `.flowkey/board.json`, keyed by stable note IDs. Removing a board placement never mutates or deletes the note.
14+
- **Recoverable removal.** The default delete action moves a note to Trash. Restore is immediate; permanent deletion is available only inside Trash and requires explicit confirmation.
15+
- **Safe schema-v2 migration.** Existing Markdown notes gain a stable `note_id` and richer metadata without losing their body, source path, or unknown frontmatter. A backup is written to `.flowkey/backups/v1/` before the first rewrite.
16+
- **Conflict-aware, atomic writes.** Notes and board state use atomic replacement and revisions, so a stale editor reports a conflict instead of overwriting newer work.
17+
18+
### Changed
19+
20+
- **The Notes tab contains notes only.** Vault, category, extraction, and local-model controls moved to Config → Notes & capture and save with the rest of Config.
21+
- **`Ctrl+Alt+N` opens the Notes composer.** A fresh selection prefills the draft; no selection opens a blank composer. Prior clipboard contents are never used as an implicit fallback, and capture no longer saves before review.
22+
- **Local-model enrichment cannot rewrite authored content.** It may fill only blank or generated metadata, preserving user-written titles and bodies.
23+
- **Legacy note actions remain compatible.** Existing callers can continue reading or moving by relative path while the new workspace uses stable IDs.
24+
25+
### Fixed
26+
27+
- **Date-only due dates keep their calendar day.** A value such as `2026-08-04` displays as August 4 in every timezone instead of shifting to the prior day west of UTC.
28+
- **Note migration no longer races concurrent edits or vanishes notes.** Upgrading a pre-2.5 note to the new schema is now serialized against other note writes, and a note whose file can't be rewritten (e.g. a read-only location) still appears in search and listings instead of silently disappearing.
29+
- **"Move to Trash" respects the same conflict check as every other note action.** If a note changed since you opened it, trashing it now reports the conflict instead of acting on stale data.
30+
- **A Quill hiccup no longer breaks Meetings.** A transient error from Quill during a scheduled digest run, the Overview widget, or a meeting Q&A now degrades gracefully instead of surfacing as an error or silently skipping a whole batch.
31+
532
## 2.4.3
633

734
**`prompt:` handles vague requests properly.** A request too thin to break into requirements now produces a prompt that names what is missing, instead of handing the request back reworded.

‎README.md‎

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,15 @@ Flowkey is a Windows desktop assistant that adds local-LLM hotkeys for grammar f
44

55
Everything runs locally through [FastFlowLM](https://fastflowlm.com) (AMD Ryzen AI NPU) or, on machines without the NPU, through [Ollama](https://ollama.com) (CPU/GPU) as a secondary provider. No cloud service, analytics, or telemetry is used by the app.
66

7-
Current version: `2.4.3`
7+
Current version: `2.5.0`
8+
9+
## What's new in 2.5
10+
11+
- **Notes is now a real thinking workspace.** Capture plain notes, tasks, ideas, links, and read-later items as readable sticky cards. Search them, filter by type, category, or tag, and organize them with colors, pins, due dates, status, Archive, and a recoverable Trash.
12+
- **The Vision Board keeps important notes alive.** Create and rename board sections, drag cards into the order you want, and keep unplaced notes nearby. Board placement never changes or deletes the underlying note.
13+
- **`Ctrl+Alt+N` opens a quick composer.** Selected text is staged as an editable draft; with no selection, Flowkey opens a blank note. Nothing is saved until you choose to save it, and old clipboard text is never captured by accident.
14+
- **Notes settings moved to Config.** The Notes tab is only for writing and organizing. Vault location, categories, extraction limits, and local-model enrichment now live together under Config → Notes & capture.
15+
- **Existing Markdown notes migrate safely.** Notes receive stable IDs and richer metadata when first read. Flowkey preserves the body and unknown frontmatter, writes a v1 backup under the vault's `.flowkey/backups/v1/` directory before rewriting, and uses atomic saves with edit-conflict detection.
816

917
## What's new in 2.4
1018

@@ -120,7 +128,7 @@ Launch the app with AutoHotkey v2:
120128
| `<custom>:` + `Ctrl+Shift+G` | Any mode you define in Dashboard → Config → Custom modes (e.g. `translate:`) |
121129
| `Ctrl+Alt+C` | Open chat (the Chat tab has a "My notes" toggle that grounds replies in your notes vault) |
122130
| `Ctrl+Shift+A` | Ask in chat with selected text |
123-
| `Ctrl+Alt+N` | Capture a note |
131+
| `Ctrl+Alt+N` | Open Notes quick capture (selected text prefills the composer; no selection opens it blank) |
124132

125133
Prefix tip: put the keyword on the first line of your selection — `prompt: rough idea here` (or `prompt` on its own line, then the text). Without a prefix, `Ctrl+Shift+G` always runs a grammar fix.
126134

@@ -135,7 +143,8 @@ The dashboard is a web page served by the local daemon — open it from the tray
135143
- **Models:** pull models with live progress — pick a suggestion or type any name (on Ollama, anything from the [library](https://ollama.com/library) works); set active, remove. Suggestions are hardware-aware: detected RAM/VRAM caps the model size (e.g. 32 GB RAM → ~4B on the NPU; 8 GB VRAM → ~9B on the GPU), oversized models are hidden, and free-typing one asks before pulling.
136144
- **Benchmark:** works on both providers — `flm bench` on FastFlowLM (~10–20 min, NPU), timed generations with native metrics on Ollama (~1–3 min, server keeps running).
137145
- **History:** Telemetry view shows what ran and how fast; Exposed view shows stored request/result text only for rows captured while history storage was visible. The History tab includes the same redacted/visible storage toggle as Config.
138-
- **Notes:** browse or search your vault.
146+
- **Notes:** write and organize notes, tasks, ideas, links, and read-later items; use smart views, categories, tags, pins, Archive/Trash, or arrange cards on the Vision Board.
147+
- **Notes settings:** Config → Notes & capture controls the vault, categories, link extraction, and optional local-model enrichment. The Notes tab itself contains no settings.
139148
- **Meetings:** connect the local [Quill](https://quillapp.com) app to search meetings, read AI digests (pre-computed after-hours), review action items (accept / reject), and generate a weekly review. Off by default — enable in Config → Meetings.
140149
- **Notifications:** per-event toggles, dedupe window, Do-Not-Disturb, and quiet hours; every toast (shown or muted) is logged to the Telemetry feed.
141150

‎SPEC.md‎

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Caveman-encoded (compression, not amputation). Paths / ids / action names / numb
1717
- LLM: FastFlowLM NPU @ `:52625` | Ollama @ `:11434`, OpenAI-compat `POST /v1/chat/completions`
1818
- dashboard: daemon-served `scripts/ui/web/{index.html,app.js,styles.css}`, CSP `default-src 'self'`
1919
- paths: `scripts/paths.py` → USER_ROOT/{config,data,logs}; `_version.py` = version src of truth
20-
- version: `2.3.0` (prompt-v2 speed+quality release); `2.2.0` released (`v2.2.0` on `25b8794`); repo `agr77one/Fastflow`
20+
- version: `2.5.0` = living Notes workspace + vision board; repo `agr77one/Fastflow`
2121
- run tree = `flowkey-pub2` (worktree, branch `live`=origin/main). old `FastFlowPrompt_Local_Setup`=1.5.0 stale.
2222

2323
## §I interfaces
@@ -26,6 +26,15 @@ Caveman-encoded (compression, not amputation). Paths / ids / action names / numb
2626
- api: `POST /action/<name>` ! header `X-FFP-API: 1` → 200 `{ok,result,error,elapsed_ms}`
2727
- api: `GET /` → dashboard; `GET /healthz` → `{ok,version,api,actions}`
2828
- action: `config_snapshot` → full cfg; `apply_config_patch {patch}` → merge (whitelist `filter_config_patch`)
29+
- action: `notes_query {query?,kind?,status?,category?,tag?,sort?,limit?,offset?}` → `{results,count,facets}`
30+
- action: `note_create {title?,body?,kind?,category?,tags?,color?,due?,source?}` → note
31+
- action: `note_update {note_id,revision,patch}` → note | conflict
32+
- action: `note_organize {note_id,revision?}` → note; local LLM may update category/suggested metadata only
33+
- action: `note_trash {note_id}` / `note_restore {note_id}` → note; `note_delete {note_id,permanent:true}` → deleted
34+
- action: `notes_board_get` → `{board,placements}`; `notes_board_save {revision,board}` → board | conflict
35+
- action: `note_stage_capture {text?,source_app?}` / `note_take_staged` → quick-capture payload
36+
- data: note Markdown frontmatter schema v2 → stable `note_id`, `kind`, `status`, `tags`, `color`, `pinned`, `due`, `created`, `updated`, `revision`
37+
- data: `<vault>/.flowkey/board.json` → board sections + placements keyed by `note_id`
2938
- action: `recent_history {limit?}` → newest history rows; `input_text`/`output_text` iff stored @ write-time
3039
- action: `prompt_builder_preview {settings?,sample?}` → deterministic local preview (`⊥` LLM call)
3140
- config: `prompt_builder.prompt_version` ∈ {`v1`,`v2`}; default `v2`; v1 = instant rollback
@@ -43,12 +52,12 @@ Caveman-encoded (compression, not amputation). Paths / ids / action names / numb
4352
- action: `meeting_actions_list {range:week|month}` → `{range,items:[{id,text,owner,status,...}],counts}`
4453
- action: `meeting_action_set_status {id,status:pending|accepted|rejected}` → `{ok}`
4554
- action: `meeting_week_summary {week_offset}` → `{ok,week_label,meeting_count,summary}`
46-
- mcp: Quill @ `http://127.0.0.1:19532/mcp` — Streamable-HTTP, SSE `data:`, `Mcp-Session-Id` header; init→notifications/initialized→tools/call
55+
- mcp: Quill @ `http://127.0.0.1:19532/mcp` — Streamable-HTTP, SSE `data:`, `Mcp-Session-Id` header; init→notifications/initialized→tools/call; `get_transcript {meeting_id,include_private_notes:true}`; tool `isError` → typed failure
4756
- cmd: `flm serve <model> --pmode turbo --host 127.0.0.1 --port 52625`
4857
- data: `data/{meeting_digests,meeting_action_status,meeting_skips,notifications,chat_threads}.jsonl`
4958
- autostart: HKCU Run `FastFlowPrompt` → bundled `AutoHotkey64.exe` + `grammarFix.ahk`; `FlowkeyGitSync` → `sync.ps1`
5059
- sched: Windows task `FlowkeyGitSync` daily 12:00 → `sync.ps1` (ff-only pull, guarded)
51-
- ACTIONS count = 75
60+
- ACTIONS count = 86
5261

5362
## §V invariants
5463

@@ -98,6 +107,22 @@ Caveman-encoded (compression, not amputation). Paths / ids / action names / numb
98107
- V45: surfaced v2 text → article agreement + fixed typo map normalized (∵ render copies user wording verbatim); ⊥ meaning change
99108
- V46: A/B rubric = 8 items; R8 = ⊥ section restates `<task>` (coverage-of-task ≥ 0.8), boilerplate-excluded set-wise on constraints; R8 false ⇒ disqualifying ∀ other scores; gate pass ≥ 7/8 ∧ R8 ∧ ⊥ invented
100109
- V40: model picker + installed list = app-styled elements (⊥ native `<datalist>`/`<select size>`, ∵ browser chrome ignores page CSS); theme-aware + keyboard-navigable (↑↓/Enter/Esc)
110+
- V47: Notes tab = notes + organization only; note config controls ∈ Config tab
111+
- V48: ∀ note API/UI identity = stable `note_id`; file move/rename ⊥ identity change
112+
- V49: AI enrichment ? fill blank/generated metadata; user-authored body/title ⊥ overwrite
113+
- V50: default remove → Trash, recoverable; permanent delete ! explicit Trash action + confirm
114+
- V51: v1 Markdown → schema v2 migration preserves body + unknown frontmatter + source path; migration backup ∃ before rewrite
115+
- V52: board placement references `note_id`; remove placement ⊥ delete/mutate note
116+
- V53: capture hotkey w/ selection → staged prefill; ⊥ selection → blank composer; stale clipboard ⊥ silent capture
117+
- V54: note writes + board writes atomic; stale `revision` → conflict, ⊥ overwrite
118+
- V55: note card/editor controls keyboard reachable; desktop split workspace + ≤720px stacked layout
119+
- V56: note `due` date-only value renders same calendar day ∀ timezone; ⊥ UTC date shift
120+
- V57: Quill transcript call matches discovered schema (`meeting_id` + `include_private_notes`); MCP JSON-RPC/tool `isError` ∨ validation payload → typed failure, ⊥ LLM input/cache; poisoned legacy digest → ⊥ idempotency hit, eligible reprocess
121+
- V58: model-created note category accepted ⟺ config opt-in ∧ `is_new` ∧ high confidence ∧ safe normalized ≤2-segment slug; accepted category atomically deduped+sorted into cfg; otherwise Inbox + suggestion; V49 holds
122+
- V59: Config → keyboard-accessible section nav + collapsible groups + one selected section visible + sticky Save/Revert; view state local-only persisted; ≤720px responsive
123+
- V60: future Activity workspace may join Telemetry+History + explicit `Save as note`; Notes remains authored knowledge; V6,V25,V26 hold
124+
- V61: note schema-migration write ∈ `_NOTES_LOCK` (double-checked, serialized vs concurrent note writes); migration-write failure → index/lookup fall back to unmigrated read, ⊥ silently drop note
125+
- V62: `ffp_quill` public read fns (`get_minutes`,`get_transcript`,`search_meetings`,`list_recent_meetings`) catch `QuillToolError` → soft-degrade (empty text/list); V57's typed-failure raise stays internal to `call_tool`, ⊥ leaks past the public API
101126

102127
## §T tasks
103128

@@ -133,6 +158,16 @@ T27|x|streaming Chat tab (SSE): `ffp_chat.stream_send` + daemon `_STREAM_ACTIONS
133158
T30|x|clarify shape for underspecified requests + typo/article normalization + R8 echo gate + reported input in fixed set (2.4.3)|V44,V45,V46
134159
T29|x|bench memory guard + keep-warm/bench mutual exclusion + provider-error surfacing (2.4.2)|V41,V42,V43
135160
T28|x|model picker 2.4.1: footprint/MoE sizing + never-hide + active-model health + merged single Models card + app-styled combobox|V38,V39,V40
161+
T31|x|Notes schema v2 repository: stable ids, zero-loss migration, CRUD, Trash, indexed query, atomic board store|V7,V48,V49,V50,V51,V52,V54
162+
T32|x|daemon Notes v2 actions + backward-compatible read/move/delete + staged capture|V1,V2,V3,V7,V48,V50,V53,V54
163+
T33|x|move vault/categories/extraction/LLM Notes settings → Config single-save; Notes tab config-free|V3,V47
164+
T34|x|Notes-only card workspace: composer, editor, smart views, filters, tags, archive/Trash, vision board drag/order|V5,V47,V48,V50,V52,V54,V55,V56
165+
T35|x|capture hotkey → Notes quick composer w/ staged selection or blank body|V53
166+
T36|x|2.5.0 docs/version/migration + full release gates|V18,V20,V47,V48,V49,V50,V51,V52,V53,V54,V55
167+
T37|x|repair Quill transcript schema/error handling + poison-cache retry + re-digest latest|V12,V14,V20,V23,V57
168+
T38|x|guarded local-model category creation + sorted category manager + note organize action|V3,V7,V20,V49,V58
169+
T39|x|compact Config section navigation + collapsible cards + sticky save|V5,V20,V55,V59
170+
T40|.|Activity workspace: merge Telemetry+History, card/detail UI, explicit Save as note|V5,V6,V25,V26,V60
136171
```
137172

138173
## §B bugs
@@ -179,4 +214,10 @@ B35|2026-07-27|`flm bench qwen3.6-moe:35b-a3b` died 4s in w/ driver `0xc01e0200`
179214
B36|2026-07-27|keep-warm thread ⊥ aware of benchmarks: warms/reloads active model on 15min tick during a 10-20min bench ∴ NPU+mem contention mid-run|V42; `ffp_benchmark.is_running()` gate in `_warm_model_once`
180215
B37|2026-07-27|FLM returns HTTP **200** + `{"error":"Failed to load <model> model!"}`; `_call_openai_compatible`/`ffp_chat` read only `choices` ∴ real cause discarded → "Local LLM returned no usable text"|V43; surface the error body
181216
B34|2026-07-27|self-caught: `_active_model_health` tested membership vs `_provider_list("all")`; ollama "all" = installed+suggested (`ffp_provider_runtime:80`) ∴ never-pulled model → false `installed=True` (⊥ warn)|V39; trust `details` (unfiltered, authoritative) else re-list w/ `installed` filter
217+
B41|2026-07-29|Notes due `2026-08-04` parsed as UTC midnight → EDT displayed Aug 3|V56; parse date-only @ local noon
218+
B42|2026-07-29|Quill `get_transcript` sent `id`; live schema requires `meeting_id`+`include_private_notes`; `call_tool` ignored `isError` ∴ 133-char validation error fed to LLM + cached as digest|V57
219+
B43|2026-07-29|`fresh_modules` teardown popped `notes` after test collection; later daemon-action tests patched a stale module while action-local `import notes` resolved a new module ∴ tests touched the real vault + became order-dependent|rebind `sys.modules["notes"]` to the isolated test module; V20 full-suite gate
220+
B44|2026-08-13|pre-release review: V57's `call_tool` raise (the B42 fix) went ⊥ caught by `get_minutes`/`get_transcript`/`search_meetings`/`list_recent_meetings`; `run_batch`'s pagination loop, `meeting_overview`, and `process_meeting`'s `NoContentError` skip-path all broke on any Quill tool error: scheduled batch could silently no-op (status never updated), Overview widget 500'd instead of `reachable=False`, dead meetings retried forever instead of perma-skip|V62; catch `QuillToolError` in the 4 public read fns, restore module's documented fail-soft contract
221+
B45|2026-08-13|pre-release review: `_ensure_note_schema`'s migration write ran outside `_NOTES_LOCK` ∴ could race a locked `note_update`/`note_organize` on the same file (dueling `uuid4()` note_id, last-writer-wins corruption); `_load_note_index`/`_find_note_path` silently dropped a note from every listing/lookup on migration-write failure (e.g. read-only file)|V61; lock + double-check inside `_ensure_note_schema`; index/lookup fall back to unmigrated read on failure instead of dropping the note
222+
B46|2026-08-13|pre-release review: `trash_note` was the only note-mutating fn ⊥ taking a `revision` param ∴ "Move to Trash" could silently act on a note that changed since the editor loaded it, unlike update/organize/archive; also `_act_note_archive`/`_act_notes_board_save` used a bare `int()` revision parse (raw 500 on bad input) unlike `_act_note_update`/`_act_note_organize`'s guarded parse|V54; add revision param + conflict check to `trash_note` + daemon action + app.js call site; guard the two bare `int()` parses to match the others
182223
```

‎config/grammar_hotkey.config.example.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
"fetch_timeout_seconds": 8,
9595
"max_extracted_chars": 2000,
9696
"low_confidence_to_inbox": true,
97+
"allow_new_categories": true,
9798
"generate_title": true,
9899
"generate_summary": true
99100
},

‎installer/installer.iss‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
#define AppURL "https://github.com/agr77one/Fastflow"
4242
#define AppExeName "Flowkey.exe" ; symbolic — actual launchers below
4343
; Keep in lockstep with scripts\_version.py.
44-
#define AppVersion "2.4.3"
44+
#define AppVersion "2.5.0"
4545

4646
[Setup]
4747
AppId={{8A4F1E6C-9B3D-4E62-9F7A-FASTFLOW140}}

‎installer/sign.ps1‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
.EXAMPLE
5353
# Sign the installer
5454
$env:FFP_SIGN_PFX_PASSWORD = "ChangeMe!"
55-
.\sign.ps1 -FilePath ..\out\Flowkey-Setup-2.4.3.exe
55+
.\sign.ps1 -FilePath ..\out\Flowkey-Setup-2.5.0.exe
5656
#>
5757

5858
[CmdletBinding(DefaultParameterSetName = "Sign")]

‎pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ build-backend = "setuptools.build_meta"
1717

1818
[project]
1919
name = "fastflowprompt"
20-
version = "2.4.3"
20+
version = "2.5.0"
2121
description = "Local-LLM-powered grammar fix, prompt rewrite, chat, and dashboard for Windows."
2222
readme = "README.md"
2323
requires-python = ">=3.11"

‎scripts/_version.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""Single source of truth for the app version. Read by grammar_fix.py."""
22

3-
__version__ = "2.4.3"
3+
__version__ = "2.5.0"

0 commit comments

Comments
 (0)