taphound 0.2.0-dev.1 → 0.2.0-dev.11

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.
Files changed (447) hide show
  1. package/README.md +49 -149
  2. package/README.zh-CN.md +47 -147
  3. package/assets/skills/taphound-case-suite/SKILL.md +363 -0
  4. package/assets/skills/taphound-case-suite/schemas/base-flow-record.schema.json +22 -0
  5. package/assets/skills/taphound-case-suite/schemas/case-catalog.schema.json +26 -0
  6. package/assets/skills/taphound-case-suite/schemas/case-ledger.schema.json +158 -0
  7. package/assets/skills/taphound-case-suite/schemas/suite-input.schema.json +41 -0
  8. package/assets/skills/taphound-case-suite/schemas/transition.schema.json +61 -0
  9. package/assets/skills/taphound-case-suite/scripts/ledger.mjs +1207 -0
  10. package/assets/skills/taphound-case-suite/templates/base-flow-record.example.json +20 -0
  11. package/assets/skills/taphound-case-suite/templates/suite-input.example.json +27 -0
  12. package/assets/skills/taphound-case-suite/templates/transition.example.json +8 -0
  13. package/assets/skills/taphound-flash/SKILL.md +115 -0
  14. package/assets/skills/taphound-flash/scripts/flash.mjs +585 -0
  15. package/assets/skills/taphound-flash/templates/flash-plan.example.json +15 -0
  16. package/assets/skills/taphound-journey-brief-author/CONTEXT-GUIDE.md +471 -0
  17. package/assets/skills/taphound-journey-brief-author/SKILL.md +494 -0
  18. package/assets/skills/taphound-journey-brief-author/prompts/brief-author-role.md +96 -0
  19. package/assets/skills/taphound-journey-brief-author/prompts/brief-author-role.zh-CN.md +80 -0
  20. package/assets/skills/taphound-journey-brief-author/prompts/context-analyze-project.md +309 -0
  21. package/assets/skills/taphound-journey-brief-author/schemas/context-refresh-result.json +123 -0
  22. package/assets/skills/taphound-journey-brief-author/schemas/project-context-module.json +192 -0
  23. package/assets/skills/taphound-journey-brief-author/schemas/project-context.json +152 -0
  24. package/assets/skills/taphound-journey-brief-author/templates/project-context-module.example.json +58 -0
  25. package/assets/skills/taphound-journey-brief-author/templates/project-context.example.json +51 -0
  26. package/assets/skills/taphound-journey-brief-author/templates/taphound-journey-brief.template.md +63 -0
  27. package/assets/skills/taphound-journey-generator/SKILL.md +500 -0
  28. package/assets/skills/{taphound-ai-journey → taphound-journey-generator}/prompts/check-completion.md +3 -0
  29. package/assets/skills/taphound-journey-generator/prompts/consume-journey-brief.md +52 -0
  30. package/assets/skills/taphound-journey-generator/prompts/generate-step.md +211 -0
  31. package/assets/skills/taphound-journey-generator/prompts/select-flow.md +91 -0
  32. package/assets/skills/taphound-journey-generator/references/bridge.md +35 -0
  33. package/assets/skills/taphound-journey-generator/references/mid-session.md +44 -0
  34. package/assets/skills/taphound-journey-generator/schemas/external-flow.json +37 -0
  35. package/assets/skills/taphound-journey-generator/schemas/flow.json +28 -0
  36. package/assets/skills/taphound-journey-generator/schemas/journey-source.json +25 -0
  37. package/assets/skills/{taphound-ai-journey → taphound-journey-generator}/schemas/observe-output.json +13 -4
  38. package/assets/skills/{taphound-ai-journey → taphound-journey-generator}/schemas/proposed-step-envelope.json +127 -5
  39. package/assets/skills/taphound-journey-generator/scripts/envelope.mjs +561 -0
  40. package/assets/skills/taphound-journey-generator/templates/external-flow.example.json +22 -0
  41. package/assets/skills/taphound-journey-generator/templates/flow.example.json +22 -0
  42. package/assets/skills/taphound-journey-generator/templates/journey-source.example.json +17 -0
  43. package/assets/skills/taphound-journey-generator/templates/taphound-journey-brief.example.md +70 -0
  44. package/assets/skills/taphound-verify-change/SKILL.md +91 -0
  45. package/assets/skills/taphound-verify-change/references/accept.md +38 -0
  46. package/assets/skills/taphound-verify-change/references/preserve.md +349 -0
  47. package/assets/skills/taphound-verify-change/schemas/workflow-manifest.schema.json +295 -0
  48. package/assets/skills/taphound-verify-change/scripts/handoff.mjs +482 -0
  49. package/assets/skills/taphound-verify-change/scripts/ui-refactor.mjs +505 -0
  50. package/dist/adapters/adb/adb-adapter.d.ts +16 -2
  51. package/dist/adapters/adb/adb-adapter.js +199 -0
  52. package/dist/adapters/adb/system-uiautomator-snapshot-provider.d.ts +13 -0
  53. package/dist/adapters/adb/system-uiautomator-snapshot-provider.js +122 -0
  54. package/dist/adapters/adb/ui-automator-parser.d.ts +3 -0
  55. package/dist/adapters/adb/ui-automator-parser.js +160 -0
  56. package/dist/adapters/adb/window-topology-parser.d.ts +2 -0
  57. package/dist/adapters/adb/window-topology-parser.js +101 -0
  58. package/dist/adapters/android-cli/android-cli-adapter.d.ts +16 -8
  59. package/dist/adapters/android-cli/android-cli-adapter.js +137 -15
  60. package/dist/adapters/android-cli/android-cli-snapshot-provider.d.ts +9 -0
  61. package/dist/adapters/android-cli/android-cli-snapshot-provider.js +99 -0
  62. package/dist/adapters/android-cli/layout-parser.js +12 -8
  63. package/dist/adapters/appium/appium-doctor.d.ts +7 -0
  64. package/dist/adapters/appium/appium-doctor.js +128 -0
  65. package/dist/adapters/appium/appium-ui-snapshot-provider.d.ts +34 -0
  66. package/dist/adapters/appium/appium-ui-snapshot-provider.js +240 -0
  67. package/dist/adapters/camera/camera-probe-adapter.d.ts +18 -0
  68. package/dist/adapters/camera/camera-probe-adapter.js +286 -0
  69. package/dist/adapters/filesystem/artifact-store.js +40 -1
  70. package/dist/adapters/filesystem/context-document-writer.d.ts +15 -0
  71. package/dist/adapters/filesystem/context-document-writer.js +112 -0
  72. package/dist/adapters/filesystem/external-flow-registry.d.ts +17 -0
  73. package/dist/adapters/filesystem/external-flow-registry.js +325 -0
  74. package/dist/adapters/filesystem/generation-session-store.d.ts +17 -11
  75. package/dist/adapters/filesystem/generation-session-store.js +281 -855
  76. package/dist/adapters/filesystem/generation-store/session-lock.d.ts +29 -0
  77. package/dist/adapters/filesystem/generation-store/session-lock.js +202 -0
  78. package/dist/adapters/filesystem/generation-store/session-transitions.d.ts +24 -0
  79. package/dist/adapters/filesystem/generation-store/session-transitions.js +340 -0
  80. package/dist/adapters/filesystem/generation-store/store-files.d.ts +50 -0
  81. package/dist/adapters/filesystem/generation-store/store-files.js +363 -0
  82. package/dist/adapters/filesystem/journey-composition-store.d.ts +23 -0
  83. package/dist/adapters/filesystem/journey-composition-store.js +151 -0
  84. package/dist/adapters/filesystem/knowledge-registry.d.ts +12 -0
  85. package/dist/adapters/filesystem/knowledge-registry.js +223 -0
  86. package/dist/adapters/filesystem/project-bound-file.js +3 -5
  87. package/dist/adapters/filesystem/project-file-inspector.d.ts +2 -0
  88. package/dist/adapters/filesystem/project-file-inspector.js +57 -28
  89. package/dist/adapters/filesystem/project-identity-inspector.d.ts +7 -0
  90. package/dist/adapters/filesystem/project-identity-inspector.js +215 -0
  91. package/dist/adapters/filesystem/project-inventory-inspector.d.ts +8 -0
  92. package/dist/adapters/filesystem/project-inventory-inspector.js +93 -0
  93. package/dist/adapters/filesystem/project-module-discoverer.d.ts +6 -0
  94. package/dist/adapters/filesystem/project-module-discoverer.js +158 -0
  95. package/dist/adapters/filesystem/skill-installer.d.ts +7 -2
  96. package/dist/adapters/filesystem/skill-installer.js +40 -11
  97. package/dist/adapters/filesystem/workspace-layout.d.ts +7 -0
  98. package/dist/adapters/filesystem/workspace-layout.js +57 -0
  99. package/dist/adapters/git/node-git-diff.d.ts +17 -0
  100. package/dist/adapters/git/node-git-diff.js +114 -0
  101. package/dist/adapters/process/node-detached-process-launcher.d.ts +6 -0
  102. package/dist/adapters/process/node-detached-process-launcher.js +96 -0
  103. package/dist/adapters/process/node-process-runner.js +11 -2
  104. package/dist/adapters/prompt/inquirer-align-prompt.d.ts +27 -0
  105. package/dist/adapters/prompt/inquirer-align-prompt.js +65 -0
  106. package/dist/adapters/prompt/inquirer-generation-prompt.js +3 -0
  107. package/dist/adapters/prompt/inquirer-recorder-prompt.d.ts +9 -1
  108. package/dist/adapters/prompt/inquirer-recorder-prompt.js +63 -0
  109. package/dist/adapters/runtime/adb-runtime-backend.d.ts +67 -0
  110. package/dist/adapters/runtime/adb-runtime-backend.js +206 -0
  111. package/dist/adapters/runtime/fake-runtime-backend.d.ts +87 -0
  112. package/dist/adapters/runtime/fake-runtime-backend.js +240 -0
  113. package/dist/adapters/runtime/mobile-mcp/mcp-tool-client.d.ts +45 -0
  114. package/dist/adapters/runtime/mobile-mcp/mcp-tool-client.js +153 -0
  115. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-errors.d.ts +23 -0
  116. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-errors.js +39 -0
  117. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-responses.d.ts +20 -0
  118. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-responses.js +180 -0
  119. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-runtime-backend.d.ts +78 -0
  120. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-runtime-backend.js +379 -0
  121. package/dist/adapters/runtime/mobile-mcp/mobile-mcp-tools.d.ts +46 -0
  122. package/dist/adapters/runtime/session-adb-view.d.ts +53 -0
  123. package/dist/adapters/runtime/session-adb-view.js +193 -0
  124. package/dist/adapters/runtime/session-backed-ports.d.ts +21 -0
  125. package/dist/adapters/runtime/session-backed-ports.js +43 -0
  126. package/dist/adapters/runtime/shared-session-runtime-backend.d.ts +12 -0
  127. package/dist/adapters/runtime/shared-session-runtime-backend.js +64 -0
  128. package/dist/adapters/ui/auto-ui-snapshot-provider.d.ts +8 -0
  129. package/dist/adapters/ui/auto-ui-snapshot-provider.js +48 -0
  130. package/dist/adapters/ui/device-layout-temp.d.ts +6 -0
  131. package/dist/adapters/ui/device-layout-temp.js +18 -0
  132. package/dist/adapters/ui/device-ui-environment.d.ts +13 -0
  133. package/dist/adapters/ui/device-ui-environment.js +81 -0
  134. package/dist/adapters/ui/layout-normalization.d.ts +9 -0
  135. package/dist/adapters/ui/layout-normalization.js +44 -0
  136. package/dist/adapters/ui/ui-snapshot-error.d.ts +11 -0
  137. package/dist/adapters/ui/ui-snapshot-error.js +12 -0
  138. package/dist/adapters/ui/ui-snapshot-support.d.ts +15 -0
  139. package/dist/adapters/ui/ui-snapshot-support.js +12 -0
  140. package/dist/application/align/align-service.d.ts +42 -0
  141. package/dist/application/align/align-service.js +106 -0
  142. package/dist/application/assertion/expectation-evaluator.d.ts +18 -5
  143. package/dist/application/assertion/expectation-evaluator.js +156 -18
  144. package/dist/application/assertion/guarded-expectation.d.ts +45 -0
  145. package/dist/application/assertion/guarded-expectation.js +74 -0
  146. package/dist/application/checkpoint/baseline-capturer.d.ts +26 -0
  147. package/dist/application/checkpoint/baseline-capturer.js +163 -0
  148. package/dist/application/checkpoint/baseline-error.d.ts +5 -0
  149. package/dist/application/checkpoint/baseline-error.js +8 -0
  150. package/dist/application/checkpoint/baseline-service.d.ts +31 -0
  151. package/dist/application/checkpoint/baseline-service.js +147 -0
  152. package/dist/application/checkpoint/checkpoint-evaluator.d.ts +35 -0
  153. package/dist/application/checkpoint/checkpoint-evaluator.js +415 -0
  154. package/dist/application/checkpoint/regression-comparator.d.ts +18 -0
  155. package/dist/application/checkpoint/regression-comparator.js +192 -0
  156. package/dist/application/collector/logcat-collector.d.ts +22 -1
  157. package/dist/application/collector/logcat-collector.js +103 -15
  158. package/dist/application/collector/logcat-event.d.ts +8 -0
  159. package/dist/application/collector/logcat-event.js +79 -0
  160. package/dist/application/collector/logcat-stop.d.ts +2 -0
  161. package/dist/application/collector/logcat-stop.js +11 -0
  162. package/dist/application/context/context-generator.d.ts +45 -0
  163. package/dist/application/context/context-generator.js +228 -0
  164. package/dist/application/context/context-loader.d.ts +40 -0
  165. package/dist/application/context/context-loader.js +223 -0
  166. package/dist/application/context/context-refresher.d.ts +59 -0
  167. package/dist/application/context/context-refresher.js +349 -0
  168. package/dist/application/context/context-rehasher.d.ts +36 -0
  169. package/dist/application/context/context-rehasher.js +110 -0
  170. package/dist/application/context/context-validator.d.ts +23 -1
  171. package/dist/application/context/context-validator.js +116 -9
  172. package/dist/application/context/evidence-hash.d.ts +1 -0
  173. package/dist/application/context/evidence-hash.js +125 -0
  174. package/dist/application/context/evidence-match.d.ts +9 -0
  175. package/dist/application/context/evidence-match.js +8 -0
  176. package/dist/application/context/shard-identity.d.ts +2 -0
  177. package/dist/application/context/shard-identity.js +7 -0
  178. package/dist/application/contract/contract-loader.d.ts +25 -0
  179. package/dist/application/contract/contract-loader.js +68 -0
  180. package/dist/application/contract/contract-review.d.ts +25 -0
  181. package/dist/application/contract/contract-review.js +51 -0
  182. package/dist/application/contract/contract-verifier.d.ts +53 -0
  183. package/dist/application/contract/contract-verifier.js +645 -0
  184. package/dist/application/devices/resolve-device-assignments.d.ts +21 -0
  185. package/dist/application/devices/resolve-device-assignments.js +60 -0
  186. package/dist/application/diagnosis/failure-classifier.d.ts +23 -0
  187. package/dist/application/diagnosis/failure-classifier.js +179 -0
  188. package/dist/application/doctor/doctor-service.d.ts +36 -3
  189. package/dist/application/doctor/doctor-service.js +255 -25
  190. package/dist/application/generation/generation-app-preparer.d.ts +20 -0
  191. package/dist/application/generation/generation-app-preparer.js +28 -0
  192. package/dist/application/generation/generation-config-service.d.ts +21 -0
  193. package/dist/application/generation/generation-config-service.js +53 -0
  194. package/dist/application/generation/generation-confirmation-service.d.ts +14 -3
  195. package/dist/application/generation/generation-confirmation-service.js +109 -24
  196. package/dist/application/generation/generation-context-snapshot.d.ts +6 -0
  197. package/dist/application/generation/generation-context-snapshot.js +29 -0
  198. package/dist/application/generation/generation-finalizer.d.ts +15 -9
  199. package/dist/application/generation/generation-finalizer.js +167 -70
  200. package/dist/application/generation/generation-publisher.js +12 -8
  201. package/dist/application/generation/generation-recovery-service.d.ts +35 -0
  202. package/dist/application/generation/generation-recovery-service.js +144 -0
  203. package/dist/application/generation/generation-reopen-service.d.ts +15 -0
  204. package/dist/application/generation/generation-reopen-service.js +37 -0
  205. package/dist/application/generation/generation-replace-service.d.ts +32 -0
  206. package/dist/application/generation/generation-replace-service.js +116 -0
  207. package/dist/application/generation/generation-starter.d.ts +44 -4
  208. package/dist/application/generation/generation-starter.js +175 -8
  209. package/dist/application/generation/generation-step-executor.d.ts +67 -5
  210. package/dist/application/generation/generation-step-executor.js +471 -108
  211. package/dist/application/generation/proposed-step-validator.js +96 -30
  212. package/dist/application/generation/replay-policy-loader.d.ts +16 -0
  213. package/dist/application/generation/replay-policy-loader.js +29 -0
  214. package/dist/application/generation/risk-evaluator.d.ts +4 -1
  215. package/dist/application/generation/risk-evaluator.js +118 -3
  216. package/dist/application/generation/runtime-observer.d.ts +39 -7
  217. package/dist/application/generation/runtime-observer.js +193 -24
  218. package/dist/application/impact/impact-resolver.d.ts +32 -0
  219. package/dist/application/impact/impact-resolver.js +160 -0
  220. package/dist/application/init/init-service.js +26 -13
  221. package/dist/application/interaction/action-executor.d.ts +3 -2
  222. package/dist/application/interaction/action-executor.js +11 -1
  223. package/dist/application/interaction/action-target.d.ts +17 -0
  224. package/dist/application/interaction/action-target.js +40 -0
  225. package/dist/application/interaction/external-step-runner.d.ts +153 -0
  226. package/dist/application/interaction/external-step-runner.js +332 -0
  227. package/dist/application/interaction/fallback-resolver.d.ts +6 -3
  228. package/dist/application/interaction/fallback-resolver.js +7 -5
  229. package/dist/application/interaction/scroll-to-executor.d.ts +17 -3
  230. package/dist/application/interaction/scroll-to-executor.js +118 -36
  231. package/dist/application/journey/external-flow-resolver.d.ts +25 -0
  232. package/dist/application/journey/external-flow-resolver.js +22 -0
  233. package/dist/application/journey/journey-check-service.d.ts +54 -0
  234. package/dist/application/journey/journey-check-service.js +207 -0
  235. package/dist/application/journey/journey-promoter.d.ts +26 -0
  236. package/dist/application/journey/journey-promoter.js +123 -0
  237. package/dist/application/journey/journey-resolver.d.ts +42 -0
  238. package/dist/application/journey/journey-resolver.js +239 -0
  239. package/dist/application/journey/journey-retirer.d.ts +25 -0
  240. package/dist/application/journey/journey-retirer.js +87 -0
  241. package/dist/application/knowledge/anchor-resolver.d.ts +36 -0
  242. package/dist/application/knowledge/anchor-resolver.js +110 -0
  243. package/dist/application/knowledge/knowledge-loader.d.ts +16 -0
  244. package/dist/application/knowledge/knowledge-loader.js +36 -0
  245. package/dist/application/locator/layout-failure-summary.d.ts +2 -0
  246. package/dist/application/locator/layout-failure-summary.js +67 -0
  247. package/dist/application/locator/layout-traversal.d.ts +11 -0
  248. package/dist/application/locator/layout-traversal.js +18 -0
  249. package/dist/application/locator/locator-resolver.d.ts +13 -1
  250. package/dist/application/locator/locator-resolver.js +182 -18
  251. package/dist/application/observe/observe-service.d.ts +20 -0
  252. package/dist/application/observe/observe-service.js +91 -0
  253. package/dist/application/project/project-describer.d.ts +14 -6
  254. package/dist/application/project/project-describer.js +38 -5
  255. package/dist/application/recognition/screen-detector.d.ts +28 -0
  256. package/dist/application/recognition/screen-detector.js +111 -0
  257. package/dist/application/recorder/locator-selector.js +151 -17
  258. package/dist/application/recorder/recorder-service.d.ts +16 -4
  259. package/dist/application/recorder/recorder-service.js +420 -128
  260. package/dist/application/report/report-writer.js +1 -1
  261. package/dist/application/runtime/cold-launch.d.ts +28 -0
  262. package/dist/application/runtime/cold-launch.js +59 -0
  263. package/dist/application/runtime/step-runner.d.ts +49 -4
  264. package/dist/application/runtime/step-runner.js +721 -130
  265. package/dist/application/runtime/verify-runtime.d.ts +57 -7
  266. package/dist/application/runtime/verify-runtime.js +554 -174
  267. package/dist/application/ui/cached-ui-snapshot-provider.d.ts +45 -0
  268. package/dist/application/ui/cached-ui-snapshot-provider.js +111 -0
  269. package/dist/application/ui/ui-snapshot-lifecycle.d.ts +2 -0
  270. package/dist/application/ui/ui-snapshot-lifecycle.js +8 -0
  271. package/dist/application/ui/ui-stability-probe.d.ts +3 -0
  272. package/dist/application/ui/ui-stability-probe.js +8 -0
  273. package/dist/application/wait/idle-advice.d.ts +6 -0
  274. package/dist/application/wait/idle-advice.js +27 -0
  275. package/dist/application/wait/idle-profiles.d.ts +15 -0
  276. package/dist/application/wait/idle-profiles.js +75 -0
  277. package/dist/application/wait/idle-waiter.d.ts +28 -4
  278. package/dist/application/wait/idle-waiter.js +186 -15
  279. package/dist/cli/commands/align.d.ts +3 -0
  280. package/dist/cli/commands/align.js +124 -0
  281. package/dist/cli/commands/baseline.d.ts +3 -0
  282. package/dist/cli/commands/baseline.js +130 -0
  283. package/dist/cli/commands/context.js +300 -27
  284. package/dist/cli/commands/contract.d.ts +3 -0
  285. package/dist/cli/commands/contract.js +120 -0
  286. package/dist/cli/commands/doctor.js +16 -9
  287. package/dist/cli/commands/failure.d.ts +3 -0
  288. package/dist/cli/commands/failure.js +63 -0
  289. package/dist/cli/commands/generation/lifecycle-commands.d.ts +5 -0
  290. package/dist/cli/commands/generation/lifecycle-commands.js +211 -0
  291. package/dist/cli/commands/generation/session-commands.d.ts +11 -0
  292. package/dist/cli/commands/generation/session-commands.js +472 -0
  293. package/dist/cli/commands/generation/shared.d.ts +1414 -0
  294. package/dist/cli/commands/generation/shared.js +291 -0
  295. package/dist/cli/commands/generation/step-commands.d.ts +8 -0
  296. package/dist/cli/commands/generation/step-commands.js +315 -0
  297. package/dist/cli/commands/generation.js +10 -507
  298. package/dist/cli/commands/impact.d.ts +3 -0
  299. package/dist/cli/commands/impact.js +87 -0
  300. package/dist/cli/commands/init.js +13 -6
  301. package/dist/cli/commands/journey.d.ts +3 -0
  302. package/dist/cli/commands/journey.js +382 -0
  303. package/dist/cli/commands/knowledge.d.ts +3 -0
  304. package/dist/cli/commands/knowledge.js +93 -0
  305. package/dist/cli/commands/observe.d.ts +3 -0
  306. package/dist/cli/commands/observe.js +127 -0
  307. package/dist/cli/commands/project.js +2 -1
  308. package/dist/cli/commands/record.js +18 -5
  309. package/dist/cli/commands/verify.js +273 -63
  310. package/dist/cli/dependencies.d.ts +125 -1
  311. package/dist/cli/dependencies.js +450 -51
  312. package/dist/cli/diff-verification.d.ts +41 -0
  313. package/dist/cli/diff-verification.js +192 -0
  314. package/dist/cli/main.js +49 -1
  315. package/dist/cli/output.d.ts +5 -3
  316. package/dist/cli/output.js +12 -0
  317. package/dist/cli/program.js +19 -1
  318. package/dist/cli/project-root.d.ts +1 -0
  319. package/dist/cli/project-root.js +6 -0
  320. package/dist/cli/runtime-selection.d.ts +22 -0
  321. package/dist/cli/runtime-selection.js +92 -0
  322. package/dist/cli/version.d.ts +1 -0
  323. package/dist/cli/version.js +14 -0
  324. package/dist/cli/workspace-guard.d.ts +2 -0
  325. package/dist/cli/workspace-guard.js +3 -0
  326. package/dist/domain/binding-reference.d.ts +4 -0
  327. package/dist/domain/binding-reference.js +23 -0
  328. package/dist/domain/checkpoint.d.ts +404 -0
  329. package/dist/domain/checkpoint.js +297 -0
  330. package/dist/domain/config.d.ts +107 -1
  331. package/dist/domain/config.js +52 -6
  332. package/dist/domain/contract.d.ts +413 -0
  333. package/dist/domain/contract.js +196 -0
  334. package/dist/domain/external-flow.d.ts +493 -0
  335. package/dist/domain/external-flow.js +15 -0
  336. package/dist/domain/failure-classification.d.ts +176 -0
  337. package/dist/domain/failure-classification.js +169 -0
  338. package/dist/domain/failure.d.ts +2 -1
  339. package/dist/domain/failure.js +123 -0
  340. package/dist/domain/generation.d.ts +1671 -132
  341. package/dist/domain/generation.js +202 -13
  342. package/dist/domain/geometry.d.ts +13 -0
  343. package/dist/domain/geometry.js +16 -0
  344. package/dist/domain/impact.d.ts +71 -0
  345. package/dist/domain/impact.js +76 -0
  346. package/dist/domain/init.d.ts +12 -4
  347. package/dist/domain/init.js +24 -18
  348. package/dist/domain/journey-composition.d.ts +2285 -0
  349. package/dist/domain/journey-composition.js +85 -0
  350. package/dist/domain/journey-lifecycle.d.ts +10 -0
  351. package/dist/domain/journey-lifecycle.js +9 -0
  352. package/dist/domain/journey.d.ts +2419 -212
  353. package/dist/domain/journey.js +384 -14
  354. package/dist/domain/knowledge.d.ts +165 -0
  355. package/dist/domain/knowledge.js +188 -0
  356. package/dist/domain/layout.d.ts +34 -5
  357. package/dist/domain/layout.js +64 -6
  358. package/dist/domain/locator-evidence.d.ts +3 -0
  359. package/dist/domain/locator-evidence.js +36 -0
  360. package/dist/domain/logcat-event.d.ts +54 -0
  361. package/dist/domain/logcat-event.js +40 -0
  362. package/dist/domain/observation.d.ts +33 -0
  363. package/dist/domain/observation.js +18 -0
  364. package/dist/domain/project-context.d.ts +219 -1
  365. package/dist/domain/project-context.js +209 -6
  366. package/dist/domain/proposed-step.d.ts +340 -60
  367. package/dist/domain/proposed-step.js +23 -3
  368. package/dist/domain/report.d.ts +1455 -28
  369. package/dist/domain/report.js +211 -14
  370. package/dist/domain/runtime-snapshot.d.ts +58 -1
  371. package/dist/domain/runtime-snapshot.js +14 -3
  372. package/dist/domain/runtime.d.ts +50 -0
  373. package/dist/domain/runtime.js +36 -0
  374. package/dist/domain/system-app-profiles.d.ts +6 -0
  375. package/dist/domain/system-app-profiles.js +43 -0
  376. package/dist/domain/ui-backend.d.ts +28 -0
  377. package/dist/domain/ui-backend.js +33 -0
  378. package/dist/domain/ui-cache.d.ts +11 -0
  379. package/dist/domain/ui-cache.js +10 -0
  380. package/dist/domain/window-hierarchy.d.ts +84 -0
  381. package/dist/domain/window-hierarchy.js +172 -0
  382. package/dist/domain/workflow-manifest.d.ts +77 -0
  383. package/dist/domain/workflow-manifest.js +136 -0
  384. package/dist/domain/workspace.d.ts +28 -0
  385. package/dist/domain/workspace.js +72 -0
  386. package/dist/ports/adb.d.ts +34 -1
  387. package/dist/ports/align-prompt.d.ts +11 -0
  388. package/dist/ports/align-prompt.js +6 -0
  389. package/dist/ports/anchor-resolver.d.ts +30 -0
  390. package/dist/ports/anchor-resolver.js +1 -0
  391. package/dist/ports/annotated-screen-resolver.d.ts +4 -0
  392. package/dist/ports/annotated-screen-resolver.js +1 -0
  393. package/dist/ports/camera-probe.d.ts +23 -0
  394. package/dist/ports/camera-probe.js +8 -0
  395. package/dist/ports/context-document-writer.d.ts +24 -0
  396. package/dist/ports/context-document-writer.js +1 -0
  397. package/dist/ports/detached-process-launcher.d.ts +12 -0
  398. package/dist/ports/detached-process-launcher.js +1 -0
  399. package/dist/ports/external-flow-registry.d.ts +37 -0
  400. package/dist/ports/external-flow-registry.js +1 -0
  401. package/dist/ports/generation-prompt.d.ts +2 -1
  402. package/dist/ports/generation-session-store.d.ts +12 -2
  403. package/dist/ports/git-diff.d.ts +9 -0
  404. package/dist/ports/git-diff.js +1 -0
  405. package/dist/ports/journey-composition-store.d.ts +17 -0
  406. package/dist/ports/journey-composition-store.js +1 -0
  407. package/dist/ports/knowledge-registry.d.ts +20 -0
  408. package/dist/ports/knowledge-registry.js +1 -0
  409. package/dist/ports/process-runner.d.ts +7 -0
  410. package/dist/ports/project-file-inspector.d.ts +1 -0
  411. package/dist/ports/project-identity-inspector.d.ts +24 -0
  412. package/dist/ports/project-identity-inspector.js +1 -0
  413. package/dist/ports/project-inventory-inspector.d.ts +21 -0
  414. package/dist/ports/project-inventory-inspector.js +1 -0
  415. package/dist/ports/project-module-discoverer.d.ts +26 -0
  416. package/dist/ports/project-module-discoverer.js +1 -0
  417. package/dist/ports/recorder-prompt.d.ts +10 -1
  418. package/dist/ports/runtime-backend.d.ts +89 -0
  419. package/dist/ports/runtime-backend.js +13 -0
  420. package/dist/ports/runtime-capability.d.ts +12 -0
  421. package/dist/ports/runtime-capability.js +20 -0
  422. package/dist/ports/runtime-session-ports.d.ts +25 -0
  423. package/dist/ports/runtime-session-ports.js +1 -0
  424. package/dist/ports/screenshot.d.ts +11 -0
  425. package/dist/ports/screenshot.js +1 -0
  426. package/dist/ports/skill-installer.d.ts +2 -2
  427. package/dist/ports/ui-snapshot.d.ts +35 -0
  428. package/dist/ports/ui-snapshot.js +1 -0
  429. package/dist/ports/ui-stability.d.ts +19 -0
  430. package/dist/ports/ui-stability.js +1 -0
  431. package/dist/ports/workspace-layout.d.ts +4 -0
  432. package/dist/ports/workspace-layout.js +1 -0
  433. package/dist/shared/errors.d.ts +2 -0
  434. package/dist/shared/errors.js +22 -0
  435. package/dist/shared/paths.d.ts +2 -0
  436. package/dist/shared/paths.js +24 -0
  437. package/dist/shared/strings.d.ts +2 -0
  438. package/dist/shared/strings.js +16 -0
  439. package/package.json +6 -1
  440. package/assets/skills/taphound-ai-journey/GUIDE.md +0 -862
  441. package/assets/skills/taphound-ai-journey/SKILL.md +0 -377
  442. package/assets/skills/taphound-ai-journey/prompts/analyze-project.md +0 -526
  443. package/assets/skills/taphound-ai-journey/prompts/generate-step.md +0 -109
  444. package/assets/skills/taphound-ai-journey/schemas/project-context.json +0 -81
  445. package/assets/skills/taphound-ai-journey/templates/project-context.example.json +0 -49
  446. package/dist/ports/android-cli.d.ts +0 -21
  447. /package/dist/{ports/android-cli.js → adapters/runtime/mobile-mcp/mobile-mcp-tools.js} +0 -0
@@ -0,0 +1,505 @@
1
+ #!/usr/bin/env node
2
+ // Skill-side cross-version evidence gate. This does not relax Core's
3
+ // same-Journey baseline compare or claim pixel/visual equivalence.
4
+ import { createHash } from "node:crypto";
5
+ import { spawnSync } from "node:child_process";
6
+ import { constants } from "node:fs";
7
+ import {
8
+ copyFile, lstat, mkdtemp, readFile, readdir, realpath, rename, rm,
9
+ writeFile
10
+ } from "node:fs/promises";
11
+ import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
12
+ import process from "node:process";
13
+
14
+ const hash = (value) => createHash("sha256").update(value).digest("hex");
15
+ const sha = (value) => /^[a-f0-9]{64}$/.test(value);
16
+ const id = (value) => typeof value === "string"
17
+ && /^[a-z][a-z0-9-]{0,63}$/.test(value);
18
+ const inside = (root, path) => {
19
+ const rel = relative(root, path);
20
+ return rel === "" || (rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel));
21
+ };
22
+ const pause = (message) => { throw new Error(message); };
23
+ const json = async (path) => JSON.parse(await readFile(path, "utf8"));
24
+ const fileHash = async (path) => hash(await readFile(path));
25
+ function canonical(value) {
26
+ if (Array.isArray(value)) return value.map(canonical);
27
+ if (value !== null && typeof value === "object") {
28
+ return Object.fromEntries(Object.entries(value)
29
+ .sort(([left], [right]) => left.localeCompare(right))
30
+ .map(([key, item]) => [key, canonical(item)]));
31
+ }
32
+ return value;
33
+ }
34
+ const journeyHash = (value) => hash(JSON.stringify(canonical(value)));
35
+ async function file(path) {
36
+ const actual = resolve(path);
37
+ if (!isAbsolute(path) || await realpath(actual) !== actual
38
+ || !(await lstat(actual)).isFile()) {
39
+ pause("Evidence must be a regular absolute file with no symlink parents");
40
+ }
41
+ return actual;
42
+ }
43
+ async function root(path) {
44
+ const actual = resolve(path);
45
+ if (!isAbsolute(path) || await realpath(actual) !== actual
46
+ || !(await lstat(actual)).isDirectory()) {
47
+ pause("Workspace must be an absolute non-symlink directory");
48
+ }
49
+ return actual;
50
+ }
51
+ function checkCase(value) {
52
+ if (value.version !== 1 || !id(value.caseId)
53
+ || typeof value.sourceRef !== "string" || !value.sourceRef.trim()
54
+ || !/^[a-zA-Z_$][\w$]*(?:\.[a-zA-Z_$][\w$]*)+$/.test(value.packageName ?? "")
55
+ || typeof value.goal !== "string" || !value.goal.trim()
56
+ || typeof value.fixtureRef !== "string" || !value.fixtureRef.trim()
57
+ || !Array.isArray(value.scenario) || value.scenario.length === 0
58
+ || value.scenario.some((step) => typeof step !== "string" || !step.trim())
59
+ || !Array.isArray(value.observables) || value.observables.length === 0
60
+ || value.observables.some((fact) => !validObservable(fact))
61
+ || new Set(value.observables.map((fact) => fact.id)).size !== value.observables.length
62
+ || Object.keys(value).sort().join(",")
63
+ !== "caseId,fixtureRef,goal,observables,packageName,scenario,sourceRef,version") {
64
+ pause("Frozen UI-refactor Case must have at least one concrete, distinct observable");
65
+ }
66
+ }
67
+ function validLocator(locator) {
68
+ if (locator === null || typeof locator !== "object"
69
+ || Object.keys(locator).length !== 1) return false;
70
+ const key = Object.keys(locator)[0];
71
+ return ["resourceId", "text", "contentDescription"].includes(key)
72
+ && typeof locator[key] === "string" && locator[key].trim().length > 0;
73
+ }
74
+ function validEvent(expect) {
75
+ const fieldValues = expect?.fields !== null && typeof expect?.fields === "object"
76
+ && !Array.isArray(expect.fields) ? Object.values(expect.fields) : [];
77
+ if (expect === null || typeof expect !== "object"
78
+ || expect.type !== "logcatEvent"
79
+ || typeof expect.tag !== "string" || !expect.tag.trim()
80
+ || typeof expect.event !== "string" || !expect.event.trim()
81
+ || expect.fields === null || typeof expect.fields !== "object"
82
+ || Array.isArray(expect.fields) || expect.unique !== true
83
+ || fieldValues.some((value) => !(
84
+ typeof value === "boolean"
85
+ || typeof value === "number" && Number.isFinite(value)
86
+ || typeof value === "string" && value.length <= 512
87
+ ))
88
+ || expect.capture !== undefined
89
+ || !["stepStart", "runStart", "marker"].includes(expect.window?.from)
90
+ || (expect.window.from === "marker") !== id(expect.window.markerId)
91
+ || Object.keys(expect.window).sort().join(",") !== (
92
+ expect.window.from === "marker" ? "from,markerId" : "from"
93
+ )) return false;
94
+ const keys = Object.keys(expect).sort().join(",");
95
+ return keys === "event,fields,tag,type,unique,window";
96
+ }
97
+ function validObservable(fact) {
98
+ if (!id(fact?.id) || !Number.isInteger(fact.timeoutMs)
99
+ || fact.timeoutMs <= 0 || fact.timeoutMs > 60000) return false;
100
+ if (fact.kind === "visibleText") {
101
+ return Object.keys(fact).sort().join(",") === "afterAction,id,kind,text,timeoutMs"
102
+ && ["click", "wait"].includes(fact.afterAction)
103
+ && typeof fact.text === "string" && fact.text.trim().length > 0;
104
+ }
105
+ if (fact.kind === "visibleElement" || fact.kind === "absentElement") {
106
+ return Object.keys(fact).sort().join(",") === "id,kind,locator,timeoutMs"
107
+ && validLocator(fact.locator);
108
+ }
109
+ if (fact.kind === "logcatEvent") {
110
+ return Object.keys(fact).sort().join(",") === "expect,id,kind,timeoutMs"
111
+ && validEvent(fact.expect);
112
+ }
113
+ return false;
114
+ }
115
+ function expectedCheckpoint(fact) {
116
+ const condition = fact.kind === "logcatEvent"
117
+ ? { kind: "logcatEvent", expect: fact.expect }
118
+ : { kind: fact.kind, locator: fact.locator };
119
+ return { condition, expect: { allOf: [condition], timeoutMs: fact.timeoutMs } };
120
+ }
121
+ function checkpointMatches(actual, expected) {
122
+ if (actual === null || typeof actual !== "object"
123
+ || actual.timeoutMs !== expected.timeoutMs
124
+ || !Array.isArray(actual.allOf) || actual.allOf.length !== 1
125
+ || JSON.stringify(canonical(actual.allOf[0]))
126
+ !== JSON.stringify(canonical(expected.allOf[0]))) {
127
+ return false;
128
+ }
129
+ return Object.keys(actual).every((key) => ["allOf", "timeoutMs"].includes(key));
130
+ }
131
+ function observablePassed(report, evidence) {
132
+ if (evidence.source === "step") {
133
+ return report.steps[evidence.index]?.expectation?.status === "passed";
134
+ }
135
+ const checkpoint = report.checkpoints?.find((entry) => entry.id === evidence.id);
136
+ return checkpoint?.status === "passed"
137
+ && checkpoint.conditions.length === 1
138
+ && checkpoint.conditions[0]?.status === "passed";
139
+ }
140
+ function validCheckpointReport(checkpoint) {
141
+ if (checkpoint.conditions?.length !== 1
142
+ || !["passed", "failed", "unresolved"].includes(checkpoint.status)
143
+ || checkpoint.status !== checkpoint.conditions[0].status) return false;
144
+ const condition = checkpoint.conditions[0];
145
+ if (condition.kind === "logcatEvent" && condition.status === "passed") {
146
+ return condition.matchedCount === 1
147
+ && sha(condition.matchedLineSha256)
148
+ && Number.isFinite(condition.matchedAtMs)
149
+ && typeof condition.evidenceRef === "string"
150
+ && condition.evidenceRef.length > 0;
151
+ }
152
+ return true;
153
+ }
154
+ function checkReplay(project, device, caseData, journey, meta, report, reportPath) {
155
+ if (journey.version !== 2 || !journey.steps?.length
156
+ || !journey.devices?.some((entry) => entry.role === "default")
157
+ || !Array.isArray(journey.steps)
158
+ || meta.version !== 1 || !["verified", "promoted"].includes(meta.status)
159
+ || typeof meta.generationId !== "string"
160
+ || !sha(meta.bindings?.projectHash) || !sha(meta.bindings?.configHash)
161
+ || !sha(meta.bindings?.contextHash)
162
+ || !sha(meta.verification?.reportSha256)
163
+ || meta.journeyPath === undefined
164
+ || !meta.replayPolicy?.generatedReplayPolicy
165
+ || !meta.replayPolicy?.requireFocusedInput
166
+ || meta.journeySha256 !== journeyHash(journey)
167
+ || report.schemaVersion !== 4
168
+ || report.project?.packageName !== caseData.packageName
169
+ || resolve(report.project?.root ?? "") !== project
170
+ || report.environment?.devices?.length !== 1
171
+ || report.environment.devices[0].deviceSerial !== device
172
+ || report.journey?.sha256 !== meta.journeySha256
173
+ || report.journey.name !== journey.name
174
+ || resolve(report.artifacts?.directory ?? "") !== dirname(reportPath)
175
+ || !Array.isArray(report.steps) || report.steps.length > journey.steps.length
176
+ || (report.status === "passed" && report.steps.length !== journey.steps.length)
177
+ || report.steps.some((step, index) => step.index !== index
178
+ || journey.steps[index] === undefined
179
+ || step.action !== journey.steps[index].action)
180
+ || report.fallbackUsed !== false) {
181
+ pause("Independent Replay, strict policy, target project and Journey do not bind");
182
+ }
183
+ const covered = [];
184
+ for (const fact of caseData.observables) {
185
+ if (fact.kind === "visibleText") {
186
+ const expected = {
187
+ type: "element", locator: { text: fact.text }, timeoutMs: fact.timeoutMs
188
+ };
189
+ const matches = journey.steps.map((step, index) =>
190
+ step.action === fact.afterAction
191
+ && JSON.stringify(canonical(step.expect)) === JSON.stringify(canonical(expected))
192
+ ? index : -1).filter((index) => index >= 0);
193
+ if (matches.length !== 1 || (report.status === "passed"
194
+ && report.steps[matches[0]]?.expectation?.type !== "element")
195
+ || (report.steps[matches[0]]?.expectation !== undefined
196
+ && report.steps[matches[0]].expectation.type !== "element")) {
197
+ pause(`Observable ${fact.id} is missing, duplicated, or unsupported in this Journey`);
198
+ }
199
+ covered.push({ id: fact.id, source: "step", index: matches[0] });
200
+ continue;
201
+ }
202
+ const expected = expectedCheckpoint(fact);
203
+ const definitions = (journey.checkpoints ?? []).filter(
204
+ (checkpoint) => checkpoint.id === fact.id
205
+ );
206
+ if (definitions.length !== 1 || definitions[0].version !== 1
207
+ || !checkpointMatches(definitions[0].expect, expected.expect)) {
208
+ pause(`Observable ${fact.id} needs one exact dedicated Checkpoint`);
209
+ }
210
+ const reports = (report.checkpoints ?? []).filter((checkpoint) =>
211
+ checkpoint.id === fact.id);
212
+ if (reports.length > 1 || (report.status === "passed" && reports.length !== 1)
213
+ || (reports[0] !== undefined && (
214
+ !validCheckpointReport(reports[0])
215
+ ||
216
+ reports[0].conditions.length !== 1
217
+ || JSON.stringify(canonical({
218
+ kind: reports[0].conditions[0]?.kind,
219
+ ...("locator" in (reports[0].conditions[0] ?? {})
220
+ ? { locator: reports[0].conditions[0].locator }
221
+ : { expect: reports[0].conditions[0]?.expect })
222
+ })) !== JSON.stringify(canonical(expected.condition))
223
+ ))) {
224
+ pause(`Observable ${fact.id} Checkpoint report is missing or changed`);
225
+ }
226
+ covered.push({ id: fact.id, source: "checkpoint", index: fact.id });
227
+ }
228
+ if (new Set(covered.map((entry) => `${entry.source}:${String(entry.index)}`)).size
229
+ !== covered.length) {
230
+ pause("Distinct observable facts must have separate evidence identities");
231
+ }
232
+ return covered;
233
+ }
234
+ async function checkReceipt(receiptPath, project, journeyPath, reportPath,
235
+ device, hashes, expectedExit = 0) {
236
+ const receipt = await json(receiptPath);
237
+ if (receipt.version !== 1 || receipt.exitCode !== expectedExit
238
+ || !Array.isArray(receipt.argv) || receipt.argv[0] !== "verify"
239
+ || !receipt.argv.includes("--policy-from-meta") || !receipt.argv.includes("--json")
240
+ || receipt.argv.includes("--contract")
241
+ || receipt.argv[receipt.argv.indexOf("--project") + 1] !== project
242
+ || receipt.argv[receipt.argv.indexOf("--journey") + 1] !== journeyPath
243
+ || receipt.argv[receipt.argv.indexOf("--device") + 1] !== device
244
+ || receipt.journeySha256 !== hashes.journey
245
+ || receipt.reportSha256 !== hashes.report
246
+ || receipt.reportPath !== reportPath
247
+ || Object.keys(receipt).sort().join(",")
248
+ !== "argv,exitCode,journeySha256,reportPath,reportSha256,version") {
249
+ pause("Independent strict verify process receipt is unavailable or does not bind the run");
250
+ }
251
+ }
252
+ const bundleFiles = [
253
+ "case.json", "before-journey.json", "before-meta.json",
254
+ "before-report.json", "before-receipt.json"
255
+ ];
256
+ async function publish(inputPath, sharedPath) {
257
+ const input = await json(await file(inputPath));
258
+ const base = await root(input.base?.projectRoot);
259
+ const shared = await root(sharedPath);
260
+ if (inside(base, shared)) pause("Shared Case directory must be outside the historical project");
261
+ const casePath = await file(input.casePath);
262
+ if (!inside(base, casePath) || inside(join(base, ".taphound", "build"), casePath)) {
263
+ pause("Frozen Case must belong to historical committed project assets");
264
+ }
265
+ const caseData = await json(casePath);
266
+ checkCase(caseData);
267
+ if (input.version !== 1 || caseData.caseId !== input.caseId
268
+ || caseData.packageName !== input.base.packageName
269
+ || !sha(input.base.apkSha256) || !/^[a-zA-Z0-9._:-]+$/.test(input.base.deviceSerial ?? "")
270
+ || await fileHash(await file(input.base.apkPath)) !== input.base.apkSha256) {
271
+ pause("Case/package/device or A's locally built APK attestation is invalid");
272
+ }
273
+ const journeyPath = await file(input.before?.journeyPath);
274
+ const metaPath = await file(input.before?.metaPath);
275
+ const reportPath = await file(input.before?.reportPath);
276
+ const receiptPath = await file(input.before?.receiptPath);
277
+ if (!inside(join(base, ".taphound", "journeys"), journeyPath)
278
+ || metaPath !== journeyPath.replace(/\.json$/, ".meta.json")
279
+ || !inside(base, reportPath) || !inside(base, receiptPath)) {
280
+ pause("Historical Journey/meta and Replay evidence must belong to the base project");
281
+ }
282
+ const expectedInputKeys = "base,before,caseId,casePath,version";
283
+ const expectedBaseKeys = "apkPath,apkSha256,deviceSerial,packageName,projectRoot";
284
+ const expectedBeforeKeys = "journeyPath,metaPath,receiptPath,reportPath";
285
+ if (Object.keys(input).sort().join(",") !== expectedInputKeys
286
+ || Object.keys(input.base).sort().join(",") !== expectedBaseKeys
287
+ || Object.keys(input.before).sort().join(",") !== expectedBeforeKeys) {
288
+ pause("Preparation input contains unsupported fields");
289
+ }
290
+ const journey = await json(journeyPath);
291
+ const meta = await json(metaPath);
292
+ const report = await json(reportPath);
293
+ if (meta.journeyPath !== relative(base, journeyPath).split(sep).join("/")) {
294
+ pause("Historical meta does not bind its conventional Journey path");
295
+ }
296
+ const covered = checkReplay(base, input.base.deviceSerial,
297
+ caseData, journey, meta, report, reportPath);
298
+ const hashes = {
299
+ journey: journeyHash(journey), report: await fileHash(reportPath)
300
+ };
301
+ await checkReceipt(receiptPath, base, journeyPath, reportPath,
302
+ input.base.deviceSerial, hashes);
303
+ if (report.status !== "passed"
304
+ || report.steps.some((step) => step.status !== "passed")
305
+ || covered.some((entry) => !observablePassed(report, entry))) {
306
+ pause("Historical APK has no passing evidence for all frozen observables");
307
+ }
308
+ const git = spawnSync("git", ["-C", base, "rev-parse", "HEAD"], {
309
+ encoding: "utf8", timeout: 10000
310
+ });
311
+ if (git.status !== 0 || !/^[a-f0-9]{40,64}$/.test(git.stdout.trim())) {
312
+ pause("Historical Git commit is unavailable");
313
+ }
314
+ const output = join(shared, caseData.caseId);
315
+ try {
316
+ await lstat(output);
317
+ pause("Case already published; silent overwrite is forbidden");
318
+ } catch (error) {
319
+ if (error.code !== "ENOENT") throw error;
320
+ }
321
+ const stage = await mkdtemp(join(shared, `.ui-refactor-${caseData.caseId}-`));
322
+ const sources = [
323
+ casePath, journeyPath, metaPath, reportPath, receiptPath
324
+ ];
325
+ try {
326
+ const before = await Promise.all(sources.map(fileHash));
327
+ for (const [index, name] of bundleFiles.entries()) {
328
+ const source = sources[index];
329
+ if (source === undefined) pause("Internal bundle source inventory is incomplete");
330
+ await copyFile(source, join(stage, name), constants.COPYFILE_EXCL);
331
+ }
332
+ const artifacts = {};
333
+ for (const name of bundleFiles) {
334
+ artifacts[name] = await fileHash(join(stage, name));
335
+ }
336
+ const after = await Promise.all(sources.map(fileHash));
337
+ if (before.some((value, index) => value !== after[index]
338
+ || bundleFiles[index] === undefined
339
+ || value !== artifacts[bundleFiles[index]])) pause("Before-run assets changed during export");
340
+ const md = [
341
+ "# UI-refactor Preserve handoff", "",
342
+ `Case: \`${caseData.caseId}\` (READY)`,
343
+ `Goal: ${caseData.goal}`,
344
+ `Test fixture reference: \`${caseData.fixtureRef}\``,
345
+ `Base Git commit: \`${git.stdout.trim()}\``,
346
+ `Package: \`${caseData.packageName}\`; device: \`${input.base.deviceSerial}\``,
347
+ `Old built/installed APK hash (Agent A attestation): \`${input.base.apkSha256}\``,
348
+ `Old report: \`${report.runId}\` (passed)`,
349
+ "", "| Frozen file | SHA-256 |", "|---|---|",
350
+ ...Object.entries(artifacts).map(([name, value]) =>
351
+ `| \`${name}\` | \`${value}\` |`),
352
+ "", "B receives only this path. Validate the Case bundle before device work.",
353
+ "Generate a NEW Journey and strict independent Replay for the same",
354
+ "frozen observables. Old Journey/meta are audit-only, not staged in B.",
355
+ ""
356
+ ].join("\n");
357
+ await writeFile(join(stage, "handoff.md"), md, { flag: "wx" });
358
+ await writeFile(join(stage, "handoff.json"), `${JSON.stringify({
359
+ version: 1, status: "READY", caseId: caseData.caseId,
360
+ base: {
361
+ projectRoot: base, commit: git.stdout.trim(), device: input.base.deviceSerial,
362
+ apkSha256: input.base.apkSha256, runId: report.runId
363
+ },
364
+ artifacts, mdSha256: hash(md)
365
+ }, null, 2)}\n`, { flag: "wx" });
366
+ await rename(stage, output);
367
+ return { status: "READY", handoff: join(output, "handoff.md") };
368
+ } catch (error) {
369
+ await rm(stage, { recursive: true, force: true });
370
+ throw error;
371
+ }
372
+ }
373
+ async function validate(handoffPath, targetPath) {
374
+ const handoff = await file(handoffPath);
375
+ if (handoff.split(sep).at(-1) !== "handoff.md") pause("B only accepts the MD handoff path");
376
+ const dir = await root(dirname(handoff));
377
+ const manifestPath = await file(join(dir, "handoff.json"));
378
+ const manifest = await json(manifestPath);
379
+ const md = await readFile(handoff, "utf8");
380
+ if (manifest.version !== 1 || manifest.status !== "READY"
381
+ || !id(manifest.caseId) || manifest.caseId !== dir.split(sep).at(-1)
382
+ || !sha(manifest.mdSha256) || hash(md) !== manifest.mdSha256
383
+ || !/^[a-f0-9]{40,64}$/.test(manifest.base?.commit ?? "")
384
+ || !sha(manifest.base?.apkSha256)
385
+ || !isAbsolute(manifest.base?.projectRoot ?? "")
386
+ || !manifest.artifacts || typeof manifest.artifacts !== "object"
387
+ || JSON.stringify((await readdir(dir)).sort()) !== JSON.stringify(
388
+ [...bundleFiles, "handoff.md", "handoff.json"].sort())
389
+ || Object.keys(manifest.artifacts).sort().join(",") !== bundleFiles.slice().sort().join(",")) {
390
+ pause("Handoff MD, manifest and file inventory disagree");
391
+ }
392
+ for (const name of bundleFiles) {
393
+ const path = await file(join(dir, name));
394
+ if (!sha(manifest.artifacts[name]) || await fileHash(path) !== manifest.artifacts[name]
395
+ || !md.includes(`| \`${name}\` | \`${manifest.artifacts[name]}\` |`)) {
396
+ pause("Frozen historical file or MD digest changed");
397
+ }
398
+ }
399
+ const caseData = await json(join(dir, "case.json"));
400
+ checkCase(caseData);
401
+ if (caseData.caseId !== manifest.caseId) pause("Frozen Case identity changed");
402
+ const journey = await json(join(dir, "before-journey.json"));
403
+ const meta = await json(join(dir, "before-meta.json"));
404
+ const report = await json(join(dir, "before-report.json"));
405
+ const originalJourney = join(manifest.base.projectRoot, meta.journeyPath ?? "");
406
+ const originalReport = join(report.artifacts?.directory ?? "", "report.json");
407
+ if (!inside(manifest.base.projectRoot, originalJourney)
408
+ || !inside(manifest.base.projectRoot, originalReport)
409
+ || report.runId !== manifest.base.runId
410
+ || caseData.packageName !== report.project?.packageName) {
411
+ pause("Historical evidence provenance disagrees with frozen Case");
412
+ }
413
+ await checkReceipt(join(dir, "before-receipt.json"), manifest.base.projectRoot,
414
+ originalJourney, originalReport, manifest.base.device, {
415
+ journey: journeyHash(journey), report: manifest.artifacts["before-report.json"]
416
+ });
417
+ const covered = checkReplay(manifest.base.projectRoot, manifest.base.device,
418
+ caseData, journey, meta, report, originalReport);
419
+ if (report.status !== "passed"
420
+ || covered.some((entry) => !observablePassed(report, entry))) {
421
+ pause("Historical observable evidence is not passed");
422
+ }
423
+ if (targetPath !== undefined) {
424
+ const project = await root(targetPath);
425
+ const configPath = await file(join(project, ".taphound", "config.json"));
426
+ const config = await json(configPath);
427
+ if (config.run?.packageName !== caseData.packageName) {
428
+ pause("Target package is not the frozen Case package");
429
+ }
430
+ }
431
+ return { status: "READY", caseId: caseData.caseId, dir, caseData, manifest };
432
+ }
433
+ async function compare(handoff, projectPath, journeyPath, reportPath, receiptPath) {
434
+ const data = await validate(handoff, projectPath);
435
+ const project = await root(projectPath);
436
+ const journeyFile = await file(journeyPath);
437
+ const metaFile = await file(journeyFile.replace(/\.json$/, ".meta.json"));
438
+ const reportFile = await file(reportPath);
439
+ const receiptFile = await file(receiptPath);
440
+ if (!inside(join(project, ".taphound", "journeys"), journeyFile)
441
+ || !inside(project, reportFile) || !inside(project, receiptFile)
442
+ || !inside(project, metaFile)) pause("Target evidence must belong to B's project");
443
+ const journey = await json(journeyFile);
444
+ const meta = await json(metaFile);
445
+ const report = await json(reportFile);
446
+ if (meta.journeyPath !== relative(project, journeyFile).split(sep).join("/")) {
447
+ pause("New strict meta does not bind B's generated Journey path");
448
+ }
449
+ await checkReceipt(receiptFile, project, journeyFile, reportFile,
450
+ data.manifest.base.device, {
451
+ journey: journeyHash(journey), report: await fileHash(reportFile)
452
+ }, report.status === "failed" ? 4 : 0);
453
+ const covered = checkReplay(project, data.manifest.base.device,
454
+ data.caseData, journey, meta, report, reportFile);
455
+ if (report.runId === data.manifest.base.runId) {
456
+ pause("Before and after evidence must come from independent run IDs");
457
+ }
458
+ if (report.journey.sha256 === (await json(join(data.dir, "before-report.json"))).journey.sha256) {
459
+ pause("UI-refactor mode needs a newly authored Journey, not the frozen old one");
460
+ }
461
+ const failed = covered.filter((entry) =>
462
+ !observablePassed(report, entry));
463
+ if (report.status === "error" || report.status === "manualRequired") {
464
+ pause("Target Replay did not produce comparable device evidence");
465
+ }
466
+ if (report.status === "failed"
467
+ && (report.primaryFailure?.code === undefined || report.layers?.run !== "failed"
468
+ || !report.steps.some((step) => step.status === "failed"))) {
469
+ pause("Failed target report lacks a deterministic primary Replay failure");
470
+ }
471
+ const outcome = report.status !== "passed" || failed.length > 0 ? "FAIL" : "PASS";
472
+ return {
473
+ status: outcome, caseId: data.caseId,
474
+ coverage: { required: covered.length, evidenced: covered.length - failed.length },
475
+ beforeRunId: data.manifest.base.runId, afterRunId: report.runId,
476
+ beforeJourneySha256: (await json(join(data.dir, "before-report.json"))).journey.sha256,
477
+ afterJourneySha256: report.journey.sha256,
478
+ ...(failed.length === 0 ? {} : { failedObservables: failed.map((entry) => entry.id) })
479
+ };
480
+ }
481
+ const [operation, ...args] = process.argv.slice(2);
482
+ function flag(name) {
483
+ const index = args.indexOf(`--${name}`);
484
+ if (index < 0 || index + 1 >= args.length) pause(`Missing --${name}`);
485
+ return args[index + 1];
486
+ }
487
+ try {
488
+ const result = operation === "prepare"
489
+ ? await publish(flag("input"), flag("out"))
490
+ : operation === "validate"
491
+ ? await validate(flag("handoff"), args.includes("--project") ? flag("project") : undefined)
492
+ : operation === "compare"
493
+ ? await compare(flag("handoff"), flag("project"), flag("journey"),
494
+ flag("report"), flag("receipt"))
495
+ : pause("Use prepare, validate or compare");
496
+ const publicResult = operation === "validate"
497
+ ? { status: result.status, caseId: result.caseId } : result;
498
+ process.stdout.write(`${JSON.stringify(publicResult)}\n`);
499
+ process.exitCode = result.status === "FAIL" ? 1 : 0;
500
+ } catch (error) {
501
+ process.stdout.write(`${JSON.stringify({
502
+ status: "PAUSED", reason: error instanceof Error ? error.message : String(error)
503
+ })}\n`);
504
+ process.exitCode = 2;
505
+ }
@@ -1,23 +1,37 @@
1
1
  import { type ForegroundComponent } from "../../domain/activity.js";
2
2
  import { type AppProcess } from "../../domain/app-process.js";
3
- import type { AdbPort, AppIdentity, DeviceInfo, LaunchActivityOptions, LogcatOptions } from "../../ports/adb.js";
4
- import type { Point } from "../../ports/android-cli.js";
3
+ import type { AdbPort, AppIdentity, DeviceIdentity, DeviceInfo, DumpLogcatOptions, LaunchActivityOptions, LogcatOptions, ResolveLauncherActivityOptions, ResolvedActivity, StartActivityByIntentOptions } from "../../ports/adb.js";
4
+ import type { Point } from "../../domain/geometry.js";
5
5
  import type { CommandResult, ProcessRunner, RunningCommand } from "../../ports/process-runner.js";
6
+ import { type WindowTopology } from "../../domain/window-hierarchy.js";
6
7
  export declare class AdbAdapter implements AdbPort {
7
8
  private readonly runner;
8
9
  constructor(runner: ProcessRunner);
9
10
  private run;
10
11
  devices(signal?: AbortSignal): Promise<readonly DeviceInfo[]>;
12
+ deviceIdentity(identity: AppIdentity): Promise<DeviceIdentity>;
11
13
  foregroundComponent(identity: AppIdentity): Promise<ForegroundComponent>;
12
14
  currentActivity(identity: AppIdentity): Promise<string>;
13
15
  isInstalled(identity: AppIdentity): Promise<boolean>;
14
16
  launchActivity(options: LaunchActivityOptions): Promise<CommandResult>;
17
+ startActivityByIntent(options: StartActivityByIntentOptions): Promise<CommandResult>;
18
+ resolveLauncherActivity(options: ResolveLauncherActivityOptions): Promise<ResolvedActivity | undefined>;
15
19
  forceStop(identity: AppIdentity): Promise<CommandResult>;
16
20
  appProcesses(identity: AppIdentity): Promise<readonly AppProcess[]>;
21
+ windowTopology(identity: AppIdentity): Promise<WindowTopology>;
17
22
  tap(point: Point, deviceSerial: string, signal?: AbortSignal): Promise<CommandResult>;
18
23
  longClick(point: Point, durationMs: number, deviceSerial: string, signal?: AbortSignal): Promise<CommandResult>;
19
24
  swipe(from: Point, to: Point, durationMs: number, deviceSerial: string, signal?: AbortSignal): Promise<CommandResult>;
20
25
  back(deviceSerial: string, signal?: AbortSignal): Promise<CommandResult>;
21
26
  inputText(text: string, deviceSerial: string, signal?: AbortSignal): Promise<CommandResult>;
27
+ /**
28
+ * `adb shell input text` cannot type non-ASCII characters (KeyCharacterMap
29
+ * resolves them to no events and the input command fails), so non-ASCII text
30
+ * is delivered through the mobilenext devicekit clipboard: set the clipboard
31
+ * by broadcast, paste it into the focused element with KEYCODE_PASTE, then
32
+ * clear the clipboard again.
33
+ */
34
+ private inputNonAsciiText;
22
35
  startLogcat(options: LogcatOptions): RunningCommand;
36
+ dumpLogcat(options: DumpLogcatOptions): Promise<CommandResult>;
23
37
  }