onto-mcp 0.4.13 → 0.4.14
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/.onto/processes/review/external-oauth-worker-contract.md +12 -5
- package/README.md +33 -17
- package/dist/core-api/reconstruct-api.js +41 -18
- package/dist/core-api/review-api.js +9 -1
- package/dist/core-runtime/cli/prepare-review-session.js +16 -1
- package/dist/core-runtime/cli/review-invocation-runner.js +6 -0
- package/dist/core-runtime/cli/review-invoke.js +26 -2
- package/dist/core-runtime/discovery/llm-override.js +262 -0
- package/dist/core-runtime/discovery/review-cert-assemble.js +1 -1
- package/dist/core-runtime/discovery/review-cert-record.js +119 -21
- package/dist/core-runtime/discovery/settings-chain.js +33 -11
- package/dist/core-runtime/llm/model-switcher.js +7 -1
- package/dist/core-runtime/reconstruct/run.js +1 -1
- package/dist/core-runtime/review/materializers.js +4 -0
- package/dist/core-runtime/review/semantic-quality-gate.js +227 -32
- package/dist/mcp/server.js +177 -14
- package/dist/mcp/tool-schemas.js +22 -5
- package/package.json +1 -1
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { defaultAuthForProvider, } from "../llm/model-switcher.js";
|
|
2
|
+
import { reviewExecutionUnitActor } from "../review/review-execution-profile.js";
|
|
3
|
+
import { PerCallLlmOverrideSchema, RECONSTRUCT_ACTOR_KEYS, REVIEW_EXECUTION_UNIT_IDS, } from "./settings-chain.js";
|
|
4
|
+
/**
|
|
5
|
+
* Per-call LLM override overlay (design v4 §2.2). A `PerCallLlmOverride` is an
|
|
6
|
+
* ephemeral settings-`llm` edit applied to the resolved {@link OntoSettings}
|
|
7
|
+
* for ONE review/reconstruct invocation, after which the existing pipeline runs
|
|
8
|
+
* unchanged. These helpers are PURE and IMMUTABLE: they never mutate the input
|
|
9
|
+
* settings, and an absent/empty override is the IDENTITY (returns the same
|
|
10
|
+
* object reference), which is what makes the default-off path byte-identical.
|
|
11
|
+
*
|
|
12
|
+
* Per-block semantics:
|
|
13
|
+
* - `override.provider` present → REPLACE: the block becomes `{...override}`.
|
|
14
|
+
* The switched-in provider's transport (base_url/api_key_env/service_tier/
|
|
15
|
+
* timeout_ms) resolves fresh from model-switcher defaults; the old provider's
|
|
16
|
+
* transport MUST NOT leak, so the previous block is dropped entirely. The
|
|
17
|
+
* override schema excludes transport/credential fields, so a plain copy is
|
|
18
|
+
* already transport-clean.
|
|
19
|
+
* - `override.provider` absent → field-OVERLAY: `{...block, ...override}` — only
|
|
20
|
+
* the named fields (effort/model/service_tier/auth) win over the existing
|
|
21
|
+
* same-provider block.
|
|
22
|
+
*/
|
|
23
|
+
function isEmptyOverride(override) {
|
|
24
|
+
return Object.keys(override).length === 0;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Parse a per-call override serialized as a single `--llm-override <json>` CLI
|
|
28
|
+
* arg back into a validated {@link PerCallLlmOverride}. The review pipeline
|
|
29
|
+
* crosses a process/argv boundary, so the override is JSON-serialized by the API
|
|
30
|
+
* seam and re-materialized (+ shape-revalidated via the canonical schema) at
|
|
31
|
+
* each settings-resolution seam that must apply it. Absent/empty → undefined
|
|
32
|
+
* (identity). Throws on malformed JSON or a shape violation (fail-loud).
|
|
33
|
+
*/
|
|
34
|
+
export function parsePerCallLlmOverrideArg(raw) {
|
|
35
|
+
if (raw === undefined || raw.length === 0)
|
|
36
|
+
return undefined;
|
|
37
|
+
const parsed = PerCallLlmOverrideSchema.parse(JSON.parse(raw));
|
|
38
|
+
// An EMPTY override is semantically an omission. Canonicalize it to undefined
|
|
39
|
+
// so every downstream truthiness check agrees: otherwise the overlay treats
|
|
40
|
+
// `{}` as identity while a `if (llmOverride)` gate still fires, making `{}`
|
|
41
|
+
// observably different from omission (breaking the default-off guarantee).
|
|
42
|
+
return isEmptyOverride(parsed) ? undefined : parsed;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Route identity of a block AFTER an override. Compared by EFFECTIVE value, not
|
|
46
|
+
* by field presence: an override may legitimately restate the current
|
|
47
|
+
* provider/auth just to change the model, and that is NOT a route change — so it
|
|
48
|
+
* must keep the block's route-scoped transport (api_key_env/base_url/
|
|
49
|
+
* service_tier) and any provider-scoped runtime settings.
|
|
50
|
+
*/
|
|
51
|
+
function effectiveRoute(block, override) {
|
|
52
|
+
// Compare the DEFAULTED auth, not the raw field: a block that omits `auth`
|
|
53
|
+
// still dispatches on `defaultAuthForProvider(provider)` (anthropic/grok →
|
|
54
|
+
// api_key, openai → oauth, lmstudio → local). Comparing the raw undefined
|
|
55
|
+
// would read an override that merely restates that effective auth as a switch
|
|
56
|
+
// and discard the block's still-valid transport (api_key_env/base_url).
|
|
57
|
+
const blockAuth = block.auth ??
|
|
58
|
+
(block.provider !== undefined ? defaultAuthForProvider(block.provider) : undefined);
|
|
59
|
+
return {
|
|
60
|
+
providerChanged: (override.provider ?? block.provider) !== block.provider,
|
|
61
|
+
authChanged: (override.auth ?? blockAuth) !== blockAuth,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/** Whether an override changes the block's ROUTE identity (provider or auth). */
|
|
65
|
+
function overrideChangesRoute(block, override) {
|
|
66
|
+
const { providerChanged, authChanged } = effectiveRoute(block, override);
|
|
67
|
+
return providerChanged || authChanged;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Core per-`llm`-block override (REPLACE vs route-cleaned vs field-OVERLAY).
|
|
71
|
+
*
|
|
72
|
+
* `routeBasis` is the block whose ROUTE identity the decision is made on, which
|
|
73
|
+
* is not always `block` itself: a review UNIT's llm is a PARTIAL block merged
|
|
74
|
+
* over its default actor, so its route lives in the merged result. Judging a
|
|
75
|
+
* provider-less unit on its own partial block would report a provider change for
|
|
76
|
+
* every provider-bearing override (comparing against `undefined`). The override
|
|
77
|
+
* is still APPLIED to `block` so the unit keeps its partial shape.
|
|
78
|
+
*/
|
|
79
|
+
function applyLlmBlockOverride(block, override, routeBasis = block) {
|
|
80
|
+
const { providerChanged, authChanged } = effectiveRoute(routeBasis, override);
|
|
81
|
+
if (providerChanged) {
|
|
82
|
+
// REPLACE (provider switch): drop the old block so no stale transport
|
|
83
|
+
// (base_url/api_key_env/service_tier/timeout_ms) leaks into the new
|
|
84
|
+
// provider. override carries only {provider, auth?, model?, effort?,
|
|
85
|
+
// service_tier?}, so the copy is transport-clean by construction.
|
|
86
|
+
return { ...override };
|
|
87
|
+
}
|
|
88
|
+
if (authChanged) {
|
|
89
|
+
// AUTH switch is ALSO a route switch (oauth ↔ api_key normalize to
|
|
90
|
+
// different execution routes/adapters), so the previous route's scoped
|
|
91
|
+
// fields must not survive: `service_tier` is openai+oauth-only (
|
|
92
|
+
// normalizeLlmModelSwitcher rejects it on any other route) and
|
|
93
|
+
// `api_key_env`/`base_url` belong to the direct-call routes. Keeping them
|
|
94
|
+
// would turn an otherwise valid auth switch into a hard failure (or leak an
|
|
95
|
+
// api-key endpoint into an OAuth route). The override may re-state
|
|
96
|
+
// service_tier; base_url/api_key_env stay settings-owned by design.
|
|
97
|
+
const { service_tier: _serviceTier, api_key_env: _apiKeyEnv, base_url: _baseUrl, ...routeAgnostic } = block;
|
|
98
|
+
return { ...routeAgnostic, ...override };
|
|
99
|
+
}
|
|
100
|
+
// OVERLAY (same route — including an override that restates the current
|
|
101
|
+
// provider/auth): only the named fields win; transport is preserved.
|
|
102
|
+
return { ...block, ...override };
|
|
103
|
+
}
|
|
104
|
+
// ── review scope ──────────────────────────────────────────────────────────
|
|
105
|
+
const REVIEW_ACTOR_KEYS = ["teamlead", "lens", "synthesize"];
|
|
106
|
+
/**
|
|
107
|
+
* Apply a per-call override to the review dispatch seats of `settings`
|
|
108
|
+
* (design v4 §2.2). Overlays every configured actor llm
|
|
109
|
+
* (`review.execution.{teamlead,lens,synthesize}.llm`) and every configured unit
|
|
110
|
+
* llm (`review.execution.units[id].llm`), plus the salvage transcription model.
|
|
111
|
+
* Actors/units WITHOUT an explicit llm are left untouched (their inheritance is
|
|
112
|
+
* unchanged). In REPLACE mode each unit's own llm is DROPPED so it inherits the
|
|
113
|
+
* replaced actor rather than keeping a stale unit model.
|
|
114
|
+
*/
|
|
115
|
+
export function applyReviewLlmOverride(settings, override) {
|
|
116
|
+
if (!override || isEmptyOverride(override))
|
|
117
|
+
return settings; // IDENTITY
|
|
118
|
+
const execution = settings.review?.execution;
|
|
119
|
+
if (!execution)
|
|
120
|
+
return settings;
|
|
121
|
+
const nextExecution = { ...execution };
|
|
122
|
+
for (const actorKey of REVIEW_ACTOR_KEYS) {
|
|
123
|
+
const actor = execution[actorKey];
|
|
124
|
+
if (actor?.llm) {
|
|
125
|
+
nextExecution[actorKey] = {
|
|
126
|
+
...actor,
|
|
127
|
+
llm: applyLlmBlockOverride(actor.llm, override),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (execution.units) {
|
|
132
|
+
const nextUnits = {};
|
|
133
|
+
for (const unitId of REVIEW_EXECUTION_UNIT_IDS) {
|
|
134
|
+
const unit = execution.units[unitId];
|
|
135
|
+
if (!unit)
|
|
136
|
+
continue;
|
|
137
|
+
if (!unit.llm) {
|
|
138
|
+
nextUnits[unitId] = unit;
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
// A unit's llm is a PARTIAL block that the runtime merges over its default
|
|
142
|
+
// actor ({...actorLlm, ...unitLlm}), so the unit's real route is that
|
|
143
|
+
// merge — judge the override against it, not against the partial block.
|
|
144
|
+
const unitRouteBasis = {
|
|
145
|
+
...(execution[reviewExecutionUnitActor(unitId)]?.llm ?? {}),
|
|
146
|
+
...unit.llm,
|
|
147
|
+
};
|
|
148
|
+
if (effectiveRoute(unitRouteBasis, override).providerChanged) {
|
|
149
|
+
// REPLACE: drop the unit's llm so it inherits the replaced actor
|
|
150
|
+
// (no stale unit model on the old provider).
|
|
151
|
+
const { llm: _dropped, ...rest } = unit;
|
|
152
|
+
nextUnits[unitId] = rest;
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
// Units go through the SAME block rules as actors — a raw spread here
|
|
156
|
+
// would keep the previous route's scoped fields (e.g. a unit's own
|
|
157
|
+
// openai/oauth `service_tier` surviving an `auth: "api_key"` override),
|
|
158
|
+
// so the unit route would be rejected even though the actor was cleaned.
|
|
159
|
+
nextUnits[unitId] = {
|
|
160
|
+
...unit,
|
|
161
|
+
llm: applyLlmBlockOverride(unit.llm, override, unitRouteBasis),
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
nextExecution.units = nextUnits;
|
|
166
|
+
}
|
|
167
|
+
const transcription = execution.retry?.salvage?.transcription_llm;
|
|
168
|
+
if (transcription) {
|
|
169
|
+
nextExecution.retry = {
|
|
170
|
+
...execution.retry,
|
|
171
|
+
salvage: {
|
|
172
|
+
...execution.retry?.salvage,
|
|
173
|
+
transcription_llm: applySalvageTranscriptionOverride(transcription, override),
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
...settings,
|
|
179
|
+
review: { ...settings.review, execution: nextExecution },
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Salvage transcription_llm has a RESTRICTED shape ({provider?: "anthropic" |
|
|
184
|
+
* "openai"; model: string}) — it is a cheap-tier transcription model run by the
|
|
185
|
+
* unit's OWN adapter, so only anthropic/openai are representable. Overlay the
|
|
186
|
+
* model always; overlay the provider only when salvage can express it.
|
|
187
|
+
*
|
|
188
|
+
* A grok/lmstudio override leaves the seat untouched, and that is INERT rather
|
|
189
|
+
* than a mixed route: those providers resolve to the direct-call route, and the
|
|
190
|
+
* runner only attempts salvage on a `claude_code`/`codex` worker executor (see
|
|
191
|
+
* run-review-prompt-execution: `salvageAdapter === "claude_code" || "codex"`),
|
|
192
|
+
* so salvage cannot dispatch there at all. This is also exactly what editing the
|
|
193
|
+
* same provider into settings would do — the override is a settings overlay, so
|
|
194
|
+
* it must not invent a stricter contract than the settings path has.
|
|
195
|
+
*/
|
|
196
|
+
function applySalvageTranscriptionOverride(transcription, override) {
|
|
197
|
+
// A switch to a salvage-incompatible provider (grok/lmstudio) leaves salvage
|
|
198
|
+
// ENTIRELY unchanged — the model would otherwise land on an anthropic/openai
|
|
199
|
+
// route it does not belong to. Salvage is opt-in / default-off, so
|
|
200
|
+
// under-applying here fails closed, never dispatches an unverified call.
|
|
201
|
+
if (override.provider === "grok" || override.provider === "lmstudio") {
|
|
202
|
+
return transcription;
|
|
203
|
+
}
|
|
204
|
+
const next = { ...transcription };
|
|
205
|
+
if (override.model !== undefined)
|
|
206
|
+
next.model = override.model;
|
|
207
|
+
if (override.provider === "anthropic" || override.provider === "openai") {
|
|
208
|
+
next.provider = override.provider;
|
|
209
|
+
}
|
|
210
|
+
return next;
|
|
211
|
+
}
|
|
212
|
+
// ── reconstruct scope ───────────────────────────────────────────────────────
|
|
213
|
+
/**
|
|
214
|
+
* Apply a per-call override to the reconstruct dispatch seats of `settings`
|
|
215
|
+
* (design v4 §2.2). Overlays the actor seats
|
|
216
|
+
* (`reconstruct.execution.actors.{semantic_author,confirmation_provider,
|
|
217
|
+
* semantic_map_synthesize}.llm`) with full REPLACE/OVERLAY semantics, and threads
|
|
218
|
+
* the override EFFORT into the opt-in `dispatch_fallback.llm` (the faithful
|
|
219
|
+
* replacement of the removed `llmEffort` pin).
|
|
220
|
+
*/
|
|
221
|
+
export function applyReconstructLlmOverride(settings, override) {
|
|
222
|
+
if (!override || isEmptyOverride(override))
|
|
223
|
+
return settings; // IDENTITY
|
|
224
|
+
const execution = settings.reconstruct?.execution;
|
|
225
|
+
if (!execution)
|
|
226
|
+
return settings;
|
|
227
|
+
const nextExecution = { ...execution };
|
|
228
|
+
if (execution.actors) {
|
|
229
|
+
const nextActors = { ...execution.actors };
|
|
230
|
+
for (const actorKey of RECONSTRUCT_ACTOR_KEYS) {
|
|
231
|
+
const actor = execution.actors[actorKey];
|
|
232
|
+
if (actor) {
|
|
233
|
+
const llm = applyLlmBlockOverride(actor.llm, override);
|
|
234
|
+
// `llm_runtime` (openai Responses output headroom) is scoped to the
|
|
235
|
+
// openai + api_key direct-call route, so it must NOT survive a route
|
|
236
|
+
// change: reconstruct would apply openai-only headroom to the new route
|
|
237
|
+
// and fail before dispatch, and the caller cannot clear it through
|
|
238
|
+
// `llmOverride` (transport/runtime fields are settings-owned).
|
|
239
|
+
nextActors[actorKey] = overrideChangesRoute(actor.llm, override)
|
|
240
|
+
? { llm }
|
|
241
|
+
: { ...actor, llm };
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
nextExecution.actors = nextActors;
|
|
245
|
+
}
|
|
246
|
+
// dispatch_fallback.llm is a RESTRICTED, alternate-provider shape (requires
|
|
247
|
+
// api_key_env; provider constrained to openai/anthropic; model is
|
|
248
|
+
// provider-specific). It exists specifically to be a DIFFERENT provider than
|
|
249
|
+
// the primary, so a per-call provider/model switch must NOT collapse it onto
|
|
250
|
+
// the primary. Only the provider-agnostic EFFORT knob flows through — this is
|
|
251
|
+
// exactly what the removed `llmEffort` pin used to apply here (v4 §6(a)).
|
|
252
|
+
if (execution.dispatch_fallback?.enabled === true && override.effort !== undefined) {
|
|
253
|
+
nextExecution.dispatch_fallback = {
|
|
254
|
+
...execution.dispatch_fallback,
|
|
255
|
+
llm: { ...execution.dispatch_fallback.llm, effort: override.effort },
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
return {
|
|
259
|
+
...settings,
|
|
260
|
+
reconstruct: { ...settings.reconstruct, execution: nextExecution },
|
|
261
|
+
};
|
|
262
|
+
}
|
|
@@ -163,7 +163,7 @@ export function assembleReviewCertRecord(args) {
|
|
|
163
163
|
if (guardViolations.length > 0) {
|
|
164
164
|
return { record: null, violations: guardViolations };
|
|
165
165
|
}
|
|
166
|
-
const aggregates = computeReviewCertAggregates(args.runs, args.fixtures
|
|
166
|
+
const aggregates = computeReviewCertAggregates(args.runs, args.fixtures);
|
|
167
167
|
const armDispatch = (arm) => {
|
|
168
168
|
const effort = projection.witnessed[arm].reasoning_effort;
|
|
169
169
|
return effort !== undefined ? { reasoning_effort: effort } : {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { SEMANTIC_QUALITY_GATE_CHECK_IDS, } from "../review/semantic-quality-gate.js";
|
|
2
|
+
import { CLEAN_TARGET_EXCLUDED_CHECK_IDS, SEMANTIC_QUALITY_GATE_CHECK_IDS, } from "../review/semantic-quality-gate.js";
|
|
3
3
|
import { SynthesizeCertDispatchConfigSchema } from "./synthesize-cert-record.js";
|
|
4
4
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
5
5
|
// review-cert/v1 — the evidence contract behind the `review` role
|
|
@@ -51,14 +51,49 @@ const Sha256Schema = z.string().regex(/^[0-9a-f]{64}$/, "expected a lowercase sh
|
|
|
51
51
|
const ArmModelSchema = z
|
|
52
52
|
.object({ provider: IdSchema, model: IdSchema })
|
|
53
53
|
.strict();
|
|
54
|
+
const CheckIdSchema = z.enum(SEMANTIC_QUALITY_GATE_CHECK_IDS);
|
|
55
|
+
/** The single legal reduced applicable set (v3 clean-target): the full gate
|
|
56
|
+
* universe minus the checks a clean target omits. Derived from the gate's
|
|
57
|
+
* CLEAN_TARGET_EXCLUDED authority so the two cannot drift. Sorted for compare. */
|
|
58
|
+
const CLEAN_TARGET_APPLICABLE_CHECK_IDS = [
|
|
59
|
+
...SEMANTIC_QUALITY_GATE_CHECK_IDS,
|
|
60
|
+
]
|
|
61
|
+
.filter((id) => !CLEAN_TARGET_EXCLUDED_CHECK_IDS.has(id))
|
|
62
|
+
.sort();
|
|
63
|
+
/** Fixtures permitted to declare a REDUCED applicable_check_ids. A reduced set
|
|
64
|
+
* shrinks a fixture's core floor, so the deterministic validator — not just the
|
|
65
|
+
* honest harness — must gate it: only a designated clean-target fixture may
|
|
66
|
+
* reduce, or a material-bearing fixture could drop its recall spine and certify
|
|
67
|
+
* silently. Identity is by fixture_id; binding the id to the actual clean blob
|
|
68
|
+
* via content_sha256 is Phase B (design §D2/§D5). */
|
|
69
|
+
const REDUCED_APPLICABLE_FIXTURE_IDS = new Set([
|
|
70
|
+
"clean-target-v1",
|
|
71
|
+
]);
|
|
72
|
+
/** The applicable_check_ids a fixture's manifest entry MUST declare — the single
|
|
73
|
+
* authority shared by the cert-record PRODUCER (scripts/review-cert-run.mts) and
|
|
74
|
+
* this validator, so the two cannot drift. A designated clean-target fixture
|
|
75
|
+
* declares the reduced set; every other fixture declares nothing (undefined =
|
|
76
|
+
* absent = full universe, byte-identical v2 recompute). The producer emits
|
|
77
|
+
* exactly this; the validator below recomputes and rejects any mismatch. */
|
|
78
|
+
export function fixtureApplicableCheckIds(fixtureId) {
|
|
79
|
+
return REDUCED_APPLICABLE_FIXTURE_IDS.has(fixtureId)
|
|
80
|
+
? CLEAN_TARGET_APPLICABLE_CHECK_IDS
|
|
81
|
+
: undefined;
|
|
82
|
+
}
|
|
54
83
|
const FixtureManifestEntrySchema = z
|
|
55
84
|
.object({
|
|
56
85
|
fixture_id: IdSchema,
|
|
57
86
|
target_anchor: z.string().min(1),
|
|
58
87
|
content_sha256: Sha256Schema,
|
|
88
|
+
/** v3 (design 20260712 §D2): the check subset this fixture's ok runs emit
|
|
89
|
+
* and that aggregates iterate. ABSENT = the full gate universe — so v2
|
|
90
|
+
* records and the existing code fixtures recompute byte-for-byte. A
|
|
91
|
+
* clean-target fixture declares its reduced applicable set here (recall/
|
|
92
|
+
* grounding/actionability are N/A with no material defect). Additive-
|
|
93
|
+
* optional: the wire contract stays review-cert/v2, no G7 bump. */
|
|
94
|
+
applicable_check_ids: z.array(CheckIdSchema).min(1).optional(),
|
|
59
95
|
})
|
|
60
96
|
.strict();
|
|
61
|
-
const CheckIdSchema = z.enum(SEMANTIC_QUALITY_GATE_CHECK_IDS);
|
|
62
97
|
const RunCheckSchema = z
|
|
63
98
|
.object({
|
|
64
99
|
check_id: CheckIdSchema,
|
|
@@ -174,26 +209,40 @@ function passRate(runs, arm, fixtureId, checkId) {
|
|
|
174
209
|
const passed = completed.filter((run) => run.checks.some((check) => check.check_id === checkId && check.status === "passed")).length;
|
|
175
210
|
return passed / completed.length;
|
|
176
211
|
}
|
|
212
|
+
/** The check set a fixture's ok runs emit and that aggregates iterate: its
|
|
213
|
+
* declared applicable_check_ids, or the full gate universe when absent (v2 /
|
|
214
|
+
* existing fixtures — byte-identical recompute). */
|
|
215
|
+
function applicableChecks(fixture) {
|
|
216
|
+
return fixture.applicable_check_ids ?? SEMANTIC_QUALITY_GATE_CHECK_IDS;
|
|
217
|
+
}
|
|
177
218
|
/**
|
|
178
219
|
* The ONE aggregate computation both the validator (recompute/compare) and the
|
|
179
220
|
* harness assembler (declare) consume — a second rate implementation could
|
|
180
|
-
* silently disagree with the gate.
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
221
|
+
* silently disagree with the gate. Iterates each fixture's APPLICABLE check set
|
|
222
|
+
* only (v3 §D2): a clean-target fixture omits recall/grounding/actionability, so
|
|
223
|
+
* they never enter the core-floor judgment as vacuous 0-rate rows. Fixture×check
|
|
224
|
+
* pairs where either arm has no completed run are OMITTED (rep_floor reports
|
|
225
|
+
* that state separately). `quality_pass` = every recall-first core check that IS
|
|
226
|
+
* applicable meets the absolute floor. Candidate<baseline regressions outside
|
|
227
|
+
* that decisive spine are retained in the aggregate rows and projected by
|
|
228
|
+
* {@link reviewCertQualityDisclosures}. Duplicate fixture_ids are collapsed
|
|
229
|
+
* (first wins; duplicate_manifest_input is reported separately).
|
|
185
230
|
*/
|
|
186
|
-
export function computeReviewCertAggregates(runs,
|
|
231
|
+
export function computeReviewCertAggregates(runs, fixtures) {
|
|
187
232
|
const rows = [];
|
|
188
233
|
let qualityPass = true;
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
234
|
+
const seen = new Set();
|
|
235
|
+
for (const fixture of fixtures) {
|
|
236
|
+
if (seen.has(fixture.fixture_id))
|
|
237
|
+
continue;
|
|
238
|
+
seen.add(fixture.fixture_id);
|
|
239
|
+
for (const checkId of applicableChecks(fixture)) {
|
|
240
|
+
const baselineRate = passRate(runs, "baseline", fixture.fixture_id, checkId);
|
|
241
|
+
const candidateRate = passRate(runs, "candidate", fixture.fixture_id, checkId);
|
|
193
242
|
if (baselineRate === null || candidateRate === null)
|
|
194
243
|
continue;
|
|
195
244
|
rows.push({
|
|
196
|
-
fixture_id:
|
|
245
|
+
fixture_id: fixture.fixture_id,
|
|
197
246
|
check_id: checkId,
|
|
198
247
|
baseline_pass_rate: baselineRate,
|
|
199
248
|
candidate_pass_rate: candidateRate,
|
|
@@ -236,8 +285,7 @@ export function reviewCertResubmitDisclosure(record) {
|
|
|
236
285
|
});
|
|
237
286
|
}
|
|
238
287
|
export function reviewCertQualityDisclosures(record) {
|
|
239
|
-
|
|
240
|
-
return computeReviewCertAggregates(record.runs, fixtureIds).per_fixture_check
|
|
288
|
+
return computeReviewCertAggregates(record.runs, record.fixtures).per_fixture_check
|
|
241
289
|
.filter((row) => row.candidate_pass_rate < row.baseline_pass_rate &&
|
|
242
290
|
!ratesEqual(row.candidate_pass_rate, row.baseline_pass_rate))
|
|
243
291
|
.map((row) => {
|
|
@@ -296,8 +344,35 @@ export function validateReviewCertRecord(record) {
|
|
|
296
344
|
if (!record.gate_pin.issue_artifacts_provided) {
|
|
297
345
|
push("check_universe_mismatch", "gate_pin.issue_artifacts_provided must be true — without issue artifacts the gate emits a subset universe");
|
|
298
346
|
}
|
|
347
|
+
// v3 §D2: applicable_check_ids is a per-fixture DECLARATION that can shrink a
|
|
348
|
+
// fixture's core floor, so the deterministic validator must constrain it — an
|
|
349
|
+
// unconstrained reduced set lets a MATERIAL-bearing fixture drop its recall
|
|
350
|
+
// spine and certify silently. Only a designated clean-target fixture may
|
|
351
|
+
// reduce, and only to the single legal clean-target reduction.
|
|
352
|
+
for (const fixture of record.fixtures) {
|
|
353
|
+
if (fixture.applicable_check_ids === undefined)
|
|
354
|
+
continue;
|
|
355
|
+
if (!REDUCED_APPLICABLE_FIXTURE_IDS.has(fixture.fixture_id)) {
|
|
356
|
+
push("applicable_check_ids_invalid", `fixture ${fixture.fixture_id} declares a reduced applicable_check_ids but is not a designated clean-target fixture — a material-bearing fixture must emit the full check universe`, fixture.fixture_id);
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
const declared = [...new Set(fixture.applicable_check_ids)].sort();
|
|
360
|
+
if (declared.length !== CLEAN_TARGET_APPLICABLE_CHECK_IDS.length ||
|
|
361
|
+
declared.some((id, index) => id !== CLEAN_TARGET_APPLICABLE_CHECK_IDS[index])) {
|
|
362
|
+
push("applicable_check_ids_invalid", `fixture ${fixture.fixture_id} applicable_check_ids must equal the clean-target reduction (${CLEAN_TARGET_APPLICABLE_CHECK_IDS.length} checks: ${CLEAN_TARGET_APPLICABLE_CHECK_IDS.join(", ")})`, fixture.fixture_id);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
299
365
|
// Run rows: unique coordinates, manifest membership, completion honesty,
|
|
300
|
-
//
|
|
366
|
+
// applicable-set emission on every completed run. Each fixture's ok runs must
|
|
367
|
+
// emit EXACTLY its applicable set (the full universe when it declares none —
|
|
368
|
+
// v2 / existing fixtures); a clean-target fixture emits its reduced set. The
|
|
369
|
+
// check_universe pin above stays the full vocabulary regardless (§D2).
|
|
370
|
+
const expectedEmissionByFixture = new Map();
|
|
371
|
+
for (const fixture of record.fixtures) {
|
|
372
|
+
if (expectedEmissionByFixture.has(fixture.fixture_id))
|
|
373
|
+
continue;
|
|
374
|
+
expectedEmissionByFixture.set(fixture.fixture_id, [...new Set(applicableChecks(fixture))].sort());
|
|
375
|
+
}
|
|
301
376
|
const seen = new Set();
|
|
302
377
|
const fixtureSet = new Set(fixtureIds);
|
|
303
378
|
for (const run of record.runs) {
|
|
@@ -313,11 +388,12 @@ export function validateReviewCertRecord(record) {
|
|
|
313
388
|
if (run.units_completed !== run.units_total) {
|
|
314
389
|
push("units_incomplete", `run ${coordinate} claims completion=ok with ${run.units_completed}/${run.units_total} units — a partial run must be not_run`, coordinate);
|
|
315
390
|
}
|
|
391
|
+
const expected = expectedEmissionByFixture.get(run.fixture_id) ?? canonical;
|
|
316
392
|
const emitted = [...new Set(run.checks.map((check) => check.check_id))].sort();
|
|
317
|
-
if (run.checks.length !==
|
|
318
|
-
emitted.length !==
|
|
319
|
-
emitted.some((id, index) => id !==
|
|
320
|
-
push("check_emission_incomplete", `run ${coordinate} must emit
|
|
393
|
+
if (run.checks.length !== expected.length ||
|
|
394
|
+
emitted.length !== expected.length ||
|
|
395
|
+
emitted.some((id, index) => id !== expected[index])) {
|
|
396
|
+
push("check_emission_incomplete", `run ${coordinate} must emit its applicable check set exactly once (${expected.length} checks); got ${run.checks.length}`, coordinate);
|
|
321
397
|
}
|
|
322
398
|
}
|
|
323
399
|
else if (run.checks.length > 0) {
|
|
@@ -344,7 +420,7 @@ export function validateReviewCertRecord(record) {
|
|
|
344
420
|
if (!ratesEqual(record.declared_aggregates.core_check_floor, REVIEW_CERT_CORE_CHECK_FLOOR)) {
|
|
345
421
|
push("aggregate_mismatch", `declared core_check_floor=${record.declared_aggregates.core_check_floor} does not match the contract floor ${REVIEW_CERT_CORE_CHECK_FLOOR}`);
|
|
346
422
|
}
|
|
347
|
-
const computed = computeReviewCertAggregates(record.runs,
|
|
423
|
+
const computed = computeReviewCertAggregates(record.runs, record.fixtures);
|
|
348
424
|
for (const row of computed.per_fixture_check) {
|
|
349
425
|
const subject = `${row.fixture_id}/${row.check_id}`;
|
|
350
426
|
const declared = declaredByKey.get(`${row.fixture_id}\u0000${row.check_id}`);
|
|
@@ -359,6 +435,28 @@ export function validateReviewCertRecord(record) {
|
|
|
359
435
|
push("core_check_floor", `${subject}: candidate pass-rate ${row.candidate_pass_rate.toFixed(4)} is below the absolute core-check floor ${REVIEW_CERT_CORE_CHECK_FLOOR.toFixed(4)}`, subject);
|
|
360
436
|
}
|
|
361
437
|
}
|
|
438
|
+
// Declared-aggregate set integrity (v3 A-2): the loop above proves every
|
|
439
|
+
// COMPUTED row is declared and recomputes; this reverse pass proves the declared
|
|
440
|
+
// side carries no EXTRA and no DUPLICATE rows. A declared aggregate for a
|
|
441
|
+
// fixture×check pair that is not applicable (a clean-target's excluded recall/
|
|
442
|
+
// grounding checks) or has no completed run never appears in `computed`, so its
|
|
443
|
+
// asserted rate cannot be recomputed; a repeated pair would let a spurious rate
|
|
444
|
+
// ride alongside the honest one. Together the passes pin the declared aggregate
|
|
445
|
+
// set to EXACTLY the computed set — one rate per applicable, computed pair.
|
|
446
|
+
const computedKeys = new Set(computed.per_fixture_check.map((row) => `${row.fixture_id}\u0000${row.check_id}`));
|
|
447
|
+
const declaredSeen = new Set();
|
|
448
|
+
for (const row of record.declared_aggregates.per_fixture_check) {
|
|
449
|
+
const key = `${row.fixture_id}\u0000${row.check_id}`;
|
|
450
|
+
const subject = `${row.fixture_id}/${row.check_id}`;
|
|
451
|
+
if (declaredSeen.has(key)) {
|
|
452
|
+
push("aggregate_mismatch", `declared aggregate for ${subject} appears more than once — a fixture×check pair must declare exactly one rate`, subject);
|
|
453
|
+
continue;
|
|
454
|
+
}
|
|
455
|
+
declaredSeen.add(key);
|
|
456
|
+
if (computedKeys.has(key))
|
|
457
|
+
continue;
|
|
458
|
+
push("aggregate_mismatch", `declared aggregate for ${subject} does not recompute — the pair is not applicable or has no completed run (over-declaration)`, subject);
|
|
459
|
+
}
|
|
362
460
|
if (record.declared_aggregates.quality_pass !== computed.quality_pass) {
|
|
363
461
|
push("aggregate_mismatch", `declared quality_pass=${record.declared_aggregates.quality_pass} does not recompute (recomputed ${computed.quality_pass})`);
|
|
364
462
|
}
|
|
@@ -55,6 +55,21 @@ const ReviewActorLlmSettingsSchema = z
|
|
|
55
55
|
timeout_ms: z.number().int().min(1).optional(),
|
|
56
56
|
})
|
|
57
57
|
.strict();
|
|
58
|
+
// Per-call LLM override (MCP tool surface): an ephemeral settings-`llm` overlay
|
|
59
|
+
// applied to the resolved settings for one review/reconstruct invocation, after
|
|
60
|
+
// which the existing pipeline runs unchanged. Single-sourced from
|
|
61
|
+
// LlmSettingsSchema minus the transport/credential fields (base_url, api_key_env,
|
|
62
|
+
// timeout_ms) — those stay settings-owned so a per-call override cannot select an
|
|
63
|
+
// arbitrary endpoint or credential env. The chosen provider's transport resolves
|
|
64
|
+
// from settings exactly as a settings edit would. .strict() rejects the excluded
|
|
65
|
+
// fields. See development-records/design/per-call-llm-override-design-v4.md.
|
|
66
|
+
export const PerCallLlmOverrideSchema = LlmSettingsSchema.pick({
|
|
67
|
+
provider: true,
|
|
68
|
+
auth: true,
|
|
69
|
+
model: true,
|
|
70
|
+
effort: true,
|
|
71
|
+
service_tier: true,
|
|
72
|
+
}).strict();
|
|
58
73
|
const LlmRefSchema = LlmSettingsSchema;
|
|
59
74
|
const ReviewWorkerSeatSchema = z.enum(["main", "worker"]);
|
|
60
75
|
const ReviewExecutionModeSchema = z.enum(["main-workers", "nested-workers"]);
|
|
@@ -187,8 +202,10 @@ const DEFAULT_REVIEW_EXECUTION = {
|
|
|
187
202
|
deliberation: "controlled-lens-deliberation",
|
|
188
203
|
};
|
|
189
204
|
export const DEFAULT_DISPATCH_BREAKER_SETTINGS = {
|
|
190
|
-
//
|
|
191
|
-
|
|
205
|
+
// DEFAULT ON (2026-07-15 owner-directed promotion): the fan-out dispatch
|
|
206
|
+
// breaker (trip/poison/recover) is on unless a setting disables it. Disable
|
|
207
|
+
// with `dispatch_breaker.enabled=false`.
|
|
208
|
+
enabled: true,
|
|
192
209
|
systemic_threshold: 3,
|
|
193
210
|
per_call_max_attempts: 3,
|
|
194
211
|
backoff_initial_ms: 3000,
|
|
@@ -212,11 +229,15 @@ const DEFAULT_REVIEW_RETRY_SETTINGS = {
|
|
|
212
229
|
deliberation_max_retries: 2,
|
|
213
230
|
synthesis_max_retries: 2,
|
|
214
231
|
retry_initial_delay_ms: 3000,
|
|
215
|
-
//
|
|
216
|
-
|
|
217
|
-
//
|
|
218
|
-
|
|
219
|
-
|
|
232
|
+
// DEFAULT ON (2026-07-15 owner-directed promotion): submit-salvage recovery is
|
|
233
|
+
// on in real usage unless disabled with `salvage.enabled=false`. The review-cert
|
|
234
|
+
// harness pins it OFF explicitly (benchmark --no-salvage) for raw, reproducible
|
|
235
|
+
// measurement — that pin is cert-only, not the product default.
|
|
236
|
+
salvage: { enabled: true, delta_completion: "unit_llm" },
|
|
237
|
+
// DEFAULT ON (2026-07-15 owner-directed promotion): bounded unit resubmit on
|
|
238
|
+
// validation rejection is on unless disabled with `resubmit.enabled=false`.
|
|
239
|
+
resubmit: { enabled: true },
|
|
240
|
+
// DEFAULT ON via DEFAULT_DISPATCH_BREAKER_SETTINGS (2026-07-15 promotion).
|
|
220
241
|
dispatch_breaker: DEFAULT_DISPATCH_BREAKER_SETTINGS,
|
|
221
242
|
};
|
|
222
243
|
const DEFAULT_REVIEW_UNIT_TIMEOUT_MS = 240000;
|
|
@@ -564,11 +585,12 @@ export function resolveOptionalReconstructActorLlmSettings(settings, actorName)
|
|
|
564
585
|
normalizeLlmModelSwitcher(actor.llm);
|
|
565
586
|
return actor.llm;
|
|
566
587
|
}
|
|
567
|
-
/**
|
|
568
|
-
*
|
|
569
|
-
*
|
|
588
|
+
/** Semantic-map authoring stage (design §5.5). DEFAULT ON (2026-07-15
|
|
589
|
+
* owner-directed promotion): absent = on; only an explicit `false` disables it,
|
|
590
|
+
* which detaches the capability pair AND leaves the synthesize seat dormant
|
|
591
|
+
* (excluded from the gate walk — U6, salvage precedent). */
|
|
570
592
|
export function isReconstructSemanticMapAuthoringEnabled(settings) {
|
|
571
|
-
return settings.reconstruct?.execution?.semantic_map_authoring
|
|
593
|
+
return settings.reconstruct?.execution?.semantic_map_authoring !== false;
|
|
572
594
|
}
|
|
573
595
|
export const SETTINGS_FILENAME = "settings.json";
|
|
574
596
|
export const RETIRED_CONFIG_FILENAMES = [
|
|
@@ -6,7 +6,13 @@ export function isDirectModelCallSelection(selection) {
|
|
|
6
6
|
}
|
|
7
7
|
export const DEFAULT_GROK_BASE_URL = "https://api.x.ai/v1";
|
|
8
8
|
export const DEFAULT_LMSTUDIO_BASE_URL = "http://localhost:1234/v1";
|
|
9
|
-
|
|
9
|
+
/**
|
|
10
|
+
* The auth a block resolves to when it omits `auth`. Exported because callers
|
|
11
|
+
* that reason about ROUTE IDENTITY (e.g. the per-call llmOverride overlay) must
|
|
12
|
+
* compare the DEFAULTED auth, not the raw field — a block without `auth` still
|
|
13
|
+
* dispatches on this route, so an override restating it is not a route change.
|
|
14
|
+
*/
|
|
15
|
+
export function defaultAuthForProvider(provider) {
|
|
10
16
|
if (provider === "openai")
|
|
11
17
|
return "oauth";
|
|
12
18
|
if (provider === "lmstudio")
|
|
@@ -7200,7 +7200,7 @@ export function createDirectCallReconstructDirectiveAuthor(args = {}) {
|
|
|
7200
7200
|
// of the EFFECTIVE config (model/adapter/base_url/effort — always folded,
|
|
7201
7201
|
// no equality judgment); else ⑤a arg present → legacy string, byte-identical
|
|
7202
7202
|
// to the pre-seat format (existing reuse keys never rotate); else absent
|
|
7203
|
-
// (seat 부재·인자 부재 = 현행 byte-parity — a
|
|
7203
|
+
// (seat 부재·인자 부재 = 현행 byte-parity — a per-call override effort alone
|
|
7204
7204
|
// reaches synthesize through the base config on BOTH sides, so no fold).
|
|
7205
7205
|
...(args.semanticMapSynthesizeLlmConfig !== undefined
|
|
7206
7206
|
? {
|
|
@@ -594,6 +594,10 @@ export async function bootstrapInvocationBindingArtifacts(params) {
|
|
|
594
594
|
// the A-path profile carries). Host advances read it for auditable
|
|
595
595
|
// execution-result/ReviewRecord retry reporting.
|
|
596
596
|
retry_policy: completeReviewRetrySettings(ontoConfig?.review?.execution?.retry),
|
|
597
|
+
// Durable stamp of the per-call override (design v4 §7): continuation
|
|
598
|
+
// re-overlays the freshly resolved project profile with it so an overridden
|
|
599
|
+
// session's units cannot revert to the non-overlaid project models.
|
|
600
|
+
...(params.llmOverride ? { llm_override: params.llmOverride } : {}),
|
|
597
601
|
interpretation_artifact_path: interpretationArtifactPath,
|
|
598
602
|
binding_output_path: bindingOutputPath,
|
|
599
603
|
session_metadata_path: sessionMetadataPath,
|