/** * EvaluatorRunner — Peer runner for principle evaluation (PRI-67). * * Migrated to extend BasePeerRunner (PRI-302). The shared lease → buildContext → * invoke → poll → fetch → validate → succeed/fail pipeline is now in the base * class. This file only contains Evaluator-specific logic. * * Key business semantics: * - Evaluator approved → must validate the principle-bearing Scribe artifact, * NOT the Artificer plan artifact. This is the critical lineage contract. * - resolvePrincipleBearerArtifact uses sourceTrace.scribeArtifactId first, * then falls back to lineage search. Ambiguous candidates → fail loud. * - updateValidationStatus returning false → structured telemetry, no silent skip. * * ERR considerations: * - ERR-001 / ERR-005: output is `unknown` until validateOutput passes. * - ERR-004 / ERR-008: sourceTrace / scribeArtifactId / sourceArtificerArtifactId * must be internally consistent. * - ERR-018 / ERR-019: validationStatus update target must be the correct * principle-bearing artifact, never stale or wrong. * - ERR-025: tests must exercise real evaluator runner path. * - ERR-048: activation write/read path must not break. * * @see docs/adr/0003-peer-agent-state-machine-orchestration.md * @see BasePeerRunner in runner/base-peer-runner.ts */ import type { RunHandle } from '../runtime-protocol.js'; import type { EvaluatorOutputV1, EvaluatorValidator } from './evaluator-output.js'; import type { TaskRecord } from '../task-status.js'; import { type PDErrorCategory } from '../error-categories.js'; import { type RepairPayload, type LastValidatorErrors } from './pitask-metadata.js'; import { type PreviousEvaluationContext, type HostToolCatalogFacts } from './evaluator-prompt-builder.js'; import type { FormationContext } from './formation-context.js'; import type { ArtificerHostSemanticContext } from './artificer-prompt-builder.js'; import { type InternalizationChannel, type PipelineTopologyMode, type ArtifactRef } from './peer-runner-contracts.js'; import { BasePeerRunner } from '../runner/base-peer-runner.js'; import type { PeerRunnerOptions, PeerRunnerDeps, PeerRunnerResult, PeerRunnerValidationResult } from '../runner/peer-runner-types.js'; import type { EffectivePdConfig } from '../config/pd-config-types.js'; import type { OutputLanguage } from '../language-directive.js'; import { type RefinerRuleHostGateDeps } from './refiner-rulehost-gate.js'; /** Context built by EvaluatorRunner.buildContext() and consumed by invokeRuntime(). */ interface EvaluatorContext { readonly contextHash: string; readonly artificerArtifact: string | null; readonly sourceArtificerArtifactId: string | null; /** * Scribe principle artifact contentJson (RuleHost MVP Activation, PRD Decision 12). * Loaded so the evaluator LLM can judge code intentConsistency/scopePrecision * against the original principle text. Null when no scribe artifact is resolvable * (scribeArtifactId missing/malformed or upstream artifact unavailable). */ readonly scribeArtifact: string | null; readonly sourceScribeArtifactId: string | null; /** * PRI-630 收敛契约: 依赖 artificer 的 repairPayload (第 2+ 轮存在)。 * 派生 previousEvaluation 注入 prompt — 上轮 decision/score/concerns/ * requiredChanges(稳定 id)/repairIteration。 */ readonly dependencyRepairPayload?: RepairPayload; /** PRI-630: 由 dependencyRepairPayload 解析的上轮评估上下文 (首轮 undefined) */ readonly previousEvaluation?: PreviousEvaluationContext; /** * PRI-843 (DC-4a): bounded formation-evidence projection (dreamer proposals + * source diagnosis + provenance) resolved from the scribe artifact's * authoritative `sourceTrace.dreamerArtifactId`. Undefined when the lineage id * is absent or the formation cannot be resolved — the prompt then keeps its * pre-PRI-843 shape exactly (legacy / degraded compatibility). */ readonly formationContext?: FormationContext; /** * PRI-644 (PRI-700 factor B wiring): verbatim validator rejection reasons * from the immediately-preceding attempt, re-fed only when fresh * (sourceAttemptCount === leased attempt - 1). Undefined on first attempt * and when suppressed — the prompt then keeps its pre-PRI-644 shape exactly. * Ephemeral prompt context: NEVER part of contextHash (same posture as the * artificer — cache identity covers evidence, not feedback). */ readonly priorValidatorErrors?: LastValidatorErrors; } export type EvaluatorRunnerResultStatus = 'succeeded' | 'failed' | 'retried'; export interface EvaluatorRunnerResult { readonly status: EvaluatorRunnerResultStatus; readonly taskId: string; readonly runId?: string; readonly artifactId?: string; readonly resultRef?: string; readonly contextHash?: string; readonly output?: EvaluatorOutputV1; readonly errorCategory?: PDErrorCategory; readonly failureReason?: string; readonly attemptCount: number; } /** * EvaluatorRunner options. Extends PeerRunnerOptions with an optional * RuleHost sandbox gate deps (PRI-426). When `gateDeps` is provided AND the * evaluator output is V2 (code-bearing), succeedTask runs a single-round * adversarial sandbox replay and populates `adversarialResult`. * * When `gateDeps` is absent, V2 outputs still validate but no replay runs — * this preserves backward compatibility for callers not yet wired to the * sandbox (e.g. V1-only test fixtures, pre-Phase-6 assembly). */ export interface EvaluatorRunnerOptions extends PeerRunnerOptions { readonly gateDeps?: RefinerRuleHostGateDeps; /** * PRI-630 工具目录权威: runtime-authoritative host tool facts (readOnly / * write 工具名)。由宿主装配层注入;缺失时 prompt 声明 degraded 规则 — * 工具名差异不得成为 hard blocker。 */ readonly hostToolCatalog?: HostToolCatalogFacts; /** * PRI-741: host semantic projection (real host tool names + kinds from the * SAME registry provenance as gateDeps). Used ONLY to generate the * host-name parity replay case — the prompt itself is unchanged. Undefined = * no host-alias case is generated (observable skip event). */ readonly hostSemanticContext?: ArtificerHostSemanticContext; /** * PR B (ADR-0019 pattern, mirrors ArtificerRunner): effective config for * feature flag resolution (e.g. the repair-loop and degradation flags). */ readonly effectiveConfig?: EffectivePdConfig; } export interface ResolvedEvaluatorRunnerOptions { readonly pollIntervalMs: number; readonly timeoutMs: number; readonly defaultMaxAttempts: number; readonly owner: string; readonly runtimeKind: string; readonly agentId: string; /** * Owner's preferred language for review fields (PRI-714). Forwarded to * EvaluatorPromptBuilder so summary/concerns/requiredChanges/codeReview * explanations follow the owner's language. Undefined = no directive * (backward compatible). */ readonly outputLanguage?: OutputLanguage; } export declare const DEFAULT_EVALUATOR_RUNNER_OPTIONS: Readonly>; export declare function resolveEvaluatorRunnerOptions(options: EvaluatorRunnerOptions): ResolvedEvaluatorRunnerOptions; /** * PRI-509: Parameters for seeding an artificer repair task. * * The evaluator runner constructs the repairPayload (core logic — 6 fields * sourced from the current evaluator output) and resolves inherited lineage * from the dependency artificer task. The actual task record creation is * delegated to the plugin layer via `seedArtificerRepairTask` to preserve * the core/plugin boundary (core peer runners do NOT directly orchestrate * task creation — enforced by architecture-regression.test.ts). */ export interface SeedArtificerRepairParams { /** The repair payload (6 fields) constructed by the evaluator runner. */ readonly repairPayload: RepairPayload; /** Scribe task IDs inherited from the original artificer task. */ readonly inheritedDependencyTaskIds: readonly string[]; /** Channel inherited from the original artificer task. */ readonly inheritedChannel: InternalizationChannel; /** PRI-720: topology mode inherited from the original artificer task (full_chain override). */ readonly inheritedPipelineMode?: PipelineTopologyMode; /** Timeout inherited from the original artificer task. */ readonly inheritedTimeoutMs: number; /** Input artifact refs inherited from the original artificer task. */ readonly inheritedInputArtifactRefs: readonly ArtifactRef[]; } export interface EvaluatorRunnerDeps extends PeerRunnerDeps { readonly validator: EvaluatorValidator; /** * PRI-509: feature flag resolver for the evaluator→artificer repair loop. * When omitted or returns false, evaluator needs_revision follows the * legacy path (no repair task seeded). When returns true and the * decision is needs_revision, the evaluator seeds an artificer repair * task (up to 2 rounds) or marks the task needs_human_review on the * 3rd round (fail loud, EP-03). * * Injected by the plugin layer (which reads EffectivePdConfig); core * stays pure logic with no direct config coupling (D5). */ readonly isRepairLoopEnabled?: () => boolean; /** * PRI-509: Seeder function for artificer repair tasks. * * The plugin layer implements this by serializing the repairPayload + * inherited metadata into diagnosticJson and calling stateManager * task-creation. Core peer runners must NOT call task-creation directly * (architecture-regression.test.ts enforces this boundary). * * Returns the newly created repair task's ID. */ readonly seedArtificerRepairTask?: (params: SeedArtificerRepairParams) => Promise; /** * PRI-911A — WRITER IDENTITY BOUNDARY (I1+I3). The ledger membership check the * rule-assembly stamp uses to verify a UUID it is about to carry forward * (`PrincipleTreeLedgerAdapter.hasPrinciple`). Optional and inject-only, same * shape as ScribeRunner's `ledgerIdentity` (PR #1856): core stays free of any * ledger I/O (D5), and when absent the shape gate still holds — the check is * what turns "UUID-shaped" into "UUID the ledger actually knows". * * PRI-915: `isAvailable` lets the stamp distinguish "a readable ledger does * not know this id" (invalid_identity, drift) from "the ledger cannot be * read right now" (identity_source_unavailable, transient). */ readonly ledgerIdentity?: { readonly isAvailable: () => boolean; readonly hasPrinciple: (principleId: string) => boolean; }; } /** * PRI-911A SPEC §6.4 — the identity writer's failure vocabulary. Every refusal * to carry a principle identity reports exactly one of these; there is no * "unknown" bucket, because an unclassified refusal is an unobservable one. * * - `missing_identity` — the bearer artifact carries no identity at all * (its `source_principle_id` column is empty). * - `non_canonical_identity` — a value exists but it is content masquerading as * identity (philosopher title, `T-NN`, any text). * - `invalid_identity` — UUID-shaped, but a READABLE ledger does not know * it (data drift; never upgraded to a guess). * - `identity_source_unavailable` — the ledger backing file exists but cannot * be read/parsed, so membership is not decidable * right now. Transient outage, NOT drift (PRI-915): * the id may be perfectly real; retry after the * ledger is repaired instead of treating it as * drift. * - `ambiguous_identity` — more than one plausible identity (reported by * the bearer/lineage resolvers, which own that * comparison). */ export type IdentityStampFailureReason = 'missing_identity' | 'non_canonical_identity' | 'invalid_identity' | 'identity_source_unavailable' | 'ambiguous_identity'; export declare class EvaluatorRunner extends BasePeerRunner { private readonly validator; /** * Optional RuleHost sandbox gate deps (PRI-426). When present and the output * is V2, succeedTask runs a single adversarial sandbox replay. Absent = no * replay (backward compatible). */ private readonly gateDeps; /** * PRI-509: feature flag resolver for the evaluator→artificer repair loop. * Null when the deps did not inject the resolver (= disabled, legacy path). */ private readonly repairLoopEnabledResolver; /** * PRI-509: seeder function for artificer repair tasks. * Null when the deps did not inject the seeder (= repair seeding unavailable). */ private readonly repairTaskSeeder; /** PRI-630: runtime-authoritative tool facts; null = catalog unavailable (degraded rule in prompt) */ private readonly hostToolCatalog; /** PRI-741: host semantic projection for the host-name parity replay case; null = skipped. */ private readonly hostSemanticContext; /** * PRI-714: owner's preferred language for review fields, forwarded into * the evaluator prompt (language directive on summary/concerns/…). * Undefined = no directive (backward compatible). */ private readonly outputLanguage; /** PRI-911A: ledger membership authority for the rule-assembly identity stamp; null = shape gate only. */ private readonly ledgerIdentity; constructor(deps: EvaluatorRunnerDeps, options: EvaluatorRunnerOptions); /** * Returns true iff the evaluator→artificer repair loop feature flag is on. */ private isRepairLoopEnabled; get permanentErrorCategories(): ReadonlySet; buildContext(taskId: string): Promise; invokeRuntime(taskId: string, context: EvaluatorContext): Promise; /** * PRI-843: narrow task view for formation-context resolution (the scribe * adapter, verbatim): phase identity lives on the task row, never on the * artifact, so the diagnostic predecessor can only be found via task lookup. */ private lookupFormationTask; /** * PRI-630 收敛契约 (SPEC §18.1): 解析上轮评估上下文 — 从 dependency * artificer 的 repairPayload.sourceEvaluatorTaskId 找到上轮 evaluator, * 读取其最近 principle artifact,按 rc-1/rc-2 守卫解析 evaluation 字段。 * requirements 用稳定 id (req-1..N, 上轮 requiredChanges 顺序)。 * 解析失败 → 结构化降级事件 + undefined (保持既有行为,可观测)。 */ private resolvePreviousEvaluation; /** * Build the evaluator prompt from the full predecessor artifacts. */ private buildEvaluatorPrompt; validateOutput(output: unknown, taskId: string, context: EvaluatorContext): Promise; succeedTask(taskId: string, runId: string, output: EvaluatorOutputV1, task: TaskRecord, contextHash: string, context: EvaluatorContext): Promise>; /** * P0 (verdict drift): 执行 decision 的治理效果 (fresh 与 resume 共用, * 幂等): approved → validate principle bearer;needs_revision (repair loop * on) → deterministic repair seed / max-iterations needs_human_review; * rejected → 无效果。 * * 返回非 null = terminal 结果 (needs_human_review 族,caller 直接返回, * 不得 markTaskSucceeded);null = effects 完成,caller 标 intent applied * 后 markSucceeded。 */ private applyEvaluatorDecisionEffects; /** * PRI-703 Phase 2: extract failed replay caseIds from the evaluator output. * Trust-boundary (rc-1/rc-4): adversarialResult.failedCases is untrusted * artifact content — validate each element's caseId shape; malformed entries * are skipped (they cannot inform scoping decisions). */ private static extractFailedCases; /** * PRI-703 Phase 2: resolve the rule's requiresContextVersion from the * DURABLE artificer artifact (never from the LLM-forgible evaluator copy). * Three-way result (see resolveRequiresContextVersionFromArtifact): * 2 / undefined → resolved (v2 / v1) — the scope partition MUST run; * null → unresolvable (attribution stays inconclusive — fail-open to the * existing repair path, never blocks the loop on a read error). * * 评审 P1 修正:key-absent on a PARSED artifact is deterministically v1 * (artificer schema only ever writes literal 2) and must reach the * partition — collapsing it into null made the out-of-scope routing * unreachable in production for exactly its target population (v1 rules). */ private resolveRequiresContextVersion; /** * PRI-509: Resolve the prior repair iteration by reading the dependency * artificer task's repairPayload (rc-7: written at task creation, never * inferred at read). Returns 0 when the dependency artificer has no * repairPayload (Round-1 evaluator → first repair). * * Loop state freshness (EP-05, ERR-015/018/019): each evaluator round reads * the CURRENT dependency artificer's repairPayload — never a cached value. * * PRI-718: the dep-derived value alone is NOT the durable truth — the dep * chain can be stale (e.g. an older repair's recovered transition re-pointed * the evaluator at a superseded artifact). The iteration budget must reflect * every durable repair round for THIS evaluator, so the result is the max of * the dep-derived value and the highest existing deterministic repair id * (probe is bounded: the budget gate stops at 2; the cap only guards against * pathological id growth). */ private resolvePriorRepairIteration; /** * 把 runner verdict 持久化进任务 diagnosticJson(commit 门控的输入)。 * 失败不静默 (rc-9): emitEvent 后吞掉 — verdict 已在 events/runs 中可观测, * 且 commit 门对缺失 verdict 走 legacy 推进,不会因记录失败而卡链。 */ /** * P0 (INV-2): needs_human_review 是 completion effect 的 materialize 操作 — * fail-closed + read-back。写失败 throw → retry_wait → 入口门 resume 同一 * effect,不问 LLM;禁止吞错后让 caller 标 intent applied * (intent applied ⇔ 其 durable effect 已 materialize)。 */ private markNeedsHumanReviewOrThrow; /** * verdict + completion intent 原子落库 (单次 metadata 写)。intent 的存在 * 证明 output 已 durable (updateRunOutput 在 succeedTask 最前)。 * 同 epoch crash/retry 重跑经 maybeResumePendingIntent resume,不重问 LLM。 */ private recordCompletionOrThrow; /** intent APPLIED 后才允许 terminal (P0 invariant 5)。写失败 fail loud。 */ private markCompletionIntentAppliedOrThrow; /** * 入口恢复门 (BasePeerRunner hook): pending completion intent (同 epoch) * 是 recovery authority。返回非 null = 本次 run 以 resume 完成 (LLM 未被 * 调用);返回 null = 走正常 LLM 管线。真正的 revision reopen 已清空 * intent (新 epoch 允许新 verdict);epoch 不匹配的残留视为 stale。 */ protected maybeResumePendingIntent(taskId: string, leasedTask: TaskRecord): Promise | null>; /** * P0 (INV-1/INV-5): applied intent 的补 terminal — effects 已 materialize * (applied ⇒ INV-2 保证),仅 markTaskSucceeded 缺失。不调用 LLM。 */ private finalizeAppliedIntentTerminal; /** * PRI-629: 应用 Owner verdict override 并收敛 terminal。 * * 顺序 (SPEC §10/§30): 恢复 durable output → 幂等效果 (override decision) * → completion intent 标 applied → resolution 标 applied → markTaskSucceeded。 * 任何 crash 窗口重放同一 resolution,不重新调用 LLM。机器 verdict * (runnerDecision) 永不改写。 */ private applyOwnerVerdictOverrideAndFinalize; /** * 从 runs 表恢复 intent 落库前已持久化的 validated output,并交叉核对 * decision 与 intent 一致 (authority 记录一致性)。intent 的存在保证 * updateRunOutput 曾成功;缺失/损坏/漂移 = 存储腐坏 → fail loud。 */ private recoverIntentOutput; /** * PRI-509: Seed an artificer repair task or mark the evaluator task * needs_human_review when max iterations (2) are reached. * * Returns: * - { kind: 'repair_seeded', taskId } — a new artificer repair task was created. * - { kind: 'max_iterations_reached' } — task marked needs_human_review (fail loud). * * Trust boundary (rc-1, rc-2): evaluator output is already validated by * validateOutput before succeedTask is called. requiredChanges / concerns * are typed as readonly string[] on EvaluatorEvaluation, so element-level * re-validation is not required here (rc-4 N/A — not unknown at this point). */ /** * Round-2 R2 (Owner 指令 2026-09-08): 从 durable 证据派生本 verdict 的治理 * 效果。fresh 与 resume 调用同一派生 —— 输入全部是 durable 事实: * - output: intent 落库前已由 executeDeterministicReplay 持久化的 * adversarialResult(resume 时 recoverIntentOutput 从 run.outputPayload * 恢复同一对象); * - sourceArtificerArtifactId → durable 工件的 requiresContextVersion; * - priorRepairIteration → durable dependency repairPayload。 * record 只持久化本函数的结论,不做判断 (指令要求: 禁止把复杂判断逻辑 * 塞进 recordCompletionOrThrow)。返回 undefined = governance_transition * (正常效果由 applyEvaluatorDecisionEffects 执行,无需特判)。 * * fresh 路径的 diagnosticReplayEvidence 仅用于验证"重放确实执行过"这一 * 控制流事实 (P1 provenance); resume 路径不传时, 以 durable adversarialResult * 的形态 (passed === false 且 failedCases 非空) 为等价判据 —— intent 存在 * 本身即证明 fresh 侧重放已执行并持久化。 */ /** * Round-2 R2: 读取当前 durable completion intent (pending 或 applied 均可读 — * applied 意味着效果已 materialize, 读侧只用于一致性核对, 不会改变路由)。 * 解析失败/缺 intent 返回 null — 出界判定回退到 fresh 快路径判定 * (fail-closed, 绝不因读不到 intent 而静默 seed)。 */ private readPendingOrAppliedCompletionIntent; private deriveGovernanceEffect; private maybeSeedArtificerRepair; /** * Re-inject taskId if stripped by stripLineageFields (PRI-272 / ERR-008). * Only fill when absent via Object.hasOwn — present-but-falsy values * must reach validation and fail loud (Runtime Contract Rule 3). * * generatedAt override is handled by the base class — subclasses must call * super.postFetchTransform() to inherit it. */ protected postFetchTransform(taskId: string, untrustedOutput: unknown, _context: EvaluatorContext): void; protected emitSuccessTelemetry(taskId: string, output: EvaluatorOutputV1): void; /** * Check lineage strip contract after validation passes. * Validates sourceTrace.scribeArtifactId consistency (ERR-004, ERR-008). */ protected checkLineageIntegrity(taskId: string, output: EvaluatorOutputV1, _context: EvaluatorContext): void; /** * Run a single-round adversarial sandbox replay (PRD Decision 11d). * PRI-634 A2/R1: the `output` is accepted in V1 shape too — a code-bearing * Artificer artifact requires the gate regardless of whether the evaluator * LLM emitted optional V2 fields. Only V2-shaped outputs can contribute * LLM-supplied adversarialCases (checked via isEvaluatorOutputV2 below). * Pure orchestration of pure functions: * 1. Skip if passive review failed (decision !== 'approved' is the LLM's * short-circuit signal — no code to defend). Now observable (R9). * 2. Convert adversarialCases → GoldenTrace (all negative, PRI-423). * 3. Merge ≥1 positive case from the Artificer golden trace. If the * artificer artifact has no goldenTraceCases (V1 mismatch), degrade: * skip replay with telemetry — do NOT crash. * 4. Invoke evaluateRefinerRuleHostGate via injected gateDeps. * 5. Populate adversarialResult from the gate result. * * Never throws — all failure modes degrade to a returned result with a * structured reason (ERR-018). The caller persists the updated output. */ private runAdversarialReplay; /** * PRI-634 R4: verdict-agnostic deterministic replay core. Runs the sandbox * gate against the Artificer implementation code and returns the updated * output carrying `adversarialResult`. Unlike runAdversarialReplay this does * NOT short-circuit on decision !== 'approved' — the caller decides when a * replay is useful (approved → binding; needs_revision → diagnostic * evidence). Never throws — all failure modes degrade to a returned result * with a structured reason (ERR-018). The caller persists the updated output. */ private executeDeterministicReplay; /** * Parse the Artificer artifact contentJson defensively (Runtime Contract * Rule 1/2/5). Returns null on any structural issue — the caller degrades. */ private parseArtificerArtifact; /** * PRI-911A — WRITER IDENTITY BOUNDARY (I1+I3): the principle id this runner * may carry onto a rule artifact is the bearer artifact's * `sourcePrincipleId` COLUMN, and only when it is a canonical ledger UUID * (shape-gated by the single authority `canonicalLedgerPrincipleId`, plus a * ledger membership check when the host injected one). * * The lenient chain this replaces (column → content.principleId → * content.sourcePrincipleId → principleDraft.title) is exactly how the * philosopher's natural-language title became a durable identity in * `pi_artifacts.source_principle_id` (22 text-valued rows in the Phase 1 * audit). A title is display data: it still lives in contentJson, where the * display surfaces' own lenient resolver reads it — dropping the fallback * here removes a WRITE path, not a read capability. * * Never re-add a content fallback (SPEC §6.2; writer inventory and the * before/after flow live in * `docs/audit/identity-writer-hardening.md`). */ private resolveRuleSourcePrincipleId; /** * Extract structurally-valid positive GoldenTraceCases from the Artificer * goldenTraceCases array. Uses buildGoldenTraceFromArtificer (which re- * validates each case) and filters for kind='positive'. Returns [] when * the input is missing/malformed — the caller degrades to a skip (ERR-069: * never trust unvalidated candidates). */ private extractPositiveCases; /** * PRI-741: host-name parity case. The author's golden trace uses their own * tool-name vocabulary (often generic LLM baseline names); the rule must * reach the SAME decision when the real host dispatches one of ITS names * with the same canonicalKind. The base case is the first NEGATIVE (block) * case — a rule matching by author tool-NAME equality silently never fires * under the host's real name, which shows up exactly as a missed block; * an allow-expectation variant would pass trivially. The host name comes * from the host semantic projection (options.hostSemanticContext — the same * registry provenance as gateDeps); the author kind comes from the core * baseline canonicalizer, so no second semantic truth is introduced. * * Returns null when no variant applies. All skips EXCEPT one are observable * (skip event with structured reason + nextAction, rc-9): a host projection * that is absent/empty, golden cases that are missing or structurally * invalid, a base case without a usable tool name, and no host tool sharing * the author's kind all emit `host_alias_case_skipped`. When the author's * name is ALREADY host-real the variant is redundant (the replay already * exercises the host surface), so that path returns null silently. */ private generateHostAliasCase; /** * PRI-485 Phase 6: derive the v2 adversarial case spec from the Artificer * artifact and generate the 5 canonical v2 cases. * * Spec derivation: * - target kind: canonicalize affectedTools[0], falling back to the first * positive case's toolName when affectedTools is missing/malformed. * - toolName + targetPath: the first positive case of that write kind with * a non-empty params.path or params.file_path, taken from the same case. * * Non-write targets retain the observable custom-case fallback. Missing * complete write examples return a typed failure that stops replay before * auxiliary cases can replace the required templates (Runtime Contract 9). */ private generateV2CasesFromArtificer; /** * Map sandbox failed cases to EvaluatorAdversarialResult.failedCases. * * The sandbox reports failedCases by caseId; we enrich each with the * adversarial case's attackType and expectedDecision. Cases not found in * the adversarial set (e.g. the merged positive case failed — which would * indicate a code bug, not an adversarial failure) are reported with * attackType='boundary' as a safe default and a note in the rationale * (Runtime Contract Rule 9: graceful degradation includes a reason). */ private mapFailedCases; /** * PD v2 Phase 1 closeout (2026-10-11), review-round budget rework: the * failed case's defining input in three INDEPENDENTLY bounded segments — * * 1. identity + context facts (history status, truncated, * priorReadOfTarget, readCount) — written FIRST so a large params * value can never crowd the context evidence out of the summary; * 2. a bounded params digest — every key survives, big values collapse * to a VISIBLE `value…(+N chars)` marker instead of inlining; * 3. the case's authored rationale, bounded. * * Each segment carries its own visible truncation marker, so a truncated * summary never looks complete. Total is capped at the shared * MAX_FAILED_CASE_INPUT_LENGTH (rc-8); the resolver uses the SAME bound, * eliminating the old second silent 300-char cut that dropped context * facts from legal 350-char-param inputs. */ private static summarizeFailedCaseInput; /** * Target-first per-key digest (review round 2, 2026-10-11): the case's * OWN target (path/file_path via the existing resolveCasePathParam * contract) gets an INDEPENDENT budget written FIRST, so legal edit * payloads whose long oldText/newText/content fields precede the target * can no longer evict `protected/…` from the repair input. A long target * truncates visibly inside its own budget; a case with no derivable * target says so explicitly (never guessed, never borrowed). Remaining * keys keep the per-key digest with visible fold markers. The total * params segment stays within the shared MAX_FAILED_CASE_INPUT_LENGTH * arithmetic — the target budget is reserved, not added on top. */ private static digestParams; /** * Assemble and persist the rule artifact when adversarial replay passed * (PRD Decision 5). The rule artifact carries implementationCode + the * Artificer full golden trace + ruleHostGateDecision, with artifactKind='rule'. * After a successful write, marks the artifact 'validated' so RuleHostWriter * can activate it. * * Returns the rule artifactId on success, null on any degradation (missing * code/trace, write failure, validation-update failure). Every null path * emits structured telemetry with a reason (Runtime Rule 9, ERR-018). */ private assembleRuleArtifact; /** * Resolve the principle-bearing artifact that the evaluator should validate. * * Strategy 1: Use scribeArtifactId from sourceTrace (the Scribe artifact * carries principleDraft). * Strategy 2: Search lineage for principle-kind artifacts with principleDraft. * Strategy 3: No principle-bearing artifact found → telemetry, return null. * * Ambiguous candidates (more than 1) → fail loud with telemetry, return null. * Never silently pick the first candidate (ERR-018, ERR-019). */ private resolvePrincipleBearerArtifact; /** * Check if an artifact's contentJson contains principle-bearing content. * Uses Object.hasOwn (ERR-013) and runtime type checks (ERR-001, ERR-005). */ private hasPrincipleDraftContent; private static isRecord; /** * Resolve principle-bearing artifacts from transitive lineage (depth 2). * * For each direct-lineage artifact, resolve its source task's dependencies * and search those artifacts for principle-kind artifacts with principleDraft * content. This handles the common case where the evaluator's direct * dependency is the artificer, and the scribe (principle-bearer) is a * transitive dependency (evaluator → artificer → scribe). * * Bounded to depth 2 to prevent unbounded traversal. Cycle-safe via the * visited set. */ private resolveTransitivePrincipleCandidates; } export {}; //# sourceMappingURL=evaluator-runner.d.ts.map