Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ This repo encodes invariants as self-declaring gates. The correct response to a

## PR Readiness Checklist
- Static gates first: required checks from **Testing Matrix** pass, `pnpm check:fallow --base origin/main` is clean when code quality/dead-code risk is relevant, CI guards are green, and no conflict markers or unmerged paths remain.
- Do not report a PR as CI-green from a local unit-only run alone: use `pnpm test:unit` for the repo unit bundle, or `vitest run --project unit-core --project android-adb` when invoking Vitest directly. The **Integration Tests** and **Coverage** jobs run the `provider-integration` project, so verify green on the actual PR head across those jobs, not just unit.
- Do not report a PR as CI-green from a local unit-only run alone: use `pnpm test:unit` for the repo unit bundle, or `vitest run --project unit-core --project subprocess-stub` when invoking Vitest directly. The **Integration Tests** and **Coverage** jobs run the `provider-integration` project, so verify green on the actual PR head across those jobs, not just unit.
- Command-surface changes preserve CLI, Node.js, daemon, MCP, help, docs, and SkillGym coverage where that surface is affected. Do not duplicate command contracts across layers.
- Device-facing behavior is not merge-ready until it has real simulator/emulator/device evidence for the changed path. Fixture-backed tests can prove contracts, but they do not replace a live run that creates or observes the artifact/state the feature claims to handle.
- If live verification is blocked, state the blocker, exact command or device needed, and downgrade the PR to residual risk instead of calling it ready.
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@
"test-app:maestro": "node scripts/run-test-app-maestro-suite.mjs",
"test-app:maestro:ios": "node scripts/run-test-app-maestro-suite.mjs --platform ios",
"test-app:maestro:android": "node scripts/run-test-app-maestro-suite.mjs --platform android",
"test": "vitest run --project unit-core --project android-adb",
"test:unit": "vitest run --project unit-core --project android-adb",
"test": "vitest run --project unit-core --project subprocess-stub",
"test:unit": "vitest run --project unit-core --project subprocess-stub",
"test:coverage": "vitest run --coverage",
"test:integration:provider": "vitest run --project provider-integration",
"test:integration:progress": "node --experimental-strip-types scripts/integration-progress.ts",
Expand Down
28 changes: 20 additions & 8 deletions vitest.config.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
import { defineConfig } from 'vitest/config';
import slowTestGateReporter from './scripts/vitest-slow-test-reporter.ts';

const ANDROID_ADB_STUB_TESTS =
'src/platforms/android/__tests__/{app-lifecycle-install,app-lifecycle-open,device-input-state,input-actions,notifications,settings}.test.ts';
// Tests that stub a real binary (adb/xcrun) by mutating process.env.PATH and
// then spawn it, so each case waits real subprocess/retry/poll time. Run at the
// unit suite's default ~7x file parallelism they contend for CPU and their stub
// spawns get starved past an internal budget, so production takes a generic
// failure path and returns a different error than the assertion expects — a
// contention flake whose failing subset shifts between runs (see
// docs/agents/testing.md "tests must not wait real time"). Serialized below with
// per-file isolation so only one such file spawns stubs at a time, the same
// execution contract the pre-split android index.test.ts aggregation provided.
const SUBPROCESS_STUB_TESTS = [
'src/platforms/android/__tests__/{app-lifecycle-install,app-lifecycle-open,device-input-state,input-actions,notifications,settings}.test.ts',
'src/daemon/__tests__/runtime-hints.test.ts',
'src/platforms/apple/core/__tests__/index.test.ts',
];

export default defineConfig({
test: {
Expand All @@ -27,19 +39,19 @@ export default defineConfig({
// The Maestro conformance oracle runs via `node --test` in its own CI
// job (scripts/maestro-conformance), like the layering guard.
],
exclude: [ANDROID_ADB_STUB_TESTS],
exclude: SUBPROCESS_STUB_TESTS,
setupFiles: ['src/__tests__/process-memo-setup.ts'],
},
},
{
// The scripted-adb tests stub the adb binary by mutating process.env
// (PATH, AGENT_DEVICE_TEST_ARGS_FILE) and wait real retry/poll time,
// so the group runs serialized with per-file isolation — the same
// The subprocess-stub tests stub adb/xcrun by mutating process.env
// (PATH, AGENT_DEVICE_TEST_ARGS_FILE) and wait real subprocess/retry/poll
// time, so the group runs serialized with per-file isolation — the same
// execution contract the pre-split android index.test.ts aggregation
// provided without leaking module caches between split files.
test: {
name: 'android-adb',
include: [ANDROID_ADB_STUB_TESTS],
name: 'subprocess-stub',
include: SUBPROCESS_STUB_TESTS,
setupFiles: ['src/__tests__/process-memo-setup.ts'],
fileParallelism: false,
isolate: true,
Expand Down
Loading