@probelabs/visor 0.1.124 → 0.1.126
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.d.ts.map +1 -1
- package/dist/docs/DEPLOYMENT.md +117 -11
- package/dist/docs/GITHUB_CHECKS.md +18 -4
- package/dist/docs/NPM_USAGE.md +112 -39
- package/dist/docs/action-reference.md +63 -9
- package/dist/docs/advanced-ai.md +58 -51
- package/dist/docs/ai-configuration.md +99 -11
- package/dist/docs/ai-custom-tools-usage.md +70 -33
- package/dist/docs/ai-custom-tools.md +50 -27
- package/dist/docs/architecture.md +1232 -0
- package/dist/docs/bot-transports-rfc.md +13 -3
- package/dist/docs/ci-cli-mode.md +116 -8
- package/dist/docs/claude-code.md +111 -41
- package/dist/docs/command-provider.md +37 -15
- package/dist/docs/commands.md +252 -6
- package/dist/docs/configuration.md +138 -4
- package/dist/docs/contributing.md +737 -0
- package/dist/docs/custom-tools.md +39 -8
- package/dist/docs/dashboards/README.md +33 -19
- package/dist/docs/debug-visualizer-progress.md +14 -13
- package/dist/docs/debug-visualizer-rfc.md +14 -13
- package/dist/docs/debug-visualizer.md +30 -5
- package/dist/docs/debugging.md +73 -8
- package/dist/docs/default-output-schema.md +24 -20
- package/dist/docs/dependencies.md +75 -21
- package/dist/docs/dev-playbook.md +85 -9
- package/dist/docs/engine-pause-resume-rfc.md +11 -11
- package/dist/docs/engine-state-machine-plan.md +10 -3
- package/dist/docs/event-driven-github-integration-rfc.md +20 -11
- package/dist/docs/event-triggers.md +95 -6
- package/dist/docs/execution-statistics-rfc.md +16 -4
- package/dist/docs/fact-validator-gap-analysis.md +12 -1
- package/dist/docs/fact-validator-implementation-plan.md +19 -11
- package/dist/docs/fail-if.md +116 -11
- package/dist/docs/failure-conditions-implementation.md +40 -6
- package/dist/docs/failure-conditions-schema.md +243 -87
- package/dist/docs/failure-routing-rfc.md +43 -18
- package/dist/docs/failure-routing.md +80 -23
- package/dist/docs/faq.md +836 -0
- package/dist/docs/foreach-dependency-propagation.md +32 -15
- package/dist/docs/github-ops.md +6 -5
- package/dist/docs/glossary.md +322 -0
- package/dist/docs/goto-forward-run-plan.md +23 -10
- package/dist/docs/guides/criticality-modes.md +15 -13
- package/dist/docs/guides/fault-management-and-contracts.md +8 -5
- package/dist/docs/guides/workflow-style-guide.md +17 -8
- package/dist/docs/http.md +102 -3
- package/dist/docs/human-input-provider.md +20 -36
- package/dist/docs/index.md +206 -0
- package/dist/docs/lifecycle-hooks.md +322 -2
- package/dist/docs/limits.md +20 -5
- package/dist/docs/liquid-templates.md +86 -14
- package/dist/docs/loop-routing-refactor.md +4 -2
- package/dist/docs/mcp-provider.md +53 -19
- package/dist/docs/mcp.md +27 -1
- package/dist/docs/memory.md +7 -2
- package/dist/docs/migration.md +596 -0
- package/dist/docs/observability.md +227 -6
- package/dist/docs/output-formats.md +388 -9
- package/dist/docs/output-history.md +36 -6
- package/dist/docs/performance.md +510 -4
- package/dist/docs/pluggable.md +95 -4
- package/dist/docs/proposals/snapshot-scope-execution.md +6 -5
- package/dist/docs/providers/git-checkout.md +16 -14
- package/dist/docs/providers/noop.md +696 -0
- package/dist/docs/recipes.md +8 -9
- package/dist/docs/rfc/git-checkout-step.md +3 -1
- package/dist/docs/rfc/on_init-hook.md +18 -5
- package/dist/docs/rfc/workspace-isolation.md +16 -0
- package/dist/docs/roadmap/criticality-implementation-tasks.md +27 -27
- package/dist/docs/router-patterns.md +155 -43
- package/dist/docs/schema-templates.md +51 -15
- package/dist/docs/script.md +162 -13
- package/dist/docs/sdk.md +46 -12
- package/dist/docs/security.md +464 -5
- package/dist/docs/slack-integration.md +481 -0
- package/dist/docs/tag-filtering.md +60 -20
- package/dist/docs/telemetry-setup.md +157 -46
- package/dist/docs/test-framework-rfc.md +37 -36
- package/dist/docs/testing/assertions.md +92 -4
- package/dist/docs/testing/ci.md +56 -7
- package/dist/docs/testing/cli.md +57 -15
- package/dist/docs/testing/cookbook.md +53 -20
- package/dist/docs/testing/dsl-reference.md +110 -9
- package/dist/docs/testing/fixtures-and-mocks.md +28 -3
- package/dist/docs/testing/flows.md +59 -4
- package/dist/docs/testing/getting-started.md +14 -13
- package/dist/docs/testing/troubleshooting.md +39 -2
- package/dist/docs/timeouts.md +174 -18
- package/dist/docs/troubleshooting.md +176 -6
- package/dist/docs/workflow-creation-guide.md +101 -3
- package/dist/docs/workflows.md +138 -41
- package/dist/examples/README.md +169 -4
- package/dist/examples/ai-custom-tools-simple.yaml +2 -3
- package/dist/examples/cron-webhook-config.yaml +15 -0
- package/dist/examples/forEach-example.yaml +6 -0
- package/dist/examples/git-checkout-basic.yaml +4 -0
- package/dist/examples/git-checkout-compare.yaml +6 -0
- package/dist/examples/git-checkout-cross-repo.yaml +7 -0
- package/dist/examples/http-integration-config.yaml +30 -0
- package/dist/examples/https-server-config.yaml +15 -0
- package/dist/examples/mcp-provider-example.yaml +10 -10
- package/dist/examples/transform-example.yaml +3 -0
- package/dist/examples/webhook-pipeline-config.yaml +18 -0
- package/dist/examples/workflows/workflow-composition-example.yaml +4 -0
- package/dist/frontends/slack-frontend.d.ts +2 -0
- package/dist/frontends/slack-frontend.d.ts.map +1 -1
- package/dist/generated/config-schema.d.ts +11 -7
- package/dist/generated/config-schema.d.ts.map +1 -1
- package/dist/generated/config-schema.json +11 -7
- package/dist/index.js +3127 -974
- package/dist/output/traces/{run-2026-01-28T16-15-24-569Z.ndjson → run-2026-01-31T16-37-22-321Z.ndjson} +84 -84
- package/dist/output/traces/{run-2026-01-28T16-16-09-757Z.ndjson → run-2026-01-31T16-38-06-031Z.ndjson} +1013 -1013
- package/dist/providers/ai-check-provider.d.ts +9 -2
- package/dist/providers/ai-check-provider.d.ts.map +1 -1
- package/dist/providers/command-check-provider.d.ts.map +1 -1
- package/dist/providers/mcp-custom-sse-server.d.ts +17 -1
- package/dist/providers/mcp-custom-sse-server.d.ts.map +1 -1
- package/dist/providers/workflow-check-provider.d.ts.map +1 -1
- package/dist/providers/workflow-tool-executor.d.ts +68 -0
- package/dist/providers/workflow-tool-executor.d.ts.map +1 -0
- package/dist/sdk/{check-provider-registry-AQ3JETBG.mjs → check-provider-registry-3KI5RKXT.mjs} +6 -5
- package/dist/sdk/check-provider-registry-IYILYY35.mjs +28 -0
- package/dist/sdk/chunk-2CPMMNIX.mjs +1459 -0
- package/dist/sdk/chunk-2CPMMNIX.mjs.map +1 -0
- package/dist/sdk/chunk-5LI6T4O3.mjs +3600 -0
- package/dist/sdk/chunk-5LI6T4O3.mjs.map +1 -0
- package/dist/sdk/{chunk-YLQ4UN62.mjs → chunk-A4PGHURG.mjs} +6838 -6257
- package/dist/sdk/chunk-A4PGHURG.mjs.map +1 -0
- package/dist/sdk/chunk-EXFGO4FX.mjs +147 -0
- package/dist/sdk/chunk-EXFGO4FX.mjs.map +1 -0
- package/dist/sdk/chunk-PJ7K5UFC.mjs +17732 -0
- package/dist/sdk/chunk-PJ7K5UFC.mjs.map +1 -0
- package/dist/sdk/{chunk-BHZ4CKUS.mjs → chunk-PXFIALUH.mjs} +77 -8
- package/dist/sdk/chunk-PXFIALUH.mjs.map +1 -0
- package/dist/sdk/{chunk-PVITVJ6J.mjs → chunk-RTKJXNZS.mjs} +32 -9
- package/dist/sdk/chunk-RTKJXNZS.mjs.map +1 -0
- package/dist/sdk/chunk-VW2GBXQT.mjs +606 -0
- package/dist/sdk/chunk-VW2GBXQT.mjs.map +1 -0
- package/dist/sdk/{config-RQQPMLRD.mjs → config-5AUYQFHE.mjs} +2 -2
- package/dist/sdk/config-6CUVEH7H.mjs +16 -0
- package/dist/sdk/config-6CUVEH7H.mjs.map +1 -0
- package/dist/sdk/{github-frontend-6Q4BISZX.mjs → github-frontend-BZ4N3BFZ.mjs} +7 -3
- package/dist/sdk/github-frontend-BZ4N3BFZ.mjs.map +1 -0
- package/dist/sdk/host-4MT3EW2I.mjs +52 -0
- package/dist/sdk/{host-P5NQICP7.mjs → host-NYWXLIFC.mjs} +2 -2
- package/dist/sdk/host-NYWXLIFC.mjs.map +1 -0
- package/dist/sdk/{routing-DEY2AIXM.mjs → routing-6R42GXUO.mjs} +2 -2
- package/dist/sdk/routing-6R42GXUO.mjs.map +1 -0
- package/dist/sdk/routing-7FXPULTO.mjs +24 -0
- package/dist/sdk/routing-7FXPULTO.mjs.map +1 -0
- package/dist/sdk/sdk.d.mts +3 -1
- package/dist/sdk/sdk.d.ts +3 -1
- package/dist/sdk/sdk.js +12163 -11204
- package/dist/sdk/sdk.js.map +1 -1
- package/dist/sdk/sdk.mjs +14 -10
- package/dist/sdk/sdk.mjs.map +1 -1
- package/dist/sdk/slack-frontend-JUT3TYVC.mjs +821 -0
- package/dist/sdk/slack-frontend-JUT3TYVC.mjs.map +1 -0
- package/dist/sdk/workflow-check-provider-H3CUOLUD.mjs +28 -0
- package/dist/sdk/workflow-check-provider-H3CUOLUD.mjs.map +1 -0
- package/dist/sdk/workflow-check-provider-YUNNF4KC.mjs +28 -0
- package/dist/sdk/workflow-check-provider-YUNNF4KC.mjs.map +1 -0
- package/dist/sdk/workflow-registry-KFWSDSLM.mjs +12 -0
- package/dist/sdk/workflow-registry-KFWSDSLM.mjs.map +1 -0
- package/dist/slack/socket-runner.d.ts +2 -0
- package/dist/slack/socket-runner.d.ts.map +1 -1
- package/dist/state-machine/context/workflow-inputs.d.ts +20 -0
- package/dist/state-machine/context/workflow-inputs.d.ts.map +1 -0
- package/dist/state-machine/dispatch/execution-invoker.d.ts.map +1 -1
- package/dist/state-machine/dispatch/foreach-processor.d.ts.map +1 -1
- package/dist/state-machine/dispatch/stats-manager.d.ts.map +1 -1
- package/dist/state-machine/states/level-dispatch.d.ts.map +1 -1
- package/dist/state-machine/states/routing.d.ts +2 -1
- package/dist/state-machine/states/routing.d.ts.map +1 -1
- package/dist/traces/{run-2026-01-28T16-15-24-569Z.ndjson → run-2026-01-31T16-37-22-321Z.ndjson} +84 -84
- package/dist/traces/{run-2026-01-28T16-16-09-757Z.ndjson → run-2026-01-31T16-38-06-031Z.ndjson} +1013 -1013
- package/dist/types/config.d.ts +3 -1
- package/dist/types/config.d.ts.map +1 -1
- package/dist/utils/human-id.d.ts +12 -0
- package/dist/utils/human-id.d.ts.map +1 -0
- package/dist/utils/worktree-manager.d.ts +3 -0
- package/dist/utils/worktree-manager.d.ts.map +1 -1
- package/dist/workflow-executor.d.ts.map +1 -1
- package/dist/workflow-registry.d.ts +1 -0
- package/dist/workflow-registry.d.ts.map +1 -1
- package/package.json +2 -2
- package/dist/sdk/chunk-BHZ4CKUS.mjs.map +0 -1
- package/dist/sdk/chunk-PVITVJ6J.mjs.map +0 -1
- package/dist/sdk/chunk-YLQ4UN62.mjs.map +0 -1
- package/dist/sdk/github-frontend-6Q4BISZX.mjs.map +0 -1
- /package/dist/sdk/{check-provider-registry-AQ3JETBG.mjs.map → check-provider-registry-3KI5RKXT.mjs.map} +0 -0
- /package/dist/sdk/{config-RQQPMLRD.mjs.map → check-provider-registry-IYILYY35.mjs.map} +0 -0
- /package/dist/sdk/{routing-DEY2AIXM.mjs.map → config-5AUYQFHE.mjs.map} +0 -0
- /package/dist/sdk/{host-P5NQICP7.mjs.map → host-4MT3EW2I.mjs.map} +0 -0
|
@@ -0,0 +1,1232 @@
|
|
|
1
|
+
# Visor Architecture
|
|
2
|
+
|
|
3
|
+
This document provides a comprehensive overview of Visor's internal architecture, explaining how the system works at a high level.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [System Overview](#system-overview)
|
|
8
|
+
- [Entry Points](#entry-points)
|
|
9
|
+
- [Core Components](#core-components)
|
|
10
|
+
- [Provider Architecture](#provider-architecture)
|
|
11
|
+
- [State Machine](#state-machine)
|
|
12
|
+
- [Event Sources](#event-sources)
|
|
13
|
+
- [Data Flow](#data-flow)
|
|
14
|
+
- [Extension Points](#extension-points)
|
|
15
|
+
- [Telemetry and Observability](#telemetry-and-observability)
|
|
16
|
+
- [Error Handling](#error-handling)
|
|
17
|
+
- [Security Considerations](#security-considerations)
|
|
18
|
+
- [Memory and State Management](#memory-and-state-management)
|
|
19
|
+
- [Performance Optimization](#performance-optimization)
|
|
20
|
+
- [Related Documentation](#related-documentation)
|
|
21
|
+
- [Appendix A: File Structure](#appendix-a-file-structure)
|
|
22
|
+
- [Appendix B: Configuration Schema Reference](#appendix-b-configuration-schema-reference)
|
|
23
|
+
- [Appendix C: Event Types](#appendix-c-event-types)
|
|
24
|
+
- [Appendix D: Glossary](#appendix-d-glossary)
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## System Overview
|
|
29
|
+
|
|
30
|
+
Visor is an AI-powered workflow orchestration tool that can run as a GitHub Action, CLI tool, or Slack bot. The system uses a state machine-based execution engine to orchestrate checks (steps) with sophisticated dependency resolution, routing, and error handling.
|
|
31
|
+
|
|
32
|
+
### High-Level Architecture Diagram
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
+------------------+
|
|
36
|
+
| Entry Points |
|
|
37
|
+
+------------------+
|
|
38
|
+
| |
|
|
39
|
+
+-------------+---+----------+---+-------------+
|
|
40
|
+
| | | |
|
|
41
|
+
v v v v
|
|
42
|
+
+------+------+ +------+------+ +---+---+ +-----+-----+
|
|
43
|
+
| GitHub | | CLI | | Slack | | HTTP |
|
|
44
|
+
| Action | | (cli-main) | | Socket| | Webhook |
|
|
45
|
+
| (index.ts) | | | | Mode | | Server |
|
|
46
|
+
+------+------+ +------+------+ +---+---+ +-----+-----+
|
|
47
|
+
| | | |
|
|
48
|
+
+--------+--------+--------------+-------------+
|
|
49
|
+
|
|
|
50
|
+
v
|
|
51
|
+
+----------+-----------+
|
|
52
|
+
| Configuration |
|
|
53
|
+
| Manager |
|
|
54
|
+
| (config.ts) |
|
|
55
|
+
+----------+-----------+
|
|
56
|
+
|
|
|
57
|
+
v
|
|
58
|
+
+----------+-----------+
|
|
59
|
+
| State Machine |
|
|
60
|
+
| Execution Engine |
|
|
61
|
+
+----------+-----------+
|
|
62
|
+
|
|
|
63
|
+
+--------------------+--------------------+
|
|
64
|
+
| | |
|
|
65
|
+
v v v
|
|
66
|
+
+------+------+ +------+------+ +------+------+
|
|
67
|
+
| Dependency | | Journal | | Provider |
|
|
68
|
+
| Resolver | | (Snapshot | | Registry |
|
|
69
|
+
| | | Store) | | |
|
|
70
|
+
+-------------+ +-------------+ +------+------+
|
|
71
|
+
|
|
|
72
|
+
+----------------+------------------------+
|
|
73
|
+
| | | |
|
|
74
|
+
v v v v
|
|
75
|
+
+------+------+ +------+------+ +-----+----+ +--+--+
|
|
76
|
+
| AI Provider | | Command | | HTTP | | ... |
|
|
77
|
+
| | | Provider | | Provider | | |
|
|
78
|
+
+-------------+ +-------------+ +----------+ +-----+
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Key Components
|
|
82
|
+
|
|
83
|
+
| Component | Description |
|
|
84
|
+
|-----------|-------------|
|
|
85
|
+
| **Entry Points** | GitHub Action, CLI, Slack Socket Mode, HTTP webhooks |
|
|
86
|
+
| **Configuration Manager** | Loads and validates YAML configuration |
|
|
87
|
+
| **State Machine Engine** | Orchestrates check execution with wave-based scheduling |
|
|
88
|
+
| **Provider Registry** | Registry of pluggable check providers |
|
|
89
|
+
| **Journal** | Execution state and output history |
|
|
90
|
+
| **Dependency Resolver** | Builds execution graph from check dependencies |
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Entry Points
|
|
95
|
+
|
|
96
|
+
Visor supports multiple entry points for different integration scenarios.
|
|
97
|
+
|
|
98
|
+
### GitHub Action Entry (`src/index.ts`)
|
|
99
|
+
|
|
100
|
+
The primary entry point for GitHub Actions:
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
// Simplified flow
|
|
104
|
+
async function run(): Promise<void> {
|
|
105
|
+
const { octokit, authType } = await createAuthenticatedOctokit();
|
|
106
|
+
const configManager = new ConfigManager();
|
|
107
|
+
const config = await configManager.loadConfig(configPath);
|
|
108
|
+
await handleEvent(octokit, inputs, eventName, context, config);
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Responsibilities:**
|
|
113
|
+
- Authenticate with GitHub (token or App)
|
|
114
|
+
- Load GitHub event context from `GITHUB_EVENT_PATH`
|
|
115
|
+
- Map GitHub events to Visor event triggers
|
|
116
|
+
- Execute checks via the state machine engine
|
|
117
|
+
- Post results as PR comments
|
|
118
|
+
|
|
119
|
+
**Supported GitHub Events:**
|
|
120
|
+
- `pull_request` (opened, synchronize, edited)
|
|
121
|
+
- `issue_comment` (command-driven reviews)
|
|
122
|
+
- `issues` (issue assistant workflows)
|
|
123
|
+
- `push` (associated PR detection)
|
|
124
|
+
|
|
125
|
+
### CLI Entry (`src/cli-main.ts`)
|
|
126
|
+
|
|
127
|
+
The command-line interface for local development and CI:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
visor --config .visor.yaml --checks security,performance
|
|
131
|
+
visor review # Built-in code review workflow
|
|
132
|
+
visor test tests/ # Run YAML test suites
|
|
133
|
+
visor validate # Validate configuration
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**CLI Features:**
|
|
137
|
+
- Configuration validation and loading
|
|
138
|
+
- Check filtering by name or tags
|
|
139
|
+
- Multiple output formats (table, json, markdown, sarif)
|
|
140
|
+
- Debug visualizer integration
|
|
141
|
+
- TUI (Terminal User Interface) mode
|
|
142
|
+
- Telemetry/tracing support
|
|
143
|
+
|
|
144
|
+
### Slack Socket Mode
|
|
145
|
+
|
|
146
|
+
For Slack bot integration:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
visor --slack --config .visor.yaml
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Uses Slack's Socket Mode for real-time event handling without requiring a public webhook endpoint.
|
|
153
|
+
|
|
154
|
+
### HTTP Webhook Server
|
|
155
|
+
|
|
156
|
+
For receiving external webhooks:
|
|
157
|
+
|
|
158
|
+
```yaml
|
|
159
|
+
http_server:
|
|
160
|
+
enabled: true
|
|
161
|
+
port: 8080
|
|
162
|
+
endpoints:
|
|
163
|
+
- path: /webhook
|
|
164
|
+
transform: "{{ request.body | json }}"
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Core Components
|
|
170
|
+
|
|
171
|
+
### Configuration Loading (`src/config.ts`)
|
|
172
|
+
|
|
173
|
+
The `ConfigManager` handles all configuration operations:
|
|
174
|
+
|
|
175
|
+
```
|
|
176
|
+
+------------------+
|
|
177
|
+
| ConfigManager |
|
|
178
|
+
+------------------+
|
|
179
|
+
| - loadConfig() | <-- Load from file path
|
|
180
|
+
| - findAndLoad() | <-- Auto-discover .visor.yaml
|
|
181
|
+
| - validateConfig | <-- Schema validation (Ajv)
|
|
182
|
+
| - mergeDefaults | <-- Apply default values
|
|
183
|
+
+------------------+
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
**Configuration Resolution Order:**
|
|
187
|
+
1. CLI `--config` parameter
|
|
188
|
+
2. `visor.yaml` or `.visor.yaml` in project root
|
|
189
|
+
3. `visor.yaml` or `.visor.yaml` in git repository root
|
|
190
|
+
4. Bundled default configuration
|
|
191
|
+
|
|
192
|
+
**Key Features:**
|
|
193
|
+
- YAML parsing with `js-yaml`
|
|
194
|
+
- Schema validation with Ajv
|
|
195
|
+
- Configuration inheritance via `extends`/`include`
|
|
196
|
+
- Environment variable interpolation
|
|
197
|
+
- Remote configuration loading (HTTP/HTTPS)
|
|
198
|
+
|
|
199
|
+
### Check Execution Engine (`src/state-machine-execution-engine.ts`)
|
|
200
|
+
|
|
201
|
+
The main orchestration layer that coordinates check execution:
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
class StateMachineExecutionEngine {
|
|
205
|
+
async executeGroupedChecks(
|
|
206
|
+
prInfo: PRInfo,
|
|
207
|
+
checks: string[],
|
|
208
|
+
timeout?: number,
|
|
209
|
+
config?: VisorConfig,
|
|
210
|
+
// ... additional options
|
|
211
|
+
): Promise<ExecutionResult>
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**Responsibilities:**
|
|
216
|
+
- Build engine context from configuration
|
|
217
|
+
- Initialize workspace isolation (if enabled)
|
|
218
|
+
- Create and run the state machine runner
|
|
219
|
+
- Manage frontends (GitHub, Slack integration)
|
|
220
|
+
- Aggregate results into `ExecutionResult`
|
|
221
|
+
|
|
222
|
+
### Provider System
|
|
223
|
+
|
|
224
|
+
Providers are pluggable components that implement specific check types:
|
|
225
|
+
|
|
226
|
+
```
|
|
227
|
+
+-------------------+
|
|
228
|
+
| CheckProvider | <-- Abstract base class
|
|
229
|
+
+-------------------+
|
|
230
|
+
| + getName() |
|
|
231
|
+
| + execute() |
|
|
232
|
+
| + validateConfig()|
|
|
233
|
+
| + isAvailable() |
|
|
234
|
+
+-------------------+
|
|
235
|
+
^
|
|
236
|
+
|
|
|
237
|
+
+----+----+----+----+----+
|
|
238
|
+
| | | | | |
|
|
239
|
+
AI Cmd Script HTTP MCP ...
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
See [Provider Architecture](#provider-architecture) for details.
|
|
243
|
+
|
|
244
|
+
### Routing and Flow Control
|
|
245
|
+
|
|
246
|
+
Checks can define routing rules for success, failure, and completion:
|
|
247
|
+
|
|
248
|
+
```yaml
|
|
249
|
+
checks:
|
|
250
|
+
my-check:
|
|
251
|
+
type: ai
|
|
252
|
+
prompt: "..."
|
|
253
|
+
on_fail:
|
|
254
|
+
retry:
|
|
255
|
+
max: 3
|
|
256
|
+
backoff:
|
|
257
|
+
mode: exponential
|
|
258
|
+
delay_ms: 1000
|
|
259
|
+
run: [remediation-step]
|
|
260
|
+
goto: previous-step
|
|
261
|
+
on_success:
|
|
262
|
+
run: [post-process]
|
|
263
|
+
on_finish: # For forEach checks
|
|
264
|
+
run: [aggregation-step]
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## Provider Architecture
|
|
270
|
+
|
|
271
|
+
### Base Provider Interface
|
|
272
|
+
|
|
273
|
+
All providers implement the `CheckProvider` abstract class:
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
abstract class CheckProvider {
|
|
277
|
+
abstract getName(): string;
|
|
278
|
+
abstract getDescription(): string;
|
|
279
|
+
abstract validateConfig(config: unknown): Promise<boolean>;
|
|
280
|
+
abstract execute(
|
|
281
|
+
prInfo: PRInfo,
|
|
282
|
+
config: CheckProviderConfig,
|
|
283
|
+
dependencyResults?: Map<string, ReviewSummary>,
|
|
284
|
+
context?: ExecutionContext
|
|
285
|
+
): Promise<ReviewSummary>;
|
|
286
|
+
abstract getSupportedConfigKeys(): string[];
|
|
287
|
+
abstract isAvailable(): Promise<boolean>;
|
|
288
|
+
abstract getRequirements(): string[];
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### Provider Registry
|
|
293
|
+
|
|
294
|
+
The singleton `CheckProviderRegistry` manages provider registration:
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
class CheckProviderRegistry {
|
|
298
|
+
static getInstance(): CheckProviderRegistry;
|
|
299
|
+
register(provider: CheckProvider): void;
|
|
300
|
+
getProvider(name: string): CheckProvider | undefined;
|
|
301
|
+
getAvailableProviders(): string[];
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
**Registration Flow:**
|
|
306
|
+
1. Registry instantiates on first access
|
|
307
|
+
2. Default providers registered automatically
|
|
308
|
+
3. Custom providers can be registered programmatically
|
|
309
|
+
|
|
310
|
+
### Built-in Providers
|
|
311
|
+
|
|
312
|
+
| Provider | Type | Description |
|
|
313
|
+
|----------|------|-------------|
|
|
314
|
+
| `ai` | AI-powered | Uses Gemini, Claude, OpenAI, or Bedrock for analysis |
|
|
315
|
+
| `command` | Command | Executes shell commands |
|
|
316
|
+
| `script` | Script | Executes JavaScript in a sandbox |
|
|
317
|
+
| `http` | HTTP Output | Sends results to webhooks |
|
|
318
|
+
| `http_input` | HTTP Input | Receives webhook data |
|
|
319
|
+
| `http_client` | HTTP Client | Makes HTTP requests |
|
|
320
|
+
| `mcp` | MCP | Connects to MCP tool servers |
|
|
321
|
+
| `claude-code` | Claude Code | Uses Claude Code SDK with MCP tools |
|
|
322
|
+
| `memory` | Memory | Persistent key-value storage |
|
|
323
|
+
| `log` | Logger | Debug logging output |
|
|
324
|
+
| `noop` | No-op | Placeholder for orchestration |
|
|
325
|
+
| `human-input` | Human Input | Collects user input interactively |
|
|
326
|
+
| `workflow` | Workflow | Invokes nested workflows |
|
|
327
|
+
| `git-checkout` | Git Checkout | Checks out git references |
|
|
328
|
+
| `github` | GitHub Ops | GitHub API operations (labels, comments) |
|
|
329
|
+
|
|
330
|
+
### Provider Lifecycle
|
|
331
|
+
|
|
332
|
+
```
|
|
333
|
+
+-------------+ +-------------+ +-------------+
|
|
334
|
+
| Instantiate | --> | Validate | --> | Execute |
|
|
335
|
+
| Provider | | Config | | Check |
|
|
336
|
+
+-------------+ +-------------+ +-------------+
|
|
337
|
+
| |
|
|
338
|
+
v v
|
|
339
|
+
Config Error? +-----+-----+
|
|
340
|
+
| | Result |
|
|
341
|
+
v | (Summary) |
|
|
342
|
+
Skip Check +-----------+
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
## State Machine
|
|
348
|
+
|
|
349
|
+
Visor uses a state machine-based execution engine for orchestrating checks. This provides deterministic execution, observability, and support for complex workflows.
|
|
350
|
+
|
|
351
|
+
### State Machine States
|
|
352
|
+
|
|
353
|
+
```
|
|
354
|
+
+-------+ +-----------+ +-------------+
|
|
355
|
+
| Init | --> | PlanReady | --> | WavePlanning|
|
|
356
|
+
+-------+ +-----------+ +-------------+
|
|
357
|
+
|
|
|
358
|
+
v
|
|
359
|
+
+-------------+ +---------------+ |
|
|
360
|
+
| Completed | <-- | LevelDispatch | <+
|
|
361
|
+
+-------------+ +---------------+
|
|
362
|
+
^ |
|
|
363
|
+
| v
|
|
364
|
+
+-------+ +-------------+
|
|
365
|
+
| Error | | CheckRunning|
|
|
366
|
+
+-------+ +-------------+
|
|
367
|
+
|
|
|
368
|
+
v
|
|
369
|
+
+-------------+
|
|
370
|
+
| Routing |
|
|
371
|
+
+-------------+
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### State Descriptions
|
|
375
|
+
|
|
376
|
+
| State | Description |
|
|
377
|
+
|-------|-------------|
|
|
378
|
+
| **Init** | Initialize context, load configuration, build dependency graph |
|
|
379
|
+
| **PlanReady** | Execution plan is ready, dependency graph built |
|
|
380
|
+
| **WavePlanning** | Plan the next execution wave based on dependency levels |
|
|
381
|
+
| **LevelDispatch** | Dispatch checks at the current dependency level |
|
|
382
|
+
| **CheckRunning** | Execute dispatched checks (parallel execution) |
|
|
383
|
+
| **Routing** | Evaluate routing rules (on_success, on_fail, on_finish) |
|
|
384
|
+
| **Completed** | All checks completed, aggregate results |
|
|
385
|
+
| **Error** | Fatal error occurred, cleanup and exit |
|
|
386
|
+
|
|
387
|
+
### Wave-Based Execution
|
|
388
|
+
|
|
389
|
+
Checks are executed in waves based on their dependency levels:
|
|
390
|
+
|
|
391
|
+
```
|
|
392
|
+
Wave 0: [check-a, check-b] # No dependencies
|
|
393
|
+
|
|
|
394
|
+
v
|
|
395
|
+
Wave 1: [check-c] # depends_on: [check-a]
|
|
396
|
+
|
|
|
397
|
+
v
|
|
398
|
+
Wave 2: [check-d, check-e] # depends_on: [check-c]
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
**Wave Planning Rules:**
|
|
402
|
+
1. Checks with no unmet dependencies are scheduled
|
|
403
|
+
2. Checks run in parallel within a wave (respecting `max_parallelism`)
|
|
404
|
+
3. A new wave starts when all checks in the current wave complete
|
|
405
|
+
4. Routing can trigger additional waves (on_fail.goto, on_success.run)
|
|
406
|
+
|
|
407
|
+
### Dependency Resolution
|
|
408
|
+
|
|
409
|
+
The `DependencyResolver` builds an execution graph:
|
|
410
|
+
|
|
411
|
+
```typescript
|
|
412
|
+
interface DependencyGraph {
|
|
413
|
+
levels: ExecutionGroup[]; // Checks grouped by dependency level
|
|
414
|
+
checkDeps: Map<string, string[]>; // Check -> its dependencies
|
|
415
|
+
checkDependents: Map<string, string[]>; // Check -> checks that depend on it
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
**Dependency Features:**
|
|
420
|
+
- Linear dependencies: `depends_on: [step-a]`
|
|
421
|
+
- OR dependencies: `depends_on: "step-a | step-b"` (either can satisfy)
|
|
422
|
+
- Cycle detection and error reporting
|
|
423
|
+
- Skip propagation (failed dependencies skip dependents)
|
|
424
|
+
|
|
425
|
+
### State Management
|
|
426
|
+
|
|
427
|
+
Runtime state is tracked in `RunState`:
|
|
428
|
+
|
|
429
|
+
```typescript
|
|
430
|
+
interface RunState {
|
|
431
|
+
currentState: EngineState;
|
|
432
|
+
wave: number;
|
|
433
|
+
levelQueue: ExecutionGroup[];
|
|
434
|
+
eventQueue: EngineEvent[];
|
|
435
|
+
activeDispatches: Map<string, DispatchRecord>;
|
|
436
|
+
completedChecks: Set<string>;
|
|
437
|
+
stats: Map<string, CheckExecutionStats>;
|
|
438
|
+
historyLog: EngineEvent[];
|
|
439
|
+
// ... additional tracking fields
|
|
440
|
+
}
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
### Output History
|
|
444
|
+
|
|
445
|
+
The `ExecutionJournal` stores all check outputs for cross-check access:
|
|
446
|
+
|
|
447
|
+
```typescript
|
|
448
|
+
// In Liquid templates
|
|
449
|
+
{{ outputs["previous-check"].result }}
|
|
450
|
+
{{ outputs["data-fetch"] | json }}
|
|
451
|
+
|
|
452
|
+
// In JavaScript expressions (fail_if, transform_js)
|
|
453
|
+
outputs["check-name"].issues.length > 0
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
---
|
|
457
|
+
|
|
458
|
+
## Event Sources
|
|
459
|
+
|
|
460
|
+
### GitHub Webhook Integration
|
|
461
|
+
|
|
462
|
+
GitHub events are processed by the action entry point:
|
|
463
|
+
|
|
464
|
+
```
|
|
465
|
+
GitHub Event --> GITHUB_EVENT_PATH --> Event Parser --> Event Trigger
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
**Event Mapping:**
|
|
469
|
+
| GitHub Event | Action | Visor Trigger |
|
|
470
|
+
|--------------|--------|---------------|
|
|
471
|
+
| `pull_request` | `opened` | `pr_opened` |
|
|
472
|
+
| `pull_request` | `synchronize` | `pr_updated` |
|
|
473
|
+
| `issue_comment` | `created` | `issue_comment` |
|
|
474
|
+
| `issues` | `opened` | `issue_opened` |
|
|
475
|
+
|
|
476
|
+
### Slack Socket Mode
|
|
477
|
+
|
|
478
|
+
Real-time Slack integration without public webhooks:
|
|
479
|
+
|
|
480
|
+
```typescript
|
|
481
|
+
class SlackSocketRunner {
|
|
482
|
+
async start(): Promise<void> {
|
|
483
|
+
// Connect to Slack Socket Mode
|
|
484
|
+
// Handle app_mention and message events
|
|
485
|
+
// Route to engine execution
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
**Event Flow:**
|
|
491
|
+
1. User mentions bot or sends message
|
|
492
|
+
2. Slack Socket Mode delivers event
|
|
493
|
+
3. Visor extracts thread context
|
|
494
|
+
4. Engine executes configured checks
|
|
495
|
+
5. Results posted back to thread
|
|
496
|
+
|
|
497
|
+
### HTTP Input Provider
|
|
498
|
+
|
|
499
|
+
For custom webhook sources:
|
|
500
|
+
|
|
501
|
+
```yaml
|
|
502
|
+
checks:
|
|
503
|
+
webhook-handler:
|
|
504
|
+
type: http_input
|
|
505
|
+
endpoint: /api/webhook
|
|
506
|
+
transform: |
|
|
507
|
+
{% assign data = request.body | json %}
|
|
508
|
+
{{ data.message }}
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
---
|
|
512
|
+
|
|
513
|
+
## Data Flow
|
|
514
|
+
|
|
515
|
+
### PR Review Request Flow
|
|
516
|
+
|
|
517
|
+
```
|
|
518
|
+
1. GitHub PR Event
|
|
519
|
+
|
|
|
520
|
+
v
|
|
521
|
+
2. Load Configuration
|
|
522
|
+
|
|
|
523
|
+
v
|
|
524
|
+
3. Build Dependency Graph
|
|
525
|
+
|
|
|
526
|
+
v
|
|
527
|
+
4. Wave Planning (Level 0)
|
|
528
|
+
|
|
|
529
|
+
v
|
|
530
|
+
5. Dispatch Checks -----> [AI Provider]
|
|
531
|
+
| |
|
|
532
|
+
| v
|
|
533
|
+
| 6. AI Analysis
|
|
534
|
+
| |
|
|
535
|
+
v v
|
|
536
|
+
7. Collect Results <----- [ReviewSummary]
|
|
537
|
+
|
|
|
538
|
+
v
|
|
539
|
+
8. Route (on_success/on_fail)
|
|
540
|
+
|
|
|
541
|
+
v
|
|
542
|
+
9. Next Wave or Complete
|
|
543
|
+
|
|
|
544
|
+
v
|
|
545
|
+
10. Aggregate Results
|
|
546
|
+
|
|
|
547
|
+
v
|
|
548
|
+
11. Post PR Comment
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
### Output Propagation Between Checks
|
|
552
|
+
|
|
553
|
+
```yaml
|
|
554
|
+
checks:
|
|
555
|
+
fetch-data:
|
|
556
|
+
type: command
|
|
557
|
+
exec: "curl -s https://api.example.com/data"
|
|
558
|
+
|
|
559
|
+
analyze:
|
|
560
|
+
type: ai
|
|
561
|
+
depends_on: [fetch-data]
|
|
562
|
+
prompt: |
|
|
563
|
+
Analyze this data:
|
|
564
|
+
{{ outputs["fetch-data"] | json }}
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
**Output Access:**
|
|
568
|
+
- Liquid templates: `{{ outputs["check-name"] }}`
|
|
569
|
+
- JavaScript: `outputs["check-name"]`
|
|
570
|
+
- Transform: `transform_js: "return outputs['fetch-data'].items"`
|
|
571
|
+
|
|
572
|
+
### Template Rendering with Liquid
|
|
573
|
+
|
|
574
|
+
Visor uses Liquid templates extensively:
|
|
575
|
+
|
|
576
|
+
```yaml
|
|
577
|
+
checks:
|
|
578
|
+
example:
|
|
579
|
+
type: ai
|
|
580
|
+
prompt: |
|
|
581
|
+
Review this PR:
|
|
582
|
+
Title: {{ pr.title }}
|
|
583
|
+
Author: {{ pr.author }}
|
|
584
|
+
Files changed: {{ pr.files | size }}
|
|
585
|
+
|
|
586
|
+
{% for file in pr.files %}
|
|
587
|
+
- {{ file.filename }} (+{{ file.additions }}/-{{ file.deletions }})
|
|
588
|
+
{% endfor %}
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
**Available Context:**
|
|
592
|
+
| Variable | Description |
|
|
593
|
+
|----------|-------------|
|
|
594
|
+
| `pr` | PR information (title, body, author, files) |
|
|
595
|
+
| `outputs` | Previous check outputs |
|
|
596
|
+
| `env` | Environment variables |
|
|
597
|
+
| `event` | GitHub event context |
|
|
598
|
+
| `memory` | Memory store accessor |
|
|
599
|
+
|
|
600
|
+
---
|
|
601
|
+
|
|
602
|
+
## Extension Points
|
|
603
|
+
|
|
604
|
+
### Adding New Providers
|
|
605
|
+
|
|
606
|
+
Create a new provider by extending `CheckProvider`:
|
|
607
|
+
|
|
608
|
+
```typescript
|
|
609
|
+
import { CheckProvider, CheckProviderConfig } from './check-provider.interface';
|
|
610
|
+
|
|
611
|
+
export class CustomProvider extends CheckProvider {
|
|
612
|
+
getName(): string { return 'custom'; }
|
|
613
|
+
getDescription(): string { return 'My custom provider'; }
|
|
614
|
+
|
|
615
|
+
async execute(
|
|
616
|
+
prInfo: PRInfo,
|
|
617
|
+
config: CheckProviderConfig,
|
|
618
|
+
dependencyResults?: Map<string, ReviewSummary>,
|
|
619
|
+
context?: ExecutionContext
|
|
620
|
+
): Promise<ReviewSummary> {
|
|
621
|
+
// Implementation
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// ... other required methods
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
// Register
|
|
628
|
+
registry.register(new CustomProvider());
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
### Custom Tools
|
|
632
|
+
|
|
633
|
+
Define tools in configuration for use with MCP/AI checks:
|
|
634
|
+
|
|
635
|
+
```yaml
|
|
636
|
+
tools:
|
|
637
|
+
search-docs:
|
|
638
|
+
description: "Search documentation"
|
|
639
|
+
inputSchema:
|
|
640
|
+
type: object
|
|
641
|
+
properties:
|
|
642
|
+
query: { type: string }
|
|
643
|
+
required: [query]
|
|
644
|
+
exec: "grep -r '{{ query }}' docs/"
|
|
645
|
+
transform_js: |
|
|
646
|
+
return { results: output.split('\n').filter(Boolean) }
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
Tools are exposed to AI providers via an ephemeral MCP server.
|
|
650
|
+
|
|
651
|
+
### MCP Integration
|
|
652
|
+
|
|
653
|
+
Connect to external MCP servers:
|
|
654
|
+
|
|
655
|
+
```yaml
|
|
656
|
+
checks:
|
|
657
|
+
code-analysis:
|
|
658
|
+
type: ai
|
|
659
|
+
ai:
|
|
660
|
+
mcpServers:
|
|
661
|
+
probe:
|
|
662
|
+
command: "npx"
|
|
663
|
+
args: ["-y", "@anthropic/probe-mcp"]
|
|
664
|
+
prompt: "Analyze the codebase"
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
Or use the MCP provider directly:
|
|
668
|
+
|
|
669
|
+
```yaml
|
|
670
|
+
checks:
|
|
671
|
+
direct-mcp:
|
|
672
|
+
type: mcp
|
|
673
|
+
transport: stdio
|
|
674
|
+
exec: "npx @my-org/tool"
|
|
675
|
+
method: analyze
|
|
676
|
+
methodArgs:
|
|
677
|
+
path: "{{ pr.files[0].filename }}"
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
### Workflow Composition
|
|
681
|
+
|
|
682
|
+
Create reusable workflows:
|
|
683
|
+
|
|
684
|
+
```yaml
|
|
685
|
+
# workflows/security-scan.yaml
|
|
686
|
+
id: security-scan
|
|
687
|
+
inputs:
|
|
688
|
+
- name: severity_threshold
|
|
689
|
+
default: warning
|
|
690
|
+
steps:
|
|
691
|
+
scan:
|
|
692
|
+
type: ai
|
|
693
|
+
prompt: "Scan for security issues..."
|
|
694
|
+
outputs:
|
|
695
|
+
- name: issues
|
|
696
|
+
value: "{{ outputs['scan'].issues }}"
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
Use in main configuration:
|
|
700
|
+
|
|
701
|
+
```yaml
|
|
702
|
+
checks:
|
|
703
|
+
security:
|
|
704
|
+
type: workflow
|
|
705
|
+
workflow: workflows/security-scan.yaml
|
|
706
|
+
args:
|
|
707
|
+
severity_threshold: error
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
### Frontend Integration
|
|
711
|
+
|
|
712
|
+
Create event-driven integrations:
|
|
713
|
+
|
|
714
|
+
```typescript
|
|
715
|
+
class CustomFrontend implements Frontend {
|
|
716
|
+
async start(ctx: FrontendContext): Promise<void> {
|
|
717
|
+
ctx.eventBus.on('CheckCompleted', async (event) => {
|
|
718
|
+
// Handle check completion
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
Frontends receive events via the event bus:
|
|
725
|
+
- `CheckScheduled`
|
|
726
|
+
- `CheckCompleted`
|
|
727
|
+
- `CheckErrored`
|
|
728
|
+
- `HumanInputRequested`
|
|
729
|
+
- `StateTransition`
|
|
730
|
+
|
|
731
|
+
---
|
|
732
|
+
|
|
733
|
+
## Telemetry and Observability
|
|
734
|
+
|
|
735
|
+
Visor includes comprehensive telemetry support for debugging, monitoring, and performance analysis.
|
|
736
|
+
|
|
737
|
+
### OpenTelemetry Integration
|
|
738
|
+
|
|
739
|
+
Visor supports OpenTelemetry (OTEL) for distributed tracing:
|
|
740
|
+
|
|
741
|
+
```yaml
|
|
742
|
+
# visor.yaml
|
|
743
|
+
telemetry:
|
|
744
|
+
enabled: true
|
|
745
|
+
sink: otlp # or 'file', 'console'
|
|
746
|
+
file:
|
|
747
|
+
dir: ./output/traces
|
|
748
|
+
ndjson: true
|
|
749
|
+
tracing:
|
|
750
|
+
auto_instrumentations: true
|
|
751
|
+
trace_report:
|
|
752
|
+
enabled: true
|
|
753
|
+
```
|
|
754
|
+
|
|
755
|
+
**Environment Variables:**
|
|
756
|
+
| Variable | Description |
|
|
757
|
+
|----------|-------------|
|
|
758
|
+
| `VISOR_TELEMETRY_ENABLED` | Enable telemetry (`true`/`false`) |
|
|
759
|
+
| `VISOR_TELEMETRY_SINK` | Sink type: `otlp`, `file`, `console` |
|
|
760
|
+
| `VISOR_TRACE_DIR` | Directory for trace files |
|
|
761
|
+
| `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | OTLP endpoint (e.g., `http://localhost:4318/v1/traces`) |
|
|
762
|
+
|
|
763
|
+
### Trace Structure
|
|
764
|
+
|
|
765
|
+
```
|
|
766
|
+
visor.run (root span)
|
|
767
|
+
|
|
|
768
|
+
+-- engine.state.init
|
|
769
|
+
| +-- dependency_resolver.build_graph
|
|
770
|
+
|
|
|
771
|
+
+-- engine.state.wave_planning
|
|
772
|
+
| +-- wave: 0, wave_kind: initial
|
|
773
|
+
|
|
|
774
|
+
+-- engine.state.level_dispatch
|
|
775
|
+
| +-- level_size: 3, level_checks_preview: [check-a, check-b, check-c]
|
|
776
|
+
|
|
|
777
|
+
+-- visor.check.check-a
|
|
778
|
+
| +-- visor.check.id: check-a
|
|
779
|
+
| +-- visor.check.type: ai
|
|
780
|
+
|
|
|
781
|
+
+-- visor.routing (events)
|
|
782
|
+
+-- trigger: on_success, action: run, target: next-check
|
|
783
|
+
```
|
|
784
|
+
|
|
785
|
+
### Debug Visualizer
|
|
786
|
+
|
|
787
|
+
Start the debug visualizer for interactive debugging:
|
|
788
|
+
|
|
789
|
+
```bash
|
|
790
|
+
visor --debug-server --debug-port 3456 --config .visor.yaml
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+
The visualizer provides:
|
|
794
|
+
- Real-time span streaming
|
|
795
|
+
- State machine visualization
|
|
796
|
+
- Execution timeline
|
|
797
|
+
- Pause/resume/step controls
|
|
798
|
+
|
|
799
|
+
---
|
|
800
|
+
|
|
801
|
+
## Error Handling
|
|
802
|
+
|
|
803
|
+
Visor implements comprehensive error handling at multiple levels.
|
|
804
|
+
|
|
805
|
+
### Provider-Level Error Handling
|
|
806
|
+
|
|
807
|
+
Each provider handles errors and returns them in the `ReviewSummary`:
|
|
808
|
+
|
|
809
|
+
```typescript
|
|
810
|
+
try {
|
|
811
|
+
const result = await provider.execute(prInfo, config, deps, context);
|
|
812
|
+
return result;
|
|
813
|
+
} catch (error) {
|
|
814
|
+
return {
|
|
815
|
+
issues: [{
|
|
816
|
+
severity: 'error',
|
|
817
|
+
ruleId: `${checkName}/execution-error`,
|
|
818
|
+
message: error.message,
|
|
819
|
+
file: '',
|
|
820
|
+
line: 0
|
|
821
|
+
}]
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
```
|
|
825
|
+
|
|
826
|
+
### Routing-Based Error Recovery
|
|
827
|
+
|
|
828
|
+
Checks can define error recovery strategies:
|
|
829
|
+
|
|
830
|
+
```yaml
|
|
831
|
+
checks:
|
|
832
|
+
risky-operation:
|
|
833
|
+
type: command
|
|
834
|
+
exec: "./risky-script.sh"
|
|
835
|
+
on_fail:
|
|
836
|
+
retry:
|
|
837
|
+
max: 3
|
|
838
|
+
backoff:
|
|
839
|
+
mode: exponential
|
|
840
|
+
delay_ms: 1000
|
|
841
|
+
max_delay_ms: 30000
|
|
842
|
+
run: [cleanup-step]
|
|
843
|
+
goto: safe-fallback
|
|
844
|
+
|
|
845
|
+
cleanup-step:
|
|
846
|
+
type: command
|
|
847
|
+
exec: "./cleanup.sh"
|
|
848
|
+
|
|
849
|
+
safe-fallback:
|
|
850
|
+
type: noop
|
|
851
|
+
message: "Using fallback due to failure"
|
|
852
|
+
```
|
|
853
|
+
|
|
854
|
+
### Fail-Fast Mode
|
|
855
|
+
|
|
856
|
+
Enable fail-fast to stop execution on first critical error:
|
|
857
|
+
|
|
858
|
+
```yaml
|
|
859
|
+
# visor.yaml
|
|
860
|
+
fail_fast: true
|
|
861
|
+
|
|
862
|
+
# Or via CLI
|
|
863
|
+
visor --fail-fast --config .visor.yaml
|
|
864
|
+
```
|
|
865
|
+
|
|
866
|
+
### Error Propagation
|
|
867
|
+
|
|
868
|
+
Errors propagate through the dependency graph:
|
|
869
|
+
|
|
870
|
+
```
|
|
871
|
+
check-a (fails)
|
|
872
|
+
|
|
|
873
|
+
v
|
|
874
|
+
check-b (depends_on: [check-a]) --> SKIPPED
|
|
875
|
+
|
|
|
876
|
+
v
|
|
877
|
+
check-c (depends_on: [check-b]) --> SKIPPED
|
|
878
|
+
```
|
|
879
|
+
|
|
880
|
+
Skipped checks are marked with `severity: info` and `ruleId: checkName/__skipped`.
|
|
881
|
+
|
|
882
|
+
---
|
|
883
|
+
|
|
884
|
+
## Security Considerations
|
|
885
|
+
|
|
886
|
+
### Workspace Isolation
|
|
887
|
+
|
|
888
|
+
For untrusted code execution, enable workspace isolation:
|
|
889
|
+
|
|
890
|
+
```yaml
|
|
891
|
+
# visor.yaml
|
|
892
|
+
workspace:
|
|
893
|
+
enabled: true
|
|
894
|
+
base_path: /tmp/visor-workspaces
|
|
895
|
+
cleanup: true # Remove after execution
|
|
896
|
+
```
|
|
897
|
+
|
|
898
|
+
This creates isolated directories for each execution, preventing cross-contamination.
|
|
899
|
+
|
|
900
|
+
### Environment Variable Handling
|
|
901
|
+
|
|
902
|
+
Sensitive values can be passed via environment:
|
|
903
|
+
|
|
904
|
+
```yaml
|
|
905
|
+
checks:
|
|
906
|
+
api-call:
|
|
907
|
+
type: http_client
|
|
908
|
+
url: "https://api.example.com"
|
|
909
|
+
headers:
|
|
910
|
+
Authorization: "Bearer {{ env.API_TOKEN }}"
|
|
911
|
+
```
|
|
912
|
+
|
|
913
|
+
**Security practices:**
|
|
914
|
+
- Never commit secrets to configuration files
|
|
915
|
+
- Use GitHub Secrets or environment variables
|
|
916
|
+
- Visor does not log environment variable values
|
|
917
|
+
|
|
918
|
+
### Command Execution Security
|
|
919
|
+
|
|
920
|
+
The `command` provider executes shell commands. Mitigate risks by:
|
|
921
|
+
|
|
922
|
+
1. **Using explicit paths:** `exec: "/usr/bin/grep"`
|
|
923
|
+
2. **Avoiding shell expansion:** Use `args` array instead of string interpolation
|
|
924
|
+
3. **Setting working directory:** `workingDirectory: /safe/path`
|
|
925
|
+
4. **Limiting execution time:** `timeout: 30000`
|
|
926
|
+
|
|
927
|
+
### AI Provider Security
|
|
928
|
+
|
|
929
|
+
When using AI providers:
|
|
930
|
+
|
|
931
|
+
- API keys are never logged or included in outputs
|
|
932
|
+
- Prompts containing sensitive data should use redaction
|
|
933
|
+
- Review AI outputs before automated actions (labels, comments)
|
|
934
|
+
|
|
935
|
+
### HTTP Input Validation
|
|
936
|
+
|
|
937
|
+
For webhook endpoints, configure authentication:
|
|
938
|
+
|
|
939
|
+
```yaml
|
|
940
|
+
http_server:
|
|
941
|
+
enabled: true
|
|
942
|
+
port: 8080
|
|
943
|
+
auth:
|
|
944
|
+
type: hmac
|
|
945
|
+
secret_env: WEBHOOK_SECRET
|
|
946
|
+
header: X-Signature-256
|
|
947
|
+
```
|
|
948
|
+
|
|
949
|
+
---
|
|
950
|
+
|
|
951
|
+
## Memory and State Management
|
|
952
|
+
|
|
953
|
+
### Memory Store
|
|
954
|
+
|
|
955
|
+
Visor provides a persistent key-value memory store:
|
|
956
|
+
|
|
957
|
+
```yaml
|
|
958
|
+
checks:
|
|
959
|
+
store-data:
|
|
960
|
+
type: memory
|
|
961
|
+
operation: set
|
|
962
|
+
key: "session:{{ pr.number }}"
|
|
963
|
+
value: "{{ outputs['fetch-data'] | json }}"
|
|
964
|
+
|
|
965
|
+
retrieve-data:
|
|
966
|
+
type: memory
|
|
967
|
+
operation: get
|
|
968
|
+
key: "session:{{ pr.number }}"
|
|
969
|
+
depends_on: [store-data]
|
|
970
|
+
```
|
|
971
|
+
|
|
972
|
+
Memory persists across checks within a single execution but not across separate runs (unless using external persistence).
|
|
973
|
+
|
|
974
|
+
### Execution Journal
|
|
975
|
+
|
|
976
|
+
The `ExecutionJournal` (snapshot store) maintains:
|
|
977
|
+
- Output history per check
|
|
978
|
+
- Scoped visibility for nested workflows
|
|
979
|
+
- Snapshot points for rollback scenarios
|
|
980
|
+
|
|
981
|
+
```typescript
|
|
982
|
+
// Read outputs visible to a check
|
|
983
|
+
const outputs = journal.readVisible(sessionId, snapshot, scope);
|
|
984
|
+
|
|
985
|
+
// Write check result
|
|
986
|
+
journal.write(sessionId, checkId, scope, result);
|
|
987
|
+
```
|
|
988
|
+
|
|
989
|
+
### Session Registry
|
|
990
|
+
|
|
991
|
+
AI sessions are managed via `SessionRegistry`:
|
|
992
|
+
|
|
993
|
+
```yaml
|
|
994
|
+
checks:
|
|
995
|
+
first-analysis:
|
|
996
|
+
type: ai
|
|
997
|
+
prompt: "Initial analysis..."
|
|
998
|
+
reuse_ai_session: self # Create named session
|
|
999
|
+
|
|
1000
|
+
follow-up:
|
|
1001
|
+
type: ai
|
|
1002
|
+
prompt: "Follow-up question..."
|
|
1003
|
+
depends_on: [first-analysis]
|
|
1004
|
+
reuse_ai_session: first-analysis # Reuse session
|
|
1005
|
+
session_mode: append # or 'clone'
|
|
1006
|
+
```
|
|
1007
|
+
|
|
1008
|
+
---
|
|
1009
|
+
|
|
1010
|
+
## Performance Optimization
|
|
1011
|
+
|
|
1012
|
+
### Parallel Execution
|
|
1013
|
+
|
|
1014
|
+
Control parallelism via configuration:
|
|
1015
|
+
|
|
1016
|
+
```yaml
|
|
1017
|
+
max_parallelism: 5 # Execute up to 5 checks concurrently
|
|
1018
|
+
```
|
|
1019
|
+
|
|
1020
|
+
Or via CLI:
|
|
1021
|
+
|
|
1022
|
+
```bash
|
|
1023
|
+
visor --max-parallelism 10 --config .visor.yaml
|
|
1024
|
+
```
|
|
1025
|
+
|
|
1026
|
+
### Dependency-Aware Scheduling
|
|
1027
|
+
|
|
1028
|
+
The wave-based scheduler automatically optimizes execution:
|
|
1029
|
+
|
|
1030
|
+
1. Identifies independent checks (no dependencies)
|
|
1031
|
+
2. Schedules them in parallel
|
|
1032
|
+
3. Waits for wave completion before next level
|
|
1033
|
+
4. Respects `max_parallelism` limits
|
|
1034
|
+
|
|
1035
|
+
### Caching Considerations
|
|
1036
|
+
|
|
1037
|
+
Currently, Visor does not cache AI responses between runs. For expensive operations:
|
|
1038
|
+
|
|
1039
|
+
1. Use `memory` provider to store intermediate results
|
|
1040
|
+
2. Implement `if` conditions to skip unnecessary checks
|
|
1041
|
+
3. Consider external caching for HTTP responses
|
|
1042
|
+
|
|
1043
|
+
---
|
|
1044
|
+
|
|
1045
|
+
## Related Documentation
|
|
1046
|
+
|
|
1047
|
+
- [Configuration](./configuration.md) - Configuration file reference
|
|
1048
|
+
- [Providers](./providers/) - Provider-specific documentation
|
|
1049
|
+
- [Custom Tools](./custom-tools.md) - Creating custom tools
|
|
1050
|
+
- [MCP Provider](./mcp-provider.md) - MCP integration details
|
|
1051
|
+
- [Command Provider](./command-provider.md) - Shell command execution
|
|
1052
|
+
- [HTTP Integration](./http.md) - HTTP server and client features
|
|
1053
|
+
- [Debugging](./debugging.md) - Debugging techniques
|
|
1054
|
+
|
|
1055
|
+
---
|
|
1056
|
+
|
|
1057
|
+
## Appendix A: File Structure
|
|
1058
|
+
|
|
1059
|
+
```
|
|
1060
|
+
src/
|
|
1061
|
+
index.ts # GitHub Action entry point
|
|
1062
|
+
cli-main.ts # CLI entry point
|
|
1063
|
+
config.ts # Configuration loading
|
|
1064
|
+
state-machine-execution-engine.ts # Main orchestration
|
|
1065
|
+
|
|
1066
|
+
types/
|
|
1067
|
+
config.ts # Configuration types
|
|
1068
|
+
engine.ts # Engine state types
|
|
1069
|
+
execution.ts # Execution result types
|
|
1070
|
+
cli.ts # CLI option types
|
|
1071
|
+
|
|
1072
|
+
state-machine/
|
|
1073
|
+
runner.ts # State machine runner
|
|
1074
|
+
states/
|
|
1075
|
+
init.ts # Init state handler
|
|
1076
|
+
plan-ready.ts # PlanReady state handler
|
|
1077
|
+
wave-planning.ts # WavePlanning state handler
|
|
1078
|
+
level-dispatch.ts # LevelDispatch state handler
|
|
1079
|
+
check-running.ts # CheckRunning state handler
|
|
1080
|
+
routing.ts # Routing logic
|
|
1081
|
+
completed.ts # Completed state handler
|
|
1082
|
+
error.ts # Error state handler
|
|
1083
|
+
|
|
1084
|
+
providers/
|
|
1085
|
+
check-provider.interface.ts # Provider base class
|
|
1086
|
+
check-provider-registry.ts # Provider registry
|
|
1087
|
+
ai-check-provider.ts # AI provider (Gemini, Claude, OpenAI)
|
|
1088
|
+
claude-code-check-provider.ts # Claude Code SDK provider
|
|
1089
|
+
command-check-provider.ts # Shell command provider
|
|
1090
|
+
script-check-provider.ts # JavaScript sandbox provider
|
|
1091
|
+
http-check-provider.ts # HTTP output provider
|
|
1092
|
+
http-input-provider.ts # HTTP webhook input provider
|
|
1093
|
+
http-client-provider.ts # HTTP client provider
|
|
1094
|
+
mcp-check-provider.ts # MCP tool provider
|
|
1095
|
+
mcp-tools.ts # MCP server management
|
|
1096
|
+
memory-check-provider.ts # Key-value memory provider
|
|
1097
|
+
log-check-provider.ts # Debug logging provider
|
|
1098
|
+
noop-check-provider.ts # No-op orchestration provider
|
|
1099
|
+
human-input-check-provider.ts # Interactive input provider
|
|
1100
|
+
workflow-check-provider.ts # Nested workflow provider
|
|
1101
|
+
git-checkout-provider.ts # Git reference checkout provider
|
|
1102
|
+
github-ops-provider.ts # GitHub API operations provider
|
|
1103
|
+
custom-tool-executor.ts # Custom tool execution
|
|
1104
|
+
|
|
1105
|
+
event-bus/
|
|
1106
|
+
event-bus.ts # Event bus for frontends
|
|
1107
|
+
types.ts # Event envelope types
|
|
1108
|
+
|
|
1109
|
+
frontends/
|
|
1110
|
+
host.ts # Frontend manager
|
|
1111
|
+
github/ # GitHub integration frontend
|
|
1112
|
+
slack/ # Slack integration frontend
|
|
1113
|
+
|
|
1114
|
+
utils/
|
|
1115
|
+
config-loader.ts # Remote config loading
|
|
1116
|
+
config-merger.ts # Configuration merging
|
|
1117
|
+
workspace-manager.ts # Workspace isolation
|
|
1118
|
+
sandbox.ts # JavaScript sandbox
|
|
1119
|
+
liquid-helpers.ts # Liquid template helpers
|
|
1120
|
+
|
|
1121
|
+
telemetry/
|
|
1122
|
+
opentelemetry.ts # OTEL initialization
|
|
1123
|
+
trace-helpers.ts # Span creation helpers
|
|
1124
|
+
fallback-ndjson.ts # File-based trace export
|
|
1125
|
+
|
|
1126
|
+
debug-visualizer/
|
|
1127
|
+
ws-server.ts # WebSocket server for debug UI
|
|
1128
|
+
```
|
|
1129
|
+
|
|
1130
|
+
---
|
|
1131
|
+
|
|
1132
|
+
## Appendix B: Configuration Schema Reference
|
|
1133
|
+
|
|
1134
|
+
The complete configuration schema is defined in `src/types/config.ts`. Key types:
|
|
1135
|
+
|
|
1136
|
+
```typescript
|
|
1137
|
+
interface VisorConfig {
|
|
1138
|
+
version: string;
|
|
1139
|
+
checks: Record<string, CheckConfig>;
|
|
1140
|
+
steps?: Record<string, CheckConfig>; // Alias for checks
|
|
1141
|
+
|
|
1142
|
+
// Global settings
|
|
1143
|
+
max_parallelism?: number;
|
|
1144
|
+
fail_fast?: boolean;
|
|
1145
|
+
fail_if?: string;
|
|
1146
|
+
|
|
1147
|
+
// AI configuration
|
|
1148
|
+
ai_provider?: string;
|
|
1149
|
+
ai_model?: string;
|
|
1150
|
+
ai_mcp_servers?: Record<string, McpServerConfig>;
|
|
1151
|
+
|
|
1152
|
+
// Output configuration
|
|
1153
|
+
output?: OutputConfig;
|
|
1154
|
+
|
|
1155
|
+
// HTTP server
|
|
1156
|
+
http_server?: HttpServerConfig;
|
|
1157
|
+
|
|
1158
|
+
// Workflow imports
|
|
1159
|
+
imports?: string[];
|
|
1160
|
+
|
|
1161
|
+
// Frontends (GitHub, Slack)
|
|
1162
|
+
frontends?: FrontendConfig[];
|
|
1163
|
+
|
|
1164
|
+
// Telemetry
|
|
1165
|
+
telemetry?: TelemetryConfig;
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
interface CheckConfig {
|
|
1169
|
+
type: ConfigCheckType;
|
|
1170
|
+
prompt?: string;
|
|
1171
|
+
exec?: string;
|
|
1172
|
+
url?: string;
|
|
1173
|
+
|
|
1174
|
+
// Dependencies and routing
|
|
1175
|
+
depends_on?: string[];
|
|
1176
|
+
on_success?: RoutingConfig;
|
|
1177
|
+
on_fail?: RoutingConfig;
|
|
1178
|
+
on_finish?: RoutingConfig;
|
|
1179
|
+
|
|
1180
|
+
// Execution control
|
|
1181
|
+
if?: string;
|
|
1182
|
+
forEach?: string | boolean;
|
|
1183
|
+
timeout?: number;
|
|
1184
|
+
|
|
1185
|
+
// Output handling
|
|
1186
|
+
schema?: string | object;
|
|
1187
|
+
transform_js?: string;
|
|
1188
|
+
fail_if?: string;
|
|
1189
|
+
|
|
1190
|
+
// Metadata
|
|
1191
|
+
tags?: string[];
|
|
1192
|
+
group?: string;
|
|
1193
|
+
on?: EventTrigger[];
|
|
1194
|
+
}
|
|
1195
|
+
```
|
|
1196
|
+
|
|
1197
|
+
---
|
|
1198
|
+
|
|
1199
|
+
## Appendix C: Event Types
|
|
1200
|
+
|
|
1201
|
+
Events that flow through the state machine:
|
|
1202
|
+
|
|
1203
|
+
| Event | Description | Fields |
|
|
1204
|
+
|-------|-------------|--------|
|
|
1205
|
+
| `PlanBuilt` | Dependency graph constructed | `graph` |
|
|
1206
|
+
| `WaveRequested` | New wave requested | `wave` |
|
|
1207
|
+
| `LevelReady` | Execution level ready | `level`, `wave` |
|
|
1208
|
+
| `LevelDepleted` | All checks in level complete | `level`, `wave` |
|
|
1209
|
+
| `CheckScheduled` | Check dispatched for execution | `checkId`, `scope` |
|
|
1210
|
+
| `CheckCompleted` | Check finished successfully | `checkId`, `scope`, `result` |
|
|
1211
|
+
| `CheckErrored` | Check failed with error | `checkId`, `scope`, `error` |
|
|
1212
|
+
| `ForwardRunRequested` | Routing triggered new check | `target`, `gotoEvent`, `scope`, `origin` |
|
|
1213
|
+
| `WaveRetry` | Wave needs re-execution | `reason` |
|
|
1214
|
+
| `StateTransition` | State machine transitioned | `from`, `to` |
|
|
1215
|
+
| `Shutdown` | Engine shutting down | `error?` |
|
|
1216
|
+
|
|
1217
|
+
---
|
|
1218
|
+
|
|
1219
|
+
## Appendix D: Glossary
|
|
1220
|
+
|
|
1221
|
+
| Term | Definition |
|
|
1222
|
+
|------|------------|
|
|
1223
|
+
| **Check** | A single unit of work (AI analysis, command, HTTP call, etc.) |
|
|
1224
|
+
| **Step** | Alias for check (used interchangeably) |
|
|
1225
|
+
| **Wave** | A batch of checks executed in parallel |
|
|
1226
|
+
| **Level** | Dependency level in the execution graph |
|
|
1227
|
+
| **Provider** | Implementation of a check type |
|
|
1228
|
+
| **Journal** | Execution history and output storage |
|
|
1229
|
+
| **Scope** | Hierarchical path for nested workflow execution |
|
|
1230
|
+
| **Frontend** | Integration point (GitHub, Slack) |
|
|
1231
|
+
| **Routing** | Control flow based on check results |
|
|
1232
|
+
| **MCP** | Model Context Protocol - standard for AI tool integration |
|