@probelabs/visor 0.1.106 → 0.1.111
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/README.md +71 -2
- package/action.yml +1 -1
- package/defaults/code-refiner.yaml +114 -0
- package/defaults/{.visor.yaml → code-review.yaml} +35 -226
- package/defaults/override.yaml +52 -0
- package/defaults/task-refinement.yaml +624 -0
- package/defaults/visor.tests.yaml +685 -0
- package/defaults/visor.yaml +483 -0
- package/dist/action-cli-bridge.d.ts +11 -82
- package/dist/action-cli-bridge.d.ts.map +1 -1
- package/dist/ai-review-service.d.ts +28 -9
- package/dist/ai-review-service.d.ts.map +1 -1
- package/dist/check-execution-engine.d.ts +19 -331
- package/dist/check-execution-engine.d.ts.map +1 -1
- package/dist/cli-main.d.ts.map +1 -1
- package/dist/cli.d.ts +0 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/config.d.ts +16 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/cron-scheduler.d.ts +3 -3
- package/dist/cron-scheduler.d.ts.map +1 -1
- package/dist/debug-visualizer/ws-server.d.ts +7 -1
- package/dist/debug-visualizer/ws-server.d.ts.map +1 -1
- package/dist/defaults/code-refiner.yaml +114 -0
- package/dist/defaults/{.visor.yaml → code-review.yaml} +35 -226
- package/dist/defaults/override.yaml +52 -0
- package/dist/defaults/task-refinement.yaml +624 -0
- package/dist/defaults/visor.tests.yaml +685 -0
- package/dist/defaults/visor.yaml +483 -0
- package/dist/docs/DEPLOYMENT.md +118 -0
- package/dist/docs/GITHUB_CHECKS.md +280 -0
- package/dist/docs/NPM_USAGE.md +208 -0
- package/dist/docs/action-reference.md +19 -0
- package/dist/docs/advanced-ai.md +237 -0
- package/dist/docs/ai-configuration.md +535 -0
- package/dist/docs/ai-custom-tools-usage.md +261 -0
- package/dist/docs/ai-custom-tools.md +392 -0
- package/dist/docs/author-permissions.md +610 -0
- package/dist/docs/bot-transports-rfc.md +23 -0
- package/dist/docs/ci-cli-mode.md +34 -0
- package/dist/docs/claude-code.md +74 -0
- package/dist/docs/command-provider.md +559 -0
- package/dist/docs/commands.md +8 -0
- package/dist/docs/configuration.md +324 -0
- package/dist/docs/custom-tools.md +424 -0
- package/dist/docs/dashboards/README.md +23 -0
- package/dist/docs/dashboards/grafana-visor-diagrams.json +20 -0
- package/dist/docs/dashboards/grafana-visor-overview.json +33 -0
- package/dist/docs/debug-visualizer-progress.md +572 -0
- package/dist/docs/debug-visualizer-rfc.md +691 -0
- package/dist/docs/debug-visualizer.md +114 -0
- package/dist/docs/debugging.md +636 -0
- package/dist/docs/default-output-schema.md +28 -0
- package/dist/docs/dependencies.md +369 -0
- package/dist/docs/dev-playbook.md +9 -0
- package/dist/docs/engine-pause-resume-rfc.md +192 -0
- package/dist/docs/engine-state-machine-plan.md +333 -0
- package/dist/docs/event-driven-github-integration-rfc.md +743 -0
- package/dist/docs/event-triggers.md +292 -0
- package/dist/docs/execution-statistics-rfc.md +290 -0
- package/dist/docs/fact-validator-gap-analysis.md +178 -0
- package/dist/docs/fact-validator-implementation-plan.md +1235 -0
- package/dist/docs/fail-if.md +95 -0
- package/dist/docs/failure-conditions-implementation.md +271 -0
- package/dist/docs/failure-conditions-schema.md +173 -0
- package/dist/docs/failure-routing-rfc.md +193 -0
- package/dist/docs/failure-routing.md +507 -0
- package/dist/docs/foreach-dependency-propagation.md +473 -0
- package/dist/docs/github-ops.md +89 -0
- package/dist/docs/goto-forward-run-plan.md +113 -0
- package/dist/docs/guides/criticality-modes.md +332 -0
- package/dist/docs/guides/fault-management-and-contracts.md +738 -0
- package/dist/docs/guides/workflow-style-guide.md +224 -0
- package/dist/docs/http.md +299 -0
- package/dist/docs/human-input-provider.md +372 -0
- package/dist/docs/lifecycle-hooks.md +253 -0
- package/dist/docs/limits.md +64 -0
- package/dist/docs/liquid-templates.md +490 -0
- package/dist/docs/loop-routing-refactor.md +89 -0
- package/dist/docs/mcp-provider.md +557 -0
- package/dist/docs/mcp.md +124 -0
- package/dist/docs/memory.md +903 -0
- package/dist/docs/observability.md +12 -0
- package/dist/docs/output-formats.md +20 -0
- package/dist/docs/output-formatting.md +29 -0
- package/dist/docs/output-history.md +383 -0
- package/dist/docs/performance.md +6 -0
- package/dist/docs/pluggable.md +124 -0
- package/dist/docs/proposals/snapshot-scope-execution.md +236 -0
- package/dist/docs/providers/git-checkout.md +589 -0
- package/dist/docs/recipes.md +474 -0
- package/dist/docs/rfc/git-checkout-step.md +601 -0
- package/dist/docs/rfc/on_init-hook.md +1294 -0
- package/dist/docs/rfc/workspace-isolation.md +216 -0
- package/dist/docs/roadmap/criticality-implementation-tasks.md +92 -0
- package/dist/docs/router-patterns.md +339 -0
- package/dist/docs/schema-next-pr.md +10 -0
- package/dist/docs/schema-templates.md +68 -0
- package/dist/docs/script.md +34 -0
- package/dist/docs/sdk.md +222 -0
- package/dist/docs/security.md +7 -0
- package/dist/docs/suppressions.md +89 -0
- package/dist/docs/tag-filtering.md +258 -0
- package/dist/docs/telemetry-setup.md +119 -0
- package/dist/docs/telemetry-tracing-rfc.md +275 -0
- package/dist/docs/test-framework-rfc.md +680 -0
- package/dist/docs/testing/assertions.md +85 -0
- package/dist/docs/testing/ci.md +44 -0
- package/dist/docs/testing/cli.md +41 -0
- package/dist/docs/testing/cookbook.md +172 -0
- package/dist/docs/testing/dsl-reference.md +199 -0
- package/dist/docs/testing/fixtures-and-mocks.md +91 -0
- package/dist/docs/testing/flows.md +92 -0
- package/dist/docs/testing/getting-started.md +93 -0
- package/dist/docs/testing/troubleshooting.md +55 -0
- package/dist/docs/timeouts.md +50 -0
- package/dist/docs/troubleshooting.md +7 -0
- package/dist/docs/visor-sdk-rfc.md +186 -0
- package/dist/docs/workflows.md +569 -0
- package/dist/engine/on-finish/orchestrator.d.ts +19 -0
- package/dist/engine/on-finish/orchestrator.d.ts.map +1 -0
- package/dist/engine/on-finish/utils.d.ts +44 -0
- package/dist/engine/on-finish/utils.d.ts.map +1 -0
- package/dist/event-bus/event-bus.d.ts +13 -0
- package/dist/event-bus/event-bus.d.ts.map +1 -0
- package/dist/event-bus/types.d.ts +71 -0
- package/dist/event-bus/types.d.ts.map +1 -0
- package/dist/examples/.claude/agents/code-reviewer.md +69 -0
- package/dist/examples/.mcp.json +34 -0
- package/dist/examples/CALCULATOR-SDK.md +364 -0
- package/dist/examples/README.md +384 -0
- package/dist/examples/ai-custom-tools-example.yaml +206 -0
- package/dist/examples/ai-custom-tools-simple.yaml +76 -0
- package/dist/examples/ai-retry-fallback-config.yaml +180 -0
- package/dist/examples/ai-with-bash.yaml +126 -0
- package/dist/examples/ai-with-mcp.yaml +82 -0
- package/dist/examples/basic-human-input.yaml +15 -0
- package/dist/examples/bedrock-config.yaml +77 -0
- package/dist/examples/calculator-config.yaml +133 -0
- package/dist/examples/calculator-json-output-guide.md +311 -0
- package/dist/examples/calculator-sdk-automated.ts +340 -0
- package/dist/examples/calculator-sdk-example.ts +275 -0
- package/dist/examples/calculator-sdk-json.ts +331 -0
- package/dist/examples/calculator-sdk-real.ts +374 -0
- package/dist/examples/calculator-sdk-test.ts +148 -0
- package/dist/examples/claude-code-config.yaml +191 -0
- package/dist/examples/cron-webhook-config.yaml +215 -0
- package/dist/examples/custom-template.liquid +57 -0
- package/dist/examples/custom-tools-example.yaml +281 -0
- package/dist/examples/enhanced-config.yaml +165 -0
- package/dist/examples/environments/visor.base.yaml +92 -0
- package/dist/examples/environments/visor.dev.yaml +33 -0
- package/dist/examples/environments/visor.prod.yaml +95 -0
- package/dist/examples/environments/visor.staging.yaml +46 -0
- package/dist/examples/fact-validator.yaml +361 -0
- package/dist/examples/fail-if-simple.yaml +90 -0
- package/dist/examples/failure-conditions-advanced.yaml +136 -0
- package/dist/examples/failure-conditions-basic.yaml +48 -0
- package/dist/examples/failure-conditions-github-style.yaml +119 -0
- package/dist/examples/failure-conditions-migration.yaml +74 -0
- package/dist/examples/for-loop-example.yaml +176 -0
- package/dist/examples/forEach-example.yaml +120 -0
- package/dist/examples/git-checkout-basic.yaml +32 -0
- package/dist/examples/git-checkout-compare.yaml +59 -0
- package/dist/examples/git-checkout-cross-repo.yaml +76 -0
- package/dist/examples/github-workflow-with-tags.yml +163 -0
- package/dist/examples/http-integration-config.yaml +240 -0
- package/dist/examples/https-server-config.yaml +209 -0
- package/dist/examples/human-input-example.yaml +63 -0
- package/dist/examples/if-conditions.yaml +173 -0
- package/dist/examples/jira-simple-example.yaml +56 -0
- package/dist/examples/jira-single-issue-workflow.yaml +166 -0
- package/dist/examples/jira-workflow-mcp.yaml +182 -0
- package/dist/examples/mcp/analyzer.py +119 -0
- package/dist/examples/mcp-provider-example.yaml +301 -0
- package/dist/examples/memory-counter.yaml +99 -0
- package/dist/examples/memory-error-collection.yaml +104 -0
- package/dist/examples/memory-exec-js.yaml +247 -0
- package/dist/examples/memory-namespace-isolation.yaml +184 -0
- package/dist/examples/memory-retry-counter.yaml +65 -0
- package/dist/examples/memory-state-machine.yaml +170 -0
- package/dist/examples/on-init-import-demo.yaml +179 -0
- package/dist/examples/outputs-raw-basic.yaml +26 -0
- package/dist/examples/project-with-tools.yaml +174 -0
- package/dist/examples/prompts/architecture-analysis.liquid +116 -0
- package/dist/examples/prompts/security-comprehensive.liquid +107 -0
- package/dist/examples/quick-start-tags.yaml +53 -0
- package/dist/examples/reusable-tools.yaml +92 -0
- package/dist/examples/reusable-workflows.yaml +88 -0
- package/dist/examples/routing-basic.yaml +35 -0
- package/dist/examples/routing-dynamic-js.yaml +46 -0
- package/dist/examples/routing-foreach.yaml +34 -0
- package/dist/examples/routing-goto-event.yaml +34 -0
- package/dist/examples/routing-on-success.yaml +25 -0
- package/dist/examples/run-calculator-demo.sh +71 -0
- package/dist/examples/sdk-basic.mjs +10 -0
- package/dist/examples/sdk-cjs.cjs +10 -0
- package/dist/examples/sdk-comprehensive.mjs +175 -0
- package/dist/examples/sdk-manual-config.mjs +65 -0
- package/dist/examples/sdk-typescript.js +81 -0
- package/dist/examples/sdk-typescript.ts +92 -0
- package/dist/examples/session-reuse-config.yaml +151 -0
- package/dist/examples/session-reuse-self.yaml +81 -0
- package/dist/examples/slack-simple-chat.yaml +775 -0
- package/dist/examples/templates/security-report.liquid +137 -0
- package/dist/examples/tools-library.yaml +281 -0
- package/dist/examples/transform-example.yaml +199 -0
- package/dist/examples/visor-with-tags.yaml +198 -0
- package/dist/examples/webhook-pipeline-config.yaml +218 -0
- package/dist/examples/workflows/calculator-workflow.yaml +163 -0
- package/dist/examples/workflows/code-quality.yaml +222 -0
- package/dist/examples/workflows/quick-pr-check.yaml +90 -0
- package/dist/examples/workflows/workflow-composition-example.yaml +130 -0
- package/dist/failure-condition-evaluator.d.ts +3 -0
- package/dist/failure-condition-evaluator.d.ts.map +1 -1
- package/dist/frontends/github-frontend.d.ts +58 -0
- package/dist/frontends/github-frontend.d.ts.map +1 -0
- package/dist/frontends/host.d.ts +47 -0
- package/dist/frontends/host.d.ts.map +1 -0
- package/dist/frontends/ndjson-sink.d.ts +12 -0
- package/dist/frontends/ndjson-sink.d.ts.map +1 -0
- package/dist/frontends/slack-frontend.d.ts +58 -0
- package/dist/frontends/slack-frontend.d.ts.map +1 -0
- package/dist/generated/config-schema.d.ts +967 -57
- package/dist/generated/config-schema.d.ts.map +1 -1
- package/dist/generated/config-schema.json +1033 -56
- package/dist/github-check-service.d.ts +4 -6
- package/dist/github-check-service.d.ts.map +1 -1
- package/dist/github-comments.d.ts +2 -4
- package/dist/github-comments.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +134327 -99004
- package/dist/liquid-extensions.d.ts.map +1 -1
- package/dist/logger.d.ts +2 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/memory-store.d.ts +6 -0
- package/dist/memory-store.d.ts.map +1 -1
- package/dist/output/assistant-json/template.liquid +0 -0
- package/dist/output/traces/run-2026-01-20T19-22-58-043Z.ndjson +138 -0
- package/dist/output/traces/run-2026-01-20T19-23-52-175Z.ndjson +1067 -0
- package/dist/output-formatters.d.ts +1 -1
- package/dist/output-formatters.d.ts.map +1 -1
- package/dist/providers/ai-check-provider.d.ts +12 -0
- package/dist/providers/ai-check-provider.d.ts.map +1 -1
- package/dist/providers/check-provider-registry.d.ts +6 -0
- package/dist/providers/check-provider-registry.d.ts.map +1 -1
- package/dist/providers/check-provider.interface.d.ts +43 -1
- package/dist/providers/check-provider.interface.d.ts.map +1 -1
- package/dist/providers/claude-code-check-provider.d.ts.map +1 -1
- package/dist/providers/command-check-provider.d.ts +1 -1
- package/dist/providers/command-check-provider.d.ts.map +1 -1
- package/dist/providers/custom-tool-executor.d.ts +61 -0
- package/dist/providers/custom-tool-executor.d.ts.map +1 -0
- package/dist/providers/git-checkout-provider.d.ts +25 -0
- package/dist/providers/git-checkout-provider.d.ts.map +1 -0
- package/dist/providers/github-ops-provider.d.ts.map +1 -1
- package/dist/providers/http-client-provider.d.ts +4 -4
- package/dist/providers/http-client-provider.d.ts.map +1 -1
- package/dist/providers/human-input-check-provider.d.ts +5 -0
- package/dist/providers/human-input-check-provider.d.ts.map +1 -1
- package/dist/providers/index.d.ts +1 -0
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/log-check-provider.d.ts +2 -5
- package/dist/providers/log-check-provider.d.ts.map +1 -1
- package/dist/providers/mcp-check-provider.d.ts +10 -4
- package/dist/providers/mcp-check-provider.d.ts.map +1 -1
- package/dist/providers/mcp-custom-sse-server.d.ts +66 -0
- package/dist/providers/mcp-custom-sse-server.d.ts.map +1 -0
- package/dist/providers/memory-check-provider.d.ts +2 -8
- package/dist/providers/memory-check-provider.d.ts.map +1 -1
- package/dist/providers/script-check-provider.d.ts +25 -0
- package/dist/providers/script-check-provider.d.ts.map +1 -0
- package/dist/providers/workflow-check-provider.d.ts +56 -0
- package/dist/providers/workflow-check-provider.d.ts.map +1 -0
- package/dist/reviewer.d.ts +2 -1
- package/dist/reviewer.d.ts.map +1 -1
- package/dist/sdk/check-provider-registry-534KL5HT.mjs +27 -0
- package/dist/sdk/chunk-23L3QRYX.mjs +16872 -0
- package/dist/sdk/chunk-23L3QRYX.mjs.map +1 -0
- package/dist/sdk/{chunk-TUTOLSFV.mjs → chunk-3OMWVM6J.mjs} +11 -1
- package/dist/sdk/chunk-3OMWVM6J.mjs.map +1 -0
- package/dist/sdk/chunk-7UK3NIIT.mjs +482 -0
- package/dist/sdk/chunk-7UK3NIIT.mjs.map +1 -0
- package/dist/sdk/chunk-AGIZJ4UZ.mjs +173 -0
- package/dist/sdk/chunk-AGIZJ4UZ.mjs.map +1 -0
- package/dist/sdk/chunk-AIVFBIS4.mjs +1371 -0
- package/dist/sdk/chunk-AIVFBIS4.mjs.map +1 -0
- package/dist/sdk/chunk-AK6BVWIT.mjs +426 -0
- package/dist/sdk/chunk-AK6BVWIT.mjs.map +1 -0
- package/dist/sdk/chunk-AUT26LHW.mjs +139 -0
- package/dist/sdk/chunk-AUT26LHW.mjs.map +1 -0
- package/dist/sdk/chunk-BOVFH3LI.mjs +232 -0
- package/dist/sdk/chunk-BOVFH3LI.mjs.map +1 -0
- package/dist/sdk/chunk-CNX7V5JK.mjs +89 -0
- package/dist/sdk/chunk-CNX7V5JK.mjs.map +1 -0
- package/dist/sdk/chunk-HTOKWMPO.mjs +157 -0
- package/dist/sdk/chunk-HTOKWMPO.mjs.map +1 -0
- package/dist/sdk/chunk-NAW3DB3I.mjs +197 -0
- package/dist/sdk/chunk-NAW3DB3I.mjs.map +1 -0
- package/dist/sdk/chunk-O5EZDNYL.mjs +274 -0
- package/dist/sdk/chunk-O5EZDNYL.mjs.map +1 -0
- package/dist/sdk/chunk-QR7MOMJH.mjs +558 -0
- package/dist/sdk/chunk-QR7MOMJH.mjs.map +1 -0
- package/dist/sdk/chunk-QY2XYPEV.mjs +3556 -0
- package/dist/sdk/chunk-QY2XYPEV.mjs.map +1 -0
- package/dist/sdk/chunk-S2RUE2RG.mjs +145 -0
- package/dist/sdk/chunk-S2RUE2RG.mjs.map +1 -0
- package/dist/sdk/chunk-SIWNBRTK.mjs +800 -0
- package/dist/sdk/chunk-SIWNBRTK.mjs.map +1 -0
- package/dist/sdk/chunk-YSN4G6CI.mjs +146 -0
- package/dist/sdk/chunk-YSN4G6CI.mjs.map +1 -0
- package/dist/sdk/chunk-ZYAUYXSW.mjs +206 -0
- package/dist/sdk/chunk-ZYAUYXSW.mjs.map +1 -0
- package/dist/sdk/command-executor-TYUV6HUS.mjs +14 -0
- package/dist/sdk/config-YNC2EOOT.mjs +16 -0
- package/dist/sdk/config-merger-PX3WIT57.mjs +10 -0
- package/dist/sdk/event-bus-5BEVPQ6T.mjs +35 -0
- package/dist/sdk/event-bus-5BEVPQ6T.mjs.map +1 -0
- package/dist/sdk/failure-condition-evaluator-YGTF2GHG.mjs +17 -0
- package/dist/sdk/git-repository-analyzer-HJC4MYW4.mjs +458 -0
- package/dist/sdk/git-repository-analyzer-HJC4MYW4.mjs.map +1 -0
- package/dist/sdk/github-frontend-SIAEOCON.mjs +1420 -0
- package/dist/sdk/github-frontend-SIAEOCON.mjs.map +1 -0
- package/dist/sdk/host-DXUYTNMU.mjs +52 -0
- package/dist/sdk/host-DXUYTNMU.mjs.map +1 -0
- package/dist/sdk/{liquid-extensions-KVL4MKRH.mjs → liquid-extensions-PKWCKK7E.mjs} +8 -2
- package/dist/sdk/memory-store-XGBB7LX7.mjs +12 -0
- package/dist/sdk/memory-store-XGBB7LX7.mjs.map +1 -0
- package/dist/sdk/metrics-7PP3EJUH.mjs +29 -0
- package/dist/sdk/metrics-7PP3EJUH.mjs.map +1 -0
- package/dist/sdk/ndjson-sink-B4V4NTAQ.mjs +44 -0
- package/dist/sdk/ndjson-sink-B4V4NTAQ.mjs.map +1 -0
- package/dist/sdk/prompt-state-YRJY6QAL.mjs +16 -0
- package/dist/sdk/prompt-state-YRJY6QAL.mjs.map +1 -0
- package/dist/sdk/renderer-schema-LPKN5UJS.mjs +51 -0
- package/dist/sdk/renderer-schema-LPKN5UJS.mjs.map +1 -0
- package/dist/sdk/routing-6N45MJ4F.mjs +24 -0
- package/dist/sdk/routing-6N45MJ4F.mjs.map +1 -0
- package/dist/sdk/sdk.d.mts +541 -22
- package/dist/sdk/sdk.d.ts +541 -22
- package/dist/sdk/sdk.js +27963 -16505
- package/dist/sdk/sdk.js.map +1 -1
- package/dist/sdk/sdk.mjs +1116 -2169
- package/dist/sdk/sdk.mjs.map +1 -1
- package/dist/sdk/session-registry-4E6YRQ77.mjs +10 -0
- package/dist/sdk/session-registry-4E6YRQ77.mjs.map +1 -0
- package/dist/sdk/slack-frontend-BVKW3GD5.mjs +735 -0
- package/dist/sdk/slack-frontend-BVKW3GD5.mjs.map +1 -0
- package/dist/sdk/trace-helpers-VP6QYVBX.mjs +23 -0
- package/dist/sdk/trace-helpers-VP6QYVBX.mjs.map +1 -0
- package/dist/sdk/{tracer-init-WC75N5NW.mjs → tracer-init-GSLPPLCD.mjs} +2 -2
- package/dist/sdk/tracer-init-GSLPPLCD.mjs.map +1 -0
- package/dist/sdk/workflow-registry-R6KSACFR.mjs +12 -0
- package/dist/sdk/workflow-registry-R6KSACFR.mjs.map +1 -0
- package/dist/sdk.d.ts.map +1 -1
- package/dist/slack/adapter.d.ts +36 -0
- package/dist/slack/adapter.d.ts.map +1 -0
- package/dist/slack/cache-prewarmer.d.ts +31 -0
- package/dist/slack/cache-prewarmer.d.ts.map +1 -0
- package/dist/slack/client.d.ts +77 -0
- package/dist/slack/client.d.ts.map +1 -0
- package/dist/slack/markdown.d.ts +45 -0
- package/dist/slack/markdown.d.ts.map +1 -0
- package/dist/slack/prompt-state.d.ts +33 -0
- package/dist/slack/prompt-state.d.ts.map +1 -0
- package/dist/slack/rate-limiter.d.ts +56 -0
- package/dist/slack/rate-limiter.d.ts.map +1 -0
- package/dist/slack/signature.d.ts +2 -0
- package/dist/slack/signature.d.ts.map +1 -0
- package/dist/slack/socket-runner.d.ts +42 -0
- package/dist/slack/socket-runner.d.ts.map +1 -0
- package/dist/slack/thread-cache.d.ts +51 -0
- package/dist/slack/thread-cache.d.ts.map +1 -0
- package/dist/snapshot-store.d.ts +59 -0
- package/dist/snapshot-store.d.ts.map +1 -0
- package/dist/state-machine/context/build-engine-context.d.ts +17 -0
- package/dist/state-machine/context/build-engine-context.d.ts.map +1 -0
- package/dist/state-machine/dispatch/dependency-gating.d.ts +12 -0
- package/dist/state-machine/dispatch/dependency-gating.d.ts.map +1 -0
- package/dist/state-machine/dispatch/execution-invoker.d.ts +14 -0
- package/dist/state-machine/dispatch/execution-invoker.d.ts.map +1 -0
- package/dist/state-machine/dispatch/foreach-processor.d.ts +8 -0
- package/dist/state-machine/dispatch/foreach-processor.d.ts.map +1 -0
- package/dist/state-machine/dispatch/history-snapshot.d.ts +8 -0
- package/dist/state-machine/dispatch/history-snapshot.d.ts.map +1 -0
- package/dist/state-machine/dispatch/on-init-handlers.d.ts +43 -0
- package/dist/state-machine/dispatch/on-init-handlers.d.ts.map +1 -0
- package/dist/state-machine/dispatch/renderer-schema.d.ts +8 -0
- package/dist/state-machine/dispatch/renderer-schema.d.ts.map +1 -0
- package/dist/state-machine/dispatch/stats-manager.d.ts +15 -0
- package/dist/state-machine/dispatch/stats-manager.d.ts.map +1 -0
- package/dist/state-machine/dispatch/template-renderer.d.ts +7 -0
- package/dist/state-machine/dispatch/template-renderer.d.ts.map +1 -0
- package/dist/state-machine/execution/summary.d.ts +8 -0
- package/dist/state-machine/execution/summary.d.ts.map +1 -0
- package/dist/state-machine/runner.d.ts +79 -0
- package/dist/state-machine/runner.d.ts.map +1 -0
- package/dist/state-machine/states/check-running.d.ts +14 -0
- package/dist/state-machine/states/check-running.d.ts.map +1 -0
- package/dist/state-machine/states/completed.d.ts +12 -0
- package/dist/state-machine/states/completed.d.ts.map +1 -0
- package/dist/state-machine/states/error.d.ts +11 -0
- package/dist/state-machine/states/error.d.ts.map +1 -0
- package/dist/state-machine/states/init.d.ts +11 -0
- package/dist/state-machine/states/init.d.ts.map +1 -0
- package/dist/state-machine/states/level-dispatch.d.ts +17 -0
- package/dist/state-machine/states/level-dispatch.d.ts.map +1 -0
- package/dist/state-machine/states/plan-ready.d.ts +12 -0
- package/dist/state-machine/states/plan-ready.d.ts.map +1 -0
- package/dist/state-machine/states/routing.d.ts +52 -0
- package/dist/state-machine/states/routing.d.ts.map +1 -0
- package/dist/state-machine/states/wave-planning.d.ts +14 -0
- package/dist/state-machine/states/wave-planning.d.ts.map +1 -0
- package/dist/state-machine/workflow-projection.d.ts +47 -0
- package/dist/state-machine/workflow-projection.d.ts.map +1 -0
- package/dist/state-machine-execution-engine.d.ts +159 -0
- package/dist/state-machine-execution-engine.d.ts.map +1 -0
- package/dist/telemetry/opentelemetry.d.ts.map +1 -1
- package/dist/telemetry/state-capture.d.ts +5 -0
- package/dist/telemetry/state-capture.d.ts.map +1 -1
- package/dist/test-runner/assertions.d.ts +59 -0
- package/dist/test-runner/assertions.d.ts.map +1 -0
- package/dist/test-runner/core/environment.d.ts +8 -0
- package/dist/test-runner/core/environment.d.ts.map +1 -0
- package/dist/test-runner/core/fixture.d.ts +3 -0
- package/dist/test-runner/core/fixture.d.ts.map +1 -0
- package/dist/test-runner/core/flow-stage.d.ts +32 -0
- package/dist/test-runner/core/flow-stage.d.ts.map +1 -0
- package/dist/test-runner/core/mocks.d.ts +8 -0
- package/dist/test-runner/core/mocks.d.ts.map +1 -0
- package/dist/test-runner/core/test-execution-wrapper.d.ts +18 -0
- package/dist/test-runner/core/test-execution-wrapper.d.ts.map +1 -0
- package/dist/test-runner/evaluators.d.ts +45 -0
- package/dist/test-runner/evaluators.d.ts.map +1 -0
- package/dist/test-runner/fixture-loader.d.ts +30 -0
- package/dist/test-runner/fixture-loader.d.ts.map +1 -0
- package/dist/test-runner/index.d.ts +127 -0
- package/dist/test-runner/index.d.ts.map +1 -0
- package/dist/test-runner/recorders/github-recorder.d.ts +23 -0
- package/dist/test-runner/recorders/github-recorder.d.ts.map +1 -0
- package/dist/test-runner/recorders/global-recorder.d.ts +4 -0
- package/dist/test-runner/recorders/global-recorder.d.ts.map +1 -0
- package/dist/test-runner/recorders/slack-recorder.d.ts +17 -0
- package/dist/test-runner/recorders/slack-recorder.d.ts.map +1 -0
- package/dist/test-runner/utils/selectors.d.ts +2 -0
- package/dist/test-runner/utils/selectors.d.ts.map +1 -0
- package/dist/test-runner/validator.d.ts +8 -0
- package/dist/test-runner/validator.d.ts.map +1 -0
- package/dist/traces/run-2026-01-20T19-22-58-043Z.ndjson +138 -0
- package/dist/traces/run-2026-01-20T19-23-52-175Z.ndjson +1067 -0
- package/dist/types/bot.d.ts +109 -0
- package/dist/types/bot.d.ts.map +1 -0
- package/dist/types/cli.d.ts +8 -1
- package/dist/types/cli.d.ts.map +1 -1
- package/dist/types/config.d.ts +459 -9
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/engine.d.ts +177 -0
- package/dist/types/engine.d.ts.map +1 -0
- package/dist/types/execution.d.ts +73 -0
- package/dist/types/execution.d.ts.map +1 -0
- package/dist/types/git-checkout.d.ts +76 -0
- package/dist/types/git-checkout.d.ts.map +1 -0
- package/dist/types/github.d.ts +51 -0
- package/dist/types/github.d.ts.map +1 -0
- package/dist/types/workflow.d.ts +237 -0
- package/dist/types/workflow.d.ts.map +1 -0
- package/dist/utils/command-executor.d.ts +43 -0
- package/dist/utils/command-executor.d.ts.map +1 -0
- package/dist/utils/comment-metadata.d.ts +21 -0
- package/dist/utils/comment-metadata.d.ts.map +1 -0
- package/dist/utils/config-loader.d.ts.map +1 -1
- package/dist/utils/config-merger.d.ts.map +1 -1
- package/dist/utils/env-exposure.d.ts +3 -0
- package/dist/utils/env-exposure.d.ts.map +1 -0
- package/dist/utils/file-exclusion.d.ts.map +1 -1
- package/dist/utils/interactive-prompt.d.ts +1 -1
- package/dist/utils/interactive-prompt.d.ts.map +1 -1
- package/dist/utils/json-text-extractor.d.ts +17 -0
- package/dist/utils/json-text-extractor.d.ts.map +1 -0
- package/dist/utils/sandbox.d.ts +10 -0
- package/dist/utils/sandbox.d.ts.map +1 -1
- package/dist/utils/script-memory-ops.d.ts +21 -0
- package/dist/utils/script-memory-ops.d.ts.map +1 -0
- package/dist/utils/template-context.d.ts +8 -0
- package/dist/utils/template-context.d.ts.map +1 -0
- package/dist/utils/tracer-init.d.ts.map +1 -1
- package/dist/utils/workspace-manager.d.ts +118 -0
- package/dist/utils/workspace-manager.d.ts.map +1 -0
- package/dist/utils/worktree-cleanup.d.ts +33 -0
- package/dist/utils/worktree-cleanup.d.ts.map +1 -0
- package/dist/utils/worktree-manager.d.ts +153 -0
- package/dist/utils/worktree-manager.d.ts.map +1 -0
- package/dist/webhook-server.d.ts +3 -3
- package/dist/webhook-server.d.ts.map +1 -1
- package/dist/workflow-executor.d.ts +81 -0
- package/dist/workflow-executor.d.ts.map +1 -0
- package/dist/workflow-registry.d.ts +79 -0
- package/dist/workflow-registry.d.ts.map +1 -0
- package/package.json +12 -5
- package/dist/output/traces/run-2025-10-22T18-22-56-873Z.ndjson +0 -218
- package/dist/sdk/check-execution-engine-2YYKUUSH.mjs +0 -11
- package/dist/sdk/check-execution-engine-6QJXYYON.mjs +0 -11
- package/dist/sdk/check-execution-engine-PJZ4ZOKG.mjs +0 -11
- package/dist/sdk/chunk-33QVZ2D4.mjs +0 -316
- package/dist/sdk/chunk-33QVZ2D4.mjs.map +0 -1
- package/dist/sdk/chunk-B5QBV2QJ.mjs +0 -752
- package/dist/sdk/chunk-B5QBV2QJ.mjs.map +0 -1
- package/dist/sdk/chunk-BVFNRCHT.mjs +0 -14129
- package/dist/sdk/chunk-BVFNRCHT.mjs.map +0 -1
- package/dist/sdk/chunk-KWZW23FG.mjs +0 -14129
- package/dist/sdk/chunk-KWZW23FG.mjs.map +0 -1
- package/dist/sdk/chunk-O4RP4BRH.mjs +0 -14092
- package/dist/sdk/chunk-O4RP4BRH.mjs.map +0 -1
- package/dist/sdk/chunk-TUTOLSFV.mjs.map +0 -1
- package/dist/sdk/chunk-U5D2LY66.mjs +0 -245
- package/dist/sdk/chunk-U5D2LY66.mjs.map +0 -1
- package/dist/sdk/chunk-U7X54EMV.mjs +0 -331
- package/dist/sdk/chunk-U7X54EMV.mjs.map +0 -1
- package/dist/sdk/config-merger-TWUBWFC2.mjs +0 -8
- package/dist/sdk/mermaid-telemetry-SN6A2TKW.mjs +0 -61
- package/dist/sdk/mermaid-telemetry-SN6A2TKW.mjs.map +0 -1
- package/dist/sdk/mermaid-telemetry-YCTIG76M.mjs +0 -61
- package/dist/sdk/mermaid-telemetry-YCTIG76M.mjs.map +0 -1
- package/dist/traces/run-2025-10-22T18-22-56-873Z.ndjson +0 -218
- /package/dist/sdk/{check-execution-engine-2YYKUUSH.mjs.map → check-provider-registry-534KL5HT.mjs.map} +0 -0
- /package/dist/sdk/{check-execution-engine-6QJXYYON.mjs.map → command-executor-TYUV6HUS.mjs.map} +0 -0
- /package/dist/sdk/{check-execution-engine-PJZ4ZOKG.mjs.map → config-YNC2EOOT.mjs.map} +0 -0
- /package/dist/sdk/{config-merger-TWUBWFC2.mjs.map → config-merger-PX3WIT57.mjs.map} +0 -0
- /package/dist/sdk/{liquid-extensions-KVL4MKRH.mjs.map → failure-condition-evaluator-YGTF2GHG.mjs.map} +0 -0
- /package/dist/sdk/{tracer-init-WC75N5NW.mjs.map → liquid-extensions-PKWCKK7E.mjs.map} +0 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# Failure Routing (Retry/Goto/Remediate) — RFC
|
|
2
|
+
|
|
3
|
+
Status: Draft
|
|
4
|
+
|
|
5
|
+
Last updated: 2025-10-03
|
|
6
|
+
|
|
7
|
+
Owner: Visor team
|
|
8
|
+
|
|
9
|
+
## Objectives
|
|
10
|
+
|
|
11
|
+
- Enable a workflow step to handle failure by retrying, jumping back to a prior step, or running a remediation step, then continuing.
|
|
12
|
+
- Keep runs deterministic and safe: no infinite loops, clear audit trail, reproducible results.
|
|
13
|
+
- Make it ergonomic in YAML and backward-compatible with existing Visor configs.
|
|
14
|
+
|
|
15
|
+
## Approach
|
|
16
|
+
|
|
17
|
+
- Add “failure routes” to the execution graph: normal success edges remain; failure edges are now explicit.
|
|
18
|
+
- Introduce lightweight “checkpoints” via step IDs (no heavy snapshots). Re-execution rebuilds state deterministically.
|
|
19
|
+
- Provide per-step retry policies with backoff and a global loop budget to avoid livelock.
|
|
20
|
+
|
|
21
|
+
## Config Sketch (MVP)
|
|
22
|
+
|
|
23
|
+
Proposed additions use Visor’s existing 2.0 style (type/exec/depends_on). New keys are `on_fail`, `on_success`, and optional top‑level `routing` for defaults.
|
|
24
|
+
|
|
25
|
+
```yaml
|
|
26
|
+
version: "2.0"
|
|
27
|
+
|
|
28
|
+
# Optional global defaults for routing (new)
|
|
29
|
+
routing:
|
|
30
|
+
max_loops: 10 # per-scope cap on routing transitions
|
|
31
|
+
defaults:
|
|
32
|
+
on_fail:
|
|
33
|
+
retry:
|
|
34
|
+
max: 0 # attempts per step on failure (0 = disabled)
|
|
35
|
+
backoff:
|
|
36
|
+
mode: fixed # fixed|exponential
|
|
37
|
+
delay_ms: 2000 # initial delay in milliseconds
|
|
38
|
+
|
|
39
|
+
steps:
|
|
40
|
+
setup-env:
|
|
41
|
+
type: command
|
|
42
|
+
exec: "npm ci"
|
|
43
|
+
|
|
44
|
+
unit-tests:
|
|
45
|
+
type: command
|
|
46
|
+
exec: "npm test"
|
|
47
|
+
on_fail:
|
|
48
|
+
retry: { max: 1, backoff: { mode: fixed, delay_ms: 3000 } }
|
|
49
|
+
goto: setup-env # jump back, then continue forward
|
|
50
|
+
# Dynamic routing alternatives (evaluated on failure):
|
|
51
|
+
# - goto_js returns a step-id or null
|
|
52
|
+
# - run_js returns an array of step-ids or []
|
|
53
|
+
goto_js: |
|
|
54
|
+
// Provided variables: step, attempt, loop, error, foreach, outputs, pr, files
|
|
55
|
+
if (error.message?.includes('module not found')) return 'setup-env';
|
|
56
|
+
return null;
|
|
57
|
+
|
|
58
|
+
run_js: |
|
|
59
|
+
// Optionally compute remediation steps dynamically.
|
|
60
|
+
const fixes = [];
|
|
61
|
+
if (error.stderr?.includes('lint')) fixes.push('lint-fix');
|
|
62
|
+
return fixes;
|
|
63
|
+
|
|
64
|
+
lint-fix:
|
|
65
|
+
type: command
|
|
66
|
+
exec: "npm run lint:fix"
|
|
67
|
+
|
|
68
|
+
build:
|
|
69
|
+
type: command
|
|
70
|
+
exec: "npm run build"
|
|
71
|
+
on_fail:
|
|
72
|
+
run: [lint-fix] # remediation steps
|
|
73
|
+
retry: { max: 1 }
|
|
74
|
+
|
|
75
|
+
summary:
|
|
76
|
+
type: command
|
|
77
|
+
exec: "node scripts/summarize.js"
|
|
78
|
+
on_success:
|
|
79
|
+
run: [notify]
|
|
80
|
+
# Allow goto on success (ancestor-only) with optional dynamic variant
|
|
81
|
+
goto: unit-tests
|
|
82
|
+
goto_js: |
|
|
83
|
+
// Jump back to re-validate if summary indicates retest
|
|
84
|
+
if (/retest/i.test(outputs?.overview ?? '')) return 'unit-tests';
|
|
85
|
+
return null;
|
|
86
|
+
|
|
87
|
+
notify:
|
|
88
|
+
type: command
|
|
89
|
+
exec: "echo notify"
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Execution Semantics
|
|
93
|
+
|
|
94
|
+
- Retry: Re-run the same step up to `retry.max` with backoff; counts toward `max_loops`.
|
|
95
|
+
- Goto (failure): After retries are exhausted, jump to `goto` step (ancestor-only), then proceed forward to eventually re-run the failed step.
|
|
96
|
+
- Run (failure remediation): On failure, run listed steps in order; if all succeed, re-attempt the failed step once (counted).
|
|
97
|
+
- on_success actions: After a step succeeds, run `on_success.run` plus `run_js` (if any), then optionally `goto`/`goto_js` (ancestor-only). After the jump, proceed forward along the normal path.
|
|
98
|
+
- Loop safety: Maintain per-check attempt counters (for retries) and a per-scope `max_loops` routing counter covering ALL routing transitions (failure and success). Abort with a clear error if exceeded.
|
|
99
|
+
- State: Carry forward run context; recompute outputs of any steps that are re-executed (no stale artifact reuse by default).
|
|
100
|
+
- Telemetry: Log structured events for failure, retry, goto, remediation, and loop-abort to aid debugging.
|
|
101
|
+
|
|
102
|
+
### Dynamic Routing (goto_js, run_js)
|
|
103
|
+
|
|
104
|
+
- When present, `goto_js` and `run_js` are evaluated inside a safe JS sandbox on routing events:
|
|
105
|
+
- on_fail: evaluate both after a failure.
|
|
106
|
+
- on_success: evaluate `run_js` after success; evaluate `goto_js` after run/run_js.
|
|
107
|
+
- Provided variables (read‑only):
|
|
108
|
+
- `step` – current step metadata: `{ id, tags, group }`.
|
|
109
|
+
- `attempt` – attempt count for this step (1 on first try).
|
|
110
|
+
- `loop` – number of routing transitions taken so far in this scope.
|
|
111
|
+
- `error` – for failures: `{ message, code, stdout, stderr, exitCode }` (truncated strings).
|
|
112
|
+
- `foreach` – if inside forEach: `{ key, index, total, path }`, else `null`.
|
|
113
|
+
- `outputs` – dependency outputs (same as in templates/transform_js).
|
|
114
|
+
- `pr`, `files`, `env` – standard Visor template context.
|
|
115
|
+
- Results:
|
|
116
|
+
- `goto_js`: must return a step-id string or `null`/`undefined`.
|
|
117
|
+
- `run_js`: must return an array of step-id strings (may be empty).
|
|
118
|
+
- Precedence and merge:
|
|
119
|
+
- If `goto_js` returns a valid id, it overrides static `goto`.
|
|
120
|
+
- `run_js` result is concatenated after static `run` (duplicates removed, original order preserved).
|
|
121
|
+
- on_success ordering: execute run/run_js first; then evaluate and apply goto/goto_js.
|
|
122
|
+
- Determinism: No IO, no randomness unless seeded; evaluation is pure and time-limited.
|
|
123
|
+
|
|
124
|
+
### Safe Evaluation
|
|
125
|
+
|
|
126
|
+
- Engine executes JS via a restricted VM with:
|
|
127
|
+
- Whitelisted globals only: `Math`, `JSON`, limited `Date.now()`; no `require`, no `process`, no timers, no async.
|
|
128
|
+
- CPU time limit (e.g., 25 ms) and memory cap; code size limit (e.g., 8 KB) per evaluation.
|
|
129
|
+
- Inputs are immutable; outputs validated (types, size) before use.
|
|
130
|
+
- Any sandbox violation or timeout is treated as evaluation failure; static `on_fail` keys still apply.
|
|
131
|
+
|
|
132
|
+
### Loop Counters and Routing Behavior
|
|
133
|
+
|
|
134
|
+
- Per-check counters: Each step maintains its own `attempt` counter; increments on each retry of that step.
|
|
135
|
+
- Per-scope loop budget: `max_loops` is tracked per execution scope and counts ALL routing transitions (failure gotos, success gotos, and remediation-triggered reattempts):
|
|
136
|
+
- Root scope: applies across the top-level graph.
|
|
137
|
+
- `forEach` scope: each item has its own independent loop counter (a separate “parallel universe”).
|
|
138
|
+
- On exceeding `max_loops`, the engine fails the current scope immediately with a clear error (no silent recovery), regardless of whether routing was triggered by failure or success.
|
|
139
|
+
- Counters are keyed by `(scopeId, stepId)`; `scopeId` changes for each `forEach` item.
|
|
140
|
+
|
|
141
|
+
### forEach Semantics
|
|
142
|
+
|
|
143
|
+
- Each `forEach` item creates an isolated subgraph with:
|
|
144
|
+
- Independent step attempt counters and loop budget.
|
|
145
|
+
- Local name resolution for `goto`/`run`/`*_js` targets (must reference steps within the same scope).
|
|
146
|
+
- Cross-scope jumps are disallowed by default to prevent non-local effects and loops.
|
|
147
|
+
- The `foreach` context exposes `{ key, index, total, path }` to drive dynamic decisions in `*_js` code.
|
|
148
|
+
|
|
149
|
+
## Engine Work
|
|
150
|
+
|
|
151
|
+
- Represent failure edges in the internal DAG and scheduler.
|
|
152
|
+
- Implement retry/backoff, goto transitions, and remediation chains.
|
|
153
|
+
- Add loop detection with `max_loops` and per-step retry budgets.
|
|
154
|
+
- Validate `goto` targets, detect cycles, and provide actionable errors.
|
|
155
|
+
- Add a JS evaluator module with sandboxing, type guards, and timeouts.
|
|
156
|
+
- Integrate dynamic routing evaluation and precedence rules.
|
|
157
|
+
|
|
158
|
+
## CLI and UX
|
|
159
|
+
|
|
160
|
+
- Flags: `--on-fail-max-loops`, `--retry-max`, `--no-failure-routing` (to disable feature globally).
|
|
161
|
+
- Run summary shows failure routes taken with timestamps and attempt counts.
|
|
162
|
+
- Debug: when `--debug` is set, include evaluated `*_js` results (with sensitive data redacted), sandbox timing, retry/backoff decisions, goto/run transitions, and per-scope loop counters.
|
|
163
|
+
|
|
164
|
+
## Tests and Demo
|
|
165
|
+
|
|
166
|
+
- Unit tests: each policy in isolation (retry, goto, remediation, loop-abort, `goto_js`, `run_js`, timeouts, type errors).
|
|
167
|
+
- Integration: top-level and `forEach` flows, ensuring per-item loop isolation and correct scoping.
|
|
168
|
+
|
|
169
|
+
## Acceptance Criteria
|
|
170
|
+
|
|
171
|
+
- Goto: Given a failing step with `goto: setup-env`, engine jumps to `setup-env`, proceeds, and re-runs the failed step.
|
|
172
|
+
- Remediation: Given `run: [lint-fix]`, if remediation succeeds, the failed step re-runs once; if remediation fails, the run stops with a clear message.
|
|
173
|
+
- Retry: Per-step retries respect backoff and caps; global `max_loops` prevents infinite ping-pong.
|
|
174
|
+
- Compatibility: Configs without `on_fail` behave exactly as today.
|
|
175
|
+
- Observability: Logs show ordered trace of retries and jumps; exit codes reflect final outcome.
|
|
176
|
+
- Dynamic routing: `goto_js` and `run_js` work with pure, time-limited evaluation; precedence and merging behave as specified.
|
|
177
|
+
- forEach: Each item runs with isolated counters; `*_js` receives `foreach` context and cannot jump across scopes.
|
|
178
|
+
- on_success goto: After a step succeeds, `goto` (ancestor-only) can jump back to a prior step; with `max_loops` enforcement the run either converges or fails with a clear trace.
|
|
179
|
+
|
|
180
|
+
## Open Questions
|
|
181
|
+
|
|
182
|
+
- Should `goto` support only step IDs, or also labeled checkpoints (e.g., `goto_checkpoint`)?
|
|
183
|
+
- Should remediation steps be “ephemeral” (only run on failure) or regular steps reused elsewhere?
|
|
184
|
+
- Any preferred global default (e.g., retry once everywhere with linear 2s backoff)?
|
|
185
|
+
- Should we allow opt-in cross-scope targets via explicit qualifiers (e.g., `parent:setup-env`), guarded by additional loop caps?
|
|
186
|
+
|
|
187
|
+
## Next Steps
|
|
188
|
+
|
|
189
|
+
1. Audit current workflow engine & failure handling.
|
|
190
|
+
2. Finalize config keys and schema validation messages.
|
|
191
|
+
3. Implement engine changes with loop safeguards.
|
|
192
|
+
4. Add tests and the `lab-05-retry.yaml` demo.
|
|
193
|
+
5. Extend docs and workshop slides.
|
|
@@ -0,0 +1,507 @@
|
|
|
1
|
+
# Failure Routing (Auto-fix Loops)
|
|
2
|
+
|
|
3
|
+
This guide explains how to configure Visor to automatically remediate failures and re-run steps until convergence, using safe, deterministic routing.
|
|
4
|
+
|
|
5
|
+
## What You Can Do
|
|
6
|
+
|
|
7
|
+
- Retry failed steps with backoff (fixed or exponential)
|
|
8
|
+
- Run remediation steps on failure (e.g., `lint-fix`, `npm ci`)
|
|
9
|
+
- Jump back to an ancestor step on failure or success (`goto`)
|
|
10
|
+
- Compute remediation and targets dynamically with safe JS (`run_js`, `goto_js`)
|
|
11
|
+
- Protect against infinite loops with per-scope loop caps and per-step attempt counters
|
|
12
|
+
- Use the same semantics inside forEach branches (each item is isolated)
|
|
13
|
+
|
|
14
|
+
## Outputs Surface (Routing JS)
|
|
15
|
+
|
|
16
|
+
When writing `run_js`/`goto_js`, you have three accessors:
|
|
17
|
+
|
|
18
|
+
- `outputs['x']` — nearest value for check `x` in the current snapshot and scope.
|
|
19
|
+
- `outputs_raw['x']` — aggregate value for `x` (e.g., the full array from a forEach parent).
|
|
20
|
+
- `outputs.history['x']` (alias: `outputs_history['x']`) — all historical values for `x` up to this snapshot.
|
|
21
|
+
|
|
22
|
+
Precedence for `outputs['x']` in routing sandboxes:
|
|
23
|
+
- If running inside a forEach item of `x`, resolves to that item’s value.
|
|
24
|
+
- Else prefer an ancestor scope value of `x`.
|
|
25
|
+
- Else the latest committed value of `x` in the snapshot.
|
|
26
|
+
|
|
27
|
+
Quick example using `outputs_raw` in `goto_js`:
|
|
28
|
+
|
|
29
|
+
```yaml
|
|
30
|
+
checks:
|
|
31
|
+
list:
|
|
32
|
+
type: command
|
|
33
|
+
exec: echo '["a","b","c"]'
|
|
34
|
+
forEach: true
|
|
35
|
+
|
|
36
|
+
decide:
|
|
37
|
+
type: script
|
|
38
|
+
depends_on: [list]
|
|
39
|
+
content: 'return { n: (outputs_raw["list"] || []).length }'
|
|
40
|
+
on_success:
|
|
41
|
+
goto_js: |
|
|
42
|
+
// Branch by aggregate size, not per-item value
|
|
43
|
+
return (outputs_raw['list'] || []).length >= 3 ? 'bulk-process' : null;
|
|
44
|
+
|
|
45
|
+
bulk-process: { type: log, message: 'bulk mode' }
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Tip: `outputs_raw` is also available in provider templates (AI/command/log/memory), mirroring routing JS.
|
|
49
|
+
|
|
50
|
+
## Quick Examples
|
|
51
|
+
|
|
52
|
+
Retry + goto on failure:
|
|
53
|
+
```yaml
|
|
54
|
+
version: "2.0"
|
|
55
|
+
routing: { max_loops: 5 }
|
|
56
|
+
steps:
|
|
57
|
+
setup: { type: command, exec: "echo setup" }
|
|
58
|
+
build:
|
|
59
|
+
type: command
|
|
60
|
+
depends_on: [setup]
|
|
61
|
+
exec: |
|
|
62
|
+
test -f .ok || (echo first try fails >&2; touch .ok; exit 1)
|
|
63
|
+
echo ok
|
|
64
|
+
on_fail:
|
|
65
|
+
goto: setup
|
|
66
|
+
retry: { max: 1, backoff: { mode: exponential, delay_ms: 400 } }
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
on_success jump-back once + post-steps:
|
|
70
|
+
```yaml
|
|
71
|
+
steps:
|
|
72
|
+
unit: { type: command, exec: "echo unit" }
|
|
73
|
+
build:
|
|
74
|
+
type: command
|
|
75
|
+
depends_on: [unit]
|
|
76
|
+
exec: "echo build"
|
|
77
|
+
on_success:
|
|
78
|
+
run: [notify]
|
|
79
|
+
goto_js: |
|
|
80
|
+
return attempt === 1 ? 'unit' : null; # only once
|
|
81
|
+
notify: { type: command, exec: "echo notify" }
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Note on outputs access:
|
|
85
|
+
- `outputs['step-id']` returns the latest value for that step in the current snapshot.
|
|
86
|
+
- `outputs.history['step-id']` returns the cross-loop history array.
|
|
87
|
+
- `outputs_history['step-id']` is an alias for `outputs.history['step-id']` and is available in routing JS and provider templates.
|
|
88
|
+
See [Output History](./output-history.md) for more details.
|
|
89
|
+
|
|
90
|
+
forEach remediation with retry:
|
|
91
|
+
```yaml
|
|
92
|
+
steps:
|
|
93
|
+
list: { type: command, exec: "echo '[\\"a\\",\\"b\\"]'", forEach: true }
|
|
94
|
+
mark: { type: command, depends_on: [list], exec: "touch .m_{{ outputs.list }}" }
|
|
95
|
+
process:
|
|
96
|
+
type: command
|
|
97
|
+
depends_on: [list]
|
|
98
|
+
exec: "test -f .m_{{ outputs.list }} || exit 1"
|
|
99
|
+
on_fail:
|
|
100
|
+
run: [mark]
|
|
101
|
+
retry: { max: 1 }
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Configuration Keys
|
|
105
|
+
|
|
106
|
+
Top-level defaults (optional):
|
|
107
|
+
```yaml
|
|
108
|
+
routing:
|
|
109
|
+
max_loops: 10 # per-scope cap on routing transitions
|
|
110
|
+
defaults:
|
|
111
|
+
on_fail:
|
|
112
|
+
retry: { max: 1, backoff: { mode: fixed, delay_ms: 300 } }
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Per-step actions:
|
|
116
|
+
- `on_fail`:
|
|
117
|
+
- `retry`: `{ max, backoff: { mode: fixed|exponential, delay_ms } }`
|
|
118
|
+
- `run`: `[step-id, …]`
|
|
119
|
+
- `goto`: `step-id` (ancestor-only)
|
|
120
|
+
- `run_js`: JS returning `string[]`
|
|
121
|
+
- `goto_js`: JS returning `string | null`
|
|
122
|
+
- `on_success`:
|
|
123
|
+
- `run`, `goto`, `run_js`, `goto_js` (same types and constraints as above)
|
|
124
|
+
|
|
125
|
+
## Semantics
|
|
126
|
+
|
|
127
|
+
- Retry: re-run the same step up to `retry.max`; backoff adds fixed or exponential delay with deterministic jitter.
|
|
128
|
+
- Run: on failure (or success), run listed steps first; if successful, the failed step is re-attempted once (failure path).
|
|
129
|
+
- Goto (ancestor-only): jump back to a previously executed dependency, then continue forward. On success, Visor re-runs the current step once after the jump.
|
|
130
|
+
- Loop safety: `routing.max_loops` counts all routing transitions (runs, gotos, retries). Exceeding it aborts the current scope with a clear error. For a hard cap on repeated executions of the same step, see [Execution Limits](./limits.md).
|
|
131
|
+
- forEach: each item is isolated with its own loop/attempt counters; `*_js` receives `{ foreach: { index, total, parent } }`.
|
|
132
|
+
|
|
133
|
+
### Fan‑out vs. Reduce (Phase 5)
|
|
134
|
+
|
|
135
|
+
You can control how routing targets behave when invoked from a forEach context:
|
|
136
|
+
|
|
137
|
+
- `fanout: map` — schedule the target once per item (runs under each item scope).
|
|
138
|
+
- `fanout: reduce` (or `reduce: true`) — schedule a single aggregation run (default/back‑compat).
|
|
139
|
+
|
|
140
|
+
Where it applies:
|
|
141
|
+
- on_success.run / on_success.goto
|
|
142
|
+
- on_fail.run / on_fail.goto
|
|
143
|
+
- on_finish.run / on_finish.goto (when defined on the forEach producer)
|
|
144
|
+
|
|
145
|
+
Example — per‑item side‑effects via routing:
|
|
146
|
+
```yaml
|
|
147
|
+
checks:
|
|
148
|
+
list:
|
|
149
|
+
type: command
|
|
150
|
+
exec: echo '["a","b","c"]'
|
|
151
|
+
forEach: true
|
|
152
|
+
on_success:
|
|
153
|
+
run: [notify-item]
|
|
154
|
+
|
|
155
|
+
notify-item:
|
|
156
|
+
type: log
|
|
157
|
+
fanout: map # ← run once for each item from 'list'
|
|
158
|
+
message: "Item: {{ outputs['list'] }}"
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Example — single aggregation after forEach:
|
|
162
|
+
```yaml
|
|
163
|
+
checks:
|
|
164
|
+
extract:
|
|
165
|
+
forEach: true
|
|
166
|
+
on_success:
|
|
167
|
+
run: [summarize]
|
|
168
|
+
|
|
169
|
+
summarize:
|
|
170
|
+
type: script
|
|
171
|
+
fanout: reduce # ← single run
|
|
172
|
+
content: |
|
|
173
|
+
const arr = outputs_raw['extract'] || [];
|
|
174
|
+
return { total: arr.length };
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Goto Event Override (goto_event)
|
|
178
|
+
|
|
179
|
+
You can instruct a `goto` jump to simulate a different event so that the target step is filtered as if that event occurred. This is useful when you need to re-run an ancestor step under PR semantics from a different context (e.g., from an issue comment or internal assistant flow).
|
|
180
|
+
|
|
181
|
+
Key points:
|
|
182
|
+
- Add `goto_event: <event>` alongside `goto` or use it with `goto_js`.
|
|
183
|
+
- Valid values are the same as `on:` triggers (e.g., `pr_updated`, `pr_opened`, `issue_comment`, `issue_opened`).
|
|
184
|
+
- During the inline `goto` execution, Visor sets an internal event override:
|
|
185
|
+
- Event filtering uses the overridden event for the target step.
|
|
186
|
+
- `if:` expressions see `event.event_name` derived from the override (e.g., `pr_*` → `pull_request`, `issue_comment` → `issue_comment`, `issue_*` → `issues`).
|
|
187
|
+
- After the jump, the current step is re-run once; the override applies only to the inline target and that immediate re-run.
|
|
188
|
+
- `goto` remains ancestor-only.
|
|
189
|
+
|
|
190
|
+
Example: After `security` succeeds, jump back to `overview` and re-run `security`, evaluating both as if a PR update happened:
|
|
191
|
+
|
|
192
|
+
```yaml
|
|
193
|
+
steps:
|
|
194
|
+
overview:
|
|
195
|
+
type: ai
|
|
196
|
+
on: [pr_opened, pr_updated]
|
|
197
|
+
|
|
198
|
+
security:
|
|
199
|
+
type: ai
|
|
200
|
+
depends_on: [overview]
|
|
201
|
+
on: [pr_opened, pr_updated]
|
|
202
|
+
on_success:
|
|
203
|
+
goto: overview # ancestor-only
|
|
204
|
+
goto_event: pr_updated # simulate PR updated during the jump
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Dynamic variant (only jump on first success):
|
|
208
|
+
|
|
209
|
+
```yaml
|
|
210
|
+
steps:
|
|
211
|
+
quality:
|
|
212
|
+
type: ai
|
|
213
|
+
depends_on: [overview]
|
|
214
|
+
on: [pr_opened, pr_updated]
|
|
215
|
+
on_success:
|
|
216
|
+
goto_js: |
|
|
217
|
+
return attempt === 1 ? 'overview' : null
|
|
218
|
+
goto_event: pr_updated
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
When to use goto_event vs. full re-run:
|
|
222
|
+
- Use `goto_event` for a targeted, in-process jump to a specific ancestor step with PR semantics.
|
|
223
|
+
- Use a higher-level “internal re-invoke” (e.g., synthesize a `pull_request` `synchronize` event and call the action entrypoint) when you need to re-run the entire PR workflow chain from an issue comment trigger.
|
|
224
|
+
|
|
225
|
+
## Dynamic JS (safe, sync only)
|
|
226
|
+
|
|
227
|
+
- `goto_js` / `run_js` are evaluated in a sandbox with:
|
|
228
|
+
- Read-only context: `{ step, attempt, loop, error, foreach, outputs, pr, files, env }`
|
|
229
|
+
- `outputs` contains current values, `outputs.history` contains arrays of all previous values (see [Output History](./output-history.md))
|
|
230
|
+
- Pure sync execution; no IO, no async, no timers, no require/process.
|
|
231
|
+
- Time and size limits (short wall time; small code/output caps) — evaluation failures fall back to static routing.
|
|
232
|
+
|
|
233
|
+
Return types:
|
|
234
|
+
- `goto_js`: a `string` (step id) or `null`/`undefined` to skip.
|
|
235
|
+
- `run_js`: a `string[]` (may be empty). Duplicates are removed preserving order.
|
|
236
|
+
|
|
237
|
+
## Guardrails & Tips
|
|
238
|
+
|
|
239
|
+
- Keep `max_loops` small (5–10). Add retries sparingly and prefer remediation over blind loops.
|
|
240
|
+
- Restrict `goto` to ancestors to preserve dependency semantics and avoid hard-to-reason paths.
|
|
241
|
+
- For expensive remediations, put them behind `run_js` conditions keyed to `error.message`/`stderr`.
|
|
242
|
+
- In CI, you can override defaults with CLI flags (future): `--on-fail-max-loops`, `--retry-max`.
|
|
243
|
+
|
|
244
|
+
## on_finish Hook (forEach Aggregation & Routing)
|
|
245
|
+
|
|
246
|
+
The `on_finish` hook is a special routing action that triggers **once** after a `forEach` check completes **all** of its dependent checks across **all** iterations. This is the ideal place to aggregate results from forEach iterations and make routing decisions based on the collective outcome.
|
|
247
|
+
|
|
248
|
+
### When on_finish Triggers
|
|
249
|
+
|
|
250
|
+
- Only on checks with `forEach: true`
|
|
251
|
+
- Triggers **after** all dependent checks complete all their iterations
|
|
252
|
+
- Does **not** trigger if the forEach array is empty
|
|
253
|
+
- Executes even if some iterations failed (you decide how to handle failures)
|
|
254
|
+
|
|
255
|
+
### Execution Order
|
|
256
|
+
|
|
257
|
+
```
|
|
258
|
+
forEach check executes once → outputs array [item1, item2, ...]
|
|
259
|
+
↓
|
|
260
|
+
All dependent checks execute N times (forEach propagation)
|
|
261
|
+
- dependent-check runs for item1
|
|
262
|
+
- dependent-check runs for item2
|
|
263
|
+
- ...
|
|
264
|
+
↓
|
|
265
|
+
on_finish.run executes (checks run sequentially in order)
|
|
266
|
+
↓
|
|
267
|
+
on_finish.run_js evaluates (dynamic check selection)
|
|
268
|
+
↓
|
|
269
|
+
on_finish.goto_js evaluates (routing decision)
|
|
270
|
+
↓
|
|
271
|
+
If goto returned, jump to ancestor check and re-run current check
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### How It Differs from on_success and on_fail
|
|
275
|
+
|
|
276
|
+
| Hook | Triggers When | Use Case |
|
|
277
|
+
|------|--------------|----------|
|
|
278
|
+
| `on_fail` | Check fails | Handle single check failure, retry, remediate |
|
|
279
|
+
| `on_success` | Check succeeds | Post-process single check success |
|
|
280
|
+
| `on_finish` | **All** forEach dependents complete | Aggregate **all** forEach iteration results, decide next step |
|
|
281
|
+
|
|
282
|
+
Key difference: `on_finish` sees the **complete picture** of all forEach iterations and all dependent check results, making it perfect for validation and aggregation scenarios.
|
|
283
|
+
|
|
284
|
+
### Available Context in on_finish
|
|
285
|
+
|
|
286
|
+
The `on_finish` hooks have access to the complete execution context:
|
|
287
|
+
|
|
288
|
+
```javascript
|
|
289
|
+
{
|
|
290
|
+
step: { id: 'extract-facts', tags: [...], group: '...' },
|
|
291
|
+
attempt: 1, // Current attempt number for this check
|
|
292
|
+
loop: 2, // Current loop number in routing
|
|
293
|
+
outputs: {
|
|
294
|
+
'extract-facts': [...], // Array of forEach items
|
|
295
|
+
'validate-fact': [...], // Array of ALL dependent results
|
|
296
|
+
},
|
|
297
|
+
outputs.history: {
|
|
298
|
+
'extract-facts': [[...], ...], // Cross-loop history
|
|
299
|
+
'validate-fact': [[...], ...], // All results from all iterations
|
|
300
|
+
},
|
|
301
|
+
// Alias (also available):
|
|
302
|
+
outputs_history: {
|
|
303
|
+
'extract-facts': [[...], ...],
|
|
304
|
+
'validate-fact': [[...], ...],
|
|
305
|
+
},
|
|
306
|
+
forEach: {
|
|
307
|
+
total: 3, // Total number of items
|
|
308
|
+
successful: 3, // Number of successful iterations
|
|
309
|
+
failed: 0, // Number of failed iterations
|
|
310
|
+
items: [...] // The forEach items array
|
|
311
|
+
},
|
|
312
|
+
memory, // Memory access functions
|
|
313
|
+
pr, // PR metadata
|
|
314
|
+
files, // Changed files
|
|
315
|
+
env // Environment variables
|
|
316
|
+
}
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Configuration
|
|
320
|
+
|
|
321
|
+
```yaml
|
|
322
|
+
checks:
|
|
323
|
+
extract-facts:
|
|
324
|
+
type: ai
|
|
325
|
+
forEach: true
|
|
326
|
+
# ... regular check configuration ...
|
|
327
|
+
|
|
328
|
+
on_finish:
|
|
329
|
+
# Optional: Run additional checks to aggregate results
|
|
330
|
+
run: [aggregate-validations]
|
|
331
|
+
|
|
332
|
+
# Optional: Dynamically compute additional checks to run
|
|
333
|
+
run_js: |
|
|
334
|
+
return error ? ['log-error'] : [];
|
|
335
|
+
|
|
336
|
+
# Optional: Static routing decision
|
|
337
|
+
goto: previous-check
|
|
338
|
+
|
|
339
|
+
# Optional: Dynamic routing decision
|
|
340
|
+
goto_js: |
|
|
341
|
+
const allValid = memory.get('all_facts_valid', 'fact-validation');
|
|
342
|
+
const attempt = memory.get('fact_validation_attempt', 'fact-validation') || 0;
|
|
343
|
+
|
|
344
|
+
if (allValid) {
|
|
345
|
+
return null; // Continue normal flow
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (attempt >= 1) {
|
|
349
|
+
return null; // Max attempts reached, give up
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Retry with correction context
|
|
353
|
+
memory.increment('fact_validation_attempt', 1, 'fact-validation');
|
|
354
|
+
return 'issue-assistant'; // Jump back to ancestor
|
|
355
|
+
|
|
356
|
+
# Optional: Override event for goto target
|
|
357
|
+
goto_event: pr_updated
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Common Patterns
|
|
361
|
+
|
|
362
|
+
#### Pattern 1: Validation with Retry
|
|
363
|
+
|
|
364
|
+
Aggregate validation results and retry if any fail:
|
|
365
|
+
|
|
366
|
+
```yaml
|
|
367
|
+
checks:
|
|
368
|
+
extract-claims:
|
|
369
|
+
type: ai
|
|
370
|
+
forEach: true
|
|
371
|
+
transform_js: JSON.parse(output).claims
|
|
372
|
+
on_finish:
|
|
373
|
+
run: [aggregate-results]
|
|
374
|
+
goto_js: |
|
|
375
|
+
const allValid = memory.get('all_valid', 'validation');
|
|
376
|
+
const attempt = memory.get('attempt', 'validation') || 0;
|
|
377
|
+
|
|
378
|
+
if (allValid || attempt >= 2) {
|
|
379
|
+
return null; // Success or max attempts
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
memory.increment('attempt', 1, 'validation');
|
|
383
|
+
return 'generate-response'; // Retry
|
|
384
|
+
|
|
385
|
+
validate-claim:
|
|
386
|
+
type: ai
|
|
387
|
+
depends_on: [extract-claims]
|
|
388
|
+
# Validates each claim individually
|
|
389
|
+
|
|
390
|
+
aggregate-results:
|
|
391
|
+
type: script
|
|
392
|
+
content: |
|
|
393
|
+
const results = outputs.history['validate-claim'];
|
|
394
|
+
const allValid = results.every(r => r.is_valid);
|
|
395
|
+
memory.set('all_valid', allValid, 'validation');
|
|
396
|
+
return { total: results.length, valid: results.filter(r => r.is_valid).length };
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
#### Pattern 2: Conditional Post-Processing
|
|
400
|
+
|
|
401
|
+
Run different post-processing based on forEach results:
|
|
402
|
+
|
|
403
|
+
```yaml
|
|
404
|
+
checks:
|
|
405
|
+
scan-files:
|
|
406
|
+
type: command
|
|
407
|
+
forEach: true
|
|
408
|
+
exec: "find . -name '*.ts'"
|
|
409
|
+
on_finish:
|
|
410
|
+
run_js: |
|
|
411
|
+
const hasErrors = outputs.history['analyze-file'].some(r => r.errors > 0);
|
|
412
|
+
return hasErrors ? ['generate-fix-pr'] : ['post-success-comment'];
|
|
413
|
+
|
|
414
|
+
analyze-file:
|
|
415
|
+
type: command
|
|
416
|
+
depends_on: [scan-files]
|
|
417
|
+
exec: "eslint {{ outputs['scan-files'] }}"
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
#### Pattern 3: Multi-Dependent Aggregation
|
|
421
|
+
|
|
422
|
+
Aggregate results from **multiple** dependent checks:
|
|
423
|
+
|
|
424
|
+
```yaml
|
|
425
|
+
checks:
|
|
426
|
+
extract-facts:
|
|
427
|
+
type: ai
|
|
428
|
+
forEach: true
|
|
429
|
+
on_finish:
|
|
430
|
+
run: [aggregate-all-validations]
|
|
431
|
+
goto_js: |
|
|
432
|
+
const securityValid = memory.get('security_valid', 'validation');
|
|
433
|
+
const technicalValid = memory.get('technical_valid', 'validation');
|
|
434
|
+
const formatValid = memory.get('format_valid', 'validation');
|
|
435
|
+
|
|
436
|
+
if (!securityValid || !technicalValid || !formatValid) {
|
|
437
|
+
return 'retry-with-context';
|
|
438
|
+
}
|
|
439
|
+
return null;
|
|
440
|
+
|
|
441
|
+
validate-security:
|
|
442
|
+
type: ai
|
|
443
|
+
depends_on: [extract-facts]
|
|
444
|
+
# Runs N times, validates security aspects
|
|
445
|
+
|
|
446
|
+
validate-technical:
|
|
447
|
+
type: ai
|
|
448
|
+
depends_on: [extract-facts]
|
|
449
|
+
# Runs N times, validates technical aspects
|
|
450
|
+
|
|
451
|
+
validate-format:
|
|
452
|
+
type: ai
|
|
453
|
+
depends_on: [extract-facts]
|
|
454
|
+
# Runs N times, validates format/style
|
|
455
|
+
|
|
456
|
+
aggregate-all-validations:
|
|
457
|
+
type: script
|
|
458
|
+
content: |
|
|
459
|
+
// Access ALL results from ALL dependent checks
|
|
460
|
+
const securityResults = outputs.history['validate-security'];
|
|
461
|
+
const technicalResults = outputs.history['validate-technical'];
|
|
462
|
+
const formatResults = outputs.history['validate-format'];
|
|
463
|
+
|
|
464
|
+
memory.set('security_valid', securityResults.every(r => r.is_valid), 'validation');
|
|
465
|
+
memory.set('technical_valid', technicalResults.every(r => r.is_valid), 'validation');
|
|
466
|
+
memory.set('format_valid', formatResults.every(r => r.is_valid), 'validation');
|
|
467
|
+
|
|
468
|
+
return { aggregated: true };
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
### Error Handling
|
|
472
|
+
|
|
473
|
+
- If `on_finish.run` checks fail, the forEach check is marked as failed
|
|
474
|
+
- If `goto_js` throws an error, the engine falls back to static `goto` (if present)
|
|
475
|
+
- Clear error messages are logged for debugging
|
|
476
|
+
- Loop safety: `on_finish.goto` counts toward `max_loops`
|
|
477
|
+
|
|
478
|
+
### Best Practices
|
|
479
|
+
|
|
480
|
+
1. **Use for Aggregation**: Perfect for collecting and analyzing results from all forEach iterations
|
|
481
|
+
2. **Memory for State**: Store aggregated results in memory for use in routing decisions and downstream checks
|
|
482
|
+
3. **Fail Gracefully**: Handle both success and failure scenarios in `goto_js`
|
|
483
|
+
4. **Limit Retries**: Use attempt counters to prevent infinite loops
|
|
484
|
+
5. **Log Decisions**: Use `log()` in JS to debug routing decisions
|
|
485
|
+
6. **Validate First**: Run aggregation checks before routing to ensure data is ready
|
|
486
|
+
|
|
487
|
+
### Debugging
|
|
488
|
+
|
|
489
|
+
```javascript
|
|
490
|
+
// In on_finish.goto_js
|
|
491
|
+
log('forEach stats:', forEach);
|
|
492
|
+
log('All results:', outputs.history['dependent-check']);
|
|
493
|
+
log('Current attempt:', attempt, 'loop:', loop);
|
|
494
|
+
|
|
495
|
+
const results = outputs.history['validate-fact'];
|
|
496
|
+
log('Valid:', results.filter(r => r.is_valid).length);
|
|
497
|
+
log('Invalid:', results.filter(r => !r.is_valid).length);
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
## Full Examples
|
|
501
|
+
|
|
502
|
+
See the repository examples:
|
|
503
|
+
- `examples/routing-basic.yaml`
|
|
504
|
+
- `examples/routing-on-success.yaml`
|
|
505
|
+
- `examples/routing-foreach.yaml`
|
|
506
|
+
- `examples/routing-dynamic-js.yaml`
|
|
507
|
+
- `examples/fact-validator.yaml` - Complete `on_finish` example with validation and retry
|