@harness-engineering/intelligence 0.5.0 → 0.7.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/dist/index.js CHANGED
@@ -31,20 +31,39 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
33
  ACCEPTANCE_EVAL_SYSTEM_PROMPT: () => ACCEPTANCE_EVAL_SYSTEM_PROMPT,
34
+ AUTO_EXECUTE_CATEGORIES: () => AUTO_EXECUTE_CATEGORIES,
34
35
  AcceptanceEvaluator: () => AcceptanceEvaluator,
35
36
  AnthropicAnalysisProvider: () => AnthropicAnalysisProvider,
37
+ BLAST_TOLERANCE_ABS: () => BLAST_TOLERANCE_ABS,
38
+ BLAST_TOLERANCE_FACTOR: () => BLAST_TOLERANCE_FACTOR,
36
39
  ClaudeCliAnalysisProvider: () => ClaudeCliAnalysisProvider,
40
+ DEFAULT_DEGRADE_AT_PCT: () => DEFAULT_DEGRADE_AT_PCT,
41
+ DEFAULT_RATCHET_CONFIG: () => DEFAULT_RATCHET_CONFIG,
42
+ DEFAULT_RETROSPECTIVE_CONFIG: () => DEFAULT_RETROSPECTIVE_CONFIG,
43
+ DEPTH_BY_LEVEL: () => DEPTH_BY_LEVEL,
37
44
  ExecutionOutcomeConnector: () => ExecutionOutcomeConnector,
38
45
  GraphValidator: () => GraphValidator,
39
46
  IntelligencePipeline: () => IntelligencePipeline,
47
+ LEVEL_RANK: () => LEVEL_RANK,
40
48
  OUTCOME_EVAL_SYSTEM_PROMPT: () => OUTCOME_EVAL_SYSTEM_PROMPT,
41
49
  OpenAICompatibleAnalysisProvider: () => OpenAICompatibleAnalysisProvider,
42
50
  OutcomeEvaluator: () => OutcomeEvaluator,
43
51
  PeslSimulator: () => PeslSimulator,
52
+ RANK_TIER: () => RANK_TIER,
53
+ SENSITIVE_BLAST_THRESHOLD: () => SENSITIVE_BLAST_THRESHOLD,
54
+ STATIC_WEIGHTS: () => STATIC_WEIGHTS,
55
+ TIER_RANK: () => TIER_RANK,
56
+ V1_MAX_STAGE: () => V1_MAX_STAGE,
44
57
  acceptanceVerdictSchema: () => acceptanceVerdictSchema,
58
+ aggregatePrecedent: () => aggregatePrecedent,
59
+ applyBudgetClamp: () => applyBudgetClamp,
60
+ baseTier: () => baseTier,
61
+ blastRadiusVeto: () => blastRadiusVeto,
45
62
  buildAcceptanceUserPrompt: () => buildUserPrompt3,
46
63
  buildSpecializationProfile: () => buildSpecializationProfile,
47
64
  buildUserPrompt: () => buildUserPrompt2,
65
+ classify: () => classify,
66
+ compareToPrediction: () => compareToPrediction,
48
67
  computeExpertiseLevel: () => computeExpertiseLevel,
49
68
  computeHistoricalComplexity: () => computeHistoricalComplexity,
50
69
  computePersonaEffectiveness: () => computePersonaEffectiveness,
@@ -53,24 +72,40 @@ __export(index_exports, {
53
72
  computeStructuralComplexity: () => computeStructuralComplexity,
54
73
  createCanaryAdapter: () => createCanaryAdapter,
55
74
  decayWeight: () => decayWeight,
75
+ depthForLevel: () => depthForLevel,
56
76
  deriveAcceptanceAuthority: () => deriveAcceptanceAuthority,
57
77
  deriveAuthority: () => deriveAuthority,
78
+ deriveRequiredTier: () => deriveRequiredTier,
58
79
  detectBlindSpots: () => detectBlindSpots,
80
+ dispatchableShapeKey: () => dispatchableShapeKey,
59
81
  enrich: () => enrich,
82
+ extractEntities: () => extractEntities,
60
83
  findingSchema: () => findingSchema,
61
84
  githubToRawWorkItem: () => githubToRawWorkItem,
62
85
  jiraToRawWorkItem: () => jiraToRawWorkItem,
63
86
  linearToRawWorkItem: () => linearToRawWorkItem,
87
+ llmTiebreak: () => llmTiebreak,
64
88
  loadProfiles: () => loadProfiles,
65
89
  manualToRawWorkItem: () => manualToRawWorkItem,
90
+ pilotScore: () => pilotScore,
91
+ precedentLookupFromRecords: () => precedentLookupFromRecords,
92
+ rankTriageCandidates: () => rankTriageCandidates,
66
93
  recommendPersona: () => recommendPersona,
67
94
  refreshProfiles: () => refreshProfiles,
95
+ resolveGoNoGo: () => resolveGoNoGo,
96
+ resolveGoNoGoStaged: () => resolveGoNoGoStaged,
68
97
  resolveSection: () => resolveSection,
98
+ resolveStage: () => resolveStage,
99
+ runAutoBrainstorm: () => runAutoBrainstorm,
69
100
  runGraphOnlyChecks: () => runGraphOnlyChecks,
70
101
  runLlmSimulation: () => runLlmSimulation,
102
+ runScopingProbe: () => runScopingProbe,
103
+ runStaticPass: () => runStaticPass,
71
104
  saveProfiles: () => saveProfiles,
72
105
  scoreCML: () => score,
73
106
  scoreToConcernSignals: () => scoreToConcernSignals,
107
+ serializeSignals: () => serializeSignals,
108
+ shapeKey: () => shapeKey,
74
109
  temporalSuccessRate: () => temporalSuccessRate,
75
110
  toRawWorkItem: () => toRawWorkItem,
76
111
  verdictSchema: () => verdictSchema,
@@ -2120,23 +2155,624 @@ function refreshProfiles(projectRoot, graphStore, opts) {
2120
2155
  saveProfiles(projectRoot, store);
2121
2156
  return store;
2122
2157
  }
2158
+
2159
+ // src/complexity/signals.ts
2160
+ function serializeSignals(s) {
2161
+ const out = {
2162
+ descriptionLength: s.descriptionLength,
2163
+ specExists: s.specExists,
2164
+ acceptanceMeasurable: s.acceptanceMeasurable
2165
+ };
2166
+ if (s.filesTouched !== void 0) out.filesTouched = s.filesTouched;
2167
+ if (s.layersTouched !== void 0) out.layersTouched = s.layersTouched;
2168
+ if (s.blastRadius !== void 0) out.blastRadius = s.blastRadius;
2169
+ if (s.hotspotChurn !== void 0) out.hotspotChurn = s.hotspotChurn;
2170
+ return out;
2171
+ }
2172
+
2173
+ // src/complexity/static-pass.ts
2174
+ var STATIC_WEIGHTS = {
2175
+ filesTouched: 0.25,
2176
+ layersTouched: 0.2,
2177
+ blastRadius: 0.3,
2178
+ hotspotChurn: 0.1,
2179
+ /** Text-only signals — the only inputs available pre-diff. */
2180
+ descriptionLength: 0.1,
2181
+ /** A present spec / measurable acceptance *lowers* complexity (well-scoped). */
2182
+ specExists: 0.025,
2183
+ acceptanceMeasurable: 0.025
2184
+ };
2185
+ var REF_MAX = {
2186
+ filesTouched: 20,
2187
+ layersTouched: 4,
2188
+ blastRadius: 40,
2189
+ hotspotChurn: 1,
2190
+ descriptionLength: 500
2191
+ };
2192
+ var CONFIDENCE_RANK = {
2193
+ low: 0,
2194
+ medium: 1,
2195
+ high: 2
2196
+ };
2197
+ function clamp01(n) {
2198
+ return Math.max(0, Math.min(1, n));
2199
+ }
2200
+ function scoreToLevel(score2) {
2201
+ if (score2 < 0.2) return "trivial";
2202
+ if (score2 < 0.45) return "simple";
2203
+ if (score2 < 0.7) return "moderate";
2204
+ return "complex";
2205
+ }
2206
+ function scoreToConfidence(score2) {
2207
+ const boundaries = [0.2, 0.45, 0.7];
2208
+ const nearest = Math.min(...boundaries.map((b) => Math.abs(score2 - b)));
2209
+ const atExtremeEnd = score2 < 0.2 || score2 >= 0.7;
2210
+ if (nearest < 0.1) return "low";
2211
+ if (nearest < 0.18 && !atExtremeEnd) return "medium";
2212
+ return "high";
2213
+ }
2214
+ function runStaticPass(signals, phase) {
2215
+ const isPostDiff = phase === "post-diff";
2216
+ const contributions = [];
2217
+ const weightsUsed = [];
2218
+ if (isPostDiff) {
2219
+ if (signals.filesTouched !== void 0) {
2220
+ contributions.push(
2221
+ clamp01(signals.filesTouched / REF_MAX.filesTouched) * STATIC_WEIGHTS.filesTouched
2222
+ );
2223
+ weightsUsed.push(STATIC_WEIGHTS.filesTouched);
2224
+ }
2225
+ if (signals.layersTouched !== void 0) {
2226
+ contributions.push(
2227
+ clamp01(signals.layersTouched / REF_MAX.layersTouched) * STATIC_WEIGHTS.layersTouched
2228
+ );
2229
+ weightsUsed.push(STATIC_WEIGHTS.layersTouched);
2230
+ }
2231
+ if (signals.blastRadius !== void 0) {
2232
+ contributions.push(
2233
+ clamp01(signals.blastRadius / REF_MAX.blastRadius) * STATIC_WEIGHTS.blastRadius
2234
+ );
2235
+ weightsUsed.push(STATIC_WEIGHTS.blastRadius);
2236
+ }
2237
+ if (signals.hotspotChurn !== void 0) {
2238
+ contributions.push(
2239
+ clamp01(signals.hotspotChurn / REF_MAX.hotspotChurn) * STATIC_WEIGHTS.hotspotChurn
2240
+ );
2241
+ weightsUsed.push(STATIC_WEIGHTS.hotspotChurn);
2242
+ }
2243
+ }
2244
+ contributions.push(
2245
+ clamp01(signals.descriptionLength / REF_MAX.descriptionLength) * STATIC_WEIGHTS.descriptionLength
2246
+ );
2247
+ weightsUsed.push(STATIC_WEIGHTS.descriptionLength);
2248
+ const scopeReduction = (signals.specExists ? STATIC_WEIGHTS.specExists : 0) + (signals.acceptanceMeasurable ? STATIC_WEIGHTS.acceptanceMeasurable : 0);
2249
+ const totalWeight = weightsUsed.reduce((a, b) => a + b, 0);
2250
+ const rawScore = contributions.reduce((a, b) => a + b, 0);
2251
+ const normalized = totalWeight > 0 ? rawScore / totalWeight : 0;
2252
+ const score2 = clamp01(normalized - scopeReduction);
2253
+ const level = scoreToLevel(score2);
2254
+ let confidence = scoreToConfidence(score2);
2255
+ if (!isPostDiff && CONFIDENCE_RANK[confidence] > CONFIDENCE_RANK.medium) {
2256
+ confidence = "medium";
2257
+ }
2258
+ return {
2259
+ level,
2260
+ confidence,
2261
+ signals: serializeSignals(signals)
2262
+ };
2263
+ }
2264
+
2265
+ // src/complexity/tiebreak.ts
2266
+ var import_zod6 = require("zod");
2267
+ var TiebreakSchema = import_zod6.z.object({
2268
+ level: import_zod6.z.enum(["trivial", "simple", "moderate", "complex"]),
2269
+ confidence: import_zod6.z.enum(["high", "medium", "low"])
2270
+ });
2271
+ async function llmTiebreak(provider, prompt, fastModel) {
2272
+ try {
2273
+ const { result } = await provider.analyze({
2274
+ prompt,
2275
+ responseSchema: TiebreakSchema,
2276
+ // Only include `model` when supplied (exactOptionalPropertyTypes).
2277
+ ...fastModel !== void 0 ? { model: fastModel } : {},
2278
+ maxTokens: 256
2279
+ });
2280
+ return result;
2281
+ } catch {
2282
+ return { level: "moderate", confidence: "low" };
2283
+ }
2284
+ }
2285
+
2286
+ // src/complexity/classifier.ts
2287
+ var CONFIDENCE_RANK2 = { low: 0, medium: 1, high: 2 };
2288
+ function capForPhase(confidence, phase) {
2289
+ if (phase === "pre-diff" && CONFIDENCE_RANK2[confidence] > CONFIDENCE_RANK2.medium) {
2290
+ return "medium";
2291
+ }
2292
+ return confidence;
2293
+ }
2294
+ async function classify(input, provider, models) {
2295
+ const staticVerdict = runStaticPass(input.signals, input.phase);
2296
+ if (staticVerdict.confidence !== "low" || provider === void 0) {
2297
+ return {
2298
+ level: staticVerdict.level,
2299
+ confidence: staticVerdict.confidence,
2300
+ signals: staticVerdict.signals,
2301
+ source: "static"
2302
+ };
2303
+ }
2304
+ const tiebreak = await llmTiebreak(provider, input.prompt, models?.fast);
2305
+ if (tiebreak.confidence === "low" && input.riskHigh) {
2306
+ const escalated = await llmTiebreak(provider, input.prompt, models?.standard);
2307
+ return {
2308
+ level: escalated.level,
2309
+ confidence: capForPhase(escalated.confidence, input.phase),
2310
+ signals: staticVerdict.signals,
2311
+ source: "escalated"
2312
+ };
2313
+ }
2314
+ return {
2315
+ level: tiebreak.level,
2316
+ confidence: capForPhase(tiebreak.confidence, input.phase),
2317
+ signals: staticVerdict.signals,
2318
+ source: "llm-tiebreak"
2319
+ };
2320
+ }
2321
+
2322
+ // src/complexity/derive-tier.ts
2323
+ var TIER_RANK = { fast: 0, standard: 1, strong: 2 };
2324
+ var RANK_TIER = ["fast", "standard", "strong"];
2325
+ var SENSITIVE_BLAST_THRESHOLD = 25;
2326
+ var DEFAULT_MATRIX = {
2327
+ trivial: "fast",
2328
+ simple: "fast",
2329
+ moderate: "standard",
2330
+ complex: "strong"
2331
+ };
2332
+ function tierAtRank(rank) {
2333
+ const clamped = Math.max(0, Math.min(RANK_TIER.length - 1, rank));
2334
+ return RANK_TIER[clamped];
2335
+ }
2336
+ function blastRadiusVeto(risk) {
2337
+ if (!risk) return false;
2338
+ return risk.sensitivePath === true || risk.publicApi === true || risk.layer === "core" || risk.layer === "types" || typeof risk.blastRadius === "number" && risk.blastRadius >= SENSITIVE_BLAST_THRESHOLD;
2339
+ }
2340
+ function baseTier(complexity, risk, policy, skillKey) {
2341
+ const veto = blastRadiusVeto(risk);
2342
+ const override = skillKey !== void 0 ? policy.skillTierOverrides?.[skillKey] : void 0;
2343
+ const matrix = policy.complexityTierMatrix ?? {};
2344
+ const fromMatrix = matrix[complexity.level] ?? DEFAULT_MATRIX[complexity.level];
2345
+ let rank = Math.max(override !== void 0 ? TIER_RANK[override] : 0, TIER_RANK[fromMatrix]);
2346
+ if (complexity.confidence === "low") {
2347
+ rank = rank + 1;
2348
+ }
2349
+ if (veto) rank = Math.max(rank, TIER_RANK.strong);
2350
+ return tierAtRank(rank);
2351
+ }
2352
+ var DEFAULT_DEGRADE_AT_PCT = 90;
2353
+ function applyBudgetClamp(tier, risk, policy, spend) {
2354
+ const budget = policy.budget;
2355
+ if (!budget || budget.capUsd <= 0) return tier;
2356
+ const degradeAt = (budget.degradeAtPct ?? DEFAULT_DEGRADE_AT_PCT) / 100;
2357
+ const spentFraction = spend.spentUsd / budget.capUsd;
2358
+ if (spentFraction < degradeAt) return tier;
2359
+ if (blastRadiusVeto(risk)) return tier;
2360
+ const mode = budget.onBudgetExhausted ?? "degrade";
2361
+ if (spentFraction >= 1 && mode !== "human") return "fast";
2362
+ return tierAtRank(TIER_RANK[tier] - 1);
2363
+ }
2364
+ function deriveRequiredTier(complexity, risk, policy, spend, escalationFloor, skillKey) {
2365
+ const base = baseTier(complexity, risk, policy, skillKey);
2366
+ const clamped = applyBudgetClamp(base, risk, policy, spend);
2367
+ return tierAtRank(Math.max(TIER_RANK[escalationFloor], TIER_RANK[clamped]));
2368
+ }
2369
+
2370
+ // src/triage/record.ts
2371
+ function shapeKey(labels, category, level) {
2372
+ const sortedLabels = Array.from(
2373
+ new Set(labels.map((l) => l.trim().replace(/[,|]/g, "")).filter((l) => l.length > 0))
2374
+ ).sort();
2375
+ return `${sortedLabels.join(",")}|${category}|${level}`;
2376
+ }
2377
+ function dispatchableShapeKey(labels, level) {
2378
+ return shapeKey(labels, "dispatchable", level);
2379
+ }
2380
+
2381
+ // src/triage/precedent.ts
2382
+ function aggregatePrecedent(records, shapeKey2) {
2383
+ let matched = 0;
2384
+ let total = 0;
2385
+ for (const rec of records) {
2386
+ if (rec.shapeKey !== shapeKey2) continue;
2387
+ if (!rec.outcome) continue;
2388
+ total += 1;
2389
+ if (rec.outcome.matched) matched += 1;
2390
+ }
2391
+ if (total === 0) return { kind: "unknown" };
2392
+ return { kind: "rate", matched, total, rate: matched / total };
2393
+ }
2394
+ function precedentLookupFromRecords(records) {
2395
+ return {
2396
+ rateForShape: (shapeKey2) => aggregatePrecedent(records, shapeKey2)
2397
+ };
2398
+ }
2399
+
2400
+ // src/triage/entities.ts
2401
+ var BACKTICKED_RE = /`([^`]+)`/g;
2402
+ var PATH_RE = /(?:\.\/)?(?:[A-Za-z0-9_@.-]+\/)+[A-Za-z0-9_.-]+/g;
2403
+ var CAMEL_RE = /\b([A-Z][a-z]+[A-Za-z0-9]*[A-Z][A-Za-z0-9]*|[a-z]+[A-Z][A-Za-z0-9]*)\b/g;
2404
+ var DOTTED_RE = /\b[A-Za-z_$][A-Za-z0-9_$]*(?:\.[A-Za-z_$][A-Za-z0-9_$]*)+\b/g;
2405
+ function extractEntities(text) {
2406
+ if (typeof text !== "string") return [];
2407
+ const trimmed = text.trim();
2408
+ if (trimmed.length === 0) return [];
2409
+ const seen = /* @__PURE__ */ new Set();
2410
+ const result = [];
2411
+ const add = (raw) => {
2412
+ const token = raw.trim();
2413
+ if (token.length === 0) return;
2414
+ if (seen.has(token)) return;
2415
+ seen.add(token);
2416
+ result.push(token);
2417
+ };
2418
+ for (const m of trimmed.matchAll(BACKTICKED_RE)) {
2419
+ const inner = m[1];
2420
+ if (inner !== void 0) add(inner);
2421
+ }
2422
+ for (const m of trimmed.matchAll(PATH_RE)) add(m[0]);
2423
+ for (const m of trimmed.matchAll(CAMEL_RE)) add(m[0]);
2424
+ for (const m of trimmed.matchAll(DOTTED_RE)) add(m[0]);
2425
+ return result;
2426
+ }
2427
+
2428
+ // src/triage/probe.ts
2429
+ var import_zod7 = require("zod");
2430
+ var DEFAULT_CONFIG = {
2431
+ boundedScopeMax: 40,
2432
+ dispatchConfidence: "medium",
2433
+ precedentBlockRate: 0,
2434
+ precedentMinSample: 1
2435
+ };
2436
+ var OpenDecisionsSchema = import_zod7.z.object({
2437
+ openDecisions: import_zod7.z.array(import_zod7.z.object({ question: import_zod7.z.string() })).describe(
2438
+ "Choices requiring human judgment (API shape, product tradeoff, irreversible action)"
2439
+ )
2440
+ });
2441
+ var OPEN_DECISIONS_SYSTEM_PROMPT = "You are triaging a backlog item for autonomous execution. Identify ONLY choices that genuinely require human judgment before an agent could safely proceed \u2014 API/interface shape, product tradeoffs, or irreversible/outward-facing actions. If the item is fully scoped and an agent could proceed without a human decision, return an empty array. Do not invent decisions; be conservative about what truly needs a human.";
2442
+ async function runScopingProbe(input, deps = {}) {
2443
+ const config = { ...DEFAULT_CONFIG, ...deps.config };
2444
+ const scope = await runScopeLever(input, deps.graph);
2445
+ const semanticRead = await runSemanticReadLever(input, scope.value, deps);
2446
+ const openDecisions = await runOpenDecisionsLever(input, deps.provider);
2447
+ const precedent = runPrecedentLever(input, semanticRead.value, deps.precedent);
2448
+ const levers = { scope, semanticRead, openDecisions, precedent };
2449
+ const verdict = semanticRead.value === "unknown" ? { level: "moderate", confidence: "low", signals: {}, source: "static" } : semanticRead.value;
2450
+ const { dispatchable, holdReason } = gate(levers, config);
2451
+ const rationale = buildRationale(levers, dispatchable, holdReason);
2452
+ return {
2453
+ externalId: input.externalId,
2454
+ verdict,
2455
+ dispatchable,
2456
+ ...holdReason !== void 0 ? { holdReason } : {},
2457
+ levers,
2458
+ rationale
2459
+ };
2460
+ }
2461
+ async function runScopeLever(input, graph) {
2462
+ if (graph === void 0) {
2463
+ return { value: "unknown", reason: "scope: no graph seam available" };
2464
+ }
2465
+ try {
2466
+ const resolved = [];
2467
+ for (const candidate of input.entityCandidates) {
2468
+ const hit = await graph.resolve(candidate);
2469
+ if (hit) resolved.push(hit);
2470
+ }
2471
+ const blastRadius = resolved.reduce((max, r) => Math.max(max, r.blastRadius), 0);
2472
+ const estimate = {
2473
+ candidates: input.entityCandidates,
2474
+ resolved,
2475
+ blastRadius,
2476
+ layersTouched: Math.min(resolved.length, 4),
2477
+ filesTouched: resolved.length
2478
+ };
2479
+ const note = resolved.length === 0 ? "scope: no entity resolved in graph (unresolved-scope)" : `scope: ${resolved.length} entity(ies) resolved, blastRadius ${blastRadius}`;
2480
+ return { value: estimate, reason: note };
2481
+ } catch (err) {
2482
+ return { value: "unknown", reason: `scope: graph lookup failed \u2014 ${errMsg(err)}` };
2483
+ }
2484
+ }
2485
+ async function runSemanticReadLever(input, scope, deps) {
2486
+ try {
2487
+ const signals = {
2488
+ descriptionLength: input.taskText.descriptionLength,
2489
+ specExists: input.taskText.specExists,
2490
+ acceptanceMeasurable: input.taskText.acceptanceMeasurable,
2491
+ ...scope !== "unknown" && scope.resolved.length > 0 ? {
2492
+ filesTouched: scope.filesTouched,
2493
+ layersTouched: scope.layersTouched,
2494
+ blastRadius: scope.blastRadius
2495
+ } : {}
2496
+ };
2497
+ const phase = scope !== "unknown" && scope.resolved.length > 0 ? "post-diff" : "pre-diff";
2498
+ const classifyInput = {
2499
+ signals,
2500
+ phase,
2501
+ riskHigh: input.riskHigh === true,
2502
+ prompt: input.taskText.prompt
2503
+ };
2504
+ const verdict = await classify(classifyInput, deps.provider, deps.models);
2505
+ return { value: verdict, reason: `semantic-read: ${verdict.level}/${verdict.confidence}` };
2506
+ } catch (err) {
2507
+ return { value: "unknown", reason: `semantic-read: classify failed \u2014 ${errMsg(err)}` };
2508
+ }
2509
+ }
2510
+ async function runOpenDecisionsLever(input, provider) {
2511
+ if (provider === void 0) {
2512
+ return { value: "unknown", reason: "open-decisions: no provider (offline)" };
2513
+ }
2514
+ try {
2515
+ const { result } = await provider.analyze({
2516
+ prompt: input.taskText.prompt,
2517
+ systemPrompt: OPEN_DECISIONS_SYSTEM_PROMPT,
2518
+ responseSchema: OpenDecisionsSchema,
2519
+ maxTokens: 512
2520
+ });
2521
+ const decisions = result.openDecisions ?? [];
2522
+ const note = decisions.length === 0 ? "open-decisions: none surfaced" : `open-decisions: ${decisions.length} requiring human judgment`;
2523
+ return { value: decisions, reason: note };
2524
+ } catch (err) {
2525
+ return { value: "unknown", reason: `open-decisions: assessment failed \u2014 ${errMsg(err)}` };
2526
+ }
2527
+ }
2528
+ function runPrecedentLever(input, verdict, precedent) {
2529
+ if (precedent === void 0) {
2530
+ return { value: "unknown", reason: "precedent: no store (cold-start)" };
2531
+ }
2532
+ try {
2533
+ const level = verdict === "unknown" ? "moderate" : verdict.level;
2534
+ const key = dispatchableShapeKey(input.labels, level);
2535
+ const rate = precedent.rateForShape(key);
2536
+ const note = rate.kind === "unknown" ? "precedent: unknown for this shape (no graded records)" : `precedent: ${rate.matched}/${rate.total} (${(rate.rate * 100).toFixed(0)}%)`;
2537
+ return { value: rate, reason: note };
2538
+ } catch (err) {
2539
+ return { value: "unknown", reason: `precedent: lookup failed \u2014 ${errMsg(err)}` };
2540
+ }
2541
+ }
2542
+ function gate(levers, config) {
2543
+ const scope = levers.scope.value;
2544
+ if (scope === "unknown" || scope.resolved.length === 0) {
2545
+ return { dispatchable: false, holdReason: "unresolved-scope" };
2546
+ }
2547
+ if (scope.blastRadius > config.boundedScopeMax) {
2548
+ return { dispatchable: false, holdReason: "unresolved-scope" };
2549
+ }
2550
+ const read = levers.semanticRead.value;
2551
+ if (read === "unknown") {
2552
+ return { dispatchable: false, holdReason: "not-in-band" };
2553
+ }
2554
+ const inBand = read.level === "trivial" || read.level === "simple";
2555
+ const confidentEnough = CONFIDENCE_RANK[read.confidence] >= CONFIDENCE_RANK[config.dispatchConfidence];
2556
+ if (!inBand || !confidentEnough) {
2557
+ return { dispatchable: false, holdReason: "not-in-band" };
2558
+ }
2559
+ const decisions = levers.openDecisions.value;
2560
+ if (decisions === "unknown") {
2561
+ return { dispatchable: false, holdReason: "read-incomplete" };
2562
+ }
2563
+ if (decisions.length > 0) {
2564
+ return { dispatchable: false, holdReason: "open-decision" };
2565
+ }
2566
+ const precedent = levers.precedent.value;
2567
+ if (precedent !== "unknown" && precedent.kind === "rate" && precedent.total >= (config.precedentMinSample ?? 1) && precedent.rate <= (config.precedentBlockRate ?? 0)) {
2568
+ return { dispatchable: false, holdReason: "precedent-contradicts" };
2569
+ }
2570
+ return { dispatchable: true };
2571
+ }
2572
+ function buildRationale(levers, dispatchable, holdReason) {
2573
+ const parts = [];
2574
+ for (const lever of [levers.scope, levers.semanticRead, levers.openDecisions, levers.precedent]) {
2575
+ if (lever.reason) parts.push(lever.reason);
2576
+ }
2577
+ const decision = dispatchable ? "DISPATCHABLE \u2014 all levers corroborate." : `HELD (${holdReason}).`;
2578
+ return `${decision} ${parts.join("; ")}`;
2579
+ }
2580
+ function errMsg(err) {
2581
+ return err instanceof Error ? err.message : String(err);
2582
+ }
2583
+
2584
+ // src/triage/rank.ts
2585
+ function pilotScore(c) {
2586
+ const effort = c.effort > 0 ? c.effort : 1e-6;
2587
+ const score2 = c.impact * c.confidence / effort;
2588
+ return Number.isFinite(score2) ? score2 : 0;
2589
+ }
2590
+ function rankTriageCandidates(candidates) {
2591
+ return [...candidates].sort((a, b) => {
2592
+ const sa = pilotScore(a);
2593
+ const sb = pilotScore(b);
2594
+ if (sa !== sb) return sb - sa;
2595
+ if (a.impact !== b.impact) return b.impact - a.impact;
2596
+ return a.externalId < b.externalId ? -1 : a.externalId > b.externalId ? 1 : 0;
2597
+ });
2598
+ }
2599
+
2600
+ // src/triage/brainstorm/types.ts
2601
+ var DEPTH_BY_LEVEL = {
2602
+ trivial: { maxForks: 2 },
2603
+ simple: { maxForks: 4 },
2604
+ // moderate/complex are OUT of the eligible band (the probe holds them to human) — a depth
2605
+ // is defined only so the mapping is total; the brainstorm should never run on them.
2606
+ moderate: { maxForks: 4 },
2607
+ complex: { maxForks: 4 }
2608
+ };
2609
+ function depthForLevel(level) {
2610
+ return DEPTH_BY_LEVEL[level] ?? { maxForks: 2 };
2611
+ }
2612
+
2613
+ // src/triage/brainstorm/runner.ts
2614
+ async function runAutoBrainstorm(input, generator, depth) {
2615
+ const accepted = [];
2616
+ const ceiling = Math.max(0, depth.maxForks);
2617
+ for (let index = 0; index < ceiling; index++) {
2618
+ let decision;
2619
+ try {
2620
+ decision = await generator.next(index, accepted);
2621
+ } catch (err) {
2622
+ return {
2623
+ kind: "halted",
2624
+ fork: { id: `fork-${index}`, question: "brainstorm errored before deciding", options: [] },
2625
+ reason: "error",
2626
+ detail: errMsg2(err)
2627
+ };
2628
+ }
2629
+ if (decision === null || decision === void 0) break;
2630
+ if (decision.confidence !== "high") {
2631
+ return {
2632
+ kind: "halted",
2633
+ fork: decision.fork,
2634
+ reason: "low-confidence",
2635
+ detail: `fork '${decision.fork.id}' recommended '${decision.recommendation}' at confidence '${decision.confidence}' (below the 'high' bar) \u2014 ${decision.rationale}`
2636
+ };
2637
+ }
2638
+ accepted.push(decision);
2639
+ }
2640
+ const spec = {
2641
+ externalId: input.externalId,
2642
+ title: input.title,
2643
+ summary: input.summary,
2644
+ decisions: accepted
2645
+ };
2646
+ return { kind: "completed", spec };
2647
+ }
2648
+ function errMsg2(err) {
2649
+ return err instanceof Error ? err.message : String(err);
2650
+ }
2651
+
2652
+ // src/triage/retrospective.ts
2653
+ var LEVEL_RANK = {
2654
+ trivial: 0,
2655
+ simple: 1,
2656
+ moderate: 2,
2657
+ complex: 3
2658
+ };
2659
+ var BLAST_TOLERANCE_FACTOR = 1.5;
2660
+ var BLAST_TOLERANCE_ABS = 2;
2661
+ var DEFAULT_RETROSPECTIVE_CONFIG = { exceededByBands: 1 };
2662
+ function blockEscalate(exceededBy = 1) {
2663
+ return { matched: false, exceededBy: Math.max(1, exceededBy), action: "block-escalate" };
2664
+ }
2665
+ function isLevel(value) {
2666
+ return typeof value === "string" && Object.prototype.hasOwnProperty.call(LEVEL_RANK, value);
2667
+ }
2668
+ function blastRadiusOf(verdict) {
2669
+ const raw = verdict.signals?.blastRadius;
2670
+ return typeof raw === "number" && Number.isFinite(raw) ? raw : void 0;
2671
+ }
2672
+ function compareToPrediction(prediction, postDiffVerdict, config = DEFAULT_RETROSPECTIVE_CONFIG) {
2673
+ if (prediction == null || postDiffVerdict == null) return blockEscalate();
2674
+ const predictedLevel = prediction.verdict?.level;
2675
+ const actualLevel = postDiffVerdict.level;
2676
+ if (!isLevel(predictedLevel) || !isLevel(actualLevel)) return blockEscalate();
2677
+ const levelDelta = LEVEL_RANK[actualLevel] - LEVEL_RANK[predictedLevel];
2678
+ const bandThreshold = Math.max(1, Math.floor(config.exceededByBands));
2679
+ if (levelDelta >= bandThreshold) return blockEscalate(levelDelta);
2680
+ const predictedScope = typeof prediction.scopeEstimate === "number" && Number.isFinite(prediction.scopeEstimate) ? prediction.scopeEstimate : 0;
2681
+ const actualBlast = blastRadiusOf(postDiffVerdict);
2682
+ if (actualBlast !== void 0) {
2683
+ const threshold = predictedScope * BLAST_TOLERANCE_FACTOR + BLAST_TOLERANCE_ABS;
2684
+ if (actualBlast > threshold) return blockEscalate();
2685
+ }
2686
+ return { matched: true, exceededBy: 0, action: "verify" };
2687
+ }
2688
+
2689
+ // src/triage/ratchet.ts
2690
+ var V1_MAX_STAGE = 2;
2691
+ var DEFAULT_RATCHET_CONFIG = {
2692
+ threshold: 0.9,
2693
+ minSample: 5,
2694
+ window: 10
2695
+ };
2696
+ function resolveStage(history, config = DEFAULT_RATCHET_CONFIG) {
2697
+ const window = Math.max(1, Math.floor(config.window));
2698
+ const ordered = history.length > 1 ? [...history].sort((a, b) => (a.ts ?? "").localeCompare(b.ts ?? "")) : history;
2699
+ const recent = ordered.slice(-window);
2700
+ if (recent.length < config.minSample) return 1;
2701
+ if (recent[recent.length - 1]?.matched === false) return 1;
2702
+ const matched = recent.reduce((n, o) => o.matched ? n + 1 : n, 0);
2703
+ const rate = matched / recent.length;
2704
+ if (rate < config.threshold) return 1;
2705
+ return Math.min(2, V1_MAX_STAGE);
2706
+ }
2707
+
2708
+ // src/triage/gate.ts
2709
+ var AUTO_EXECUTE_CATEGORIES = /* @__PURE__ */ new Set([
2710
+ "quick-fix",
2711
+ "diagnostic"
2712
+ ]);
2713
+ function resolveGoNoGoStaged(candidates) {
2714
+ const approved = [];
2715
+ const held = [];
2716
+ for (const c of candidates) {
2717
+ if (c.effectiveStage !== 1 && c.effectiveStage !== 2) {
2718
+ held.push({
2719
+ externalId: c.externalId,
2720
+ category: c.category,
2721
+ reason: "ratchet-stage-unsupported"
2722
+ });
2723
+ continue;
2724
+ }
2725
+ if (!AUTO_EXECUTE_CATEGORIES.has(c.category)) {
2726
+ held.push({ externalId: c.externalId, category: c.category, reason: "not-auto-executable" });
2727
+ continue;
2728
+ }
2729
+ if (!c.humanApproved) {
2730
+ held.push({ externalId: c.externalId, category: c.category, reason: "awaiting-human-go" });
2731
+ continue;
2732
+ }
2733
+ approved.push({ ...c, effectiveStage: c.effectiveStage });
2734
+ }
2735
+ return { approved, held };
2736
+ }
2737
+ function resolveGoNoGo(candidates, stage) {
2738
+ return resolveGoNoGoStaged(candidates.map((c) => ({ ...c, effectiveStage: stage })));
2739
+ }
2123
2740
  // Annotate the CommonJS export names for ESM import in node:
2124
2741
  0 && (module.exports = {
2125
2742
  ACCEPTANCE_EVAL_SYSTEM_PROMPT,
2743
+ AUTO_EXECUTE_CATEGORIES,
2126
2744
  AcceptanceEvaluator,
2127
2745
  AnthropicAnalysisProvider,
2746
+ BLAST_TOLERANCE_ABS,
2747
+ BLAST_TOLERANCE_FACTOR,
2128
2748
  ClaudeCliAnalysisProvider,
2749
+ DEFAULT_DEGRADE_AT_PCT,
2750
+ DEFAULT_RATCHET_CONFIG,
2751
+ DEFAULT_RETROSPECTIVE_CONFIG,
2752
+ DEPTH_BY_LEVEL,
2129
2753
  ExecutionOutcomeConnector,
2130
2754
  GraphValidator,
2131
2755
  IntelligencePipeline,
2756
+ LEVEL_RANK,
2132
2757
  OUTCOME_EVAL_SYSTEM_PROMPT,
2133
2758
  OpenAICompatibleAnalysisProvider,
2134
2759
  OutcomeEvaluator,
2135
2760
  PeslSimulator,
2761
+ RANK_TIER,
2762
+ SENSITIVE_BLAST_THRESHOLD,
2763
+ STATIC_WEIGHTS,
2764
+ TIER_RANK,
2765
+ V1_MAX_STAGE,
2136
2766
  acceptanceVerdictSchema,
2767
+ aggregatePrecedent,
2768
+ applyBudgetClamp,
2769
+ baseTier,
2770
+ blastRadiusVeto,
2137
2771
  buildAcceptanceUserPrompt,
2138
2772
  buildSpecializationProfile,
2139
2773
  buildUserPrompt,
2774
+ classify,
2775
+ compareToPrediction,
2140
2776
  computeExpertiseLevel,
2141
2777
  computeHistoricalComplexity,
2142
2778
  computePersonaEffectiveness,
@@ -2145,24 +2781,40 @@ function refreshProfiles(projectRoot, graphStore, opts) {
2145
2781
  computeStructuralComplexity,
2146
2782
  createCanaryAdapter,
2147
2783
  decayWeight,
2784
+ depthForLevel,
2148
2785
  deriveAcceptanceAuthority,
2149
2786
  deriveAuthority,
2787
+ deriveRequiredTier,
2150
2788
  detectBlindSpots,
2789
+ dispatchableShapeKey,
2151
2790
  enrich,
2791
+ extractEntities,
2152
2792
  findingSchema,
2153
2793
  githubToRawWorkItem,
2154
2794
  jiraToRawWorkItem,
2155
2795
  linearToRawWorkItem,
2796
+ llmTiebreak,
2156
2797
  loadProfiles,
2157
2798
  manualToRawWorkItem,
2799
+ pilotScore,
2800
+ precedentLookupFromRecords,
2801
+ rankTriageCandidates,
2158
2802
  recommendPersona,
2159
2803
  refreshProfiles,
2804
+ resolveGoNoGo,
2805
+ resolveGoNoGoStaged,
2160
2806
  resolveSection,
2807
+ resolveStage,
2808
+ runAutoBrainstorm,
2161
2809
  runGraphOnlyChecks,
2162
2810
  runLlmSimulation,
2811
+ runScopingProbe,
2812
+ runStaticPass,
2163
2813
  saveProfiles,
2164
2814
  scoreCML,
2165
2815
  scoreToConcernSignals,
2816
+ serializeSignals,
2817
+ shapeKey,
2166
2818
  temporalSuccessRate,
2167
2819
  toRawWorkItem,
2168
2820
  verdictSchema,