outcome-cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/README.md +261 -0
  2. package/package.json +95 -0
  3. package/src/agents/README.md +139 -0
  4. package/src/agents/adapters/anthropic.adapter.ts +166 -0
  5. package/src/agents/adapters/dalle.adapter.ts +145 -0
  6. package/src/agents/adapters/gemini.adapter.ts +134 -0
  7. package/src/agents/adapters/imagen.adapter.ts +106 -0
  8. package/src/agents/adapters/nano-banana.adapter.ts +129 -0
  9. package/src/agents/adapters/openai.adapter.ts +165 -0
  10. package/src/agents/adapters/veo.adapter.ts +130 -0
  11. package/src/agents/agent.schema.property.test.ts +379 -0
  12. package/src/agents/agent.schema.test.ts +148 -0
  13. package/src/agents/agent.schema.ts +263 -0
  14. package/src/agents/index.ts +60 -0
  15. package/src/agents/registered-agent.schema.ts +356 -0
  16. package/src/agents/registry.ts +97 -0
  17. package/src/agents/tournament-configs.property.test.ts +266 -0
  18. package/src/cli/README.md +145 -0
  19. package/src/cli/commands/define.ts +79 -0
  20. package/src/cli/commands/list.ts +46 -0
  21. package/src/cli/commands/logs.ts +83 -0
  22. package/src/cli/commands/run.ts +416 -0
  23. package/src/cli/commands/verify.ts +110 -0
  24. package/src/cli/index.ts +81 -0
  25. package/src/config/README.md +128 -0
  26. package/src/config/env.ts +262 -0
  27. package/src/config/index.ts +19 -0
  28. package/src/eval/README.md +318 -0
  29. package/src/eval/ai-judge.test.ts +435 -0
  30. package/src/eval/ai-judge.ts +368 -0
  31. package/src/eval/code-validators.ts +414 -0
  32. package/src/eval/evaluateOutcome.property.test.ts +1174 -0
  33. package/src/eval/evaluateOutcome.ts +591 -0
  34. package/src/eval/immigration-validators.ts +122 -0
  35. package/src/eval/index.ts +90 -0
  36. package/src/eval/judge-cache.ts +402 -0
  37. package/src/eval/tournament-validators.property.test.ts +439 -0
  38. package/src/eval/validators.property.test.ts +1118 -0
  39. package/src/eval/validators.ts +1199 -0
  40. package/src/eval/weighted-scorer.ts +285 -0
  41. package/src/index.ts +17 -0
  42. package/src/league/README.md +188 -0
  43. package/src/league/health-check.ts +353 -0
  44. package/src/league/index.ts +93 -0
  45. package/src/league/killAgent.ts +151 -0
  46. package/src/league/league.test.ts +1151 -0
  47. package/src/league/runLeague.ts +843 -0
  48. package/src/league/scoreAgent.ts +175 -0
  49. package/src/modules/omnibridge/__tests__/.gitkeep +1 -0
  50. package/src/modules/omnibridge/__tests__/auth-tunnel.property.test.ts +524 -0
  51. package/src/modules/omnibridge/__tests__/deterministic-logger.property.test.ts +965 -0
  52. package/src/modules/omnibridge/__tests__/ghost-api.property.test.ts +461 -0
  53. package/src/modules/omnibridge/__tests__/omnibridge-integration.test.ts +542 -0
  54. package/src/modules/omnibridge/__tests__/parallel-executor.property.test.ts +671 -0
  55. package/src/modules/omnibridge/__tests__/semantic-normalizer.property.test.ts +521 -0
  56. package/src/modules/omnibridge/__tests__/semantic-normalizer.test.ts +254 -0
  57. package/src/modules/omnibridge/__tests__/session-vault.property.test.ts +367 -0
  58. package/src/modules/omnibridge/__tests__/shadow-session.property.test.ts +523 -0
  59. package/src/modules/omnibridge/__tests__/triangulation-engine.property.test.ts +292 -0
  60. package/src/modules/omnibridge/__tests__/verification-engine.property.test.ts +769 -0
  61. package/src/modules/omnibridge/api/.gitkeep +1 -0
  62. package/src/modules/omnibridge/api/ghost-api.ts +1087 -0
  63. package/src/modules/omnibridge/auth/.gitkeep +1 -0
  64. package/src/modules/omnibridge/auth/auth-tunnel.ts +843 -0
  65. package/src/modules/omnibridge/auth/session-vault.ts +577 -0
  66. package/src/modules/omnibridge/core/.gitkeep +1 -0
  67. package/src/modules/omnibridge/core/semantic-normalizer.ts +702 -0
  68. package/src/modules/omnibridge/core/triangulation-engine.ts +530 -0
  69. package/src/modules/omnibridge/core/types.ts +610 -0
  70. package/src/modules/omnibridge/execution/.gitkeep +1 -0
  71. package/src/modules/omnibridge/execution/deterministic-logger.ts +629 -0
  72. package/src/modules/omnibridge/execution/parallel-executor.ts +542 -0
  73. package/src/modules/omnibridge/execution/shadow-session.ts +794 -0
  74. package/src/modules/omnibridge/index.ts +212 -0
  75. package/src/modules/omnibridge/omnibridge.ts +510 -0
  76. package/src/modules/omnibridge/verification/.gitkeep +1 -0
  77. package/src/modules/omnibridge/verification/verification-engine.ts +783 -0
  78. package/src/outcomes/README.md +75 -0
  79. package/src/outcomes/acquire-pilot-customer.ts +297 -0
  80. package/src/outcomes/code-delivery-outcomes.ts +89 -0
  81. package/src/outcomes/code-outcomes.ts +256 -0
  82. package/src/outcomes/code_review_battle.test.ts +135 -0
  83. package/src/outcomes/code_review_battle.ts +135 -0
  84. package/src/outcomes/cold_email_battle.ts +97 -0
  85. package/src/outcomes/content_creation_battle.ts +160 -0
  86. package/src/outcomes/f1_stem_opt_compliance.ts +61 -0
  87. package/src/outcomes/index.ts +107 -0
  88. package/src/outcomes/lead_gen_battle.test.ts +113 -0
  89. package/src/outcomes/lead_gen_battle.ts +99 -0
  90. package/src/outcomes/outcome.schema.property.test.ts +229 -0
  91. package/src/outcomes/outcome.schema.ts +187 -0
  92. package/src/outcomes/qualified_sales_interest.ts +118 -0
  93. package/src/outcomes/swarm_planner.property.test.ts +370 -0
  94. package/src/outcomes/swarm_planner.ts +96 -0
  95. package/src/outcomes/web_extraction.ts +234 -0
  96. package/src/runtime/README.md +220 -0
  97. package/src/runtime/agentRunner.test.ts +341 -0
  98. package/src/runtime/agentRunner.ts +746 -0
  99. package/src/runtime/claudeAdapter.ts +232 -0
  100. package/src/runtime/costTracker.ts +123 -0
  101. package/src/runtime/index.ts +34 -0
  102. package/src/runtime/modelAdapter.property.test.ts +305 -0
  103. package/src/runtime/modelAdapter.ts +144 -0
  104. package/src/runtime/openaiAdapter.ts +235 -0
  105. package/src/utils/README.md +122 -0
  106. package/src/utils/command-runner.ts +134 -0
  107. package/src/utils/cost-guard.ts +379 -0
  108. package/src/utils/errors.test.ts +290 -0
  109. package/src/utils/errors.ts +442 -0
  110. package/src/utils/index.ts +37 -0
  111. package/src/utils/logger.test.ts +361 -0
  112. package/src/utils/logger.ts +419 -0
  113. package/src/utils/output-parsers.ts +216 -0
@@ -0,0 +1,75 @@
1
+ # Outcomes Module
2
+
3
+ The Outcomes module defines business outcomes as code. An **Outcome** is a declarative, deterministic definition of a business goal including success criteria, payout amount, and constraints.
4
+
5
+ ## Key Concepts
6
+
7
+ - **Outcome**: A complete definition of what success looks like, including payout and limits
8
+ - **SuccessCriterion**: A single verifiable condition that must be met
9
+ - **Validator**: A pure function that evaluates whether a criterion is satisfied
10
+
11
+ ## Interfaces
12
+
13
+ ### Outcome
14
+
15
+ ```typescript
16
+ interface Outcome {
17
+ name: string; // Unique identifier
18
+ description: string; // Human-readable description
19
+ payoutAmount: number; // Payment on success (dollars)
20
+ maxAttempts: number; // Max attempts per agent
21
+ timeLimitMs: number; // Time limit (milliseconds)
22
+ successCriteria: SuccessCriterion[]; // All must pass
23
+ failureReasons: string[]; // Predefined failure messages
24
+ }
25
+ ```
26
+
27
+ ### SuccessCriterion
28
+
29
+ ```typescript
30
+ interface SuccessCriterion {
31
+ name: string; // Criterion identifier
32
+ validator: string; // Validator function name
33
+ params: Record<string, unknown>; // Validator parameters
34
+ }
35
+ ```
36
+
37
+ ## Usage
38
+
39
+ ```typescript
40
+ import { Outcome, validateOutcome, isOutcome } from './outcome.schema.js';
41
+
42
+ // Define an outcome
43
+ const myOutcome: Outcome = {
44
+ name: 'qualified_lead',
45
+ description: 'Generate a qualified sales lead',
46
+ payoutAmount: 250,
47
+ maxAttempts: 5,
48
+ timeLimitMs: 300000,
49
+ successCriteria: [
50
+ { name: 'valid_email', validator: 'validateEmail', params: {} }
51
+ ],
52
+ failureReasons: ['Invalid email', 'Company too small']
53
+ };
54
+
55
+ // Validate before use
56
+ const result = validateOutcome(myOutcome);
57
+ if (!result.valid) {
58
+ console.error('Invalid outcome:', result.errors);
59
+ }
60
+
61
+ // Type guard usage
62
+ if (isOutcome(unknownData)) {
63
+ // unknownData is now typed as Outcome
64
+ }
65
+ ```
66
+
67
+ ## Requirements Reference
68
+
69
+ - **Requirement 1.1**: Outcomes require name, payout, max attempts, time limit, success criteria, failure reasons
70
+ - **Requirement 1.2**: Outcomes are validated against schema before execution
71
+
72
+ ## Files
73
+
74
+ - `outcome.schema.ts` - Type definitions and validation
75
+ - `qualified_sales_interest.ts` - Demo outcome implementation
@@ -0,0 +1,297 @@
1
+ /**
2
+ * Acquire Pilot Customer Outcome
3
+ *
4
+ * Outcome definition for the Snowdevil Hunter Squad campaign.
5
+ * Agents compete to find and convert B2B SaaS founders into
6
+ * WAI Championship pilot customers.
7
+ *
8
+ * @module outcomes/acquire-pilot-customer
9
+ */
10
+
11
+ import type { Outcome } from './outcome.schema.js';
12
+
13
+ /**
14
+ * Payout amount for successfully acquiring a pilot customer.
15
+ * Set to $50 as per Requirements 1.5.
16
+ */
17
+ export const PILOT_PAYOUT_AMOUNT = 50;
18
+
19
+ /**
20
+ * Maximum number of attempts per day per agent.
21
+ * Set to 50 as per Requirements 1.6.
22
+ */
23
+ export const PILOT_MAX_ATTEMPTS = 50;
24
+
25
+ /**
26
+ * Time limit for achieving the outcome (24 hours in milliseconds).
27
+ */
28
+ export const PILOT_TIME_LIMIT_MS = 86400000;
29
+
30
+ /**
31
+ * Keywords that indicate positive reply intent.
32
+ * Reply must contain at least one of these keywords.
33
+ */
34
+ export const POSITIVE_REPLY_KEYWORDS = ['interested', 'demo', 'call', 'yes', 'tell me more'];
35
+
36
+ /**
37
+ * Pain signals indicating Sales/SDR hiring activity.
38
+ */
39
+ export const PAIN_SIGNALS = ['hiring_sdr', 'hiring_sales', 'sales_pain_tweet'];
40
+
41
+ /**
42
+ * Minimum message length for outreach validation.
43
+ */
44
+ export const MIN_OUTREACH_LENGTH = 50;
45
+
46
+ /**
47
+ * Maximum message length for outreach validation.
48
+ */
49
+ export const MAX_OUTREACH_LENGTH = 500;
50
+
51
+ // ============================================================================
52
+ // Validators
53
+ // ============================================================================
54
+
55
+ /**
56
+ * Email regex pattern based on RFC 5322 simplified.
57
+ */
58
+ const EMAIL_REGEX = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
59
+
60
+ /**
61
+ * LinkedIn profile URL pattern.
62
+ */
63
+ const LINKEDIN_REGEX = /^https?:\/\/(www\.)?linkedin\.com\/in\/[a-zA-Z0-9_-]+\/?$/;
64
+
65
+ /**
66
+ * Validates contact information (email or LinkedIn URL).
67
+ *
68
+ * @param contact - The contact string to validate
69
+ * @param params - Validation parameters
70
+ * @returns True if contact is valid email or LinkedIn URL
71
+ *
72
+ * @example
73
+ * validateContactInfo('john@example.com', { requireEmail: true, allowLinkedIn: true }) // true
74
+ * validateContactInfo('https://linkedin.com/in/johndoe', { requireEmail: false, allowLinkedIn: true }) // true
75
+ */
76
+ export function validateContactInfo(
77
+ contact: string,
78
+ params: { requireEmail?: boolean; allowLinkedIn?: boolean }
79
+ ): boolean {
80
+ if (typeof contact !== 'string' || contact.trim() === '') {
81
+ return false;
82
+ }
83
+
84
+ const trimmedContact = contact.trim();
85
+ const isValidEmail = EMAIL_REGEX.test(trimmedContact);
86
+ const isValidLinkedIn = LINKEDIN_REGEX.test(trimmedContact);
87
+
88
+ if (params.requireEmail && !params.allowLinkedIn) {
89
+ return isValidEmail;
90
+ }
91
+
92
+ if (params.allowLinkedIn) {
93
+ return isValidEmail || isValidLinkedIn;
94
+ }
95
+
96
+ return isValidEmail;
97
+ }
98
+
99
+ /**
100
+ * Validates pain signal detection for Sales/SDR hiring.
101
+ *
102
+ * @param companyData - Object containing job listings or signals
103
+ * @param params - Validation parameters with signal keywords
104
+ * @returns True if at least one pain signal is detected
105
+ *
106
+ * @example
107
+ * validatePainSignal({ jobListings: ['SDR Manager', 'Sales Rep'] }, { signals: ['hiring_sdr'] }) // true
108
+ */
109
+ export function validatePainSignal(
110
+ companyData: { jobListings?: string[]; signals?: string[] },
111
+ params: { signals: string[] }
112
+ ): boolean {
113
+ if (typeof companyData !== 'object' || companyData === null) {
114
+ return false;
115
+ }
116
+
117
+ const painKeywords = [
118
+ 'sdr',
119
+ 'sales development',
120
+ 'bdr',
121
+ 'business development representative',
122
+ 'account executive',
123
+ 'sales rep',
124
+ 'sales representative',
125
+ 'hiring sales',
126
+ 'hiring sdr',
127
+ ];
128
+
129
+ // Check job listings for pain signals
130
+ if (Array.isArray(companyData.jobListings)) {
131
+ for (const listing of companyData.jobListings) {
132
+ if (typeof listing === 'string') {
133
+ const lowerListing = listing.toLowerCase();
134
+ for (const keyword of painKeywords) {
135
+ if (lowerListing.includes(keyword)) {
136
+ return true;
137
+ }
138
+ }
139
+ }
140
+ }
141
+ }
142
+
143
+ // Check explicit signals
144
+ if (Array.isArray(companyData.signals)) {
145
+ for (const signal of companyData.signals) {
146
+ if (typeof signal === 'string' && params.signals.includes(signal)) {
147
+ return true;
148
+ }
149
+ }
150
+ }
151
+
152
+ return false;
153
+ }
154
+
155
+ /**
156
+ * Validates outreach message requirements.
157
+ *
158
+ * @param message - The outreach message to validate
159
+ * @param params - Validation parameters
160
+ * @returns True if message meets length and personalization requirements
161
+ *
162
+ * @example
163
+ * validateOutreachSent('Hello John from Acme Corp...', { minLength: 50, maxLength: 500, requirePersonalization: true }) // depends on length
164
+ */
165
+ export function validateOutreachSent(
166
+ message: string,
167
+ params: { minLength: number; maxLength: number; requirePersonalization: boolean }
168
+ ): boolean {
169
+ if (typeof message !== 'string') {
170
+ return false;
171
+ }
172
+
173
+ const trimmedMessage = message.trim();
174
+ const length = trimmedMessage.length;
175
+
176
+ // Check length constraints
177
+ if (length < params.minLength || length > params.maxLength) {
178
+ return false;
179
+ }
180
+
181
+ // Check for personalization if required
182
+ if (params.requirePersonalization) {
183
+ // Look for personalization tokens: names, company references, specific details
184
+ const personalizationPatterns = [
185
+ /\b(hi|hello|hey)\s+[A-Z][a-z]+/i, // Greeting with name
186
+ /\byour\s+(company|team|product|business)/i, // Company reference
187
+ /\b(you|your)\s+(tweeted|posted|mentioned|said)/i, // Social reference
188
+ /\bI\s+(saw|noticed|read)\s+(you|your)/i, // Personal observation
189
+ /\b[A-Z][a-z]+\s+(Corp|Inc|LLC|Ltd|Company)/i, // Company name
190
+ ];
191
+
192
+ const hasPersonalization = personalizationPatterns.some((pattern) => pattern.test(trimmedMessage));
193
+ if (!hasPersonalization) {
194
+ return false;
195
+ }
196
+ }
197
+
198
+ return true;
199
+ }
200
+
201
+ /**
202
+ * Validates positive reply detection.
203
+ *
204
+ * @param reply - The reply message to validate
205
+ * @param params - Validation parameters with positive keywords
206
+ * @returns True if reply contains positive intent keywords
207
+ *
208
+ * @example
209
+ * validatePositiveReply('Yes, I am interested in a demo', { keywords: ['interested', 'demo'] }) // true
210
+ */
211
+ export function validatePositiveReply(reply: string, params: { keywords: string[] }): boolean {
212
+ if (typeof reply !== 'string' || reply.trim() === '') {
213
+ return false;
214
+ }
215
+
216
+ const lowerReply = reply.toLowerCase();
217
+
218
+ for (const keyword of params.keywords) {
219
+ if (lowerReply.includes(keyword.toLowerCase())) {
220
+ return true;
221
+ }
222
+ }
223
+
224
+ return false;
225
+ }
226
+
227
+ // ============================================================================
228
+ // Outcome Definition
229
+ // ============================================================================
230
+
231
+ /**
232
+ * Acquire Pilot Customer Outcome Definition
233
+ *
234
+ * Success requires meeting ALL 4 criteria:
235
+ * 1. Valid contact information (email or LinkedIn URL)
236
+ * 2. Pain signal verified (Sales/SDR hiring activity)
237
+ * 3. Outreach message sent (50-500 chars, personalized)
238
+ * 4. Positive reply received (contains interest keywords)
239
+ *
240
+ * @see Requirements 1.1, 1.2, 1.3, 1.4, 1.5, 1.6
241
+ */
242
+ export const acquirePilotCustomerOutcome: Outcome = {
243
+ name: 'acquire_pilot_customer',
244
+ description:
245
+ 'Find and convert a B2B SaaS founder into a WAI Championship pilot customer by identifying pain signals, sending personalized outreach, and receiving a positive reply.',
246
+ payoutAmount: PILOT_PAYOUT_AMOUNT,
247
+ maxAttempts: PILOT_MAX_ATTEMPTS,
248
+ timeLimitMs: PILOT_TIME_LIMIT_MS,
249
+ successCriteria: [
250
+ {
251
+ // Requirement 1.1: Validate email or LinkedIn URL format
252
+ name: 'valid_contact',
253
+ validator: 'validateContactInfo',
254
+ params: {
255
+ requireEmail: true,
256
+ allowLinkedIn: true,
257
+ },
258
+ },
259
+ {
260
+ // Requirement 1.2: Check for Sales/SDR hiring signals
261
+ name: 'pain_verified',
262
+ validator: 'validatePainSignal',
263
+ params: {
264
+ signals: PAIN_SIGNALS,
265
+ },
266
+ },
267
+ {
268
+ // Requirement 1.3: Confirm personalized message generation
269
+ name: 'outreach_sent',
270
+ validator: 'validateOutreachSent',
271
+ params: {
272
+ minLength: MIN_OUTREACH_LENGTH,
273
+ maxLength: MAX_OUTREACH_LENGTH,
274
+ requirePersonalization: true,
275
+ },
276
+ },
277
+ {
278
+ // Requirement 1.4: Detect positive reply as win condition
279
+ name: 'positive_reply',
280
+ validator: 'validatePositiveReply',
281
+ params: {
282
+ keywords: POSITIVE_REPLY_KEYWORDS,
283
+ },
284
+ },
285
+ ],
286
+ failureReasons: [
287
+ 'invalid_contact',
288
+ 'no_pain_signal',
289
+ 'outreach_rejected',
290
+ 'no_reply',
291
+ 'negative_reply',
292
+ 'budget_exceeded',
293
+ 'rate_limited',
294
+ ],
295
+ };
296
+
297
+ export default acquirePilotCustomerOutcome;
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Code Delivery Outcomes - Outcome-based code generation definitions
3
+ *
4
+ * Defines outcome schemas for feature implementation, refactor, and test generation
5
+ * using deterministic validators (tests/build/lint/benchmark/security).
6
+ */
7
+ import type { Outcome } from './outcome.schema.js';
8
+
9
+ const DEFAULT_PAYOUT = 300;
10
+ const DEFAULT_MAX_ATTEMPTS = 5;
11
+ const DEFAULT_TIME_LIMIT_MS = 30 * 60 * 1000; // 30 minutes
12
+ const DEFAULT_BENCH_P95_MS = 200;
13
+
14
+ function buildCodeOutcome(
15
+ name: Outcome['name'],
16
+ description: string,
17
+ overrides: Partial<Outcome> & {
18
+ benchmarkP95Ms?: number;
19
+ allowWarnings?: boolean;
20
+ } = {}
21
+ ): Outcome {
22
+ const benchmarkP95Ms = overrides.benchmarkP95Ms ?? DEFAULT_BENCH_P95_MS;
23
+ const allowWarnings = overrides.allowWarnings ?? false;
24
+
25
+ return {
26
+ name,
27
+ description,
28
+ payoutAmount: overrides.payoutAmount ?? DEFAULT_PAYOUT,
29
+ maxAttempts: overrides.maxAttempts ?? DEFAULT_MAX_ATTEMPTS,
30
+ timeLimitMs: overrides.timeLimitMs ?? DEFAULT_TIME_LIMIT_MS,
31
+ successCriteria: [
32
+ {
33
+ name: 'Tests pass',
34
+ validator: 'validateTestsPass',
35
+ params: { minPassRate: 1 },
36
+ },
37
+ {
38
+ name: 'Build succeeds',
39
+ validator: 'validateBuilds',
40
+ params: {},
41
+ },
42
+ {
43
+ name: 'Lint clean',
44
+ validator: 'validateLintClean',
45
+ params: { allowWarnings },
46
+ },
47
+ {
48
+ name: 'Benchmark within threshold',
49
+ validator: 'validateBenchmark',
50
+ params: { p95ThresholdMs: benchmarkP95Ms },
51
+ },
52
+ {
53
+ name: 'Security scan clean',
54
+ validator: 'validateSecurityScan',
55
+ params: { maxSeverity: 'high' },
56
+ },
57
+ ],
58
+ failureReasons: [
59
+ 'Tests failing or below pass rate',
60
+ 'Build fails',
61
+ 'Lint errors (or warnings when not allowed)',
62
+ 'Performance regression above threshold',
63
+ 'Security scan has blocking findings',
64
+ ],
65
+ };
66
+ }
67
+
68
+ export const featureImplementationOutcome: Outcome = buildCodeOutcome(
69
+ 'feature_implementation',
70
+ 'Implements a feature with passing tests, clean build, lint, performance, and security gates.'
71
+ );
72
+
73
+ export const refactorTaskOutcome: Outcome = buildCodeOutcome(
74
+ 'refactor_task',
75
+ 'Refactors code while preserving correctness and performance, with clean build/lint/tests.',
76
+ { allowWarnings: false, benchmarkP95Ms: 250 }
77
+ );
78
+
79
+ export const testGenerationOutcome: Outcome = buildCodeOutcome(
80
+ 'test_generation',
81
+ 'Generates automated tests that pass, with clean build/lint and acceptable performance.',
82
+ { allowWarnings: true, benchmarkP95Ms: 300 }
83
+ );
84
+
85
+ export const CODE_DELIVERY_OUTCOMES: Record<string, Outcome> = {
86
+ feature_implementation: featureImplementationOutcome,
87
+ refactor_task: refactorTaskOutcome,
88
+ test_generation: testGenerationOutcome,
89
+ };