Skip to content

feat: dev loop bundles 6-12 (reporting, safe calls, test results, run loop, UI driving, inner loop, tool surface) - #6

Merged
justinthevoid merged 28 commits into
mainfrom
claude/project-thread-7t3aif
Oct 9, 2026
Merged

justinthevoid merged 28 commits into
mainfrom
claude/project-thread-7t3aif

Conversation

@justinthevoid

@justinthevoid justinthevoid commented Oct 9, 2026 •

Copy link
Copy Markdown
Owner

Stacked on #5. Until #5 merges this diff also shows #5's commits. One commit per bundle (plus small CI fixes and real-Mac fixes): 7 feat: stop orphaned builds, report progress, scope config per project, 8 feat: test results agents can act on, 6 fix: build and test reporting agents can trust, 9 feat: run the app and see what happened, 10 feat: UI driving that hits the right screen, 11 feat: a faster inner loop, 12 feat: a smaller, consistent tool surface, then fix: findings from the real-Mac run. Bundle 6 moved here from #5's thread. CI only runs on PRs to main, which is why the base is main.

What

Bundle 7: calls that don't strand agents

Before: cancelling an MCP call or hitting a timeout killed only the direct child, so xcodebuild kept running with DerivedData locked ("database is locked" on the next build). Long builds sent nothing back, so some clients timed out. Two parallel calls from one agent raced on the same DerivedData. A project passed explicitly from another repo still used the cwd repo's .xcforge.yaml, quoted values and # comments in the yaml were read literally, and misspelled MCP arguments were silently ignored.

After: cancel, timeout, Ctrl-C, SIGTERM/SIGHUP and the client closing stdin all stop the whole process tree. Calls with a progress token get a heartbeat every 10s carrying the latest output line. xcodebuild calls sharing a DerivedData folder or project queue in-process. An explicit project uses its own repo's yaml, results name the project used, yaml problems come back as warnings with a suggested key, and unknown MCP arguments are an error naming the closest real one.

Bundle 8: test results agents can act on

Before: test IDs came in several spellings, so IDs copied from a result didn't always work as a filter or in known-failures. The target prefix for short filters was guessed. Failures showed one joined message, test failures were reported as "test target build failed", long failure lists printed in full, and there was no way to retry, repeat or time-limit tests. rerun-failed always rebuilt, forgot the test plan and env, and "reran" nothing when the last run died before any test.

After: one ID format (Target/Suite/test() / Target/Class/testMethod) everywhere; short IDs get the target from the scheme or test plan. Each failure carries every message with file/line, the argument or repetition label, attachments and optional console tail. Identical messages are grouped and text output is capped at 20. New run options: retries, iterations, untilFailure, parallel, testTimeoutSeconds, skipBuild; tests that pass on retry are listed as flaky. rerun-failed reuses recorded settings, skips the build when no source changed, and refuses when the last run failed before any test.

Bundle 6: build and test reporting agents can trust

Before: CLI test run showed a test-file compile error as a fake failing test with no file or line. test failures returned the previous run's failures after a test build failed, and silently ran the whole suite when nothing was recorded. build-for-testing ignored the test plan and filter, and passed different flags from build compile, so alternating rebuilt. The hang watchdog sampled healthy builds at 60s and printed a "Diagnostic snapshot" on success. build compile ran an extra -showBuildSettings and failed unless exactly one simulator was booted, and three simulator-name lookups could pick different devices. A zero-match filter started a second build. Agent JSON had no result-bundle path and no timeout reason when some failures were read. build run exited 0 without launching the app.

After: test build failures read as compile errors with file:line in every output. test failures reads only the last recorded run, says when there is none, and returns the compile errors of a failed test build. build-for-testing uses the test plan and builds only the targets a filter names. All compiling calls share one flag set. The watchdog samples at 300s/540s and reports only on timeout or diagnose. build compile skips the settings call and compiles for a booted or the newest iPhone simulator. One simulator lookup everywhere (booted first, newest OS, no prefix matches). Zero-match suggestions come from the last build. Agent JSON adds xcresult and a timeout reason. build run exits non-zero unless the app is running.

Bundle 9: run the app and see what happened

Before: a launch reported success even when the app crashed at startup. There was no way to pass launch arguments, environment or a deep link. build_run_sim could install an extension instead of the app (it took the last target listed) and launched before the simulator finished booting. CLI log start and console launch captures died with the command that started them, reads returned everything, wait_for_log cleared the buffer, and the crashes topic matched default-level lines. Auto-detect could pick the workspace inside .xcodeproj or the wrong scheme.

After: launches watch the app for 8s and report the crash (exception, reason, top frames, .ips path). args, env, url and terminate on launch_app / build_run_sim and the CLI; new open_url. build_run_sim boots after the build, waits for boot, and installs the scheme's application target. CLI captures run in the background and read the newest 200 lines by default; wait_for_log tracks a position. clean takes a configuration and can delete this project's DerivedData.

Bundle 10: UI driving that hits the right screen

Before: all simulators shared WDA port 8100, so with two booted the screenshot showed one and taps went to the other. A recreated WDA session relaunched the app silently, and restarting WDA fell through to a two-minute rebuild. Several fast paths read the whole Simulator app's macOS accessibility tree (menus and every simulator). Trees kept every wrapper and had no value/enabled/selected; lookups took the first of several matches; actions couldn't wait for a result; type_text typed into the first text field and echoed passwords. Screenshots reported the Simulator window size as points, came back at full resolution, and the fallback wrote one shared /tmp file. There were no tools for text size, locale, app containers, push or privacy, and the accessibility/localization checks reset other sessions' settings. Coordinate taps ignored landscape.

After: every UI tool takes simulator, and each simulator gets its own WDA port (XCFORGE_SIMULATOR for the CLI). Recreated sessions send forceAppLaunch: false; restart reruns the cached xctestrun. The accessibility-tree shortcut runs only with one simulator booted and no phone, scoped to its windows; get_source / list_elements start WDA instead of falling back. Trees drop empty wrappers and hidden/off-screen nodes and show value, disabled and selected. find_element lists every match and takes index; actions take wait_for, until_gone, timeout. type_text types into the focused field, with key, dismiss_keyboard and secure. alert_action / XCFORGE_ALERT_ACTION, and not-found errors mention a visible alert. Screenshots report device points and take crop and max_dimension, with unique file paths. New sim_content_size, sim_locale, app_container, sim_push, sim_privacy (and sim content-size|locale|container|push|privacy); setters report what they replaced, and the checks restore what they found. Coordinate taps read the interface orientation first.

Bundle 11: a faster inner loop

Before: checking one target meant building the whole scheme. A build ran against the live working tree, so edits made mid-build mixed into it. SourceKit-LSP in an editor knew nothing about xcforge's DerivedData. There was no -jobs control and xcodebuild stopped at the first failing target. The SwiftPM tools needed a path, returned raw output, and ignored the build lock and idle timeout.

After: build typecheck / build_typecheck compiles one target (its scheme if it has one, else -target). --from-snapshot builds a git-worktree snapshot of the working tree and maps errors back to the real paths. lsp setup / lsp_setup writes buildServer.json pointed at xcforge's DerivedData. jobs and allErrors (with its own DerivedData slot so it doesn't evict the normal one). SwiftPM tools find the package (path, yaml packagePath, cwd, or the only package in the repo), parse errors and test failures, and take the build lock and idle timeout.

Bundle 12: a smaller, consistent tool surface

Before: 126 tools listed, eight of them different ways to tap, plus separate tools for HID swipes, flat element lists, finding all matches and device screenshots. Argument names mixed snake_case and camelCase. The ten diagnose tools were always listed. Build tools returned long text over MCP. plan decide from the CLI couldn't resume a plan paused by an earlier plan run. defaults set could report success while saving nothing, and a saved simulator that had been deleted broke every call.

After: 104 tools listed (114 with diagnose). tap replaces the eight tap tools; swipe takes hid, get_source takes format: list, find_element takes all, screenshot takes device. The old names stay callable for one release and their results say what replaces them. Every listed argument is camelCase and snake_case is still accepted. The diagnose group starts off (XCFORGE_TOOL_GROUPS or yaml toolGroups pick the starting groups) and tool_groups changes send notifications/tools/list_changed. build_compile, build_sim, build_typecheck, test_sim and build_and_test return slim JSON by default (for: human for text). Read-only tools carry readOnlyHint. test_sim takes rerunFailed. Paused CLI plans are saved for an hour and plan decide resumes them. defaults set resolves the project first and fails when nothing was saved; a missing saved simulator is dropped with a warning. Last results are keyed on the canonical project path and written under a lock. xcforge --version; CLI JSON errors go to stdout.

Fixes from the real-Mac runs (Xcode 27.2, ShutterCoach)

Before: device list showed simulators as phones. build typecheck --target failed with a configured DerivedData (xcodebuild rejects -derivedDataPath with -target) and was labelled a compile error. Every hid: true tap fell back to WDA because Xcode 27 moved SimulatorKit. Crashes 2.6s and 5.5s after launch were reported as running, and late .ips files were never read. A relative --project broke lsp setup and --from-snapshot. The first WDA start gave up after about 80s and blamed a missing install. lock status listed queued xcforge processes as holders, a timed-out test run counted xcodebuild's own entries as failed tests, --json errors went to stderr as text, watchdog snapshots and result bundles piled up in /tmp, landscape screenshots reported portrait sizes, and set_orientation and most ui commands couldn't name a simulator.

After: simulators are dropped from device list and the state comes from tunnelState. -target builds write into the configured DerivedData through SYMROOT/OBJROOT, and usage errors are xcodebuild_usage. SimulatorKit is found in Contents/SharedFrameworks, and a WDA fallback says why HID wasn't used. Launches are watched for 8s (XCFORGE_LAUNCH_WATCH_SECONDS) and wait up to 15s for the report (XCFORGE_CRASH_REPORT_WAIT_SECONDS); an exit without one says when it died and where to look. --project is made absolute. The first WDA start gets 15 minutes to build and 180s to answer (XCFORGE_WDA_START_SECONDS), concurrent callers wait for it, and failures name the cause (such as an old Homebrew xcforgeWDA copy). Queued processes are left out of holders; timeouts aren't counted as failed tests; --json errors are a JSON envelope on stdout; unreported snapshots are deleted and xcforge's artifacts older than two days are pruned; landscape screenshots report landscape points; every ui command takes --simulator and set_orientation takes simulator. The snapshot worktree stays registered on purpose so snapshot builds are incremental; the docs say how to remove it.

Why

Bundles 6-12 of the dev loop plan (https://claude.ai/artifact/FKqRxvqGuk9xsYe11mpUEN and the #5 thread's audit): stranded calls, orphaned xcodebuild, build/test output Shuttercoach agents worked around with raw xcodebuild and xcresulttool, launches that hid crashes, UI calls that hit the wrong screen, slow inner loops, and a tool list too large and inconsistent for agents to use well. Justin's decisions applied: old tool names stay as aliases for one release, slim JSON is the MCP default (text stays for the CLI), and diagnose tools are in an off-by-default group.

How

Bundle 7: ProcessTree, ChildProcesses + signal handling in Main, ProgressHeartbeat, XcodebuildQueue, per-project yaml in SessionState.resolveProject, ToolRegistry.unknownArgumentsError.

Bundle 8: TestIDs, FailureMessage, TestFailureText; SchemeFile and TestPlanInspector.testTargetNames; TestRunOptions; xcresult node-tree parsing; LastFailuresStore run settings.

Bundle 6: TestTools.compileFlags/compileSettings; runBuildForTesting(testplan:targets:); AutoDetect.pickSimulator; HangWatchdog.defaultSampleAt; executeListTests(withoutBuilding:).

Bundle 9: AppLiveness and CrashReports (pid from simctl launch, newest .ips since launch); DetachedCapture (nohup'd capture writing to ~/.xcforge/capture); BuildTools.appProduct(fromSettings:) from -showBuildSettings -json; LogCapture.position.

Bundle 10: WDAPorts; UITarget (adds simulator to UI tool schemas, selects the WDA before dispatch) and UIWait (adds and runs the waits), both applied in ProviderRegistry; WDAClient.sessionCapabilities, startRunner with TEST_RUNNER_USE_PORT; UITools.axpTargetsSimulator; flattenWDASource pruning; ScreenshotShaping + ScreenInfoCache; SimSetupTools.swift; IndigoHIDClient.normalize(…orientation:).

Bundle 11: BuildTypecheck, BuildSnapshot (git worktree + path mapping), LSPSetup, XcodebuildOptions.jobs/allErrors, SwiftPM package discovery and output parsing in SwiftPackageProvider.

Bundle 12: ToolSurface (group specs, camelCase listing and argument normalising, deprecated aliases, read-only annotations), UITap, BuildAgentResult, PlanSessionFile, XCForgeVersion; ToolRegistry.declaredTool/onToolListChanged; LastResultStore.canonicalProject + flock.

Real-Mac fixes: DeviceTools.physicalEntry/isSimulatorEntry/connectionState; BuildTools.targetBuildRoots/xcodebuildUsageError; IndigoHIDClient.simulatorKitCandidates; AppLiveness.check polling with afterSeconds; SessionState.absolutePath; WDAClient.explainWDABuildFailure, runnerStartSeconds, deploy waiting; BuildLock.externalHolders; ArtifactPruning; ScreenshotShaping.orientedPointSize; UISimulatorTarget on ui commands; XCForgeCLI.runCLI + printJSONError. Tests in DeviceSupportTests and MacVerifyFixesTests.

Testing

  • swift test passes (CI)
  • swift build -c release compiles (CI)
  • New/changed tools have tests (AgentCallSafetyTests, TestResultsTests, BuildLoopTests, RunLoopTests, UIDrivingTests, ToolSurfaceTests, DeviceSupportTests with devicectl shapes captured from Xcode 27.2, MacVerifyFixesTests); a test checks the documented tool count and that every listed tool is named in the references
  • Skill references updated (shared-mac.md, test-tools.md, build-tools.md, spm-tools.md, simulator-tools.md, log-console.md, ui-automation.md, screenshot-visual.md, device-tools.md, plan-execution.md, auto-detection.md, cli-commands.md, new tool-surface.md)

Verified on a real Mac (Xcode 27.2, ShutterCoach):

  • device list shows only the phone, 27.2 [localNetwork, connected], no simulators
  • another session's lockf holder shows in lock status, and builds queue behind it without overlapping
  • cold compile with a custom DerivedData and --jobs 6 (0 errors); no-op rebuild about 32s of xcodebuild time; build compile straight after test run recompiles nothing
  • build typecheck on a target with its own scheme
  • filtered tests with a full ID and a short ID (target inferred, build reused)
  • a total timeout, SIGTERM to xcforge, and MCP notifications/cancelled each leave no xcodebuild behind
  • MCP progress heartbeat arrives; 104 tools listed, no snake_case args, unknown args error, tool_groups sends list_changed (114 tools)
  • lsp setup with an absolute path gives working jump-to-definition into ShutterCoachShared
  • --from-snapshot compile with an absolute path reports warnings at the real repo paths
  • launch_app args and env reach the app
  • WDA on its own port for the booted simulator; landscape tree and a landscape tap via WDA land correctly
  • log start keeps capturing after the shell exits

Fixed after that run, waiting on a re-check: typecheck --target with a custom DerivedData, HID taps on Xcode 27 (and the landscape HID mapping), crashes 2-6s after launch with late .ips files, relative --project, first WDA start, and the smaller items above.

Still needs a real-Mac check:

  • wda start on a phone;
  • two booted simulators each with their own WDA, a screenshot plus tap with simulator: hitting the same one;
  • retries: 2 on a flaky test lists it under Flaky;
  • build-for-testing -only-testing: builds only the named target (ShutterCoach has one test target, so it couldn't tell);
  • sim_locale and sim_privacy take effect after a relaunch;
  • plan run that pauses, then plan decide in a new shell;
  • build typecheck on a framework target with no scheme; --from-snapshot error paths (only warnings were seen);
  • a recreated session with forceAppLaunch: false leaves the app's screen in place; WDA restart without a rebuild;
  • devicectl screenshot, payload-url and env flags.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N

claude added 6 commits October 9, 2026 14:12
Route every xcodebuild call through one entry point that applies
per-call options (DerivedData path, exact result bundle, extra args),
holds an optional first-come, first-served build lock compatible with
lockf(1), and warns on low disk or heavy swap.

- simRecovery defaults to off; auto only reboots, erase must be asked for
- never pkill every xcodebuild; hang snapshots match this run's bundle
- WDA port cleanup only kills WDA runners on the configured local port
- collision-free result bundle names; per-project last-result records
  used by build diagnose, test failures and test coverage
- --isolated-sim runs on a throwaway simulator
- xcforge lock status / build_lock_status

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NVNakyntY4u9MvhsjbzRhZ
…sting tests

Adds an idle timeout (600s of no output) to every build and test xcodebuild,
raises the total limit to 1800s, retries test-without-building once when the
runner never starts, continues building after errors and reads diagnostics
from stdout, lists tests through JSON enumeration (Swift Testing included,
with --testplan), and fixes test plan discovery and decoding.

Also fixes the Bundle 1 test that failed to type-check and a lint nit.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NVNakyntY4u9MvhsjbzRhZ
…ice Hub

Adds wda start/status/stop (CLI and MCP) to build, sign, launch and reach
xcforgeWDA on a device, device screenshots, deep links and env vars on device
launch, clear messages for locked devices, UI Automation off and signing
failures, and stops simulator self-heal from running against device targets.
Opens Device Hub when Simulator.app is gone and accepts both apps in capture
and accessibility code. Raises xcforgeWDA's deployment target to iOS 15.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NVNakyntY4u9MvhsjbzRhZ
Auto-promotion becomes opt-in, xcforge's own build flags can be turned off,
lldb continue and simulator launch timeouts are longer and configurable,
bless stops suggesting commit messages, and tool text and the skill lose
preference claims and marketing copy. Fixes stale paths in CLAUDE.md.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NVNakyntY4u9MvhsjbzRhZ
Suites that stub WDA's localhost URL ran in parallel and could answer each
other's requests. A shared async gate now holds each stubbed region.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NVNakyntY4u9MvhsjbzRhZ
- Cancelling an MCP call, a timeout, or xcforge exiting now kills the whole
  child process tree, so xcodebuild no longer keeps DerivedData locked.
- MCP calls with a progress token get a notification every 10s with the
  latest output line, so clients don't time out long builds.
- xcodebuild calls on the same DerivedData or project queue in-process.
- An explicit project uses its own repo's .xcforge.yaml; results name the
  project used; yaml quotes and comments are handled and problems warned.
- Unknown MCP arguments are rejected with the closest real name.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
xcforge-web dee6828 Oct 09 2026, 09:38 PM

@justinthevoid
justinthevoid marked this pull request as ready for review October 9, 2026 15:44
Copilot AI balanced review requested due to automatic review settings October 9, 2026 15:44
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

- One test ID format (Target/Suite/test()) in results, list_tests,
  last-failures and known-failures; filters accept it as printed, and
  short IDs get the target from the scheme or test plan
- Every failure message with file and line, argument/repetition label,
  attachments and optional console tail; identical messages grouped,
  text output capped with a count of the rest
- Run options on test_sim/build_and_test and the CLI: retries,
  iterations, until-failure, parallel, per-test timeout, skip build
- Flaky tests (failed then passed on retry) reported
- rerun-failed reuses recorded run settings, skips the build when no
  source changed, and refuses when the last run failed before any test
- Test failures are no longer reported as a test target build failure;
  agent JSON gives a reason when no test reported

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
@justinthevoid justinthevoid changed the title feat: calls that don't strand agents (dev loop bundle 7) feat: dev loop bundles 7-8 (calls that don't strand agents, test results agents can act on) Oct 9, 2026
- CLI test run shows a failed test build as compile errors with file
  and line; test_sim uses the parsed build errors too
- test failures never runs tests: it reads the last test run, says when
  there is none, and returns compile errors when that run's
  build-for-testing failed instead of an older run's failures
- build-for-testing passes the test plan and builds only the targets a
  filter names; the last-build record tracks plan and targets
- build compile, build-for-testing, test and test list share build flags
- hang watchdog samples at 300s/540s and only reports on timeout or
  diagnose
- build compile skips -showBuildSettings and compiles without a booted
  simulator
- one simulator name lookup everywhere (booted first, newest OS, no
  prefix matches)
- a zero-match filter lists suggestions without rebuilding
- agent JSON carries the result bundle and the timeout reason
- build run exits non-zero when the app didn't launch

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
@justinthevoid justinthevoid changed the title feat: dev loop bundles 7-8 (calls that don't strand agents, test results agents can act on) feat: dev loop bundles 6-8 (reporting fixes, calls that don't strand agents, test results agents can act on) Oct 9, 2026
claude added 3 commits October 9, 2026 16:15
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
- launch_app, build_run_sim, sim launch and build run check the app is
  still running 2s after launch and report the crash (exception, reason,
  top frames, .ips path) when it isn't
- Launch args, env, deep link and terminate options; new open_url and
  sim openurl
- build_run_sim boots after a successful build, waits for boot, and
  installs the scheme's application target from -showBuildSettings -json
- CLI log and console capture run in the background and survive the
  start command; reads default to the newest 200 lines and say what was
  left out; wait_for_log no longer clears the buffer
- crashes topic matches error/fault lines instead of default-level ones
- Auto-detect ignores the workspace inside .xcodeproj and picks the
  project-named scheme; clean takes a configuration and can delete this
  project's DerivedData
- A new process loads the last build's bundle ID and app path

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
- Every UI tool takes simulator; each simulator gets its own WDA port
  (saved in ~/.xcforge/wda/simulator-ports.json), XCFORGE_SIMULATOR for
  the CLI
- Recreated WDA sessions send forceAppLaunch:false; restart reruns the
  cached xctestrun with test-without-building and the runner has no
  one-hour cap
- The AXP fast path runs only with one simulator booted and no phone,
  reads that simulator's windows, and get_source/list_elements start WDA
  instead of falling back to Simulator.app
- Smaller trees: empty wrappers, hidden and off-screen nodes dropped;
  value, disabled and selected shown
- find_element reports every match and takes index/timeout/until_gone;
  actions take wait_for, until_gone and timeout
- type_text types into the focused field, with key, dismiss_keyboard
  and secure (redacted) options
- alert_action / XCFORGE_ALERT_ACTION and a visible-alert hint in
  not-found errors
- Screenshots report device points, take crop and max_dimension, and
  never share a fixed /tmp file
- sim_content_size, sim_locale, app_container, sim_push, sim_privacy
  (and sim content-size|locale|container|push|privacy)
- Accessibility, localization and multi-device checks restore the text
  size, language and appearance they found
- Coordinate taps and swipes account for interface orientation

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
@justinthevoid justinthevoid changed the title feat: dev loop bundles 6-8 (reporting fixes, calls that don't strand agents, test results agents can act on) feat: dev loop bundles 6-10 (reporting, safe calls, test results, run loop, UI driving) Oct 9, 2026
claude added 3 commits October 9, 2026 17:00
- build typecheck / build_typecheck compiles one target (its scheme, else -target)
- --from-snapshot builds a git-worktree snapshot of the working tree; errors map back
- lsp setup / lsp_setup writes buildServer.json pointed at xcforge's DerivedData
- --jobs, --all-errors with a separate diagnostic DerivedData slot
- SwiftPM tools find the package (path, yaml packagePath, cwd, only package in repo),
  parse errors and test failures, and take the build lock and idle timeout

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
claude added 2 commits October 9, 2026 17:11
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
- tap replaces eight tap tools; swipe takes hid, get_source takes
  format: list, find_element takes all, screenshot takes device. Old
  names stay callable for one release with a note naming the replacement
- The diagnose group starts off; XCFORGE_TOOL_GROUPS / yaml toolGroups
  pick the starting groups, and tool_groups changes send
  notifications/tools/list_changed
- Every listed argument is camelCase; snake_case is still accepted
- build_compile, build_sim, build_typecheck, test_sim and build_and_test
  return compact JSON by default over MCP (for: human for text);
  diagnose and plan JSON are compact
- Read-only tools carry readOnlyHint
- test_sim rerunFailed
- plan decide resumes from a saved session in a new CLI process
- defaults set finds the project first and fails when nothing is saved;
  a saved simulator that no longer exists is ignored
- Last results keyed on the canonical project path, written under a lock
- xcforge --version; CLI JSON errors on stdout
- Docs: tool-surface reference, counts checked by a test

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
@justinthevoid justinthevoid changed the title feat: dev loop bundles 6-10 (reporting, safe calls, test results, run loop, UI driving) feat: dev loop bundles 6-12 (reporting, safe calls, test results, run loop, UI driving, inner loop, tool surface) Oct 9, 2026
claude added 4 commits October 9, 2026 17:24
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
Device Hub's devicectl lists simulators alongside phones. Drop entries
that report a non-physical reality, a simulator platform or device type,
or a UDID simctl knows, and read the state from the tunnel instead of
visibilityClass.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
properties groups fields as hardware, state and connection; simulators
report reality: simulated, visibilityClass: simulators and a CoreSimulator
provider. Tests use shapes captured from Xcode 27.2.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
- typecheck --target builds into the configured DerivedData via SYMROOT/OBJROOT;
  xcodebuild usage errors are reported as xcodebuild_usage
- HID input finds SimulatorKit in Xcode 27's SharedFrameworks; WDA fallbacks say why
- launches are watched 8s and wait up to 15s for a late crash report
- relative --project paths resolve against the working directory
- first WDA start gets time to build and start, and failures name the cause
- lock status skips queued xcforge processes; timed-out test runs aren't
  miscounted; unreported snapshots and old artifacts are cleaned up
- landscape screenshot sizes, --simulator on every ui command, JSON errors
  with --json, bless help text

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
claude added 8 commits October 9, 2026 19:04
The stub answered every loopback request, so another suite's ungated
createSession against 127.0.0.1:1 could take the queued POST /session
response and fail the healthy-path test. It now intercepts port 8100 only.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
--help, help <command> and --version end in a parser error with a success
exit code; the JSON error path wrapped it as execution_failed and exited 1
whenever output was piped.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
On a loaded runner the 10ms deadline's capture hadn't finished after 200ms,
so the result and file were missing.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
lookup:error: returns a mach_port_t; calling it through perform() treated
the port as an object and crashed ui tap. Call it by its real signature,
and return an error for an unexpected one. Screenshot point sizes now
follow the interface orientation from WDA, since the framebuffer stays
portrait. Document that the lock queue orders xcforge waiters only.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
wda start spots WDA's "Unable to start web server" log line and fails at
once with a --port hint, reports errors as JSON under --json, and builds
into the configured DerivedData folder. Starting or restarting the
simulator runner stops any xcforgeWDA runner already on that simulator.
ui source accepts --format list and rejects unknown formats up front.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
On Xcode 27 IndigoHID events are sent without error but never reach the
app, so results said "via IndigoHID" for taps that didn't land. HID is
now off on Xcode 27 (XCFORGE_FORCE_HID=1 overrides) and taps and swipes
use WDA, saying so in the result. Add iPhone 16 Pro, 17 family and Air
screen sizes to the HID table.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
Starting WDA ran xcodebuild against the target simulator, which boots it
when it is shut down; with another simulator already booted that left two
running, which exhausts memory on a 16 GB Mac. UI commands now fail with
"Simulator <name> is shutdown; boot it first" and name any booted one.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
@justinthevoid
justinthevoid merged commit 84ae6fd into main Oct 9, 2026
6 checks passed
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.

3 participants