@principles/core 1.284.9 → 1.284.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/dist/runtime-v2/__tests__/architecture-regression.test.js +5 -3
  2. package/dist/runtime-v2/__tests__/architecture-regression.test.js.map +1 -1
  3. package/dist/runtime-v2/__tests__/intake-to-internalization-bridge.test.js +18 -1
  4. package/dist/runtime-v2/__tests__/intake-to-internalization-bridge.test.js.map +1 -1
  5. package/dist/runtime-v2/__tests__/internalization-state-machine.test.js +4 -0
  6. package/dist/runtime-v2/__tests__/internalization-state-machine.test.js.map +1 -1
  7. package/dist/runtime-v2/__tests__/rollout-reviewer-runner-vslice.test.js +3 -3
  8. package/dist/runtime-v2/__tests__/rollout-reviewer-runner-vslice.test.js.map +1 -1
  9. package/dist/runtime-v2/adapter/__tests__/artificer-l2-adapter.test.js +221 -0
  10. package/dist/runtime-v2/adapter/__tests__/artificer-l2-adapter.test.js.map +1 -1
  11. package/dist/runtime-v2/adapter/__tests__/l2-resolve-model-compat.test.js +66 -0
  12. package/dist/runtime-v2/adapter/__tests__/l2-resolve-model-compat.test.js.map +1 -1
  13. package/dist/runtime-v2/adapter/artificer-l2-adapter.d.ts +17 -0
  14. package/dist/runtime-v2/adapter/artificer-l2-adapter.d.ts.map +1 -1
  15. package/dist/runtime-v2/adapter/artificer-l2-adapter.js +217 -28
  16. package/dist/runtime-v2/adapter/artificer-l2-adapter.js.map +1 -1
  17. package/dist/runtime-v2/adapter/l2-agent-loop-adapter.d.ts +21 -5
  18. package/dist/runtime-v2/adapter/l2-agent-loop-adapter.d.ts.map +1 -1
  19. package/dist/runtime-v2/adapter/l2-agent-loop-adapter.js +60 -9
  20. package/dist/runtime-v2/adapter/l2-agent-loop-adapter.js.map +1 -1
  21. package/dist/runtime-v2/feature-flags/feature-flag-contract.d.ts.map +1 -1
  22. package/dist/runtime-v2/feature-flags/feature-flag-contract.js +10 -0
  23. package/dist/runtime-v2/feature-flags/feature-flag-contract.js.map +1 -1
  24. package/dist/runtime-v2/feature-flags/feature-flag-lifecycle.d.ts.map +1 -1
  25. package/dist/runtime-v2/feature-flags/feature-flag-lifecycle.js +8 -0
  26. package/dist/runtime-v2/feature-flags/feature-flag-lifecycle.js.map +1 -1
  27. package/dist/runtime-v2/index.d.ts +1 -3
  28. package/dist/runtime-v2/index.d.ts.map +1 -1
  29. package/dist/runtime-v2/index.js +5 -3
  30. package/dist/runtime-v2/index.js.map +1 -1
  31. package/dist/runtime-v2/internalization/__tests__/a-liveness-reconciliation.test.js +2 -1
  32. package/dist/runtime-v2/internalization/__tests__/a-liveness-reconciliation.test.js.map +1 -1
  33. package/dist/runtime-v2/internalization/__tests__/c2-live-runner-chain.test.js +109 -20
  34. package/dist/runtime-v2/internalization/__tests__/c2-live-runner-chain.test.js.map +1 -1
  35. package/dist/runtime-v2/internalization/__tests__/intake-bridge-channel-admission.test.d.ts +2 -0
  36. package/dist/runtime-v2/internalization/__tests__/intake-bridge-channel-admission.test.d.ts.map +1 -0
  37. package/dist/runtime-v2/internalization/__tests__/intake-bridge-channel-admission.test.js +128 -0
  38. package/dist/runtime-v2/internalization/__tests__/intake-bridge-channel-admission.test.js.map +1 -0
  39. package/dist/runtime-v2/internalization/__tests__/internalization-transition-decision.test.js +2 -0
  40. package/dist/runtime-v2/internalization/__tests__/internalization-transition-decision.test.js.map +1 -1
  41. package/dist/runtime-v2/internalization/__tests__/mvp-core-loop-journeys.test.js +6 -0
  42. package/dist/runtime-v2/internalization/__tests__/mvp-core-loop-journeys.test.js.map +1 -1
  43. package/dist/runtime-v2/internalization/__tests__/rollout-reviewer-principle-semantic.test.d.ts +2 -0
  44. package/dist/runtime-v2/internalization/__tests__/rollout-reviewer-principle-semantic.test.d.ts.map +1 -0
  45. package/dist/runtime-v2/internalization/__tests__/rollout-reviewer-principle-semantic.test.js +328 -0
  46. package/dist/runtime-v2/internalization/__tests__/rollout-reviewer-principle-semantic.test.js.map +1 -0
  47. package/dist/runtime-v2/internalization/__tests__/verdict-drift-regressions.test.js +2 -1
  48. package/dist/runtime-v2/internalization/__tests__/verdict-drift-regressions.test.js.map +1 -1
  49. package/dist/runtime-v2/internalization/evaluator-runner.d.ts +3 -1
  50. package/dist/runtime-v2/internalization/evaluator-runner.d.ts.map +1 -1
  51. package/dist/runtime-v2/internalization/evaluator-runner.js +3 -0
  52. package/dist/runtime-v2/internalization/evaluator-runner.js.map +1 -1
  53. package/dist/runtime-v2/internalization/index.d.ts +1 -1
  54. package/dist/runtime-v2/internalization/index.d.ts.map +1 -1
  55. package/dist/runtime-v2/internalization/index.js +1 -1
  56. package/dist/runtime-v2/internalization/index.js.map +1 -1
  57. package/dist/runtime-v2/internalization/intake-to-internalization-bridge.d.ts +44 -1
  58. package/dist/runtime-v2/internalization/intake-to-internalization-bridge.d.ts.map +1 -1
  59. package/dist/runtime-v2/internalization/intake-to-internalization-bridge.js +104 -9
  60. package/dist/runtime-v2/internalization/intake-to-internalization-bridge.js.map +1 -1
  61. package/dist/runtime-v2/internalization/internalization-job-graph.d.ts +49 -10
  62. package/dist/runtime-v2/internalization/internalization-job-graph.d.ts.map +1 -1
  63. package/dist/runtime-v2/internalization/internalization-job-graph.js +71 -13
  64. package/dist/runtime-v2/internalization/internalization-job-graph.js.map +1 -1
  65. package/dist/runtime-v2/internalization/internalization-orchestrator.d.ts.map +1 -1
  66. package/dist/runtime-v2/internalization/internalization-orchestrator.js +1 -0
  67. package/dist/runtime-v2/internalization/internalization-orchestrator.js.map +1 -1
  68. package/dist/runtime-v2/internalization/internalization-state-machine.d.ts +13 -10
  69. package/dist/runtime-v2/internalization/internalization-state-machine.d.ts.map +1 -1
  70. package/dist/runtime-v2/internalization/internalization-state-machine.js +20 -15
  71. package/dist/runtime-v2/internalization/internalization-state-machine.js.map +1 -1
  72. package/dist/runtime-v2/internalization/peer-runner-contracts.d.ts +24 -0
  73. package/dist/runtime-v2/internalization/peer-runner-contracts.d.ts.map +1 -1
  74. package/dist/runtime-v2/internalization/peer-runner-contracts.js +17 -0
  75. package/dist/runtime-v2/internalization/peer-runner-contracts.js.map +1 -1
  76. package/dist/runtime-v2/internalization/pitask-metadata.d.ts +8 -1
  77. package/dist/runtime-v2/internalization/pitask-metadata.d.ts.map +1 -1
  78. package/dist/runtime-v2/internalization/pitask-metadata.js +10 -1
  79. package/dist/runtime-v2/internalization/pitask-metadata.js.map +1 -1
  80. package/dist/runtime-v2/internalization/revision-reopen.d.ts +2 -1
  81. package/dist/runtime-v2/internalization/revision-reopen.d.ts.map +1 -1
  82. package/dist/runtime-v2/internalization/revision-reopen.js +14 -2
  83. package/dist/runtime-v2/internalization/revision-reopen.js.map +1 -1
  84. package/dist/runtime-v2/internalization/rollout-reviewer-output.d.ts +32 -7
  85. package/dist/runtime-v2/internalization/rollout-reviewer-output.d.ts.map +1 -1
  86. package/dist/runtime-v2/internalization/rollout-reviewer-output.js +58 -16
  87. package/dist/runtime-v2/internalization/rollout-reviewer-output.js.map +1 -1
  88. package/dist/runtime-v2/internalization/rollout-reviewer-prompt-builder.d.ts +29 -4
  89. package/dist/runtime-v2/internalization/rollout-reviewer-prompt-builder.d.ts.map +1 -1
  90. package/dist/runtime-v2/internalization/rollout-reviewer-prompt-builder.js +80 -0
  91. package/dist/runtime-v2/internalization/rollout-reviewer-prompt-builder.js.map +1 -1
  92. package/dist/runtime-v2/internalization/rollout-reviewer-runner.d.ts +43 -9
  93. package/dist/runtime-v2/internalization/rollout-reviewer-runner.d.ts.map +1 -1
  94. package/dist/runtime-v2/internalization/rollout-reviewer-runner.js +213 -51
  95. package/dist/runtime-v2/internalization/rollout-reviewer-runner.js.map +1 -1
  96. package/dist/runtime-v2/pain-signal-bridge.d.ts +9 -0
  97. package/dist/runtime-v2/pain-signal-bridge.d.ts.map +1 -1
  98. package/dist/runtime-v2/pain-signal-bridge.js +23 -4
  99. package/dist/runtime-v2/pain-signal-bridge.js.map +1 -1
  100. package/dist/runtime-v2/pain-signal-runtime-factory.d.ts.map +1 -1
  101. package/dist/runtime-v2/pain-signal-runtime-factory.js +15 -3
  102. package/dist/runtime-v2/pain-signal-runtime-factory.js.map +1 -1
  103. package/dist/runtime-v2/tools/__tests__/artificer-l2-tool-contract.test.js +8 -0
  104. package/dist/runtime-v2/tools/__tests__/artificer-l2-tool-contract.test.js.map +1 -1
  105. package/dist/runtime-v2/tools/__tests__/artificer-output-typebox.test.js +15 -1
  106. package/dist/runtime-v2/tools/__tests__/artificer-output-typebox.test.js.map +1 -1
  107. package/dist/runtime-v2/tools/artificer-l2-tool-contract.d.ts.map +1 -1
  108. package/dist/runtime-v2/tools/artificer-l2-tool-contract.js +24 -4
  109. package/dist/runtime-v2/tools/artificer-l2-tool-contract.js.map +1 -1
  110. package/dist/runtime-v2/tools/artificer-output-typebox.d.ts +1 -0
  111. package/dist/runtime-v2/tools/artificer-output-typebox.d.ts.map +1 -1
  112. package/dist/runtime-v2/tools/artificer-output-typebox.js +6 -0
  113. package/dist/runtime-v2/tools/artificer-output-typebox.js.map +1 -1
  114. package/dist/telemetry-event.d.ts +2 -2
  115. package/dist/telemetry-event.d.ts.map +1 -1
  116. package/dist/telemetry-event.js +7 -1
  117. package/dist/telemetry-event.js.map +1 -1
  118. package/package.json +1 -1
  119. package/dist/runtime-v2/__tests__/workspace-guidance-migration.test.d.ts +0 -2
  120. package/dist/runtime-v2/__tests__/workspace-guidance-migration.test.d.ts.map +0 -1
  121. package/dist/runtime-v2/__tests__/workspace-guidance-migration.test.js +0 -328
  122. package/dist/runtime-v2/__tests__/workspace-guidance-migration.test.js.map +0 -1
  123. package/dist/runtime-v2/workspace-guidance-migration.d.ts +0 -12
  124. package/dist/runtime-v2/workspace-guidance-migration.d.ts.map +0 -1
  125. package/dist/runtime-v2/workspace-guidance-migration.js +0 -193
  126. package/dist/runtime-v2/workspace-guidance-migration.js.map +0 -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, evaluatorArtifact, sourceEvaluatorArtifactId } = await this.buildContext(taskId);
104
- if (!evaluatorArtifact || !sourceEvaluatorArtifactId) {
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: sourceEvaluatorArtifactId ? 'Evaluator dependency artifact not found' : 'Evaluator dependency artifact ID not resolved',
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, evaluatorArtifact, sourceEvaluatorArtifactId });
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 sourceEvaluatorArtifactId are runner-owned lineage
127
- // metadata whose authoritative sources are the task record and the
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, sourceEvaluatorArtifactId);
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, sourceEvaluatorArtifactId ?? undefined);
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
- sourceEvaluatorArtifactId,
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 / sourceEvaluatorArtifactId /
172
- * sourceTrace.evaluatorArtifactId verbatim, but long artifact IDs are
173
- * routinely truncated or altered on echo. Because a mismatch is classified
174
- * output_invalid (a permanent error — no retry), a bad echo permanently
175
- * blocks the candidate from reaching the approval queue. Lineage is
176
- * runner-owned metadata (rc-6): the authoritative values come from the
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, authoritativeEvaluatorArtifactId) {
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
- { field: 'sourceEvaluatorArtifactId', authoritativeValue: authoritativeEvaluatorArtifactId },
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: [{ field: 'evaluatorArtifactId', authoritativeValue: authoritativeEvaluatorArtifactId }],
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
- authoritativeSourceEvaluatorArtifactId: authoritativeEvaluatorArtifactId,
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
- this.emitRolloutReviewerEvent('rollout_reviewer_no_dependencies', taskId, {});
210
- return { contextHash: 'empty', evaluatorArtifact: null, sourceEvaluatorArtifactId: null };
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 (!depTask)
215
- continue;
216
- if (depTask.taskKind !== 'evaluator')
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
- const artifactRef = firstArtifact.artifactId;
231
- this.emitRolloutReviewerEvent('rollout_reviewer_evaluator_dep_selected', taskId, {
232
- depTaskId: depId,
233
- artifactId: firstArtifact.artifactId,
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([artifactRef]),
237
- evaluatorArtifact: firstArtifact.contentJson,
238
- sourceEvaluatorArtifactId: firstArtifact.artifactId,
292
+ contextHash: RolloutReviewerRunner.hashContextRefs([firstArtifact.artifactId]),
293
+ sourceArtifact: firstArtifact.contentJson,
294
+ sourceArtifactId: firstArtifact.artifactId,
295
+ reviewMode,
239
296
  };
240
297
  }
241
298
  }
242
- this.emitRolloutReviewerEvent('rollout_reviewer_no_evaluator_artifact', taskId, {});
243
- return { contextHash: 'empty', evaluatorArtifact: null, sourceEvaluatorArtifactId: null };
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 parsedEvaluatorArtifact = null;
265
- if (params.evaluatorArtifact) {
326
+ let parsedSourceArtifact = null;
327
+ if (params.sourceArtifact) {
266
328
  try {
267
- parsedEvaluatorArtifact = JSON.parse(params.evaluatorArtifact);
329
+ parsedSourceArtifact = JSON.parse(params.sourceArtifact);
268
330
  }
269
331
  catch {
270
- parsedEvaluatorArtifact = params.evaluatorArtifact;
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
- evaluatorArtifact: parsedEvaluatorArtifact,
278
- sourceEvaluatorArtifactId: params.sourceEvaluatorArtifactId,
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
- sourceEvaluatorArtifactId: output.sourceEvaluatorArtifactId,
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
- sourceEvaluatorArtifactId: output.sourceEvaluatorArtifactId,
1070
- channel: hydratePITaskRecord(leasedTask)?.channel,
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 vr = await this.validator.validate(candidate, taskId, echoedArtifactId);
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
  }