@substrate-ai/factory 0.20.1 → 0.20.3
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 +39 -0
- package/package.json +6 -3
- package/dist/__tests__/config.test.d.ts +0 -11
- package/dist/__tests__/config.test.d.ts.map +0 -1
- package/dist/__tests__/config.test.js +0 -215
- package/dist/__tests__/config.test.js.map +0 -1
- package/dist/__tests__/factory-run-command.test.d.ts +0 -12
- package/dist/__tests__/factory-run-command.test.d.ts.map +0 -1
- package/dist/__tests__/factory-run-command.test.js +0 -454
- package/dist/__tests__/factory-run-command.test.js.map +0 -1
- package/dist/__tests__/factory-validate-command.test.d.ts +0 -15
- package/dist/__tests__/factory-validate-command.test.d.ts.map +0 -1
- package/dist/__tests__/factory-validate-command.test.js +0 -339
- package/dist/__tests__/factory-validate-command.test.js.map +0 -1
- package/dist/__tests__/fixtures/advanced-cross-project-validation.dot.d.ts +0 -72
- package/dist/__tests__/fixtures/advanced-cross-project-validation.dot.d.ts.map +0 -1
- package/dist/__tests__/fixtures/advanced-cross-project-validation.dot.js +0 -121
- package/dist/__tests__/fixtures/advanced-cross-project-validation.dot.js.map +0 -1
- package/dist/__tests__/fixtures/llm-edge-routing.dot.d.ts +0 -28
- package/dist/__tests__/fixtures/llm-edge-routing.dot.d.ts.map +0 -1
- package/dist/__tests__/fixtures/llm-edge-routing.dot.js +0 -55
- package/dist/__tests__/fixtures/llm-edge-routing.dot.js.map +0 -1
- package/dist/__tests__/fixtures/manager-loop.dot.d.ts +0 -34
- package/dist/__tests__/fixtures/manager-loop.dot.d.ts.map +0 -1
- package/dist/__tests__/fixtures/manager-loop.dot.js +0 -61
- package/dist/__tests__/fixtures/manager-loop.dot.js.map +0 -1
- package/dist/__tests__/fixtures/parallel-fan-out-fan-in.dot.d.ts +0 -42
- package/dist/__tests__/fixtures/parallel-fan-out-fan-in.dot.d.ts.map +0 -1
- package/dist/__tests__/fixtures/parallel-fan-out-fan-in.dot.js +0 -118
- package/dist/__tests__/fixtures/parallel-fan-out-fan-in.dot.js.map +0 -1
- package/dist/__tests__/fixtures/subgraph-parent.dot.d.ts +0 -35
- package/dist/__tests__/fixtures/subgraph-parent.dot.d.ts.map +0 -1
- package/dist/__tests__/fixtures/subgraph-parent.dot.js +0 -69
- package/dist/__tests__/fixtures/subgraph-parent.dot.js.map +0 -1
- package/dist/__tests__/integration/advanced-graph-events.test.d.ts +0 -19
- package/dist/__tests__/integration/advanced-graph-events.test.d.ts.map +0 -1
- package/dist/__tests__/integration/advanced-graph-events.test.js +0 -288
- package/dist/__tests__/integration/advanced-graph-events.test.js.map +0 -1
- package/dist/__tests__/integration/checkpoint-resume.test.d.ts +0 -10
- package/dist/__tests__/integration/checkpoint-resume.test.d.ts.map +0 -1
- package/dist/__tests__/integration/checkpoint-resume.test.js +0 -125
- package/dist/__tests__/integration/checkpoint-resume.test.js.map +0 -1
- package/dist/__tests__/integration/conditional-pipeline.test.d.ts +0 -10
- package/dist/__tests__/integration/conditional-pipeline.test.d.ts.map +0 -1
- package/dist/__tests__/integration/conditional-pipeline.test.js +0 -106
- package/dist/__tests__/integration/conditional-pipeline.test.js.map +0 -1
- package/dist/__tests__/integration/convergence-validation.test.d.ts +0 -14
- package/dist/__tests__/integration/convergence-validation.test.d.ts.map +0 -1
- package/dist/__tests__/integration/convergence-validation.test.js +0 -449
- package/dist/__tests__/integration/convergence-validation.test.js.map +0 -1
- package/dist/__tests__/integration/epic44-coverage-gate.test.d.ts +0 -12
- package/dist/__tests__/integration/epic44-coverage-gate.test.d.ts.map +0 -1
- package/dist/__tests__/integration/epic44-coverage-gate.test.js +0 -58
- package/dist/__tests__/integration/epic44-coverage-gate.test.js.map +0 -1
- package/dist/__tests__/integration/epic45-coverage-gate.test.d.ts +0 -11
- package/dist/__tests__/integration/epic45-coverage-gate.test.d.ts.map +0 -1
- package/dist/__tests__/integration/epic45-coverage-gate.test.js +0 -64
- package/dist/__tests__/integration/epic45-coverage-gate.test.js.map +0 -1
- package/dist/__tests__/integration/epic46-scenario-primary-executor.test.d.ts +0 -2
- package/dist/__tests__/integration/epic46-scenario-primary-executor.test.d.ts.map +0 -1
- package/dist/__tests__/integration/epic46-scenario-primary-executor.test.js +0 -285
- package/dist/__tests__/integration/epic46-scenario-primary-executor.test.js.map +0 -1
- package/dist/__tests__/integration/events.test.d.ts +0 -8
- package/dist/__tests__/integration/events.test.d.ts.map +0 -1
- package/dist/__tests__/integration/events.test.js +0 -194
- package/dist/__tests__/integration/events.test.js.map +0 -1
- package/dist/__tests__/integration/graphs.d.ts +0 -59
- package/dist/__tests__/integration/graphs.d.ts.map +0 -1
- package/dist/__tests__/integration/graphs.js +0 -164
- package/dist/__tests__/integration/graphs.js.map +0 -1
- package/dist/__tests__/integration/helpers.d.ts +0 -127
- package/dist/__tests__/integration/helpers.d.ts.map +0 -1
- package/dist/__tests__/integration/helpers.js +0 -167
- package/dist/__tests__/integration/helpers.js.map +0 -1
- package/dist/__tests__/integration/integrity.test.d.ts +0 -8
- package/dist/__tests__/integration/integrity.test.d.ts.map +0 -1
- package/dist/__tests__/integration/integrity.test.js +0 -198
- package/dist/__tests__/integration/integrity.test.js.map +0 -1
- package/dist/__tests__/integration/llm-edge-routing.test.d.ts +0 -21
- package/dist/__tests__/integration/llm-edge-routing.test.d.ts.map +0 -1
- package/dist/__tests__/integration/llm-edge-routing.test.js +0 -341
- package/dist/__tests__/integration/llm-edge-routing.test.js.map +0 -1
- package/dist/__tests__/integration/manager-loop.test.d.ts +0 -24
- package/dist/__tests__/integration/manager-loop.test.d.ts.map +0 -1
- package/dist/__tests__/integration/manager-loop.test.js +0 -276
- package/dist/__tests__/integration/manager-loop.test.js.map +0 -1
- package/dist/__tests__/integration/multi-type-graph.test.d.ts +0 -10
- package/dist/__tests__/integration/multi-type-graph.test.d.ts.map +0 -1
- package/dist/__tests__/integration/multi-type-graph.test.js +0 -100
- package/dist/__tests__/integration/multi-type-graph.test.js.map +0 -1
- package/dist/__tests__/integration/parallel-fan-out-fan-in.test.d.ts +0 -22
- package/dist/__tests__/integration/parallel-fan-out-fan-in.test.d.ts.map +0 -1
- package/dist/__tests__/integration/parallel-fan-out-fan-in.test.js +0 -515
- package/dist/__tests__/integration/parallel-fan-out-fan-in.test.js.map +0 -1
- package/dist/__tests__/integration/persistence.test.d.ts +0 -8
- package/dist/__tests__/integration/persistence.test.d.ts.map +0 -1
- package/dist/__tests__/integration/persistence.test.js +0 -129
- package/dist/__tests__/integration/persistence.test.js.map +0 -1
- package/dist/__tests__/integration/pipeline-templates-integration.test.d.ts +0 -16
- package/dist/__tests__/integration/pipeline-templates-integration.test.d.ts.map +0 -1
- package/dist/__tests__/integration/pipeline-templates-integration.test.js +0 -171
- package/dist/__tests__/integration/pipeline-templates-integration.test.js.map +0 -1
- package/dist/__tests__/integration/scenario-pipeline.test.d.ts +0 -11
- package/dist/__tests__/integration/scenario-pipeline.test.d.ts.map +0 -1
- package/dist/__tests__/integration/scenario-pipeline.test.js +0 -243
- package/dist/__tests__/integration/scenario-pipeline.test.js.map +0 -1
- package/dist/__tests__/integration/stylesheet-application.test.d.ts +0 -12
- package/dist/__tests__/integration/stylesheet-application.test.d.ts.map +0 -1
- package/dist/__tests__/integration/stylesheet-application.test.js +0 -119
- package/dist/__tests__/integration/stylesheet-application.test.js.map +0 -1
- package/dist/__tests__/integration/subgraph-execution.test.d.ts +0 -24
- package/dist/__tests__/integration/subgraph-execution.test.d.ts.map +0 -1
- package/dist/__tests__/integration/subgraph-execution.test.js +0 -291
- package/dist/__tests__/integration/subgraph-execution.test.js.map +0 -1
- package/dist/__tests__/integration/validation-errors.test.d.ts +0 -8
- package/dist/__tests__/integration/validation-errors.test.d.ts.map +0 -1
- package/dist/__tests__/integration/validation-errors.test.js +0 -150
- package/dist/__tests__/integration/validation-errors.test.js.map +0 -1
- package/dist/agent/__tests__/loop-detection.test.d.ts +0 -2
- package/dist/agent/__tests__/loop-detection.test.d.ts.map +0 -1
- package/dist/agent/__tests__/loop-detection.test.js +0 -236
- package/dist/agent/__tests__/loop-detection.test.js.map +0 -1
- package/dist/agent/__tests__/loop.test.d.ts +0 -2
- package/dist/agent/__tests__/loop.test.d.ts.map +0 -1
- package/dist/agent/__tests__/loop.test.js +0 -868
- package/dist/agent/__tests__/loop.test.js.map +0 -1
- package/dist/agent/__tests__/truncation.test.d.ts +0 -2
- package/dist/agent/__tests__/truncation.test.d.ts.map +0 -1
- package/dist/agent/__tests__/truncation.test.js +0 -276
- package/dist/agent/__tests__/truncation.test.js.map +0 -1
- package/dist/agent/tools/__tests__/anthropic-tools.test.d.ts +0 -6
- package/dist/agent/tools/__tests__/anthropic-tools.test.d.ts.map +0 -1
- package/dist/agent/tools/__tests__/anthropic-tools.test.js +0 -49
- package/dist/agent/tools/__tests__/anthropic-tools.test.js.map +0 -1
- package/dist/agent/tools/__tests__/environment.test.d.ts +0 -6
- package/dist/agent/tools/__tests__/environment.test.d.ts.map +0 -1
- package/dist/agent/tools/__tests__/environment.test.js +0 -33
- package/dist/agent/tools/__tests__/environment.test.js.map +0 -1
- package/dist/agent/tools/__tests__/gemini-tools.test.d.ts +0 -6
- package/dist/agent/tools/__tests__/gemini-tools.test.d.ts.map +0 -1
- package/dist/agent/tools/__tests__/gemini-tools.test.js +0 -98
- package/dist/agent/tools/__tests__/gemini-tools.test.js.map +0 -1
- package/dist/agent/tools/__tests__/openai-tools.test.d.ts +0 -6
- package/dist/agent/tools/__tests__/openai-tools.test.d.ts.map +0 -1
- package/dist/agent/tools/__tests__/openai-tools.test.js +0 -53
- package/dist/agent/tools/__tests__/openai-tools.test.js.map +0 -1
- package/dist/agent/tools/__tests__/patch.test.d.ts +0 -6
- package/dist/agent/tools/__tests__/patch.test.d.ts.map +0 -1
- package/dist/agent/tools/__tests__/patch.test.js +0 -116
- package/dist/agent/tools/__tests__/patch.test.js.map +0 -1
- package/dist/agent/tools/__tests__/profiles.test.d.ts +0 -6
- package/dist/agent/tools/__tests__/profiles.test.d.ts.map +0 -1
- package/dist/agent/tools/__tests__/profiles.test.js +0 -125
- package/dist/agent/tools/__tests__/profiles.test.js.map +0 -1
- package/dist/agent/tools/__tests__/registry.test.d.ts +0 -6
- package/dist/agent/tools/__tests__/registry.test.d.ts.map +0 -1
- package/dist/agent/tools/__tests__/registry.test.js +0 -94
- package/dist/agent/tools/__tests__/registry.test.js.map +0 -1
- package/dist/agent/tools/__tests__/shared.test.d.ts +0 -6
- package/dist/agent/tools/__tests__/shared.test.d.ts.map +0 -1
- package/dist/agent/tools/__tests__/shared.test.js +0 -131
- package/dist/agent/tools/__tests__/shared.test.js.map +0 -1
- package/dist/backend/__tests__/direct-backend.test.d.ts +0 -14
- package/dist/backend/__tests__/direct-backend.test.d.ts.map +0 -1
- package/dist/backend/__tests__/direct-backend.test.js +0 -393
- package/dist/backend/__tests__/direct-backend.test.js.map +0 -1
- package/dist/backend/__tests__/direct-bootstrap.test.d.ts +0 -7
- package/dist/backend/__tests__/direct-bootstrap.test.d.ts.map +0 -1
- package/dist/backend/__tests__/direct-bootstrap.test.js +0 -177
- package/dist/backend/__tests__/direct-bootstrap.test.js.map +0 -1
- package/dist/backend/__tests__/mock-backend.test.d.ts +0 -7
- package/dist/backend/__tests__/mock-backend.test.d.ts.map +0 -1
- package/dist/backend/__tests__/mock-backend.test.js +0 -273
- package/dist/backend/__tests__/mock-backend.test.js.map +0 -1
- package/dist/backend/__tests__/parity.test.d.ts +0 -17
- package/dist/backend/__tests__/parity.test.d.ts.map +0 -1
- package/dist/backend/__tests__/parity.test.js +0 -411
- package/dist/backend/__tests__/parity.test.js.map +0 -1
- package/dist/context/__tests__/auto-summarizer.test.d.ts +0 -14
- package/dist/context/__tests__/auto-summarizer.test.d.ts.map +0 -1
- package/dist/context/__tests__/auto-summarizer.test.js +0 -189
- package/dist/context/__tests__/auto-summarizer.test.js.map +0 -1
- package/dist/context/__tests__/context-cli-command.test.d.ts +0 -7
- package/dist/context/__tests__/context-cli-command.test.d.ts.map +0 -1
- package/dist/context/__tests__/context-cli-command.test.js +0 -331
- package/dist/context/__tests__/context-cli-command.test.js.map +0 -1
- package/dist/context/__tests__/pyramid-summary-integration.test.d.ts +0 -2
- package/dist/context/__tests__/pyramid-summary-integration.test.d.ts.map +0 -1
- package/dist/context/__tests__/pyramid-summary-integration.test.js +0 -533
- package/dist/context/__tests__/pyramid-summary-integration.test.js.map +0 -1
- package/dist/context/__tests__/summarizer.test.d.ts +0 -2
- package/dist/context/__tests__/summarizer.test.d.ts.map +0 -1
- package/dist/context/__tests__/summarizer.test.js +0 -189
- package/dist/context/__tests__/summarizer.test.js.map +0 -1
- package/dist/context/__tests__/summary-cache.test.d.ts +0 -2
- package/dist/context/__tests__/summary-cache.test.d.ts.map +0 -1
- package/dist/context/__tests__/summary-cache.test.js +0 -214
- package/dist/context/__tests__/summary-cache.test.js.map +0 -1
- package/dist/context/__tests__/summary-metrics.test.d.ts +0 -2
- package/dist/context/__tests__/summary-metrics.test.d.ts.map +0 -1
- package/dist/context/__tests__/summary-metrics.test.js +0 -172
- package/dist/context/__tests__/summary-metrics.test.js.map +0 -1
- package/dist/context/__tests__/summary-types.test.d.ts +0 -2
- package/dist/context/__tests__/summary-types.test.d.ts.map +0 -1
- package/dist/context/__tests__/summary-types.test.js +0 -130
- package/dist/context/__tests__/summary-types.test.js.map +0 -1
- package/dist/convergence/__tests__/budget.test.d.ts +0 -6
- package/dist/convergence/__tests__/budget.test.d.ts.map +0 -1
- package/dist/convergence/__tests__/budget.test.js +0 -187
- package/dist/convergence/__tests__/budget.test.js.map +0 -1
- package/dist/convergence/__tests__/controller.test.d.ts +0 -9
- package/dist/convergence/__tests__/controller.test.d.ts.map +0 -1
- package/dist/convergence/__tests__/controller.test.js +0 -585
- package/dist/convergence/__tests__/controller.test.js.map +0 -1
- package/dist/convergence/__tests__/dual-signal.test.d.ts +0 -14
- package/dist/convergence/__tests__/dual-signal.test.d.ts.map +0 -1
- package/dist/convergence/__tests__/dual-signal.test.js +0 -123
- package/dist/convergence/__tests__/dual-signal.test.js.map +0 -1
- package/dist/convergence/__tests__/epic46-integration.test.d.ts +0 -15
- package/dist/convergence/__tests__/epic46-integration.test.d.ts.map +0 -1
- package/dist/convergence/__tests__/epic46-integration.test.js +0 -522
- package/dist/convergence/__tests__/epic46-integration.test.js.map +0 -1
- package/dist/convergence/__tests__/plateau.test.d.ts +0 -6
- package/dist/convergence/__tests__/plateau.test.d.ts.map +0 -1
- package/dist/convergence/__tests__/plateau.test.js +0 -163
- package/dist/convergence/__tests__/plateau.test.js.map +0 -1
- package/dist/convergence/__tests__/remediation.test.d.ts +0 -11
- package/dist/convergence/__tests__/remediation.test.d.ts.map +0 -1
- package/dist/convergence/__tests__/remediation.test.js +0 -209
- package/dist/convergence/__tests__/remediation.test.js.map +0 -1
- package/dist/convergence/__tests__/scenario-primary.test.d.ts +0 -13
- package/dist/convergence/__tests__/scenario-primary.test.d.ts.map +0 -1
- package/dist/convergence/__tests__/scenario-primary.test.js +0 -183
- package/dist/convergence/__tests__/scenario-primary.test.js.map +0 -1
- package/dist/factory-command.test.d.ts +0 -8
- package/dist/factory-command.test.d.ts.map +0 -1
- package/dist/factory-command.test.js +0 -304
- package/dist/factory-command.test.js.map +0 -1
- package/dist/graph/__tests__/attractor-compliance.test.d.ts +0 -10
- package/dist/graph/__tests__/attractor-compliance.test.d.ts.map +0 -1
- package/dist/graph/__tests__/attractor-compliance.test.js +0 -766
- package/dist/graph/__tests__/attractor-compliance.test.js.map +0 -1
- package/dist/graph/__tests__/checkpoint.test.d.ts +0 -8
- package/dist/graph/__tests__/checkpoint.test.d.ts.map +0 -1
- package/dist/graph/__tests__/checkpoint.test.js +0 -329
- package/dist/graph/__tests__/checkpoint.test.js.map +0 -1
- package/dist/graph/__tests__/condition-parser.test.d.ts +0 -14
- package/dist/graph/__tests__/condition-parser.test.d.ts.map +0 -1
- package/dist/graph/__tests__/condition-parser.test.js +0 -406
- package/dist/graph/__tests__/condition-parser.test.js.map +0 -1
- package/dist/graph/__tests__/context.test.d.ts +0 -14
- package/dist/graph/__tests__/context.test.d.ts.map +0 -1
- package/dist/graph/__tests__/context.test.js +0 -276
- package/dist/graph/__tests__/context.test.js.map +0 -1
- package/dist/graph/__tests__/edge-selector-events.test.d.ts +0 -11
- package/dist/graph/__tests__/edge-selector-events.test.d.ts.map +0 -1
- package/dist/graph/__tests__/edge-selector-events.test.js +0 -184
- package/dist/graph/__tests__/edge-selector-events.test.js.map +0 -1
- package/dist/graph/__tests__/edge-selector.test.d.ts +0 -6
- package/dist/graph/__tests__/edge-selector.test.d.ts.map +0 -1
- package/dist/graph/__tests__/edge-selector.test.js +0 -452
- package/dist/graph/__tests__/edge-selector.test.js.map +0 -1
- package/dist/graph/__tests__/executor-convergence.test.d.ts +0 -12
- package/dist/graph/__tests__/executor-convergence.test.d.ts.map +0 -1
- package/dist/graph/__tests__/executor-convergence.test.js +0 -432
- package/dist/graph/__tests__/executor-convergence.test.js.map +0 -1
- package/dist/graph/__tests__/executor-fidelity.test.d.ts +0 -13
- package/dist/graph/__tests__/executor-fidelity.test.d.ts.map +0 -1
- package/dist/graph/__tests__/executor-fidelity.test.js +0 -335
- package/dist/graph/__tests__/executor-fidelity.test.js.map +0 -1
- package/dist/graph/__tests__/executor.test.d.ts +0 -14
- package/dist/graph/__tests__/executor.test.d.ts.map +0 -1
- package/dist/graph/__tests__/executor.test.js +0 -901
- package/dist/graph/__tests__/executor.test.js.map +0 -1
- package/dist/graph/__tests__/fidelity.test.d.ts +0 -8
- package/dist/graph/__tests__/fidelity.test.d.ts.map +0 -1
- package/dist/graph/__tests__/fidelity.test.js +0 -135
- package/dist/graph/__tests__/fidelity.test.js.map +0 -1
- package/dist/graph/__tests__/llm-evaluator.test.d.ts +0 -7
- package/dist/graph/__tests__/llm-evaluator.test.d.ts.map +0 -1
- package/dist/graph/__tests__/llm-evaluator.test.js +0 -106
- package/dist/graph/__tests__/llm-evaluator.test.js.map +0 -1
- package/dist/graph/__tests__/parser-chaining.test.d.ts +0 -13
- package/dist/graph/__tests__/parser-chaining.test.d.ts.map +0 -1
- package/dist/graph/__tests__/parser-chaining.test.js +0 -215
- package/dist/graph/__tests__/parser-chaining.test.js.map +0 -1
- package/dist/graph/__tests__/parser.test.d.ts +0 -22
- package/dist/graph/__tests__/parser.test.d.ts.map +0 -1
- package/dist/graph/__tests__/parser.test.js +0 -452
- package/dist/graph/__tests__/parser.test.js.map +0 -1
- package/dist/graph/__tests__/run-state.test.d.ts +0 -13
- package/dist/graph/__tests__/run-state.test.d.ts.map +0 -1
- package/dist/graph/__tests__/run-state.test.js +0 -189
- package/dist/graph/__tests__/run-state.test.js.map +0 -1
- package/dist/graph/__tests__/transformer.test.d.ts +0 -16
- package/dist/graph/__tests__/transformer.test.d.ts.map +0 -1
- package/dist/graph/__tests__/transformer.test.js +0 -350
- package/dist/graph/__tests__/transformer.test.js.map +0 -1
- package/dist/graph/__tests__/validator-errors.test.d.ts +0 -15
- package/dist/graph/__tests__/validator-errors.test.d.ts.map +0 -1
- package/dist/graph/__tests__/validator-errors.test.js +0 -572
- package/dist/graph/__tests__/validator-errors.test.js.map +0 -1
- package/dist/graph/__tests__/validator-warnings.test.d.ts +0 -15
- package/dist/graph/__tests__/validator-warnings.test.d.ts.map +0 -1
- package/dist/graph/__tests__/validator-warnings.test.js +0 -363
- package/dist/graph/__tests__/validator-warnings.test.js.map +0 -1
- package/dist/handlers/__tests__/codergen-handler.test.d.ts +0 -14
- package/dist/handlers/__tests__/codergen-handler.test.d.ts.map +0 -1
- package/dist/handlers/__tests__/codergen-handler.test.js +0 -442
- package/dist/handlers/__tests__/codergen-handler.test.js.map +0 -1
- package/dist/handlers/__tests__/fan-in.test.d.ts +0 -14
- package/dist/handlers/__tests__/fan-in.test.d.ts.map +0 -1
- package/dist/handlers/__tests__/fan-in.test.js +0 -399
- package/dist/handlers/__tests__/fan-in.test.js.map +0 -1
- package/dist/handlers/__tests__/join-policy.test.d.ts +0 -9
- package/dist/handlers/__tests__/join-policy.test.d.ts.map +0 -1
- package/dist/handlers/__tests__/join-policy.test.js +0 -201
- package/dist/handlers/__tests__/join-policy.test.js.map +0 -1
- package/dist/handlers/__tests__/manager-loop.test.d.ts +0 -14
- package/dist/handlers/__tests__/manager-loop.test.d.ts.map +0 -1
- package/dist/handlers/__tests__/manager-loop.test.js +0 -322
- package/dist/handlers/__tests__/manager-loop.test.js.map +0 -1
- package/dist/handlers/__tests__/parallel-events.test.d.ts +0 -12
- package/dist/handlers/__tests__/parallel-events.test.d.ts.map +0 -1
- package/dist/handlers/__tests__/parallel-events.test.js +0 -252
- package/dist/handlers/__tests__/parallel-events.test.js.map +0 -1
- package/dist/handlers/__tests__/parallel-handler.test.d.ts +0 -14
- package/dist/handlers/__tests__/parallel-handler.test.d.ts.map +0 -1
- package/dist/handlers/__tests__/parallel-handler.test.js +0 -337
- package/dist/handlers/__tests__/parallel-handler.test.js.map +0 -1
- package/dist/handlers/__tests__/parallel-join.test.d.ts +0 -9
- package/dist/handlers/__tests__/parallel-join.test.d.ts.map +0 -1
- package/dist/handlers/__tests__/parallel-join.test.js +0 -267
- package/dist/handlers/__tests__/parallel-join.test.js.map +0 -1
- package/dist/handlers/__tests__/registry.test.d.ts +0 -14
- package/dist/handlers/__tests__/registry.test.d.ts.map +0 -1
- package/dist/handlers/__tests__/registry.test.js +0 -315
- package/dist/handlers/__tests__/registry.test.js.map +0 -1
- package/dist/handlers/__tests__/subgraph-events.test.d.ts +0 -10
- package/dist/handlers/__tests__/subgraph-events.test.d.ts.map +0 -1
- package/dist/handlers/__tests__/subgraph-events.test.js +0 -189
- package/dist/handlers/__tests__/subgraph-events.test.js.map +0 -1
- package/dist/handlers/__tests__/subgraph-inheritance.test.d.ts +0 -14
- package/dist/handlers/__tests__/subgraph-inheritance.test.d.ts.map +0 -1
- package/dist/handlers/__tests__/subgraph-inheritance.test.js +0 -267
- package/dist/handlers/__tests__/subgraph-inheritance.test.js.map +0 -1
- package/dist/handlers/__tests__/subgraph.test.d.ts +0 -14
- package/dist/handlers/__tests__/subgraph.test.d.ts.map +0 -1
- package/dist/handlers/__tests__/subgraph.test.js +0 -369
- package/dist/handlers/__tests__/subgraph.test.js.map +0 -1
- package/dist/handlers/__tests__/tool-handler.test.d.ts +0 -11
- package/dist/handlers/__tests__/tool-handler.test.d.ts.map +0 -1
- package/dist/handlers/__tests__/tool-handler.test.js +0 -184
- package/dist/handlers/__tests__/tool-handler.test.js.map +0 -1
- package/dist/handlers/__tests__/tool-scenario.test.d.ts +0 -12
- package/dist/handlers/__tests__/tool-scenario.test.d.ts.map +0 -1
- package/dist/handlers/__tests__/tool-scenario.test.js +0 -222
- package/dist/handlers/__tests__/tool-scenario.test.js.map +0 -1
- package/dist/handlers/__tests__/wait-human-handler.test.d.ts +0 -11
- package/dist/handlers/__tests__/wait-human-handler.test.d.ts.map +0 -1
- package/dist/handlers/__tests__/wait-human-handler.test.js +0 -251
- package/dist/handlers/__tests__/wait-human-handler.test.js.map +0 -1
- package/dist/llm/__tests__/client.test.d.ts +0 -2
- package/dist/llm/__tests__/client.test.d.ts.map +0 -1
- package/dist/llm/__tests__/client.test.js +0 -198
- package/dist/llm/__tests__/client.test.js.map +0 -1
- package/dist/llm/__tests__/types.test.d.ts +0 -2
- package/dist/llm/__tests__/types.test.d.ts.map +0 -1
- package/dist/llm/__tests__/types.test.js +0 -289
- package/dist/llm/__tests__/types.test.js.map +0 -1
- package/dist/llm/middleware/__tests__/cost-tracking.test.d.ts +0 -2
- package/dist/llm/middleware/__tests__/cost-tracking.test.d.ts.map +0 -1
- package/dist/llm/middleware/__tests__/cost-tracking.test.js +0 -73
- package/dist/llm/middleware/__tests__/cost-tracking.test.js.map +0 -1
- package/dist/llm/middleware/__tests__/logging.test.d.ts +0 -2
- package/dist/llm/middleware/__tests__/logging.test.d.ts.map +0 -1
- package/dist/llm/middleware/__tests__/logging.test.js +0 -127
- package/dist/llm/middleware/__tests__/logging.test.js.map +0 -1
- package/dist/llm/middleware/__tests__/retry.test.d.ts +0 -2
- package/dist/llm/middleware/__tests__/retry.test.d.ts.map +0 -1
- package/dist/llm/middleware/__tests__/retry.test.js +0 -126
- package/dist/llm/middleware/__tests__/retry.test.js.map +0 -1
- package/dist/llm/providers/__tests__/anthropic.test.d.ts +0 -2
- package/dist/llm/providers/__tests__/anthropic.test.d.ts.map +0 -1
- package/dist/llm/providers/__tests__/anthropic.test.js +0 -412
- package/dist/llm/providers/__tests__/anthropic.test.js.map +0 -1
- package/dist/llm/providers/__tests__/gemini.test.d.ts +0 -2
- package/dist/llm/providers/__tests__/gemini.test.d.ts.map +0 -1
- package/dist/llm/providers/__tests__/gemini.test.js +0 -591
- package/dist/llm/providers/__tests__/gemini.test.js.map +0 -1
- package/dist/llm/providers/__tests__/openai.test.d.ts +0 -2
- package/dist/llm/providers/__tests__/openai.test.d.ts.map +0 -1
- package/dist/llm/providers/__tests__/openai.test.js +0 -546
- package/dist/llm/providers/__tests__/openai.test.js.map +0 -1
- package/dist/persistence/__tests__/factory-queries.test.d.ts +0 -9
- package/dist/persistence/__tests__/factory-queries.test.d.ts.map +0 -1
- package/dist/persistence/__tests__/factory-queries.test.js +0 -372
- package/dist/persistence/__tests__/factory-queries.test.js.map +0 -1
- package/dist/persistence/__tests__/factory-schema.test.d.ts +0 -6
- package/dist/persistence/__tests__/factory-schema.test.d.ts.map +0 -1
- package/dist/persistence/__tests__/factory-schema.test.js +0 -105
- package/dist/persistence/__tests__/factory-schema.test.js.map +0 -1
- package/dist/scenarios/__tests__/cli-command-list.test.d.ts +0 -7
- package/dist/scenarios/__tests__/cli-command-list.test.d.ts.map +0 -1
- package/dist/scenarios/__tests__/cli-command-list.test.js +0 -237
- package/dist/scenarios/__tests__/cli-command-list.test.js.map +0 -1
- package/dist/scenarios/__tests__/cli-command.test.d.ts +0 -11
- package/dist/scenarios/__tests__/cli-command.test.d.ts.map +0 -1
- package/dist/scenarios/__tests__/cli-command.test.js +0 -275
- package/dist/scenarios/__tests__/cli-command.test.js.map +0 -1
- package/dist/scenarios/__tests__/integrity-pipeline.test.d.ts +0 -15
- package/dist/scenarios/__tests__/integrity-pipeline.test.d.ts.map +0 -1
- package/dist/scenarios/__tests__/integrity-pipeline.test.js +0 -318
- package/dist/scenarios/__tests__/integrity-pipeline.test.js.map +0 -1
- package/dist/scenarios/__tests__/runner-twins.test.d.ts +0 -13
- package/dist/scenarios/__tests__/runner-twins.test.d.ts.map +0 -1
- package/dist/scenarios/__tests__/runner-twins.test.js +0 -205
- package/dist/scenarios/__tests__/runner-twins.test.js.map +0 -1
- package/dist/scenarios/__tests__/scorer.test.d.ts +0 -11
- package/dist/scenarios/__tests__/scorer.test.d.ts.map +0 -1
- package/dist/scenarios/__tests__/scorer.test.js +0 -225
- package/dist/scenarios/__tests__/scorer.test.js.map +0 -1
- package/dist/scenarios/__tests__/scoring-integration.test.d.ts +0 -8
- package/dist/scenarios/__tests__/scoring-integration.test.d.ts.map +0 -1
- package/dist/scenarios/__tests__/scoring-integration.test.js +0 -178
- package/dist/scenarios/__tests__/scoring-integration.test.js.map +0 -1
- package/dist/scenarios/__tests__/store.test.d.ts +0 -5
- package/dist/scenarios/__tests__/store.test.d.ts.map +0 -1
- package/dist/scenarios/__tests__/store.test.js +0 -169
- package/dist/scenarios/__tests__/store.test.js.map +0 -1
- package/dist/stylesheet/__tests__/stylesheet.test.d.ts +0 -17
- package/dist/stylesheet/__tests__/stylesheet.test.d.ts.map +0 -1
- package/dist/stylesheet/__tests__/stylesheet.test.js +0 -368
- package/dist/stylesheet/__tests__/stylesheet.test.js.map +0 -1
- package/dist/templates/__tests__/templates.test.d.ts +0 -7
- package/dist/templates/__tests__/templates.test.d.ts.map +0 -1
- package/dist/templates/__tests__/templates.test.js +0 -92
- package/dist/templates/__tests__/templates.test.js.map +0 -1
- package/dist/twins/__tests__/docker-compose.test.d.ts +0 -13
- package/dist/twins/__tests__/docker-compose.test.d.ts.map +0 -1
- package/dist/twins/__tests__/docker-compose.test.js +0 -247
- package/dist/twins/__tests__/docker-compose.test.js.map +0 -1
- package/dist/twins/__tests__/health-monitor.test.d.ts +0 -19
- package/dist/twins/__tests__/health-monitor.test.d.ts.map +0 -1
- package/dist/twins/__tests__/health-monitor.test.js +0 -301
- package/dist/twins/__tests__/health-monitor.test.js.map +0 -1
- package/dist/twins/__tests__/integration/e2e.test.d.ts +0 -18
- package/dist/twins/__tests__/integration/e2e.test.d.ts.map +0 -1
- package/dist/twins/__tests__/integration/e2e.test.js +0 -146
- package/dist/twins/__tests__/integration/e2e.test.js.map +0 -1
- package/dist/twins/__tests__/integration/helpers.d.ts +0 -32
- package/dist/twins/__tests__/integration/helpers.d.ts.map +0 -1
- package/dist/twins/__tests__/integration/helpers.js +0 -67
- package/dist/twins/__tests__/integration/helpers.js.map +0 -1
- package/dist/twins/__tests__/integration/lifecycle.test.d.ts +0 -14
- package/dist/twins/__tests__/integration/lifecycle.test.d.ts.map +0 -1
- package/dist/twins/__tests__/integration/lifecycle.test.js +0 -127
- package/dist/twins/__tests__/integration/lifecycle.test.js.map +0 -1
- package/dist/twins/__tests__/integration/persistence-integration.test.d.ts +0 -14
- package/dist/twins/__tests__/integration/persistence-integration.test.d.ts.map +0 -1
- package/dist/twins/__tests__/integration/persistence-integration.test.js +0 -132
- package/dist/twins/__tests__/integration/persistence-integration.test.js.map +0 -1
- package/dist/twins/__tests__/persistence.test.d.ts +0 -10
- package/dist/twins/__tests__/persistence.test.d.ts.map +0 -1
- package/dist/twins/__tests__/persistence.test.js +0 -300
- package/dist/twins/__tests__/persistence.test.js.map +0 -1
- package/dist/twins/__tests__/registry.test.d.ts +0 -7
- package/dist/twins/__tests__/registry.test.d.ts.map +0 -1
- package/dist/twins/__tests__/registry.test.js +0 -282
- package/dist/twins/__tests__/registry.test.js.map +0 -1
- package/dist/twins/__tests__/run-state.test.d.ts +0 -9
- package/dist/twins/__tests__/run-state.test.d.ts.map +0 -1
- package/dist/twins/__tests__/run-state.test.js +0 -112
- package/dist/twins/__tests__/run-state.test.js.map +0 -1
- package/dist/twins/__tests__/templates-cli.test.d.ts +0 -10
- package/dist/twins/__tests__/templates-cli.test.d.ts.map +0 -1
- package/dist/twins/__tests__/templates-cli.test.js +0 -187
- package/dist/twins/__tests__/templates-cli.test.js.map +0 -1
- package/dist/twins/__tests__/templates.test.d.ts +0 -7
- package/dist/twins/__tests__/templates.test.d.ts.map +0 -1
- package/dist/twins/__tests__/templates.test.js +0 -87
- package/dist/twins/__tests__/templates.test.js.map +0 -1
- package/dist/twins/__tests__/twins-cli.test.d.ts +0 -11
- package/dist/twins/__tests__/twins-cli.test.d.ts.map +0 -1
- package/dist/twins/__tests__/twins-cli.test.js +0 -365
- package/dist/twins/__tests__/twins-cli.test.js.map +0 -1
|
@@ -1,766 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Spec compliance tests for the Attractor graph engine.
|
|
3
|
-
*
|
|
4
|
-
* Replays pseudocode examples from the Attractor spec through
|
|
5
|
-
* selectEdge(), evaluateGates(), and checkpoint resume APIs.
|
|
6
|
-
*
|
|
7
|
-
* Story 42-17.
|
|
8
|
-
*/
|
|
9
|
-
// AttractorBench structural conformance parity — Phase A mandatory
|
|
10
|
-
// See: https://github.com/strongdm/attractorbench
|
|
11
|
-
// Behavioral conformance tests are advisory in Phase A; required by Phase B exit.
|
|
12
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
13
|
-
import os from 'node:os';
|
|
14
|
-
import path from 'node:path';
|
|
15
|
-
import { mkdir, rm } from 'node:fs/promises';
|
|
16
|
-
import crypto from 'node:crypto';
|
|
17
|
-
import { selectEdge } from '../edge-selector.js';
|
|
18
|
-
import { createConvergenceController } from '../../convergence/controller.js';
|
|
19
|
-
import { CheckpointManager } from '../checkpoint.js';
|
|
20
|
-
import { createGraphExecutor } from '../executor.js';
|
|
21
|
-
import { createValidator } from '../validator.js';
|
|
22
|
-
import { parseGraph } from '../parser.js';
|
|
23
|
-
import { GraphContext } from '../context.js';
|
|
24
|
-
// ---------------------------------------------------------------------------
|
|
25
|
-
// DOT fixture for resume/fidelity tests (spec Section 5.3)
|
|
26
|
-
// ---------------------------------------------------------------------------
|
|
27
|
-
const COMPLIANCE_RESUME_DOT = `
|
|
28
|
-
digraph compliance_resume {
|
|
29
|
-
graph [goal="Resume compliance test"]
|
|
30
|
-
start [shape=Mdiamond]
|
|
31
|
-
node1 [type=codergen, prompt="Step 1"]
|
|
32
|
-
node2 [type=codergen, prompt="Step 2"]
|
|
33
|
-
exit [shape=Msquare]
|
|
34
|
-
|
|
35
|
-
start -> node1
|
|
36
|
-
node1 -> node2
|
|
37
|
-
node2 -> exit
|
|
38
|
-
}
|
|
39
|
-
`;
|
|
40
|
-
const COMPLIANCE_FIDELITY_RESUME_DOT = `
|
|
41
|
-
digraph compliance_fidelity_resume {
|
|
42
|
-
graph [goal="Fidelity degradation test"]
|
|
43
|
-
start [shape=Mdiamond]
|
|
44
|
-
node1 [type=codergen, prompt="Step 1", fidelity=full]
|
|
45
|
-
node2 [type=codergen, prompt="Step 2"]
|
|
46
|
-
exit [shape=Msquare]
|
|
47
|
-
|
|
48
|
-
start -> node1
|
|
49
|
-
node1 -> node2
|
|
50
|
-
node2 -> exit
|
|
51
|
-
}
|
|
52
|
-
`;
|
|
53
|
-
// ---------------------------------------------------------------------------
|
|
54
|
-
// Shared helper factories
|
|
55
|
-
// ---------------------------------------------------------------------------
|
|
56
|
-
/** A node with all required fields at sensible defaults. */
|
|
57
|
-
const MINIMAL_NODE = {
|
|
58
|
-
id: '',
|
|
59
|
-
label: '',
|
|
60
|
-
shape: 'box',
|
|
61
|
-
type: 'codergen',
|
|
62
|
-
prompt: '',
|
|
63
|
-
maxRetries: 0,
|
|
64
|
-
goalGate: false,
|
|
65
|
-
retryTarget: '',
|
|
66
|
-
fallbackRetryTarget: '',
|
|
67
|
-
fidelity: '',
|
|
68
|
-
threadId: '',
|
|
69
|
-
class: '',
|
|
70
|
-
timeout: 0,
|
|
71
|
-
llmModel: '',
|
|
72
|
-
llmProvider: '',
|
|
73
|
-
reasoningEffort: '',
|
|
74
|
-
autoStatus: false,
|
|
75
|
-
allowPartial: false,
|
|
76
|
-
toolCommand: '',
|
|
77
|
-
backend: '',
|
|
78
|
-
};
|
|
79
|
-
function makeNode(id, overrides) {
|
|
80
|
-
return { ...MINIMAL_NODE, id, ...overrides };
|
|
81
|
-
}
|
|
82
|
-
function makeEdge(fromNode, toNode, overrides) {
|
|
83
|
-
return {
|
|
84
|
-
fromNode,
|
|
85
|
-
toNode,
|
|
86
|
-
label: '',
|
|
87
|
-
condition: '',
|
|
88
|
-
weight: 0,
|
|
89
|
-
fidelity: '',
|
|
90
|
-
threadId: '',
|
|
91
|
-
loopRestart: false,
|
|
92
|
-
...overrides,
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Build a minimal Graph conforming to the Graph interface.
|
|
97
|
-
* startNodeId / exitNodeId control which nodes serve as start/exit.
|
|
98
|
-
*/
|
|
99
|
-
function makeGraph(nodes, edges, startNodeId, exitNodeId) {
|
|
100
|
-
const nodeMap = new Map(nodes.map((n) => [n.id, n]));
|
|
101
|
-
const resolveStart = () => {
|
|
102
|
-
if (startNodeId)
|
|
103
|
-
return nodeMap.get(startNodeId);
|
|
104
|
-
for (const n of nodeMap.values()) {
|
|
105
|
-
if (n.shape === 'Mdiamond')
|
|
106
|
-
return n;
|
|
107
|
-
}
|
|
108
|
-
throw new Error('No start node');
|
|
109
|
-
};
|
|
110
|
-
const resolveExit = () => {
|
|
111
|
-
if (exitNodeId)
|
|
112
|
-
return nodeMap.get(exitNodeId);
|
|
113
|
-
for (const n of nodeMap.values()) {
|
|
114
|
-
if (n.shape === 'Msquare')
|
|
115
|
-
return n;
|
|
116
|
-
}
|
|
117
|
-
throw new Error('No exit node');
|
|
118
|
-
};
|
|
119
|
-
return {
|
|
120
|
-
id: '',
|
|
121
|
-
goal: '',
|
|
122
|
-
label: '',
|
|
123
|
-
modelStylesheet: '',
|
|
124
|
-
defaultMaxRetries: 0,
|
|
125
|
-
retryTarget: '',
|
|
126
|
-
fallbackRetryTarget: '',
|
|
127
|
-
defaultFidelity: '',
|
|
128
|
-
nodes: nodeMap,
|
|
129
|
-
edges,
|
|
130
|
-
outgoingEdges: (nodeId) => edges.filter((e) => e.fromNode === nodeId),
|
|
131
|
-
startNode: resolveStart,
|
|
132
|
-
exitNode: resolveExit,
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
/** Build a Graph with only the minimal start/exit structure for lint-rule tests. */
|
|
136
|
-
function makeLintGraph(extraNodes, extraEdges, overrides) {
|
|
137
|
-
const baseNodes = [
|
|
138
|
-
makeNode('start', { shape: 'Mdiamond', type: '' }),
|
|
139
|
-
makeNode('exit', { shape: 'Msquare', type: '' }),
|
|
140
|
-
...extraNodes.map((n) => makeNode(n.id, n)),
|
|
141
|
-
];
|
|
142
|
-
const baseEdges = extraEdges.map((e) => makeEdge(e.fromNode, e.toNode, {
|
|
143
|
-
condition: e.condition ?? '',
|
|
144
|
-
loopRestart: e.loopRestart ?? false,
|
|
145
|
-
}));
|
|
146
|
-
const nodeMap = new Map(baseNodes.map((n) => [n.id, n]));
|
|
147
|
-
return {
|
|
148
|
-
id: 'test',
|
|
149
|
-
goal: '',
|
|
150
|
-
label: '',
|
|
151
|
-
modelStylesheet: overrides?.modelStylesheet ?? '',
|
|
152
|
-
defaultMaxRetries: 0,
|
|
153
|
-
retryTarget: overrides?.retryTarget ?? '',
|
|
154
|
-
fallbackRetryTarget: overrides?.fallbackRetryTarget ?? '',
|
|
155
|
-
defaultFidelity: '',
|
|
156
|
-
nodes: nodeMap,
|
|
157
|
-
edges: baseEdges,
|
|
158
|
-
outgoingEdges: (nodeId) => baseEdges.filter((e) => e.fromNode === nodeId),
|
|
159
|
-
startNode: () => nodeMap.get('start'),
|
|
160
|
-
exitNode: () => nodeMap.get('exit'),
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
/** Build a mock IHandlerRegistry that resolves every node via the given handler factory. */
|
|
164
|
-
function makeRegistry(handlerFactory, statusOverride) {
|
|
165
|
-
return {
|
|
166
|
-
register: vi.fn(),
|
|
167
|
-
registerShape: vi.fn(),
|
|
168
|
-
setDefault: vi.fn(),
|
|
169
|
-
resolve: vi.fn().mockImplementation(() => async (node, ctx) => {
|
|
170
|
-
handlerFactory(node, ctx);
|
|
171
|
-
return { status: statusOverride ?? 'SUCCESS' };
|
|
172
|
-
}),
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
/** Build a mock TypedEventBus that captures all emitted events. */
|
|
176
|
-
function makeEventBus() {
|
|
177
|
-
const nodeStartedIds = [];
|
|
178
|
-
const bus = {
|
|
179
|
-
emit: vi.fn().mockImplementation((event, payload) => {
|
|
180
|
-
if (event === 'graph:node-started') {
|
|
181
|
-
nodeStartedIds.push(payload.nodeId);
|
|
182
|
-
}
|
|
183
|
-
}),
|
|
184
|
-
on: vi.fn(),
|
|
185
|
-
off: vi.fn(),
|
|
186
|
-
};
|
|
187
|
-
return { bus, nodeStartedIds: () => [...nodeStartedIds] };
|
|
188
|
-
}
|
|
189
|
-
// ---------------------------------------------------------------------------
|
|
190
|
-
// Task 2: Edge selection compliance — spec Section 3.3
|
|
191
|
-
// ---------------------------------------------------------------------------
|
|
192
|
-
describe('selectEdge compliance — spec Section 3.3', () => {
|
|
193
|
-
// Step 1: Condition-matched edges beat weight
|
|
194
|
-
it('AC1 — Step 1: condition match returns conditional edge over high-weight unconditional', async () => {
|
|
195
|
-
const node = makeNode('origin');
|
|
196
|
-
const condEdge = makeEdge('origin', 'dest-conditional', { condition: 'outcome=success', weight: 0 });
|
|
197
|
-
const unconEdge = makeEdge('origin', 'dest-unconditional', { weight: 10 });
|
|
198
|
-
const graph = makeGraph([node], [condEdge, unconEdge]);
|
|
199
|
-
const ctx = new GraphContext({ outcome: 'success' });
|
|
200
|
-
const outcome = { status: 'SUCCESS' };
|
|
201
|
-
const selected = await selectEdge(node, outcome, ctx, graph);
|
|
202
|
-
expect(selected).toBe(condEdge);
|
|
203
|
-
});
|
|
204
|
-
it('AC1 variant — Step 1: no condition match falls through to Step 4 (weight)', async () => {
|
|
205
|
-
const node = makeNode('origin');
|
|
206
|
-
const condEdge = makeEdge('origin', 'dest-conditional', { condition: 'outcome=success', weight: 0 });
|
|
207
|
-
const unconEdge = makeEdge('origin', 'dest-unconditional', { weight: 10 });
|
|
208
|
-
const graph = makeGraph([node], [condEdge, unconEdge]);
|
|
209
|
-
const ctx = new GraphContext({ outcome: 'failure' });
|
|
210
|
-
const outcome = { status: 'SUCCESS' };
|
|
211
|
-
const selected = await selectEdge(node, outcome, ctx, graph);
|
|
212
|
-
expect(selected).toBe(unconEdge);
|
|
213
|
-
});
|
|
214
|
-
// Step 2: Preferred label with accelerator prefix stripping
|
|
215
|
-
it('AC2 — Step 2: [Y] prefix stripped, label matches preferredLabel "yes"', async () => {
|
|
216
|
-
const node = makeNode('origin');
|
|
217
|
-
const yesEdge = makeEdge('origin', 'yes-target', { label: '[Y] Yes' });
|
|
218
|
-
const noEdge = makeEdge('origin', 'no-target', { label: 'No' });
|
|
219
|
-
const graph = makeGraph([node], [yesEdge, noEdge]);
|
|
220
|
-
const ctx = new GraphContext();
|
|
221
|
-
const outcome = { status: 'SUCCESS', preferredLabel: 'yes' };
|
|
222
|
-
const selected = await selectEdge(node, outcome, ctx, graph);
|
|
223
|
-
expect(selected).toBe(yesEdge);
|
|
224
|
-
});
|
|
225
|
-
it('AC2 variant — Step 2: Y) prefix stripped, label matches preferredLabel "confirm"', async () => {
|
|
226
|
-
const node = makeNode('origin');
|
|
227
|
-
const confirmEdge = makeEdge('origin', 'confirm-target', { label: 'Y) Confirm' });
|
|
228
|
-
const otherEdge = makeEdge('origin', 'other-target', { label: 'Cancel' });
|
|
229
|
-
const graph = makeGraph([node], [confirmEdge, otherEdge]);
|
|
230
|
-
const ctx = new GraphContext();
|
|
231
|
-
const outcome = { status: 'SUCCESS', preferredLabel: 'confirm' };
|
|
232
|
-
const selected = await selectEdge(node, outcome, ctx, graph);
|
|
233
|
-
expect(selected).toBe(confirmEdge);
|
|
234
|
-
});
|
|
235
|
-
// Step 3: Suggested next IDs
|
|
236
|
-
it('Step 3: suggestedNextIds matches edge target', async () => {
|
|
237
|
-
const node = makeNode('origin');
|
|
238
|
-
const edgeA = makeEdge('origin', 'a');
|
|
239
|
-
const edgeB = makeEdge('origin', 'b');
|
|
240
|
-
const edgeC = makeEdge('origin', 'c');
|
|
241
|
-
const graph = makeGraph([node], [edgeA, edgeB, edgeC]);
|
|
242
|
-
const ctx = new GraphContext();
|
|
243
|
-
const outcome = { status: 'SUCCESS', suggestedNextIds: ['b'] };
|
|
244
|
-
const selected = await selectEdge(node, outcome, ctx, graph);
|
|
245
|
-
expect(selected).toBe(edgeB);
|
|
246
|
-
});
|
|
247
|
-
// Steps 4 & 5: Weight with lexical tiebreak
|
|
248
|
-
it('AC3 — Steps 4&5: weight 5 tie between charlie and alpha → lexically-first alpha wins', async () => {
|
|
249
|
-
const node = makeNode('origin');
|
|
250
|
-
const charlieEdge = makeEdge('origin', 'charlie', { weight: 5 });
|
|
251
|
-
const alphaEdge = makeEdge('origin', 'alpha', { weight: 5 });
|
|
252
|
-
const bravoEdge = makeEdge('origin', 'bravo', { weight: 3 });
|
|
253
|
-
const graph = makeGraph([node], [charlieEdge, alphaEdge, bravoEdge]);
|
|
254
|
-
const ctx = new GraphContext();
|
|
255
|
-
const outcome = { status: 'SUCCESS' };
|
|
256
|
-
const selected = await selectEdge(node, outcome, ctx, graph);
|
|
257
|
-
expect(selected).toBe(alphaEdge);
|
|
258
|
-
expect(selected?.toNode).toBe('alpha');
|
|
259
|
-
});
|
|
260
|
-
// Empty edges
|
|
261
|
-
it('empty edges: no outgoing edges → returns null', async () => {
|
|
262
|
-
const node = makeNode('origin');
|
|
263
|
-
const graph = makeGraph([node], []);
|
|
264
|
-
const ctx = new GraphContext();
|
|
265
|
-
const outcome = { status: 'SUCCESS' };
|
|
266
|
-
const selected = await selectEdge(node, outcome, ctx, graph);
|
|
267
|
-
expect(selected).toBeNull();
|
|
268
|
-
});
|
|
269
|
-
});
|
|
270
|
-
// ---------------------------------------------------------------------------
|
|
271
|
-
// Task 3: Goal gate compliance — spec Section 3.4
|
|
272
|
-
// ---------------------------------------------------------------------------
|
|
273
|
-
describe('evaluateGates compliance — spec Section 3.4', () => {
|
|
274
|
-
it('SUCCESS satisfies a goalGate=true node', () => {
|
|
275
|
-
const controller = createConvergenceController();
|
|
276
|
-
controller.recordOutcome('nodeA', 'SUCCESS');
|
|
277
|
-
const graph = makeGraph([makeNode('nodeA', { goalGate: true })], []);
|
|
278
|
-
const result = controller.evaluateGates(graph);
|
|
279
|
-
expect(result).toEqual({ satisfied: true, failingNodes: [] });
|
|
280
|
-
});
|
|
281
|
-
it('AC4 — PARTIAL_SUCCESS satisfies a goalGate=true node (spec Section 3.4)', () => {
|
|
282
|
-
const controller = createConvergenceController();
|
|
283
|
-
controller.recordOutcome('nodeA', 'PARTIAL_SUCCESS');
|
|
284
|
-
const graph = makeGraph([makeNode('nodeA', { goalGate: true })], []);
|
|
285
|
-
const result = controller.evaluateGates(graph);
|
|
286
|
-
expect(result).toEqual({ satisfied: true, failingNodes: [] });
|
|
287
|
-
});
|
|
288
|
-
it('AC4 — FAILURE does not satisfy goalGate=true node, appears in failingNodes', () => {
|
|
289
|
-
const controller = createConvergenceController();
|
|
290
|
-
controller.recordOutcome('nodeA', 'FAILURE');
|
|
291
|
-
const graph = makeGraph([makeNode('nodeA', { goalGate: true })], []);
|
|
292
|
-
const result = controller.evaluateGates(graph);
|
|
293
|
-
expect(result.satisfied).toBe(false);
|
|
294
|
-
expect(result.failingNodes).toContain('nodeA');
|
|
295
|
-
});
|
|
296
|
-
it('AC4 — Unrecorded outcome (never executed) fails gate', () => {
|
|
297
|
-
const controller = createConvergenceController();
|
|
298
|
-
// nodeA has no recorded outcome
|
|
299
|
-
const graph = makeGraph([makeNode('nodeA', { goalGate: true })], []);
|
|
300
|
-
const result = controller.evaluateGates(graph);
|
|
301
|
-
expect(result.satisfied).toBe(false);
|
|
302
|
-
expect(result.failingNodes).toContain('nodeA');
|
|
303
|
-
});
|
|
304
|
-
it('AC4 — PARTIAL_SUCCESS satisfies; FAILURE and unrecorded both fail', () => {
|
|
305
|
-
const controller = createConvergenceController();
|
|
306
|
-
controller.recordOutcome('partial', 'PARTIAL_SUCCESS');
|
|
307
|
-
controller.recordOutcome('failing', 'FAILURE');
|
|
308
|
-
// 'unrecorded' has no recorded outcome
|
|
309
|
-
const graph = makeGraph([
|
|
310
|
-
makeNode('partial', { goalGate: true }),
|
|
311
|
-
makeNode('failing', { goalGate: true }),
|
|
312
|
-
makeNode('unrecorded', { goalGate: true }),
|
|
313
|
-
], []);
|
|
314
|
-
const result = controller.evaluateGates(graph);
|
|
315
|
-
expect(result.satisfied).toBe(false);
|
|
316
|
-
expect(result.failingNodes).toContain('failing');
|
|
317
|
-
expect(result.failingNodes).toContain('unrecorded');
|
|
318
|
-
expect(result.failingNodes).not.toContain('partial');
|
|
319
|
-
});
|
|
320
|
-
it('mixed SUCCESS + PARTIAL_SUCCESS: both satisfied → { satisfied: true, failingNodes: [] }', () => {
|
|
321
|
-
const controller = createConvergenceController();
|
|
322
|
-
controller.recordOutcome('nodeA', 'SUCCESS');
|
|
323
|
-
controller.recordOutcome('nodeB', 'PARTIAL_SUCCESS');
|
|
324
|
-
const graph = makeGraph([makeNode('nodeA', { goalGate: true }), makeNode('nodeB', { goalGate: true })], []);
|
|
325
|
-
const result = controller.evaluateGates(graph);
|
|
326
|
-
expect(result).toEqual({ satisfied: true, failingNodes: [] });
|
|
327
|
-
});
|
|
328
|
-
it('mixed SUCCESS + FAILURE: only failing node in failingNodes', () => {
|
|
329
|
-
const controller = createConvergenceController();
|
|
330
|
-
controller.recordOutcome('nodeA', 'SUCCESS');
|
|
331
|
-
controller.recordOutcome('nodeB', 'FAILURE');
|
|
332
|
-
const graph = makeGraph([makeNode('nodeA', { goalGate: true }), makeNode('nodeB', { goalGate: true })], []);
|
|
333
|
-
const result = controller.evaluateGates(graph);
|
|
334
|
-
expect(result.satisfied).toBe(false);
|
|
335
|
-
expect(result.failingNodes).toEqual(['nodeB']);
|
|
336
|
-
expect(result.failingNodes).not.toContain('nodeA');
|
|
337
|
-
});
|
|
338
|
-
it('no goalGate=true nodes: vacuously satisfied', () => {
|
|
339
|
-
const controller = createConvergenceController();
|
|
340
|
-
const graph = makeGraph([makeNode('nodeA', { goalGate: false }), makeNode('nodeB', { goalGate: false })], []);
|
|
341
|
-
const result = controller.evaluateGates(graph);
|
|
342
|
-
expect(result).toEqual({ satisfied: true, failingNodes: [] });
|
|
343
|
-
});
|
|
344
|
-
});
|
|
345
|
-
// ---------------------------------------------------------------------------
|
|
346
|
-
// Task 4: Checkpoint resume compliance — spec Section 5.3
|
|
347
|
-
// ---------------------------------------------------------------------------
|
|
348
|
-
describe('checkpoint resume compliance — spec Section 5.3', () => {
|
|
349
|
-
let tmpDir;
|
|
350
|
-
beforeEach(async () => {
|
|
351
|
-
tmpDir = path.join(os.tmpdir(), `attractor-compliance-${crypto.randomUUID()}`);
|
|
352
|
-
await mkdir(tmpDir, { recursive: true });
|
|
353
|
-
});
|
|
354
|
-
afterEach(async () => {
|
|
355
|
-
await rm(tmpDir, { recursive: true, force: true });
|
|
356
|
-
});
|
|
357
|
-
it('AC5 — completed nodes are skipped and context is restored on resume', async () => {
|
|
358
|
-
// Write seed checkpoint using CheckpointManager.save()
|
|
359
|
-
const checkpointManager = new CheckpointManager();
|
|
360
|
-
const seedContext = new GraphContext({ result: 'ok', attempt: '3' });
|
|
361
|
-
await checkpointManager.save(tmpDir, {
|
|
362
|
-
currentNode: 'node1',
|
|
363
|
-
completedNodes: ['start', 'node1'],
|
|
364
|
-
nodeRetries: {},
|
|
365
|
-
context: seedContext,
|
|
366
|
-
});
|
|
367
|
-
// Parse and validate the compliance resume graph
|
|
368
|
-
const graph = parseGraph(COMPLIANCE_RESUME_DOT);
|
|
369
|
-
const validator = createValidator();
|
|
370
|
-
const validationErrors = validator.validate(graph).filter((d) => d.severity === 'error');
|
|
371
|
-
expect(validationErrors).toHaveLength(0);
|
|
372
|
-
// Track graph:node-started events and captured contexts per node
|
|
373
|
-
const { bus, nodeStartedIds } = makeEventBus();
|
|
374
|
-
const capturedContextByNode = {};
|
|
375
|
-
const registry = makeRegistry((node, ctx) => {
|
|
376
|
-
capturedContextByNode[node.id] = ctx;
|
|
377
|
-
});
|
|
378
|
-
const executor = createGraphExecutor();
|
|
379
|
-
const config = {
|
|
380
|
-
runId: 'compliance-resume-test',
|
|
381
|
-
logsRoot: tmpDir,
|
|
382
|
-
handlerRegistry: registry,
|
|
383
|
-
eventBus: bus,
|
|
384
|
-
checkpointPath: path.join(tmpDir, 'checkpoint.json'),
|
|
385
|
-
};
|
|
386
|
-
const finalOutcome = await executor.run(graph, config);
|
|
387
|
-
// graph:node-started must NOT include 'start' or 'node1' (skipped)
|
|
388
|
-
expect(nodeStartedIds()).not.toContain('start');
|
|
389
|
-
expect(nodeStartedIds()).not.toContain('node1');
|
|
390
|
-
// graph:node-started MUST include 'node2' and 'exit' is never dispatched (exit check)
|
|
391
|
-
expect(nodeStartedIds()).toContain('node2');
|
|
392
|
-
// Context is restored: node2 handler receives context seeded from checkpoint
|
|
393
|
-
expect(capturedContextByNode['node2']).toBeDefined();
|
|
394
|
-
expect(capturedContextByNode['node2'].getString('result')).toBe('ok');
|
|
395
|
-
expect(capturedContextByNode['node2'].getString('attempt')).toBe('3');
|
|
396
|
-
// Final outcome is SUCCESS
|
|
397
|
-
expect(finalOutcome.status).toBe('SUCCESS');
|
|
398
|
-
});
|
|
399
|
-
});
|
|
400
|
-
// ---------------------------------------------------------------------------
|
|
401
|
-
// Task 5: Fidelity degradation on resume — spec Section 5.3 step 6
|
|
402
|
-
// ---------------------------------------------------------------------------
|
|
403
|
-
describe('fidelity degradation on resume — spec Section 5.3 step 6', () => {
|
|
404
|
-
let tmpDir;
|
|
405
|
-
beforeEach(async () => {
|
|
406
|
-
tmpDir = path.join(os.tmpdir(), `attractor-fidelity-${crypto.randomUUID()}`);
|
|
407
|
-
await mkdir(tmpDir, { recursive: true });
|
|
408
|
-
});
|
|
409
|
-
afterEach(async () => {
|
|
410
|
-
await rm(tmpDir, { recursive: true, force: true });
|
|
411
|
-
});
|
|
412
|
-
it('first resumed node gets fidelity="summary:high" when last completed node had fidelity="full"', async () => {
|
|
413
|
-
// Write seed checkpoint: node1 (which has fidelity=full in the DOT) is the last completed node
|
|
414
|
-
const checkpointManager = new CheckpointManager();
|
|
415
|
-
const seedContext = new GraphContext({ result: 'ok' });
|
|
416
|
-
await checkpointManager.save(tmpDir, {
|
|
417
|
-
currentNode: 'node1',
|
|
418
|
-
completedNodes: ['start', 'node1'],
|
|
419
|
-
nodeRetries: {},
|
|
420
|
-
context: seedContext,
|
|
421
|
-
});
|
|
422
|
-
// Parse graph where node1 has fidelity=full
|
|
423
|
-
const graph = parseGraph(COMPLIANCE_FIDELITY_RESUME_DOT);
|
|
424
|
-
// Capture the node argument passed to each handler dispatch
|
|
425
|
-
const capturedNodeByNodeId = {};
|
|
426
|
-
const registry = makeRegistry((node) => {
|
|
427
|
-
capturedNodeByNodeId[node.id] = node;
|
|
428
|
-
});
|
|
429
|
-
const { bus } = makeEventBus();
|
|
430
|
-
const config = {
|
|
431
|
-
runId: 'compliance-fidelity-test',
|
|
432
|
-
logsRoot: tmpDir,
|
|
433
|
-
handlerRegistry: registry,
|
|
434
|
-
eventBus: bus,
|
|
435
|
-
checkpointPath: path.join(tmpDir, 'checkpoint.json'),
|
|
436
|
-
};
|
|
437
|
-
await createGraphExecutor().run(graph, config);
|
|
438
|
-
// node2 is the first resumed node; its fidelity should be degraded to 'summary:high'
|
|
439
|
-
expect(capturedNodeByNodeId['node2']).toBeDefined();
|
|
440
|
-
expect(capturedNodeByNodeId['node2'].fidelity).toBe('summary:high');
|
|
441
|
-
});
|
|
442
|
-
it('subsequent resumed nodes use their configured fidelity (not the degraded value)', async () => {
|
|
443
|
-
// Extend the graph by adding node3 after node2
|
|
444
|
-
const EXTENDED_DOT = `
|
|
445
|
-
digraph compliance_fidelity_extended {
|
|
446
|
-
graph [goal="Extended fidelity test"]
|
|
447
|
-
start [shape=Mdiamond]
|
|
448
|
-
node1 [type=codergen, prompt="Step 1", fidelity=full]
|
|
449
|
-
node2 [type=codergen, prompt="Step 2", fidelity=medium]
|
|
450
|
-
node3 [type=codergen, prompt="Step 3", fidelity=high]
|
|
451
|
-
exit [shape=Msquare]
|
|
452
|
-
|
|
453
|
-
start -> node1
|
|
454
|
-
node1 -> node2
|
|
455
|
-
node2 -> node3
|
|
456
|
-
node3 -> exit
|
|
457
|
-
}
|
|
458
|
-
`;
|
|
459
|
-
const checkpointManager = new CheckpointManager();
|
|
460
|
-
const seedCtx = new GraphContext();
|
|
461
|
-
await checkpointManager.save(tmpDir, {
|
|
462
|
-
currentNode: 'node1',
|
|
463
|
-
completedNodes: ['start', 'node1'],
|
|
464
|
-
nodeRetries: {},
|
|
465
|
-
context: seedCtx,
|
|
466
|
-
});
|
|
467
|
-
const graph = parseGraph(EXTENDED_DOT);
|
|
468
|
-
const capturedNodeByNodeId = {};
|
|
469
|
-
const registry = makeRegistry((node) => {
|
|
470
|
-
capturedNodeByNodeId[node.id] = node;
|
|
471
|
-
});
|
|
472
|
-
const { bus } = makeEventBus();
|
|
473
|
-
const config = {
|
|
474
|
-
runId: 'compliance-fidelity-extended-test',
|
|
475
|
-
logsRoot: tmpDir,
|
|
476
|
-
handlerRegistry: registry,
|
|
477
|
-
eventBus: bus,
|
|
478
|
-
checkpointPath: path.join(tmpDir, 'checkpoint.json'),
|
|
479
|
-
};
|
|
480
|
-
await createGraphExecutor().run(graph, config);
|
|
481
|
-
// node2 is first resumed → degraded to 'summary:high'
|
|
482
|
-
expect(capturedNodeByNodeId['node2'].fidelity).toBe('summary:high');
|
|
483
|
-
// node3 is subsequent → uses its configured fidelity='high'
|
|
484
|
-
expect(capturedNodeByNodeId['node3'].fidelity).toBe('high');
|
|
485
|
-
});
|
|
486
|
-
});
|
|
487
|
-
// ---------------------------------------------------------------------------
|
|
488
|
-
// Task 6: Structural conformance — AttractorBench parity
|
|
489
|
-
// ---------------------------------------------------------------------------
|
|
490
|
-
describe('structural conformance — AttractorBench parity', () => {
|
|
491
|
-
// AttractorBench structural conformance parity — Phase A mandatory
|
|
492
|
-
// See: https://github.com/strongdm/attractorbench
|
|
493
|
-
// Behavioral conformance tests are advisory in Phase A; required by Phase B exit.
|
|
494
|
-
// -------------------------------------------------------------------------
|
|
495
|
-
// Attribute coverage — nodes (all 17 spec-defined + toolCommand from 42-11 + backend from 48-10)
|
|
496
|
-
// -------------------------------------------------------------------------
|
|
497
|
-
it('AC6 — all node attributes extracted with correct values from DOT', () => {
|
|
498
|
-
const dot = `
|
|
499
|
-
digraph compliance_node_attrs {
|
|
500
|
-
graph [goal="Attribute coverage"]
|
|
501
|
-
start [shape=Mdiamond]
|
|
502
|
-
target [
|
|
503
|
-
shape=box,
|
|
504
|
-
type=codergen,
|
|
505
|
-
label="Target Label",
|
|
506
|
-
prompt="Target prompt",
|
|
507
|
-
max_retries=3,
|
|
508
|
-
goal_gate=true,
|
|
509
|
-
retry_target="start",
|
|
510
|
-
fallback_retry_target="start",
|
|
511
|
-
fidelity=high,
|
|
512
|
-
thread_id="thread-1",
|
|
513
|
-
class="myclass",
|
|
514
|
-
timeout=30,
|
|
515
|
-
llm_model="gpt-4o",
|
|
516
|
-
llm_provider=openai,
|
|
517
|
-
reasoning_effort=high,
|
|
518
|
-
auto_status=false,
|
|
519
|
-
allow_partial=true,
|
|
520
|
-
tool_command="echo hello",
|
|
521
|
-
backend=direct
|
|
522
|
-
]
|
|
523
|
-
exit [shape=Msquare]
|
|
524
|
-
start -> target
|
|
525
|
-
target -> exit
|
|
526
|
-
}
|
|
527
|
-
`;
|
|
528
|
-
const graph = parseGraph(dot);
|
|
529
|
-
const node = graph.nodes.get('target');
|
|
530
|
-
expect(node).toBeDefined();
|
|
531
|
-
expect(node.id).toBe('target');
|
|
532
|
-
expect(node.label).toBe('Target Label');
|
|
533
|
-
expect(node.shape).toBe('box');
|
|
534
|
-
expect(node.type).toBe('codergen');
|
|
535
|
-
expect(node.prompt).toBe('Target prompt');
|
|
536
|
-
expect(node.maxRetries).toBe(3);
|
|
537
|
-
expect(node.goalGate).toBe(true);
|
|
538
|
-
expect(node.retryTarget).toBe('start');
|
|
539
|
-
expect(node.fallbackRetryTarget).toBe('start');
|
|
540
|
-
expect(node.fidelity).toBe('high');
|
|
541
|
-
expect(node.threadId).toBe('thread-1');
|
|
542
|
-
expect(node.class).toBe('myclass');
|
|
543
|
-
expect(node.timeout).toBe(30);
|
|
544
|
-
expect(node.llmModel).toBe('gpt-4o');
|
|
545
|
-
expect(node.llmProvider).toBe('openai');
|
|
546
|
-
expect(node.reasoningEffort).toBe('high');
|
|
547
|
-
expect(node.autoStatus).toBe(false);
|
|
548
|
-
expect(node.allowPartial).toBe(true);
|
|
549
|
-
expect(node.toolCommand).toBe('echo hello');
|
|
550
|
-
expect(node.backend).toBe('direct');
|
|
551
|
-
});
|
|
552
|
-
// -------------------------------------------------------------------------
|
|
553
|
-
// Attribute coverage — edges (all 6 edge attributes)
|
|
554
|
-
// -------------------------------------------------------------------------
|
|
555
|
-
it('AC6 — all 6 edge attributes extracted with correct values from DOT', () => {
|
|
556
|
-
const dot = `
|
|
557
|
-
digraph compliance_edge_attrs {
|
|
558
|
-
graph [goal="Edge attribute coverage"]
|
|
559
|
-
start [shape=Mdiamond]
|
|
560
|
-
nodeA [type=codergen, prompt="a"]
|
|
561
|
-
exit [shape=Msquare]
|
|
562
|
-
start -> nodeA [
|
|
563
|
-
label="go",
|
|
564
|
-
condition="outcome=success",
|
|
565
|
-
weight=5,
|
|
566
|
-
fidelity=high,
|
|
567
|
-
thread_id="t2",
|
|
568
|
-
loop_restart=true
|
|
569
|
-
]
|
|
570
|
-
nodeA -> exit
|
|
571
|
-
}
|
|
572
|
-
`;
|
|
573
|
-
const graph = parseGraph(dot);
|
|
574
|
-
const edge = graph.edges.find((e) => e.fromNode === 'start' && e.toNode === 'nodeA');
|
|
575
|
-
expect(edge).toBeDefined();
|
|
576
|
-
expect(edge.label).toBe('go');
|
|
577
|
-
expect(edge.condition).toBe('outcome=success');
|
|
578
|
-
expect(edge.weight).toBe(5);
|
|
579
|
-
expect(edge.fidelity).toBe('high');
|
|
580
|
-
expect(edge.threadId).toBe('t2');
|
|
581
|
-
expect(edge.loopRestart).toBe(true);
|
|
582
|
-
});
|
|
583
|
-
// -------------------------------------------------------------------------
|
|
584
|
-
// Lint rule coverage — 8 error-level rules (stories 42-4)
|
|
585
|
-
// -------------------------------------------------------------------------
|
|
586
|
-
it('AC6 — lint rule "start_node" fires (severity=error) when no start node exists', () => {
|
|
587
|
-
// Graph with no Mdiamond node and no id=start/Start
|
|
588
|
-
const graph = makeLintGraph([{ id: 'work', type: 'codergen', prompt: 'work' }], [{ fromNode: 'work', toNode: 'exit' }]);
|
|
589
|
-
// Override the nodes map to remove start (the makeLintGraph adds start by default — use manual build)
|
|
590
|
-
const nodeMap = new Map();
|
|
591
|
-
nodeMap.set('work', makeNode('work', { type: 'codergen', prompt: 'work' }));
|
|
592
|
-
nodeMap.set('exit', makeNode('exit', { shape: 'Msquare', type: '' }));
|
|
593
|
-
const edges = [makeEdge('work', 'exit')];
|
|
594
|
-
const g = {
|
|
595
|
-
id: 'test', goal: '', label: '', modelStylesheet: '',
|
|
596
|
-
defaultMaxRetries: 0, retryTarget: '', fallbackRetryTarget: '', defaultFidelity: '',
|
|
597
|
-
nodes: nodeMap,
|
|
598
|
-
edges,
|
|
599
|
-
outgoingEdges: (id) => edges.filter((e) => e.fromNode === id),
|
|
600
|
-
startNode: () => { throw new Error('no start'); },
|
|
601
|
-
exitNode: () => nodeMap.get('exit'),
|
|
602
|
-
};
|
|
603
|
-
const diags = createValidator().validate(g);
|
|
604
|
-
const rule = diags.find((d) => d.ruleId === 'start_node');
|
|
605
|
-
expect(rule).toBeDefined();
|
|
606
|
-
expect(rule.severity).toBe('error');
|
|
607
|
-
});
|
|
608
|
-
it('AC6 — lint rule "terminal_node" fires (severity=error) when no exit node exists', () => {
|
|
609
|
-
const nodeMap = new Map();
|
|
610
|
-
nodeMap.set('start', makeNode('start', { shape: 'Mdiamond', type: '' }));
|
|
611
|
-
nodeMap.set('work', makeNode('work', { type: 'codergen', prompt: 'work' }));
|
|
612
|
-
const edges = [makeEdge('start', 'work')];
|
|
613
|
-
const g = {
|
|
614
|
-
id: 'test', goal: '', label: '', modelStylesheet: '',
|
|
615
|
-
defaultMaxRetries: 0, retryTarget: '', fallbackRetryTarget: '', defaultFidelity: '',
|
|
616
|
-
nodes: nodeMap,
|
|
617
|
-
edges,
|
|
618
|
-
outgoingEdges: (id) => edges.filter((e) => e.fromNode === id),
|
|
619
|
-
startNode: () => nodeMap.get('start'),
|
|
620
|
-
exitNode: () => { throw new Error('no exit'); },
|
|
621
|
-
};
|
|
622
|
-
const diags = createValidator().validate(g);
|
|
623
|
-
const rule = diags.find((d) => d.ruleId === 'terminal_node');
|
|
624
|
-
expect(rule).toBeDefined();
|
|
625
|
-
expect(rule.severity).toBe('error');
|
|
626
|
-
});
|
|
627
|
-
it('AC6 — lint rule "start_no_incoming" fires (severity=error) when edge targets the start node', () => {
|
|
628
|
-
const graph = makeLintGraph([{ id: 'work', type: 'codergen', prompt: 'work' }], [
|
|
629
|
-
{ fromNode: 'start', toNode: 'work' },
|
|
630
|
-
{ fromNode: 'work', toNode: 'exit' },
|
|
631
|
-
{ fromNode: 'work', toNode: 'start' }, // incoming edge to start
|
|
632
|
-
]);
|
|
633
|
-
const diags = createValidator().validate(graph);
|
|
634
|
-
const rule = diags.find((d) => d.ruleId === 'start_no_incoming');
|
|
635
|
-
expect(rule).toBeDefined();
|
|
636
|
-
expect(rule.severity).toBe('error');
|
|
637
|
-
});
|
|
638
|
-
it('AC6 — lint rule "exit_no_outgoing" fires (severity=error) when exit node has an outgoing edge', () => {
|
|
639
|
-
const graph = makeLintGraph([{ id: 'work', type: 'codergen', prompt: 'work' }], [
|
|
640
|
-
{ fromNode: 'start', toNode: 'work' },
|
|
641
|
-
{ fromNode: 'work', toNode: 'exit' },
|
|
642
|
-
{ fromNode: 'exit', toNode: 'work' }, // outgoing edge from exit
|
|
643
|
-
]);
|
|
644
|
-
const diags = createValidator().validate(graph);
|
|
645
|
-
const rule = diags.find((d) => d.ruleId === 'exit_no_outgoing');
|
|
646
|
-
expect(rule).toBeDefined();
|
|
647
|
-
expect(rule.severity).toBe('error');
|
|
648
|
-
});
|
|
649
|
-
it('AC6 — lint rule "edge_target_exists" fires (severity=error) when edge targets non-existent node', () => {
|
|
650
|
-
// Build graph directly — DOT auto-creates referenced nodes, so we use in-memory construction
|
|
651
|
-
const nodeMap = new Map();
|
|
652
|
-
nodeMap.set('start', makeNode('start', { shape: 'Mdiamond', type: '' }));
|
|
653
|
-
nodeMap.set('work', makeNode('work', { type: 'codergen', prompt: 'work' }));
|
|
654
|
-
nodeMap.set('exit', makeNode('exit', { shape: 'Msquare', type: '' }));
|
|
655
|
-
const edges = [
|
|
656
|
-
makeEdge('start', 'work'),
|
|
657
|
-
makeEdge('work', 'exit'),
|
|
658
|
-
makeEdge('work', 'ghost_node'), // ghost_node does NOT exist in nodeMap
|
|
659
|
-
];
|
|
660
|
-
const g = {
|
|
661
|
-
id: 'test', goal: '', label: '', modelStylesheet: '',
|
|
662
|
-
defaultMaxRetries: 0, retryTarget: '', fallbackRetryTarget: '', defaultFidelity: '',
|
|
663
|
-
nodes: nodeMap,
|
|
664
|
-
edges,
|
|
665
|
-
outgoingEdges: (id) => edges.filter((e) => e.fromNode === id),
|
|
666
|
-
startNode: () => nodeMap.get('start'),
|
|
667
|
-
exitNode: () => nodeMap.get('exit'),
|
|
668
|
-
};
|
|
669
|
-
const diags = createValidator().validate(g);
|
|
670
|
-
const rule = diags.find((d) => d.ruleId === 'edge_target_exists');
|
|
671
|
-
expect(rule).toBeDefined();
|
|
672
|
-
expect(rule.severity).toBe('error');
|
|
673
|
-
});
|
|
674
|
-
it('AC6 — lint rule "reachability" fires (severity=error) when a node is unreachable', () => {
|
|
675
|
-
const graph = makeLintGraph([
|
|
676
|
-
{ id: 'work', type: 'codergen', prompt: 'work' },
|
|
677
|
-
{ id: 'unreachable', type: 'codergen', prompt: 'unreachable' },
|
|
678
|
-
], [
|
|
679
|
-
{ fromNode: 'start', toNode: 'work' },
|
|
680
|
-
{ fromNode: 'work', toNode: 'exit' },
|
|
681
|
-
// 'unreachable' has no incoming edges from start
|
|
682
|
-
]);
|
|
683
|
-
const diags = createValidator().validate(graph);
|
|
684
|
-
const rule = diags.find((d) => d.ruleId === 'reachability');
|
|
685
|
-
expect(rule).toBeDefined();
|
|
686
|
-
expect(rule.severity).toBe('error');
|
|
687
|
-
});
|
|
688
|
-
it('AC6 — lint rule "condition_syntax" fires (severity=error) for invalid condition expression', () => {
|
|
689
|
-
const graph = makeLintGraph([{ id: 'work', type: 'codergen', prompt: 'work' }], [
|
|
690
|
-
{ fromNode: 'start', toNode: 'work' },
|
|
691
|
-
{ fromNode: 'work', toNode: 'exit', condition: 'a==b' }, // double-equals is invalid
|
|
692
|
-
]);
|
|
693
|
-
const diags = createValidator().validate(graph);
|
|
694
|
-
const rule = diags.find((d) => d.ruleId === 'condition_syntax');
|
|
695
|
-
expect(rule).toBeDefined();
|
|
696
|
-
expect(rule.severity).toBe('error');
|
|
697
|
-
});
|
|
698
|
-
it('AC6 — lint rule "stylesheet_syntax" fires (severity=error) for invalid stylesheet content', () => {
|
|
699
|
-
// Build graph with invalid inline stylesheet
|
|
700
|
-
const graph = makeLintGraph([{ id: 'work', type: 'codergen', prompt: 'work' }], [{ fromNode: 'start', toNode: 'work' }, { fromNode: 'work', toNode: 'exit' }], { modelStylesheet: '!!! invalid stylesheet content !!!' });
|
|
701
|
-
const diags = createValidator().validate(graph);
|
|
702
|
-
const rule = diags.find((d) => d.ruleId === 'stylesheet_syntax');
|
|
703
|
-
expect(rule).toBeDefined();
|
|
704
|
-
expect(rule.severity).toBe('error');
|
|
705
|
-
});
|
|
706
|
-
// -------------------------------------------------------------------------
|
|
707
|
-
// Lint rule coverage — 5 warning-level rules (stories 42-5)
|
|
708
|
-
// -------------------------------------------------------------------------
|
|
709
|
-
it('AC6 — lint rule "type_known" fires (severity=warning) for unknown node type', () => {
|
|
710
|
-
const graph = makeLintGraph([{ id: 'work', type: 'wizard_type', prompt: 'work' }], // wizard_type is unknown
|
|
711
|
-
[{ fromNode: 'start', toNode: 'work' }, { fromNode: 'work', toNode: 'exit' }]);
|
|
712
|
-
const diags = createValidator().validate(graph);
|
|
713
|
-
const rule = diags.find((d) => d.ruleId === 'type_known');
|
|
714
|
-
expect(rule).toBeDefined();
|
|
715
|
-
expect(rule.severity).toBe('warning');
|
|
716
|
-
});
|
|
717
|
-
it('AC6 — lint rule "fidelity_valid" fires (severity=warning) for invalid fidelity value', () => {
|
|
718
|
-
const graph = makeLintGraph([{ id: 'work', type: 'codergen', prompt: 'work', fidelity: 'ultra_high' }], // invalid fidelity
|
|
719
|
-
[{ fromNode: 'start', toNode: 'work' }, { fromNode: 'work', toNode: 'exit' }]);
|
|
720
|
-
const diags = createValidator().validate(graph);
|
|
721
|
-
const rule = diags.find((d) => d.ruleId === 'fidelity_valid');
|
|
722
|
-
expect(rule).toBeDefined();
|
|
723
|
-
expect(rule.severity).toBe('warning');
|
|
724
|
-
});
|
|
725
|
-
it('AC6 — lint rule "retry_target_exists" fires (severity=warning) for missing retryTarget node', () => {
|
|
726
|
-
const graph = makeLintGraph([{ id: 'work', type: 'codergen', prompt: 'work', retryTarget: 'nonexistent' }], [{ fromNode: 'start', toNode: 'work' }, { fromNode: 'work', toNode: 'exit' }]);
|
|
727
|
-
const diags = createValidator().validate(graph);
|
|
728
|
-
const rule = diags.find((d) => d.ruleId === 'retry_target_exists');
|
|
729
|
-
expect(rule).toBeDefined();
|
|
730
|
-
expect(rule.severity).toBe('warning');
|
|
731
|
-
});
|
|
732
|
-
it('AC6 — lint rule "goal_gate_has_retry" fires (severity=warning) when goalGate=true node has no retryTarget', () => {
|
|
733
|
-
const graph = makeLintGraph([{ id: 'work', type: 'codergen', prompt: 'work', goalGate: true, retryTarget: '' }], [{ fromNode: 'start', toNode: 'work' }, { fromNode: 'work', toNode: 'exit' }]);
|
|
734
|
-
const diags = createValidator().validate(graph);
|
|
735
|
-
const rule = diags.find((d) => d.ruleId === 'goal_gate_has_retry');
|
|
736
|
-
expect(rule).toBeDefined();
|
|
737
|
-
expect(rule.severity).toBe('warning');
|
|
738
|
-
});
|
|
739
|
-
it('AC6 — lint rule "prompt_on_llm_nodes" fires (severity=warning) for codergen node with no prompt or label', () => {
|
|
740
|
-
const graph = makeLintGraph([{ id: 'work', type: 'codergen', prompt: '', label: '' }], // no prompt AND no label
|
|
741
|
-
[{ fromNode: 'start', toNode: 'work' }, { fromNode: 'work', toNode: 'exit' }]);
|
|
742
|
-
const diags = createValidator().validate(graph);
|
|
743
|
-
const rule = diags.find((d) => d.ruleId === 'prompt_on_llm_nodes');
|
|
744
|
-
expect(rule).toBeDefined();
|
|
745
|
-
expect(rule.severity).toBe('warning');
|
|
746
|
-
});
|
|
747
|
-
// -------------------------------------------------------------------------
|
|
748
|
-
// Edge selection determinism — identical inputs always produce same output
|
|
749
|
-
// -------------------------------------------------------------------------
|
|
750
|
-
it('AC6 — selectEdge is deterministic: identical inputs always produce same edge', async () => {
|
|
751
|
-
const node = makeNode('origin');
|
|
752
|
-
const edgeA = makeEdge('origin', 'alpha', { weight: 5 });
|
|
753
|
-
const edgeB = makeEdge('origin', 'bravo', { weight: 5 });
|
|
754
|
-
const edgeC = makeEdge('origin', 'charlie', { weight: 3 });
|
|
755
|
-
const graph = makeGraph([node], [edgeA, edgeB, edgeC]);
|
|
756
|
-
const ctx = new GraphContext({ x: '1' });
|
|
757
|
-
const outcome = { status: 'SUCCESS' };
|
|
758
|
-
const first = await selectEdge(node, outcome, ctx, graph);
|
|
759
|
-
const second = await selectEdge(node, outcome, ctx, graph);
|
|
760
|
-
const third = await selectEdge(node, outcome, ctx, graph);
|
|
761
|
-
expect(first?.toNode).toBe(second?.toNode);
|
|
762
|
-
expect(second?.toNode).toBe(third?.toNode);
|
|
763
|
-
expect(first?.toNode).toBe('alpha'); // lexically first among weight-5 ties
|
|
764
|
-
});
|
|
765
|
-
});
|
|
766
|
-
//# sourceMappingURL=attractor-compliance.test.js.map
|