@miller-tech/uap 1.163.13 → 1.165.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 +1 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/bin/cli.js +5 -4
- package/dist/bin/cli.js.map +1 -1
- package/dist/browser/web-browser.d.ts +7 -0
- package/dist/browser/web-browser.d.ts.map +1 -1
- package/dist/browser/web-browser.js +15 -0
- package/dist/browser/web-browser.js.map +1 -1
- package/dist/cli/deliver.d.ts +32 -3
- package/dist/cli/deliver.d.ts.map +1 -1
- package/dist/cli/deliver.js +198 -31
- package/dist/cli/deliver.js.map +1 -1
- package/dist/cli/guided-setup.d.ts.map +1 -1
- package/dist/cli/guided-setup.js +13 -1
- package/dist/cli/guided-setup.js.map +1 -1
- package/dist/cli/model.d.ts.map +1 -1
- package/dist/cli/model.js +9 -1
- package/dist/cli/model.js.map +1 -1
- package/dist/cli/proxy-lifecycle.d.ts +66 -13
- package/dist/cli/proxy-lifecycle.d.ts.map +1 -1
- package/dist/cli/proxy-lifecycle.js +116 -49
- package/dist/cli/proxy-lifecycle.js.map +1 -1
- package/dist/cli/proxy.d.ts +46 -0
- package/dist/cli/proxy.d.ts.map +1 -1
- package/dist/cli/proxy.js +353 -23
- package/dist/cli/proxy.js.map +1 -1
- package/dist/cli/verify.d.ts +6 -0
- package/dist/cli/verify.d.ts.map +1 -1
- package/dist/cli/verify.js +64 -0
- package/dist/cli/verify.js.map +1 -1
- package/dist/cli/wizard-config.d.ts +4 -0
- package/dist/cli/wizard-config.d.ts.map +1 -1
- package/dist/cli/wizard-config.js +14 -3
- package/dist/cli/wizard-config.js.map +1 -1
- package/dist/config/settings-registry.d.ts.map +1 -1
- package/dist/config/settings-registry.js +25 -0
- package/dist/config/settings-registry.js.map +1 -1
- package/dist/coordination/effort-profile.d.ts +39 -0
- package/dist/coordination/effort-profile.d.ts.map +1 -0
- package/dist/coordination/effort-profile.js +74 -0
- package/dist/coordination/effort-profile.js.map +1 -0
- package/dist/dashboard/server.d.ts.map +1 -1
- package/dist/dashboard/server.js +68 -10
- package/dist/dashboard/server.js.map +1 -1
- package/dist/delivery/agentic-executor.d.ts +15 -0
- package/dist/delivery/agentic-executor.d.ts.map +1 -1
- package/dist/delivery/agentic-executor.js +64 -3
- package/dist/delivery/agentic-executor.js.map +1 -1
- package/dist/delivery/convergence-loop.d.ts +76 -1
- package/dist/delivery/convergence-loop.d.ts.map +1 -1
- package/dist/delivery/convergence-loop.js +71 -6
- package/dist/delivery/convergence-loop.js.map +1 -1
- package/dist/delivery/critic.d.ts +7 -0
- package/dist/delivery/critic.d.ts.map +1 -1
- package/dist/delivery/critic.js.map +1 -1
- package/dist/delivery/decompose.d.ts +9 -0
- package/dist/delivery/decompose.d.ts.map +1 -1
- package/dist/delivery/decompose.js +11 -0
- package/dist/delivery/decompose.js.map +1 -1
- package/dist/delivery/execution-gate.d.ts +4 -0
- package/dist/delivery/execution-gate.d.ts.map +1 -1
- package/dist/delivery/execution-gate.js +194 -2
- package/dist/delivery/execution-gate.js.map +1 -1
- package/dist/delivery/graph-safety.d.ts +75 -0
- package/dist/delivery/graph-safety.d.ts.map +1 -0
- package/dist/delivery/graph-safety.js +98 -0
- package/dist/delivery/graph-safety.js.map +1 -0
- package/dist/delivery/mission-acceptance.d.ts.map +1 -1
- package/dist/delivery/mission-acceptance.js +69 -21
- package/dist/delivery/mission-acceptance.js.map +1 -1
- package/dist/delivery/per-phase-escalation-controller.d.ts +37 -0
- package/dist/delivery/per-phase-escalation-controller.d.ts.map +1 -0
- package/dist/delivery/per-phase-escalation-controller.js +68 -0
- package/dist/delivery/per-phase-escalation-controller.js.map +1 -0
- package/dist/delivery/per-phase-escalation.d.ts +53 -0
- package/dist/delivery/per-phase-escalation.d.ts.map +1 -0
- package/dist/delivery/per-phase-escalation.js +57 -0
- package/dist/delivery/per-phase-escalation.js.map +1 -0
- package/dist/delivery/reflect.d.ts +56 -0
- package/dist/delivery/reflect.d.ts.map +1 -0
- package/dist/delivery/reflect.js +79 -0
- package/dist/delivery/reflect.js.map +1 -0
- package/dist/delivery/user-paths.d.ts +42 -0
- package/dist/delivery/user-paths.d.ts.map +1 -1
- package/dist/delivery/user-paths.js +254 -6
- package/dist/delivery/user-paths.js.map +1 -1
- package/dist/delivery/user-validation.d.ts.map +1 -1
- package/dist/delivery/user-validation.js +21 -1
- package/dist/delivery/user-validation.js.map +1 -1
- package/dist/delivery/verification-provenance.d.ts +47 -0
- package/dist/delivery/verification-provenance.d.ts.map +1 -0
- package/dist/delivery/verification-provenance.js +48 -0
- package/dist/delivery/verification-provenance.js.map +1 -0
- package/dist/delivery/vision-judge.d.ts.map +1 -1
- package/dist/delivery/vision-judge.js +8 -1
- package/dist/delivery/vision-judge.js.map +1 -1
- package/dist/models/complexity.d.ts +36 -0
- package/dist/models/complexity.d.ts.map +1 -0
- package/dist/models/complexity.js +75 -0
- package/dist/models/complexity.js.map +1 -0
- package/dist/models/openai-compat-client.d.ts +26 -0
- package/dist/models/openai-compat-client.d.ts.map +1 -1
- package/dist/models/openai-compat-client.js +60 -8
- package/dist/models/openai-compat-client.js.map +1 -1
- package/dist/models/router.d.ts.map +1 -1
- package/dist/models/router.js +39 -10
- package/dist/models/router.js.map +1 -1
- package/dist/models/types.d.ts +60 -10
- package/dist/models/types.d.ts.map +1 -1
- package/dist/models/types.js +128 -11
- package/dist/models/types.js.map +1 -1
- package/dist/self-tuning/prompt-variants.d.ts +93 -0
- package/dist/self-tuning/prompt-variants.d.ts.map +1 -0
- package/dist/self-tuning/prompt-variants.js +143 -0
- package/dist/self-tuning/prompt-variants.js.map +1 -0
- package/dist/tasks/types.d.ts +2 -2
- package/dist/types/config.d.ts +8 -0
- package/dist/types/config.d.ts.map +1 -1
- package/docs/INDEX.md +1 -1
- package/docs/benchmarks/COMPREHENSIVE_BENCHMARKS.md +47 -53
- package/docs/benchmarks/VALIDATION_RESULTS.md +0 -2
- package/docs/guides/COORDINATION.md +1 -1
- package/docs/guides/DEPLOY_BATCHING.md +1 -1
- package/docs/guides/DROIDS_AND_SKILLS.md +3 -2
- package/docs/guides/LOCAL_MODELS.md +1 -1
- package/docs/guides/MCP_ROUTER.md +1 -1
- package/docs/guides/MEMORY.md +2 -2
- package/docs/guides/MULTI_MODEL.md +1 -1
- package/docs/guides/POLICIES.md +1 -1
- package/docs/guides/PROXY.md +47 -4
- package/docs/guides/SANDBOX.md +1 -1
- package/docs/guides/WORKTREE_WORKFLOW.md +1 -1
- package/docs/reference/CLI.md +16 -3
- package/docs/reference/CONFIGURATION.md +1 -1
- package/docs/reference/FEATURES.md +1 -1
- package/package.json +1 -1
- package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
- package/src/policies/enforcers/iac_plan_destruction_check.py +32 -4
- package/templates/hooks/__pycache__/deliver_autoroute.cpython-312.pyc +0 -0
- package/templates/hooks/session-end.sh +9 -0
- package/templates/hooks/session-start.sh +10 -0
- package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-312.pyc +0 -0
package/dist/cli/deliver.js
CHANGED
|
@@ -11,13 +11,15 @@ import { existsSync, mkdirSync, openSync, readFileSync, rmSync, writeSync, close
|
|
|
11
11
|
import { join, resolve, relative } from 'path';
|
|
12
12
|
import { ConvergenceLoop, composeIterationHooks } from '../delivery/convergence-loop.js';
|
|
13
13
|
import { createModelJudge } from '../delivery/judge.js';
|
|
14
|
+
import { resolveJudgePlan, formatVerificationProvenance, } from '../delivery/verification-provenance.js';
|
|
14
15
|
import { createModelCritic } from '../delivery/critic.js';
|
|
15
16
|
import { MAX_CANDIDATES } from '../delivery/explorer.js';
|
|
16
17
|
import { generateStrategySeeds, seedsFromIdeas } from '../delivery/ideation.js';
|
|
17
18
|
import { DEFAULT_STRATEGY_SEEDS } from '../delivery/explorer.js';
|
|
18
19
|
import { planAutoOptimization } from '../delivery/auto-optimizer.js';
|
|
19
|
-
import { RoutingPresets, resolvePresetModel } from '../models/types.js';
|
|
20
|
-
import {
|
|
20
|
+
import { RoutingPresets, resolvePresetModel, resolvePhaseChain } from '../models/types.js';
|
|
21
|
+
import { classifyComplexity, tierToRouting } from '../models/complexity.js';
|
|
22
|
+
import { promptSelectionFromConfig } from '../self-tuning/prompt-variants.js';
|
|
21
23
|
import { authorAcceptanceGate } from '../delivery/self-gate.js';
|
|
22
24
|
import { applyPendingIntents } from '../delivery/pending-intents.js';
|
|
23
25
|
import { runAcceptanceGate } from '../delivery/acceptance-judge.js';
|
|
@@ -47,6 +49,36 @@ export function shouldSkipAcceptanceJudge(opts) {
|
|
|
47
49
|
opts.complexity === 'simple' &&
|
|
48
50
|
!opts.acceptancePrimary);
|
|
49
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* When the model fails to author a self-gate, is that fatal?
|
|
54
|
+
*
|
|
55
|
+
* Only if the run would then have NO convergence target at all. Getting this
|
|
56
|
+
* wrong in the permissive direction re-opens the false-green door the
|
|
57
|
+
* anti-vacuous floor exists to close: with no self-gate rung and no acceptance
|
|
58
|
+
* judge, a repo whose gates were already green is "delivered" having written
|
|
59
|
+
* nothing.
|
|
60
|
+
*
|
|
61
|
+
* Two traps this predicate exists to avoid, both of which `!options.acceptance`
|
|
62
|
+
* alone walks straight into:
|
|
63
|
+
*
|
|
64
|
+
* - `--acceptance` is on but the judge is SKIPPED for a simple task
|
|
65
|
+
* (shouldSkipAcceptanceJudge), so no gate object is ever built.
|
|
66
|
+
* - max fidelity is treated as a standing blocker. It is not: the vision review
|
|
67
|
+
* lives INSIDE the acceptance gate, so if that gate was never built there is
|
|
68
|
+
* nothing to block, and visionAcceptanceFeedback additionally fails open on
|
|
69
|
+
* five separate conditions (no vision model, no screenshots, non-final epic,
|
|
70
|
+
* any thrown error, fidelity not max).
|
|
71
|
+
*
|
|
72
|
+
* So the only thing that makes a missing self-gate survivable is a judge that
|
|
73
|
+
* will actually run — plus at least one real rung to converge against.
|
|
74
|
+
*/
|
|
75
|
+
export function selfGateFailureIsFatal(opts) {
|
|
76
|
+
// A judge that will actually run is the only substitute. Surviving rungs do not
|
|
77
|
+
// rescue this: needsSelfGate is raised precisely when the objective gates are
|
|
78
|
+
// all GREEN (no convergence pressure) or absent entirely (the loop throws), so
|
|
79
|
+
// in both cases a missing self-gate with no judge leaves nothing to satisfy.
|
|
80
|
+
return !(opts.acceptanceEnabled && !opts.judgeSkipped);
|
|
81
|
+
}
|
|
50
82
|
/**
|
|
51
83
|
* Generator≠Evaluator: resolve which model should AUTHOR + JUDGE the acceptance
|
|
52
84
|
* gate. Returns the evaluator preset id when it must differ from the generator
|
|
@@ -105,7 +137,7 @@ import { initLedger, markItem } from '../delivery/completion-ledger.js';
|
|
|
105
137
|
import { loadUapConfigRaw } from '../utils/config-loader.js';
|
|
106
138
|
import { resolveEscalateModelId } from '../delivery/repair-escalation.js';
|
|
107
139
|
import { createUserValidationRunner, resolveUserValidationMode, synthesizeUserValidationRung, } from '../delivery/user-validation.js';
|
|
108
|
-
import { deriveUserPaths, fallbackWebManifest, loadUserPaths, mergeUserPaths, USER_PATHS_FILE } from '../delivery/user-paths.js';
|
|
140
|
+
import { deriveUserPaths, fallbackWebManifest, loadUserPaths, mergeUserPaths, renderAcceptanceContract, USER_PATHS_FILE } from '../delivery/user-paths.js';
|
|
109
141
|
import { detectArtifactType } from '../delivery/execution-gate.js';
|
|
110
142
|
/** Raw .uap.json reader usable before the main cfgRawEarly declaration. */
|
|
111
143
|
function cfgRawEarlyForUvFactory(projectRoot) {
|
|
@@ -182,20 +214,14 @@ function fail(message) {
|
|
|
182
214
|
}
|
|
183
215
|
class ExitError extends Error {
|
|
184
216
|
}
|
|
185
|
-
/** Deliver's 3-level complexity (simple/moderate/complex) → the routing tier
|
|
186
|
-
* scale (low/medium/high/critical). Deliver's classifier never emits
|
|
187
|
-
* 'critical' — that tier is reserved for keyword-driven escalation. */
|
|
188
|
-
const COMPLEXITY_TO_TIER = {
|
|
189
|
-
simple: 'low',
|
|
190
|
-
moderate: 'medium',
|
|
191
|
-
complex: 'high',
|
|
192
|
-
};
|
|
193
217
|
/**
|
|
194
218
|
* Complexity-tier routing (P: per-task model selection). Returns the model id
|
|
195
219
|
* to execute this task on, or null to leave the caller's default untouched.
|
|
196
|
-
* PURE — unit-tested.
|
|
197
|
-
*
|
|
198
|
-
*
|
|
220
|
+
* PURE — unit-tested. Uses the unified complexity classifier (src/models/
|
|
221
|
+
* complexity.ts) so `critical` is preserved end-to-end — the old
|
|
222
|
+
* COMPLEXITY_TO_TIER bridge silently dropped it. Precedence is enforced by the
|
|
223
|
+
* caller: an explicit --model always wins; this only fires when routing is
|
|
224
|
+
* requested and no model was pinned.
|
|
199
225
|
*/
|
|
200
226
|
export function resolveTierModel(routingId, instruction) {
|
|
201
227
|
const id = routingId ?? process.env.UAP_DELIVER_ROUTING;
|
|
@@ -204,8 +230,7 @@ export function resolveTierModel(routingId, instruction) {
|
|
|
204
230
|
const preset = RoutingPresets[id];
|
|
205
231
|
if (!preset)
|
|
206
232
|
return null;
|
|
207
|
-
const
|
|
208
|
-
const tier = COMPLEXITY_TO_TIER[complexity] ?? 'medium';
|
|
233
|
+
const tier = tierToRouting(classifyComplexity({ instruction }).tier);
|
|
209
234
|
return { model: resolvePresetModel(preset, { complexity: tier, role: 'executor' }), tier, preset: id };
|
|
210
235
|
}
|
|
211
236
|
function resolveModel(presetId, endpointOverride) {
|
|
@@ -997,8 +1022,52 @@ async function runDeliver(instruction, options) {
|
|
|
997
1022
|
}
|
|
998
1023
|
let evaluatorExecutor = blindExecutor;
|
|
999
1024
|
let verdictExecutor = jsonBlindExecutor;
|
|
1000
|
-
|
|
1001
|
-
|
|
1025
|
+
// Verification provenance (S1 — Principle 3: "the model cannot verify itself").
|
|
1026
|
+
// Deliver must never let the generator be the SOLE grader of its own output.
|
|
1027
|
+
const allowSelfJudge = process.env.UAP_DELIVER_ALLOW_SELF_JUDGE === '1' ||
|
|
1028
|
+
(() => {
|
|
1029
|
+
try {
|
|
1030
|
+
const c = loadUapConfigRaw(projectRoot);
|
|
1031
|
+
return c?.recipes?.allowSelfJudge === true;
|
|
1032
|
+
}
|
|
1033
|
+
catch {
|
|
1034
|
+
return false;
|
|
1035
|
+
}
|
|
1036
|
+
})();
|
|
1037
|
+
// Q3: when a routing preset is active, prefer ITS review-phase model as the
|
|
1038
|
+
// distinct judge (compose S1 verification with the S3 per-phase matrix) instead
|
|
1039
|
+
// of the hardcoded haiku default — the preset's designated reviewer IS the
|
|
1040
|
+
// Generator≠Evaluator choice. resolveJudgePlan still swaps to the alt judge if
|
|
1041
|
+
// this collides with the generator, and only uses it on the auto-distinct path.
|
|
1042
|
+
const routingIdForJudge = options.routing ?? process.env.UAP_DELIVER_ROUTING;
|
|
1043
|
+
const presetForJudge = routingIdForJudge ? RoutingPresets[routingIdForJudge] : undefined;
|
|
1044
|
+
const reviewJudgeId = presetForJudge
|
|
1045
|
+
? resolvePhaseChain(presetForJudge, {
|
|
1046
|
+
complexity: tierToRouting(classifyComplexity({ instruction }).tier),
|
|
1047
|
+
phase: 'review',
|
|
1048
|
+
})[0]
|
|
1049
|
+
: undefined;
|
|
1050
|
+
const judgePlan = resolveJudgePlan({
|
|
1051
|
+
evaluatorPresetId,
|
|
1052
|
+
generatorId: model.id,
|
|
1053
|
+
generatorProvider: ModelPresets[presetId]?.provider,
|
|
1054
|
+
allowSelfJudge,
|
|
1055
|
+
distinctJudgeId: reviewJudgeId,
|
|
1056
|
+
hasPreset: (id) => Boolean(ModelPresets[id]),
|
|
1057
|
+
});
|
|
1058
|
+
const judgeModelId = judgePlan.judgeModelId;
|
|
1059
|
+
const judgeDistinct = judgePlan.distinct;
|
|
1060
|
+
// Acceptance-scoped executors: they author the self-gate (verify.sh) and run
|
|
1061
|
+
// the acceptance JUDGE. They default to the generator and are the ONLY channels
|
|
1062
|
+
// the auto-distinct judge redirects — PLANNING (planDeliveryPhases /
|
|
1063
|
+
// deriveUserPaths on verdictExecutor) stays on the generator to avoid a
|
|
1064
|
+
// planning-quality regression (review C1).
|
|
1065
|
+
let gateAuthorExecutor = evaluatorExecutor;
|
|
1066
|
+
let acceptanceJudgeExecutor = verdictExecutor;
|
|
1067
|
+
if (judgePlan.reason === 'configured-evaluator') {
|
|
1068
|
+
// A distinct configured evaluator drives the evaluation channel AND planning
|
|
1069
|
+
// (pre-existing barbell behavior): reassign the globals.
|
|
1070
|
+
const evalModel = resolveModel(judgeModelId, options.evaluatorEndpoint);
|
|
1002
1071
|
const evalClient = new OpenAICompatClient();
|
|
1003
1072
|
evaluatorExecutor = async (prompt) => {
|
|
1004
1073
|
// Evaluators judge cool + deterministic; they do not brainstorm.
|
|
@@ -1009,10 +1078,28 @@ async function runDeliver(instruction, options) {
|
|
|
1009
1078
|
const r = await evalClient.complete(evalModel, prompt, { temperature: 0, jsonResponse: true });
|
|
1010
1079
|
return r.content;
|
|
1011
1080
|
};
|
|
1081
|
+
gateAuthorExecutor = evaluatorExecutor;
|
|
1082
|
+
acceptanceJudgeExecutor = verdictExecutor;
|
|
1012
1083
|
if (!options.dryRun) {
|
|
1013
1084
|
console.log(chalk.cyan(`⚖ generator≠evaluator: gen=${model.id} eval=${evalModel.id}`));
|
|
1014
1085
|
}
|
|
1015
1086
|
}
|
|
1087
|
+
else if (judgePlan.reason === 'auto-distinct-judge') {
|
|
1088
|
+
// No evaluator configured + cloud generator → run the acceptance JUDGE and
|
|
1089
|
+
// self-gate author on a cheap DISTINCT cloud model, WITHOUT hijacking the
|
|
1090
|
+
// planning executors (C1). The generator still plans; it no longer grades
|
|
1091
|
+
// itself.
|
|
1092
|
+
const jModel = resolveModel(judgeModelId);
|
|
1093
|
+
const jClient = new OpenAICompatClient();
|
|
1094
|
+
gateAuthorExecutor = async (prompt) => (await jClient.complete(jModel, prompt, { temperature: 0 })).content;
|
|
1095
|
+
acceptanceJudgeExecutor = async (prompt) => (await jClient.complete(jModel, prompt, { temperature: 0, jsonResponse: true })).content;
|
|
1096
|
+
if (!options.dryRun) {
|
|
1097
|
+
console.log(chalk.cyan(`⚖ generator≠evaluator (auto distinct acceptance judge): gen=${model.id} judge=${jModel.id}`));
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
else if (!judgeDistinct && !allowSelfJudge && !options.dryRun) {
|
|
1101
|
+
console.log(chalk.yellow(`⚠ verify: no distinct judge reachable (gen=${model.id} local/offline) — generator grades its own output`));
|
|
1102
|
+
}
|
|
1016
1103
|
// Dynamic executor selection. The blind executor is one completion per turn
|
|
1017
1104
|
// (cheap, but cannot inspect or run anything); the agentic executor runs a
|
|
1018
1105
|
// tool-using loop (read/list/bash/write) and mutates the repo directly. 'auto'
|
|
@@ -1142,23 +1229,40 @@ async function runDeliver(instruction, options) {
|
|
|
1142
1229
|
console.log(chalk.cyan('⚖ self-gate: authoring a task-specific acceptance check…'));
|
|
1143
1230
|
// Author the gate with the blind executor — it is a single-shot script
|
|
1144
1231
|
// write, not a task to solve agentically.
|
|
1145
|
-
const sg = await authorAcceptanceGate({ instruction, projectRoot, executor:
|
|
1232
|
+
const sg = await authorAcceptanceGate({ instruction, projectRoot, executor: gateAuthorExecutor });
|
|
1146
1233
|
for (const note of sg.notes)
|
|
1147
1234
|
console.log(chalk.dim(` ${note}`));
|
|
1148
1235
|
if (!sg.rung) {
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
//
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1236
|
+
// A failed self-gate is survivable only when a real acceptance judge will
|
|
1237
|
+
// run and become the convergence target. See selfGateFailureIsFatal — in
|
|
1238
|
+
// particular, max fidelity is NOT a substitute: the vision review lives
|
|
1239
|
+
// inside the acceptance gate, so when that gate is never built there is
|
|
1240
|
+
// nothing for it to block.
|
|
1241
|
+
if (selfGateFailureIsFatal({
|
|
1242
|
+
acceptanceEnabled: Boolean(options.acceptance),
|
|
1243
|
+
judgeSkipped: shouldSkipAcceptanceJudge({
|
|
1244
|
+
acceptanceEnabled: Boolean(options.acceptance),
|
|
1245
|
+
complexity: autoPlan?.complexity,
|
|
1246
|
+
acceptancePrimary,
|
|
1247
|
+
}),
|
|
1248
|
+
})) {
|
|
1249
|
+
fail('Could not author an acceptance gate (model produced no runnable script).');
|
|
1157
1250
|
}
|
|
1158
|
-
console.log(chalk.yellow(' ⚠
|
|
1251
|
+
console.log(chalk.yellow(' ⚠ self-gate authoring failed — the acceptance judge is the convergence target for this run.'));
|
|
1159
1252
|
}
|
|
1160
1253
|
else {
|
|
1161
|
-
|
|
1254
|
+
rungs.push(sg.rung);
|
|
1255
|
+
if (sg.vacuous) {
|
|
1256
|
+
// P0 hard-fail: a REQUIRED self-gate that passes on the unsolved repo
|
|
1257
|
+
// re-opens the false-green door — "delivered" would be meaningless.
|
|
1258
|
+
if (process.env.UAP_DELIVER_ALLOW_WEAK_SELF_GATE !== '1') {
|
|
1259
|
+
fail('The self-gate is REQUIRED for this run (anti-vacuous floor) but stayed vacuous after retries — it passes on the unsolved repo. Add concrete, checkable ACCEPTANCE CRITERIA to the instruction, or set UAP_DELIVER_ALLOW_WEAK_SELF_GATE=1 to accept the risk.');
|
|
1260
|
+
}
|
|
1261
|
+
console.log(chalk.yellow(' ⚠ acceptance gate may be weak (could not force an initially-failing check); UAP_DELIVER_ALLOW_WEAK_SELF_GATE=1 — running multi-turn anyway.'));
|
|
1262
|
+
}
|
|
1263
|
+
else {
|
|
1264
|
+
console.log(chalk.green(' ✓ acceptance gate authored — fails on the unsolved repo, a real convergence target.'));
|
|
1265
|
+
}
|
|
1162
1266
|
}
|
|
1163
1267
|
}
|
|
1164
1268
|
// Phase 5: escalation ladder. Cheap strategies first (widen exploration →
|
|
@@ -1388,7 +1492,7 @@ async function runDeliver(instruction, options) {
|
|
|
1388
1492
|
? buildMissionAcceptanceGate({
|
|
1389
1493
|
primary: acceptancePrimary,
|
|
1390
1494
|
specs,
|
|
1391
|
-
judgeExecutor:
|
|
1495
|
+
judgeExecutor: acceptanceJudgeExecutor,
|
|
1392
1496
|
note: (line) => console.log(chalk.yellow(` ${line}`)),
|
|
1393
1497
|
})
|
|
1394
1498
|
: undefined;
|
|
@@ -1464,6 +1568,16 @@ async function runDeliver(instruction, options) {
|
|
|
1464
1568
|
projectRoot,
|
|
1465
1569
|
maxTurns,
|
|
1466
1570
|
rungs,
|
|
1571
|
+
// MIPRO (S8/3a): feed any tuner-selected prompt-fragment variants from
|
|
1572
|
+
// .uap.json to the prompt builder. Empty/absent → the byte-identical default.
|
|
1573
|
+
promptSelection: (() => {
|
|
1574
|
+
try {
|
|
1575
|
+
return promptSelectionFromConfig((loadUapConfigRaw(projectRoot) ?? {}));
|
|
1576
|
+
}
|
|
1577
|
+
catch {
|
|
1578
|
+
return undefined;
|
|
1579
|
+
}
|
|
1580
|
+
})(),
|
|
1467
1581
|
// The agentic executor mutates the repo directly (no-op applier), so
|
|
1468
1582
|
// gates must run every turn regardless of applier file count.
|
|
1469
1583
|
alwaysVerify: agentic ? true : undefined,
|
|
@@ -1663,6 +1777,27 @@ async function runDeliver(instruction, options) {
|
|
|
1663
1777
|
console.log(chalk.dim(' user-validation: no manifest derived — gate will report NA until one exists'));
|
|
1664
1778
|
}
|
|
1665
1779
|
}
|
|
1780
|
+
// Surface the acceptance contract (the user-path journeys + required selectors)
|
|
1781
|
+
// to the executor on EVERY turn. The manifest is authored up front but was
|
|
1782
|
+
// otherwise invisible to the implementer, so a weak model built artifacts that
|
|
1783
|
+
// the real-client gate could not drive (e.g. a canvas-only UI with no DOM
|
|
1784
|
+
// handles) and then could not self-correct from the gate's selector-not-found
|
|
1785
|
+
// feedback. Injecting the contract closes the author→implement loop: the model
|
|
1786
|
+
// builds a complete, drivable artifact from turn 1. Fail-soft — an aid, never a
|
|
1787
|
+
// blocker.
|
|
1788
|
+
if (userValidationMode !== 'off') {
|
|
1789
|
+
try {
|
|
1790
|
+
const loaded = loadUserPaths(projectRoot);
|
|
1791
|
+
const contract = renderAcceptanceContract(loaded?.manifest ?? null);
|
|
1792
|
+
if (contract) {
|
|
1793
|
+
loopConfig.acceptanceContract = contract;
|
|
1794
|
+
console.log(chalk.dim(' user-validation: acceptance contract (journeys + required selectors) injected into the executor prompt'));
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
catch {
|
|
1798
|
+
/* contract injection is a best-effort completeness aid */
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1666
1801
|
const runState = {
|
|
1667
1802
|
runId,
|
|
1668
1803
|
instruction,
|
|
@@ -1695,6 +1830,12 @@ async function runDeliver(instruction, options) {
|
|
|
1695
1830
|
runState.checkpoint = cp;
|
|
1696
1831
|
saveRunState(runState);
|
|
1697
1832
|
};
|
|
1833
|
+
// Under max fidelity the vision aesthetic review is a BLOCKING release gate;
|
|
1834
|
+
// let the acceptance gate run (and feed vision findings) even when the ladder
|
|
1835
|
+
// is red on a synthetic anti-vacuous self-gate, so the model gets aesthetic
|
|
1836
|
+
// feedback instead of stalling. The gate re-checks operational + behavioral
|
|
1837
|
+
// itself before grading (mission-acceptance), preserving the required ordering.
|
|
1838
|
+
loopConfig.runAcceptanceDespiteLadder = deliverFidelity.max;
|
|
1698
1839
|
// Cooperative cancel from the dashboard: the loop polls this each turn.
|
|
1699
1840
|
loopConfig.shouldStop = () => isStopRequested(projectRoot, runId);
|
|
1700
1841
|
clearStop(projectRoot, runId);
|
|
@@ -2122,7 +2263,18 @@ async function runDeliver(instruction, options) {
|
|
|
2122
2263
|
// Regression scoring uses only the fast tier — a cheap, synchronous, always-
|
|
2123
2264
|
// run signal. Integration/deploy-dev gates may not run at baseline (promotion
|
|
2124
2265
|
// is cheap-first), so they are not a trustworthy regression comparison.
|
|
2125
|
-
|
|
2266
|
+
// keep-best scores the DETERMINISTIC objective gates — build/test/lint (fast) PLUS
|
|
2267
|
+
// the execution smoke (runtime) and user-path (final) gates — not just the fast tier.
|
|
2268
|
+
// On a project whose only meaningful gates are runtime/final (e.g. a vanilla-JS web
|
|
2269
|
+
// app with no build step) the fast subset is trivially 1.00 every turn, so
|
|
2270
|
+
// best-intermediate never advanced and a 100%-functional turn that later regressed
|
|
2271
|
+
// was DISCARDED (octopus, 2026-07-24: the lazy turn hit 100% of gates, the
|
|
2272
|
+
// vision-chasing turns then rewrote game.js and broke the HUD wiring, and the win was
|
|
2273
|
+
// lost — the harness kept the regressed end state). Exclude the LLM judge rungs
|
|
2274
|
+
// (bootstrap/acceptance): non-deterministic, they would make the rollback target flap.
|
|
2275
|
+
const KEEP_BEST_TIERS = new Set(['fast', 'runtime', 'integration', 'final']);
|
|
2276
|
+
const KEEP_BEST_EXCLUDE_IDS = new Set(['bootstrap', 'acceptance']);
|
|
2277
|
+
const fastRungs = rungs.filter((r) => KEEP_BEST_TIERS.has(tierOf(r)) && !KEEP_BEST_EXCLUDE_IDS.has(r.id));
|
|
2126
2278
|
const keepBest = Boolean(options.keepBest) && fastRungs.length > 0 && !needsSelfGate;
|
|
2127
2279
|
let regressSnapshot = null;
|
|
2128
2280
|
let baselineGateScore = 0;
|
|
@@ -2168,6 +2320,12 @@ async function runDeliver(instruction, options) {
|
|
|
2168
2320
|
redetectRungs: true,
|
|
2169
2321
|
redetectFilter: loopConfig.redetectFilter,
|
|
2170
2322
|
protectTests: options.protectTests,
|
|
2323
|
+
// The acceptance contract is a REQUIREMENT spec, not a convergence aid:
|
|
2324
|
+
// carry it into the bare turn so a one-shot build already exposes the
|
|
2325
|
+
// journeys/selectors the gate will drive (otherwise the lazy turn builds
|
|
2326
|
+
// blind, e.g. a canvas-only UI with no DOM handles, and only later turns
|
|
2327
|
+
// — which do get the contract — can recover it).
|
|
2328
|
+
acceptanceContract: loopConfig.acceptanceContract,
|
|
2171
2329
|
onIteration: (record) => { updateDeliverHeartbeat(projectRoot); printProgress(record); },
|
|
2172
2330
|
}, executor, seams);
|
|
2173
2331
|
const lazyResult = await lazyLoop.deliver(instruction);
|
|
@@ -2433,6 +2591,15 @@ async function runDeliver(instruction, options) {
|
|
|
2433
2591
|
catch {
|
|
2434
2592
|
// Memory recording is best-effort
|
|
2435
2593
|
}
|
|
2594
|
+
// Verification provenance banner (S1) — makes the judge boundary observable.
|
|
2595
|
+
if (!options.json && !options.dryRun) {
|
|
2596
|
+
const line = formatVerificationProvenance({
|
|
2597
|
+
executorModel: model.id,
|
|
2598
|
+
judgeModel: judgeModelId,
|
|
2599
|
+
distinct: judgeDistinct,
|
|
2600
|
+
});
|
|
2601
|
+
console.log(judgeDistinct ? chalk.dim(line) : chalk.yellow(line));
|
|
2602
|
+
}
|
|
2436
2603
|
if (options.json) {
|
|
2437
2604
|
const { finalOutput, ...rest } = result;
|
|
2438
2605
|
console.log(JSON.stringify({
|