@principles/pd-cli 1.73.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 (298) hide show
  1. package/README.md +90 -0
  2. package/dist/commands/artifact.d.ts +14 -0
  3. package/dist/commands/artifact.d.ts.map +1 -0
  4. package/dist/commands/artifact.js +67 -0
  5. package/dist/commands/artifact.js.map +1 -0
  6. package/dist/commands/candidate.d.ts +83 -0
  7. package/dist/commands/candidate.d.ts.map +1 -0
  8. package/dist/commands/candidate.js +891 -0
  9. package/dist/commands/candidate.js.map +1 -0
  10. package/dist/commands/central-sync.d.ts +10 -0
  11. package/dist/commands/central-sync.d.ts.map +1 -0
  12. package/dist/commands/central-sync.js +32 -0
  13. package/dist/commands/central-sync.js.map +1 -0
  14. package/dist/commands/console.d.ts +9 -0
  15. package/dist/commands/console.d.ts.map +1 -0
  16. package/dist/commands/console.js +114 -0
  17. package/dist/commands/console.js.map +1 -0
  18. package/dist/commands/context.d.ts +7 -0
  19. package/dist/commands/context.d.ts.map +1 -0
  20. package/dist/commands/context.js +55 -0
  21. package/dist/commands/context.js.map +1 -0
  22. package/dist/commands/demo-story-a.d.ts +12 -0
  23. package/dist/commands/demo-story-a.d.ts.map +1 -0
  24. package/dist/commands/demo-story-a.js +175 -0
  25. package/dist/commands/demo-story-a.js.map +1 -0
  26. package/dist/commands/diagnose.d.ts +35 -0
  27. package/dist/commands/diagnose.d.ts.map +1 -0
  28. package/dist/commands/diagnose.js +390 -0
  29. package/dist/commands/diagnose.js.map +1 -0
  30. package/dist/commands/evolution-tasks-list.d.ts +15 -0
  31. package/dist/commands/evolution-tasks-list.d.ts.map +1 -0
  32. package/dist/commands/evolution-tasks-list.js +34 -0
  33. package/dist/commands/evolution-tasks-list.js.map +1 -0
  34. package/dist/commands/evolution-tasks-show.d.ts +14 -0
  35. package/dist/commands/evolution-tasks-show.d.ts.map +1 -0
  36. package/dist/commands/evolution-tasks-show.js +52 -0
  37. package/dist/commands/evolution-tasks-show.js.map +1 -0
  38. package/dist/commands/flow.d.ts +7 -0
  39. package/dist/commands/flow.d.ts.map +1 -0
  40. package/dist/commands/flow.js +57 -0
  41. package/dist/commands/flow.js.map +1 -0
  42. package/dist/commands/health.d.ts +16 -0
  43. package/dist/commands/health.d.ts.map +1 -0
  44. package/dist/commands/health.js +150 -0
  45. package/dist/commands/health.js.map +1 -0
  46. package/dist/commands/history.d.ts +11 -0
  47. package/dist/commands/history.d.ts.map +1 -0
  48. package/dist/commands/history.js +50 -0
  49. package/dist/commands/history.js.map +1 -0
  50. package/dist/commands/legacy-cleanup.d.ts +27 -0
  51. package/dist/commands/legacy-cleanup.d.ts.map +1 -0
  52. package/dist/commands/legacy-cleanup.js +171 -0
  53. package/dist/commands/legacy-cleanup.js.map +1 -0
  54. package/dist/commands/legacy-import.d.ts +7 -0
  55. package/dist/commands/legacy-import.d.ts.map +1 -0
  56. package/dist/commands/legacy-import.js +86 -0
  57. package/dist/commands/legacy-import.js.map +1 -0
  58. package/dist/commands/pain-record.d.ts +10 -0
  59. package/dist/commands/pain-record.d.ts.map +1 -0
  60. package/dist/commands/pain-record.js +162 -0
  61. package/dist/commands/pain-record.js.map +1 -0
  62. package/dist/commands/proven-channel-baseline.d.ts +12 -0
  63. package/dist/commands/proven-channel-baseline.d.ts.map +1 -0
  64. package/dist/commands/proven-channel-baseline.js +97 -0
  65. package/dist/commands/proven-channel-baseline.js.map +1 -0
  66. package/dist/commands/remediation-output.d.ts +40 -0
  67. package/dist/commands/remediation-output.d.ts.map +1 -0
  68. package/dist/commands/remediation-output.js +23 -0
  69. package/dist/commands/remediation-output.js.map +1 -0
  70. package/dist/commands/run.d.ts +10 -0
  71. package/dist/commands/run.d.ts.map +1 -0
  72. package/dist/commands/run.js +68 -0
  73. package/dist/commands/run.js.map +1 -0
  74. package/dist/commands/runtime-activation.d.ts +11 -0
  75. package/dist/commands/runtime-activation.d.ts.map +1 -0
  76. package/dist/commands/runtime-activation.js +150 -0
  77. package/dist/commands/runtime-activation.js.map +1 -0
  78. package/dist/commands/runtime-canary.d.ts +30 -0
  79. package/dist/commands/runtime-canary.d.ts.map +1 -0
  80. package/dist/commands/runtime-canary.js +343 -0
  81. package/dist/commands/runtime-canary.js.map +1 -0
  82. package/dist/commands/runtime-diagnostics-export.d.ts +20 -0
  83. package/dist/commands/runtime-diagnostics-export.d.ts.map +1 -0
  84. package/dist/commands/runtime-diagnostics-export.js +177 -0
  85. package/dist/commands/runtime-diagnostics-export.js.map +1 -0
  86. package/dist/commands/runtime-features.d.ts +26 -0
  87. package/dist/commands/runtime-features.d.ts.map +1 -0
  88. package/dist/commands/runtime-features.js +70 -0
  89. package/dist/commands/runtime-features.js.map +1 -0
  90. package/dist/commands/runtime-gfi-snapshot.d.ts +7 -0
  91. package/dist/commands/runtime-gfi-snapshot.d.ts.map +1 -0
  92. package/dist/commands/runtime-gfi-snapshot.js +101 -0
  93. package/dist/commands/runtime-gfi-snapshot.js.map +1 -0
  94. package/dist/commands/runtime-health-snapshot.d.ts +7 -0
  95. package/dist/commands/runtime-health-snapshot.d.ts.map +1 -0
  96. package/dist/commands/runtime-health-snapshot.js +93 -0
  97. package/dist/commands/runtime-health-snapshot.js.map +1 -0
  98. package/dist/commands/runtime-idle-trigger.d.ts +12 -0
  99. package/dist/commands/runtime-idle-trigger.d.ts.map +1 -0
  100. package/dist/commands/runtime-idle-trigger.js +102 -0
  101. package/dist/commands/runtime-idle-trigger.js.map +1 -0
  102. package/dist/commands/runtime-internalization-enqueue-successors.d.ts +9 -0
  103. package/dist/commands/runtime-internalization-enqueue-successors.d.ts.map +1 -0
  104. package/dist/commands/runtime-internalization-enqueue-successors.js +393 -0
  105. package/dist/commands/runtime-internalization-enqueue-successors.js.map +1 -0
  106. package/dist/commands/runtime-internalization-integrity-repair.d.ts +9 -0
  107. package/dist/commands/runtime-internalization-integrity-repair.d.ts.map +1 -0
  108. package/dist/commands/runtime-internalization-integrity-repair.js +54 -0
  109. package/dist/commands/runtime-internalization-integrity-repair.js.map +1 -0
  110. package/dist/commands/runtime-internalization-integrity.d.ts +7 -0
  111. package/dist/commands/runtime-internalization-integrity.d.ts.map +1 -0
  112. package/dist/commands/runtime-internalization-integrity.js +53 -0
  113. package/dist/commands/runtime-internalization-integrity.js.map +1 -0
  114. package/dist/commands/runtime-internalization-queue.d.ts +7 -0
  115. package/dist/commands/runtime-internalization-queue.d.ts.map +1 -0
  116. package/dist/commands/runtime-internalization-queue.js +85 -0
  117. package/dist/commands/runtime-internalization-queue.js.map +1 -0
  118. package/dist/commands/runtime-internalization-run-once.d.ts +12 -0
  119. package/dist/commands/runtime-internalization-run-once.d.ts.map +1 -0
  120. package/dist/commands/runtime-internalization-run-once.js +546 -0
  121. package/dist/commands/runtime-internalization-run-once.js.map +1 -0
  122. package/dist/commands/runtime-internalization-wake-once.d.ts +8 -0
  123. package/dist/commands/runtime-internalization-wake-once.d.ts.map +1 -0
  124. package/dist/commands/runtime-internalization-wake-once.js +72 -0
  125. package/dist/commands/runtime-internalization-wake-once.js.map +1 -0
  126. package/dist/commands/runtime-pain-flood-simulation.d.ts +10 -0
  127. package/dist/commands/runtime-pain-flood-simulation.d.ts.map +1 -0
  128. package/dist/commands/runtime-pain-flood-simulation.js +104 -0
  129. package/dist/commands/runtime-pain-flood-simulation.js.map +1 -0
  130. package/dist/commands/runtime-pruning.d.ts +45 -0
  131. package/dist/commands/runtime-pruning.d.ts.map +1 -0
  132. package/dist/commands/runtime-pruning.js +355 -0
  133. package/dist/commands/runtime-pruning.js.map +1 -0
  134. package/dist/commands/runtime-recovery.d.ts +9 -0
  135. package/dist/commands/runtime-recovery.d.ts.map +1 -0
  136. package/dist/commands/runtime-recovery.js +94 -0
  137. package/dist/commands/runtime-recovery.js.map +1 -0
  138. package/dist/commands/runtime-synthetic-baseline.d.ts +7 -0
  139. package/dist/commands/runtime-synthetic-baseline.d.ts.map +1 -0
  140. package/dist/commands/runtime-synthetic-baseline.js +59 -0
  141. package/dist/commands/runtime-synthetic-baseline.js.map +1 -0
  142. package/dist/commands/runtime-uat.d.ts +52 -0
  143. package/dist/commands/runtime-uat.d.ts.map +1 -0
  144. package/dist/commands/runtime-uat.js +274 -0
  145. package/dist/commands/runtime-uat.js.map +1 -0
  146. package/dist/commands/runtime.d.ts +20 -0
  147. package/dist/commands/runtime.d.ts.map +1 -0
  148. package/dist/commands/runtime.js +256 -0
  149. package/dist/commands/runtime.js.map +1 -0
  150. package/dist/commands/samples-list.d.ts +11 -0
  151. package/dist/commands/samples-list.d.ts.map +1 -0
  152. package/dist/commands/samples-list.js +37 -0
  153. package/dist/commands/samples-list.js.map +1 -0
  154. package/dist/commands/samples-review.d.ts +14 -0
  155. package/dist/commands/samples-review.d.ts.map +1 -0
  156. package/dist/commands/samples-review.js +22 -0
  157. package/dist/commands/samples-review.js.map +1 -0
  158. package/dist/commands/task.d.ts +14 -0
  159. package/dist/commands/task.d.ts.map +1 -0
  160. package/dist/commands/task.js +92 -0
  161. package/dist/commands/task.js.map +1 -0
  162. package/dist/commands/trace.d.ts +19 -0
  163. package/dist/commands/trace.d.ts.map +1 -0
  164. package/dist/commands/trace.js +154 -0
  165. package/dist/commands/trace.js.map +1 -0
  166. package/dist/commands/trajectory.d.ts +11 -0
  167. package/dist/commands/trajectory.d.ts.map +1 -0
  168. package/dist/commands/trajectory.js +47 -0
  169. package/dist/commands/trajectory.js.map +1 -0
  170. package/dist/index.d.ts +9 -0
  171. package/dist/index.d.ts.map +1 -0
  172. package/dist/index.js +736 -0
  173. package/dist/index.js.map +1 -0
  174. package/dist/legacy/legacy-import.d.ts +15 -0
  175. package/dist/legacy/legacy-import.d.ts.map +1 -0
  176. package/dist/legacy/legacy-import.js +141 -0
  177. package/dist/legacy/legacy-import.js.map +1 -0
  178. package/dist/legacy/session-history-import.d.ts +26 -0
  179. package/dist/legacy/session-history-import.d.ts.map +1 -0
  180. package/dist/legacy/session-history-import.js +151 -0
  181. package/dist/legacy/session-history-import.js.map +1 -0
  182. package/dist/principle-tree-ledger-adapter.d.ts +12 -0
  183. package/dist/principle-tree-ledger-adapter.d.ts.map +1 -0
  184. package/dist/principle-tree-ledger-adapter.js +12 -0
  185. package/dist/principle-tree-ledger-adapter.js.map +1 -0
  186. package/dist/resolve-workspace.d.ts +12 -0
  187. package/dist/resolve-workspace.d.ts.map +1 -0
  188. package/dist/resolve-workspace.js +20 -0
  189. package/dist/resolve-workspace.js.map +1 -0
  190. package/dist/services/demo-story-a-runner.d.ts +8 -0
  191. package/dist/services/demo-story-a-runner.d.ts.map +1 -0
  192. package/dist/services/demo-story-a-runner.js +369 -0
  193. package/dist/services/demo-story-a-runner.js.map +1 -0
  194. package/dist/services/feature-flag-loader.d.ts +6 -0
  195. package/dist/services/feature-flag-loader.d.ts.map +1 -0
  196. package/dist/services/feature-flag-loader.js +54 -0
  197. package/dist/services/feature-flag-loader.js.map +1 -0
  198. package/dist/services/pain-flood-simulation-runner.d.ts +10 -0
  199. package/dist/services/pain-flood-simulation-runner.d.ts.map +1 -0
  200. package/dist/services/pain-flood-simulation-runner.js +289 -0
  201. package/dist/services/pain-flood-simulation-runner.js.map +1 -0
  202. package/dist/services/proven-channel-baseline-runner.d.ts +12 -0
  203. package/dist/services/proven-channel-baseline-runner.d.ts.map +1 -0
  204. package/dist/services/proven-channel-baseline-runner.js +114 -0
  205. package/dist/services/proven-channel-baseline-runner.js.map +1 -0
  206. package/dist/services/synthetic-baseline-runner.d.ts +8 -0
  207. package/dist/services/synthetic-baseline-runner.d.ts.map +1 -0
  208. package/dist/services/synthetic-baseline-runner.js +251 -0
  209. package/dist/services/synthetic-baseline-runner.js.map +1 -0
  210. package/package.json +35 -0
  211. package/src/commands/artifact.ts +82 -0
  212. package/src/commands/candidate.ts +1117 -0
  213. package/src/commands/central-sync.ts +44 -0
  214. package/src/commands/console.ts +121 -0
  215. package/src/commands/context.ts +72 -0
  216. package/src/commands/demo-story-a.ts +195 -0
  217. package/src/commands/diagnose.ts +452 -0
  218. package/src/commands/evolution-tasks-list.ts +44 -0
  219. package/src/commands/evolution-tasks-show.ts +60 -0
  220. package/src/commands/flow.ts +60 -0
  221. package/src/commands/health.ts +189 -0
  222. package/src/commands/history.ts +63 -0
  223. package/src/commands/legacy-cleanup.ts +206 -0
  224. package/src/commands/legacy-import.ts +104 -0
  225. package/src/commands/pain-record.ts +167 -0
  226. package/src/commands/proven-channel-baseline.ts +113 -0
  227. package/src/commands/remediation-output.ts +66 -0
  228. package/src/commands/run.ts +89 -0
  229. package/src/commands/runtime-activation.ts +176 -0
  230. package/src/commands/runtime-canary.ts +371 -0
  231. package/src/commands/runtime-diagnostics-export.ts +229 -0
  232. package/src/commands/runtime-features.ts +103 -0
  233. package/src/commands/runtime-gfi-snapshot.ts +135 -0
  234. package/src/commands/runtime-health-snapshot.ts +106 -0
  235. package/src/commands/runtime-internalization-enqueue-successors.ts +479 -0
  236. package/src/commands/runtime-internalization-integrity-repair.ts +69 -0
  237. package/src/commands/runtime-internalization-integrity.ts +63 -0
  238. package/src/commands/runtime-internalization-queue.ts +106 -0
  239. package/src/commands/runtime-internalization-run-once.ts +658 -0
  240. package/src/commands/runtime-internalization-wake-once.ts +87 -0
  241. package/src/commands/runtime-pain-flood-simulation.ts +121 -0
  242. package/src/commands/runtime-pruning.ts +438 -0
  243. package/src/commands/runtime-recovery.ts +107 -0
  244. package/src/commands/runtime-synthetic-baseline.ts +70 -0
  245. package/src/commands/runtime-uat.ts +339 -0
  246. package/src/commands/runtime.ts +281 -0
  247. package/src/commands/samples-list.ts +43 -0
  248. package/src/commands/samples-review.ts +32 -0
  249. package/src/commands/task.ts +130 -0
  250. package/src/commands/trace.ts +174 -0
  251. package/src/commands/trajectory.ts +64 -0
  252. package/src/index.ts +829 -0
  253. package/src/legacy/legacy-import.ts +179 -0
  254. package/src/legacy/session-history-import.ts +231 -0
  255. package/src/principle-tree-ledger-adapter.ts +13 -0
  256. package/src/resolve-workspace.ts +20 -0
  257. package/src/services/demo-story-a-runner.ts +472 -0
  258. package/src/services/feature-flag-loader.ts +73 -0
  259. package/src/services/pain-flood-simulation-runner.ts +354 -0
  260. package/src/services/proven-channel-baseline-runner.ts +150 -0
  261. package/src/services/synthetic-baseline-runner.ts +291 -0
  262. package/tests/commands/candidate-audit-repair.test.ts +338 -0
  263. package/tests/commands/candidate-intake.test.ts +589 -0
  264. package/tests/commands/candidate-internalization-backfill.test.ts +480 -0
  265. package/tests/commands/candidate-internalize.test.ts +272 -0
  266. package/tests/commands/candidate-route.test.ts +328 -0
  267. package/tests/commands/candidate-show.test.ts +95 -0
  268. package/tests/commands/cli-command-tree.test.ts +64 -0
  269. package/tests/commands/context.test.ts +114 -0
  270. package/tests/commands/demo-story-a.test.ts +255 -0
  271. package/tests/commands/diagnose.test.ts +792 -0
  272. package/tests/commands/health.test.ts +330 -0
  273. package/tests/commands/pain-record.test.ts +316 -0
  274. package/tests/commands/plugin-config-resolution-cutover.test.ts +220 -0
  275. package/tests/commands/proven-channel-baseline.test.ts +441 -0
  276. package/tests/commands/runtime-activation.test.ts +168 -0
  277. package/tests/commands/runtime-canary.test.ts +369 -0
  278. package/tests/commands/runtime-diagnostics-export.test.ts +170 -0
  279. package/tests/commands/runtime-features.test.ts +114 -0
  280. package/tests/commands/runtime-health-snapshot.test.ts +357 -0
  281. package/tests/commands/runtime-internalization-enqueue-successors.test.ts +803 -0
  282. package/tests/commands/runtime-internalization-integrity-repair.test.ts +169 -0
  283. package/tests/commands/runtime-internalization-integrity.test.ts +102 -0
  284. package/tests/commands/runtime-internalization-queue.test.ts +252 -0
  285. package/tests/commands/runtime-internalization-run-once.test.ts +1318 -0
  286. package/tests/commands/runtime-internalization-wake-once.test.ts +170 -0
  287. package/tests/commands/runtime-internalization.test.ts +52 -0
  288. package/tests/commands/runtime-pain-flood-simulation.test.ts +418 -0
  289. package/tests/commands/runtime-pruning.test.ts +693 -0
  290. package/tests/commands/runtime-recovery.test.ts +96 -0
  291. package/tests/commands/runtime-synthetic-baseline.test.ts +249 -0
  292. package/tests/commands/runtime-uat.test.ts +397 -0
  293. package/tests/commands/runtime.test.ts +262 -0
  294. package/tests/commands/trace.test.ts +314 -0
  295. package/tests/e2e/candidate-intake-e2e.test.ts +316 -0
  296. package/tests/services/feature-flag-loader.test.ts +207 -0
  297. package/tests/services/proven-channel-baseline-runner.test.ts +30 -0
  298. package/tsconfig.json +26 -0
@@ -0,0 +1,354 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import { RuntimeStateManager } from '@principles/core/runtime-v2';
4
+ import { SqliteContextAssembler } from '@principles/core/runtime-v2';
5
+ import { SqliteHistoryQuery } from '@principles/core/runtime-v2';
6
+ import { StoreEventEmitter } from '@principles/core/runtime-v2';
7
+ import { DiagnosticianRunner } from '@principles/core/runtime-v2';
8
+ import { PassThroughValidator } from '@principles/core/runtime-v2';
9
+ import { SqliteDiagnosticianCommitter } from '@principles/core/runtime-v2';
10
+ import { TestDoubleRuntimeAdapter } from '@principles/core/runtime-v2';
11
+ import { PainSignalBridge } from '@principles/core/runtime-v2';
12
+ import { CandidateIntakeService } from '@principles/core/runtime-v2';
13
+ import { PrincipleTreeLedgerAdapter } from '@principles/core/runtime-v2';
14
+ import { makeDeterministicDiagnosticianOutput } from '@principles/core/runtime-v2';
15
+ import type {
16
+ PainFloodSimulationSummary,
17
+ PainFloodStage,
18
+ PainFloodScenarioName,
19
+ } from '@principles/core/runtime-v2';
20
+ import {
21
+ computeFloodStatus,
22
+ computeFloodTotals,
23
+ recommendFloodNextIssue,
24
+ boundedFloodEvidence,
25
+ maxEvidencePreviewLength,
26
+ formatContextBudgetSummary,
27
+ truncateReason,
28
+ FLOOD_SCENARIO_EXPECTATIONS,
29
+ computeMaxAllowedTasks,
30
+ } from '@principles/core/runtime-v2';
31
+
32
+ export interface PainFloodSimulationRunnerOptions {
33
+ workspaceDir: string;
34
+ workspaceMode: 'temp' | 'explicit_workspace';
35
+ identicalCount?: number;
36
+ similarCount?: number;
37
+ stressCount?: number;
38
+ }
39
+
40
+ function generatePainId(prefix: string, index: number): string {
41
+ return `flood-${prefix}-${index}`;
42
+ }
43
+
44
+ function generateSimilarReason(index: number): string {
45
+ return `Flood simulation pain signal #${index}: deterministic test message with small variation.`;
46
+ }
47
+
48
+ type PainSignalInput = { painId: string; painType: 'tool_failure' | 'subagent_error'; reason: string };
49
+
50
+ interface RunScenarioOptions {
51
+ stateManager: RuntimeStateManager;
52
+ bridge: PainSignalBridge;
53
+ }
54
+
55
+ async function runScenario(
56
+ deps: RunScenarioOptions,
57
+ scenarioName: PainFloodScenarioName,
58
+ painSignals: PainSignalInput[],
59
+ ): Promise<PainFloodStage> {
60
+ const { stateManager, bridge } = deps;
61
+ const errors: string[] = [];
62
+ let skippedDuplicateCount = 0;
63
+ let failedSignalCount = 0;
64
+ const seenTaskIds = new Set<string>();
65
+
66
+ const tasksBefore = await stateManager.listTasks();
67
+ let candidatesBefore = 0;
68
+ for (const task of tasksBefore) {
69
+ const candidates = await stateManager.getCandidatesByTaskId(task.taskId);
70
+ candidatesBefore += candidates.length;
71
+ }
72
+
73
+ for (const signal of painSignals) {
74
+ try {
75
+ const result = await bridge.onPainDetected({
76
+ painId: signal.painId,
77
+ painType: signal.painType,
78
+ source: 'pain-flood-simulation',
79
+ reason: signal.reason,
80
+ });
81
+
82
+ if (result.status === 'failed' || result.status === 'retried') {
83
+ failedSignalCount++;
84
+ errors.push(`${signal.painId}: bridge returned ${result.status} — ${result.message ?? 'no message'}`);
85
+ } else if (result.status === 'skipped') {
86
+ skippedDuplicateCount++;
87
+ } else if (result.status === 'succeeded') {
88
+ if (seenTaskIds.has(result.taskId)) {
89
+ skippedDuplicateCount++;
90
+ } else {
91
+ seenTaskIds.add(result.taskId);
92
+ }
93
+ }
94
+ } catch (err) {
95
+ failedSignalCount++;
96
+ errors.push(`${signal.painId}: threw — ${err instanceof Error ? err.message : String(err)}`);
97
+ }
98
+ }
99
+
100
+ const tasksAfter = await stateManager.listTasks();
101
+ let candidatesAfter = 0;
102
+ for (const task of tasksAfter) {
103
+ const candidates = await stateManager.getCandidatesByTaskId(task.taskId);
104
+ candidatesAfter += candidates.length;
105
+ }
106
+
107
+ const deltaTasks = tasksAfter.length - tasksBefore.length;
108
+ const deltaCandidates = candidatesAfter - candidatesBefore;
109
+
110
+ const expectation = FLOOD_SCENARIO_EXPECTATIONS[scenarioName];
111
+ const maxAllowedTasks = computeMaxAllowedTasks(painSignals, expectation);
112
+ const dedupeViolation = deltaTasks > maxAllowedTasks;
113
+
114
+ const passed = errors.length === 0 && !dedupeViolation;
115
+
116
+ const stage: PainFloodStage = {
117
+ scenarioName,
118
+ status: passed ? 'passed' : 'failed',
119
+ inputCount: painSignals.length,
120
+ acceptedCount: deltaTasks,
121
+ skippedCount: skippedDuplicateCount,
122
+ failedCount: failedSignalCount,
123
+ taskCount: deltaTasks,
124
+ candidateCount: deltaCandidates,
125
+ evidence: boundedFloodEvidence({
126
+ inputCount: painSignals.length,
127
+ acceptedCount: deltaTasks,
128
+ skippedCount: skippedDuplicateCount,
129
+ failedCount: failedSignalCount,
130
+ uniqueTaskIds: seenTaskIds.size,
131
+ deltaTasks,
132
+ deltaCandidates,
133
+ errorCount: errors.length,
134
+ }),
135
+ };
136
+
137
+ if (errors.length > 0) {
138
+ stage.reason = truncateReason(`Scenario ${scenarioName}: ${errors.length} errors. First: ${errors[0]}`);
139
+ } else if (dedupeViolation) {
140
+ stage.reason = truncateReason(`Scenario ${scenarioName}: dedupe violation — created ${deltaTasks} tasks, expected at most ${maxAllowedTasks} (${expectation.description})`);
141
+ }
142
+
143
+ return stage;
144
+ }
145
+
146
+ export async function runPainFloodSimulation(opts: PainFloodSimulationRunnerOptions): Promise<PainFloodSimulationSummary> {
147
+ const { workspaceDir, workspaceMode } = opts;
148
+ const identicalCount = opts.identicalCount ?? 10;
149
+ const similarCount = opts.similarCount ?? 10;
150
+ const stressCount = opts.stressCount ?? 50;
151
+
152
+ const stages: PainFloodStage[] = [];
153
+ const generatedAt = new Date().toISOString();
154
+
155
+ const pdDir = path.join(workspaceDir, '.pd');
156
+ const stateDir = path.join(workspaceDir, '.state');
157
+
158
+ let stateManager: RuntimeStateManager | undefined = undefined;
159
+
160
+ try {
161
+ await fs.promises.mkdir(pdDir, { recursive: true });
162
+ await fs.promises.mkdir(stateDir, { recursive: true });
163
+
164
+ stateManager = new RuntimeStateManager({ workspaceDir });
165
+ await stateManager.initialize();
166
+
167
+ // Create shared infrastructure
168
+ const { connection: sqliteConn, taskStore, runStore } = stateManager;
169
+ const historyQuery = new SqliteHistoryQuery(sqliteConn);
170
+ const contextAssembler = new SqliteContextAssembler(taskStore, historyQuery, runStore);
171
+ const eventEmitter = new StoreEventEmitter();
172
+ const committer = new SqliteDiagnosticianCommitter(sqliteConn);
173
+ const validator = new PassThroughValidator();
174
+
175
+ // Single test-double adapter handles all pains with deterministic output
176
+ const runtimeAdapter = new TestDoubleRuntimeAdapter({
177
+ onFetchOutput: (runId: string) => ({
178
+ runId,
179
+ payload: makeDeterministicDiagnosticianOutput(runId),
180
+ }),
181
+ });
182
+
183
+ const runner = new DiagnosticianRunner(
184
+ {
185
+ stateManager,
186
+ contextAssembler,
187
+ runtimeAdapter,
188
+ eventEmitter,
189
+ validator,
190
+ committer,
191
+ },
192
+ {
193
+ owner: 'pain-flood-simulation',
194
+ runtimeKind: 'test-double',
195
+ pollIntervalMs: 50,
196
+ timeoutMs: 10000,
197
+ },
198
+ );
199
+
200
+ const ledgerAdapter = new PrincipleTreeLedgerAdapter({ stateDir });
201
+ const intakeService = new CandidateIntakeService({ stateManager, ledgerAdapter });
202
+
203
+ const bridge = new PainSignalBridge({
204
+ stateManager,
205
+ runner,
206
+ intakeService,
207
+ ledgerAdapter,
208
+ autoIntakeEnabled: true,
209
+ });
210
+
211
+ // ── Scenario 1: Identical flood ──────────────────────────────────────────────
212
+ const identicalPainId = 'flood-identical-shared';
213
+ const identicalSignals = Array.from({ length: identicalCount }, () => ({
214
+ painId: identicalPainId,
215
+ painType: 'tool_failure' as const,
216
+ reason: 'Identical pain flood: repeated tool failure signal',
217
+ }));
218
+ stages.push(await runScenario({ stateManager, bridge }, 'identical_flood', identicalSignals));
219
+
220
+ // ── Scenario 2: Similar flood ────────────────────────────────────────────────
221
+ const similarSignals = Array.from({ length: similarCount }, (_, i) => ({
222
+ painId: generatePainId('similar', i),
223
+ painType: 'tool_failure' as const,
224
+ reason: generateSimilarReason(i),
225
+ }));
226
+ stages.push(await runScenario({ stateManager, bridge }, 'similar_flood', similarSignals));
227
+
228
+ // ── Scenario 3: Duplicate submission ─────────────────────────────────────────
229
+ const dupPainId = 'flood-dup-test';
230
+ const dupSignals = [
231
+ { painId: dupPainId, painType: 'tool_failure' as const, reason: 'Duplicate submission: first' },
232
+ { painId: dupPainId, painType: 'tool_failure' as const, reason: 'Duplicate submission: repeat' },
233
+ ];
234
+ stages.push(await runScenario({ stateManager, bridge }, 'duplicate_submission', dupSignals));
235
+
236
+ // ── Scenario 4: Tool failure flood ───────────────────────────────────────────
237
+ const toolFailPainId = 'flood-tool-failure-shared';
238
+ const toolFailSignals = Array.from({ length: 5 }, (_, i) => ({
239
+ painId: toolFailPainId,
240
+ painType: 'tool_failure' as const,
241
+ reason: `Tool failure flood #${i}: EACCES error on file write`,
242
+ }));
243
+ stages.push(await runScenario({ stateManager, bridge }, 'tool_failure_flood', toolFailSignals));
244
+
245
+ // ── Scenario 5: Stress test ──────────────────────────────────────────────────
246
+ const stressSignals: PainSignalInput[] = [];
247
+ // 60% unique, 40% duplicates using shared painIds
248
+ const stressUniqueCount = Math.floor(stressCount * 0.6);
249
+ const stressDupCount = stressCount - stressUniqueCount;
250
+
251
+ for (let i = 0; i < stressUniqueCount; i++) {
252
+ stressSignals.push({
253
+ painId: generatePainId('stress-unique', i),
254
+ painType: i % 3 === 0 ? 'subagent_error' : 'tool_failure',
255
+ reason: `Stress test unique pain #${i}`,
256
+ });
257
+ }
258
+ // Add duplicate batches using shared painIds
259
+ const sharedIds = ['flood-stress-shared-A', 'flood-stress-shared-B', 'flood-stress-shared-C'];
260
+ for (let i = 0; i < stressDupCount; i++) {
261
+ const sharedId = sharedIds[i % sharedIds.length];
262
+ stressSignals.push({
263
+ painId: sharedId,
264
+ painType: 'tool_failure',
265
+ reason: `Stress test duplicate batch ${i}`,
266
+ });
267
+ }
268
+
269
+ stages.push(await runScenario({ stateManager, bridge }, 'stress_test', stressSignals));
270
+
271
+ // ── Build summary ────────────────────────────────────────────────────────────
272
+ const totals = computeFloodTotals(stages);
273
+ const maxPreview = maxEvidencePreviewLength(stages);
274
+ const status = computeFloodStatus(stages);
275
+ const recommendedNextIssue = recommendFloodNextIssue(stages);
276
+
277
+ const failedStages = stages.filter(s => s.status === 'failed');
278
+ const reason = failedStages.length > 0
279
+ ? truncateReason(`${failedStages.length} scenario(s) failed: ${failedStages.map(s => s.scenarioName).join(', ')}. ${failedStages[0].reason ?? 'unknown reason'}`)
280
+ : undefined;
281
+ const nextAction = recommendedNextIssue
282
+ ? `Investigate: ${recommendedNextIssue}`
283
+ : undefined;
284
+
285
+ return {
286
+ status,
287
+ workspaceMode,
288
+ generatedAt,
289
+ inputPainCount: totals.inputPainCount,
290
+ acceptedPainCount: totals.acceptedPainCount,
291
+ skippedDuplicateCount: totals.skippedDuplicateCount,
292
+ failedCount: totals.failedCount,
293
+ candidateCount: totals.candidateCount,
294
+ taskCount: totals.taskCount,
295
+ maxEvidencePreviewLength: maxPreview,
296
+ contextBudgetSummary: formatContextBudgetSummary(maxPreview),
297
+ stages,
298
+ reason,
299
+ nextAction,
300
+ recommendedNextIssue,
301
+ };
302
+ } catch (err) {
303
+ const errorMessage = err instanceof Error ? err.message : String(err);
304
+ const scenarioNames: PainFloodScenarioName[] = [
305
+ 'identical_flood',
306
+ 'similar_flood',
307
+ 'duplicate_submission',
308
+ 'tool_failure_flood',
309
+ 'stress_test',
310
+ ];
311
+ const existingNames = new Set(stages.map(s => s.scenarioName));
312
+ for (const name of scenarioNames) {
313
+ if (!existingNames.has(name)) {
314
+ stages.push({
315
+ scenarioName: name,
316
+ status: 'failed',
317
+ inputCount: 0,
318
+ acceptedCount: 0,
319
+ skippedCount: 0,
320
+ failedCount: 0,
321
+ taskCount: 0,
322
+ candidateCount: 0,
323
+ reason: truncateReason(`Unexpected error: ${errorMessage}`),
324
+ });
325
+ }
326
+ }
327
+
328
+ const partialTotals = computeFloodTotals(stages);
329
+ const partialMaxPreview = maxEvidencePreviewLength(stages);
330
+ const recommendedNextIssue = recommendFloodNextIssue(stages);
331
+
332
+ return {
333
+ status: 'error',
334
+ workspaceMode,
335
+ generatedAt,
336
+ inputPainCount: partialTotals.inputPainCount,
337
+ acceptedPainCount: partialTotals.acceptedPainCount,
338
+ skippedDuplicateCount: partialTotals.skippedDuplicateCount,
339
+ failedCount: partialTotals.failedCount,
340
+ candidateCount: partialTotals.candidateCount,
341
+ taskCount: partialTotals.taskCount,
342
+ maxEvidencePreviewLength: partialMaxPreview,
343
+ contextBudgetSummary: formatContextBudgetSummary(partialMaxPreview),
344
+ stages,
345
+ reason: truncateReason(`Simulation error: ${errorMessage}`),
346
+ nextAction: 'Check workspace permissions and disk space, then re-run',
347
+ recommendedNextIssue,
348
+ };
349
+ } finally {
350
+ if (stateManager) {
351
+ await stateManager.close();
352
+ }
353
+ }
354
+ }
@@ -0,0 +1,150 @@
1
+ import * as path from 'path';
2
+ import * as os from 'os';
3
+ import {
4
+ runPromptFixture,
5
+ runRuleHostFixture,
6
+ runDeferArchiveFixture,
7
+ computeProvenChannelStatus,
8
+ generateContinuityMatrix,
9
+ recommendProvenChannelNextIssue,
10
+ isMvpChannel,
11
+ MVP_CHANNELS,
12
+ } from '@principles/core/runtime-v2';
13
+ import type {
14
+ ChannelFixtureResult,
15
+ ProvenChannelBaselineSummary,
16
+ MvpChannel,
17
+ } from '@principles/core/runtime-v2';
18
+
19
+ export interface ProvenChannelBaselineRunnerOptions {
20
+ workspaceDir: string;
21
+ workspaceMode: 'temp' | 'explicit_workspace';
22
+ channels?: MvpChannel[];
23
+ unknownChannels?: string[];
24
+ emptyChannelInput?: boolean;
25
+ }
26
+
27
+ function isProductionWorkspace(workspaceDir: string): boolean {
28
+ const normalized = path.resolve(workspaceDir).toLowerCase();
29
+ const productionPrefixes = [
30
+ path.resolve('D:\\.openclaw\\workspace').toLowerCase(),
31
+ path.resolve('C:\\Users\\Administrator\\.openclaw\\workspace').toLowerCase(),
32
+ path.resolve(path.join(os.homedir(), '.openclaw', 'workspace')).toLowerCase(),
33
+ ];
34
+ for (const prefix of productionPrefixes) {
35
+ if (normalized === prefix || normalized.startsWith(prefix + path.sep)) {
36
+ return true;
37
+ }
38
+ }
39
+ return false;
40
+ }
41
+
42
+ export async function runProvenChannelBaseline(
43
+ opts: ProvenChannelBaselineRunnerOptions,
44
+ ): Promise<ProvenChannelBaselineSummary> {
45
+ const { workspaceDir, workspaceMode } = opts;
46
+ const channels = opts.channels ?? [...MVP_CHANNELS];
47
+ const unknownChannels = opts.unknownChannels ?? [];
48
+ const emptyChannelInput = opts.emptyChannelInput ?? false;
49
+ const generatedAt = new Date().toISOString();
50
+
51
+ if (emptyChannelInput) {
52
+ return {
53
+ status: 'failed',
54
+ generatedAt,
55
+ workspaceMode,
56
+ channels: [],
57
+ inputValidationFailure: {
58
+ reason: 'empty_channel_input',
59
+ message: '--channels was provided but contained no valid channel names',
60
+ nextAction: 'Provide at least one valid MVP channel: prompt, code_tool_hook, defer_archive',
61
+ },
62
+ continuityMatrix: generateContinuityMatrix(),
63
+ recommendedNextIssue: 'PRI-240: --channels input was empty — no fixtures were executed',
64
+ };
65
+ }
66
+
67
+ if (unknownChannels.length > 0) {
68
+ return {
69
+ status: 'failed',
70
+ generatedAt,
71
+ workspaceMode,
72
+ channels: [],
73
+ inputValidationFailure: {
74
+ reason: 'unknown_channels',
75
+ message: `Unknown channels: ${unknownChannels.join(', ')}. Valid channels: prompt, code_tool_hook, defer_archive`,
76
+ nextAction: 'Use only valid MVP channels: prompt, code_tool_hook, defer_archive',
77
+ unknownChannels,
78
+ },
79
+ continuityMatrix: generateContinuityMatrix(),
80
+ recommendedNextIssue: `PRI-240: Unknown channels provided: ${unknownChannels.join(', ')}`,
81
+ };
82
+ }
83
+
84
+ if (isProductionWorkspace(workspaceDir)) {
85
+ return {
86
+ status: 'failed',
87
+ generatedAt,
88
+ workspaceMode,
89
+ channels: [],
90
+ inputValidationFailure: {
91
+ reason: 'production_workspace_blocked',
92
+ message: 'Baseline must not write to production workspace',
93
+ nextAction: 'Use a temp workspace or explicit non-production directory',
94
+ },
95
+ continuityMatrix: generateContinuityMatrix(),
96
+ recommendedNextIssue: 'PRI-240: Production workspace blocked — use temp workspace',
97
+ };
98
+ }
99
+
100
+ const results: ChannelFixtureResult[] = [];
101
+
102
+ for (const channel of channels) {
103
+ if (!isMvpChannel(channel)) {
104
+ continue;
105
+ }
106
+
107
+ let result: ChannelFixtureResult = {
108
+ channel,
109
+ status: 'failed',
110
+ canActivateResult: { ok: false, reason: 'unsupported_channel', riskLevel: 'low' },
111
+ activationDecision: { decision: 'refused', reason: 'unsupported_channel', channel },
112
+ evidence: {},
113
+ failureReason: `Channel ${channel} is not an MVP channel`,
114
+ nextAction: 'Use one of: prompt, code_tool_hook, defer_archive',
115
+ dependsOnLegacy: false,
116
+ evidenceSource: 'channel_validation',
117
+ };
118
+
119
+ switch (channel) {
120
+ case 'prompt':
121
+ result = await runPromptFixture();
122
+ break;
123
+ case 'code_tool_hook':
124
+ result = await runRuleHostFixture();
125
+ break;
126
+ case 'defer_archive':
127
+ result = await runDeferArchiveFixture();
128
+ break;
129
+ default: {
130
+ const _exhaustive: never = channel;
131
+ }
132
+ }
133
+
134
+ results.push(result);
135
+ }
136
+
137
+ const status = computeProvenChannelStatus(results);
138
+ const continuityMatrix = generateContinuityMatrix();
139
+
140
+ return {
141
+ status,
142
+ generatedAt,
143
+ workspaceMode,
144
+ channels: results,
145
+ continuityMatrix,
146
+ recommendedNextIssue: recommendProvenChannelNextIssue(results),
147
+ };
148
+ }
149
+
150
+ export { isProductionWorkspace };