Skip to content

feat(rewind): add deviceName to screenshots for multi-device disambiguation#6321

Open
eulicesl wants to merge 2 commits intoBasedHardware:mainfrom
eulicesl:feat/add-device-name-to-screenshots
Open

feat(rewind): add deviceName to screenshots for multi-device disambiguation#6321
eulicesl wants to merge 2 commits intoBasedHardware:mainfrom
eulicesl:feat/add-device-name-to-screenshots

Conversation

@eulicesl
Copy link
Copy Markdown

@eulicesl eulicesl commented Apr 4, 2026

Summary

  • Problem: screenshot records from multiple Macs are indistinguishable because they do not persist the originating machine name
  • What changed: added a nullable deviceName field to desktop screenshot records, stamped new screenshots with Host.current().localizedName, and included deviceName in sync payloads
  • What did NOT change (scope boundary): no UI changes, no backend API contract changes, no backfill of historical rows, no FTS changes

Linked Issue / Bounty

  • N/A — independent contribution

Root Cause (bug fixes only)

  • N/A — new feature

How It Works

  1. RewindModels.swift adds deviceName: String? to Screenshot for backward-compatible persistence.
  2. RewindDatabase.swift registers an additive migration that adds the nullable deviceName column.
  3. RewindIndexer.swift passes Host.current().localizedName at each screenshot creation site, including rebuild paths.
  4. ScreenActivitySyncService.swift includes deviceName in the sync query and payload when present.
  5. ChatPrompts.swift annotates the new column so chat-side querying can reason about device provenance.

Change Type

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Chore / infra

Scope

  • Mobile app (Flutter)
  • Desktop app (Swift/macOS)
  • Backend (Python API)
  • Firmware / hardware
  • Plugins / integrations
  • SDKs (Expo / React Native / Python / Swift)
  • MCP server
  • Web frontend
  • Docs
  • CI / build / infra

Security Impact

  • New permissions or capabilities introduced? No
  • Auth or token handling changed? No
  • Data access scope changed (screen data, OCR, user data)? No — this adds device provenance to already-captured screenshot rows
  • New or changed network calls? No — existing sync payload now carries one additive field when available
  • Plugin or tool execution surface changed? No
  • If any Yes — risk and mitigation: N/A

Testing

  • Built locally
  • Manual verification:
    • reviewed all 4 Screenshot(...) creation sites in RewindIndexer.swift
    • verified the migration is registered after createLocalKnowledgeGraph
    • confirmed Host.current().localizedName is used at capture time
    • verified ScreenActivitySyncService includes deviceName only when non-nil
  • Existing tests pass
  • New tests added (if applicable)

Human Verification

  • Verified scenarios: new screenshot creation stamps the current Mac name; legacy rows remain readable with deviceName = nil; sync payload includes deviceName when present
  • Edge cases checked: historical rows stay NULL; rebuild path also stamps the current machine name; additive migration remains nullable
  • What I did not verify (and why): no UI verification because this is a data-layer-only change; no retroactive backfill because historical provenance cannot be recovered safely

Evidence

  • Log output / code-path verification
  • Screenshot or recording
  • Test output
  • N/A — change is not directly user-visible in the UI

Notes:

  • reviewed all screenshot creation sites and sync payload wiring on the final branch head
  • additive migration remains nullable and backward-compatible for existing rows

Docs

  • N/A — no docs impact

Performance, Privacy, and Reliability

  • No material performance impact; this is one additional nullable field on screenshot rows and sync payloads.
  • Privacy/data scope does not expand beyond existing screenshot capture; the field only disambiguates which local machine created the row.
  • Reliability remains backward-compatible because existing rows stay NULL and current queries tolerate absent values.

Migration / Backward Compatibility

  • Backward compatible? Yes
  • Migration needed? Yes
  • If yes, upgrade steps:
    1. Apply the additive GRDB migration that adds nullable deviceName to screenshots.
    2. Allow existing rows to remain NULL.
    3. New captures populate deviceName automatically.

Risks and Mitigations

  • Risk: rebuild/import flows stamp historical frames with the current machine name rather than original provenance.
    • Mitigation: the PR intentionally does not claim historical accuracy or perform backfill; old rows remain NULL, and current-machine stamping is limited to rebuild-time reconstruction.
  • Risk: repeated Host.current() lookups are minor overhead in hot paths.
    • Mitigation: current scale is small and behavior is correct; caching can be a follow-up if profiling shows it matters.

AI Disclosure

  • Tools used: Claude Code
  • AI-assisted portions: implementation scaffold and change drafting
  • How I verified correctness: code-path review across all capture sites, migration ordering review, sync payload verification, backward-compatibility review

Copilot AI review requested due to automatic review settings April 4, 2026 17:36
…uation

Users running Omi on multiple Macs have no way to distinguish which machine
captured a given screenshot. This stamps each screenshot with the local
machine hostname (Host.current().localizedName) at capture time.

Changes:
- Add deviceName field to Screenshot model (nullable for backward compat)
- Add GRDB migration to add deviceName column to screenshots table
- Wire Host.current().localizedName into all 4 Screenshot creation sites
- Include deviceName in ScreenActivitySyncService backend payload
- Add deviceName to ChatPrompts column annotations for AI queries
@eulicesl eulicesl force-pushed the feat/add-device-name-to-screenshots branch from 8baebcc to ac9a668 Compare April 4, 2026 17:39
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 4, 2026

Greptile Summary

This PR adds a deviceName column to the screenshots table (via a nullable GRDB migration) and stamps each new screenshot with Host.current().localizedName at all four creation sites, enabling multi-device disambiguation in both the local database and the backend sync payload. The change is additive and backward-compatible — existing rows stay NULL and all query paths handle that gracefully.

Confidence Score: 5/5

Safe to merge — all findings are P2 suggestions with no blocking correctness issues.

The migration is correctly ordered, nullable, and additive. Model and sync changes are consistent. Both findings are P2: one is a performance/style suggestion (cache Host.current()), the other is a semantic edge case in rebuildFromVideoFiles that only matters in an unusual data-migration scenario the PR doesn't claim to support.

desktop/Desktop/Sources/Rewind/Services/RewindIndexer.swift — minor concerns around Host.current() caching and rebuildFromVideoFiles provenance semantics.

Important Files Changed

Filename Overview
desktop/Desktop/Sources/Rewind/Services/RewindIndexer.swift All 4 Screenshot creation sites now pass Host.current().localizedName; the rebuildFromVideoFiles path silently stamps historical frames with the current machine's name even when those frames may have originated elsewhere.
desktop/Desktop/Sources/Rewind/Core/RewindDatabase.swift Registers addDeviceName GRDB migration at the end of the migration chain — correctly uses ALTER TABLE ADD COLUMN with nullable TEXT, no regressions.
desktop/Desktop/Sources/Rewind/Core/RewindModels.swift Adds nullable deviceName: String? field to Screenshot struct with a default of nil — clean, backward-compatible model change.
desktop/Desktop/Sources/ScreenActivitySyncService.swift SQL query and payload construction correctly include deviceName, with nil-guarded insertion so legacy rows with NULL are silently omitted from the sync payload.
desktop/Desktop/Sources/Chat/ChatPrompts.swift Adds deviceName column annotation for chat AI — trailing comma before ] is valid Swift dictionary literal syntax, no issue.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Screen Frame Captured] --> B{Creation Path}
    B --> C[processFrame variant 1]
    B --> D[processFrame variant 2]
    B --> E[processFrame variant 3]
    B --> F[rebuildFromVideoFiles]
    C --> G["deviceName: Host.current().localizedName"]
    D --> G
    E --> G
    F --> G
    G --> H[Screenshot struct\ndeviceName: String?]
    H --> I[RewindDatabase\ninsertScreenshot]
    I --> J[(screenshots table\ndeviceName TEXT nullable)]
    J --> K{ScreenActivitySyncService\nsyncTick}
    K -->|deviceName != nil| L[Sync payload\nincludes deviceName]
    K -->|deviceName == nil\nlegacy rows| M[Sync payload\nomits deviceName]
    L --> N[Backend API]
    M --> N
Loading

Reviews (1): Last reviewed commit: "feat(rewind): add deviceName to screensh..." | Re-trigger Greptile

ocrDataJson: nil,
isIndexed: false // Will need re-OCR
isIndexed: false, // Will need re-OCR
deviceName: Host.current().localizedName
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Host.current() called on every screenshot instead of cached

Host.current().localizedName is called at all 4 Screenshot creation sites, performing a system-level lookup each time. Since the hostname never changes mid-run, caching this once at the actor level avoids the repeated overhead and makes the intent clearer.

// At the top of the actor body
private static let deviceName: String? = Host.current().localizedName

Then each call site becomes deviceName: RewindIndexer.deviceName. The same pattern applies to the other three call sites in this file.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Latest commit 737ff7a updates this branch to cache the computer name once at actor scope instead of calling Host.current().localizedName at each screenshot construction site. The visible check on the current PR head is still green (Lint & Format Check).

ocrDataJson: nil,
isIndexed: false // Will need re-OCR
isIndexed: false, // Will need re-OCR
deviceName: Host.current().localizedName
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 rebuildFromVideoFiles stamps historical frames with current machine's name

rebuildFromVideoFiles reconstructs database rows from video files that already exist on disk — potentially files that were recorded before the migration or even copied from another machine. Tagging them with the current machine's localizedName silently asserts provenance that isn't actually known. A user who migrates their Rewind database from Machine A to Machine B and then triggers a rebuild will see all historical frames attributed to Machine B.

A safer approach is to pass nil here (leave provenance unknown for historical frames) or document in a code comment that the assumption is "rebuild only runs on the originating machine".

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Latest commit 737ff7a changes rebuildFromVideoFiles to leave historical screenshot provenance unset (deviceName: nil) rather than stamping rebuilt rows with the current machine name.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds per-screenshot device attribution to support multi-Mac usage by persisting a device label in the local DB and including it in the backend sync payload.

Changes:

  • Add nullable deviceName to the Screenshot model and screenshots table via GRDB migration.
  • Stamp deviceName at all Screenshot(...) creation sites in RewindIndexer.
  • Include deviceName in ScreenActivitySyncService SQL selection + outbound sync payload, and annotate the column for chat/schema prompts.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
desktop/Desktop/Sources/ScreenActivitySyncService.swift Selects deviceName from screenshots and conditionally includes it in the sync payload.
desktop/Desktop/Sources/Rewind/Services/RewindIndexer.swift Stamps a device identifier into new screenshot rows at all creation points.
desktop/Desktop/Sources/Rewind/Core/RewindModels.swift Adds deviceName: String? to the Screenshot record model and initializer.
desktop/Desktop/Sources/Rewind/Core/RewindDatabase.swift Adds GRDB migration to add the deviceName column to screenshots.
desktop/Desktop/Sources/Chat/ChatPrompts.swift Annotates deviceName in schema hints so chat tooling can query/filter by device.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +212 to +216
ocrText: ocrText,
ocrDataJson: ocrDataJson,
isIndexed: isIndexed,
skippedForBattery: skippedForBattery
skippedForBattery: skippedForBattery,
deviceName: Host.current().localizedName
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

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

Host.current().localizedName is the user-facing “Computer Name” and is not necessarily the machine hostname. Either switch to a true hostname source (e.g., ProcessInfo.processInfo.hostName / Host.current().name) if the backend expects hostnames, or rename the field/docs/PR description to reflect that this is a localized device/computer name.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Latest commit 737ff7a keeps Host.current().localizedName and renames the surrounding docs/schema text from hostname to computer/device name so the stored value matches the implementation.

/// Whether OCR was skipped because the Mac was on battery (needs backfill when AC reconnects)
var skippedForBattery: Bool

/// Machine hostname that captured this screenshot (for multi-device disambiguation)
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

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

The docstring says this stores a “Machine hostname”, but the implementation is currently using Host.current().localizedName (computer name). Please update the comment/field description to match what’s actually stored, or change the stored value to a true hostname.

Suggested change
/// Machine hostname that captured this screenshot (for multi-device disambiguation)
/// Computer/device name that captured this screenshot (for multi-device disambiguation)

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Latest commit 737ff7a updates the screenshot model comment to describe this field as a computer/device name rather than a hostname.

"ocrText": "Full OCR-extracted text from the screen",
"focusStatus": "Whether user was focused or distracted (focused/distracted)",
"skippedForBattery": "OCR was skipped on battery; text may be missing",
"deviceName": "Machine hostname that captured this screenshot (for multi-device queries)",
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

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

Column annotation describes deviceName as a “Machine hostname”, but the code path is using Host.current().localizedName (computer name). Update this annotation (and any related docs) to avoid misleading prompt/schema guidance, or change the stored value to an actual hostname.

Suggested change
"deviceName": "Machine hostname that captured this screenshot (for multi-device queries)",
"deviceName": "Computer/device name that captured this screenshot (from the system display name, for multi-device queries)",

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Latest commit 737ff7a updates the prompt/schema annotation to describe deviceName as the computer/device name captured from the system display name, not a hostname.

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