Skip to content

2.5.0: Notes workspace, vision board, and Quill/notes reliability fixes - #39

Merged
agr77one merged 12 commits into
mainfrom
codex/notes-2.5
Aug 13, 2026
Merged

2.5.0: Notes workspace, vision board, and Quill/notes reliability fixes#39
agr77one merged 12 commits into
mainfrom
codex/notes-2.5

Conversation

@agr77one

Copy link
Copy Markdown
Owner

Summary

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.

Added

  • 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.
  • Smart organization. Search, type views, Pinned/Archive/Trash views, category facets, and tag facets work over an indexed vault feed.
  • 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.
  • 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.
  • 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.
  • 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.

Changed

  • 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.
  • 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.
  • Local-model enrichment cannot rewrite authored content. It may fill only blank or generated metadata, preserving user-written titles and bodies.
  • Legacy note actions remain compatible. Existing callers can continue reading or moving by relative path while the new workspace uses stable IDs.

Fixed

  • Date-only due dates keep their calendar day.
  • Note migration no longer races concurrent edits or vanishes notes (V61) -- migration writes are now serialized against other note writes, and a note whose file can't be rewritten still appears in search/listings instead of silently disappearing.
  • "Move to Trash" respects the same conflict check as every other note action -- trashing a stale-loaded note now reports the conflict instead of acting on it.
  • A Quill hiccup no longer breaks Meetings (V62) -- restored the module's documented fail-soft contract so a transient Quill error degrades gracefully instead of silently no-oping a scheduled digest batch, 500ing the Overview widget, or breaking the permanent-skip path for dead meetings.

Review

Ran an 8-angle code-review pass (correctness, removed-behavior, cross-file tracing, reuse, simplification, efficiency, altitude, conventions) before opening this PR. Found and fixed 3 real pre-release bugs (B44-B46 in SPEC.md): the Quill error-handling regression above, the note-migration race/silent-drop above, and a missing revision check on trash_note. Deferred as follow-ups (not release blockers): an O(n) vault rescan on note-by-id lookup, a few duplicated-logic spots, and two accessibility gaps on custom widgets.

Test plan

  • ruff check . clean
  • Full pytest suite green (536 tests, including 6 new regression tests for the fixes above)
  • CI green on this PR

🤖 Generated with Claude Code

agcycle and others added 12 commits July 29, 2026 17:30
Cites V7, V48, V49, V50, V51, V52, V54.
Cites V1, V2, V3, V7, V48, V50, V53, V54.
Cites V5, V47, V48, V50, V52, V54, V55, V56.
Cites V18, V20, V47, V48, V49, V50, V51, V52, V53, V54, V55.
- 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>
@agr77one
agr77one merged commit 66bb892 into main Aug 13, 2026
5 checks passed
@agr77one
agr77one deleted the codex/notes-2.5 branch August 13, 2026 15:34

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 53dbdecd6c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/notes.py
Comment on lines +595 to +601
parsed = _FRONTMATTER_LINE_RE.match(line)
if not parsed:
rendered.append(line)
continue
key = parsed.group(1)
if key in wanted:
rendered.append(f"{key}: {_encode_frontmatter_value(wanted[key])}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve multiline YAML values during migration

When an existing Markdown note uses standard block-style YAML such as tags: followed by indented - alpha entries, _note_parts reads the field as empty and this loop replaces only the tags: line while retaining its indented children. Merely listing the vault therefore rewrites the note as invalid YAML (tags: [] followed by the old list) and loses the tags from Flowkey's index; migration should parse and replace the complete YAML value rather than a single line.

Useful? React with 👍 / 👎.

Comment thread scripts/notes.py
Comment on lines +1133 to +1135
metadata, body, text = _ensure_note_schema(source_path)
current_revision = int(metadata.get("revision") or 1)
if revision is not None and int(revision) != current_revision:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Detect filesystem edits before overwriting a note

When a note open in the dashboard is edited by Obsidian or another filesystem client, that edit does not increment the stored revision, so this comparison still succeeds. Because the dashboard subsequently submits all editor fields, even saving an unrelated tag can overwrite the newer external body without a conflict; the edit token needs to incorporate the file version or fingerprint observed when the note was opened.

Useful? React with 👍 / 👎.

Comment thread scripts/ui/web/app.js
setEditorStatus(saved.error || "Save failed.", false);
return;
}
await syncEditorBoardPlacement(saved.note_id);

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 Stop reporting success when board placement conflicts

When another dashboard tab saves the board after this editor loaded it, syncEditorBoardPlacement receives a board revision conflict and returns after dropping the requested placement, but this call's result is ignored. The flow then reloads state and displays Saved., so a user who checked “on board” is told the whole operation succeeded even though the placement was lost; propagate the failed board save and surface the conflict instead.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants