Files
Marcos Paulo 15d7f5e873 feat: editor blocks and lint, vault knowledge, calendar plugin, macOS menu
- notescraft-lint (rules, safe fixes, learned style) and notescraft-blocks (block model and edits); engine lint.* and block.* commands with CLI and MCP parity
- Lint tab, underlines, slash menu with preview in Source and Visual, block grip and menu, Settings > Editor and lint, Vim block keys
- Visual view: tables, note embeds, %% comments, wrapping property chips
- Vault knowledge (Related tab, assistant sources, Settings) and calendar plugin
- Sidebar top padding fixed, dot grid follows PaperCraft spacing, right-card tabs no longer cover the collapse button

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-18 12:36:00 -03:00

9.1 KiB

Tasks: Calendar plugin redesign

Rules: each task is independently testable. No task depends on work in an unfinished task. Order = execution order. Each task is large; /implement splits it into single-file subtasks. Tag [H] = mechanical parts may go to Haiku 5.5 subagents, [M] = main model only. Every task ends with cargo test -p, clippy -D warnings, cargo fmt; every phase end runs cargo xtask ci.

  • 1. Scaffold the notescraft-calendar crate and decide MCP naming [H] Files: crates/calendar/Cargo.toml, crates/calendar/src/{lib.rs,error.rs,model.rs,time.rs}, Cargo.toml (workspace deps), xtask/src/layers.rs Done when: cargo xtask layers accepts the crate at L3; Event, Reminder, Calendar serde round-trip in a unit test; deny-header present; MCP naming decision (calendar_* aliases in alias.rs) written into plan.md.

  • 2. Event file format: parse and write frontmatter [M] Files: crates/calendar/src/{file.rs,error.rs,lib.rs}, crates/calendar/Cargo.toml, crates/calendar/tests/event_file.rs Done when: tests pass for round trip, unknown keys preserved, malformed/out-of-range dates return Err (no panic), and a hostile-input suite (huge, nested, non-UTF-8 fields) passes.

  • 3. Recurrence subset: daily, weekly, monthly, yearly, interval, weekdays, until/count, exceptions [M] Files: crates/calendar/src/recur.rs, crates/calendar/tests/recur.rs Done when: table tests cover month end, leap day, DST and zone shift; property test shows expansion is capped and every occurrence lies in the query window; "this / following / all" split produces correct series.

  • 4. Query layer: filter, sort, group, saved filters, conflicts, free time, year heat, week review [M] Files: crates/calendar/src/{query.rs,free.rs,review.rs,item.rs,lib.rs}, crates/calendar/tests/query.rs Done when: tests cover every filter field, every sort and group key, overlap detection and free-slot search; #[ignore] bench filters and sorts 10,000 events under 100 ms p99.

  • 5. Quick-add parser (English) [M] Files: crates/calendar/src/quickadd.rs, crates/calendar/src/quickadd/clock.rs, crates/calendar/src/recur.rs (added Rule::validate), crates/calendar/tests/quickadd.rs Done when: phrases with title, date or weekday and time parse to the expected event; an unparsable part returns an error naming it; nothing partial is returned.

  • 6. ICS reader (read-only, in-house) [M] Files: crates/calendar/src/ics.rs, crates/calendar/src/lib.rs, crates/calendar/src/error.rs, crates/calendar/tests/ics.rs Done when: valid fixtures import; recurrence rules outside the subset import as read-only flagged; oversize, deeply nested and malformed files return Err within caps, no panic.

  • 7. Engine read commands: agenda, next, free, conflicts, year, parse, review [H] Files: crates/engine/src/calendar/{mod.rs,load.rs,read.rs,tasks.rs}, crates/engine/src/{commands.rs,lib.rs} (registration), crates/engine/src/vault/daily.rs (two helpers made pub(crate)), crates/engine/src/prefs/config.rs (calendar_folder), crates/engine/Cargo.toml, crates/engine/tests/calendar_commands.rs, crates/calendar/src/time.rs (day-then-clock ordering) Done when: each command returns the documented JSON from a fixture vault; the commands are listed by Registry::list; they run on a read-only session; added to the read allow-lists in assistant_gate.rs and mcp/calls/gate.rs.

  • 8. Engine write commands: add, edit, move, delete, note, bulk, saved filters, with scope handling [M] Files: crates/engine/src/calendar/{events.rs,series.rs,store.rs,bulk.rs,filters.rs} (reminders move to task 13), crates/engine/src/ai/assistant_gate.rs, crates/mcp/src/calls/gate.rs, crates/engine/tests/{calendar_write.rs,core_commands.rs} Done when: tests prove file on disk after each command; missing scope on a repeating event fails naming this|following|all; read-only session refuses; writes go through the atomic writer; calendar.* commands join panic_hunt and survive all 22 hostile inputs.

  • 9. Expose through CLI, MCP and assistant, with parity [H] Files: crates/mcp/src/calls/{calendar_alias.rs,alias.rs,tools.rs,gate.rs}, crates/mcp/src/calls.rs, crates/engine/src/ai/assistant_gate.rs (core-plugin toggle), crates/mcp/tests/{calendar_tools.rs,aliases.rs,via_control.rs}, apps/notescraft-cli/tests/calendar.rs (the CLI needed no new code: run <id> <json> reaches every command) Done when: engine, run --json and the MCP tool return identical JSON (tests in mcp/tests/calendar_tools.rs and notescraft-cli/tests/calendar.rs); with the calendar plugin toggle off no calendar tool is listed or callable; assistant write calls become pending changes (engine/tests/calendar_write.rs). The assistant list carries three calendar tools (calendar_agenda, calendar_add, calendar_edit) to keep its context small; the rest stay callable by name and through run_command.

  • 10. Calendar view shell: month, week, day, agenda, year, remembered view, truncation [M] Files: crates/ui-egui/src/views/calendar_view/{mod.rs,month.rs,week.rs,day.rs,agenda.rs,year.rs}, crates/ui-egui/src/views/center.rs, crates/ui-egui/src/panels/sidebar.rs:318-386 Done when: egui_kittest renders each view from a fixture vault; long titles end in an ellipsis with full text on hover; the chosen view survives a restart; snapshot PNGs match the PaperCraft artboards by eye; 10,000-event month render stays under 16 ms in the #[ignore] bench.

  • 11. Keyboard model: navigation, selection, move/resize/copy, find, gn, gR [M] Files: crates/ui-egui/src/views/calendar_view/{keymap.rs,keymap/helpers.rs,keymap_tests.rs,mod.rs,paint.rs}, crates/calendar/src/item.rs (repeats), crates/ui-egui/src/shell/app/window/frame.rs Done when: a test drives every key in the spec (t h j k l H L [ ] gd gm gw gD gy ga v V x o O e dd yy p . z / f s c gn gR) and checks the effect; a uniqueness test finds no duplicate binding; editor keys are untouched when the Calendar lacks focus.

  • 12. Quick add, filter and sort popover, bulk bar, saved filters UI, i18n [H] Files: crates/ui-egui/src/views/calendar_view/{prompt.rs,filters.rs,review.rs,agenda.rs,mod.rs,paint.rs} (quick add, go to, find, bulk bar, filter popover, saved filters, week review), crates/engine/src/calendar/{mod.rs,filters.rs} (saved_filters), crates/i18n/locales/en.toml, crates/ui-egui/src/views/settings_view/categories.rs (calendar folder row) Done when: kittest flows pass (type phrase, see preview, Escape saves nothing; filter, sort, group, save, reload); bulk bar shows affected count before confirm; cargo xtask i18n reports no hardcoded strings.

  • 13. Reminders: scheduler, inbox, snooze, quiet hours, missed on start [M] Files: crates/calendar/src/remind.rs, crates/engine/src/calendar/{reminders.rs,clock.rs}, crates/engine/src/prefs/{reminders_config.rs,config.rs}, crates/ui-egui/src/views/calendar_view/inbox.rs, crates/ui-egui/src/shell/app.rs (30 s reminder tick), tests crates/calendar/tests/remind.rs, crates/engine/tests/calendar_reminders.rs Done when: tests with an injected clock fire once, snooze, respect quiet hours and list missed reminders on start without re-notifying; the notifier is a trait (no OS crate yet) with a test double.

  • 14. ICS feeds and cached offline events (sync crate, no network crate yet) [M] Files: crates/calendar/src/sync.rs (a module of the calendar crate: a separate L3 crate could not be used by the L4 engine and also use calendar), crates/engine/src/calendar/{feeds.rs,load.rs}, crates/ui-egui/src/shell/{feed_sync.rs,app.rs}, tests crates/calendar/tests/sync.rs, crates/engine/tests/calendar_feeds.rs Done when: a local fixture feed refreshes on an injected clock into the cache via write_private; offline start shows cached events with an offline flag; the sync log holds no tokens or event bodies (canary test); fetching sits behind a trait so no HTTP crate is needed.

  • 15. Network, keychain, notifications and Google view-only without new crates, then docs [M] Files: crates/calendar/src/feed/google.rs (pure), apps/notescraft/src/{net,notify,keychain,google,desktop}.rs (curl, osascript/notify-send, security/secret-tool, loopback sign-in; no new crate, so no approval was needed), crates/ui-egui/src/shell/app/{services.rs,feed_sync.rs}, crates/ui-egui/src/views/settings_view/categories/calendar.rs, ROADMAP.md, docs/ARCHITECTURE.md, SPEC.md item 26 Done when: no crate was added, so nothing needed approval; Google sign-in lists calendars and loads events view-only (tested against scripted HTTP, an in-memory keychain and a pretend browser; not against live Google, which needs a client id); an edit on an imported event is refused; the Google token is only in the keychain and errors, statuses and logs never carry addresses or tokens (canary tests); a system notification fires through osascript (run once by hand: exit 0); ROADMAP, ARCHITECTURE and SPEC 26 reflect the real command list; cargo xtask ci is green.