From 5c6a873bd7f8c0d63774713eea838a4224af7cac Mon Sep 17 00:00:00 2001 From: "prompt.ac/@jeffrey" Date: Mon, 21 Sep 2026 13:08:11 -0700 Subject: [PATCH] Require Accessibility trust in fleet release verification and record rollout limits --- slab/computer-use-release/README.md | 12 +++++++++++- slab/computer-use-release/verify.mjs | 15 ++++++++------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/slab/computer-use-release/README.md b/slab/computer-use-release/README.md index a609f87669..6d7c5d98af 100644 --- a/slab/computer-use-release/README.md +++ b/slab/computer-use-release/README.md @@ -20,7 +20,7 @@ backs up changed files, configures Frame/Puppet launch agents and HTTP clients, and updates the Frame module imported by Captutor. Existing Captutor copies are updated only when they match the known predecessor; custom versions are reported and preserved. Verification checks release hashes, native build UUID, MCP tools, -session isolation, native capture, and Captutor's imported Frame without sending +session isolation, native capture, Accessibility trust, and Captutor's imported Frame without sending mouse/keyboard input or printing screen contents. It does not benchmark latency. `~/.local/share/slab/computer-use/installed.json` records the release and backup. @@ -28,3 +28,13 @@ For rollback, restore the saved native app and changed files (including launch agents and client configs), remove newly introduced wrappers if no predecessor exists, and restart those launch agents. Keep older release directories until the new release is verified; never clear another controller's input lease. + +September 21 rollout: installed on Blueberry, Neo, Chicken, Panda, and Poorslice. +Blueberry and Chicken passed capture and Accessibility checks. Neo and Panda +capture correctly but report no Accessibility trust, including after a restart; +their old and new code-signing requirements match. Enable SlabMenubar under +System Settings → Privacy & Security → Accessibility on those seats. Poorslice +has Screen Recording and Accessibility grants but is locked; unlock it before +the final capture check. The legacy `mac-mini` and `jeffrey-macbook` entries were +unreachable. Chicken's older Captutor predates the Frame integration and was +preserved; other existing Captutor copies received the in-process Frame client. diff --git a/slab/computer-use-release/verify.mjs b/slab/computer-use-release/verify.mjs index 3e258f14c8..f4bc3d52f2 100644 --- a/slab/computer-use-release/verify.mjs +++ b/slab/computer-use-release/verify.mjs @@ -6,11 +6,11 @@ import { homedir } from 'node:os'; import { pathToFileURL } from 'node:url'; import { createHash } from 'node:crypto'; import { execFileSync } from 'node:child_process'; -import { captureFrame } from '../bin/frame.mjs'; -import { createComputerUseClient } from '../lib/computer-use-client.mjs'; -import { inspectMachineLeases } from '../lib/computer-use-lease.mjs'; - -const root = resolve(import.meta.dirname, '../..'); +// A newer verifier can check an already-installed immutable release unchanged. +const root = process.argv[2] ? resolve(process.argv[2]) : resolve(import.meta.dirname, '../..'); +const { captureFrame } = await import(pathToFileURL(join(root, 'slab/bin/frame.mjs'))); +const { createComputerUseClient } = await import(pathToFileURL(join(root, 'slab/lib/computer-use-client.mjs'))); +const { inspectMachineLeases } = await import(pathToFileURL(join(root, 'slab/lib/computer-use-lease.mjs'))); const manifest = JSON.parse(readFileSync(join(root, 'release.json'))); for (const [path, expected] of Object.entries(manifest.sha256)) { assert.equal(createHash('sha256').update(readFileSync(join(root, path))).digest('hex'), expected, path); @@ -31,7 +31,7 @@ for (const name of ['frame_click', 'frame_drag']) { const schema = catalog.tools.find(t => t.name === name).inputSchema.properties; assert(schema.holdMs && schema.verify && schema.settleMs, name + ' speed controls'); } -const { chromium } = await import('playwright-core'); +const { chromium } = await import(pathToFileURL(join(root, 'node_modules/playwright-core/index.mjs'))); assert.equal(typeof chromium.connectOverCDP, 'function'); assert.deepEqual(inspectMachineLeases(), [], 'Do not interrupt active input'); const session = 'deploy_' + Date.now(); @@ -41,6 +41,7 @@ const b = await captureFrame('local', { ...options, session: session + 'b', base const diff = await captureFrame('local', { ...options, session: session + 'a', diff: true }); for (const { env, jpg } of [a, b, diff]) { assert.equal(env.capture, 'ok', 'Native capture permission/availability'); + assert.equal(env.ax?.trusted, true, 'Native Accessibility permission required for input'); assert(jpg?.length, 'Native pixels'); for (const cap of ['target-guard-v1', 'guarded-click-v1', 'ax-verify-v1', 'click-hold-v1', 'guarded-drag-v1']) assert(env.nativeCapabilities.includes(cap), cap); @@ -50,4 +51,4 @@ assert.equal(diff.env.diff_baseline, 'matched'); assert.deepEqual(inspectMachineLeases(), []); console.log(JSON.stringify({ ok: true, revision: manifest.revision, nativeUUID: uuid, hashes: Object.keys(manifest.sha256).length, mcp: 'Frame + Puppet', nativeCapture: true, - sessionIsolation: true, captutorFrameImport: true, captutor: installed.captutor })); + accessibility: true, sessionIsolation: true, captutorFrameImport: true, captutor: installed.captutor })); -- 2.51.2