flakelab 0.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 (245) hide show
  1. package/README.md +341 -0
  2. package/bin/flakelab.mjs +3 -0
  3. package/dist/artifacts/ndjson.d.ts +3 -0
  4. package/dist/artifacts/ndjson.js +16 -0
  5. package/dist/artifacts/ndjson.js.map +1 -0
  6. package/dist/bisect/archive.d.ts +1 -0
  7. package/dist/bisect/archive.js +14 -0
  8. package/dist/bisect/archive.js.map +1 -0
  9. package/dist/bisect/confidence.d.ts +7 -0
  10. package/dist/bisect/confidence.js +27 -0
  11. package/dist/bisect/confidence.js.map +1 -0
  12. package/dist/bisect/engine.d.ts +10 -0
  13. package/dist/bisect/engine.js +110 -0
  14. package/dist/bisect/engine.js.map +1 -0
  15. package/dist/bisect/git.d.ts +7 -0
  16. package/dist/bisect/git.js +56 -0
  17. package/dist/bisect/git.js.map +1 -0
  18. package/dist/bisect/schema.d.ts +82 -0
  19. package/dist/bisect/schema.js +33 -0
  20. package/dist/bisect/schema.js.map +1 -0
  21. package/dist/bisect/solari-command.d.ts +7 -0
  22. package/dist/bisect/solari-command.js +95 -0
  23. package/dist/bisect/solari-command.js.map +1 -0
  24. package/dist/bisect/solari-evaluator.d.ts +20 -0
  25. package/dist/bisect/solari-evaluator.js +310 -0
  26. package/dist/bisect/solari-evaluator.js.map +1 -0
  27. package/dist/ci/changed-tests.d.ts +7 -0
  28. package/dist/ci/changed-tests.js +104 -0
  29. package/dist/ci/changed-tests.js.map +1 -0
  30. package/dist/ci/job-summary.d.ts +11 -0
  31. package/dist/ci/job-summary.js +45 -0
  32. package/dist/ci/job-summary.js.map +1 -0
  33. package/dist/ci/select-command.d.ts +1 -0
  34. package/dist/ci/select-command.js +49 -0
  35. package/dist/ci/select-command.js.map +1 -0
  36. package/dist/ci/summary-command.d.ts +1 -0
  37. package/dist/ci/summary-command.js +46 -0
  38. package/dist/ci/summary-command.js.map +1 -0
  39. package/dist/cli-help.d.ts +2 -0
  40. package/dist/cli-help.js +47 -0
  41. package/dist/cli-help.js.map +1 -0
  42. package/dist/cli.d.ts +1 -0
  43. package/dist/cli.js +121 -0
  44. package/dist/cli.js.map +1 -0
  45. package/dist/commands/bisect.d.ts +2 -0
  46. package/dist/commands/bisect.js +63 -0
  47. package/dist/commands/bisect.js.map +1 -0
  48. package/dist/commands/diagnose.d.ts +2 -0
  49. package/dist/commands/diagnose.js +27 -0
  50. package/dist/commands/diagnose.js.map +1 -0
  51. package/dist/commands/discover.d.ts +2 -0
  52. package/dist/commands/discover.js +46 -0
  53. package/dist/commands/discover.js.map +1 -0
  54. package/dist/commands/doctor.d.ts +8 -0
  55. package/dist/commands/doctor.js +78 -0
  56. package/dist/commands/doctor.js.map +1 -0
  57. package/dist/commands/investigate.d.ts +2 -0
  58. package/dist/commands/investigate.js +43 -0
  59. package/dist/commands/investigate.js.map +1 -0
  60. package/dist/commands/options.d.ts +38 -0
  61. package/dist/commands/options.js +35 -0
  62. package/dist/commands/options.js.map +1 -0
  63. package/dist/commands/prove.d.ts +2 -0
  64. package/dist/commands/prove.js +21 -0
  65. package/dist/commands/prove.js.map +1 -0
  66. package/dist/commands/repair.d.ts +2 -0
  67. package/dist/commands/repair.js +55 -0
  68. package/dist/commands/repair.js.map +1 -0
  69. package/dist/commands/replay.d.ts +2 -0
  70. package/dist/commands/replay.js +26 -0
  71. package/dist/commands/replay.js.map +1 -0
  72. package/dist/commands/report.d.ts +2 -0
  73. package/dist/commands/report.js +79 -0
  74. package/dist/commands/report.js.map +1 -0
  75. package/dist/commands/scan.d.ts +12 -0
  76. package/dist/commands/scan.js +86 -0
  77. package/dist/commands/scan.js.map +1 -0
  78. package/dist/core/plan.d.ts +3 -0
  79. package/dist/core/plan.js +17 -0
  80. package/dist/core/plan.js.map +1 -0
  81. package/dist/discovery/evaluate.d.ts +22 -0
  82. package/dist/discovery/evaluate.js +81 -0
  83. package/dist/discovery/evaluate.js.map +1 -0
  84. package/dist/discovery/minimize.d.ts +24 -0
  85. package/dist/discovery/minimize.js +73 -0
  86. package/dist/discovery/minimize.js.map +1 -0
  87. package/dist/domain/schema.d.ts +155 -0
  88. package/dist/domain/schema.js +67 -0
  89. package/dist/domain/schema.js.map +1 -0
  90. package/dist/faults/install.d.ts +4 -0
  91. package/dist/faults/install.js +9 -0
  92. package/dist/faults/install.js.map +1 -0
  93. package/dist/faults/network-delay.d.ts +5 -0
  94. package/dist/faults/network-delay.js +34 -0
  95. package/dist/faults/network-delay.js.map +1 -0
  96. package/dist/faults/request-failure.d.ts +5 -0
  97. package/dist/faults/request-failure.js +24 -0
  98. package/dist/faults/request-failure.js.map +1 -0
  99. package/dist/investigator/agent.d.ts +26 -0
  100. package/dist/investigator/agent.js +197 -0
  101. package/dist/investigator/agent.js.map +1 -0
  102. package/dist/investigator/budget.d.ts +14 -0
  103. package/dist/investigator/budget.js +39 -0
  104. package/dist/investigator/budget.js.map +1 -0
  105. package/dist/investigator/file.d.ts +2 -0
  106. package/dist/investigator/file.js +7 -0
  107. package/dist/investigator/file.js.map +1 -0
  108. package/dist/investigator/groq.d.ts +5 -0
  109. package/dist/investigator/groq.js +26 -0
  110. package/dist/investigator/groq.js.map +1 -0
  111. package/dist/investigator/ledger.d.ts +17 -0
  112. package/dist/investigator/ledger.js +121 -0
  113. package/dist/investigator/ledger.js.map +1 -0
  114. package/dist/investigator/safe-source.d.ts +6 -0
  115. package/dist/investigator/safe-source.js +79 -0
  116. package/dist/investigator/safe-source.js.map +1 -0
  117. package/dist/investigator/schema.d.ts +110 -0
  118. package/dist/investigator/schema.js +53 -0
  119. package/dist/investigator/schema.js.map +1 -0
  120. package/dist/playwright.d.ts +3 -0
  121. package/dist/playwright.js +21 -0
  122. package/dist/playwright.js.map +1 -0
  123. package/dist/providers/errors.d.ts +21 -0
  124. package/dist/providers/errors.js +159 -0
  125. package/dist/providers/errors.js.map +1 -0
  126. package/dist/repair/file.d.ts +2 -0
  127. package/dist/repair/file.js +6 -0
  128. package/dist/repair/file.js.map +1 -0
  129. package/dist/repair/generator.d.ts +12 -0
  130. package/dist/repair/generator.js +74 -0
  131. package/dist/repair/generator.js.map +1 -0
  132. package/dist/repair/policy.d.ts +2 -0
  133. package/dist/repair/policy.js +70 -0
  134. package/dist/repair/policy.js.map +1 -0
  135. package/dist/repair/remote-proof-runner.d.ts +1 -0
  136. package/dist/repair/remote-proof-runner.js +53 -0
  137. package/dist/repair/remote-proof-runner.js.map +1 -0
  138. package/dist/repair/schema.d.ts +78 -0
  139. package/dist/repair/schema.js +44 -0
  140. package/dist/repair/schema.js.map +1 -0
  141. package/dist/repair/solari-validator.d.ts +25 -0
  142. package/dist/repair/solari-validator.js +231 -0
  143. package/dist/repair/solari-validator.js.map +1 -0
  144. package/dist/repair/validator.d.ts +16 -0
  145. package/dist/repair/validator.js +81 -0
  146. package/dist/repair/validator.js.map +1 -0
  147. package/dist/repair/workspace.d.ts +7 -0
  148. package/dist/repair/workspace.js +40 -0
  149. package/dist/repair/workspace.js.map +1 -0
  150. package/dist/report/browser-entry.d.ts +2 -0
  151. package/dist/report/browser-entry.js +15 -0
  152. package/dist/report/browser-entry.js.map +1 -0
  153. package/dist/report/bundle.d.ts +2 -0
  154. package/dist/report/bundle.js +77 -0
  155. package/dist/report/bundle.js.map +1 -0
  156. package/dist/report/classification.d.ts +9 -0
  157. package/dist/report/classification.js +55 -0
  158. package/dist/report/classification.js.map +1 -0
  159. package/dist/report/components/artifacts.d.ts +4 -0
  160. package/dist/report/components/artifacts.js +6 -0
  161. package/dist/report/components/artifacts.js.map +1 -0
  162. package/dist/report/components/experiments.d.ts +4 -0
  163. package/dist/report/components/experiments.js +7 -0
  164. package/dist/report/components/experiments.js.map +1 -0
  165. package/dist/report/components/hypotheses.d.ts +4 -0
  166. package/dist/report/components/hypotheses.js +26 -0
  167. package/dist/report/components/hypotheses.js.map +1 -0
  168. package/dist/report/components/indicators.d.ts +32 -0
  169. package/dist/report/components/indicators.js +42 -0
  170. package/dist/report/components/indicators.js.map +1 -0
  171. package/dist/report/components/layout.d.ts +29 -0
  172. package/dist/report/components/layout.js +20 -0
  173. package/dist/report/components/layout.js.map +1 -0
  174. package/dist/report/components/proof.d.ts +4 -0
  175. package/dist/report/components/proof.js +34 -0
  176. package/dist/report/components/proof.js.map +1 -0
  177. package/dist/report/components/reproduction.d.ts +4 -0
  178. package/dist/report/components/reproduction.js +19 -0
  179. package/dist/report/components/reproduction.js.map +1 -0
  180. package/dist/report/components/summary.d.ts +4 -0
  181. package/dist/report/components/summary.js +12 -0
  182. package/dist/report/components/summary.js.map +1 -0
  183. package/dist/report/components/verdict.d.ts +4 -0
  184. package/dist/report/components/verdict.js +27 -0
  185. package/dist/report/components/verdict.js.map +1 -0
  186. package/dist/report/model.d.ts +19 -0
  187. package/dist/report/model.js +82 -0
  188. package/dist/report/model.js.map +1 -0
  189. package/dist/report/open.d.ts +1 -0
  190. package/dist/report/open.js +24 -0
  191. package/dist/report/open.js.map +1 -0
  192. package/dist/report/publish.d.ts +12 -0
  193. package/dist/report/publish.js +67 -0
  194. package/dist/report/publish.js.map +1 -0
  195. package/dist/report/redaction.d.ts +1 -0
  196. package/dist/report/redaction.js +58 -0
  197. package/dist/report/redaction.js.map +1 -0
  198. package/dist/report/report-app.d.ts +6 -0
  199. package/dist/report/report-app.js +16 -0
  200. package/dist/report/report-app.js.map +1 -0
  201. package/dist/report/report-details.css +182 -0
  202. package/dist/report/report.css +404 -0
  203. package/dist/report/schema.d.ts +101 -0
  204. package/dist/report/schema.js +71 -0
  205. package/dist/report/schema.js.map +1 -0
  206. package/dist/reproducer/file.d.ts +3 -0
  207. package/dist/reproducer/file.js +14 -0
  208. package/dist/reproducer/file.js.map +1 -0
  209. package/dist/reproducer/schema.d.ts +17 -0
  210. package/dist/reproducer/schema.js +14 -0
  211. package/dist/reproducer/schema.js.map +1 -0
  212. package/dist/runner/local.d.ts +12 -0
  213. package/dist/runner/local.js +72 -0
  214. package/dist/runner/local.js.map +1 -0
  215. package/dist/runner/playwright-executor.d.ts +10 -0
  216. package/dist/runner/playwright-executor.js +148 -0
  217. package/dist/runner/playwright-executor.js.map +1 -0
  218. package/dist/security/credentials.d.ts +23 -0
  219. package/dist/security/credentials.js +105 -0
  220. package/dist/security/credentials.js.map +1 -0
  221. package/dist/solari/checkout-fixture.d.ts +3 -0
  222. package/dist/solari/checkout-fixture.js +54 -0
  223. package/dist/solari/checkout-fixture.js.map +1 -0
  224. package/dist/solari/executor.d.ts +17 -0
  225. package/dist/solari/executor.js +280 -0
  226. package/dist/solari/executor.js.map +1 -0
  227. package/dist/solari/retry.d.ts +7 -0
  228. package/dist/solari/retry.js +50 -0
  229. package/dist/solari/retry.js.map +1 -0
  230. package/dist/solari/run-demo.d.ts +21 -0
  231. package/dist/solari/run-demo.js +64 -0
  232. package/dist/solari/run-demo.js.map +1 -0
  233. package/dist/solari/transport.d.ts +1 -0
  234. package/dist/solari/transport.js +21 -0
  235. package/dist/solari/transport.js.map +1 -0
  236. package/dist/solari/usage.d.ts +23 -0
  237. package/dist/solari/usage.js +53 -0
  238. package/dist/solari/usage.js.map +1 -0
  239. package/dist/solari/websocket-proxy.d.ts +7 -0
  240. package/dist/solari/websocket-proxy.js +131 -0
  241. package/dist/solari/websocket-proxy.js.map +1 -0
  242. package/dist/ui/progress.d.ts +5 -0
  243. package/dist/ui/progress.js +16 -0
  244. package/dist/ui/progress.js.map +1 -0
  245. package/package.json +79 -0
@@ -0,0 +1,17 @@
1
+ const UINT32_RANGE = 0x1_0000_0000;
2
+ const GOLDEN_RATIO_32 = 2_654_435_761;
3
+ export function deriveTrialSeed(seed, index) {
4
+ return (seed + index * GOLDEN_RATIO_32) % UINT32_RANGE;
5
+ }
6
+ export function planTrials(request) {
7
+ return Array.from({ length: request.runs }, (_, index) => {
8
+ const seed = deriveTrialSeed(request.seed, index);
9
+ return {
10
+ trialId: `trial-${String(index + 1).padStart(3, "0")}-${seed}`,
11
+ index,
12
+ seed,
13
+ fault: index % 2 === 1 ? request.fault : undefined,
14
+ };
15
+ });
16
+ }
17
+ //# sourceMappingURL=plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.js","sourceRoot":"","sources":["../../src/core/plan.ts"],"names":[],"mappings":"AAEA,MAAM,YAAY,GAAG,aAAa,CAAA;AAClC,MAAM,eAAe,GAAG,aAAa,CAAA;AAErC,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,KAAa;IACzD,OAAO,CAAC,IAAI,GAAG,KAAK,GAAG,eAAe,CAAC,GAAG,YAAY,CAAA;AACxD,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAmB;IAC5C,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE;QACvD,MAAM,IAAI,GAAG,eAAe,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;QACjD,OAAO;YACL,OAAO,EAAE,SAAS,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,IAAI,EAAE;YAC9D,KAAK;YACL,IAAI;YACJ,KAAK,EAAE,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;SACnD,CAAA;IACH,CAAC,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { Fault } from "../domain/schema.js";
2
+ import type { TrialExecutor } from "../runner/playwright-executor.js";
3
+ export interface ExperimentOptions {
4
+ concurrency: number;
5
+ fault?: Fault;
6
+ minimumFailureRate: number;
7
+ seed: number;
8
+ signal?: AbortSignal;
9
+ trials: number;
10
+ }
11
+ export interface ExperimentResult {
12
+ confirmed: boolean;
13
+ dominantFailureSignature?: string;
14
+ dominantFailureReason?: string;
15
+ errors: number;
16
+ failed: number;
17
+ failureRate: number;
18
+ lowerBound80: number;
19
+ passed: number;
20
+ trials: number;
21
+ }
22
+ export declare function evaluateExperiment(execute: TrialExecutor, options: ExperimentOptions): Promise<ExperimentResult>;
@@ -0,0 +1,81 @@
1
+ import { deriveTrialSeed } from "../core/plan.js";
2
+ const WILSON_Z_80 = 1.281_551_565_545;
3
+ function lowerWilsonBound(failures, trials) {
4
+ if (trials === 0) {
5
+ return 0;
6
+ }
7
+ const probability = failures / trials;
8
+ const squaredZ = WILSON_Z_80 ** 2;
9
+ const denominator = 1 + squaredZ / trials;
10
+ const center = probability + squaredZ / (2 * trials);
11
+ const margin = WILSON_Z_80 * Math.sqrt((probability * (1 - probability) + squaredZ / (4 * trials)) / trials);
12
+ return Math.max(0, (center - margin) / denominator);
13
+ }
14
+ function dominantSignature(outcomes) {
15
+ const counts = new Map();
16
+ for (const outcome of outcomes) {
17
+ if (outcome.status === "failed" && outcome.failureSignature) {
18
+ counts.set(outcome.failureSignature, (counts.get(outcome.failureSignature) ?? 0) + 1);
19
+ }
20
+ }
21
+ return [...counts.entries()].sort((left, right) => right[1] - left[1])[0]?.[0];
22
+ }
23
+ function dominantReason(outcomes, signature) {
24
+ return outcomes.find((outcome) => outcome.failureSignature === signature && outcome.failureReason)?.failureReason;
25
+ }
26
+ function validateOptions(options) {
27
+ if (!Number.isInteger(options.trials) || options.trials < 2 || options.trials > 100) {
28
+ throw new Error("experiment trials must be an integer between 2 and 100");
29
+ }
30
+ if (!Number.isInteger(options.concurrency) || options.concurrency < 1 || options.concurrency > 32) {
31
+ throw new Error("experiment concurrency must be an integer between 1 and 32");
32
+ }
33
+ if (options.minimumFailureRate <= 0 || options.minimumFailureRate > 1) {
34
+ throw new Error("minimum failure rate must be greater than 0 and at most 1");
35
+ }
36
+ }
37
+ export async function evaluateExperiment(execute, options) {
38
+ validateOptions(options);
39
+ const plans = Array.from({ length: options.trials }, (_, index) => ({
40
+ trialId: `experiment-${index + 1}`,
41
+ index,
42
+ seed: deriveTrialSeed(options.seed, index),
43
+ fault: options.fault,
44
+ }));
45
+ const outcomes = new Map();
46
+ let nextIndex = 0;
47
+ const worker = async () => {
48
+ while (!options.signal?.aborted) {
49
+ const index = nextIndex;
50
+ nextIndex += 1;
51
+ if (index >= plans.length) {
52
+ return;
53
+ }
54
+ outcomes.set(index, await execute(plans[index]));
55
+ }
56
+ };
57
+ await Promise.all(Array.from({ length: Math.min(options.concurrency, plans.length) }, worker));
58
+ const completed = [...outcomes.entries()]
59
+ .sort((left, right) => left[0] - right[0])
60
+ .map((entry) => entry[1]);
61
+ const failed = completed.filter((outcome) => outcome.status === "failed").length;
62
+ const errors = completed.filter((outcome) => outcome.status === "error").length;
63
+ const passed = completed.filter((outcome) => outcome.status === "passed").length;
64
+ const failureRate = completed.length === 0 ? 0 : failed / completed.length;
65
+ const lowerBound80 = lowerWilsonBound(failed, completed.length);
66
+ const dominantFailureSignature = dominantSignature(completed);
67
+ return {
68
+ confirmed: errors === 0
69
+ && failureRate >= options.minimumFailureRate
70
+ && lowerBound80 >= options.minimumFailureRate,
71
+ dominantFailureSignature,
72
+ dominantFailureReason: dominantReason(completed, dominantFailureSignature),
73
+ errors,
74
+ failed,
75
+ failureRate,
76
+ lowerBound80,
77
+ passed,
78
+ trials: completed.length,
79
+ };
80
+ }
81
+ //# sourceMappingURL=evaluate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evaluate.js","sourceRoot":"","sources":["../../src/discovery/evaluate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import type { NetworkDelayFault } from "../domain/schema.js";
2
+ import type { TrialExecutor } from "../runner/playwright-executor.js";
3
+ import type { ExperimentResult } from "./evaluate.js";
4
+ export interface DelayDiscoveryOptions {
5
+ concurrency: number;
6
+ maximumDelayMs: number;
7
+ minimumFailureRate: number;
8
+ pattern: string;
9
+ seed: number;
10
+ signal?: AbortSignal;
11
+ trials: number;
12
+ }
13
+ export interface DelayExperiment {
14
+ delayMs: number;
15
+ result: ExperimentResult;
16
+ }
17
+ export interface DelayDiscoveryResult {
18
+ baseline: ExperimentResult;
19
+ experiments: DelayExperiment[];
20
+ trigger: NetworkDelayFault;
21
+ triggerResult: ExperimentResult;
22
+ }
23
+ export declare function minimizeItems<T>(values: readonly T[], reproduces: (candidate: readonly T[]) => Promise<boolean>): Promise<T[]>;
24
+ export declare function discoverNetworkDelay(execute: TrialExecutor, options: DelayDiscoveryOptions): Promise<DelayDiscoveryResult>;
@@ -0,0 +1,73 @@
1
+ import { evaluateExperiment } from "./evaluate.js";
2
+ const MINIMUM_CONFIRMATION_TRIALS = 12;
3
+ async function confirmStableDelay(experiments, minimumDelayMs, evaluateDelay, confirmationTrials) {
4
+ const candidates = [...new Set(experiments
5
+ .filter((entry) => entry.delayMs >= minimumDelayMs && entry.result.confirmed)
6
+ .map((entry) => entry.delayMs))]
7
+ .sort((left, right) => left - right);
8
+ for (const delayMs of candidates) {
9
+ const result = await evaluateDelay(delayMs, confirmationTrials);
10
+ if (result.confirmed) {
11
+ return { delayMs, result };
12
+ }
13
+ }
14
+ throw new Error("No network delay reproduced the failure in two independent trial batches");
15
+ }
16
+ export async function minimizeItems(values, reproduces) {
17
+ let minimal = [...values];
18
+ let index = 0;
19
+ while (index < minimal.length) {
20
+ const candidate = minimal.filter((_value, candidateIndex) => candidateIndex !== index);
21
+ if (candidate.length > 0 && await reproduces(candidate)) {
22
+ minimal = candidate;
23
+ }
24
+ else {
25
+ index += 1;
26
+ }
27
+ }
28
+ return minimal;
29
+ }
30
+ export async function discoverNetworkDelay(execute, options) {
31
+ const common = {
32
+ concurrency: options.concurrency,
33
+ minimumFailureRate: options.minimumFailureRate,
34
+ seed: options.seed,
35
+ signal: options.signal,
36
+ trials: options.trials,
37
+ };
38
+ const baseline = await evaluateExperiment(execute, common);
39
+ if (baseline.failureRate > 0) {
40
+ throw new Error("Baseline failures must be resolved before fault minimization");
41
+ }
42
+ const experiments = [];
43
+ const evaluateDelay = async (delayMs, trials = options.trials) => {
44
+ const fault = { kind: "network-delay", pattern: options.pattern, delayMs };
45
+ const result = await evaluateExperiment(execute, { ...common, fault, trials });
46
+ experiments.push({ delayMs, result });
47
+ return result;
48
+ };
49
+ const maximum = await evaluateDelay(options.maximumDelayMs);
50
+ if (!maximum.confirmed) {
51
+ throw new Error("Maximum network delay did not reproduce the failure confidently");
52
+ }
53
+ let passingDelayMs = 0;
54
+ let failingDelayMs = options.maximumDelayMs;
55
+ while (failingDelayMs - passingDelayMs > 1) {
56
+ const candidate = Math.floor((passingDelayMs + failingDelayMs) / 2);
57
+ const result = await evaluateDelay(candidate);
58
+ if (result.confirmed) {
59
+ failingDelayMs = candidate;
60
+ }
61
+ else {
62
+ passingDelayMs = candidate;
63
+ }
64
+ }
65
+ const stable = await confirmStableDelay(experiments, failingDelayMs, evaluateDelay, Math.max(options.trials, MINIMUM_CONFIRMATION_TRIALS));
66
+ return {
67
+ baseline,
68
+ experiments,
69
+ trigger: { kind: "network-delay", pattern: options.pattern, delayMs: stable.delayMs },
70
+ triggerResult: stable.result,
71
+ };
72
+ }
73
+ //# sourceMappingURL=minimize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"minimize.js","sourceRoot":"","sources":["../../src/discovery/minimize.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,155 @@
1
+ import { z } from "zod";
2
+ export declare const networkDelayFaultSchema: z.ZodObject<{
3
+ kind: z.ZodLiteral<"network-delay">;
4
+ pattern: z.ZodString;
5
+ delayMs: z.ZodNumber;
6
+ }, z.core.$strip>;
7
+ export declare const requestFailureFaultSchema: z.ZodObject<{
8
+ kind: z.ZodLiteral<"request-failure">;
9
+ pattern: z.ZodString;
10
+ statusCode: z.ZodNumber;
11
+ }, z.core.$strip>;
12
+ export declare const faultSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
13
+ kind: z.ZodLiteral<"network-delay">;
14
+ pattern: z.ZodString;
15
+ delayMs: z.ZodNumber;
16
+ }, z.core.$strip>, z.ZodObject<{
17
+ kind: z.ZodLiteral<"request-failure">;
18
+ pattern: z.ZodString;
19
+ statusCode: z.ZodNumber;
20
+ }, z.core.$strip>], "kind">;
21
+ export declare const runRequestSchema: z.ZodObject<{
22
+ selector: z.ZodString;
23
+ runs: z.ZodNumber;
24
+ seed: z.ZodNumber;
25
+ artifactDirectory: z.ZodString;
26
+ fault: z.ZodDiscriminatedUnion<[z.ZodObject<{
27
+ kind: z.ZodLiteral<"network-delay">;
28
+ pattern: z.ZodString;
29
+ delayMs: z.ZodNumber;
30
+ }, z.core.$strip>, z.ZodObject<{
31
+ kind: z.ZodLiteral<"request-failure">;
32
+ pattern: z.ZodString;
33
+ statusCode: z.ZodNumber;
34
+ }, z.core.$strip>], "kind">;
35
+ }, z.core.$strip>;
36
+ export declare const trialPlanSchema: z.ZodObject<{
37
+ trialId: z.ZodString;
38
+ index: z.ZodNumber;
39
+ seed: z.ZodNumber;
40
+ fault: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
41
+ kind: z.ZodLiteral<"network-delay">;
42
+ pattern: z.ZodString;
43
+ delayMs: z.ZodNumber;
44
+ }, z.core.$strip>, z.ZodObject<{
45
+ kind: z.ZodLiteral<"request-failure">;
46
+ pattern: z.ZodString;
47
+ statusCode: z.ZodNumber;
48
+ }, z.core.$strip>], "kind">>;
49
+ }, z.core.$strip>;
50
+ export declare const trialOutcomeSchema: z.ZodObject<{
51
+ status: z.ZodEnum<{
52
+ error: "error";
53
+ passed: "passed";
54
+ failed: "failed";
55
+ }>;
56
+ durationMs: z.ZodNumber;
57
+ exitCode: z.ZodNullable<z.ZodNumber>;
58
+ failureSignature: z.ZodOptional<z.ZodString>;
59
+ failureReason: z.ZodOptional<z.ZodString>;
60
+ }, z.core.$strip>;
61
+ export declare const runSummarySchema: z.ZodObject<{
62
+ total: z.ZodNumber;
63
+ passed: z.ZodNumber;
64
+ failed: z.ZodNumber;
65
+ errors: z.ZodNumber;
66
+ baselineFailureRate: z.ZodNumber;
67
+ faultFailureRate: z.ZodNumber;
68
+ }, z.core.$strip>;
69
+ export declare const runEventSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
70
+ runId: z.ZodString;
71
+ timestamp: z.ZodISODateTime;
72
+ type: z.ZodLiteral<"run.started">;
73
+ request: z.ZodObject<{
74
+ selector: z.ZodString;
75
+ runs: z.ZodNumber;
76
+ seed: z.ZodNumber;
77
+ artifactDirectory: z.ZodString;
78
+ fault: z.ZodDiscriminatedUnion<[z.ZodObject<{
79
+ kind: z.ZodLiteral<"network-delay">;
80
+ pattern: z.ZodString;
81
+ delayMs: z.ZodNumber;
82
+ }, z.core.$strip>, z.ZodObject<{
83
+ kind: z.ZodLiteral<"request-failure">;
84
+ pattern: z.ZodString;
85
+ statusCode: z.ZodNumber;
86
+ }, z.core.$strip>], "kind">;
87
+ }, z.core.$strip>;
88
+ }, z.core.$strip>, z.ZodObject<{
89
+ runId: z.ZodString;
90
+ timestamp: z.ZodISODateTime;
91
+ type: z.ZodLiteral<"trial.started">;
92
+ trial: z.ZodObject<{
93
+ trialId: z.ZodString;
94
+ index: z.ZodNumber;
95
+ seed: z.ZodNumber;
96
+ fault: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
97
+ kind: z.ZodLiteral<"network-delay">;
98
+ pattern: z.ZodString;
99
+ delayMs: z.ZodNumber;
100
+ }, z.core.$strip>, z.ZodObject<{
101
+ kind: z.ZodLiteral<"request-failure">;
102
+ pattern: z.ZodString;
103
+ statusCode: z.ZodNumber;
104
+ }, z.core.$strip>], "kind">>;
105
+ }, z.core.$strip>;
106
+ }, z.core.$strip>, z.ZodObject<{
107
+ runId: z.ZodString;
108
+ timestamp: z.ZodISODateTime;
109
+ type: z.ZodLiteral<"trial.completed">;
110
+ trial: z.ZodObject<{
111
+ trialId: z.ZodString;
112
+ index: z.ZodNumber;
113
+ seed: z.ZodNumber;
114
+ fault: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
115
+ kind: z.ZodLiteral<"network-delay">;
116
+ pattern: z.ZodString;
117
+ delayMs: z.ZodNumber;
118
+ }, z.core.$strip>, z.ZodObject<{
119
+ kind: z.ZodLiteral<"request-failure">;
120
+ pattern: z.ZodString;
121
+ statusCode: z.ZodNumber;
122
+ }, z.core.$strip>], "kind">>;
123
+ }, z.core.$strip>;
124
+ outcome: z.ZodObject<{
125
+ status: z.ZodEnum<{
126
+ error: "error";
127
+ passed: "passed";
128
+ failed: "failed";
129
+ }>;
130
+ durationMs: z.ZodNumber;
131
+ exitCode: z.ZodNullable<z.ZodNumber>;
132
+ failureSignature: z.ZodOptional<z.ZodString>;
133
+ failureReason: z.ZodOptional<z.ZodString>;
134
+ }, z.core.$strip>;
135
+ }, z.core.$strip>, z.ZodObject<{
136
+ runId: z.ZodString;
137
+ timestamp: z.ZodISODateTime;
138
+ type: z.ZodLiteral<"run.completed">;
139
+ summary: z.ZodObject<{
140
+ total: z.ZodNumber;
141
+ passed: z.ZodNumber;
142
+ failed: z.ZodNumber;
143
+ errors: z.ZodNumber;
144
+ baselineFailureRate: z.ZodNumber;
145
+ faultFailureRate: z.ZodNumber;
146
+ }, z.core.$strip>;
147
+ }, z.core.$strip>], "type">;
148
+ export type Fault = z.infer<typeof faultSchema>;
149
+ export type NetworkDelayFault = z.infer<typeof networkDelayFaultSchema>;
150
+ export type RequestFailureFault = z.infer<typeof requestFailureFaultSchema>;
151
+ export type RunEvent = z.infer<typeof runEventSchema>;
152
+ export type RunRequest = z.infer<typeof runRequestSchema>;
153
+ export type RunSummary = z.infer<typeof runSummarySchema>;
154
+ export type TrialOutcome = z.infer<typeof trialOutcomeSchema>;
155
+ export type TrialPlan = z.infer<typeof trialPlanSchema>;
@@ -0,0 +1,67 @@
1
+ import { z } from "zod";
2
+ export const networkDelayFaultSchema = z.object({
3
+ kind: z.literal("network-delay"),
4
+ pattern: z.string().min(1),
5
+ delayMs: z.number().int().min(1).max(30_000),
6
+ });
7
+ export const requestFailureFaultSchema = z.object({
8
+ kind: z.literal("request-failure"),
9
+ pattern: z.string().min(1),
10
+ statusCode: z.number().int().min(400).max(599),
11
+ });
12
+ export const faultSchema = z.discriminatedUnion("kind", [
13
+ networkDelayFaultSchema,
14
+ requestFailureFaultSchema,
15
+ ]);
16
+ export const runRequestSchema = z.object({
17
+ selector: z.string().min(1),
18
+ runs: z.number().int().min(2).max(100),
19
+ seed: z.number().int().min(0).max(0xffff_ffff),
20
+ artifactDirectory: z.string().min(1),
21
+ fault: faultSchema,
22
+ });
23
+ export const trialPlanSchema = z.object({
24
+ trialId: z.string().min(1),
25
+ index: z.number().int().nonnegative(),
26
+ seed: z.number().int().min(0).max(0xffff_ffff),
27
+ fault: faultSchema.optional(),
28
+ });
29
+ export const trialOutcomeSchema = z.object({
30
+ status: z.enum(["passed", "failed", "error"]),
31
+ durationMs: z.number().int().nonnegative(),
32
+ exitCode: z.number().int().nullable(),
33
+ failureSignature: z.string().min(1).optional(),
34
+ failureReason: z.string().min(1).max(2_000).optional(),
35
+ });
36
+ export const runSummarySchema = z.object({
37
+ total: z.number().int().nonnegative(),
38
+ passed: z.number().int().nonnegative(),
39
+ failed: z.number().int().nonnegative(),
40
+ errors: z.number().int().nonnegative(),
41
+ baselineFailureRate: z.number().min(0).max(1),
42
+ faultFailureRate: z.number().min(0).max(1),
43
+ });
44
+ const eventBaseSchema = z.object({
45
+ runId: z.string().min(1),
46
+ timestamp: z.iso.datetime(),
47
+ });
48
+ export const runEventSchema = z.discriminatedUnion("type", [
49
+ eventBaseSchema.extend({
50
+ type: z.literal("run.started"),
51
+ request: runRequestSchema,
52
+ }),
53
+ eventBaseSchema.extend({
54
+ type: z.literal("trial.started"),
55
+ trial: trialPlanSchema,
56
+ }),
57
+ eventBaseSchema.extend({
58
+ type: z.literal("trial.completed"),
59
+ trial: trialPlanSchema,
60
+ outcome: trialOutcomeSchema,
61
+ }),
62
+ eventBaseSchema.extend({
63
+ type: z.literal("run.completed"),
64
+ summary: runSummarySchema,
65
+ }),
66
+ ]);
67
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/domain/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { Page } from "@playwright/test";
2
+ import type { Fault } from "../domain/schema.js";
3
+ export type RemoveFault = () => Promise<void>;
4
+ export declare function installFault(page: Page, fault: Fault): Promise<RemoveFault>;
@@ -0,0 +1,9 @@
1
+ import { installNetworkDelay } from "./network-delay.js";
2
+ import { installRequestFailure } from "./request-failure.js";
3
+ export async function installFault(page, fault) {
4
+ if (fault.kind === "network-delay") {
5
+ return installNetworkDelay(page, fault);
6
+ }
7
+ return installRequestFailure(page, fault);
8
+ }
9
+ //# sourceMappingURL=install.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/faults/install.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AAI5D,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAU,EAAE,KAAY;IACzD,IAAI,KAAK,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;QACnC,OAAO,mBAAmB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IACzC,CAAC;IACD,OAAO,qBAAqB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;AAC3C,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { Page } from "@playwright/test";
2
+ import type { NetworkDelayFault } from "../domain/schema.js";
3
+ export type RemoveNetworkDelay = () => Promise<void>;
4
+ export declare function installNetworkDelay(page: Page, fault: NetworkDelayFault): Promise<RemoveNetworkDelay>;
5
+ export declare function readNetworkDelayFromEnvironment(): NetworkDelayFault | undefined;
@@ -0,0 +1,34 @@
1
+ import { setTimeout as delay } from "node:timers/promises";
2
+ import { networkDelayFaultSchema } from "../domain/schema.js";
3
+ async function continueRoute(route) {
4
+ try {
5
+ await route.continue();
6
+ }
7
+ catch (error) {
8
+ const releasedDuringCleanup = error instanceof Error && error.message.includes("Route is already handled");
9
+ if (!releasedDuringCleanup) {
10
+ throw error;
11
+ }
12
+ }
13
+ }
14
+ export async function installNetworkDelay(page, fault) {
15
+ const validatedFault = networkDelayFaultSchema.parse(fault);
16
+ const handler = async (route) => {
17
+ await delay(validatedFault.delayMs);
18
+ await continueRoute(route);
19
+ };
20
+ await page.route(validatedFault.pattern, handler);
21
+ return async () => page.unroute(validatedFault.pattern, handler);
22
+ }
23
+ export function readNetworkDelayFromEnvironment() {
24
+ if (process.env.FLAKELAB_FAULT_KIND === "request-failure") {
25
+ return undefined;
26
+ }
27
+ const delayMs = Number(process.env.FLAKELAB_NETWORK_DELAY_MS ?? "0");
28
+ const pattern = process.env.FLAKELAB_NETWORK_PATTERN?.trim();
29
+ if (!pattern || delayMs <= 0) {
30
+ return undefined;
31
+ }
32
+ return networkDelayFaultSchema.parse({ kind: "network-delay", pattern, delayMs });
33
+ }
34
+ //# sourceMappingURL=network-delay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network-delay.js","sourceRoot":"","sources":["../../src/faults/network-delay.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,IAAI,KAAK,EAAE,MAAM,sBAAsB,CAAA;AAE1D,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAA;AAI7D,KAAK,UAAU,aAAa,CAAC,KAAY;IACvC,IAAI,CAAC;QACH,MAAM,KAAK,CAAC,QAAQ,EAAE,CAAA;IACxB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,qBAAqB,GACzB,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,0BAA0B,CAAC,CAAA;QAC9E,IAAI,CAAC,qBAAqB,EAAE,CAAC;YAC3B,MAAM,KAAK,CAAA;QACb,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,IAAU,EACV,KAAwB;IAExB,MAAM,cAAc,GAAG,uBAAuB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IAC3D,MAAM,OAAO,GAAG,KAAK,EAAE,KAAY,EAAiB,EAAE;QACpD,MAAM,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAA;QACnC,MAAM,aAAa,CAAC,KAAK,CAAC,CAAA;IAC5B,CAAC,CAAA;IAED,MAAM,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IACjD,OAAO,KAAK,IAAI,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;AAClE,CAAC;AAED,MAAM,UAAU,+BAA+B;IAC7C,IAAI,OAAO,CAAC,GAAG,CAAC,mBAAmB,KAAK,iBAAiB,EAAE,CAAC;QAC1D,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,yBAAyB,IAAI,GAAG,CAAC,CAAA;IACpE,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,wBAAwB,EAAE,IAAI,EAAE,CAAA;IAC5D,IAAI,CAAC,OAAO,IAAI,OAAO,IAAI,CAAC,EAAE,CAAC;QAC7B,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,OAAO,uBAAuB,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAA;AACnF,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { Page } from "@playwright/test";
2
+ import type { RequestFailureFault } from "../domain/schema.js";
3
+ export type RemoveRequestFailure = () => Promise<void>;
4
+ export declare function installRequestFailure(page: Page, fault: RequestFailureFault): Promise<RemoveRequestFailure>;
5
+ export declare function readRequestFailureFromEnvironment(): RequestFailureFault | undefined;
@@ -0,0 +1,24 @@
1
+ import { requestFailureFaultSchema } from "../domain/schema.js";
2
+ export async function installRequestFailure(page, fault) {
3
+ const validatedFault = requestFailureFaultSchema.parse(fault);
4
+ const handler = async (route) => {
5
+ await route.fulfill({
6
+ status: validatedFault.statusCode,
7
+ contentType: "application/json",
8
+ body: '{"error":"flakelab-injected-failure"}',
9
+ });
10
+ };
11
+ await page.route(validatedFault.pattern, handler);
12
+ return async () => page.unroute(validatedFault.pattern, handler);
13
+ }
14
+ export function readRequestFailureFromEnvironment() {
15
+ if (process.env.FLAKELAB_FAULT_KIND !== "request-failure") {
16
+ return undefined;
17
+ }
18
+ return requestFailureFaultSchema.parse({
19
+ kind: "request-failure",
20
+ pattern: process.env.FLAKELAB_NETWORK_PATTERN,
21
+ statusCode: Number(process.env.FLAKELAB_REQUEST_FAILURE_STATUS ?? "503"),
22
+ });
23
+ }
24
+ //# sourceMappingURL=request-failure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-failure.js","sourceRoot":"","sources":["../../src/faults/request-failure.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAA;AAI/D,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,IAAU,EACV,KAA0B;IAE1B,MAAM,cAAc,GAAG,yBAAyB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IAC7D,MAAM,OAAO,GAAG,KAAK,EAAE,KAAY,EAAiB,EAAE;QACpD,MAAM,KAAK,CAAC,OAAO,CAAC;YAClB,MAAM,EAAE,cAAc,CAAC,UAAU;YACjC,WAAW,EAAE,kBAAkB;YAC/B,IAAI,EAAE,uCAAuC;SAC9C,CAAC,CAAA;IACJ,CAAC,CAAA;IACD,MAAM,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IACjD,OAAO,KAAK,IAAI,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;AAClE,CAAC;AAED,MAAM,UAAU,iCAAiC;IAC/C,IAAI,OAAO,CAAC,GAAG,CAAC,mBAAmB,KAAK,iBAAiB,EAAE,CAAC;QAC1D,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,OAAO,yBAAyB,CAAC,KAAK,CAAC;QACrC,IAAI,EAAE,iBAAiB;QACvB,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,wBAAwB;QAC7C,UAAU,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,+BAA+B,IAAI,KAAK,CAAC;KACzE,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,26 @@
1
+ import type { LanguageModel } from "ai";
2
+ import type { TrialExecutor } from "../runner/playwright-executor.js";
3
+ import type { InvestigationReport } from "./schema.js";
4
+ export interface InvestigatorOptions {
5
+ concurrency: number;
6
+ execute: TrialExecutor;
7
+ inputUsdPerMillion: number;
8
+ maxCostUsd: number;
9
+ maxExperiments: number;
10
+ maximumDelayMs: number;
11
+ maxSeconds: number;
12
+ maxSteps: number;
13
+ maxTrials: number;
14
+ minimumFailureRate: number;
15
+ model: LanguageModel;
16
+ modelId: string;
17
+ outputTokenLimit: number;
18
+ outputUsdPerMillion: number;
19
+ pattern: string;
20
+ projectRoot: string;
21
+ seed: number;
22
+ signal?: AbortSignal;
23
+ test: string;
24
+ trialsPerExperiment: number;
25
+ }
26
+ export declare function runInvestigation(options: InvestigatorOptions): Promise<InvestigationReport>;