@miller-tech/uap 1.26.6 → 1.28.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 (41) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/bin/cli.js +20 -0
  3. package/dist/bin/cli.js.map +1 -1
  4. package/dist/cli/deliver.d.ts +21 -0
  5. package/dist/cli/deliver.d.ts.map +1 -0
  6. package/dist/cli/deliver.js +197 -0
  7. package/dist/cli/deliver.js.map +1 -0
  8. package/dist/delivery/applier.d.ts +50 -0
  9. package/dist/delivery/applier.d.ts.map +1 -0
  10. package/dist/delivery/applier.js +258 -0
  11. package/dist/delivery/applier.js.map +1 -0
  12. package/dist/delivery/convergence-loop.d.ts +143 -0
  13. package/dist/delivery/convergence-loop.d.ts.map +1 -0
  14. package/dist/delivery/convergence-loop.js +301 -0
  15. package/dist/delivery/convergence-loop.js.map +1 -0
  16. package/dist/delivery/critic.d.ts +35 -0
  17. package/dist/delivery/critic.d.ts.map +1 -0
  18. package/dist/delivery/critic.js +77 -0
  19. package/dist/delivery/critic.js.map +1 -0
  20. package/dist/delivery/explorer.d.ts +77 -0
  21. package/dist/delivery/explorer.d.ts.map +1 -0
  22. package/dist/delivery/explorer.js +166 -0
  23. package/dist/delivery/explorer.js.map +1 -0
  24. package/dist/delivery/index.d.ts +15 -0
  25. package/dist/delivery/index.d.ts.map +1 -0
  26. package/dist/delivery/index.js +15 -0
  27. package/dist/delivery/index.js.map +1 -0
  28. package/dist/delivery/judge.d.ts +33 -0
  29. package/dist/delivery/judge.d.ts.map +1 -0
  30. package/dist/delivery/judge.js +70 -0
  31. package/dist/delivery/judge.js.map +1 -0
  32. package/dist/delivery/verifier-ladder.d.ts +78 -0
  33. package/dist/delivery/verifier-ladder.d.ts.map +1 -0
  34. package/dist/delivery/verifier-ladder.js +213 -0
  35. package/dist/delivery/verifier-ladder.js.map +1 -0
  36. package/dist/models/openai-compat-client.d.ts +34 -0
  37. package/dist/models/openai-compat-client.d.ts.map +1 -0
  38. package/dist/models/openai-compat-client.js +82 -0
  39. package/dist/models/openai-compat-client.js.map +1 -0
  40. package/package.json +1 -1
  41. package/tools/agents/docker-compose.qdrant.yml +7 -1
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Explorer — best-of-N candidate exploration (Phase 2)
3
+ *
4
+ * Instead of committing to a single attempt per turn, the explorer:
5
+ * 1. generates N candidates concurrently, each steered by a distinct
6
+ * strategy seed (diversity by prompt, not temperature — small-model
7
+ * profiles pin temperature low for stability)
8
+ * 2. evaluates each candidate against the real gates on the same baseline
9
+ * tree via apply → verify → rollback
10
+ * 3. ranks objectively (gates passed, then score); a judge tie-breaks
11
+ * candidates tied at the top
12
+ * 4. commits only the winner to the tree
13
+ *
14
+ * Candidate verification is sequential by design: gates (npm test, builds)
15
+ * cannot safely run concurrently in one tree. Worktree-isolated parallel
16
+ * verification is a planned optimization (inject via `revertibleApplier` +
17
+ * a per-candidate workspace), not a Phase 2 requirement.
18
+ *
19
+ * Fairness caveat: rollback reverts files the model wrote, but gate commands
20
+ * also mutate the tree (dist/ output, snapshots, caches). Candidate N+1 may
21
+ * therefore see candidate N's gate side effects. The committed winner is
22
+ * re-verified after commit so its reported ladder reflects real on-disk
23
+ * state. Full per-candidate isolation requires the workspace seam above.
24
+ */
25
+ import { runLadder } from './verifier-ladder.js';
26
+ import { applyFileBlocks, applyFileBlocksWithRollback } from './applier.js';
27
+ /** Hard ceiling on candidates per turn — guards direct library callers
28
+ * (the CLI caps lower); each candidate costs a model call + a full gate run. */
29
+ export const MAX_CANDIDATES = 8;
30
+ export const DEFAULT_STRATEGY_SEEDS = [
31
+ {
32
+ id: 'direct',
33
+ hint: 'STRATEGY: Make the most direct, minimal change that satisfies the task. Touch as few files as possible.',
34
+ },
35
+ {
36
+ id: 'test-first',
37
+ hint: 'STRATEGY: Reason from the failing gates first. Identify exactly what the gates check, then implement precisely that.',
38
+ },
39
+ {
40
+ id: 'defensive',
41
+ hint: 'STRATEGY: Implement with rigorous edge-case handling — empty inputs, wrong types, boundary values.',
42
+ },
43
+ {
44
+ id: 'rewrite',
45
+ hint: 'STRATEGY: Re-derive the solution from scratch rather than patching the previous attempt.',
46
+ },
47
+ ];
48
+ const DEFAULT_CANDIDATES = 3;
49
+ /**
50
+ * Generate, evaluate, and commit the best candidate for one loop turn.
51
+ * The base prompt comes from the loop's prompt builder; each candidate
52
+ * appends its strategy seed.
53
+ */
54
+ export async function exploreAndCommit(task, basePrompt, executor, config) {
55
+ const count = Math.min(MAX_CANDIDATES, Math.max(1, config.candidates ?? DEFAULT_CANDIDATES));
56
+ const seeds = config.seeds && config.seeds.length > 0 ? config.seeds : DEFAULT_STRATEGY_SEEDS;
57
+ const ladderRunner = config.ladderRunner ?? runLadder;
58
+ const apply = config.applier ?? applyFileBlocks;
59
+ const applyRevertible = config.revertibleApplier ?? applyFileBlocksWithRollback;
60
+ // 1. Generate all candidates concurrently (model calls parallelize fine)
61
+ const generations = await Promise.all(Array.from({ length: count }, async (_, i) => {
62
+ const seed = seeds[i % seeds.length];
63
+ const prompt = `${basePrompt}\n\n${seed.hint}`;
64
+ try {
65
+ return { seed, output: await executor(prompt), error: undefined };
66
+ }
67
+ catch (err) {
68
+ return { seed, output: '', error: err instanceof Error ? err.message : String(err) };
69
+ }
70
+ }));
71
+ // 2. Evaluate sequentially on the same baseline via apply → verify → rollback
72
+ const candidates = [];
73
+ for (let i = 0; i < generations.length; i++) {
74
+ const { seed, output, error } = generations[i];
75
+ const id = `c${i + 1}`;
76
+ if (error) {
77
+ const candidate = {
78
+ id,
79
+ strategy: seed.id,
80
+ output: '',
81
+ applyResult: null,
82
+ ladder: null,
83
+ error,
84
+ passed: false,
85
+ score: 0,
86
+ };
87
+ candidates.push(candidate);
88
+ config.onCandidate?.(candidate);
89
+ continue;
90
+ }
91
+ const { result: applyResult, restore } = applyRevertible(output, config.projectRoot);
92
+ let ladder = null;
93
+ try {
94
+ if (!applyResult.error && applyResult.filesWritten.length > 0) {
95
+ ladder = await ladderRunner(config.rungs, config.projectRoot, config.ladderOptions);
96
+ }
97
+ }
98
+ finally {
99
+ restore();
100
+ }
101
+ const candidate = {
102
+ id,
103
+ strategy: seed.id,
104
+ output,
105
+ applyResult,
106
+ ladder,
107
+ passed: ladder?.passed ?? false,
108
+ score: ladder?.score ?? 0,
109
+ };
110
+ candidates.push(candidate);
111
+ config.onCandidate?.(candidate);
112
+ }
113
+ // Only candidates that wrote files and reached a real ladder run are
114
+ // committable; anything else (executor error, no/rejected blocks) cannot
115
+ // win regardless of its zero score.
116
+ const committable = (c) => c.ladder !== null && (c.applyResult?.filesWritten.length ?? 0) > 0;
117
+ // 3. Rank by evaluation tier, then pass/score. A committable candidate
118
+ // always outranks a non-committable one (fixes error candidates with
119
+ // score 0 tying with evaluated candidates that scored 0).
120
+ const ranked = [...candidates].sort((a, b) => {
121
+ const ca = committable(a);
122
+ const cb = committable(b);
123
+ if (ca !== cb)
124
+ return ca ? -1 : 1;
125
+ if (a.passed !== b.passed)
126
+ return a.passed ? -1 : 1;
127
+ return b.score - a.score;
128
+ });
129
+ const top = ranked[0];
130
+ if (!top || !committable(top)) {
131
+ return { winner: null, candidates, ladder: null };
132
+ }
133
+ // Judge tie-break among committable candidates tied with the top result
134
+ let winner = top;
135
+ let judgeRationale;
136
+ const tied = ranked.filter((c) => committable(c) && c.passed === top.passed && c.score === top.score);
137
+ if (tied.length > 1 && config.judge) {
138
+ try {
139
+ const verdict = await config.judge(task, tied.map((c) => ({
140
+ id: c.id,
141
+ strategy: c.strategy,
142
+ output: c.output,
143
+ ladderFeedback: c.ladder?.feedback ?? '',
144
+ score: c.score,
145
+ })));
146
+ const chosen = tied.find((c) => c.id === verdict.winnerId);
147
+ if (chosen) {
148
+ winner = chosen;
149
+ judgeRationale = verdict.rationale;
150
+ }
151
+ }
152
+ catch {
153
+ // Judge is a public seam; a throwing judge falls back to objective top.
154
+ }
155
+ }
156
+ // 4. Commit the winner by re-applying. Re-verify the committed tree so the
157
+ // reported ladder reflects on-disk state (losers' gate side effects may
158
+ // have perturbed it between the winner's evaluation and now).
159
+ const committed = await apply(winner.output, config.projectRoot);
160
+ let finalLadder = winner.ladder;
161
+ if (committed.filesWritten.length > 0) {
162
+ finalLadder = await ladderRunner(config.rungs, config.projectRoot, config.ladderOptions);
163
+ }
164
+ return { winner: { ...winner, ladder: finalLadder }, candidates, judgeRationale, ladder: finalLadder };
165
+ }
166
+ //# sourceMappingURL=explorer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"explorer.js","sourceRoot":"","sources":["../../src/delivery/explorer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Delivery harness — Fable-parity convergence loop.
3
+ *
4
+ * Drives underlying models through execute → apply → verify → feedback
5
+ * iterations against the project's real completion gates until delivery
6
+ * is achieved.
7
+ */
8
+ export { ConvergenceLoop, defaultPromptBuilder, type CandidateSummary, type ConvergenceConfig, type DeliveryResult, type ExplorerSettings, type IterationRecord, type LoopExecutor, type LadderRunner, type PromptBuilder, type PromptContext, } from './convergence-loop.js';
9
+ export { exploreAndCommit, DEFAULT_STRATEGY_SEEDS, MAX_CANDIDATES, type CandidateResult, type ExplorationResult, type ExplorerConfig, type StrategySeed, } from './explorer.js';
10
+ export { createModelJudge, extractJson, type Judge, type JudgeCandidate, type JudgeVerdict, } from './judge.js';
11
+ export { createModelCritic, parseFixList, type Critic, type Critique, type CritiqueInput, } from './critic.js';
12
+ export { detectRungs, runLadder, runRung, formatFeedback, type GateRung, type RungResult, type RungFailureReason, type LadderResult, type LadderOptions, } from './verifier-ladder.js';
13
+ export { applyFileBlocks, applyFileBlocksWithRollback, parseFileBlocks, type Applier, type ApplyResult, type FileBlock, type RevertibleApply, } from './applier.js';
14
+ export { OpenAICompatClient, type OpenAICompatClientOptions } from '../models/openai-compat-client.js';
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/delivery/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,aAAa,GACnB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,YAAY,GAClB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,gBAAgB,EAChB,WAAW,EACX,KAAK,KAAK,EACV,KAAK,cAAc,EACnB,KAAK,YAAY,GAClB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,KAAK,MAAM,EACX,KAAK,QAAQ,EACb,KAAK,aAAa,GACnB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,WAAW,EACX,SAAS,EACT,OAAO,EACP,cAAc,EACd,KAAK,QAAQ,EACb,KAAK,UAAU,EACf,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,aAAa,GACnB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,eAAe,EACf,2BAA2B,EAC3B,eAAe,EACf,KAAK,OAAO,EACZ,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,eAAe,GACrB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,kBAAkB,EAAE,KAAK,yBAAyB,EAAE,MAAM,mCAAmC,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Delivery harness — Fable-parity convergence loop.
3
+ *
4
+ * Drives underlying models through execute → apply → verify → feedback
5
+ * iterations against the project's real completion gates until delivery
6
+ * is achieved.
7
+ */
8
+ export { ConvergenceLoop, defaultPromptBuilder, } from './convergence-loop.js';
9
+ export { exploreAndCommit, DEFAULT_STRATEGY_SEEDS, MAX_CANDIDATES, } from './explorer.js';
10
+ export { createModelJudge, extractJson, } from './judge.js';
11
+ export { createModelCritic, parseFixList, } from './critic.js';
12
+ export { detectRungs, runLadder, runRung, formatFeedback, } from './verifier-ladder.js';
13
+ export { applyFileBlocks, applyFileBlocksWithRollback, parseFileBlocks, } from './applier.js';
14
+ export { OpenAICompatClient } from '../models/openai-compat-client.js';
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/delivery/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,eAAe,EACf,oBAAoB,GAUrB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,cAAc,GAKf,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,gBAAgB,EAChB,WAAW,GAIZ,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,iBAAiB,EACjB,YAAY,GAIb,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,WAAW,EACX,SAAS,EACT,OAAO,EACP,cAAc,GAMf,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,eAAe,EACf,2BAA2B,EAC3B,eAAe,GAKhB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,kBAAkB,EAAkC,MAAM,mCAAmC,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Candidate Judge
3
+ *
4
+ * Tie-breaks exploration candidates whose objective (gate) scores are equal.
5
+ * Objective verification always outranks judgment — the judge is consulted
6
+ * only among candidates tied at the top of the ladder ranking.
7
+ */
8
+ import type { LoopExecutor } from './convergence-loop.js';
9
+ export interface JudgeCandidate {
10
+ /** Stable candidate id, e.g. 'c1' */
11
+ id: string;
12
+ /** Strategy seed that produced this candidate */
13
+ strategy: string;
14
+ /** Model output (the judge prompt truncates it before embedding) */
15
+ output: string;
16
+ /** Gate feedback for this candidate */
17
+ ladderFeedback: string;
18
+ /** Objective gate score */
19
+ score: number;
20
+ }
21
+ export interface JudgeVerdict {
22
+ winnerId: string;
23
+ rationale: string;
24
+ }
25
+ export type Judge = (task: string, candidates: JudgeCandidate[]) => Promise<JudgeVerdict>;
26
+ /** Extract the first JSON object from model output (tolerates prose/fences). */
27
+ export declare function extractJson(text: string): Record<string, unknown> | null;
28
+ /**
29
+ * Model-backed judge. Falls back to the first candidate (caller's ranking
30
+ * order) when the verdict is unparseable or names an unknown candidate.
31
+ */
32
+ export declare function createModelJudge(executor: LoopExecutor): Judge;
33
+ //# sourceMappingURL=judge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"judge.d.ts","sourceRoot":"","sources":["../../src/delivery/judge.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D,MAAM,WAAW,cAAc;IAC7B,qCAAqC;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,iDAAiD;IACjD,QAAQ,EAAE,MAAM,CAAC;IACjB,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,cAAc,EAAE,MAAM,CAAC;IACvB,2BAA2B;IAC3B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,KAAK,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;AA4B1F,gFAAgF;AAChF,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAQxE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,YAAY,GAAG,KAAK,CA0B9D"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Candidate Judge
3
+ *
4
+ * Tie-breaks exploration candidates whose objective (gate) scores are equal.
5
+ * Objective verification always outranks judgment — the judge is consulted
6
+ * only among candidates tied at the top of the ladder ranking.
7
+ */
8
+ const CANDIDATE_OUTPUT_CHARS = 2_000;
9
+ function buildJudgePrompt(task, candidates) {
10
+ const sections = [
11
+ 'You are a strict senior code reviewer judging competing solutions to the same task.',
12
+ 'Rate on: correctness, completeness, simplicity, and how well gate feedback was addressed.',
13
+ '',
14
+ `TASK: ${task}`,
15
+ '',
16
+ ];
17
+ for (const c of candidates) {
18
+ sections.push(`=== CANDIDATE ${c.id} (strategy: ${c.strategy}, gate score: ${Math.round(c.score * 100)}%) ===`);
19
+ sections.push(c.output.slice(0, CANDIDATE_OUTPUT_CHARS));
20
+ if (c.ladderFeedback) {
21
+ sections.push(`Gate feedback: ${c.ladderFeedback.slice(0, 500)}`);
22
+ }
23
+ sections.push('');
24
+ }
25
+ sections.push(`Respond with ONLY a JSON object: {"winner": "<candidate id>", "rationale": "<one sentence>"}`);
26
+ return sections.join('\n');
27
+ }
28
+ /** Extract the first JSON object from model output (tolerates prose/fences). */
29
+ export function extractJson(text) {
30
+ const match = text.match(/\{[\s\S]*?\}/);
31
+ if (!match)
32
+ return null;
33
+ try {
34
+ return JSON.parse(match[0]);
35
+ }
36
+ catch {
37
+ return null;
38
+ }
39
+ }
40
+ /**
41
+ * Model-backed judge. Falls back to the first candidate (caller's ranking
42
+ * order) when the verdict is unparseable or names an unknown candidate.
43
+ */
44
+ export function createModelJudge(executor) {
45
+ return async (task, candidates) => {
46
+ const fallback = {
47
+ winnerId: candidates[0].id,
48
+ rationale: 'judge fallback: kept objective ranking',
49
+ };
50
+ if (candidates.length < 2)
51
+ return fallback;
52
+ let raw;
53
+ try {
54
+ raw = await executor(buildJudgePrompt(task, candidates));
55
+ }
56
+ catch {
57
+ return fallback;
58
+ }
59
+ const parsed = extractJson(raw);
60
+ const winner = typeof parsed?.winner === 'string' ? parsed.winner : undefined;
61
+ if (!winner || !candidates.some((c) => c.id === winner)) {
62
+ return fallback;
63
+ }
64
+ return {
65
+ winnerId: winner,
66
+ rationale: typeof parsed?.rationale === 'string' ? parsed.rationale : '',
67
+ };
68
+ };
69
+ }
70
+ //# sourceMappingURL=judge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"judge.js","sourceRoot":"","sources":["../../src/delivery/judge.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAwBH,MAAM,sBAAsB,GAAG,KAAK,CAAC;AAErC,SAAS,gBAAgB,CAAC,IAAY,EAAE,UAA4B;IAClE,MAAM,QAAQ,GAAG;QACf,qFAAqF;QACrF,2FAA2F;QAC3F,EAAE;QACF,SAAS,IAAI,EAAE;QACf,EAAE;KACH,CAAC;IAEF,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QAC3B,QAAQ,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC,QAAQ,iBAAiB,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC;QAChH,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,sBAAsB,CAAC,CAAC,CAAC;QACzD,IAAI,CAAC,CAAC,cAAc,EAAE,CAAC;YACrB,QAAQ,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QACpE,CAAC;QACD,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACpB,CAAC;IAED,QAAQ,CAAC,IAAI,CACX,8FAA8F,CAC/F,CAAC;IACF,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IACzC,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAA4B,CAAC;IACzD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAsB;IACrD,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE;QAChC,MAAM,QAAQ,GAAiB;YAC7B,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE;YAC1B,SAAS,EAAE,wCAAwC;SACpD,CAAC;QACF,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,QAAQ,CAAC;QAE3C,IAAI,GAAW,CAAC;QAChB,IAAI,CAAC;YACH,GAAG,GAAG,MAAM,QAAQ,CAAC,gBAAgB,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC;QAC3D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,QAAQ,CAAC;QAClB,CAAC;QAED,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;QAChC,MAAM,MAAM,GAAG,OAAO,MAAM,EAAE,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;QAC9E,IAAI,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,EAAE,CAAC;YACxD,OAAO,QAAQ,CAAC;QAClB,CAAC;QAED,OAAO;YACL,QAAQ,EAAE,MAAM;YAChB,SAAS,EAAE,OAAO,MAAM,EAAE,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE;SACzE,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Verifier Ladder
3
+ *
4
+ * Turns the repository's completion gates (build, type-check, test, lint)
5
+ * into a programmatic verifier that the convergence loop can call after each
6
+ * model iteration. Each rung runs a real command in the project root; the
7
+ * ladder reports a pass/fail per rung, an aggregate score (fraction of rungs
8
+ * passed), and structured feedback sized for small-model context budgets.
9
+ *
10
+ * Detection is npm-centric (package.json scripts); callers targeting other
11
+ * ecosystems can pass explicit rungs.
12
+ */
13
+ export interface GateRung {
14
+ /** Stable identifier, e.g. 'build', 'typecheck', 'test', 'lint' */
15
+ id: string;
16
+ /** Human-readable name shown in feedback */
17
+ name: string;
18
+ /** Executable to run (no shell interpolation) */
19
+ command: string;
20
+ /** Arguments passed verbatim */
21
+ args: string[];
22
+ /**
23
+ * Required rungs gate delivery: the ladder only passes when all required
24
+ * rungs pass, and a required failure stops later rungs (fail-fast).
25
+ * Optional rungs are reported but never block delivery.
26
+ */
27
+ required: boolean;
28
+ /** Per-rung timeout in milliseconds */
29
+ timeoutMs: number;
30
+ }
31
+ export type RungFailureReason = 'exit' | 'timeout' | 'signal' | 'spawn-error';
32
+ export interface RungResult {
33
+ id: string;
34
+ name: string;
35
+ passed: boolean;
36
+ /** True when the rung never ran because an earlier required rung failed */
37
+ skipped: boolean;
38
+ exitCode: number | null;
39
+ /** Why the rung failed; undefined when it passed or was skipped */
40
+ failureReason?: RungFailureReason;
41
+ durationMs: number;
42
+ /** Tail of combined stdout+stderr, truncated for prompt injection */
43
+ outputTail: string;
44
+ }
45
+ export interface LadderResult {
46
+ /** True when every required rung passed */
47
+ passed: boolean;
48
+ /** Fraction of all rungs that passed (skipped rungs count as not passed) */
49
+ score: number;
50
+ results: RungResult[];
51
+ /** Structured feedback for the next loop iteration */
52
+ feedback: string;
53
+ }
54
+ export interface LadderOptions {
55
+ /** Stop at the first failing required rung (default true — cheaper feedback) */
56
+ failFast?: boolean;
57
+ /** Max characters of command output included per failing rung (default 2000) */
58
+ outputTailChars?: number;
59
+ /** Default per-rung timeout in ms (default 300000) */
60
+ timeoutMs?: number;
61
+ }
62
+ /**
63
+ * Detect the gate rungs available in a project from its package.json scripts.
64
+ * Order matters: cheap/structural gates run before expensive ones so failure
65
+ * feedback arrives fast.
66
+ */
67
+ export declare function detectRungs(projectRoot: string, timeoutMs?: number): GateRung[];
68
+ /** Run a single rung synchronously in the project root. */
69
+ export declare function runRung(rung: GateRung, projectRoot: string, tailChars?: number): RungResult;
70
+ /**
71
+ * Build feedback text from rung results. Only the first failing required
72
+ * rung's output is included in detail — small models do better with one
73
+ * concrete problem at a time than with a wall of every failure.
74
+ */
75
+ export declare function formatFeedback(results: RungResult[], rungs: GateRung[]): string;
76
+ /** Run the full ladder, honoring fail-fast for required rungs. */
77
+ export declare function runLadder(rungs: GateRung[], projectRoot: string, options?: LadderOptions): LadderResult;
78
+ //# sourceMappingURL=verifier-ladder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verifier-ladder.d.ts","sourceRoot":"","sources":["../../src/delivery/verifier-ladder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,MAAM,WAAW,QAAQ;IACvB,mEAAmE;IACnE,EAAE,EAAE,MAAM,CAAC;IACX,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAC;IAChB,gCAAgC;IAChC,IAAI,EAAE,MAAM,EAAE,CAAC;IACf;;;;OAIG;IACH,QAAQ,EAAE,OAAO,CAAC;IAClB,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,aAAa,CAAC;AAE9E,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,2EAA2E;IAC3E,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,mEAAmE;IACnE,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,qEAAqE;IACrE,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,2CAA2C;IAC3C,MAAM,EAAE,OAAO,CAAC;IAChB,4EAA4E;IAC5E,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,sDAAsD;IACtD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,gFAAgF;IAChF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gFAAgF;IAChF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAmBD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,GAAE,MAA2B,GAAG,QAAQ,EAAE,CAmEnG;AAQD,2DAA2D;AAC3D,wBAAgB,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,GAAE,MAA2B,GAAG,UAAU,CA+C/G;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM,CAqB/E;AAED,kEAAkE;AAClE,wBAAgB,SAAS,CACvB,KAAK,EAAE,QAAQ,EAAE,EACjB,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,aAAkB,GAC1B,YAAY,CA8Cd"}
@@ -0,0 +1,213 @@
1
+ /**
2
+ * Verifier Ladder
3
+ *
4
+ * Turns the repository's completion gates (build, type-check, test, lint)
5
+ * into a programmatic verifier that the convergence loop can call after each
6
+ * model iteration. Each rung runs a real command in the project root; the
7
+ * ladder reports a pass/fail per rung, an aggregate score (fraction of rungs
8
+ * passed), and structured feedback sized for small-model context budgets.
9
+ *
10
+ * Detection is npm-centric (package.json scripts); callers targeting other
11
+ * ecosystems can pass explicit rungs.
12
+ */
13
+ import { spawnSync } from 'child_process';
14
+ import { existsSync, readFileSync } from 'fs';
15
+ import { join } from 'path';
16
+ const DEFAULT_TIMEOUT_MS = 300_000;
17
+ const DEFAULT_TAIL_CHARS = 2_000;
18
+ /** Env vars matching these patterns are stripped before running gate
19
+ * commands — project scripts (and npm lifecycle hooks) in arbitrary
20
+ * --project-root checkouts must not inherit provider credentials. */
21
+ const SECRET_ENV_RE = /(API_KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL)/i;
22
+ function sanitizedEnv() {
23
+ const env = { CI: 'true' };
24
+ for (const [key, value] of Object.entries(process.env)) {
25
+ if (SECRET_ENV_RE.test(key))
26
+ continue;
27
+ env[key] = value;
28
+ }
29
+ return env;
30
+ }
31
+ /**
32
+ * Detect the gate rungs available in a project from its package.json scripts.
33
+ * Order matters: cheap/structural gates run before expensive ones so failure
34
+ * feedback arrives fast.
35
+ */
36
+ export function detectRungs(projectRoot, timeoutMs = DEFAULT_TIMEOUT_MS) {
37
+ const pkgPath = join(projectRoot, 'package.json');
38
+ if (!existsSync(pkgPath)) {
39
+ return [];
40
+ }
41
+ let scripts = {};
42
+ try {
43
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
44
+ scripts = pkg.scripts ?? {};
45
+ }
46
+ catch {
47
+ return [];
48
+ }
49
+ const rungs = [];
50
+ if (scripts['build']) {
51
+ rungs.push({
52
+ id: 'build',
53
+ name: 'Build (npm run build)',
54
+ command: 'npm',
55
+ args: ['run', 'build'],
56
+ required: true,
57
+ timeoutMs,
58
+ });
59
+ }
60
+ // Type-check is meaningful for TypeScript projects even when build exists
61
+ // (build may use a bundler that skips type errors). --no-install fails
62
+ // closed instead of letting npx fetch the registry package named 'tsc'.
63
+ if (existsSync(join(projectRoot, 'tsconfig.json')) &&
64
+ existsSync(join(projectRoot, 'node_modules', '.bin', 'tsc'))) {
65
+ rungs.push({
66
+ id: 'typecheck',
67
+ name: 'Type-check (tsc --noEmit)',
68
+ command: 'npx',
69
+ args: ['--no-install', 'tsc', '--noEmit'],
70
+ required: true,
71
+ timeoutMs,
72
+ });
73
+ }
74
+ if (scripts['test']) {
75
+ rungs.push({
76
+ id: 'test',
77
+ name: 'Tests (npm test)',
78
+ command: 'npm',
79
+ args: ['test'],
80
+ required: true,
81
+ timeoutMs,
82
+ });
83
+ }
84
+ if (scripts['lint']) {
85
+ rungs.push({
86
+ id: 'lint',
87
+ name: 'Lint (npm run lint)',
88
+ command: 'npm',
89
+ args: ['run', 'lint'],
90
+ required: false,
91
+ timeoutMs,
92
+ });
93
+ }
94
+ return rungs;
95
+ }
96
+ function truncateTail(text, maxChars) {
97
+ const trimmed = text.trim();
98
+ if (trimmed.length <= maxChars)
99
+ return trimmed;
100
+ return `…(truncated)…\n${trimmed.slice(-maxChars)}`;
101
+ }
102
+ /** Run a single rung synchronously in the project root. */
103
+ export function runRung(rung, projectRoot, tailChars = DEFAULT_TAIL_CHARS) {
104
+ const start = Date.now();
105
+ const res = spawnSync(rung.command, rung.args, {
106
+ cwd: projectRoot,
107
+ encoding: 'utf-8',
108
+ timeout: rung.timeoutMs,
109
+ maxBuffer: 16 * 1024 * 1024,
110
+ env: sanitizedEnv(),
111
+ });
112
+ const durationMs = Date.now() - start;
113
+ const exitCode = res.status;
114
+ const passed = exitCode === 0;
115
+ // spawnSync reports timeouts/missing binaries via res.error (+ res.signal),
116
+ // with status null and empty output — without this the model would get a
117
+ // bare "FAIL" and burn its turn budget against an unexplained gate.
118
+ let failureReason;
119
+ let diagnostic = '';
120
+ if (!passed) {
121
+ const errCode = res.error?.code;
122
+ if (errCode === 'ETIMEDOUT' || (res.error && res.signal === 'SIGTERM')) {
123
+ failureReason = 'timeout';
124
+ diagnostic = `Gate timed out after ${rung.timeoutMs}ms.`;
125
+ }
126
+ else if (res.error) {
127
+ failureReason = 'spawn-error';
128
+ diagnostic = `Gate could not run: ${res.error.message}`;
129
+ }
130
+ else if (res.signal) {
131
+ failureReason = 'signal';
132
+ diagnostic = `Gate was killed by signal ${res.signal}.`;
133
+ }
134
+ else {
135
+ failureReason = 'exit';
136
+ }
137
+ }
138
+ const combined = `${diagnostic}\n${res.stdout ?? ''}\n${res.stderr ?? ''}`;
139
+ return {
140
+ id: rung.id,
141
+ name: rung.name,
142
+ passed,
143
+ skipped: false,
144
+ exitCode,
145
+ failureReason,
146
+ durationMs,
147
+ outputTail: passed ? '' : truncateTail(combined, tailChars),
148
+ };
149
+ }
150
+ /**
151
+ * Build feedback text from rung results. Only the first failing required
152
+ * rung's output is included in detail — small models do better with one
153
+ * concrete problem at a time than with a wall of every failure.
154
+ */
155
+ export function formatFeedback(results, rungs) {
156
+ const requiredIds = new Set(rungs.filter((r) => r.required).map((r) => r.id));
157
+ const lines = ['Gate results:'];
158
+ for (const r of results) {
159
+ const status = r.skipped ? 'SKIPPED (earlier gate failed)' : r.passed ? 'PASS' : 'FAIL';
160
+ const optional = requiredIds.has(r.id) ? '' : ' (optional)';
161
+ lines.push(`- ${r.name}${optional}: ${status}`);
162
+ }
163
+ const firstFailure = results.find((r) => !r.passed && !r.skipped && requiredIds.has(r.id)) ??
164
+ results.find((r) => !r.passed && !r.skipped);
165
+ if (firstFailure && firstFailure.outputTail) {
166
+ lines.push('');
167
+ lines.push(`Fix this gate first — ${firstFailure.name} output:`);
168
+ lines.push('```');
169
+ lines.push(firstFailure.outputTail);
170
+ lines.push('```');
171
+ }
172
+ return lines.join('\n');
173
+ }
174
+ /** Run the full ladder, honoring fail-fast for required rungs. */
175
+ export function runLadder(rungs, projectRoot, options = {}) {
176
+ const failFast = options.failFast ?? true;
177
+ const tailChars = options.outputTailChars ?? DEFAULT_TAIL_CHARS;
178
+ const results = [];
179
+ let stop = false;
180
+ for (const rung of rungs) {
181
+ if (stop) {
182
+ results.push({
183
+ id: rung.id,
184
+ name: rung.name,
185
+ passed: false,
186
+ skipped: true,
187
+ exitCode: null,
188
+ durationMs: 0,
189
+ outputTail: '',
190
+ });
191
+ continue;
192
+ }
193
+ const result = runRung(rung, projectRoot, tailChars);
194
+ results.push(result);
195
+ if (!result.passed && rung.required && failFast) {
196
+ stop = true;
197
+ }
198
+ }
199
+ const passedCount = results.filter((r) => r.passed).length;
200
+ const score = rungs.length > 0 ? passedCount / rungs.length : 1;
201
+ // Delivery is gated on required rungs only; optional gates (lint) are
202
+ // reported but never block convergence.
203
+ const requiredRungs = rungs.filter((r) => r.required);
204
+ const requiredPassed = results.filter((r) => r.passed && requiredRungs.some((rung) => rung.id === r.id)).length;
205
+ const passed = requiredPassed === requiredRungs.length;
206
+ return {
207
+ passed,
208
+ score,
209
+ results,
210
+ feedback: formatFeedback(results, rungs),
211
+ };
212
+ }
213
+ //# sourceMappingURL=verifier-ladder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verifier-ladder.js","sourceRoot":"","sources":["../../src/delivery/verifier-ladder.ts"],"names":[],"mappings":"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"}