@probelabs/visor 0.1.148 → 0.1.149

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 (70) hide show
  1. package/defaults/assistant.yaml +2141 -0
  2. package/defaults/code-talk.yaml +1250 -0
  3. package/defaults/intent-router.yaml +478 -0
  4. package/dist/defaults/assistant.yaml +2141 -0
  5. package/dist/defaults/code-talk.yaml +1250 -0
  6. package/dist/defaults/intent-router.yaml +478 -0
  7. package/dist/index.js +17 -4
  8. package/dist/output/traces/{run-2026-03-02T18-32-11-359Z.ndjson → run-2026-03-03T07-19-07-543Z.ndjson} +84 -84
  9. package/dist/{traces/run-2026-03-02T18-32-55-702Z.ndjson → output/traces/run-2026-03-03T07-19-50-933Z.ndjson} +1866 -1187
  10. package/dist/sdk/{check-provider-registry-35BPTY4W.mjs → check-provider-registry-IYSUDKPB.mjs} +7 -7
  11. package/dist/sdk/{check-provider-registry-DVQDGTOE.mjs → check-provider-registry-LVLC4EPF.mjs} +4 -4
  12. package/dist/sdk/{check-provider-registry-KHPY6LB4.mjs → check-provider-registry-X4OZJWPK.mjs} +4 -4
  13. package/dist/sdk/{chunk-6N6JRWCW.mjs → chunk-6EXCUX7Y.mjs} +10 -10
  14. package/dist/sdk/{chunk-S2YO4ZE3.mjs → chunk-BR7DYA3S.mjs} +2 -2
  15. package/dist/sdk/{chunk-DIND4ZCV.mjs → chunk-DNDS7R3N.mjs} +11 -1
  16. package/dist/sdk/{chunk-DIND4ZCV.mjs.map → chunk-DNDS7R3N.mjs.map} +1 -1
  17. package/dist/sdk/{chunk-IF2UD2KS.mjs → chunk-GFNXX64M.mjs} +18 -18
  18. package/dist/sdk/{chunk-AYQE4JCU.mjs → chunk-Q6EPAJ6Z.mjs} +3 -3
  19. package/dist/sdk/{chunk-H4AYMOAT.mjs → chunk-V6GI4U2M.mjs} +10 -10
  20. package/dist/sdk/{chunk-EGUHXVWS.mjs → chunk-VLUGLWLA.mjs} +2 -2
  21. package/dist/sdk/{chunk-EGUHXVWS.mjs.map → chunk-VLUGLWLA.mjs.map} +1 -1
  22. package/dist/sdk/{chunk-XNTBSV6M.mjs → chunk-YYZAN5NK.mjs} +3 -3
  23. package/dist/sdk/{config-G5UU4WXT.mjs → config-KQH254CA.mjs} +2 -2
  24. package/dist/sdk/{failure-condition-evaluator-I6QWFKV3.mjs → failure-condition-evaluator-LZ2AG5PY.mjs} +3 -3
  25. package/dist/sdk/{github-frontend-2MC77L7F.mjs → github-frontend-S523EEJB.mjs} +3 -3
  26. package/dist/sdk/{host-4F6I3ZXN.mjs → host-7YKRMOUJ.mjs} +2 -2
  27. package/dist/sdk/{routing-UT3BXBXH.mjs → routing-ZMBKWMVI.mjs} +4 -4
  28. package/dist/sdk/{schedule-tool-K3GQXCBN.mjs → schedule-tool-CDVUSZEG.mjs} +7 -7
  29. package/dist/sdk/{schedule-tool-SBXAEBDD.mjs → schedule-tool-EOMZFICZ.mjs} +4 -4
  30. package/dist/sdk/{schedule-tool-CONR4VW3.mjs → schedule-tool-NX75VKGA.mjs} +4 -4
  31. package/dist/sdk/{schedule-tool-handler-GFQCJAVZ.mjs → schedule-tool-handler-3FJHDIPG.mjs} +7 -7
  32. package/dist/sdk/{schedule-tool-handler-R7PG3VMR.mjs → schedule-tool-handler-KKN7XJYT.mjs} +4 -4
  33. package/dist/sdk/{schedule-tool-handler-YUC6CAXX.mjs → schedule-tool-handler-QNZG55DX.mjs} +4 -4
  34. package/dist/sdk/sdk.js +11 -1
  35. package/dist/sdk/sdk.js.map +1 -1
  36. package/dist/sdk/sdk.mjs +6 -6
  37. package/dist/sdk/{trace-helpers-J463EU4B.mjs → trace-helpers-EJUIOP6L.mjs} +2 -2
  38. package/dist/sdk/{workflow-check-provider-GJNGTS3F.mjs → workflow-check-provider-6ERNNCNA.mjs} +7 -7
  39. package/dist/sdk/{workflow-check-provider-DYSO3PML.mjs → workflow-check-provider-AGZ5JY2I.mjs} +4 -4
  40. package/dist/sdk/{workflow-check-provider-FIFFQDQU.mjs → workflow-check-provider-PTNUWM5W.mjs} +4 -4
  41. package/dist/sdk/{workflow-registry-AAD37XKZ.mjs → workflow-registry-MHUSKSD6.mjs} +2 -2
  42. package/dist/traces/{run-2026-03-02T18-32-11-359Z.ndjson → run-2026-03-03T07-19-07-543Z.ndjson} +84 -84
  43. package/dist/{output/traces/run-2026-03-02T18-32-55-702Z.ndjson → traces/run-2026-03-03T07-19-50-933Z.ndjson} +1866 -1187
  44. package/dist/workflow-registry.d.ts.map +1 -1
  45. package/package.json +1 -1
  46. /package/dist/sdk/{check-provider-registry-35BPTY4W.mjs.map → check-provider-registry-IYSUDKPB.mjs.map} +0 -0
  47. /package/dist/sdk/{check-provider-registry-DVQDGTOE.mjs.map → check-provider-registry-LVLC4EPF.mjs.map} +0 -0
  48. /package/dist/sdk/{check-provider-registry-KHPY6LB4.mjs.map → check-provider-registry-X4OZJWPK.mjs.map} +0 -0
  49. /package/dist/sdk/{chunk-6N6JRWCW.mjs.map → chunk-6EXCUX7Y.mjs.map} +0 -0
  50. /package/dist/sdk/{chunk-S2YO4ZE3.mjs.map → chunk-BR7DYA3S.mjs.map} +0 -0
  51. /package/dist/sdk/{chunk-IF2UD2KS.mjs.map → chunk-GFNXX64M.mjs.map} +0 -0
  52. /package/dist/sdk/{chunk-AYQE4JCU.mjs.map → chunk-Q6EPAJ6Z.mjs.map} +0 -0
  53. /package/dist/sdk/{chunk-H4AYMOAT.mjs.map → chunk-V6GI4U2M.mjs.map} +0 -0
  54. /package/dist/sdk/{chunk-XNTBSV6M.mjs.map → chunk-YYZAN5NK.mjs.map} +0 -0
  55. /package/dist/sdk/{config-G5UU4WXT.mjs.map → config-KQH254CA.mjs.map} +0 -0
  56. /package/dist/sdk/{failure-condition-evaluator-I6QWFKV3.mjs.map → failure-condition-evaluator-LZ2AG5PY.mjs.map} +0 -0
  57. /package/dist/sdk/{github-frontend-2MC77L7F.mjs.map → github-frontend-S523EEJB.mjs.map} +0 -0
  58. /package/dist/sdk/{host-4F6I3ZXN.mjs.map → host-7YKRMOUJ.mjs.map} +0 -0
  59. /package/dist/sdk/{routing-UT3BXBXH.mjs.map → routing-ZMBKWMVI.mjs.map} +0 -0
  60. /package/dist/sdk/{schedule-tool-CONR4VW3.mjs.map → schedule-tool-CDVUSZEG.mjs.map} +0 -0
  61. /package/dist/sdk/{schedule-tool-K3GQXCBN.mjs.map → schedule-tool-EOMZFICZ.mjs.map} +0 -0
  62. /package/dist/sdk/{schedule-tool-SBXAEBDD.mjs.map → schedule-tool-NX75VKGA.mjs.map} +0 -0
  63. /package/dist/sdk/{schedule-tool-handler-GFQCJAVZ.mjs.map → schedule-tool-handler-3FJHDIPG.mjs.map} +0 -0
  64. /package/dist/sdk/{schedule-tool-handler-R7PG3VMR.mjs.map → schedule-tool-handler-KKN7XJYT.mjs.map} +0 -0
  65. /package/dist/sdk/{schedule-tool-handler-YUC6CAXX.mjs.map → schedule-tool-handler-QNZG55DX.mjs.map} +0 -0
  66. /package/dist/sdk/{trace-helpers-J463EU4B.mjs.map → trace-helpers-EJUIOP6L.mjs.map} +0 -0
  67. /package/dist/sdk/{workflow-check-provider-DYSO3PML.mjs.map → workflow-check-provider-6ERNNCNA.mjs.map} +0 -0
  68. /package/dist/sdk/{workflow-check-provider-FIFFQDQU.mjs.map → workflow-check-provider-AGZ5JY2I.mjs.map} +0 -0
  69. /package/dist/sdk/{workflow-check-provider-GJNGTS3F.mjs.map → workflow-check-provider-PTNUWM5W.mjs.map} +0 -0
  70. /package/dist/sdk/{workflow-registry-AAD37XKZ.mjs.map → workflow-registry-MHUSKSD6.mjs.map} +0 -0
@@ -0,0 +1,1250 @@
1
+ # =============================================================================
2
+ # code-talk: Reusable workflow for AI-powered code exploration
3
+ # =============================================================================
4
+ #
5
+ # A battle-tested workflow for answering code questions across multiple
6
+ # repositories with architecture-aware routing.
7
+ #
8
+ # Usage:
9
+ # imports:
10
+ # - ./code-talk.yaml
11
+ #
12
+ # checks:
13
+ # code-help:
14
+ # type: workflow
15
+ # workflow: code-talk
16
+ # args:
17
+ # question: "How does authentication work?"
18
+ # architecture: ./ARCHITECTURE.md
19
+ # docs_repo: my-org/docs
20
+ # projects:
21
+ # - id: backend
22
+ # repo: my-org/backend
23
+ # description: Backend API services
24
+ #
25
+ # =============================================================================
26
+
27
+ id: code-talk
28
+ name: Code Talk
29
+ description: AI-powered code exploration across multiple repositories with confidence scoring
30
+ version: "1.0.0"
31
+
32
+ inputs:
33
+ - name: question
34
+ required: true
35
+ description: The code question to answer
36
+ schema:
37
+ type: string
38
+
39
+ - name: architecture
40
+ required: true
41
+ description: |
42
+ Architecture model - file path or inline markdown describing
43
+ project topology and routing rules
44
+ schema:
45
+ type: string
46
+
47
+ - name: docs_repo
48
+ required: true
49
+ description: Documentation repository (owner/name format)
50
+ schema:
51
+ type: string
52
+
53
+ - name: projects
54
+ required: true
55
+ description: |
56
+ Array of available code projects for AI routing:
57
+ - id: unique identifier for routing
58
+ - repo: GitHub repository (owner/name)
59
+ - description: used by AI for routing decisions
60
+ schema:
61
+ type: array
62
+ items:
63
+ type: object
64
+ properties:
65
+ id: { type: string }
66
+ repo: { type: string }
67
+ description: { type: string }
68
+ required: [id, repo, description]
69
+
70
+ - name: max_projects
71
+ default: 3
72
+ description: Maximum code projects to checkout (excludes docs)
73
+ schema:
74
+ type: number
75
+
76
+ - name: docs_ref
77
+ default: main
78
+ description: Git ref for docs repository
79
+ schema:
80
+ type: string
81
+
82
+ - name: routing_prompt
83
+ required: false
84
+ description: Additional routing instructions (appended to built-in)
85
+ schema:
86
+ type: string
87
+
88
+ - name: exploration_prompt
89
+ required: false
90
+ description: Additional exploration instructions (appended to built-in)
91
+ schema:
92
+ type: string
93
+
94
+ - name: ai_model
95
+ required: false
96
+ description: Override AI model for exploration step
97
+ schema:
98
+ type: string
99
+
100
+ outputs:
101
+ - name: answer
102
+ description: The answer object with text
103
+ value_js: |
104
+ const result = outputs?.['explore-code'];
105
+ if (result?.answer) return result.answer;
106
+ const routeOutput = outputs?.['route-projects'];
107
+ // Handle proper notes field
108
+ const routeNotes = routeOutput?.notes;
109
+ if (typeof routeNotes === 'string' && routeNotes.trim().length > 0) {
110
+ return { text: routeNotes };
111
+ }
112
+ // Fallback: if AI returned {text: "..."} instead of proper schema
113
+ const routeText = routeOutput?.text;
114
+ if (typeof routeText === 'string' && routeText.trim().length > 0) {
115
+ return { text: routeText };
116
+ }
117
+ return null;
118
+
119
+ - name: references
120
+ description: Code/doc references from exploration
121
+ value_js: |
122
+ const result = outputs?.['explore-code'];
123
+ return result?.references ?? [];
124
+
125
+ - name: confidence
126
+ description: |
127
+ Confidence in the final answer ("high", "medium", or "low").
128
+ Treat non-high confidence as a signal to verify before acting.
129
+ value_js: |
130
+ const result = outputs?.['explore-code'];
131
+ if (result?.confidence) return result.confidence;
132
+ const routeNotes = outputs?.['route-projects']?.notes;
133
+ if (typeof routeNotes === 'string' && routeNotes.trim().length > 0) return 'low';
134
+ return 'low';
135
+
136
+ - name: confidence_reason
137
+ description: |
138
+ Why confidence is not high. Should explain missing evidence,
139
+ ambiguity, or investigation gaps. May be empty only when confidence is high.
140
+ value_js: |
141
+ const result = outputs?.['explore-code'];
142
+ const confidence = result?.confidence;
143
+ const reason = result?.confidence_reason;
144
+ if (typeof reason === 'string') return reason;
145
+ if (confidence === 'high') return '';
146
+ const routeNotes = outputs?.['route-projects']?.notes;
147
+ if (typeof routeNotes === 'string' && routeNotes.trim().length > 0) return routeNotes;
148
+ return 'No confidence explanation was provided by explore-code.';
149
+
150
+ - name: projects_explored
151
+ description: Which project IDs were checked out
152
+ value_js: |
153
+ // Try to get from history first
154
+ const historyItems = outputs?.history?.['project-items'];
155
+ const lastItems = Array.isArray(historyItems) ? historyItems[historyItems.length - 1] : historyItems;
156
+ if (Array.isArray(lastItems)) {
157
+ return lastItems
158
+ .map(function (p) { return p?.project_id; })
159
+ .filter(function (p) { return p; });
160
+ }
161
+ // Fallback: try to get from direct output
162
+ const projectItems = outputs?.['project-items'];
163
+ if (Array.isArray(projectItems)) {
164
+ return projectItems
165
+ .map(function (p) { return p?.project_id; })
166
+ .filter(function (p) { return p; });
167
+ }
168
+ return [];
169
+
170
+ - name: projects_explored_details
171
+ description: Project details for routed projects (id, repo, description, reason)
172
+ value_js: |
173
+ const normalizeFromItems = function (items) {
174
+ const result = [];
175
+ if (!Array.isArray(items)) return result;
176
+ for (let i = 0; i < items.length; i++) {
177
+ const item = items[i];
178
+ if (!item) continue;
179
+ const id = item.project_id ?? item.id;
180
+ const repo = item.repository ?? item.repo;
181
+ if (!id || !repo) continue;
182
+ result.push({
183
+ id,
184
+ repo,
185
+ description: item.description ?? '',
186
+ reason: item.reason ?? ''
187
+ });
188
+ }
189
+ return result;
190
+ };
191
+
192
+ // Prefer detailed project-items output (includes repo/description/reason)
193
+ const historyItems = outputs?.history?.['project-items'];
194
+ const lastHistory = Array.isArray(historyItems) ? historyItems[historyItems.length - 1] : historyItems;
195
+ const normalized = normalizeFromItems(lastHistory);
196
+ if (normalized.length > 0) return normalized;
197
+
198
+ const directItems = normalizeFromItems(outputs?.['project-items']);
199
+ if (directItems.length > 0) return directItems;
200
+
201
+ // Fallback: map project IDs to inputs.projects
202
+ let ids = [];
203
+ if (Array.isArray(lastHistory)) {
204
+ ids = lastHistory
205
+ .map(function (p) { return p?.project_id; })
206
+ .filter(function (p) { return p; });
207
+ }
208
+ if (ids.length === 0) {
209
+ const projectItems = outputs?.['project-items'];
210
+ if (Array.isArray(projectItems)) {
211
+ ids = projectItems
212
+ .map(function (p) { return p?.project_id; })
213
+ .filter(function (p) { return p; });
214
+ }
215
+ }
216
+
217
+ const inputProjects = Array.isArray(inputs?.projects) ? inputs.projects : [];
218
+ const byId = {};
219
+ for (let j = 0; j < inputProjects.length; j++) {
220
+ const p = inputProjects[j];
221
+ if (p?.id) {
222
+ byId[p.id] = p;
223
+ }
224
+ }
225
+
226
+ const mapped = [];
227
+ for (let k = 0; k < ids.length; k++) {
228
+ const pid = ids[k];
229
+ const info = byId[pid];
230
+ if (!info?.repo) continue;
231
+ mapped.push({
232
+ id: pid,
233
+ repo: info.repo,
234
+ description: info.description ?? '',
235
+ reason: ''
236
+ });
237
+ }
238
+ return mapped;
239
+
240
+ - name: checkout_projects
241
+ description: Checked-out project paths aligned with routed projects
242
+ value_js: |
243
+ const historyItems = outputs?.history?.['project-items'];
244
+ const lastHistory = Array.isArray(historyItems) ? historyItems[historyItems.length - 1] : historyItems;
245
+ const items = Array.isArray(outputs?.['project-items'])
246
+ ? outputs['project-items']
247
+ : Array.isArray(lastHistory)
248
+ ? lastHistory
249
+ : [];
250
+
251
+ const checkoutOutput = outputs?.['checkout-projects'];
252
+ const checkouts = Array.isArray(checkoutOutput?.forEachItems)
253
+ ? checkoutOutput.forEachItems
254
+ : Array.isArray(checkoutOutput)
255
+ ? checkoutOutput
256
+ : [];
257
+
258
+ const result = [];
259
+ if (!Array.isArray(items) || !Array.isArray(checkouts)) return result;
260
+
261
+ for (let i = 0; i < items.length; i++) {
262
+ const item = items[i];
263
+ if (!item?.project_id) continue;
264
+ const checkout = checkouts[i] ?? {};
265
+ result.push({
266
+ project_id: item.project_id,
267
+ repository: item.repository ?? item.repo ?? '',
268
+ description: item.description ?? '',
269
+ path: checkout.path ?? checkout.workspace_path ?? ''
270
+ });
271
+ }
272
+ return result;
273
+
274
+ steps:
275
+ # ===========================================================================
276
+ # Step 1: Checkout documentation (always first)
277
+ # ===========================================================================
278
+ checkout-docs:
279
+ type: git-checkout
280
+ criticality: internal
281
+ assume:
282
+ - "true"
283
+ repository: "{{ inputs.docs_repo }}"
284
+ ref: "{{ inputs.docs_ref }}"
285
+ fetch_depth: 1
286
+ description: "Documentation"
287
+
288
+ # ===========================================================================
289
+ # Step 2: Route to relevant projects (AI-based)
290
+ # ===========================================================================
291
+ route-projects:
292
+ type: ai
293
+ criticality: internal
294
+ depends_on: [checkout-docs]
295
+ assume:
296
+ - "true"
297
+ guarantee: "(output?.projects?.length ?? 0) > 0 || (output?.notes?.length ?? 0) > 0"
298
+ fail_if: "(output?.projects?.length ?? 0) === 0 && !(output?.notes?.length > 0)"
299
+ ai:
300
+ skip_code_context: true
301
+ prompt_type: general
302
+ system_prompt: |
303
+ <role>
304
+ You are a project routing planner. Given a question, you decide which
305
+ code repositories need to be queried. You do NOT answer questions
306
+ directly - you only plan which projects to consult.
307
+ </role>
308
+
309
+ <context_handling>
310
+ The full conversation context (e.g., Slack thread) may be available in
311
+ <slack_context>. Use it to understand the real question. If the latest
312
+ message is a correction, reconstruct the original question from earlier
313
+ messages. Questions may include appended ticket/page context.
314
+ </context_handling>
315
+
316
+ <architecture>
317
+ {{ inputs.architecture }}
318
+ </architecture>
319
+
320
+ <docs_context>
321
+ The documentation has been checked out and is available for reference.
322
+ Use it to understand feature behavior, configuration options, and
323
+ cross-component interactions before deciding which code projects to query.
324
+ Path: {{ outputs['checkout-docs'].path }}
325
+ </docs_context>
326
+
327
+ <available_projects>
328
+ {% for p in inputs.projects %}
329
+ - id: {{ p.id }}
330
+ repo: {{ p.repo }}
331
+ description: {{ p.description }}
332
+ {% endfor %}
333
+ </available_projects>
334
+ schema: |
335
+ {
336
+ "type": "object",
337
+ "additionalProperties": false,
338
+ "properties": {
339
+ "projects": {
340
+ "type": "array",
341
+ "description": "Projects to query for this question",
342
+ "items": {
343
+ "type": "object",
344
+ "additionalProperties": false,
345
+ "properties": {
346
+ "project_id": {
347
+ "type": "string",
348
+ "description": "One of the available project IDs",
349
+ "enum": [{% for p in inputs.projects %}"{{ p.id }}"{% unless forloop.last %}, {% endunless %}{% endfor %}]
350
+ },
351
+ "reason": {
352
+ "type": "string",
353
+ "description": "Brief explanation of why this project is relevant"
354
+ }
355
+ },
356
+ "required": ["project_id"]
357
+ }
358
+ },
359
+ "notes": {
360
+ "type": "string",
361
+ "description": "Optional planner notes"
362
+ }
363
+ },
364
+ "required": ["projects"]
365
+ }
366
+ prompt: |
367
+ <question>{{ inputs.question }}</question>
368
+
369
+ <task>
370
+ Determine which projects are needed to answer this question.
371
+ Consider all parts of the stack that the functionality touches, then
372
+ select the smallest set that covers the behavior.
373
+
374
+ Return a "projects" array with:
375
+ - "project_id": from the available project IDs listed above
376
+ - "reason" (optional): brief explanation of relevance
377
+ - If you cannot determine the relevant projects from the context,
378
+ return an empty "projects" array and include a "notes" string
379
+ asking for the missing details you need.
380
+
381
+ Routing guidelines:
382
+ - When functionality spans multiple components, include ALL affected projects
383
+ - It's better to include a small superset than to miss a relevant project
384
+ - Typical plans contain 1-3 projects, but include more when necessary
385
+ - Consider how data/config flows between services
386
+ - Maximum projects: {{ inputs.max_projects }}
387
+ </task>
388
+
389
+ {% if inputs.routing_prompt %}
390
+ <additional_routing_rules>
391
+ {{ inputs.routing_prompt }}
392
+ </additional_routing_rules>
393
+ {% endif %}
394
+
395
+ # ===========================================================================
396
+ # Step 3: Map routed projects to checkout descriptors
397
+ # ===========================================================================
398
+ project-items:
399
+ type: script
400
+ criticality: internal
401
+ depends_on: [route-projects]
402
+ assume:
403
+ - "Array.isArray(outputs['route-projects']?.projects)"
404
+ guarantee: "Array.isArray(output)"
405
+ schema:
406
+ type: array
407
+ content: |
408
+ // Build a map of project ID -> project info from inputs
409
+ const projectMap = {};
410
+ const inputProjects = Array.isArray(inputs?.projects) ? inputs.projects : [];
411
+ for (let i = 0; i < inputProjects.length; i++) {
412
+ const p = inputProjects[i];
413
+ if (p?.id) {
414
+ projectMap[p.id] = {
415
+ repo: p.repo,
416
+ description: p.description ?? p.id
417
+ };
418
+ }
419
+ }
420
+
421
+ // Get routed projects from AI
422
+ const routeOutput = outputs?.['route-projects'];
423
+ const routedProjects = Array.isArray(routeOutput?.projects) ? routeOutput.projects : [];
424
+
425
+ // Map to checkout descriptors
426
+ const result = [];
427
+ const maxProjects = inputs?.max_projects ?? 3;
428
+
429
+ for (let j = 0; j < routedProjects.length && result.length < maxProjects; j++) {
430
+ const routed = routedProjects[j];
431
+ if (!routed?.project_id) continue;
432
+
433
+ const info = projectMap[routed.project_id];
434
+ // Skip unknown project IDs (not in inputs.projects)
435
+ if (!info?.repo) continue;
436
+
437
+ result.push({
438
+ project_id: routed.project_id,
439
+ reason: routed.reason ?? '',
440
+ repository: info.repo,
441
+ description: info.description
442
+ });
443
+ }
444
+
445
+ return result;
446
+ forEach: true
447
+
448
+ # ===========================================================================
449
+ # Step 4: Checkout each selected project
450
+ # ===========================================================================
451
+ checkout-projects:
452
+ type: git-checkout
453
+ criticality: internal
454
+ depends_on: [project-items]
455
+ assume:
456
+ - "output != null"
457
+ repository: "{{ outputs['project-items'].repository }}"
458
+ ref: main
459
+ fetch_depth: 1
460
+ description: "{{ outputs['project-items'].description }}"
461
+
462
+ # ===========================================================================
463
+ # Step 5: Explore code across all projects (main AI call)
464
+ # ===========================================================================
465
+ explore-code:
466
+ type: ai
467
+ criticality: internal
468
+ depends_on: [route-projects, project-items, checkout-projects]
469
+ fanout: reduce
470
+ assume:
471
+ - "outputs['route-projects']?.projects?.length > 0"
472
+ ai:
473
+ skip_code_context: true
474
+ enableDelegate: true
475
+ enableExecutePlan: true
476
+ max_iterations: 40
477
+ prompt_type: code-explorer
478
+ allowBash: true
479
+ bashConfig:
480
+ allow:
481
+ # Git read-only commands only (no commit, push, reset, rebase, merge, etc.)
482
+ - "git:diff:*"
483
+ - "git:log:*"
484
+ - "git:show:*"
485
+ - "git:blame:*"
486
+ - "git:checkout:*"
487
+ - "git:branch:*"
488
+ - "git:tag:*"
489
+ - "git:fetch:*"
490
+ - "git:status:*"
491
+ - "git:rev-parse:*"
492
+ - "git:ls-files:*"
493
+ - "git:ls-tree:*"
494
+ # File operations
495
+ - "ls:*"
496
+ - "find:*"
497
+ - "cat:*"
498
+ - "head:*"
499
+ - "tail:*"
500
+ - "wc:*"
501
+ - "grep:*"
502
+ # GitHub CLI read-only operations
503
+ - "gh:run:*"
504
+ - "gh:run:list:*"
505
+ - "gh:run:view:*"
506
+ - "gh:run:watch:*"
507
+ - "gh:workflow:*"
508
+ - "gh:workflow:list:*"
509
+ - "gh:workflow:view:*"
510
+ - "gh:pr:*"
511
+ - "gh:pr:list:*"
512
+ - "gh:pr:view:*"
513
+ - "gh:pr:checks:*"
514
+ - "gh:pr:diff:*"
515
+ - "gh:issue:*"
516
+ - "gh:issue:list:*"
517
+ - "gh:issue:view:*"
518
+ - "gh:repo:view:*"
519
+ - "gh:repo:list:*"
520
+ - "gh:api:*"
521
+ - "gh:release:list:*"
522
+ - "gh:release:view:*"
523
+ # Curl for API calls (read-only)
524
+ - "curl:-s:*"
525
+ - "curl:*"
526
+ timeout: 60000
527
+ completion_prompt: |
528
+ Before finalizing your answer, triple-check everything:
529
+
530
+ Challenge assumptions:
531
+ 1. Did you INDEPENDENTLY verify the user's claims, or just search for confirmation?
532
+ 2. If the user assumed a root cause, did you verify it's actually correct?
533
+ 3. Could the real issue be something completely different from what was suggested?
534
+
535
+ Investigation completeness:
536
+ 4. Did you consider MULTIPLE possible explanations, not just the first one?
537
+ 5. If there were alternative theories, did you investigate each one?
538
+ 6. Have you ruled out other possibilities with evidence, not assumptions?
539
+
540
+ Cross-project verification:
541
+ 4. Are there dependencies or interactions between projects you examined?
542
+ 5. Did you verify how data/config flows between components?
543
+ 6. Did you check both the happy path AND error handling paths?
544
+
545
+ Reference accuracy:
546
+ 7. Are all code references accurate (file paths, function names, line numbers)?
547
+ 8. Do the docs match what the code actually does?
548
+
549
+ CRITICAL: When you identify a configuration variable, you MUST then perform
550
+ a second search to find the code that consumes that variable. You cannot draw
551
+ conclusions from a variable's name alone. You must confirm its purpose by
552
+ analyzing how it is used in the application logic.
553
+
554
+ If you found any ambiguity, gaps in your investigation, or unexplored
555
+ hypotheses - use delegate tool to investigate further before concluding.
556
+
557
+ When you finish, ensure your answer includes:
558
+ - All relevant details grounded in code
559
+ - If multiple theories were considered, explain which one is correct and WHY
560
+ (with evidence ruling out alternatives)
561
+ - A confidence score ("high", "medium", or "low")
562
+ - If confidence is "medium" or "low", include a clear confidence_reason
563
+ explaining what evidence is missing or ambiguous
564
+ - At the END of your answer.text, append a "## References" section with a
565
+ bulleted list of all code/doc references in this format:
566
+ - [file.go:42-50](https://github.com/org/repo/blob/main/path/file.go#L42-L50) - brief description
567
+ - Also populate the references array with structured data for each reference
568
+ schema:
569
+ type: object
570
+ additionalProperties: false
571
+ properties:
572
+ answer:
573
+ type: object
574
+ additionalProperties: false
575
+ properties:
576
+ text:
577
+ type: string
578
+ description: |
579
+ The complete answer with explanations. MUST end with a
580
+ "## References" section containing clickable GitHub links.
581
+ summary:
582
+ type: string
583
+ description: One-line summary (optional)
584
+ required: [text]
585
+ references:
586
+ type: array
587
+ description: Code and documentation references (must not be empty)
588
+ items:
589
+ type: object
590
+ properties:
591
+ project:
592
+ type: string
593
+ description: Project ID (e.g., "tyk", "tyk-analytics")
594
+ file:
595
+ type: string
596
+ description: File path relative to repo root
597
+ lines:
598
+ type: array
599
+ description: Line numbers (start and end if range)
600
+ items: { type: number }
601
+ url:
602
+ type: string
603
+ description: Full GitHub URL with line numbers (e.g., https://github.com/org/repo/blob/main/file.go#L42-L50)
604
+ snippet:
605
+ type: string
606
+ description: Brief description of what this reference shows
607
+ required: [project, file, url]
608
+ confidence:
609
+ type: string
610
+ enum: [high, medium, low]
611
+ description: |
612
+ Confidence in the answer based on evidence quality and investigation coverage.
613
+ Use "high" only when claims are directly backed by code/doc evidence.
614
+ confidence_reason:
615
+ type: string
616
+ description: |
617
+ Why confidence is not high (required to be meaningful for medium/low confidence).
618
+ Leave empty string only when confidence is high.
619
+ required: [answer, references, confidence, confidence_reason]
620
+ prompt: |
621
+ <instructions>
622
+ You are a code/documentation explorer. Your goal is to answer a code-level
623
+ question that can span multiple projects, using:
624
+ - the checked-out code repositories
625
+ - the checked-out documentation repository
626
+ - code-explorer tools (search, extract, query, listFiles, searchFiles)
627
+ - delegate sub-agents when you need to dive deeply into a project
628
+ - ensure that delegate sub-agents return detailed references with files and line numbers
629
+
630
+ IMPORTANT - Handling ambiguity:
631
+ If the question is ambiguous, unclear, or you need more details to provide
632
+ a useful answer, DO NOT guess or make assumptions. Instead:
633
+ - Clearly state what information is missing or unclear
634
+ - Ask specific clarifying questions
635
+ - Explain what you could investigate with more context
636
+
637
+ Examples of when to ask for clarification:
638
+ - "Which version/branch are you asking about?"
639
+ - "Are you asking about the gateway or dashboard implementation?"
640
+ - "Could you provide the specific error message or log output?"
641
+ - "Which repository or component is this related to?"
642
+
643
+ It's better to ask for clarification than to provide an incorrect or
644
+ irrelevant answer based on assumptions.
645
+
646
+ Git bash commands:
647
+ You can use git commands for deeper code investigation:
648
+ - `git diff` to compare branches or commits
649
+ - `git log` to see commit history and understand changes over time
650
+ - `git show` to inspect specific commits
651
+ - `git blame` to understand who changed what and when
652
+ - `git checkout` to switch branches when comparing implementations
653
+ - `git branch -a` to list ALL branches (local and remote)
654
+ - `git fetch --tags` to fetch all tags from remote
655
+ - `git tag -l` to list all available tags
656
+
657
+ IMPORTANT - Fetching remote branches:
658
+ Repositories are checked out with shallow clone (fetch_depth: 1), so remote
659
+ branches are NOT available locally by default. Before checking out a branch:
660
+ 1. First fetch the specific branch: `git fetch origin <branch-name>`
661
+ 2. Then checkout: `git checkout <branch-name>` or `git checkout origin/<branch-name>`
662
+
663
+ Example for checking out release-5.3:
664
+ - First: `git fetch origin release-5.3`
665
+ - Then: `git checkout release-5.3`
666
+
667
+ If the branch doesn't exist, try listing available branches:
668
+ - `git branch -r` to see remote branches
669
+ - `git ls-remote --heads origin` to list all remote branches
670
+
671
+ Use these when you need to understand how code evolved, compare different
672
+ versions, or investigate recent changes related to the question.
673
+
674
+ GitHub CLI (gh) for PR review and GitHub data:
675
+ You have access to the `gh` CLI tool for GitHub operations:
676
+ - `gh pr view <number> --repo owner/repo` - View PR details, description, status
677
+ - `gh pr diff <number> --repo owner/repo` - Get the full diff of a PR
678
+ - `gh pr checks <number> --repo owner/repo` - View CI/CD check status
679
+ - `gh pr list --repo owner/repo` - List open PRs
680
+ - `gh issue view <number> --repo owner/repo` - View issue details
681
+ - `gh api repos/owner/repo/pulls/<number>/files` - Get list of changed files
682
+ - `gh api repos/owner/repo/pulls/<number>/comments` - Get PR review comments
683
+ - `gh run list --repo owner/repo` - List workflow runs
684
+ - `gh run view <run-id> --repo owner/repo --log` - View workflow run logs
685
+
686
+ IMPORTANT - Checking out PR branches for review:
687
+ When asked to review a PR or investigate PR code, you need to checkout the PR branch:
688
+ 1. GitHub maintains special refs for PRs: `refs/pull/<PR_NUMBER>/head`
689
+ 2. To fetch and checkout a PR:
690
+ - `git fetch origin pull/<PR_NUMBER>/head:pr-<PR_NUMBER>` - fetch PR to local branch
691
+ - `git checkout pr-<PR_NUMBER>` - checkout the PR branch
692
+ 3. Example for PR #123:
693
+ - `git fetch origin pull/123/head:pr-123`
694
+ - `git checkout pr-123`
695
+ 4. To see what changed vs main/master:
696
+ - `git diff main...pr-123` or `git diff origin/main...HEAD`
697
+
698
+ IMPORTANT bash tool usage rules:
699
+ - Do NOT use shell operators like && or || or | (pipes)
700
+ - Do NOT use `cd dir && command`
701
+ - INSTEAD, use the workingDirectory parameter to specify where to run commands
702
+ - Each bash call should be a single simple command with workingDirectory set
703
+
704
+ Version-specific queries:
705
+ - When asked about a specific version (e.g., "5.8", "v5.8.3"):
706
+ 1. First run `git fetch --tags` (with workingDirectory set)
707
+ 2. Then run `git tag -l "v5.8*"` to find matching tags
708
+ 3. Checkout to the relevant tag: `git checkout v5.8.10`
709
+ - Always verify you're on the correct version/branch before investigating code
710
+
711
+ Path rules: Always use relative paths (e.g., "gateway/mw_jwt.go"), never absolute
712
+ paths with /tmp/ or workspace UUIDs.
713
+
714
+ High-level behavior:
715
+ - Use documentation to understand intended behavior, configuration, and
716
+ cross-component interactions for the feature in question
717
+ - For each selected project, read the code and relevant docs to answer
718
+ the question as concretely as possible
719
+ - When multiple projects are involved, deliberately follow data and control
720
+ flow across them and explain the connections
721
+ - Always ground your answer in actual code/docs, not speculation
722
+ - When you see a dependency - prove it by code
723
+
724
+ Critical thinking - DO NOT blindly trust user assumptions:
725
+ - Users often report real problems but with INCORRECT root cause analysis
726
+ - The symptom may be real, but the explanation in the question may be wrong
727
+ - Verify every claim independently - don't just search for what the user expects
728
+ - If a bug report says "X causes Y", verify BOTH that Y happens AND that X is the cause
729
+ - Look at the bigger picture - the real issue might be elsewhere entirely
730
+ - Be unbiased: don't anchor on the user's theory, form your own based on evidence
731
+
732
+ Investigation methodology - IMPORTANT:
733
+ 1. Before diving deep, form multiple hypotheses about the answer:
734
+ - What are the possible explanations or root causes?
735
+ - Could the behavior be in component A, B, or both?
736
+ - Is this a configuration issue, code bug, or expected behavior?
737
+ - Is the user's assumption about the cause actually correct?
738
+ 2. Investigate each hypothesis systematically:
739
+ - Don't assume your first theory OR the user's theory is correct
740
+ - If evidence contradicts a hypothesis, note it and move on
741
+ - Look for edge cases and alternative code paths
742
+ 3. When investigating complex issues:
743
+ - Check error handling paths, not just happy paths
744
+ - Look at configuration options that might affect behavior
745
+ - Consider version differences if relevant
746
+ 4. Cross-reference your findings:
747
+ - Do the docs match what the code actually does?
748
+ - Are there any TODOs, FIXMEs, or known issues?
749
+ - Check git blame/log for recent changes if behavior seems unexpected
750
+
751
+ Using delegate tool effectively:
752
+ - Use delegate when you need DEEP investigation into a specific component
753
+ - Each delegate call should focus on ONE specific question or hypothesis
754
+ - Good delegate uses:
755
+ - "Investigate how JWT validation handles expired tokens in gateway"
756
+ - "Find all places where rate limit counters are incremented"
757
+ - "Trace the request flow from API entry to database query"
758
+ - Bad delegate uses:
759
+ - "Look at the code" (too vague)
760
+ - "Find everything about auth" (too broad)
761
+ - Run multiple delegates in PARALLEL when investigating different hypotheses
762
+ or different components - don't wait for one to finish before starting another
763
+ - Always ask delegates to return specific file paths and line numbers
764
+
765
+ CRITICAL - Preserve detailed output from tools and delegates:
766
+ When tools or delegates return detailed data (customer insights, code analysis, etc.):
767
+ - DO NOT summarize or compress the output
768
+ - RELAY THE FULL DATA including all names, specifics, and details
769
+ - If a tool returns 10 items with details, include ALL 10 in your answer
770
+ - Never say "based on the analysis" without presenting the actual data
771
+ - Tools already synthesize data - your job is to present it completely
772
+
773
+ CRITICAL: Always use `attempt_completion` tool to submit your final answer.
774
+ This enables validation of your investigation before the response is finalized.
775
+ </instructions>
776
+
777
+ {% if inputs.exploration_prompt %}
778
+ <additional_instructions>
779
+ {{ inputs.exploration_prompt }}
780
+ </additional_instructions>
781
+ {% endif %}
782
+
783
+ <context>
784
+ <question>
785
+ {{ inputs.question }}
786
+ </question>
787
+
788
+ <architecture>
789
+ The following architecture document describes the project topology, routing rules,
790
+ and important guidelines for working with this codebase. Use it to understand
791
+ project relationships, debugging procedures, and special instructions.
792
+
793
+ {{ inputs.architecture }}
794
+ </architecture>
795
+
796
+ <routing_decision>
797
+ {{ outputs['route-projects'] | json }}
798
+ </routing_decision>
799
+
800
+ <docs_checkout>
801
+ <repo>{{ inputs.docs_repo }}</repo>
802
+ <ref>{{ inputs.docs_ref }}</ref>
803
+ <path>{{ outputs['checkout-docs'].path }}</path>
804
+ </docs_checkout>
805
+
806
+ {% assign items = outputs.history['project-items'] | last %}
807
+ {% assign checkouts = outputs.history['checkout-projects'] %}
808
+ {% if checkouts == nil or checkouts == empty %}
809
+ {% assign checkouts = outputs['checkout-projects'].forEachItems %}
810
+ {% endif %}
811
+ <projects>
812
+ {% if items and checkouts %}
813
+ {% for p in items %}
814
+ {% assign co = checkouts[forloop.index0] %}
815
+ <project>
816
+ <id>{{ p.project_id }}</id>
817
+ <repo>{{ p.repository }}</repo>
818
+ <path>{{ co.path }}</path>
819
+ <reason>{{ p.reason | default: 'not provided' }}</reason>
820
+ </project>
821
+ {% endfor %}
822
+ {% endif %}
823
+ </projects>
824
+ </context>
825
+
826
+ <task>
827
+ For each project listed in <projects>:
828
+ - Use code-explorer tools to:
829
+ - listFiles/searchFiles to understand the structure and find relevant
830
+ directories and files
831
+ - search/query/extract to locate and read the core implementation
832
+ - consult documentation to confirm semantics, configuration options,
833
+ and cross-project responsibilities
834
+ - When multiple projects are involved, pay attention to:
835
+ - how configuration flows between components
836
+ - how identity, auth, or data flows between services
837
+ - any shared libraries used by more than one project
838
+ - Use delegate tools per project when a deeper, focused investigation
839
+ is needed
840
+
841
+ Finally, synthesize everything into a single answer that explains the
842
+ behavior to an engineer:
843
+ - Be concrete and code/doc-grounded (mention key components, files, and
844
+ configuration options when helpful)
845
+ - Keep the explanation focused and coherent
846
+ - IMPORTANT: At the END of your answer, include a "## References" section
847
+ with clickable GitHub links pointing to specific lines
848
+
849
+ Return your answer in the schema format with:
850
+ - answer.text: your complete explanation, ending with a "## References" section like:
851
+ ```
852
+ ## References
853
+ - [mw_jwt.go:142-180](https://github.com/TykTechnologies/tyk/blob/master/gateway/mw_jwt.go#L142-L180) - JWT validation middleware
854
+ - [jwt.md](https://github.com/TykTechnologies/tyk-docs/blob/main/tyk-docs/content/basic-config-and-security/security/authentication-authorization/jwt.md) - JWT documentation
855
+ ```
856
+ - answer.summary: one-line summary (optional)
857
+ - references: array with structured data for each reference (project, file, url, snippet)
858
+ - confidence: "high" | "medium" | "low"
859
+ - confidence_reason: required explanation when confidence is "medium" or "low";
860
+ use empty string only when confidence is "high"
861
+ </task>
862
+
863
+ # =============================================================================
864
+ # Tests
865
+ # =============================================================================
866
+ tests:
867
+ defaults:
868
+ strict: true
869
+ ai_provider: mock
870
+
871
+ cases:
872
+ - name: basic-code-question
873
+ event: manual
874
+ fixture: local.minimal
875
+ workflow_input:
876
+ question: "How does authentication work?"
877
+ architecture: |
878
+ # Architecture
879
+ ## Projects
880
+ - gateway: API Gateway
881
+ - backend: Backend services
882
+ docs_repo: org/docs
883
+ projects:
884
+ - id: gateway
885
+ repo: org/gateway
886
+ description: API Gateway
887
+ - id: backend
888
+ repo: org/backend
889
+ description: Backend services
890
+ mocks:
891
+ checkout-docs:
892
+ path: "/tmp/visor/docs"
893
+ repository: "org/docs"
894
+ ref: "main"
895
+ checkout-projects:
896
+ path: "/tmp/visor/project"
897
+ repository: "org/gateway"
898
+ ref: "main"
899
+ route-projects:
900
+ projects:
901
+ - project_id: "gateway"
902
+ reason: "Gateway handles authentication"
903
+ explore-code:
904
+ answer:
905
+ text: "Authentication is handled in the gateway via JWT middleware."
906
+ summary: "JWT auth in gateway"
907
+ references:
908
+ - project: gateway
909
+ file: src/auth/jwt.go
910
+ url: https://github.com/org/gateway/blob/main/src/auth/jwt.go#L42
911
+ confidence: high
912
+ confidence_reason: ""
913
+ expect:
914
+ calls:
915
+ - step: checkout-docs
916
+ exactly: 1
917
+ - step: route-projects
918
+ exactly: 1
919
+ - step: project-items
920
+ exactly: 1
921
+ - step: checkout-projects
922
+ exactly: 1
923
+ - step: explore-code
924
+ exactly: 1
925
+ # Verify workflow inputs are rendered in the route-projects prompt
926
+ # Note: prompts assertion captures the user prompt only, not system_prompt.
927
+ # The user prompt contains question; system_prompt contains architecture and projects.
928
+ prompts:
929
+ - step: route-projects
930
+ contains:
931
+ # Verify question input is rendered (from inputs.question)
932
+ - "How does authentication work?"
933
+ # Verify max_projects default is rendered
934
+ - "Maximum projects:"
935
+ workflow_output:
936
+ - path: answer.text
937
+ equals: "Authentication is handled in the gateway via JWT middleware."
938
+ - path: confidence
939
+ equals: "high"
940
+
941
+ - name: multi-project-question
942
+ event: manual
943
+ fixture: local.minimal
944
+ workflow_input:
945
+ question: "How does data flow from gateway to backend?"
946
+ architecture: "# Arch"
947
+ docs_repo: org/docs
948
+ projects:
949
+ - id: gateway
950
+ repo: org/gateway
951
+ description: API Gateway
952
+ - id: backend
953
+ repo: org/backend
954
+ description: Backend services
955
+ mocks:
956
+ checkout-docs:
957
+ path: "/tmp/visor/docs"
958
+ repository: "org/docs"
959
+ ref: "main"
960
+ checkout-projects:
961
+ path: "/tmp/visor/project"
962
+ repository: "org/example"
963
+ ref: "main"
964
+ route-projects:
965
+ projects:
966
+ - project_id: "gateway"
967
+ reason: "Handles incoming requests"
968
+ - project_id: "backend"
969
+ reason: "Processes data"
970
+ explore-code:
971
+ answer:
972
+ text: "Data flows via gRPC from gateway to backend."
973
+ references: []
974
+ confidence: medium
975
+ confidence_reason: "References are incomplete for full cross-project verification."
976
+ expect:
977
+ calls:
978
+ - step: checkout-docs
979
+ exactly: 1
980
+ - step: route-projects
981
+ exactly: 1
982
+ - step: project-items
983
+ exactly: 1
984
+ - step: checkout-projects
985
+ exactly: 2
986
+ - step: explore-code
987
+ exactly: 1
988
+
989
+ - name: empty-routing-returns-notes
990
+ strict: false
991
+ event: manual
992
+ fixture: local.minimal
993
+ workflow_input:
994
+ question: "Vague question"
995
+ architecture: "# Arch"
996
+ docs_repo: org/docs
997
+ projects:
998
+ - id: main
999
+ repo: org/main
1000
+ description: Main app
1001
+ mocks:
1002
+ checkout-docs:
1003
+ path: "/tmp/visor/docs"
1004
+ repository: "org/docs"
1005
+ ref: "main"
1006
+ route-projects:
1007
+ projects: []
1008
+ notes: "I need more context to route this question."
1009
+ expect:
1010
+ calls:
1011
+ - step: route-projects
1012
+ exactly: 1
1013
+ workflow_output:
1014
+ - path: answer.text
1015
+ equals: "I need more context to route this question."
1016
+
1017
+ # =========================================================================
1018
+ # Edge Case: Checkout failure stops downstream checks
1019
+ # When checkout fails (success: false), dependent checks don't run.
1020
+ # =========================================================================
1021
+ - name: checkout-failure-stops-exploration
1022
+ strict: false
1023
+ event: manual
1024
+ fixture: local.minimal
1025
+ workflow_input:
1026
+ question: "How does auth work?"
1027
+ architecture: "# Arch"
1028
+ docs_repo: org/docs
1029
+ projects:
1030
+ - id: backend
1031
+ repo: org/backend
1032
+ description: Backend services
1033
+ mocks:
1034
+ checkout-docs:
1035
+ path: "/tmp/visor/docs"
1036
+ repository: "org/docs"
1037
+ ref: "main"
1038
+ route-projects:
1039
+ projects:
1040
+ - project_id: "backend"
1041
+ reason: "Auth is in backend"
1042
+ checkout-projects:
1043
+ # Failure stops downstream checks
1044
+ success: false
1045
+ error: "Repository not found: org/backend"
1046
+ expect:
1047
+ calls:
1048
+ - step: checkout-docs
1049
+ exactly: 1
1050
+ - step: route-projects
1051
+ exactly: 1
1052
+ - step: project-items
1053
+ exactly: 1
1054
+ - step: checkout-projects
1055
+ exactly: 1
1056
+ # explore-code does NOT run because checkout failed
1057
+ - step: explore-code
1058
+ exactly: 0
1059
+
1060
+ # =========================================================================
1061
+ # Edge Case: Malformed AI response (missing required fields)
1062
+ # =========================================================================
1063
+ - name: malformed-routing-response
1064
+ strict: false
1065
+ event: manual
1066
+ fixture: local.minimal
1067
+ workflow_input:
1068
+ question: "What is X?"
1069
+ architecture: "# Arch"
1070
+ docs_repo: org/docs
1071
+ projects:
1072
+ - id: app
1073
+ repo: org/app
1074
+ description: Main app
1075
+ mocks:
1076
+ checkout-docs:
1077
+ path: "/tmp/visor/docs"
1078
+ repository: "org/docs"
1079
+ ref: "main"
1080
+ route-projects:
1081
+ # Missing 'projects' field entirely - malformed response
1082
+ notes: "I couldn't determine the projects"
1083
+ expect:
1084
+ calls:
1085
+ - step: route-projects
1086
+ exactly: 1
1087
+ # Should fail because projects array is missing/empty
1088
+
1089
+ # =========================================================================
1090
+ # Edge Case: Unknown project ID in routing (not in input projects)
1091
+ # The script filters out unknown IDs and only processes valid ones.
1092
+ # =========================================================================
1093
+ - name: unknown-project-id-filtered
1094
+ event: manual
1095
+ fixture: local.minimal
1096
+ workflow_input:
1097
+ question: "How does caching work?"
1098
+ architecture: "# Arch"
1099
+ docs_repo: org/docs
1100
+ projects:
1101
+ - id: backend
1102
+ repo: org/backend
1103
+ description: Backend services
1104
+ mocks:
1105
+ checkout-docs:
1106
+ path: "/tmp/visor/docs"
1107
+ repository: "org/docs"
1108
+ ref: "main"
1109
+ checkout-projects:
1110
+ path: "/tmp/visor/project"
1111
+ repository: "org/backend"
1112
+ ref: "main"
1113
+ route-projects:
1114
+ projects:
1115
+ # AI returns a project ID that doesn't exist in inputs
1116
+ - project_id: "nonexistent-service"
1117
+ reason: "This doesn't exist"
1118
+ # Plus a valid one
1119
+ - project_id: "backend"
1120
+ reason: "Caching logic"
1121
+ explore-code:
1122
+ answer:
1123
+ text: "Caching is implemented in the backend."
1124
+ references: []
1125
+ confidence: medium
1126
+ confidence_reason: "One routed project was invalid and filtered before exploration."
1127
+ expect:
1128
+ calls:
1129
+ - step: checkout-docs
1130
+ exactly: 1
1131
+ - step: route-projects
1132
+ exactly: 1
1133
+ - step: project-items
1134
+ exactly: 1
1135
+ # Only 1 checkout because nonexistent-service is filtered out
1136
+ - step: checkout-projects
1137
+ exactly: 1
1138
+ - step: explore-code
1139
+ exactly: 1
1140
+ workflow_output:
1141
+ - path: answer.text
1142
+ equals: "Caching is implemented in the backend."
1143
+
1144
+ # =========================================================================
1145
+ # Edge Case: max_projects limit respected
1146
+ # =========================================================================
1147
+ - name: max-projects-limit
1148
+ event: manual
1149
+ fixture: local.minimal
1150
+ workflow_input:
1151
+ question: "How does the full stack work?"
1152
+ architecture: "# Arch"
1153
+ docs_repo: org/docs
1154
+ max_projects: 2
1155
+ projects:
1156
+ - id: frontend
1157
+ repo: org/frontend
1158
+ description: Frontend app
1159
+ - id: backend
1160
+ repo: org/backend
1161
+ description: Backend services
1162
+ - id: database
1163
+ repo: org/database
1164
+ description: Database layer
1165
+ - id: cache
1166
+ repo: org/cache
1167
+ description: Cache layer
1168
+ mocks:
1169
+ checkout-docs:
1170
+ path: "/tmp/visor/docs"
1171
+ repository: "org/docs"
1172
+ ref: "main"
1173
+ checkout-projects:
1174
+ path: "/tmp/visor/project"
1175
+ repository: "org/example"
1176
+ ref: "main"
1177
+ route-projects:
1178
+ projects:
1179
+ - project_id: "frontend"
1180
+ - project_id: "backend"
1181
+ - project_id: "database"
1182
+ - project_id: "cache"
1183
+ explore-code:
1184
+ answer:
1185
+ text: "The stack flows from frontend through backend."
1186
+ references: []
1187
+ confidence: medium
1188
+ confidence_reason: "Exploration scope was reduced by max_projects limit."
1189
+ expect:
1190
+ calls:
1191
+ - step: checkout-docs
1192
+ exactly: 1
1193
+ - step: route-projects
1194
+ exactly: 1
1195
+ - step: project-items
1196
+ exactly: 1
1197
+ # Only 2 checkouts due to max_projects: 2
1198
+ - step: checkout-projects
1199
+ exactly: 2
1200
+ - step: explore-code
1201
+ exactly: 1
1202
+
1203
+ # =========================================================================
1204
+ # Edge Case: Empty explore-code response handled gracefully
1205
+ # =========================================================================
1206
+ - name: empty-exploration-response
1207
+ event: manual
1208
+ fixture: local.minimal
1209
+ workflow_input:
1210
+ question: "What is feature X?"
1211
+ architecture: "# Arch"
1212
+ docs_repo: org/docs
1213
+ projects:
1214
+ - id: main
1215
+ repo: org/main
1216
+ description: Main app
1217
+ mocks:
1218
+ checkout-docs:
1219
+ path: "/tmp/visor/docs"
1220
+ repository: "org/docs"
1221
+ ref: "main"
1222
+ checkout-projects:
1223
+ path: "/tmp/visor/project"
1224
+ repository: "org/main"
1225
+ ref: "main"
1226
+ route-projects:
1227
+ projects:
1228
+ - project_id: "main"
1229
+ explore-code:
1230
+ # Minimal valid response with empty text
1231
+ answer:
1232
+ text: ""
1233
+ references: []
1234
+ confidence: low
1235
+ confidence_reason: "The final answer text is empty."
1236
+ expect:
1237
+ calls:
1238
+ - step: checkout-docs
1239
+ exactly: 1
1240
+ - step: route-projects
1241
+ exactly: 1
1242
+ - step: project-items
1243
+ exactly: 1
1244
+ - step: checkout-projects
1245
+ exactly: 1
1246
+ - step: explore-code
1247
+ exactly: 1
1248
+ workflow_output:
1249
+ - path: answer.text
1250
+ equals: ""