mobile-ai-agents 1.0.35 → 1.0.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +5 -1
- package/CLAUDE.md +1 -1
- package/README.md +18 -3
- package/agents/cross-platform/mobile-harness/agent.md +183 -38
- package/cli/commands/doctor.js +182 -0
- package/cli/commands/harness.js +262 -0
- package/cli/commands/mcp.js +330 -0
- package/cli/commands/router.js +34 -0
- package/cli/index.js +28 -37
- package/cli/lib/options.js +24 -0
- package/docs/android-device-setup.md +138 -0
- package/docs/index.html +7 -1
- package/docs/installation.md +8 -0
- package/docs/mobile-harness-enterprise.md +163 -0
- package/docs/mobile-harness.md +18 -0
- package/docs/mobile-mcp.md +14 -0
- package/docs/release-process.md +1 -0
- package/docs/wiki.md +11 -0
- package/package.json +4 -1
- package/skills/cross-platform/mobile-mcp-qa.md +14 -0
- package/templates/android-device-config.json +16 -0
- package/templates/mobile-harness-evidence.example.json +36 -0
- package/templates/mobile-harness-evidence.schema.json +31 -0
- package/templates/mobile-harness-policy.json +60 -0
- package/templates/mobile-harness-policy.schema.json +122 -0
- package/templates/mobile-harness-result.example.json +44 -0
- package/templates/mobile-harness-result.schema.json +84 -0
- package/workflows/mobile-harness.md +39 -11
package/AGENTS.md
CHANGED
|
@@ -101,7 +101,7 @@ Use the agent at agents/android/code-reviewer/agent.md to review this file:
|
|
|
101
101
|
| Release Notes Generator | `agents/cross-platform/release-notes-generator/` | All | Git commits → user-facing release notes |
|
|
102
102
|
| CI/CD Pipeline Generator | `agents/cross-platform/ci-cd-generator/` | All | Generates GitHub Actions / Bitrise / Fastlane configs |
|
|
103
103
|
| Store Listing Writer | `agents/cross-platform/store-listing-writer/` | All | Play Store / App Store descriptions optimised for ASO |
|
|
104
|
-
| Mobile Harness | `agents/cross-platform/mobile-harness/` | All | Guide
|
|
104
|
+
| Mobile Harness | `agents/cross-platform/mobile-harness/` | All | Guide projects from idea through verified delivery; enterprise mode adds policy, risk gates, scoped changes, rollback, and auditable evidence |
|
|
105
105
|
|
|
106
106
|
---
|
|
107
107
|
|
|
@@ -109,6 +109,10 @@ Use the agent at agents/android/code-reviewer/agent.md to review this file:
|
|
|
109
109
|
|
|
110
110
|
Use `skills/cross-platform/mobile-mcp-qa.md` and `workflows/mobile-mcp-qa.md` when a user wants device, emulator, or simulator QA through Mobile MCP. This belongs after implementation, during APPFORGE UI match review, full QA, launch readiness, or screenshot validation.
|
|
111
111
|
|
|
112
|
+
## Enterprise Mobile Harness
|
|
113
|
+
|
|
114
|
+
Use `HARNESS_POLICY.json` when a team needs governed Mobile Harness runs. Initialize it with `npx mobile-ai-agents harness policy init --profile enterprise`, validate it, then follow `docs/mobile-harness-enterprise.md`. Governed runs must classify risk, constrain scope, enforce required gates, define rollback for high-risk work, and produce schema-backed `result.json` plus SHA-256 `evidence.json`.
|
|
115
|
+
|
|
112
116
|
## Android ANR Investigation
|
|
113
117
|
|
|
114
118
|
Use `skills/android/anr-investigation.md` when a user provides an Android ANR trace, Play Console cluster, Crashlytics ANR, `ApplicationExitInfo`, or related source code. The skill requires evidence-based thread, lock, binder, and timeout analysis before recommending a fix.
|
package/CLAUDE.md
CHANGED
|
@@ -86,7 +86,7 @@ mobile-ai-agents/
|
|
|
86
86
|
| **FORGE** | `agents/gaming/forge/` | Unity | C# performance + frame budget + architecture |
|
|
87
87
|
| **UNREAL** | `agents/gaming/unreal/` | Unreal | Blueprint → C++ + performance + GC safety |
|
|
88
88
|
| **APPFORGE** | `agents/cross-platform/appforge/` | All | Rough app idea → PRD → tasks → QA → Play Store |
|
|
89
|
-
| **MOBILE-HARNESS** | `agents/cross-platform/mobile-harness/` | All | Build
|
|
89
|
+
| **MOBILE-HARNESS** | `agents/cross-platform/mobile-harness/` | All | Build and verify mobile work; enterprise mode adds policy gates, risk, rollback, and auditable evidence |
|
|
90
90
|
| **CRASHER** | `agents/cross-platform/crasher/` | All | Crash log → root cause → fix |
|
|
91
91
|
| **SENTINEL** | `agents/cross-platform/sentinel/` | All | OWASP Mobile Top 10 security audit |
|
|
92
92
|
| **Mobile Memory** | `agents/cross-platform/mobile-memory/` | All | Mobile knowledge graph + context preservation |
|
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ Android · iOS · Flutter · React Native · Kotlin Multiplatform · Unity · Un
|
|
|
11
11
|
[](https://github.com/salmanashraf/mobile-agency/stargazers)
|
|
12
12
|
[](#core-loops-and-platform-plugins)
|
|
13
13
|
|
|
14
|
-
[Installation Guide](docs/installation.md) · [Core Loops](docs/core-loops.md) · [mobile-karpathy](mobile-karpathy.md) · [Wiki](https://github.com/salmanashraf/mobile-
|
|
14
|
+
[Installation Guide](docs/installation.md) · [Android Device Setup](docs/android-device-setup.md) · [Core Loops](docs/core-loops.md) · [mobile-karpathy](mobile-karpathy.md) · [Wiki](https://github.com/salmanashraf/mobile-ai-agents/wiki) · [Getting Started](docs/getting-started.md) · [Contributing](CONTRIBUTING.md)
|
|
15
15
|
|
|
16
16
|
> AI agents, skills, and Loop Engineering workflows for building, testing, reviewing, and shipping Android, iOS, Flutter, React Native, Unity, and Unreal apps.
|
|
17
17
|
|
|
@@ -398,6 +398,14 @@ Mobile MCP fits the QA stage next: use it for emulator, simulator, or real-devic
|
|
|
398
398
|
|
|
399
399
|
Mobile MCP gives Mobile AI Agents a device automation layer for iOS and Android simulators, emulators, and real devices.
|
|
400
400
|
|
|
401
|
+
Prepare an Android project with environment diagnostics and settings-preserving MCP setup:
|
|
402
|
+
|
|
403
|
+
```bash
|
|
404
|
+
npx mobile-ai-agents doctor --platform android
|
|
405
|
+
npx mobile-ai-agents mcp setup --client claude
|
|
406
|
+
npx mobile-ai-agents mcp config init --app-id com.example.app
|
|
407
|
+
```
|
|
408
|
+
|
|
401
409
|
```
|
|
402
410
|
/mobile-mcp-qa
|
|
403
411
|
1. List devices
|
|
@@ -408,7 +416,7 @@ Mobile MCP gives Mobile AI Agents a device automation layer for iOS and Android
|
|
|
408
416
|
6. Produce MOBILE_MCP_QA_REPORT.md
|
|
409
417
|
```
|
|
410
418
|
|
|
411
|
-
Use it inside APPFORGE Stage 7 Full QA, UI match review, launch readiness checks, and screenshot validation.
|
|
419
|
+
Use it inside APPFORGE Stage 7 Full QA, UI match review, launch readiness checks, and screenshot validation. Guides: [Android Device QA Setup](docs/android-device-setup.md), [wiki guide](https://github.com/salmanashraf/mobile-ai-agents/wiki/Android-Device-QA-Setup), and [Mobile MCP Integration](docs/mobile-mcp.md).
|
|
412
420
|
|
|
413
421
|
### Device Proof Reports
|
|
414
422
|
|
|
@@ -431,6 +439,13 @@ device-proof-report
|
|
|
431
439
|
|
|
432
440
|
Mobile Harness is the Loop Engineering orchestrator for Mobile AI Agents. It can start from a rough app idea or an existing codebase, then coordinate APPFORGE, Mobile Memory, platform reviewers, tests, UI verification, Mobile MCP QA, accessibility, performance, security, release prep, and store/growth work.
|
|
433
441
|
|
|
442
|
+
For larger engineering organizations, Mobile Harness supports a governed execution contract with repository policy, run IDs, baseline commits, change budgets, risk-based gates, protected paths, accountable exceptions, rollback plans, SHA-256 evidence manifests, and machine-readable CI results. See [Mobile Harness Enterprise Contract](docs/mobile-harness-enterprise.md).
|
|
443
|
+
|
|
444
|
+
```bash
|
|
445
|
+
npx mobile-ai-agents harness policy init --profile enterprise
|
|
446
|
+
npx mobile-ai-agents harness policy validate
|
|
447
|
+
```
|
|
448
|
+
|
|
434
449
|
Use it when you want to define the goal once and have the system keep moving through the app-building loop without repeating the same manual prompts at every stage.
|
|
435
450
|
|
|
436
451
|
Use `BEGINNER_GUIDED` mode when this is your first mobile app or you are unsure about the platform or stack. Use APPFORGE alone when you only want planning documents; use Mobile Harness when you want implementation, verification, device proof, memory, and the next task too.
|
|
@@ -542,7 +557,7 @@ Design direction: Playful gamified, polished consumer app. Colorful but clean. S
|
|
|
542
557
|
Ask clarification first if needed, then run the full loop without asking me to repeat the idea.
|
|
543
558
|
```
|
|
544
559
|
|
|
545
|
-
|
|
560
|
+
Guides: [Mobile Harness](docs/mobile-harness.md) · [Enterprise Contract](docs/mobile-harness-enterprise.md)
|
|
546
561
|
|
|
547
562
|
---
|
|
548
563
|
|
|
@@ -233,12 +233,55 @@ MOBILE-HARNESS verifies against project documents, not memory:
|
|
|
233
233
|
| `TASKS.md` | APPFORGE | Task scope and acceptance criteria |
|
|
234
234
|
| `DEPENDENCIES.md` | APPFORGE / PIPELINE | Libraries, APIs, env vars, build constraints |
|
|
235
235
|
| `ROADMAP.md` | APPFORGE | Sequencing and milestones |
|
|
236
|
+
| `HARNESS_POLICY.json` | Engineering / security / release owners | Risk rules, gates, protected paths, commands, evidence retention, exceptions |
|
|
236
237
|
| `MOBILE_HARNESS_REPORT.md` | MOBILE-HARNESS | Evidence, pass/fail state, next action |
|
|
238
|
+
| `.mobile-ai-agents/harness/runs/<runId>/result.json` | MOBILE-HARNESS | Machine-readable result for CI and audit systems |
|
|
237
239
|
|
|
238
240
|
If an artifact is missing or stale, update it before implementing.
|
|
239
241
|
|
|
240
242
|
---
|
|
241
243
|
|
|
244
|
+
## Enterprise Execution Contract
|
|
245
|
+
|
|
246
|
+
Use this contract whenever `HARNESS_POLICY.json` exists or the user selects an enterprise or regulated profile. For other projects, apply the same model with lightweight defaults and do not force organization-only approvals.
|
|
247
|
+
|
|
248
|
+
### Run Identity And Reproducibility
|
|
249
|
+
|
|
250
|
+
Create a unique `runId` before editing: `<UTC timestamp>-<short baseline commit>-<task slug>`. Record repository, branch, baseline commit, starting dirty state, tool/model identity, approved task source, allowed and protected paths, delivery profile, risk classification, policy version, and required gates.
|
|
251
|
+
|
|
252
|
+
Write artifacts under `.mobile-ai-agents/harness/runs/<runId>/`. Never overwrite another run. Resume the same run by appending a checkpoint; when scope or baseline changes materially, start a linked run.
|
|
253
|
+
|
|
254
|
+
### Risk Classification
|
|
255
|
+
|
|
256
|
+
| Risk | Typical Change | Minimum Handling |
|
|
257
|
+
|---|---|---|
|
|
258
|
+
| `low` | Copy, isolated styling, docs | Scope check and focused tests |
|
|
259
|
+
| `medium` | Ordinary feature or refactor | Platform review, static analysis, tests, PRD verification |
|
|
260
|
+
| `high` | Auth, payments, sensitive storage, permissions, migrations, CI/signing | Owner approval, integration/device QA, security and supply-chain review, rollback plan |
|
|
261
|
+
| `regulated` | Health, finance, children, identity, legal retention, controlled data | High-risk gates plus privacy review, traceable approval, retention controls, release approval |
|
|
262
|
+
|
|
263
|
+
Use the highest matching policy rule. Never lower risk to avoid a gate.
|
|
264
|
+
|
|
265
|
+
### Scope And Change Budget
|
|
266
|
+
|
|
267
|
+
Before editing, state allowed files/modules and expected file count. Compare actual changes against that budget. Any out-of-scope file, protected path, unrelated user change, generated lockfile, dependency update, or migration must be explained and re-approved when policy requires it. Do not absorb unrelated dirty-worktree changes into harness evidence.
|
|
268
|
+
|
|
269
|
+
### Gates And Exceptions
|
|
270
|
+
|
|
271
|
+
Read required gates from `HARNESS_POLICY.json`. A required gate can be `PASS`, `FAIL`, `BLOCKED`, or `WAIVED`; it cannot silently become `SKIPPED`. A waiver is valid only when policy permits it and records an ID, gate, accountable owner, reason, tracking ticket, creation time, and expiry. Expired waivers fail the gate. Never waive a gate listed in `exceptions.forbiddenGates`.
|
|
272
|
+
|
|
273
|
+
### Evidence And Claims
|
|
274
|
+
|
|
275
|
+
Every PASS claim must reference an artifact ID in `evidence.json`. Evidence records use relative paths, UTC timestamps, producer, SHA-256, and the command or source that created them. Redact secrets and personal data before hashing or storage. Never fabricate screenshots, command output, approvals, hashes, or device results.
|
|
276
|
+
|
|
277
|
+
Produce `MOBILE_HARNESS_REPORT.md` for people, `result.json` for CI/governance tools, and append-only `evidence.json`. Use the versioned schemas in `templates/mobile-harness-*.schema.json`.
|
|
278
|
+
|
|
279
|
+
### Rollback And Recovery
|
|
280
|
+
|
|
281
|
+
For high or regulated risk, define rollback before implementation: trigger, owner, exact revert/feature-flag/data-recovery action, validation command, and maximum recovery time. A data migration also needs backup/restore evidence and forward-fix limits. Git revert alone does not prove data rollback.
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
242
285
|
## Operating Rules
|
|
243
286
|
|
|
244
287
|
- MOBILE-HARNESS is the top-level orchestrator; route work to APPFORGE, Mobile Memory, reviewers, skills, and workflows as needed.
|
|
@@ -248,6 +291,8 @@ If an artifact is missing or stale, update it before implementing.
|
|
|
248
291
|
- If terminal access is available, initialize local memory with `npx mobile-ai-agents memory init` and capture durable decisions, stage completions, findings, and next actions with `npx mobile-ai-agents memory capture`.
|
|
249
292
|
- Work on one task only.
|
|
250
293
|
- Do not modify unrelated files.
|
|
294
|
+
- Establish run identity, baseline commit, risk classification, policy, scope, and required gates before editing.
|
|
295
|
+
- Preserve unrelated user changes and fail the scope gate when changes exceed the approved budget without authorization.
|
|
251
296
|
- Read dependencies before implementation.
|
|
252
297
|
- Route the task to the narrowest relevant agent or skill and record that choice in Orchestration State; MOBILE-HARNESS retains ownership of the result.
|
|
253
298
|
- Use `/mobile-app-design` for new screens, redesigns, reskins, navigation changes, and design-system work; require approval of multi-screen reskin plans before editing.
|
|
@@ -257,6 +302,7 @@ If an artifact is missing or stale, update it before implementing.
|
|
|
257
302
|
- Verify UI against the design artifact, not memory.
|
|
258
303
|
- Use Mobile MCP for device, emulator, or simulator evidence when available.
|
|
259
304
|
- Capture screenshots, element lists, and failures in the report.
|
|
305
|
+
- Record evidence by artifact ID in `evidence.json`; narrative statements alone are not proof.
|
|
260
306
|
- Update `MOBILE_MEMORY.md` after every approved stage, completed task, blocker, failed QA pass, and end-of-day checkpoint.
|
|
261
307
|
- Run the `mobile-flight-recorder` workflow at the end of each session so changed files, commands, evidence, blockers, and the next action survive the handoff.
|
|
262
308
|
- Run `npx mobile-ai-agents memory checkpoint` when local memory should produce or refresh `MOBILE_MEMORY.md`.
|
|
@@ -275,6 +321,15 @@ Status: PASS | FAIL | BLOCKED
|
|
|
275
321
|
Mode: BEGINNER_GUIDED | IDEA_TO_STORE | EXISTING_PROJECT | FEATURE_EXECUTION | QA_ONLY
|
|
276
322
|
Delivery Profile: SMALLEST_MVP | DEMO_GRADE_MVP | PRODUCTION_READY_MVP
|
|
277
323
|
Current Loop Stage: PLAN | DESIGN | TASK | IMPLEMENT | VERIFY | DEVICE_PROOF | MEMORY | NEXT_ACTION
|
|
324
|
+
Run ID:
|
|
325
|
+
Policy: <path + version | lightweight defaults>
|
|
326
|
+
Risk: low | medium | high | regulated
|
|
327
|
+
Repository State: <branch, baseline commit, head commit, dirty-at-start>
|
|
328
|
+
|
|
329
|
+
Scope:
|
|
330
|
+
- Allowed paths:
|
|
331
|
+
- Changed files:
|
|
332
|
+
- Out-of-scope files:
|
|
278
333
|
|
|
279
334
|
Beginner Checkpoint:
|
|
280
335
|
- What happened:
|
|
@@ -283,23 +338,31 @@ Beginner Checkpoint:
|
|
|
283
338
|
- What happens next:
|
|
284
339
|
|
|
285
340
|
Artifacts Read:
|
|
286
|
-
| Artifact | Status | Notes |
|
|
287
|
-
|
|
341
|
+
| Artifact | Status | Evidence IDs | Notes |
|
|
342
|
+
|---|---|---|---|
|
|
288
343
|
|
|
289
344
|
Orchestration State:
|
|
290
345
|
| Stage | Tool | Status | Evidence |
|
|
291
346
|
|---|---|---|---|
|
|
292
347
|
|
|
348
|
+
Gate Decisions:
|
|
349
|
+
| Gate | Required | Status | Evidence IDs | Exception ID |
|
|
350
|
+
|---|---|---|---|---|
|
|
351
|
+
|
|
352
|
+
Exceptions:
|
|
353
|
+
| ID | Gate | Owner | Reason | Ticket | Expires |
|
|
354
|
+
|---|---|---|---|---|---|
|
|
355
|
+
|
|
293
356
|
Implementation Summary:
|
|
294
|
-
- <changed file and purpose>
|
|
357
|
+
- <changed file and purpose> [evidence ID]
|
|
295
358
|
|
|
296
359
|
Code Review:
|
|
297
|
-
| Reviewer | Finding | Status |
|
|
298
|
-
|
|
360
|
+
| Reviewer | Finding | Status | Evidence IDs |
|
|
361
|
+
|---|---|---|---|
|
|
299
362
|
|
|
300
363
|
Tests:
|
|
301
|
-
| Command | Result | Notes |
|
|
302
|
-
|
|
364
|
+
| Command | Result | Evidence IDs | Notes |
|
|
365
|
+
|---|---|---|---|
|
|
303
366
|
|
|
304
367
|
PRD Verification:
|
|
305
368
|
| Requirement | Source | Result | Evidence |
|
|
@@ -308,6 +371,7 @@ PRD Verification:
|
|
|
308
371
|
UI Match:
|
|
309
372
|
Match: <percentage>
|
|
310
373
|
Source: <DESIGN.md section, wireframe, or screenshot>
|
|
374
|
+
Evidence: <evidence IDs>
|
|
311
375
|
Differences:
|
|
312
376
|
- <difference>
|
|
313
377
|
Fixes:
|
|
@@ -316,7 +380,7 @@ Fixes:
|
|
|
316
380
|
Mobile MCP QA:
|
|
317
381
|
Device:
|
|
318
382
|
Screenshots:
|
|
319
|
-
- <screen
|
|
383
|
+
- <evidence ID>: <screen>
|
|
320
384
|
Result: PASS | FAIL | SKIPPED
|
|
321
385
|
|
|
322
386
|
Acceptance Criteria:
|
|
@@ -326,6 +390,12 @@ Acceptance Criteria:
|
|
|
326
390
|
Memory Update:
|
|
327
391
|
- <what changed in project memory>
|
|
328
392
|
|
|
393
|
+
Evidence Manifest:
|
|
394
|
+
- <path to evidence.json>
|
|
395
|
+
|
|
396
|
+
Rollback:
|
|
397
|
+
- <NOT_REQUIRED, or trigger + owner + action + validation>
|
|
398
|
+
|
|
329
399
|
Remaining Issues:
|
|
330
400
|
- <issue or "Nothing">
|
|
331
401
|
|
|
@@ -351,6 +421,10 @@ Coordinate specialized systems:
|
|
|
351
421
|
|
|
352
422
|
Require approved PRD, design, task, and dependency context before coding. If missing, create it through APPFORGE and wait for approval. Choose a delivery profile and design direction first.
|
|
353
423
|
|
|
424
|
+
When HARNESS_POLICY.json exists or enterprise/regulated delivery is requested, enforce the Enterprise Execution Contract. Before editing, create a runId and immutable run directory; record branch, baseline commit, dirty state, task source, risk classification, allowed and protected paths, policy version, change budget, and required gates. High and regulated risk require an executable rollback plan.
|
|
425
|
+
|
|
426
|
+
A required gate may be PASS, FAIL, BLOCKED, or policy-authorized WAIVED, never silently skipped. A waiver requires owner, reason, ticket, and expiry and cannot waive a forbidden gate. Every PASS claim must cite a SHA-256 artifact in evidence.json. Redact secrets and personal data. Never invent command output, screenshots, approvals, or evidence. Produce MOBILE_HARNESS_REPORT.md plus versioned result.json and evidence.json for CI and audit systems.
|
|
427
|
+
|
|
354
428
|
Use BEGINNER_GUIDED mode for a rough idea, a new mobile developer, or an unknown platform/stack. Ask only what cannot be inferred. Recommend one stack with plain-language reasons and tradeoffs. Create beginner-readable PRD.md, DESIGN.md, TASKS.md, DEPENDENCIES.md, ROADMAP.md, and MOBILE_MEMORY.md. Show the current stage in this fixed loop: PLAN -> DESIGN -> TASK -> IMPLEMENT -> VERIFY -> DEVICE PROOF -> MEMORY -> NEXT ACTION. Explain what happened, why it matters, any decision needed, and what happens next.
|
|
355
429
|
|
|
356
430
|
Stop for product approval, credentials, destructive or irreversible actions, paid services, and public release approval. For demos or marketing, default to Demo-grade MVP with seeded data, multiple visible states, screenshot planning, and UI polish.
|
|
@@ -364,6 +438,39 @@ Output MUST follow the exact format specified. Do not add extra sections or omit
|
|
|
364
438
|
|
|
365
439
|
---
|
|
366
440
|
|
|
441
|
+
## Rough Idea To Verified Feature Example
|
|
442
|
+
|
|
443
|
+
```text
|
|
444
|
+
ROUGH IDEA
|
|
445
|
+
"An offline habit tracker for busy parents"
|
|
446
|
+
|
|
447
|
+
PLAN
|
|
448
|
+
APPFORGE creates PRD.md, ROADMAP.md, and acceptance criteria. The user approves the smallest useful outcome: create one habit and mark today complete.
|
|
449
|
+
|
|
450
|
+
DESIGN
|
|
451
|
+
DESIGN.md defines dashboard empty/populated states, add-habit form, completion feedback, accessibility, and screenshot targets. The user approves the direction.
|
|
452
|
+
|
|
453
|
+
TASK
|
|
454
|
+
TASKS.md splits storage, add-habit UI, completion behavior, and device proof. Mobile Harness selects only the storage task.
|
|
455
|
+
|
|
456
|
+
ENTERPRISE PREFLIGHT
|
|
457
|
+
The harness creates runId, records baseline commit and dirty state, classifies the task medium risk, loads HARNESS_POLICY.json, approves allowed paths, and identifies required gates.
|
|
458
|
+
|
|
459
|
+
IMPLEMENT
|
|
460
|
+
The platform specialist implements only the selected task without touching unrelated files.
|
|
461
|
+
|
|
462
|
+
VERIFY
|
|
463
|
+
Build, static analysis, unit tests, platform review, and /prd-verification produce hashed evidence. Actual changed files are compared with approved scope.
|
|
464
|
+
|
|
465
|
+
DEVICE PROOF
|
|
466
|
+
/mobile-mcp-qa creates and completes a habit, restarts the app, and captures screenshots. device-proof-report records the result.
|
|
467
|
+
|
|
468
|
+
RESULT AND MEMORY
|
|
469
|
+
MOBILE_HARNESS_REPORT.md, result.json, and evidence.json are written. Mobile Memory and mobile-flight-recorder save decisions, evidence, and exactly one next task.
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
---
|
|
473
|
+
|
|
367
474
|
## Example
|
|
368
475
|
|
|
369
476
|
### Input
|
|
@@ -400,48 +507,80 @@ Platform: Android
|
|
|
400
507
|
Task: Implement invoice creation form
|
|
401
508
|
Status: FAIL
|
|
402
509
|
Mode: FEATURE_EXECUTION
|
|
510
|
+
Delivery Profile: PRODUCTION_READY_MVP
|
|
511
|
+
Current Loop Stage: VERIFY
|
|
512
|
+
Run ID: 2026-10-05T08-30-00Z-a1b2c3d-task-4-invoice-form
|
|
513
|
+
Policy: HARNESS_POLICY.json v1
|
|
514
|
+
Risk: medium
|
|
515
|
+
Repository State: feature/invoices, baseline a1b2c3d, head d4e5f6a, dirty-at-start false
|
|
516
|
+
|
|
517
|
+
Scope:
|
|
518
|
+
- Allowed paths: app/invoice/**, test/invoice/**
|
|
519
|
+
- Changed files: InvoiceFormScreen.kt, InvoiceViewModel.kt, InvoiceDao.kt
|
|
520
|
+
- Out-of-scope files: None
|
|
521
|
+
|
|
522
|
+
Beginner Checkpoint:
|
|
523
|
+
- What happened: Task 4 was implemented and verified against the approved artifacts.
|
|
524
|
+
- Why it matters: The form works, but persistence and UI evidence block completion.
|
|
525
|
+
- Decision needed: None
|
|
526
|
+
- What happens next: Fix persistence, then rerun failed gates.
|
|
403
527
|
|
|
404
528
|
Artifacts Read:
|
|
405
|
-
| Artifact | Status | Notes |
|
|
406
|
-
|
|
407
|
-
| MOBILE_MEMORY.md | PASS | Current feature state loaded |
|
|
408
|
-
| PRD.md | PASS | Invoice creation requires client, amount, due date |
|
|
409
|
-
| DESIGN.md | PASS | Form uses single-column layout and sticky Save button |
|
|
410
|
-
| TASKS.md | PASS | Task 4 selected |
|
|
411
|
-
| DEPENDENCIES.md | PASS | Room and Hilt already configured |
|
|
529
|
+
| Artifact | Status | Evidence IDs | Notes |
|
|
530
|
+
|---|---|---|---|
|
|
531
|
+
| MOBILE_MEMORY.md | PASS | memory-001 | Current feature state loaded |
|
|
532
|
+
| PRD.md | PASS | artifact-001 | Invoice creation requires client, amount, due date |
|
|
533
|
+
| DESIGN.md | PASS | artifact-002 | Form uses single-column layout and sticky Save button |
|
|
534
|
+
| TASKS.md | PASS | artifact-003 | Task 4 selected |
|
|
535
|
+
| DEPENDENCIES.md | PASS | artifact-004 | Room and Hilt already configured |
|
|
536
|
+
| HARNESS_POLICY.json | PASS | policy-001 | Enterprise policy version 1 loaded |
|
|
412
537
|
|
|
413
538
|
Orchestration State:
|
|
414
539
|
| Stage | Tool | Status | Evidence |
|
|
415
540
|
|---|---|---|---|
|
|
416
|
-
| Product planning | APPFORGE | PASS |
|
|
417
|
-
| Memory | Mobile Memory | PASS |
|
|
418
|
-
| Implementation | MOBILE-HARNESS | PASS |
|
|
419
|
-
| Device QA | /mobile-mcp-qa | FAIL |
|
|
541
|
+
| Product planning | APPFORGE | PASS | approval-001 |
|
|
542
|
+
| Memory | Mobile Memory | PASS | memory-001 |
|
|
543
|
+
| Implementation | MOBILE-HARNESS | PASS | diff-001 |
|
|
544
|
+
| Device QA | /mobile-mcp-qa | FAIL | device-001 |
|
|
545
|
+
|
|
546
|
+
Gate Decisions:
|
|
547
|
+
| Gate | Required | Status | Evidence IDs | Exception ID |
|
|
548
|
+
|---|---|---|---|---|
|
|
549
|
+
| scope | yes | PASS | diff-001 | - |
|
|
550
|
+
| platform_review | yes | PASS | review-001 | - |
|
|
551
|
+
| tests | yes | PASS | test-001 | - |
|
|
552
|
+
| prd_verification | yes | FAIL | prd-001 | - |
|
|
553
|
+
|
|
554
|
+
Exceptions:
|
|
555
|
+
| ID | Gate | Owner | Reason | Ticket | Expires |
|
|
556
|
+
|---|---|---|---|---|---|
|
|
557
|
+
| - | - | - | No exceptions | - | - |
|
|
420
558
|
|
|
421
559
|
Implementation Summary:
|
|
422
|
-
- app/invoice/ui/InvoiceFormScreen.kt: added Compose form fields and validation.
|
|
423
|
-
- app/invoice/InvoiceViewModel.kt: added state and save action.
|
|
424
|
-
- app/invoice/data/InvoiceDao.kt: added insert call.
|
|
560
|
+
- app/invoice/ui/InvoiceFormScreen.kt: added Compose form fields and validation. [diff-001]
|
|
561
|
+
- app/invoice/InvoiceViewModel.kt: added state and save action. [diff-001]
|
|
562
|
+
- app/invoice/data/InvoiceDao.kt: added insert call. [diff-001]
|
|
425
563
|
|
|
426
564
|
Code Review:
|
|
427
|
-
| Reviewer | Finding | Status |
|
|
428
|
-
|
|
429
|
-
| AXIOM | ViewModel exposes immutable StateFlow and uses viewModelScope | PASS |
|
|
565
|
+
| Reviewer | Finding | Status | Evidence IDs |
|
|
566
|
+
|---|---|---|---|
|
|
567
|
+
| AXIOM | ViewModel exposes immutable StateFlow and uses viewModelScope | PASS | review-001 |
|
|
430
568
|
|
|
431
569
|
Tests:
|
|
432
|
-
| Command | Result | Notes |
|
|
433
|
-
|
|
434
|
-
| ./gradlew testDebugUnitTest | PASS | ViewModel validation tests pass |
|
|
570
|
+
| Command | Result | Evidence IDs | Notes |
|
|
571
|
+
|---|---|---|---|
|
|
572
|
+
| ./gradlew testDebugUnitTest | PASS | test-001 | ViewModel validation tests pass |
|
|
435
573
|
|
|
436
574
|
PRD Verification:
|
|
437
575
|
| Requirement | Source | Result | Evidence |
|
|
438
576
|
|---|---|---|---|
|
|
439
|
-
| User can save invoice offline | PRD.md > Offline Requirements | FAIL |
|
|
440
|
-
| Save button disabled until valid | PRD.md > Functional Requirements | PASS |
|
|
577
|
+
| User can save invoice offline | PRD.md > Offline Requirements | FAIL | device-001 |
|
|
578
|
+
| Save button disabled until valid | PRD.md > Functional Requirements | PASS | screenshot-001 |
|
|
441
579
|
|
|
442
580
|
UI Match:
|
|
443
581
|
Match: 88%
|
|
444
582
|
Source: DESIGN.md > Invoice Form
|
|
583
|
+
Evidence: ui-001
|
|
445
584
|
Differences:
|
|
446
585
|
- Save button vertical padding is 8dp smaller than design.
|
|
447
586
|
- Error text color uses theme error but design expects darker red.
|
|
@@ -452,25 +591,31 @@ Fixes:
|
|
|
452
591
|
Mobile MCP QA:
|
|
453
592
|
Device: Pixel_7_API_35
|
|
454
593
|
Screenshots:
|
|
455
|
-
- invoice-form-empty
|
|
456
|
-
- invoice-form-valid
|
|
457
|
-
- dashboard-after-save
|
|
458
|
-
- dashboard-after-restart
|
|
594
|
+
- screenshot-001: invoice-form-empty
|
|
595
|
+
- screenshot-002: invoice-form-valid
|
|
596
|
+
- screenshot-003: dashboard-after-save
|
|
597
|
+
- screenshot-004: dashboard-after-restart
|
|
459
598
|
Result: FAIL
|
|
460
599
|
|
|
461
600
|
Acceptance Criteria:
|
|
462
601
|
| Criteria | Source | Result | Evidence |
|
|
463
602
|
|---|---|---|---|
|
|
464
|
-
| User can enter required fields | PRD.md > Invoice Flow | PASS |
|
|
465
|
-
| Save disabled until valid | TASKS.md > Task 4 | PASS |
|
|
466
|
-
| Saved invoice appears after restart | PRD.md > Offline Persistence | FAIL |
|
|
467
|
-
| UI matches design within 90% | DESIGN.md > Invoice Form | FAIL |
|
|
603
|
+
| User can enter required fields | PRD.md > Invoice Flow | PASS | device-001 |
|
|
604
|
+
| Save disabled until valid | TASKS.md > Task 4 | PASS | screenshot-001 |
|
|
605
|
+
| Saved invoice appears after restart | PRD.md > Offline Persistence | FAIL | screenshot-004 |
|
|
606
|
+
| UI matches design within 90% | DESIGN.md > Invoice Form | FAIL | ui-001 |
|
|
468
607
|
|
|
469
608
|
Memory Update:
|
|
470
609
|
- Task 4 implementation is partially complete.
|
|
471
610
|
- Tests passed, but Mobile MCP restart persistence failed.
|
|
472
611
|
- NEXT ACTION updated to persistence fix.
|
|
473
612
|
|
|
613
|
+
Evidence Manifest:
|
|
614
|
+
- .mobile-ai-agents/harness/runs/2026-10-05T08-30-00Z-a1b2c3d-task-4-invoice-form/evidence.json
|
|
615
|
+
|
|
616
|
+
Rollback:
|
|
617
|
+
- NOT_REQUIRED for this medium-risk, pre-release feature task.
|
|
618
|
+
|
|
474
619
|
Remaining Issues:
|
|
475
620
|
- Invoice persistence after restart fails.
|
|
476
621
|
- UI match below threshold by 2%.
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const childProcess = require('child_process');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const { parseOptions } = require('../lib/options');
|
|
8
|
+
const { inspectMcpConfiguration, loadProjectConfig } = require('./mcp');
|
|
9
|
+
|
|
10
|
+
function executableOnPath(name, env, fsImpl) {
|
|
11
|
+
const entries = String(env.PATH || '').split(path.delimiter).filter(Boolean);
|
|
12
|
+
for (const entry of entries) {
|
|
13
|
+
const candidate = path.join(entry, name);
|
|
14
|
+
if (fsImpl.existsSync(candidate)) return candidate;
|
|
15
|
+
}
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function defaultRunner(command, args, env) {
|
|
20
|
+
const result = childProcess.spawnSync(command, args, {
|
|
21
|
+
encoding: 'utf8',
|
|
22
|
+
env,
|
|
23
|
+
timeout: 10000,
|
|
24
|
+
});
|
|
25
|
+
return {
|
|
26
|
+
status: result.status,
|
|
27
|
+
stdout: result.stdout || '',
|
|
28
|
+
stderr: result.stderr || '',
|
|
29
|
+
error: result.error ? result.error.message : '',
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function resultStatus(checks) {
|
|
34
|
+
if (checks.some(check => check.status === 'FAIL')) return 'FAIL';
|
|
35
|
+
if (checks.some(check => check.status === 'WARN')) return 'WARN';
|
|
36
|
+
return 'PASS';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function parseAdbDevices(output) {
|
|
40
|
+
return String(output)
|
|
41
|
+
.split(/\r?\n/)
|
|
42
|
+
.slice(1)
|
|
43
|
+
.map(line => line.trim())
|
|
44
|
+
.filter(Boolean)
|
|
45
|
+
.map(line => {
|
|
46
|
+
const [serial, state] = line.split(/\s+/);
|
|
47
|
+
return { serial, state };
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function collectAndroidDoctor(options = {}) {
|
|
52
|
+
const cwd = options.cwd || process.cwd();
|
|
53
|
+
const env = options.env || process.env;
|
|
54
|
+
const homedir = options.homedir || os.homedir();
|
|
55
|
+
const fsImpl = options.fsImpl || fs;
|
|
56
|
+
const runner = options.runner || defaultRunner;
|
|
57
|
+
const checks = [];
|
|
58
|
+
const sdkCandidates = [
|
|
59
|
+
env.ANDROID_HOME,
|
|
60
|
+
env.ANDROID_SDK_ROOT,
|
|
61
|
+
path.join(homedir, 'Library', 'Android', 'sdk'),
|
|
62
|
+
path.join(homedir, 'Android', 'Sdk'),
|
|
63
|
+
].filter(Boolean);
|
|
64
|
+
const sdkRoot = sdkCandidates.find(candidate => fsImpl.existsSync(candidate));
|
|
65
|
+
|
|
66
|
+
checks.push(sdkRoot
|
|
67
|
+
? { id: 'android-sdk', status: 'PASS', summary: `Android SDK found at ${sdkRoot}` }
|
|
68
|
+
: {
|
|
69
|
+
id: 'android-sdk',
|
|
70
|
+
status: 'FAIL',
|
|
71
|
+
summary: 'Android SDK was not found.',
|
|
72
|
+
action: 'Install Android Studio/SDK and set ANDROID_HOME or ANDROID_SDK_ROOT.',
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const adb = executableOnPath('adb', env, fsImpl) ||
|
|
76
|
+
(sdkRoot && fsImpl.existsSync(path.join(sdkRoot, 'platform-tools', 'adb')) ? path.join(sdkRoot, 'platform-tools', 'adb') : null);
|
|
77
|
+
if (!adb) {
|
|
78
|
+
checks.push({
|
|
79
|
+
id: 'adb',
|
|
80
|
+
status: 'FAIL',
|
|
81
|
+
summary: 'adb was not found.',
|
|
82
|
+
action: 'Install Android SDK Platform-Tools and add platform-tools to PATH.',
|
|
83
|
+
});
|
|
84
|
+
} else {
|
|
85
|
+
const version = runner(adb, ['version'], env);
|
|
86
|
+
checks.push(version.status === 0
|
|
87
|
+
? { id: 'adb', status: 'PASS', summary: `adb available at ${adb}`, details: version.stdout.split(/\r?\n/)[0] }
|
|
88
|
+
: { id: 'adb', status: 'FAIL', summary: 'adb exists but could not run.', details: version.stderr || version.error, action: 'Repair Platform-Tools or PATH permissions.' });
|
|
89
|
+
|
|
90
|
+
const devicesResult = runner(adb, ['devices'], env);
|
|
91
|
+
const devices = devicesResult.status === 0 ? parseAdbDevices(devicesResult.stdout) : [];
|
|
92
|
+
checks.push(devicesResult.status !== 0
|
|
93
|
+
? { id: 'android-devices', status: 'FAIL', summary: 'adb could not list devices.', details: devicesResult.stderr || devicesResult.error, action: 'Restart adb and reconnect or authorize the target device.' }
|
|
94
|
+
: devices.some(device => device.state === 'device')
|
|
95
|
+
? { id: 'android-devices', status: 'PASS', summary: `${devices.filter(device => device.state === 'device').length} ready Android device(s).`, details: devices }
|
|
96
|
+
: { id: 'android-devices', status: 'WARN', summary: 'No ready Android device or emulator is connected.', details: devices, action: 'Start an emulator or connect and authorize a device.' });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const emulator = executableOnPath('emulator', env, fsImpl) ||
|
|
100
|
+
(sdkRoot && fsImpl.existsSync(path.join(sdkRoot, 'emulator', 'emulator')) ? path.join(sdkRoot, 'emulator', 'emulator') : null);
|
|
101
|
+
if (!emulator) {
|
|
102
|
+
checks.push({
|
|
103
|
+
id: 'android-emulator',
|
|
104
|
+
status: 'WARN',
|
|
105
|
+
summary: 'Android Emulator command was not found.',
|
|
106
|
+
action: 'Install Android Emulator from SDK Manager if emulator QA is required.',
|
|
107
|
+
});
|
|
108
|
+
} else {
|
|
109
|
+
const avdResult = runner(emulator, ['-list-avds'], env);
|
|
110
|
+
const avds = avdResult.status === 0 ? avdResult.stdout.split(/\r?\n/).map(v => v.trim()).filter(Boolean) : [];
|
|
111
|
+
checks.push(avdResult.status !== 0
|
|
112
|
+
? { id: 'android-emulator', status: 'WARN', summary: 'Emulator exists but AVDs could not be listed.', details: avdResult.stderr || avdResult.error, action: 'Open Android Studio Device Manager and repair the emulator installation.' }
|
|
113
|
+
: avds.length
|
|
114
|
+
? { id: 'android-emulator', status: 'PASS', summary: `${avds.length} Android Virtual Device image(s) available.`, details: avds }
|
|
115
|
+
: { id: 'android-emulator', status: 'WARN', summary: 'No Android Virtual Device images are configured.', action: 'Create an AVD in Android Studio Device Manager.' });
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const mcpConfigs = inspectMcpConfiguration({ cwd, homedir, fsImpl });
|
|
119
|
+
checks.push(mcpConfigs.length
|
|
120
|
+
? { id: 'mobile-mcp', status: 'PASS', summary: `Mobile MCP configured for ${mcpConfigs.map(item => item.client).join(', ')}.`, details: mcpConfigs.map(item => item.file) }
|
|
121
|
+
: { id: 'mobile-mcp', status: 'WARN', summary: 'Mobile MCP is not configured for a supported client.', action: 'Run mobile-ai-agents mcp setup --client <client>.' });
|
|
122
|
+
|
|
123
|
+
const project = loadProjectConfig({ cwd, fsImpl, configPath: options.configPath });
|
|
124
|
+
if (!project.config) {
|
|
125
|
+
checks.push({
|
|
126
|
+
id: 'project-config',
|
|
127
|
+
status: 'WARN',
|
|
128
|
+
summary: 'Android device QA project configuration is missing.',
|
|
129
|
+
action: 'Run mobile-ai-agents mcp config init --app-id com.example.app.',
|
|
130
|
+
});
|
|
131
|
+
} else if (project.errors.length) {
|
|
132
|
+
checks.push({
|
|
133
|
+
id: 'project-config',
|
|
134
|
+
status: 'FAIL',
|
|
135
|
+
summary: 'Android device QA project configuration is invalid.',
|
|
136
|
+
details: project.errors,
|
|
137
|
+
action: 'Run mobile-ai-agents mcp config validate and correct the listed fields.',
|
|
138
|
+
});
|
|
139
|
+
} else {
|
|
140
|
+
const apk = path.resolve(cwd, project.config.apkPath);
|
|
141
|
+
checks.push(fsImpl.existsSync(apk)
|
|
142
|
+
? { id: 'project-config', status: 'PASS', summary: 'Android device QA project configuration is valid.', details: project.file }
|
|
143
|
+
: { id: 'project-config', status: 'WARN', summary: 'Project configuration is valid, but the APK does not exist yet.', details: project.config.apkPath, action: 'Build the configured APK before device QA.' });
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
platform: 'android',
|
|
148
|
+
status: resultStatus(checks),
|
|
149
|
+
checkedAt: new Date().toISOString(),
|
|
150
|
+
checks,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function printDoctorReport(report) {
|
|
155
|
+
console.log('');
|
|
156
|
+
console.log(' Android Environment Doctor');
|
|
157
|
+
console.log(` Result: ${report.status}`);
|
|
158
|
+
console.log('');
|
|
159
|
+
for (const check of report.checks) {
|
|
160
|
+
const icon = check.status === 'PASS' ? '✓' : check.status === 'WARN' ? '!' : '✗';
|
|
161
|
+
console.log(` ${icon} [${check.status}] ${check.summary}`);
|
|
162
|
+
if (check.action) console.log(` Action: ${check.action}`);
|
|
163
|
+
}
|
|
164
|
+
console.log('');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async function cmdDoctor(args) {
|
|
168
|
+
const opts = parseOptions(args);
|
|
169
|
+
const platform = opts.platform === true ? '' : opts.platform;
|
|
170
|
+
if (platform !== 'android') throw new Error('doctor currently requires --platform android.');
|
|
171
|
+
const report = collectAndroidDoctor({ configPath: opts.config === true ? undefined : opts.config });
|
|
172
|
+
if (opts.json) console.log(JSON.stringify(report, null, 2));
|
|
173
|
+
else printDoctorReport(report);
|
|
174
|
+
if (report.status === 'FAIL') process.exitCode = 1;
|
|
175
|
+
return report;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
module.exports = {
|
|
179
|
+
cmdDoctor,
|
|
180
|
+
collectAndroidDoctor,
|
|
181
|
+
parseAdbDevices,
|
|
182
|
+
};
|