Apple device env: live simulator across machines, agent recording and proof integrity - #1299
Conversation
…r, live column, recording, cross-machine stream Replace idb with a vendored serve-sim Swift helper (ADESimHelper) signed and built by every macOS dist script. One simulator per lane, created on first ask and deleted on lane archive. Add the Apple sibling column in the Work tab with 3D and flat device views, live inspect, Home and Rotate, and recording with tap rings and typed-text badges. Add a ticketed H.264 stream through the brain for the web client (interact) and the iOS app (view only). Rename the CLI to `ade apple` with an `ade ios-sim` alias for one minor release. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
package:alpha and package:beta never built the notch, the capture helper, or the new sim helper, all gitignored build outputs. An alpha build then lacked ade-sim-helper and every Apple device action failed. The channel script now runs build:mac-native before the desktop build, as dist:mac already does. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…-and-live-stream # Conflicts: # apps/desktop/src/renderer/components/app/CommandPalette.test.tsx # apps/desktop/src/renderer/components/app/CommandPalette.tsx # apps/desktop/src/renderer/components/terminals/NativeToolFeedsContext.test.tsx # apps/desktop/src/renderer/components/terminals/NativeToolFeedsContext.tsx # apps/desktop/src/renderer/components/terminals/TerminalsPage.tsx # apps/desktop/src/renderer/components/terminals/WorkSidebar.test.tsx # apps/desktop/src/renderer/components/terminals/WorkToolPicker.test.tsx # apps/desktop/src/renderer/components/terminals/workTools.ts # apps/ios/ADE.xcodeproj/project.pbxproj # docs/features/terminals-and-sessions/ui-surfaces.md
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ot-on-start, tools drawer Replace the sibling column, create dialog, header toggle, and unlabeled icon stack with a single pane inside the tools pane, modeled on t3code's device pane. The picker is the empty state; Start attaches, boots, and streams in one click through the new deviceStart contract. Every rail button has a tooltip. Errors map to one sentence via describeAppleError. Preview Lab moves into a drawer section. Add `ade apple start`. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Show Borders is iOS's Button Shapes flag; add it as the button-shapes accessibility option through the same preference write and change notification the other switches use. Add getForegroundApp, which asks the helper's accessibility bridge for the frontmost app, and poll it from the tools drawer so apps opened outside ADE show up and the Event log follows them. Wire the method through IPC, preload, the web adapter, the remote allowlist, and the action policy. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A dev brain must never compete for the machine-wide sync host lease. On 2026-09-21 a lane dev brain took the lease from the installed ADE and the agents under that brain died with it. Set ADE_DEV_RUNTIME_SYNC=1 to opt in. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…me, with an isolation report The launcher's auto path spawned the dev brain without --no-sync, so it could take the machine-wide sync host lease from the installed ADE. It now always passes --no-sync (ADE_DEV_RUNTIME_SYNC=1 opts in) and prints a dev isolation report before the window opens. The local-development doc and the context skill state the single supported command. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The renderer fetches the stream from another origin with an authorization header, so the browser preflights with OPTIONS. The helper answered 405 and every in-app reader failed with "Failed to fetch" while curl worked. The frame server now answers the preflight and labels every response with Access-Control-Allow-Origin, so a 403 is readable too. Map the browser's fetch failure to "Could not connect to the video stream." with Reconnect. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…d solid Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…n-tool decision Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Function: an 8 s per-control expiry in the serial simulator queue ends the tap storm (the helper's 30 s timeout outlived the desktop's 25 s one, so one wedged touch stalled every later tap); auto-record starts only for agent input; every recording that stops becomes a proof artifact with a Saved receipt; status reads route through the bound runtime; decoder errors recover in place; the floating mini player renders the shared stream with hover-only chrome and follows the shared close/minimize/preview rules (mirrors of lane mac-desktop's workLiveCardState, chatCompanionUiState, workLiveCard, workToolPreviewControls, closeWorkToolForReal). Design: the tool is Apple Development with the Apple logo; the picker is gradient cards grouped by iPhone, iPad, Apple Watch, Apple TV, and Apple Vision with the model under custom names; every surface is opaque; drawer rows never collide; the loading card says what is booting and which step. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…nfig `@import "tailwindcss"` in v4 does not read tailwind.config.cjs, and the CSS never declared @config. So every token utility in that config compiled to nothing: bg-surface, bg-surface-overlay, text-fg, muted-fg and border-border produced no CSS at all. Panels rendered with no background, text inherited its colour, and the shutdown dialog shipped see-through. Class-name tests could not catch it because jsdom computes no styles. One @config line restores all of them. A guard test now fails if the directive goes missing, if a token disappears, or if a token points at a CSS variable index.css never defines. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ols, four-group drawer Owner decisions from the live test. Record and Inspect move into the rail. The procedural body and its realistic flag are deleted, so the vendored Apple models always load; the black cover glass shared a plane with the screen, so the screen now lifts fractionally out of the tie. 3D and Flat become one toggle, 3D by default. 3D taps were measured in decoded pixels rather than device points and landed three times off. Reset view rebuilt the renderer and exhausted the WebGL contexts. The flat view capped its scale at 1, so a large pane stayed mostly empty. Inspect was never mounted at all; it now draws frames that follow the tilted body and its card inserts the element into the chat. The drawer becomes four collapsible groups with a closed group unmounted. Closing the tab powers the device off behind an ADE dialog. Reopening the tool used to hang on "Connecting video": the outgoing floating player released the lease the arriving pane had just adopted, which stopped the capture the service still reported as running. Leases are epoch-stamped and the retake hands over. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… take the compiling guard The mac-desktop lane challenged 938eee2. Two corrections, one accepted. Accepted: my guard was the wrong shape. It asserted the config CONTAINS token keys, while the real failure is a class the product spells that the config does NOT define. Their guard compiles the real stylesheet and fails on any colour class in our own vocabulary that emits no CSS. Taken from d1de304, with the blind spot closed: a name also counts as ours when index.css defines --color-<name> for it, which is how text-success was invisible to a token-family check, and React key props no longer read as class names. It then caught five dead classes here. bg-secondary is a rail button's PRESSED state, so every toggled-on rail control rendered with no background, and the CTO timeline's info/success/warning/error text had no colour. All five had variables in index.css in both themes and no token. Registered. Not accepted: that @config is a no-op. Compiling this stylesheet through @tailwindcss/postcss 4.1 here gives 742,847 bytes with it and 681,927 without, and bg-surface, bg-surface-overlay, text-fg and border-border are emitted only with it. My earlier commit message still overstated its effect: that commit also swept in 34 lines of card CSS left uncommitted by another unit, which I did not notice and which did part of the work I credited to the directive. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Both round-5 units hit the Opus session limit mid-write. The tree they left compiles and passes (desktop tsc clean, 1757 tests across apple, terminals, services/ios and the token guard), so this records it rather than risking it across a session boundary. Landed: the flat view now knows about orientation, so a landscape device is drawn upright instead of lying on its side; the rail gains a named orientation control; the inspect context path is reworked; closing the tab powers the device off for real; and the agent-facing surfaces (action policy, apple remote commands, CLI verbs, screenshot-as-proof) are part way through the parity pass. Not done: the skills and docs rewrite, and the CLI help regeneration. Neither unit reached it. Nothing here is verified on screen yet. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Finishes round 5 §S5, which neither unit reached before the Opus limit. The skill is rewritten around what an agent actually does: read `status` for `capabilities` rather than guessing verbs, use `start` to bring the device up and `stop` to power it off, drive by element query, and rely on proof filing itself. Its old pin-to-proof instructions were wrong — every recording and every screenshot now files itself and returns a proofArtifactId. The three historical negations (idb, window capture, live-start, --backend) collapse to one line that tells a reader those docs are describing a dead version. Also: `stop`, `scroll` and `foreground` were implemented but appeared in no help text, so an agent could never find them — added and the help regenerated. ARCHITECTURE listed simulatorAppShell.ts, deleted two rounds ago. The feature README gains the shipped desktop surface and the discovery contract, and loses its wrong proof-bundle claim. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…the chat A capture with no chat session arrived at the broker with an EMPTY owner list, because both paths claimed only a chat owner. The broker links an artifact to its owners and derives the lane from them, so that artifact belonged to nobody: `ade proof list` returned it under no scope, project-wide included, while `screenshot` still handed back a real artifact id. It looked filed and was unreachable — which is every CLI and agent capture, the exact workflow the feature exists for. `ade proof attach` never had the bug because it claims both owners. Verified live: a screenshot taken with no chat session now appears in the drawer owned by its lane. Two tests, one of them the empty-owner case. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…s a screen An agent asked the owner for permission to use a simulator another lane owned, and offered to substitute a passing unit test for the screenshot he asked for. Both were reasonable from the inside and both were wrong, because the skill never said otherwise. It now states that a device owned elsewhere is not a blocker — one runtime serves unlimited devices, so cloning one for your lane costs seconds and no iOS copy — and that a request for simulator proof is owed a screen, with the real obstacle named when the screen cannot be reached. It also separates runtime from device, so 'Shutdown' reads as installed and ready to boot rather than as missing. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
My own regression test omitted chatSessionId, which vitest accepted because it does not typecheck. It is now passed explicitly as null, which is the point of the case rather than an omission: that is what a CLI or agent capture looks like, and what used to leave the artifact with no owner at all. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…vice Rotation. My diagnosis was half wrong: applied:true only meant a mach message was sent, but rotation works with no Simulator.app — this Xcode does not even have that app on disk. What refused was the app on screen. The Home Screen and Settings are portrait-only on a non-Max iPhone, so they ignored every rotation while the OS took all of them; launching Safari on the same device came up landscape on its first frame, which proves it. Rotate now measures the real framebuffer before and after and claims success only on a confirmed turn, or when the screen already sits on the requested axis. A refusal returns a reason and reads "The app on screen does not support that orientation", replacing an invented excuse about a window needing to be open. Picker. The owner counted four of five simulators, because the hero card sat outside its family section — and that hero was not even his lane's device, since the picker fell back to the newest iPhone when the lane had none. It also offered Open on a simulator another lane owned, with no hint, which is the same blind spot that made an agent stop and ask him for permission. Now: one inventory line naming the runtimes, counts and device-data size; a hero only for the lane's real device; unowned devices under Available; and another lane's device shown with that lane's name, no Open, and a takeover behind a confirmation. Per-device sizes from one cached directory pass. One lane owns a device at a time. deviceAttach duplicated a binding instead of moving it, which nothing could reach until the takeover button existed. The registry now re-keys the row in a single statement, so there is no instant when two lanes own it, releases the losing lane's stream, claim and session first, and keeps the simulator running for the lane taking it. Provenance stays with the simulator, so a clone ADE made stays ADE's to delete. Whole-tree gates: desktop and CLI typecheck clean, 7236 desktop tests, 1271 CLI tests, lint clean on every changed file. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…claim banner Two faults the owner hit in one live test. An OpenCode question card is durable — it IS the `approval_request` transcript event, and the renderer rebuilds it from that event — while the waiter that receives its answer is a closure in one process. When the pooled server went away with the card open, the card was redrawn fully interactive with nothing behind it. Answering it reached `settleUnclaimedPendingInput`, which never read `answers` or `responseText`: it recorded the accept as a cancellation, painted "That request is no longer active", and returned success, so the composer never restored the draft it had cleared. The answer was lost twice, and retyping it by hand was the only way out. The card can still outlive its waiter; that is now handled rather than prevented. An accept carrying real content on a question-shaped request is re-routed as an ordinary user message, which is the mechanism async Codex questions already use. A re-route failure throws, so the card stays and the composer puts the answer back. A card already receipted `accepted` is a silent no-op, so a click racing a keypress can no longer overwrite an answered receipt with a cancellation. Approvals, plans, elicitations and secret questions keep the old settle: "Approve and implement" is not prose, and a re-routed secret would be written into the synced transcript. Separately, Apple Development no longer claims itself for another lane. The banner was written when one simulator session WAS the pane. A lane now owns a device, one runtime install serves any number of them, and the picker states per device whether it is free, this lane's, or in use elsewhere. The banner sat over a page offering four free simulators and said the view was second-hand — and it taught an agent to stop and ask for a device instead of creating its own. App Control keeps its banner, which is still one per lane.
…g it verified An agent filed two before/after screenshots, reported success, and the owner could not open either one. The files were on disk. The record's only owner was `chat_session: ade-cli:56056` — a client name and a pid — so no lane resolved from it and no proof drawer could scope to it. Four faults, stacked, each of which alone would have hidden the capture. `resolveComputerUseOwners` treated the caller id as an implicit chat session whenever the caller named none. For the direct CLI that id is minted as `<client>:<pid>`, so a process became the owner of a durable record. A synthetic id is now never an owner; the lane owner serves that caller instead. The ingest authorized a named lane only against the caller's chat-session lane. An agent whose shell carries no chat session — every OpenCode agent, because one `opencode serve` is shared across chats and so cannot carry a per-chat environment — had two ways to fail and none to succeed: name its lane and be told it does not match a session it has not got, or name nothing and depend entirely on inference. The lane inferred from `callerRoot` now also authorizes a lane the caller names, which is the stronger check: an environment variable is a claim, standing inside the worktree is a fact. A lane named from outside its worktree is still refused, and the refusal now names the requested lane, the session lane and what the callerRoot resolves to. A failed inference logged nothing. The ingest refused and the CLI printed a generic "requires an authorized lane worktree", which is why this took a night to find rather than a minute. It now warns with the callerRoot and how many lanes were considered, and an ingest that lands with no lane and no chat is warned about too — not refused, because a CTO scene still legitimately files with no owner. Worst of all, the verification passed. `summarizeProofFiling` computed the lane and the chat session in order to PRINT them and never required either, and the re-read found the row because an unscoped caller lists project-wide. The agent read "verified: re-read through ade proof list" and told its owner the proof was attached. A record with no owner is now a failure, named as one, and a synthetic owner id is discounted on this side too — the CLI is used against whatever brain is installed, including versions that still write one. Both new server tests were seen to fail against the old guard, and the CLI test against the old summary, before they passed.
… and that simctl captures lose their owner Three gaps a live agent run exposed, in the order it hit them. It read this skill and then built with xcodebuild by hand, because nothing here said `ade apple launch` resolves, builds, installs and starts a target in one call. It does, and that is the answer to "run the app on a simulator". It captured with `xcrun simctl io screenshot` and handed the file to `ade proof attach`. The picture was right and the owner was not: a shell with no chat session produced a record owned by nothing, which no drawer could show. The `ade apple` capture commands file themselves against the lane and the asking chat, so the skill now says to use them and why, and says to read the owner line when attaching a file from elsewhere. It also led with cloning, when binding a free installed device is the cheaper first move and what `start` already does.
… honest copy costs The owner's own layout, in his order, after reading round 5 on screen. Gone: the summary box at the top (the runtime, a count installed, a count running, and a paragraph about one runtime serving many devices), the caption under Available, the line at the foot of the page, and the separate section for a device another lane holds. He could see how many devices he had by looking at them; the prose sat between him and the thing he came to click. What replaced it says the same facts in the list itself. "Available" leads, with the free count beside the word and one glyph per device he owns set at the heading's own size — three iPhones and two iPads read as three iPhones and two iPads at a glance, and a device on hold elsewhere is dimmed rather than absent. Family is now the only grouping, so the number of cards on the page and the number beside the heading finally agree; round 4 lifted a hero out of the groups and five simulators read as one plus four, which is why he counted four. Each card is a glyph, a name, `iOS 26.3 · iPhone 15 Pro`, and a menu. The model shows on every card, even when the name already contains it, so a grid of cards does not disagree about its own line count. The whole card starts the device. A device another lane holds says TAKEN, carries no menu, and does not respond to a click. Round 5's "Take over…" is gone from the panel entirely at his instruction — a device on hold by a working lane is not a choice this panel should offer. The CLI keeps `--force` for recovering one from an archived lane. The menu is new down to the service. `deviceDeleteInstalled` deletes one installed simulator by udid and refuses any a lane holds with `APPLE_DEVICE_OWNED_BY_LANE`, because the picker's rendering is not a safeguard: a CLI caller and a stale renderer reach the same method, and the cost of getting it wrong is another lane's live view vanishing mid-test. It also refuses to run at all without `confirmedByUser: true` — deleting a simulator is not recoverable, the owner's standing rule is that nothing deletes one without his approval, and this verb is reachable by any agent because ADE keeps one action list per domain. The menu asks twice and names the measured size in the second ask, since "delete iPhone 17e" and "delete iPhone 17e, 3.2 GB" are different decisions. "Create a new one" sits at the foot with a row per installed device and what each copy costs. `simctl clone` duplicates the source's data directory, so the source's measured size IS the estimate, and every row says no download — which is the fact that was buried in the deleted paragraph and the reason an agent thought it had to ask for a device. One real bug fell out of writing the tests: `appleDefaultTemplateUdid` returned the project's last used template on being installed alone, without checking it was still cloneable. A template since booted or taken by another lane became the default Create source, and `simctl clone` on a booted device fails — so the one source the page offered by default was the one that could not work. Refresh moved to the top right of the pane, where a refresh belongs.
…age reachable in the web preview I looked at the rebuilt picker in a browser instead of trusting its tests, and found four things jsdom cannot see. That is the third round in a row where green unit tests shipped a page the owner had to correct, so the last change here is the one that stops it repeating. The create list offered every installed device as a clone source, including a booted one and one another lane holds. `simctl clone` fails on a booted device and a held device is not this panel's to copy, so on the owner's own machine the first row was a source that could only ever fail. It now offers what a clone can actually be made from. Each of those rows read `iPad Air 13-inch (M4) · iPad Air 13-inch M4`, because `appleDeviceModelLine` appends the model to the name and for most devices the name already IS the model. The row names the device. The page was centred vertically in the pane, so a short list floated in the middle of an empty column. It starts at the top. The inventory glyphs beside "Available" were set at 13px and 70% opacity and read as blank boxes rather than as phones and tablets, which is the one job they have. 15px at full strength, and a held device dimmed rather than erased. And the reason all four survived: the browser mock reported `supported: false` with no `deviceList`, so the Apple pane stopped at "this host is not a Mac" and the picker could only be seen by building Electron. The mock now carries the owner's machine — five devices on one runtime, two booted, one held elsewhere — so `npm run dev:vite` shows the real page. jsdom computes no styles and a class name is not a layout; this is the cheap way to look, and it was only useless because the page it was meant to show could not be reached.
…cket, read the report An agent told to start a dev app ran the launcher as an ordinary command. The command runs in the foreground for as long as the window is open, so it held its turn open, and when the harness ended the turn it killed the process group and the window closed. Nothing in AGENTS.md, the context skill, the local development doc or the desktop README said that, so the agent was not wrong to read it as a normal command. All four now carry the same block: background it, give the lane its own socket because the default is shared and two dev brains on one socket restart each other, then wait for the isolation report and read it — sync ON means a dev brain is holding the machine-wide lease that drops the installed brain's tunnel and kills the agents under it. Also states the limit that is easy to assume away: --no-sync protects the sync lease only. It does not isolate the database, so a dev brain still sees every chat and task in every lane. The desktop README additionally points at the browser preview as the answer to "does this page look right", now that its mock reaches the panes that used to be unreachable there.
The owner opened the Apple tool while the floating player showed the device. The pane sat on "Connecting video" until he switched tabs, and his phone kept playing the whole time. - The pane and the floating player counted viewers in two buckets. The pane keyed its lease by its null runtime pin, and the player by the resolved window binding. So the last viewer of one bucket stopped a capture the other had just joined. Both now key by the resolved machine, and the last viewer's stop waits 1 s and is skipped if another viewer took a lease. - Nothing made the pane ask again. The stream hook now reconnects on a stream-stopped or stream-error for its device, on a body that ends under a live viewer, and on a first-frame timeout (3 tries, backoff). A reader "stopped" with no address no longer drops a pending start to idle. The pane's 8 s re-check also reconnects "Connecting video". - A desktop viewer's stop no longer ends a capture a phone or web viewer reads through the relay. The relay adopts it and stops it when its own last viewer leaves. Checked in the dev app: pane opened over the player went live in about 1.5 s, one lease bucket, and no stream-stopped after the handover hold. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Important Review skippedToo many files! This PR contains 235 files, which is 135 over the limit of 100. To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch. Upgrade to a paid plan to raise the limit. This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry. ⚙️ Run configurationConfiguration used: Repository: arul28/ADE/.coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: ⛔ Files ignored due to path filters (15)
📒 Files selected for processing (235)
You can disable this status message by setting the Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
…1299 The Apple lane added the agent shim fix, proof integrity, `ade ui show`, idle-cut recordings, the loopback proof video server, `apple key`, and the pane/floating-player stream handover. Two conflicts, both additive: - adeRpcServer.ts: keep the mac_desktop scoping and the Apple lane's always-scoped work_tools actions and driving-action list. - workToolsStateService.ts: dispose the Mac Desktop subscription and the show-request store. The CLI grammar count rises from 110 to 112 for `ade ui show`. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…rer and iOS Eight reviewers (correctness and maintainability, four areas) reported about 110 findings. This commit fixes all of them except one rejected policy item (the work_tools pass-through methods are the intended boundary). High - Agents could label any file "Recorded/Captured by ADE": the label came from names the caller chose. The RPC server now keeps a short registry of files its own capture actions produced (path, SHA-256, 15 min). Only an unchanged file from that registry keeps ADE's label. - The phone downloaded every proof video in full as soon as its row showed. A video over 8 MiB now downloads only when played, in partial files, off the main actor, and a cancelled load retries. Behavior fixes - "Choose another device" on the off card releases the device from the lane (new deviceDetach) and never deletes a simulator. - Agents can no longer delete installed simulators; only a user client. - A device swap inside one viewer no longer stops the new stream. - A show request is not replayed after the surface opened; the chat pane waits for the drawer to be on screen; lane-less shows use the pane's lane; the floating player checks the machine too. - Picture in picture survives a stream hiccup; a hidden pane stops polling; an offline proof tile reloads when its machine returns. - An unfiled recording can be attached in place; an ownerless ingest is refused before it is stored; a helper exit reports stopped recordings; a remote viewer's bitrate cap is lifted when it leaves. - The phone's simulator chip requires the lane's own device. - `apple key --device U return`, Windows delegation (hidden console, no %VAR% re-expansion), ADE_CLI_ENTRY_PATH in the agent env, case-safe lane and artifact path checks, the account-directory relay binding is required. Structure - Extracted: proofFingerprint, appleRecordingsStore, simulatorPower, fileRange, pendingInputRequest (shared), adeCaptureRegistry, syntheticCallerId, cliGlobalArgs, Apple CLI help (about 1,100 lines out of cli.ts), useAppleDeviceStartTracker, useArtifactPreview, useWorkShowRequests, useChatPaneShowRequests, appleDeviceModelLoader, and the iOS lane-tools presenter. - One Range parser, one MIME table, one show-on-screen check, one lease key, one power map; dead code and stale comments removed. - Docs and skills describe the loopback media server, the Common tasks, and the new rules. The cliBrowserGrammar count now names the three new argv readers, and the entry guard callback is a named function. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…big files A second review of the first pass found no Blocker, High or Medium correctness issue. This commit fixes the Low items and the leftover structure work. Correctness - An ADE capture keeps its label once: the registry entry is used up on a match, so attaching the same capture again is refused as a copy. The broker files a capture whose bytes changed after ADE hashed them as an attach. - Deleting an installed simulator is a user-only action: a separate list, never shown to agents, refused to agents, automations and CLI/TUI processes that have no chat. The desktop's own connection stays a user client. - deviceDetach follows deviceStop's owner rule; the off card passes the pane's owner setting. - Proof owners: automation runs, PRs and issues count again, through one hasDrawerOwner rule shared by the server, CLI and broker. - A held show answer keeps `opened`; lifting a bitrate cap no longer races a rejoining phone; the floating player's handover timer does not stop a second device on the lane. - Artifact paths fold case only for Windows-shaped roots; a late phone download writes its own file, so it cannot delete a reopened chat's video. Structure - laneDeviceLifecycle.ts out of iosSimulatorService.ts; one power-off path (the registry uses it too); one recordings store; one file hash. - AppleDevicePane.tsx 1143 -> 993 lines (useAppleLaneDeviceList, useAppleInspect); AppleDevice3DView.tsx 1230 -> 931 lines (appleDeviceScene.ts). - Lease keys carry their lane scope; one laneOnMachineKey; one lane rule for show requests; shared pathCase, syntheticCallerId and artifactImageSrc; dead exports and orphaned comments removed. - iOS: chip names live in the chip kind; one duration formatter mirrors the desktop; the eager-video branch that never ran is gone. Checks: desktop, CLI and account-directory typechecks clean; 15,695 tests pass across main, renderer, CLI and account directory. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… gate A third review of the second pass found one Medium and thirteen Low items. This commit fixes all of them. - deviceDelete checks the session owner first (on ignoreOwnership, not force). An agent in one chat can no longer detach or delete another chat's device through `delete --force`. `ade apple device-delete` now sends the caller's chat id and accepts --ignore-ownership. - User-only verbs are allowed only for the desktop's own client names, kept in one list (isDesktopClientName) that the minting sites use too. An unknown or missing name, or any CLI name, is refused. - A capture's registry claim is handed back when filing fails or the call is not a pure capture, so a retry keeps the ADE label once. - Automation action lists hide user-only verbs. - The pane's device callbacks list ignoreChatOwnership as a dependency; the device list reports booted reads to the start tracker without a ref cycle. - The CLI's global value flags and their handlers are one typed table; the registry's delete no longer detaches (the lifecycle does); cap generations are explicit; provenance policy moved into the judge; leftover aliases and exports removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…filing A fourth review of the third pass found eight Low items. This commit fixes all of them. - deviceDelete re-checks the session owner inside the device queue for a clone, so it cannot race another chat's start; the attached path hands the caller's own identity to deviceDetach, which re-checks in its queue. `force` never bypasses ownership. - The broker files a multi-input ingest in one savepoint: a failure part-way rolls every row back, the staged files are discarded, and the capture claim can be handed back safely. Events go out after the commit. - A delete refused because the device became attached while it waited now says so and points to device-detach. - The desktop client names live in shared/runtimeClientNames.ts; a capture match carries its own release(); automations use one createAutomationAdeActionLookup; one readIgnoreOwnershipArg; dead exports made private. cliBrowserGrammar counts the new reader (111). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- deviceDelete reads the lane's device once, inside the lane's queue. An attached device is refused before the stream stops, or detached with force. The check before the queue and the hint rewrap are gone. - deviceDetach and deviceDelete share detachStep. - close-device reads the ownership flag inline. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- deviceDelete passes the udid it read to the registry. The registry does nothing when the lane holds another device by then, because the standalone attach and create verbs are not queued. - Tests pin the no-device early return and the unforced "read once" case. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…scoped delete Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…S and Windows parity - computerUse: the byte-range and stream-protocol tests join the media server suite; the provenance and MP4 creation-time tests join the broker suite. The folder goes from 8 test files to 4. - ios: the simulator-power and lane-device-lifecycle tests join the lane device registry suite. chat: the two single-parent helper suites join agentChatService.test.ts. No test case is lost (counted before and after). - CLI: typed `ade apple device-detach`, a help page for `stop`, and help that matches the current flags. - iOS: older-host range-read fallback helper and tests; laneDevice decoding. - dev-detached.mjs finds npm on Windows, hides the console, and reports a failed start instead of printing "undefined". - Windows CI runs the new path, media server, shim and delegation tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… suite Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e code - Apple device: detach/delete decide in the lane queue; the user-only gate; the turn-time hint, `apple show`, `type --submit`, `key`; one source map row per file. - Computer use: the IPC names are `ade.computerUse.*`; the media server, fingerprint and byte-range modules are in the source map. - Proof, chat, terminals, agents, sync and ARCHITECTURE: the capture registry, pane show requests, CLI delegation and the new modules. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- An automation's action list hides the user-only verbs it would refuse. - A booted event shows the lane device card as running at once. - A tool is on screen only when its own pane has a layout, on its own machine. - Deleting a lane's clone powers it off once, through the power path. - A proof whose only owner is a PR, an issue or an automation run is stored. - iOS: the preview-versus-play video decision, the failed-load outcome and the chip symbol move into plain functions, with tests. - Remove a duplicate type import that the test merge added. Each new desktop and CLI test fails when its fix is reverted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts resolved: - account-directory: keep this lane's environmentSection reader and the manual-redirect assertion; take main's USAGE_RESEARCH_* warning test. - adeCliService: main's cleanup of a missing bundled CLI, plus this lane's ADE_CLI_ENTRY_PATH rule when the CLI is present. - laneService: keep both getLaneIdForPath and main's findLaneIdentity. - AgentChatPane: main's proof section classes, with the show-request ref. - agentChatService.test: keep both appended suites. - cliBrowserGrammar: 112 argv readers (this lane's 111 plus main's buildChatLaunchPlan). - iOS: WorkToolsRow.swift stays deleted; main's "pr" display name moves to WorkLaneToolChips.swift. - The ade ui show proof test gives the chat one artifact, because main's drawer shows a proof section only when the chat has proof. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… no proof After the merge of main, the chat actions drawer mounts the proof section only when the chat has proof, and a visible grid tile that is not focused keeps no proof snapshot. A show then waited 3 s and told the agent the window was not in front, which was false. - A show re-reads the chat's proof, mounts the proof section even with no proof, and says "This chat has no proof yet." when there is none. - The drawer scrolls the proof section into view, so "shown" means visible. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…urvives a held delivery - The proof section a show mounts waits for the forced proof read, so "shown" and "This chat has no proof yet." both follow loaded data, and a tile that loses its snapshot never claims the chat has no proof. - The drawer scrolls to the proof once per show, not on every capture. - The pane resets its drawers for a new chat before the show handler registers, so a show held while the chat was out of view is not closed again in the same commit. - The show tests clean up in finally and cover a read that has not returned. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hing - Each proof show takes a token; closing the drawer or changing chat invalidates it, so a read that returns later does not reopen the section. - A failed read from a show keeps the proof already on screen. - A test pins "scroll once per show" and the late read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ed read writes nothing - keepOnError keeps the snapshot only when it belongs to the chat being read, so a failed read after a chat switch never shows another chat's proof as this one's. - A read that a newer read replaced neither writes its older data nor clears the newer snapshot. - A test pins that a failed show re-read keeps the chat's proof on screen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_f0f3716d-356d-4d0e-b431-d2baea52f687) |
…; annotate a case-folding gate - resolveCliDelegationTarget and canonical use path.win32 or path.posix for the platform they are given, not the host's. On the Windows runner the darwin-layout test joined POSIX paths with backslashes and missed the packaged bin/ade -> ../cli.cjs sibling. - laneService.test.ts: the case-insensitive lookup test carries a WINDOWS-GATE note, because the Windows fold is pinned by pathCase and pathCompare in windows-foundation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…1299 The Apple lane brought its quality passes (split files, a capture registry for proof, a new show-request hook, a lane-tool presenter on iOS), a merge of main (the PR tool, router usage, activity detail), and proof-read fixes. 45 conflict blocks in 23 files. Each keeps both sides; where the parent moved or split code, this lane's Mac Desktop additions moved onto the new structure: - CLI: the capture registry gets mac_desktop.screenshot, so `mac-desktop proof` still files as an ADE capture; show labels and workToolShowPlan follow the parent's move; Apple and Mac Desktop share one activity type. - Renderer: Mac Desktop show handling moves into useWorkShowRequests; the floating player reports "shown" through the key-based check; both the PR tool and Mac Desktop are Work tools; the backdrop keeps main's newest recipe (header slice) plus this lane's perf savings. - iOS: the Mac Desktop chip follows the parent's data-carrying chip kind, and the lane-tool presenter opens MacDesktopViewer. Grammar count 112 → 113 (buildMacDesktopPlan). Type-checks clean; Swift 150 passed; desktop renderer 10,056 and main 10,807 passed; CLI 4,996 passed. Under full-run load, a stdio daemon test and two Codex watchdog tests failed; each passes alone, and the chat test file passes 1,277 of 1,277 twice. The old "mobile sync host is still retrying" test still fails. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…review-and-live-stream AgentChatPane: this lane moved the session reset effect above the show handler, and main changed its body to keep the pending bubble of the chat just selected. Main's body now lives in the moved effect. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_39979aac-abf4-4c6b-b57b-96c802f19390) |
#1299 (the Apple device tool, this lane's former parent) was squash-merged into main as ed21a34. Its content equals the parent tip 02c8cf8 that this lane already contains, plus a merge of #1301 (send-handoff animation). 27 files conflicted only because the squash commit and this lane's history carry the same parent changes: for the 26 files main did not change after the parent tip, this lane's version is kept; for AgentChatPane.tsx, #1301's change is applied on top. The merge adds exactly main's diff since the parent tip (checked file by file). Type-checks clean; chat, terminals, apple, RPC, work-tools and CLI mac-desktop suites pass. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Problem
An agent on an iOS task had no ADE-owned simulator to drive, watch or record, so it used the Simulator app and
simctldirectly. The user saw nothing, the recordings were not ADE's, and an agent could attach an old or copied video as proof.What this lane adds
Live simulator, everywhere
Agents
adeshim names the brain that launched it, so two ADEs on one Mac never cross.adeon PATH hands the call to the CLI that ADE launched."$ADE_CLI_PATH" apple, record withrecord-start/record-stop, check each step, and never attach an old recording.ade ui show/ade apple showopen the pane, the floating player or the proof drawer, and answer "shown" only when it is on screen.apple type --submit,apple key return|tab,apple button app-switcher.Recording and proof
protocol.handlecannot serve media ranges; see the commit message of b8b7476..movproof plays on desktop, and large videos stream on paired desktops and the phone.iOS app
Other fixes found on the way
redirect: "manual", and the relay service binding (merged separately as fix(account-directory): the device sign-in never exchanged a token in production #1288/fix(account-directory): re-pair and machine removal threw on every relay call #1290/fix(account-directory): reach the push relay through a service binding #1291).Tested live
On the owner's MacBook (ADE Alpha) with the Studio, Windows and the iPhone:
Verification
/quality: 7 review passes (about 110, 45, 14, 8, 3, 3 and 1 findings), then a post-merge revalidation with 4 more passes. Every accepted finding is fixed. The gate is empty./test: 75 correctness findings, each with a named regression test or an alternate check. 8 findings had no test; each now has a regression test that fails without its fix.AgentChatPane324, CLI 561, account directory 216, scripts 14. Desktop, CLI and account-directory typechecks are clean.swiftc -parse. XCTest did not run locally, because the disk had less than 30 GB free; the iOS CI job runs it.windows-latestjob.mainmerged in at 5dc796a, with 9 conflicts resolved (see that commit message).Not merged by the lane: the owner stacks the desktop lane on top and merges.
Authored with Claude Opus 5.5 via Claude Code.
🤖 Generated with Claude Code
Note
High Risk
Changes authentication-adjacent RPC scoping, proof ownership, and account-directory relay transport; mistakes could hide proof, cross lanes, or break machine-removal relay in production.
Overview
This PR tightens proof and computer-use filing so agent screenshots and recordings land in the right lane drawer and cannot be mislabeled. The RPC server infers lane ownership from the caller's worktree for chat-less CLI agents, rejects synthetic
ade-cli:<pid>chat owners, blocks ownerless ingests before storage, and routes ingests through a capture registry that classifies bytes as ADE capture vs attach (SHA-256 match, duplicate rules, older-video warnings) instead of trusting CLI arguments. Scoped callers can prune broken ownerless artifacts; localscreenshot_environment/record_environmentproof paths get the same lane inference and provenance metadata.Apple / agent control expands the CLI (
ade applestart/stop/detach,type --submit,key,show,ui show, record flags) with dedicated help, wires work_tools.show through the runtime to desktops, scopes agentshowto their chat, and notifies the UI when agents drive the simulator. User-only simulator deletes are limited to named desktop clients; the headless brain getsresolveLaneIdForPathso Apple actions file against the correct lane. Agentadeshims embed the launching brain's socket/ADE_HOME; older CLIs can delegate via an early entry hook.Account directory drops public relay fetch (service binding required,
redirect: "manual"on relay calls) and simplifies wrangler env section lookup. Docs/CI document detacheddev:desktopandade-apple; Windows CI runs extra CLI shim, delegation, and desktop path/media tests.Reviewed by Cursor Bugbot for commit 8336448. Configure here.