groundswell 0.0.1 → 0.0.2
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/.claude/commands/subtask-planning/prp-base-create.md +120 -0
- package/.claude/commands/subtask-planning/prp-base-execute.md +65 -0
- package/.claude/commands/task-breakdown.md +94 -0
- package/.claude/system_prompts/task-breakdown.md +1 -0
- package/CHANGELOG.md +188 -0
- package/PRD.md +543 -0
- package/README.md +99 -5
- package/examples/README.md +15 -1
- package/examples/examples/11-reparenting-workflows.ts +269 -0
- package/examples/index.ts +4 -0
- package/package-lock.json +2398 -0
- package/package.json +3 -1
- package/plan/001_d3bb02af4886/TEST_RESULTS.md +259 -0
- package/plan/001_d3bb02af4886/bug_fix_tasks.json +484 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M1T1S1/PRP.md +488 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M1T1S2/PRP.md +581 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M1T1S3/PRP.md +687 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S1/PRP.md +492 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S3/PRP.md +932 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S3/research/concurrent_error_testing_patterns.md +1109 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S3/research/vitest_concurrent_testing.md +802 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S3/research/workflow_engine_test_references.md +603 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T2S1/PRP.md +564 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T2S3/PRP.md +518 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T2S4/PRP.md +1252 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/PRP.md +364 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/research/CODEBASE_INVENTORY.md +114 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/research/DECORATOR_DOCUMENTATION_PATTERNS.md +205 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/research/PRD_LOCATION_ANALYSIS.md +199 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/research/ULTRATHINK_PRP_PLAN.md +134 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T1S1/PRP.md +495 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T1S1/research/console_error_inventory.md +435 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T1S2/PRP.md +506 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T1S3/PRP.md +612 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T2S2/PRP.md +558 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T2S2/research/external_research.md +788 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T3S2/PRP.md +460 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T3S3/PRP.md +454 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S1/PRP.md +520 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S1/RECOMMENDATION.md +417 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S1/research/external_workflow_engines_research.md +760 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S1/research/security_implications_analysis.md +245 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S2/PRP.md +792 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S1/PRP.md +535 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S1/TEST_EXECUTION_REPORT.md +190 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/PRP.md +654 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/TEST_FIX_REPORT.md +227 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/research/KEY_FINDINGS.md +345 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/research/QUICK_REFERENCE.md +193 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/research/test_maintenance_research.md +1323 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T3S1/BREAKING_CHANGES_AUDIT.md +1011 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T3S1/PRP.md +927 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T3S2/PRP.md +505 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/architecture/logger_child_signature_analysis.md +401 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S3/child_implementation_research.md +142 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S3/test_patterns_research.md +112 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S3/vitest_patterns_research.md +159 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S4/PRP.md +549 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S4/VERIFICATION_REPORT.md +368 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S4/edge_case_analysis.md +172 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S4/usage_inventory.md +175 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T1S2/PRP.md +696 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T1S4/PRP.md +860 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/PRP.md +1066 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/01-testing-aggregated-errors.md +1103 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/01_typescript_error_aggregation_patterns.md +789 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/02-error-merge-strategy-testing-guide.md +1098 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/02_aggregate_error_patterns.md +1037 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/03-promise-allsettled-testing-patterns.md +916 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/03_error_merging_strategies.md +1045 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/04_github_stackoverflow_examples.md +890 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/05_comprehensive_summary.md +822 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/INDEX.md +668 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/QUICK_REFERENCE.md +706 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/README.md +265 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/RESEARCH_REPORT.md +655 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S4/research/vitest_testing_patterns.md +1103 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T3S2/PRP.md +426 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S2/PRP.md +506 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S2/research/QUICK_REFERENCE.md +114 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S2/research/RESEARCH_SUMMARY.md +316 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S2/research/vitest_observer_error_logging_best_practices.md +754 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S3/PRP.md +612 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T2S1/PRP.md +719 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T2S1/README.md +215 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T2S1/analysis.md +765 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T2S3/PRP.md +718 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/DECISION.md +149 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/PRP.md +470 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/research/ULTRATHINK_PLAN.md +332 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/research/codebase_workflow_name_analysis.md +167 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/research/external_best_practices.md +265 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/research/validation_patterns.md +273 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T4S1/workflow_engine_ancestry_api_research.md +760 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T4S3-PRP.md +434 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M4T2S1/PRP.md +717 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M4T2S2/PRP.md +472 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M4T2S2/VALIDATION_REPORT.md +125 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M4T2S2/research/ULTRATHINK_PRP_PLAN.md +301 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/error-logging-best-practices.md +1170 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/research_typescript_partial_and_overloads.md +940 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/vitest-quick-reference.md +151 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/vitest-research.md +650 -0
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/prd_snapshot.md +259 -0
- package/plan/001_d3bb02af4886/bugfix/P1M1T1S1/PRP.md +457 -0
- package/plan/001_d3bb02af4886/bugfix/RESEARCH_SUMMARY.md +346 -0
- package/plan/001_d3bb02af4886/bugfix/architecture/codebase_structure.md +311 -0
- package/plan/001_d3bb02af4886/bugfix/architecture/concurrent_execution_best_practices.md +1565 -0
- package/plan/001_d3bb02af4886/bugfix/architecture/error_handling_patterns.md +288 -0
- package/plan/001_d3bb02af4886/bugfix/architecture/promise_all_analysis.md +741 -0
- package/plan/001_d3bb02af4886/docs/PRP/P1M1T1S4-functional-workflow-error-state-capture-test.md +652 -0
- package/plan/001_d3bb02af4886/docs/PRP/PRP.md +527 -0
- package/plan/001_d3bb02af4886/docs/PRP/bugfix/P1M1T2S1-PRP.md +415 -0
- package/plan/001_d3bb02af4886/docs/PRP/bugfix/P1M1T2S2-PRP.md +378 -0
- package/plan/001_d3bb02af4886/docs/PRP/bugfix/P1M1T2S4-PRP.md +713 -0
- package/plan/001_d3bb02af4886/docs/PRP/bugfix/P1M2T1S4-PRP.md +370 -0
- package/plan/001_d3bb02af4886/docs/PRP_P1M3T1S3.md +499 -0
- package/plan/001_d3bb02af4886/docs/TEST_RESULTS.md +230 -0
- package/plan/001_d3bb02af4886/docs/bugfix/ANALYSIS_PRD_VS_IMPLEMENTATION.md +1134 -0
- package/plan/001_d3bb02af4886/docs/bugfix/GAP_ANALYSIS_SUMMARY.md +179 -0
- package/plan/001_d3bb02af4886/docs/bugfix/P1M4T2S1/PRP.md +629 -0
- package/plan/001_d3bb02af4886/docs/bugfix/P1M4T2S1/validation-report.md +214 -0
- package/plan/001_d3bb02af4886/docs/bugfix/PRP_P1M4T2S3.md +629 -0
- package/plan/001_d3bb02af4886/docs/bugfix/bugfix_PRP.md +529 -0
- package/plan/001_d3bb02af4886/docs/bugfix/bugfix_QUICK_REFERENCE.md +142 -0
- package/plan/001_d3bb02af4886/docs/bugfix/bugfix_README.md +304 -0
- package/plan/001_d3bb02af4886/docs/bugfix/bugfix_TEST_RESULTS.md +558 -0
- package/plan/001_d3bb02af4886/docs/bugfix/bugfix_VALIDATION_SUMMARY.md +256 -0
- package/plan/001_d3bb02af4886/docs/bugfix/system_context.md +346 -0
- package/plan/001_d3bb02af4886/docs/bugfix-architecture/bug_analysis.md +415 -0
- package/plan/001_d3bb02af4886/docs/bugfix-architecture/implementation_patterns.md +489 -0
- package/plan/001_d3bb02af4886/docs/bugfix-architecture/system_context.md +218 -0
- package/plan/001_d3bb02af4886/docs/bugfix_INITIATION_SUMMARY.md +380 -0
- package/plan/001_d3bb02af4886/docs/research/CYCLE_DETECTION_PATTERNS.md +1923 -0
- package/plan/001_d3bb02af4886/docs/research/CYCLE_DETECTION_QUICK_REF.md +319 -0
- package/plan/001_d3bb02af4886/docs/research/P1M1T2S1/codebase-context.md +115 -0
- package/plan/001_d3bb02af4886/docs/research/P1M1T2S1/cycle-detection-algorithms.md +134 -0
- package/plan/001_d3bb02af4886/docs/research/P1M1T2S1/test-patterns.md +153 -0
- package/plan/001_d3bb02af4886/docs/research/P1M1T2S1/workflow-class.md +132 -0
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/DECORATOR_DOCUMENTATION_BEST_PRACTICES.md +716 -0
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/DECORATOR_DOCUMENTATION_QUICK_REF.md +186 -0
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/GROUNDSWELL_DECORATOR_EXAMPLES.md +604 -0
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/INDEX.md +213 -0
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/codebase_structure.md +30 -0
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/existing_test_pattern.md +56 -0
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/getRootObservers_implementation.md +53 -0
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/test_conventions.md +49 -0
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/PRP.md +958 -0
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/QUICK_REFERENCE.md +339 -0
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/README.md +305 -0
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/SUMMARY.md +433 -0
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/bidirectional-tree-consistency-testing.md +1574 -0
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/test-pattern-examples.md +1014 -0
- package/plan/001_d3bb02af4886/docs/research/PROMISE_ALLSETTLED_QUICK_REF.md +376 -0
- package/plan/001_d3bb02af4886/docs/research/PROMISE_ALLSETTLED_RESEARCH.md +1507 -0
- package/plan/001_d3bb02af4886/docs/research/bugfix_typescript_patterns.md +949 -0
- package/plan/001_d3bb02af4886/docs/research/error-testing-research.md +619 -0
- package/plan/001_d3bb02af4886/docs/research/error_handling_patterns.md +723 -0
- package/plan/{research → 001_d3bb02af4886/docs/research/general}/introspection-security-guide.md +56 -0
- package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/PRP_TEMPLATE.md +460 -0
- package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/QUICK_REFERENCE.md +324 -0
- package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/README.md +175 -0
- package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/RESEARCH_REPORT.md +499 -0
- package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/SUMMARY.md +163 -0
- package/plan/bugfix/BUG_FIX_SUMMARY.md +961 -0
- package/src/__tests__/adversarial/attachChild-performance.test.ts +216 -0
- package/src/__tests__/adversarial/circular-reference.test.ts +101 -0
- package/src/__tests__/adversarial/complex-circular-reference.test.ts +139 -0
- package/src/__tests__/adversarial/concurrent-task-failures.test.ts +571 -0
- package/src/__tests__/adversarial/deep-analysis.test.ts +729 -0
- package/src/__tests__/adversarial/deep-hierarchy-stress.test.ts +213 -0
- package/src/__tests__/adversarial/e2e-prd-validation.test.ts +448 -0
- package/src/__tests__/adversarial/edge-case.test.ts +703 -0
- package/src/__tests__/adversarial/error-merge-strategy.test.ts +760 -0
- package/src/__tests__/adversarial/incremental-performance.test.ts +140 -0
- package/src/__tests__/adversarial/node-map-update-benchmarks.test.ts +457 -0
- package/src/__tests__/adversarial/observer-propagation.test.ts +487 -0
- package/src/__tests__/adversarial/parent-validation.test.ts +143 -0
- package/src/__tests__/adversarial/prd-12-2-compliance.test.ts +611 -0
- package/src/__tests__/adversarial/prd-compliance.test.ts +731 -0
- package/src/__tests__/compatibility/backward-compatibility.test.ts +1572 -0
- package/src/__tests__/helpers/index.ts +18 -0
- package/src/__tests__/helpers/tree-verification.ts +257 -0
- package/src/__tests__/integration/bidirectional-consistency.test.ts +847 -0
- package/src/__tests__/integration/observer-logging.test.ts +643 -0
- package/src/__tests__/integration/tree-mirroring.test.ts +37 -0
- package/src/__tests__/integration/workflow-reparenting.test.ts +303 -0
- package/src/__tests__/unit/context.test.ts +79 -0
- package/src/__tests__/unit/logger.test.ts +293 -0
- package/src/__tests__/unit/observable.test.ts +321 -0
- package/src/__tests__/unit/tree-debugger-incremental.test.ts +170 -0
- package/src/__tests__/unit/utils/workflow-error-utils.test.ts +209 -0
- package/src/__tests__/unit/workflow-detachChild.test.ts +100 -0
- package/src/__tests__/unit/workflow-emitEvent-childDetached.test.ts +153 -0
- package/src/__tests__/unit/workflow-isDescendantOf.test.ts +180 -0
- package/src/__tests__/unit/workflow.test.ts +277 -1
- package/src/core/agent.ts +21 -1
- package/src/core/logger.ts +27 -2
- package/src/core/workflow-context.ts +6 -4
- package/src/core/workflow.ts +252 -14
- package/src/debugger/tree-debugger.ts +52 -7
- package/src/decorators/task.ts +65 -2
- package/src/index.ts +4 -2
- package/src/types/decorators.ts +8 -1
- package/src/types/events.ts +1 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/observable.ts +32 -3
- package/src/utils/workflow-error-utils.ts +56 -0
- package/tsconfig.json +1 -1
- package/llms_full.txt +0 -5890
- package/tasks.json +0 -0
- /package/plan/{backlog.json → 001_d3bb02af4886/backlog.json} +0 -0
- /package/plan/{P1P2/PRP.md → 001_d3bb02af4886/docs/PRP/P1P2-PRP.md} +0 -0
- /package/plan/{P3P4/PRP.md → 001_d3bb02af4886/docs/PRP/P3P4-PRP.md} +0 -0
- /package/plan/{P4P5/PRP.md → 001_d3bb02af4886/docs/PRP/P4P5-PRP.md} +0 -0
- /package/plan/{architecture → 001_d3bb02af4886/docs/architecture}/external_deps.md +0 -0
- /package/plan/{architecture → 001_d3bb02af4886/docs/architecture}/system_context.md +0 -0
- /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/LRU_CACHE_BEST_PRACTICES.md +0 -0
- /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/LRU_CACHE_CODE_PATTERNS.md +0 -0
- /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/LRU_CACHE_INTEGRATION_GUIDE.md +0 -0
- /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/LRU_CACHE_RESEARCH_INDEX.md +0 -0
- /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/REFLECTION_INDEX.md +0 -0
- /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/REFLECTION_RESEARCH_REPORT.md +0 -0
- /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/RESEARCH_SUMMARY.md +0 -0
- /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/anthropic-sdk.md +0 -0
- /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/async-local-storage.md +0 -0
- /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/reflection-code-patterns.md +0 -0
- /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/reflection-decision-matrix.md +0 -0
- /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/reflection-implementation-guide.md +0 -0
- /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/reflection-integration-guide.md +0 -0
- /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/reflection-patterns.md +0 -0
- /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/reflection-quick-reference.md +0 -0
- /package/plan/{P1P2/research → 001_d3bb02af4886/docs/research/P1P2}/zod-schema.md +0 -0
- /package/plan/{P3P4/research → 001_d3bb02af4886/docs/research/P3P4}/caching-lru.md +0 -0
- /package/plan/{P3P4/research → 001_d3bb02af4886/docs/research/P3P4}/introspection-tools.md +0 -0
- /package/plan/{P3P4/research → 001_d3bb02af4886/docs/research/P3P4}/reflection-patterns.md +0 -0
- /package/plan/{P4P5/research → 001_d3bb02af4886/docs/research/P4P5}/RESEARCH_SUMMARY.md +0 -0
- /package/plan/{research → 001_d3bb02af4886/docs/research/general}/INTROSPECTION_RESEARCH_SUMMARY.md +0 -0
- /package/plan/{research → 001_d3bb02af4886/docs/research/general}/README-INTROSPECTION.md +0 -0
- /package/plan/{research → 001_d3bb02af4886/docs/research/general}/agent-introspection-patterns.md +0 -0
- /package/plan/{research → 001_d3bb02af4886/docs/research/general}/introspection-tool-examples.md +0 -0
- /package/{PRPs/PRDs/001-hierarchical-workflow-engine.md → plan/001_d3bb02af4886/prd_snapshot.md} +0 -0
package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/QUICK_REFERENCE.md
ADDED
|
@@ -0,0 +1,706 @@
|
|
|
1
|
+
# Quick Reference: Test Templates & Helper Functions
|
|
2
|
+
|
|
3
|
+
**Purpose:** Copy-paste templates for error aggregation testing
|
|
4
|
+
**Task:** P1.M2.T2.S2 - Implement error aggregation logic
|
|
5
|
+
**Last Updated:** 2026-01-12
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Helper Functions (Copy to Test File)
|
|
10
|
+
|
|
11
|
+
### Type Guards
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
/**
|
|
15
|
+
* Type guard for WorkflowError
|
|
16
|
+
*/
|
|
17
|
+
function isWorkflowError(error: unknown): error is WorkflowError {
|
|
18
|
+
return (
|
|
19
|
+
typeof error === 'object' &&
|
|
20
|
+
error !== null &&
|
|
21
|
+
'message' in error &&
|
|
22
|
+
'workflowId' in error &&
|
|
23
|
+
'logs' in error
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Type guard for rejected PromiseSettledResult
|
|
29
|
+
*/
|
|
30
|
+
function isRejected(result: PromiseSettledResult<unknown>): result is PromiseRejectedResult {
|
|
31
|
+
return result.status === 'rejected';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Type guard for fulfilled PromiseSettledResult
|
|
36
|
+
*/
|
|
37
|
+
function isFulfilled<T>(result: PromiseSettledResult<T>): result is PromiseFulfilledResult<T> {
|
|
38
|
+
return result.status === 'fulfilled';
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Error Creation
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
/**
|
|
46
|
+
* Create a mock WorkflowError
|
|
47
|
+
*/
|
|
48
|
+
function createMockError(overrides?: Partial<WorkflowError>): WorkflowError {
|
|
49
|
+
return {
|
|
50
|
+
message: 'Mock error',
|
|
51
|
+
original: new Error('Mock'),
|
|
52
|
+
workflowId: 'mock-workflow',
|
|
53
|
+
state: {} as any,
|
|
54
|
+
logs: [],
|
|
55
|
+
stack: 'Error: Mock\n at mock.js:10:15',
|
|
56
|
+
...overrides
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Create a failing child workflow
|
|
62
|
+
*/
|
|
63
|
+
function createFailingWorkflow(parent: Workflow, name: string, errorMessage: string): Workflow {
|
|
64
|
+
return new (class extends Workflow {
|
|
65
|
+
constructor(n: string, p: Workflow) {
|
|
66
|
+
super(n, p);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async run() {
|
|
70
|
+
throw new Error(errorMessage);
|
|
71
|
+
}
|
|
72
|
+
})(name, parent);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Create a child workflow with configurable failure
|
|
77
|
+
*/
|
|
78
|
+
function createChildWorkflow(
|
|
79
|
+
parent: Workflow,
|
|
80
|
+
name: string,
|
|
81
|
+
shouldFail: boolean = false
|
|
82
|
+
): Workflow {
|
|
83
|
+
return new (class extends Workflow {
|
|
84
|
+
constructor(n: string, p: Workflow) {
|
|
85
|
+
super(n, p);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@Step()
|
|
89
|
+
async executeStep() {
|
|
90
|
+
if (shouldFail) {
|
|
91
|
+
throw new Error(`${name} failed`);
|
|
92
|
+
}
|
|
93
|
+
return `${name} succeeded`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async run() {
|
|
97
|
+
return this.executeStep();
|
|
98
|
+
}
|
|
99
|
+
})(name, parent);
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Test Setup
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
/**
|
|
107
|
+
* Setup event observer for testing
|
|
108
|
+
*/
|
|
109
|
+
function setupEventObserver(workflow: Workflow): WorkflowEvent[] {
|
|
110
|
+
const events: WorkflowEvent[] = [];
|
|
111
|
+
workflow.addObserver({
|
|
112
|
+
onLog: () => {},
|
|
113
|
+
onEvent: (e) => events.push(e),
|
|
114
|
+
onStateUpdated: () => {},
|
|
115
|
+
onTreeChanged: () => {},
|
|
116
|
+
});
|
|
117
|
+
return events;
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Test Templates
|
|
124
|
+
|
|
125
|
+
### Template 1: Basic Error Aggregation
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
describe('Error Aggregation - Basic', () => {
|
|
129
|
+
it('should aggregate errors from multiple concurrent workflows', async () => {
|
|
130
|
+
// ARRANGE: Create parent workflow
|
|
131
|
+
class ParentWorkflow extends Workflow {
|
|
132
|
+
@Task({
|
|
133
|
+
concurrent: true,
|
|
134
|
+
errorMergeStrategy: { enabled: true }
|
|
135
|
+
})
|
|
136
|
+
async spawnChildren() {
|
|
137
|
+
return [
|
|
138
|
+
createFailingWorkflow(this, 'Child-1', 'Error 1'),
|
|
139
|
+
createFailingWorkflow(this, 'Child-2', 'Error 2'),
|
|
140
|
+
createFailingWorkflow(this, 'Child-3', 'Error 3'),
|
|
141
|
+
];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async run() {
|
|
145
|
+
try {
|
|
146
|
+
await this.spawnChildren();
|
|
147
|
+
} catch (err) {
|
|
148
|
+
return err;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const parent = new ParentWorkflow('Parent');
|
|
154
|
+
|
|
155
|
+
// ACT: Run the workflow
|
|
156
|
+
const result = await parent.run();
|
|
157
|
+
|
|
158
|
+
// ASSERT: Verify error aggregation
|
|
159
|
+
expect(result.message).toContain('3 concurrent errors');
|
|
160
|
+
expect(result.message).toContain('[1] Error 1');
|
|
161
|
+
expect(result.message).toContain('[2] Error 2');
|
|
162
|
+
expect(result.message).toContain('[3] Error 3');
|
|
163
|
+
expect(result.logs).toHaveLength(3);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Template 2: Custom Error Merger
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
describe('Error Aggregation - Custom Merger', () => {
|
|
172
|
+
it('should use custom error merge function', async () => {
|
|
173
|
+
// ARRANGE: Define custom merger
|
|
174
|
+
const customMerger = (errors: WorkflowError[]) => ({
|
|
175
|
+
message: `CUSTOM: ${errors.length} errors`,
|
|
176
|
+
original: errors,
|
|
177
|
+
workflowId: 'custom-merged',
|
|
178
|
+
state: {} as any,
|
|
179
|
+
logs: errors.flatMap(e => e.logs),
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
class ParentWorkflow extends Workflow {
|
|
183
|
+
@Task({
|
|
184
|
+
concurrent: true,
|
|
185
|
+
errorMergeStrategy: {
|
|
186
|
+
enabled: true,
|
|
187
|
+
combine: customMerger
|
|
188
|
+
}
|
|
189
|
+
})
|
|
190
|
+
async spawnChildren() {
|
|
191
|
+
return [
|
|
192
|
+
createFailingWorkflow(this, 'A', 'Error A'),
|
|
193
|
+
createFailingWorkflow(this, 'B', 'Error B'),
|
|
194
|
+
];
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async run() {
|
|
198
|
+
try {
|
|
199
|
+
await this.spawnChildren();
|
|
200
|
+
} catch (err) {
|
|
201
|
+
return err;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const parent = new ParentWorkflow('Parent');
|
|
207
|
+
|
|
208
|
+
// ACT
|
|
209
|
+
const result = await parent.run();
|
|
210
|
+
|
|
211
|
+
// ASSERT: Custom merger was used
|
|
212
|
+
expect(result.message).toBe('CUSTOM: 2 errors');
|
|
213
|
+
expect(result.workflowId).toBe('custom-merged');
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Template 3: Backward Compatibility
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
describe('Error Aggregation - Backward Compatibility', () => {
|
|
222
|
+
it('should throw first error when disabled', async () => {
|
|
223
|
+
// ARRANGE: Create parent without error merge strategy
|
|
224
|
+
class ParentWorkflow extends Workflow {
|
|
225
|
+
@Task({
|
|
226
|
+
concurrent: true,
|
|
227
|
+
errorMergeStrategy: { enabled: false } // Disabled
|
|
228
|
+
})
|
|
229
|
+
async spawnChildren() {
|
|
230
|
+
return [
|
|
231
|
+
createFailingWorkflow(this, 'First', 'First error'),
|
|
232
|
+
createFailingWorkflow(this, 'Second', 'Second error'),
|
|
233
|
+
createFailingWorkflow(this, 'Third', 'Third error'),
|
|
234
|
+
];
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
async run() {
|
|
238
|
+
try {
|
|
239
|
+
await this.spawnChildren();
|
|
240
|
+
} catch (err) {
|
|
241
|
+
return err;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const parent = new ParentWorkflow('Parent');
|
|
247
|
+
|
|
248
|
+
// ACT
|
|
249
|
+
const result = await parent.run();
|
|
250
|
+
|
|
251
|
+
// ASSERT: Only first error thrown (backward compatible)
|
|
252
|
+
expect(result.message).toContain('First error');
|
|
253
|
+
expect(result.message).not.toContain('Second error');
|
|
254
|
+
expect(result.message).not.toContain('Third error');
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it('should throw first error when errorMergeStrategy not provided', async () => {
|
|
258
|
+
// ARRANGE: No errorMergeStrategy at all
|
|
259
|
+
class ParentWorkflow extends Workflow {
|
|
260
|
+
@Task({ concurrent: true }) // No errorMergeStrategy
|
|
261
|
+
async spawnChildren() {
|
|
262
|
+
return [
|
|
263
|
+
createFailingWorkflow(this, 'First', 'First error'),
|
|
264
|
+
createFailingWorkflow(this, 'Second', 'Second error'),
|
|
265
|
+
];
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
async run() {
|
|
269
|
+
try {
|
|
270
|
+
await this.spawnChildren();
|
|
271
|
+
} catch (err) {
|
|
272
|
+
return err;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const parent = new ParentWorkflow('Parent');
|
|
278
|
+
|
|
279
|
+
// ACT
|
|
280
|
+
const result = await parent.run();
|
|
281
|
+
|
|
282
|
+
// ASSERT: First error thrown (backward compatible default)
|
|
283
|
+
expect(result.message).toContain('First error');
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Template 4: Event Emission
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
describe('Error Aggregation - Event Emission', () => {
|
|
292
|
+
it('should emit error event with merged error', async () => {
|
|
293
|
+
// ARRANGE: Setup event collection
|
|
294
|
+
class ParentWorkflow extends Workflow {
|
|
295
|
+
@Task({
|
|
296
|
+
concurrent: true,
|
|
297
|
+
errorMergeStrategy: { enabled: true }
|
|
298
|
+
})
|
|
299
|
+
async spawnChildren() {
|
|
300
|
+
return [
|
|
301
|
+
createFailingWorkflow(this, 'Bad1', 'Error 1'),
|
|
302
|
+
createFailingWorkflow(this, 'Bad2', 'Error 2'),
|
|
303
|
+
];
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
async run() {
|
|
307
|
+
try {
|
|
308
|
+
await this.spawnChildren();
|
|
309
|
+
} catch (err) {
|
|
310
|
+
// Expected
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const parent = new ParentWorkflow('Parent');
|
|
316
|
+
const events = setupEventObserver(parent);
|
|
317
|
+
|
|
318
|
+
// ACT
|
|
319
|
+
await parent.run();
|
|
320
|
+
|
|
321
|
+
// ASSERT: Error events emitted
|
|
322
|
+
const errorEvents = events.filter(e => e.type === 'error');
|
|
323
|
+
expect(errorEvents.length).toBeGreaterThanOrEqual(2);
|
|
324
|
+
|
|
325
|
+
// ASSERT: Merged error event present
|
|
326
|
+
const mergedErrorEvent = errorEvents.find(e =>
|
|
327
|
+
e.type === 'error' && e.error.message.includes('2 concurrent errors')
|
|
328
|
+
);
|
|
329
|
+
expect(mergedErrorEvent).toBeDefined();
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### Template 5: Edge Cases
|
|
335
|
+
|
|
336
|
+
```typescript
|
|
337
|
+
describe('Error Aggregation - Edge Cases', () => {
|
|
338
|
+
it('should handle empty error array', () => {
|
|
339
|
+
// ARRANGE
|
|
340
|
+
const errors: WorkflowError[] = [];
|
|
341
|
+
|
|
342
|
+
// ACT & ASSERT
|
|
343
|
+
expect(() => mergeWorkflowErrors(errors)).toThrow('Cannot merge empty error array');
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
it('should handle single error', () => {
|
|
347
|
+
// ARRANGE
|
|
348
|
+
const singleError = createMockError({ message: 'Single error' });
|
|
349
|
+
|
|
350
|
+
// ACT
|
|
351
|
+
const result = mergeWorkflowErrors([singleError]);
|
|
352
|
+
|
|
353
|
+
// ASSERT: Returns same error object
|
|
354
|
+
expect(result).toBe(singleError);
|
|
355
|
+
expect(result.message).toBe('Single error');
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
it('should handle all workflows failing', async () => {
|
|
359
|
+
// ARRANGE: All children will fail
|
|
360
|
+
class ParentWorkflow extends Workflow {
|
|
361
|
+
@Task({
|
|
362
|
+
concurrent: true,
|
|
363
|
+
errorMergeStrategy: { enabled: true }
|
|
364
|
+
})
|
|
365
|
+
async spawnChildren() {
|
|
366
|
+
return Array.from({ length: 5 }, (_, i) =>
|
|
367
|
+
createFailingWorkflow(this, `FailChild-${i}`, `Error ${i}`)
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
async run() {
|
|
372
|
+
try {
|
|
373
|
+
await this.spawnChildren();
|
|
374
|
+
} catch (err) {
|
|
375
|
+
return err;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const parent = new ParentWorkflow('Parent');
|
|
381
|
+
|
|
382
|
+
// ACT
|
|
383
|
+
const result = await parent.run();
|
|
384
|
+
|
|
385
|
+
// ASSERT: All 5 errors aggregated
|
|
386
|
+
expect(result.message).toContain('5 concurrent errors');
|
|
387
|
+
for (let i = 0; i < 5; i++) {
|
|
388
|
+
expect(result.message).toContain(`Error ${i}`);
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
### Template 6: Promise.allSettled Patterns
|
|
395
|
+
|
|
396
|
+
```typescript
|
|
397
|
+
describe('Promise.allSettled Patterns', () => {
|
|
398
|
+
it('should collect all errors from allSettled promises', async () => {
|
|
399
|
+
// ARRANGE: Create promises with mixed results
|
|
400
|
+
const promises = [
|
|
401
|
+
Promise.reject(new Error('Error 1')),
|
|
402
|
+
Promise.reject(new Error('Error 2')),
|
|
403
|
+
Promise.resolve('success'),
|
|
404
|
+
Promise.reject(new Error('Error 3')),
|
|
405
|
+
];
|
|
406
|
+
|
|
407
|
+
// ACT: Use Promise.allSettled
|
|
408
|
+
const results = await Promise.allSettled(promises);
|
|
409
|
+
|
|
410
|
+
// ASSERT: Filter and verify errors
|
|
411
|
+
const rejected = results.filter(isRejected);
|
|
412
|
+
|
|
413
|
+
expect(rejected).toHaveLength(3);
|
|
414
|
+
expect(rejected[0].reason.message).toBe('Error 1');
|
|
415
|
+
expect(rejected[1].reason.message).toBe('Error 2');
|
|
416
|
+
expect(rejected[2].reason.message).toBe('Error 3');
|
|
417
|
+
|
|
418
|
+
// ASSERT: Verify fulfilled promises
|
|
419
|
+
const fulfilled = results.filter(isFulfilled);
|
|
420
|
+
|
|
421
|
+
expect(fulfilled).toHaveLength(1);
|
|
422
|
+
expect(fulfilled[0].value).toBe('success');
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
it('should complete all workflows despite failures', async () => {
|
|
426
|
+
// ARRANGE: Track all completions
|
|
427
|
+
const completedWorkflows = new Set<string>();
|
|
428
|
+
|
|
429
|
+
class ParentWorkflow extends Workflow {
|
|
430
|
+
@Task({ concurrent: true })
|
|
431
|
+
async spawnChildren() {
|
|
432
|
+
const children = [
|
|
433
|
+
createChildWorkflow(this, 'Success-1', false),
|
|
434
|
+
createChildWorkflow(this, 'Fail-1', true),
|
|
435
|
+
createChildWorkflow(this, 'Success-2', false),
|
|
436
|
+
createChildWorkflow(this, 'Fail-2', true),
|
|
437
|
+
createChildWorkflow(this, 'Success-3', false),
|
|
438
|
+
];
|
|
439
|
+
|
|
440
|
+
// Track all completions
|
|
441
|
+
children.forEach(child => {
|
|
442
|
+
child.run().then(
|
|
443
|
+
() => completedWorkflows.add(child.id),
|
|
444
|
+
() => completedWorkflows.add(child.id)
|
|
445
|
+
);
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
return children;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
async run() {
|
|
452
|
+
try {
|
|
453
|
+
await this.spawnChildren();
|
|
454
|
+
} catch (err) {
|
|
455
|
+
// Expected
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
const parent = new ParentWorkflow('Parent');
|
|
461
|
+
|
|
462
|
+
// ACT
|
|
463
|
+
await parent.run();
|
|
464
|
+
|
|
465
|
+
// ASSERT: All 5 workflows completed (no orphans)
|
|
466
|
+
expect(completedWorkflows.size).toBe(5);
|
|
467
|
+
expect(parent.children.length).toBe(5);
|
|
468
|
+
});
|
|
469
|
+
});
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
### Template 7: Performance Testing
|
|
473
|
+
|
|
474
|
+
```typescript
|
|
475
|
+
describe('Error Aggregation - Performance', () => {
|
|
476
|
+
it('should handle large error counts efficiently', async () => {
|
|
477
|
+
// ARRANGE: Create 50 concurrent workflows
|
|
478
|
+
const workflowCount = 50;
|
|
479
|
+
|
|
480
|
+
class ParentWorkflow extends Workflow {
|
|
481
|
+
@Task({
|
|
482
|
+
concurrent: true,
|
|
483
|
+
errorMergeStrategy: { enabled: true }
|
|
484
|
+
})
|
|
485
|
+
async spawnManyChildren() {
|
|
486
|
+
return Array.from({ length: workflowCount }, (_, i) =>
|
|
487
|
+
createFailingWorkflow(this, `child-${i}`, `Error ${i}`)
|
|
488
|
+
);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
async run() {
|
|
492
|
+
const startTime = performance.now();
|
|
493
|
+
try {
|
|
494
|
+
await this.spawnManyChildren();
|
|
495
|
+
} catch (err) {
|
|
496
|
+
const duration = performance.now() - startTime;
|
|
497
|
+
return { error: err, duration };
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
const parent = new ParentWorkflow('Parent');
|
|
503
|
+
|
|
504
|
+
// ACT
|
|
505
|
+
const { error, duration } = await parent.run();
|
|
506
|
+
|
|
507
|
+
// ASSERT: All errors aggregated
|
|
508
|
+
expect(error.message).toContain('50 concurrent errors');
|
|
509
|
+
|
|
510
|
+
// ASSERT: Completed in reasonable time (< 5 seconds)
|
|
511
|
+
expect(duration).toBeLessThan(5000);
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
it('should execute workflows concurrently not sequentially', async () => {
|
|
515
|
+
// ARRANGE: Track execution times
|
|
516
|
+
const startTimes = new Map<string, number>();
|
|
517
|
+
|
|
518
|
+
class TimedWorkflow extends Workflow {
|
|
519
|
+
async run() {
|
|
520
|
+
startTimes.set(this.id, Date.now());
|
|
521
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
522
|
+
return this.id;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
class ParentWorkflow extends Workflow {
|
|
527
|
+
@Task({ concurrent: true })
|
|
528
|
+
async spawnChildren() {
|
|
529
|
+
return [
|
|
530
|
+
new TimedWorkflow('child-1', this),
|
|
531
|
+
new TimedWorkflow('child-2', this),
|
|
532
|
+
new TimedWorkflow('child-3', this),
|
|
533
|
+
];
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
async run() {
|
|
537
|
+
const startTime = Date.now();
|
|
538
|
+
try {
|
|
539
|
+
await this.spawnChildren();
|
|
540
|
+
} catch (err) {
|
|
541
|
+
// Expected
|
|
542
|
+
}
|
|
543
|
+
return Date.now() - startTime;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
const parent = new ParentWorkflow('Parent');
|
|
548
|
+
|
|
549
|
+
// ACT
|
|
550
|
+
const totalTime = await parent.run();
|
|
551
|
+
|
|
552
|
+
// ASSERT: Concurrent execution (total time < sequential time)
|
|
553
|
+
expect(totalTime).toBeLessThan(250); // Should be ~100ms, not 300ms
|
|
554
|
+
|
|
555
|
+
// ASSERT: All started around same time (within 50ms)
|
|
556
|
+
const times = Array.from(startTimes.values());
|
|
557
|
+
const maxDiff = Math.max(...times) - Math.min(...times);
|
|
558
|
+
expect(maxDiff).toBeLessThan(50);
|
|
559
|
+
});
|
|
560
|
+
});
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
---
|
|
564
|
+
|
|
565
|
+
## Assertion Patterns
|
|
566
|
+
|
|
567
|
+
### Message Assertions
|
|
568
|
+
|
|
569
|
+
```typescript
|
|
570
|
+
// Contains substring
|
|
571
|
+
expect(result.message).toContain('concurrent errors');
|
|
572
|
+
|
|
573
|
+
// Matches regex
|
|
574
|
+
expect(result.message).toMatch(/\d+ concurrent errors/);
|
|
575
|
+
|
|
576
|
+
// Exact match
|
|
577
|
+
expect(result.message).toBe('3 concurrent errors: [1] Error 1; [2] Error 2; [3] Error 3');
|
|
578
|
+
|
|
579
|
+
// Multiple substrings
|
|
580
|
+
expect(result.message).toContain('Error 1');
|
|
581
|
+
expect(result.message).toContain('Error 2');
|
|
582
|
+
expect(result.message).toContain('Error 3');
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
### Object Matching
|
|
586
|
+
|
|
587
|
+
```typescript
|
|
588
|
+
// Partial match
|
|
589
|
+
expect(result).toMatchObject({
|
|
590
|
+
message: expect.stringContaining('errors'),
|
|
591
|
+
workflowId: expect.any(String),
|
|
592
|
+
logs: expect.any(Array),
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
// Nested array matching
|
|
596
|
+
expect(result.logs).toEqual(
|
|
597
|
+
expect.arrayContaining([
|
|
598
|
+
expect.objectContaining({
|
|
599
|
+
workflowId: expect.any(String),
|
|
600
|
+
level: expect.any(String),
|
|
601
|
+
})
|
|
602
|
+
])
|
|
603
|
+
);
|
|
604
|
+
|
|
605
|
+
// Exact array match
|
|
606
|
+
expect(result.original).toEqual([error1, error2, error3]);
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
### Type Assertions
|
|
610
|
+
|
|
611
|
+
```typescript
|
|
612
|
+
// Type guard assertion
|
|
613
|
+
if (isWorkflowError(result)) {
|
|
614
|
+
expect(result.message).toBeDefined();
|
|
615
|
+
expect(result.workflowId).toBeDefined();
|
|
616
|
+
expect(Array.isArray(result.logs)).toBe(true);
|
|
617
|
+
} else {
|
|
618
|
+
fail('Result is not a WorkflowError');
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// Instance check
|
|
622
|
+
expect(result).toBeInstanceOf(Error);
|
|
623
|
+
expect(result.original).toBeInstanceOf(Error);
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
### Error Assertions
|
|
627
|
+
|
|
628
|
+
```typescript
|
|
629
|
+
// Async error with rejects
|
|
630
|
+
await expect(workflow.run()).rejects.toThrow('Expected error');
|
|
631
|
+
|
|
632
|
+
// Try-catch with assertion
|
|
633
|
+
try {
|
|
634
|
+
await workflow.run();
|
|
635
|
+
fail('Expected error to be thrown');
|
|
636
|
+
} catch (error) {
|
|
637
|
+
expect(error).toMatchObject({
|
|
638
|
+
message: expect.stringContaining('error'),
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// Return error from try-catch
|
|
643
|
+
const error = await workflow.run().catch(err => err);
|
|
644
|
+
expect(error).toBeDefined();
|
|
645
|
+
expect(error.message).toContain('error');
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
---
|
|
649
|
+
|
|
650
|
+
## Test Structure Template
|
|
651
|
+
|
|
652
|
+
```typescript
|
|
653
|
+
describe('Feature Name', () => {
|
|
654
|
+
describe('Specific Scenario', () => {
|
|
655
|
+
it('should do something specific', async () => {
|
|
656
|
+
// ARRANGE: Setup test data and conditions
|
|
657
|
+
const workflow = new TestWorkflow('Test');
|
|
658
|
+
|
|
659
|
+
// ACT: Execute the code being tested
|
|
660
|
+
const result = await workflow.run();
|
|
661
|
+
|
|
662
|
+
// ASSERT: Verify expected outcomes
|
|
663
|
+
expect(result).toBeDefined();
|
|
664
|
+
expect(result.message).toContain('expected');
|
|
665
|
+
});
|
|
666
|
+
});
|
|
667
|
+
});
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
---
|
|
671
|
+
|
|
672
|
+
## Import Statements
|
|
673
|
+
|
|
674
|
+
```typescript
|
|
675
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
676
|
+
import { Workflow, Task, Step } from '../../index.js';
|
|
677
|
+
import type { WorkflowError, WorkflowEvent } from '../../types/index.js';
|
|
678
|
+
import { mergeWorkflowErrors } from '../../utils/error-merger.js';
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
---
|
|
682
|
+
|
|
683
|
+
## Common Test Scenarios Checklist
|
|
684
|
+
|
|
685
|
+
- [ ] Empty error array
|
|
686
|
+
- [ ] Single error
|
|
687
|
+
- [ ] Two errors
|
|
688
|
+
- [ ] Multiple errors (3, 5, 10+)
|
|
689
|
+
- [ ] All workflows succeed
|
|
690
|
+
- [ ] All workflows fail
|
|
691
|
+
- [ ] Mixed success/failure
|
|
692
|
+
- [ ] Custom merge function
|
|
693
|
+
- [ ] Disabled merge strategy (backward compat)
|
|
694
|
+
- [ ] No merge strategy provided (backward compat)
|
|
695
|
+
- [ ] Event emission
|
|
696
|
+
- [ ] Event propagation to parent
|
|
697
|
+
- [ ] Large error counts (50+)
|
|
698
|
+
- [ ] Performance/concurrency verification
|
|
699
|
+
- [ ] Orphan prevention
|
|
700
|
+
- [ ] Memory leaks (if applicable)
|
|
701
|
+
|
|
702
|
+
---
|
|
703
|
+
|
|
704
|
+
**Quick Reference Version:** 1.0
|
|
705
|
+
**Last Updated:** 2026-01-12
|
|
706
|
+
**For:** P1.M2.T2.S2 Implementation
|