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
|
@@ -0,0 +1,1252 @@
|
|
|
1
|
+
# Product Requirement Prompt (PRP): Add Tests for ErrorMergeStrategy Functionality
|
|
2
|
+
|
|
3
|
+
**Work Item:** P1.M2.T2.S4 - Add tests for ErrorMergeStrategy functionality
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Goal
|
|
8
|
+
|
|
9
|
+
**Feature Goal:** Create a comprehensive test suite for the ErrorMergeStrategy functionality implemented in P1.M2.T2.S2 and P1.M2.T2.S3, validating that error aggregation works correctly for concurrent task execution scenarios.
|
|
10
|
+
|
|
11
|
+
**Deliverable:** A new test file `src/__tests__/adversarial/error-merge-strategy.test.ts` containing comprehensive tests for:
|
|
12
|
+
- `errorMergeStrategy.enabled=false` (default behavior - first error wins)
|
|
13
|
+
- `errorMergeStrategy.enabled=true` with default merge (uses `mergeWorkflowErrors`)
|
|
14
|
+
- `errorMergeStrategy.enabled=true` with custom combine function
|
|
15
|
+
- `maxMergeDepth` validation (if implemented)
|
|
16
|
+
- Merged error contains aggregated information from all failures
|
|
17
|
+
|
|
18
|
+
**Success Definition:**
|
|
19
|
+
1. New test file `src/__tests__/adversarial/error-merge-strategy.test.ts` created
|
|
20
|
+
2. All test scenarios from the contract definition are covered
|
|
21
|
+
3. Tests use vitest framework with `describe`, `it`, `expect` patterns matching existing codebase
|
|
22
|
+
4. Tests verify error event emission with merged errors
|
|
23
|
+
5. All new tests pass
|
|
24
|
+
6. All existing tests continue to pass (no regressions)
|
|
25
|
+
7. Tests follow the project's test organization structure
|
|
26
|
+
|
|
27
|
+
## User Persona (if applicable)
|
|
28
|
+
|
|
29
|
+
**Target User:** Library Developer / QA Engineer
|
|
30
|
+
|
|
31
|
+
**Use Case:** A developer needs confidence that the ErrorMergeStrategy feature works correctly across all scenarios (enabled/disabled, default/custom merger, edge cases).
|
|
32
|
+
|
|
33
|
+
**User Journey:**
|
|
34
|
+
1. Developer runs the test suite to verify ErrorMergeStrategy functionality
|
|
35
|
+
2. Tests cover all code paths in the error aggregation logic
|
|
36
|
+
3. Test failures clearly indicate what functionality is broken
|
|
37
|
+
4. Tests serve as documentation for expected behavior
|
|
38
|
+
|
|
39
|
+
**Pain Points Addressed:**
|
|
40
|
+
- Currently no tests verify `errorMergeStrategy` configuration works
|
|
41
|
+
- No coverage for custom `combine()` function scenarios
|
|
42
|
+
- No validation that merged errors contain aggregated information
|
|
43
|
+
- Unclear what happens when `enabled=false` vs `enabled=true`
|
|
44
|
+
|
|
45
|
+
## Why
|
|
46
|
+
|
|
47
|
+
- **Quality Assurance:** P1.M2.T2.S2 implemented error aggregation logic, P1.M2.T2.S3 extracted the merger utility - both need comprehensive test coverage
|
|
48
|
+
- **Regression Prevention:** Future changes to concurrent execution could break error aggregation - tests prevent this
|
|
49
|
+
- **Documentation:** Tests serve as executable documentation of expected ErrorMergeStrategy behavior
|
|
50
|
+
- **PRD Compliance:** PRD Section 10 specifies "Optional Multi-Error Merging" with specific behaviors that need validation
|
|
51
|
+
- **Confidence:** Without tests, we cannot verify the implementation works as designed
|
|
52
|
+
|
|
53
|
+
## What
|
|
54
|
+
|
|
55
|
+
Create comprehensive tests for ErrorMergeStrategy functionality covering all scenarios specified in the contract definition.
|
|
56
|
+
|
|
57
|
+
### Test Scenarios Required
|
|
58
|
+
|
|
59
|
+
Based on contract definition and PRD requirements:
|
|
60
|
+
|
|
61
|
+
1. **Default Behavior (disabled)**: `errorMergeStrategy.enabled=false` → first error thrown
|
|
62
|
+
2. **Enabled with Default Merge**: `errorMergeStrategy.enabled=true` without `combine()` → uses `mergeWorkflowErrors`
|
|
63
|
+
3. **Enabled with Custom Combine**: `errorMergeStrategy.enabled=true` with `combine()` function → custom merger called
|
|
64
|
+
4. **maxMergeDepth Validation**: If implemented, verify depth limiting works
|
|
65
|
+
5. **Aggregated Information**: Verify merged error contains all error details
|
|
66
|
+
|
|
67
|
+
### Success Criteria
|
|
68
|
+
|
|
69
|
+
- [ ] Test file created at `src/__tests__/adversarial/error-merge-strategy.test.ts`
|
|
70
|
+
- [ ] Tests for `enabled=false` default behavior (first error wins)
|
|
71
|
+
- [ ] Tests for `enabled=true` with default merge
|
|
72
|
+
- [ ] Tests for `enabled=true` with custom combine function
|
|
73
|
+
- [ ] Tests for maxMergeDepth if implemented in P1.M2.T2.S2
|
|
74
|
+
- [ ] Tests verify merged error contains aggregated information
|
|
75
|
+
- [ ] All tests pass: `npm test -- error-merge-strategy.test.ts`
|
|
76
|
+
- [ ] All existing tests pass: `npm test` (no regressions)
|
|
77
|
+
- [ ] Test coverage for error merge strategy code paths > 90%
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## All Needed Context
|
|
82
|
+
|
|
83
|
+
### Context Completeness Check
|
|
84
|
+
|
|
85
|
+
**"No Prior Knowledge" Test**: If someone knew nothing about this codebase, would they have everything needed to implement this successfully?
|
|
86
|
+
|
|
87
|
+
**Answer**: YES - This PRP provides:
|
|
88
|
+
- Exact file locations for implementation files to test
|
|
89
|
+
- Complete existing test patterns to follow
|
|
90
|
+
- Helper function patterns from existing tests
|
|
91
|
+
- Test structure and organization patterns
|
|
92
|
+
- All type definitions needed
|
|
93
|
+
- Validation commands specific to this project
|
|
94
|
+
|
|
95
|
+
### Documentation & References
|
|
96
|
+
|
|
97
|
+
```yaml
|
|
98
|
+
# MUST READ - Implementation Files to Test
|
|
99
|
+
- file: src/decorators/task.ts (lines 119-143)
|
|
100
|
+
why: Contains the error merge strategy implementation to test
|
|
101
|
+
critical: This is the main code under test - shows how errorMergeStrategy is checked and used
|
|
102
|
+
gotcha: Error merge only happens when opts.concurrent=true AND opts.errorMergeStrategy?.enabled=true
|
|
103
|
+
|
|
104
|
+
- file: src/utils/workflow-error-utils.ts
|
|
105
|
+
why: Contains mergeWorkflowErrors function used as default merger
|
|
106
|
+
pattern: Follow existing test patterns from workflow-error-utils.test.ts
|
|
107
|
+
critical: Default merger behavior must match this function's implementation
|
|
108
|
+
|
|
109
|
+
- file: src/types/error-strategy.ts
|
|
110
|
+
why: ErrorMergeStrategy interface definition
|
|
111
|
+
current_content: |
|
|
112
|
+
export interface ErrorMergeStrategy {
|
|
113
|
+
enabled: boolean;
|
|
114
|
+
maxMergeDepth?: number;
|
|
115
|
+
combine?(errors: WorkflowError[]): WorkflowError;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
- file: src/types/decorators.ts
|
|
119
|
+
why: TaskOptions interface with errorMergeStrategy field
|
|
120
|
+
current_content: |
|
|
121
|
+
export interface TaskOptions {
|
|
122
|
+
name?: string;
|
|
123
|
+
concurrent?: boolean;
|
|
124
|
+
errorMergeStrategy?: ErrorMergeStrategy;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
- file: src/types/error.ts
|
|
128
|
+
why: WorkflowError interface structure - what errors look like
|
|
129
|
+
critical: Tests must verify WorkflowError structure is correct
|
|
130
|
+
|
|
131
|
+
# MUST READ - Existing Test Patterns to Follow
|
|
132
|
+
- file: src/__tests__/adversarial/concurrent-task-failures.test.ts
|
|
133
|
+
why: Shows test patterns for concurrent task error scenarios
|
|
134
|
+
pattern: createChildWorkflow helper, setupEventObserver helper, @Task decorator usage
|
|
135
|
+
critical: Follow this exact pattern for test structure and helper functions
|
|
136
|
+
|
|
137
|
+
- file: src/__tests__/unit/utils/workflow-error-utils.test.ts
|
|
138
|
+
why: Shows test patterns for mergeWorkflowErrors function
|
|
139
|
+
pattern: createMockWorkflowError helper, metadata validation assertions
|
|
140
|
+
critical: Use similar assertion patterns for validating merged errors
|
|
141
|
+
|
|
142
|
+
- file: src/__tests__/unit/decorators.test.ts (lines 1-100)
|
|
143
|
+
why: Shows basic @Task decorator test patterns
|
|
144
|
+
pattern: Workflow class extension, decorator usage, async test patterns
|
|
145
|
+
|
|
146
|
+
# EXTERNAL RESEARCH - Vitest Testing Patterns
|
|
147
|
+
- docfile: plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T2S4/research/vitest_testing_patterns.md
|
|
148
|
+
why: Comprehensive vitest testing patterns for error handling
|
|
149
|
+
section: "Testing Error Aggregation Behavior", "Async Error Testing Best Practices"
|
|
150
|
+
critical: |
|
|
151
|
+
- await expect().rejects.toThrow() for async error testing
|
|
152
|
+
- try-catch for detailed error inspection
|
|
153
|
+
- Event observer setup pattern
|
|
154
|
+
- Promise.allSettled testing patterns
|
|
155
|
+
|
|
156
|
+
# ARCHITECTURE DOCUMENTATION
|
|
157
|
+
- file: plan/001_d3bb02af4886/bugfix/architecture/codebase_structure.md (lines 269-287)
|
|
158
|
+
why: Testing strategy section - shows how tests are organized in this codebase
|
|
159
|
+
section: "## 9. Testing Strategy"
|
|
160
|
+
critical: Tests organized into unit/, integration/, and adversarial/ directories
|
|
161
|
+
|
|
162
|
+
- file: plan/001_d3bb02af4886/bugfix/architecture/error_handling_patterns.md (lines 147-217)
|
|
163
|
+
why: Shows recommended implementation pattern for error aggregation
|
|
164
|
+
section: "## 5. Recommended Pattern for Promise.allSettled with Error Aggregation"
|
|
165
|
+
critical: Understanding the implementation helps write accurate tests
|
|
166
|
+
|
|
167
|
+
# PREVIOUS WORK - PRPs for Related Tasks
|
|
168
|
+
- docfile: plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T2S3/PRP.md
|
|
169
|
+
why: PRP for default error merger implementation
|
|
170
|
+
section: "Implementation Blueprint" shows mergeWorkflowErrors specification
|
|
171
|
+
critical: Tests must verify this exact merge behavior
|
|
172
|
+
|
|
173
|
+
- docfile: plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/PRP.md
|
|
174
|
+
why: PRP for error aggregation logic in @Task decorator
|
|
175
|
+
section: "Implementation Blueprint" shows error merge strategy check logic
|
|
176
|
+
critical: Tests must verify opts.errorMergeStrategy?.enabled check works
|
|
177
|
+
|
|
178
|
+
# PRD REQUIREMENTS
|
|
179
|
+
- docfile: plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/prd_snapshot.md (lines 72-90)
|
|
180
|
+
why: Issue 3 - Missing Error Merge Strategy Implementation
|
|
181
|
+
section: "### Issue 3: Missing Error Merge Strategy Implementation"
|
|
182
|
+
critical: PRD specifies disabled by default, maxMergeDepth for recursion, combine() for custom logic
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Current Codebase Tree (relevant portions)
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
/home/dustin/projects/groundswell/
|
|
189
|
+
├── src/
|
|
190
|
+
│ ├── decorators/
|
|
191
|
+
│ │ └── task.ts # Lines 119-143 - error merge strategy implementation
|
|
192
|
+
│ ├── utils/
|
|
193
|
+
│ │ └── workflow-error-utils.ts # mergeWorkflowErrors function
|
|
194
|
+
│ ├── types/
|
|
195
|
+
│ │ ├── error-strategy.ts # ErrorMergeStrategy interface
|
|
196
|
+
│ │ ├── error.ts # WorkflowError interface
|
|
197
|
+
│ │ └── decorators.ts # TaskOptions interface
|
|
198
|
+
│ └── __tests__/
|
|
199
|
+
│ ├── adversarial/
|
|
200
|
+
│ │ ├── concurrent-task-failures.test.ts # Pattern to follow
|
|
201
|
+
│ │ ├── edge-case.test.ts # Additional patterns
|
|
202
|
+
│ │ └── error-merge-strategy.test.ts # NEW - TO BE CREATED
|
|
203
|
+
│ └── unit/
|
|
204
|
+
│ ├── decorators.test.ts # @Task decorator tests
|
|
205
|
+
│ └── utils/
|
|
206
|
+
│ └── workflow-error-utils.test.ts # mergeWorkflowErrors tests
|
|
207
|
+
├── vitest.config.ts # Test configuration
|
|
208
|
+
└── package.json # Test scripts
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Desired Codebase Tree (files to be added)
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
# NEW FILE:
|
|
215
|
+
src/__tests__/adversarial/error-merge-strategy.test.ts # Comprehensive ErrorMergeStrategy tests
|
|
216
|
+
|
|
217
|
+
# NO FILES MODIFIED - This is a test-only task
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Known Gotchas of Our Codebase & Library Quirks
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
// CRITICAL GOTCHA #1: Error merge strategy only works with concurrent=true
|
|
224
|
+
// The opts.errorMergeStrategy check is INSIDE the opts.concurrent block
|
|
225
|
+
// Location: src/decorators/task.ts lines 106-143
|
|
226
|
+
// IMPLICATION: Tests MUST use @Task({ concurrent: true, errorMergeStrategy: {...} })
|
|
227
|
+
// BAD: @Task({ errorMergeStrategy: { enabled: true } }) // concurrent defaults to false
|
|
228
|
+
// GOOD: @Task({ concurrent: true, errorMergeStrategy: { enabled: true } })
|
|
229
|
+
|
|
230
|
+
// CRITICAL GOTCHA #2: Individual workflow error events already emitted by @Step
|
|
231
|
+
// Each failing workflow emits its own error event BEFORE @Task aggregates them
|
|
232
|
+
// When errorMergeStrategy.enabled=true, @Task emits ONE additional error event with merged error
|
|
233
|
+
// When errorMergeStrategy not enabled, @Task does NOT emit additional error event
|
|
234
|
+
// IMPLICATION: Tests should count total error events = individual errors + (1 if enabled else 0)
|
|
235
|
+
|
|
236
|
+
// CRITICAL GOTCHA #3: WorkflowError.original contains metadata object
|
|
237
|
+
// For merged errors, original field has: { name, message, errors, totalChildren, failedChildren, failedWorkflowIds }
|
|
238
|
+
// Tests should verify this metadata structure
|
|
239
|
+
// PATTERN: (result.original as { failedWorkflowIds: string[] }).failedWorkflowIds
|
|
240
|
+
|
|
241
|
+
// CRITICAL GOTCHA #4: mergeWorkflowErrors signature
|
|
242
|
+
// mergeWorkflowErrors(errors: WorkflowError[], taskName: string, parentWorkflowId: string, totalChildren: number)
|
|
243
|
+
// The decorator calls this with: mergeWorkflowErrors(errors, taskName, wf.id, runnable.length)
|
|
244
|
+
// Tests should verify the message format includes all these parameters
|
|
245
|
+
|
|
246
|
+
// CRITICAL GOTCHA #5: First error wins when disabled
|
|
247
|
+
// When errorMergeStrategy is undefined or enabled=false, decorator throws rejected[0].reason
|
|
248
|
+
// This is the FIRST rejected promise, which may not be deterministic in concurrent execution
|
|
249
|
+
// Tests should use try-catch and verify error structure, not exact error message
|
|
250
|
+
|
|
251
|
+
// CRITICAL GOTCHA #6: Custom combine() can return any WorkflowError
|
|
252
|
+
// User controls the merge behavior when providing combine() function
|
|
253
|
+
// Tests should verify combine() was called, not specific merge behavior
|
|
254
|
+
// Use vi.fn() spy to verify combine() was called with correct arguments
|
|
255
|
+
|
|
256
|
+
// CRITICAL GOTCHA #7: Workflow completion vs error propagation
|
|
257
|
+
// Promise.allSettled ensures ALL workflows complete even when some fail
|
|
258
|
+
// The error is thrown AFTER all workflows complete (fulfilled or rejected)
|
|
259
|
+
// Tests should verify parent.children.length equals total children spawned
|
|
260
|
+
|
|
261
|
+
// CRITICAL GOTCHA #8: Test file location matters
|
|
262
|
+
// This is an adversarial test (edge cases, complex scenarios)
|
|
263
|
+
// Place in src/__tests__/adversarial/ not src/__tests__/unit/
|
|
264
|
+
|
|
265
|
+
// CRITICAL GOTCHA #9: Import paths use .js extensions
|
|
266
|
+
// TypeScript ES modules require .js extensions in import statements
|
|
267
|
+
// import { Workflow } from '../../index.js'; // Note .js extension
|
|
268
|
+
|
|
269
|
+
// CRITICAL GOTCHA #10: maxMergeDepth is NOT currently implemented
|
|
270
|
+
// The interface has maxMergeDepth?: number but it's not used in P1.M2.T2.S2 implementation
|
|
271
|
+
// Tests should note this as "future work" or "not yet implemented"
|
|
272
|
+
// Don't write failing tests for unimplemented features
|
|
273
|
+
|
|
274
|
+
// CRITICAL GOTCHA #11: Event observer must be added BEFORE workflow.run()
|
|
275
|
+
// If observer is added after run(), events emitted during run() won't be captured
|
|
276
|
+
// PATTERN: setup observer, then await workflow.run()
|
|
277
|
+
|
|
278
|
+
// CRITICAL GOTCHA #12: Helper function pattern for creating failing workflows
|
|
279
|
+
// Use the createChildWorkflow pattern from concurrent-task-failures.test.ts
|
|
280
|
+
// It creates anonymous class extending Workflow with @Step decorated method
|
|
281
|
+
// This ensures WorkflowError wrapping happens correctly
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## Implementation Blueprint
|
|
287
|
+
|
|
288
|
+
### Data Models and Structure
|
|
289
|
+
|
|
290
|
+
**No new data models** - using existing types:
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
// From src/types/error-strategy.ts
|
|
294
|
+
export interface ErrorMergeStrategy {
|
|
295
|
+
enabled: boolean;
|
|
296
|
+
maxMergeDepth?: number;
|
|
297
|
+
combine?(errors: WorkflowError[]): WorkflowError;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// From src/types/error.ts
|
|
301
|
+
export interface WorkflowError {
|
|
302
|
+
message: string;
|
|
303
|
+
original: unknown;
|
|
304
|
+
workflowId: string;
|
|
305
|
+
stack?: string;
|
|
306
|
+
state: SerializedWorkflowState;
|
|
307
|
+
logs: LogEntry[];
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Merged error metadata structure (in original field)
|
|
311
|
+
interface MergedErrorMetadata {
|
|
312
|
+
name: string; // 'WorkflowAggregateError'
|
|
313
|
+
message: string; // Aggregated message
|
|
314
|
+
errors: WorkflowError[]; // All original errors
|
|
315
|
+
totalChildren: number; // Total spawned
|
|
316
|
+
failedChildren: number; // How many failed
|
|
317
|
+
failedWorkflowIds: string[]; // Unique workflow IDs
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### Implementation Tasks (ordered by dependencies)
|
|
322
|
+
|
|
323
|
+
```yaml
|
|
324
|
+
Task 1: CREATE src/__tests__/adversarial/error-merge-strategy.test.ts
|
|
325
|
+
- IMPLEMENT: Test file scaffolding with imports and describe blocks
|
|
326
|
+
- IMPORT from: vitest (describe, it, expect), Workflow/Task/Step from '../../index.js', WorkflowEvent from '../../types/index.js'
|
|
327
|
+
- STRUCTURE: Follow pattern from concurrent-task-failures.test.ts
|
|
328
|
+
- NAMING: describe('@Task decorator ErrorMergeStrategy', () => { ... })
|
|
329
|
+
- PLACEMENT: src/__tests__/adversarial/ directory
|
|
330
|
+
|
|
331
|
+
Task 2: IMPLEMENT helper functions (top of test file)
|
|
332
|
+
- FUNCTION: createChildWorkflow(parent, name, shouldFail) - creates failing/succeeding workflow
|
|
333
|
+
- PATTERN: Copy from concurrent-task-failures.test.ts lines 30-52
|
|
334
|
+
- FUNCTION: setupEventObserver(workflow) - returns events array
|
|
335
|
+
- PATTERN: Copy from concurrent-task-failures.test.ts lines 58-67
|
|
336
|
+
- FUNCTION: createMockWorkflowError(overrides) - for custom combine() tests
|
|
337
|
+
- PATTERN: Copy from workflow-error-utils.test.ts lines 7-25
|
|
338
|
+
|
|
339
|
+
Task 3: IMPLEMENT tests for enabled=false (default behavior)
|
|
340
|
+
- DESCRIBE: 'Default behavior (errorMergeStrategy disabled)'
|
|
341
|
+
- TEST: 'should throw first error when errorMergeStrategy not provided'
|
|
342
|
+
- TEST: 'should throw first error when errorMergeStrategy.enabled=false'
|
|
343
|
+
- VERIFY: Only first error thrown, not aggregated
|
|
344
|
+
- VERIFY: No additional error event emitted (only individual workflow errors)
|
|
345
|
+
- PATTERN: Use try-catch, verify error structure, count error events
|
|
346
|
+
|
|
347
|
+
Task 4: IMPLEMENT tests for enabled=true with default merge
|
|
348
|
+
- DESCRIBE: 'Enabled with default error merge'
|
|
349
|
+
- TEST: 'should merge all errors when errorMergeStrategy.enabled=true'
|
|
350
|
+
- TEST: 'should create aggregated error message with counts and task name'
|
|
351
|
+
- TEST: 'should aggregate all logs from all failed workflows'
|
|
352
|
+
- TEST: 'should include metadata in original field'
|
|
353
|
+
- VERIFY: Message format "${X} of ${Y} concurrent child workflows failed in task '${taskName}'"
|
|
354
|
+
- VERIFY: failedWorkflowIds array contains unique workflow IDs
|
|
355
|
+
- VERIFY: logs array is flattened from all errors
|
|
356
|
+
- VERIFY: Error event emitted with merged error
|
|
357
|
+
|
|
358
|
+
Task 5: IMPLEMENT tests for enabled=true with custom combine
|
|
359
|
+
- DESCRIBE: 'Enabled with custom combine function'
|
|
360
|
+
- TEST: 'should call custom combine function when provided'
|
|
361
|
+
- TEST: 'should use custom merge result from combine function'
|
|
362
|
+
- TEST: 'should pass all errors to custom combine function'
|
|
363
|
+
- TECHNIQUE: Use vi.fn() to spy on combine function
|
|
364
|
+
- VERIFY: combine() called with array of WorkflowError
|
|
365
|
+
- VERIFY: combine() return value is thrown as error
|
|
366
|
+
|
|
367
|
+
Task 6: IMPLEMENT edge case tests
|
|
368
|
+
- DESCRIBE: 'Edge cases and error scenarios'
|
|
369
|
+
- TEST: 'should handle single failure with merge enabled'
|
|
370
|
+
- TEST: 'should handle all workflows failing with merge enabled'
|
|
371
|
+
- TEST: 'should handle mixed success/failure with merge enabled'
|
|
372
|
+
- TEST: 'should complete all workflows even when errors occur'
|
|
373
|
+
- VERIFY: parent.children.length equals total children spawned
|
|
374
|
+
|
|
375
|
+
Task 7: IMPLEMENT maxMergeDepth tests (if applicable)
|
|
376
|
+
- CHECK: Does src/decorators/task.ts use maxMergeDepth?
|
|
377
|
+
- IF NOT IMPLEMENTED: Add test with todo.skip() or note as future work
|
|
378
|
+
- IF IMPLEMENTED: Test depth limiting behavior
|
|
379
|
+
- CURRENT STATUS: maxMergeDepth is in interface but NOT used in implementation
|
|
380
|
+
- RECOMMENDATION: Add todo('maxMergeDepth not yet implemented')
|
|
381
|
+
|
|
382
|
+
Task 8: VERIFY all tests pass
|
|
383
|
+
- RUN: npm test -- error-merge-strategy.test.ts
|
|
384
|
+
- VERIFY: All new tests pass
|
|
385
|
+
- RUN: npm test (full suite)
|
|
386
|
+
- VERIFY: No regressions in existing tests
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Implementation Patterns & Key Details
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
// ============================================================================
|
|
393
|
+
// PATTERN 1: Test File Structure
|
|
394
|
+
// Location: Top of src/__tests__/adversarial/error-merge-strategy.test.ts
|
|
395
|
+
// ============================================================================
|
|
396
|
+
|
|
397
|
+
import { describe, it, expect } from 'vitest';
|
|
398
|
+
import { Workflow, Task, Step } from '../../index.js';
|
|
399
|
+
import type { WorkflowEvent, WorkflowError } from '../../types/index.js';
|
|
400
|
+
|
|
401
|
+
describe('@Task decorator ErrorMergeStrategy', () => {
|
|
402
|
+
// Helper functions defined here
|
|
403
|
+
// Test suites defined here
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
// ============================================================================
|
|
407
|
+
// PATTERN 2: Helper Function - Create Child Workflow
|
|
408
|
+
// Follow: src/__tests__/adversarial/concurrent-task-failures.test.ts lines 30-52
|
|
409
|
+
// ============================================================================
|
|
410
|
+
|
|
411
|
+
function createChildWorkflow(
|
|
412
|
+
parent: Workflow,
|
|
413
|
+
name: string,
|
|
414
|
+
shouldFail: boolean = false
|
|
415
|
+
): Workflow {
|
|
416
|
+
return new (class extends Workflow {
|
|
417
|
+
constructor(n: string, p: Workflow) {
|
|
418
|
+
super(n, p);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
@Step()
|
|
422
|
+
async executeStep() {
|
|
423
|
+
if (shouldFail) {
|
|
424
|
+
throw new Error(`${name} failed`);
|
|
425
|
+
}
|
|
426
|
+
return `${name} succeeded`;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
async run() {
|
|
430
|
+
return this.executeStep();
|
|
431
|
+
}
|
|
432
|
+
})(name, parent);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// KEY INSIGHTS:
|
|
436
|
+
// - Creates anonymous class extending Workflow
|
|
437
|
+
// - Uses @Step decorator to ensure error wrapping
|
|
438
|
+
// - shouldFail parameter controls success/failure
|
|
439
|
+
// - Error message includes workflow name for identification
|
|
440
|
+
|
|
441
|
+
// ============================================================================
|
|
442
|
+
// PATTERN 3: Helper Function - Setup Event Observer
|
|
443
|
+
// Follow: src/__tests__/adversarial/concurrent-task-failures.test.ts lines 58-67
|
|
444
|
+
// ============================================================================
|
|
445
|
+
|
|
446
|
+
function setupEventObserver(workflow: Workflow): WorkflowEvent[] {
|
|
447
|
+
const events: WorkflowEvent[] = [];
|
|
448
|
+
workflow.addObserver({
|
|
449
|
+
onLog: () => {},
|
|
450
|
+
onEvent: (e) => events.push(e),
|
|
451
|
+
onStateUpdated: () => {},
|
|
452
|
+
onTreeChanged: () => {},
|
|
453
|
+
});
|
|
454
|
+
return events;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// KEY INSIGHTS:
|
|
458
|
+
// - MUST be called before workflow.run()
|
|
459
|
+
// - Returns array for assertion
|
|
460
|
+
// - Captures all workflow events including errors
|
|
461
|
+
|
|
462
|
+
// ============================================================================
|
|
463
|
+
// PATTERN 4: Test for Default Behavior (disabled)
|
|
464
|
+
// ============================================================================
|
|
465
|
+
|
|
466
|
+
describe('Default behavior (errorMergeStrategy disabled)', () => {
|
|
467
|
+
it('should throw first error when errorMergeStrategy not provided', async () => {
|
|
468
|
+
// ARRANGE: Create parent with concurrent tasks, no error merge strategy
|
|
469
|
+
class ParentWorkflow extends Workflow {
|
|
470
|
+
@Task({ concurrent: true }) // CRITICAL: concurrent=true required
|
|
471
|
+
async spawnChildren() {
|
|
472
|
+
return [
|
|
473
|
+
createChildWorkflow(this, 'Child-0', false),
|
|
474
|
+
createChildWorkflow(this, 'Child-1', true), // Will fail
|
|
475
|
+
createChildWorkflow(this, 'Child-2', false),
|
|
476
|
+
];
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
async run() {
|
|
480
|
+
try {
|
|
481
|
+
await this.spawnChildren();
|
|
482
|
+
} catch (err) {
|
|
483
|
+
// Expected - capture error for validation
|
|
484
|
+
return err;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
const parent = new ParentWorkflow('Parent');
|
|
490
|
+
const events = setupEventObserver(parent);
|
|
491
|
+
|
|
492
|
+
// ACT: Run parent workflow
|
|
493
|
+
const thrownError = await parent.run();
|
|
494
|
+
|
|
495
|
+
// ASSERT: All children completed (Promise.allSettled behavior)
|
|
496
|
+
expect(parent.children.length).toBe(3);
|
|
497
|
+
|
|
498
|
+
// ASSERT: Error was thrown (first error wins)
|
|
499
|
+
expect(thrownError).toBeDefined();
|
|
500
|
+
expect((thrownError as WorkflowError).message).toContain('Child-1 failed');
|
|
501
|
+
|
|
502
|
+
// ASSERT: No additional error event from @Task (only individual workflow errors)
|
|
503
|
+
const errorEvents = events.filter((e) => e.type === 'error');
|
|
504
|
+
expect(errorEvents.length).toBe(1); // Only Child-1's error event
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
it('should throw first error when errorMergeStrategy.enabled=false', async () => {
|
|
508
|
+
// Similar to above but with explicit enabled=false
|
|
509
|
+
class ParentWorkflow extends Workflow {
|
|
510
|
+
@Task({
|
|
511
|
+
concurrent: true,
|
|
512
|
+
errorMergeStrategy: { enabled: false } // Explicitly disabled
|
|
513
|
+
})
|
|
514
|
+
async spawnChildren() {
|
|
515
|
+
return [
|
|
516
|
+
createChildWorkflow(this, 'Alpha', false),
|
|
517
|
+
createChildWorkflow(this, 'Beta', true),
|
|
518
|
+
createChildWorkflow(this, 'Gamma', true),
|
|
519
|
+
];
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
async run() {
|
|
523
|
+
try {
|
|
524
|
+
await this.spawnChildren();
|
|
525
|
+
} catch (err) {
|
|
526
|
+
return err;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
const parent = new ParentWorkflow('Parent');
|
|
532
|
+
const events = setupEventObserver(parent);
|
|
533
|
+
const thrownError = await parent.run();
|
|
534
|
+
|
|
535
|
+
// ASSERT: First error thrown (not aggregated)
|
|
536
|
+
expect(thrownError).toBeDefined();
|
|
537
|
+
const errorMsg = (thrownError as WorkflowError).message;
|
|
538
|
+
expect(errorMsg).toMatch(/Alpha failed|Beta failed|Gamma failed/);
|
|
539
|
+
|
|
540
|
+
// ASSERT: Only individual error events (no merge event)
|
|
541
|
+
const errorEvents = events.filter((e) => e.type === 'error');
|
|
542
|
+
expect(errorEvents.length).toBe(2); // Beta and Gamma errors only
|
|
543
|
+
});
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
// ============================================================================
|
|
547
|
+
// PATTERN 5: Test for Enabled with Default Merge
|
|
548
|
+
// ============================================================================
|
|
549
|
+
|
|
550
|
+
describe('Enabled with default error merge', () => {
|
|
551
|
+
it('should merge all errors when errorMergeStrategy.enabled=true', async () => {
|
|
552
|
+
// ARRANGE: Create parent with error merge enabled
|
|
553
|
+
class ParentWorkflow extends Workflow {
|
|
554
|
+
@Task({
|
|
555
|
+
concurrent: true,
|
|
556
|
+
errorMergeStrategy: { enabled: true } // No combine() - use default
|
|
557
|
+
})
|
|
558
|
+
async spawnChildren() {
|
|
559
|
+
return [
|
|
560
|
+
createChildWorkflow(this, 'Alpha', false),
|
|
561
|
+
createChildWorkflow(this, 'Beta', true), // Will fail
|
|
562
|
+
createChildWorkflow(this, 'Gamma', false),
|
|
563
|
+
createChildWorkflow(this, 'Delta', true), // Will fail
|
|
564
|
+
createChildWorkflow(this, 'Epsilon', false),
|
|
565
|
+
];
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
async run() {
|
|
569
|
+
try {
|
|
570
|
+
await this.spawnChildren();
|
|
571
|
+
} catch (err) {
|
|
572
|
+
return err;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
const parent = new ParentWorkflow('Parent');
|
|
578
|
+
const events = setupEventObserver(parent);
|
|
579
|
+
|
|
580
|
+
// ACT
|
|
581
|
+
const thrownError = await parent.run();
|
|
582
|
+
|
|
583
|
+
// ASSERT: All children completed
|
|
584
|
+
expect(parent.children.length).toBe(5);
|
|
585
|
+
|
|
586
|
+
// ASSERT: Merged error thrown
|
|
587
|
+
expect(thrownError).toBeDefined();
|
|
588
|
+
const error = thrownError as WorkflowError;
|
|
589
|
+
|
|
590
|
+
// ASSERT: Message includes count and task name
|
|
591
|
+
expect(error.message).toBe("2 of 5 concurrent child workflows failed in task 'spawnChildren'");
|
|
592
|
+
|
|
593
|
+
// ASSERT: Metadata in original field
|
|
594
|
+
const metadata = error.original as {
|
|
595
|
+
name: string;
|
|
596
|
+
message: string;
|
|
597
|
+
errors: WorkflowError[];
|
|
598
|
+
totalChildren: number;
|
|
599
|
+
failedChildren: number;
|
|
600
|
+
failedWorkflowIds: string[];
|
|
601
|
+
};
|
|
602
|
+
|
|
603
|
+
expect(metadata.name).toBe('WorkflowAggregateError');
|
|
604
|
+
expect(metadata.totalChildren).toBe(5);
|
|
605
|
+
expect(metadata.failedChildren).toBe(2);
|
|
606
|
+
expect(metadata.failedWorkflowIds).toHaveLength(2);
|
|
607
|
+
expect(metadata.errors).toHaveLength(2);
|
|
608
|
+
|
|
609
|
+
// ASSERT: Logs aggregated from all errors
|
|
610
|
+
expect(error.logs).toBeDefined();
|
|
611
|
+
expect(Array.isArray(error.logs)).toBe(true);
|
|
612
|
+
expect(error.logs.length).toBeGreaterThan(0);
|
|
613
|
+
|
|
614
|
+
// ASSERT: Error event emitted with merged error
|
|
615
|
+
const errorEvents = events.filter((e) => e.type === 'error');
|
|
616
|
+
expect(errorEvents.length).toBeGreaterThanOrEqual(3); // 2 individual + 1 merged
|
|
617
|
+
|
|
618
|
+
// Find the merged error event (has different message format)
|
|
619
|
+
const mergedErrorEvent = errorEvents.find((e) => {
|
|
620
|
+
if (e.type === 'error') {
|
|
621
|
+
return e.error.message.includes('2 of 5 concurrent');
|
|
622
|
+
}
|
|
623
|
+
return false;
|
|
624
|
+
});
|
|
625
|
+
expect(mergedErrorEvent).toBeDefined();
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
it('should aggregate logs from all failed workflows', async () => {
|
|
629
|
+
// Create workflows that log before failing
|
|
630
|
+
class LoggingWorkflow extends Workflow {
|
|
631
|
+
constructor(name: string, parent: Workflow, private shouldFail: boolean) {
|
|
632
|
+
super(name, parent);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
@Step()
|
|
636
|
+
async executeStep() {
|
|
637
|
+
this.logger.info(`${this.node.name} starting`);
|
|
638
|
+
if (this.shouldFail) {
|
|
639
|
+
this.logger.error(`${this.node.name} failing`);
|
|
640
|
+
throw new Error(`${this.node.name} failed`);
|
|
641
|
+
}
|
|
642
|
+
this.logger.info(`${this.node.name} completed`);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
async run() {
|
|
646
|
+
return this.executeStep();
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
class ParentWorkflow extends Workflow {
|
|
651
|
+
@Task({
|
|
652
|
+
concurrent: true,
|
|
653
|
+
errorMergeStrategy: { enabled: true }
|
|
654
|
+
})
|
|
655
|
+
async spawnChildren() {
|
|
656
|
+
return [
|
|
657
|
+
new LoggingWorkflow('Workflow-1', this, true),
|
|
658
|
+
new LoggingWorkflow('Workflow-2', this, true),
|
|
659
|
+
new LoggingWorkflow('Workflow-3', this, false),
|
|
660
|
+
];
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
async run() {
|
|
664
|
+
try {
|
|
665
|
+
await this.spawnChildren();
|
|
666
|
+
} catch (err) {
|
|
667
|
+
return err;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
const parent = new ParentWorkflow('Parent');
|
|
673
|
+
const thrownError = await parent.run() as WorkflowError;
|
|
674
|
+
|
|
675
|
+
// ASSERT: Logs from both failed workflows aggregated
|
|
676
|
+
expect(thrownError.logs).toBeDefined();
|
|
677
|
+
const logMessages = thrownError.logs.map(l => l.message);
|
|
678
|
+
|
|
679
|
+
// Should have logs from both failing workflows
|
|
680
|
+
expect(logMessages.some(m => m.includes('Workflow-1'))).toBe(true);
|
|
681
|
+
expect(logMessages.some(m => m.includes('Workflow-2'))).toBe(true);
|
|
682
|
+
});
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
// ============================================================================
|
|
686
|
+
// PATTERN 6: Test for Custom Combine Function
|
|
687
|
+
// ============================================================================
|
|
688
|
+
|
|
689
|
+
describe('Enabled with custom combine function', () => {
|
|
690
|
+
it('should call custom combine function when provided', async () => {
|
|
691
|
+
// ARRANGE: Create spy for combine function
|
|
692
|
+
const combineSpy = vi.fn((errors: WorkflowError[]) => ({
|
|
693
|
+
message: `Custom merge: ${errors.length} errors`,
|
|
694
|
+
original: errors,
|
|
695
|
+
workflowId: 'custom-parent',
|
|
696
|
+
logs: errors.flatMap(e => e.logs),
|
|
697
|
+
}));
|
|
698
|
+
|
|
699
|
+
class ParentWorkflow extends Workflow {
|
|
700
|
+
@Task({
|
|
701
|
+
concurrent: true,
|
|
702
|
+
errorMergeStrategy: {
|
|
703
|
+
enabled: true,
|
|
704
|
+
combine: combineSpy // Custom combine function
|
|
705
|
+
}
|
|
706
|
+
})
|
|
707
|
+
async spawnChildren() {
|
|
708
|
+
return [
|
|
709
|
+
createChildWorkflow(this, 'Alpha', true),
|
|
710
|
+
createChildWorkflow(this, 'Beta', true),
|
|
711
|
+
createChildWorkflow(this, 'Gamma', false),
|
|
712
|
+
];
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
async run() {
|
|
716
|
+
try {
|
|
717
|
+
await this.spawnChildren();
|
|
718
|
+
} catch (err) {
|
|
719
|
+
return err;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
const parent = new ParentWorkflow('Parent');
|
|
725
|
+
|
|
726
|
+
// ACT
|
|
727
|
+
await parent.run();
|
|
728
|
+
|
|
729
|
+
// ASSERT: Custom combine function was called
|
|
730
|
+
expect(combineSpy).toHaveBeenCalledTimes(1);
|
|
731
|
+
|
|
732
|
+
// ASSERT: Called with array of WorkflowError objects
|
|
733
|
+
const calls = combineSpy.mock.calls;
|
|
734
|
+
expect(calls).toHaveLength(1);
|
|
735
|
+
const errorsArg = calls[0][0] as WorkflowError[];
|
|
736
|
+
expect(Array.isArray(errorsArg)).toBe(true);
|
|
737
|
+
expect(errorsArg).toHaveLength(2); // Alpha and Beta failed
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
it('should use custom merge result from combine function', async () => {
|
|
741
|
+
// ARRANGE: Custom combine that returns specific format
|
|
742
|
+
const customMerger = (errors: WorkflowError[]): WorkflowError => ({
|
|
743
|
+
message: `MERGED: ${errors.map(e => e.message).join(' | ')}`,
|
|
744
|
+
original: {
|
|
745
|
+
customField: 'custom-value',
|
|
746
|
+
errors,
|
|
747
|
+
},
|
|
748
|
+
workflowId: 'merged-workflow',
|
|
749
|
+
logs: errors.flatMap(e => e.logs),
|
|
750
|
+
});
|
|
751
|
+
|
|
752
|
+
class ParentWorkflow extends Workflow {
|
|
753
|
+
@Task({
|
|
754
|
+
concurrent: true,
|
|
755
|
+
errorMergeStrategy: {
|
|
756
|
+
enabled: true,
|
|
757
|
+
combine: customMerger
|
|
758
|
+
}
|
|
759
|
+
})
|
|
760
|
+
async spawnChildren() {
|
|
761
|
+
return [
|
|
762
|
+
createChildWorkflow(this, 'First', true),
|
|
763
|
+
createChildWorkflow(this, 'Second', true),
|
|
764
|
+
];
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
async run() {
|
|
768
|
+
try {
|
|
769
|
+
await this.spawnChildren();
|
|
770
|
+
} catch (err) {
|
|
771
|
+
return err;
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
const parent = new ParentWorkflow('Parent');
|
|
777
|
+
const thrownError = await parent.run() as WorkflowError;
|
|
778
|
+
|
|
779
|
+
// ASSERT: Custom merge result used
|
|
780
|
+
expect(thrownError.message).toBe('MERGED: First failed | Second failed');
|
|
781
|
+
expect(thrownError.workflowId).toBe('merged-workflow');
|
|
782
|
+
|
|
783
|
+
// ASSERT: Custom fields preserved
|
|
784
|
+
const customMetadata = thrownError.original as { customField: string };
|
|
785
|
+
expect(customMetadata.customField).toBe('custom-value');
|
|
786
|
+
});
|
|
787
|
+
});
|
|
788
|
+
|
|
789
|
+
// ============================================================================
|
|
790
|
+
// PATTERN 7: Edge Case Tests
|
|
791
|
+
// ============================================================================
|
|
792
|
+
|
|
793
|
+
describe('Edge cases and error scenarios', () => {
|
|
794
|
+
it('should handle single failure with merge enabled', async () => {
|
|
795
|
+
class ParentWorkflow extends Workflow {
|
|
796
|
+
@Task({
|
|
797
|
+
concurrent: true,
|
|
798
|
+
errorMergeStrategy: { enabled: true }
|
|
799
|
+
})
|
|
800
|
+
async spawnChildren() {
|
|
801
|
+
return [
|
|
802
|
+
createChildWorkflow(this, 'OnlyChild', true),
|
|
803
|
+
];
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
async run() {
|
|
807
|
+
try {
|
|
808
|
+
await this.spawnChildren();
|
|
809
|
+
} catch (err) {
|
|
810
|
+
return err;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
const parent = new ParentWorkflow('Parent');
|
|
816
|
+
const thrownError = await parent.run() as WorkflowError;
|
|
817
|
+
|
|
818
|
+
// ASSERT: Message format correct for single failure
|
|
819
|
+
expect(thrownError.message).toBe("1 of 1 concurrent child workflows failed in task 'spawnChildren'");
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
it('should handle all workflows failing with merge enabled', async () => {
|
|
823
|
+
class ParentWorkflow extends Workflow {
|
|
824
|
+
@Task({
|
|
825
|
+
concurrent: true,
|
|
826
|
+
errorMergeStrategy: { enabled: true }
|
|
827
|
+
})
|
|
828
|
+
async spawnChildren() {
|
|
829
|
+
return [
|
|
830
|
+
createChildWorkflow(this, 'W1', true),
|
|
831
|
+
createChildWorkflow(this, 'W2', true),
|
|
832
|
+
createChildWorkflow(this, 'W3', true),
|
|
833
|
+
createChildWorkflow(this, 'W4', true),
|
|
834
|
+
];
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
async run() {
|
|
838
|
+
try {
|
|
839
|
+
await this.spawnChildren();
|
|
840
|
+
} catch (err) {
|
|
841
|
+
return err;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
const parent = new ParentWorkflow('Parent');
|
|
847
|
+
const thrownError = await parent.run() as WorkflowError;
|
|
848
|
+
|
|
849
|
+
// ASSERT: All failures counted
|
|
850
|
+
expect(thrownError.message).toBe("4 of 4 concurrent child workflows failed in task 'spawnChildren'");
|
|
851
|
+
|
|
852
|
+
// ASSERT: All workflows completed
|
|
853
|
+
expect(parent.children.length).toBe(4);
|
|
854
|
+
});
|
|
855
|
+
|
|
856
|
+
it('should complete all workflows even when errors occur', async () => {
|
|
857
|
+
const completedWorkflows = new Set<string>();
|
|
858
|
+
|
|
859
|
+
class ParentWorkflow extends Workflow {
|
|
860
|
+
@Task({
|
|
861
|
+
concurrent: true,
|
|
862
|
+
errorMergeStrategy: { enabled: true }
|
|
863
|
+
})
|
|
864
|
+
async spawnChildren() {
|
|
865
|
+
const children = [
|
|
866
|
+
createChildWorkflow(this, 'Success1', false),
|
|
867
|
+
createChildWorkflow(this, 'Fail1', true),
|
|
868
|
+
createChildWorkflow(this, 'Success2', false),
|
|
869
|
+
createChildWorkflow(this, 'Fail2', true),
|
|
870
|
+
];
|
|
871
|
+
|
|
872
|
+
// Track completion
|
|
873
|
+
children.forEach(child => {
|
|
874
|
+
child.run().then(
|
|
875
|
+
() => completedWorkflows.add(child.id),
|
|
876
|
+
() => completedWorkflows.add(child.id)
|
|
877
|
+
);
|
|
878
|
+
});
|
|
879
|
+
|
|
880
|
+
return children;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
async run() {
|
|
884
|
+
try {
|
|
885
|
+
await this.spawnChildren();
|
|
886
|
+
} catch (err) {
|
|
887
|
+
// Expected
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
const parent = new ParentWorkflow('Parent');
|
|
893
|
+
await parent.run();
|
|
894
|
+
|
|
895
|
+
// ASSERT: All workflows completed (no orphans)
|
|
896
|
+
expect(completedWorkflows.size).toBe(4);
|
|
897
|
+
expect(parent.children.length).toBe(4);
|
|
898
|
+
});
|
|
899
|
+
});
|
|
900
|
+
|
|
901
|
+
// ============================================================================
|
|
902
|
+
// PATTERN 8: maxMergeDepth Tests (Future Work)
|
|
903
|
+
// ============================================================================
|
|
904
|
+
|
|
905
|
+
describe.todo('maxMergeDepth validation', () => {
|
|
906
|
+
// NOTE: maxMergeDepth is defined in ErrorMergeStrategy interface
|
|
907
|
+
// but not currently implemented in src/decorators/task.ts
|
|
908
|
+
// These tests should be implemented when maxMergeDepth is added
|
|
909
|
+
|
|
910
|
+
it('should respect maxMergeDepth when merging nested errors');
|
|
911
|
+
it('should handle maxMergeDepth=0 (no merging)');
|
|
912
|
+
it('should handle maxMergeDepth=1 (single level merging)');
|
|
913
|
+
});
|
|
914
|
+
```
|
|
915
|
+
|
|
916
|
+
### Integration Points
|
|
917
|
+
|
|
918
|
+
```yaml
|
|
919
|
+
TASK_DECORATOR:
|
|
920
|
+
- file: src/decorators/task.ts
|
|
921
|
+
- lines: 119-143 - error merge strategy implementation
|
|
922
|
+
- test: Verify opts.errorMergeStrategy?.enabled check works
|
|
923
|
+
- test: Verify default merger called when no combine() provided
|
|
924
|
+
- test: Verify custom combine() called when provided
|
|
925
|
+
|
|
926
|
+
WORKFLOW_ERROR_UTILS:
|
|
927
|
+
- file: src/utils/workflow-error-utils.ts
|
|
928
|
+
- function: mergeWorkflowErrors()
|
|
929
|
+
- test: Verify default merge behavior matches this function
|
|
930
|
+
- test: Verify message format, log aggregation, metadata
|
|
931
|
+
|
|
932
|
+
EVENT_SYSTEM:
|
|
933
|
+
- test: Verify error events emitted for individual failures
|
|
934
|
+
- test: Verify additional error event emitted when merge enabled
|
|
935
|
+
- test: Verify merged error event has correct structure
|
|
936
|
+
|
|
937
|
+
CONCURRENT_EXECUTION:
|
|
938
|
+
- test: Verify Promise.allSettled completes all workflows
|
|
939
|
+
- test: Verify no orphaned promises
|
|
940
|
+
- test: Verify parent.children.length equals spawned count
|
|
941
|
+
```
|
|
942
|
+
|
|
943
|
+
---
|
|
944
|
+
|
|
945
|
+
## Validation Loop
|
|
946
|
+
|
|
947
|
+
### Level 1: Syntax & Style (Immediate Feedback)
|
|
948
|
+
|
|
949
|
+
```bash
|
|
950
|
+
# After creating test file, run TypeScript check
|
|
951
|
+
npx tsc --noEmit
|
|
952
|
+
|
|
953
|
+
# Expected: Zero type errors
|
|
954
|
+
# Common errors to fix:
|
|
955
|
+
# - "Cannot find module" → Check import paths use .js extension
|
|
956
|
+
# - "Property 'errorMergeStrategy' does not exist" → Check TaskOptions import
|
|
957
|
+
# - Type errors in spy functions → Check vi.fn() typing
|
|
958
|
+
|
|
959
|
+
# Run linter if configured
|
|
960
|
+
npm run lint 2>/dev/null || echo "No linter configured"
|
|
961
|
+
```
|
|
962
|
+
|
|
963
|
+
### Level 2: Unit Tests (Component Validation)
|
|
964
|
+
|
|
965
|
+
```bash
|
|
966
|
+
# Run the new test file specifically
|
|
967
|
+
npm test -- error-merge-strategy.test.ts
|
|
968
|
+
|
|
969
|
+
# Run with verbose output to see all test names
|
|
970
|
+
npm test -- error-merge-strategy.test.ts --reporter=verbose
|
|
971
|
+
|
|
972
|
+
# Expected: All new tests pass
|
|
973
|
+
# If tests fail, debug:
|
|
974
|
+
# - Check that @Task has concurrent: true
|
|
975
|
+
# - Check that observer is added before run()
|
|
976
|
+
# - Check try-catch error handling
|
|
977
|
+
# - Verify error structure with type guards
|
|
978
|
+
|
|
979
|
+
# Run related test files
|
|
980
|
+
npm test -- concurrent-task-failures.test.ts
|
|
981
|
+
npm test -- workflow-error-utils.test.ts
|
|
982
|
+
npm test -- decorators.test.ts
|
|
983
|
+
|
|
984
|
+
# Expected: All related tests still pass (no regressions)
|
|
985
|
+
```
|
|
986
|
+
|
|
987
|
+
### Level 3: Integration Testing (System Validation)
|
|
988
|
+
|
|
989
|
+
```bash
|
|
990
|
+
# Run full adversarial test suite
|
|
991
|
+
npm test -- src/__tests__/adversarial/
|
|
992
|
+
|
|
993
|
+
# Expected: All adversarial tests pass
|
|
994
|
+
# This validates new tests work alongside existing edge case tests
|
|
995
|
+
|
|
996
|
+
# Run full test suite
|
|
997
|
+
npm test
|
|
998
|
+
|
|
999
|
+
# Expected: All tests pass (no regressions)
|
|
1000
|
+
# Current test count: ~344 tests
|
|
1001
|
+
# New tests should add ~10-15 tests
|
|
1002
|
+
|
|
1003
|
+
# Check test coverage
|
|
1004
|
+
npm test -- --coverage
|
|
1005
|
+
|
|
1006
|
+
# Expected: Error merge strategy code paths > 90% coverage
|
|
1007
|
+
```
|
|
1008
|
+
|
|
1009
|
+
### Level 4: Manual Verification
|
|
1010
|
+
|
|
1011
|
+
```bash
|
|
1012
|
+
# Create a manual test to verify behavior
|
|
1013
|
+
cat > /tmp/test_error_merge_strategy.ts << 'EOF'
|
|
1014
|
+
import { Workflow, Task, Step } from './dist/index.js';
|
|
1015
|
+
|
|
1016
|
+
class ChildWorkflow extends Workflow {
|
|
1017
|
+
constructor(name: string, parent: Workflow, private fail: boolean) {
|
|
1018
|
+
super(name, parent);
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
@Step()
|
|
1022
|
+
async run() {
|
|
1023
|
+
if (this.fail) {
|
|
1024
|
+
throw new Error(`${this.id} failed`);
|
|
1025
|
+
}
|
|
1026
|
+
return `${this.id} succeeded`;
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
// Test 1: Default behavior (disabled)
|
|
1031
|
+
class Parent1 extends Workflow {
|
|
1032
|
+
@Task({ concurrent: true })
|
|
1033
|
+
async spawn() {
|
|
1034
|
+
return [
|
|
1035
|
+
new ChildWorkflow('c1', this, false),
|
|
1036
|
+
new ChildWorkflow('c2', this, true),
|
|
1037
|
+
new ChildWorkflow('c3', this, false),
|
|
1038
|
+
];
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
// Test 2: Enabled with default merge
|
|
1043
|
+
class Parent2 extends Workflow {
|
|
1044
|
+
@Task({ concurrent: true, errorMergeStrategy: { enabled: true } })
|
|
1045
|
+
async spawn() {
|
|
1046
|
+
return [
|
|
1047
|
+
new ChildWorkflow('c1', this, false),
|
|
1048
|
+
new ChildWorkflow('c2', this, true),
|
|
1049
|
+
new ChildWorkflow('c3', this, true),
|
|
1050
|
+
];
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
async function test() {
|
|
1055
|
+
console.log('Test 1: Default behavior');
|
|
1056
|
+
try {
|
|
1057
|
+
const p1 = new Parent1('p1');
|
|
1058
|
+
await p1.spawn();
|
|
1059
|
+
} catch (err) {
|
|
1060
|
+
console.log('Error:', err.message);
|
|
1061
|
+
console.log('Is first error only:', !err.message.includes('concurrent'));
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
console.log('\nTest 2: Enabled with default merge');
|
|
1065
|
+
try {
|
|
1066
|
+
const p2 = new Parent2('p2');
|
|
1067
|
+
await p2.spawn();
|
|
1068
|
+
} catch (err) {
|
|
1069
|
+
console.log('Error:', err.message);
|
|
1070
|
+
console.log('Is aggregated:', err.message.includes('concurrent'));
|
|
1071
|
+
console.log('Failed children:', err.original?.failedChildren);
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
test();
|
|
1076
|
+
EOF
|
|
1077
|
+
|
|
1078
|
+
# Build and run
|
|
1079
|
+
npm run build
|
|
1080
|
+
node /tmp/test_error_merge_strategy.ts
|
|
1081
|
+
|
|
1082
|
+
# Expected output:
|
|
1083
|
+
# Test 1: Error message contains single child name (not "concurrent")
|
|
1084
|
+
# Test 2: Error message contains "X of Y concurrent" and failedChildren count
|
|
1085
|
+
|
|
1086
|
+
# Clean up
|
|
1087
|
+
rm /tmp/test_error_merge_strategy.ts
|
|
1088
|
+
```
|
|
1089
|
+
|
|
1090
|
+
---
|
|
1091
|
+
|
|
1092
|
+
## Final Validation Checklist
|
|
1093
|
+
|
|
1094
|
+
### Technical Validation
|
|
1095
|
+
|
|
1096
|
+
- [ ] Test file created at `src/__tests__/adversarial/error-merge-strategy.test.ts`
|
|
1097
|
+
- [ ] All imports use `.js` extensions for ES modules
|
|
1098
|
+
- [ ] Test follows patterns from `concurrent-task-failures.test.ts`
|
|
1099
|
+
- [ ] Helper functions defined (createChildWorkflow, setupEventObserver)
|
|
1100
|
+
- [ ] TypeScript compilation succeeds: `npx tsc --noEmit`
|
|
1101
|
+
- [ ] All new tests pass: `npm test -- error-merge-strategy.test.ts`
|
|
1102
|
+
- [ ] All existing tests pass: `npm test`
|
|
1103
|
+
|
|
1104
|
+
### Feature Validation - Default Behavior (disabled)
|
|
1105
|
+
|
|
1106
|
+
- [ ] Test for no errorMergeStrategy provided
|
|
1107
|
+
- [ ] Test for errorMergeStrategy.enabled=false
|
|
1108
|
+
- [ ] Verifies first error thrown (not aggregated)
|
|
1109
|
+
- [ ] Verifies no additional error event from @Task
|
|
1110
|
+
- [ ] Verifies all workflows complete (Promise.allSettled)
|
|
1111
|
+
|
|
1112
|
+
### Feature Validation - Enabled with Default Merge
|
|
1113
|
+
|
|
1114
|
+
- [ ] Test for errorMergeStrategy.enabled=true without combine()
|
|
1115
|
+
- [ ] Verifies aggregated error message with counts and task name
|
|
1116
|
+
- [ ] Verifies metadata in original field (name, errors, totalChildren, failedChildren, failedWorkflowIds)
|
|
1117
|
+
- [ ] Verifies logs aggregated from all failed workflows
|
|
1118
|
+
- [ ] Verifies error event emitted with merged error
|
|
1119
|
+
- [ ] Verifies all workflows complete despite failures
|
|
1120
|
+
|
|
1121
|
+
### Feature Validation - Enabled with Custom Combine
|
|
1122
|
+
|
|
1123
|
+
- [ ] Test for errorMergeStrategy.enabled=true with combine()
|
|
1124
|
+
- [ ] Verifies custom combine() function is called
|
|
1125
|
+
- [ ] Verifies all errors passed to combine()
|
|
1126
|
+
- [ ] Verifies combine() return value is thrown
|
|
1127
|
+
- [ ] Uses vi.fn() spy to verify call count and arguments
|
|
1128
|
+
|
|
1129
|
+
### Feature Validation - Edge Cases
|
|
1130
|
+
|
|
1131
|
+
- [ ] Test for single failure with merge enabled
|
|
1132
|
+
- [ ] Test for all workflows failing
|
|
1133
|
+
- [ ] Test for mixed success/failure scenarios
|
|
1134
|
+
- [ ] Test for no orphaned workflows (completion tracking)
|
|
1135
|
+
|
|
1136
|
+
### Code Quality Validation
|
|
1137
|
+
|
|
1138
|
+
- [ ] Follows existing codebase test patterns
|
|
1139
|
+
- [ ] File placement in adversarial/ directory
|
|
1140
|
+
- [ ] Test names are descriptive and follow convention
|
|
1141
|
+
- [ ] Helper functions follow existing patterns
|
|
1142
|
+
- [ ] Comments clarify complex test scenarios
|
|
1143
|
+
- [ ] No hardcoded timeouts or delays
|
|
1144
|
+
- [ ] Tests are deterministic (no race conditions)
|
|
1145
|
+
|
|
1146
|
+
### Documentation & Deployment
|
|
1147
|
+
|
|
1148
|
+
- [ ] Test file has clear header comment describing purpose
|
|
1149
|
+
- [ ] Test suites are logically organized
|
|
1150
|
+
- [ ] Edge cases are documented in test descriptions
|
|
1151
|
+
- [ ] Future work (maxMergeDepth) noted with todo/todo.skip
|
|
1152
|
+
- [ ] Tests serve as documentation of expected behavior
|
|
1153
|
+
|
|
1154
|
+
---
|
|
1155
|
+
|
|
1156
|
+
## Anti-Patterns to Avoid
|
|
1157
|
+
|
|
1158
|
+
- ❌ **Don't forget `concurrent: true`** - Error merge strategy only works with concurrent execution
|
|
1159
|
+
- ❌ **Don't add observer after run()** - Events during run() won't be captured
|
|
1160
|
+
- ❌ **Don't assume error message order** - First error may not be deterministic
|
|
1161
|
+
- ❌ **Don't skip try-catch for expected errors** - Tests will fail with unhandled rejection
|
|
1162
|
+
- ❌ **Don't use `await` before expect in try-catch** - Errors won't be caught properly
|
|
1163
|
+
- ❌ **Don't test unimplemented features** - maxMergeDepth is not implemented, use todo.skip()
|
|
1164
|
+
- ❌ **Don't forget to verify all workflows completed** - Check parent.children.length
|
|
1165
|
+
- ❌ **Don't count error events incorrectly** - Individual errors + 1 merged event when enabled
|
|
1166
|
+
- ❌ **Don't hardcode expected error messages** - Use regex or partial matching
|
|
1167
|
+
- ❌ **Don't place test file in unit/ directory** - This is an adversarial test
|
|
1168
|
+
|
|
1169
|
+
---
|
|
1170
|
+
|
|
1171
|
+
## Confidence Score
|
|
1172
|
+
|
|
1173
|
+
**9/10** - High confidence for one-pass implementation success
|
|
1174
|
+
|
|
1175
|
+
**Reasoning:**
|
|
1176
|
+
- Clear implementation files to test (task.ts, workflow-error-utils.ts)
|
|
1177
|
+
- Existing test patterns provide exact templates to follow
|
|
1178
|
+
- Comprehensive research documentation available
|
|
1179
|
+
- Well-defined test scenarios from contract definition
|
|
1180
|
+
- Helper function patterns established in codebase
|
|
1181
|
+
|
|
1182
|
+
**Risk Factors:**
|
|
1183
|
+
- Must remember `concurrent: true` is required for error merge
|
|
1184
|
+
- Event counting can be tricky (individual + merged events)
|
|
1185
|
+
- maxMergeDepth not implemented - need to handle gracefully
|
|
1186
|
+
- First error may not be deterministic in concurrent execution
|
|
1187
|
+
|
|
1188
|
+
---
|
|
1189
|
+
|
|
1190
|
+
## Appendix: Test File Template
|
|
1191
|
+
|
|
1192
|
+
```typescript
|
|
1193
|
+
/**
|
|
1194
|
+
* ErrorMergeStrategy Functionality Test Suite
|
|
1195
|
+
*
|
|
1196
|
+
* Tests the ErrorMergeStrategy feature for concurrent task error aggregation.
|
|
1197
|
+
*
|
|
1198
|
+
* Validates:
|
|
1199
|
+
* - Default behavior (disabled) - first error wins
|
|
1200
|
+
* - Enabled with default merge - uses mergeWorkflowErrors
|
|
1201
|
+
* - Enabled with custom combine - user-provided merger
|
|
1202
|
+
* - Edge cases - single failure, all failing, mixed scenarios
|
|
1203
|
+
* - Event emission - individual and merged error events
|
|
1204
|
+
* - Completion verification - all workflows complete
|
|
1205
|
+
*
|
|
1206
|
+
* Related:
|
|
1207
|
+
* - P1.M2.T2.S2: Error aggregation logic implementation
|
|
1208
|
+
* - P1.M2.T2.S3: Default error merger utility
|
|
1209
|
+
* - Bug: 001_e8e04329daf3 - Concurrent task error handling
|
|
1210
|
+
*/
|
|
1211
|
+
|
|
1212
|
+
import { describe, it, expect, todo } from 'vitest';
|
|
1213
|
+
import { Workflow, Task, Step } from '../../index.js';
|
|
1214
|
+
import type { WorkflowEvent, WorkflowError } from '../../types/index.js';
|
|
1215
|
+
|
|
1216
|
+
describe('@Task decorator ErrorMergeStrategy', () => {
|
|
1217
|
+
// Helper functions
|
|
1218
|
+
function createChildWorkflow(parent: Workflow, name: string, shouldFail: boolean = false): Workflow {
|
|
1219
|
+
// ... implementation
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
function setupEventObserver(workflow: Workflow): WorkflowEvent[] {
|
|
1223
|
+
// ... implementation
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
// Test suites
|
|
1227
|
+
describe('Default behavior (errorMergeStrategy disabled)', () => {
|
|
1228
|
+
// ... tests
|
|
1229
|
+
});
|
|
1230
|
+
|
|
1231
|
+
describe('Enabled with default error merge', () => {
|
|
1232
|
+
// ... tests
|
|
1233
|
+
});
|
|
1234
|
+
|
|
1235
|
+
describe('Enabled with custom combine function', () => {
|
|
1236
|
+
// ... tests
|
|
1237
|
+
});
|
|
1238
|
+
|
|
1239
|
+
describe('Edge cases and error scenarios', () => {
|
|
1240
|
+
// ... tests
|
|
1241
|
+
});
|
|
1242
|
+
|
|
1243
|
+
describe.todo('maxMergeDepth validation', () => {
|
|
1244
|
+
// Future: maxMergeDepth tests
|
|
1245
|
+
});
|
|
1246
|
+
});
|
|
1247
|
+
```
|
|
1248
|
+
|
|
1249
|
+
---
|
|
1250
|
+
|
|
1251
|
+
**PRP Status**: ✅ Complete - Ready for Implementation
|
|
1252
|
+
**Next Task**: P1.M2.T3.S1 - Document trackTiming Default Value in PRD
|