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/src/core/workflow.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type {
|
|
|
3
3
|
WorkflowStatus,
|
|
4
4
|
WorkflowEvent,
|
|
5
5
|
WorkflowObserver,
|
|
6
|
+
LogEntry,
|
|
6
7
|
} from '../types/index.js';
|
|
7
8
|
import type { WorkflowContext, WorkflowConfig, WorkflowResult } from '../types/workflow-context.js';
|
|
8
9
|
import { generateId } from '../utils/id.js';
|
|
@@ -57,7 +58,7 @@ export class Workflow<T = unknown> {
|
|
|
57
58
|
protected readonly logger: WorkflowLogger;
|
|
58
59
|
|
|
59
60
|
/** The node representation of this workflow */
|
|
60
|
-
|
|
61
|
+
public readonly node: WorkflowNode;
|
|
61
62
|
|
|
62
63
|
/** Observers (only populated on root workflow) */
|
|
63
64
|
private observers: WorkflowObserver[] = [];
|
|
@@ -94,6 +95,17 @@ export class Workflow<T = unknown> {
|
|
|
94
95
|
this.parent = (parentOrExecutor as Workflow) ?? null;
|
|
95
96
|
}
|
|
96
97
|
|
|
98
|
+
// Validate workflow name (after config is normalized)
|
|
99
|
+
if (typeof this.config.name === 'string') {
|
|
100
|
+
const trimmedName = this.config.name.trim();
|
|
101
|
+
if (trimmedName.length === 0) {
|
|
102
|
+
throw new Error('Workflow name cannot be empty or whitespace only');
|
|
103
|
+
}
|
|
104
|
+
if (this.config.name.length > 100) {
|
|
105
|
+
throw new Error('Workflow name cannot exceed 100 characters');
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
97
109
|
// Create the node representation
|
|
98
110
|
this.node = {
|
|
99
111
|
id: this.id,
|
|
@@ -118,22 +130,113 @@ export class Workflow<T = unknown> {
|
|
|
118
130
|
/**
|
|
119
131
|
* Get observers from the root workflow
|
|
120
132
|
* Traverses up the tree to find the root
|
|
133
|
+
* Uses cycle detection to prevent infinite loops from circular parent-child relationships
|
|
121
134
|
*/
|
|
122
135
|
private getRootObservers(): WorkflowObserver[] {
|
|
123
|
-
|
|
124
|
-
|
|
136
|
+
const visited = new Set<Workflow>();
|
|
137
|
+
let root: Workflow = this;
|
|
138
|
+
let current: Workflow | null = this;
|
|
139
|
+
|
|
140
|
+
while (current) {
|
|
141
|
+
if (visited.has(current)) {
|
|
142
|
+
throw new Error('Circular parent-child relationship detected');
|
|
143
|
+
}
|
|
144
|
+
visited.add(current);
|
|
145
|
+
root = current;
|
|
146
|
+
current = current.parent;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return root.observers;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Check if this workflow is a descendant of the given ancestor workflow.
|
|
154
|
+
*
|
|
155
|
+
* Traverses the parent chain upward looking for the ancestor reference.
|
|
156
|
+
* Uses a visited Set to detect cycles during traversal. This method provides
|
|
157
|
+
* a convenient way to check workflow hierarchy relationships without manually
|
|
158
|
+
* traversing the parent chain.
|
|
159
|
+
*
|
|
160
|
+
* @warning This method reveals workflow hierarchy information. If your
|
|
161
|
+
* application exposes workflows via an API, ensure you implement proper
|
|
162
|
+
* access control to prevent unauthorized topology discovery. Note that
|
|
163
|
+
* the `parent` and `children` properties are already public, so this
|
|
164
|
+
* method does not expose any new information beyond what is currently
|
|
165
|
+
* accessible.
|
|
166
|
+
*
|
|
167
|
+
* **Time Complexity**: O(d) where d is the depth of the hierarchy
|
|
168
|
+
* **Space Complexity**: O(d) for the visited Set in worst case (cycle detection)
|
|
169
|
+
*
|
|
170
|
+
* @example Check if a workflow belongs to a specific hierarchy
|
|
171
|
+
* ```typescript
|
|
172
|
+
* const root = new Workflow('root');
|
|
173
|
+
* const child = new Workflow('child', { parent: root });
|
|
174
|
+
*
|
|
175
|
+
* if (child.isDescendantOf(root)) {
|
|
176
|
+
* console.log('Child is in root hierarchy');
|
|
177
|
+
* }
|
|
178
|
+
* ```
|
|
179
|
+
*
|
|
180
|
+
* @example Validate before attaching to prevent circular references
|
|
181
|
+
* ```typescript
|
|
182
|
+
* if (!newChild.isDescendantOf(parent)) {
|
|
183
|
+
* parent.attachChild(newChild);
|
|
184
|
+
* } else {
|
|
185
|
+
* throw new Error('Would create circular reference');
|
|
186
|
+
* }
|
|
187
|
+
* ```
|
|
188
|
+
*
|
|
189
|
+
* @example Check for ancestor relationship in conditional logic
|
|
190
|
+
* ```typescript
|
|
191
|
+
* const isInProductionBranch = workflow.isDescendantOf(productionRoot);
|
|
192
|
+
* if (isInProductionBranch) {
|
|
193
|
+
* // Apply production-specific logic
|
|
194
|
+
* }
|
|
195
|
+
* ```
|
|
196
|
+
*
|
|
197
|
+
* @param ancestor - The potential ancestor workflow to check
|
|
198
|
+
* @returns true if ancestor is found in parent chain, false otherwise
|
|
199
|
+
* @throws {Error} If a cycle is detected during traversal (indicates corrupted tree structure)
|
|
200
|
+
*/
|
|
201
|
+
public isDescendantOf(ancestor: Workflow): boolean {
|
|
202
|
+
const visited = new Set<Workflow>();
|
|
203
|
+
let current: Workflow | null = this.parent;
|
|
204
|
+
|
|
205
|
+
while (current !== null) {
|
|
206
|
+
if (visited.has(current)) {
|
|
207
|
+
throw new Error('Circular parent-child relationship detected');
|
|
208
|
+
}
|
|
209
|
+
visited.add(current);
|
|
210
|
+
|
|
211
|
+
if (current === ancestor) {
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
current = current.parent;
|
|
125
216
|
}
|
|
126
|
-
|
|
217
|
+
|
|
218
|
+
return false;
|
|
127
219
|
}
|
|
128
220
|
|
|
129
221
|
/**
|
|
130
222
|
* Get the root workflow
|
|
223
|
+
* Uses cycle detection to prevent infinite loops from circular parent-child relationships
|
|
131
224
|
*/
|
|
132
225
|
protected getRoot(): Workflow {
|
|
133
|
-
|
|
134
|
-
|
|
226
|
+
const visited = new Set<Workflow>();
|
|
227
|
+
let root: Workflow = this;
|
|
228
|
+
let current: Workflow | null = this;
|
|
229
|
+
|
|
230
|
+
while (current) {
|
|
231
|
+
if (visited.has(current)) {
|
|
232
|
+
throw new Error('Circular parent-child relationship detected');
|
|
233
|
+
}
|
|
234
|
+
visited.add(current);
|
|
235
|
+
root = current;
|
|
236
|
+
current = current.parent;
|
|
135
237
|
}
|
|
136
|
-
|
|
238
|
+
|
|
239
|
+
return root;
|
|
137
240
|
}
|
|
138
241
|
|
|
139
242
|
/**
|
|
@@ -158,10 +261,88 @@ export class Workflow<T = unknown> {
|
|
|
158
261
|
}
|
|
159
262
|
|
|
160
263
|
/**
|
|
161
|
-
* Attach a child workflow
|
|
162
|
-
*
|
|
264
|
+
* Attach a child workflow to this parent workflow.
|
|
265
|
+
*
|
|
266
|
+
* Validates that the child can be attached by checking:
|
|
267
|
+
* 1. Child is not already attached to this parent workflow
|
|
268
|
+
* 2. Child does not have a different parent (enforces single-parent invariant)
|
|
269
|
+
* 3. Child is not an ancestor of this parent (prevents circular references)
|
|
270
|
+
*
|
|
271
|
+
* **Structural Changes:**
|
|
272
|
+
* - Adds child to this.children array (workflow tree)
|
|
273
|
+
* - Adds child.node to this.node.children array (node tree)
|
|
274
|
+
* - Sets child.parent = this (workflow tree)
|
|
275
|
+
* - Sets child.node.parent = this.node (node tree)
|
|
276
|
+
* - Emits childAttached event to notify observers
|
|
277
|
+
*
|
|
278
|
+
* **Invariants Maintained:**
|
|
279
|
+
* - Single-parent rule: A workflow can only have one parent
|
|
280
|
+
* - 1:1 tree mirror: workflow tree and node tree stay synchronized
|
|
281
|
+
* - No cycles: A workflow cannot be its own ancestor
|
|
282
|
+
*
|
|
283
|
+
* **Cycle Detection:**
|
|
284
|
+
* - Uses isDescendantOf() helper with Set-based cycle detection
|
|
285
|
+
* - Throws immediately if circular reference would be created
|
|
286
|
+
*
|
|
287
|
+
* @param child - The child workflow to attach
|
|
288
|
+
* @throws {Error} If the child is already attached to this workflow
|
|
289
|
+
* @throws {Error} If the child already has a different parent (use detachChild() first for reparenting)
|
|
290
|
+
* @throws {Error} If the child is an ancestor of this parent (would create circular reference)
|
|
291
|
+
*
|
|
292
|
+
* @example
|
|
293
|
+
* ```ts
|
|
294
|
+
* const parent = new Workflow('Parent');
|
|
295
|
+
* const child = new Workflow('Child');
|
|
296
|
+
* parent.attachChild(child);
|
|
297
|
+
* // child.parent === parent
|
|
298
|
+
* // parent.children.includes(child) === true
|
|
299
|
+
* ```
|
|
300
|
+
*
|
|
301
|
+
* @example Reparenting workflow
|
|
302
|
+
* ```ts
|
|
303
|
+
* const parent1 = new Workflow('Parent1');
|
|
304
|
+
* const parent2 = new Workflow('Parent2');
|
|
305
|
+
* const child = new Workflow('Child', parent1); // Attached to parent1
|
|
306
|
+
*
|
|
307
|
+
* // Later, move child to parent2
|
|
308
|
+
* parent1.detachChild(child);
|
|
309
|
+
* parent2.attachChild(child);
|
|
310
|
+
* // child.parent === parent2
|
|
311
|
+
* ```
|
|
312
|
+
*
|
|
313
|
+
* @see detachChild - For detaching children (enables reparenting)
|
|
314
|
+
* @see isDescendantOf - Private helper for circular reference detection
|
|
163
315
|
*/
|
|
164
316
|
public attachChild(child: Workflow): void {
|
|
317
|
+
if (this.children.includes(child)) {
|
|
318
|
+
throw new Error('Child already attached to this workflow');
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Check if child already has a different parent
|
|
322
|
+
if (child.parent !== null && child.parent !== this) {
|
|
323
|
+
const errorMessage =
|
|
324
|
+
`Child '${child.node.name}' already has a parent '${child.parent.node.name}'. ` +
|
|
325
|
+
`A workflow can only have one parent. ` +
|
|
326
|
+
`Use detachChild() on '${child.parent.node.name}' first if you need to reparent.`;
|
|
327
|
+
console.error(errorMessage);
|
|
328
|
+
throw new Error(errorMessage);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Check if child is an ancestor (would create circular reference)
|
|
332
|
+
if (this.isDescendantOf(child)) {
|
|
333
|
+
const errorMessage =
|
|
334
|
+
`Cannot attach child '${child.node.name}' - it is an ancestor of '${this.node.name}'. ` +
|
|
335
|
+
`This would create a circular reference.`;
|
|
336
|
+
console.error(errorMessage);
|
|
337
|
+
throw new Error(errorMessage);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Update child's parent if it's currently null
|
|
341
|
+
if (child.parent === null) {
|
|
342
|
+
child.parent = this;
|
|
343
|
+
child.node.parent = this.node; // Maintain 1:1 mirror between workflow tree and node tree
|
|
344
|
+
}
|
|
345
|
+
|
|
165
346
|
this.children.push(child);
|
|
166
347
|
this.node.children.push(child.node);
|
|
167
348
|
|
|
@@ -173,6 +354,59 @@ export class Workflow<T = unknown> {
|
|
|
173
354
|
});
|
|
174
355
|
}
|
|
175
356
|
|
|
357
|
+
/**
|
|
358
|
+
* Detach a child workflow from this parent workflow.
|
|
359
|
+
*
|
|
360
|
+
* Removes the child from both the workflow tree (this.children) and
|
|
361
|
+
* the node tree (this.node.children), clears the child's parent reference,
|
|
362
|
+
* and emits a childDetached event to notify observers.
|
|
363
|
+
*
|
|
364
|
+
* This enables reparenting workflows: oldParent.detachChild(child); newParent.attachChild(child);
|
|
365
|
+
*
|
|
366
|
+
* @param child - The child workflow to detach
|
|
367
|
+
* @throws {Error} If the child is not attached to this parent workflow
|
|
368
|
+
*
|
|
369
|
+
* @example
|
|
370
|
+
* ```ts
|
|
371
|
+
* const parent = new Workflow('Parent');
|
|
372
|
+
* const child = new Workflow('Child', parent);
|
|
373
|
+
*
|
|
374
|
+
* // Later, reparent to a different parent
|
|
375
|
+
* parent.detachChild(child);
|
|
376
|
+
* newParent.attachChild(child);
|
|
377
|
+
* ```
|
|
378
|
+
*/
|
|
379
|
+
public detachChild(child: Workflow): void {
|
|
380
|
+
// Validate child is actually attached
|
|
381
|
+
const index = this.children.indexOf(child);
|
|
382
|
+
|
|
383
|
+
if (index === -1) {
|
|
384
|
+
throw new Error(
|
|
385
|
+
`Child '${child.node.name}' is not attached to workflow '${this.node.name}'`
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Remove from workflow tree (this.children array)
|
|
390
|
+
this.children.splice(index, 1);
|
|
391
|
+
|
|
392
|
+
// Remove from node tree (this.node.children array)
|
|
393
|
+
const nodeIndex = this.node.children.indexOf(child.node);
|
|
394
|
+
if (nodeIndex !== -1) {
|
|
395
|
+
this.node.children.splice(nodeIndex, 1);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Clear child's parent reference (both workflow tree and node tree)
|
|
399
|
+
child.parent = null;
|
|
400
|
+
child.node.parent = null; // Maintain 1:1 mirror between workflow tree and node tree
|
|
401
|
+
|
|
402
|
+
// Emit childDetached event
|
|
403
|
+
this.emitEvent({
|
|
404
|
+
type: 'childDetached',
|
|
405
|
+
parentId: this.id,
|
|
406
|
+
childId: child.id,
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
|
|
176
410
|
/**
|
|
177
411
|
* Emit an event to all root observers
|
|
178
412
|
*/
|
|
@@ -185,11 +419,11 @@ export class Workflow<T = unknown> {
|
|
|
185
419
|
obs.onEvent(event);
|
|
186
420
|
|
|
187
421
|
// Also notify tree changed for tree update events
|
|
188
|
-
if (event.type === 'treeUpdated' || event.type === 'childAttached') {
|
|
422
|
+
if (event.type === 'treeUpdated' || event.type === 'childAttached' || event.type === 'childDetached') {
|
|
189
423
|
obs.onTreeChanged(this.getRoot().node);
|
|
190
424
|
}
|
|
191
425
|
} catch (err) {
|
|
192
|
-
|
|
426
|
+
this.logger.error('Observer onEvent error', { error: err, eventType: event.type });
|
|
193
427
|
}
|
|
194
428
|
}
|
|
195
429
|
}
|
|
@@ -207,7 +441,7 @@ export class Workflow<T = unknown> {
|
|
|
207
441
|
try {
|
|
208
442
|
obs.onStateUpdated(this.node);
|
|
209
443
|
} catch (err) {
|
|
210
|
-
|
|
444
|
+
this.logger.error('Observer onStateUpdated error', { error: err, nodeId: this.node.id });
|
|
211
445
|
}
|
|
212
446
|
}
|
|
213
447
|
|
|
@@ -216,6 +450,9 @@ export class Workflow<T = unknown> {
|
|
|
216
450
|
type: 'stateSnapshot',
|
|
217
451
|
node: this.node,
|
|
218
452
|
});
|
|
453
|
+
|
|
454
|
+
// Emit treeUpdated event to trigger tree debugger rebuild
|
|
455
|
+
this.emitEvent({ type: 'treeUpdated', root: this.getRoot().node });
|
|
219
456
|
}
|
|
220
457
|
|
|
221
458
|
/**
|
|
@@ -224,6 +461,7 @@ export class Workflow<T = unknown> {
|
|
|
224
461
|
public setStatus(status: WorkflowStatus): void {
|
|
225
462
|
this.status = status;
|
|
226
463
|
this.node.status = status;
|
|
464
|
+
this.emitEvent({ type: 'treeUpdated', root: this.getRoot().node });
|
|
227
465
|
}
|
|
228
466
|
|
|
229
467
|
/**
|
|
@@ -291,8 +529,8 @@ export class Workflow<T = unknown> {
|
|
|
291
529
|
original: error,
|
|
292
530
|
workflowId: this.id,
|
|
293
531
|
stack: error instanceof Error ? error.stack : undefined,
|
|
294
|
-
state:
|
|
295
|
-
logs: [],
|
|
532
|
+
state: getObservedState(this),
|
|
533
|
+
logs: [...this.node.logs] as LogEntry[],
|
|
296
534
|
},
|
|
297
535
|
});
|
|
298
536
|
|
|
@@ -57,6 +57,32 @@ export class WorkflowTreeDebugger implements WorkflowObserver {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Remove entire subtree from node map using BFS traversal
|
|
62
|
+
* O(k) complexity where k = number of nodes in subtree
|
|
63
|
+
* Uses iterative BFS to avoid stack overflow on deep trees
|
|
64
|
+
*/
|
|
65
|
+
private removeSubtreeNodes(nodeId: string): void {
|
|
66
|
+
const node = this.nodeMap.get(nodeId);
|
|
67
|
+
if (!node) return; // Already removed or never existed
|
|
68
|
+
|
|
69
|
+
// BFS traversal to collect all descendant IDs
|
|
70
|
+
const toRemove: string[] = [];
|
|
71
|
+
const queue: WorkflowNode[] = [node];
|
|
72
|
+
|
|
73
|
+
while (queue.length > 0) {
|
|
74
|
+
const current = queue.shift()!;
|
|
75
|
+
toRemove.push(current.id);
|
|
76
|
+
// Add children to queue for BFS traversal
|
|
77
|
+
queue.push(...current.children);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Batch delete all collected keys (atomic update)
|
|
81
|
+
for (const id of toRemove) {
|
|
82
|
+
this.nodeMap.delete(id);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
60
86
|
// WorkflowObserver implementation
|
|
61
87
|
|
|
62
88
|
onLog(_entry: LogEntry): void {
|
|
@@ -64,12 +90,29 @@ export class WorkflowTreeDebugger implements WorkflowObserver {
|
|
|
64
90
|
}
|
|
65
91
|
|
|
66
92
|
onEvent(event: WorkflowEvent): void {
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
|
|
93
|
+
// Handle structural events with incremental updates
|
|
94
|
+
switch (event.type) {
|
|
95
|
+
case 'childAttached':
|
|
96
|
+
// Keep existing logic - already optimal O(k)
|
|
97
|
+
this.buildNodeMap(event.child);
|
|
98
|
+
break;
|
|
99
|
+
|
|
100
|
+
case 'childDetached':
|
|
101
|
+
// NEW: Incremental subtree removal
|
|
102
|
+
this.removeSubtreeNodes(event.childId);
|
|
103
|
+
break;
|
|
104
|
+
|
|
105
|
+
case 'treeUpdated':
|
|
106
|
+
// NEW: Update root reference only
|
|
107
|
+
this.root = event.root;
|
|
108
|
+
break;
|
|
109
|
+
|
|
110
|
+
default:
|
|
111
|
+
// Non-structural events - no map update needed
|
|
112
|
+
break;
|
|
70
113
|
}
|
|
71
114
|
|
|
72
|
-
//
|
|
115
|
+
// Always forward to event stream (existing behavior)
|
|
73
116
|
this.events.next(event);
|
|
74
117
|
}
|
|
75
118
|
|
|
@@ -78,9 +121,11 @@ export class WorkflowTreeDebugger implements WorkflowObserver {
|
|
|
78
121
|
}
|
|
79
122
|
|
|
80
123
|
onTreeChanged(root: WorkflowNode): void {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
this.
|
|
124
|
+
// All tree changes now handled incrementally in onEvent()
|
|
125
|
+
// Just update root reference if different
|
|
126
|
+
if (this.root !== root) {
|
|
127
|
+
this.root = root;
|
|
128
|
+
}
|
|
84
129
|
}
|
|
85
130
|
|
|
86
131
|
// Public API
|
package/src/decorators/task.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { TaskOptions, WorkflowNode, WorkflowEvent } from '../types/index.js';
|
|
1
|
+
import type { TaskOptions, WorkflowNode, WorkflowEvent, WorkflowError, SerializedWorkflowState } from '../types/index.js';
|
|
2
|
+
import { mergeWorkflowErrors } from '../utils/workflow-error-utils.js';
|
|
2
3
|
|
|
3
4
|
// Type for workflow-like objects
|
|
4
5
|
interface WorkflowLike {
|
|
@@ -29,6 +30,38 @@ interface WorkflowClass {
|
|
|
29
30
|
* ];
|
|
30
31
|
* }
|
|
31
32
|
* }
|
|
33
|
+
*
|
|
34
|
+
* @example Non-workflow return (silently skipped)
|
|
35
|
+
* class MyWorkflow extends Workflow {
|
|
36
|
+
* @Task()
|
|
37
|
+
* async returnsString(): Promise<string> {
|
|
38
|
+
* return 'not a workflow'; // Returned as-is, not attached
|
|
39
|
+
* }
|
|
40
|
+
* }
|
|
41
|
+
*
|
|
42
|
+
* @example Mixed return (only workflows attached)
|
|
43
|
+
* class MyWorkflow extends Workflow {
|
|
44
|
+
* @Task()
|
|
45
|
+
* async mixedReturn(): Promise<(Workflow | string)[]> {
|
|
46
|
+
* return [
|
|
47
|
+
* new ChildWorkflow('child1', this), // Attached
|
|
48
|
+
* 'some string', // Skipped
|
|
49
|
+
* new ChildWorkflow('child2', this), // Attached
|
|
50
|
+
* ];
|
|
51
|
+
* }
|
|
52
|
+
* }
|
|
53
|
+
*
|
|
54
|
+
* Validation Behavior
|
|
55
|
+
*
|
|
56
|
+
* The decorator uses lenient validation for return values:
|
|
57
|
+
* - Workflow objects (with 'id' property) are automatically attached
|
|
58
|
+
* - Non-workflow objects are silently skipped (not attached)
|
|
59
|
+
* - The original return value is always preserved
|
|
60
|
+
*
|
|
61
|
+
* This lenient approach enables:
|
|
62
|
+
* - Duck-typing: Works with workflow-like objects, not just Workflow instances
|
|
63
|
+
* - Flexible signatures: Methods can return any type without breaking
|
|
64
|
+
* - Graceful handling: Edge cases (null, undefined, primitives) don't throw errors
|
|
32
65
|
*/
|
|
33
66
|
export function Task(opts: TaskOptions = {}) {
|
|
34
67
|
return function <This, Args extends unknown[], Return>(
|
|
@@ -77,7 +110,37 @@ export function Task(opts: TaskOptions = {}) {
|
|
|
77
110
|
);
|
|
78
111
|
|
|
79
112
|
if (runnable.length > 0) {
|
|
80
|
-
await Promise.
|
|
113
|
+
const results = await Promise.allSettled(runnable.map((w) => w.run()));
|
|
114
|
+
|
|
115
|
+
const rejected = results.filter(
|
|
116
|
+
(r): r is PromiseRejectedResult => r.status === 'rejected'
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
if (rejected.length > 0) {
|
|
120
|
+
// Check if error merge strategy is enabled
|
|
121
|
+
if (opts.errorMergeStrategy?.enabled) {
|
|
122
|
+
// Extract WorkflowError objects from rejected promises
|
|
123
|
+
const errors = rejected.map((r) => r.reason as WorkflowError);
|
|
124
|
+
|
|
125
|
+
// Merge errors using custom combine() or default merger
|
|
126
|
+
const mergedError = opts.errorMergeStrategy?.combine
|
|
127
|
+
? opts.errorMergeStrategy.combine(errors)
|
|
128
|
+
: mergeWorkflowErrors(errors, taskName, wf.id, runnable.length);
|
|
129
|
+
|
|
130
|
+
// Emit error event with merged error
|
|
131
|
+
wf.emitEvent({
|
|
132
|
+
type: 'error',
|
|
133
|
+
node: wf.node,
|
|
134
|
+
error: mergedError,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Throw merged error
|
|
138
|
+
throw mergedError;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Backward compatibility: throw first error
|
|
142
|
+
throw rejected[0].reason;
|
|
143
|
+
}
|
|
81
144
|
}
|
|
82
145
|
}
|
|
83
146
|
|
package/src/index.ts
CHANGED
|
@@ -84,6 +84,7 @@ export { WorkflowTreeDebugger } from './debugger/tree-debugger.js';
|
|
|
84
84
|
export { Observable } from './utils/observable.js';
|
|
85
85
|
export type { Subscription, Observer } from './utils/observable.js';
|
|
86
86
|
export { generateId } from './utils/id.js';
|
|
87
|
+
export { mergeWorkflowErrors } from './utils/workflow-error-utils.js';
|
|
87
88
|
|
|
88
89
|
// Factory functions
|
|
89
90
|
export {
|
|
@@ -136,5 +137,6 @@ export type {
|
|
|
136
137
|
} from './tools/introspection.js';
|
|
137
138
|
|
|
138
139
|
// Examples (for reference)
|
|
139
|
-
|
|
140
|
-
export {
|
|
140
|
+
// Temporarily commented out due to decorator compatibility issues with vitest
|
|
141
|
+
// export { TestCycleWorkflow } from './examples/test-cycle-workflow.js';
|
|
142
|
+
// export { TDDOrchestrator } from './examples/tdd-orchestrator.js';
|
package/src/types/decorators.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { ErrorMergeStrategy } from './error-strategy.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Configuration options for @Step decorator
|
|
3
5
|
*/
|
|
@@ -6,7 +8,7 @@ export interface StepOptions {
|
|
|
6
8
|
name?: string;
|
|
7
9
|
/** If true, capture state snapshot after step completion */
|
|
8
10
|
snapshotState?: boolean;
|
|
9
|
-
/**
|
|
11
|
+
/** Track and emit step duration (default: true) */
|
|
10
12
|
trackTiming?: boolean;
|
|
11
13
|
/** If true, log message at step start */
|
|
12
14
|
logStart?: boolean;
|
|
@@ -16,10 +18,15 @@ export interface StepOptions {
|
|
|
16
18
|
|
|
17
19
|
/**
|
|
18
20
|
* Configuration options for @Task decorator
|
|
21
|
+
*
|
|
22
|
+
* @note The decorator uses lenient validation - non-Workflow returns are
|
|
23
|
+
* silently skipped. See the @Task decorator JSDoc for details.
|
|
19
24
|
*/
|
|
20
25
|
export interface TaskOptions {
|
|
21
26
|
/** Custom task name (defaults to method name) */
|
|
22
27
|
name?: string;
|
|
23
28
|
/** If true, run returned workflows concurrently */
|
|
24
29
|
concurrent?: boolean;
|
|
30
|
+
/** Strategy for merging errors from concurrent task execution */
|
|
31
|
+
errorMergeStrategy?: ErrorMergeStrategy;
|
|
25
32
|
}
|
package/src/types/events.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type { TokenUsage } from './sdk-primitives.js';
|
|
|
8
8
|
export type WorkflowEvent =
|
|
9
9
|
// Core workflow events
|
|
10
10
|
| { type: 'childAttached'; parentId: string; child: WorkflowNode }
|
|
11
|
+
| { type: 'childDetached'; parentId: string; childId: string }
|
|
11
12
|
| { type: 'stateSnapshot'; node: WorkflowNode }
|
|
12
13
|
| { type: 'stepStart'; node: WorkflowNode; step: string }
|
|
13
14
|
| { type: 'stepEnd'; node: WorkflowNode; step: string; duration: number }
|
package/src/utils/index.ts
CHANGED
package/src/utils/observable.ts
CHANGED
|
@@ -12,8 +12,25 @@ export interface Observer<T> {
|
|
|
12
12
|
complete?: () => void;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Logger interface for Observable error logging
|
|
17
|
+
* Matches WorkflowLogger.error() signature for compatibility
|
|
18
|
+
*/
|
|
19
|
+
export interface ObservableLogger {
|
|
20
|
+
error(message: string, data?: unknown): void;
|
|
21
|
+
}
|
|
22
|
+
|
|
15
23
|
export class Observable<T> {
|
|
16
24
|
private subscribers: Set<Observer<T>> = new Set();
|
|
25
|
+
private logger?: ObservableLogger;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Create a new Observable
|
|
29
|
+
* @param logger Optional logger for error reporting (falls back to console.error)
|
|
30
|
+
*/
|
|
31
|
+
constructor(logger?: ObservableLogger) {
|
|
32
|
+
this.logger = logger;
|
|
33
|
+
}
|
|
17
34
|
|
|
18
35
|
/**
|
|
19
36
|
* Subscribe to this observable
|
|
@@ -28,6 +45,18 @@ export class Observable<T> {
|
|
|
28
45
|
};
|
|
29
46
|
}
|
|
30
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Log errors using injected logger or fallback to console.error
|
|
50
|
+
*/
|
|
51
|
+
private logError(message: string, error: unknown): void {
|
|
52
|
+
if (this.logger) {
|
|
53
|
+
this.logger.error(message, { error });
|
|
54
|
+
} else {
|
|
55
|
+
// Fallback for backward compatibility
|
|
56
|
+
console.error(message, error);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
31
60
|
/**
|
|
32
61
|
* Emit a value to all subscribers
|
|
33
62
|
*/
|
|
@@ -36,7 +65,7 @@ export class Observable<T> {
|
|
|
36
65
|
try {
|
|
37
66
|
subscriber.next?.(value);
|
|
38
67
|
} catch (err) {
|
|
39
|
-
|
|
68
|
+
this.logError('Observable subscriber error', err);
|
|
40
69
|
}
|
|
41
70
|
}
|
|
42
71
|
}
|
|
@@ -49,7 +78,7 @@ export class Observable<T> {
|
|
|
49
78
|
try {
|
|
50
79
|
subscriber.error?.(err);
|
|
51
80
|
} catch (e) {
|
|
52
|
-
|
|
81
|
+
this.logError('Observable error handler failed', e);
|
|
53
82
|
}
|
|
54
83
|
}
|
|
55
84
|
}
|
|
@@ -62,7 +91,7 @@ export class Observable<T> {
|
|
|
62
91
|
try {
|
|
63
92
|
subscriber.complete?.();
|
|
64
93
|
} catch (err) {
|
|
65
|
-
|
|
94
|
+
this.logError('Observable complete handler failed', err);
|
|
66
95
|
}
|
|
67
96
|
}
|
|
68
97
|
this.subscribers.clear();
|