@principles/core 1.284.9 → 1.284.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/runtime-v2/__tests__/intake-to-internalization-bridge.test.js +18 -1
- package/dist/runtime-v2/__tests__/intake-to-internalization-bridge.test.js.map +1 -1
- package/dist/runtime-v2/__tests__/internalization-state-machine.test.js +4 -0
- package/dist/runtime-v2/__tests__/internalization-state-machine.test.js.map +1 -1
- package/dist/runtime-v2/__tests__/rollout-reviewer-runner-vslice.test.js +3 -3
- package/dist/runtime-v2/__tests__/rollout-reviewer-runner-vslice.test.js.map +1 -1
- package/dist/runtime-v2/feature-flags/feature-flag-contract.d.ts.map +1 -1
- package/dist/runtime-v2/feature-flags/feature-flag-contract.js +10 -0
- package/dist/runtime-v2/feature-flags/feature-flag-contract.js.map +1 -1
- package/dist/runtime-v2/feature-flags/feature-flag-lifecycle.d.ts.map +1 -1
- package/dist/runtime-v2/feature-flags/feature-flag-lifecycle.js +8 -0
- package/dist/runtime-v2/feature-flags/feature-flag-lifecycle.js.map +1 -1
- package/dist/runtime-v2/index.d.ts +1 -1
- package/dist/runtime-v2/index.d.ts.map +1 -1
- package/dist/runtime-v2/index.js +1 -1
- package/dist/runtime-v2/index.js.map +1 -1
- package/dist/runtime-v2/internalization/__tests__/a-liveness-reconciliation.test.js +2 -1
- package/dist/runtime-v2/internalization/__tests__/a-liveness-reconciliation.test.js.map +1 -1
- package/dist/runtime-v2/internalization/__tests__/c2-live-runner-chain.test.js +109 -20
- package/dist/runtime-v2/internalization/__tests__/c2-live-runner-chain.test.js.map +1 -1
- package/dist/runtime-v2/internalization/__tests__/intake-bridge-channel-admission.test.d.ts +2 -0
- package/dist/runtime-v2/internalization/__tests__/intake-bridge-channel-admission.test.d.ts.map +1 -0
- package/dist/runtime-v2/internalization/__tests__/intake-bridge-channel-admission.test.js +128 -0
- package/dist/runtime-v2/internalization/__tests__/intake-bridge-channel-admission.test.js.map +1 -0
- package/dist/runtime-v2/internalization/__tests__/internalization-transition-decision.test.js +2 -0
- package/dist/runtime-v2/internalization/__tests__/internalization-transition-decision.test.js.map +1 -1
- package/dist/runtime-v2/internalization/__tests__/mvp-core-loop-journeys.test.js +6 -0
- package/dist/runtime-v2/internalization/__tests__/mvp-core-loop-journeys.test.js.map +1 -1
- package/dist/runtime-v2/internalization/__tests__/rollout-reviewer-principle-semantic.test.d.ts +2 -0
- package/dist/runtime-v2/internalization/__tests__/rollout-reviewer-principle-semantic.test.d.ts.map +1 -0
- package/dist/runtime-v2/internalization/__tests__/rollout-reviewer-principle-semantic.test.js +328 -0
- package/dist/runtime-v2/internalization/__tests__/rollout-reviewer-principle-semantic.test.js.map +1 -0
- package/dist/runtime-v2/internalization/__tests__/verdict-drift-regressions.test.js +2 -1
- package/dist/runtime-v2/internalization/__tests__/verdict-drift-regressions.test.js.map +1 -1
- package/dist/runtime-v2/internalization/evaluator-runner.d.ts +3 -1
- package/dist/runtime-v2/internalization/evaluator-runner.d.ts.map +1 -1
- package/dist/runtime-v2/internalization/evaluator-runner.js +3 -0
- package/dist/runtime-v2/internalization/evaluator-runner.js.map +1 -1
- package/dist/runtime-v2/internalization/index.d.ts +1 -1
- package/dist/runtime-v2/internalization/index.d.ts.map +1 -1
- package/dist/runtime-v2/internalization/index.js +1 -1
- package/dist/runtime-v2/internalization/index.js.map +1 -1
- package/dist/runtime-v2/internalization/intake-to-internalization-bridge.d.ts +44 -1
- package/dist/runtime-v2/internalization/intake-to-internalization-bridge.d.ts.map +1 -1
- package/dist/runtime-v2/internalization/intake-to-internalization-bridge.js +104 -9
- package/dist/runtime-v2/internalization/intake-to-internalization-bridge.js.map +1 -1
- package/dist/runtime-v2/internalization/internalization-job-graph.d.ts +49 -10
- package/dist/runtime-v2/internalization/internalization-job-graph.d.ts.map +1 -1
- package/dist/runtime-v2/internalization/internalization-job-graph.js +71 -13
- package/dist/runtime-v2/internalization/internalization-job-graph.js.map +1 -1
- package/dist/runtime-v2/internalization/internalization-orchestrator.d.ts.map +1 -1
- package/dist/runtime-v2/internalization/internalization-orchestrator.js +1 -0
- package/dist/runtime-v2/internalization/internalization-orchestrator.js.map +1 -1
- package/dist/runtime-v2/internalization/internalization-state-machine.d.ts +13 -10
- package/dist/runtime-v2/internalization/internalization-state-machine.d.ts.map +1 -1
- package/dist/runtime-v2/internalization/internalization-state-machine.js +20 -15
- package/dist/runtime-v2/internalization/internalization-state-machine.js.map +1 -1
- package/dist/runtime-v2/internalization/peer-runner-contracts.d.ts +24 -0
- package/dist/runtime-v2/internalization/peer-runner-contracts.d.ts.map +1 -1
- package/dist/runtime-v2/internalization/peer-runner-contracts.js +17 -0
- package/dist/runtime-v2/internalization/peer-runner-contracts.js.map +1 -1
- package/dist/runtime-v2/internalization/pitask-metadata.d.ts +8 -1
- package/dist/runtime-v2/internalization/pitask-metadata.d.ts.map +1 -1
- package/dist/runtime-v2/internalization/pitask-metadata.js +10 -1
- package/dist/runtime-v2/internalization/pitask-metadata.js.map +1 -1
- package/dist/runtime-v2/internalization/revision-reopen.d.ts +2 -1
- package/dist/runtime-v2/internalization/revision-reopen.d.ts.map +1 -1
- package/dist/runtime-v2/internalization/revision-reopen.js +14 -2
- package/dist/runtime-v2/internalization/revision-reopen.js.map +1 -1
- package/dist/runtime-v2/internalization/rollout-reviewer-output.d.ts +32 -7
- package/dist/runtime-v2/internalization/rollout-reviewer-output.d.ts.map +1 -1
- package/dist/runtime-v2/internalization/rollout-reviewer-output.js +58 -16
- package/dist/runtime-v2/internalization/rollout-reviewer-output.js.map +1 -1
- package/dist/runtime-v2/internalization/rollout-reviewer-prompt-builder.d.ts +29 -4
- package/dist/runtime-v2/internalization/rollout-reviewer-prompt-builder.d.ts.map +1 -1
- package/dist/runtime-v2/internalization/rollout-reviewer-prompt-builder.js +80 -0
- package/dist/runtime-v2/internalization/rollout-reviewer-prompt-builder.js.map +1 -1
- package/dist/runtime-v2/internalization/rollout-reviewer-runner.d.ts +43 -9
- package/dist/runtime-v2/internalization/rollout-reviewer-runner.d.ts.map +1 -1
- package/dist/runtime-v2/internalization/rollout-reviewer-runner.js +213 -51
- package/dist/runtime-v2/internalization/rollout-reviewer-runner.js.map +1 -1
- package/dist/runtime-v2/pain-signal-bridge.d.ts +9 -0
- package/dist/runtime-v2/pain-signal-bridge.d.ts.map +1 -1
- package/dist/runtime-v2/pain-signal-bridge.js +23 -4
- package/dist/runtime-v2/pain-signal-bridge.js.map +1 -1
- package/dist/runtime-v2/pain-signal-runtime-factory.d.ts.map +1 -1
- package/dist/runtime-v2/pain-signal-runtime-factory.js +15 -3
- package/dist/runtime-v2/pain-signal-runtime-factory.js.map +1 -1
- package/dist/telemetry-event.d.ts +2 -2
- package/dist/telemetry-event.d.ts.map +1 -1
- package/dist/telemetry-event.js +6 -0
- package/dist/telemetry-event.js.map +1 -1
- package/package.json +1 -1
|
@@ -22,6 +22,27 @@ export function resolveRolloutReviewerRunnerOptions(options) {
|
|
|
22
22
|
outputLanguage: options.outputLanguage,
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* PRI-720: resolves the review contract mode for a rollout task.
|
|
27
|
+
*
|
|
28
|
+
* - code_tool_hook/skill channels and any explicit `full_chain` override run
|
|
29
|
+
* the legacy code-chain review (byte-compatible).
|
|
30
|
+
* - prompt/defer_archive chains in the STANDARD topology run the principle
|
|
31
|
+
* semantic review — recognized structurally by a scribe dependency and the
|
|
32
|
+
* ABSENCE of an evaluator dependency (the graph never creates one there).
|
|
33
|
+
* - A legacy (pre-PRI-720) prompt chain carries an evaluator dep; AC12 forbids
|
|
34
|
+
* reinterpreting in-flight chains, so it keeps the code-chain contract.
|
|
35
|
+
*/
|
|
36
|
+
export function resolveRolloutReviewMode(channel, pipelineMode, dependencyTaskKinds) {
|
|
37
|
+
if (pipelineMode === 'full_chain')
|
|
38
|
+
return 'code_chain';
|
|
39
|
+
if (channel !== 'prompt' && channel !== 'defer_archive')
|
|
40
|
+
return 'code_chain';
|
|
41
|
+
const kinds = dependencyTaskKinds ?? [];
|
|
42
|
+
const hasEvaluatorDep = kinds.includes('evaluator');
|
|
43
|
+
const hasScribeDep = kinds.includes('scribe');
|
|
44
|
+
return !hasEvaluatorDep && hasScribeDep ? 'principle_semantic' : 'code_chain';
|
|
45
|
+
}
|
|
25
46
|
const ROLLOUT_REVIEWER_PERMANENT_ERROR_CATEGORIES = new Set([
|
|
26
47
|
'storage_unavailable', 'workspace_invalid', 'capability_missing', 'cancelled', 'input_invalid', 'output_invalid',
|
|
27
48
|
]);
|
|
@@ -100,18 +121,20 @@ export class RolloutReviewerRunner {
|
|
|
100
121
|
}
|
|
101
122
|
const storeRunId = await this.resolveStoreRunId(taskId);
|
|
102
123
|
this.phase = RunnerPhase.BuildingContext;
|
|
103
|
-
const { contextHash,
|
|
104
|
-
if (!
|
|
124
|
+
const { contextHash, sourceArtifact, sourceArtifactId, reviewMode } = await this.buildContext(taskId);
|
|
125
|
+
if (!sourceArtifact || !sourceArtifactId) {
|
|
105
126
|
return this.retryOrFail({
|
|
106
127
|
taskId,
|
|
107
128
|
task: leasedTask,
|
|
108
129
|
errorCategory: 'input_invalid',
|
|
109
|
-
failureReason:
|
|
130
|
+
failureReason: reviewMode === 'principle_semantic'
|
|
131
|
+
? 'Scribe dependency artifact not found'
|
|
132
|
+
: 'Evaluator dependency artifact not found',
|
|
110
133
|
});
|
|
111
134
|
}
|
|
112
|
-
this.emitRolloutReviewerEvent('rollout_reviewer_context_built', taskId, { contextHash });
|
|
135
|
+
this.emitRolloutReviewerEvent('rollout_reviewer_context_built', taskId, { contextHash, reviewMode });
|
|
113
136
|
this.phase = RunnerPhase.Invoking;
|
|
114
|
-
const runHandle = await this.invokeRuntime({ taskId, contextHash,
|
|
137
|
+
const runHandle = await this.invokeRuntime({ taskId, contextHash, sourceArtifact, sourceArtifactId, reviewMode });
|
|
115
138
|
this.emitRolloutReviewerEvent('rollout_reviewer_run_started', taskId, {
|
|
116
139
|
runtimeKind: this.resolvedOptions.runtimeKind,
|
|
117
140
|
});
|
|
@@ -123,18 +146,24 @@ export class RolloutReviewerRunner {
|
|
|
123
146
|
this.phase = RunnerPhase.FetchingOutput;
|
|
124
147
|
const output = await this.fetchAndParseOutput(runHandle.runId);
|
|
125
148
|
// Lineage echo reconciliation (PRI-272 / ERR-004 / ERR-008 class):
|
|
126
|
-
// taskId and
|
|
127
|
-
// metadata whose authoritative sources are the task record and
|
|
128
|
-
// artifact store read in buildContext(). LLMs routinely truncate or
|
|
149
|
+
// taskId and the mode-specific source artifact id are runner-owned
|
|
150
|
+
// lineage metadata whose authoritative sources are the task record and
|
|
151
|
+
// the artifact store read in buildContext(). LLMs routinely truncate or
|
|
129
152
|
// alter long IDs when echoing them back, and a mismatch fails
|
|
130
153
|
// validation as output_invalid — a permanent error with no retry —
|
|
131
154
|
// dead-ending the candidate before it can reach the approval queue.
|
|
132
155
|
// Overwrite the echoed lineage with the authoritative values before
|
|
133
156
|
// validation; emit telemetry whenever an echo differed so the
|
|
134
157
|
// correction rate stays observable (rc-9-no-silent-fallback).
|
|
135
|
-
this.reconcileLineageEcho(taskId, output,
|
|
158
|
+
this.reconcileLineageEcho(taskId, output, {
|
|
159
|
+
authoritativeSourceArtifactId: sourceArtifactId,
|
|
160
|
+
reviewMode,
|
|
161
|
+
});
|
|
136
162
|
this.phase = RunnerPhase.Validating;
|
|
137
|
-
const validationResult = await this.validator.validate(output, taskId,
|
|
163
|
+
const validationResult = await this.validator.validate(output, taskId, {
|
|
164
|
+
expectedSourceArtifactId: sourceArtifactId,
|
|
165
|
+
reviewMode,
|
|
166
|
+
});
|
|
138
167
|
if (!validationResult.valid) {
|
|
139
168
|
return await this.handleValidationError({
|
|
140
169
|
taskId,
|
|
@@ -153,7 +182,9 @@ export class RolloutReviewerRunner {
|
|
|
153
182
|
output,
|
|
154
183
|
task: leasedTask,
|
|
155
184
|
contextHash,
|
|
156
|
-
|
|
185
|
+
...(reviewMode === 'principle_semantic'
|
|
186
|
+
? { reviewMode, sourceScribeArtifactId: sourceArtifactId }
|
|
187
|
+
: { reviewMode, sourceEvaluatorArtifactId: sourceArtifactId }),
|
|
157
188
|
channel: hydratePITaskRecord(leasedTask)?.channel,
|
|
158
189
|
});
|
|
159
190
|
}
|
|
@@ -168,33 +199,42 @@ export class RolloutReviewerRunner {
|
|
|
168
199
|
* BasePeerRunner, so it calls the shared helper directly and emits its own
|
|
169
200
|
* telemetry (no automatic runnerName prefix).
|
|
170
201
|
*
|
|
171
|
-
* The prompt asks the model to copy taskId /
|
|
172
|
-
* sourceTrace
|
|
173
|
-
* routinely truncated or altered on echo. Because a mismatch is
|
|
174
|
-
* output_invalid (a permanent error — no retry), a bad echo
|
|
175
|
-
* blocks the candidate from reaching the approval queue.
|
|
176
|
-
* runner-owned metadata (rc-6): the authoritative values come
|
|
177
|
-
* task record and the artifact store read in buildContext().
|
|
202
|
+
* The prompt asks the model to copy taskId / the mode-specific source
|
|
203
|
+
* artifact id / its sourceTrace counterpart verbatim, but long artifact IDs
|
|
204
|
+
* are routinely truncated or altered on echo. Because a mismatch is
|
|
205
|
+
* classified output_invalid (a permanent error — no retry), a bad echo
|
|
206
|
+
* permanently blocks the candidate from reaching the approval queue.
|
|
207
|
+
* Lineage is runner-owned metadata (rc-6): the authoritative values come
|
|
208
|
+
* from the task record and the artifact store read in buildContext().
|
|
209
|
+
* PRI-720: in principle semantic mode the reviewed source is the scribe
|
|
210
|
+
* artifact, so the reconciled fields are sourceScribeArtifactId /
|
|
211
|
+
* sourceTrace.scribeArtifactId.
|
|
178
212
|
*
|
|
179
213
|
* Whenever an echo differed (or sourceTrace was missing), a
|
|
180
214
|
* rollout_reviewer_lineage_echo_corrected telemetry event is emitted so
|
|
181
215
|
* the correction rate stays observable (rc-9-no-silent-fallback).
|
|
182
216
|
*/
|
|
183
|
-
reconcileLineageEcho(taskId, output,
|
|
217
|
+
reconcileLineageEcho(taskId, output, params) {
|
|
218
|
+
const principleSemantic = params.reviewMode === 'principle_semantic';
|
|
184
219
|
const correctedFields = reconcileLineageEcho(output, {
|
|
185
220
|
topFields: [
|
|
186
221
|
{ field: 'taskId', authoritativeValue: taskId },
|
|
187
|
-
|
|
222
|
+
principleSemantic
|
|
223
|
+
? { field: 'sourceScribeArtifactId', authoritativeValue: params.authoritativeSourceArtifactId }
|
|
224
|
+
: { field: 'sourceEvaluatorArtifactId', authoritativeValue: params.authoritativeSourceArtifactId },
|
|
188
225
|
],
|
|
189
226
|
trace: {
|
|
190
227
|
traceField: 'sourceTrace',
|
|
191
|
-
fields: [
|
|
228
|
+
fields: [principleSemantic
|
|
229
|
+
? { field: 'scribeArtifactId', authoritativeValue: params.authoritativeSourceArtifactId }
|
|
230
|
+
: { field: 'evaluatorArtifactId', authoritativeValue: params.authoritativeSourceArtifactId }],
|
|
192
231
|
},
|
|
193
232
|
});
|
|
194
233
|
if (correctedFields.length > 0) {
|
|
195
234
|
this.emitRolloutReviewerEvent('rollout_reviewer_lineage_echo_corrected', taskId, {
|
|
196
235
|
correctedFields,
|
|
197
|
-
|
|
236
|
+
authoritativeSourceArtifactId: params.authoritativeSourceArtifactId,
|
|
237
|
+
reviewMode: params.reviewMode,
|
|
198
238
|
});
|
|
199
239
|
}
|
|
200
240
|
}
|
|
@@ -206,14 +246,23 @@ export class RolloutReviewerRunner {
|
|
|
206
246
|
const piTask = hydratePITaskRecord(task);
|
|
207
247
|
const deps = piTask?.dependencyTaskIds ?? [];
|
|
208
248
|
if (deps.length === 0) {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
249
|
+
const emptyMode = resolveRolloutReviewMode(piTask?.channel, piTask?.pipelineMode);
|
|
250
|
+
this.emitRolloutReviewerEvent('rollout_reviewer_no_dependencies', taskId, { reviewMode: emptyMode });
|
|
251
|
+
return { contextHash: 'empty', sourceArtifact: null, sourceArtifactId: null, reviewMode: emptyMode };
|
|
252
|
+
}
|
|
253
|
+
// Fetch dep tasks once; the review mode is structural (PRI-720/AC12):
|
|
254
|
+
// a scribe dep without an evaluator dep = principle semantic review; an
|
|
255
|
+
// evaluator dep (legacy or full_chain chain) = code-chain review.
|
|
256
|
+
const depEntries = [];
|
|
212
257
|
for (const depId of deps) {
|
|
213
258
|
const depTask = await this.stateManager.getTask(depId);
|
|
214
|
-
if (
|
|
215
|
-
|
|
216
|
-
|
|
259
|
+
if (depTask)
|
|
260
|
+
depEntries.push({ depId, task: depTask });
|
|
261
|
+
}
|
|
262
|
+
const reviewMode = resolveRolloutReviewMode(piTask?.channel, piTask?.pipelineMode, depEntries.map((d) => d.task.taskKind));
|
|
263
|
+
const wantedDepKind = reviewMode === 'principle_semantic' ? 'scribe' : 'evaluator';
|
|
264
|
+
for (const { depId, task: depTask } of depEntries) {
|
|
265
|
+
if (depTask.taskKind !== wantedDepKind)
|
|
217
266
|
continue;
|
|
218
267
|
if (depTask.status !== 'succeeded') {
|
|
219
268
|
this.emitRolloutReviewerEvent('rollout_reviewer_dependency_not_succeeded', taskId, {
|
|
@@ -227,20 +276,33 @@ export class RolloutReviewerRunner {
|
|
|
227
276
|
const [firstArtifact] = artifacts;
|
|
228
277
|
if (!firstArtifact)
|
|
229
278
|
continue;
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
279
|
+
if (reviewMode === 'principle_semantic') {
|
|
280
|
+
this.emitRolloutReviewerEvent('rollout_reviewer_scribe_dep_selected', taskId, {
|
|
281
|
+
depTaskId: depId,
|
|
282
|
+
artifactId: firstArtifact.artifactId,
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
this.emitRolloutReviewerEvent('rollout_reviewer_evaluator_dep_selected', taskId, {
|
|
287
|
+
depTaskId: depId,
|
|
288
|
+
artifactId: firstArtifact.artifactId,
|
|
289
|
+
});
|
|
290
|
+
}
|
|
235
291
|
return {
|
|
236
|
-
contextHash: RolloutReviewerRunner.hashContextRefs([
|
|
237
|
-
|
|
238
|
-
|
|
292
|
+
contextHash: RolloutReviewerRunner.hashContextRefs([firstArtifact.artifactId]),
|
|
293
|
+
sourceArtifact: firstArtifact.contentJson,
|
|
294
|
+
sourceArtifactId: firstArtifact.artifactId,
|
|
295
|
+
reviewMode,
|
|
239
296
|
};
|
|
240
297
|
}
|
|
241
298
|
}
|
|
242
|
-
|
|
243
|
-
|
|
299
|
+
if (reviewMode === 'principle_semantic') {
|
|
300
|
+
this.emitRolloutReviewerEvent('rollout_reviewer_no_scribe_artifact', taskId, { reviewMode });
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
this.emitRolloutReviewerEvent('rollout_reviewer_no_evaluator_artifact', taskId, { reviewMode });
|
|
304
|
+
}
|
|
305
|
+
return { contextHash: 'empty', sourceArtifact: null, sourceArtifactId: null, reviewMode };
|
|
244
306
|
}
|
|
245
307
|
static hashContextRefs(refs) {
|
|
246
308
|
if (refs.length === 0)
|
|
@@ -261,21 +323,23 @@ export class RolloutReviewerRunner {
|
|
|
261
323
|
return latestRun.runId;
|
|
262
324
|
}
|
|
263
325
|
async invokeRuntime(params) {
|
|
264
|
-
let
|
|
265
|
-
if (params.
|
|
326
|
+
let parsedSourceArtifact = null;
|
|
327
|
+
if (params.sourceArtifact) {
|
|
266
328
|
try {
|
|
267
|
-
|
|
329
|
+
parsedSourceArtifact = JSON.parse(params.sourceArtifact);
|
|
268
330
|
}
|
|
269
331
|
catch {
|
|
270
|
-
|
|
332
|
+
parsedSourceArtifact = params.sourceArtifact;
|
|
271
333
|
}
|
|
272
334
|
}
|
|
273
335
|
const builder = new RolloutReviewerPromptBuilder();
|
|
274
336
|
const { message, systemPrompt } = builder.buildPrompt({
|
|
275
337
|
taskId: params.taskId,
|
|
276
338
|
contextHash: params.contextHash,
|
|
277
|
-
|
|
278
|
-
|
|
339
|
+
reviewMode: params.reviewMode,
|
|
340
|
+
...(params.reviewMode === 'principle_semantic'
|
|
341
|
+
? { sourceScribeArtifactId: params.sourceArtifactId, scribeArtifact: parsedSourceArtifact }
|
|
342
|
+
: { sourceEvaluatorArtifactId: params.sourceArtifactId, evaluatorArtifact: parsedSourceArtifact }),
|
|
279
343
|
// PRI-714: language directive for review fields (undefined = none).
|
|
280
344
|
outputLanguage: this.resolvedOptions.outputLanguage,
|
|
281
345
|
});
|
|
@@ -557,6 +621,51 @@ export class RolloutReviewerRunner {
|
|
|
557
621
|
}
|
|
558
622
|
return acc;
|
|
559
623
|
}
|
|
624
|
+
/**
|
|
625
|
+
* PRI-720 C3: flip the exact reviewed scribe principle artifact to
|
|
626
|
+
* `validated` on semantic approve. Mirrors the evaluator's approved-path
|
|
627
|
+
* behavior (evaluator-runner.ts): idempotent updateValidationStatus;
|
|
628
|
+
* a `false` return is a deterministic store inconsistency surfaced via
|
|
629
|
+
* structured telemetry (the activation resolver then fails closed to
|
|
630
|
+
* needs_human_review); a store throw retries (the write is a required
|
|
631
|
+
* effect of the approve verdict — resume replays it, never a second LLM
|
|
632
|
+
* verdict).
|
|
633
|
+
*/
|
|
634
|
+
async validateReviewedScribeArtifactOrThrow(ctx) {
|
|
635
|
+
const principleArtifactId = ctx.sourceScribeArtifactId;
|
|
636
|
+
if (!principleArtifactId) {
|
|
637
|
+
// No reviewed scribe artifact on the context — the validator already
|
|
638
|
+
// required it, so this is an invariant breach; fail loud (retryOrFail
|
|
639
|
+
// treats input_invalid as permanent and the approval queue stays honest).
|
|
640
|
+
throw new PDRuntimeError('input_invalid', `principle semantic approve requires sourceScribeArtifactId on task ${ctx.taskId}`);
|
|
641
|
+
}
|
|
642
|
+
try {
|
|
643
|
+
const updated = await this.artifactStore.updateValidationStatus(principleArtifactId, 'validated');
|
|
644
|
+
if (!updated) {
|
|
645
|
+
this.emitRolloutReviewerEvent('rollout_principle_validation_update_not_found', ctx.taskId, {
|
|
646
|
+
runId: ctx.runId,
|
|
647
|
+
sourceArtifactId: principleArtifactId,
|
|
648
|
+
reason: 'scribe_principle_artifact_not_found_in_store',
|
|
649
|
+
nextAction: 'verify_artifact_lineage_and_store_consistency',
|
|
650
|
+
});
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
this.emitRolloutReviewerEvent('rollout_principle_validated', ctx.taskId, {
|
|
654
|
+
runId: ctx.runId,
|
|
655
|
+
sourceArtifactId: principleArtifactId,
|
|
656
|
+
validatedBy: 'rollout_reviewer_principle_semantic_approve',
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
catch (updateErr) {
|
|
660
|
+
this.emitRolloutReviewerEvent('rollout_principle_validation_update_failed', ctx.taskId, {
|
|
661
|
+
runId: ctx.runId,
|
|
662
|
+
sourceArtifactId: principleArtifactId,
|
|
663
|
+
errorMessage: updateErr instanceof Error ? updateErr.message : String(updateErr),
|
|
664
|
+
nextAction: 'task_will_retry; repeated validated write is idempotent',
|
|
665
|
+
});
|
|
666
|
+
throw updateErr;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
560
669
|
/**
|
|
561
670
|
* P0-2 (外部复核): dispatch + 结果分类。
|
|
562
671
|
* 返回 true = governance transition 完成 (activated / already_activated /
|
|
@@ -692,7 +801,8 @@ export class RolloutReviewerRunner {
|
|
|
692
801
|
}
|
|
693
802
|
/**
|
|
694
803
|
* 解析修订目标任务 (只读遍历 dep 链):
|
|
695
|
-
* rollout → evaluator → artificer → scribe
|
|
804
|
+
* rollout → evaluator → artificer → scribe (code 链);
|
|
805
|
+
* rollout → scribe (PRI-720 principle 语义链: scribe 是直接依赖)。
|
|
696
806
|
* code_tool_hook → artificer; 其他 → scribe (走到底)。
|
|
697
807
|
*/
|
|
698
808
|
async resolveRevisionTarget(taskId, channel) {
|
|
@@ -729,8 +839,19 @@ export class RolloutReviewerRunner {
|
|
|
729
839
|
break;
|
|
730
840
|
}
|
|
731
841
|
}
|
|
732
|
-
if (!artificerTaskId)
|
|
842
|
+
if (!artificerTaskId) {
|
|
843
|
+
// PRI-720 (C4): principle semantic chain — the scribe is a DIRECT dep of
|
|
844
|
+
// the rollout (the graph never created an artificer/evaluator). Route
|
|
845
|
+
// the revision straight back to the authoring scribe instead of
|
|
846
|
+
// dead-ending in needs_human_review.
|
|
847
|
+
for (const depId of firstHop) {
|
|
848
|
+
const dep = await this.stateManager.getTask(depId);
|
|
849
|
+
if (!dep || dep.taskKind !== 'scribe')
|
|
850
|
+
continue;
|
|
851
|
+
return { taskId: dep.taskId, kind: 'scribe' };
|
|
852
|
+
}
|
|
733
853
|
return null;
|
|
854
|
+
}
|
|
734
855
|
if (channel === 'code_tool_hook') {
|
|
735
856
|
return { taskId: artificerTaskId, kind: 'artificer' };
|
|
736
857
|
}
|
|
@@ -892,6 +1013,17 @@ export class RolloutReviewerRunner {
|
|
|
892
1013
|
decisionOverride) {
|
|
893
1014
|
const decision = decisionOverride ?? ctx.output.review.decision;
|
|
894
1015
|
if (decision === 'approve_rollout') {
|
|
1016
|
+
// ── PRI-720 C3: validated 翻转归属转移 ──
|
|
1017
|
+
// In principle semantic mode the evaluator (the legacy validated writer,
|
|
1018
|
+
// evaluator-runner.ts approved path) does not exist on the chain, so the
|
|
1019
|
+
// semantic review's approve owns flipping the EXACT reviewed scribe
|
|
1020
|
+
// artifact to validated. Idempotent (crash-resume re-applies without
|
|
1021
|
+
// contradiction) and identity-preserving: reviewed = validated = approved
|
|
1022
|
+
// = PromptWriter-consumed artifact. Runs inside applyDecisionEffects so
|
|
1023
|
+
// resume replays it as part of the same governance effects.
|
|
1024
|
+
if (ctx.reviewMode === 'principle_semantic') {
|
|
1025
|
+
await this.validateReviewedScribeArtifactOrThrow(ctx);
|
|
1026
|
+
}
|
|
895
1027
|
const { candidateId, rejectionDetail } = await this.resolveActivationCandidate(ctx);
|
|
896
1028
|
if (!candidateId) {
|
|
897
1029
|
// PRI-634: rejectionDetail 透传给 Owner —— 说明候选为何不合格
|
|
@@ -938,6 +1070,23 @@ export class RolloutReviewerRunner {
|
|
|
938
1070
|
throw err;
|
|
939
1071
|
}
|
|
940
1072
|
}
|
|
1073
|
+
/**
|
|
1074
|
+
* PRI-720: dep-aware review mode for a hydrated task (resume/owner-override
|
|
1075
|
+
* paths, where buildContext did not run). Structural rule — see
|
|
1076
|
+
* resolveRolloutReviewMode: a scribe dep without an evaluator dep is a
|
|
1077
|
+
* principle semantic chain; anything else keeps the code-chain contract.
|
|
1078
|
+
*/
|
|
1079
|
+
async resolveReviewModeForTask(piTask) {
|
|
1080
|
+
if (!piTask)
|
|
1081
|
+
return 'code_chain';
|
|
1082
|
+
const kinds = [];
|
|
1083
|
+
for (const depId of piTask.dependencyTaskIds) {
|
|
1084
|
+
const dep = await this.stateManager.getTask(depId);
|
|
1085
|
+
if (dep)
|
|
1086
|
+
kinds.push(dep.taskKind);
|
|
1087
|
+
}
|
|
1088
|
+
return resolveRolloutReviewMode(piTask.channel, piTask.pipelineMode, kinds);
|
|
1089
|
+
}
|
|
941
1090
|
/**
|
|
942
1091
|
* 入口恢复门: pending completion intent (同 epoch) 是 recovery authority。
|
|
943
1092
|
* 返回非 null = 本次 run 以 resume 完成 (LLM 未被调用);
|
|
@@ -996,13 +1145,16 @@ export class RolloutReviewerRunner {
|
|
|
996
1145
|
});
|
|
997
1146
|
}
|
|
998
1147
|
const output = await this.recoverIntentOutput(taskId, intent.sourceRunId);
|
|
1148
|
+
const reviewMode = await this.resolveReviewModeForTask(piTask);
|
|
999
1149
|
const ctx = {
|
|
1000
1150
|
taskId,
|
|
1001
1151
|
runId: intent.sourceRunId,
|
|
1002
1152
|
output,
|
|
1003
1153
|
task: leasedTask,
|
|
1004
1154
|
contextHash: `resume-${intent.sourceRunId}`,
|
|
1005
|
-
|
|
1155
|
+
...(reviewMode === 'principle_semantic'
|
|
1156
|
+
? { reviewMode, sourceScribeArtifactId: output.sourceScribeArtifactId }
|
|
1157
|
+
: { reviewMode, sourceEvaluatorArtifactId: output.sourceEvaluatorArtifactId }),
|
|
1006
1158
|
channel: piTask.channel,
|
|
1007
1159
|
};
|
|
1008
1160
|
const artifactId = `pi-art-${taskId}-${intent.sourceRunId}`;
|
|
@@ -1060,14 +1212,18 @@ export class RolloutReviewerRunner {
|
|
|
1060
1212
|
if (output.review.decision !== resolution.machineDecision) {
|
|
1061
1213
|
throw new PDRuntimeError('storage_unavailable', `owner resolution output mismatch for task ${taskId}: recovered decision '${output.review.decision}' != recorded machine decision '${resolution.machineDecision}'`);
|
|
1062
1214
|
}
|
|
1215
|
+
const overridePiTask = hydratePITaskRecord(leasedTask);
|
|
1216
|
+
const overrideReviewMode = await this.resolveReviewModeForTask(overridePiTask ?? undefined);
|
|
1063
1217
|
const ctx = {
|
|
1064
1218
|
taskId,
|
|
1065
1219
|
runId: resolution.sourceRunId,
|
|
1066
1220
|
output,
|
|
1067
1221
|
task: leasedTask,
|
|
1068
1222
|
contextHash: `owner-override-${resolution.resolutionId}`,
|
|
1069
|
-
|
|
1070
|
-
|
|
1223
|
+
...(overrideReviewMode === 'principle_semantic'
|
|
1224
|
+
? { reviewMode: overrideReviewMode, sourceScribeArtifactId: output.sourceScribeArtifactId }
|
|
1225
|
+
: { reviewMode: overrideReviewMode, sourceEvaluatorArtifactId: output.sourceEvaluatorArtifactId }),
|
|
1226
|
+
channel: overridePiTask?.channel,
|
|
1071
1227
|
};
|
|
1072
1228
|
const artifactId = `pi-art-${taskId}-${resolution.sourceRunId}`;
|
|
1073
1229
|
const resultRef = `rollout-reviewer://${resolution.sourceRunId}`;
|
|
@@ -1182,13 +1338,19 @@ export class RolloutReviewerRunner {
|
|
|
1182
1338
|
// validate + lineage echo reconciliation,这里是对存储腐坏的防御)
|
|
1183
1339
|
let echoedArtifactId;
|
|
1184
1340
|
if (typeof parsed === 'object' && parsed !== null) {
|
|
1185
|
-
const v = parsed.sourceEvaluatorArtifactId
|
|
1341
|
+
const v = parsed.sourceEvaluatorArtifactId
|
|
1342
|
+
?? parsed.sourceScribeArtifactId;
|
|
1186
1343
|
if (typeof v === 'string')
|
|
1187
1344
|
echoedArtifactId = v;
|
|
1188
1345
|
}
|
|
1189
1346
|
// runtime-contract-exempt: ERR-001 minimal shape check then full validator.validate — same pattern as fetchAndParseOutput; the cast only narrows post-validation
|
|
1190
1347
|
const candidate = parsed;
|
|
1191
|
-
const
|
|
1348
|
+
const rawTask = await this.stateManager.getTask(taskId);
|
|
1349
|
+
const reviewMode = await this.resolveReviewModeForTask(rawTask ? hydratePITaskRecord(rawTask) ?? undefined : undefined);
|
|
1350
|
+
const vr = await this.validator.validate(candidate, taskId, {
|
|
1351
|
+
expectedSourceArtifactId: echoedArtifactId,
|
|
1352
|
+
reviewMode,
|
|
1353
|
+
});
|
|
1192
1354
|
if (!vr.valid) {
|
|
1193
1355
|
throw new PDRuntimeError('storage_unavailable', `completion intent output unrecoverable: run ${sourceRunId} of task ${taskId} failed revalidation (${vr.errors.join('; ')})`);
|
|
1194
1356
|
}
|