@principles/core 1.270.5 → 1.272.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/runtime-v2/__tests__/admission-gate.test.js +23 -6
- package/dist/runtime-v2/__tests__/admission-gate.test.js.map +1 -1
- package/dist/runtime-v2/__tests__/adversarial-loop.test.js +22 -0
- package/dist/runtime-v2/__tests__/adversarial-loop.test.js.map +1 -1
- package/dist/runtime-v2/__tests__/context-payload-validation.test.js +1 -1
- package/dist/runtime-v2/__tests__/context-payload-validation.test.js.map +1 -1
- package/dist/runtime-v2/__tests__/evaluator-repair-loop.test.js +20 -0
- package/dist/runtime-v2/__tests__/evaluator-repair-loop.test.js.map +1 -1
- package/dist/runtime-v2/__tests__/evidence-guards.test.js +2 -2
- package/dist/runtime-v2/__tests__/evidence-guards.test.js.map +1 -1
- package/dist/runtime-v2/__tests__/golden-path-diagnostician-e2e.test.js +1 -1
- package/dist/runtime-v2/__tests__/golden-path-diagnostician-e2e.test.js.map +1 -1
- package/dist/runtime-v2/__tests__/governance-experience.test.js +19 -0
- package/dist/runtime-v2/__tests__/governance-experience.test.js.map +1 -1
- package/dist/runtime-v2/__tests__/pain-evidence-contract.test.js +3 -3
- package/dist/runtime-v2/__tests__/pain-evidence-contract.test.js.map +1 -1
- package/dist/runtime-v2/__tests__/pain-signal-bridge-result-shaping.test.js +1 -1
- package/dist/runtime-v2/__tests__/pain-signal-bridge-result-shaping.test.js.map +1 -1
- package/dist/runtime-v2/__tests__/rollout-reviewer-verdict-paths.test.js +26 -5
- package/dist/runtime-v2/__tests__/rollout-reviewer-verdict-paths.test.js.map +1 -1
- package/dist/runtime-v2/__tests__/rollout-rule-candidate-contract.test.d.ts +2 -0
- package/dist/runtime-v2/__tests__/rollout-rule-candidate-contract.test.d.ts.map +1 -0
- package/dist/runtime-v2/__tests__/rollout-rule-candidate-contract.test.js +243 -0
- package/dist/runtime-v2/__tests__/rollout-rule-candidate-contract.test.js.map +1 -0
- package/dist/runtime-v2/admission-gate.d.ts +8 -1
- package/dist/runtime-v2/admission-gate.d.ts.map +1 -1
- package/dist/runtime-v2/admission-gate.js +14 -0
- package/dist/runtime-v2/admission-gate.js.map +1 -1
- package/dist/runtime-v2/context-payload.d.ts +9 -3
- package/dist/runtime-v2/context-payload.d.ts.map +1 -1
- package/dist/runtime-v2/context-payload.js +3 -1
- package/dist/runtime-v2/context-payload.js.map +1 -1
- package/dist/runtime-v2/evidence-triage/__tests__/observation-resolver.test.js +2 -2
- package/dist/runtime-v2/evidence-triage/__tests__/observation-resolver.test.js.map +1 -1
- package/dist/runtime-v2/evidence-triage/observation-resolver.d.ts +1 -1
- package/dist/runtime-v2/evidence-triage/observation-resolver.d.ts.map +1 -1
- package/dist/runtime-v2/evidence-triage/observation-resolver.js +2 -2
- package/dist/runtime-v2/evidence-triage/observation-resolver.js.map +1 -1
- package/dist/runtime-v2/evidence-triage/types.d.ts +1 -1
- package/dist/runtime-v2/evidence-triage/types.d.ts.map +1 -1
- package/dist/runtime-v2/evolution/evolution-types.d.ts +8 -4
- package/dist/runtime-v2/evolution/evolution-types.d.ts.map +1 -1
- package/dist/runtime-v2/evolution/evolution-types.js +3 -1
- package/dist/runtime-v2/evolution/evolution-types.js.map +1 -1
- package/dist/runtime-v2/governance-experience-contract.d.ts +16 -16
- package/dist/runtime-v2/governance-experience-contract.d.ts.map +1 -1
- package/dist/runtime-v2/governance-experience-contract.js +4 -1
- package/dist/runtime-v2/governance-experience-contract.js.map +1 -1
- package/dist/runtime-v2/governance-experience.d.ts.map +1 -1
- package/dist/runtime-v2/governance-experience.js +30 -8
- package/dist/runtime-v2/governance-experience.js.map +1 -1
- package/dist/runtime-v2/index.d.ts +11 -1
- package/dist/runtime-v2/index.d.ts.map +1 -1
- package/dist/runtime-v2/index.js +7 -0
- package/dist/runtime-v2/index.js.map +1 -1
- package/dist/runtime-v2/internalization/__tests__/artificer-code-bearing.test.d.ts +2 -0
- package/dist/runtime-v2/internalization/__tests__/artificer-code-bearing.test.d.ts.map +1 -0
- package/dist/runtime-v2/internalization/__tests__/artificer-code-bearing.test.js +114 -0
- package/dist/runtime-v2/internalization/__tests__/artificer-code-bearing.test.js.map +1 -0
- package/dist/runtime-v2/internalization/__tests__/evaluator-gate-authority.test.d.ts +2 -0
- package/dist/runtime-v2/internalization/__tests__/evaluator-gate-authority.test.d.ts.map +1 -0
- package/dist/runtime-v2/internalization/__tests__/evaluator-gate-authority.test.js +256 -0
- package/dist/runtime-v2/internalization/__tests__/evaluator-gate-authority.test.js.map +1 -0
- package/dist/runtime-v2/internalization/__tests__/evaluator-prompt-builder.test.js +1 -1
- package/dist/runtime-v2/internalization/__tests__/internalization-transition-decision.test.js +15 -14
- package/dist/runtime-v2/internalization/__tests__/internalization-transition-decision.test.js.map +1 -1
- package/dist/runtime-v2/internalization/__tests__/owner-decision-architecture.test.d.ts +2 -0
- package/dist/runtime-v2/internalization/__tests__/owner-decision-architecture.test.d.ts.map +1 -0
- package/dist/runtime-v2/internalization/__tests__/owner-decision-architecture.test.js +56 -0
- package/dist/runtime-v2/internalization/__tests__/owner-decision-architecture.test.js.map +1 -0
- package/dist/runtime-v2/internalization/__tests__/owner-decision-review.test.d.ts +2 -0
- package/dist/runtime-v2/internalization/__tests__/owner-decision-review.test.d.ts.map +1 -0
- package/dist/runtime-v2/internalization/__tests__/owner-decision-review.test.js +229 -0
- package/dist/runtime-v2/internalization/__tests__/owner-decision-review.test.js.map +1 -0
- package/dist/runtime-v2/internalization/__tests__/owner-decision.test.d.ts +2 -0
- package/dist/runtime-v2/internalization/__tests__/owner-decision.test.d.ts.map +1 -0
- package/dist/runtime-v2/internalization/__tests__/owner-decision.test.js +1054 -0
- package/dist/runtime-v2/internalization/__tests__/owner-decision.test.js.map +1 -0
- package/dist/runtime-v2/internalization/__tests__/owner-override-resume.test.d.ts +2 -0
- package/dist/runtime-v2/internalization/__tests__/owner-override-resume.test.d.ts.map +1 -0
- package/dist/runtime-v2/internalization/__tests__/owner-override-resume.test.js +328 -0
- package/dist/runtime-v2/internalization/__tests__/owner-override-resume.test.js.map +1 -0
- package/dist/runtime-v2/internalization/__tests__/rule-activation-contract.test.d.ts +2 -0
- package/dist/runtime-v2/internalization/__tests__/rule-activation-contract.test.d.ts.map +1 -0
- package/dist/runtime-v2/internalization/__tests__/rule-activation-contract.test.js +187 -0
- package/dist/runtime-v2/internalization/__tests__/rule-activation-contract.test.js.map +1 -0
- package/dist/runtime-v2/internalization/artificer-code-bearing.d.ts +39 -0
- package/dist/runtime-v2/internalization/artificer-code-bearing.d.ts.map +1 -0
- package/dist/runtime-v2/internalization/artificer-code-bearing.js +33 -0
- package/dist/runtime-v2/internalization/artificer-code-bearing.js.map +1 -0
- package/dist/runtime-v2/internalization/evaluator-output.d.ts +63 -2
- package/dist/runtime-v2/internalization/evaluator-output.d.ts.map +1 -1
- package/dist/runtime-v2/internalization/evaluator-output.js +139 -2
- package/dist/runtime-v2/internalization/evaluator-output.js.map +1 -1
- package/dist/runtime-v2/internalization/evaluator-prompt-builder.d.ts +54 -2
- package/dist/runtime-v2/internalization/evaluator-prompt-builder.d.ts.map +1 -1
- package/dist/runtime-v2/internalization/evaluator-prompt-builder.js +49 -2
- package/dist/runtime-v2/internalization/evaluator-prompt-builder.js.map +1 -1
- package/dist/runtime-v2/internalization/evaluator-runner.d.ts +45 -8
- package/dist/runtime-v2/internalization/evaluator-runner.d.ts.map +1 -1
- package/dist/runtime-v2/internalization/evaluator-runner.js +465 -43
- package/dist/runtime-v2/internalization/evaluator-runner.js.map +1 -1
- package/dist/runtime-v2/internalization/index.d.ts +2 -0
- package/dist/runtime-v2/internalization/index.d.ts.map +1 -1
- package/dist/runtime-v2/internalization/index.js +1 -0
- package/dist/runtime-v2/internalization/index.js.map +1 -1
- package/dist/runtime-v2/internalization/internalization-orchestrator.d.ts.map +1 -1
- package/dist/runtime-v2/internalization/internalization-orchestrator.js +10 -54
- package/dist/runtime-v2/internalization/internalization-orchestrator.js.map +1 -1
- package/dist/runtime-v2/internalization/internalization-transition-decision.d.ts +7 -1
- package/dist/runtime-v2/internalization/internalization-transition-decision.d.ts.map +1 -1
- package/dist/runtime-v2/internalization/internalization-transition-decision.js +9 -2
- package/dist/runtime-v2/internalization/internalization-transition-decision.js.map +1 -1
- package/dist/runtime-v2/internalization/owner-decision-review.d.ts +98 -0
- package/dist/runtime-v2/internalization/owner-decision-review.d.ts.map +1 -0
- package/dist/runtime-v2/internalization/owner-decision-review.js +287 -0
- package/dist/runtime-v2/internalization/owner-decision-review.js.map +1 -0
- package/dist/runtime-v2/internalization/owner-resolution-service.d.ts +100 -0
- package/dist/runtime-v2/internalization/owner-resolution-service.d.ts.map +1 -0
- package/dist/runtime-v2/internalization/owner-resolution-service.js +385 -0
- package/dist/runtime-v2/internalization/owner-resolution-service.js.map +1 -0
- package/dist/runtime-v2/internalization/owner-retry.d.ts +12 -0
- package/dist/runtime-v2/internalization/owner-retry.d.ts.map +1 -1
- package/dist/runtime-v2/internalization/owner-retry.js +23 -0
- package/dist/runtime-v2/internalization/owner-retry.js.map +1 -1
- package/dist/runtime-v2/internalization/owner-review.d.ts +199 -0
- package/dist/runtime-v2/internalization/owner-review.d.ts.map +1 -0
- package/dist/runtime-v2/internalization/owner-review.js +470 -0
- package/dist/runtime-v2/internalization/owner-review.js.map +1 -0
- package/dist/runtime-v2/internalization/peer-runner-contracts.d.ts +5 -1
- package/dist/runtime-v2/internalization/peer-runner-contracts.d.ts.map +1 -1
- package/dist/runtime-v2/internalization/peer-runner-contracts.js.map +1 -1
- package/dist/runtime-v2/internalization/pitask-metadata.d.ts +84 -0
- package/dist/runtime-v2/internalization/pitask-metadata.d.ts.map +1 -1
- package/dist/runtime-v2/internalization/pitask-metadata.js +166 -0
- package/dist/runtime-v2/internalization/pitask-metadata.js.map +1 -1
- package/dist/runtime-v2/internalization/revision-reopen.d.ts +56 -0
- package/dist/runtime-v2/internalization/revision-reopen.d.ts.map +1 -0
- package/dist/runtime-v2/internalization/revision-reopen.js +144 -0
- package/dist/runtime-v2/internalization/revision-reopen.js.map +1 -0
- package/dist/runtime-v2/internalization/rollout-reviewer-runner.d.ts +25 -0
- package/dist/runtime-v2/internalization/rollout-reviewer-runner.d.ts.map +1 -1
- package/dist/runtime-v2/internalization/rollout-reviewer-runner.js +202 -23
- package/dist/runtime-v2/internalization/rollout-reviewer-runner.js.map +1 -1
- package/dist/runtime-v2/internalization/rule-activation-contract.d.ts +35 -0
- package/dist/runtime-v2/internalization/rule-activation-contract.d.ts.map +1 -0
- package/dist/runtime-v2/internalization/rule-activation-contract.js +36 -0
- package/dist/runtime-v2/internalization/rule-activation-contract.js.map +1 -0
- package/dist/runtime-v2/pain-signal-bridge.d.ts +2 -0
- package/dist/runtime-v2/pain-signal-bridge.d.ts.map +1 -1
- package/dist/runtime-v2/pain-signal-bridge.js +4 -3
- package/dist/runtime-v2/pain-signal-bridge.js.map +1 -1
- package/dist/runtime-v2/pain-to-principle-service.d.ts +1 -0
- package/dist/runtime-v2/pain-to-principle-service.d.ts.map +1 -1
- package/dist/runtime-v2/pain-to-principle-service.js +1 -0
- package/dist/runtime-v2/pain-to-principle-service.js.map +1 -1
- package/dist/runtime-v2/runner/__tests__/pain-signal-bridge-admission.test.js +6 -6
- package/dist/runtime-v2/runner/__tests__/pain-signal-bridge-admission.test.js.map +1 -1
- package/dist/runtime-v2/store/context/sqlite-context-assembler.d.ts.map +1 -1
- package/dist/runtime-v2/store/context/sqlite-context-assembler.js +25 -5
- package/dist/runtime-v2/store/context/sqlite-context-assembler.js.map +1 -1
- package/dist/runtime-v2/store/context/sqlite-context-assembler.test.js +39 -9
- package/dist/runtime-v2/store/context/sqlite-context-assembler.test.js.map +1 -1
- package/dist/runtime-v2/store/runtime-state-manager.d.ts +12 -0
- package/dist/runtime-v2/store/runtime-state-manager.d.ts.map +1 -1
- package/dist/runtime-v2/store/runtime-state-manager.js +17 -0
- package/dist/runtime-v2/store/runtime-state-manager.js.map +1 -1
- package/dist/runtime-v2/store/task/__tests__/sqlite-task-store-cas.test.d.ts +2 -0
- package/dist/runtime-v2/store/task/__tests__/sqlite-task-store-cas.test.d.ts.map +1 -0
- package/dist/runtime-v2/store/task/__tests__/sqlite-task-store-cas.test.js +114 -0
- package/dist/runtime-v2/store/task/__tests__/sqlite-task-store-cas.test.js.map +1 -0
- package/dist/runtime-v2/store/task/memory-task-store.d.ts +1 -0
- package/dist/runtime-v2/store/task/memory-task-store.d.ts.map +1 -1
- package/dist/runtime-v2/store/task/memory-task-store.js +8 -0
- package/dist/runtime-v2/store/task/memory-task-store.js.map +1 -1
- package/dist/runtime-v2/store/task/sqlite-task-store.d.ts +20 -0
- package/dist/runtime-v2/store/task/sqlite-task-store.d.ts.map +1 -1
- package/dist/runtime-v2/store/task/sqlite-task-store.js +81 -0
- package/dist/runtime-v2/store/task/sqlite-task-store.js.map +1 -1
- package/dist/runtime-v2/store/task/task-store.d.ts +9 -0
- package/dist/runtime-v2/store/task/task-store.d.ts.map +1 -1
- package/dist/runtime-v2/store/workspace-isolation.test.js +1 -0
- package/dist/runtime-v2/store/workspace-isolation.test.js.map +1 -1
- package/dist/runtime-v2/task-status.d.ts +2 -1
- package/dist/runtime-v2/task-status.d.ts.map +1 -1
- package/dist/runtime-v2/task-status.js +5 -1
- package/dist/runtime-v2/task-status.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { isEvaluatorOutputV2 } from './evaluator-output.js';
|
|
2
|
+
// PRI-634 A2 (authority migration): gate necessity derives from the durable
|
|
3
|
+
// Artificer artifact, not from optional LLM output shape.
|
|
4
|
+
import { assessArtificerCodeBearing } from './artificer-code-bearing.js';
|
|
2
5
|
import { PDRuntimeError, isPDErrorCategory } from '../error-categories.js';
|
|
3
6
|
import { hydratePITaskRecord, createPITaskDiagnosticJson, mergePITaskMetadata } from './pitask-metadata.js';
|
|
4
|
-
import {
|
|
7
|
+
import { HUMAN_REVIEW_REASON, planOwnerVerdictOverrideResume, markOwnerResolutionApplied, computeArtifactContentHash, } from './owner-review.js';
|
|
8
|
+
import { EvaluatorPromptBuilder, deriveRequirementLedger } from './evaluator-prompt-builder.js';
|
|
5
9
|
import { reconcileLineageEcho } from './peer-runner-contracts.js';
|
|
6
10
|
import { BasePeerRunner } from '../runner/base-peer-runner.js';
|
|
7
11
|
import { EVALUATOR_STAGE1_MANIFEST, EVALUATOR_STAGE2_MANIFEST } from './context-manifests.js';
|
|
@@ -17,6 +21,31 @@ import { canonicalizeToolKind } from './rule-context-v2.js';
|
|
|
17
21
|
function isRecordValue(value) {
|
|
18
22
|
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
19
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* PRI-634: resolve a golden-trace case's target path across the host tool
|
|
26
|
+
* schemas actually observed on real chains.
|
|
27
|
+
*
|
|
28
|
+
* Golden-trace params are echoed verbatim from the host trajectory
|
|
29
|
+
* (behavior-example-pack-assembler `params: parsedParams`), so the field name
|
|
30
|
+
* follows whatever the host tool used — the dominant write-tool schema spells
|
|
31
|
+
* it `file_path` (OpenClaw / Claude Code), while PRI-485's v2 generator only
|
|
32
|
+
* read `path`. Reading a single spelling made v2 auto-generation structurally
|
|
33
|
+
* unreachable: the merged adversarial set stayed empty, the deterministic gate
|
|
34
|
+
* never ran, and an LLM-declared `adversarialResult.passed = true` silently
|
|
35
|
+
* stood in for a real replay (chain 48371236).
|
|
36
|
+
*
|
|
37
|
+
* Returns null when neither spelling yields a non-empty string.
|
|
38
|
+
*/
|
|
39
|
+
function resolveCasePathParam(params) {
|
|
40
|
+
if (!isRecordValue(params))
|
|
41
|
+
return null;
|
|
42
|
+
for (const key of ['path', 'file_path']) {
|
|
43
|
+
const raw = params[key];
|
|
44
|
+
if (typeof raw === 'string' && raw.trim() !== '')
|
|
45
|
+
return raw;
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
20
49
|
/**
|
|
21
50
|
* Extract scribeArtifactId from an artificer artifact's contentJson (PRD Decision 12).
|
|
22
51
|
* The contentJson is untrusted — parsed defensively with type guards, never as-cast
|
|
@@ -132,6 +161,8 @@ export class EvaluatorRunner extends BasePeerRunner {
|
|
|
132
161
|
* Null when the deps did not inject the seeder (= repair seeding unavailable).
|
|
133
162
|
*/
|
|
134
163
|
repairTaskSeeder;
|
|
164
|
+
/** PRI-630: runtime-authoritative tool facts; null = catalog unavailable (degraded rule in prompt) */
|
|
165
|
+
hostToolCatalog;
|
|
135
166
|
constructor(deps, options) {
|
|
136
167
|
super(deps, options, {
|
|
137
168
|
runnerName: 'evaluator',
|
|
@@ -143,6 +174,7 @@ export class EvaluatorRunner extends BasePeerRunner {
|
|
|
143
174
|
this.gateDeps = options.gateDeps ?? null;
|
|
144
175
|
this.repairLoopEnabledResolver = deps.isRepairLoopEnabled ?? null;
|
|
145
176
|
this.repairTaskSeeder = deps.seedArtificerRepairTask ?? null;
|
|
177
|
+
this.hostToolCatalog = options.hostToolCatalog ?? null;
|
|
146
178
|
}
|
|
147
179
|
/**
|
|
148
180
|
* Returns true iff the evaluator→artificer repair loop feature flag is on.
|
|
@@ -179,6 +211,8 @@ export class EvaluatorRunner extends BasePeerRunner {
|
|
|
179
211
|
});
|
|
180
212
|
continue;
|
|
181
213
|
}
|
|
214
|
+
const depPi = hydratePITaskRecord(depTask);
|
|
215
|
+
const dependencyRepairPayload = depPi?.repairPayload;
|
|
182
216
|
const artifacts = await this.artifactStore.listBySourceTaskId(depId);
|
|
183
217
|
if (artifacts.length > 0) {
|
|
184
218
|
const [firstArtifact] = artifacts;
|
|
@@ -211,12 +245,19 @@ export class EvaluatorRunner extends BasePeerRunner {
|
|
|
211
245
|
const contextRefs = scribeContent && scribeRef
|
|
212
246
|
? [artifactRef, scribeRef]
|
|
213
247
|
: [artifactRef];
|
|
248
|
+
// PRI-630: 修复轮 (repairPayload 存在) 时解析上轮评估上下文
|
|
249
|
+
let previousEvaluation;
|
|
250
|
+
if (dependencyRepairPayload) {
|
|
251
|
+
previousEvaluation = await this.resolvePreviousEvaluation(taskId, dependencyRepairPayload, firstArtifact.contentJson);
|
|
252
|
+
}
|
|
214
253
|
return {
|
|
215
254
|
contextHash: BasePeerRunner.hashContextRefs(contextRefs),
|
|
216
255
|
artificerArtifact: firstArtifact.contentJson,
|
|
217
256
|
sourceArtificerArtifactId: firstArtifact.artifactId,
|
|
218
257
|
scribeArtifact: scribeContent,
|
|
219
258
|
sourceScribeArtifactId: scribeRef,
|
|
259
|
+
...(dependencyRepairPayload !== undefined ? { dependencyRepairPayload } : {}),
|
|
260
|
+
...(previousEvaluation !== undefined ? { previousEvaluation } : {}),
|
|
220
261
|
};
|
|
221
262
|
}
|
|
222
263
|
}
|
|
@@ -312,6 +353,116 @@ export class EvaluatorRunner extends BasePeerRunner {
|
|
|
312
353
|
* Build the evaluator prompt with the given manifest's resolved context.
|
|
313
354
|
* Shared by single-stage and two-stage paths.
|
|
314
355
|
*/
|
|
356
|
+
/**
|
|
357
|
+
* PRI-630 收敛契约 (SPEC §18.1): 解析上轮评估上下文 — 从 dependency
|
|
358
|
+
* artificer 的 repairPayload.sourceEvaluatorTaskId 找到上轮 evaluator,
|
|
359
|
+
* 读取其最近 principle artifact,按 rc-1/rc-2 守卫解析 evaluation 字段。
|
|
360
|
+
* requirements 用稳定 id (req-1..N, 上轮 requiredChanges 顺序)。
|
|
361
|
+
* 解析失败 → 结构化降级事件 + undefined (保持既有行为,可观测)。
|
|
362
|
+
*/
|
|
363
|
+
async resolvePreviousEvaluation(taskId, repairPayload, repairArtifactContentJson) {
|
|
364
|
+
const priorTaskId = repairPayload.sourceEvaluatorTaskId;
|
|
365
|
+
const priorArtifactJson = await this.artifactStore
|
|
366
|
+
.listBySourceTaskId(priorTaskId)
|
|
367
|
+
.then((artifacts) => {
|
|
368
|
+
let latest = null;
|
|
369
|
+
for (const a of artifacts) {
|
|
370
|
+
if (a.artifactKind !== 'principle')
|
|
371
|
+
continue;
|
|
372
|
+
if (!latest || a.updatedAt > latest.updatedAt)
|
|
373
|
+
latest = a;
|
|
374
|
+
}
|
|
375
|
+
return latest?.contentJson ?? null;
|
|
376
|
+
})
|
|
377
|
+
.catch(() => null);
|
|
378
|
+
if (priorArtifactJson === null) {
|
|
379
|
+
this.emitEvent('previous_evaluation_context_degraded', taskId, {
|
|
380
|
+
priorEvaluatorTaskId: priorTaskId,
|
|
381
|
+
reason: 'prior_evaluation_artifact_unavailable',
|
|
382
|
+
repairIteration: repairPayload.repairIteration,
|
|
383
|
+
});
|
|
384
|
+
return undefined;
|
|
385
|
+
}
|
|
386
|
+
let parsed;
|
|
387
|
+
try {
|
|
388
|
+
parsed = JSON.parse(priorArtifactJson);
|
|
389
|
+
}
|
|
390
|
+
catch {
|
|
391
|
+
this.emitEvent('previous_evaluation_context_degraded', taskId, {
|
|
392
|
+
priorEvaluatorTaskId: priorTaskId,
|
|
393
|
+
reason: 'prior_evaluation_artifact_unparseable',
|
|
394
|
+
});
|
|
395
|
+
return undefined;
|
|
396
|
+
}
|
|
397
|
+
if (typeof parsed !== 'object' || parsed === null)
|
|
398
|
+
return undefined;
|
|
399
|
+
// runtime-contract-exempt: ERR-001 object-guarded unknown property extraction; typeof guard follows immediately
|
|
400
|
+
const { evaluation } = parsed;
|
|
401
|
+
if (typeof evaluation !== 'object' || evaluation === null) {
|
|
402
|
+
this.emitEvent('previous_evaluation_context_degraded', taskId, {
|
|
403
|
+
priorEvaluatorTaskId: priorTaskId,
|
|
404
|
+
reason: 'prior_evaluation_shape_invalid',
|
|
405
|
+
});
|
|
406
|
+
return undefined;
|
|
407
|
+
}
|
|
408
|
+
const ev = evaluation;
|
|
409
|
+
const decision = typeof ev.decision === 'string' ? ev.decision : 'needs_revision';
|
|
410
|
+
const score = typeof ev.score === 'number' && Number.isFinite(ev.score) ? Math.min(1, Math.max(0, ev.score)) : 0;
|
|
411
|
+
const toStringArray = (v, cap) => {
|
|
412
|
+
if (!Array.isArray(v))
|
|
413
|
+
return [];
|
|
414
|
+
const out = [];
|
|
415
|
+
for (const item of v.slice(0, cap)) {
|
|
416
|
+
if (typeof item === 'string' && item.trim() !== '')
|
|
417
|
+
out.push(item.slice(0, 500));
|
|
418
|
+
}
|
|
419
|
+
return out;
|
|
420
|
+
};
|
|
421
|
+
const concerns = toStringArray(ev.concerns, 10);
|
|
422
|
+
const requiredChanges = toStringArray(ev.requiredChanges, 10);
|
|
423
|
+
// PRI-630 P1 评审修复: 需求身份跨轮稳定 — 上轮 echo 的 requirementLedger
|
|
424
|
+
// (若有) 中 still_open/regressed 条目保留原 id 与原 statement;本轮
|
|
425
|
+
// requiredChanges 的非重述项作为新需求从最大序号递增。无 ledger 时
|
|
426
|
+
// (首个修复轮) 退回顺序编号。
|
|
427
|
+
let prevLedger;
|
|
428
|
+
if (Array.isArray(ev.requirementLedger)) {
|
|
429
|
+
prevLedger = [];
|
|
430
|
+
for (const entry of ev.requirementLedger) {
|
|
431
|
+
if (entry === null || typeof entry !== 'object')
|
|
432
|
+
continue;
|
|
433
|
+
const rec = entry;
|
|
434
|
+
if (typeof rec.id !== 'string' || typeof rec.statement !== 'string')
|
|
435
|
+
continue;
|
|
436
|
+
if (rec.status !== 'resolved' && rec.status !== 'still_open' && rec.status !== 'regressed')
|
|
437
|
+
continue;
|
|
438
|
+
prevLedger.push({ id: rec.id, statement: rec.statement, status: rec.status });
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
const requirements = deriveRequirementLedger(prevLedger, requiredChanges);
|
|
442
|
+
// 修复说明: 当前 (被修复) artificer artifact 的声明性摘要 — 有界提取
|
|
443
|
+
let repairSummary;
|
|
444
|
+
try {
|
|
445
|
+
const art = JSON.parse(repairArtifactContentJson);
|
|
446
|
+
if (typeof art === 'object' && art !== null) {
|
|
447
|
+
// runtime-contract-exempt: ERR-001 object-guarded unknown property extraction; typeof guard follows immediately
|
|
448
|
+
const { implementationSummary } = art;
|
|
449
|
+
if (typeof implementationSummary === 'string' && implementationSummary.trim() !== '') {
|
|
450
|
+
repairSummary = implementationSummary.slice(0, 800);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
catch {
|
|
455
|
+
repairSummary = undefined;
|
|
456
|
+
}
|
|
457
|
+
return {
|
|
458
|
+
decision,
|
|
459
|
+
score,
|
|
460
|
+
concerns,
|
|
461
|
+
requirements,
|
|
462
|
+
repairIteration: repairPayload.repairIteration,
|
|
463
|
+
...(repairSummary !== undefined ? { repairSummary } : {}),
|
|
464
|
+
};
|
|
465
|
+
}
|
|
315
466
|
buildEvaluatorPrompt(taskId, context, manifest) {
|
|
316
467
|
let parsedArtificerArtifact = null;
|
|
317
468
|
if (context.artificerArtifact) {
|
|
@@ -346,6 +497,8 @@ export class EvaluatorRunner extends BasePeerRunner {
|
|
|
346
497
|
artificerArtifact: parsedArtificerArtifact,
|
|
347
498
|
scribeArtifact: parsedScribeArtifact,
|
|
348
499
|
sourceArtificerArtifactId: context.sourceArtificerArtifactId ?? '',
|
|
500
|
+
previousEvaluation: context.previousEvaluation,
|
|
501
|
+
hostToolCatalog: this.hostToolCatalog ?? undefined,
|
|
349
502
|
});
|
|
350
503
|
return message;
|
|
351
504
|
}
|
|
@@ -362,7 +515,12 @@ export class EvaluatorRunner extends BasePeerRunner {
|
|
|
362
515
|
});
|
|
363
516
|
}
|
|
364
517
|
async validateOutput(output, taskId, context) {
|
|
365
|
-
|
|
518
|
+
// PRI-630 P1 评审修复: 修复轮把上轮 requirement ids 传入做完整覆盖校验
|
|
519
|
+
// (缺 priorRequirementStatuses 或漏 id → output_invalid),不再仅靠 prompt。
|
|
520
|
+
const convergence = context.previousEvaluation
|
|
521
|
+
? { expectedRequirements: context.previousEvaluation.requirements.map((r) => ({ id: r.id, statement: r.statement })) }
|
|
522
|
+
: undefined;
|
|
523
|
+
const result = await this.validator.validate(output, taskId, context.sourceArtificerArtifactId ?? undefined, convergence);
|
|
366
524
|
// Trust-boundary: validator is an injected dependency returning `string | undefined`
|
|
367
525
|
// for errorCategory. We must not `as`-cast; validate at runtime (ERR-001, ERR-005).
|
|
368
526
|
const rawCategory = result.errorCategory;
|
|
@@ -457,21 +615,64 @@ export class EvaluatorRunner extends BasePeerRunner {
|
|
|
457
615
|
});
|
|
458
616
|
}
|
|
459
617
|
// ── PRI-426: single-round adversarial sandbox replay ──
|
|
460
|
-
//
|
|
461
|
-
//
|
|
462
|
-
//
|
|
463
|
-
//
|
|
618
|
+
// PRI-634 A2 (authority migration): gate necessity is decided by the
|
|
619
|
+
// DURABLE Artificer artifact — assessArtificerCodeBearing() mirrors
|
|
620
|
+
// assembleRuleArtifact()'s static preconditions exactly — never by
|
|
621
|
+
// whether the LLM happened to emit optional V2 fields
|
|
622
|
+
// (isEvaluatorOutputV2). A V2-shaped output over a non-code-bearing
|
|
623
|
+
// Artificer keeps the legacy behavior (attempt replay, degrade with
|
|
624
|
+
// telemetry) so the old path stays observable.
|
|
464
625
|
//
|
|
465
626
|
// PRI-423 contract: adversarialCasesToGoldenTrace yields an all-negative
|
|
466
627
|
// trace. We MUST merge ≥1 positive case from the Artificer golden trace
|
|
467
628
|
// before replaying, otherwise the merged trace fails validateGoldenTrace.
|
|
468
629
|
//
|
|
469
|
-
//
|
|
470
|
-
// to adversarialResult.passed=false with a structured reason
|
|
471
|
-
// The principle artifact is already persisted, so
|
|
472
|
-
// remains available regardless of replay outcome
|
|
630
|
+
// The replay itself never throws into the caller — a sandbox/gate failure
|
|
631
|
+
// degrades to adversarialResult.passed=false with a structured reason
|
|
632
|
+
// (ERR-018). The principle artifact is already persisted, so
|
|
633
|
+
// prompt-channel fallback remains available regardless of replay outcome
|
|
634
|
+
// (PRD Decision 11d §h). The ONE deliberate exception is the R2 wiring
|
|
635
|
+
// guard below: a code-bearing artifact without gateDeps fails loud
|
|
636
|
+
// (capability_missing, permanent) instead of succeeding un-gated.
|
|
473
637
|
let finalOutput = output;
|
|
474
|
-
|
|
638
|
+
const gateAssessment = assessArtificerCodeBearing(context.artificerArtifact);
|
|
639
|
+
const outputWantsGate = isEvaluatorOutputV2(output);
|
|
640
|
+
const evaluatorDecision = output.evaluation.decision;
|
|
641
|
+
if (evaluatorDecision !== 'approved') {
|
|
642
|
+
// Passive review short-circuit: the LLM emits needs_revision/rejected
|
|
643
|
+
// when its review fails — no gate needed. Previously silent inside
|
|
644
|
+
// runAdversarialReplay; now observable at the gate decision point
|
|
645
|
+
// (Runtime Contract Rule 9).
|
|
646
|
+
if (gateAssessment.codeBearing || outputWantsGate) {
|
|
647
|
+
this.emitEvent('adversarial_replay_skipped', taskId, {
|
|
648
|
+
runId,
|
|
649
|
+
reason: 'evaluation_not_approved',
|
|
650
|
+
nextAction: 'repair_loop_or_next_review_round',
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
else if (!this.gateDeps) {
|
|
655
|
+
this.emitEvent('adversarial_replay_skipped', taskId, {
|
|
656
|
+
runId,
|
|
657
|
+
reason: 'gate_deps_not_injected',
|
|
658
|
+
nextAction: 'wire_gateDeps_createProductionGateDeps_into_evaluator_runner_assembly',
|
|
659
|
+
});
|
|
660
|
+
if (gateAssessment.codeBearing) {
|
|
661
|
+
// PRI-634 R2 (wiring regression guard): a code-bearing Artificer
|
|
662
|
+
// artifact REQUIRES the deterministic gate. Proceeding would yield
|
|
663
|
+
// succeeded + adversarialResult=null — the exact state that broke
|
|
664
|
+
// chain 48371236. capability_missing is a permanent error: the task
|
|
665
|
+
// fails loud (markTaskFailed) instead of retry-burning LLM budget —
|
|
666
|
+
// a missing gateDeps is an assembly defect, not a transient fault.
|
|
667
|
+
return this.retryOrFail({
|
|
668
|
+
taskId,
|
|
669
|
+
task,
|
|
670
|
+
errorCategory: 'capability_missing',
|
|
671
|
+
failureReason: `PRI-634 R2: Artificer artifact ${context.sourceArtificerArtifactId ?? '(unknown)'} is code-bearing but EvaluatorRunner was assembled without gateDeps — deterministic adversarial replay cannot run. Fix: inject gateDeps: createProductionGateDeps() into the EvaluatorRunner assembly.`,
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
else if (gateAssessment.codeBearing || outputWantsGate) {
|
|
475
676
|
const replayOutcome = await this.runAdversarialReplay(output, taskId, runId, context);
|
|
476
677
|
if (replayOutcome.updatedOutput) {
|
|
477
678
|
finalOutput = replayOutcome.updatedOutput;
|
|
@@ -503,6 +704,30 @@ export class EvaluatorRunner extends BasePeerRunner {
|
|
|
503
704
|
});
|
|
504
705
|
}
|
|
505
706
|
}
|
|
707
|
+
else if (gateAssessment.codeBearing) {
|
|
708
|
+
// ── PRI-634 R3 (terminal-state invariant) ──
|
|
709
|
+
// A code-bearing Artificer artifact that reached `approved` MUST leave
|
|
710
|
+
// this block carrying an adversarialResult. Anything else reproduces
|
|
711
|
+
// the chain-48371236 terminal state — approved with the deterministic
|
|
712
|
+
// gate never executed, adversarialResult=null, and therefore no
|
|
713
|
+
// pi-rule-* downstream — regardless of WHICH cause prevented the
|
|
714
|
+
// replay (zero adversarial cases, no positive case, unparseable
|
|
715
|
+
// artifact, conversion drift…). Telemetry is not recovery: a task that
|
|
716
|
+
// ends `succeeded` here is indistinguishable from the original defect.
|
|
717
|
+
//
|
|
718
|
+
// errorCategory is `input_invalid` (already listed in
|
|
719
|
+
// permanentErrorCategories) rather than `capability_missing`: the gate
|
|
720
|
+
// IS wired correctly (R2 owns the wiring case) — the upstream
|
|
721
|
+
// Artificer content simply cannot be turned into gate input. Permanent
|
|
722
|
+
// because retrying the LLM cannot make the Artificer emit the missing
|
|
723
|
+
// affectedTools / positive-case path.
|
|
724
|
+
return this.retryOrFail({
|
|
725
|
+
taskId,
|
|
726
|
+
task,
|
|
727
|
+
errorCategory: 'input_invalid',
|
|
728
|
+
failureReason: `PRI-634 R3: Artificer artifact ${context.sourceArtificerArtifactId ?? '(unknown)'} is code-bearing and the evaluator approved it, but the deterministic adversarial replay did not run (reason: ${replayOutcome.skipReason ?? 'unknown'}). Refusing to report succeeded with adversarialResult=null — that is the chain-48371236 terminal state. Fix: have the Artificer emit affectedTools plus at least one positive golden-trace case carrying a path, or supply LLM adversarialCases.`,
|
|
729
|
+
});
|
|
730
|
+
}
|
|
506
731
|
}
|
|
507
732
|
// ── P0 (verdict drift): verdict + completion intent 原子落库 ──
|
|
508
733
|
// 必须先于一切治理 side effect (validate bearer / seed repair / rule
|
|
@@ -568,7 +793,7 @@ export class EvaluatorRunner extends BasePeerRunner {
|
|
|
568
793
|
*/
|
|
569
794
|
async applyEvaluatorDecisionEffects(args) {
|
|
570
795
|
const { taskId, runId, finalOutput, task, artifactId, contextHash, sourceArtificerArtifactId } = args;
|
|
571
|
-
const
|
|
796
|
+
const decision = args.decisionOverride ?? finalOutput.evaluation.decision;
|
|
572
797
|
// ── Evaluator-specific: validate principle-bearing Scribe artifact ──
|
|
573
798
|
// This is the critical business logic: approved evaluator must validate
|
|
574
799
|
// the Scribe principle artifact, NOT the Artificer plan artifact.
|
|
@@ -632,6 +857,10 @@ export class EvaluatorRunner extends BasePeerRunner {
|
|
|
632
857
|
if (decision === 'needs_revision' && this.isRepairLoopEnabled()) {
|
|
633
858
|
const repairOutcome = await this.maybeSeedArtificerRepair(taskId, { runId, output: finalOutput, sourceArtificerArtifactId });
|
|
634
859
|
if (repairOutcome.kind === 'max_iterations_reached') {
|
|
860
|
+
// PRI-629: budget 耗尽(decision-capable)与 seed 失败(recovery)拆分原因码
|
|
861
|
+
const reasonCode = repairOutcome.detail === 'budget_exhausted'
|
|
862
|
+
? HUMAN_REVIEW_REASON.evaluatorRepairBudgetExhausted
|
|
863
|
+
: HUMAN_REVIEW_REASON.evaluatorRepairSeedFailed;
|
|
635
864
|
// Fail loud (rc-9, EP-03, ERR-002): mark the task needs_human_review
|
|
636
865
|
// so it does NOT stay in 'leased' state (which would cause the lease
|
|
637
866
|
// to expire and the evaluator to re-run the same verdict infinitely).
|
|
@@ -639,14 +868,14 @@ export class EvaluatorRunner extends BasePeerRunner {
|
|
|
639
868
|
// fail-closed: 写失败 throw → retry_wait → 入口门 resume 同一效果,
|
|
640
869
|
// 不问 LLM;禁止吞错后继续 (intent applied ⇔ effect 已 durable)。
|
|
641
870
|
const resultRef = `${this.config.resultRefPrefix}://${runId}`;
|
|
642
|
-
await this.markNeedsHumanReviewOrThrow(taskId, runId,
|
|
871
|
+
await this.markNeedsHumanReviewOrThrow(taskId, { runId, reasonCode, sourceArtifactId: artifactId });
|
|
643
872
|
this.emitEvent('task_needs_human_review', taskId, {
|
|
644
873
|
attemptCount: task.attemptCount,
|
|
645
874
|
resultRef,
|
|
646
875
|
evaluationDecision: finalOutput.evaluation.decision,
|
|
647
876
|
evaluationScore: finalOutput.evaluation.score,
|
|
648
877
|
ruleArtifactId: null,
|
|
649
|
-
reason:
|
|
878
|
+
reason: `repair_loop_${reasonCode}`,
|
|
650
879
|
});
|
|
651
880
|
return {
|
|
652
881
|
kind: 'human_review',
|
|
@@ -711,9 +940,39 @@ export class EvaluatorRunner extends BasePeerRunner {
|
|
|
711
940
|
* effect,不问 LLM;禁止吞错后让 caller 标 intent applied
|
|
712
941
|
* (intent applied ⇔ 其 durable effect 已 materialize)。
|
|
713
942
|
*/
|
|
714
|
-
async markNeedsHumanReviewOrThrow(taskId,
|
|
943
|
+
async markNeedsHumanReviewOrThrow(taskId, review) {
|
|
944
|
+
const { runId, reasonCode, sourceArtifactId } = review;
|
|
715
945
|
try {
|
|
716
|
-
|
|
946
|
+
// PRI-629: status + humanReviewContext 同一次 task-row mutation 原子落库
|
|
947
|
+
// (SPEC §4 — context 缺失的 NHR 是 legacy,只能靠推断)。
|
|
948
|
+
const raw = await this.stateManager.getTask(taskId);
|
|
949
|
+
if (!raw)
|
|
950
|
+
throw new Error(`task ${taskId} not found`);
|
|
951
|
+
const piTask = hydratePITaskRecord(raw);
|
|
952
|
+
if (!piTask)
|
|
953
|
+
throw new Error(`task ${taskId} not hydratable`);
|
|
954
|
+
let sourceArtifactHash;
|
|
955
|
+
try {
|
|
956
|
+
const artifact = await this.artifactStore.getArtifactById(sourceArtifactId);
|
|
957
|
+
if (artifact)
|
|
958
|
+
sourceArtifactHash = computeArtifactContentHash(artifact.contentJson);
|
|
959
|
+
}
|
|
960
|
+
catch {
|
|
961
|
+
sourceArtifactHash = undefined; // hash 可选 — capability 侧要求 artifact 存在,届时重算
|
|
962
|
+
}
|
|
963
|
+
const context = {
|
|
964
|
+
reasonCode,
|
|
965
|
+
sourceRunId: runId,
|
|
966
|
+
sourceArtifactId,
|
|
967
|
+
...(sourceArtifactHash !== undefined ? { sourceArtifactHash } : {}),
|
|
968
|
+
revisionEpoch: piTask.revisionCount ?? 0,
|
|
969
|
+
createdAt: new Date().toISOString(),
|
|
970
|
+
};
|
|
971
|
+
const merged = mergePITaskMetadata(piTask, { humanReviewContext: context });
|
|
972
|
+
await this.stateManager.updateTask(taskId, {
|
|
973
|
+
status: 'needs_human_review',
|
|
974
|
+
diagnosticJson: createPITaskDiagnosticJson(merged),
|
|
975
|
+
});
|
|
717
976
|
// read-back invariant (INV-2): 只有 effect durable 才允许 caller 标 applied
|
|
718
977
|
const current = await this.stateManager.getTask(taskId);
|
|
719
978
|
if (!current || current.status !== 'needs_human_review') {
|
|
@@ -723,7 +982,7 @@ export class EvaluatorRunner extends BasePeerRunner {
|
|
|
723
982
|
catch (err) {
|
|
724
983
|
this.emitEvent('repair_loop_mark_review_failed', taskId, {
|
|
725
984
|
runId,
|
|
726
|
-
reason,
|
|
985
|
+
reason: reasonCode,
|
|
727
986
|
errorMessage: err instanceof Error ? err.message : String(err),
|
|
728
987
|
nextAction: 'task_will_retry_then_resume_completion_intent_without_llm',
|
|
729
988
|
});
|
|
@@ -797,6 +1056,14 @@ export class EvaluatorRunner extends BasePeerRunner {
|
|
|
797
1056
|
*/
|
|
798
1057
|
async maybeResumePendingIntent(taskId, leasedTask) {
|
|
799
1058
|
const piTask = hydratePITaskRecord(leasedTask);
|
|
1059
|
+
// ── PRI-629: pending Owner Resolution 优先于一切 (SPEC §10) ──
|
|
1060
|
+
// Owner accept_current / reject_current 已 durable 记录且任务被翻回
|
|
1061
|
+
// pending — 本次 run 应用 override,绝不重新调用 LLM。applied 但未
|
|
1062
|
+
// terminal 的 crash 窗口同样由此收敛 (SPEC §30)。
|
|
1063
|
+
const ownerOverride = piTask ? planOwnerVerdictOverrideResume(piTask) : null;
|
|
1064
|
+
if (ownerOverride) {
|
|
1065
|
+
return await this.applyOwnerVerdictOverrideAndFinalize(taskId, leasedTask, ownerOverride);
|
|
1066
|
+
}
|
|
800
1067
|
const intent = piTask?.completionIntent;
|
|
801
1068
|
if (!piTask || !intent || intent.status !== 'pending') {
|
|
802
1069
|
// P0 (INV-1/INV-5): applied 但任务未 terminal (标 applied 后、
|
|
@@ -925,6 +1192,117 @@ export class EvaluatorRunner extends BasePeerRunner {
|
|
|
925
1192
|
attemptCount: leasedTask.attemptCount,
|
|
926
1193
|
};
|
|
927
1194
|
}
|
|
1195
|
+
/**
|
|
1196
|
+
* PRI-629: 应用 Owner verdict override 并收敛 terminal。
|
|
1197
|
+
*
|
|
1198
|
+
* 顺序 (SPEC §10/§30): 恢复 durable output → 幂等效果 (override decision)
|
|
1199
|
+
* → completion intent 标 applied → resolution 标 applied → markTaskSucceeded。
|
|
1200
|
+
* 任何 crash 窗口重放同一 resolution,不重新调用 LLM。机器 verdict
|
|
1201
|
+
* (runnerDecision) 永不改写。
|
|
1202
|
+
*/
|
|
1203
|
+
async applyOwnerVerdictOverrideAndFinalize(taskId, leasedTask, plan) {
|
|
1204
|
+
const { resolution, overrideDecision } = plan;
|
|
1205
|
+
this.emitEvent('owner_resolution_applying', taskId, {
|
|
1206
|
+
resolutionId: resolution.resolutionId,
|
|
1207
|
+
action: resolution.action,
|
|
1208
|
+
machineDecision: resolution.machineDecision,
|
|
1209
|
+
effectiveDecision: overrideDecision,
|
|
1210
|
+
sourceRunId: resolution.sourceRunId,
|
|
1211
|
+
});
|
|
1212
|
+
// 恢复裁决时的 durable output — decision 必须与 resolution 记录的机器判定一致
|
|
1213
|
+
const output = await this.recoverIntentOutput(taskId, resolution.sourceRunId, resolution.machineDecision);
|
|
1214
|
+
// SPEC §20 纵深防御: 确定性对抗门失败不允许 approved override (capability
|
|
1215
|
+
// 层已挡一道;此处防事实漂移窗口)。
|
|
1216
|
+
if (overrideDecision === 'approved'
|
|
1217
|
+
&& isEvaluatorOutputV2(output)
|
|
1218
|
+
&& output.adversarialResult
|
|
1219
|
+
&& output.adversarialResult.passed === false) {
|
|
1220
|
+
this.emitEvent('owner_resolution_rejected_by_policy', taskId, {
|
|
1221
|
+
resolutionId: resolution.resolutionId,
|
|
1222
|
+
reason: 'deterministic_hard_gate_failed',
|
|
1223
|
+
nextAction: 'owner_may_choose_revise_once_or_reject_current',
|
|
1224
|
+
});
|
|
1225
|
+
throw new PDRuntimeError('input_invalid', `Owner accept_current refused for task ${taskId}: deterministic adversarial gate failed (owner cannot override hard safety)`);
|
|
1226
|
+
}
|
|
1227
|
+
const artifactId = resolution.sourceArtifactId;
|
|
1228
|
+
const contextHash = `owner-override-${resolution.resolutionId}`;
|
|
1229
|
+
// rule assembly 输入由 durable lineage 重建 (与 intent resume 相同)
|
|
1230
|
+
const assemblySourceId = output.sourceArtificerArtifactId ?? null;
|
|
1231
|
+
let artificerContent = null;
|
|
1232
|
+
if (assemblySourceId) {
|
|
1233
|
+
try {
|
|
1234
|
+
const rec = await this.artifactStore.getArtifactById(assemblySourceId);
|
|
1235
|
+
artificerContent = rec?.contentJson ?? null;
|
|
1236
|
+
}
|
|
1237
|
+
catch {
|
|
1238
|
+
artificerContent = null;
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
const effectResult = await this.applyEvaluatorDecisionEffects({
|
|
1242
|
+
taskId,
|
|
1243
|
+
runId: resolution.sourceRunId,
|
|
1244
|
+
finalOutput: output,
|
|
1245
|
+
task: leasedTask,
|
|
1246
|
+
artifactId,
|
|
1247
|
+
contextHash,
|
|
1248
|
+
sourceArtificerArtifactId: assemblySourceId,
|
|
1249
|
+
ruleAssemblyInput: { artificerArtifact: artificerContent, sourceArtificerArtifactId: assemblySourceId },
|
|
1250
|
+
decisionOverride: overrideDecision === 'approved' ? 'approved' : 'rejected',
|
|
1251
|
+
});
|
|
1252
|
+
if (effectResult.kind === 'human_review') {
|
|
1253
|
+
// P0 评审修复: 与 rollout 对称——override 驱动的 effects 落入 recovery
|
|
1254
|
+
// NHR 时,Owner 裁决已被执行,resolution 标 applied (否则 pending 残留
|
|
1255
|
+
// + Recover guard 拒绝 = 死胡同)。applied 后 Recover 放行,resume 门
|
|
1256
|
+
// 确定性重放。
|
|
1257
|
+
await markOwnerResolutionApplied({
|
|
1258
|
+
updateDiagnosticJson: (tid, json) => this.stateManager.updateTaskDiagnosticJson(tid, json),
|
|
1259
|
+
getTask: (tid) => this.stateManager.getTask(tid),
|
|
1260
|
+
taskId,
|
|
1261
|
+
resolutionId: resolution.resolutionId,
|
|
1262
|
+
appliedAt: new Date().toISOString(),
|
|
1263
|
+
});
|
|
1264
|
+
return effectResult.result;
|
|
1265
|
+
}
|
|
1266
|
+
await this.markCompletionIntentAppliedOrThrow(taskId);
|
|
1267
|
+
await markOwnerResolutionApplied({
|
|
1268
|
+
updateDiagnosticJson: (tid, json) => this.stateManager.updateTaskDiagnosticJson(tid, json),
|
|
1269
|
+
getTask: (tid) => this.stateManager.getTask(tid),
|
|
1270
|
+
taskId,
|
|
1271
|
+
resolutionId: resolution.resolutionId,
|
|
1272
|
+
appliedAt: new Date().toISOString(),
|
|
1273
|
+
});
|
|
1274
|
+
const resultRef = `${this.config.resultRefPrefix}://${resolution.sourceRunId}`;
|
|
1275
|
+
try {
|
|
1276
|
+
await this.stateManager.markTaskSucceeded(taskId, resultRef);
|
|
1277
|
+
}
|
|
1278
|
+
catch (stateErr) {
|
|
1279
|
+
this.emitEvent('mark_succeeded_failed', taskId, {
|
|
1280
|
+
taskId,
|
|
1281
|
+
runId: resolution.sourceRunId,
|
|
1282
|
+
errorMessage: stateErr instanceof Error ? stateErr.message : String(stateErr),
|
|
1283
|
+
});
|
|
1284
|
+
throw stateErr;
|
|
1285
|
+
}
|
|
1286
|
+
this.emitEvent('task_succeeded', taskId, {
|
|
1287
|
+
attemptCount: leasedTask.attemptCount,
|
|
1288
|
+
resultRef,
|
|
1289
|
+
evaluationDecision: output.evaluation.decision,
|
|
1290
|
+
evaluationScore: output.evaluation.score,
|
|
1291
|
+
ruleArtifactId: effectResult.ruleArtifactId,
|
|
1292
|
+
ownerResolutionApplied: resolution.resolutionId,
|
|
1293
|
+
effectiveDecision: overrideDecision,
|
|
1294
|
+
});
|
|
1295
|
+
return {
|
|
1296
|
+
status: 'succeeded',
|
|
1297
|
+
taskId,
|
|
1298
|
+
runId: resolution.sourceRunId,
|
|
1299
|
+
artifactId,
|
|
1300
|
+
resultRef,
|
|
1301
|
+
contextHash,
|
|
1302
|
+
output,
|
|
1303
|
+
attemptCount: leasedTask.attemptCount,
|
|
1304
|
+
};
|
|
1305
|
+
}
|
|
928
1306
|
/**
|
|
929
1307
|
* 从 runs 表恢复 intent 落库前已持久化的 validated output,并交叉核对
|
|
930
1308
|
* decision 与 intent 一致 (authority 记录一致性)。intent 的存在保证
|
|
@@ -985,7 +1363,7 @@ export class EvaluatorRunner extends BasePeerRunner {
|
|
|
985
1363
|
nextAction: 'owner_manual_review_required',
|
|
986
1364
|
priorRepairIteration,
|
|
987
1365
|
});
|
|
988
|
-
return { kind: 'max_iterations_reached' };
|
|
1366
|
+
return { kind: 'max_iterations_reached', detail: 'budget_exhausted' };
|
|
989
1367
|
}
|
|
990
1368
|
// ── Slice 4: seed artificer repair task ──
|
|
991
1369
|
// sourceArtificerArtifactId 由 caller 解析 (fresh: context ?? output;
|
|
@@ -997,7 +1375,7 @@ export class EvaluatorRunner extends BasePeerRunner {
|
|
|
997
1375
|
runId: evaluatorRunId,
|
|
998
1376
|
reason: 'source_artificer_artifact_id_unresolved',
|
|
999
1377
|
});
|
|
1000
|
-
return { kind: 'max_iterations_reached' };
|
|
1378
|
+
return { kind: 'max_iterations_reached', detail: 'seed_failed' };
|
|
1001
1379
|
}
|
|
1002
1380
|
// Construct the new repairPayload (repairIteration = prior + 1).
|
|
1003
1381
|
// The 6 fields are sourced from the current evaluator output (rc-7:
|
|
@@ -1048,7 +1426,7 @@ export class EvaluatorRunner extends BasePeerRunner {
|
|
|
1048
1426
|
runId: evaluatorRunId,
|
|
1049
1427
|
reason: 'seed_artificer_repair_task_not_injected',
|
|
1050
1428
|
});
|
|
1051
|
-
return { kind: 'max_iterations_reached' };
|
|
1429
|
+
return { kind: 'max_iterations_reached', detail: 'seed_failed' };
|
|
1052
1430
|
}
|
|
1053
1431
|
try {
|
|
1054
1432
|
const repairTaskId = await this.repairTaskSeeder({
|
|
@@ -1074,7 +1452,7 @@ export class EvaluatorRunner extends BasePeerRunner {
|
|
|
1074
1452
|
// Treat as max iterations reached so the caller skips markTaskSucceeded
|
|
1075
1453
|
// and returns succeeded — the evaluator verdict stands; only the repair
|
|
1076
1454
|
// seeding failed, which is logged.
|
|
1077
|
-
return { kind: 'max_iterations_reached' };
|
|
1455
|
+
return { kind: 'max_iterations_reached', detail: 'seed_failed' };
|
|
1078
1456
|
}
|
|
1079
1457
|
}
|
|
1080
1458
|
// ── Optional hooks ──────────────────────────────────────────────────────────
|
|
@@ -1129,17 +1507,20 @@ export class EvaluatorRunner extends BasePeerRunner {
|
|
|
1129
1507
|
}
|
|
1130
1508
|
// ── PRI-426: adversarial sandbox replay ─────────────────────────────────────
|
|
1131
1509
|
/**
|
|
1132
|
-
* Run a single-round adversarial sandbox replay
|
|
1133
|
-
*
|
|
1510
|
+
* Run a single-round adversarial sandbox replay (PRD Decision 11d).
|
|
1511
|
+
* PRI-634 A2/R1: the `output` is accepted in V1 shape too — a code-bearing
|
|
1512
|
+
* Artificer artifact requires the gate regardless of whether the evaluator
|
|
1513
|
+
* LLM emitted optional V2 fields. Only V2-shaped outputs can contribute
|
|
1514
|
+
* LLM-supplied adversarialCases (checked via isEvaluatorOutputV2 below).
|
|
1515
|
+
* Pure orchestration of pure functions:
|
|
1134
1516
|
* 1. Skip if passive review failed (decision !== 'approved' is the LLM's
|
|
1135
|
-
* short-circuit signal — no code to defend).
|
|
1136
|
-
* 2.
|
|
1137
|
-
* 3.
|
|
1138
|
-
* 4. Merge ≥1 positive case from the Artificer golden trace. If the
|
|
1517
|
+
* short-circuit signal — no code to defend). Now observable (R9).
|
|
1518
|
+
* 2. Convert adversarialCases → GoldenTrace (all negative, PRI-423).
|
|
1519
|
+
* 3. Merge ≥1 positive case from the Artificer golden trace. If the
|
|
1139
1520
|
* artificer artifact has no goldenTraceCases (V1 mismatch), degrade:
|
|
1140
1521
|
* skip replay with telemetry — do NOT crash.
|
|
1141
|
-
*
|
|
1142
|
-
*
|
|
1522
|
+
* 4. Invoke evaluateRefinerRuleHostGate via injected gateDeps.
|
|
1523
|
+
* 5. Populate adversarialResult from the gate result.
|
|
1143
1524
|
*
|
|
1144
1525
|
* Never throws — all failure modes degrade to a returned result with a
|
|
1145
1526
|
* structured reason (ERR-018). The caller persists the updated output.
|
|
@@ -1151,15 +1532,29 @@ export class EvaluatorRunner extends BasePeerRunner {
|
|
|
1151
1532
|
// eslint-disable-next-line @typescript-eslint/prefer-destructuring
|
|
1152
1533
|
const gateDeps = this.gateDeps;
|
|
1153
1534
|
if (!gateDeps) {
|
|
1154
|
-
|
|
1535
|
+
// Defensive — unreachable via the current caller (R2 fails loud before
|
|
1536
|
+
// this point). Kept observable so a future caller cannot silently
|
|
1537
|
+
// reintroduce the un-gated path (PRI-634 A3).
|
|
1538
|
+
this.emitEvent('adversarial_replay_skipped', taskId, {
|
|
1539
|
+
runId,
|
|
1540
|
+
reason: 'gate_deps_not_injected',
|
|
1541
|
+
nextAction: 'wire_gateDeps_createProductionGateDeps_into_evaluator_runner_assembly',
|
|
1542
|
+
});
|
|
1543
|
+
return { updatedOutput: null, skipReason: 'gate_deps_not_injected' };
|
|
1155
1544
|
}
|
|
1156
1545
|
// (1) Passive review short-circuit: the LLM emits decision='needs_revision'
|
|
1157
1546
|
// when any of intentConsistency/scopePrecision/traceCoverage fails. Only
|
|
1158
1547
|
// replay when the LLM judged the code worth defending. This check is
|
|
1159
|
-
// defensive —
|
|
1160
|
-
//
|
|
1548
|
+
// defensive — the caller already gated on decision='approved' (PRI-634 A2
|
|
1549
|
+
// moved the check to the gate decision point); kept here so a future
|
|
1550
|
+
// caller cannot silently bypass it (Runtime Contract Rule 3).
|
|
1161
1551
|
if (output.evaluation.decision !== 'approved') {
|
|
1162
|
-
|
|
1552
|
+
this.emitEvent('adversarial_replay_skipped', taskId, {
|
|
1553
|
+
runId,
|
|
1554
|
+
reason: 'evaluation_not_approved',
|
|
1555
|
+
nextAction: 'repair_loop_or_next_review_round',
|
|
1556
|
+
});
|
|
1557
|
+
return { updatedOutput: null, skipReason: 'evaluation_not_approved' };
|
|
1163
1558
|
}
|
|
1164
1559
|
// (2) Resolve the Artificer artifact early — we need it both to derive
|
|
1165
1560
|
// the v2 adversarial spec (PRI-485) and to merge positive cases (PRI-423).
|
|
@@ -1169,7 +1564,7 @@ export class EvaluatorRunner extends BasePeerRunner {
|
|
|
1169
1564
|
reason: 'no_artificer_artifact_in_context',
|
|
1170
1565
|
nextAction: 'verify_buildContext_resolves_artificer_artifact',
|
|
1171
1566
|
});
|
|
1172
|
-
return { updatedOutput: null };
|
|
1567
|
+
return { updatedOutput: null, skipReason: 'no_artificer_artifact_in_context' };
|
|
1173
1568
|
}
|
|
1174
1569
|
const artificerParsed = this.parseArtificerArtifact(context.artificerArtifact);
|
|
1175
1570
|
if (!artificerParsed) {
|
|
@@ -1178,7 +1573,7 @@ export class EvaluatorRunner extends BasePeerRunner {
|
|
|
1178
1573
|
reason: 'artificer_artifact_unparseable',
|
|
1179
1574
|
nextAction: 'verify_artificer_artifact_contentJson',
|
|
1180
1575
|
});
|
|
1181
|
-
return { updatedOutput: null };
|
|
1576
|
+
return { updatedOutput: null, skipReason: 'artificer_artifact_unparseable' };
|
|
1182
1577
|
}
|
|
1183
1578
|
const { implementationCode, goldenTraceCases, affectedTools } = artificerParsed;
|
|
1184
1579
|
if (typeof implementationCode !== 'string' || implementationCode.trim() === '') {
|
|
@@ -1189,7 +1584,7 @@ export class EvaluatorRunner extends BasePeerRunner {
|
|
|
1189
1584
|
reason: 'artificer_artifact_has_no_implementation_code',
|
|
1190
1585
|
nextAction: 'verify_artificer_l2_adapter_emitted_v2',
|
|
1191
1586
|
});
|
|
1192
|
-
return { updatedOutput: null };
|
|
1587
|
+
return { updatedOutput: null, skipReason: 'artificer_artifact_has_no_implementation_code' };
|
|
1193
1588
|
}
|
|
1194
1589
|
// Merge positive cases from the Artificer golden trace into the adversarial
|
|
1195
1590
|
// trace. buildGoldenTraceFromArtificer validates each case structurally
|
|
@@ -1201,20 +1596,36 @@ export class EvaluatorRunner extends BasePeerRunner {
|
|
|
1201
1596
|
reason: 'no_positive_case_in_artificer_golden_trace',
|
|
1202
1597
|
nextAction: 'verify_artificer_emitted_at_least_one_positive_case',
|
|
1203
1598
|
});
|
|
1204
|
-
return { updatedOutput: null };
|
|
1599
|
+
return { updatedOutput: null, skipReason: 'no_positive_case_in_artificer_golden_trace' };
|
|
1205
1600
|
}
|
|
1206
1601
|
// (3) PRI-485 Phase 6: auto-generate 5 v2 adversarial cases from the
|
|
1207
1602
|
// Artificer's affectedTools + first positive case's path. These defend
|
|
1208
1603
|
// against the most common false-positive patterns (unavailable/truncation/
|
|
1209
1604
|
// alias/path/combination). Degrade with telemetry (rc-9) if the spec
|
|
1210
1605
|
// cannot be derived — LLM-supplied adversarialCases still replay.
|
|
1211
|
-
|
|
1606
|
+
// PRI-634 R1: V1-shaped outputs carry no adversarialCases — the merged
|
|
1607
|
+
// set below then relies entirely on the auto-generated v2 cases.
|
|
1608
|
+
const llmCases = isEvaluatorOutputV2(output) ? (output.adversarialCases ?? []) : [];
|
|
1212
1609
|
const v2Cases = this.generateV2CasesFromArtificer(affectedTools, positiveCases, taskId, runId);
|
|
1213
1610
|
const mergedAdversarialCases = [...v2Cases, ...llmCases];
|
|
1214
1611
|
// (4) No adversarial cases (neither v2-generated nor LLM-supplied) →
|
|
1215
1612
|
// nothing to replay. codeReview may still be present (passive review only).
|
|
1613
|
+
// Previously a silent return (PRI-634 A3 hole #3); now observable (R9).
|
|
1614
|
+
//
|
|
1615
|
+
// PRI-634 R3: for a code-bearing Artificer artifact this is NOT a benign
|
|
1616
|
+
// degradation — it reproduces the chain-48371236 terminal state (approved
|
|
1617
|
+
// with no gate executed, adversarialResult=null, no pi-rule-* ever
|
|
1618
|
+
// emitted). The caller fails loud on this skipReason instead of reporting
|
|
1619
|
+
// succeeded. Non-code-bearing artifacts keep the legacy degrade (R1's
|
|
1620
|
+
// authority is the durable Artificer content, and only code-bearing
|
|
1621
|
+
// content can reach rule assembly).
|
|
1216
1622
|
if (mergedAdversarialCases.length === 0) {
|
|
1217
|
-
|
|
1623
|
+
this.emitEvent('adversarial_replay_skipped', taskId, {
|
|
1624
|
+
runId,
|
|
1625
|
+
reason: 'no_adversarial_cases_after_merge',
|
|
1626
|
+
nextAction: 'verify_artificer_affectedTools_or_positive_case_path_derivable',
|
|
1627
|
+
});
|
|
1628
|
+
return { updatedOutput: null, skipReason: 'no_adversarial_cases_after_merge' };
|
|
1218
1629
|
}
|
|
1219
1630
|
// (5) Convert the merged adversarial cases to an all-negative GoldenTrace.
|
|
1220
1631
|
const conversion = adversarialCasesToGoldenTrace(mergedAdversarialCases);
|
|
@@ -1226,7 +1637,7 @@ export class EvaluatorRunner extends BasePeerRunner {
|
|
|
1226
1637
|
reason: `adversarial_conversion_failed: ${conversion.reason}`,
|
|
1227
1638
|
nextAction: 'verify_adversarial_case_validator_alignment',
|
|
1228
1639
|
});
|
|
1229
|
-
return { updatedOutput: null };
|
|
1640
|
+
return { updatedOutput: null, skipReason: `adversarial_conversion_failed: ${conversion.reason}` };
|
|
1230
1641
|
}
|
|
1231
1642
|
const mergedTrace = {
|
|
1232
1643
|
traceId: `golden-trace-evaluator-replay-${taskId}-${Date.now().toString(36)}`,
|
|
@@ -1255,6 +1666,7 @@ export class EvaluatorRunner extends BasePeerRunner {
|
|
|
1255
1666
|
};
|
|
1256
1667
|
return {
|
|
1257
1668
|
updatedOutput: { ...output, adversarialResult: failedResult },
|
|
1669
|
+
skipReason: null,
|
|
1258
1670
|
};
|
|
1259
1671
|
}
|
|
1260
1672
|
this.emitEvent('adversarial_replay', taskId, {
|
|
@@ -1274,6 +1686,7 @@ export class EvaluatorRunner extends BasePeerRunner {
|
|
|
1274
1686
|
};
|
|
1275
1687
|
return {
|
|
1276
1688
|
updatedOutput: { ...output, adversarialResult },
|
|
1689
|
+
skipReason: null,
|
|
1277
1690
|
};
|
|
1278
1691
|
}
|
|
1279
1692
|
/**
|
|
@@ -1408,12 +1821,21 @@ export class EvaluatorRunner extends BasePeerRunner {
|
|
|
1408
1821
|
});
|
|
1409
1822
|
return [];
|
|
1410
1823
|
}
|
|
1411
|
-
|
|
1412
|
-
|
|
1824
|
+
// PRI-634: golden-trace params are echoed verbatim from the host
|
|
1825
|
+
// trajectory, and the dominant write-tool schema names its target
|
|
1826
|
+
// `file_path` (OpenClaw / Claude Code), not `path`. Reading only `path`
|
|
1827
|
+
// made v2 auto-generation structurally unreachable on real chains: the
|
|
1828
|
+
// merged case set stayed empty, so the gate never ran and an
|
|
1829
|
+
// LLM-declared adversarialResult.passed=true could stand in for a real
|
|
1830
|
+
// replay (chain 48371236). Accept both spellings — this is the input that
|
|
1831
|
+
// makes the deterministic gate reachable at all.
|
|
1832
|
+
const { params } = firstPositive;
|
|
1833
|
+
const pathParam = resolveCasePathParam(params);
|
|
1834
|
+
if (pathParam === null) {
|
|
1413
1835
|
this.emitEvent('v2_adversarial_cases_skipped', taskId, {
|
|
1414
1836
|
runId,
|
|
1415
1837
|
reason: 'no_path_param_for_v2_adversarial_cases',
|
|
1416
|
-
nextAction: '
|
|
1838
|
+
nextAction: 'verify_positive_case_has_string_path_or_file_path_param',
|
|
1417
1839
|
});
|
|
1418
1840
|
return [];
|
|
1419
1841
|
}
|