Skip to content

Windows support — recording pipeline (capture + processing) [main / umbrella PR] - #43

Open
shaik-zeeshan wants to merge 133 commits into
mainfrom
t3code/windows-support-gating
Open

Windows support — recording pipeline (capture + processing) [main / umbrella PR]#43
shaik-zeeshan wants to merge 133 commits into
mainfrom
t3code/windows-support-gating

Conversation

@shaik-zeeshan

@shaik-zeeshan shaik-zeeshan commented May 30, 2026

Copy link
Copy Markdown
Owner

Windows support — main / umbrella PR

This is the main Windows-support PR. It carries the complete Windows recording pipeline (tracking issue #85): runtime capture (screen + microphone + system audio) plus the media-processing seams that consume the captured artifacts — transcription, speaker analysis, speech-activity gating, exact previews, and scrub previews — at parity with macOS, minus the deliberately deferred items at the end.

Status — not merging yet: This PR stays open and is not being merged into main right now. It is the long-lived integration home for Windows support, which is being assembled feature by feature; the merge into main happens only once all the features are in place. The per-feature status index below records what is implemented (with its commit) and what is still open — it grows as more Windows features land on this branch.


Status index

✅ Implemented, verified, and closed

Each item below was verified against the branch code — and, where practical, by running the crate's tests on Windows — and its tracking issue has been closed. The on-device / HITL verification box on each lives in the consolidated operator smoke pass (#84).

Issue Feature Commit
#73 Inline frame-index sidecar on the Windows screen-capture path de880a1
#74 Audio-writer inactivity-tail hold-back (mic + system audio) f34a4a5
#75 .mp4 as a first-class visible-segment container (.mov sweep) e455905
#76 media-decode seam crate (MF Source Reader), via system-audio speech activity 8f6a49d
#79 Local Whisper transcription via the seam f67b3fa
#80 Parakeet int8-only offering + onnxruntime.dll packaging 5e0695c
#81 MF video frame extraction: exact preview + finalized-video validation 97b8745
#82 Sherpa speaker analysis via decode + subprocess helper a2292ed
#83 Scrub preview generation for Windows screen segments c13c095

🔓 Open / deferred (tracked separately)

Foundation (landed earlier on this branch, issues already closed)


What's delivered (detail)

Foundation: platform gating, packaging, screen capture MVP (#44#50)

Audio capture (#51#58)

Inactivity & transient liveness (#59#64 — ADR 0023)

Media processing (#73#83 — ADRs 0024/0025)

Smoke harness & docs (#84, #85)

  • On-device smoke harness with auto-checked milestone invariants (monotonic frame-index sidecars, audio tail hold-back) and an operator runbook (docs/windows/on-device-capture-smoke-runbook.md). The HITL pass itself (session-lock, system-suspend, inactivity) stays open as On-device Windows capture smoke pass: session-lock, system-suspend, inactivity #84.
  • ADRs 0022–0025, SUPPORTS.md / docs/windows/supports.md updates, Windows research docs, packaged-app Sherpa/ONNX DLL bundling, and a follow-up fix for system-audio permission handling + H.264 video processing.

Decisions (ADRs — do not re-litigate)

  • ADR 0022 — system audio is an independent source on Windows.
  • ADR 0023 — Windows transient capture recovery reuses inactivity pause.
  • ADR 0024 — Media Foundation is the only Windows media backend (no FFmpeg / Symphonia shipped).
  • ADR 0025 — Windows v1 ships without App Privacy Exclusion.

Explicitly out of scope / deferred

Testing

  • CI green: Full repo type check and Windows compile check.
  • Local workspace checks clean (PowerShell build-env helper with Strawberry Perl + NASM + vcvars64 for vendored OpenSSL): cargo fmt --check, cargo check / cargo test for capture-screen, media-decode, audio-transcription (local-whisper / parakeet-onnx), speaker-analysis (sherpa-onnx), and the desktop Tauri crate; bun run check; bun scripts/prepare-mnema-cli-sidecar.mjs debug.
  • Per-feature on-device smoke harnesses ran during development (mic / system-audio capture, inactivity, transient liveness, resolution/bitrate presets, scrub previews); the consolidated On-device Windows capture smoke pass: session-lock, system-suspend, inactivity #84 HITL pass (session-lock, system-suspend, inactivity with invariant assertions) is deferred to the operator and tracked separately.

- Gate macOS-only capture and privacy paths behind `target_os = "macos"`
- Add `tauri-plugin-os` and update settings copy for Windows/Linux
- Replace the sidecar prep shell script with a portable Bun/Node script

@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: 30954bd0d3

ℹ️ 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 on lines +6 to +9
#[cfg(target_os = "macos")]
use super::output::{
append_committed_segment_output_files, cleanup_unusable_segment_artifacts,
finalize_capture_outputs,

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 Gate the lifecycle activity dependency on macOS

On non-macOS builds this module is still compiled, but native_capture::activity is only declared behind #[cfg(target_os = "macos")] in native_capture.rs. The unconditional use super::activity::current_activity_snapshot at the top of this file will therefore fail on Windows/Linux before these newly gated imports help, so the cross-platform build still cannot compile. Please gate that import/usage as well, or provide a non-mac stub/gate the module.

Useful? React with 👍 / 👎.

- Document Windows 11 capture floor and runtime gating
- Capture MVP scope, limitations, and settings copy for Windows
- Gate macOS-only tests and runtime paths for Windows CI
- Introduce a cross-platform screen capture session trait
- Update SUPPORTS.md and type-check workflow for Windows coverage
- Build the real `mnema-cli` sidecar in Windows CI
- Use `libsqlite3-sys` vendored OpenSSL for SQLCipher on Windows
- Update Windows storage-access research to match the new setup
- Add a Windows Graphics Capture/MF capture path behind platform support checks
- Factor frame pacing and segment timestamp helpers into shared code
- Update Windows support docs and segment output extensions
- Extend the last frame to wall-clock elapsed time before rotate/stop/finalize
- Build staging textures from the source frame size so odd capture heights copy correctly
- Enable Windows-only segment rotation and lifecycle plumbing
- Hold the last frame until segment close so durations clamp correctly
- Add lookahead timing helpers and tests for frame scheduling
shaik-zeeshan and others added 5 commits May 30, 2026 20:31
- Add Windows Capture Index key store via Credential Manager
- Split OCR dependencies by target platform
- Update platform support tracking for Windows
- Delete local SQLite, WAL, SHM, and index artifacts
- Stop tracking transient `.mnema` runtime files
On Windows, WebviewWindowBuilder::build() deadlocks the WebView2 controller initialization when called from a synchronous command or event-loop callback: the controller is created via a callback that needs the main event loop to keep pumping, but the synchronous caller is blocking that loop. The native window frame appeared while its webview never initialized, leaving a blank white surface until something forced a reload. Startup windows were unaffected because they build in setup(), before the loop starts blocking.

Build runtime windows on a separate thread so the main loop stays free to drive WebView2 creation. macOS/Linux keep building inline (macOS must run the Cocoa corner-radius tweak on the main thread). open_startup_window stays inline (pre-loop), and complete_onboarding closes onboarding only after main is built so the app never drops to zero windows.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Ignore the local `.mnema` directory in `apps/desktop/src-tauri`
- Keep generated Tauri artifacts excluded from version control
- Wire Windows capture to emit low-cadence JPEG frame artifacts
- Flush frame artifacts on segment rotation and capture stop
- Mark Windows frame export support in SUPPORTS.md

Copy link
Copy Markdown
Owner Author

Issue #47 implementation note for this PR.

Latest commit on this branch contains the Windows frame export work:

Summary:

  • Windows capture now emits ~1 fps JPEG ScreenFrameArtifacts using a separate reusable D3D11 staging texture/readback path.
  • The video encode path keeps its existing staging/readback path; export failures are logged and do not fail frame encoding.
  • Windows supports_frame_export() is true.
  • Desktop Windows capture wires artifacts into the existing frame artifact worker and flushes on segment rotation/stop.
  • win_smoke now reports exported artifact cadence.

Manual Win11 smoke:

  • OS: Microsoft Windows 11 Pro 10.0.26200 build 26200
  • Command: cargo run -p capture-screen --example win_smoke
  • Exported artifacts: 5
  • Dimensions: 3380x2038
  • Cadence intervals: [1153, 1132, 1167, 1158] ms
  • Video output wrote successfully: 120115 bytes

Verification also passed: cargo check -p capture-screen, cargo check --manifest-path apps/desktop/src-tauri/Cargo.toml, cargo test -p capture-screen, cargo check -p capture-screen --example win_smoke, and git diff --check.

- Add native capture support flags for non-original resolution on Windows
- Scale WGC output and update settings copy/support docs for Windows
- Expand Windows smoke test coverage for resolution and bitrate handling
- Recreate the frame pool in session when resolution, DPI, or display mode changes
- Surface Windows capture stop events as failed runtime sessions
- Update Windows support docs and status tracking
shaik-zeeshan and others added 4 commits May 31, 2026 00:02
- Hide microphone, system-audio, privacy-filter, and inactivity
  controls on Windows since the runtime MVP does not capture them
- Add a CPU-cost hint on the screen frame-rate control noting that
  software H.264 makes 30 fps expensive on Windows
- Reword Capture and Privacy sidebar descriptions to match what
  actually renders on Windows

Refs #50.
Verify the two capabilities Windows audio capture depends on, against
the vendored wasapi-0.23.0 crate source:

- Capture-clock timestamps: YES. BufferInfo.timestamp is the QPC position
  (100 ns) from IAudioCaptureClient::GetBuffer, anchoring Audio Segment
  timing to the same QPC clock WGC screen frames use.
- Peak-since-last-poll: no dedicated meter API (no IAudioMeterInformation
  wrapper), but satisfied by computing peak over the full drained PCM,
  matching the existing macOS Audio Activity Sample path.

No escalation needed; wasapi 0.23 stays viable for slice 1a.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Verify the two capabilities Windows audio capture depends on, against
the vendored wasapi-0.23.0 crate source:

- Capture-clock timestamps: YES. BufferInfo.timestamp is the QPC position
  (100 ns) from IAudioCaptureClient::GetBuffer, anchoring Audio Segment
  timing to the same QPC clock WGC screen frames use.
- Peak-since-last-poll: no dedicated meter API (no IAudioMeterInformation
  wrapper), but satisfied by computing peak over the full drained PCM,
  matching the existing macOS Audio Activity Sample path.

No escalation needed; wasapi 0.23 stays viable for slice 1a.
Drives the real WASAPI default-endpoint capture + Media Foundation
AAC/M4A sink end-to-end: records, rotates a segment, stops, and
validates each output through the production MF Source Reader
positive-duration probe. Windows-gated; no-op elsewhere.

Verified on-device: two rotated mic_session-prefixed .m4a segments,
openable (MF MF_PD_DURATION > 0) and cross-checked with ffprobe
(aac / 48kHz / stereo / ~3s duration).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@shaik-zeeshan

Copy link
Copy Markdown
Owner Author

📝 Commit-message correction: commit 40cd04b is titled "Record wasapi 0.23 capability spike finding for #51", but its diff (1,849 insertions across 18 files) is actually the full implementation of #52 — Windows microphone capture to openable .m4a segments (audio capture seam), not the #51 spike. The label is a carry-over from a prior session and is incorrect.

For the record, on this branch:

Leaving history as-is to avoid a force-push on the shared PR branch; this note is the canonical mapping.

shaik-zeeshan and others added 7 commits July 2, 2026 14:16
…ge + plan

Decisions from the grill session, probe-validated on-device against Helium
(Chromium, ships as chrome.exe) and Zen (Gecko):

- ADR 0044: UI Automation is the third Browser URL Strategy - Windows-only,
  permission-free, engine-dialected (Chromium: Document ValuePattern; Gecko:
  focused-element climb). No-guess invariant carried cross-platform (no
  IsOffscreen scan, no address-bar reads); brand-less exe-stem -> engine
  allowlist as a parallel type; bounded-cost contract (IUIAutomation2
  timeouts + wall-clock attempt budget + cold-poll for dormant Chromium);
  live reads with no BrowserUrlProbeCache; zero new UI.
- CONTEXT.md: Browser URL Strategy now names three mechanisms; new
  relationships for the cross-platform no-guess invariant, stem->engine
  recognition, bounded-cost reader shape, and the no-permission-UX stance.
- PLAN.md: implementation plan (5 slices: stem resolver, UIA reader,
  snapshot wiring, --windows-browser-url-smoke, docs/verification).

Discharges the browser-URL deferral in ADR 0043.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds UI Automation as the third Browser URL Strategy on Windows —
engine-dialected, permission-free, zero new UI — discharging the ADR 0043
browser-URL deferral.

- capture-metadata: BrowserEngine enum + brand-less known_browser_engine_for_exe_stem
  allowlist (Chromium/Gecko families; Helium-as-chrome.exe is one Chromium hit).
- native_capture_browser_url_uia.rs: bounded-cost UIA reader (Chromium
  ElementFromHandle->Document ValuePattern; Gecko GetFocusedElement->outermost
  Document climb), cold-poll for dormant Chromium, MTA COM per reading thread,
  no-guess invariant; adds the full `windows` 0.61 crate dep.
- native_capture_metadata.rs: Windows collector threads hwnd/pid, gates via pure
  windows_browser_url_probe_engine, sanitizes via sanitize_url, live off-lock read.
- --windows-browser-url-smoke [--exe <stem>] on-device smoke flag.
- SUPPORTS.md + on-device smoke runbook updated.

cargo check -p mnema green; capture-metadata (25) + gating unit tests (4) green.
On-device smoke run remains operator-deferred.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BZEoPCwtXexuuUothEkFfd
…ntics

Both "Define ... semantics" checklist items are now decided/documented:

- Screen: no per-app permission prompt/pane exists for unpackaged Win32
  desktop apps -- WGC captures with no consent UI (yellow border is the
  OS affordance). Only deny-by-exception gates exist (Win11 "Screenshots
  and screen recording" privacy page / graphicsCaptureProgrammatic
  consent store incl. NonPackaged, and the
  LetAppsAccessGraphicsCaptureProgrammatic GPO/MDM policy), surfacing as
  a capture-start failure handled like the microphone treatment.
- System audio: Windows has no permission category for WASAPI
  render-endpoint loopback at all (no prompt, no privacy page, no
  deep-link target); support = the non-prompting default-render probe,
  residual denials get the microphone treatment.
- Also documents *why* live app privacy exclusion cannot exist on
  Windows (ADR 0025): WGC/DXGI deliver the composed monitor image with
  no capturer-side content filter; WDA_EXCLUDEFROMCAPTURE is
  own-windows self-exclusion only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qga8XEwQWBJcZYT8jP9TiR
…data

Windows permissions/privacy/metadata (v1): design ADRs, metadata implementation, browser-URL design + plan
Design the Windows Broker Authorization Channel as a named pipe port of
the macOS Unix-socket channel (ADR 0014): same newline-framed JSON
protocol and one connection handler generic over AsyncRead+AsyncWrite,
only endpoint setup + accept loop cfg-forked. Endpoint
\.\pipe\{identifier}-{sid}-cli-access with a protected user-SID-only
SDDL DACL; ActiveRequestGuard stays in the handler for busy parity.

Also flips the low-disk Windows checklist/summary rows to [x]
(commit d490fea, ADR 0041) and records the decided-but-pending broker
+ crates/cli design in the Storage-access checklist.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017wmfH3R7vmpkRFAkCe1egi
…named pipe)

Vertical tracer-bullet slices from ADR 0045: shared generic handler,
pure name/SID helpers, Windows named-pipe server, CLI client transport
+ mnema:// launch fix, docs finalization.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017wmfH3R7vmpkRFAkCe1egi
Port the macOS/Linux Unix-socket CLI-access broker to Windows as a named
pipe, the last unbuilt Windows subsystem. The newline-framed JSON protocol
is unchanged; only endpoint setup and the accept loop are cfg-forked, with
the connection handler made generic over AsyncRead + AsyncWrite and shared.

Desktop server (broker_authorization_channel.rs):
- #[cfg(windows)] start() resolves the pipe name
  \.\pipe\{identifier}-{sid}-cli-access (SID from the process token, in the
  name so it keys ownership on a multi-session box) and builds a protected
  per-user-SID DACL via SDDL D:P(A;;GA;;;{sid}) once, leaked for process
  lifetime and reused per instance.
- create-first-instance -> connect().await -> create-next-instance ->
  spawn(handle_connection) loop, with reject_remote_clients + explicit
  first_pipe_instance; ActiveRequestGuard stays in the handler so a second
  client gets the fast `busy` response (macOS parity).
- Onboarding gate switched to the ungated windows::is_onboarding_complete.

CLI client (crates/cli/src/main.rs):
- #[cfg(windows)] send_authorization_request opens via ClientOptions with a
  pure, unit-tested classify_pipe_open_error mapping (FILE_NOT_FOUND ->
  app_unavailable drives launch+retry; PIPE_BUSY -> bounded retry) and the
  byte-for-byte framed write/read/decision path.
- App-launch fixed from the bare `cmd /C start "" mnema` to the registered
  `mnema://access/request` scheme.
- Minimal windows-sys dep for the token -> SID call.

Also wire the mnema-cli sidecar as a Windows externalBin, add the required
windows-sys Security/Authorization features to both crates, and flip the
SUPPORTS.md rows (:49 broker, :203/:205 CLI path) to [x].

cargo check + cargo test green on Windows (22 CLI, 17 broker). End-to-end
handshake + search verified on-device against the running app: request ->
Allow -> grant persisted -> authorized search returns real screenText /
audioTranscript results; last-day vs all-retained scope enforcement
confirmed both ways.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014tyJQYLj81gYhSd7XkcqWC
shaik-zeeshan and others added 22 commits July 3, 2026 22:33
- Updated SUPPORTS.md (status table line 49, research notes line 203, implementation map line 301) to document 2026-07-03 on-device HITL verification
- Verified flow: app publishes pipe, `mnema access request` → Allow → approved → grant recorded in `broker-grants.json` → post-grant data commands succeed
- Clarified that data commands gate on TTY via `can_prompt_for_authorization()`, making `access request` the non-TTY test driver
- Noted deny/cancel/timeout and concurrent-`busy` branches remain operator-deferred
Address code-review findings on the ADR 0045 named-pipe port:

- Accept loop: hand the connected client to handle_connection before
  creating the next instance, and recreate failed instances with a
  capped exponential backoff instead of returning — a transient
  create/connect failure no longer drops a live client, permanently
  kills the channel, or hot-spins on a dead instance.
- CLI cold start: replace the single instant post-launch retry with a
  bounded ~15s poll so `mnema` commands connect once the launched app
  binds the pipe (cmd /C start returns immediately, unlike macOS open).
- Fix UB: read TOKEN_USER out of the 1-byte-aligned Vec<u8> with
  read_unaligned rather than forming a misaligned reference (both the
  desktop and CLI SID helpers); add the missing null-pointer guard to
  the CLI copy.
- Drop the dead 2s timeout wrapping the synchronous ClientOptions::open.
- Cap the CLI authorization-response read at 64 KiB (both transports) so
  a hostile pipe server cannot exhaust CLI memory.
- Treat an empty MNEMA_CLI_ACCESS_PIPE_NAME override as unset on both
  sides; add it to turbo.json passThroughEnv.
- Reuse the already-resolved SID in start() instead of a second token
  lookup; gate the ungated Windows-only CLI helpers with
  cfg(any(test, windows)); drop the unused tokio UnixStream import and
  the orphaned current_onboarding_state_for_app; gate the duplex
  framing test for non-unix/non-windows targets.
- Docs: update SUPPORTS.md CLI-sidecar row for the Windows externalBin
  bundling and expand the Broker row's operator-deferred branch list.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D26X5WExsFFkakXxY9o7Zi
…-channel

feat(windows): Broker Authorization Channel via named pipe (ADR 0045)
…ths (#77)

Fixes the 25 Windows-only test failures (13 app-infra, 12 mnema) and the
production bugs they exposed:

- app-infra workspace DB-prefix matching used a literal '/' terminator while
  Windows rows store '\': hidden-segment-workspace classification saw zero
  references (premature-reclaim hazard for workspaces still referenced by
  incomplete batches / nonterminal OCR), frame-equivalence dedupe never fired,
  and frame-preview related-frame lookups returned empty. All prefixes now use
  std::path::MAIN_SEPARATOR (no-op on macOS).
- SegmentPlanner embedded the literal YYYY/MM/DD date prefix into paths, so
  planner-derived strings (persisted rows, active-workspace set) carried
  mixed separators and never string-matched read_dir-scanned paths; date_dir
  now joins the prefix component-wise.
- The desktop test seam around the video-preview extractor had two competing
  guards (assert-based in app_infra.rs, serializing in frame_preview.rs);
  concurrent installs collided, poisoned the seam mutex, and a panic-in-Drop
  double panic aborted the whole test process (0xc0000409) in ~40% of parallel
  runs. One shared serializing poison-tolerant guard now lives at
  frame_preview.rs module level, installed outside async blocks (the
  serialization MutexGuard is !Send).
- MicrophoneVadRuntime::default() eagerly loads Silero -> ort; under the
  Windows app's ort/load-dynamic an unset ORT_DYLIB_PATH fell through to
  LoadLibrary(onnxruntime.dll) from the system search path, deadlocking the
  loader on hosts with a Windows ML copy (the real cause of the headless CI
  windows_system_suspend hang, previously misdiagnosed as a WGC/WASAPI
  restart). capture-vad gains a dynamic-ort feature (enabled only from the
  Windows desktop target): Silero now fails fast to the non-ORT fallback when
  ORT_DYLIB_PATH is unset.
- Test fixtures build platform-correct paths (component-wise joins, tempdir
  anchors, screen_segment_extension()), settings tests derive OCR expectations
  through the production normalization, and the transcription-unavailable
  notification test tracks the Transcription settings tab.

cargo test on Windows: app-infra 528/0, mnema --lib 772/0 (6/6 parallel
repeats, with and without ORT_DYLIB_PATH), capture-runtime 21/0,
capture-vad 21/0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D26X5WExsFFkakXxY9o7Zi
)

The name-filtered desktop-crate test step existed only because of the
pre-existing Unix-path failures and the headless windows_system_suspend hang;
both are fixed (see previous commit). The step now runs the complete
mnema --lib and app-infra suites, re-including windows_system_suspend.
No ORT_DYLIB_PATH is staged: with it unset the Silero adapter fails fast to
its non-ORT fallback, and no test runs real ORT inference. The step is
bounded at 30 minutes so any future headless hang fails visibly and fast.

SUPPORTS.md: flip the Windows CI row to done — the workspace check covers the
desktop crate (workspace member), and bun run check stays macOS-only since
svelte-check output is platform-independent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D26X5WExsFFkakXxY9o7Zi
Audited every Windows checklist item against the actual code on this
branch. 48/56 checkboxes were accurate; 8 lagged behind code that had
already landed (all under-claims, never over-claims). Flip them and fix
one doc-text error:

- Active app/window metadata, Exclude Current App: implemented (ADR 0043
  metadata path + SetWinEventHook listener; tray item cfg-gated to macOS
  in status_bar.rs) -> [x]
- App candidate discovery + icons: icon materialization done, discovery
  N/A by ADR 0025 -> [~]
- Capture output format: decided + built (.mp4/.m4a, extension-agnostic
  SegmentPlanner) -> [x]
- Audit cfg stubs: Windows builds/tests clean in windows-check CI, only
  dead-string cleanup remains -> [~]
- System-audio capture, audio/video writers umbrella: fully wired
  (WASAPI loopback; Media Foundation stack per ADR 0024) -> [x]
- Verify Open Captured URL: code present + platform-agnostic, only
  on-device confirmation outstanding -> [~]
- Fix L130 wording: tauri.windows.conf.json keeps the CLI sidecar
  externalBin (does not drop it), matching the CLI-sidecar row
- Bump Last reviewed to 2026-07-04

The three verify-* items (deep links, tray, global shortcuts) stay [ ]:
their code is implemented but they are genuine on-device tasks.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GhLt1pWM5VyKNGMb4WAzpW
…y on macOS" capture stubs

The Windows checklist was corrected on 2026-07-04, but the summary table
(rows 15-51) still lagged the landed code. Reconcile the Windows column
against verified source:

- Browser URL metadata [ ]->[x]: UIA strategy shipped (ADR 0044,
  native_capture_browser_url_uia.rs); table still said "deferred/unsupported".
- Audio decode to mono PCM [~]->[x]: the media-decode seam is wired into all
  Windows consumers (Whisper, Parakeet, speaker-analysis, system-audio), not
  "only system-audio ... so far".
- Inactivity detection [~]->[x]: GetLastInputInfo-backed per-family
  pause/resume is implemented (native_capture_system_idle.rs).
- Ask AI / User Context: keep [~] (cloud keys genuinely still macOS-only) but
  disambiguate the AI provider key store from the Capture Index key store, so
  the "see that row" pointer no longer misimplies Windows cloud keys work.

Also tighten the residual dead capture stubs in crates/capture-screen/src/lib.rs:
the non-macOS error paths that route through Windows adapters at runtime were
gated cfg(not(macos)) and reported "supported only on macOS". Narrow the five
Windows-dead stubs (start_capture_session, pause/resume_system_audio_writer,
pause_screen_outputs_for_inactivity, resume_screen_outputs) to Linux-only and
correct all eight messages to "not supported on this platform". Every caller of
these free functions is macOS-gated, so the Windows build is unaffected
(cargo check -p capture-screen green).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WvmQhiAPWJQvFSYCiWrXkF
Brings in 270 commits from main: Deepgram cloud transcription (ADR
0047/0048), MCP tool connectors incl. OAuth (ADR 0051), fractional
frame rates (default 0.5 fps), the macOS capture-suspension model for
unexpected stream stops, Lucide icons via unplugin-icons, the
onboarding permissions factory, and the release.mnema.day updater feed.

Conflict resolutions of note:
- audio-transcription manifest: kept the platform-gated `models` vec
  (Windows hides Apple providers + full-precision Parakeet) and
  appended main's two Deepgram descriptors.
- desktop_processing_registry: takes main's `deepgram_auth_status`
  param AND returns this branch's `(registry, gpu_state)` tuple; both
  managed states are installed.
- lifecycle.rs: adopted main's `suspend_screen_after_unexpected_stop`
  path in tick_inactivity; kept both cfg-disjoint test modules
  (windows transient-liveness + macos suspension).
- Icon.svelte deleted (main); the gpuAcceleration glyph maps to
  lucide/cpu in section-icons.ts; IconName now comes from groups.ts.
- Onboarding: ported the Windows `permissionPermitsCapture` count into
  main's new onboarding-permissions.svelte.ts; main's new
  stepper/jump-to-blocker code uses the platform-filtered feature list.
- updater endpoint: main's release.mnema.day + this branch's Windows
  passive installMode.

Semantic (compile-level) merge fixes:
- Ported fractional f64 frame rates through the Windows capture stack:
  WGC frame cap (frame_schedule.rs), MF sink-writer frame-rate ratio
  (round(rate*1000)/1000), default-bitrate math, win_smoke example,
  and the desktop segment-start path. 0.5 fps now works on Windows
  instead of truncating to 0.
- Windows metadata refresh publishes through main's snapshot-history
  ring (publish_snapshot) instead of the removed latest_snapshot field.
- capture_retention test uses main's CaptureDb::single constructor.
- Gated 8 of main's new MCP tests to macOS: they dial real `sh -c`
  fixtures and/or hit the platform keychain, which is macOS-only
  (SUPPORTS.md marks MCP "Windows unaddressed"; the
  MNEMA_MCP_SERVER_SECRET_DIR fallback is cfg(test)-internal to
  app-infra and compiled out for these tests).

Verified on Windows: cargo check --workspace --all-targets clean;
mnema --lib 905/905, app-infra 633/633, capture crates green;
svelte-check 0 errors; bun test 743/744 (the one failure spawns node,
which this box lacks — passes in CI).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NyVrNDANGmVgbR1WTyEHHY
…d spawn via the parent PATH

The bogus-PATH test asserts Unix executable resolution (the child's PATH
env decides the spawn). On Windows, CreateProcess resolves `node` through
the parent's search path regardless of the child PATH override, so the CI
runner's installed Node is still found and the assertion fails (the one
remaining windows-check failure on the merged tree, 904/905). The
login-shell PATH mechanism these tests guard is Windows-unaddressed per
SUPPORTS.md, matching the sibling MCP test gates from the merge.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NyVrNDANGmVgbR1WTyEHHY
…der and MCP secret stores

Add a shared #[cfg(windows)] backend (windows_credential_store.rs):
CredReadW/CredWriteW/CredDeleteW with an RAII CredFree guard, the
capture-index conventions ({service}:{account} target, app-id user
name, CRED_TYPE_GENERIC, CRED_PERSIST_LOCAL_MACHINE), ERROR_NOT_FOUND
mapped to absent/no-op, Zeroizing around plaintext on write, and the
2560-byte CRED_MAX_CREDENTIAL_BLOB_SIZE cap surfaced as a named
pre-write error.

ai_provider_key_store and mcp_server_secret_store consume it through
their existing platform free-function seams; their unsupported stubs
narrow to not(any(macos, windows)). This unblocks Ask AI / User
Context cloud keys, the Deepgram key, and bearer/OAuth MCP connector
secrets on Windows.

cargo test -p app-infra: 636 passed, 0 failed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018s32P9jwDFquLkFpoud9Wu
…dows via Job Objects

Wrap the spawned connector child in process-wrap JobObject +
KillOnDrop under #[cfg(windows)], the sibling of the Unix
ProcessGroup::leader() wrap. KillOnDrop is required, not optional:
process-wrap only sets JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE when that
wrap is present, and that limit is the backstop that kills the tree
when rmcp's spawned kill task never gets to run.

Adds a Windows grandchild-kill test mirroring the Unix one (launcher
powershell spawns a hidden sleeper, writes its PID, test drops the
transport and asserts the grandchild dies). Verified by negative
control: with the wrap disabled the test fails with a surviving
grandchild.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018s32P9jwDFquLkFpoud9Wu
…ed on-device

Add an #[ignore]d, env-gated measurement test that drives the
production SemanticSearchEmbedder on the CPU backend over a mixed
document/query workload and reports wall time, throughput, per-text
latency, CPU% (GetProcessTimes / getrusage) and RSS
(K32GetProcessMemoryInfo). Target-gated dev-deps only.

Run on Windows 11 x86_64 against the real on-device
nomic-embed-text-v1.5 model (release build): 5.33 texts/s batched
(~7.6k chars/s) at ~7.2 cores, 0.76 s model load, 542 MiB working set
after load (1052 MiB transient peak), ~24 ms per query embed. This
discharges the ADR 0037 gate for claiming Windows Semantic Search
support; the static vec0 link is already exercised on Windows by the
app-infra suite.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018s32P9jwDFquLkFpoud9Wu
…adapters exist

capture-screen: reword the frame-index rebuild-from-video error to
platform-neutral (the path is genuinely unported off macOS and is
reachable via the offline repair bin), and tighten the dead
update_active_privacy_filter stub out of the Windows build
(not(any(macos, windows))), matching its sibling stubs.

Frontend: the video-fallback preview message in +page.svelte now
matches the backend "macOS and Windows" reality, and twelve settings
strings saying secrets live in the "macOS keychain" now say "system
keychain" - on Windows they live in Credential Manager.

cargo check -p capture-screen and svelte-check (1011 files) clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018s32P9jwDFquLkFpoud9Wu
…nd (secret-gated)

Replace the post-build no-op stub with a real seam that fixes its own
documented ordering flaw: bundle.windows.signCommand invokes
scripts/windows-sign.ps1 per produced binary DURING bundling, so
Authenticode lands before tauri emits the minisign -setup.exe.sig and
the updater signature covers the signed bytes (verified against the
tauri-bundler source: NSIS signs in nsis/mod.rs before the updater
step runs).

The script no-ops with a notice when AZURE_SIGNING_ENABLED is not
"true" (unsigned preview posture unchanged), and when enabled requires
all six AZURE_* values, signs via the TrustedSigning PowerShell module
(Azure Artifact Signing) with an RFC 3161 timestamp, verifies the
result, and fails hard on any error - never silently unsigned when
signing was requested.

release.yml passes the Azure secrets into the build step and replaces
the old stub with a secret-gated Get-AuthenticodeSignature assertion;
docs/release-process.md describes the seam, the six secrets, and the
provisioning path (Azure Artifact Signing; individual accounts are
US/Canada-only).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018s32P9jwDFquLkFpoud9Wu
… verification, signing seam

Summary rows: Semantic Search Windows [~]->[x] with the on-device
candle-CPU measurement numbers (ADR 0037 gate discharged); Ask AI /
User Context / MCP OAuth rows updated for the Windows AI-provider and
MCP-secret Credential Manager backends (Linux is now the only
key-store-blocked platform, and the OAuth row documents the 2560-byte
Credential Manager blob cap); MCP login-shell PATH marked [x]
(deliberately N/A on Windows - GUI processes inherit the full user
PATH) and node detection [~] (works via inherited PATH, on-device
exercise owed); release row describes the signCommand seam.

Checklist: cfg-audit and remove-only-macOS-errors items closed out; a
new item records the shared windows_credential_store backend; the
Ask AI verification item now points at the provider key store that
unblocks it; the signing item describes the seam and the remaining
Azure Artifact Signing provisioning gate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018s32P9jwDFquLkFpoud9Wu
…d-final-gaps

# Conflicts:
#	SUPPORTS.md
#	apps/desktop/src-tauri/Cargo.toml
The macOS blur-purge exists because WKWebView retains one decoded
IOSurface per asset URL with no working-set ceiling and macOS swaps
rather than purges under pressure. WebView2/Chromium has no such
pathology: its decoded-image cache (cc/tiles) is a fixed working-set
budget with LRU eviction, and Blink's MemoryCache is pruned by a
MemoryPressureListener. There is also no WebView2 API that drops the
in-RAM decoded-image cache (ClearBrowsingDataAsync clears disk cache
only; SetMemoryUsageTargetLevel is a coarse best-effort process hint).

So the shared handle_window_event blur seam already calls
purge_webview_memory_cache_on_blur, which no-ops off macOS, and that is
correct. Flip the Windows cell to [x] with the deliberately-N/A note,
following the login-shell-PATH row precedent. No code change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011mEwzvhwvq63d46EqR2wqh
The snapshot-interval capture-rate control lowered the screenFrameRate
floor to 1/60 fps (one snapshot per minute). The macOS ScreenCaptureKit
backend was updated so long intervals stay exact; the Windows Media
Foundation backend had the same latent imprecision.

Actual Windows frame pacing is already exact — `frame_cap_min_interval_ticks`
is pure f64 division (1/60 fps -> 600_000_000 ticks = 60s). But the
`MF_MT_FRAME_RATE` metadata ratio encoded the rate on the numerator side
(`(rate * 1000).round() / 1000`), which truncated 1/60 fps to 17/1000
(0.017 fps, ~2% off) instead of the exact 1000/60000. Move the scale to
the denominator (ms-per-frame) side so the full 1/60-10 fps ladder lands
exactly, mirroring the macOS CMTime value-side fix.

Add regression tests: frame_cap_min_interval_ticks(1/60) == 600M ticks,
and frame_rate_ratio exactness across the ladder (1/60, 0.5, 1, 10).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011mEwzvhwvq63d46EqR2wqh
…al-gaps

Windows: key-store ports, MCP Job Object teardown, semantic-search verification, signing seam
The first Quick Recall summon called WebviewWindowBuilder::build()
synchronously inside toggle_quick_recall_window, which is reached from
summon_quick_recall_window_command (a sync command) and
handle_global_shortcut (an event-loop callback) — both contexts the
open_new_app_window comment documents as deadlocking WebView2 controller
creation on Windows. The deadlock wedged the main event loop and
cascaded into a botched graceful exit, leaving a zombie process holding
the single-instance lock (windows won't open, no page shows data).

Mirror open_new_app_window: on Windows build + summon on a spawned
thread so the main loop stays free to drive WebView2 creation. Other
platforms keep building inline (macOS must run its Cocoa panel config on
the calling main thread).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PBK1EGsD6WVN2JAFvomEwu
macOS uses the native overlay title bar (traffic lights drawn over the
webview), but Windows has no overlay equivalent, so the main window was
showing native decorations *plus* the custom `.titlebar` header (double
chrome) with no window controls in the custom bar.

Make an `overlay_title_bar` window frameless (`decorations: false`) on
Windows/Linux while keeping native decorations + overlay on macOS. The
frontend now draws the whole title bar, including its own minimize /
maximize-restore / close caption controls (new WindowsCaptionControls
component), and drops the macOS traffic-light left inset on Windows so
the controls hug the top-right corner.

- windows.rs: `decorations = config.decorations && !overlay_title_bar`
  on non-macOS; drop the now-stale dead_code allow on the field.
- WindowsCaptionControls.svelte: min/max/close via getCurrentWindow(),
  maximized state tracked via isMaximized() + onResized.
- +layout.svelte: render caption controls on Windows; `.titlebar--windows`
  trims the left inset and hugs the right edge.
- capabilities: allow window minimize/maximize/unmaximize/toggle-maximize/
  is-maximized/close.

Only the Main window sets `overlay_title_bar`, so it is the only window
whose behavior changes. svelte-check + `cargo check -p mnema` green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PBK1EGsD6WVN2JAFvomEwu
The JPEG snapshot export (the frames shown in the timeline/Recall) ignored
the configured screenFrameRate on Windows and fired every second regardless
of the capture-rate setting.

screenFrameRate gated only the H.264 encode via should_drop_frame; the
snapshot export decision used the bare 1s DEFAULT_SCREEN_FRAME_EXPORT_INTERVAL
floor. WGC delivers change-driven frames up to the refresh rate, so a snapshot
landed every second whenever the screen changed. macOS is unaffected because
ScreenCaptureKit is told to deliver samples only at screenFrameRate, giving it
a max(1/rate, 1s) snapshot cadence; Windows had no equivalent, so it diverged
even at the 0.5fps default (macOS every 2s, Windows every 1s).

Add effective_frame_export_interval(floor, rate) = floor.max(1/rate) and
thread the frame rate into windows_frame_export_runtime so the Windows export
interval matches the macOS max(capture_interval, 1s floor) semantics. Set once
at construction and preserved across segment rotation. The internal screen-
activity sampler stays at 1s (change-detection, not user-facing snapshots).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PBK1EGsD6WVN2JAFvomEwu
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