Skip to content

Test harness (prototype)

This is the one part of DoesItARM that is built, not just specced: a zero-dependency Swift CLI (apps/doesitarm-harness) that takes a Mac app from “we have a copy” to “here’s the verdict, with evidence.” Everything below reflects what runs today; planned pieces are marked.

System architecture — current vs planned

Section titled “System architecture — current vs planned”
flowchart TB
  ORC["Harness CLI (orchestrator)<br/>swift run harness · on the operator's Mac"]
  subgraph VM["macOS VM · tart / Virtualization.framework — DEFAULT (--driver vm)"]
    H["harness (in guest)"]
    subgraph DRV["Driver seams (swappable)"]
      UI["UIDriver<br/>ax ✓"]
      REC["EvidenceRecorder<br/>screencapturekit ✓ · screencapture · ffmpeg · none"]
      ISO["IsolationBackend<br/>host ✓ · user"]
    end
    VAPP["App under test<br/>VLC · HandBrake · Audacity"]
    H --> DRV
    DRV --> VAPP
  end
  subgraph HOSTRUN["bare host · real Metal — GATED (--driver host --allow-host)"]
    HAPP["App under test on the real machine"]
  end
  ORC == "--driver vm (default): clone · boot · push harness · run in guest · import evidence" ==> VM
  ORC -. "--driver host (needs human approval)" .-> HOSTRUN
  classDef gated stroke-dasharray:6 4,opacity:0.6;
  class HOSTRUN,HAPP gated;
CapabilityStatus
Static arch / Rosetta classification (lipo/otool)✅ built
Bulk scan of all installed apps (no permissions)✅ built — 113 apps classified
Acquire via Homebrew cask + locate✅ built
Launch + window-ready + crash detection✅ built
Drive UI via Accessibility (menus, buttons, files)✅ built (needs Accessibility grant)
Audio Unit lane via auval (zero-UI)✅ built
Record video + screenshots✅ built — needs Screen Recording grant to capture
Local store (SQLite = D1 mirror, r2/ = R2 mirror)✅ built
Swappable run driver, recorder + isolation seams✅ built (vm/host drivers, capture backends, host isolation); user isolation = stub
VM run driver (--driver vm, the default)✅ built in code — clone/boot/push/run-in-guest/import/teardown; end-to-end boot needs tart + a base image on the host
Host execution gated behind human approval (--driver host --allow-host)✅ built — refuses without consent
Live D1/R2 sync◌ dry-run only

One invocation of harness test <app> runs this, start to finish — inside the disposable VM guest by default (--driver vm), or on the bare host when explicitly approved (--driver host --allow-host):

flowchart LR
  A["acquire<br/>brew cask / locate"] --> B["static arch<br/>lipo · otool"]
  B --> C["isolation.prepare<br/>host: clean state"]
  C --> D["launch<br/>NSWorkspace"]
  D --> E["record<br/>ScreenCaptureKit"]
  E --> F["drive profile<br/>Accessibility API"]
  F --> G["evidence<br/>video · shots · crash · logs"]
  G --> H["classify<br/>passed / native …"]
  H --> I[("store<br/>SQLite + r2/")]
  I -. "sync (later)" .-> J["D1 + R2"]

Four seams are pluggable at runtime, so we can A/B the tooling — or run permission-free — with a flag. = working, (gated) = built but needs human approval, (stub) = wired seam, not implemented. The headline seam is RunDriver (--driver): which machine the test runs on.

flowchart TB
  E["AutomationEngine"]
  E --> D{{"RunDriver (where it runs)"}}
  E --> U{{"UIDriver"}}
  E --> R{{"EvidenceRecorder"}}
  E --> S{{"IsolationBackend (state hygiene)"}}
  D --> d1["vm ✓ (default)"]
  D -.-> d2["host ✓ (needs --allow-host)"]
  U --> u1["ax ✓"]
  U -.-> u2["cgevent (stub)"]
  U -.-> u3["xcuitest (stub)"]
  R --> r1["screencapturekit ✓"]
  R --> r2["screencapture ✓"]
  R --> r3["ffmpeg ✓"]
  R --> r4["none ✓"]
  S --> s1["host ✓"]
  S -.-> s3["user account (stub)"]

--driver (which machine) and --isolation (state hygiene once on a machine) are different axes. The VM driver runs the in-guest harness with --isolation host because a disposable VM is the isolation.

--driver / laneGPUIsolationApprovalStatus
vm (tart macOS VM) (default)⚠️ paravirtual (capped)full clean-room, disposablenone — safebuilt; end-to-end boot needs tart + base image
host (bare host)✅ real Metalprefs/containers resethuman (--allow-host)built — refuses without consent
host + --isolation user✅ real Metalseparate account statehumanstub
bare-metal worker (prod)✅✅ fullone app per boxfleet policyplanned

Results map 1:1 onto the data model. The local store is built so “connect Cloudflare later” is a flat upload: SQLite is D1, and r2/ file paths are R2 object keys.

flowchart LR
  subgraph M["Domain model"]
    T["Title"] --> SG["Signal"]
    TR["TestRun"] --> SG
    SG --> V["Verdict"]
  end
  subgraph L["Local store · .darm-data"]
    DB[("SQLite<br/>doesitarm.db")]
    FS["r2/ mirror<br/>recordings · logs · diagnostics"]
  end
  M --> DB
  TR -. "evidence keys" .-> FS
  DB -.->|"wrangler d1 execute"| D1[("Cloudflare D1")]
  FS -.->|"wrangler r2 object put"| CR[("Cloudflare R2")]
mindmap
  root(("doesitarm-harness"))
    CLI
      setup
      arch
      scan
      test
      plugins
      sync
    Core["CompatHarnessCore"]
      Acquirer
      ArchInspect
      Launcher
      EvidenceRecorder
      IsolationBackend
      Accessibility
      Profiles["Base + VLC/HandBrake/Audacity"]
      Storage["Storage + Schema"]
      VerdictResolver
      AutomationEngine
    Store[".darm-data"]
      SQLite["SQLite (D1 mirror)"]
      r2["r2/ evidence (R2 mirror)"]

macOS gates UI driving (Accessibility) and capture (Screen Recording) behind TCC grants that can’t be set from the CLI. The arch/scan/plugin lanes need neither, so we collect real data today regardless.

flowchart TB
  subgraph FREE["Works now — no permissions"]
    sc["scan · 113 apps classified"]
    pl["plugins · auval"]
    ar["arch · static Mach-O"]
  end
  subgraph AXG["Accessibility ✓ granted"]
    dr["drive UI · menus · buttons"]
  end
  subgraph SRG["Screen Recording ✗ NOT granted"]
    vid["video clips"]
    shot["screenshots"]
  end
  classDef ok fill:#0f3d2e,stroke:#5ee0a8,color:#dffaf0;
  classDef warn fill:#3a2f12,stroke:#e5c463,color:#fff7e0;
  class FREE,sc,pl,ar,AXG,dr ok;
  class SRG,vid,shot warn;

Planned: beta-OS coverage + a driverless printer lane

Section titled “Planned: beta-OS coverage + a driverless printer lane”

Three near-term extensions, from the timeline and data-sources memos:

  • macOS 27 “Golden Gate” beta data. To classify apps against the next OS before GA, the harness needs a beta-OS VM. tart create --from-ipsw is blocked by a confirmed Apple Virtualization.framework bug, so the working path is to boot a Tahoe (macOS 26) VM image and upgrade to 27 in-guest via OTA (e.g. the MacStadium Orka Tahoe image, which has a recovery partition). Verify store-distributed apps actually install in the VM before trusting it as a data source.
  • A driverless printer/peripheral lane. macOS printing is a software pipeline you can exercise without the physical device — a three-tier check on one Mac mini: lipo static inspection → cupsfilter -e PASS/FAIL → ippeveprinter/ipptool.
  • Acquisition guardrail. Don’t build acquisition on disposable Mac App Store VMs (sign-in fails, re-auth per clone, device caps + rate limits, account-termination risk); lead with Homebrew casks + KVR plugins.
Terminal window
cd apps/doesitarm-harness
swift build
swift run harness scan # classify every installed app (no permissions)
swift run harness test vlc # acquire, launch, drive, record, store
swift run harness test vlc --recorder ffmpeg --isolation host # swap backends

The full options matrix and decisions live in the repo at apps/doesitarm-harness/docs/driver-architecture-and-tooling-options.md.