App-improvement follow-through: action-item done/due metadata, semantic search, silent-failure instrumentation, disk guards#1352
Conversation
list_action_items has advertised a status filter since the rollup tools shipped, but nothing ever populated status: the extractor wrote bare 'Owner: text' bullets, the parser never looked for metadata, and the index hardcoded 'AND 1=0' for status:done. The filter could never match. Give action bullets a small trailing-marker grammar that closes the loop: - '(due: Friday)' — free-text due hint - '(status: done)' or bare '(done)' — closed status (curated token set, so real parentheticals like 'call Bob (the vendor)' are never shredded) Wired through all three layers: - MeetingQuickSummaryExtractor appends '(due: ...)' when a commitment sentence carries a recognizable deadline cue. Extraction never marks anything done — closing an item is a write-back: edit the marker in the saved Markdown and the MCP file watcher reindexes it. - CaptureSummaryParser parses markers into ActionItem.status/.due (defaulted init params keep existing call sites source-compatible). - TranscriptIndex schema v3 adds status/due columns (version gate rebuilds existing indexes from disk), inserts populate them, and list_action_items/digest filter and count on real values. Items with no marker — including everything saved before this change — still count as open. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012muZDEbHFBfzsuhR2XHmJC
…cation failures
Two classes of silent failure now produce events:
- Retained-audio maintenance (retention prune, WAV->M4A compression,
playback mix, stale-temp cleanup, failed-audio compression) kept its
deliberate skip-and-continue behavior but every skipped file vanished
into 'catch { continue }' — orphaned WAVs and unpruned audio built up
with no signal anywhere. Each swallowed error now reports through a
hook on MeetingAudioStorageManager (operation + NSError domain/code
only, no paths), which TranscriptedAppState wires to EventReporter as
a warning-level 'audio_maintenance_failure' event. A hook rather than
a direct EventReporter call keeps the manager compilable in the fast-
test runner, which doesn't include EventReporter's sources.
- ParakeetEngine's 30-minute streaming buffer cap dropped the oldest
audio via removeFirst with no trace. The dictation session cap should
end a recording long before that fires, so any hit means real audio
silently left the transcription buffer; it now emits a once-per-
recording 'audio_buffer_truncated' warning with the dropped duration.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012muZDEbHFBfzsuhR2XHmJC
…ry growth visibility Two disk-safety gaps around retained audio: - The pre-recording disk check passed with 100MB free, but meeting capture writes dual WAV at ~1.4GB/hour and the in-recording watchdog only hard-stops at 50MB — so a recording could start with minutes of runway and die mid-meeting. Raise RecordingValidator.minimumDiskSpace to 1GB so a doomed recording fails upfront with a clear message instead of losing audio later. - Audio retention defaults to 'never' and nothing ever reported how big the capture library had grown; unbounded growth was invisible until the disk filled. After the launch audio-maintenance pass, measure the library (symlink-safe, skips linked folders) and emit a 'capture_library_size' event with a coarse size bucket plus the retention setting — the signal a future retention nudge needs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012muZDEbHFBfzsuhR2XHmJC
The one major NEXT_WORK bet still open: paraphrase queries against the
meeting library ('pricing pushback' should find 'they balked at the
cost'), previously impossible because search was lexical FTS5 only. The
blocker was bundling an embedding model — Apple's NLEmbedding sentence
model ships with macOS, so nothing needs downloading and nothing leaves
the machine.
- SemanticEmbedding.swift: embedding seam (protocol + NLEmbedding-backed
provider), Float32 blob codec over normalized vectors, and a chunker
that groups consecutive utterances into ~400-char windows (one vector
per utterance is wasteful, one per meeting is useless).
- TranscriptIndex schema v4 -> semantic_chunks table (version gate
rebuilds old indexes); meeting chunks and dictation entries embed at
index time inside the existing transactions; brute-force cosine scan
at query time — fine at personal-library scale.
- New read-only semantic_search MCP tool (query/kind/count/date window)
returning scored hits with hydrated meeting titles. When the model
asset is missing the tool says so explicitly and indexing skips
semantic rows; lexical search is unaffected.
- Tests use a deterministic FNV-1a bag-of-words embedding so CI never
depends on the model asset: chunking, ranking, filters, reindex
cleanup, unavailable-model fallback, codec round-trip.
- e2e smoke source list gains SemanticEmbedding.swift (it compiles
TranscriptIndex.swift with raw swiftc).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012muZDEbHFBfzsuhR2XHmJC
NEXT_WORK item 2, the last cheap piece of the 'ask my history' loop: the agent's orientation tools answered 'what was this meeting' with the least informative content in the file — recap previewed the first 15 dialogue lines and recent_context previewed the literal first utterance, i.e. greetings and audio checks — while the structured summary sat in the same Markdown and in the index. - recap: prefer a structured preview (summary prose bullets from local_summary/auto_summary frontmatter plus compact Decisions / Action items / Open questions lines, parsed via CaptureSummaryParser); fall back to dialogue lines only when a meeting has no summary. - recent_context: prefer a one-line preview built from the meeting's indexed summary facts (decisions first, then owner-prefixed action items); fall back to the first utterance as before. Index-only, no new file reads. - Also refresh docs/NEXT_WORK.md against the tree: most of the original list has shipped; what remains is speaker-identity trust work and the items blocked on a real Mac. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012muZDEbHFBfzsuhR2XHmJC
|
Maestro hold review: HOLD at head f9fc688. Still draft and broad: action-item writeback semantics, MCP schema rebuild to v4, local semantic embeddings/chunks, summary previews, audio-maintenance failure events, buffer truncation warnings, disk-space start gate, and capture-library telemetry. This needs Mac/tool proof and real-library behavior proof, not just green CI. Smallest clear path: run MCP/Tools tests, semantic_search on a real local library, action-item (done)/(due) reindex check, index rebuild/perf sanity on a nontrivial library, and confirm telemetry carries only operation/error buckets. |
|
Fair hold — CI green isn't real-library proof, and this branch was authored from a Linux box, so the Mac-side proofs need someone at a Mac. Making that as cheap as possible: 1. MCP/Tools tests (same suites CI ran, locally): swift test --package-path Tools/TranscriptedCaptureKit
swift test --package-path Tools/TranscriptedMCP2 + 4. Real-library semantic_search + rebuild/perf sanity, without touching the production index — point only the index at scratch, keep the real capture library as data: cd Tools/TranscriptedMCP && swift build -c release
TRANSCRIPTED_INDEX_DIR=$(mktemp -d) time ./.build/release/transcripted-mcp --self-test
3. (done)/(due) reindex check: edit any saved meeting's Action Items bullet to end with 5. Telemetry payloads — provable from source without a Mac: both new events are fixed-key literal dictionaries, no interpolated paths/titles:
If the breadth is the concern, happy to split this into 3 PRs (metadata+previews / semantic search / instrumentation+disk) — say the word and I'll cut them. Generated by Claude Code |
Why
Follow-through on the "what else would you improve" review: the highest-leverage gaps that were still real after re-verifying the tree (most of FIX_ROADMAP already landed). The biggest two:
list_action_itemshas advertised astatusfilter since the cross-meeting tools shipped but nothing ever wrote a status (the filter could never match), and search was lexical-only so paraphrase queries against the meeting library came up empty.Product Impact
meetings/agent artifacts/dictationagent workflow/meeting reliabilitysemantic_searchfinds paraphrases FTS5 can't ("pricing pushback" finds "they balked at the cost") with a fully on-device embedding, and the agent orientation tools (recap/recent_context) now answer "what was this meeting" with the summary instead of greetings. Also turns three classes of silent failure (skipped audio maintenance, dictation buffer truncation, doomed-from-the-start low-disk recordings) into visible, upfront signals.What changed
done/duemetadata via trailing bullet markers —(due: Friday),(status: done), bare(done):MeetingQuickSummaryExtractorappends(due: ...)when a commitment sentence has a recognizable deadline cue. Extraction never marks anything done — closing an item is a write-back: edit the marker in the saved Markdown, the MCP file watcher reindexes.CaptureSummaryParserparses markers intoActionItem.status/.due(bare-token set is curated so real parentheticals like "call Bob (the vendor)" are never shredded).TranscriptIndexaddsstatus/duecolumns,list_action_itemsfilters on real values instead ofAND 1=0, anddigestopen-counts become accurate. Unmarked items — including all pre-existing summaries — stay open.semantic_searchMCP tool): Apple's on-deviceNLEmbeddingsentence model (ships with macOS — no bundled weights, nothing leaves the machine) embeds ~400-char chunks of consecutive meeting utterances plus dictation entries at index time into a newsemantic_chunkstable; queries brute-force cosine over normalized Float32 blobs. Model-unavailable systems get an explicit message and lexical search is unaffected. Tests use a deterministic bag-of-words embedding so CI never depends on the model asset. Index schema bumped to v4 (existing indexes rebuild from disk via the existing version gate).recap/recent_contextpreviews use the real summary (NEXT_WORK refactor(ui): Split FloatingPanel.swift into 10 organized files #2): recap prefers summary prose + compact Decisions/Action-items/Open-questions lines parsed from the Markdown; recent_context builds a one-line preview from the meeting's indexed summary facts. Both fall back to the old dialogue/first-utterance behavior only when a meeting has no summary.catch { continue }in prune / WAV→M4A / playback-mix / stale-temp / failed-audio paths) forwards each skip through a hook toEventReporterasaudio_maintenance_failure(operation + NSError domain/code only, no paths). ParakeetEngine's 30-min buffer cap emits a once-per-recordingaudio_buffer_truncatedwarning with the dropped duration.RecordingValidator.minimumDiskSpaceraised 100MB → 1GB (dual-WAV capture burns ~1.4GB/hour and the runtime watchdog only stops at 50MB, so 100MB start floors let doomed recordings begin). NewCaptureLibrarySizemeasures the capture library after launch maintenance and emits a bucketedcapture_library_sizeevent with the retention setting — first visibility into unbounded growth under the defaultneverretention.docs/NEXT_WORK.mdrefreshed against the tree — most of the original list had shipped; the doc now says what's actually left (speaker-identity trust work, plus the items blocked on a real Mac).How I checked it
scripts/dev/check-build-source-lists.py(source-list contract passes with the new files)swift test+ integration/e2e smokes + all four Tools packages); latest commit re-runningbash build.sh --no-openlocally — not run: authored in a Linux container, no macOS toolchain; relying on CICaptureLibrarySizeTests, extractor due-marker suite, maintenance-failure reporting suite (fast tests);CaptureSummaryParserTestsmarker grammar;SummaryRollupTestsstatus filter + digest counts + recap/recent_context preview behavior;SemanticSearchTestschunking/ranking/filters/reindex/fallback(done)in a saved meeting, confirmlist_action_items status:"open"stops returning it after reindex; runsemantic_searchwith a paraphrase query against a real libraryRisk Review
Notes
Still deferred, with reasons (also captured in the refreshed
docs/NEXT_WORK.md):Agent handoff
COORD_DONE: BRIEF | this PR | action-item done/due metadata + semantic_search + summary previews + silent-failure instrumentation + disk guards | none | verify on macOS CI + manual semantic_search sanity run | check-build-source-lists.py only (no local toolchain) | run full test matrix on macOS🤖 Generated with Claude Code
https://claude.ai/code/session_012muZDEbHFBfzsuhR2XHmJC