Ten days ago we published the 200-run v3 unseen-task rematch of mobile-agent-bench (REPORT.md). On the 7-task UI and logcat phone subset (35 runs per stack), Callstack's agent-device 0.21.27 matched our linc column at 34/35 (97.1%). On the full 9-task phone grid (45 runs per stack), linc scored 44/45 (97.8%) against 34/45 (75.6%) for agent-device, 20/45 (44.4%) for @mobilenext/mobile-mcp 1.0.10, and 14/45 (31.1%) for maestro 2.11.0.
Every single run of that 10-run gap came from two tasks (c10 and c11). And at the time we published v3, a fair critic had a one-sentence objection: only linc-nerve 0.1.0 (LincForge/nerve@5477863) was on PyPI. The other two packages in the linc column (crucible and spectra) were still in private repos, with only their raw transcripts published.
Today both are public under Apache-2.0:
linc-spectra 0.1.0on PyPI andgithub.com/LincForge/spectra(CHANGELOG.md)linc-crucible 0.2.0on PyPI andgithub.com/LincForge/crucible(CHANGELOG.md)linc-cli-kit 0.1.4on PyPI andgithub.com/LincForge/cli-kit(addingstrict_toolsschema enforcement across the stack)
First, the reproducibility caveat
Per PREREGISTRATION-v3.md, the v3 grid scored a frozen column: linc-nerve==0.1.0 from PyPI paired with pre-release commits crucible@cf04279 and spectra@c471dad.
Those pre-release commits are not the release tags we shipped today (linc-crucible 0.2.0 and linc-spectra 0.1.0). Between the v3 freeze and today's release, we fixed defects that v3 itself exposed and hardened both packages for outside machines. We have not re-run the 200-cell v3 grid against the new wheels, so we do not claim the released versions reproduce the v3 numbers without a fresh run.
linc-spectra 0.1.0: runtime JDWP and LLDB inspection for agents
UI trees and logcat only show what an app renders or logs. When a bug lives inside a local variable or a caught exception that prints a generic banner, an agent without a debugger is stuck guessing or curve-fitting from the screen.
linc-spectra exposes 14 MCP tools across two debugging lanes:
- Zero-config Android
jdb(spectra_method_breakpoint,spectra_method_locals,spectra_exception_breakpoint,spectra_exception_info): Breaks on a method or caught exception by its fully-qualified JVM name (com.example.app.CheckoutViewModel.applyDiscount) with zero local source files required. It needs onlyadb, a debuggable APK, andjdbfrom any standard JDK. - Full DAP (
spectra_attach,spectra_breakpoint,spectra_continue,spectra_step,spectra_stacktrace,spectra_variables,spectra_evaluate): Connects tokotlin-debug-adapterover Android JDWP or Xcode'slldb-dapon booted iOS Simulators when you have local source files and want line breakpoints or frame expression evaluation.
You can check your local setup in one command without cloning a repo:
uvx linc-spectra doctor
claude mcp add linc-spectra -- spectra bellows
spectra doctor probes adb, kotlin-debug-adapter, jdb, lldb-dap, xcrun, and Python (>= 3.13). It prints a hint: line for any missing tool and exits 0 when at least one debugger lane is ready.
Fixing the 10 ANR dialogs v3 caught in spectra
In v3, linc went 10/10 across c10 (results_v3/linc/c10) and c11 (results_v3/linc/c11), reading discountFactor (0.755) in 100.2s median on c10 and auditToken (PROMO-BENCH26-7956) plus rebateCents (1355) in 119.5s median on c11.
But as we documented in docs/v3-deep-dive.md, all 10 of those passing runs hit an Android "Application Not Responding" (ANR) dialog along the way.
Here is why. The agent armed spectra_method_breakpoint on a ViewModel method triggered by onClick, then called nerve_tap to click the button. When the breakpoint fired inside onClick, jdb suspended the app's main UI thread while adb shell input tap was still waiting for the input event to finish. Every run hit a 10-second nerve_tap read timeout and triggered Android's 5-second ANR dialog before the agent called spectra_method_locals to resume the process.
In linc-spectra 0.1.0, spectra_method_breakpoint now runs an auto-capture collector when the breakpoint hits. It steps through the method, snapshots the local variables into the session cache for spectra_method_locals, and resumes the app within a hard deadline before Android's input dispatcher can trip the 5-second ANR timer. We also made spectra_detach idempotent on auto-captured sessions and added device= as a parameter alias for device_serial across spectra_attach, spectra_method_breakpoint, and spectra_exception_breakpoint.
linc-crucible 0.2.0: mobile ship gates with zero device code
While linc-nerve handles raw device I/O and linc-spectra inspects runtime state, linc-crucible answers one question before a build leaves the bench: is this app ready to ship?
linc-crucible 0.2.0 (0.1.0 was our internal baseline and was never published to PyPI) exposes 8 MCP tools and matching CLI commands:
- Quality scoring (
crucible_assay,crucible_score): Runs project lint, unit tests, optional MobSF security scans, static accessibility checks, and live accessibility-tree checks, then computes a 7-dimension weighted composite score (0-100) with a ship gate at85. - Build and multi-target routing (
crucible_pour): Builds Android (Gradle) and iOS (xcodebuild) artifacts and routes phone and watch modules to the right physical device or simulator through the localnerveHTTP daemon (127.0.0.1:8111). Crucible itself contains zero device code. - Visual and accessibility baselines (
crucible_bvt,crucible_baseline): Executes YAML Build Verification Test (BVT) flows on devices, comparing screenshots via Structural Similarity Index (SSIM,0.95pass threshold,0.70fail floor) and diffing live accessibility trees against 6 WCAG rules (including48dpAndroid and44ptiOS touch targets). - App-store readiness (
crucible_preflight,crucible_extract_assets): Evaluates 5 store-readiness dimensions against an80%threshold and extracts deduplicated marketing screenshots from device video recordings.
Quickstart on a fresh machine (the first four commands need no daemon, device, or Docker):
pipx install linc-crucible
crucible fuelcheck
crucible assay --project myapp
crucible score myapp
crucible preflight --project myapp
Privacy and trust boundaries by default
Before publishing either package, we ran the same stranger-machine and adversarial security gates we used for linc-nerve (spectra/SECURITY.md, crucible/SECURITY.md):
- No telemetry or phone-home: Both tools run locally over stdio MCP and talk only to your local toolchains (
adb,jdb,kotlin-debug-adapter,xcrun,lldb-dap, and your localnervedaemon). - Redacted-by-default local usage logs (
0600permissions,O_NOFOLLOW): Both tools log MCP calls to$LINC_HOME/mcp-calls.jsonlwithSPECTRA_USAGE_LOG=redactedandCRUCIBLE_USAGE_LOG=redactedby default. Sensitive fields (expression,source_path, device serials, host paths, and credentials) are replaced with<redacted len=N>placeholders and scrubbed from error strings. Setting=offwrites nothing to disk. - Strict input validation (
shell=False): Every package name, bundle ID, simulator UDID, and JVM class or method identifier is validated withre.fullmatchbefore reachingadb,xcrun, orjdb, and every MCP server usesstrict_toolsfromlinc-cli-kit 0.1.4to reject unknown arguments.
Inspect the stack
All three tools in the linc benchmark column are now public on GitHub and PyPI, and the updated catalog and llms.txt are live on lincforge.com:
- Device control (
48MCP tools):github.com/LincForge/nerve(uvx linc-nerve doctor) - Runtime debugger (
14MCP tools):github.com/LincForge/spectra(uvx linc-spectra doctor) - Quality and ship gates (
8MCP tools):github.com/LincForge/crucible(pipx install linc-crucible && crucible fuelcheck) - Benchmark harness and
600run transcripts:github.com/LincForge/mobile-agent-bench