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.
- package/README.md +261 -0
- package/package.json +95 -0
- package/src/agents/README.md +139 -0
- package/src/agents/adapters/anthropic.adapter.ts +166 -0
- package/src/agents/adapters/dalle.adapter.ts +145 -0
- package/src/agents/adapters/gemini.adapter.ts +134 -0
- package/src/agents/adapters/imagen.adapter.ts +106 -0
- package/src/agents/adapters/nano-banana.adapter.ts +129 -0
- package/src/agents/adapters/openai.adapter.ts +165 -0
- package/src/agents/adapters/veo.adapter.ts +130 -0
- package/src/agents/agent.schema.property.test.ts +379 -0
- package/src/agents/agent.schema.test.ts +148 -0
- package/src/agents/agent.schema.ts +263 -0
- package/src/agents/index.ts +60 -0
- package/src/agents/registered-agent.schema.ts +356 -0
- package/src/agents/registry.ts +97 -0
- package/src/agents/tournament-configs.property.test.ts +266 -0
- package/src/cli/README.md +145 -0
- package/src/cli/commands/define.ts +79 -0
- package/src/cli/commands/list.ts +46 -0
- package/src/cli/commands/logs.ts +83 -0
- package/src/cli/commands/run.ts +416 -0
- package/src/cli/commands/verify.ts +110 -0
- package/src/cli/index.ts +81 -0
- package/src/config/README.md +128 -0
- package/src/config/env.ts +262 -0
- package/src/config/index.ts +19 -0
- package/src/eval/README.md +318 -0
- package/src/eval/ai-judge.test.ts +435 -0
- package/src/eval/ai-judge.ts +368 -0
- package/src/eval/code-validators.ts +414 -0
- package/src/eval/evaluateOutcome.property.test.ts +1174 -0
- package/src/eval/evaluateOutcome.ts +591 -0
- package/src/eval/immigration-validators.ts +122 -0
- package/src/eval/index.ts +90 -0
- package/src/eval/judge-cache.ts +402 -0
- package/src/eval/tournament-validators.property.test.ts +439 -0
- package/src/eval/validators.property.test.ts +1118 -0
- package/src/eval/validators.ts +1199 -0
- package/src/eval/weighted-scorer.ts +285 -0
- package/src/index.ts +17 -0
- package/src/league/README.md +188 -0
- package/src/league/health-check.ts +353 -0
- package/src/league/index.ts +93 -0
- package/src/league/killAgent.ts +151 -0
- package/src/league/league.test.ts +1151 -0
- package/src/league/runLeague.ts +843 -0
- package/src/league/scoreAgent.ts +175 -0
- package/src/modules/omnibridge/__tests__/.gitkeep +1 -0
- package/src/modules/omnibridge/__tests__/auth-tunnel.property.test.ts +524 -0
- package/src/modules/omnibridge/__tests__/deterministic-logger.property.test.ts +965 -0
- package/src/modules/omnibridge/__tests__/ghost-api.property.test.ts +461 -0
- package/src/modules/omnibridge/__tests__/omnibridge-integration.test.ts +542 -0
- package/src/modules/omnibridge/__tests__/parallel-executor.property.test.ts +671 -0
- package/src/modules/omnibridge/__tests__/semantic-normalizer.property.test.ts +521 -0
- package/src/modules/omnibridge/__tests__/semantic-normalizer.test.ts +254 -0
- package/src/modules/omnibridge/__tests__/session-vault.property.test.ts +367 -0
- package/src/modules/omnibridge/__tests__/shadow-session.property.test.ts +523 -0
- package/src/modules/omnibridge/__tests__/triangulation-engine.property.test.ts +292 -0
- package/src/modules/omnibridge/__tests__/verification-engine.property.test.ts +769 -0
- package/src/modules/omnibridge/api/.gitkeep +1 -0
- package/src/modules/omnibridge/api/ghost-api.ts +1087 -0
- package/src/modules/omnibridge/auth/.gitkeep +1 -0
- package/src/modules/omnibridge/auth/auth-tunnel.ts +843 -0
- package/src/modules/omnibridge/auth/session-vault.ts +577 -0
- package/src/modules/omnibridge/core/.gitkeep +1 -0
- package/src/modules/omnibridge/core/semantic-normalizer.ts +702 -0
- package/src/modules/omnibridge/core/triangulation-engine.ts +530 -0
- package/src/modules/omnibridge/core/types.ts +610 -0
- package/src/modules/omnibridge/execution/.gitkeep +1 -0
- package/src/modules/omnibridge/execution/deterministic-logger.ts +629 -0
- package/src/modules/omnibridge/execution/parallel-executor.ts +542 -0
- package/src/modules/omnibridge/execution/shadow-session.ts +794 -0
- package/src/modules/omnibridge/index.ts +212 -0
- package/src/modules/omnibridge/omnibridge.ts +510 -0
- package/src/modules/omnibridge/verification/.gitkeep +1 -0
- package/src/modules/omnibridge/verification/verification-engine.ts +783 -0
- package/src/outcomes/README.md +75 -0
- package/src/outcomes/acquire-pilot-customer.ts +297 -0
- package/src/outcomes/code-delivery-outcomes.ts +89 -0
- package/src/outcomes/code-outcomes.ts +256 -0
- package/src/outcomes/code_review_battle.test.ts +135 -0
- package/src/outcomes/code_review_battle.ts +135 -0
- package/src/outcomes/cold_email_battle.ts +97 -0
- package/src/outcomes/content_creation_battle.ts +160 -0
- package/src/outcomes/f1_stem_opt_compliance.ts +61 -0
- package/src/outcomes/index.ts +107 -0
- package/src/outcomes/lead_gen_battle.test.ts +113 -0
- package/src/outcomes/lead_gen_battle.ts +99 -0
- package/src/outcomes/outcome.schema.property.test.ts +229 -0
- package/src/outcomes/outcome.schema.ts +187 -0
- package/src/outcomes/qualified_sales_interest.ts +118 -0
- package/src/outcomes/swarm_planner.property.test.ts +370 -0
- package/src/outcomes/swarm_planner.ts +96 -0
- package/src/outcomes/web_extraction.ts +234 -0
- package/src/runtime/README.md +220 -0
- package/src/runtime/agentRunner.test.ts +341 -0
- package/src/runtime/agentRunner.ts +746 -0
- package/src/runtime/claudeAdapter.ts +232 -0
- package/src/runtime/costTracker.ts +123 -0
- package/src/runtime/index.ts +34 -0
- package/src/runtime/modelAdapter.property.test.ts +305 -0
- package/src/runtime/modelAdapter.ts +144 -0
- package/src/runtime/openaiAdapter.ts +235 -0
- package/src/utils/README.md +122 -0
- package/src/utils/command-runner.ts +134 -0
- package/src/utils/cost-guard.ts +379 -0
- package/src/utils/errors.test.ts +290 -0
- package/src/utils/errors.ts +442 -0
- package/src/utils/index.ts +37 -0
- package/src/utils/logger.test.ts +361 -0
- package/src/utils/logger.ts +419 -0
- 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
|
+
};
|