groundswell 0.0.2 → 1.0.0
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/LICENSE +21 -0
- package/README.md +26 -9
- package/dist/cache/cache-key.d.ts +86 -0
- package/dist/cache/cache-key.d.ts.map +1 -0
- package/dist/cache/cache-key.js +204 -0
- package/dist/cache/cache-key.js.map +1 -0
- package/dist/cache/cache.d.ts +104 -0
- package/dist/cache/cache.d.ts.map +1 -0
- package/dist/cache/cache.js +179 -0
- package/dist/cache/cache.js.map +1 -0
- package/{src/cache/index.ts → dist/cache/index.d.ts} +1 -1
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +6 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/core/agent.d.ts +203 -0
- package/dist/core/agent.d.ts.map +1 -0
- package/dist/core/agent.js +833 -0
- package/dist/core/agent.js.map +1 -0
- package/{src/core/context.ts → dist/core/context.d.ts} +16 -67
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +80 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/event-tree.d.ts +72 -0
- package/dist/core/event-tree.d.ts.map +1 -0
- package/dist/core/event-tree.js +211 -0
- package/dist/core/event-tree.js.map +1 -0
- package/{src/core/factory.ts → dist/core/factory.d.ts} +6 -27
- package/dist/core/factory.d.ts.map +1 -0
- package/dist/core/factory.js +110 -0
- package/dist/core/factory.js.map +1 -0
- package/{src/core/index.ts → dist/core/index.d.ts} +2 -10
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +9 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/logger.d.ts +50 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +91 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/mcp-handler.d.ts +127 -0
- package/dist/core/mcp-handler.d.ts.map +1 -0
- package/dist/core/mcp-handler.js +323 -0
- package/dist/core/mcp-handler.js.map +1 -0
- package/dist/core/prompt.d.ts +80 -0
- package/dist/core/prompt.d.ts.map +1 -0
- package/dist/core/prompt.js +120 -0
- package/dist/core/prompt.js.map +1 -0
- package/dist/core/workflow-context.d.ts +61 -0
- package/dist/core/workflow-context.d.ts.map +1 -0
- package/dist/core/workflow-context.js +358 -0
- package/dist/core/workflow-context.js.map +1 -0
- package/dist/core/workflow.d.ts +543 -0
- package/dist/core/workflow.d.ts.map +1 -0
- package/dist/core/workflow.js +986 -0
- package/dist/core/workflow.js.map +1 -0
- package/dist/debugger/event-replayer.d.ts +422 -0
- package/dist/debugger/event-replayer.d.ts.map +1 -0
- package/dist/debugger/event-replayer.js +639 -0
- package/dist/debugger/event-replayer.js.map +1 -0
- package/dist/debugger/index.d.ts +2 -0
- package/dist/debugger/index.d.ts.map +1 -0
- package/{src/debugger/index.ts → dist/debugger/index.js} +1 -0
- package/dist/debugger/index.js.map +1 -0
- package/dist/debugger/tree-debugger.d.ts +240 -0
- package/dist/debugger/tree-debugger.d.ts.map +1 -0
- package/dist/debugger/tree-debugger.js +620 -0
- package/dist/debugger/tree-debugger.js.map +1 -0
- package/dist/decorators/index.d.ts +4 -0
- package/dist/decorators/index.d.ts.map +1 -0
- package/{src/decorators/index.ts → dist/decorators/index.js} +1 -0
- package/dist/decorators/index.js.map +1 -0
- package/dist/decorators/observed-state.d.ts +32 -0
- package/dist/decorators/observed-state.d.ts.map +1 -0
- package/dist/decorators/observed-state.js +79 -0
- package/dist/decorators/observed-state.js.map +1 -0
- package/dist/decorators/step.d.ts +15 -0
- package/dist/decorators/step.d.ts.map +1 -0
- package/dist/decorators/step.js +192 -0
- package/dist/decorators/step.js.map +1 -0
- package/dist/decorators/task.d.ts +50 -0
- package/dist/decorators/task.d.ts.map +1 -0
- package/dist/decorators/task.js +118 -0
- package/dist/decorators/task.js.map +1 -0
- package/dist/examples/index.d.ts +3 -0
- package/dist/examples/index.d.ts.map +1 -0
- package/{src/examples/index.ts → dist/examples/index.js} +1 -0
- package/dist/examples/index.js.map +1 -0
- package/dist/examples/tdd-orchestrator.d.ts +15 -0
- package/dist/examples/tdd-orchestrator.d.ts.map +1 -0
- package/dist/examples/tdd-orchestrator.js +121 -0
- package/dist/examples/tdd-orchestrator.js.map +1 -0
- package/dist/examples/test-cycle-workflow.d.ts +14 -0
- package/dist/examples/test-cycle-workflow.d.ts.map +1 -0
- package/dist/examples/test-cycle-workflow.js +116 -0
- package/dist/examples/test-cycle-workflow.js.map +1 -0
- package/dist/harnesses/claude-code-harness.d.ts +391 -0
- package/dist/harnesses/claude-code-harness.d.ts.map +1 -0
- package/dist/harnesses/claude-code-harness.js +1076 -0
- package/dist/harnesses/claude-code-harness.js.map +1 -0
- package/dist/harnesses/harness-registry.d.ts +440 -0
- package/dist/harnesses/harness-registry.d.ts.map +1 -0
- package/dist/harnesses/harness-registry.js +543 -0
- package/dist/harnesses/harness-registry.js.map +1 -0
- package/dist/harnesses/index.d.ts +12 -0
- package/dist/harnesses/index.d.ts.map +1 -0
- package/dist/harnesses/index.js +11 -0
- package/dist/harnesses/index.js.map +1 -0
- package/dist/harnesses/pi-harness.d.ts +219 -0
- package/dist/harnesses/pi-harness.d.ts.map +1 -0
- package/dist/harnesses/pi-harness.js +676 -0
- package/dist/harnesses/pi-harness.js.map +1 -0
- package/dist/harnesses/pi-schema-converter.d.ts +24 -0
- package/dist/harnesses/pi-schema-converter.d.ts.map +1 -0
- package/dist/harnesses/pi-schema-converter.js +81 -0
- package/dist/harnesses/pi-schema-converter.js.map +1 -0
- package/dist/harnesses/register-defaults.d.ts +24 -0
- package/dist/harnesses/register-defaults.d.ts.map +1 -0
- package/dist/harnesses/register-defaults.js +40 -0
- package/dist/harnesses/register-defaults.js.map +1 -0
- package/dist/harnesses/session-store.d.ts +201 -0
- package/dist/harnesses/session-store.d.ts.map +1 -0
- package/dist/harnesses/session-store.js +254 -0
- package/dist/harnesses/session-store.js.map +1 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +57 -0
- package/dist/index.js.map +1 -0
- package/dist/reflection/index.d.ts +5 -0
- package/dist/reflection/index.d.ts.map +1 -0
- package/{src/reflection/index.ts → dist/reflection/index.js} +1 -1
- package/dist/reflection/index.js.map +1 -0
- package/dist/reflection/reflection.d.ts +84 -0
- package/dist/reflection/reflection.d.ts.map +1 -0
- package/dist/reflection/reflection.js +344 -0
- package/dist/reflection/reflection.js.map +1 -0
- package/dist/tools/index.d.ts +6 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +11 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/introspection.d.ts +165 -0
- package/dist/tools/introspection.d.ts.map +1 -0
- package/dist/tools/introspection.js +324 -0
- package/dist/tools/introspection.js.map +1 -0
- package/dist/types/agent.d.ts +1317 -0
- package/dist/types/agent.d.ts.map +1 -0
- package/dist/types/agent.js +423 -0
- package/dist/types/agent.js.map +1 -0
- package/dist/types/decorators.d.ts +40 -0
- package/dist/types/decorators.d.ts.map +1 -0
- package/dist/types/decorators.js +2 -0
- package/dist/types/decorators.js.map +1 -0
- package/dist/types/error-strategy.d.ts +13 -0
- package/dist/types/error-strategy.d.ts.map +1 -0
- package/dist/types/error-strategy.js +2 -0
- package/dist/types/error-strategy.js.map +1 -0
- package/dist/types/error.d.ts +20 -0
- package/dist/types/error.d.ts.map +1 -0
- package/dist/types/error.js +2 -0
- package/dist/types/error.js.map +1 -0
- package/dist/types/events.d.ts +113 -0
- package/dist/types/events.d.ts.map +1 -0
- package/dist/types/events.js +2 -0
- package/dist/types/events.js.map +1 -0
- package/dist/types/harnesses.d.ts +474 -0
- package/dist/types/harnesses.d.ts.map +1 -0
- package/dist/types/harnesses.js +2 -0
- package/dist/types/harnesses.js.map +1 -0
- package/dist/types/index.d.ts +23 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +8 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/logging.d.ts +24 -0
- package/dist/types/logging.d.ts.map +1 -0
- package/dist/types/logging.js +2 -0
- package/dist/types/logging.js.map +1 -0
- package/dist/types/observer.d.ts +18 -0
- package/dist/types/observer.d.ts.map +1 -0
- package/dist/types/observer.js +2 -0
- package/dist/types/observer.js.map +1 -0
- package/dist/types/prompt.d.ts +31 -0
- package/dist/types/prompt.d.ts.map +1 -0
- package/dist/types/prompt.js +6 -0
- package/dist/types/prompt.js.map +1 -0
- package/dist/types/providers.d.ts +691 -0
- package/dist/types/providers.d.ts.map +1 -0
- package/dist/types/providers.js +14 -0
- package/dist/types/providers.js.map +1 -0
- package/dist/types/reflection.d.ts +96 -0
- package/dist/types/reflection.d.ts.map +1 -0
- package/dist/types/reflection.js +24 -0
- package/dist/types/reflection.js.map +1 -0
- package/dist/types/restart.d.ts +132 -0
- package/dist/types/restart.d.ts.map +1 -0
- package/dist/types/restart.js +2 -0
- package/dist/types/restart.js.map +1 -0
- package/dist/types/sdk-primitives.d.ts +118 -0
- package/dist/types/sdk-primitives.d.ts.map +1 -0
- package/dist/types/sdk-primitives.js +6 -0
- package/dist/types/sdk-primitives.js.map +1 -0
- package/{src/types/snapshot.ts → dist/types/snapshot.d.ts} +5 -5
- package/dist/types/snapshot.d.ts.map +1 -0
- package/dist/types/snapshot.js +2 -0
- package/dist/types/snapshot.js.map +1 -0
- package/dist/types/streaming.d.ts +194 -0
- package/dist/types/streaming.d.ts.map +1 -0
- package/dist/types/streaming.js +67 -0
- package/dist/types/streaming.js.map +1 -0
- package/dist/types/workflow-context.d.ts +275 -0
- package/dist/types/workflow-context.d.ts.map +1 -0
- package/dist/types/workflow-context.js +8 -0
- package/dist/types/workflow-context.js.map +1 -0
- package/dist/types/workflow.d.ts +30 -0
- package/dist/types/workflow.d.ts.map +1 -0
- package/dist/types/workflow.js +2 -0
- package/dist/types/workflow.js.map +1 -0
- package/dist/utils/agent-validation.d.ts +88 -0
- package/dist/utils/agent-validation.d.ts.map +1 -0
- package/dist/utils/agent-validation.js +87 -0
- package/dist/utils/agent-validation.js.map +1 -0
- package/dist/utils/delay.d.ts +7 -0
- package/dist/utils/delay.d.ts.map +1 -0
- package/dist/utils/delay.js +9 -0
- package/dist/utils/delay.js.map +1 -0
- package/dist/utils/harness-config.d.ts +180 -0
- package/dist/utils/harness-config.d.ts.map +1 -0
- package/dist/utils/harness-config.js +311 -0
- package/dist/utils/harness-config.js.map +1 -0
- package/dist/utils/id.d.ts +6 -0
- package/dist/utils/id.d.ts.map +1 -0
- package/dist/utils/id.js +12 -0
- package/dist/utils/id.js.map +1 -0
- package/dist/utils/index.d.ts +13 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +11 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/model-spec.d.ts +110 -0
- package/dist/utils/model-spec.d.ts.map +1 -0
- package/dist/utils/model-spec.js +149 -0
- package/dist/utils/model-spec.js.map +1 -0
- package/dist/utils/observable.d.ts +54 -0
- package/dist/utils/observable.d.ts.map +1 -0
- package/dist/utils/observable.js +82 -0
- package/dist/utils/observable.js.map +1 -0
- package/dist/utils/provider-config.d.ts +10 -0
- package/dist/utils/provider-config.d.ts.map +1 -0
- package/dist/utils/provider-config.js +10 -0
- package/dist/utils/provider-config.js.map +1 -0
- package/dist/utils/restart-analysis.d.ts +202 -0
- package/dist/utils/restart-analysis.d.ts.map +1 -0
- package/dist/utils/restart-analysis.js +426 -0
- package/dist/utils/restart-analysis.js.map +1 -0
- package/dist/utils/session-serialization.d.ts +118 -0
- package/dist/utils/session-serialization.d.ts.map +1 -0
- package/dist/utils/session-serialization.js +217 -0
- package/dist/utils/session-serialization.js.map +1 -0
- package/dist/utils/workflow-error-utils.d.ts +22 -0
- package/dist/utils/workflow-error-utils.d.ts.map +1 -0
- package/dist/utils/workflow-error-utils.js +45 -0
- package/dist/utils/workflow-error-utils.js.map +1 -0
- package/package.json +34 -5
- package/.claude/commands/subtask-planning/prp-base-create.md +0 -120
- package/.claude/commands/subtask-planning/prp-base-execute.md +0 -65
- package/.claude/commands/task-breakdown.md +0 -94
- package/.claude/settings.local.json +0 -9
- package/.claude/system_prompts/task-breakdown.md +0 -101
- package/CHANGELOG.md +0 -188
- package/PRD.md +0 -543
- package/PRPs/001-hierarchical-workflow-engine.md +0 -2438
- package/PRPs/PRDs/002-agent-prompt.md +0 -390
- package/PRPs/PRDs/003-agent-prompt.md +0 -943
- package/PRPs/PRDs/004-agent-prompt.md +0 -1136
- package/PRPs/PRDs/tasks-001.json +0 -492
- package/PRPs/README.md +0 -83
- package/PRPs/templates/prp_base.md +0 -222
- package/docs/agent.md +0 -422
- package/docs/prompt.md +0 -419
- package/docs/workflow.md +0 -600
- package/examples/README.md +0 -258
- package/examples/examples/01-basic-workflow.ts +0 -100
- package/examples/examples/02-decorator-options.ts +0 -217
- package/examples/examples/03-parent-child.ts +0 -241
- package/examples/examples/04-observers-debugger.ts +0 -340
- package/examples/examples/05-error-handling.ts +0 -387
- package/examples/examples/06-concurrent-tasks.ts +0 -352
- package/examples/examples/07-agent-loops.ts +0 -432
- package/examples/examples/08-sdk-features.ts +0 -667
- package/examples/examples/09-reflection.ts +0 -573
- package/examples/examples/10-introspection.ts +0 -550
- package/examples/examples/11-reparenting-workflows.ts +0 -269
- package/examples/index.ts +0 -147
- package/examples/utils/helpers.ts +0 -57
- package/package-lock.json +0 -2398
- package/plan/001_d3bb02af4886/TEST_RESULTS.md +0 -259
- package/plan/001_d3bb02af4886/backlog.json +0 -867
- package/plan/001_d3bb02af4886/bug_fix_tasks.json +0 -484
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M1T1S1/PRP.md +0 -488
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M1T1S2/PRP.md +0 -581
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M1T1S3/PRP.md +0 -687
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S1/PRP.md +0 -492
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S3/PRP.md +0 -932
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S3/research/concurrent_error_testing_patterns.md +0 -1109
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S3/research/vitest_concurrent_testing.md +0 -802
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S3/research/workflow_engine_test_references.md +0 -603
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T2S1/PRP.md +0 -564
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T2S3/PRP.md +0 -518
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T2S4/PRP.md +0 -1252
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/PRP.md +0 -364
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/research/CODEBASE_INVENTORY.md +0 -114
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/research/DECORATOR_DOCUMENTATION_PATTERNS.md +0 -205
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/research/PRD_LOCATION_ANALYSIS.md +0 -199
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/research/ULTRATHINK_PRP_PLAN.md +0 -134
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T1S1/PRP.md +0 -495
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T1S1/research/console_error_inventory.md +0 -435
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T1S2/PRP.md +0 -506
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T1S3/PRP.md +0 -612
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T2S2/PRP.md +0 -558
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T2S2/research/external_research.md +0 -788
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T3S2/PRP.md +0 -460
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T3S3/PRP.md +0 -454
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S1/PRP.md +0 -520
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S1/RECOMMENDATION.md +0 -417
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S1/research/external_workflow_engines_research.md +0 -760
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S1/research/security_implications_analysis.md +0 -245
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S2/PRP.md +0 -792
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S1/PRP.md +0 -535
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S1/TEST_EXECUTION_REPORT.md +0 -190
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/PRP.md +0 -654
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/TEST_FIX_REPORT.md +0 -227
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/research/KEY_FINDINGS.md +0 -345
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/research/QUICK_REFERENCE.md +0 -193
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/research/test_maintenance_research.md +0 -1323
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T3S1/BREAKING_CHANGES_AUDIT.md +0 -1011
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T3S1/PRP.md +0 -927
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T3S2/PRP.md +0 -505
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/architecture/logger_child_signature_analysis.md +0 -401
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S3/child_implementation_research.md +0 -142
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S3/test_patterns_research.md +0 -112
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S3/vitest_patterns_research.md +0 -159
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S4/PRP.md +0 -549
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S4/VERIFICATION_REPORT.md +0 -368
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S4/edge_case_analysis.md +0 -172
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S4/usage_inventory.md +0 -175
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T1S2/PRP.md +0 -696
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T1S4/PRP.md +0 -860
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/PRP.md +0 -1066
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/01-testing-aggregated-errors.md +0 -1103
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/01_typescript_error_aggregation_patterns.md +0 -789
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/02-error-merge-strategy-testing-guide.md +0 -1098
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/02_aggregate_error_patterns.md +0 -1037
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/03-promise-allsettled-testing-patterns.md +0 -916
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/03_error_merging_strategies.md +0 -1045
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/04_github_stackoverflow_examples.md +0 -890
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/05_comprehensive_summary.md +0 -822
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/INDEX.md +0 -668
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/QUICK_REFERENCE.md +0 -706
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/README.md +0 -265
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/RESEARCH_REPORT.md +0 -655
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S4/research/vitest_testing_patterns.md +0 -1103
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T3S2/PRP.md +0 -426
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S2/PRP.md +0 -506
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S2/research/QUICK_REFERENCE.md +0 -114
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S2/research/RESEARCH_SUMMARY.md +0 -316
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S2/research/vitest_observer_error_logging_best_practices.md +0 -754
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S3/PRP.md +0 -612
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T2S1/PRP.md +0 -719
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T2S1/README.md +0 -215
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T2S1/analysis.md +0 -765
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T2S3/PRP.md +0 -718
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/DECISION.md +0 -149
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/PRP.md +0 -470
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/research/ULTRATHINK_PLAN.md +0 -332
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/research/codebase_workflow_name_analysis.md +0 -167
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/research/external_best_practices.md +0 -265
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/research/validation_patterns.md +0 -273
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T4S1/workflow_engine_ancestry_api_research.md +0 -760
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T4S3-PRP.md +0 -434
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M4T2S1/PRP.md +0 -717
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M4T2S2/PRP.md +0 -472
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M4T2S2/VALIDATION_REPORT.md +0 -125
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M4T2S2/research/ULTRATHINK_PRP_PLAN.md +0 -301
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/error-logging-best-practices.md +0 -1170
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/research_typescript_partial_and_overloads.md +0 -940
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/vitest-quick-reference.md +0 -151
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/vitest-research.md +0 -650
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/prd_snapshot.md +0 -259
- package/plan/001_d3bb02af4886/bugfix/P1M1T1S1/PRP.md +0 -457
- package/plan/001_d3bb02af4886/bugfix/RESEARCH_SUMMARY.md +0 -346
- package/plan/001_d3bb02af4886/bugfix/architecture/codebase_structure.md +0 -311
- package/plan/001_d3bb02af4886/bugfix/architecture/concurrent_execution_best_practices.md +0 -1565
- package/plan/001_d3bb02af4886/bugfix/architecture/error_handling_patterns.md +0 -288
- package/plan/001_d3bb02af4886/bugfix/architecture/promise_all_analysis.md +0 -741
- package/plan/001_d3bb02af4886/docs/PRP/P1M1T1S4-functional-workflow-error-state-capture-test.md +0 -652
- package/plan/001_d3bb02af4886/docs/PRP/P1P2-PRP.md +0 -527
- package/plan/001_d3bb02af4886/docs/PRP/P3P4-PRP.md +0 -1388
- package/plan/001_d3bb02af4886/docs/PRP/P4P5-PRP.md +0 -1136
- package/plan/001_d3bb02af4886/docs/PRP/PRP.md +0 -527
- package/plan/001_d3bb02af4886/docs/PRP/bugfix/P1M1T2S1-PRP.md +0 -415
- package/plan/001_d3bb02af4886/docs/PRP/bugfix/P1M1T2S2-PRP.md +0 -378
- package/plan/001_d3bb02af4886/docs/PRP/bugfix/P1M1T2S4-PRP.md +0 -713
- package/plan/001_d3bb02af4886/docs/PRP/bugfix/P1M2T1S4-PRP.md +0 -370
- package/plan/001_d3bb02af4886/docs/PRP_P1M3T1S3.md +0 -499
- package/plan/001_d3bb02af4886/docs/TEST_RESULTS.md +0 -230
- package/plan/001_d3bb02af4886/docs/architecture/external_deps.md +0 -358
- package/plan/001_d3bb02af4886/docs/architecture/system_context.md +0 -242
- package/plan/001_d3bb02af4886/docs/bugfix/ANALYSIS_PRD_VS_IMPLEMENTATION.md +0 -1134
- package/plan/001_d3bb02af4886/docs/bugfix/GAP_ANALYSIS_SUMMARY.md +0 -179
- package/plan/001_d3bb02af4886/docs/bugfix/P1M4T2S1/PRP.md +0 -629
- package/plan/001_d3bb02af4886/docs/bugfix/P1M4T2S1/validation-report.md +0 -214
- package/plan/001_d3bb02af4886/docs/bugfix/PRP_P1M4T2S3.md +0 -629
- package/plan/001_d3bb02af4886/docs/bugfix/bugfix_PRP.md +0 -529
- package/plan/001_d3bb02af4886/docs/bugfix/bugfix_QUICK_REFERENCE.md +0 -142
- package/plan/001_d3bb02af4886/docs/bugfix/bugfix_README.md +0 -304
- package/plan/001_d3bb02af4886/docs/bugfix/bugfix_TEST_RESULTS.md +0 -558
- package/plan/001_d3bb02af4886/docs/bugfix/bugfix_VALIDATION_SUMMARY.md +0 -256
- package/plan/001_d3bb02af4886/docs/bugfix/system_context.md +0 -346
- package/plan/001_d3bb02af4886/docs/bugfix-architecture/bug_analysis.md +0 -415
- package/plan/001_d3bb02af4886/docs/bugfix-architecture/implementation_patterns.md +0 -489
- package/plan/001_d3bb02af4886/docs/bugfix-architecture/system_context.md +0 -218
- package/plan/001_d3bb02af4886/docs/bugfix_INITIATION_SUMMARY.md +0 -380
- package/plan/001_d3bb02af4886/docs/research/CYCLE_DETECTION_PATTERNS.md +0 -1923
- package/plan/001_d3bb02af4886/docs/research/CYCLE_DETECTION_QUICK_REF.md +0 -319
- package/plan/001_d3bb02af4886/docs/research/P1M1T2S1/codebase-context.md +0 -115
- package/plan/001_d3bb02af4886/docs/research/P1M1T2S1/cycle-detection-algorithms.md +0 -134
- package/plan/001_d3bb02af4886/docs/research/P1M1T2S1/test-patterns.md +0 -153
- package/plan/001_d3bb02af4886/docs/research/P1M1T2S1/workflow-class.md +0 -132
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/DECORATOR_DOCUMENTATION_BEST_PRACTICES.md +0 -716
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/DECORATOR_DOCUMENTATION_QUICK_REF.md +0 -186
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/GROUNDSWELL_DECORATOR_EXAMPLES.md +0 -604
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/INDEX.md +0 -213
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/codebase_structure.md +0 -30
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/existing_test_pattern.md +0 -56
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/getRootObservers_implementation.md +0 -53
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/test_conventions.md +0 -49
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/PRP.md +0 -958
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/QUICK_REFERENCE.md +0 -339
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/README.md +0 -305
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/SUMMARY.md +0 -433
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/bidirectional-tree-consistency-testing.md +0 -1574
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/test-pattern-examples.md +0 -1014
- package/plan/001_d3bb02af4886/docs/research/P1P2/LRU_CACHE_BEST_PRACTICES.md +0 -1929
- package/plan/001_d3bb02af4886/docs/research/P1P2/LRU_CACHE_CODE_PATTERNS.md +0 -857
- package/plan/001_d3bb02af4886/docs/research/P1P2/LRU_CACHE_INTEGRATION_GUIDE.md +0 -738
- package/plan/001_d3bb02af4886/docs/research/P1P2/LRU_CACHE_RESEARCH_INDEX.md +0 -424
- package/plan/001_d3bb02af4886/docs/research/P1P2/REFLECTION_INDEX.md +0 -291
- package/plan/001_d3bb02af4886/docs/research/P1P2/REFLECTION_RESEARCH_REPORT.md +0 -1342
- package/plan/001_d3bb02af4886/docs/research/P1P2/RESEARCH_SUMMARY.md +0 -342
- package/plan/001_d3bb02af4886/docs/research/P1P2/anthropic-sdk.md +0 -174
- package/plan/001_d3bb02af4886/docs/research/P1P2/async-local-storage.md +0 -200
- package/plan/001_d3bb02af4886/docs/research/P1P2/reflection-code-patterns.md +0 -1205
- package/plan/001_d3bb02af4886/docs/research/P1P2/reflection-decision-matrix.md +0 -421
- package/plan/001_d3bb02af4886/docs/research/P1P2/reflection-implementation-guide.md +0 -1341
- package/plan/001_d3bb02af4886/docs/research/P1P2/reflection-integration-guide.md +0 -834
- package/plan/001_d3bb02af4886/docs/research/P1P2/reflection-patterns.md +0 -1468
- package/plan/001_d3bb02af4886/docs/research/P1P2/reflection-quick-reference.md +0 -558
- package/plan/001_d3bb02af4886/docs/research/P1P2/zod-schema.md +0 -152
- package/plan/001_d3bb02af4886/docs/research/P3P4/caching-lru.md +0 -116
- package/plan/001_d3bb02af4886/docs/research/P3P4/introspection-tools.md +0 -177
- package/plan/001_d3bb02af4886/docs/research/P3P4/reflection-patterns.md +0 -117
- package/plan/001_d3bb02af4886/docs/research/P4P5/RESEARCH_SUMMARY.md +0 -151
- package/plan/001_d3bb02af4886/docs/research/PROMISE_ALLSETTLED_QUICK_REF.md +0 -376
- package/plan/001_d3bb02af4886/docs/research/PROMISE_ALLSETTLED_RESEARCH.md +0 -1507
- package/plan/001_d3bb02af4886/docs/research/bugfix_typescript_patterns.md +0 -949
- package/plan/001_d3bb02af4886/docs/research/error-testing-research.md +0 -619
- package/plan/001_d3bb02af4886/docs/research/error_handling_patterns.md +0 -723
- package/plan/001_d3bb02af4886/docs/research/general/INTROSPECTION_RESEARCH_SUMMARY.md +0 -378
- package/plan/001_d3bb02af4886/docs/research/general/README-INTROSPECTION.md +0 -352
- package/plan/001_d3bb02af4886/docs/research/general/agent-introspection-patterns.md +0 -1085
- package/plan/001_d3bb02af4886/docs/research/general/introspection-security-guide.md +0 -984
- package/plan/001_d3bb02af4886/docs/research/general/introspection-tool-examples.md +0 -875
- package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/PRP_TEMPLATE.md +0 -460
- package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/QUICK_REFERENCE.md +0 -324
- package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/README.md +0 -175
- package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/RESEARCH_REPORT.md +0 -499
- package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/SUMMARY.md +0 -163
- package/plan/001_d3bb02af4886/prd_snapshot.md +0 -543
- package/plan/bugfix/BUG_FIX_SUMMARY.md +0 -961
- package/scripts/generate-llms-full.ts +0 -206
- package/src/__tests__/adversarial/attachChild-performance.test.ts +0 -216
- package/src/__tests__/adversarial/circular-reference.test.ts +0 -101
- package/src/__tests__/adversarial/complex-circular-reference.test.ts +0 -139
- package/src/__tests__/adversarial/concurrent-task-failures.test.ts +0 -571
- package/src/__tests__/adversarial/deep-analysis.test.ts +0 -729
- package/src/__tests__/adversarial/deep-hierarchy-stress.test.ts +0 -213
- package/src/__tests__/adversarial/e2e-prd-validation.test.ts +0 -448
- package/src/__tests__/adversarial/edge-case.test.ts +0 -703
- package/src/__tests__/adversarial/error-merge-strategy.test.ts +0 -760
- package/src/__tests__/adversarial/incremental-performance.test.ts +0 -140
- package/src/__tests__/adversarial/node-map-update-benchmarks.test.ts +0 -457
- package/src/__tests__/adversarial/observer-propagation.test.ts +0 -487
- package/src/__tests__/adversarial/parent-validation.test.ts +0 -143
- package/src/__tests__/adversarial/prd-12-2-compliance.test.ts +0 -611
- package/src/__tests__/adversarial/prd-compliance.test.ts +0 -731
- package/src/__tests__/compatibility/backward-compatibility.test.ts +0 -1572
- package/src/__tests__/helpers/index.ts +0 -18
- package/src/__tests__/helpers/tree-verification.ts +0 -257
- package/src/__tests__/integration/agent-workflow.test.ts +0 -256
- package/src/__tests__/integration/bidirectional-consistency.test.ts +0 -847
- package/src/__tests__/integration/observer-logging.test.ts +0 -643
- package/src/__tests__/integration/tree-mirroring.test.ts +0 -151
- package/src/__tests__/integration/workflow-reparenting.test.ts +0 -303
- package/src/__tests__/unit/agent.test.ts +0 -169
- package/src/__tests__/unit/cache-key.test.ts +0 -182
- package/src/__tests__/unit/cache.test.ts +0 -172
- package/src/__tests__/unit/context.test.ts +0 -217
- package/src/__tests__/unit/decorators.test.ts +0 -100
- package/src/__tests__/unit/introspection-tools.test.ts +0 -277
- package/src/__tests__/unit/logger.test.ts +0 -293
- package/src/__tests__/unit/observable.test.ts +0 -321
- package/src/__tests__/unit/prompt.test.ts +0 -135
- package/src/__tests__/unit/reflection.test.ts +0 -210
- package/src/__tests__/unit/tree-debugger-incremental.test.ts +0 -170
- package/src/__tests__/unit/tree-debugger.test.ts +0 -85
- package/src/__tests__/unit/utils/workflow-error-utils.test.ts +0 -209
- package/src/__tests__/unit/workflow-detachChild.test.ts +0 -100
- package/src/__tests__/unit/workflow-emitEvent-childDetached.test.ts +0 -153
- package/src/__tests__/unit/workflow-isDescendantOf.test.ts +0 -180
- package/src/__tests__/unit/workflow.test.ts +0 -357
- package/src/cache/cache-key.ts +0 -244
- package/src/cache/cache.ts +0 -236
- package/src/core/agent.ts +0 -593
- package/src/core/event-tree.ts +0 -260
- package/src/core/logger.ts +0 -112
- package/src/core/mcp-handler.ts +0 -184
- package/src/core/prompt.ts +0 -150
- package/src/core/workflow-context.ts +0 -351
- package/src/core/workflow.ts +0 -540
- package/src/debugger/tree-debugger.ts +0 -255
- package/src/decorators/observed-state.ts +0 -95
- package/src/decorators/step.ts +0 -139
- package/src/decorators/task.ts +0 -159
- package/src/examples/tdd-orchestrator.ts +0 -65
- package/src/examples/test-cycle-workflow.ts +0 -64
- package/src/index.ts +0 -142
- package/src/reflection/reflection.ts +0 -407
- package/src/tools/index.ts +0 -36
- package/src/tools/introspection.ts +0 -464
- package/src/types/agent.ts +0 -90
- package/src/types/decorators.ts +0 -32
- package/src/types/error-strategy.ts +0 -13
- package/src/types/error.ts +0 -20
- package/src/types/events.ts +0 -75
- package/src/types/index.ts +0 -55
- package/src/types/logging.ts +0 -24
- package/src/types/observer.ts +0 -18
- package/src/types/prompt.ts +0 -40
- package/src/types/reflection.ts +0 -117
- package/src/types/sdk-primitives.ts +0 -128
- package/src/types/workflow-context.ts +0 -163
- package/src/types/workflow.ts +0 -37
- package/src/utils/id.ts +0 -11
- package/src/utils/index.ts +0 -4
- package/src/utils/observable.ts +0 -106
- package/src/utils/workflow-error-utils.ts +0 -56
- package/tsconfig.json +0 -22
- package/vitest.config.ts +0 -16
package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/bidirectional-tree-consistency-testing.md
DELETED
|
@@ -1,1574 +0,0 @@
|
|
|
1
|
-
# Research: Testing Bidirectional Consistency Between Dual Tree Structures
|
|
2
|
-
|
|
3
|
-
**Research Date:** 2026-01-12
|
|
4
|
-
**Task:** P1M3T1S4 - Research external best practices for testing bidirectional consistency
|
|
5
|
-
**Focus:** Workflow instance tree (parent/children) vs WorkflowNode tree (node.parent/node.children)
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Executive Summary
|
|
10
|
-
|
|
11
|
-
This research document compiles best practices for testing bidirectional consistency between dual tree representations. While web search resources were limited due to service constraints, this document synthesizes patterns from:
|
|
12
|
-
|
|
13
|
-
1. **Existing codebase patterns** (workflow-reparenting.test.ts, tree-mirroring.test.ts, prd-compliance.test.ts)
|
|
14
|
-
2. **Established software engineering principles** for tree data structures
|
|
15
|
-
3. **Testing patterns** from well-known open-source projects
|
|
16
|
-
4. **Academic research** on invariant testing
|
|
17
|
-
|
|
18
|
-
---
|
|
19
|
-
|
|
20
|
-
## Table of Contents
|
|
21
|
-
|
|
22
|
-
1. [Core Testing Patterns](#core-testing-patterns)
|
|
23
|
-
2. [Bidirectional Consistency Validation](#bidirectional-consistency-validation)
|
|
24
|
-
3. [Tree Operation Testing](#tree-operation-testing)
|
|
25
|
-
4. [Invariant Testing Patterns](#invariant-testing-patterns)
|
|
26
|
-
5. [Adversarial Testing Approaches](#adversarial-testing-approaches)
|
|
27
|
-
6. [External Best Practices](#external-best-practices)
|
|
28
|
-
7. [Test Pattern Catalog](#test-pattern-catalog)
|
|
29
|
-
8. [Implementation Checklist](#implementation-checklist)
|
|
30
|
-
|
|
31
|
-
---
|
|
32
|
-
|
|
33
|
-
## 1. Core Testing Patterns
|
|
34
|
-
|
|
35
|
-
### 1.1 The AAA Pattern (Arrange-Act-Assert)
|
|
36
|
-
|
|
37
|
-
**Pattern from:** `/home/dustin/projects/groundswell/src/__tests__/integration/workflow-reparenting.test.ts`
|
|
38
|
-
|
|
39
|
-
```typescript
|
|
40
|
-
describe('Bidirectional Consistency', () => {
|
|
41
|
-
it('should maintain consistency after reparenting', () => {
|
|
42
|
-
// ============================================================
|
|
43
|
-
// PHASE 1: ARRANGE - Set up test data
|
|
44
|
-
// ============================================================
|
|
45
|
-
const parent1 = new SimpleWorkflow('Parent1');
|
|
46
|
-
const parent2 = new SimpleWorkflow('Parent2');
|
|
47
|
-
const child = new SimpleWorkflow('Child', parent1);
|
|
48
|
-
|
|
49
|
-
// ASSERT: Verify initial state
|
|
50
|
-
expect(child.parent).toBe(parent1);
|
|
51
|
-
expect(parent1.children).toContain(child);
|
|
52
|
-
expect(parent2.children).not.toContain(child);
|
|
53
|
-
|
|
54
|
-
// ============================================================
|
|
55
|
-
// PHASE 2: ACT - Execute the behavior
|
|
56
|
-
// ============================================================
|
|
57
|
-
parent1.detachChild(child);
|
|
58
|
-
parent2.attachChild(child);
|
|
59
|
-
|
|
60
|
-
// ============================================================
|
|
61
|
-
// PHASE 3: ASSERT - Verify the result
|
|
62
|
-
// ============================================================
|
|
63
|
-
// Workflow tree state
|
|
64
|
-
expect(child.parent).toBe(parent2);
|
|
65
|
-
expect(parent2.children).toContain(child);
|
|
66
|
-
expect(parent1.children).not.toContain(child);
|
|
67
|
-
|
|
68
|
-
// Node tree state (dual tree mirror)
|
|
69
|
-
expect(child.node.parent).toBe(parent2.node);
|
|
70
|
-
expect(parent2.node.children).toContain(child.node);
|
|
71
|
-
expect(parent1.node.children).not.toContain(child.node);
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
**Key Principles:**
|
|
77
|
-
- Clear phase separation with comments
|
|
78
|
-
- Multiple assertions per phase
|
|
79
|
-
- Cross-tree verification (workflow tree + node tree)
|
|
80
|
-
- State verification before and after operations
|
|
81
|
-
|
|
82
|
-
### 1.2 Test Naming Conventions
|
|
83
|
-
|
|
84
|
-
**Pattern from:** `/home/dustin/projects/groundswell/src/__tests__/adversarial/prd-compliance.test.ts`
|
|
85
|
-
|
|
86
|
-
```typescript
|
|
87
|
-
// Good: Descriptive, action-oriented
|
|
88
|
-
describe('PRD: Perfect 1:1 Tree Mirror Requirement', () => {
|
|
89
|
-
it('should maintain perfect tree structure in logs and events', async () => {
|
|
90
|
-
// Test implementation
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
// Good: Specific operation tested
|
|
95
|
-
describe('Tree Integrity Tests', () => {
|
|
96
|
-
it('should prevent attaching child with existing parent', () => {
|
|
97
|
-
// Test implementation
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it('should allow attaching child with null parent', () => {
|
|
101
|
-
// Test implementation
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
**Naming Template:**
|
|
107
|
-
- `should [expected behavior]` - for positive cases
|
|
108
|
-
- `should prevent [invalid behavior]` - for negative cases
|
|
109
|
-
- `should maintain [invariant] after [operation]` - for consistency tests
|
|
110
|
-
|
|
111
|
-
---
|
|
112
|
-
|
|
113
|
-
## 2. Bidirectional Consistency Validation
|
|
114
|
-
|
|
115
|
-
### 2.1 The 1:1 Tree Mirror Invariant
|
|
116
|
-
|
|
117
|
-
**Critical Pattern from:** `/home/dustin/projects/groundswell/src/__tests__/integration/workflow-reparenting.test.ts:280-302`
|
|
118
|
-
|
|
119
|
-
This is the **core invariant** that must always be maintained:
|
|
120
|
-
|
|
121
|
-
```typescript
|
|
122
|
-
describe('CRITICAL VALIDATION: 1:1 Tree Mirror Invariant', () => {
|
|
123
|
-
it('should verify perfect synchronization between trees', () => {
|
|
124
|
-
const parent = new SimpleWorkflow('Parent');
|
|
125
|
-
const child = new SimpleWorkflow('Child', parent);
|
|
126
|
-
|
|
127
|
-
// ============================================================
|
|
128
|
-
// INVARIANT CHECK: For every relationship in workflow tree,
|
|
129
|
-
// there must be an equivalent relationship in node tree
|
|
130
|
-
// ============================================================
|
|
131
|
-
|
|
132
|
-
// Workflow tree state:
|
|
133
|
-
// - child.parent points to parent
|
|
134
|
-
expect(child.parent).toBe(parent);
|
|
135
|
-
|
|
136
|
-
// - parent.children contains child
|
|
137
|
-
expect(parent.children).toEqual([child]);
|
|
138
|
-
|
|
139
|
-
// Node tree state (MUST mirror workflow tree exactly):
|
|
140
|
-
// - child.node.parent points to parent.node
|
|
141
|
-
expect(child.node.parent).toBe(parent.node);
|
|
142
|
-
|
|
143
|
-
// - parent.node.children contains child.node
|
|
144
|
-
expect(parent.node.children).toEqual([child.node]);
|
|
145
|
-
|
|
146
|
-
// ============================================================
|
|
147
|
-
// CROSS-VERIFICATION: Debugger lookup matches direct access
|
|
148
|
-
// ============================================================
|
|
149
|
-
const debugger = new WorkflowTreeDebugger(parent);
|
|
150
|
-
|
|
151
|
-
// Debugger should see same structure
|
|
152
|
-
expect(debugger.getNode(child.id)).toBe(child.node);
|
|
153
|
-
expect(debugger.getNode(parent.id)).toBe(parent.node);
|
|
154
|
-
});
|
|
155
|
-
});
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
### 2.2 Bidirectional Reference Verification Pattern
|
|
159
|
-
|
|
160
|
-
**Purpose:** Ensure parent→child and child→parent links are mutually consistent
|
|
161
|
-
|
|
162
|
-
```typescript
|
|
163
|
-
describe('Bidirectional Reference Verification', () => {
|
|
164
|
-
/**
|
|
165
|
-
* Core invariant: If A is B's parent, then:
|
|
166
|
-
* 1. B must be in A's children list
|
|
167
|
-
* 2. A must be B's parent reference
|
|
168
|
-
* 3. Both must be true in BOTH trees (workflow + node)
|
|
169
|
-
*/
|
|
170
|
-
function verifyBidirectionalLink(
|
|
171
|
-
parent: Workflow,
|
|
172
|
-
child: Workflow
|
|
173
|
-
): void {
|
|
174
|
-
// Workflow tree checks
|
|
175
|
-
expect(child.parent).toBe(parent);
|
|
176
|
-
expect(parent.children).toContain(child);
|
|
177
|
-
|
|
178
|
-
// Node tree checks (must mirror workflow tree)
|
|
179
|
-
expect(child.node.parent).toBe(parent.node);
|
|
180
|
-
expect(parent.node.children).toContain(child.node);
|
|
181
|
-
|
|
182
|
-
// Verify no orphaned references
|
|
183
|
-
expect(child.node.parent).toBeDefined();
|
|
184
|
-
expect(parent.node.children).toHaveLengthGreaterThan(0);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
it('should maintain bidirectional consistency after attach', () => {
|
|
188
|
-
const parent = new SimpleWorkflow('Parent');
|
|
189
|
-
const child = new SimpleWorkflow('Child'); // No parent
|
|
190
|
-
|
|
191
|
-
parent.attachChild(child);
|
|
192
|
-
|
|
193
|
-
verifyBidirectionalLink(parent, child);
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
it('should maintain bidirectional consistency after detach', () => {
|
|
197
|
-
const parent = new SimpleWorkflow('Parent');
|
|
198
|
-
const child = new SimpleWorkflow('Child', parent);
|
|
199
|
-
|
|
200
|
-
parent.detachChild(child);
|
|
201
|
-
|
|
202
|
-
// Verify complete detachment
|
|
203
|
-
expect(child.parent).toBeNull();
|
|
204
|
-
expect(parent.children).not.toContain(child);
|
|
205
|
-
|
|
206
|
-
expect(child.node.parent).toBeNull();
|
|
207
|
-
expect(parent.node.children).not.toContain(child.node);
|
|
208
|
-
});
|
|
209
|
-
});
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
### 2.3 Tree-Wide Consistency Validation
|
|
213
|
-
|
|
214
|
-
**Purpose:** Verify consistency across entire tree hierarchy
|
|
215
|
-
|
|
216
|
-
```typescript
|
|
217
|
-
describe('Tree-Wide Consistency', () => {
|
|
218
|
-
/**
|
|
219
|
-
* Validates entire tree structure for bidirectional consistency
|
|
220
|
-
* Returns array of inconsistencies found
|
|
221
|
-
*/
|
|
222
|
-
function validateTreeConsistency(root: Workflow): string[] {
|
|
223
|
-
const inconsistencies: string[] = [];
|
|
224
|
-
const visited = new Set<Workflow>();
|
|
225
|
-
|
|
226
|
-
function traverse(node: Workflow, depth: number = 0): void {
|
|
227
|
-
if (visited.has(node)) {
|
|
228
|
-
inconsistencies.push(`Circular reference at ${node.node.name}`);
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
|
-
visited.add(node);
|
|
232
|
-
|
|
233
|
-
// Check parent→child consistency
|
|
234
|
-
if (node.parent) {
|
|
235
|
-
if (!node.parent.children.includes(node)) {
|
|
236
|
-
inconsistencies.push(
|
|
237
|
-
`Orphaned child: ${node.node.name} not in parent's children`
|
|
238
|
-
);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// Check child→parent consistency
|
|
243
|
-
node.children.forEach(child => {
|
|
244
|
-
if (child.parent !== node) {
|
|
245
|
-
inconsistencies.push(
|
|
246
|
-
`Mismatched parent: ${child.node.name}.parent !== ${node.node.name}`
|
|
247
|
-
);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// Check node tree mirrors workflow tree
|
|
251
|
-
if (child.node.parent !== node.node) {
|
|
252
|
-
inconsistencies.push(
|
|
253
|
-
`Node tree mismatch: ${child.node.name}.node.parent !== ${node.node.name}.node`
|
|
254
|
-
);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
if (!node.node.children.includes(child.node)) {
|
|
258
|
-
inconsistencies.push(
|
|
259
|
-
`Node tree orphan: ${child.node.name}.node not in parent's node.children`
|
|
260
|
-
);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
traverse(child, depth + 1);
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
traverse(root);
|
|
268
|
-
return inconsistencies;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
it('should maintain consistency across entire tree', () => {
|
|
272
|
-
const root = new SimpleWorkflow('Root');
|
|
273
|
-
const child1 = new SimpleWorkflow('Child1', root);
|
|
274
|
-
const child2 = new SimpleWorkflow('Child2', root);
|
|
275
|
-
const grandchild = new SimpleWorkflow('Grandchild', child1);
|
|
276
|
-
|
|
277
|
-
const inconsistencies = validateTreeConsistency(root);
|
|
278
|
-
|
|
279
|
-
expect(inconsistencies).toEqual([]);
|
|
280
|
-
expect(inconsistencies.length).toBe(0);
|
|
281
|
-
});
|
|
282
|
-
});
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
---
|
|
286
|
-
|
|
287
|
-
## 3. Tree Operation Testing
|
|
288
|
-
|
|
289
|
-
### 3.1 Attach Operation Testing
|
|
290
|
-
|
|
291
|
-
**Pattern from:** `/home/dustin/projects/groundswell/plan/docs/bugfix-architecture/implementation_patterns.md:274-313`
|
|
292
|
-
|
|
293
|
-
```typescript
|
|
294
|
-
describe('attachChild() Bidirectional Consistency', () => {
|
|
295
|
-
describe('Positive Cases', () => {
|
|
296
|
-
it('should synchronize both trees on successful attach', () => {
|
|
297
|
-
const parent = new SimpleWorkflow('Parent');
|
|
298
|
-
const child = new SimpleWorkflow('Child'); // null parent
|
|
299
|
-
|
|
300
|
-
parent.attachChild(child);
|
|
301
|
-
|
|
302
|
-
// Workflow tree updated
|
|
303
|
-
expect(child.parent).toBe(parent);
|
|
304
|
-
expect(parent.children).toContain(child);
|
|
305
|
-
|
|
306
|
-
// Node tree updated (must match)
|
|
307
|
-
expect(child.node.parent).toBe(parent.node);
|
|
308
|
-
expect(parent.node.children).toContain(child.node);
|
|
309
|
-
|
|
310
|
-
// Verify event emission
|
|
311
|
-
const events = parent.node.events.filter(e => e.type === 'childAttached');
|
|
312
|
-
expect(events.length).toBeGreaterThan(0);
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
it('should handle idempotent attach to same parent', () => {
|
|
316
|
-
const parent = new SimpleWorkflow('Parent');
|
|
317
|
-
const child = new SimpleWorkflow('Child', parent); // Already attached
|
|
318
|
-
|
|
319
|
-
// Should not throw (child.parent === this)
|
|
320
|
-
parent.attachChild(child);
|
|
321
|
-
|
|
322
|
-
// Structure should remain valid
|
|
323
|
-
expect(parent.children).toEqual([child]);
|
|
324
|
-
expect(parent.node.children).toEqual([child.node]);
|
|
325
|
-
});
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
describe('Negative Cases', () => {
|
|
329
|
-
it('should prevent attaching child with different parent', () => {
|
|
330
|
-
const parent1 = new SimpleWorkflow('Parent1');
|
|
331
|
-
const parent2 = new SimpleWorkflow('Parent2');
|
|
332
|
-
const child = new SimpleWorkflow('Child', parent1);
|
|
333
|
-
|
|
334
|
-
// Should throw - child already has parent
|
|
335
|
-
expect(() => parent2.attachChild(child)).toThrow(/already has a parent/);
|
|
336
|
-
|
|
337
|
-
// Trees should remain unchanged
|
|
338
|
-
expect(child.parent).toBe(parent1);
|
|
339
|
-
expect(parent1.children).toContain(child);
|
|
340
|
-
expect(parent2.children).not.toContain(child);
|
|
341
|
-
|
|
342
|
-
expect(child.node.parent).toBe(parent1.node);
|
|
343
|
-
expect(parent1.node.children).toContain(child.node);
|
|
344
|
-
expect(parent2.node.children).not.toContain(child.node);
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
it('should prevent circular references', () => {
|
|
348
|
-
const root = new SimpleWorkflow('Root');
|
|
349
|
-
const child = new SimpleWorkflow('Child', root);
|
|
350
|
-
|
|
351
|
-
// Try to create cycle
|
|
352
|
-
expect(() => {
|
|
353
|
-
child.attachChild(root as any);
|
|
354
|
-
}).toThrow(/circular|cycle/i);
|
|
355
|
-
|
|
356
|
-
// Verify no corruption occurred
|
|
357
|
-
expect(root.parent).toBeNull();
|
|
358
|
-
expect(child.parent).toBe(root);
|
|
359
|
-
});
|
|
360
|
-
|
|
361
|
-
it('should prevent duplicate attachment', () => {
|
|
362
|
-
const parent = new SimpleWorkflow('Parent');
|
|
363
|
-
const child = new SimpleWorkflow('Child', parent);
|
|
364
|
-
|
|
365
|
-
// Already attached - should throw
|
|
366
|
-
expect(() => parent.attachChild(child)).toThrow(/already attached/);
|
|
367
|
-
|
|
368
|
-
// Verify only one reference exists
|
|
369
|
-
const childCount = parent.children.filter(c => c === child).length;
|
|
370
|
-
expect(childCount).toBe(1);
|
|
371
|
-
|
|
372
|
-
const nodeChildCount = parent.node.children.filter(c => c === child.node).length;
|
|
373
|
-
expect(nodeChildCount).toBe(1);
|
|
374
|
-
});
|
|
375
|
-
});
|
|
376
|
-
});
|
|
377
|
-
```
|
|
378
|
-
|
|
379
|
-
### 3.2 Detach Operation Testing
|
|
380
|
-
|
|
381
|
-
```typescript
|
|
382
|
-
describe('detachChild() Bidirectional Consistency', () => {
|
|
383
|
-
it('should remove from both trees completely', () => {
|
|
384
|
-
const parent = new SimpleWorkflow('Parent');
|
|
385
|
-
const child = new SimpleWorkflow('Child', parent);
|
|
386
|
-
|
|
387
|
-
parent.detachChild(child);
|
|
388
|
-
|
|
389
|
-
// Workflow tree: both directions cleared
|
|
390
|
-
expect(child.parent).toBeNull();
|
|
391
|
-
expect(parent.children).not.toContain(child);
|
|
392
|
-
|
|
393
|
-
// Node tree: both directions cleared
|
|
394
|
-
expect(child.node.parent).toBeNull();
|
|
395
|
-
expect(parent.node.children).not.toContain(child.node);
|
|
396
|
-
|
|
397
|
-
// Verify event emission
|
|
398
|
-
const events = parent.node.events.filter(e => e.type === 'childDetached');
|
|
399
|
-
expect(events.length).toBeGreaterThan(0);
|
|
400
|
-
});
|
|
401
|
-
|
|
402
|
-
it('should throw when detaching non-existent child', () => {
|
|
403
|
-
const parent = new SimpleWorkflow('Parent');
|
|
404
|
-
const child = new SimpleWorkflow('Child');
|
|
405
|
-
|
|
406
|
-
expect(() => parent.detachChild(child)).toThrow(/not attached/);
|
|
407
|
-
});
|
|
408
|
-
|
|
409
|
-
it('should handle multiple children correctly', () => {
|
|
410
|
-
const parent = new SimpleWorkflow('Parent');
|
|
411
|
-
const child1 = new SimpleWorkflow('Child1', parent);
|
|
412
|
-
const child2 = new SimpleWorkflow('Child2', parent);
|
|
413
|
-
const child3 = new SimpleWorkflow('Child3', parent);
|
|
414
|
-
|
|
415
|
-
// Detach middle child
|
|
416
|
-
parent.detachChild(child2);
|
|
417
|
-
|
|
418
|
-
// Verify correct structure
|
|
419
|
-
expect(parent.children).toEqual([child1, child3]);
|
|
420
|
-
expect(parent.node.children).toEqual([child1.node, child3.node]);
|
|
421
|
-
|
|
422
|
-
// Verify detached child is orphaned
|
|
423
|
-
expect(child2.parent).toBeNull();
|
|
424
|
-
expect(child2.node.parent).toBeNull();
|
|
425
|
-
|
|
426
|
-
// Verify other children unaffected
|
|
427
|
-
expect(child1.parent).toBe(parent);
|
|
428
|
-
expect(child3.parent).toBe(parent);
|
|
429
|
-
});
|
|
430
|
-
});
|
|
431
|
-
```
|
|
432
|
-
|
|
433
|
-
### 3.3 Reparenting Operation Testing
|
|
434
|
-
|
|
435
|
-
**Pattern from:** `/home/dustin/projects/groundswell/src/__tests__/integration/workflow-reparenting.test.ts:77-127`
|
|
436
|
-
|
|
437
|
-
```typescript
|
|
438
|
-
describe('Reparenting Bidirectional Consistency', () => {
|
|
439
|
-
it('should maintain consistency during reparenting workflow', () => {
|
|
440
|
-
const parent1 = new SimpleWorkflow('Parent1');
|
|
441
|
-
const parent2 = new SimpleWorkflow('Parent2');
|
|
442
|
-
const child = new SimpleWorkflow('Child', parent1);
|
|
443
|
-
|
|
444
|
-
// Verify initial state
|
|
445
|
-
expect(child.parent).toBe(parent1);
|
|
446
|
-
expect(parent1.children).toContain(child);
|
|
447
|
-
expect(child.node.parent).toBe(parent1.node);
|
|
448
|
-
expect(parent1.node.children).toContain(child.node);
|
|
449
|
-
|
|
450
|
-
// Execute reparenting
|
|
451
|
-
parent1.detachChild(child);
|
|
452
|
-
parent2.attachChild(child);
|
|
453
|
-
|
|
454
|
-
// Verify workflow tree updated
|
|
455
|
-
expect(child.parent).toBe(parent2);
|
|
456
|
-
expect(parent2.children).toContain(child);
|
|
457
|
-
expect(parent1.children).not.toContain(child);
|
|
458
|
-
|
|
459
|
-
// Verify node tree updated (MUST match)
|
|
460
|
-
expect(child.node.parent).toBe(parent2.node);
|
|
461
|
-
expect(parent2.node.children).toContain(child.node);
|
|
462
|
-
expect(parent1.node.children).not.toContain(child.node);
|
|
463
|
-
|
|
464
|
-
// Verify no dangling references
|
|
465
|
-
expect(child.node.parent).toBeDefined();
|
|
466
|
-
expect(parent2.node.children).toContain(child.node);
|
|
467
|
-
expect(parent1.node.children).not.toContain(child.node);
|
|
468
|
-
});
|
|
469
|
-
|
|
470
|
-
it('should handle multiple reparenting cycles', () => {
|
|
471
|
-
const parentA = new SimpleWorkflow('ParentA');
|
|
472
|
-
const parentB = new SimpleWorkflow('ParentB');
|
|
473
|
-
const parentC = new SimpleWorkflow('ParentC');
|
|
474
|
-
const child = new SimpleWorkflow('Child', parentA);
|
|
475
|
-
|
|
476
|
-
// Cycle 1: A -> B
|
|
477
|
-
parentA.detachChild(child);
|
|
478
|
-
parentB.attachChild(child);
|
|
479
|
-
expect(child.parent).toBe(parentB);
|
|
480
|
-
expect(child.node.parent).toBe(parentB.node);
|
|
481
|
-
|
|
482
|
-
// Cycle 2: B -> C
|
|
483
|
-
parentB.detachChild(child);
|
|
484
|
-
parentC.attachChild(child);
|
|
485
|
-
expect(child.parent).toBe(parentC);
|
|
486
|
-
expect(child.node.parent).toBe(parentC.node);
|
|
487
|
-
|
|
488
|
-
// Cycle 3: C -> A (back to original)
|
|
489
|
-
parentC.detachChild(child);
|
|
490
|
-
parentA.attachChild(child);
|
|
491
|
-
expect(child.parent).toBe(parentA);
|
|
492
|
-
expect(child.node.parent).toBe(parentA.node);
|
|
493
|
-
});
|
|
494
|
-
});
|
|
495
|
-
```
|
|
496
|
-
|
|
497
|
-
---
|
|
498
|
-
|
|
499
|
-
## 4. Invariant Testing Patterns
|
|
500
|
-
|
|
501
|
-
### 4.1 Structural Invariants
|
|
502
|
-
|
|
503
|
-
**Core invariants that must always hold:**
|
|
504
|
-
|
|
505
|
-
```typescript
|
|
506
|
-
describe('Tree Structural Invariants', () => {
|
|
507
|
-
/**
|
|
508
|
-
* Invariant 1: Acyclicity
|
|
509
|
-
* No cycles exist (every node has exactly one parent except root)
|
|
510
|
-
*/
|
|
511
|
-
it('should maintain acyclicity invariant', () => {
|
|
512
|
-
const root = new SimpleWorkflow('Root');
|
|
513
|
-
const child = new SimpleWorkflow('Child', root);
|
|
514
|
-
|
|
515
|
-
// Try to create cycle
|
|
516
|
-
expect(() => root.attachChild(child as any)).toThrow();
|
|
517
|
-
|
|
518
|
-
// Verify: following parent links eventually reaches null
|
|
519
|
-
let current: Workflow | null = child;
|
|
520
|
-
const visited = new Set<Workflow>();
|
|
521
|
-
|
|
522
|
-
while (current !== null) {
|
|
523
|
-
expect(visited.has(current)).toBe(false); // No cycles
|
|
524
|
-
visited.add(current);
|
|
525
|
-
current = current.parent;
|
|
526
|
-
}
|
|
527
|
-
});
|
|
528
|
-
|
|
529
|
-
/**
|
|
530
|
-
* Invariant 2: Single Root
|
|
531
|
-
* Exactly one root node exists (parent == null)
|
|
532
|
-
*/
|
|
533
|
-
it('should maintain single root invariant', () => {
|
|
534
|
-
const root = new SimpleWorkflow('Root');
|
|
535
|
-
const child1 = new SimpleWorkflow('Child1', root);
|
|
536
|
-
const child2 = new SimpleWorkflow('Child2', root);
|
|
537
|
-
|
|
538
|
-
// Count roots (nodes with null parent)
|
|
539
|
-
const roots = collectAllNodes(root).filter(n => n.parent === null);
|
|
540
|
-
|
|
541
|
-
expect(roots.length).toBe(1);
|
|
542
|
-
expect(roots[0]).toBe(root);
|
|
543
|
-
});
|
|
544
|
-
|
|
545
|
-
/**
|
|
546
|
-
* Invariant 3: Connectedness
|
|
547
|
-
* All nodes are reachable from the root
|
|
548
|
-
*/
|
|
549
|
-
it('should maintain connectedness invariant', () => {
|
|
550
|
-
const root = new SimpleWorkflow('Root');
|
|
551
|
-
const child1 = new SimpleWorkflow('Child1', root);
|
|
552
|
-
const child2 = new SimpleWorkflow('Child2', root);
|
|
553
|
-
const grandchild = new SimpleWorkflow('Grandchild', child1);
|
|
554
|
-
|
|
555
|
-
const allNodes = collectAllNodes(root);
|
|
556
|
-
const reachableFromRoot = new Set<Workflow>();
|
|
557
|
-
|
|
558
|
-
function traverse(node: Workflow): void {
|
|
559
|
-
reachableFromRoot.add(node);
|
|
560
|
-
node.children.forEach(traverse);
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
traverse(root);
|
|
564
|
-
|
|
565
|
-
// All nodes should be reachable
|
|
566
|
-
allNodes.forEach(node => {
|
|
567
|
-
expect(reachableFromRoot.has(node)).toBe(true);
|
|
568
|
-
});
|
|
569
|
-
});
|
|
570
|
-
|
|
571
|
-
/**
|
|
572
|
-
* Invariant 4: Parent-Child Consistency
|
|
573
|
-
* If A is B's parent, then B must be in A's children list
|
|
574
|
-
*/
|
|
575
|
-
it('should maintain parent-child consistency invariant', () => {
|
|
576
|
-
const root = new SimpleWorkflow('Root');
|
|
577
|
-
const child = new SimpleWorkflow('Child', root);
|
|
578
|
-
|
|
579
|
-
// Forward direction: parent knows about child
|
|
580
|
-
expect(root.children).toContain(child);
|
|
581
|
-
|
|
582
|
-
// Reverse direction: child knows about parent
|
|
583
|
-
expect(child.parent).toBe(root);
|
|
584
|
-
|
|
585
|
-
// Both must be true (bidirectional)
|
|
586
|
-
const forward = root.children.includes(child);
|
|
587
|
-
const reverse = child.parent === root;
|
|
588
|
-
|
|
589
|
-
expect(forward && reverse).toBe(true);
|
|
590
|
-
});
|
|
591
|
-
|
|
592
|
-
/**
|
|
593
|
-
* Invariant 5: Tree Mirror Consistency
|
|
594
|
-
* Workflow tree perfectly mirrors node tree
|
|
595
|
-
*/
|
|
596
|
-
it('should maintain tree mirror invariant', () => {
|
|
597
|
-
const root = new SimpleWorkflow('Root');
|
|
598
|
-
const child = new SimpleWorkflow('Child', root);
|
|
599
|
-
|
|
600
|
-
// For every workflow tree relationship, node tree must match
|
|
601
|
-
function verifyMirror(workflowNode: Workflow): void {
|
|
602
|
-
const node = workflowNode.node;
|
|
603
|
-
|
|
604
|
-
// Verify parent relationship
|
|
605
|
-
if (workflowNode.parent) {
|
|
606
|
-
expect(node.parent).toBe(workflowNode.parent.node);
|
|
607
|
-
} else {
|
|
608
|
-
expect(node.parent).toBeNull();
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
// Verify children relationship
|
|
612
|
-
expect(node.children.length).toBe(workflowNode.children.length);
|
|
613
|
-
|
|
614
|
-
workflowNode.children.forEach((childWorkflow, index) => {
|
|
615
|
-
expect(node.children[index]).toBe(childWorkflow.node);
|
|
616
|
-
verifyMirror(childWorkflow);
|
|
617
|
-
});
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
verifyMirror(root);
|
|
621
|
-
});
|
|
622
|
-
});
|
|
623
|
-
```
|
|
624
|
-
|
|
625
|
-
### 4.2 Counting Invariants
|
|
626
|
-
|
|
627
|
-
```typescript
|
|
628
|
-
describe('Counting Invariants', () => {
|
|
629
|
-
/**
|
|
630
|
-
* Invariant: Edge Count
|
|
631
|
-
* Exactly (n-1) edges for n nodes in a tree
|
|
632
|
-
*/
|
|
633
|
-
it('should maintain correct edge count', () => {
|
|
634
|
-
const root = new SimpleWorkflow('Root');
|
|
635
|
-
const child1 = new SimpleWorkflow('Child1', root);
|
|
636
|
-
const child2 = new SimpleWorkflow('Child2', root);
|
|
637
|
-
const grandchild = new SimpleWorkflow('Grandchild', child1);
|
|
638
|
-
|
|
639
|
-
const allNodes = collectAllNodes(root);
|
|
640
|
-
|
|
641
|
-
// Count edges (each child has exactly one parent edge)
|
|
642
|
-
let edgeCount = 0;
|
|
643
|
-
allNodes.forEach(node => {
|
|
644
|
-
if (node.parent !== null) {
|
|
645
|
-
edgeCount++;
|
|
646
|
-
}
|
|
647
|
-
});
|
|
648
|
-
|
|
649
|
-
// Tree with n nodes has exactly n-1 edges
|
|
650
|
-
expect(edgeCount).toBe(allNodes.length - 1);
|
|
651
|
-
});
|
|
652
|
-
|
|
653
|
-
/**
|
|
654
|
-
* Invariant: Node Count Consistency
|
|
655
|
-
* Total nodes = sum of all subtree sizes + 1
|
|
656
|
-
*/
|
|
657
|
-
it('should maintain consistent node counts', () => {
|
|
658
|
-
const root = new SimpleWorkflow('Root');
|
|
659
|
-
const child1 = new SimpleWorkflow('Child1', root);
|
|
660
|
-
const child2 = new SimpleWorkflow('Child2', root);
|
|
661
|
-
|
|
662
|
-
function countDescendants(node: Workflow): number {
|
|
663
|
-
let count = 0;
|
|
664
|
-
node.children.forEach(child => {
|
|
665
|
-
count += 1 + countDescendants(child);
|
|
666
|
-
});
|
|
667
|
-
return count;
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
const totalNodes = collectAllNodes(root).length;
|
|
671
|
-
const descendantCount = countDescendants(root);
|
|
672
|
-
|
|
673
|
-
expect(totalNodes).toBe(descendantCount + 1); // +1 for root itself
|
|
674
|
-
});
|
|
675
|
-
});
|
|
676
|
-
```
|
|
677
|
-
|
|
678
|
-
### 4.3 Depth Invariants
|
|
679
|
-
|
|
680
|
-
```typescript
|
|
681
|
-
describe('Depth Invariants', () => {
|
|
682
|
-
/**
|
|
683
|
-
* Invariant: Depth Consistency
|
|
684
|
-
* Node depth = parent depth + 1
|
|
685
|
-
*/
|
|
686
|
-
it('should maintain consistent depths', () => {
|
|
687
|
-
const root = new SimpleWorkflow('Root');
|
|
688
|
-
const child1 = new SimpleWorkflow('Child1', root);
|
|
689
|
-
const child2 = new SimpleWorkflow('Child2', root);
|
|
690
|
-
const grandchild = new SimpleWorkflow('Grandchild', child1);
|
|
691
|
-
|
|
692
|
-
function getDepth(node: Workflow): number {
|
|
693
|
-
let depth = 0;
|
|
694
|
-
let current: Workflow | null = node;
|
|
695
|
-
|
|
696
|
-
while (current !== null) {
|
|
697
|
-
depth++;
|
|
698
|
-
current = current.parent;
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
return depth - 1; // Subtract 1 because we counted the node itself
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
expect(getDepth(root)).toBe(0);
|
|
705
|
-
expect(getDepth(child1)).toBe(1);
|
|
706
|
-
expect(getDepth(child2)).toBe(1);
|
|
707
|
-
expect(getDepth(grandchild)).toBe(2);
|
|
708
|
-
|
|
709
|
-
// Verify: child depth = parent depth + 1
|
|
710
|
-
expect(getDepth(child1)).toBe(getDepth(root) + 1);
|
|
711
|
-
expect(getDepth(grandchild)).toBe(getDepth(child1) + 1);
|
|
712
|
-
});
|
|
713
|
-
});
|
|
714
|
-
```
|
|
715
|
-
|
|
716
|
-
---
|
|
717
|
-
|
|
718
|
-
## 5. Adversarial Testing Approaches
|
|
719
|
-
|
|
720
|
-
### 5.1 Manual Mutation Tests
|
|
721
|
-
|
|
722
|
-
**Pattern from:** `/home/dustin/projects/groundswell/src/__tests__/adversarial/edge-case.test.ts:490-505`
|
|
723
|
-
|
|
724
|
-
```typescript
|
|
725
|
-
describe('Adversarial: Manual Parent Mutation', () => {
|
|
726
|
-
it('should detect inconsistency from manual parent mutation', () => {
|
|
727
|
-
const parent1 = new SimpleWorkflow('Parent1');
|
|
728
|
-
const parent2 = new SimpleWorkflow('Parent2');
|
|
729
|
-
const child = new SimpleWorkflow('Child', parent1);
|
|
730
|
-
|
|
731
|
-
// Manually mutate parent (bypassing attachChild)
|
|
732
|
-
// This simulates a bug or malicious action
|
|
733
|
-
(child as any).parent = parent2;
|
|
734
|
-
|
|
735
|
-
// Now trees are inconsistent:
|
|
736
|
-
// - workflow tree: child.parent === parent2
|
|
737
|
-
// - parent1.children: still contains child
|
|
738
|
-
// - parent2.children: does NOT contain child
|
|
739
|
-
|
|
740
|
-
// Detect inconsistency
|
|
741
|
-
const inconsistencies = validateTreeConsistency(parent1);
|
|
742
|
-
|
|
743
|
-
expect(inconsistencies.length).toBeGreaterThan(0);
|
|
744
|
-
expect(inconsistencies.some(i => i.includes('Mismatched parent'))).toBe(true);
|
|
745
|
-
});
|
|
746
|
-
|
|
747
|
-
it('should prevent manual parent mutation via TypeScript', () => {
|
|
748
|
-
// TypeScript should prevent this at compile time
|
|
749
|
-
// But if someone uses 'as any', we need runtime checks
|
|
750
|
-
|
|
751
|
-
const parent = new SimpleWorkflow('Parent');
|
|
752
|
-
const child = new SimpleWorkflow('Child', parent);
|
|
753
|
-
|
|
754
|
-
// This should throw in attachChild due to validation
|
|
755
|
-
const parent2 = new SimpleWorkflow('Parent2');
|
|
756
|
-
|
|
757
|
-
expect(() => {
|
|
758
|
-
(child as any).parent = parent2; // Manual mutation
|
|
759
|
-
parent2.attachChild(child); // Should detect and throw
|
|
760
|
-
}).toThrow();
|
|
761
|
-
});
|
|
762
|
-
});
|
|
763
|
-
```
|
|
764
|
-
|
|
765
|
-
### 5.2 Circular Reference Detection
|
|
766
|
-
|
|
767
|
-
**Pattern from:** `/home/dustin/projects/groundswell/plan/docs/research/P1M2T1S4/existing_test_pattern.md:10-24`
|
|
768
|
-
|
|
769
|
-
```typescript
|
|
770
|
-
describe('Adversarial: Circular References', () => {
|
|
771
|
-
it('should detect simple circular reference', () => {
|
|
772
|
-
const parent = new SimpleWorkflow('Parent');
|
|
773
|
-
const child = new SimpleWorkflow('Child', parent);
|
|
774
|
-
|
|
775
|
-
// Create circular reference manually
|
|
776
|
-
parent.parent = child;
|
|
777
|
-
|
|
778
|
-
// getRoot() should detect and throw
|
|
779
|
-
expect(() => (parent as any).getRoot()).toThrow(
|
|
780
|
-
'Circular parent-child relationship detected'
|
|
781
|
-
);
|
|
782
|
-
});
|
|
783
|
-
|
|
784
|
-
it('should detect complex circular references', () => {
|
|
785
|
-
const root = new SimpleWorkflow('Root');
|
|
786
|
-
const child1 = new SimpleWorkflow('Child1', root);
|
|
787
|
-
const child2 = new SimpleWorkflow('Child2', child1);
|
|
788
|
-
const child3 = new SimpleWorkflow('Child3', child2);
|
|
789
|
-
|
|
790
|
-
// Try to create cycle: child3 -> root
|
|
791
|
-
expect(() => {
|
|
792
|
-
child3.attachChild(root as any);
|
|
793
|
-
}).toThrow(/circular|cycle/i);
|
|
794
|
-
});
|
|
795
|
-
|
|
796
|
-
it('should prevent cycles in node tree', () => {
|
|
797
|
-
const root = new SimpleWorkflow('Root');
|
|
798
|
-
const child = new SimpleWorkflow('Child', root);
|
|
799
|
-
|
|
800
|
-
// Try to manually mutate node tree
|
|
801
|
-
expect(() => {
|
|
802
|
-
root.node.parent = child.node;
|
|
803
|
-
}).not.toThrow(); // TypeScript allows with 'as any'
|
|
804
|
-
|
|
805
|
-
// But validation should detect it
|
|
806
|
-
const debugger = new WorkflowTreeDebugger(root);
|
|
807
|
-
|
|
808
|
-
// Try to traverse - should detect cycle
|
|
809
|
-
expect(() => {
|
|
810
|
-
debugger.toTreeString();
|
|
811
|
-
}).toThrow(); // Or handle gracefully
|
|
812
|
-
});
|
|
813
|
-
});
|
|
814
|
-
```
|
|
815
|
-
|
|
816
|
-
### 5.3 Stress Testing
|
|
817
|
-
|
|
818
|
-
**Pattern from:** `/home/dustin/projects/groundswell/src/__tests__/adversarial/edge-case.test.ts:262-295`
|
|
819
|
-
|
|
820
|
-
```typescript
|
|
821
|
-
describe('Adversarial: Stress Tests', () => {
|
|
822
|
-
it('should handle very deep hierarchies', () => {
|
|
823
|
-
let lastWorkflow: Workflow | null = null;
|
|
824
|
-
|
|
825
|
-
// Create 100 levels deep
|
|
826
|
-
for (let i = 0; i < 100; i++) {
|
|
827
|
-
const workflow = new SimpleWorkflow(`Level-${i}`);
|
|
828
|
-
if (lastWorkflow) {
|
|
829
|
-
lastWorkflow.attachChild(workflow);
|
|
830
|
-
}
|
|
831
|
-
lastWorkflow = workflow;
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
// Verify consistency at each level
|
|
835
|
-
let current = lastWorkflow;
|
|
836
|
-
let depth = 0;
|
|
837
|
-
|
|
838
|
-
while (current && current.parent) {
|
|
839
|
-
// Verify bidirectional link at each level
|
|
840
|
-
expect(current.parent.children).toContain(current);
|
|
841
|
-
expect(current.node.parent).toBe(current.parent.node);
|
|
842
|
-
|
|
843
|
-
depth++;
|
|
844
|
-
current = current.parent;
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
expect(depth).toBe(99);
|
|
848
|
-
});
|
|
849
|
-
|
|
850
|
-
it('should handle wide hierarchies (many siblings)', () => {
|
|
851
|
-
const parent = new SimpleWorkflow('Parent');
|
|
852
|
-
const children: Workflow[] = [];
|
|
853
|
-
|
|
854
|
-
// Create 100 children
|
|
855
|
-
for (let i = 0; i < 100; i++) {
|
|
856
|
-
const child = new SimpleWorkflow(`Child-${i}`, parent);
|
|
857
|
-
children.push(child);
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
// Verify all children are correctly attached
|
|
861
|
-
expect(parent.children.length).toBe(100);
|
|
862
|
-
expect(parent.node.children.length).toBe(100);
|
|
863
|
-
|
|
864
|
-
// Verify each child has correct parent
|
|
865
|
-
children.forEach(child => {
|
|
866
|
-
expect(child.parent).toBe(parent);
|
|
867
|
-
expect(child.node.parent).toBe(parent.node);
|
|
868
|
-
expect(parent.children).toContain(child);
|
|
869
|
-
expect(parent.node.children).toContain(child.node);
|
|
870
|
-
});
|
|
871
|
-
});
|
|
872
|
-
|
|
873
|
-
it('should handle rapid attach/detach cycles', () => {
|
|
874
|
-
const parent1 = new SimpleWorkflow('Parent1');
|
|
875
|
-
const parent2 = new SimpleWorkflow('Parent2');
|
|
876
|
-
const child = new SimpleWorkflow('Child', parent1);
|
|
877
|
-
|
|
878
|
-
// Rapid reparenting
|
|
879
|
-
for (let i = 0; i < 100; i++) {
|
|
880
|
-
if (i % 2 === 0) {
|
|
881
|
-
parent1.detachChild(child);
|
|
882
|
-
parent2.attachChild(child);
|
|
883
|
-
} else {
|
|
884
|
-
parent2.detachChild(child);
|
|
885
|
-
parent1.attachChild(child);
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
// Verify consistency after each cycle
|
|
889
|
-
const expectedParent = i % 2 === 0 ? parent2 : parent1;
|
|
890
|
-
expect(child.parent).toBe(expectedParent);
|
|
891
|
-
expect(child.node.parent).toBe(expectedParent.node);
|
|
892
|
-
}
|
|
893
|
-
});
|
|
894
|
-
});
|
|
895
|
-
```
|
|
896
|
-
|
|
897
|
-
---
|
|
898
|
-
|
|
899
|
-
## 6. External Best Practices
|
|
900
|
-
|
|
901
|
-
### 6.1 DOM Tree API Patterns
|
|
902
|
-
|
|
903
|
-
**Reference:** [DOM Specification - Tree Concepts](https://dom.spec.whatwg.org/#concept-tree-parent)
|
|
904
|
-
|
|
905
|
-
The DOM specification provides excellent patterns for tree structure validation:
|
|
906
|
-
|
|
907
|
-
```typescript
|
|
908
|
-
/**
|
|
909
|
-
* Pattern: DOM-style Tree Validation
|
|
910
|
-
* Source: https://dom.spec.whatwg.org/#concept-tree-parent
|
|
911
|
-
*
|
|
912
|
-
* Key principles:
|
|
913
|
-
* 1. Every node (except root) has exactly one parent
|
|
914
|
-
* 2. Parent-child relationships are bidirectional
|
|
915
|
-
* 3. Tree structure is maintained through mutation operations
|
|
916
|
-
*/
|
|
917
|
-
describe('DOM-style Tree Validation', () => {
|
|
918
|
-
it('should follow DOM tree mutation principles', () => {
|
|
919
|
-
const parent = new SimpleWorkflow('Parent');
|
|
920
|
-
const child = new SimpleWorkflow('Child', parent);
|
|
921
|
-
|
|
922
|
-
// DOM principle:appendChild() updates both parent and child
|
|
923
|
-
// Similar to our attachChild()
|
|
924
|
-
const newChild = new SimpleWorkflow('NewChild');
|
|
925
|
-
parent.attachChild(newChild);
|
|
926
|
-
|
|
927
|
-
// Verify: parent knows about child (DOM: children list)
|
|
928
|
-
expect(parent.children.includes(newChild)).toBe(true);
|
|
929
|
-
|
|
930
|
-
// Verify: child knows about parent (DOM: parentNode property)
|
|
931
|
-
expect(newChild.parent).toBe(parent);
|
|
932
|
-
|
|
933
|
-
// Verify: node tree mirrors (DOM: ownerDocument relationship)
|
|
934
|
-
expect(newChild.node.parent).toBe(parent.node);
|
|
935
|
-
});
|
|
936
|
-
|
|
937
|
-
it('should follow DOM removal principles', () => {
|
|
938
|
-
const parent = new SimpleWorkflow('Parent');
|
|
939
|
-
const child = new SimpleWorkflow('Child', parent);
|
|
940
|
-
|
|
941
|
-
// DOM principle: removeChild() updates both parent and child
|
|
942
|
-
parent.detachChild(child);
|
|
943
|
-
|
|
944
|
-
// Verify: parent no longer references child
|
|
945
|
-
expect(parent.children.includes(child)).toBe(false);
|
|
946
|
-
|
|
947
|
-
// Verify: child parent is null (DOM: orphaned node)
|
|
948
|
-
expect(child.parent).toBeNull();
|
|
949
|
-
|
|
950
|
-
// Verify: node tree mirrors
|
|
951
|
-
expect(child.node.parent).toBeNull();
|
|
952
|
-
});
|
|
953
|
-
});
|
|
954
|
-
```
|
|
955
|
-
|
|
956
|
-
### 6.2 React Fiber Tree Patterns
|
|
957
|
-
|
|
958
|
-
**Reference:** [React Fiber Architecture](https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiber.js)
|
|
959
|
-
|
|
960
|
-
React's Fiber tree maintains dual tree representation (current tree and work-in-progress tree):
|
|
961
|
-
|
|
962
|
-
```typescript
|
|
963
|
-
/**
|
|
964
|
-
* Pattern: React-style Dual Tree Consistency
|
|
965
|
-
* Source: React Fiber Architecture
|
|
966
|
-
*
|
|
967
|
-
* Key principles:
|
|
968
|
-
* 1. Dual tree representations must stay in sync
|
|
969
|
-
* 2. Changes are batched and applied atomically
|
|
970
|
-
* 3. Consistency is verified after each operation
|
|
971
|
-
*/
|
|
972
|
-
describe('React Fiber-style Dual Tree', () => {
|
|
973
|
-
it('should maintain dual tree consistency like React Fiber', () => {
|
|
974
|
-
const parent = new SimpleWorkflow('Parent');
|
|
975
|
-
const child = new SimpleWorkflow('Child', parent);
|
|
976
|
-
|
|
977
|
-
// React Fiber: current tree and work-in-progress tree
|
|
978
|
-
// Our equivalent: workflow tree and node tree
|
|
979
|
-
|
|
980
|
-
// Verify both trees represent same structure
|
|
981
|
-
function verifyFiberConsistency(workflow: Workflow): void {
|
|
982
|
-
const fiberNode = workflow.node;
|
|
983
|
-
|
|
984
|
-
// Check: parent pointers match
|
|
985
|
-
if (workflow.parent) {
|
|
986
|
-
expect(fiberNode.parent).toBe(workflow.parent.node);
|
|
987
|
-
}
|
|
988
|
-
|
|
989
|
-
// Check: child arrays match
|
|
990
|
-
expect(fiberNode.children.length).toBe(workflow.children.length);
|
|
991
|
-
|
|
992
|
-
// Check: each child matches
|
|
993
|
-
workflow.children.forEach((childWf, index) => {
|
|
994
|
-
expect(fiberNode.children[index]).toBe(childWf.node);
|
|
995
|
-
verifyFiberConsistency(childWf);
|
|
996
|
-
});
|
|
997
|
-
}
|
|
998
|
-
|
|
999
|
-
verifyFiberConsistency(parent);
|
|
1000
|
-
});
|
|
1001
|
-
|
|
1002
|
-
it('should apply changes atomically to both trees', () => {
|
|
1003
|
-
const parent1 = new SimpleWorkflow('Parent1');
|
|
1004
|
-
const parent2 = new SimpleWorkflow('Parent2');
|
|
1005
|
-
const child = new SimpleWorkflow('Child', parent1);
|
|
1006
|
-
|
|
1007
|
-
// React: changes are batched and committed atomically
|
|
1008
|
-
// Our: attachChild/detachChild update both trees
|
|
1009
|
-
|
|
1010
|
-
// Execute reparenting
|
|
1011
|
-
parent1.detachChild(child);
|
|
1012
|
-
parent2.attachChild(child);
|
|
1013
|
-
|
|
1014
|
-
// Verify: both trees updated atomically
|
|
1015
|
-
// (No intermediate state where trees are inconsistent)
|
|
1016
|
-
|
|
1017
|
-
// Workflow tree state
|
|
1018
|
-
expect(child.parent).toBe(parent2);
|
|
1019
|
-
expect(parent2.children).toContain(child);
|
|
1020
|
-
|
|
1021
|
-
// Node tree state (must match exactly)
|
|
1022
|
-
expect(child.node.parent).toBe(parent2.node);
|
|
1023
|
-
expect(parent2.node.children).toContain(child.node);
|
|
1024
|
-
|
|
1025
|
-
// Verify no intermediate state leaked
|
|
1026
|
-
expect(parent1.children).not.toContain(child);
|
|
1027
|
-
expect(parent1.node.children).not.toContain(child.node);
|
|
1028
|
-
});
|
|
1029
|
-
});
|
|
1030
|
-
```
|
|
1031
|
-
|
|
1032
|
-
### 6.3 Property-Based Testing Patterns
|
|
1033
|
-
|
|
1034
|
-
**Pattern inspired by:** [Hypothesis](https://hypothesis.works/), [QuickCheck](https://www.cs.tufts.edu/~nr/cs257/archive/john-hughes/quick.pdf))
|
|
1035
|
-
|
|
1036
|
-
```typescript
|
|
1037
|
-
/**
|
|
1038
|
-
* Pattern: Property-Based Testing for Tree Invariants
|
|
1039
|
-
* Source: QuickCheck, Hypothesis
|
|
1040
|
-
*
|
|
1041
|
-
* Key principles:
|
|
1042
|
-
* 1. Define invariants as properties
|
|
1043
|
-
* 2. Generate random tree structures
|
|
1044
|
-
* 3. Verify invariants hold for all inputs
|
|
1045
|
-
*/
|
|
1046
|
-
describe('Property-Based Tree Invariants', () => {
|
|
1047
|
-
/**
|
|
1048
|
-
* Property: Tree Round-Trip
|
|
1049
|
-
* If you detach then reattach a child, structure should be valid
|
|
1050
|
-
*/
|
|
1051
|
-
it('should satisfy round-trip property', () => {
|
|
1052
|
-
// Arrange: Create random tree structure
|
|
1053
|
-
const root = new SimpleWorkflow('Root');
|
|
1054
|
-
const child1 = new SimpleWorkflow('Child1', root);
|
|
1055
|
-
const child2 = new SimpleWorkflow('Child2', root);
|
|
1056
|
-
const grandchild = new SimpleWorkflow('Grandchild', child1);
|
|
1057
|
-
|
|
1058
|
-
// Act: Detach and reattach
|
|
1059
|
-
const originalParent = child1.parent;
|
|
1060
|
-
const originalChildren = [...root.children];
|
|
1061
|
-
|
|
1062
|
-
root.detachChild(child1);
|
|
1063
|
-
root.attachChild(child1);
|
|
1064
|
-
|
|
1065
|
-
// Assert: Structure should be consistent
|
|
1066
|
-
expect(child1.parent).toBe(originalParent);
|
|
1067
|
-
expect(root.children).toEqual(originalChildren);
|
|
1068
|
-
|
|
1069
|
-
// Verify both trees
|
|
1070
|
-
expect(child1.node.parent).toBe(originalParent.node);
|
|
1071
|
-
expect(root.node.children).toEqual(originalChildren.map(c => c.node));
|
|
1072
|
-
});
|
|
1073
|
-
|
|
1074
|
-
/**
|
|
1075
|
-
* Property: Idempotence
|
|
1076
|
-
* Calling attachChild twice on same parent should be safe
|
|
1077
|
-
*/
|
|
1078
|
-
it('should satisfy idempotence property', () => {
|
|
1079
|
-
const parent = new SimpleWorkflow('Parent');
|
|
1080
|
-
const child = new SimpleWorkflow('Child', parent);
|
|
1081
|
-
|
|
1082
|
-
// Already attached - should not throw (child.parent === this)
|
|
1083
|
-
parent.attachChild(child);
|
|
1084
|
-
|
|
1085
|
-
// Verify structure unchanged
|
|
1086
|
-
expect(parent.children).toEqual([child]);
|
|
1087
|
-
expect(parent.node.children).toEqual([child.node]);
|
|
1088
|
-
});
|
|
1089
|
-
|
|
1090
|
-
/**
|
|
1091
|
-
* Property: Commutativity (for siblings)
|
|
1092
|
-
* Attaching children in different orders should yield same structure
|
|
1093
|
-
*/
|
|
1094
|
-
it('should satisfy commutativity property for siblings', () => {
|
|
1095
|
-
const parent = new SimpleWorkflow('Parent');
|
|
1096
|
-
|
|
1097
|
-
// Method 1: Attach in order 1, 2, 3
|
|
1098
|
-
const child1 = new SimpleWorkflow('Child1');
|
|
1099
|
-
const child2 = new SimpleWorkflow('Child2');
|
|
1100
|
-
const child3 = new SimpleWorkflow('Child3');
|
|
1101
|
-
|
|
1102
|
-
parent.attachChild(child1);
|
|
1103
|
-
parent.attachChild(child2);
|
|
1104
|
-
parent.attachChild(child3);
|
|
1105
|
-
|
|
1106
|
-
const structure1 = {
|
|
1107
|
-
children: [...parent.children],
|
|
1108
|
-
nodeChildren: [...parent.node.children],
|
|
1109
|
-
};
|
|
1110
|
-
|
|
1111
|
-
// Clean up
|
|
1112
|
-
parent.detachChild(child1);
|
|
1113
|
-
parent.detachChild(child2);
|
|
1114
|
-
parent.detachChild(child3);
|
|
1115
|
-
|
|
1116
|
-
// Method 2: Attach in order 3, 2, 1
|
|
1117
|
-
parent.attachChild(child3);
|
|
1118
|
-
parent.attachChild(child2);
|
|
1119
|
-
parent.attachChild(child1);
|
|
1120
|
-
|
|
1121
|
-
const structure2 = {
|
|
1122
|
-
children: [...parent.children],
|
|
1123
|
-
nodeChildren: [...parent.node.children],
|
|
1124
|
-
};
|
|
1125
|
-
|
|
1126
|
-
// Verify: Same final structure (order may differ)
|
|
1127
|
-
expect(structure1.children.sort()).toEqual(structure2.children.sort());
|
|
1128
|
-
expect(structure1.nodeChildren.sort()).toEqual(structure2.nodeChildren.sort());
|
|
1129
|
-
});
|
|
1130
|
-
});
|
|
1131
|
-
```
|
|
1132
|
-
|
|
1133
|
-
### 6.4 Academic Research Patterns
|
|
1134
|
-
|
|
1135
|
-
**Pattern from:** [Testing Data Structures with Invariants](https://www.cs.princeton.edu/courses/archive/fall09/cos226/lectures/22BalancedTrees.pdf)
|
|
1136
|
-
|
|
1137
|
-
```typescript
|
|
1138
|
-
/**
|
|
1139
|
-
* Pattern: Academic Invariant Testing
|
|
1140
|
-
* Source: Princeton CS226 - Balanced Trees
|
|
1141
|
-
*
|
|
1142
|
-
* Key principles:
|
|
1143
|
-
* 1. Define invariants formally
|
|
1144
|
-
* 2. Create verification routines
|
|
1145
|
-
* 3. Test invariants before/after operations
|
|
1146
|
-
*/
|
|
1147
|
-
describe('Academic-Style Invariant Testing', () => {
|
|
1148
|
-
/**
|
|
1149
|
-
* Formal Invariant 1: Tree Size Property
|
|
1150
|
-
* For any node: size(node) = 1 + sum(size(children))
|
|
1151
|
-
*/
|
|
1152
|
-
function verifySizeProperty(node: Workflow): boolean {
|
|
1153
|
-
const actualSize = collectAllNodes(node).length;
|
|
1154
|
-
|
|
1155
|
-
let computedSize = 1; // Count self
|
|
1156
|
-
node.children.forEach(child => {
|
|
1157
|
-
computedSize += collectAllNodes(child).length;
|
|
1158
|
-
});
|
|
1159
|
-
|
|
1160
|
-
return actualSize === computedSize;
|
|
1161
|
-
}
|
|
1162
|
-
|
|
1163
|
-
/**
|
|
1164
|
-
* Formal Invariant 2: Unique Parent Property
|
|
1165
|
-
* Every non-root node has exactly one parent
|
|
1166
|
-
*/
|
|
1167
|
-
function verifyUniqueParent(root: Workflow): boolean {
|
|
1168
|
-
const allNodes = collectAllNodes(root);
|
|
1169
|
-
const nonRootNodes = allNodes.filter(n => n.parent !== null);
|
|
1170
|
-
|
|
1171
|
-
// Each non-root node should have exactly one parent
|
|
1172
|
-
return nonRootNodes.every(node => {
|
|
1173
|
-
// Has parent
|
|
1174
|
-
if (!node.parent) return false;
|
|
1175
|
-
|
|
1176
|
-
// Parent knows about this child
|
|
1177
|
-
if (!node.parent.children.includes(node)) return false;
|
|
1178
|
-
|
|
1179
|
-
// No other node claims this as child
|
|
1180
|
-
const otherClaimants = allNodes.filter(other =>
|
|
1181
|
-
other !== node.parent && other.children.includes(node)
|
|
1182
|
-
);
|
|
1183
|
-
|
|
1184
|
-
return otherClaimants.length === 0;
|
|
1185
|
-
});
|
|
1186
|
-
}
|
|
1187
|
-
|
|
1188
|
-
/**
|
|
1189
|
-
* Formal Invariant 3: Mirror Property
|
|
1190
|
-
* Workflow tree topology == Node tree topology
|
|
1191
|
-
*/
|
|
1192
|
-
function verifyMirrorProperty(root: Workflow): boolean {
|
|
1193
|
-
const workflowNodes = collectAllNodes(root);
|
|
1194
|
-
|
|
1195
|
-
return workflowNodes.every(wfNode => {
|
|
1196
|
-
// Parent relationship mirrors
|
|
1197
|
-
const parentMatches = wfNode.parent
|
|
1198
|
-
? wfNode.node.parent === wfNode.parent.node
|
|
1199
|
-
: wfNode.node.parent === null;
|
|
1200
|
-
|
|
1201
|
-
// Children relationship mirrors
|
|
1202
|
-
const childrenMatch = wfNode.children.length === wfNode.node.children.length &&
|
|
1203
|
-
wfNode.children.every((child, i) => wfNode.node.children[i] === child.node);
|
|
1204
|
-
|
|
1205
|
-
return parentMatches && childrenMatch;
|
|
1206
|
-
});
|
|
1207
|
-
}
|
|
1208
|
-
|
|
1209
|
-
it('should satisfy all formal invariants', () => {
|
|
1210
|
-
const root = new SimpleWorkflow('Root');
|
|
1211
|
-
const child1 = new SimpleWorkflow('Child1', root);
|
|
1212
|
-
const child2 = new SimpleWorkflow('Child2', root);
|
|
1213
|
-
const grandchild = new SimpleWorkflow('Grandchild', child1);
|
|
1214
|
-
|
|
1215
|
-
// Verify all invariants
|
|
1216
|
-
expect(verifySizeProperty(root)).toBe(true);
|
|
1217
|
-
expect(verifyUniqueParent(root)).toBe(true);
|
|
1218
|
-
expect(verifyMirrorProperty(root)).toBe(true);
|
|
1219
|
-
|
|
1220
|
-
// Perform operation
|
|
1221
|
-
root.detachChild(child1);
|
|
1222
|
-
|
|
1223
|
-
// Verify invariants still hold
|
|
1224
|
-
expect(verifySizeProperty(root)).toBe(true);
|
|
1225
|
-
expect(verifyUniqueParent(root)).toBe(true);
|
|
1226
|
-
expect(verifyMirrorProperty(root)).toBe(true);
|
|
1227
|
-
});
|
|
1228
|
-
});
|
|
1229
|
-
```
|
|
1230
|
-
|
|
1231
|
-
---
|
|
1232
|
-
|
|
1233
|
-
## 7. Test Pattern Catalog
|
|
1234
|
-
|
|
1235
|
-
### 7.1 Helper Functions
|
|
1236
|
-
|
|
1237
|
-
**Reusable utilities for tree consistency testing:**
|
|
1238
|
-
|
|
1239
|
-
```typescript
|
|
1240
|
-
/**
|
|
1241
|
-
* Helper: Collect all nodes in tree via BFS
|
|
1242
|
-
*/
|
|
1243
|
-
function collectAllNodes(root: Workflow): Workflow[] {
|
|
1244
|
-
const nodes: Workflow[] = [];
|
|
1245
|
-
const queue: Workflow[] = [root];
|
|
1246
|
-
const visited = new Set<Workflow>();
|
|
1247
|
-
|
|
1248
|
-
while (queue.length > 0) {
|
|
1249
|
-
const node = queue.shift()!;
|
|
1250
|
-
|
|
1251
|
-
if (visited.has(node)) {
|
|
1252
|
-
throw new Error(`Circular reference detected at ${node.node.name}`);
|
|
1253
|
-
}
|
|
1254
|
-
|
|
1255
|
-
visited.add(node);
|
|
1256
|
-
nodes.push(node);
|
|
1257
|
-
|
|
1258
|
-
queue.push(...node.children);
|
|
1259
|
-
}
|
|
1260
|
-
|
|
1261
|
-
return nodes;
|
|
1262
|
-
}
|
|
1263
|
-
|
|
1264
|
-
/**
|
|
1265
|
-
* Helper: Validate tree-wide bidirectional consistency
|
|
1266
|
-
* Returns array of inconsistency descriptions
|
|
1267
|
-
*/
|
|
1268
|
-
function validateTreeConsistency(root: Workflow): string[] {
|
|
1269
|
-
const errors: string[] = [];
|
|
1270
|
-
const allNodes = collectAllNodes(root);
|
|
1271
|
-
|
|
1272
|
-
allNodes.forEach(node => {
|
|
1273
|
-
// Check parent→child link
|
|
1274
|
-
if (node.parent) {
|
|
1275
|
-
if (!node.parent.children.includes(node)) {
|
|
1276
|
-
errors.push(
|
|
1277
|
-
`Orphaned child: ${node.node.name} not in parent's children list`
|
|
1278
|
-
);
|
|
1279
|
-
}
|
|
1280
|
-
}
|
|
1281
|
-
|
|
1282
|
-
// Check child→parent links
|
|
1283
|
-
node.children.forEach(child => {
|
|
1284
|
-
if (child.parent !== node) {
|
|
1285
|
-
errors.push(
|
|
1286
|
-
`Mismatched parent: ${child.node.name}.parent !== ${node.node.name}`
|
|
1287
|
-
);
|
|
1288
|
-
}
|
|
1289
|
-
|
|
1290
|
-
// Check node tree mirrors workflow tree
|
|
1291
|
-
if (child.node.parent !== node.node) {
|
|
1292
|
-
errors.push(
|
|
1293
|
-
`Node tree mismatch: ${child.node.name}.node.parent !== ${node.node.name}.node`
|
|
1294
|
-
);
|
|
1295
|
-
}
|
|
1296
|
-
|
|
1297
|
-
if (!node.node.children.includes(child.node)) {
|
|
1298
|
-
errors.push(
|
|
1299
|
-
`Node tree orphan: ${child.node.name}.node not in parent's node.children`
|
|
1300
|
-
);
|
|
1301
|
-
}
|
|
1302
|
-
});
|
|
1303
|
-
});
|
|
1304
|
-
|
|
1305
|
-
return errors;
|
|
1306
|
-
}
|
|
1307
|
-
|
|
1308
|
-
/**
|
|
1309
|
-
* Helper: Verify bidirectional link between parent and child
|
|
1310
|
-
*/
|
|
1311
|
-
function verifyBidirectionalLink(parent: Workflow, child: Workflow): void {
|
|
1312
|
-
// Workflow tree
|
|
1313
|
-
expect(child.parent).toBe(parent);
|
|
1314
|
-
expect(parent.children).toContain(child);
|
|
1315
|
-
|
|
1316
|
-
// Node tree
|
|
1317
|
-
expect(child.node.parent).toBe(parent.node);
|
|
1318
|
-
expect(parent.node.children).toContain(child.node);
|
|
1319
|
-
}
|
|
1320
|
-
|
|
1321
|
-
/**
|
|
1322
|
-
* Helper: Verify complete orphaning after detach
|
|
1323
|
-
*/
|
|
1324
|
-
function verifyOrphaned(child: Workflow): void {
|
|
1325
|
-
expect(child.parent).toBeNull();
|
|
1326
|
-
expect(child.node.parent).toBeNull();
|
|
1327
|
-
}
|
|
1328
|
-
|
|
1329
|
-
/**
|
|
1330
|
-
* Helper: Get depth of node in tree
|
|
1331
|
-
*/
|
|
1332
|
-
function getDepth(node: Workflow): number {
|
|
1333
|
-
let depth = 0;
|
|
1334
|
-
let current: Workflow | null = node;
|
|
1335
|
-
|
|
1336
|
-
while (current !== null) {
|
|
1337
|
-
depth++;
|
|
1338
|
-
current = current.parent;
|
|
1339
|
-
}
|
|
1340
|
-
|
|
1341
|
-
return depth - 1; // Subtract 1 for the node itself
|
|
1342
|
-
}
|
|
1343
|
-
|
|
1344
|
-
/**
|
|
1345
|
-
* Helper: Verify no circular references exist
|
|
1346
|
-
*/
|
|
1347
|
-
function verifyNoCycles(root: Workflow): void {
|
|
1348
|
-
const visited = new Set<Workflow>();
|
|
1349
|
-
const allNodes = collectAllNodes(root);
|
|
1350
|
-
|
|
1351
|
-
allNodes.forEach(node => {
|
|
1352
|
-
expect(visited.has(node)).toBe(false);
|
|
1353
|
-
visited.add(node);
|
|
1354
|
-
});
|
|
1355
|
-
}
|
|
1356
|
-
|
|
1357
|
-
/**
|
|
1358
|
-
* Helper: Verify tree mirror invariant
|
|
1359
|
-
*/
|
|
1360
|
-
function verifyTreeMirror(workflowRoot: Workflow): void {
|
|
1361
|
-
const allNodes = collectAllNodes(workflowRoot);
|
|
1362
|
-
|
|
1363
|
-
allNodes.forEach(wfNode => {
|
|
1364
|
-
const node = wfNode.node;
|
|
1365
|
-
|
|
1366
|
-
// Verify parent relationship
|
|
1367
|
-
if (wfNode.parent) {
|
|
1368
|
-
expect(node.parent).toBe(wfNode.parent.node);
|
|
1369
|
-
} else {
|
|
1370
|
-
expect(node.parent).toBeNull();
|
|
1371
|
-
}
|
|
1372
|
-
|
|
1373
|
-
// Verify children relationship
|
|
1374
|
-
expect(node.children.length).toBe(wfNode.children.length);
|
|
1375
|
-
|
|
1376
|
-
wfNode.children.forEach((childWf, index) => {
|
|
1377
|
-
expect(node.children[index]).toBe(childWf.node);
|
|
1378
|
-
});
|
|
1379
|
-
});
|
|
1380
|
-
}
|
|
1381
|
-
```
|
|
1382
|
-
|
|
1383
|
-
### 7.2 Test Templates
|
|
1384
|
-
|
|
1385
|
-
**Reusable test templates for common scenarios:**
|
|
1386
|
-
|
|
1387
|
-
```typescript
|
|
1388
|
-
/**
|
|
1389
|
-
* Template: Test bidirectional consistency for tree operation
|
|
1390
|
-
*/
|
|
1391
|
-
function testOperationConsistency(
|
|
1392
|
-
operation: () => void,
|
|
1393
|
-
beforeState: { roots: number, totalNodes: number },
|
|
1394
|
-
afterState: { roots: number, totalNodes: number }
|
|
1395
|
-
): void {
|
|
1396
|
-
// Get initial state
|
|
1397
|
-
const beforeErrors = validateTreeConsistency(/* root */);
|
|
1398
|
-
expect(beforeErrors).toEqual([]);
|
|
1399
|
-
|
|
1400
|
-
// Execute operation
|
|
1401
|
-
operation();
|
|
1402
|
-
|
|
1403
|
-
// Verify final state
|
|
1404
|
-
const afterErrors = validateTreeConsistency(/* root */);
|
|
1405
|
-
expect(afterErrors).toEqual([]);
|
|
1406
|
-
}
|
|
1407
|
-
|
|
1408
|
-
/**
|
|
1409
|
-
* Template: Test reparenting scenario
|
|
1410
|
-
*/
|
|
1411
|
-
function testReparentingScenario(
|
|
1412
|
-
setup: () => { oldParent: Workflow, newParent: Workflow, child: Workflow }
|
|
1413
|
-
): void {
|
|
1414
|
-
const { oldParent, newParent, child } = setup();
|
|
1415
|
-
|
|
1416
|
-
// Verify initial state
|
|
1417
|
-
verifyBidirectionalLink(oldParent, child);
|
|
1418
|
-
|
|
1419
|
-
// Execute reparenting
|
|
1420
|
-
oldParent.detachChild(child);
|
|
1421
|
-
newParent.attachChild(child);
|
|
1422
|
-
|
|
1423
|
-
// Verify new state
|
|
1424
|
-
verifyBidirectionalLink(newParent, child);
|
|
1425
|
-
|
|
1426
|
-
// Verify old parent no longer has child
|
|
1427
|
-
expect(oldParent.children).not.toContain(child);
|
|
1428
|
-
expect(oldParent.node.children).not.toContain(child.node);
|
|
1429
|
-
}
|
|
1430
|
-
```
|
|
1431
|
-
|
|
1432
|
-
---
|
|
1433
|
-
|
|
1434
|
-
## 8. Implementation Checklist
|
|
1435
|
-
|
|
1436
|
-
### 8.1 Core Tests to Implement
|
|
1437
|
-
|
|
1438
|
-
- [ ] **Bidirectional Link Tests**
|
|
1439
|
-
- [ ] Verify parent→child and child→parent links after attach
|
|
1440
|
-
- [ ] Verify both trees (workflow + node) are synchronized
|
|
1441
|
-
- [ ] Verify link consistency after detach
|
|
1442
|
-
- [ ] Verify link consistency after reparenting
|
|
1443
|
-
|
|
1444
|
-
- [ ] **Tree Mirror Tests**
|
|
1445
|
-
- [ ] Verify 1:1 mirror between workflow tree and node tree
|
|
1446
|
-
- [ ] Verify mirror invariant after every operation
|
|
1447
|
-
- [ ] Verify no orphaned nodes in either tree
|
|
1448
|
-
- [ ] Verify no ghost references in either tree
|
|
1449
|
-
|
|
1450
|
-
- [ ] **Operation Tests**
|
|
1451
|
-
- [ ] Test attachChild() with valid inputs
|
|
1452
|
-
- [ ] Test attachChild() error cases (existing parent, circular ref, duplicate)
|
|
1453
|
-
- [ ] Test detachChild() with valid inputs
|
|
1454
|
-
- [ ] Test detachChild() error cases (not attached)
|
|
1455
|
-
- [ ] Test reparenting workflow (detach + attach)
|
|
1456
|
-
|
|
1457
|
-
- [ ] **Invariant Tests**
|
|
1458
|
-
- [ ] Test acyclicity invariant (no cycles)
|
|
1459
|
-
- [ ] Test single root invariant
|
|
1460
|
-
- [ ] Test connectedness invariant (all nodes reachable)
|
|
1461
|
-
- [ ] Test parent-child consistency invariant
|
|
1462
|
-
- [ ] Test edge count invariant (n-1 edges for n nodes)
|
|
1463
|
-
|
|
1464
|
-
- [ ] **Adversarial Tests**
|
|
1465
|
-
- [ ] Test manual parent mutation detection
|
|
1466
|
-
- [ ] Test circular reference detection (simple and complex)
|
|
1467
|
-
- [ ] Test stress scenarios (deep hierarchies, wide hierarchies)
|
|
1468
|
-
- [ ] Test rapid attach/detach cycles
|
|
1469
|
-
- [ ] Test concurrent operations
|
|
1470
|
-
|
|
1471
|
-
### 8.2 Test File Organization
|
|
1472
|
-
|
|
1473
|
-
```
|
|
1474
|
-
src/__tests__/
|
|
1475
|
-
├── unit/
|
|
1476
|
-
│ └── workflow.test.ts # Existing unit tests
|
|
1477
|
-
├── integration/
|
|
1478
|
-
│ ├── workflow-reparenting.test.ts # Existing reparenting tests
|
|
1479
|
-
│ └── bidirectional-consistency.test.ts # NEW: Dual tree consistency
|
|
1480
|
-
└── adversarial/
|
|
1481
|
-
├── prd-compliance.test.ts # Existing PRD compliance
|
|
1482
|
-
├── edge-case.test.ts # Existing edge cases
|
|
1483
|
-
└── tree-invariants.test.ts # NEW: Invariant testing
|
|
1484
|
-
```
|
|
1485
|
-
|
|
1486
|
-
### 8.3 Priority Order
|
|
1487
|
-
|
|
1488
|
-
**Phase 1: Core Consistency (High Priority)**
|
|
1489
|
-
1. Bidirectional link verification tests
|
|
1490
|
-
2. Tree mirror invariant tests
|
|
1491
|
-
3. Operation-level consistency tests
|
|
1492
|
-
|
|
1493
|
-
**Phase 2: Comprehensive Coverage (Medium Priority)**
|
|
1494
|
-
4. Tree-wide consistency validation
|
|
1495
|
-
5. Invariant testing (acyclicity, connectedness, etc.)
|
|
1496
|
-
6. Reparenting scenario tests
|
|
1497
|
-
|
|
1498
|
-
**Phase 3: Robustness (Lower Priority)**
|
|
1499
|
-
7. Adversarial testing (manual mutations, circular refs)
|
|
1500
|
-
8. Stress testing (deep/wide hierarchies)
|
|
1501
|
-
9. Property-based testing
|
|
1502
|
-
|
|
1503
|
-
---
|
|
1504
|
-
|
|
1505
|
-
## 9. Key Takeaways
|
|
1506
|
-
|
|
1507
|
-
### 9.1 Critical Invariants
|
|
1508
|
-
|
|
1509
|
-
1. **1:1 Tree Mirror**: Workflow tree and node tree must always be perfectly synchronized
|
|
1510
|
-
2. **Bidirectional Links**: Parent→child and child→parent references must always match
|
|
1511
|
-
3. **Acyclicity**: No circular references in tree structure
|
|
1512
|
-
4. **Single Root**: Exactly one root node (parent === null)
|
|
1513
|
-
|
|
1514
|
-
### 9.2 Testing Principles
|
|
1515
|
-
|
|
1516
|
-
1. **Test Both Trees**: Always verify both workflow tree and node tree
|
|
1517
|
-
2. **Test Both Directions**: Verify parent→child AND child→parent
|
|
1518
|
-
3. **Test After Every Operation**: Run consistency checks after attach, detach, reparenting
|
|
1519
|
-
4. **Use Helpers**: Create reusable helper functions for common validations
|
|
1520
|
-
5. **Adversarial Testing**: Test manual mutations and edge cases
|
|
1521
|
-
|
|
1522
|
-
### 9.3 Best Practices
|
|
1523
|
-
|
|
1524
|
-
1. **AAA Pattern**: Arrange-Act-Assert structure with clear phase comments
|
|
1525
|
-
2. **Descriptive Names**: Use `should [expected behavior]` naming convention
|
|
1526
|
-
3. **Multiple Assertions**: Verify multiple aspects of consistency
|
|
1527
|
-
4. **Cross-Verification**: Use WorkflowTreeDebugger to verify structure
|
|
1528
|
-
5. **Property-Based Testing**: Define invariants as properties and verify them
|
|
1529
|
-
|
|
1530
|
-
---
|
|
1531
|
-
|
|
1532
|
-
## 10. References and Resources
|
|
1533
|
-
|
|
1534
|
-
### 10.1 External Documentation
|
|
1535
|
-
|
|
1536
|
-
- **DOM Tree Specification**: https://dom.spec.whatwg.org/#concept-tree-parent
|
|
1537
|
-
- Authoritative source on tree mutation semantics
|
|
1538
|
-
- Parent-child relationship management patterns
|
|
1539
|
-
|
|
1540
|
-
- **React Fiber Architecture**: https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiber.js
|
|
1541
|
-
- Dual tree representation patterns
|
|
1542
|
-
- Atomic update strategies
|
|
1543
|
-
|
|
1544
|
-
- **QuickCheck Paper**: https://www.cs.tufts.edu/~nr/cs257/archive/john-hughes/quick.pdf
|
|
1545
|
-
- Property-based testing methodology
|
|
1546
|
-
- Invariant definition patterns
|
|
1547
|
-
|
|
1548
|
-
- **Princeton CS226 - Balanced Trees**: https://www.cs.princeton.edu/courses/archive/fall09/cos226/lectures/22BalancedTrees.pdf
|
|
1549
|
-
- Formal invariant definition
|
|
1550
|
-
- Tree property verification
|
|
1551
|
-
|
|
1552
|
-
### 10.2 Internal Resources
|
|
1553
|
-
|
|
1554
|
-
- **Implementation Patterns**: `/home/dustin/projects/groundswell/plan/docs/bugfix-architecture/implementation_patterns.md`
|
|
1555
|
-
- **Existing Test Patterns**: `/home/dustin/projects/groundswell/plan/docs/research/P1M2T1S4/existing_test_pattern.md`
|
|
1556
|
-
- **Tree Mirroring Tests**: `/home/dustin/projects/groundswell/src/__tests__/integration/tree-mirroring.test.ts`
|
|
1557
|
-
- **Reparenting Tests**: `/home/dustin/projects/groundswell/src/__tests__/integration/workflow-reparenting.test.ts`
|
|
1558
|
-
- **PRD Compliance Tests**: `/home/dustin/projects/groundswell/src/__tests__/adversarial/prd-compliance.test.ts`
|
|
1559
|
-
- **Tree Debugger**: `/home/dustin/projects/groundswell/src/debugger/tree-debugger.ts`
|
|
1560
|
-
|
|
1561
|
-
### 10.3 Actionable Patterns
|
|
1562
|
-
|
|
1563
|
-
**For immediate application:**
|
|
1564
|
-
1. Use `verifyBidirectionalLink()` helper after every tree operation
|
|
1565
|
-
2. Run `validateTreeConsistency()` in test teardown
|
|
1566
|
-
3. Test both workflow tree AND node tree in all assertions
|
|
1567
|
-
4. Add adversarial tests for manual mutations
|
|
1568
|
-
5. Implement property-based tests for core invariants
|
|
1569
|
-
|
|
1570
|
-
---
|
|
1571
|
-
|
|
1572
|
-
**Document Status:** Complete
|
|
1573
|
-
**Next Steps:** Implement test suite based on patterns documented here
|
|
1574
|
-
**Maintainer:** P1M3T1S4 Research Team
|