Skip to content

Add Apple Reminders and Calendar voice commands - #1

Draft
hele211 wants to merge 28 commits into
mainfrom
feat/cricknote-voice-integration
Draft

Add Apple Reminders and Calendar voice commands#1
hele211 wants to merge 28 commits into
mainfrom
feat/cricknote-voice-integration

Conversation

@hele211

@hele211 hele211 commented Jul 20, 2026

Copy link
Copy Markdown
Owner

What changed

Adds an opt-in spoken command: saying “CrickNote please” in a Voice Memo lets Voice2Note classify the request and create an item in Apple Reminders or Apple Calendar.

  • Tasks and follow-ups become Reminders.
  • Appointments and meetings with a clear date and time become Calendar events.
  • A saved Apple item ID prevents duplicate items when a vault write is retried.
  • The generated note records the Apple action for visibility.

Configuration

The integration is disabled by default and requires VOICE2NOTE_APPLE_AUTOMATION=true. Optional settings select a Reminders list and Calendar by name.

Validation

  • PYTHONPATH=. /usr/bin/python3 -m unittest discover -s tests -t .
  • PYTHONPATH=. /usr/bin/python3 -m compileall -q voice2note
  • PYTHONPATH=. /usr/bin/python3 -m voice2note.run --check

hele211 and others added 28 commits June 18, 2026 08:22
Two-stage design: voice2note classifies each recording (conversation |
talk | record) and writes one note to Memory/voiceMemo/ via the CrickNote
CLI; records land as status:draft (a queryable triage queue) while
conversation/talk are finalized. A new cricknote-voice-inbox skill drains
the queue with confirmation, routing records to an experiment append, a
new Ideas/ note, or tasks. Feasibility verified against CrickNote's parser,
vault tools, apply path, task scanning, and WAL storage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Address code review (14 findings):
- write-once + hash guard keyed on source_id (no overwrite-after-triage,
  no filename collisions)
- resumable idempotent routing with per-route markers (no duplicate
  appends/tasks); status draft→in-progress→complete
- provisional classification (no whisper diarization); bias-to-record;
  reclassify/dismiss actions
- mandatory reindex before inbox listing (silent index failure can't hide
  drafts; file on disk is source of truth)
- multi-destination routing; dismiss/archive disposition
- no auto direct-write fallback (standalone-only); CLI error → retry
- vault-path equality + MEMO_RELDIR validation
- edit repo CLAUDE.md AND AGENTS.md templates (setup overwrites both)
- drop busy_timeout to keep the no-engine-code non-goal; WAL + retry instead
- stdlib YAML escaping + confidence coercion with hostile-input tests
- explicit state model: captured→pending→in-progress→complete|dismissed

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Second review round:
- drop hash-guarded re-analysis: the re-check→vault_write window is a real
  TOCTOU the CLI's snapshot-less write can't guard, and a compare-and-swap
  tool would be engine code. voice2note is now strictly write-once;
  transcript-only notes are summarized by the triage agent (human present).
- crash-before-state-save: pre-write deterministic-path existence check
  (adopt-not-rewrite) + atomic state.json (temp + os.replace).
- idea routing gets a deterministic path + source_id marker (idempotent).
- USE_CRICKNOTE is an explicit boolean (default false); true + missing CLI
  hard-fails (no silent direct-write fallback).
- hashes are 16 hex (collision-resistant, not "collision-free").
- testing split: unittest/Vitest for code; manual acceptance tests for the
  Markdown skill's routing/resume/idempotency (Vitest can't drive a skill).
- add Implementation-status note: design only; names exact code touchpoints.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…fy, lock, gated config)

Third review round (architecture approved pending these four):
- per-task task marker [v2n:<memo-h16>:<task-h8>] so a partial routing retry
  doesn't skip a memo's still-unfinished tasks
- note adoption verifies on-disk source_id; missing/mismatch is a hard error
  (no silent adopt, no overwrite) — applies to voice notes and idea notes
- single-instance fcntl.flock lock is mandatory (manual + scheduled runs can
  otherwise both pass the existence check and double-write)
- vault/config equality check runs only when USE_CRICKNOTE=true; standalone
  mode requires no CrickNote config

Status: Approved for implementation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bite-sized test-first plan for the voice2note engine changes from the r4
spec: routing policy, stdlib YAML escaper, recording_type classification,
deterministic note path + checkbox/daily-block removal, v2 atomic state,
mandatory flock lock, cricknote CLI write bridge, gated config validation,
and the create-once orchestration with adopt-verify + transcript-only
fallback. Phases 2 (skill) and 3 (ops/docs) to be planned separately.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ence clamp

Code-review follow-ups from Batch B: quote the recorded timestamp so it is
unambiguously a string for gray-matter, and cover the parse_analysis →
coerce_confidence clamp path end-to-end.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…matter

Batch C code-review follow-ups: state.save() now uses a unique tempfile.mkstemp
name and unlinks it on any failure (no orphaned .tmp); read_frontmatter returns
{} for a missing note and strips single quotes too. (Concurrent-writer and
missing-file paths are already gated by the flock and note_exists at the call
site; these make the utilities robust standalone.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tate

Batch D code-review follow-ups: process_memo now reads external-tool settings
(brctl/whisper/ffmpeg/codex) via the injected cfg like the rest, so it's a
single source of truth and fully unit-testable; add write_memo docstring; the
analysis-failure test now asserts the draft is recorded in state (the core
write-once/no-retry invariant).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ead config

Final-review follow-ups:
- run.assert_cricknote_ready: when USE_CRICKNOTE=true, fail fast at startup if
  the cricknote CLI isn't runnable (spec §7/§10 hard-fail; was failing per-memo)
- vault_writer.read_frontmatter now unescapes double-quoted scalars (\", \\,
  \xNN), so adopt-verify matches a source_id containing a quote/backslash;
  + round-trip test pairing notes.render_memo_note → read_frontmatter
- remove unused config DAILY_DIR/MEMO_DIR (superseded by MEMO_RELDIR)

Full suite: 80 tests passing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Folds in five reviewer notes, all verified against code: FDA precondition for
the real-vault --check gate; Memory/voiceMemo-scoped read-only manifest (avoids
Nutstore sync noise); real empty-file config isolation instead of /dev/null;
whisper stub .txt suffix; rg->grep in verification snippets.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Round 3: folded 7 Codex findings (stub env-var selection, unified
CRICKNOTE_DATA_DIR config + vault-resolve assertion, fail-closed 6.3 gate,
XML-safe plist generation, vault-match-gated auto-enable, added installer
tests, {ok,result} envelope parsing). Round 4: 6.3 snapshot tolerant of a
missing Memory/voiceMemo/ dir. Codex VERDICT: APPROVED.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add VOICE2NOTE_USE_CRICKNOTE / _CRICKNOTE_BIN / _MEMO_RELDIR to the
LaunchAgent template and generate the plist via plistlib (safe XML
escaping) instead of sed-into-XML.

install.sh now resolves CrickNote integration with precedence
--no-cricknote > process env > config file (parsed, not sourced) >
auto-detect. Auto-enable is gated on a vaultPath realpath match against
~/.cricknote/config.json. Explicit misconfiguration (invalid boolean,
unresolvable binary, vault mismatch, bad MEMO_RELDIR) fails closed before
any plist is written or launchctl is invoked.

Adds tests/test_install.py (17 tests) running the installer in an
isolated subprocess with a temp HOME and stub launchctl/cricknote.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Remove obsolete VOICE2NOTE_MEMO_DIR and VOICE2NOTE_DAILY_DIR examples.
Add commented examples and explanations for VOICE2NOTE_USE_CRICKNOTE,
VOICE2NOTE_CRICKNOTE_BIN, VOICE2NOTE_MEMO_RELDIR, and
VOICE2NOTE_CONFIDENCE_MIN, including notes on install.sh auto-detection
vs manual ./run.sh PATH and the vault-path alignment requirement.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…5 polish)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

1 participant