mav

package
v0.16.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 4, 2026 License: MIT Imports: 38 Imported by: 0

Documentation

Index

Constants

View Source
const (
	MavDir        = ".mav"
	ConfigFile    = ".mav/config.yaml"
	AppMapFile    = ".mav/app-map.yaml"
	CurrentRunRef = ".mav/current-run"
)
View Source
const EvidenceStepsFile = "evidence.jsonl"
View Source
const TreesDir = "trees"

TreesDir is the subdirectory under a run dir where snapshot/delta JSON files live. Created on demand by PersistTree.

Variables

View Source
var Version = "dev"

Version is what this binary reports as itself. It is stamped at link time by the release build (-X github.com/bitomule/mav/internal/mav.Version) and left as "dev" otherwise, so a locally built binary never claims to be a release it is not.

It exists because until now mav could not answer the question. An agent reporting a bug, a run whose evidence is read weeks later, and anyone comparing what is installed against what a project expects all need a version, and `mav --version` answered `unknown_command`.

Functions

func AppendEvidenceStep

func AppendEvidenceStep(run RunState, step EvidenceStep) error

func GenerateReport

func GenerateReport(run RunState) (string, error)

func NewExecutor added in v0.4.0

func NewExecutor(r Runner) drivers.Executor

NewExecutor adapts a mav.Runner to drivers.Executor. Drivers receive this in their constructors instead of the full Runner.

func RegisterDefaultDrivers added in v0.4.0

func RegisterDefaultDrivers(reg *drivers.Registry, exec drivers.Executor)

RegisterDefaultDrivers wires the canonical driver portfolio into reg: AXe (fast a11y + semantic tap), idb (device coord tap / screenshot / logs / crashes / install), Baguette (sim multitouch + system UI + hardware buttons), simctl (sim lifecycle / video / locale / log stream).

idb is the canonical device driver. Sim-only multitouch / system-UI / hardware-button operations go through baguette. On device targets where baguette is unavailable, cli.go surfaces a structured error rather than silently falling back.

func Run

func Run(ctx context.Context, args []string, stdout, stderr io.Writer) error

func SaveConfig

func SaveConfig(root string, cfg Config) error

SaveConfig serializa cfg a .mav/config.yaml.

It uses yaml.Marshal deliberately instead of the hand-written writer that lived here before: that one omitted empty values (writeCommandKV), which makes it impossible to express "this field is present and holds the empty string". That distinction did not matter while the config was flat, but it is exactly the one platform profiles need so a profile can *cancel* a command inherited from the base instead of inheriting it.

Note on what does NOT change: this function rebuilds the whole file, so comments the user wrote by hand are lost. That already happened with the previous writer; SaveConfig has never read the prior file to preserve anything.

func SaveCurrentRun

func SaveCurrentRun(root string, run RunState) error

Types

type AgentElement added in v0.4.0

type AgentElement struct {
	ID         string `json:"id,omitempty"`
	Label      string `json:"label,omitempty"`
	Role       string `json:"role,omitempty"`
	Value      string `json:"value,omitempty"`
	Title      string `json:"title,omitempty"`
	Subrole    string `json:"subrole,omitempty"`
	Focused    string `json:"focused,omitempty"`
	Enabled    string `json:"enabled,omitempty"`
	Frame      string `json:"frame,omitempty"`
	Actionable bool   `json:"actionable"`
}

AgentElement is the LLM-facing projection of Element. It drops fields agents rarely use (Frame by default, redundant Hint-like fields), promotes the "is this useful for the current decision?" verdict (Actionable), and ranks the list so the most relevant ~40 nodes are at the top.

The goal is the same that drove the RocketSim CLI's --agent mode: ~50%+ fewer tokens vs the full tree without losing decision-grade information.

func AgentTree added in v0.4.0

func AgentTree(elements []Element, opts AgentTreeOptions) []AgentElement

AgentTree projects elements to the agent-facing shape, ranked by relevance:

  1. focused first (the field the user is in matters most)
  2. then actionable + enabled + visible-ish (taps/inputs the agent can use)
  3. then everything else, in original order

Ranking is stable across runs because Element ordering from ExtractElements is already deterministic. The cap is applied after sort.

type AgentTreeOptions added in v0.4.0

type AgentTreeOptions struct {
	// Max caps the output length. Zero -> agentDefaultMax (40).
	Max int
	// WithFrame keeps the Frame field on each element. Off by default
	// because frames are 30-50 bytes of noise per element for most agent
	// decisions.
	WithFrame bool
}

AgentTreeOptions controls the compact projection.

type CLI

type CLI struct {
	Runner Runner
	Stdin  io.Reader
	Stdout io.Writer
	Stderr io.Writer
	Root   string
	// contains filtered or unexported fields
}

func (CLI) OK added in v0.8.0

func (c CLI) OK(cmd string, fields map[string]string) Output

OK is the CLI-bound counterpart of the package-level OK: it's the single place a command's success fields pick up which simulator or device they actually acted on. Route every success output through c.OK (not the bare OK) so nobody has to remember to add the field by hand -- in hot-path usage (an agent driving mav command-by-command, not just via `mav run`) that field is how the next call knows which target to keep using instead of guessing; guessing wrong with several agents on one machine means silently driving someone else's simulator while taps and assertions keep passing.

func (CLI) Run

func (c CLI) Run(ctx context.Context, args []string) error

type Capabilities added in v0.0.10

type Capabilities struct {
	// Kind is the target these capabilities were resolved FOR. fields()
	// needs it because the right guidance differs by platform: prescribing
	// baguette or simtime to a macos target sends the reader to install
	// tools that provide nothing there.
	Kind                 drivers.TargetKind
	Tools                map[string]bool
	LaunchRecipe         bool
	Accessibility        bool
	AccessibilityDriver  string
	SemanticActions      bool
	CoordinateTap        bool
	CoordinateTapDriver  string
	DeviceFallback       bool
	DeviceFallbackDriver string
	Multitouch           bool
	MultitouchDriver     string
	NetworkCapture       bool
	NetworkCaptureDriver string
	WallClock            bool
	Debug                bool
	IDBIssue             string
	IDBNext              string

	// macOS: TCC is the deciding factor, not the API. It is reported
	// separately because the permission holder is NOT mav but the process
	// running it, and that must be said or the user looks where it is not.
	MacPermissions     string
	MacPermissionsNext string
}

type CommandFailed added in v0.6.0

type CommandFailed struct{}

CommandFailed requests a non-zero process exit after a structured failure line has already been written. It avoids duplicating the failure on stderr.

func (CommandFailed) Error added in v0.6.0

func (CommandFailed) Error() string

type CommandResult

type CommandResult struct {
	Stdout                string
	Stderr                string
	Code                  int
	Err                   error
	IDBCompanionRefreshed bool
}

type Config

type Config struct {
	ProjectName       string
	Root              string
	TargetKind        string
	AppTarget         string
	DeviceTarget      string
	DeviceUDID        string
	DeviceName        string
	BundleID          string
	ProcessName       string
	SimulatorUDID     string
	SimulatorName     string
	SimulatorRuntime  string
	Locale            string
	Language          string
	LogSubsystem      string
	LogCategory       string
	PreferredUIDriver string
	AllowShell        bool
	TargetCommand     string

	// TargetCommandRequired says a configured target_command is the only
	// acceptable source of the simulator: if it fails, times out or prints
	// nothing, the command fails instead of quietly falling back to whatever
	// simulator happens to be booted. nil means unset, which resolves to
	// true (see targetCommandRequired) -- the config declared how the target
	// is chosen, so choosing differently in silence contradicts it. Setting
	// it to false is the explicit opt-out back to warn-and-fall-back.
	TargetCommandRequired *bool

	// TargetCommandTimeout bounds a single target_command invocation. Empty
	// means the default (see defaultTargetCommandTimeout). A Go duration
	// string: "90s", "3m".
	TargetCommandTimeout string

	Launch LaunchConfig
	Tools  map[string]bool

	// DefaultProfile and Profiles are kept raw so they can be rewritten
	// without being lost, and so `mav doctor` can list them. ActiveProfile
	// is the one resolved for this invocation ("" if none).
	DefaultProfile string
	Profiles       map[string]profileYAML
	ActiveProfile  string
	Fixtures       map[string][]string

	// VM says the target app runs inside a disposable macOS VM instead of
	// on this machine. Which tool provides that VM is mav's business, not
	// the config's: see internal/mav/vm.go.
	VM bool

	// AppPath is filled by the launch recipe at run time (app_path step);
	// it is neither read from nor written to the YAML. It is how the macOS
	// driver knows which bundle to run, its equivalent of the UDID.
	AppPath string
}

func DefaultConfig

func DefaultConfig(root string) Config

func LoadConfig

func LoadConfig(root string) (Config, error)

LoadConfig loads .mav/config.yaml applying whichever profile the documented precedence selects. Equivalent to LoadConfigWithProfile(root, "").

func LoadConfigRaw added in v0.12.0

func LoadConfigRaw(root string) (Config, error)

LoadConfigRaw loads the config WITHOUT applying any profile. It is what the paths that later write (setup, sim select, device select) must use: only a config without an overlay can go back to disk without flattening the profile onto the base. See SaveConfig's guardrail.

func LoadConfigWithProfile added in v0.12.0

func LoadConfigWithProfile(root, profileOverride string) (Config, error)

LoadConfigWithProfile loads the config and overlays a platform profile.

Selection precedence, strongest to weakest:

  1. profileOverride, whatever an explicit --profile brings
  2. MAV_PROFILE in the environment
  3. default_profile in the config itself
  4. none: the base fields are used as is

A requested profile that does not exist is an error, never a no-op: accepting the flag and carrying on with the base would be dead configuration of the same kind target_command_ignored exists to make visible.

func SetupConfig added in v0.0.10

func SetupConfig(root string, runner Runner) (Config, error)

type CrashSummary added in v0.4.0

type CrashSummary struct {
	// Header fields
	BundleID   string `json:"bundle_id,omitempty"`
	AppName    string `json:"app_name,omitempty"`
	AppVersion string `json:"app_version,omitempty"`
	OSVersion  string `json:"os_version,omitempty"`
	Timestamp  string `json:"timestamp,omitempty"`
	IncidentID string `json:"incident_id,omitempty"`
	BugType    string `json:"bug_type,omitempty"`

	// Body fields (best-effort, may be empty depending on bug_type)
	Process       string `json:"process,omitempty"`
	Exception     string `json:"exception,omitempty"`      // e.g., "EXC_BAD_ACCESS (SIGSEGV)"
	Termination   string `json:"termination,omitempty"`    // e.g., "SIGNAL Code -1"
	Reason        string `json:"reason,omitempty"`         // exception.subtype + asi when present
	CrashedThread string `json:"crashed_thread,omitempty"` // thread name when present
}

CrashSummary is the parsed view of an iOS .ips crash report. iOS 15+ stores .ips as two concatenated JSON documents: a header line followed by a body. We extract just the fields agents and humans actually need for triage; the full body stays in the original .ips on disk.

func ParseIPS added in v0.4.0

func ParseIPS(raw []byte) (CrashSummary, error)

ParseIPS reads an iOS 15+ .ips body and extracts a CrashSummary.

The .ips format is two JSON documents separated by a newline: the first line is the header (app/bundle/version metadata), the rest is the body (exception, termination, threads, ...). We split on the first `\n{` boundary so a header with arbitrary keys still parses cleanly.

Older formats (iOS 14 and below; PLIST-style symbol-rich text) are NOT supported -- this parser intentionally targets the modern JSON format where idb's `crash show` output now lives.

func (CrashSummary) OneLiner added in v0.4.0

func (s CrashSummary) OneLiner() string

OneLiner renders a single-line summary suitable for the HTML report's crash card. Format: "EXC_BAD_ACCESS (SIGSEGV) in MyApp on com.example — reason". Empty when the summary lacks the bits we need.

type Element

type Element struct {
	ID       string `json:"id,omitempty"`
	Label    string `json:"label,omitempty"`
	Role     string `json:"role,omitempty"`
	Value    string `json:"value,omitempty"`
	Frame    string `json:"frame,omitempty"`
	Enabled  string `json:"enabled,omitempty"`
	Selected string `json:"selected,omitempty"`
	Visible  string `json:"visible,omitempty"`
	Subrole  string `json:"subrole,omitempty"`
	Title    string `json:"title,omitempty"`
	PID      string `json:"pid,omitempty"`
	Focused  string `json:"focused,omitempty"`
	Depth    int    `json:"depth,omitempty"`
}

Element is the framework-neutral view of a single node in an accessibility tree (axe or baguette JSON). It collapses the differences between drivers so callers (`mav ui tree`, `mav ui tap`, flow conditions, gesture helpers, focus probes) can reason in a single vocabulary.

func Compact added in v0.4.0

func Compact(elements []Element) []Element

Compact applies the 80-element cap on top of an already-dedup'd slice. Exported so PersistTree can re-cap a raw extraction without re-parsing.

func ExtractElements

func ExtractElements(rawTree string) []Element

ExtractElements parses an AX tree (axe JSON or any structure that walkAX understands) and returns a flat, de-duplicated list of Elements. Bounded at 80 to keep output tractable for downstream callers; the same cap drove `compactElements` historically.

func ExtractElementsRaw added in v0.4.0

func ExtractElementsRaw(rawTree string) []Element

ExtractElementsRaw parses an AX tree and returns the de-duplicated flat list WITHOUT the 80-element cap. Used by evidence persistence so the `*.full.json` fixture stays complete; downstream tooling (agents, the HTML report) still consumes the compact (capped) variant from ExtractElements / Compact.

func LoadPersistedTree added in v0.4.0

func LoadPersistedTree(path string) ([]Element, error)

LoadPersistedTree reads back a compact tree snapshot. Used by `mav ui tree --since step-N` to compute a fresh delta against an older step without needing the caller to keep history in memory.

func MatchElements added in v0.6.0

func MatchElements(elements []Element, selector Selector) ([]Element, error)

MatchElements returns matching nodes in tree order. It works against the full flattened pre-order tree so parent/descendant relationships remain available through Depth.

type ElementChange added in v0.4.0

type ElementChange struct {
	ID    string            `json:"id,omitempty"`
	Key   string            `json:"key,omitempty"` // populated when ID is empty
	Diffs map[string]string `json:"diffs"`
}

ElementChange records the per-field deltas between the same logical element (matched by ID, or by structural key when ID is empty) in two snapshots. Diffs maps field name -> "old → new".

type EvidenceStep

type EvidenceStep struct {
	Name      string `json:"name"`
	Note      string `json:"note,omitempty"`
	File      string `json:"file"`
	Kind      string `json:"kind"`
	CreatedAt string `json:"created_at"`

	// Tree snapshot fields (P4 upgrade). Populated when the step also
	// persisted an accessibility tree under <runDir>/trees/. Absent on
	// older evidence steps; readers MUST tolerate empty values.
	TreePath  string `json:"tree_path,omitempty"`
	FullPath  string `json:"tree_full_path,omitempty"`
	DeltaPath string `json:"tree_delta_path,omitempty"`
	TreeHash  string `json:"tree_hash,omitempty"`

	// Video correlation (P4 upgrade). When a recording is active,
	// MonotonicMs is the host monotonic clock at capture; VideoOffsetMs
	// is monotonic_ms minus the recording's start, in milliseconds.
	// Both empty when no recording is running.
	MonotonicMs   int64 `json:"monotonic_ms,omitempty"`
	VideoOffsetMs int64 `json:"video_offset_ms,omitempty"`
}

func LoadEvidenceSteps

func LoadEvidenceSteps(run RunState) []EvidenceStep

type ExecRunner

type ExecRunner struct{}

func (ExecRunner) LookPath

func (ExecRunner) LookPath(file string) (string, error)

func (ExecRunner) Run

func (ExecRunner) Run(ctx context.Context, name string, args ...string) CommandResult

func (ExecRunner) Start

func (ExecRunner) Start(ctx context.Context, logPath string, name string, args ...string) (int, error)

Start launches a background process and returns its PID.

An empty logPath means "discard the output", not an error. Launching a macOS app needs it: there stdout and stderr are not the real log channel, OSLog is, and mav already captures that on its own with `log stream`, so forcing the driver to invent a file just to throw it away would be worse. Before this, an empty logPath died with an `open : no such file or directory` that said nothing about the cause.

type FailurePolicy added in v0.6.0

type FailurePolicy struct {
	Strategy    string   `yaml:"strategy,omitempty"`
	MaxAttempts int      `yaml:"maxAttempts,omitempty"`
	Delay       string   `yaml:"delay,omitempty"`
	Backoff     float64  `yaml:"backoff,omitempty"`
	RetryOn     []string `yaml:"retryOn,omitempty"`
}

type Flow

type Flow struct {
	Name   string               `yaml:"name"`
	Params map[string]FlowParam `yaml:"params,omitempty"`
	Steps  []FlowStep           `yaml:"steps"`
}

func LoadFlow

func LoadFlow(path string) (Flow, error)

func ParseFlow

func ParseFlow(data []byte) (Flow, error)

type FlowAfter added in v0.6.0

type FlowAfter struct {
	Wait    *FlowWait `yaml:"wait,omitempty"`
	Observe string    `yaml:"observe,omitempty"`
}

type FlowCondition

type FlowCondition struct {
	ID             string          `yaml:"id,omitempty"`
	Text           string          `yaml:"text,omitempty"`
	TextContains   string          `yaml:"textContains,omitempty"`
	TextStartsWith string          `yaml:"textStartsWith,omitempty"`
	TextRegex      string          `yaml:"textRegex,omitempty"`
	Value          string          `yaml:"value,omitempty"`
	ValueContains  string          `yaml:"valueContains,omitempty"`
	Role           string          `yaml:"role,omitempty"`
	Enabled        *bool           `yaml:"enabled,omitempty"`
	Selected       *bool           `yaml:"selected,omitempty"`
	Focused        *bool           `yaml:"focused,omitempty"`
	Visible        *bool           `yaml:"visible,omitempty"`
	Index          *int            `yaml:"index,omitempty"`
	Bounds         string          `yaml:"bounds,omitempty"`
	Near           *NearSelector   `yaml:"near,omitempty"`
	ParentOf       *Selector       `yaml:"parentOf,omitempty"`
	ChangedFrom    string          `yaml:"changedFrom,omitempty"`
	Stable         bool            `yaml:"stable,omitempty"`
	Any            []FlowCondition `yaml:"any,omitempty"`
	All            []FlowCondition `yaml:"all,omitempty"`
	Not            *FlowCondition  `yaml:"not,omitempty"`
}

func (FlowCondition) Selector added in v0.6.0

func (c FlowCondition) Selector() Selector

type FlowCoordinate added in v0.6.0

type FlowCoordinate struct {
	X string `yaml:"x"`
	Y string `yaml:"y"`
}

type FlowParam added in v0.6.0

type FlowParam struct {
	Required bool   `yaml:"required,omitempty"`
	Default  string `yaml:"default,omitempty"`
}

type FlowPathPoint added in v0.6.0

type FlowPathPoint struct {
	X          int    `yaml:"x"`
	Y          int    `yaml:"y"`
	Duration   string `yaml:"duration,omitempty"`
	DurationMs int    `yaml:"durationMs,omitempty"`
}

type FlowStep

type FlowStep struct {
	Action    string
	Params    map[string]string
	Where     Selector
	After     *FlowAfter
	OnFailure FailurePolicy
	Any       []FlowCondition
	All       []FlowCondition
	Not       *FlowCondition
	Points    []FlowPathPoint
	Do        []FlowStep
	Env       map[string]string
}

type FlowWait added in v0.6.0

type FlowWait struct {
	ID           string          `yaml:"id,omitempty"`
	Text         string          `yaml:"text,omitempty"`
	TextContains string          `yaml:"textContains,omitempty"`
	Value        string          `yaml:"value,omitempty"`
	ChangedFrom  string          `yaml:"changedFrom,omitempty"`
	Stable       bool            `yaml:"stable,omitempty"`
	Any          []FlowCondition `yaml:"any,omitempty"`
	All          []FlowCondition `yaml:"all,omitempty"`
	Not          *FlowCondition  `yaml:"not,omitempty"`
	Timeout      string          `yaml:"timeout,omitempty"`
}

type GlobalOptions

type GlobalOptions struct {
	Verbose      bool
	Raw          bool
	Help         bool
	Version      bool
	PreferDriver string
	Profile      string
}

type ImageEvidence added in v0.3.3

type ImageEvidence struct {
	OK     bool   `json:"ok"`
	Issue  string `json:"issue,omitempty"`
	Width  int    `json:"width,omitempty"`
	Height int    `json:"height,omitempty"`
	Size   int64  `json:"size,omitempty"`
}

func ValidateEvidenceImage added in v0.3.3

func ValidateEvidenceImage(path string) ImageEvidence

type LaunchCandidate added in v0.0.10

type LaunchCandidate struct {
	Source      string
	Confidence  int
	Description string
	Launch      LaunchConfig
	AppTarget   string
	BundleID    string
}

type LaunchCommands added in v0.0.10

type LaunchCommands struct {
	Healthcheck string `yaml:"healthcheck,omitempty"`
	Build       string `yaml:"build,omitempty"`
	AppPath     string `yaml:"app_path,omitempty"`
	Install     string `yaml:"install,omitempty"`
	Launch      string `yaml:"launch,omitempty"`
	Cleanup     string `yaml:"cleanup,omitempty"`
}

LaunchCommands is the base config's launch recipe. The fields carry omitempty because in the base "empty" and "absent" mean the same thing: no command. The distinction does matter in a platform profile, which needs to be able to *override* an inherited command with nothing, which is why profiles use their own pointer-based type (see profileLaunchCommandsYAML) instead of reusing this one.

type LaunchConfig added in v0.0.10

type LaunchConfig struct {
	Mode     string         `yaml:"mode"`
	Commands LaunchCommands `yaml:"commands"`
}

type NearSelector added in v0.6.0

type NearSelector struct {
	Where       Selector `yaml:"where" json:"where"`
	Direction   string   `yaml:"direction,omitempty" json:"direction,omitempty"`
	MaxDistance float64  `yaml:"maxDistance,omitempty" json:"maxDistance,omitempty"`
}

type NetworkEvidence added in v0.5.0

type NetworkEvidence struct {
	HAR           string `json:"har,omitempty"`
	OK            bool   `json:"ok"`
	Issue         string `json:"issue,omitempty"`
	Requests      int    `json:"requests,omitempty"`
	Responses     int    `json:"responses,omitempty"`
	Status4xx     int    `json:"status_4xx,omitempty"`
	Status5xx     int    `json:"status_5xx,omitempty"`
	UniqueDomains int    `json:"unique_domains,omitempty"`
	Active        bool   `json:"active,omitempty"`
}

type Output

type Output struct {
	OK     bool              `json:"ok"`
	Cmd    string            `json:"cmd,omitempty"`
	Code   string            `json:"code,omitempty"`
	Fields map[string]string `json:"fields,omitempty"`
}

func Fail

func Fail(code string, fields map[string]string) Output

func FailCode added in v0.5.0

func FailCode(code codes.Code, fields map[string]string) Output

func OK

func OK(cmd string, fields map[string]string) Output

func (Output) Write

func (o Output) Write(w io.Writer) error

type PersistedTree added in v0.4.0

type PersistedTree struct {
	CompactPath string // <runDir>/trees/step-NN_<name>.json (capped, agent-facing)
	FullPath    string // <runDir>/trees/step-NN_<name>.full.json (uncapped, debug)
	DeltaPath   string // <runDir>/trees/step-NN_<name>.delta.json — empty when previous == nil
	Hash        string // sha256 hex of the compact JSON; populates EvidenceStep.TreeHash
}

PersistedTree is the metadata PersistTree returns to its caller so the evidence step record can reference it.

func PersistTree added in v0.4.0

func PersistTree(runDir string, stepIdx int, name string, raw []Element, previous []Element) (PersistedTree, error)

PersistTree writes the compact and full snapshots of a tree under <runDir>/trees/, optionally also writing a delta vs `previous`.

Naming uses the step index zero-padded to 2 digits plus a slug derived from name (alphanumerics + dashes). The leading index keeps the directory listing chronological — important for the HTML report.

When previous == nil no delta is written and DeltaPath is empty. The hash is the sha256 of the compact JSON so EvidenceStep.TreeHash can identify identical screen states cheaply.

type PhysicalDevice added in v0.1.0

type PhysicalDevice struct {
	UDID string
	Name string
}

func ListPhysicalDevices added in v0.1.0

func ListPhysicalDevices(ctx context.Context, runner Runner) ([]PhysicalDevice, error)

type ReportData

type ReportData struct {
	RunID     string `json:"run_id"`
	CreatedAt string `json:"created_at"`
	// Fixture is the named state seeded before launching the app, if there
	// was one. Without this the manifest cannot answer "what state did
	// this start from?", and a run whose evidence does not say that is not
	// reproducible, which is exactly what the verified manifest promises.
	Fixture    string `json:"fixture,omitempty"`
	Dir        string `json:"dir"`
	Screenshot string `json:"screenshot,omitempty"`
	// A pointer so it can be ABSENT. As a value, a run without a loose
	// screenshot, which is every flow, serialized
	// `"screenshot_evidence":{"ok":false}`: a negative verdict on something
	// that never existed. In an evidence layer that is worse than saying
	// nothing, because an agent cannot tell it apart from a broken capture.
	ScreenshotEvidence *ImageEvidence       `json:"screenshot_evidence,omitempty"`
	Steps              []ReportEvidenceStep `json:"steps"`
	Video              string               `json:"video,omitempty"`
	VideoMP4           string               `json:"video_mp4,omitempty"`
	VideoStatus        string               `json:"video_status"`
	VideoIssue         string               `json:"video_issue,omitempty"`
	VideoDuration      string               `json:"video_duration,omitempty"`
	VideoFrames        string               `json:"video_frames,omitempty"`
	Logs               string               `json:"logs,omitempty"`
	Network            NetworkEvidence      `json:"network,omitempty"`
	Crashes            []string             `json:"crashes,omitempty"`
	Commands           []string             `json:"commands,omitempty"`
	Issues             []ReportIssue        `json:"issues,omitempty"`
	ValidStepCount     int                  `json:"valid_step_count"`
	InvalidStepCount   int                  `json:"invalid_step_count"`
	Verdict            string               `json:"verdict"`
	Outputs            map[string]string    `json:"outputs,omitempty"`
}

type ReportEvidenceStep added in v0.3.3

type ReportEvidenceStep struct {
	EvidenceStep
	Index       int           `json:"index"`
	DisplayName string        `json:"display_name"`
	Image       ImageEvidence `json:"image"`
}

type ReportIssue added in v0.3.3

type ReportIssue struct {
	Severity string `json:"severity"`
	Title    string `json:"title"`
	Detail   string `json:"detail"`
}

type RunState

type RunState struct {
	ID        string
	Dir       string
	LogsPath  string
	Commands  string
	Processes string
	Started   time.Time
}

func LoadRun

func LoadRun(root, id string) (RunState, error)

func NewProjectRunState added in v0.3.2

func NewProjectRunState(root string) (RunState, error)

func NewRunState

func NewRunState() (RunState, error)

type Runner

type Runner interface {
	LookPath(file string) (string, error)
	Run(ctx context.Context, name string, args ...string) CommandResult
	Start(ctx context.Context, logPath string, name string, args ...string) (int, error)
}

type Selector added in v0.6.0

type Selector struct {
	ID             string        `yaml:"id,omitempty" json:"id,omitempty"`
	Text           string        `yaml:"text,omitempty" json:"text,omitempty"`
	TextContains   string        `yaml:"textContains,omitempty" json:"textContains,omitempty"`
	TextStartsWith string        `yaml:"textStartsWith,omitempty" json:"textStartsWith,omitempty"`
	TextRegex      string        `yaml:"textRegex,omitempty" json:"textRegex,omitempty"`
	Value          string        `yaml:"value,omitempty" json:"value,omitempty"`
	ValueContains  string        `yaml:"valueContains,omitempty" json:"valueContains,omitempty"`
	Role           string        `yaml:"role,omitempty" json:"role,omitempty"`
	Enabled        *bool         `yaml:"enabled,omitempty" json:"enabled,omitempty"`
	Selected       *bool         `yaml:"selected,omitempty" json:"selected,omitempty"`
	Focused        *bool         `yaml:"focused,omitempty" json:"focused,omitempty"`
	Visible        *bool         `yaml:"visible,omitempty" json:"visible,omitempty"`
	Index          *int          `yaml:"index,omitempty" json:"index,omitempty"`
	Bounds         string        `yaml:"bounds,omitempty" json:"bounds,omitempty"`
	Near           *NearSelector `yaml:"near,omitempty" json:"near,omitempty"`
	ParentOf       *Selector     `yaml:"parentOf,omitempty" json:"parentOf,omitempty"`
}

Selector is the typed, driver-neutral predicate used by CLI commands and flows. All populated fields are combined with AND. Index is applied after every other filter.

func (Selector) IsZero added in v0.6.0

func (s Selector) IsZero() bool

func (Selector) Validate added in v0.6.0

func (s Selector) Validate() error

type Simulator

type Simulator struct {
	UDID    string
	Name    string
	Runtime string
	State   string
}

func ListSimulators

func ListSimulators(runner Runner) ([]Simulator, error)

func SelectSimulator

func SelectSimulator(sims []Simulator, name, runtime, udid string) (Simulator, bool)

type TreeDelta added in v0.4.0

type TreeDelta struct {
	Added   []Element       `json:"added,omitempty"`
	Removed []Element       `json:"removed,omitempty"`
	Changed []ElementChange `json:"changed,omitempty"`
}

TreeDelta is the difference between two Element snapshots. Agents consume it to reason about state transitions without re-reading the full tree on every step. The shape is intentionally JSON-stable: agents can rely on added/removed/changed keys.

func TreeDiff added in v0.4.0

func TreeDiff(prev, next []Element) TreeDelta

TreeDiff computes the delta from prev to next. Elements are matched by non-empty ID first; for elements without ID, by a structural key (role + label + frame). Empty deltas (matched, no field difference) are omitted.

Determinism: outputs are sorted by ID then Key so JSON serialisation is stable across runs — critical for golden tests and for the HTML report's diff renderer.

type VideoValidation added in v0.0.7

type VideoValidation struct {
	OK       bool
	Duration time.Duration
	Frames   int
	Issue    string
}

func ValidateEvidenceVideo added in v0.0.7

func ValidateEvidenceVideo(path string) VideoValidation

Directories

Path Synopsis
Package codes is MAV's structured error vocabulary.
Package codes is MAV's structured error vocabulary.
Package drivers defines the pluggable driver layer that MAV uses to talk to iOS simulators and physical devices.
Package drivers defines the pluggable driver layer that MAV uses to talk to iOS simulators and physical devices.
axe
Package axe wraps the AXe accessibility CLI as a MAV driver.
Package axe wraps the AXe accessibility CLI as a MAV driver.
baguette
Package baguette wraps the baguette CLI (https://github.com/tddworks/baguette) — a Swift host-side simulator driver built on private SimulatorKit symbols.
Package baguette wraps the baguette CLI (https://github.com/tddworks/baguette) — a Swift host-side simulator driver built on private SimulatorKit symbols.
idb
Package idb wraps Facebook's idb_companion.
Package idb wraps Facebook's idb_companion.
macos
Package macos groups the drivers that operate on the Mac's own apps.
Package macos groups the drivers that operate on the Mac's own apps.
network
Package network provides the network-capture drivers.
Package network provides the network-capture drivers.
simctl
Package simctl wraps Apple's `xcrun simctl` for simulator lifecycle, video recording, log streaming, screenshots, and locale config.
Package simctl wraps Apple's `xcrun simctl` for simulator lifecycle, video recording, log streaming, screenshots, and locale config.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL