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 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 beginners from idea and stack choice through one-task implementation, verification, device proof, and memory |
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, test, verify UI, and run Mobile MCP QA |
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
  [![GitHub Stars](https://img.shields.io/github/stars/salmanashraf/mobile-agency?style=social)](https://github.com/salmanashraf/mobile-agency/stargazers)
12
12
  [![Platform](https://img.shields.io/badge/platforms-Android%20%7C%20iOS%20%7C%20Flutter%20%7C%20RN%20%7C%20KMP%20%7C%20Unity%20%7C%20Unreal-brightgreen)](#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-agency/wiki) · [Getting Started](docs/getting-started.md) · [Contributing](CONTRIBUTING.md)
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. Full guide: [docs/mobile-mcp.md](docs/mobile-mcp.md)
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
- Full guide: [docs/mobile-harness.md](docs/mobile-harness.md)
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/evidence>
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 | PRD/design/tasks approved |
417
- | Memory | Mobile Memory | PASS | Current task loaded from MOBILE_MEMORY.md |
418
- | Implementation | MOBILE-HARNESS | PASS | Three files changed |
419
- | Device QA | /mobile-mcp-qa | FAIL | Restart persistence failed |
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 | App restart loses invoice |
440
- | Save button disabled until valid | PRD.md > Functional Requirements | PASS | Empty form shows disabled Save |
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 | Mobile MCP typed client, amount, due date |
465
- | Save disabled until valid | TASKS.md > Task 4 | PASS | Button disabled on empty form |
466
- | Saved invoice appears after restart | PRD.md > Offline Persistence | FAIL | Dashboard empty after restart |
467
- | UI matches design within 90% | DESIGN.md > Invoice Form | FAIL | 88% match |
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
+ };