decision_agent 0.3.0 → 1.1.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.
Files changed (220) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +234 -14
  3. data/lib/decision_agent/ab_testing/ab_test.rb +5 -1
  4. data/lib/decision_agent/ab_testing/ab_test_assignment.rb +2 -0
  5. data/lib/decision_agent/ab_testing/ab_test_manager.rb +2 -0
  6. data/lib/decision_agent/ab_testing/ab_testing_agent.rb +2 -0
  7. data/lib/decision_agent/ab_testing/storage/activerecord_adapter.rb +2 -13
  8. data/lib/decision_agent/ab_testing/storage/adapter.rb +2 -0
  9. data/lib/decision_agent/ab_testing/storage/memory_adapter.rb +2 -0
  10. data/lib/decision_agent/agent.rb +78 -9
  11. data/lib/decision_agent/audit/adapter.rb +2 -0
  12. data/lib/decision_agent/audit/logger_adapter.rb +2 -0
  13. data/lib/decision_agent/audit/null_adapter.rb +2 -0
  14. data/lib/decision_agent/auth/access_audit_logger.rb +2 -0
  15. data/lib/decision_agent/auth/authenticator.rb +2 -0
  16. data/lib/decision_agent/auth/password_reset_manager.rb +2 -0
  17. data/lib/decision_agent/auth/password_reset_token.rb +2 -0
  18. data/lib/decision_agent/auth/permission.rb +2 -0
  19. data/lib/decision_agent/auth/permission_checker.rb +2 -0
  20. data/lib/decision_agent/auth/rbac_adapter.rb +2 -0
  21. data/lib/decision_agent/auth/rbac_config.rb +2 -0
  22. data/lib/decision_agent/auth/role.rb +2 -0
  23. data/lib/decision_agent/auth/session.rb +2 -0
  24. data/lib/decision_agent/auth/session_manager.rb +2 -0
  25. data/lib/decision_agent/auth/user.rb +2 -0
  26. data/lib/decision_agent/context.rb +14 -0
  27. data/lib/decision_agent/decision.rb +113 -4
  28. data/lib/decision_agent/dmn/adapter.rb +2 -0
  29. data/lib/decision_agent/dmn/cache.rb +2 -2
  30. data/lib/decision_agent/dmn/decision_graph.rb +7 -7
  31. data/lib/decision_agent/dmn/decision_tree.rb +16 -8
  32. data/lib/decision_agent/dmn/errors.rb +2 -0
  33. data/lib/decision_agent/dmn/exporter.rb +2 -0
  34. data/lib/decision_agent/dmn/feel/evaluator.rb +130 -114
  35. data/lib/decision_agent/dmn/feel/functions.rb +2 -0
  36. data/lib/decision_agent/dmn/feel/parser.rb +2 -0
  37. data/lib/decision_agent/dmn/feel/simple_parser.rb +98 -77
  38. data/lib/decision_agent/dmn/feel/transformer.rb +56 -102
  39. data/lib/decision_agent/dmn/feel/types.rb +2 -0
  40. data/lib/decision_agent/dmn/importer.rb +2 -0
  41. data/lib/decision_agent/dmn/model.rb +2 -4
  42. data/lib/decision_agent/dmn/parser.rb +2 -0
  43. data/lib/decision_agent/dmn/testing.rb +3 -2
  44. data/lib/decision_agent/dmn/validator.rb +5 -3
  45. data/lib/decision_agent/dmn/visualizer.rb +7 -6
  46. data/lib/decision_agent/dsl/condition_evaluator.rb +242 -1375
  47. data/lib/decision_agent/dsl/helpers/cache_helpers.rb +82 -0
  48. data/lib/decision_agent/dsl/helpers/comparison_helpers.rb +98 -0
  49. data/lib/decision_agent/dsl/helpers/date_helpers.rb +91 -0
  50. data/lib/decision_agent/dsl/helpers/geospatial_helpers.rb +85 -0
  51. data/lib/decision_agent/dsl/helpers/operator_evaluation_helpers.rb +160 -0
  52. data/lib/decision_agent/dsl/helpers/parameter_parsing_helpers.rb +206 -0
  53. data/lib/decision_agent/dsl/helpers/template_helpers.rb +39 -0
  54. data/lib/decision_agent/dsl/helpers/utility_helpers.rb +45 -0
  55. data/lib/decision_agent/dsl/operators/base.rb +70 -0
  56. data/lib/decision_agent/dsl/operators/basic_comparison_operators.rb +80 -0
  57. data/lib/decision_agent/dsl/operators/collection_operators.rb +60 -0
  58. data/lib/decision_agent/dsl/operators/date_arithmetic_operators.rb +206 -0
  59. data/lib/decision_agent/dsl/operators/date_time_operators.rb +47 -0
  60. data/lib/decision_agent/dsl/operators/duration_operators.rb +149 -0
  61. data/lib/decision_agent/dsl/operators/financial_operators.rb +237 -0
  62. data/lib/decision_agent/dsl/operators/geospatial_operators.rb +106 -0
  63. data/lib/decision_agent/dsl/operators/mathematical_operators.rb +234 -0
  64. data/lib/decision_agent/dsl/operators/moving_window_operators.rb +135 -0
  65. data/lib/decision_agent/dsl/operators/numeric_operators.rb +120 -0
  66. data/lib/decision_agent/dsl/operators/rate_operators.rb +65 -0
  67. data/lib/decision_agent/dsl/operators/statistical_aggregations.rb +187 -0
  68. data/lib/decision_agent/dsl/operators/string_aggregations.rb +84 -0
  69. data/lib/decision_agent/dsl/operators/string_operators.rb +72 -0
  70. data/lib/decision_agent/dsl/operators/time_component_operators.rb +72 -0
  71. data/lib/decision_agent/dsl/rule_parser.rb +2 -0
  72. data/lib/decision_agent/dsl/schema_validator.rb +37 -14
  73. data/lib/decision_agent/errors.rb +2 -0
  74. data/lib/decision_agent/evaluation.rb +14 -2
  75. data/lib/decision_agent/evaluators/base.rb +2 -0
  76. data/lib/decision_agent/evaluators/dmn_evaluator.rb +108 -19
  77. data/lib/decision_agent/evaluators/json_rule_evaluator.rb +56 -11
  78. data/lib/decision_agent/evaluators/static_evaluator.rb +2 -0
  79. data/lib/decision_agent/explainability/condition_trace.rb +85 -0
  80. data/lib/decision_agent/explainability/explainability_result.rb +50 -0
  81. data/lib/decision_agent/explainability/rule_trace.rb +41 -0
  82. data/lib/decision_agent/explainability/trace_collector.rb +26 -0
  83. data/lib/decision_agent/monitoring/alert_manager.rb +7 -16
  84. data/lib/decision_agent/monitoring/dashboard_server.rb +383 -250
  85. data/lib/decision_agent/monitoring/metrics_collector.rb +2 -0
  86. data/lib/decision_agent/monitoring/monitored_agent.rb +2 -0
  87. data/lib/decision_agent/monitoring/prometheus_exporter.rb +3 -1
  88. data/lib/decision_agent/replay/replay.rb +4 -1
  89. data/lib/decision_agent/scoring/base.rb +2 -0
  90. data/lib/decision_agent/scoring/consensus.rb +2 -0
  91. data/lib/decision_agent/scoring/max_weight.rb +2 -0
  92. data/lib/decision_agent/scoring/threshold.rb +2 -0
  93. data/lib/decision_agent/scoring/weighted_average.rb +2 -0
  94. data/lib/decision_agent/simulation/errors.rb +20 -0
  95. data/lib/decision_agent/simulation/impact_analyzer.rb +500 -0
  96. data/lib/decision_agent/simulation/monte_carlo_simulator.rb +638 -0
  97. data/lib/decision_agent/simulation/replay_engine.rb +488 -0
  98. data/lib/decision_agent/simulation/scenario_engine.rb +320 -0
  99. data/lib/decision_agent/simulation/scenario_library.rb +165 -0
  100. data/lib/decision_agent/simulation/shadow_test_engine.rb +274 -0
  101. data/lib/decision_agent/simulation/what_if_analyzer.rb +1008 -0
  102. data/lib/decision_agent/simulation.rb +19 -0
  103. data/lib/decision_agent/testing/batch_test_importer.rb +6 -2
  104. data/lib/decision_agent/testing/batch_test_runner.rb +5 -2
  105. data/lib/decision_agent/testing/test_coverage_analyzer.rb +2 -0
  106. data/lib/decision_agent/testing/test_result_comparator.rb +2 -0
  107. data/lib/decision_agent/testing/test_scenario.rb +2 -0
  108. data/lib/decision_agent/version.rb +3 -1
  109. data/lib/decision_agent/versioning/activerecord_adapter.rb +108 -43
  110. data/lib/decision_agent/versioning/adapter.rb +9 -0
  111. data/lib/decision_agent/versioning/file_storage_adapter.rb +19 -6
  112. data/lib/decision_agent/versioning/version_manager.rb +9 -0
  113. data/lib/decision_agent/web/dmn_editor/serialization.rb +74 -0
  114. data/lib/decision_agent/web/dmn_editor/xml_builder.rb +107 -0
  115. data/lib/decision_agent/web/dmn_editor.rb +8 -67
  116. data/lib/decision_agent/web/middleware/auth_middleware.rb +2 -0
  117. data/lib/decision_agent/web/middleware/permission_middleware.rb +3 -1
  118. data/lib/decision_agent/web/public/app.js +186 -26
  119. data/lib/decision_agent/web/public/batch_testing.html +80 -6
  120. data/lib/decision_agent/web/public/dmn-editor.html +2 -2
  121. data/lib/decision_agent/web/public/dmn-editor.js +74 -8
  122. data/lib/decision_agent/web/public/index.html +69 -3
  123. data/lib/decision_agent/web/public/login.html +1 -1
  124. data/lib/decision_agent/web/public/sample_batch.csv +11 -0
  125. data/lib/decision_agent/web/public/sample_impact.csv +11 -0
  126. data/lib/decision_agent/web/public/sample_replay.csv +11 -0
  127. data/lib/decision_agent/web/public/sample_rules.json +118 -0
  128. data/lib/decision_agent/web/public/sample_shadow.csv +11 -0
  129. data/lib/decision_agent/web/public/sample_whatif.csv +11 -0
  130. data/lib/decision_agent/web/public/simulation.html +146 -0
  131. data/lib/decision_agent/web/public/simulation_impact.html +495 -0
  132. data/lib/decision_agent/web/public/simulation_replay.html +547 -0
  133. data/lib/decision_agent/web/public/simulation_shadow.html +561 -0
  134. data/lib/decision_agent/web/public/simulation_whatif.html +549 -0
  135. data/lib/decision_agent/web/public/styles.css +65 -0
  136. data/lib/decision_agent/web/public/users.html +1 -1
  137. data/lib/decision_agent/web/rack_helpers.rb +106 -0
  138. data/lib/decision_agent/web/rack_request_helpers.rb +196 -0
  139. data/lib/decision_agent/web/server.rb +2126 -1374
  140. data/lib/decision_agent.rb +19 -1
  141. data/lib/generators/decision_agent/install/install_generator.rb +2 -0
  142. data/lib/generators/decision_agent/install/templates/ab_test_assignment_model.rb +2 -0
  143. data/lib/generators/decision_agent/install/templates/ab_test_model.rb +2 -0
  144. data/lib/generators/decision_agent/install/templates/ab_testing_migration.rb +2 -0
  145. data/lib/generators/decision_agent/install/templates/migration.rb +2 -0
  146. data/lib/generators/decision_agent/install/templates/rule.rb +2 -0
  147. data/lib/generators/decision_agent/install/templates/rule_version.rb +2 -0
  148. metadata +103 -89
  149. data/spec/ab_testing/ab_test_assignment_spec.rb +0 -253
  150. data/spec/ab_testing/ab_test_manager_spec.rb +0 -612
  151. data/spec/ab_testing/ab_test_spec.rb +0 -270
  152. data/spec/ab_testing/ab_testing_agent_spec.rb +0 -655
  153. data/spec/ab_testing/storage/adapter_spec.rb +0 -64
  154. data/spec/ab_testing/storage/memory_adapter_spec.rb +0 -485
  155. data/spec/activerecord_thread_safety_spec.rb +0 -553
  156. data/spec/advanced_operators_spec.rb +0 -3150
  157. data/spec/agent_spec.rb +0 -289
  158. data/spec/api_contract_spec.rb +0 -430
  159. data/spec/audit_adapters_spec.rb +0 -92
  160. data/spec/auth/access_audit_logger_spec.rb +0 -394
  161. data/spec/auth/authenticator_spec.rb +0 -112
  162. data/spec/auth/password_reset_spec.rb +0 -294
  163. data/spec/auth/permission_checker_spec.rb +0 -207
  164. data/spec/auth/permission_spec.rb +0 -73
  165. data/spec/auth/rbac_adapter_spec.rb +0 -778
  166. data/spec/auth/rbac_config_spec.rb +0 -82
  167. data/spec/auth/role_spec.rb +0 -51
  168. data/spec/auth/session_manager_spec.rb +0 -172
  169. data/spec/auth/session_spec.rb +0 -112
  170. data/spec/auth/user_spec.rb +0 -130
  171. data/spec/comprehensive_edge_cases_spec.rb +0 -1777
  172. data/spec/context_spec.rb +0 -127
  173. data/spec/decision_agent_spec.rb +0 -96
  174. data/spec/decision_spec.rb +0 -423
  175. data/spec/dmn/decision_graph_spec.rb +0 -282
  176. data/spec/dmn/decision_tree_spec.rb +0 -203
  177. data/spec/dmn/feel/errors_spec.rb +0 -18
  178. data/spec/dmn/feel/functions_spec.rb +0 -400
  179. data/spec/dmn/feel/simple_parser_spec.rb +0 -274
  180. data/spec/dmn/feel/types_spec.rb +0 -176
  181. data/spec/dmn/feel_parser_spec.rb +0 -489
  182. data/spec/dmn/hit_policy_spec.rb +0 -202
  183. data/spec/dmn/integration_spec.rb +0 -226
  184. data/spec/dsl/condition_evaluator_spec.rb +0 -774
  185. data/spec/dsl_validation_spec.rb +0 -648
  186. data/spec/edge_cases_spec.rb +0 -353
  187. data/spec/evaluation_spec.rb +0 -364
  188. data/spec/evaluation_validator_spec.rb +0 -165
  189. data/spec/examples/feedback_aware_evaluator_spec.rb +0 -460
  190. data/spec/examples.txt +0 -1909
  191. data/spec/fixtures/dmn/complex_decision.dmn +0 -81
  192. data/spec/fixtures/dmn/invalid_structure.dmn +0 -31
  193. data/spec/fixtures/dmn/simple_decision.dmn +0 -40
  194. data/spec/issue_verification_spec.rb +0 -759
  195. data/spec/json_rule_evaluator_spec.rb +0 -587
  196. data/spec/monitoring/alert_manager_spec.rb +0 -378
  197. data/spec/monitoring/metrics_collector_spec.rb +0 -501
  198. data/spec/monitoring/monitored_agent_spec.rb +0 -225
  199. data/spec/monitoring/prometheus_exporter_spec.rb +0 -242
  200. data/spec/monitoring/storage/activerecord_adapter_spec.rb +0 -498
  201. data/spec/monitoring/storage/base_adapter_spec.rb +0 -61
  202. data/spec/monitoring/storage/memory_adapter_spec.rb +0 -247
  203. data/spec/performance_optimizations_spec.rb +0 -493
  204. data/spec/replay_edge_cases_spec.rb +0 -699
  205. data/spec/replay_spec.rb +0 -210
  206. data/spec/rfc8785_canonicalization_spec.rb +0 -215
  207. data/spec/scoring_spec.rb +0 -225
  208. data/spec/spec_helper.rb +0 -60
  209. data/spec/testing/batch_test_importer_spec.rb +0 -693
  210. data/spec/testing/batch_test_runner_spec.rb +0 -307
  211. data/spec/testing/test_coverage_analyzer_spec.rb +0 -292
  212. data/spec/testing/test_result_comparator_spec.rb +0 -392
  213. data/spec/testing/test_scenario_spec.rb +0 -113
  214. data/spec/thread_safety_spec.rb +0 -490
  215. data/spec/thread_safety_spec.rb.broken +0 -878
  216. data/spec/versioning/adapter_spec.rb +0 -156
  217. data/spec/versioning_spec.rb +0 -1030
  218. data/spec/web/middleware/auth_middleware_spec.rb +0 -133
  219. data/spec/web/middleware/permission_middleware_spec.rb +0 -247
  220. data/spec/web_ui_rack_spec.rb +0 -2134
@@ -0,0 +1,495 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Impact Analysis</title>
7
+ <link rel="stylesheet" href="/styles.css">
8
+ <style>
9
+ .simulation-container {
10
+ max-width: 1400px;
11
+ margin: 0 auto;
12
+ padding: 20px;
13
+ }
14
+
15
+ .step-section {
16
+ background: var(--panel-bg);
17
+ border-radius: 10px;
18
+ padding: 20px;
19
+ margin-bottom: 20px;
20
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
21
+ }
22
+
23
+ .metrics-grid {
24
+ display: grid;
25
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
26
+ gap: 15px;
27
+ margin-top: 15px;
28
+ }
29
+
30
+ .metric-card {
31
+ background: var(--hover-bg);
32
+ padding: 15px;
33
+ border-radius: 8px;
34
+ text-align: center;
35
+ }
36
+
37
+ .metric-value {
38
+ font-size: 2rem;
39
+ font-weight: bold;
40
+ color: var(--primary-color);
41
+ }
42
+
43
+ .metric-label {
44
+ font-size: 0.9rem;
45
+ color: var(--text-secondary);
46
+ margin-top: 5px;
47
+ }
48
+
49
+ .risk-badge {
50
+ display: inline-block;
51
+ padding: 8px 16px;
52
+ border-radius: 20px;
53
+ font-weight: 600;
54
+ font-size: 1.1rem;
55
+ }
56
+
57
+ .risk-low {
58
+ background: #d1fae5;
59
+ color: #065f46;
60
+ }
61
+
62
+ .risk-medium {
63
+ background: #fef3c7;
64
+ color: #92400e;
65
+ }
66
+
67
+ .risk-high {
68
+ background: #fed7aa;
69
+ color: #9a3412;
70
+ }
71
+
72
+ .risk-critical {
73
+ background: #fee2e2;
74
+ color: #991b1b;
75
+ }
76
+
77
+ .hidden {
78
+ display: none;
79
+ }
80
+
81
+ .error-message {
82
+ background: #fee2e2;
83
+ color: #991b1b;
84
+ padding: 12px;
85
+ border-radius: 6px;
86
+ margin-top: 10px;
87
+ }
88
+
89
+ .success-message {
90
+ background: #d1fae5;
91
+ color: #065f46;
92
+ padding: 12px;
93
+ border-radius: 6px;
94
+ margin-top: 10px;
95
+ }
96
+
97
+ .header-links {
98
+ display: flex;
99
+ gap: 1rem;
100
+ align-items: center;
101
+ margin-top: 1.5rem;
102
+ padding-top: 1rem;
103
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
104
+ }
105
+
106
+ .header-links a {
107
+ padding: 0.5rem 1rem;
108
+ border-radius: 6px;
109
+ text-decoration: none;
110
+ color: var(--text-primary);
111
+ background: rgba(255, 255, 255, 0.05);
112
+ transition: all 0.2s;
113
+ font-size: 0.9rem;
114
+ }
115
+
116
+ .header-links a:hover {
117
+ background: rgba(255, 255, 255, 0.1);
118
+ transform: translateY(-1px);
119
+ }
120
+
121
+ .file-upload-area {
122
+ border: 2px dashed var(--border-color);
123
+ border-radius: 8px;
124
+ padding: 40px;
125
+ text-align: center;
126
+ cursor: pointer;
127
+ transition: all 0.3s;
128
+ }
129
+
130
+ .file-upload-area:hover {
131
+ border-color: var(--primary-color);
132
+ background: var(--hover-bg);
133
+ }
134
+ </style>
135
+ </head>
136
+ <body>
137
+ <div class="simulation-container">
138
+ <header>
139
+ <h1>📊 Impact Analysis</h1>
140
+ <p class="subtitle">Quantify the impact of rule changes before deploying to production</p>
141
+ <div class="header-links">
142
+ <a href="/simulation">← Back to Simulation Dashboard</a>
143
+ </div>
144
+ </header>
145
+
146
+ <!-- Step 1: Select Versions -->
147
+ <div class="step-section">
148
+ <h2>Select Versions to Compare</h2>
149
+
150
+ <div class="form-group">
151
+ <label for="baselineVersion">Baseline Version:</label>
152
+ <select id="baselineVersion" class="input">
153
+ <option value="">-- Select Baseline Version --</option>
154
+ </select>
155
+ </div>
156
+
157
+ <div class="form-group">
158
+ <label for="proposedVersion">Proposed Version:</label>
159
+ <select id="proposedVersion" class="input">
160
+ <option value="">-- Select Proposed Version --</option>
161
+ </select>
162
+ </div>
163
+ </div>
164
+
165
+ <!-- Step 2: Upload Test Data -->
166
+ <div class="step-section">
167
+ <h2>Upload Test Data</h2>
168
+ <p>Upload a CSV or JSON file with test contexts, or paste JSON directly</p>
169
+ <p style="margin-top: 10px;">
170
+ <strong>Need a template?</strong> Download sample files:
171
+ <a href="/sample_impact.csv" download style="color: #007bff; text-decoration: none; margin-left: 5px;">📥 Sample CSV</a> |
172
+ <a href="/sample_rules.json" download style="color: #007bff; text-decoration: none; margin-left: 5px;">📥 Sample Rules</a>
173
+ </p>
174
+
175
+ <div class="file-upload-area" id="uploadArea">
176
+ <p>📁 Drag and drop your CSV/JSON file here, or click to browse</p>
177
+ <input type="file" id="fileInput" accept=".csv,.json" style="display: none;">
178
+ </div>
179
+
180
+ <div class="form-group" style="margin-top: 20px;">
181
+ <label for="testDataJson">Or paste test data JSON:</label>
182
+ <textarea id="testDataJson" class="input" rows="5" placeholder='[{"credit_score": 650, "amount": 100000}, ...]'></textarea>
183
+ </div>
184
+
185
+ <div id="uploadStatus" class="hidden"></div>
186
+ </div>
187
+
188
+ <!-- Step 3: Run Analysis -->
189
+ <div class="step-section">
190
+ <h2>Run Impact Analysis</h2>
191
+
192
+ <div class="form-group">
193
+ <label>
194
+ <input type="checkbox" id="calculateRisk" checked> Calculate risk score
195
+ </label>
196
+ </div>
197
+ <div class="form-group">
198
+ <label>
199
+ <input type="checkbox" id="parallelExecution" checked> Parallel execution
200
+ </label>
201
+ </div>
202
+ <div class="form-group">
203
+ <label for="threadCount">Thread count:</label>
204
+ <input type="number" id="threadCount" value="4" min="1" max="16" class="input" style="width: 100px;">
205
+ </div>
206
+
207
+ <button class="btn btn-primary" id="runAnalysisBtn" disabled>▶ Run Impact Analysis</button>
208
+
209
+ <div id="runStatus" class="hidden"></div>
210
+ </div>
211
+
212
+ <!-- Step 4: Results -->
213
+ <div class="step-section" id="resultsSection" style="display: none;">
214
+ <h2>Impact Analysis Results</h2>
215
+
216
+ <!-- Risk Score -->
217
+ <div id="riskSection" class="hidden">
218
+ <h3>Risk Assessment</h3>
219
+ <div style="margin: 20px 0;">
220
+ <div style="font-size: 1.5rem; margin-bottom: 10px;">Risk Level: <span id="riskLevel" class="risk-badge"></span></div>
221
+ <div style="font-size: 1.2rem;">Risk Score: <span id="riskScore"></span></div>
222
+ </div>
223
+ </div>
224
+
225
+ <!-- Statistics -->
226
+ <div id="statisticsSection" class="hidden">
227
+ <h3>Impact Statistics</h3>
228
+ <div class="metrics-grid" id="statisticsGrid"></div>
229
+ </div>
230
+
231
+ <!-- Decision Distribution -->
232
+ <div id="distributionSection" class="hidden">
233
+ <h3>Decision Distribution Changes</h3>
234
+ <div id="distributionContent"></div>
235
+ </div>
236
+
237
+ <!-- Confidence Impact -->
238
+ <div id="confidenceSection" class="hidden">
239
+ <h3>Confidence Impact</h3>
240
+ <div id="confidenceContent"></div>
241
+ </div>
242
+ </div>
243
+ </div>
244
+
245
+ <script>
246
+ // Helper function to get the base path for API calls
247
+ function getBasePath() {
248
+ const baseTag = document.querySelector('base');
249
+ if (baseTag && baseTag.href) {
250
+ try {
251
+ const baseUrl = new URL(baseTag.href, window.location.href);
252
+ let path = baseUrl.pathname;
253
+ if (path && !path.endsWith('/')) path += '/';
254
+ return path;
255
+ } catch (e) {
256
+ // fallback
257
+ }
258
+ }
259
+ const pathname = window.location.pathname;
260
+ const dirPath = pathname.substring(0, pathname.lastIndexOf('/') + 1);
261
+ return dirPath || '/';
262
+ }
263
+
264
+ function getAuthHeaders() {
265
+ const token = localStorage.getItem('auth_token');
266
+ const headers = { 'Content-Type': 'application/json' };
267
+ if (token) headers['Authorization'] = `Bearer ${token}`;
268
+ return headers;
269
+ }
270
+
271
+ const basePath = getBasePath();
272
+ let testData = null;
273
+
274
+ // Load versions
275
+ function loadVersions() {
276
+ fetch(`${basePath}api/versions`, { headers: getAuthHeaders() })
277
+ .then(response => response.json())
278
+ .then(data => {
279
+ const baselineSelect = document.getElementById('baselineVersion');
280
+ const proposedSelect = document.getElementById('proposedVersion');
281
+
282
+ baselineSelect.innerHTML = '<option value="">-- Select Baseline Version --</option>';
283
+ proposedSelect.innerHTML = '<option value="">-- Select Proposed Version --</option>';
284
+
285
+ if (data.versions) {
286
+ data.versions.forEach(v => {
287
+ const option1 = document.createElement('option');
288
+ option1.value = v.id;
289
+ option1.textContent = `${v.rule_id} v${v.version_number} (${v.status})`;
290
+ baselineSelect.appendChild(option1);
291
+
292
+ const option2 = document.createElement('option');
293
+ option2.value = v.id;
294
+ option2.textContent = `${v.rule_id} v${v.version_number} (${v.status})`;
295
+ proposedSelect.appendChild(option2);
296
+ });
297
+ }
298
+ })
299
+ .catch(error => console.error('Error loading versions:', error));
300
+ }
301
+
302
+ loadVersions();
303
+
304
+ // File upload
305
+ const uploadArea = document.getElementById('uploadArea');
306
+ const fileInput = document.getElementById('fileInput');
307
+ const uploadStatus = document.getElementById('uploadStatus');
308
+
309
+ uploadArea.addEventListener('click', () => fileInput.click());
310
+ uploadArea.addEventListener('dragover', (e) => {
311
+ e.preventDefault();
312
+ uploadArea.classList.add('dragover');
313
+ });
314
+ uploadArea.addEventListener('dragleave', () => {
315
+ uploadArea.classList.remove('dragover');
316
+ });
317
+ uploadArea.addEventListener('drop', (e) => {
318
+ e.preventDefault();
319
+ uploadArea.classList.remove('dragover');
320
+ const files = e.dataTransfer.files;
321
+ if (files.length > 0) {
322
+ handleFileUpload(files[0]);
323
+ }
324
+ });
325
+ fileInput.addEventListener('change', (e) => {
326
+ if (e.target.files.length > 0) {
327
+ handleFileUpload(e.target.files[0]);
328
+ }
329
+ });
330
+
331
+ function handleFileUpload(file) {
332
+ const reader = new FileReader();
333
+ reader.onload = (e) => {
334
+ try {
335
+ if (file.name.endsWith('.json')) {
336
+ testData = JSON.parse(e.target.result);
337
+ } else if (file.name.endsWith('.csv')) {
338
+ const lines = e.target.result.split('\n');
339
+ const headers = lines[0].split(',');
340
+ testData = lines.slice(1).filter(l => l.trim()).map(line => {
341
+ const values = line.split(',');
342
+ const obj = {};
343
+ headers.forEach((h, i) => {
344
+ obj[h.trim()] = values[i]?.trim() || '';
345
+ });
346
+ return obj;
347
+ });
348
+ }
349
+
350
+ uploadStatus.className = 'success-message';
351
+ uploadStatus.textContent = `Successfully loaded ${testData.length} test contexts`;
352
+ uploadStatus.classList.remove('hidden');
353
+ checkReady();
354
+ } catch (error) {
355
+ uploadStatus.className = 'error-message';
356
+ uploadStatus.textContent = `Error: ${error.message}`;
357
+ uploadStatus.classList.remove('hidden');
358
+ }
359
+ };
360
+ reader.readAsText(file);
361
+ }
362
+
363
+ // Test data JSON input
364
+ document.getElementById('testDataJson').addEventListener('input', (e) => {
365
+ try {
366
+ if (e.target.value.trim()) {
367
+ testData = JSON.parse(e.target.value);
368
+ uploadStatus.className = 'success-message';
369
+ uploadStatus.textContent = `Loaded ${testData.length} test contexts from JSON`;
370
+ uploadStatus.classList.remove('hidden');
371
+ } else {
372
+ testData = null;
373
+ uploadStatus.classList.add('hidden');
374
+ }
375
+ checkReady();
376
+ } catch (error) {
377
+ testData = null;
378
+ uploadStatus.className = 'error-message';
379
+ uploadStatus.textContent = `Invalid JSON: ${error.message}`;
380
+ uploadStatus.classList.remove('hidden');
381
+ }
382
+ });
383
+
384
+ function checkReady() {
385
+ const baseline = document.getElementById('baselineVersion').value;
386
+ const proposed = document.getElementById('proposedVersion').value;
387
+ document.getElementById('runAnalysisBtn').disabled = !(baseline && proposed && testData);
388
+ }
389
+
390
+ document.getElementById('baselineVersion').addEventListener('change', checkReady);
391
+ document.getElementById('proposedVersion').addEventListener('change', checkReady);
392
+
393
+ // Run analysis
394
+ document.getElementById('runAnalysisBtn').addEventListener('click', () => {
395
+ const baselineVersion = document.getElementById('baselineVersion').value;
396
+ const proposedVersion = document.getElementById('proposedVersion').value;
397
+
398
+ if (!baselineVersion || !proposedVersion || !testData) {
399
+ alert('Please select both versions and provide test data');
400
+ return;
401
+ }
402
+
403
+ const runStatus = document.getElementById('runStatus');
404
+ runStatus.className = 'hidden';
405
+
406
+ const requestData = {
407
+ baseline_version: baselineVersion,
408
+ proposed_version: proposedVersion,
409
+ test_data: testData,
410
+ options: {
411
+ calculate_risk: document.getElementById('calculateRisk').checked,
412
+ parallel: document.getElementById('parallelExecution').checked,
413
+ thread_count: parseInt(document.getElementById('threadCount').value) || 4
414
+ }
415
+ };
416
+
417
+ fetch(`${basePath}api/simulation/impact`, {
418
+ method: 'POST',
419
+ headers: getAuthHeaders(),
420
+ body: JSON.stringify(requestData)
421
+ })
422
+ .then(response => response.json())
423
+ .then(data => {
424
+ if (data.error) {
425
+ runStatus.className = 'error-message';
426
+ runStatus.textContent = `Error: ${data.error}`;
427
+ runStatus.classList.remove('hidden');
428
+ } else {
429
+ runStatus.className = 'success-message';
430
+ runStatus.textContent = 'Impact analysis completed successfully!';
431
+ runStatus.classList.remove('hidden');
432
+ displayResults(data);
433
+ }
434
+ })
435
+ .catch(error => {
436
+ runStatus.className = 'error-message';
437
+ runStatus.textContent = `Error: ${error.message}`;
438
+ runStatus.classList.remove('hidden');
439
+ });
440
+ });
441
+
442
+ function displayResults(data) {
443
+ document.getElementById('resultsSection').style.display = 'block';
444
+ const results = data.results;
445
+
446
+ // Risk assessment
447
+ if (results.risk_score !== undefined) {
448
+ const riskLevel = document.getElementById('riskLevel');
449
+ const riskScore = document.getElementById('riskScore');
450
+ riskScore.textContent = results.risk_score.toFixed(3);
451
+
452
+ const level = results.risk_level || 'low';
453
+ riskLevel.textContent = level.toUpperCase();
454
+ riskLevel.className = 'risk-badge risk-' + level;
455
+
456
+ document.getElementById('riskSection').classList.remove('hidden');
457
+ }
458
+
459
+ // Statistics
460
+ if (results.decision_changes !== undefined) {
461
+ const statsGrid = document.getElementById('statisticsGrid');
462
+ statsGrid.innerHTML = `
463
+ <div class="metric-card">
464
+ <div class="metric-value">${results.decision_changes}</div>
465
+ <div class="metric-label">Decision Changes</div>
466
+ </div>
467
+ <div class="metric-card">
468
+ <div class="metric-value">${(results.change_rate * 100).toFixed(2)}%</div>
469
+ <div class="metric-label">Change Rate</div>
470
+ </div>
471
+ `;
472
+ document.getElementById('statisticsSection').classList.remove('hidden');
473
+ }
474
+
475
+ // Decision distribution
476
+ if (results.decision_distribution) {
477
+ const distContent = document.getElementById('distributionContent');
478
+ let html = '<h4>Baseline:</h4><pre>' + JSON.stringify(results.decision_distribution.baseline, null, 2) + '</pre>';
479
+ html += '<h4>Proposed:</h4><pre>' + JSON.stringify(results.decision_distribution.proposed, null, 2) + '</pre>';
480
+ distContent.innerHTML = html;
481
+ document.getElementById('distributionSection').classList.remove('hidden');
482
+ }
483
+
484
+ // Confidence impact
485
+ if (results.confidence_impact) {
486
+ const confContent = document.getElementById('confidenceContent');
487
+ let html = '<pre>' + JSON.stringify(results.confidence_impact, null, 2) + '</pre>';
488
+ confContent.innerHTML = html;
489
+ document.getElementById('confidenceSection').classList.remove('hidden');
490
+ }
491
+ }
492
+ </script>
493
+ </body>
494
+ </html>
495
+