waygraph 0.13.7 → 0.15.0

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 (358) hide show
  1. package/README.md +970 -232
  2. package/assets/stubs/stub.mp4 +0 -0
  3. package/assets/stubs/stub.pdf +32 -0
  4. package/assets/stubs/stub.png +0 -0
  5. package/dist/agent-dive.d.ts +9 -0
  6. package/dist/agent-dive.js +32 -0
  7. package/dist/auto-explore-run.d.ts +20 -2
  8. package/dist/auto-explore-run.js +9 -8
  9. package/dist/auto-explore.js +22 -2
  10. package/dist/auto-session-ipc.d.ts +123 -0
  11. package/dist/auto-session-ipc.js +381 -0
  12. package/dist/auto-session.d.ts +342 -0
  13. package/dist/auto-session.js +760 -0
  14. package/dist/cli.js +450 -51
  15. package/dist/engine.d.ts +201 -1
  16. package/dist/engine.js +312 -2
  17. package/dist/graph.d.ts +18 -0
  18. package/dist/graph.js +65 -2
  19. package/dist/highlights.d.ts +20 -1
  20. package/dist/highlights.js +60 -0
  21. package/dist/index.d.ts +8 -4
  22. package/dist/index.js +5 -3
  23. package/dist/map-check.d.ts +18 -0
  24. package/dist/map-check.js +140 -0
  25. package/dist/pilot-overlay.d.ts +88 -0
  26. package/dist/pilot-overlay.js +363 -0
  27. package/dist/pilot.d.ts +49 -0
  28. package/dist/pilot.js +53 -0
  29. package/dist/trait.d.ts +22 -0
  30. package/dist/trait.js +60 -3
  31. package/dist/types.d.ts +21 -1
  32. package/dist/types.js +17 -1
  33. package/examples/routed-demo/.waygraph-auto/4d4902b7.log +0 -0
  34. package/examples/routed-demo/.waygraph-auto/51ef462e.log +0 -0
  35. package/examples/routed-demo/.waygraph-auto/71527ad9.log +0 -0
  36. package/examples/routed-demo/.waygraph-auto/ca16778e.log +0 -0
  37. package/examples/routed-demo/package.json +26 -0
  38. package/examples/routed-demo/playwright.config.ts +20 -0
  39. package/examples/routed-demo/scripts/fixture-server.mjs +37 -0
  40. package/examples/routed-demo/scripts/with-fixture.mjs +70 -0
  41. package/examples/routed-demo/src/flows/routed-demo-map.flow.ts +39 -0
  42. package/examples/routed-demo/src/flows/routed-demo.flow.ts +12 -0
  43. package/examples/routed-demo/src/map/(app_base)/dashboard/_methods/click-widget.block.ts +23 -0
  44. package/examples/routed-demo/src/map/(app_base)/dashboard/_nav.block.ts +18 -0
  45. package/examples/routed-demo/src/map/(app_base)/dashboard/_page.block.ts +22 -0
  46. package/examples/routed-demo/src/map/(app_base)/dashboard/_sel.ts +10 -0
  47. package/examples/routed-demo/src/map/(external)/docs/_nav.block.ts +21 -0
  48. package/examples/routed-demo/src/map/(external)/docs/_page.block.ts +15 -0
  49. package/examples/routed-demo/src/map/(external)/docs/_sel.ts +7 -0
  50. package/examples/routed-demo/src/pages/dashboard.html +10 -0
  51. package/examples/routed-demo/src/pages/docs.html +8 -0
  52. package/examples/routed-demo/src/states/routed-demo.states.ts +10 -0
  53. package/examples/routed-demo/test-results/.last-run.json +4 -0
  54. package/examples/routed-demo/tests/routed-demo-map.spec.ts +17 -0
  55. package/examples/routed-demo/tests/routed-demo.spec.ts +75 -0
  56. package/examples/routed-demo/tsconfig.json +18 -0
  57. package/examples/routed-demo-consumer/package.json +18 -0
  58. package/examples/saucedemo/.waygraph-auto/00d5336e.log +0 -0
  59. package/examples/saucedemo/.waygraph-auto/010dd92b.log +0 -0
  60. package/examples/saucedemo/.waygraph-auto/011ad671.log +0 -0
  61. package/examples/saucedemo/.waygraph-auto/01b45645.log +0 -0
  62. package/examples/saucedemo/.waygraph-auto/03764153.log +0 -0
  63. package/examples/saucedemo/.waygraph-auto/0593587e.log +0 -0
  64. package/examples/saucedemo/.waygraph-auto/06d12823.log +0 -0
  65. package/examples/saucedemo/.waygraph-auto/083cc5b9.log +0 -0
  66. package/examples/saucedemo/.waygraph-auto/0b1f5b26.json +8 -0
  67. package/examples/saucedemo/.waygraph-auto/0c00f25f.log +0 -0
  68. package/examples/saucedemo/.waygraph-auto/0e480591.log +0 -0
  69. package/examples/saucedemo/.waygraph-auto/104751ef.log +0 -0
  70. package/examples/saucedemo/.waygraph-auto/110958b8.log +0 -0
  71. package/examples/saucedemo/.waygraph-auto/138107c1.log +0 -0
  72. package/examples/saucedemo/.waygraph-auto/159d541b.log +0 -0
  73. package/examples/saucedemo/.waygraph-auto/16aa2c01.log +0 -0
  74. package/examples/saucedemo/.waygraph-auto/16c35dd5.log +0 -0
  75. package/examples/saucedemo/.waygraph-auto/1706547d.log +0 -0
  76. package/examples/saucedemo/.waygraph-auto/17537e42.log +0 -0
  77. package/examples/saucedemo/.waygraph-auto/175655f7.log +0 -0
  78. package/examples/saucedemo/.waygraph-auto/17ede493.log +0 -0
  79. package/examples/saucedemo/.waygraph-auto/1fe2414f.log +0 -0
  80. package/examples/saucedemo/.waygraph-auto/1fe2e3b8.log +0 -0
  81. package/examples/saucedemo/.waygraph-auto/2219f00b.log +0 -0
  82. package/examples/saucedemo/.waygraph-auto/23e62571.log +0 -0
  83. package/examples/saucedemo/.waygraph-auto/25f68ada.log +0 -0
  84. package/examples/saucedemo/.waygraph-auto/26ca9cd8.log +0 -0
  85. package/examples/saucedemo/.waygraph-auto/2bb6c4e8.log +0 -0
  86. package/examples/saucedemo/.waygraph-auto/2d425004.log +0 -0
  87. package/examples/saucedemo/.waygraph-auto/2f5b3ea6.log +0 -0
  88. package/examples/saucedemo/.waygraph-auto/31bb303d.log +0 -0
  89. package/examples/saucedemo/.waygraph-auto/31f2042f.log +0 -0
  90. package/examples/saucedemo/.waygraph-auto/36551ec1.log +0 -0
  91. package/examples/saucedemo/.waygraph-auto/36a1c274.log +0 -0
  92. package/examples/saucedemo/.waygraph-auto/381aa338.log +0 -0
  93. package/examples/saucedemo/.waygraph-auto/3882bce1.log +0 -0
  94. package/examples/saucedemo/.waygraph-auto/38acd1dc.log +0 -0
  95. package/examples/saucedemo/.waygraph-auto/3b8b77b9.log +0 -0
  96. package/examples/saucedemo/.waygraph-auto/4028d2a2.log +0 -0
  97. package/examples/saucedemo/.waygraph-auto/4168b208.log +0 -0
  98. package/examples/saucedemo/.waygraph-auto/42cc9113.log +0 -0
  99. package/examples/saucedemo/.waygraph-auto/439d30aa.log +0 -0
  100. package/examples/saucedemo/.waygraph-auto/43ab66ce.log +0 -0
  101. package/examples/saucedemo/.waygraph-auto/43f66838.log +0 -0
  102. package/examples/saucedemo/.waygraph-auto/442171d4.log +0 -0
  103. package/examples/saucedemo/.waygraph-auto/453b1ce1.log +0 -0
  104. package/examples/saucedemo/.waygraph-auto/457322bd.log +0 -0
  105. package/examples/saucedemo/.waygraph-auto/46197027.log +0 -0
  106. package/examples/saucedemo/.waygraph-auto/46889468.log +0 -0
  107. package/examples/saucedemo/.waygraph-auto/4890da1c.log +0 -0
  108. package/examples/saucedemo/.waygraph-auto/4893a63b.log +0 -0
  109. package/examples/saucedemo/.waygraph-auto/4a85f496.log +0 -0
  110. package/examples/saucedemo/.waygraph-auto/4af71440.log +0 -0
  111. package/examples/saucedemo/.waygraph-auto/4f7496d1.log +0 -0
  112. package/examples/saucedemo/.waygraph-auto/50e8baaa.log +0 -0
  113. package/examples/saucedemo/.waygraph-auto/52b3a455.log +0 -0
  114. package/examples/saucedemo/.waygraph-auto/532dd9b8.log +0 -0
  115. package/examples/saucedemo/.waygraph-auto/5391ce40.log +0 -0
  116. package/examples/saucedemo/.waygraph-auto/57dbddb3.log +0 -0
  117. package/examples/saucedemo/.waygraph-auto/5897f501.log +0 -0
  118. package/examples/saucedemo/.waygraph-auto/59c0b57d.log +0 -0
  119. package/examples/saucedemo/.waygraph-auto/5b511794.log +0 -0
  120. package/examples/saucedemo/.waygraph-auto/5c0eef7f.log +0 -0
  121. package/examples/saucedemo/.waygraph-auto/5c47910f.log +0 -0
  122. package/examples/saucedemo/.waygraph-auto/5d60da77.log +0 -0
  123. package/examples/saucedemo/.waygraph-auto/5eda8273.log +0 -0
  124. package/examples/saucedemo/.waygraph-auto/5fc3a33b.log +0 -0
  125. package/examples/saucedemo/.waygraph-auto/61b6546d.log +0 -0
  126. package/examples/saucedemo/.waygraph-auto/621518ad.log +0 -0
  127. package/examples/saucedemo/.waygraph-auto/632b7118.log +0 -0
  128. package/examples/saucedemo/.waygraph-auto/64314c2b.log +0 -0
  129. package/examples/saucedemo/.waygraph-auto/650ed066.log +0 -0
  130. package/examples/saucedemo/.waygraph-auto/66516213.log +0 -0
  131. package/examples/saucedemo/.waygraph-auto/66c17079.log +0 -0
  132. package/examples/saucedemo/.waygraph-auto/69bf0b80.log +0 -0
  133. package/examples/saucedemo/.waygraph-auto/69c2347a.log +0 -0
  134. package/examples/saucedemo/.waygraph-auto/6d22bdb0.log +0 -0
  135. package/examples/saucedemo/.waygraph-auto/6d9aaad3.log +0 -0
  136. package/examples/saucedemo/.waygraph-auto/7103bebb.log +0 -0
  137. package/examples/saucedemo/.waygraph-auto/740c9faa.log +0 -0
  138. package/examples/saucedemo/.waygraph-auto/74872fc5.log +0 -0
  139. package/examples/saucedemo/.waygraph-auto/75846ca9.log +0 -0
  140. package/examples/saucedemo/.waygraph-auto/7626c85b.log +0 -0
  141. package/examples/saucedemo/.waygraph-auto/76308eec.log +0 -0
  142. package/examples/saucedemo/.waygraph-auto/76c1650e.log +0 -0
  143. package/examples/saucedemo/.waygraph-auto/7861f38f.log +0 -0
  144. package/examples/saucedemo/.waygraph-auto/78b21219.log +0 -0
  145. package/examples/saucedemo/.waygraph-auto/7af224ed.log +0 -0
  146. package/examples/saucedemo/.waygraph-auto/7c3be585.log +0 -0
  147. package/examples/saucedemo/.waygraph-auto/7ed55a2c.log +0 -0
  148. package/examples/saucedemo/.waygraph-auto/80086ba2.log +0 -0
  149. package/examples/saucedemo/.waygraph-auto/80206369.log +0 -0
  150. package/examples/saucedemo/.waygraph-auto/847f1092.log +0 -0
  151. package/examples/saucedemo/.waygraph-auto/84d6d293.log +0 -0
  152. package/examples/saucedemo/.waygraph-auto/8581abfa.log +0 -0
  153. package/examples/saucedemo/.waygraph-auto/87b7e0fd.log +0 -0
  154. package/examples/saucedemo/.waygraph-auto/87c79256.log +0 -0
  155. package/examples/saucedemo/.waygraph-auto/88af2495.log +0 -0
  156. package/examples/saucedemo/.waygraph-auto/8a3eb088.log +0 -0
  157. package/examples/saucedemo/.waygraph-auto/8c04a403.log +0 -0
  158. package/examples/saucedemo/.waygraph-auto/8c9ab09e.log +0 -0
  159. package/examples/saucedemo/.waygraph-auto/8cc5db51.log +0 -0
  160. package/examples/saucedemo/.waygraph-auto/8ea3a69c.log +0 -0
  161. package/examples/saucedemo/.waygraph-auto/9150f913.log +0 -0
  162. package/examples/saucedemo/.waygraph-auto/929a4313.log +0 -0
  163. package/examples/saucedemo/.waygraph-auto/95a599e4.log +0 -0
  164. package/examples/saucedemo/.waygraph-auto/96c45350.log +0 -0
  165. package/examples/saucedemo/.waygraph-auto/986030e6.log +0 -0
  166. package/examples/saucedemo/.waygraph-auto/99787abb.log +0 -0
  167. package/examples/saucedemo/.waygraph-auto/99ddb62c.log +0 -0
  168. package/examples/saucedemo/.waygraph-auto/a2dd30dd.log +0 -0
  169. package/examples/saucedemo/.waygraph-auto/a63a2d20.log +0 -0
  170. package/examples/saucedemo/.waygraph-auto/a78befb7.log +0 -0
  171. package/examples/saucedemo/.waygraph-auto/a84d9b79.log +0 -0
  172. package/examples/saucedemo/.waygraph-auto/a87bb3ee.log +0 -0
  173. package/examples/saucedemo/.waygraph-auto/a9270890.log +0 -0
  174. package/examples/saucedemo/.waygraph-auto/aa440153.log +0 -0
  175. package/examples/saucedemo/.waygraph-auto/ab7561e0.log +0 -0
  176. package/examples/saucedemo/.waygraph-auto/ac19ae60.log +0 -0
  177. package/examples/saucedemo/.waygraph-auto/ae21ad8e.log +0 -0
  178. package/examples/saucedemo/.waygraph-auto/af09042f.log +0 -0
  179. package/examples/saucedemo/.waygraph-auto/b1684126.log +0 -0
  180. package/examples/saucedemo/.waygraph-auto/b21e9d12.log +0 -0
  181. package/examples/saucedemo/.waygraph-auto/b5a838be.log +0 -0
  182. package/examples/saucedemo/.waygraph-auto/b603549d.log +0 -0
  183. package/examples/saucedemo/.waygraph-auto/b81e074f.log +0 -0
  184. package/examples/saucedemo/.waygraph-auto/b83e6259.log +0 -0
  185. package/examples/saucedemo/.waygraph-auto/b9cb2796.log +0 -0
  186. package/examples/saucedemo/.waygraph-auto/ba58abb7.log +0 -0
  187. package/examples/saucedemo/.waygraph-auto/bb0a0018.log +0 -0
  188. package/examples/saucedemo/.waygraph-auto/bb9fd384.log +0 -0
  189. package/examples/saucedemo/.waygraph-auto/bc0bdd0e.log +0 -0
  190. package/examples/saucedemo/.waygraph-auto/bc7d4a83.log +0 -0
  191. package/examples/saucedemo/.waygraph-auto/bd8e082a.log +0 -0
  192. package/examples/saucedemo/.waygraph-auto/bea0c93e.log +0 -0
  193. package/examples/saucedemo/.waygraph-auto/bf35201a.log +0 -0
  194. package/examples/saucedemo/.waygraph-auto/bffa3a7a.log +0 -0
  195. package/examples/saucedemo/.waygraph-auto/c02ea583.log +0 -0
  196. package/examples/saucedemo/.waygraph-auto/c2caf0a8.log +0 -0
  197. package/examples/saucedemo/.waygraph-auto/c3bdc714.log +0 -0
  198. package/examples/saucedemo/.waygraph-auto/c4afb8bc.log +0 -0
  199. package/examples/saucedemo/.waygraph-auto/c916a654.log +0 -0
  200. package/examples/saucedemo/.waygraph-auto/c9b8d69e.log +0 -0
  201. package/examples/saucedemo/.waygraph-auto/cd68bbc4.log +0 -0
  202. package/examples/saucedemo/.waygraph-auto/cdc6db50.log +0 -0
  203. package/examples/saucedemo/.waygraph-auto/d01906a7.log +0 -0
  204. package/examples/saucedemo/.waygraph-auto/d02e5e7e.log +0 -0
  205. package/examples/saucedemo/.waygraph-auto/d09aff71.log +0 -0
  206. package/examples/saucedemo/.waygraph-auto/d0e41396.log +0 -0
  207. package/examples/saucedemo/.waygraph-auto/d1370d76.log +0 -0
  208. package/examples/saucedemo/.waygraph-auto/d3714ae4.log +0 -0
  209. package/examples/saucedemo/.waygraph-auto/d9e5f8a4.log +0 -0
  210. package/examples/saucedemo/.waygraph-auto/da0685dc.log +0 -0
  211. package/examples/saucedemo/.waygraph-auto/da894ad8.log +0 -0
  212. package/examples/saucedemo/.waygraph-auto/daa2f922.log +0 -0
  213. package/examples/saucedemo/.waygraph-auto/daefb614.log +0 -0
  214. package/examples/saucedemo/.waygraph-auto/db97e833.log +0 -0
  215. package/examples/saucedemo/.waygraph-auto/dec3076e.log +0 -0
  216. package/examples/saucedemo/.waygraph-auto/e101d19a.log +0 -0
  217. package/examples/saucedemo/.waygraph-auto/e1973991.log +0 -0
  218. package/examples/saucedemo/.waygraph-auto/e207c989.log +0 -0
  219. package/examples/saucedemo/.waygraph-auto/e633e545.log +0 -0
  220. package/examples/saucedemo/.waygraph-auto/e69aec68.log +0 -0
  221. package/examples/saucedemo/.waygraph-auto/e70e2d06.log +0 -0
  222. package/examples/saucedemo/.waygraph-auto/e8306a4b.log +0 -0
  223. package/examples/saucedemo/.waygraph-auto/ebad3410.log +0 -0
  224. package/examples/saucedemo/.waygraph-auto/ec88b3ef.log +0 -0
  225. package/examples/saucedemo/.waygraph-auto/ecbb0966.log +0 -0
  226. package/examples/saucedemo/.waygraph-auto/ecdee753.log +0 -0
  227. package/examples/saucedemo/.waygraph-auto/ed888783.log +0 -0
  228. package/examples/saucedemo/.waygraph-auto/ed931dac.log +0 -0
  229. package/examples/saucedemo/.waygraph-auto/ef655a3f.log +0 -0
  230. package/examples/saucedemo/.waygraph-auto/f34c9a26.log +0 -0
  231. package/examples/saucedemo/.waygraph-auto/f3c29b21.log +0 -0
  232. package/examples/saucedemo/.waygraph-auto/f53f7cfc.log +0 -0
  233. package/examples/saucedemo/.waygraph-auto/f5525d53.log +0 -0
  234. package/examples/saucedemo/.waygraph-auto/f5ad5830.log +0 -0
  235. package/examples/saucedemo/.waygraph-auto/f90035b3.log +0 -0
  236. package/examples/saucedemo/.waygraph-auto/fa687bc2.log +0 -0
  237. package/examples/saucedemo/.waygraph-auto/faed36dd.log +0 -0
  238. package/examples/saucedemo/.waygraph-auto/fb66fba0.log +0 -0
  239. package/examples/saucedemo/.waygraph-auto/fc48fd76.log +0 -0
  240. package/examples/saucedemo/docs/CONVENTION.md +25 -5
  241. package/examples/saucedemo/docs/HELPERS.md +15 -4
  242. package/examples/saucedemo/scripts/capture-wrong-login.mjs +8 -2
  243. package/examples/saucedemo/scripts/prove-wrong-login-runtime.mjs +8 -2
  244. package/examples/saucedemo/src/blocks/SITE-MAP.md +14 -3
  245. package/examples/saucedemo/src/blocks/saucedemo-web/NAV.md +15 -1
  246. package/examples/saucedemo/src/blocks/saucedemo-web/cart/NAV.md +1 -0
  247. package/examples/saucedemo/src/blocks/saucedemo-web/cart/cart.sel.ts +9 -0
  248. package/examples/saucedemo/src/blocks/saucedemo-web/cart/nav-cart.block.ts +3 -2
  249. package/examples/saucedemo/src/blocks/saucedemo-web/cart/nav-checkout-info.block.ts +2 -1
  250. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-one/NAV.md +6 -1
  251. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-one/methods/checkout-info.sel.ts +11 -0
  252. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-one/methods/fill-first-name.method.block.ts +22 -0
  253. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-one/methods/fill-last-name.method.block.ts +21 -0
  254. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-one/methods/fill-postal-code.method.block.ts +21 -0
  255. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-one/methods/submit-checkout-info.method.block.ts +6 -7
  256. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-two/NAV.md +2 -0
  257. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-two/methods/checkout-overview.sel.ts +9 -0
  258. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-two/methods/finish-order.method.block.ts +7 -6
  259. package/examples/saucedemo/src/blocks/saucedemo-web/ff-owner-auth.block.ts +7 -2
  260. package/examples/saucedemo/src/blocks/saucedemo-web/inventory-item/NAV.md +1 -0
  261. package/examples/saucedemo/src/blocks/saucedemo-web/inventory-item/item-detail.sel.ts +9 -0
  262. package/examples/saucedemo/src/blocks/saucedemo-web/inventory-item/nav-back-to-inventory.block.ts +2 -1
  263. package/examples/saucedemo/src/blocks/saucedemo-web/inventory-item/nav-back-to-products.block.ts +2 -1
  264. package/examples/saucedemo/src/blocks/saucedemo-web/inventory-item/nav-continue-shopping.block.ts +2 -1
  265. package/examples/saucedemo/src/blocks/saucedemo-web/methods/fill-password.method.block.ts +51 -0
  266. package/examples/saucedemo/src/blocks/saucedemo-web/methods/fill-username.method.block.ts +69 -0
  267. package/examples/saucedemo/src/blocks/saucedemo-web/methods/login.sel.ts +13 -0
  268. package/examples/saucedemo/src/blocks/saucedemo-web/methods/submit-login-for-flow.ts +4 -4
  269. package/examples/saucedemo/src/blocks/saucedemo-web/methods/submit-login.method.block.ts +13 -51
  270. package/examples/saucedemo/src/blocks/saucedemo-web/methods/submit-logout.method.block.ts +4 -3
  271. package/examples/saucedemo/src/blocks/saucedemo-web/nav-login.block.ts +14 -6
  272. package/examples/saucedemo/src/flows/checkout-then-browse-again.flow.ts +15 -0
  273. package/examples/saucedemo/src/flows/checkout.flow.ts +20 -2
  274. package/examples/saucedemo/src/flows/device-showcase.flow.ts +4 -0
  275. package/examples/saucedemo/src/flows/login.flow.ts +3 -1
  276. package/examples/saucedemo/src/flows/shop.flow.ts +6 -0
  277. package/examples/saucedemo/src/flows/smoke.flow.ts +13 -7
  278. package/examples/saucedemo/src/flows/stub-on-error.flow.ts +4 -2
  279. package/examples/saucedemo/src/flows/viewer-blocked.flow.ts +6 -2
  280. package/examples/saucedemo/tests/flow-block-verify.spec.ts +3 -3
  281. package/examples/saucedemo/tests/submit-login-branch.spec.ts +5 -3
  282. package/package.json +2 -1
  283. package/templates/agents/waygraph-author.agent.md +43 -5
  284. package/templates/agents/waygraph-planner.agent.md +33 -2
  285. package/templates/quickstart/src/blocks/saucedemo-web/NAV.md +15 -1
  286. package/templates/quickstart/src/blocks/saucedemo-web/cart/NAV.md +1 -0
  287. package/templates/quickstart/src/blocks/saucedemo-web/cart/cart.sel.ts +9 -0
  288. package/templates/quickstart/src/blocks/saucedemo-web/cart/nav-cart.block.ts +19 -15
  289. package/templates/quickstart/src/blocks/saucedemo-web/cart/nav-checkout-info.block.ts +2 -1
  290. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-one/NAV.md +6 -1
  291. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-one/methods/checkout-info.sel.ts +11 -0
  292. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-one/methods/fill-first-name.method.block.ts +22 -0
  293. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-one/methods/fill-last-name.method.block.ts +21 -0
  294. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-one/methods/fill-postal-code.method.block.ts +21 -0
  295. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-one/methods/submit-checkout-info.method.block.ts +6 -7
  296. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-two/NAV.md +2 -0
  297. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-two/methods/checkout-overview.sel.ts +9 -0
  298. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-two/methods/finish-order.method.block.ts +7 -6
  299. package/templates/quickstart/src/blocks/saucedemo-web/ff-owner-auth.block.ts +7 -2
  300. package/templates/quickstart/src/blocks/saucedemo-web/inventory/methods/add-to-cart.effect.block.ts +24 -16
  301. package/templates/quickstart/src/blocks/saucedemo-web/inventory/nav-item-detail.block.ts +3 -3
  302. package/templates/quickstart/src/blocks/saucedemo-web/inventory-item/NAV.md +1 -0
  303. package/templates/quickstart/src/blocks/saucedemo-web/inventory-item/item-detail.sel.ts +9 -0
  304. package/templates/quickstart/src/blocks/saucedemo-web/inventory-item/nav-back-to-inventory.block.ts +2 -1
  305. package/templates/quickstart/src/blocks/saucedemo-web/inventory-item/nav-back-to-products.block.ts +2 -1
  306. package/templates/quickstart/src/blocks/saucedemo-web/inventory-item/nav-continue-shopping.block.ts +2 -1
  307. package/templates/quickstart/src/blocks/saucedemo-web/methods/fill-password.method.block.ts +51 -0
  308. package/templates/quickstart/src/blocks/saucedemo-web/methods/fill-username.method.block.ts +69 -0
  309. package/templates/quickstart/src/blocks/saucedemo-web/methods/login.sel.ts +13 -0
  310. package/templates/quickstart/src/blocks/saucedemo-web/methods/submit-login-for-flow.ts +4 -4
  311. package/templates/quickstart/src/blocks/saucedemo-web/methods/submit-login.method.block.ts +35 -63
  312. package/templates/quickstart/src/blocks/saucedemo-web/methods/submit-logout.method.block.ts +4 -3
  313. package/templates/quickstart/src/blocks/saucedemo-web/nav-login.block.ts +14 -6
  314. package/templates/quickstart/src/flows/checkout-then-browse-again.flow.ts +15 -0
  315. package/templates/quickstart/src/flows/checkout.flow.ts +29 -2
  316. package/templates/quickstart/src/flows/device-showcase.flow.ts +4 -0
  317. package/templates/quickstart/src/flows/login.flow.ts +3 -1
  318. package/templates/quickstart/src/flows/shop.flow.ts +6 -0
  319. package/templates/quickstart/src/flows/smoke.flow.ts +13 -7
  320. package/templates/quickstart/src/flows/viewer-blocked.flow.ts +6 -2
  321. package/templates/quickstart/tests/chain-flow.spec.ts +8 -1
  322. package/templates/scaffold/STRUCTURE.md +23 -3
  323. package/templates/scaffold/scripts/fixture-server.mjs +11 -2
  324. package/templates/scaffold/src/flows/example.flow.ts +8 -6
  325. package/templates/scaffold/src/flows/mail-verify.flow.ts +85 -0
  326. package/templates/scaffold/src/flows/shop.flow.ts +23 -6
  327. package/templates/scaffold/src/map/(app_base)/docs/_nav.block.ts +20 -0
  328. package/templates/scaffold/src/map/(app_base)/docs/_page.block.ts +15 -0
  329. package/templates/scaffold/src/map/(app_base)/docs/_sel.ts +7 -0
  330. package/templates/scaffold/src/map/(app_base)/docs/docs.html +8 -0
  331. package/templates/scaffold/src/{blocks/demo-web/methods → map/(app_base)/home/_methods}/add-item.effect.block.ts +3 -3
  332. package/templates/scaffold/src/map/(app_base)/home/_methods/assert-hello.method.block.ts +46 -0
  333. package/templates/scaffold/src/map/(app_base)/home/_methods/assert-item-added.method.block.ts +38 -0
  334. package/templates/scaffold/src/{blocks/demo-web/methods → map/(app_base)/home/_methods}/clear-cart.method.block.ts +9 -3
  335. package/templates/scaffold/src/{blocks/demo-web/methods → map/(app_base)/home/_methods}/remove-item.effect.block.ts +3 -3
  336. package/templates/scaffold/src/{blocks/demo-web/nav-home.block.ts → map/(app_base)/home/_nav.block.ts} +2 -2
  337. package/templates/scaffold/src/{blocks/demo-web/home.page.block.ts → map/(app_base)/home/_page.block.ts} +7 -7
  338. package/templates/scaffold/src/{blocks/demo-web/demo-sel.ts → map/(app_base)/home/_sel.ts} +1 -0
  339. package/templates/scaffold/src/{blocks/demo-web → map/(app_base)/home}/home.html +4 -0
  340. package/templates/scaffold/src/map/(app_base)/home/nav-verification-link.block.ts +34 -0
  341. package/templates/scaffold/src/map/(external)/mailpit/_methods/assert-email-content.method.block.ts +48 -0
  342. package/templates/scaffold/src/map/(external)/mailpit/_methods/assert-email-received.method.block.ts +49 -0
  343. package/templates/scaffold/src/map/(external)/mailpit/_methods/extract-email-link.method.block.ts +41 -0
  344. package/templates/scaffold/src/map/(external)/mailpit/_methods/open-message.method.block.ts +39 -0
  345. package/templates/scaffold/src/map/(external)/mailpit/_nav.block.ts +20 -0
  346. package/templates/scaffold/src/map/(external)/mailpit/_page.block.ts +30 -0
  347. package/templates/scaffold/src/map/(external)/mailpit/_sel.ts +5 -0
  348. package/templates/scaffold/src/states/demo.mem-keys.ts +25 -1
  349. package/templates/scaffold/src/states/demo.states.ts +11 -0
  350. package/templates/scaffold/test-results/.last-run.json +4 -0
  351. package/templates/scaffold/tests/example.spec.ts +2 -2
  352. package/templates/scaffold/tests/mail-verify.spec.ts +191 -0
  353. package/templates/skills/waygraph-blind-pilot.skill.md +61 -0
  354. package/templates/skills/waygraph-pilot.skill.md +108 -0
  355. package/templates/quickstart/README-saucedemo-commands.md +0 -72
  356. package/templates/scaffold/src/blocks/SITE-MAP.md +0 -12
  357. package/templates/scaffold/src/blocks/demo-web/NAV.md +0 -29
  358. package/templates/scaffold/src/blocks/demo-web/methods/assert-hello.method.block.ts +0 -50
package/dist/engine.d.ts CHANGED
@@ -13,6 +13,52 @@ import { type HighlightStyleDefaults, type DemoPace } from "./highlights.js";
13
13
  * since those depend on a runtime decision that hasn't happened yet.
14
14
  */
15
15
  export declare function preflight(mem: MemPage, block: Block<any, any>): void;
16
+ /**
17
+ * A cross-cutting, automatically-enforced verify - the real engine
18
+ * primitive behind "persistent UI" (a sidebar, a header, anything that's
19
+ * supposed to be present on many/most Checkpoints, not just one page's own
20
+ * Block). Deliberately NOT tied to any folder convention - `appliesTo`
21
+ * matches by Checkpoint tag, explicitly, so this works the same whether a
22
+ * project uses the Waygraph Map folder convention or the older freeform
23
+ * "manual mode" layout real production consumers (pia-waygraph,
24
+ * zsign-atomic-waygraph) already use - registering a Layout with an Engine
25
+ * is the only requirement, folder structure is irrelevant to it.
26
+ *
27
+ * Not a Block: no `act`/`resolve`/Checkpoint of its own, never a graph
28
+ * node - it only ever adds extra `verify` Traits to Checkpoints something
29
+ * else already resolved to, the same way `runVerify` already runs after
30
+ * any Block's own `resolve()` (see `runGraph`'s own verify step, just
31
+ * below) - a Layout is that mechanism made reusable across many
32
+ * Checkpoints instead of hand-repeated per Block.
33
+ */
34
+ export interface Layout {
35
+ name: string;
36
+ description?: string;
37
+ appliesTo: (tag: string) => boolean;
38
+ /** Function form gets the full resolved Checkpoint, same shape `runVerify`/a Block's own `verify` already use - not just the tag. */
39
+ verify: Trait[] | ((out: Checkpoint<string>) => Trait[]);
40
+ }
41
+ export interface LayoutOptions {
42
+ name: string;
43
+ description?: string;
44
+ /** A fixed tag list, or a predicate for open-ended matching (e.g. a naming pattern). */
45
+ appliesTo: readonly string[] | ((tag: string) => boolean);
46
+ verify: Trait[] | ((out: Checkpoint<string>) => Trait[]);
47
+ }
48
+ /**
49
+ * @example
50
+ * const AppShellLayout = defineLayout({
51
+ * name: "app-shell",
52
+ * appliesTo: ["AppHome", "Chats", "Notifications", "Settings"],
53
+ * verify: [Trait.visible(SidebarSel.root)],
54
+ * });
55
+ * const engine = new Engine({ layouts: [AppShellLayout] });
56
+ * // Now every Block resolving to one of those four Checkpoints, in any
57
+ * // Flow this engine defines, also gets AppShellLayout's own verify run
58
+ * // automatically - a page that silently drops the sidebar fails loud,
59
+ * // even though no individual Block's own `verify` mentions it.
60
+ */
61
+ export declare function defineLayout(options: LayoutOptions): Layout;
16
62
  /**
17
63
  * Runs `entry` against a fresh tab and returns the resulting terminal Checkpoint.
18
64
  * The engine owns the tab's lifecycle, not any individual Block: the tab is
@@ -52,6 +98,14 @@ export interface RunGraphOptions {
52
98
  * `page` and `closeOnFinish` together) - it just does what you ask.
53
99
  */
54
100
  closeOnFinish?: boolean;
101
+ /**
102
+ * Layouts (see {@link defineLayout}) whose `verify` runs automatically
103
+ * after any Block in this run resolves to a Checkpoint they apply to -
104
+ * `Flow.run` threads this in from `EngineConfig.layouts` by default; pass
105
+ * it directly here to call `runGraph` standalone (outside a `Flow`) with
106
+ * layouts still enforced.
107
+ */
108
+ layouts?: readonly Layout[];
55
109
  }
56
110
  /**
57
111
  * Drives a genuinely separate second tab through its own Block/Flow, in the
@@ -435,6 +489,14 @@ export interface EngineConfig {
435
489
  height: number;
436
490
  };
437
491
  };
492
+ /**
493
+ * Cross-cutting, automatically-enforced verify (see {@link defineLayout})
494
+ * - applies to every Flow this Engine defines. Works the same regardless
495
+ * of folder convention (Waygraph Map or freeform "manual mode") since
496
+ * matching is by Checkpoint tag, never by file path.
497
+ * @example new Engine({ layouts: [AppShellLayout] })
498
+ */
499
+ layouts?: readonly Layout[];
438
500
  }
439
501
  type S = Checkpoint<"__start__">;
440
502
  /**
@@ -595,7 +657,106 @@ export declare class Engine {
595
657
  Block<N, O>,
596
658
  EndMarker
597
659
  ]): Flow<O>;
660
+ /**
661
+ * Fluent, kind-checked alternative to `defineFlow([start, ...blocks, end])` -
662
+ * see {@link MapBuilder}. Real, direct request this responds to: pia/zsign's
663
+ * own agents kept hand-editing/hand-composing Blocks into ad hoc shapes
664
+ * ("locks" convention tried, still got broken) - `map()` forces every step
665
+ * through this Engine's own `define*Block` factories (checked by the same
666
+ * `__waygraphKind`/`__waygraphSalt` runtime markers `graph.ts`/`map-check.ts`
667
+ * already trust), so a hand-rolled plain-object Block can never silently
668
+ * pass as a real navigation/assertion/method step. `homeOrigin`, if given,
669
+ * also gates `.gotoPage()`/`.gotoExternal()` against each Nav/Page Block's
670
+ * own static `url` (skipped, honestly, for click-based/dynamic nav - not
671
+ * statically checkable, same limitation `map-check.ts` already documents).
672
+ * @example
673
+ * const flow = engine.map({ homeOrigin: "https://app.example.com" })
674
+ * .start()
675
+ * .gotoPage(NavHomeBlock)
676
+ * .assert(AssertHelloBlock)
677
+ * .gotoExternal(NavMailpitBlock)
678
+ * .end();
679
+ */
680
+ map(options?: MapBuilderOptions): MapBuilder<S>;
681
+ }
682
+ export interface MapBuilderOptions {
683
+ /**
684
+ * This project's own origin (e.g. `"https://app.example.com"`) - enables
685
+ * `.gotoPage()`/`.gotoExternal()`'s origin check against a Block's static
686
+ * `url`. Omit to skip that check entirely (still fully kind-checked either
687
+ * way; only the internal/external origin distinction is opt-in).
688
+ */
689
+ homeOrigin?: string;
598
690
  }
691
+ /**
692
+ * Fluent builder over {@link Engine.defineFlow} - see `map()`'s own doc
693
+ * comment for why this exists. Each step method is scoped to exactly the
694
+ * Block kind its name promises, checked at RUNTIME against the
695
+ * `__waygraphKind`/`__waygraphSalt` markers `defineNavBlock`/`definePageBlock`/
696
+ * `defineAssertBlock`/`defineMethodBlock`/`defineEffectBlock` already set
697
+ * (the same markers `graph.ts` and `map-check.ts` trust) - not just a type
698
+ * hint, since a hand-rolled object can satisfy the TypeScript `Block<In,Out>`
699
+ * shape without ever going through a real factory. Each call also
700
+ * typechecks the accumulated Checkpoint chain exactly like `defineFlow`'s
701
+ * own tuple overloads do (a step's `In` must equal the previous step's
702
+ * `Out`) - this is what "no teleporting" means: there is no method on this
703
+ * builder that can skip from one Checkpoint to an unrelated one without a
704
+ * real, kind-correct Block in between.
705
+ */
706
+ export declare class MapBuilder<Out extends Checkpoint<string>> {
707
+ private readonly engine;
708
+ private readonly steps;
709
+ private readonly homeOrigin;
710
+ private constructor();
711
+ /** @internal - use `engine.map()` or the standalone `map()` export. */
712
+ static begin(engine: Engine, homeOrigin: string | undefined): MapBuilder<S>;
713
+ /** Readable chain-opener - mirrors `defineFlow`'s leading `start` sentinel. Returns `this` unchanged; entirely optional. */
714
+ start(): MapBuilder<Out>;
715
+ /**
716
+ * Appends an internal navigation/page-arrival step - requires a Block from
717
+ * `defineNavBlock`/`defineMemNavBlock`/`definePageBlock`. Throws if given
718
+ * anything else, including a `homeOrigin`-mismatched static `url`.
719
+ */
720
+ gotoPage<NextOut extends Checkpoint<string>>(block: Block<Out, NextOut> & {
721
+ name: string;
722
+ }): MapBuilder<NextOut>;
723
+ /**
724
+ * Appends a genuinely cross-origin navigation step (e.g. `(external)/`
725
+ * Blocks in the Waygraph Map convention: mailpit, maildrop.cc) - same
726
+ * kind requirement as {@link gotoPage}, plus the inverse `homeOrigin` check.
727
+ */
728
+ gotoExternal<NextOut extends Checkpoint<string>>(block: Block<Out, NextOut> & {
729
+ name: string;
730
+ }): MapBuilder<NextOut>;
731
+ /** Appends a self-loop verification step - requires a Block from `defineAssertBlock`. */
732
+ assert<NextOut extends Checkpoint<string>>(block: Block<Out, NextOut> & {
733
+ name: string;
734
+ }): MapBuilder<NextOut>;
735
+ /**
736
+ * Appends a non-navigating page action (submit, upload, add/remove an
737
+ * instance) - requires a Block from `defineMethodBlock`/`defineActionBlock`/
738
+ * `defineEffectBlock`/`defineMemEffectBlock`.
739
+ */
740
+ method<NextOut extends Checkpoint<string>>(block: Block<Out, NextOut> & {
741
+ name: string;
742
+ }): MapBuilder<NextOut>;
743
+ /**
744
+ * Finalizes this chain into a real, runnable {@link Flow} - same object
745
+ * `defineFlow` returns, so `withBlockVerify`/`modBlockVerify` and every
746
+ * `withTitle`/`withHighlightFixtures`/etc. decorator still apply exactly
747
+ * as they do today; this builder only changes how the chain is assembled,
748
+ * never what it produces. Throws if no step was ever added - an empty map
749
+ * isn't a flow.
750
+ */
751
+ end(): Flow<Out>;
752
+ }
753
+ /**
754
+ * Standalone convenience for `new Engine(config).map(options)` - use this
755
+ * when a call site doesn't otherwise need its own `Engine` instance (no
756
+ * shared `headless`/`slowMo`/`layouts` across several flows).
757
+ * @example const flow = map({ homeOrigin: "https://app.example.com" }).gotoPage(NavHomeBlock).end();
758
+ */
759
+ export declare function map(options?: MapBuilderOptions & EngineConfig): MapBuilder<S>;
599
760
  /**
600
761
  * Attaches routing to `block` without touching its act/observe/resolve/verify -
601
762
  * `routes` must cover every tag of `block`'s output (a missing route is a compile
@@ -637,7 +798,7 @@ export declare function defineBlock<In extends Checkpoint<string>, Out extends C
637
798
  * (same shape `DefinedBlock`/`ComposedBlock` already use), so it drops into
638
799
  * `defineFlow([...])`, `connect()`, `composeBlock()` exactly like any other
639
800
  * Block - no special-casing needed anywhere that only expects a `Block`.
640
- * See `openspec/changes/nav-block-and-check/` for why this exists.
801
+ * See `openspec/specs/nav-block-and-check/spec.md` for why this exists.
641
802
  */
642
803
  export type NavBlock<Out extends Checkpoint<string>> = DefinedBlock<Checkpoint<string>, Out>;
643
804
  /**
@@ -742,6 +903,45 @@ export type ActionBlock<In extends Checkpoint<string>, Out extends Checkpoint<st
742
903
  export declare function defineMethodBlock<In extends Checkpoint<string>, Out extends Checkpoint<string>>(base: Parameters<typeof defineBlock<In, Out>>[0]): MethodBlock<In, Out>;
743
904
  /** @deprecated Prefer {@link defineMethodBlock} - identical helper (0.7 name). */
744
905
  export declare const defineActionBlock: typeof defineMethodBlock;
906
+ export interface AssertBlockOptions<Out extends Checkpoint<string> = Checkpoint<string>> {
907
+ name: string;
908
+ description?: string;
909
+ /** Self-loop target - the Checkpoint this assertion runs on and returns to. */
910
+ checkpoint: Out["__state"];
911
+ /**
912
+ * Shorthand for the wait-then-assert shape found repeatedly in hand-written
913
+ * assertion Blocks: waits for a heading with this accessible name before
914
+ * `verify` runs. Omit for a pure "assert whatever is already on the page"
915
+ * check with no wait. Not a general `act()` override - a Block that needs
916
+ * more than this stays a plain `defineMethodBlock`.
917
+ */
918
+ waitForHeading?: string;
919
+ verify: Trait[] | ((out: Out) => Trait[]);
920
+ /** Mem keys this assertion's `verify` reads (e.g. a mem-aware Trait) - checked by preflight before the flow runs. */
921
+ requires?: readonly MemKey<any>[];
922
+ stubBefore?: import("./highlights.js").HighlightStubPhaseOrFn<Out>;
923
+ stubAfter?: import("./highlights.js").HighlightStubPhaseOrFn<Out>;
924
+ stubOnError?: import("./highlights.js").HighlightStubPhaseOrFn<Out>;
925
+ /** Multi-step yap slides (demo only) - same field `Instruction.slides` exposes elsewhere. */
926
+ slides?: import("./highlights.js").WaygraphSlidesOrFn<Out>;
927
+ }
928
+ /**
929
+ * Sugar for a self-loop-only "pure assertion" Block: no hand-written
930
+ * `act`/`resolve`, just a name, a `checkpoint` tag, and a `verify` array.
931
+ * `resolve` is generated from the one `checkpoint` field given, closing the
932
+ * copy/rename footgun where a hand-restated checkpoint string can silently
933
+ * drift wrong across a copy/pasted file. Mirrors `definePageBlock`'s own
934
+ * construction pattern (flat options in, `defineBlock` built internally).
935
+ * See openspec/changes/waygraph-agent-skill-hardening/spec.md.
936
+ * @example
937
+ * defineAssertBlock({
938
+ * name: "assert-raw-materials-table",
939
+ * checkpoint: "RawMaterials",
940
+ * waitForHeading: "Raw Materials & Lead Times",
941
+ * verify: [Trait.visible(RawMaterialsSel.reorderBadge)],
942
+ * });
943
+ */
944
+ export declare function defineAssertBlock<Out extends Checkpoint<string> = Checkpoint<string>>(options: AssertBlockOptions<Out>): MethodBlock<Out, Out>;
745
945
  /** One Block (or lazy factory) registered on a {@link PageBlock}. */
746
946
  export type PageMethodEntry = DefinedBlock<any, any> | (() => DefinedBlock<any, any>);
747
947
  export type PageBlockOptions<Out extends Checkpoint<string>> = {
package/dist/engine.js CHANGED
@@ -34,6 +34,46 @@ export function preflight(mem, block) {
34
34
  .join(", ")}`);
35
35
  }
36
36
  }
37
+ /**
38
+ * @example
39
+ * const AppShellLayout = defineLayout({
40
+ * name: "app-shell",
41
+ * appliesTo: ["AppHome", "Chats", "Notifications", "Settings"],
42
+ * verify: [Trait.visible(SidebarSel.root)],
43
+ * });
44
+ * const engine = new Engine({ layouts: [AppShellLayout] });
45
+ * // Now every Block resolving to one of those four Checkpoints, in any
46
+ * // Flow this engine defines, also gets AppShellLayout's own verify run
47
+ * // automatically - a page that silently drops the sidebar fails loud,
48
+ * // even though no individual Block's own `verify` mentions it.
49
+ */
50
+ export function defineLayout(options) {
51
+ const appliesTo = typeof options.appliesTo === "function"
52
+ ? options.appliesTo
53
+ : (tag) => options.appliesTo.includes(tag);
54
+ return {
55
+ name: options.name,
56
+ ...(options.description ? { description: options.description } : {}),
57
+ appliesTo,
58
+ verify: options.verify,
59
+ };
60
+ }
61
+ /**
62
+ * Runs every registered layout whose `appliesTo(tag)` matches the just-
63
+ * resolved Checkpoint, in registration order, right after the Block's own
64
+ * `verify` already ran. A layout's own Trait failing throws the same way
65
+ * any other `runVerify` failure does, naming the layout so it's clearly
66
+ * distinguishable from the Block's own verify in the error message.
67
+ */
68
+ async function runLayouts(layouts, tag, out, page, mem, blockName) {
69
+ if (!layouts)
70
+ return;
71
+ for (const layout of layouts) {
72
+ if (!layout.appliesTo(tag))
73
+ continue;
74
+ await runVerify(layout.verify, out, page, mem, `${blockName} (layout: "${layout.name}")`);
75
+ }
76
+ }
37
77
  /**
38
78
  * Runs `entry` against a fresh tab and returns the resulting terminal Checkpoint.
39
79
  * The engine owns the tab's lifecycle, not any individual Block: the tab is
@@ -72,6 +112,7 @@ export async function runGraph(entry, terminals, context, mem, maxSteps = 5000,
72
112
  : undefined;
73
113
  const checkpoint = await current.instruction.resolve(observed);
74
114
  await runVerify(current.instruction.verify, checkpoint, page, mem, current.name);
115
+ await runLayouts(options?.layouts, checkpoint.__state, checkpoint, page, mem, current.name);
75
116
  const next = current.next?.(checkpoint);
76
117
  if (!next) {
77
118
  if (terminals && !terminals.has(checkpoint.__state)) {
@@ -218,7 +259,11 @@ function buildFlow(middle, engineConfig) {
218
259
  // withSessionReset()/withTitle() instead (non-destructive decorators,
219
260
  // same pattern as withVerify), which also re-apply themselves after any
220
261
  // further withBlockVerify/modBlockVerify patch so the flag survives.
221
- const chain = middle.reduce((a, b) => connect(a, b));
262
+ // Layouts have to be threaded into every pairwise connect(), not just the
263
+ // trailing runGraph call - see connect()'s own doc comment in types.ts:
264
+ // buildFlow's reduce IS the only place a defineFlow-built Flow's
265
+ // intermediate (non-final) Checkpoints are ever verified at all.
266
+ const chain = middle.reduce((a, b) => connect(a, b, engineConfig.layouts));
222
267
  // Declared separately (not as the object-literal method below) so the
223
268
  // three-overload `Flow.run` signature can be checked against the actual
224
269
  // per-call-shape behavior, then attached with a cast - object-literal
@@ -248,7 +293,9 @@ function buildFlow(middle, engineConfig) {
248
293
  try {
249
294
  const context = await browser.newContext(config.recordVideo ? { recordVideo: config.recordVideo } : {});
250
295
  try {
251
- return await runGraph(chain, undefined, context, mem);
296
+ return await runGraph(chain, undefined, context, mem, 5000, {
297
+ ...(config.layouts !== undefined ? { layouts: config.layouts } : {}),
298
+ });
252
299
  }
253
300
  finally {
254
301
  // Playwright finalizes recordVideo on context.close(); skip when
@@ -277,9 +324,11 @@ function buildFlow(middle, engineConfig) {
277
324
  // call from before this option existed. An explicit `closeOnFinish`
278
325
  // always wins either way.
279
326
  const closeOnFinish = options?.closeOnFinish ?? gotOwnPage;
327
+ const resolvedLayouts = options?.layouts ?? engineConfig.layouts;
280
328
  const result = await runGraph(chain, undefined, context, mem, 5000, {
281
329
  page,
282
330
  closeOnFinish: false,
331
+ ...(resolvedLayouts !== undefined ? { layouts: resolvedLayouts } : {}),
283
332
  });
284
333
  if (closeOnFinish) {
285
334
  await page.close();
@@ -655,6 +704,185 @@ export class Engine {
655
704
  defineFlow(blocks) {
656
705
  return buildFlow(blocks.slice(1, -1), this.config);
657
706
  }
707
+ /**
708
+ * Fluent, kind-checked alternative to `defineFlow([start, ...blocks, end])` -
709
+ * see {@link MapBuilder}. Real, direct request this responds to: pia/zsign's
710
+ * own agents kept hand-editing/hand-composing Blocks into ad hoc shapes
711
+ * ("locks" convention tried, still got broken) - `map()` forces every step
712
+ * through this Engine's own `define*Block` factories (checked by the same
713
+ * `__waygraphKind`/`__waygraphSalt` runtime markers `graph.ts`/`map-check.ts`
714
+ * already trust), so a hand-rolled plain-object Block can never silently
715
+ * pass as a real navigation/assertion/method step. `homeOrigin`, if given,
716
+ * also gates `.gotoPage()`/`.gotoExternal()` against each Nav/Page Block's
717
+ * own static `url` (skipped, honestly, for click-based/dynamic nav - not
718
+ * statically checkable, same limitation `map-check.ts` already documents).
719
+ * @example
720
+ * const flow = engine.map({ homeOrigin: "https://app.example.com" })
721
+ * .start()
722
+ * .gotoPage(NavHomeBlock)
723
+ * .assert(AssertHelloBlock)
724
+ * .gotoExternal(NavMailpitBlock)
725
+ * .end();
726
+ */
727
+ map(options) {
728
+ return MapBuilder.begin(this, options?.homeOrigin);
729
+ }
730
+ }
731
+ function mapKindOf(block) {
732
+ return block?.__waygraphKind;
733
+ }
734
+ function mapSaltOf(block) {
735
+ return block?.__waygraphSalt;
736
+ }
737
+ function mapNavUrlOf(block) {
738
+ const url = block?.__waygraphNavUrl;
739
+ return typeof url === "string" ? url : undefined;
740
+ }
741
+ const MAP_KIND_FACTORY_HINT = {
742
+ nav: "defineNavBlock/defineMemNavBlock",
743
+ page: "definePageBlock",
744
+ assert: "defineAssertBlock",
745
+ };
746
+ const MAP_SALT_FACTORY_HINT = {
747
+ method: "defineMethodBlock",
748
+ effect: "defineEffectBlock",
749
+ };
750
+ function assertMapKind(block, allowed, method) {
751
+ const kind = mapKindOf(block);
752
+ if (kind !== undefined && allowed.includes(kind))
753
+ return;
754
+ const wanted = allowed.map((k) => MAP_KIND_FACTORY_HINT[k] ?? k).join(" or ");
755
+ const found = kind ? `a Block of kind "${kind}"` : "an object with no waygraph kind marker at all";
756
+ throw new Error(`Waygraph map: .${method}("${block?.name ?? "?"}") requires a Block built with ${wanted} ` +
757
+ `(found ${found} - a hand-built plain object doesn't count). This check is the whole point ` +
758
+ "of the map() builder: only real Blocks from waygraph's own factories can enter a chain.");
759
+ }
760
+ function assertMapSalt(block, allowed, method) {
761
+ const salt = mapSaltOf(block);
762
+ if (salt !== undefined && allowed.includes(salt))
763
+ return;
764
+ const wanted = allowed.map((s) => MAP_SALT_FACTORY_HINT[s] ?? s).join(" or ");
765
+ const found = salt ? `a Block salted "${salt}"` : "an object with no waygraph salt marker at all";
766
+ throw new Error(`Waygraph map: .${method}("${block?.name ?? "?"}") requires a Block built with ${wanted} ` +
767
+ `(found ${found} - a hand-built plain object doesn't count). This check is the whole point ` +
768
+ "of the map() builder: only real Blocks from waygraph's own factories can enter a chain.");
769
+ }
770
+ function assertMapOrigin(block, homeOrigin, expect, method) {
771
+ if (!homeOrigin)
772
+ return; // not configured - can't validate, honest no-op
773
+ const navUrl = mapNavUrlOf(block);
774
+ if (navUrl === undefined)
775
+ return; // click-based/dynamic nav - not statically checkable
776
+ let targetOrigin;
777
+ let wantOrigin;
778
+ try {
779
+ targetOrigin = new URL(navUrl, homeOrigin).origin;
780
+ wantOrigin = new URL(homeOrigin).origin;
781
+ }
782
+ catch {
783
+ return; // malformed URL - not this check's job to validate that
784
+ }
785
+ const isExternal = targetOrigin !== wantOrigin;
786
+ if (expect === "internal" && isExternal) {
787
+ throw new Error(`Waygraph map: .gotoPage("${block.name}") targets ${targetOrigin}, which is NOT this map's ` +
788
+ `home origin (${wantOrigin}) - use .gotoExternal() for a genuinely cross-origin destination.`);
789
+ }
790
+ if (expect === "external" && !isExternal) {
791
+ throw new Error(`Waygraph map: .gotoExternal("${block.name}") targets ${targetOrigin}, which IS this map's ` +
792
+ `home origin (${wantOrigin}) - use .gotoPage() for an internal destination.`);
793
+ }
794
+ }
795
+ /**
796
+ * Fluent builder over {@link Engine.defineFlow} - see `map()`'s own doc
797
+ * comment for why this exists. Each step method is scoped to exactly the
798
+ * Block kind its name promises, checked at RUNTIME against the
799
+ * `__waygraphKind`/`__waygraphSalt` markers `defineNavBlock`/`definePageBlock`/
800
+ * `defineAssertBlock`/`defineMethodBlock`/`defineEffectBlock` already set
801
+ * (the same markers `graph.ts` and `map-check.ts` trust) - not just a type
802
+ * hint, since a hand-rolled object can satisfy the TypeScript `Block<In,Out>`
803
+ * shape without ever going through a real factory. Each call also
804
+ * typechecks the accumulated Checkpoint chain exactly like `defineFlow`'s
805
+ * own tuple overloads do (a step's `In` must equal the previous step's
806
+ * `Out`) - this is what "no teleporting" means: there is no method on this
807
+ * builder that can skip from one Checkpoint to an unrelated one without a
808
+ * real, kind-correct Block in between.
809
+ */
810
+ export class MapBuilder {
811
+ engine;
812
+ steps;
813
+ homeOrigin;
814
+ constructor(engine, steps, homeOrigin) {
815
+ this.engine = engine;
816
+ this.steps = steps;
817
+ this.homeOrigin = homeOrigin;
818
+ }
819
+ /** @internal - use `engine.map()` or the standalone `map()` export. */
820
+ static begin(engine, homeOrigin) {
821
+ return new MapBuilder(engine, [], homeOrigin);
822
+ }
823
+ /** Readable chain-opener - mirrors `defineFlow`'s leading `start` sentinel. Returns `this` unchanged; entirely optional. */
824
+ start() {
825
+ return this;
826
+ }
827
+ /**
828
+ * Appends an internal navigation/page-arrival step - requires a Block from
829
+ * `defineNavBlock`/`defineMemNavBlock`/`definePageBlock`. Throws if given
830
+ * anything else, including a `homeOrigin`-mismatched static `url`.
831
+ */
832
+ gotoPage(block) {
833
+ assertMapKind(block, ["nav", "page"], "gotoPage");
834
+ assertMapOrigin(block, this.homeOrigin, "internal", "gotoPage");
835
+ return new MapBuilder(this.engine, [...this.steps, block], this.homeOrigin);
836
+ }
837
+ /**
838
+ * Appends a genuinely cross-origin navigation step (e.g. `(external)/`
839
+ * Blocks in the Waygraph Map convention: mailpit, maildrop.cc) - same
840
+ * kind requirement as {@link gotoPage}, plus the inverse `homeOrigin` check.
841
+ */
842
+ gotoExternal(block) {
843
+ assertMapKind(block, ["nav", "page"], "gotoExternal");
844
+ assertMapOrigin(block, this.homeOrigin, "external", "gotoExternal");
845
+ return new MapBuilder(this.engine, [...this.steps, block], this.homeOrigin);
846
+ }
847
+ /** Appends a self-loop verification step - requires a Block from `defineAssertBlock`. */
848
+ assert(block) {
849
+ assertMapKind(block, ["assert"], "assert");
850
+ return new MapBuilder(this.engine, [...this.steps, block], this.homeOrigin);
851
+ }
852
+ /**
853
+ * Appends a non-navigating page action (submit, upload, add/remove an
854
+ * instance) - requires a Block from `defineMethodBlock`/`defineActionBlock`/
855
+ * `defineEffectBlock`/`defineMemEffectBlock`.
856
+ */
857
+ method(block) {
858
+ assertMapSalt(block, ["method", "effect"], "method");
859
+ return new MapBuilder(this.engine, [...this.steps, block], this.homeOrigin);
860
+ }
861
+ /**
862
+ * Finalizes this chain into a real, runnable {@link Flow} - same object
863
+ * `defineFlow` returns, so `withBlockVerify`/`modBlockVerify` and every
864
+ * `withTitle`/`withHighlightFixtures`/etc. decorator still apply exactly
865
+ * as they do today; this builder only changes how the chain is assembled,
866
+ * never what it produces. Throws if no step was ever added - an empty map
867
+ * isn't a flow.
868
+ */
869
+ end() {
870
+ if (this.steps.length === 0) {
871
+ throw new Error("Waygraph map: .end() called with zero steps - add at least one .gotoPage()/.gotoExternal()/" +
872
+ ".assert()/.method() before .end()");
873
+ }
874
+ const chain = [start, ...this.steps, end];
875
+ return this.engine.defineFlow(chain);
876
+ }
877
+ }
878
+ /**
879
+ * Standalone convenience for `new Engine(config).map(options)` - use this
880
+ * when a call site doesn't otherwise need its own `Engine` instance (no
881
+ * shared `headless`/`slowMo`/`layouts` across several flows).
882
+ * @example const flow = map({ homeOrigin: "https://app.example.com" }).gotoPage(NavHomeBlock).end();
883
+ */
884
+ export function map(options) {
885
+ return new Engine(options).map(options);
658
886
  }
659
887
  /**
660
888
  * Attaches routing to `block` without touching its act/observe/resolve/verify -
@@ -797,6 +1025,20 @@ export function defineNavBlock(options) {
797
1025
  configurable: false,
798
1026
  });
799
1027
  }
1028
+ // Same idea, for the url case - only when it's a plain string (a
1029
+ // function URL is mem-dependent/dynamic, can't be compared statically
1030
+ // without running it). Real, direct request this responds to: an agent
1031
+ // driving Blind Pilot had no structured way to ask "which of the links
1032
+ // on THIS page are already covered by a NavBlock" without re-parsing
1033
+ // every nav.block.ts's own source - this is what `AutoSession` now reads
1034
+ // to warn about real, unmapped nav links found live on the page.
1035
+ if (typeof options.url === "string") {
1036
+ Object.defineProperty(built, "__waygraphNavUrl", {
1037
+ value: options.url,
1038
+ enumerable: false,
1039
+ configurable: false,
1040
+ });
1041
+ }
800
1042
  return built;
801
1043
  }
802
1044
  /** @see {@link NavClickBlockOptions} */
@@ -826,6 +1068,60 @@ export function defineMethodBlock(base) {
826
1068
  }
827
1069
  /** @deprecated Prefer {@link defineMethodBlock} - identical helper (0.7 name). */
828
1070
  export const defineActionBlock = defineMethodBlock;
1071
+ /**
1072
+ * Sugar for a self-loop-only "pure assertion" Block: no hand-written
1073
+ * `act`/`resolve`, just a name, a `checkpoint` tag, and a `verify` array.
1074
+ * `resolve` is generated from the one `checkpoint` field given, closing the
1075
+ * copy/rename footgun where a hand-restated checkpoint string can silently
1076
+ * drift wrong across a copy/pasted file. Mirrors `definePageBlock`'s own
1077
+ * construction pattern (flat options in, `defineBlock` built internally).
1078
+ * See openspec/changes/waygraph-agent-skill-hardening/spec.md.
1079
+ * @example
1080
+ * defineAssertBlock({
1081
+ * name: "assert-raw-materials-table",
1082
+ * checkpoint: "RawMaterials",
1083
+ * waitForHeading: "Raw Materials & Lead Times",
1084
+ * verify: [Trait.visible(RawMaterialsSel.reorderBadge)],
1085
+ * });
1086
+ */
1087
+ export function defineAssertBlock(options) {
1088
+ const built = defineMethodBlock({
1089
+ name: options.name,
1090
+ ...(options.description ? { description: options.description } : {}),
1091
+ ...(options.requires ? { requires: options.requires } : {}),
1092
+ instruction: {
1093
+ async act(page) {
1094
+ if (options.waitForHeading !== undefined) {
1095
+ await page.getByRole("heading", { name: options.waitForHeading }).waitFor();
1096
+ }
1097
+ },
1098
+ resolve: () => checkpoint(options.checkpoint),
1099
+ verify: options.verify,
1100
+ ...(options.stubBefore ? { stubBefore: options.stubBefore } : {}),
1101
+ ...(options.stubAfter ? { stubAfter: options.stubAfter } : {}),
1102
+ ...(options.stubOnError ? { stubOnError: options.stubOnError } : {}),
1103
+ ...(options.slides ? { slides: options.slides } : {}),
1104
+ },
1105
+ });
1106
+ // Real bug this closes: `waygraph graph`'s static discovery only knows
1107
+ // how to read a Block's Checkpoint tags from an EXPLICIT `<In, Out>`
1108
+ // generic written in the source text (or, for Nav/Page Blocks, by calling
1109
+ // their own input-independent `resolve()` directly). `defineAssertBlock`'s
1110
+ // whole point is skipping that generic - called exactly as documented
1111
+ // (`defineAssertBlock({ name, checkpoint, verify })`, no generics), it was
1112
+ // silently invisible to both `waygraph graph` and the live Pilot menu
1113
+ // ("2 Block(s) skipped (Out not resolvable)", confirmed live against a
1114
+ // real veciro-waygraph session). `resolve()` here is ALSO input-
1115
+ // independent (always `checkpoint(options.checkpoint)`), so the same
1116
+ // resolve()-calling path Nav/Page Blocks already use is reused via this
1117
+ // marker instead of requiring authors to write generics just to be found.
1118
+ Object.defineProperty(built, "__waygraphKind", {
1119
+ value: "assert",
1120
+ enumerable: false,
1121
+ configurable: false,
1122
+ });
1123
+ return built;
1124
+ }
829
1125
  function resolvePageMethods(methods) {
830
1126
  if (!methods)
831
1127
  return [];
@@ -884,6 +1180,20 @@ export function definePageBlock(options) {
884
1180
  enumerable: false,
885
1181
  configurable: false,
886
1182
  });
1183
+ // Same marker `defineNavBlock` sets, same reason - a page hub with a
1184
+ // `url` (deep-link) is exactly as "known, coverable" as a plain
1185
+ // `defineNavBlock`, and in practice most real projects' own nav Blocks
1186
+ // ARE page hubs (methods hanging off them), not bare NavBlocks - the real
1187
+ // saucedemo fixture this whole package tests against uses definePageBlock
1188
+ // for every one of its own nav-* Blocks. Missing this here meant the
1189
+ // marker existed but was never actually set on any real project's Blocks.
1190
+ if (typeof options.url === "string") {
1191
+ Object.defineProperty(built, "__waygraphNavUrl", {
1192
+ value: options.url,
1193
+ enumerable: false,
1194
+ configurable: false,
1195
+ });
1196
+ }
887
1197
  if (options.url !== undefined || options.click !== undefined) {
888
1198
  Object.defineProperty(built, "__waygraphPageDeepLink", {
889
1199
  value: true,
package/dist/graph.d.ts CHANGED
@@ -58,6 +58,24 @@ export declare function findOrphanBlocks(projectDir: string): Promise<OrphanBloc
58
58
  * edges. NavBlocks (`from: "*"`) count from any state.
59
59
  */
60
60
  export declare function findBlockPath(graph: WaygraphGraph, fromTag: string, toTag: string): string[] | null;
61
+ export interface BlockPathStep {
62
+ block: string;
63
+ /** The specific Checkpoint this hop's edge targets - NOT re-derivable by
64
+ * looking up `block` by name afterward, since a union-Out Block (e.g.
65
+ * "Union Out tags create duplicate edges for the same Block", see
66
+ * `discoverGraph`'s own comment above) can have several edges with the
67
+ * same `block` name but different `to` tags; only the specific edge BFS
68
+ * actually routed through here is correct. */
69
+ to: string;
70
+ }
71
+ /**
72
+ * Same BFS as `findBlockPath`, but returns each hop's specific edge (block
73
+ * name + the Checkpoint it targets), not just the block name - needed by any
74
+ * caller that must verify a step actually landed where the route intended,
75
+ * not just re-guess it afterward. `findBlockPath` itself is unchanged and
76
+ * still used wherever only the block-name sequence matters.
77
+ */
78
+ export declare function findBlockPathDetailed(graph: WaygraphGraph, fromTag: string, toTag: string): BlockPathStep[] | null;
61
79
  /**
62
80
  * Walks every `*.block.ts` file under `projectDir` and builds the app's own
63
81
  * state graph. Shares the same in-process-import limitation `waygraph