/** * owner-evidence-recovery.ts — Owner evidence attach/resume (B1, PRI-983). * * SPEC-PD-IAER-001 FR-03 (persist the Owner's evidence selection with * revision/digest/provenance) and FR-06 (query → attach → atomic resume). * This module is the ONE recovery sequence for evidence-waiting tasks; * both CLI entries (`pd runtime internalization evidence attach|resume|list`) * and any future Console face drive it — no logic is duplicated at the * callers (same rule owner-retry.ts established for the NHR out-edge). * * Composition with existing authorities (P2/P4 — nothing new is invented): * - selection shape + readiness judgment: evidence-readiness.ts (A2); * - persisted location: `pi_metadata.ownerEvidenceSelection` inside * diagnosticJson (A2 field; B1 adds the provenance fields INSIDE the * selection object — pitask-metadata carries the object opaquely, the * shared shape authority validates the extended shape, ABSENT = legacy); * - the resume state transition itself: `ownerRetryNeedsHumanReviewTask` * (owner-retry.ts), called with the new optional CAS precondition — * the single recovery authority is reused, never copied; * - digest authority: `computeBehaviorExamplePackDigest` (same bytes the * readiness verdict hashes); * - audit: `.state/recovery_actions.jsonl` append family * (appendOwnerEvidenceAction); * - telemetry: `artificer_owner_evidence_attached` / * `artificer_owner_evidence_resumed` (FR-08, bounded payloads, rc-8), * both carrying the unified FR-08 run-correlation block (PRI-984, * artificer-run-correlation.ts); persistentRunId is semantically absent — * recovery runs against an NHR task / a pending requeue where no run row * exists yet. * * Contract decisions pinned here: * - attach NEVER unblocks: the task stays `needs_human_review`; only the * explicit `resume` authority reset returns it to pending (FR-06 6/7). * - resume re-assembles and compares digests: a mismatch refuses with * `owner_behavior_examples_stale` — a silently stale evidence reference * can never re-enter the model (FR-03). * - FR-06 budget semantics: the authority reset zeroes attemptCount. That * is an EXPLICIT, `--confirm`-gated, digest-verified, audited operator * action — not the forbidden "background silently clears history * attemptCount": previousAttemptCount is recorded in the audit record * and the run history / humanReviewContext stay in place (nothing is * deleted; T05 asserts run rows are preserved). * - concurrency (T06): the reset runs as ONE conditional UPDATE * (`updateTaskIfDiagnosticJsonUnchanged` via the CAS option). Two * concurrent resumes → at most one `resumed`; the loser gets a * structured `conflict` refusal with zero mutation. * - authorization expression (notes §8 reality): confirm-level command + * explicit decidedBy label + append-only audit + revision/digest * provenance. No new authentication mechanism is invented here. * * rc-1/rc-2: the stored selection arrives from parsed diagnosticJson and is * re-validated by the shared shape authority before any use. rc-3: required * preconditions fail loudly with stable reasons + nextAction (rc-9). rc-8: * telemetry payloads carry bounded references only. * * Spec: docs/specs/SPEC-PD-IAER-001-v1.0.md §5 FR-03, FR-06, §6.1-§6.3 */ import type { StoreEventEmitter } from '../store/event-emitter.js'; import type { RuntimeStateManager } from '../store/runtime-state-manager.js'; import { type OwnerEvidenceReasonCode, type OwnerEvidenceSelection } from './evidence-readiness.js'; import { type BehaviorEvidencePort } from './consumer-evidence-preflight.js'; /** * FR-04: a task is "waiting for Owner evidence" iff it sits in * needs_human_review with one of the four stable reason codes in its * humanReviewContext. Other NHR tasks (verdict reviews etc.) are NOT * evidence-waiting and are never touched by this module. */ export declare const EVIDENCE_WAIT_REASON_CODES: ReadonlySet; export interface EvidenceWaitingTaskSummary { readonly taskId: string; readonly channel: string; /** The attached selection's pain id; null while nothing is attached. */ readonly painId: string | null; readonly reasonCode: string; readonly missingFields: readonly string[]; readonly nextAction: string; readonly attemptCount: number; readonly maxAttempts: number; /** Attach revision of the stored selection; 0 = nothing attached. */ readonly evidenceRevision: number; } export declare function listEvidenceWaitingTasks(stateManager: RuntimeStateManager): Promise; export interface AttachOwnerEvidenceInput { readonly taskId: string; /** The Owner-provided selection (base contract fields only). */ readonly selection: Pick; /** Operator label (`--decidedBy`); recorded in provenance + audit. */ readonly decidedBy?: string; readonly mode: 'dry_run' | 'confirm'; /** Injected assembly (production: BehaviorExamplePackAssembler port). */ readonly assemble: BehaviorEvidencePort; /** Workspace root — audit log location. */ readonly workspaceDir: string; readonly eventEmitter: StoreEventEmitter; /** Session label for telemetry (e.g. 'pd-cli:evidence'). */ readonly owner: string; } export type OwnerEvidenceAttachOutcome = { readonly status: 'dry_run'; readonly taskId: string; /** The selection summary that WOULD be persisted (not written). */ readonly wouldPersist: { readonly revision: number; readonly contentDigest: string; readonly ownerOperation: string; readonly attachedAt: string; }; readonly painId: string; readonly nextAction: string; } | { readonly status: 'attached'; readonly taskId: string; readonly revision: number; readonly contentDigest: string; readonly ownerOperation: string; readonly attachedAt: string; /** false = attach landed but the task still blocks (always — attach never unblocks). */ readonly taskStillNeedsHumanReview: true; readonly nextAction: string; } | { readonly status: 'refused'; readonly taskId: string; /** Stable reason: FR-04 code or a structural refusal id. */ readonly reason: string; readonly detail?: string; readonly missingFields?: readonly string[]; readonly nextAction: string; } | { readonly status: 'conflict'; readonly taskId: string; readonly nextAction: string; }; /** * FR-03/FR-06 3-6: assemble + validate the Owner selection, then persist it * (with revision/contentDigest/ownerOperation/attachedAt) into * `pi_metadata.ownerEvidenceSelection` via the narrow diagnosticJson CAS. * Dry-run assembles and reports without writing ANY state (cli-5); failures * refuse with the stable FR-04 reason codes and zero mutation. */ export declare function attachOwnerEvidence(stateManager: RuntimeStateManager, input: AttachOwnerEvidenceInput): Promise; export interface ResumeOwnerEvidenceInput { readonly taskId: string; readonly decidedBy?: string; readonly mode: 'dry_run' | 'confirm'; readonly assemble: BehaviorEvidencePort; readonly workspaceDir: string; readonly eventEmitter: StoreEventEmitter; readonly owner: string; } export type OwnerEvidenceResumeOutcome = { readonly status: 'dry_run'; readonly taskId: string; readonly reasonCode: OwnerEvidenceReasonCode | 'owner_behavior_examples_verified'; readonly previousAttemptCount: number; readonly revision: number; readonly contentDigest: string; readonly painId: string; readonly nextAction: string; } | { readonly status: 'resumed'; readonly taskId: string; readonly previousStatus: 'needs_human_review'; readonly newStatus: 'pending'; readonly previousAttemptCount: number; readonly revision: number; readonly contentDigest: string; readonly nextAction: string; } | { readonly status: 'refused'; readonly taskId: string; readonly reason: string; readonly detail?: string; readonly nextAction: string; } | { readonly status: 'conflict'; readonly taskId: string; readonly nextAction: string; }; /** * FR-06 7/8: digest-verified, atomic, single-authority recovery. * * Sequence: NHR + evidence-code gate → stored selection present → re-assemble * through the shared readiness judgment → digest comparison (mismatch ⇒ * `owner_behavior_examples_stale`, missing digest ⇒ `invalid`) → ONE * conditional authority reset via `ownerRetryNeedsHumanReviewTask` (CAS * option; loser gets `conflict`). Run history / audit records are never * deleted; the attempt budget reset is explicit and audited (see header). */ export declare function resumeOwnerEvidenceTask(stateManager: RuntimeStateManager, input: ResumeOwnerEvidenceInput): Promise; //# sourceMappingURL=owner-evidence-recovery.d.ts.map