English · 日本語
Which module does what, where it depends, and which features described in the design (
DESIGN.md) are not yet wired up in the current code.
Related: concepts · the per-feature pages (linked below)
A scenario (authored by AI or by hand) is the shared artifact. run replays it deterministically with no AI in the gate. codegen and triage also consume the scenario.
Tier 1 (AI — yellow) authors and investigates only; Tier 2 (deterministic — blue) decides pass/fail from machine assertions alone.
The whole spine is platform-neutral; the only platform-specific seam is the backend the orchestrator drives (idb / XCUITest for iOS, adb for Android, playwright for web, … behind one Driver interface), so a new platform is a new backend, not a fork of the core.
Mermaid source
flowchart TB
goal(["🗣️ Natural-language goal"])
hand(["✍️ Hand-edited"])
scenario[["📄 Scenario (YAML)"]]
subgraph tier1["Tier 1 · AI — author and failure investigator"]
record["record / crawl<br/>explore + author"]
agent["Claude agent<br/>+ system-alert guard"]
record <--> agent
end
subgraph tier2["Tier 2 · Deterministic run — no AI in the CI gate"]
orch["Orchestrator<br/>observe → act → verify"]
driver["Backend-agnostic Driver API<br/>tap · type · swipe · wait · query · screenshot"]
idb["idb backend<br/>📱 iOS Simulator (simctl)"]
xcuitest["XCUITest backend<br/>📱 iOS (resident runner)"]
adb["adb backend<br/>🤖 Android"]
pw["playwright backend<br/>🌐 web browser"]
orch --> driver
driver --> idb
driver --> xcuitest
driver --> adb
driver --> pw
end
verdict{"Pass / Fail<br/>machine assertions only"}
report["📊 Reporter<br/>manifest.json · JUnit · CTRF · HTML"]
codegen["codegen<br/>→ XCUITest / Playwright / UI Automator"]
triage["triage<br/>root cause + fixes · advisory"]
goal --> record
record ==> scenario
hand ==> scenario
scenario ==> orch
scenario -.-> codegen
orch --> verdict
orch --> report
verdict -->|fail| triage
triage -.->|suggest edits| scenario
classDef ai fill:#fde68a,stroke:#d97706,color:#1f2937;
classDef det fill:#bfdbfe,stroke:#2563eb,color:#1f2937;
class tier1 ai
class tier2 det
The dependency-layer view below is the same system seen as module layers rather than data flow.
The bajutsu/ package (Python 3.13+, pydantic v2 / typer / anthropic / pyyaml / jinja2).
| Module | Role | Page |
|---|---|---|
drivers/base.py |
Driver Protocol + shared types (Element/Selector/Point) + selector resolution (the determinism core) |
selectors / drivers |
drivers/coordinate_tree.py |
CoordinateTreeDriver — the shared transient-empty retry / stable-key settle / _resolve / wait_for base class the two coordinate backends (idb, adb) inherit (BE-0254) |
drivers |
drivers/fake.py |
In-memory FakeDriver (for tests without a device) |
drivers |
drivers/idb.py |
idb backend (iOS Simulator; headless, coordinate tap) | drivers |
drivers/xcuitest.py |
XCUITest backend (iOS; ahead of idb in the stability-order ladder — semantic tap, native condition-wait, and multi-touch via a resident on-device runner, idb the headless fallback; BE-0019) | drivers |
drivers/adb.py |
adb backend (Android; uiautomator dump frame-center coordinate tap, the idb-equivalent second platform) |
drivers |
drivers/playwright.py |
Playwright web backend (browser; first slice — deterministic run) | drivers |
scenario/ |
Scenario schema (strict pydantic validation) + YAML load / dump (package: models / load / expand / select / serialize) |
scenarios |
assertions.py |
Machine assertion evaluation (total function — never raises) | selectors |
orchestrator/ |
The deterministic Tier 2 run loop (act → wait → verify) (package: loop / waits / substitution / evidence_rules / actions) |
run-loop |
evidence.py |
Evidence capture (instant / interval) and Sinks | evidence |
intervals.py |
Interval evidence (video / deviceLog) as simctl child processes | evidence |
report/ |
manifest.json + JUnit XML + CTRF JSON + interactive HTML (package: format / manifest / ctrf / rows / panels / html) |
reporting |
network.py |
Network collector + in-protocol deterministic mocks | evidence |
redaction.py |
Redaction of evidence (labels / headers / fields + secret values) | evidence |
interp.py |
${ns.key} interpolation primitive (params. / row. / secrets. / vars.) |
scenarios |
config/ |
Team defaults × per-target resolution (Effective) (package: schema / effective / resolve / accessors) |
configuration |
backends.py |
Backend availability check · actuator selection (platform-aware registry: ios / android / web / fake) · driver construction |
drivers |
simctl.py |
simctl wrapper (erase/boot/launch/openurl/io) |
drivers |
preflight.py |
Runnability gate, per backend (iOS: required CLIs + a booted Simulator; web: Playwright + its Chromium browser) | configuration |
requirements.py |
One declarative mapping: backend/capability → pip extra + external-tool probe + install method (BE-0164), shared by preflight and provision |
— |
provision.py |
Config-aware environment installer (BE-0164): resolve a config's backends + AI provider, install only their extras/tools idempotently (make install) |
— |
runner/ |
config + scenarios → report; device pool + launch sequence (package: pipeline / pool / launch) |
run-loop |
doctor.py |
Convention score (id coverage, etc.) | configuration |
agent_protocols.py · agent_factory.py |
Authoring Agent abstraction (Observation/Proposal/Agent) + construction of the one SDK-backed agent |
recording |
ai/ |
Vendor-neutral AI backend seam (BE-0104): AiBackend protocol + normalized request/response types (base), provider registry (registry), Anthropic reference adapter over anthropic_client (anthropic) — the Anthropic API, Amazon Bedrock, and the Anthropic CLI ant (BE-0163) |
configuration |
claude_agent.py |
The SDK authoring agent (forced tool use · prompt cache); provider-agnostic — Anthropic API / Bedrock / ant |
recording |
record.py |
The record loop (observe → propose → execute → emit) | recording |
crawl/ |
Autonomous breadth-first crawl → screen map: core engine + serialize, with guide / tabs / report / repro / flows |
recording |
alerts.py |
System-alert detection / dismissal (vision locator) | recording |
codegen/ |
Scenario → native test generation: XCUITest (Swift), Playwright (TypeScript), UI Automator (Kotlin) | codegen |
visual.py |
Visual-regression image comparison (the visual assertion) |
evidence |
trace.py |
Text timeline over a saved run (the trace command) |
cli |
triage.py |
M4 self-heal: rule-based HeuristicTriageAgent + structured fixes (renameId/addIndex/raiseTimeout), --apply/--write/--rerun |
cli |
claude_triage.py |
Claude-backed TriageAgent (--ai, failure screenshot) |
cli |
github.py |
GitHub helpers (CI, continuous integration) | ci |
serve/ |
Local web UI (the serve command): author / run / reports / triage a failed run |
cli |
mcp/ |
MCP server: exposes run/doctor as tools + run evidence as resources |
cli |
lint.py |
Scenario linter + JSON Schema generation (lint / schema commands) |
cli |
audit.py · coverage.py · stats.py · serve/flakiness.py |
Read-only advisory analysis, no device/AI, never gates CI: determinism/flakiness audit (audit, BE-0049), scenario id-namespace coverage (coverage, BE-0050), the aggregate run-stats dashboard (stats, BE-0102), cross-run flakiness ranking (flakiness, BE-0220) |
cli |
cli/ |
Typer-based CLI; one file per command in cli/commands/ (run/project/doctor/audit/coverage/stats/flakiness/export/trace/report/triage/record/crawl/codegen/approve/serve/mcp/worker/lint/schema) |
cli |
dotenv.py |
Minimal .env loader (never overrides an existing var) |
cli |
_yaml.py |
YAML loader that keeps on/off/yes/no as strings |
scenarios |
Lower layers are more stable; upper layers depend on lower ones. The core is drivers/base.py
(selector resolution), which every execution path depends on.
Mermaid source
flowchart TB
cli["cli/<br/>user entry (Typer): run · project · doctor · audit · coverage · stats ·<br/>flakiness · export · trace · report · triage · record · crawl · codegen ·<br/>approve · serve · mcp · worker · lint · schema"]
runner["runner/"]
record["record.py / crawl/<br/>(Tier 1 / AI)"]
codegen["codegen/<br/>(structural)"]
trace["trace.py<br/>(timeline)"]
triage["triage.py / claude_triage.py<br/>(self-heal · advisory)"]
orch["orchestrator/"]
agentStuff["agent_protocols.py / agent_factory.py /<br/>claude_agent.py / alerts.py"]
serveGh["serve/ · github.py<br/>(web UI · CI)"]
assertions["assertions.py"]
evidence["evidence.py<br/>+ intervals.py · network.py · visual.py · redaction.py"]
scenario["scenario/<br/>(interp.py)"]
report["report/"]
config["config/ · preflight.py"]
backends["backends.py"]
simctl["simctl.py"]
base["drivers/base.py<br/>the determinism core (Element / Selector / resolve_unique)"]
fake["drivers/fake"]
ios["drivers/idb · xcuitest · adb"]
pw["drivers/playwright"]
cli --> runner
cli --> record
cli --> codegen
cli --> trace
cli --> triage
runner --> orch
record --> agentStuff
triage --> serveGh
orch --> assertions
orch --> evidence
agentStuff --> assertions
assertions --> scenario
evidence --> report
orch --> config
orch --> backends
orch --> simctl
scenario --> base
report --> base
config --> base
backends --> base
simctl --> base
base --> fake
base --> ios
base --> pw
orchestrator/depends only onbase.Driverand is not coupled to any concrete driver. That is why it can be tested withFakeDriverwithout a device, while in production the same loop drives idb (iOS) or playwright (web).runner/provides the factory that launches the app and returns a ready driver, decoupling the loop from a real device.scenario/(the pydantic authoring model) anddrivers/base.py(the runtime TypedDict) are different things.Selector.as_selector()converts the former to the latter.
The layering above is not only a convention — it is an executable contract in the gate.
make lint-imports (part of make check, and a CI step) runs import-linter
against the declared layers, so a forbidden import fails the gate instead of surviving until someone
notices. The configuration lives in [tool.importlinter] in pyproject.toml. Three layers are
declared:
- Deterministic core — the path that derives a verdict and evidence with no model and no
periphery stack:
orchestrator/,runner/,drivers/base.py,assertions.py,evidence.py,report/,config/,scenario/,preflight.py/capability_preflight.py/capabilities.py,doctor.py,lint.py. It carries the prime directives. - Contract — the stable surfaces a consumer depends on: the scenario schema (
scenario/) and theDriverProtocol (drivers/base.py). - Periphery — the consumers of the contract, each removable behind an optional extra:
serve/,mcp/, the codegen emitters, the AI / agent paths (agent_protocols.py,ai_config.py,anthropic_client.py,record.py,enrich.py,triage.py,crawl/guide.py, …), and thegithub.py/notify.py/alerts.pyhelpers.
Three contracts are enforced:
- The deterministic core must not import the periphery. This is prime directives #1 and #3 as a
static contract: the verdict/evidence path stays free of the serve, AI and codegen stacks, and
cannot silently grow a dependency on them. A pure element-tree helper a core module needs (e.g.
screen_size_from_elements,shows_app_ui) lives in the core (bajutsu/elements.py), not in a periphery module such asrecord.py; likewise the resolvedaiblock (AiConfig) lives inconfig/, so the core reads it without importing the AI client. - The core must stay host-agnostic (BE-0129). Multi-tenant hosting concerns — organizations,
roles, tenancy — and the
db(SQLAlchemy/Alembic/psycopg/cryptography) andoauth(Authlib) extras belong tobajutsu/serve/alone. The org model (OrgConfig,org_for_*,targets_for_org,load_serve_config) lives inbajutsu/serve/orgs.py, notconfig/;Configcarries noorgsfield, and the core loader drops a top-levelorgs:before validation so a run in the hosted topology (which reads an org-bearing config) keeps working while the core never models orgs. The same mechanism also drops a top-levelui:key (BE-0191) — the serve UI's presentation settings (ui.default_theme) are a serve concern and are parsed inbajutsu/serve/themes.py, not modeled inConfig. A forbidden import-linter contract keepsconfig/,drivers/,runner/, andscenario/off those extras (include_external_packageslets it see the external import), on top of the periphery contract that already keeps them offbajutsu.serve. - The scenario schema and
DriverProtocol stay a portable inner contract — independent of the runtime core (orchestrator/,runner/,config/, …) as well as the periphery. This keeps the contract a stable layer a consumer can depend on without pulling the runtime, underpinning cross-version schema reads (BE-0119) and any future split of the periphery from the core.
The check is static analysis on the import graph — no model, nothing on the run / CI verdict path
beyond a deterministic pass/fail. When a new module is added, its layer decides where it belongs: if
it is on the verdict/evidence path it is core and must not reach the periphery; if it consumes the
contract it is periphery and belongs behind an extra.
tests/ holds the unit-test suite (uv run pytest -q). None require a real Simulator: command
builders are verified as pure functions, and execution paths are tested with FakeDriver /
injected runners (RunFn · Spawn · Clock). Real-device E2E against the showcase app is
make -C demos/showcase run-swiftui / make -C demos/showcase ui-test (showcase).
Prime directive #3 says every backend sits behind one Driver interface, so the determinism-core
invariants must hold identically on all of them. Per-backend tests alone cannot guarantee that: a
backend that tapped the first match on an ambiguous selector, or returned success on a zero-match,
would pass its own tests and fail no shared one. The driver conformance suite closes that gap —
one executable contract (a TCK, a technology compatibility kit) that runs the same test body
against every backend, driving the real driver instance (including code that bypasses
drivers/base), not the shared base alone.
The contract (tests/driver_conformance.py) is the "done" definition a new backend meets:
- an ambiguous selector (2+ matches) fails rather than acting on the first match;
- a zero-match selector fails rather than reporting success;
- selector failures share one error type (
SelectorError), uniform across backends; - a unique match acts without error, and
query()reports the on-screen elements; capabilities()matches observed behavior — theQUERY/ELEMENTSbaseline is declared, and multi-touch gestures work exactly whenMULTI_TOUCHis declared (else raiseUnsupportedAction);wait_foris a single-shot check of the current screen, with the sharedwait_untilloop turning it into a condition wait with no fixed sleep.
To add a backend to the suite, implement a ConformanceHarness (given a screen, return a driver
showing it) and subclass DriverConformanceContract; pytest then runs the inherited contract
against it. FakeDriver runs on the fast Linux gate (make check); Playwright runs in the web CI
job, idb / XCUITest under the iOS on-device E2E path (ios-e2e.yml), and the adb backend on a
booted Android emulator (android-e2e.yml's conformance (adb) job, BE-0270) — the same contract,
no second spec. Each harness realizes a screen its own way: FakeDriver takes the elements directly,
Playwright renders them as HTML, and the on-device harnesses launch the showcase app into conformance
mode once (SHOWCASE_CONFORMANCE) and then reseed each screen — so the real backend query and act
code is exercised, not the shared base alone. The iOS harness reseeds by writing a spec file the app
polls (conformance-spec.txt in its Documents directory): a file write rather than a per-screen
relaunch or deeplink, because simctl openurl raises iOS's "Open in app?" dialog and relaunching
per screen crashes the resident XCUITest runner after a handful of app.launch() cycles. The adb
harness instead re-launches the app's singleTask activity with a new SHOWCASE_CONFORMANCE intent
extra, delivered via onNewIntent — adb push cannot reach the app sandbox, and the intent reuses
the launchEnv→intent-extras convention (BE-0007); it is scoped to the Compose toolkit, the one that
can render a spec-driven arbitrary-id screen (testTag takes any runtime string, while a Views
resource-id must be a compile-time R entry). The suite carries an ondevice pytest marker
(deselected by the gate's default) so it never runs in make check, and runs serially on a single
device (the shared device is reseeded via one channel, so parallel workers would collide).
The design (
DESIGN.md) also includes the future vision. Here we separate what the current code actually runs from what is not yet wired up.
- Selector resolution and ambiguity detection (the determinism core)
- Platform-aware backend registry:
--backend/backend:acceptios/android/web/faketokens, each expanding to its actuators (backends.py) —iosexpands toxcuitestandidb. A multi-actuator platform is resolved per scenario in cost order (BE-0240): each scenario runs on the cheapest actuator its own steps can use, escalating only where a construct needs a capability the cheap one lacks - The XCUITest backend (
drivers/xcuitest.py): the richer iOS actuator — a resident on-device runner (BajutsuKit) driven over a loopback HTTP channel, adding semantic (identifier) tap, a native condition-wait, and thepinch/rotatemulti-touch gestures idb cannot perform (UnsupportedAction). idb is the cheap per-scenario default (no Xcode toolchain, no resident runner); a scenario escalates to XCUITest only when its steps need a capability idb lacks (BE-0240), and XCUITest is also the sole actuator on hosts where an explicit--backend xcuitestis pinned (BE-0019) - The Playwright web backend (
drivers/playwright.py): a deterministicrunagainst a browser on the Linux gate (demos/web), raised to the rich end of the capability model (BE-0054) — nativenetworkobservation + stubbing (page.route()),videoanddeviceLog-equivalent console / page-error interval evidence through the shareddriver_intervalseam, emulatedmultiTouch(pinch / rotate), parallel runs across NBrowserContextlanes, and a target-leveldeviceMode(desktop default, or a Playwright device preset for mobile emulation; BE-0228);appTracestays iOS-only (os_log/simctl-based) - The Android adb backend (
drivers/adb.py+adb.py): the coordinate driver (uiautomator dump→ frame-center tap), theAndroidEnvironmentlaunch sequence,doctorreporting, interval evidence (videoviascreenrecord,deviceLogvialogcat, both through the driver-supplieddriver_intervalseam) plus the mocked-network story reused from iOS, and fast-gate unit tests over captured XML fixtures; on-device actuation parity with idb — systemback, deeplink, a single-round-tripdoubleTap, scroll-into-view resolution, and up-front runtime-permission grants (BE-0210); a device-control subset —setLocationand clipboard read/write/clear, gated by per-operation capability tokens (BE-0211 / BE-0212), the clipboard through an in-app receiver (BajutsuAndroid, BE-0233) since a shell process cannot reach the clipboard on Android 10+, whilepush/clearKeychain/ status-bar overrides /background/foregroundstay unsupported (no emulator equivalent);pinch/rotatetwo-finger multi-touch gated on a rooted device (protocol-Bsendevent, no single-touch fallback; BE-0232); a UI Automator (Kotlin) codegen target (BE-0209); an Android e2e CI lane (emulator under KVM,android-e2e.yml; BE-0208), and adb cannot yet drive the native tab bar, so tab-scoped scenarios stay iOS-only until BE-0223 lands (BE-0007). Id matching stays verbatim in the driver: where a native id syntax can't reproduce the SPEC id (Android Viewsandroid:idmapsstable.refresh→stable_refresh), the scenario's selector lists both id forms and the shared resolver matches either as an OR — an explicit scenario-side convention, not a driver-side.↔_rewrite (BE-0221) - Scenario schema (strict validation) and YAML round-trip;
id/idMatchesaccept a list of OR candidates for cross-platform id forms (BE-0221) - Evaluation of the assertion kinds (
exists/value/label/count/enabled/disabled/selected/request/requestSequence/event/responseSchema/visual/clipboard/golden) - The Tier 2 run loop (act → wait → verify), verified with
FakeDriver - DSL: the
withinselector (geometric scoping), therelaunchstep (validated on-device), reusablesetuppreludes,localeapplied at launch, and parallel runs (--workers) over a device pool - DSL authoring reuse: reusable parameterized components (
use/${params.*}), data-driven scenarios (data/dataFilewith${row.*}), secret variables (${secrets.X}with value masking), scenario tags +--tag/--excludeselection, thesetLocation/pushdevice steps, thedoubleTapaction, and file-level + scenario-leveldescription - DSL control flow & data capture: conditional
ifandforEachloops (deterministic; the condition is a machine assertion), andextract(capture an element's value / label / identifier into${vars.*}) - DSL text-editing steps (BE-0265):
clear/delete/select/copyclose the gap left bytypeon every backend (adb, Playwright, XCUITest, fake); idb and the web context raiseUnsupportedActionforselect/copy(codegen routes those to XCUITest instead), and the web context raises forclear/deletetoo. A cross-stepSelectionStateenforces the copy-requires-a-prior-select precondition, verified only through the existingclipboardread-back since no backend exposes selection as queryable state - DSL device & system actions (iOS):
background,clearKeychain,clearClipboard,overrideStatusBar/clearStatusBar(deterministic status bar), and thehttpaction for test-data setup / webhooks - Evidence: instant (
screenshot/elements/actionLog) + interval (video/deviceLog/appTrace)- the network collector (
network.json) + visual regression (visualvs. a baseline; theapprovecommand promotes baselines) +capturePolicyfiring + redaction applied to logs / element trees / network exchanges before they are written
- the network collector (
- Network observation + deterministic mocks (scenario
mocks→ in-protocol stubs, validated on-device):requestassertions,wait: { until: request }, and offline stubbed responses - Reporting (
manifest.json/junit.xml/ctrf.json/report.html) - Config resolution (defaults × targets, redact merge) and actuator selection
- The
simctlcommand layer · the idb output parser · thedoctorscore + per-backend runnability gate (preflight.py: iOS needs the required CLIs + a booted Simulator; web needs Playwright + its Chromium browser) - The
tracecommand (trace.py): a text timeline over a saved run (steps + network + appTrace) - M4 self-healing triage (
triage.py+claude_triage.py): assemble a failed run's context + aTriageAgentdiagnosis (rule-basedHeuristicTriageAgent, or--aiClaude with the failure screenshot). An agent can propose a structured fix (renameId/addIndex/raiseTimeout);--apply/--writepatches the scenario source (diff-previewed, opt-in) and--rerunre-runs it - The CLI:
run/project/doctor/audit/coverage/stats/flakiness/export/trace/report/triage/record/crawl/codegen/approve/serve/mcp/worker/lint/schema— withrecord+crawlas the Tier 1 AI authoring paths and the alert guard - Read-only advisory analysis commands (no device, no AI, never gate CI — only a missing/unreadable input exits non-zero): a determinism/flakiness audit with static, repeat-and-diff, and longitudinal modes (
audit, BE-0049); a scenario id-namespace coverage map (coverage, BE-0050); the aggregate run-stats dashboard as CLI/HTML output (stats, BE-0102); cross-run flakiness ranking, from a runs directory or theservedatabase (flakiness, BE-0220); a finished run's export as a portable.zip(export, BE-0060); and report re-rendering (report.html/junit.xml/ctrf.json) from stored run data with no re-run (report, BE-0068) - The config project hub (
project add/ls/use/rmandrun --project, BE-0225): a named registry binding a project name to a config source, shared between the CLI and theserveweb UI (DB-backed when configured, on-disk JSON otherwise);servecarries a header project switcher plus a top-level Projects page (BE-0275) that lists, adds, removes, and switches projects, rebinding the active config with no restart - The cross-project metrics comparison dashboard (BE-0226): a
serveMetrics tab that ranks the registered projects side by side — pass-rate, flaky-rate, and p50/p95 run duration, plus a per-project trend sparkline — reusing BE-0102's per-config aggregation computed once per project (GET /api/metrics/projects); read-only and advisory, like BE-0102 - AI crawl (
crawl/): autonomous breadth-first exploration of an app → a screen map (screenmap.json) - The
servelocal web UI (Tier 1): author (record/crawl), edit, and run scenarios; open a.zipbundle of config + scenarios + the built app binary as the active config the tabs run from (BE-0073) — the server also accepts those same three pieces as independent content-addressed artifacts and composes them into that tree at bind time (POST /api/artifacts/{config,scenarios,binary}, BE-0268), a backend-only capability so far since only the combined-zip drop zone is wired into the UI; browse reports and evidence; a read-only aggregate run-stats dashboard across the run history (BE-0102), with every axis — date, backend, scenario, and step/assertion hotspot — now a deep link into the matching runs in the history list (BE-0241); a pre-run readiness panel (doctor: environment runnability + the current screen's convention score) in the Record and Replay forms (BE-0148); a pluggable theme system — drop-in visual tokens + swappable transitions, a header picker, and an in-UI editor with live preview and local-draft/server-upload persistence (BE-0191); a header version badge reporting which build of bajutsu is serving the page — the version string always, plus a short commit SHA / branch / dirty flag when serve runs from a Git checkout, or a build-time-embedded commit (BAJUTSU_BUILD_COMMIT, surfaced withsource: "build-arg") for a self-hosted Docker image shipping no.git(the checkout detail admin-gated, since a branch name can encode an in-progress topic;GET /api/versionopen,GET /api/version/checkoutadmin, read fresh per request viagitplumbing with an environment-variable fallback — no LLM; BE-0272, BE-0277); approve visual baselines; live job streaming — from a browser (not for CI) - MCP server (
bajutsu mcp):bajutsu_runandbajutsu_doctoras MCP tools + run evidence as resources, for Claude Desktop / Code integration (optional dependencyfastmcp) - Scenario linter (
bajutsu lint/bajutsu schema): validate scenarios without running them; JSON Schema output for editor integration - Codegen: scenario → native test, three targets behind a shared scenario walk (BE-0083) — XCUITest (Swift, iOS), Playwright (TypeScript, web), UI Automator (Kotlin, Android; BE-0209)
- The idb backend's subprocess execution —
describe-allparsing, frame-center tap / text / swipe, and the simctl launch sequencing — confirmed against the installedidb/idb_companionby running the showcase scenarios, evidence capture, and the triage self-heal loop on-device (make -C demos/showcase run-swiftui; theios-e2e.ymlCI workflow also exercises the idb smoke path). - The XCUITest backend's resident runner — element resolution by snapshot handle, semantic tap, and
the
pinch/rotatemulti-touch gestures idb cannot run — confirmed on-device via theios-e2e.ymlxcuitest (multi-touch)job (demos/showcase/scenarios/gestures_multitouch.yaml,--backend xcuitest).
- The Playwright web backend runs the
demos/webscenarios deterministically inside the samemake checkgate as CI (theweb-e2ejob inci.yml), confirming the deterministic core is platform-neutral. Rich-end web capture (network / video / multi-touch) has since shipped (BE-0054); a parallel web crawl across N browser processes (BE-0077) runs on this same gate.
- The adb backend's subprocess execution —
uiautomator dumpparsing, frame-center tap, theAndroidEnvironmentlaunch sequence, actuation-fidelity parity with idb, and thepinch/rotatemulti-touch and device-control slices — is confirmed against a booted x86_64 API 34 AVD under KVM (android-e2e.yml; BE-0208), driving both the Compose and Views showcase builds over the same shared scenarios idb runs, plus a golden element-tree check and a pixel visual-regression baseline for the Compose catalog. The lane also builds the resident UI Automator server (BE-0245), so those reads run over the resident channel (GET /sourceoveradb forward, replacing the ≈ 2.4 s per-readuiautomator dumpstartup) by default there, with a dump-fallback golden run guarding theuiautomator dumppath.
| Feature | Status | Location |
|---|---|---|
mockServer (external mock command) |
config schema only; the cmd/port external server is not implemented — superseded by scenario mocks (declarative in-protocol stubs, implemented) |
config/schema.py MockServer |
appTrace interval evidence on the web backend |
appTrace is os_log/simctl-based (iOS only); the Playwright backend implements the video and deviceLog-equivalent (console / page-error) interval kinds instead (BE-0054), but has no appTrace analogue |
intervals.py · drivers/playwright.py |
These are also flagged inline on the relevant feature pages.