@probelabs/visor 0.1.107 → 0.1.112

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 (235) hide show
  1. package/README.md +6 -0
  2. package/defaults/task-refinement.yaml +7 -3
  3. package/defaults/visor.tests.yaml +13 -2
  4. package/defaults/visor.yaml +1 -0
  5. package/dist/663.index.js +3 -2
  6. package/dist/80.index.js +3 -2
  7. package/dist/ai-review-service.d.ts +13 -9
  8. package/dist/ai-review-service.d.ts.map +1 -1
  9. package/dist/cli-main.d.ts.map +1 -1
  10. package/dist/cli.d.ts.map +1 -1
  11. package/dist/config.d.ts.map +1 -1
  12. package/dist/debug-visualizer/ws-server.d.ts +7 -1
  13. package/dist/debug-visualizer/ws-server.d.ts.map +1 -1
  14. package/dist/defaults/task-refinement.yaml +7 -3
  15. package/dist/defaults/visor.tests.yaml +13 -2
  16. package/dist/defaults/visor.yaml +1 -0
  17. package/dist/docs/advanced-ai.md +60 -1
  18. package/dist/docs/ai-configuration.md +67 -0
  19. package/dist/docs/ai-custom-tools-usage.md +261 -0
  20. package/dist/docs/ai-custom-tools.md +392 -0
  21. package/dist/docs/bot-transports-rfc.md +23 -0
  22. package/dist/docs/configuration.md +21 -0
  23. package/dist/docs/engine-pause-resume-rfc.md +192 -0
  24. package/dist/docs/lifecycle-hooks.md +253 -0
  25. package/dist/docs/liquid-templates.md +143 -0
  26. package/dist/docs/providers/git-checkout.md +589 -0
  27. package/dist/docs/recipes.md +458 -5
  28. package/dist/docs/rfc/git-checkout-step.md +601 -0
  29. package/dist/docs/rfc/on_init-hook.md +1294 -0
  30. package/dist/docs/rfc/workspace-isolation.md +216 -0
  31. package/dist/docs/router-patterns.md +339 -0
  32. package/dist/event-bus/types.d.ts +14 -0
  33. package/dist/event-bus/types.d.ts.map +1 -1
  34. package/dist/examples/ai-custom-tools-example.yaml +206 -0
  35. package/dist/examples/ai-custom-tools-simple.yaml +76 -0
  36. package/dist/examples/git-checkout-basic.yaml +32 -0
  37. package/dist/examples/git-checkout-compare.yaml +59 -0
  38. package/dist/examples/git-checkout-cross-repo.yaml +76 -0
  39. package/dist/examples/on-init-import-demo.yaml +179 -0
  40. package/dist/examples/reusable-tools.yaml +92 -0
  41. package/dist/examples/reusable-workflows.yaml +88 -0
  42. package/dist/examples/session-reuse-self.yaml +81 -0
  43. package/dist/examples/slack-simple-chat.yaml +775 -0
  44. package/dist/failure-condition-evaluator.d.ts +2 -0
  45. package/dist/failure-condition-evaluator.d.ts.map +1 -1
  46. package/dist/frontends/github-frontend.d.ts +20 -0
  47. package/dist/frontends/github-frontend.d.ts.map +1 -1
  48. package/dist/frontends/host.d.ts +4 -0
  49. package/dist/frontends/host.d.ts.map +1 -1
  50. package/dist/frontends/slack-frontend.d.ts +58 -0
  51. package/dist/frontends/slack-frontend.d.ts.map +1 -0
  52. package/dist/generated/config-schema.d.ts +409 -41
  53. package/dist/generated/config-schema.d.ts.map +1 -1
  54. package/dist/generated/config-schema.json +436 -47
  55. package/dist/github-comments.d.ts +2 -0
  56. package/dist/github-comments.d.ts.map +1 -1
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +83587 -56085
  59. package/dist/liquid-extensions.d.ts.map +1 -1
  60. package/dist/logger.d.ts +1 -0
  61. package/dist/logger.d.ts.map +1 -1
  62. package/dist/output/traces/{run-2025-11-21T11-50-46-505Z.ndjson → run-2026-01-21T05-37-24-446Z.ndjson} +91 -91
  63. package/dist/output/traces/run-2026-01-21T05-38-18-580Z.ndjson +1067 -0
  64. package/dist/output-formatters.d.ts.map +1 -1
  65. package/dist/providers/ai-check-provider.d.ts +12 -0
  66. package/dist/providers/ai-check-provider.d.ts.map +1 -1
  67. package/dist/providers/check-provider-registry.d.ts.map +1 -1
  68. package/dist/providers/check-provider.interface.d.ts +9 -0
  69. package/dist/providers/check-provider.interface.d.ts.map +1 -1
  70. package/dist/providers/command-check-provider.d.ts.map +1 -1
  71. package/dist/providers/custom-tool-executor.d.ts.map +1 -1
  72. package/dist/providers/git-checkout-provider.d.ts +25 -0
  73. package/dist/providers/git-checkout-provider.d.ts.map +1 -0
  74. package/dist/providers/http-client-provider.d.ts +3 -0
  75. package/dist/providers/http-client-provider.d.ts.map +1 -1
  76. package/dist/providers/human-input-check-provider.d.ts +2 -0
  77. package/dist/providers/human-input-check-provider.d.ts.map +1 -1
  78. package/dist/providers/log-check-provider.d.ts.map +1 -1
  79. package/dist/providers/mcp-check-provider.d.ts +1 -1
  80. package/dist/providers/mcp-check-provider.d.ts.map +1 -1
  81. package/dist/providers/mcp-custom-sse-server.d.ts +66 -0
  82. package/dist/providers/mcp-custom-sse-server.d.ts.map +1 -0
  83. package/dist/providers/memory-check-provider.d.ts.map +1 -1
  84. package/dist/providers/script-check-provider.d.ts.map +1 -1
  85. package/dist/providers/workflow-check-provider.d.ts.map +1 -1
  86. package/dist/reviewer.d.ts.map +1 -1
  87. package/dist/sdk/check-provider-registry-534KL5HT.mjs +27 -0
  88. package/dist/sdk/chunk-23L3QRYX.mjs +16872 -0
  89. package/dist/sdk/chunk-23L3QRYX.mjs.map +1 -0
  90. package/dist/sdk/{chunk-OOZITMRU.mjs → chunk-3OMWVM6J.mjs} +11 -1
  91. package/dist/sdk/{chunk-OOZITMRU.mjs.map → chunk-3OMWVM6J.mjs.map} +1 -1
  92. package/dist/sdk/{chunk-37ZSCMFC.mjs → chunk-7UK3NIIT.mjs} +2 -2
  93. package/dist/sdk/{chunk-VMPLF6FT.mjs → chunk-AGIZJ4UZ.mjs} +50 -4
  94. package/dist/sdk/chunk-AGIZJ4UZ.mjs.map +1 -0
  95. package/dist/sdk/{chunk-IEO6CFLG.mjs → chunk-AIVFBIS4.mjs} +161 -5
  96. package/dist/sdk/chunk-AIVFBIS4.mjs.map +1 -0
  97. package/dist/sdk/chunk-AK6BVWIT.mjs +426 -0
  98. package/dist/sdk/chunk-AK6BVWIT.mjs.map +1 -0
  99. package/dist/sdk/chunk-AUT26LHW.mjs +139 -0
  100. package/dist/sdk/chunk-AUT26LHW.mjs.map +1 -0
  101. package/dist/sdk/chunk-BOVFH3LI.mjs +232 -0
  102. package/dist/sdk/chunk-BOVFH3LI.mjs.map +1 -0
  103. package/dist/sdk/chunk-HTOKWMPO.mjs +157 -0
  104. package/dist/sdk/chunk-HTOKWMPO.mjs.map +1 -0
  105. package/dist/sdk/{chunk-6Y4YTKCF.mjs → chunk-NAW3DB3I.mjs} +2 -2
  106. package/dist/sdk/{chunk-OWUVOILT.mjs → chunk-QR7MOMJH.mjs} +4 -3
  107. package/dist/sdk/{chunk-OWUVOILT.mjs.map → chunk-QR7MOMJH.mjs.map} +1 -1
  108. package/dist/sdk/{chunk-PTL3K3PN.mjs → chunk-QY2XYPEV.mjs} +488 -60
  109. package/dist/sdk/chunk-QY2XYPEV.mjs.map +1 -0
  110. package/dist/sdk/{chunk-OZJ263FM.mjs → chunk-SIWNBRTK.mjs} +29 -215
  111. package/dist/sdk/chunk-SIWNBRTK.mjs.map +1 -0
  112. package/dist/sdk/command-executor-TYUV6HUS.mjs +14 -0
  113. package/dist/sdk/{config-M4ZNO6NU.mjs → config-YNC2EOOT.mjs} +5 -3
  114. package/dist/sdk/{failure-condition-evaluator-NBO5YRXW.mjs → failure-condition-evaluator-YGTF2GHG.mjs} +6 -5
  115. package/dist/sdk/{github-frontend-4AWRJT7D.mjs → github-frontend-SIAEOCON.mjs} +190 -12
  116. package/dist/sdk/github-frontend-SIAEOCON.mjs.map +1 -0
  117. package/dist/sdk/{host-7GBC3S7L.mjs → host-DXUYTNMU.mjs} +5 -2
  118. package/dist/sdk/host-DXUYTNMU.mjs.map +1 -0
  119. package/dist/sdk/{liquid-extensions-C7EG3YKH.mjs → liquid-extensions-PKWCKK7E.mjs} +5 -4
  120. package/dist/sdk/memory-store-XGBB7LX7.mjs +12 -0
  121. package/dist/sdk/prompt-state-YRJY6QAL.mjs +16 -0
  122. package/dist/sdk/{renderer-schema-6RF26VUS.mjs → renderer-schema-LPKN5UJS.mjs} +3 -2
  123. package/dist/sdk/{renderer-schema-6RF26VUS.mjs.map → renderer-schema-LPKN5UJS.mjs.map} +1 -1
  124. package/dist/sdk/{routing-RP56JTV2.mjs → routing-6N45MJ4F.mjs} +7 -6
  125. package/dist/sdk/sdk.d.mts +219 -5
  126. package/dist/sdk/sdk.d.ts +219 -5
  127. package/dist/sdk/sdk.js +21329 -14908
  128. package/dist/sdk/sdk.js.map +1 -1
  129. package/dist/sdk/sdk.mjs +407 -12874
  130. package/dist/sdk/sdk.mjs.map +1 -1
  131. package/dist/sdk/{session-registry-N5FFYFTM.mjs → session-registry-4E6YRQ77.mjs} +2 -2
  132. package/dist/sdk/session-registry-4E6YRQ77.mjs.map +1 -0
  133. package/dist/sdk/slack-frontend-BVKW3GD5.mjs +735 -0
  134. package/dist/sdk/slack-frontend-BVKW3GD5.mjs.map +1 -0
  135. package/dist/sdk/{tracer-init-WP4X46IF.mjs → tracer-init-GSLPPLCD.mjs} +2 -2
  136. package/dist/sdk/tracer-init-GSLPPLCD.mjs.map +1 -0
  137. package/dist/sdk/workflow-registry-R6KSACFR.mjs +12 -0
  138. package/dist/sdk/workflow-registry-R6KSACFR.mjs.map +1 -0
  139. package/dist/slack/adapter.d.ts +36 -0
  140. package/dist/slack/adapter.d.ts.map +1 -0
  141. package/dist/slack/cache-prewarmer.d.ts +31 -0
  142. package/dist/slack/cache-prewarmer.d.ts.map +1 -0
  143. package/dist/slack/client.d.ts +77 -0
  144. package/dist/slack/client.d.ts.map +1 -0
  145. package/dist/slack/markdown.d.ts +45 -0
  146. package/dist/slack/markdown.d.ts.map +1 -0
  147. package/dist/slack/prompt-state.d.ts +33 -0
  148. package/dist/slack/prompt-state.d.ts.map +1 -0
  149. package/dist/slack/rate-limiter.d.ts +56 -0
  150. package/dist/slack/rate-limiter.d.ts.map +1 -0
  151. package/dist/slack/signature.d.ts +2 -0
  152. package/dist/slack/signature.d.ts.map +1 -0
  153. package/dist/slack/socket-runner.d.ts +42 -0
  154. package/dist/slack/socket-runner.d.ts.map +1 -0
  155. package/dist/slack/thread-cache.d.ts +51 -0
  156. package/dist/slack/thread-cache.d.ts.map +1 -0
  157. package/dist/state-machine/context/build-engine-context.d.ts +8 -0
  158. package/dist/state-machine/context/build-engine-context.d.ts.map +1 -1
  159. package/dist/state-machine/dispatch/execution-invoker.d.ts.map +1 -1
  160. package/dist/state-machine/dispatch/foreach-processor.d.ts.map +1 -1
  161. package/dist/state-machine/dispatch/on-init-handlers.d.ts +43 -0
  162. package/dist/state-machine/dispatch/on-init-handlers.d.ts.map +1 -0
  163. package/dist/state-machine/dispatch/stats-manager.d.ts.map +1 -1
  164. package/dist/state-machine/dispatch/template-renderer.d.ts.map +1 -1
  165. package/dist/state-machine/runner.d.ts +6 -0
  166. package/dist/state-machine/runner.d.ts.map +1 -1
  167. package/dist/state-machine/states/level-dispatch.d.ts.map +1 -1
  168. package/dist/state-machine/states/plan-ready.d.ts.map +1 -1
  169. package/dist/state-machine/states/routing.d.ts.map +1 -1
  170. package/dist/state-machine/states/wave-planning.d.ts.map +1 -1
  171. package/dist/state-machine/workflow-projection.d.ts.map +1 -1
  172. package/dist/state-machine-execution-engine.d.ts +21 -9
  173. package/dist/state-machine-execution-engine.d.ts.map +1 -1
  174. package/dist/telemetry/state-capture.d.ts +5 -0
  175. package/dist/telemetry/state-capture.d.ts.map +1 -1
  176. package/dist/test-runner/core/flow-stage.d.ts.map +1 -1
  177. package/dist/test-runner/core/test-execution-wrapper.d.ts.map +1 -1
  178. package/dist/test-runner/evaluators.d.ts +37 -4
  179. package/dist/test-runner/evaluators.d.ts.map +1 -1
  180. package/dist/test-runner/index.d.ts +7 -0
  181. package/dist/test-runner/index.d.ts.map +1 -1
  182. package/dist/test-runner/recorders/slack-recorder.d.ts +17 -0
  183. package/dist/test-runner/recorders/slack-recorder.d.ts.map +1 -0
  184. package/dist/test-runner/validator.d.ts.map +1 -1
  185. package/dist/traces/{run-2025-11-21T11-50-46-505Z.ndjson → run-2026-01-21T05-37-24-446Z.ndjson} +91 -91
  186. package/dist/traces/run-2026-01-21T05-38-18-580Z.ndjson +1067 -0
  187. package/dist/types/bot.d.ts +109 -0
  188. package/dist/types/bot.d.ts.map +1 -0
  189. package/dist/types/cli.d.ts +4 -0
  190. package/dist/types/cli.d.ts.map +1 -1
  191. package/dist/types/config.d.ts +182 -5
  192. package/dist/types/config.d.ts.map +1 -1
  193. package/dist/types/engine.d.ts +5 -0
  194. package/dist/types/engine.d.ts.map +1 -1
  195. package/dist/types/git-checkout.d.ts +76 -0
  196. package/dist/types/git-checkout.d.ts.map +1 -0
  197. package/dist/utils/json-text-extractor.d.ts +17 -0
  198. package/dist/utils/json-text-extractor.d.ts.map +1 -0
  199. package/dist/utils/sandbox.d.ts +10 -0
  200. package/dist/utils/sandbox.d.ts.map +1 -1
  201. package/dist/utils/template-context.d.ts +1 -0
  202. package/dist/utils/template-context.d.ts.map +1 -1
  203. package/dist/utils/tracer-init.d.ts.map +1 -1
  204. package/dist/utils/workspace-manager.d.ts +118 -0
  205. package/dist/utils/workspace-manager.d.ts.map +1 -0
  206. package/dist/utils/worktree-cleanup.d.ts +33 -0
  207. package/dist/utils/worktree-cleanup.d.ts.map +1 -0
  208. package/dist/utils/worktree-manager.d.ts +153 -0
  209. package/dist/utils/worktree-manager.d.ts.map +1 -0
  210. package/dist/webhook-server.d.ts.map +1 -1
  211. package/dist/workflow-executor.d.ts.map +1 -1
  212. package/dist/workflow-registry.d.ts.map +1 -1
  213. package/package.json +5 -4
  214. package/dist/output/traces/run-2025-11-21T11-51-33-674Z.ndjson +0 -839
  215. package/dist/sdk/chunk-IEO6CFLG.mjs.map +0 -1
  216. package/dist/sdk/chunk-JEHPDJIF.mjs +0 -223
  217. package/dist/sdk/chunk-JEHPDJIF.mjs.map +0 -1
  218. package/dist/sdk/chunk-OZJ263FM.mjs.map +0 -1
  219. package/dist/sdk/chunk-PTL3K3PN.mjs.map +0 -1
  220. package/dist/sdk/chunk-VMPLF6FT.mjs.map +0 -1
  221. package/dist/sdk/github-frontend-4AWRJT7D.mjs.map +0 -1
  222. package/dist/sdk/host-7GBC3S7L.mjs.map +0 -1
  223. package/dist/sdk/memory-store-GJACZC2A.mjs +0 -11
  224. package/dist/sdk/workflow-registry-2YIIXQCK.mjs +0 -11
  225. package/dist/traces/run-2025-11-21T11-51-33-674Z.ndjson +0 -839
  226. /package/dist/sdk/{config-M4ZNO6NU.mjs.map → check-provider-registry-534KL5HT.mjs.map} +0 -0
  227. /package/dist/sdk/{chunk-37ZSCMFC.mjs.map → chunk-7UK3NIIT.mjs.map} +0 -0
  228. /package/dist/sdk/{chunk-6Y4YTKCF.mjs.map → chunk-NAW3DB3I.mjs.map} +0 -0
  229. /package/dist/sdk/{failure-condition-evaluator-NBO5YRXW.mjs.map → command-executor-TYUV6HUS.mjs.map} +0 -0
  230. /package/dist/sdk/{liquid-extensions-C7EG3YKH.mjs.map → config-YNC2EOOT.mjs.map} +0 -0
  231. /package/dist/sdk/{memory-store-GJACZC2A.mjs.map → failure-condition-evaluator-YGTF2GHG.mjs.map} +0 -0
  232. /package/dist/sdk/{routing-RP56JTV2.mjs.map → liquid-extensions-PKWCKK7E.mjs.map} +0 -0
  233. /package/dist/sdk/{session-registry-N5FFYFTM.mjs.map → memory-store-XGBB7LX7.mjs.map} +0 -0
  234. /package/dist/sdk/{tracer-init-WP4X46IF.mjs.map → prompt-state-YRJY6QAL.mjs.map} +0 -0
  235. /package/dist/sdk/{workflow-registry-2YIIXQCK.mjs.map → routing-6N45MJ4F.mjs.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  ## 📚 Examples & Recipes
2
2
 
3
- - Minimal `.visor.yaml` starter
3
+ ### Minimal `.visor.yaml` starter
4
4
  ```yaml
5
5
  version: "1.0"
6
6
  steps:
@@ -15,7 +15,460 @@ steps:
15
15
  npx husky add .husky/pre-commit "npx -y @probelabs/visor@latest --tags local,fast --output table || exit 1"
16
16
  ```
17
17
 
18
- - More examples
19
- - docs/NPM_USAGE.md – CLI usage and flags
20
- - GITHUB_CHECKS.md Checks, outputs, and workflow integration
21
- - examples/ – MCP, Jira, and advanced configs
18
+ ### Chat-like workflows (human-input + ai)
19
+
20
+ Minimal chat loop (CLI/SDK):
21
+
22
+ ```yaml
23
+ version: "1.0"
24
+
25
+ checks:
26
+ ask:
27
+ type: human-input
28
+ group: chat
29
+ prompt: |
30
+ Please type your message.
31
+
32
+ reply:
33
+ type: ai
34
+ group: chat
35
+ depends_on: ask
36
+ ai:
37
+ disableTools: true
38
+ allowedTools: []
39
+ system_prompt: "You are general assistant, follow user instructions."
40
+ prompt: |
41
+ You are a concise, friendly assistant.
42
+
43
+ Conversation so far (oldest → newest):
44
+ {% assign history = '' | chat_history: 'ask', 'reply' %}
45
+ {% for m in history %}
46
+ {{ m.role | capitalize }}: {{ m.text }}
47
+ {% endfor %}
48
+
49
+ Latest user message:
50
+ {{ outputs['ask'].text }}
51
+
52
+ Reply naturally. Keep it short (1–2 sentences).
53
+
54
+ guarantee: "(output?.text ?? '').length > 0"
55
+ on_success:
56
+ goto: ask
57
+ ```
58
+
59
+ Notes:
60
+ - `ask` (human-input) produces `{ text, ts }` by default.
61
+ - `reply` (ai) responds and loops back to `ask`.
62
+ - `chat_history('ask','reply')` merges both histories by timestamp with roles:
63
+ - `type: human-input` → `role: "user"`
64
+ - `type: ai` → `role: "assistant"`
65
+
66
+ Slack chat using the same pattern:
67
+
68
+ ```yaml
69
+ version: "1.0"
70
+
71
+ slack:
72
+ version: "v1"
73
+ mentions: all
74
+ threads: required
75
+
76
+ frontends:
77
+ - name: slack
78
+ config:
79
+ summary:
80
+ enabled: false
81
+
82
+ checks:
83
+ ask:
84
+ type: human-input
85
+ group: chat
86
+ prompt: |
87
+ Please type your message. (Posted only when the workflow is waiting.)
88
+
89
+ reply:
90
+ type: ai
91
+ group: chat
92
+ depends_on: ask
93
+ ai:
94
+ disableTools: true
95
+ allowedTools: []
96
+ # For chat-style Slack flows you can optionally turn off
97
+ # automatic PR/issue + Slack XML context and rely solely on
98
+ # chat_history + conversation objects:
99
+ # skip_transport_context: true
100
+ prompt: |
101
+ You are a concise, friendly assistant.
102
+
103
+ Conversation so far (oldest → newest):
104
+ {% assign history = '' | chat_history: 'ask', 'reply' %}
105
+ {% for m in history %}
106
+ {{ m.role | capitalize }}: {{ m.text }}
107
+ {% endfor %}
108
+
109
+ Latest user message:
110
+ {{ outputs['ask'].text }}
111
+
112
+ Reply naturally. Keep it short (1–2 sentences).
113
+
114
+ guarantee: "(output?.text ?? '').length > 0"
115
+ on_success:
116
+ goto: ask
117
+ ```
118
+
119
+ Runtime behavior:
120
+ - First Slack message in a thread:
121
+ - Treated as `ask` input.
122
+ - `reply` posts into the same thread.
123
+ - Engine loops to `ask`, posts a prompt, and saves a snapshot.
124
+ - Next Slack message in the same thread:
125
+ - Resumes from snapshot.
126
+ - `ask` consumes the new message.
127
+ - `reply` posts a new answer and loops again.
128
+
129
+ Accessing normalized conversation context in prompts:
130
+
131
+ ```liquid
132
+ {% if conversation %}
133
+ Transport: {{ conversation.transport }} {# 'slack', 'github', ... #}
134
+ Thread: {{ conversation.thread.id }}
135
+ {% for m in conversation.messages %}
136
+ {{ m.user }} ({{ m.role }}): {{ m.text }}
137
+ {% endfor %}
138
+ {% endif %}
139
+ ```
140
+
141
+ - Under Slack, `conversation` and `slack.conversation` are the same
142
+ normalized object.
143
+ - Under GitHub (PR/issue), `conversation` is built from the body +
144
+ comment history using the same `{ role, user, text, timestamp }`
145
+ structure.
146
+
147
+ Customizing chat_history (roles, text, limits):
148
+
149
+ ```liquid
150
+ {% assign history = '' |
151
+ chat_history:
152
+ 'ask',
153
+ 'clarify',
154
+ 'reply',
155
+ direction: 'asc',
156
+ limit: 50,
157
+ text: {
158
+ default_field: 'text',
159
+ by_step: {
160
+ 'summarize': 'summary.text'
161
+ }
162
+ },
163
+ roles: {
164
+ by_step: {
165
+ 'summarize': 'system'
166
+ }
167
+ },
168
+ role_map: 'ask=user,reply=assistant'
169
+ %}
170
+ {% for m in history %}
171
+ [{{ m.step }}][{{ m.role }}] {{ m.text }}
172
+ {% endfor %}
173
+ ```
174
+
175
+ Quick reference:
176
+ - `direction: 'asc' | 'desc'`, `limit: N`
177
+ - `text.default_field`, `text.by_step[step]`
178
+ - `roles.by_step[step]`, `roles.by_type[type]`, `roles.default`
179
+ - `role_map: 'step=role,other=role'` as a compact override
180
+
181
+ See also:
182
+ - `docs/human-input-provider.md`
183
+ - `docs/liquid-templates.md` (Chat History Helper)
184
+ - `docs/output-history.md`
185
+ - `examples/slack-simple-chat.yaml`
186
+
187
+ ### Advanced routing & contracts (general patterns)
188
+
189
+ #### Inner loops vs. closing the loop
190
+
191
+ - **Close the loop**: Leaf steps use `on_success: goto: <entry-step>` to end the workflow and return to a single top-level `human-input`. Each new event (Slack message, webhook, CLI run) starts a fresh execution.
192
+ - **Close the loop**: Leaf steps use `on_success: goto: <entry-step>` to end the workflow and return to a single top-level `human-input`. Each new event (Slack message, webhook, CLI run) starts a fresh execution.
193
+ - **Inner loop**: Add a local `human-input` and route inside a sub‑flow:
194
+ - Example shape: `router → section-confirm → section-answer → section-confirm`.
195
+ - Use a control field (e.g. `output.done === true`) in `transitions` to exit the section back to the top-level entry step.
196
+ - This pattern is transport-agnostic and works for Slack, GitHub, HTTP workflows, etc.
197
+ - See: `examples/slack-simple-chat.yaml` for a concrete implementation of both patterns.
198
+
199
+ #### Declarative routing (`transitions`) instead of JS
200
+
201
+ - Prefer `on_success.transitions` / `on_finish.transitions` for branching:
202
+ ```yaml
203
+ on_success:
204
+ transitions:
205
+ - when: "output && output.intent === 'chat'"
206
+ to: chat-answer
207
+ - when: "output && output.intent === 'project_help'"
208
+ to: project-intent
209
+ ```
210
+ - Reserve `goto_js` / `run_js` for legacy or very dynamic use cases.
211
+ - More details: `docs/guides/fault-management-and-contracts.md`, `docs/loop-routing-refactor.md`.
212
+
213
+ - Pattern A — central router + transitions (explicit routing):
214
+ - Use a single “router” step that sets control fields (e.g. `output.intent`, `output.kind`).
215
+ - Declare all branching in one place via `on_success.transitions` on the router:
216
+ ```yaml
217
+ router:
218
+ type: ai
219
+ on_success:
220
+ transitions:
221
+ - when: "output.intent === 'chat'"
222
+ to: chat-answer
223
+ - when: "output.intent === 'status'"
224
+ to: status-answer
225
+
226
+ chat-answer:
227
+ depends_on: [router]
228
+ if: "outputs['router']?.intent === 'chat'"
229
+
230
+ status-answer:
231
+ depends_on: [router]
232
+ if: "outputs['router']?.intent === 'status'"
233
+ ```
234
+ - Good when you want a single, centralized view of routing logic. Use `if` on branches for readability and to skip branches cleanly; reserve `assume` for hard dependency checks only.
235
+
236
+ - Pattern B — distributed routing via `depends_on` + `if`:
237
+ - Omit transitions entirely and let each branch decide whether it should run:
238
+ ```yaml
239
+ router:
240
+ type: ai
241
+ # no on_success.transitions
242
+
243
+ chat-answer:
244
+ depends_on: [router]
245
+ if: "outputs['router']?.intent === 'chat'"
246
+
247
+ status-answer:
248
+ depends_on: [router]
249
+ if: "outputs['router']?.intent === 'status'"
250
+ ```
251
+ - The DAG (`depends_on`) defines possible flows; `if` conditions select the active branch(es) per run.
252
+ - This works well when routing is simple or when you prefer fully local branch declarations over a central router table.
253
+
254
+ #### Criticality + `assume` + `guarantee` (recommended layout)
255
+
256
+ - Apply to **any** workflow, not just chat:
257
+ - `external` – step changes **external state**:
258
+ - Examples: GitHub comments/labels, HTTP POST/PUT/PATCH/DELETE, ticket creation, updating CI/CD or incident systems, filesystem writes in a shared location.
259
+ - If someone can look elsewhere and see a change after this step, it’s usually `external`.
260
+ - `internal` – step changes **the workflow’s control-plane**:
261
+ - Examples: forEach parents that fan out work; steps with `on_* transitions/goto` that decide what runs next; script/memory steps that set flags used by `if`/`assume`/`guarantee`.
262
+ - If it mostly “steers” the run (not user-facing output), treat it as `internal`.
263
+ - `policy` – step enforces **org or safety rules**:
264
+ - Examples: permission checks (who may deploy/label), change windows, compliance checks (branches, commit format, DCO/CLA).
265
+ - Often used to gate `external` steps (e.g. only label when policy passes).
266
+ - `info` – read-only / non-critical:
267
+ - Examples: summaries, hints, dashboards, advisory AI steps that do not gate other critical steps and do not mutate anything directly.
268
+ - For `internal` / `external` steps, group fields in this order:
269
+ ```yaml
270
+ some-step:
271
+ type: ai | script | command | ...
272
+ group: ...
273
+ depends_on: [...]
274
+ criticality: internal # or external / policy / info
275
+ assume:
276
+ - "upstream condition" # never reference this step's own output here
277
+ guarantee: "output?.field != null" # assertions about this step's output
278
+ schema: # JSON Schema when output is structured
279
+ ...
280
+ ```
281
+ - Use `assume` for preconditions about upstream state (memory, env, `outputs[...]`).
282
+ - Use `guarantee` for postconditions about this step’s own output (shape, control flags, size caps).
283
+ - For `info` steps, contracts are recommended but optional; keep `assume` + `guarantee` adjacent when present.
284
+ - More details: `docs/guides/criticality-modes.md`, `docs/guides/fault-management-and-contracts.md`.
285
+
286
+ #### JSON Schemas instead of `schema: plain`
287
+
288
+ - For structured outputs (routers, script integrations, control signals), prefer real JSON Schema:
289
+ ```yaml
290
+ router-step:
291
+ schema:
292
+ type: object
293
+ properties:
294
+ intent:
295
+ type: string
296
+ enum: [chat, summarize, escalate]
297
+ target:
298
+ type: string
299
+ required: [intent]
300
+ ```
301
+ - For text responses, it can still be useful to wrap in an object:
302
+ ```yaml
303
+ answer:
304
+ schema:
305
+ type: object
306
+ properties:
307
+ text: { type: string }
308
+ required: [text]
309
+ guarantee: "(output?.text ?? '').length > 0"
310
+ ```
311
+ - Use `schema: plain` only when output shape is genuinely unconstrained.
312
+
313
+ Tip: When you define a JSON Schema, you generally do **not** need to tell the model “respond only as JSON”; describe the semantics in the prompt and let the renderer/schema enforce shape.
314
+
315
+ #### Expression style (`assume`, `guarantee`, `when`)
316
+
317
+ - Prefer clear, concise expressions:
318
+ - `outputs['router']?.intent === 'chat'`
319
+ - `!!outputs['status-fetch']?.project`
320
+ - `output?.done === true`
321
+ - Avoid noisy fallbacks like `(outputs['x']?.kind ?? '') === 'status'` when `outputs['x']?.kind === 'status'` is equivalent.
322
+ - These conventions apply uniformly to any provider (`ai`, `command`, `script`, `github`, `http_client`, etc).
323
+
324
+ ### Command step best practices
325
+
326
+ When using `type: command` steps:
327
+
328
+ **Avoid external tool dependencies** like `jq`, `yq`, `python`, etc.:
329
+ - They may not be installed in all environments (GitHub Actions, Docker, CI)
330
+ - Use `transform_js` to parse and transform output instead
331
+ - Keep shell commands simple: `grep`, `sed`, `awk`, `sort`, `head` are universally available
332
+
333
+ ```yaml
334
+ # Bad - requires jq
335
+ extract-data:
336
+ type: command
337
+ exec: |
338
+ echo "$TEXT" | grep -oE '[A-Z]+-[0-9]+' | jq -R -s 'split("\n")'
339
+ parseJson: true
340
+
341
+ # Good - use transform_js for parsing
342
+ extract-data:
343
+ type: command
344
+ exec: |
345
+ echo "$TEXT" | grep -oE '[A-Z]+-[0-9]+' | sort -u
346
+ transform_js: |
347
+ const lines = (output || '').trim().split('\n').filter(Boolean);
348
+ return { data: lines, count: lines.length };
349
+ ```
350
+
351
+ **Prefer line-separated output** over JSON from shell:
352
+ - Simple to parse with `transform_js`
353
+ - No need for `parseJson: true`
354
+ - More robust across different shells/environments
355
+
356
+ **Use transform_js for structured output**:
357
+ - The sandbox provides `output` (command stdout as string)
358
+ - Return an object with the fields you need
359
+ - Works consistently across all environments
360
+
361
+ ### Testing workflows with `--no-mocks`
362
+
363
+ The `--no-mocks` flag runs your test cases with real providers instead of injecting mock responses. This is essential for:
364
+
365
+ 1. **Debugging integration issues** - See actual API responses and errors
366
+ 2. **Capturing realistic mock data** - Get real output to copy into your test cases
367
+ 3. **Validating credentials** - Verify environment variables are set correctly
368
+ 4. **Developing new workflows** - Build tests incrementally with real data
369
+
370
+ #### Basic usage
371
+
372
+ ```bash
373
+ # Run all test cases with real providers
374
+ visor test --config my-workflow.yaml --no-mocks
375
+
376
+ # Run a specific test case with real providers
377
+ visor test --config my-workflow.yaml --no-mocks --only "my-test-case"
378
+ ```
379
+
380
+ #### Suggested mocks output
381
+
382
+ When running with `--no-mocks`, Visor captures each step's output and prints it as YAML you can copy directly into your test case:
383
+
384
+ ```
385
+ 🔴 NO-MOCKS MODE: Running with real providers (no mock injection)
386
+ Step outputs will be captured and printed as suggested mocks
387
+
388
+ ... test execution ...
389
+
390
+ 📋 Suggested mocks (copy to your test case):
391
+ mocks:
392
+ extract-keys:
393
+ data:
394
+ - PROJ-123
395
+ - DEV-456
396
+ count: 2
397
+ fetch-issues:
398
+ data:
399
+ - key: PROJ-123
400
+ summary: Fix authentication bug
401
+ status: In Progress
402
+ ```
403
+
404
+ Copy the YAML under `mocks:` into your test case's `mocks:` section.
405
+
406
+ #### Workflow for building tests
407
+
408
+ 1. **Start with a minimal test case** (no mocks):
409
+ ```yaml
410
+ tests:
411
+ cases:
412
+ - name: my-new-test
413
+ event: manual
414
+ fixture: local.minimal
415
+ workflow_input:
416
+ text: "Fix bug PROJ-123"
417
+ ```
418
+
419
+ 2. **Run with `--no-mocks`** to capture real outputs:
420
+ ```bash
421
+ visor test --config workflow.yaml --no-mocks --only "my-new-test"
422
+ ```
423
+
424
+ 3. **Copy the suggested mocks** into your test case:
425
+ ```yaml
426
+ tests:
427
+ cases:
428
+ - name: my-new-test
429
+ event: manual
430
+ fixture: local.minimal
431
+ workflow_input:
432
+ text: "Fix bug PROJ-123"
433
+ mocks:
434
+ extract-keys:
435
+ data: ["PROJ-123"]
436
+ count: 1
437
+ # ... rest of captured mocks
438
+ ```
439
+
440
+ 4. **Add assertions** based on the real data:
441
+ ```yaml
442
+ expect:
443
+ workflow_output:
444
+ - path: issue_count
445
+ equals: 1
446
+ ```
447
+
448
+ 5. **Run normally** to verify mocks work:
449
+ ```bash
450
+ visor test --config workflow.yaml --only "my-new-test"
451
+ ```
452
+
453
+ #### Debugging with `--no-mocks`
454
+
455
+ When a test fails with mocks, use `--no-mocks` to see what's actually happening:
456
+
457
+ ```bash
458
+ # See real API responses and errors
459
+ visor test --config workflow.yaml --no-mocks --only "failing-test"
460
+
461
+ # Common issues revealed:
462
+ # - Missing or expired credentials
463
+ # - API endpoint changes
464
+ # - Unexpected response formats
465
+ # - Network/timeout issues
466
+ ```
467
+
468
+ The real error messages and responses help identify whether the issue is with your mocks or the actual integration.
469
+
470
+ ### More examples
471
+
472
+ - `docs/NPM_USAGE.md` – CLI usage and flags
473
+ - `GITHUB_CHECKS.md` – Checks, outputs, and workflow integration
474
+ - `examples/` – MCP, Jira, and advanced configs