Evaluated knip (webpro-nl/knip) against the fallow setup already in the
repo, cleaned up everything it surfaced, then removed knip again: measured
head-to-head on the same tree, fallow is a strict superset once two
switches it already supports are flipped.
Dead code removed:
- `daemon/artifact-materialization.ts` (224 lines) had no production
caller, only its own test. Removing it exposed that
`downloadArtifactToTempDir` and the whole URL-fetch-with-redirects path
in `artifact-download.ts` were reachable only through it — the live
upload paths use the incoming-request helpers instead. That file goes
348 -> 123 lines. `readZipEntries` then fell out of `artifact-archive.ts`.
- Dead test-helper exports: 12 unused re-exports and 6 needlessly-exported
mocks in `session-test-harness.ts`, dead barrel entries in
`__tests__/test-utils/index.ts`, plus `withMockedXcrun`, `matchesSchema`,
`IOS_FRAME`, `IOS_TAB_FRAME`, `snapshotWithOffscreenContent`.
- `androidSnapshotHelperOutput` was duplicated byte-for-byte in
`provider-scenarios/android-world.ts`; it now imports the shared copy.
- 8 unreferenced type aliases, and 17 redundant type re-export lines in
`client/client-types.ts`. The published `.d.ts` is byte-identical before
and after all 19 files: those types already reach consumers through
`contracts/*` via `CommandResult<...>`, so this is not an API change.
Tooling:
- `.fallowrc.json` gains `includeEntryExports`,
`ignoreExportsUsedInFile: {type, interface}` and `unused-types: warn`.
That combination is what made the findings above visible; the previous
config was quiet mainly because of its own suppression list.
- Dropped 2 now-obsolete `ignoreExports` suppressions, added 3 documented
ones (published `sdk/*` surface, tool-config `default` exports, and the
`AssertTrue<...>` totality guards that exist only to satisfy
`noUnusedLocals`).
`unused-types` stays at `warn`: 72 pre-existing type re-export lines across
27 files remain, tracked separately. Every other fallow detector is at zero.
* refactor: centralize known cli command checks
* feat: emit drift diagnostic for registered-but-unhandled commands
Folds #1055's telemetry into this branch: known-command fall-through now
emits cli_known_command_unhandled at error level alongside the distinct
user-facing message, so catalog/dispatch drift is visible in diagnostics
as well as to the user who hits it.
* feat: add doctor command
* fix: reduce doctor command complexity
* fix: classify doctor integration flags
* fix: simplify doctor setup
* refactor: split doctor checks
* fix: simplify doctor check set
* fix: include stopped android avds in devices
* fix: report doctor device inventory
* refactor: reuse device inventory selectors
* fix: summarize doctor inventory by platform
* fix: show metro cwd in doctor
* refactor: simplify metro doctor lookup
* fix: update doctor imports after apple consolidation
* feat: make doctor Metro probe controllable and surface hidden toolchain failures
Two gaps found while verifying the doctor command on a real environment:
- Metro host/port were uncontrollable from the CLI: --metro-host/--metro-port
were rejected by allowedFlags, and readDoctorOptions only read them from
req.runtime (populated by remote/connection profiles, never a plain CLI
flag). The Metro check's own hint told users to 'pass the correct
--metro-host/--metro-port', which did not exist. Declare the flags and read
them from req.flags (runtime kept as fallback) so the probe can target any
endpoint, e.g. from outside an RN/Expo project directory.
- A broken per-platform toolchain was silently hidden: readDoctorDeviceInventory
dropped inventory failures whenever any other platform returned devices, so a
broken Xcode or Android SDK still reported a green 'pass'. Keep the failures
and surface each as a warn (device-<platform>) when other platforms have
devices; scoped --platform runs stay quiet.
* fix: align doctor CI expectations
* feat: extend doctor preflight checks
* fix: keep doctor checks within ci gates
* fix: simplify doctor metro surface
* refactor: trim doctor bundle impact
* fix: restore useful doctor diagnostics
* refactor: reuse doctor output helpers
* refactor: share device inventory grouping
* refactor: keep doctor focused on preflight checks
* refactor: simplify doctor toolchain probes
* fix: keep scoped simulator hint generic
* fix: clarify doctor Xcode selection context
* fix: recognize provider scope in remote doctor
* fix: address doctor review gaps
* fix: keep doctor metro checks inferred
* refactor(types): reuse canonical string-unions across the codebase
Replace inline duplicates of SessionSurface, ClickButton, DeviceRotation,
BackMode, ScrollDirection, Platform/PlatformSelector/DeviceTarget,
AlertAction, and LeaseBackend with imports of the existing canonical types.
Extract BackMode into a core/back-mode.ts leaf (matching click-button.ts /
session-surface.ts) to keep backend.ts dependency-clean. Add 'satisfies
readonly T[]' drift-guards to the runtime enum tuples consumed by enumField.
Pure refactor: single source of truth for these unions, no behavior change.
* refactor(types): consolidate gesture + daemon/network/metro mode unions
- Add SwipePattern to core/scroll-gesture.ts; reuse SwipePreset,
ScrollInputDirection, ScrollDirection at remaining inline sites.
- Move DaemonServerMode / DaemonTransportPreference / SessionIsolationMode /
NetworkIncludeMode to contracts.ts (the client<->daemon boundary); daemon/
config.ts and daemon/network-log.ts re-export them. Drop client-types.ts's
three private alias copies.
- Reuse MetroPrepareKind in metro.ts and remote-config-schema.ts.
- Add satisfies-guards to the runtime enum tuples.
Pure refactor, type-only changes.
* refactor(types): single-source remaining string-literal unions
- ElementSelectorKey (core/interactor-types.ts) for the 'id|label|text|value'
selector-key subset across core/daemon/ios/maestro.
- GESTURE_KINDS + GestureKind (command-catalog.ts), dropping the duplicate
GESTURE_KIND_VALUES.
- AndroidTextInputAction (android/adb-executor.ts); reuse NetworkLogBackend for
the app-log backend union.
- Reuse contracts.ts JsonRpcId / JsonRpcRequestEnvelope in mcp router + server.
- New commands/log-command-contract.ts (LOG_ACTION_VALUES + LogAction),
mirroring perf-command-contract.ts.
- Named Android snapshot helper metadata unions (transport/captureMode/install
reason) shared between helper + backend metadata.
Pure refactor, type-only changes; full unit suite green.
* refactor(types): consolidate geometry shapes
- Reuse canonical Rect (utils/snapshot.ts) for inline {x,y,width,height}
literals (parsing, output, screenshot-diff regions/ocr, atspi-bridge).
- GestureReferenceFrame (core/scroll-gesture.ts) as the single reference-frame
type; TouchReferenceFrame becomes an alias; replace inline
{referenceWidth,referenceHeight} across daemon + maestro + commands.
- New ImageDimensions (screenshot-geometry.ts) and MovementRange
(screenshot-diff-ocr.ts) for repeated {width,height} / {min,max} shapes.
- File-local AndroidRecordingSize in record-trace-android.ts.
Pure refactor, type-only changes. Point literals with normalized-vs-absolute
semantics deliberately left untouched.
* refactor(types): consolidate result/options/resolver shapes (+oxfmt)
- Generic PlatformProviderResolver<T> collapses 6 near-identical resolver
types in request-platform-providers.ts.
- Reuse canonical DaemonError (contracts.ts) / NormalizedError (utils/errors.ts)
for the inline error DTOs in daemon/types.ts and utils/output.ts.
- Single DaemonFailureResponse: FailedDaemonResponse becomes an alias and the
maestro-local redefinition is dropped.
- Extract one shared toBackendResult() into commands/runtime-types.ts, deleting
5 duplicate copies.
- Normalize whitespace with oxfmt across the touched files.
Full unit suite green.
* refactor(types): share TransformGestureParams + RepeatedInput bundles
- TransformGestureParams (core/scroll-gesture.ts) replaces 4 identical inline
transform-gesture param shapes (core interactor, dispatch, android multitouch,
client options).
- Reuse RepeatedInput (commands/command-input.ts) for the tap-modifier bundle in
PressCommandOptions and BackendTapOptions.
Pure refactor, type-only.
* refactor(types): extract BackendResultEnvelope mix-in
Replace the repeated inline { backendResult?: Record<string, unknown>;
message?: string } pair on ~17 single-object command result types with
'& BackendResultEnvelope' (commands/runtime-types.ts). Discriminated-union
variants and single-field result types are intentionally left inline.
Pure refactor, type-only changes; failing files re-verified green in isolation
(full-suite failures were flaky timeouts).
* refactor(types): dedupe runner/replay/selector/exec result shapes
- RunnerSessionOptions = AppleRunnerLifecycleOptions (field-identical).
- Reuse ReplayActionBlockInvoker for the maestro + daemon replay invoker types.
- SelectorSnapshotOptions aliases the canonical SelectorSnapshotInput; annotate
selectorSnapshotOptionsFromFlags.
- Reuse ExecResult for the { stdout; stderr; exitCode } subset in
record-trace-errors and app-log-process.
Pure refactor, type-only.
* refactor(types): make canonical tuples the single source for enum unions
Each string-enum module now exports an 'as const' literal tuple as the single
source and derives its union via (typeof TUPLE)[number]: SESSION_SURFACES,
CLICK_BUTTONS, DEVICE_ROTATIONS, BACK_MODES, SCROLL_DIRECTIONS/SWIPE_PRESETS/
SWIPE_PATTERNS, SCROLL_INPUT_DIRECTIONS, ALERT_ACTIONS, DEVICE_TARGETS. The
command metadata/input files import these tuples instead of redefining local
*_VALUES copies, which also retires 10 now-redundant 'satisfies' drift-guards.
Member order preserved everywhere (enumField error messages unchanged). Platform
tuples intentionally left as-is (command-input vs contracts use different orders).
Net -39 lines; tuple+type dual maintenance eliminated for these unions.