gsd-pi 2.41.0-dev.0acbce9 → 2.41.0-dev.5a170d0
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 +1 -1
- package/dist/cli-web-branch.d.ts +6 -0
- package/dist/cli-web-branch.js +17 -0
- package/dist/onboarding.js +2 -1
- package/dist/resources/extensions/gsd/auto/loop.js +89 -1
- package/dist/resources/extensions/gsd/auto/phases.js +28 -10
- package/dist/resources/extensions/gsd/auto/session.js +6 -0
- package/dist/resources/extensions/gsd/auto-dashboard.js +8 -2
- package/dist/resources/extensions/gsd/auto-dispatch.js +19 -2
- package/dist/resources/extensions/gsd/auto-post-unit.js +7 -0
- package/dist/resources/extensions/gsd/auto-recovery.js +12 -4
- package/dist/resources/extensions/gsd/auto-start.js +8 -3
- package/dist/resources/extensions/gsd/auto-worktree.js +147 -13
- package/dist/resources/extensions/gsd/auto.js +64 -2
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +199 -164
- package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +62 -0
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +2 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +16 -0
- package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +7 -2
- package/dist/resources/extensions/gsd/commands/catalog.js +40 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +1 -0
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
- package/dist/resources/extensions/gsd/commands/handlers/workflow.js +146 -0
- package/dist/resources/extensions/gsd/context-injector.js +74 -0
- package/dist/resources/extensions/gsd/context-store.js +4 -3
- package/dist/resources/extensions/gsd/custom-execution-policy.js +47 -0
- package/dist/resources/extensions/gsd/custom-verification.js +145 -0
- package/dist/resources/extensions/gsd/custom-workflow-engine.js +164 -0
- package/dist/resources/extensions/gsd/dashboard-overlay.js +1 -0
- package/dist/resources/extensions/gsd/db-writer.js +5 -2
- package/dist/resources/extensions/gsd/definition-loader.js +352 -0
- package/dist/resources/extensions/gsd/detection.js +1 -1
- package/dist/resources/extensions/gsd/dev-execution-policy.js +24 -0
- package/dist/resources/extensions/gsd/dev-workflow-engine.js +82 -0
- package/dist/resources/extensions/gsd/doctor.js +11 -1
- package/dist/resources/extensions/gsd/engine-resolver.js +40 -0
- package/dist/resources/extensions/gsd/engine-types.js +8 -0
- package/dist/resources/extensions/gsd/execution-policy.js +8 -0
- package/dist/resources/extensions/gsd/exit-command.js +12 -2
- package/dist/resources/extensions/gsd/export.js +9 -13
- package/dist/resources/extensions/gsd/extension-manifest.json +2 -2
- package/dist/resources/extensions/gsd/files.js +28 -11
- package/dist/resources/extensions/gsd/forensics.js +10 -3
- package/dist/resources/extensions/gsd/git-service.js +5 -1
- package/dist/resources/extensions/gsd/graph.js +225 -0
- package/dist/resources/extensions/gsd/gsd-db.js +25 -8
- package/dist/resources/extensions/gsd/guided-flow-queue.js +1 -1
- package/dist/resources/extensions/gsd/guided-flow.js +7 -3
- package/dist/resources/extensions/gsd/journal.js +85 -0
- package/dist/resources/extensions/gsd/md-importer.js +5 -0
- package/dist/resources/extensions/gsd/milestone-ids.js +1 -1
- package/dist/resources/extensions/gsd/native-git-bridge.js +2 -2
- package/dist/resources/extensions/gsd/post-unit-hooks.js +24 -412
- package/dist/resources/extensions/gsd/preferences-types.js +1 -0
- package/dist/resources/extensions/gsd/preferences.js +1 -0
- package/dist/resources/extensions/gsd/prompt-loader.js +34 -4
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +11 -10
- package/dist/resources/extensions/gsd/prompts/discuss-headless.md +2 -2
- package/dist/resources/extensions/gsd/prompts/discuss.md +1 -1
- package/dist/resources/extensions/gsd/prompts/queue.md +1 -1
- package/dist/resources/extensions/gsd/repo-identity.js +46 -2
- package/dist/resources/extensions/gsd/rule-registry.js +489 -0
- package/dist/resources/extensions/gsd/rule-types.js +6 -0
- package/dist/resources/extensions/gsd/run-manager.js +134 -0
- package/dist/resources/extensions/gsd/service-tier.js +138 -0
- package/dist/resources/extensions/gsd/structured-data-formatter.js +2 -1
- package/dist/resources/extensions/gsd/templates/decisions.md +2 -2
- package/dist/resources/extensions/gsd/workflow-engine.js +7 -0
- package/dist/resources/extensions/gsd/workflow-templates.js +13 -1
- package/dist/resources/extensions/gsd/worktree-manager.js +20 -6
- package/dist/resources/extensions/gsd/worktree-resolver.js +19 -2
- package/dist/resources/extensions/subagent/index.js +7 -3
- package/dist/resources/extensions/voice/index.js +4 -4
- package/dist/resources/skills/create-workflow/SKILL.md +103 -0
- package/dist/resources/skills/create-workflow/references/feature-patterns.md +128 -0
- package/dist/resources/skills/create-workflow/references/verification-policies.md +76 -0
- package/dist/resources/skills/create-workflow/references/yaml-schema-v1.md +46 -0
- package/dist/resources/skills/create-workflow/templates/blog-post-pipeline.yaml +60 -0
- package/dist/resources/skills/create-workflow/templates/code-audit.yaml +60 -0
- package/dist/resources/skills/create-workflow/templates/release-checklist.yaml +66 -0
- package/dist/resources/skills/create-workflow/templates/workflow-definition.yaml +32 -0
- package/dist/resources/skills/create-workflow/workflows/create-from-scratch.md +104 -0
- package/dist/resources/skills/create-workflow/workflows/create-from-template.md +72 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +16 -16
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +16 -16
- package/dist/web/standalone/.next/server/chunks/229.js +3 -3
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/4024.c195dc1fdd2adbea.js +9 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-9afaaebf6042a1d7.js → webpack-fa307370fcf9fb2c.js} +1 -1
- package/dist/web-mode.d.ts +2 -0
- package/dist/web-mode.js +29 -7
- package/package.json +1 -1
- package/packages/native/src/__tests__/text.test.mjs +33 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.js +3 -1
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +10 -7
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +4 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/src/core/discovery-cache.test.ts +4 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/login-dialog.ts +11 -7
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +5 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +5 -1
- package/src/resources/extensions/gsd/auto/loop.ts +101 -1
- package/src/resources/extensions/gsd/auto/phases.ts +30 -10
- package/src/resources/extensions/gsd/auto/session.ts +6 -0
- package/src/resources/extensions/gsd/auto/types.ts +4 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +9 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +25 -5
- package/src/resources/extensions/gsd/auto-post-unit.ts +8 -0
- package/src/resources/extensions/gsd/auto-recovery.ts +12 -4
- package/src/resources/extensions/gsd/auto-start.ts +8 -3
- package/src/resources/extensions/gsd/auto-worktree.ts +162 -18
- package/src/resources/extensions/gsd/auto.ts +71 -2
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +209 -162
- package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +62 -0
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +2 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +13 -0
- package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +9 -2
- package/src/resources/extensions/gsd/commands/catalog.ts +40 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +1 -0
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
- package/src/resources/extensions/gsd/commands/handlers/workflow.ts +164 -0
- package/src/resources/extensions/gsd/context-injector.ts +100 -0
- package/src/resources/extensions/gsd/context-store.ts +4 -3
- package/src/resources/extensions/gsd/custom-execution-policy.ts +73 -0
- package/src/resources/extensions/gsd/custom-verification.ts +180 -0
- package/src/resources/extensions/gsd/custom-workflow-engine.ts +216 -0
- package/src/resources/extensions/gsd/dashboard-overlay.ts +1 -0
- package/src/resources/extensions/gsd/db-writer.ts +6 -2
- package/src/resources/extensions/gsd/definition-loader.ts +462 -0
- package/src/resources/extensions/gsd/detection.ts +1 -1
- package/src/resources/extensions/gsd/dev-execution-policy.ts +51 -0
- package/src/resources/extensions/gsd/dev-workflow-engine.ts +110 -0
- package/src/resources/extensions/gsd/doctor.ts +12 -1
- package/src/resources/extensions/gsd/engine-resolver.ts +57 -0
- package/src/resources/extensions/gsd/engine-types.ts +71 -0
- package/src/resources/extensions/gsd/execution-policy.ts +43 -0
- package/src/resources/extensions/gsd/exit-command.ts +14 -2
- package/src/resources/extensions/gsd/export.ts +8 -15
- package/src/resources/extensions/gsd/extension-manifest.json +2 -2
- package/src/resources/extensions/gsd/files.ts +29 -12
- package/src/resources/extensions/gsd/forensics.ts +9 -3
- package/src/resources/extensions/gsd/git-service.ts +5 -4
- package/src/resources/extensions/gsd/graph.ts +312 -0
- package/src/resources/extensions/gsd/gsd-db.ts +37 -8
- package/src/resources/extensions/gsd/guided-flow-queue.ts +1 -1
- package/src/resources/extensions/gsd/guided-flow.ts +7 -3
- package/src/resources/extensions/gsd/journal.ts +134 -0
- package/src/resources/extensions/gsd/md-importer.ts +6 -0
- package/src/resources/extensions/gsd/milestone-ids.ts +1 -1
- package/src/resources/extensions/gsd/native-git-bridge.ts +2 -2
- package/src/resources/extensions/gsd/post-unit-hooks.ts +24 -462
- package/src/resources/extensions/gsd/preferences-types.ts +3 -0
- package/src/resources/extensions/gsd/preferences.ts +1 -0
- package/src/resources/extensions/gsd/prompt-loader.ts +35 -4
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +11 -10
- package/src/resources/extensions/gsd/prompts/discuss-headless.md +2 -2
- package/src/resources/extensions/gsd/prompts/discuss.md +1 -1
- package/src/resources/extensions/gsd/prompts/queue.md +1 -1
- package/src/resources/extensions/gsd/repo-identity.ts +47 -2
- package/src/resources/extensions/gsd/rule-registry.ts +599 -0
- package/src/resources/extensions/gsd/rule-types.ts +68 -0
- package/src/resources/extensions/gsd/run-manager.ts +180 -0
- package/src/resources/extensions/gsd/service-tier.ts +171 -0
- package/src/resources/extensions/gsd/structured-data-formatter.ts +3 -1
- package/src/resources/extensions/gsd/templates/decisions.md +2 -2
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +103 -120
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +85 -0
- package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +202 -0
- package/src/resources/extensions/gsd/tests/bundled-workflow-defs.test.ts +180 -0
- package/src/resources/extensions/gsd/tests/captures.test.ts +12 -1
- package/src/resources/extensions/gsd/tests/commands-workflow-custom.test.ts +283 -0
- package/src/resources/extensions/gsd/tests/context-injector.test.ts +313 -0
- package/src/resources/extensions/gsd/tests/context-store.test.ts +10 -5
- package/src/resources/extensions/gsd/tests/continue-here.test.ts +20 -20
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +540 -0
- package/src/resources/extensions/gsd/tests/custom-verification.test.ts +382 -0
- package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +339 -0
- package/src/resources/extensions/gsd/tests/dashboard-custom-engine.test.ts +87 -0
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +10 -0
- package/src/resources/extensions/gsd/tests/definition-loader.test.ts +778 -0
- package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +318 -0
- package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +15 -10
- package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +5 -4
- package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +167 -0
- package/src/resources/extensions/gsd/tests/doctor-task-done-missing-summary-slice-loop.test.ts +174 -0
- package/src/resources/extensions/gsd/tests/e2e-workflow-pipeline-integration.test.ts +476 -0
- package/src/resources/extensions/gsd/tests/engine-interfaces-contract.test.ts +271 -0
- package/src/resources/extensions/gsd/tests/exit-command.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/graph-operations.test.ts +599 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +8 -1
- package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +7 -7
- package/src/resources/extensions/gsd/tests/iterate-engine-integration.test.ts +429 -0
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +513 -0
- package/src/resources/extensions/gsd/tests/journal-query-tool.test.ts +147 -0
- package/src/resources/extensions/gsd/tests/journal.test.ts +386 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +31 -1
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/milestone-id-reservation.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/parsers.test.ts +110 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +47 -25
- package/src/resources/extensions/gsd/tests/prompt-db.test.ts +3 -1
- package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +61 -1
- package/src/resources/extensions/gsd/tests/routing-history.test.ts +11 -22
- package/src/resources/extensions/gsd/tests/rule-registry.test.ts +413 -0
- package/src/resources/extensions/gsd/tests/run-manager.test.ts +229 -0
- package/src/resources/extensions/gsd/tests/service-tier.test.ts +98 -0
- package/src/resources/extensions/gsd/tests/skill-lifecycle.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +102 -0
- package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +45 -0
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +117 -0
- package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +6 -1
- package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +178 -0
- package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +195 -105
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +78 -3
- package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +74 -0
- package/src/resources/extensions/gsd/types.ts +3 -0
- package/src/resources/extensions/gsd/workflow-engine.ts +38 -0
- package/src/resources/extensions/gsd/workflow-templates.ts +12 -1
- package/src/resources/extensions/gsd/worktree-manager.ts +21 -6
- package/src/resources/extensions/gsd/worktree-resolver.ts +30 -9
- package/src/resources/extensions/subagent/index.ts +7 -3
- package/src/resources/extensions/voice/index.ts +4 -4
- package/src/resources/skills/create-workflow/SKILL.md +103 -0
- package/src/resources/skills/create-workflow/references/feature-patterns.md +128 -0
- package/src/resources/skills/create-workflow/references/verification-policies.md +76 -0
- package/src/resources/skills/create-workflow/references/yaml-schema-v1.md +46 -0
- package/src/resources/skills/create-workflow/templates/blog-post-pipeline.yaml +60 -0
- package/src/resources/skills/create-workflow/templates/code-audit.yaml +60 -0
- package/src/resources/skills/create-workflow/templates/release-checklist.yaml +66 -0
- package/src/resources/skills/create-workflow/templates/workflow-definition.yaml +32 -0
- package/src/resources/skills/create-workflow/workflows/create-from-scratch.md +104 -0
- package/src/resources/skills/create-workflow/workflows/create-from-template.md +72 -0
- package/dist/web/standalone/.next/static/chunks/4024.279c423e4661ece1.js +0 -9
- /package/dist/web/standalone/.next/static/{SwbKZ7JPNFlEmU4f8pKEv → K7GYOOPvQWX6TKYEKhODM}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{SwbKZ7JPNFlEmU4f8pKEv → K7GYOOPvQWX6TKYEKhODM}/_ssgManifest.js +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// gsd-tools — Structured LLM tool tests
|
|
2
2
|
//
|
|
3
|
-
// Tests the three registered tools:
|
|
3
|
+
// Tests the three registered tools: gsd_decision_save, gsd_requirement_update, gsd_summary_save.
|
|
4
4
|
// Each tool is tested via direct function invocation against an in-memory DB.
|
|
5
5
|
|
|
6
6
|
import { createTestContext } from './test-helpers.ts';
|
|
@@ -50,10 +50,10 @@ function cleanupDir(dir: string): void {
|
|
|
50
50
|
*/
|
|
51
51
|
|
|
52
52
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
53
|
-
//
|
|
53
|
+
// gsd_decision_save tool tests
|
|
54
54
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
55
55
|
|
|
56
|
-
console.log('\n──
|
|
56
|
+
console.log('\n── gsd_decision_save ──');
|
|
57
57
|
|
|
58
58
|
{
|
|
59
59
|
const tmpDir = makeTmpDir();
|
|
@@ -121,10 +121,10 @@ console.log('\n── gsd_save_decision ──');
|
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
124
|
-
//
|
|
124
|
+
// gsd_requirement_update tool tests
|
|
125
125
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
126
126
|
|
|
127
|
-
console.log('\n──
|
|
127
|
+
console.log('\n── gsd_requirement_update ──');
|
|
128
128
|
|
|
129
129
|
{
|
|
130
130
|
const tmpDir = makeTmpDir();
|
|
@@ -192,10 +192,10 @@ console.log('\n── gsd_update_requirement ──');
|
|
|
192
192
|
}
|
|
193
193
|
|
|
194
194
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
195
|
-
//
|
|
195
|
+
// gsd_summary_save tool tests
|
|
196
196
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
197
197
|
|
|
198
|
-
console.log('\n──
|
|
198
|
+
console.log('\n── gsd_summary_save ──');
|
|
199
199
|
|
|
200
200
|
{
|
|
201
201
|
const tmpDir = makeTmpDir();
|
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* iterate-engine-integration.test.ts — Integration tests for iterate/fan-out
|
|
3
|
+
* expansion wired into CustomWorkflowEngine.
|
|
4
|
+
*
|
|
5
|
+
* Proves the full expansion→dispatch→reconcile cycle: the engine reads
|
|
6
|
+
* iterate config from frozen DEFINITION.yaml, reads the source artifact,
|
|
7
|
+
* extracts items via regex, calls expandIteration() to rewrite the graph,
|
|
8
|
+
* persists it, and dispatches instance steps sequentially.
|
|
9
|
+
*
|
|
10
|
+
* Uses real temp directories with actual DEFINITION.yaml, GRAPH.yaml,
|
|
11
|
+
* and source artifact files — no mocks.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { describe, it, afterEach } from "node:test";
|
|
15
|
+
import assert from "node:assert/strict";
|
|
16
|
+
import { mkdtempSync, rmSync, writeFileSync, mkdirSync } from "node:fs";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
import { tmpdir } from "node:os";
|
|
19
|
+
import { stringify } from "yaml";
|
|
20
|
+
|
|
21
|
+
import { CustomWorkflowEngine } from "../custom-workflow-engine.ts";
|
|
22
|
+
import {
|
|
23
|
+
writeGraph,
|
|
24
|
+
readGraph,
|
|
25
|
+
type WorkflowGraph,
|
|
26
|
+
type GraphStep,
|
|
27
|
+
} from "../graph.ts";
|
|
28
|
+
import type { WorkflowDefinition } from "../definition-loader.ts";
|
|
29
|
+
|
|
30
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
const tmpDirs: string[] = [];
|
|
33
|
+
|
|
34
|
+
function makeTmpDir(): string {
|
|
35
|
+
const dir = mkdtempSync(join(tmpdir(), "iterate-test-"));
|
|
36
|
+
tmpDirs.push(dir);
|
|
37
|
+
return dir;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
afterEach(() => {
|
|
41
|
+
for (const d of tmpDirs) {
|
|
42
|
+
try { rmSync(d, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 }); } catch { /* Windows EPERM */ }
|
|
43
|
+
}
|
|
44
|
+
tmpDirs.length = 0;
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Create a temp run directory with DEFINITION.yaml, GRAPH.yaml, and optional
|
|
49
|
+
* artifact files. Returns the run dir path and engine instance.
|
|
50
|
+
*/
|
|
51
|
+
function makeTempRun(
|
|
52
|
+
def: WorkflowDefinition,
|
|
53
|
+
graphSteps: GraphStep[],
|
|
54
|
+
files?: Record<string, string>,
|
|
55
|
+
): { runDir: string; engine: CustomWorkflowEngine } {
|
|
56
|
+
const runDir = makeTmpDir();
|
|
57
|
+
|
|
58
|
+
// Write frozen DEFINITION.yaml (camelCase — serialized from TS object)
|
|
59
|
+
writeFileSync(join(runDir, "DEFINITION.yaml"), stringify(def), "utf-8");
|
|
60
|
+
|
|
61
|
+
// Write GRAPH.yaml via the standard writer
|
|
62
|
+
const graph: WorkflowGraph = {
|
|
63
|
+
steps: graphSteps,
|
|
64
|
+
metadata: { name: def.name, createdAt: "2026-01-01T00:00:00.000Z" },
|
|
65
|
+
};
|
|
66
|
+
writeGraph(runDir, graph);
|
|
67
|
+
|
|
68
|
+
// Write optional artifact files
|
|
69
|
+
if (files) {
|
|
70
|
+
for (const [relPath, content] of Object.entries(files)) {
|
|
71
|
+
const absPath = join(runDir, relPath);
|
|
72
|
+
mkdirSync(join(absPath, ".."), { recursive: true });
|
|
73
|
+
writeFileSync(absPath, content, "utf-8");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return { runDir, engine: new CustomWorkflowEngine(runDir) };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Shorthand to build a GraphStep. */
|
|
81
|
+
function makeStep(overrides: Partial<GraphStep> & { id: string }): GraphStep {
|
|
82
|
+
return {
|
|
83
|
+
title: overrides.id,
|
|
84
|
+
status: "pending",
|
|
85
|
+
prompt: `Do ${overrides.id}`,
|
|
86
|
+
dependsOn: [],
|
|
87
|
+
...overrides,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Drive a full deriveState→resolveDispatch cycle. */
|
|
92
|
+
async function dispatch(engine: CustomWorkflowEngine) {
|
|
93
|
+
const state = await engine.deriveState("/unused");
|
|
94
|
+
return engine.resolveDispatch(state, { basePath: "/unused" });
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Drive a full deriveState→reconcile cycle for a given unitId. */
|
|
98
|
+
async function reconcile(engine: CustomWorkflowEngine, unitId: string) {
|
|
99
|
+
const state = await engine.deriveState("/unused");
|
|
100
|
+
return engine.reconcile(state, {
|
|
101
|
+
unitType: "custom-step",
|
|
102
|
+
unitId,
|
|
103
|
+
startedAt: Date.now() - 1000,
|
|
104
|
+
finishedAt: Date.now(),
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ─── Tests ───────────────────────────────────────────────────────────────
|
|
109
|
+
|
|
110
|
+
describe("iterate expansion — basic", () => {
|
|
111
|
+
it("expands an iterate step into 3 instances and dispatches the first", async () => {
|
|
112
|
+
const def: WorkflowDefinition = {
|
|
113
|
+
version: 1,
|
|
114
|
+
name: "iter-wf",
|
|
115
|
+
steps: [
|
|
116
|
+
{
|
|
117
|
+
id: "iter-step",
|
|
118
|
+
name: "Iterate Step",
|
|
119
|
+
prompt: "Process {{item}}",
|
|
120
|
+
requires: [],
|
|
121
|
+
produces: [],
|
|
122
|
+
iterate: { source: "topics.md", pattern: "^- (.+)$" },
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const graphSteps = [
|
|
128
|
+
makeStep({ id: "iter-step", prompt: "Process {{item}}" }),
|
|
129
|
+
];
|
|
130
|
+
|
|
131
|
+
const { runDir, engine } = makeTempRun(def, graphSteps, {
|
|
132
|
+
"topics.md": "- Alpha\n- Beta\n- Gamma\n",
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const result = await dispatch(engine);
|
|
136
|
+
|
|
137
|
+
// Should dispatch the first instance step
|
|
138
|
+
assert.equal(result.action, "dispatch");
|
|
139
|
+
if (result.action === "dispatch") {
|
|
140
|
+
assert.equal(result.step.unitId, "iter-wf/iter-step--001");
|
|
141
|
+
assert.equal(result.step.prompt, "Process Alpha");
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Verify on-disk graph state
|
|
145
|
+
const graph = readGraph(runDir);
|
|
146
|
+
const parent = graph.steps.find((s) => s.id === "iter-step");
|
|
147
|
+
assert.ok(parent, "Parent step should exist");
|
|
148
|
+
assert.equal(parent.status, "expanded");
|
|
149
|
+
|
|
150
|
+
const instances = graph.steps.filter((s) => s.parentStepId === "iter-step");
|
|
151
|
+
assert.equal(instances.length, 3);
|
|
152
|
+
assert.equal(instances[0].id, "iter-step--001");
|
|
153
|
+
assert.equal(instances[1].id, "iter-step--002");
|
|
154
|
+
assert.equal(instances[2].id, "iter-step--003");
|
|
155
|
+
assert.equal(instances[0].prompt, "Process Alpha");
|
|
156
|
+
assert.equal(instances[1].prompt, "Process Beta");
|
|
157
|
+
assert.equal(instances[2].prompt, "Process Gamma");
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
describe("iterate expansion — full dispatch→reconcile sequence", () => {
|
|
162
|
+
it("dispatches all 3 instances sequentially then stops", async () => {
|
|
163
|
+
const def: WorkflowDefinition = {
|
|
164
|
+
version: 1,
|
|
165
|
+
name: "seq-wf",
|
|
166
|
+
steps: [
|
|
167
|
+
{
|
|
168
|
+
id: "fan",
|
|
169
|
+
name: "Fan Step",
|
|
170
|
+
prompt: "Handle {{item}}",
|
|
171
|
+
requires: [],
|
|
172
|
+
produces: [],
|
|
173
|
+
iterate: { source: "items.md", pattern: "^- (.+)$" },
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const graphSteps = [makeStep({ id: "fan", prompt: "Handle {{item}}" })];
|
|
179
|
+
|
|
180
|
+
const { engine } = makeTempRun(def, graphSteps, {
|
|
181
|
+
"items.md": "- One\n- Two\n- Three\n",
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// First dispatch triggers expansion, returns instance 1
|
|
185
|
+
let result = await dispatch(engine);
|
|
186
|
+
assert.equal(result.action, "dispatch");
|
|
187
|
+
if (result.action === "dispatch") {
|
|
188
|
+
assert.equal(result.step.unitId, "seq-wf/fan--001");
|
|
189
|
+
assert.equal(result.step.prompt, "Handle One");
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Reconcile instance 1, dispatch → instance 2
|
|
193
|
+
await reconcile(engine, "seq-wf/fan--001");
|
|
194
|
+
result = await dispatch(engine);
|
|
195
|
+
assert.equal(result.action, "dispatch");
|
|
196
|
+
if (result.action === "dispatch") {
|
|
197
|
+
assert.equal(result.step.unitId, "seq-wf/fan--002");
|
|
198
|
+
assert.equal(result.step.prompt, "Handle Two");
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Reconcile instance 2, dispatch → instance 3
|
|
202
|
+
await reconcile(engine, "seq-wf/fan--002");
|
|
203
|
+
result = await dispatch(engine);
|
|
204
|
+
assert.equal(result.action, "dispatch");
|
|
205
|
+
if (result.action === "dispatch") {
|
|
206
|
+
assert.equal(result.step.unitId, "seq-wf/fan--003");
|
|
207
|
+
assert.equal(result.step.prompt, "Handle Three");
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Reconcile instance 3, dispatch → should stop (all done)
|
|
211
|
+
await reconcile(engine, "seq-wf/fan--003");
|
|
212
|
+
result = await dispatch(engine);
|
|
213
|
+
assert.equal(result.action, "stop");
|
|
214
|
+
if (result.action === "stop") {
|
|
215
|
+
assert.equal(result.reason, "All steps complete");
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
describe("iterate expansion — downstream blocking", () => {
|
|
221
|
+
it("blocks downstream step until all instances are complete", async () => {
|
|
222
|
+
const def: WorkflowDefinition = {
|
|
223
|
+
version: 1,
|
|
224
|
+
name: "block-wf",
|
|
225
|
+
steps: [
|
|
226
|
+
{
|
|
227
|
+
id: "fan",
|
|
228
|
+
name: "Fan Step",
|
|
229
|
+
prompt: "Process {{item}}",
|
|
230
|
+
requires: [],
|
|
231
|
+
produces: [],
|
|
232
|
+
iterate: { source: "items.md", pattern: "^- (.+)$" },
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
id: "merge",
|
|
236
|
+
name: "Merge Step",
|
|
237
|
+
prompt: "Merge all results",
|
|
238
|
+
requires: ["fan"],
|
|
239
|
+
produces: [],
|
|
240
|
+
},
|
|
241
|
+
],
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const graphSteps = [
|
|
245
|
+
makeStep({ id: "fan", prompt: "Process {{item}}" }),
|
|
246
|
+
makeStep({ id: "merge", prompt: "Merge all results", dependsOn: ["fan"] }),
|
|
247
|
+
];
|
|
248
|
+
|
|
249
|
+
const { runDir, engine } = makeTempRun(def, graphSteps, {
|
|
250
|
+
"items.md": "- X\n- Y\n",
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
// First dispatch: expands and returns instance 1
|
|
254
|
+
let result = await dispatch(engine);
|
|
255
|
+
assert.equal(result.action, "dispatch");
|
|
256
|
+
if (result.action === "dispatch") {
|
|
257
|
+
assert.equal(result.step.unitId, "block-wf/fan--001");
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Verify downstream dep was rewritten: merge now depends on fan--001, fan--002
|
|
261
|
+
let graph = readGraph(runDir);
|
|
262
|
+
const mergeStep = graph.steps.find((s) => s.id === "merge");
|
|
263
|
+
assert.ok(mergeStep);
|
|
264
|
+
assert.deepStrictEqual(mergeStep.dependsOn.sort(), ["fan--001", "fan--002"]);
|
|
265
|
+
|
|
266
|
+
// Complete instance 1 only — merge should NOT be dispatchable yet
|
|
267
|
+
await reconcile(engine, "block-wf/fan--001");
|
|
268
|
+
result = await dispatch(engine);
|
|
269
|
+
assert.equal(result.action, "dispatch");
|
|
270
|
+
if (result.action === "dispatch") {
|
|
271
|
+
// Should get fan--002, not merge
|
|
272
|
+
assert.equal(result.step.unitId, "block-wf/fan--002");
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Complete instance 2 — now merge should be dispatchable
|
|
276
|
+
await reconcile(engine, "block-wf/fan--002");
|
|
277
|
+
result = await dispatch(engine);
|
|
278
|
+
assert.equal(result.action, "dispatch");
|
|
279
|
+
if (result.action === "dispatch") {
|
|
280
|
+
assert.equal(result.step.unitId, "block-wf/merge");
|
|
281
|
+
assert.equal(result.step.prompt, "Merge all results");
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Complete merge — all done
|
|
285
|
+
await reconcile(engine, "block-wf/merge");
|
|
286
|
+
result = await dispatch(engine);
|
|
287
|
+
assert.equal(result.action, "stop");
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
describe("iterate expansion — zero matches", () => {
|
|
292
|
+
it("handles zero-match expansion gracefully", async () => {
|
|
293
|
+
const def: WorkflowDefinition = {
|
|
294
|
+
version: 1,
|
|
295
|
+
name: "zero-wf",
|
|
296
|
+
steps: [
|
|
297
|
+
{
|
|
298
|
+
id: "fan",
|
|
299
|
+
name: "Fan Step",
|
|
300
|
+
prompt: "Process {{item}}",
|
|
301
|
+
requires: [],
|
|
302
|
+
produces: [],
|
|
303
|
+
iterate: { source: "items.md", pattern: "^- (.+)$" },
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
id: "after",
|
|
307
|
+
name: "After Step",
|
|
308
|
+
prompt: "Do after",
|
|
309
|
+
requires: ["fan"],
|
|
310
|
+
produces: [],
|
|
311
|
+
},
|
|
312
|
+
],
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
const graphSteps = [
|
|
316
|
+
makeStep({ id: "fan", prompt: "Process {{item}}" }),
|
|
317
|
+
makeStep({ id: "after", prompt: "Do after", dependsOn: ["fan"] }),
|
|
318
|
+
];
|
|
319
|
+
|
|
320
|
+
// Source file exists but has no matching lines
|
|
321
|
+
const { runDir, engine } = makeTempRun(def, graphSteps, {
|
|
322
|
+
"items.md": "No bullet items here\nJust plain text\n",
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
// Dispatch should expand with zero instances
|
|
326
|
+
const result = await dispatch(engine);
|
|
327
|
+
|
|
328
|
+
// Verify parent is expanded
|
|
329
|
+
const graph = readGraph(runDir);
|
|
330
|
+
const parent = graph.steps.find((s) => s.id === "fan");
|
|
331
|
+
assert.ok(parent);
|
|
332
|
+
assert.equal(parent.status, "expanded");
|
|
333
|
+
|
|
334
|
+
// With zero instances, no instance deps exist.
|
|
335
|
+
// expandIteration rewrites "fan" → [] in the downstream dep list,
|
|
336
|
+
// so "after" now has empty dependsOn and becomes dispatchable.
|
|
337
|
+
// But first dispatch after expansion finds no pending instance steps.
|
|
338
|
+
// The engine should either dispatch "after" or return stop.
|
|
339
|
+
// Let's check what actually happened:
|
|
340
|
+
if (result.action === "dispatch") {
|
|
341
|
+
// The re-query found "after" step (since its deps were rewritten to [])
|
|
342
|
+
assert.equal(result.step.unitId, "zero-wf/after");
|
|
343
|
+
} else {
|
|
344
|
+
// The engine returned stop for zero instances
|
|
345
|
+
assert.equal(result.action, "stop");
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
describe("iterate expansion — missing source artifact", () => {
|
|
351
|
+
it("throws an error mentioning the missing file path", async () => {
|
|
352
|
+
const def: WorkflowDefinition = {
|
|
353
|
+
version: 1,
|
|
354
|
+
name: "missing-wf",
|
|
355
|
+
steps: [
|
|
356
|
+
{
|
|
357
|
+
id: "fan",
|
|
358
|
+
name: "Fan Step",
|
|
359
|
+
prompt: "Process {{item}}",
|
|
360
|
+
requires: [],
|
|
361
|
+
produces: [],
|
|
362
|
+
iterate: { source: "nonexistent.md", pattern: "^- (.+)$" },
|
|
363
|
+
},
|
|
364
|
+
],
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
const graphSteps = [
|
|
368
|
+
makeStep({ id: "fan", prompt: "Process {{item}}" }),
|
|
369
|
+
];
|
|
370
|
+
|
|
371
|
+
// No source file written
|
|
372
|
+
const { engine } = makeTempRun(def, graphSteps);
|
|
373
|
+
|
|
374
|
+
await assert.rejects(
|
|
375
|
+
() => dispatch(engine),
|
|
376
|
+
(err: Error) => {
|
|
377
|
+
assert.ok(err.message.includes("nonexistent.md"), `Error should mention the filename: ${err.message}`);
|
|
378
|
+
assert.ok(err.message.includes("Iterate source artifact not found"), `Error should mention it's an iterate source: ${err.message}`);
|
|
379
|
+
return true;
|
|
380
|
+
},
|
|
381
|
+
);
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
describe("iterate expansion — idempotency", () => {
|
|
386
|
+
it("does not re-expand an already expanded step on subsequent dispatch", async () => {
|
|
387
|
+
const def: WorkflowDefinition = {
|
|
388
|
+
version: 1,
|
|
389
|
+
name: "idem-wf",
|
|
390
|
+
steps: [
|
|
391
|
+
{
|
|
392
|
+
id: "fan",
|
|
393
|
+
name: "Fan Step",
|
|
394
|
+
prompt: "Process {{item}}",
|
|
395
|
+
requires: [],
|
|
396
|
+
produces: [],
|
|
397
|
+
iterate: { source: "items.md", pattern: "^- (.+)$" },
|
|
398
|
+
},
|
|
399
|
+
],
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
const graphSteps = [makeStep({ id: "fan", prompt: "Process {{item}}" })];
|
|
403
|
+
|
|
404
|
+
const { runDir, engine } = makeTempRun(def, graphSteps, {
|
|
405
|
+
"items.md": "- Uno\n- Dos\n",
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
// First dispatch: triggers expansion
|
|
409
|
+
let result = await dispatch(engine);
|
|
410
|
+
assert.equal(result.action, "dispatch");
|
|
411
|
+
if (result.action === "dispatch") {
|
|
412
|
+
assert.equal(result.step.unitId, "idem-wf/fan--001");
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Second dispatch without reconciling: should return the same instance
|
|
416
|
+
// (graph already expanded on disk, parent is "expanded" so getNextPendingStep
|
|
417
|
+
// skips it and returns the first pending instance step)
|
|
418
|
+
result = await dispatch(engine);
|
|
419
|
+
assert.equal(result.action, "dispatch");
|
|
420
|
+
if (result.action === "dispatch") {
|
|
421
|
+
assert.equal(result.step.unitId, "idem-wf/fan--001");
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Verify no double-expansion: still only 2 instances
|
|
425
|
+
const graph = readGraph(runDir);
|
|
426
|
+
const instances = graph.steps.filter((s) => s.parentStepId === "fan");
|
|
427
|
+
assert.equal(instances.length, 2);
|
|
428
|
+
});
|
|
429
|
+
});
|