/** * Pain Ingress shared invariants + persisted payload contract — PRI-642. * * This module is THE single source for: * - the report field parsers (origin / correlation / evidence / score) * shared by write-time evaluation and persisted-payload re-entry; * - the origin/correlation invariant (`validateOriginCorrelationInvariant`) * — the valid-combination rules of SPEC §8.2 that must never diverge * between write time and re-entry; * - the sentinel session constant (frozen decision #4); * - the versioned `painIngress.v1` persisted namespace (SPEC §9) written * by PainSignalBridge.buildDiagnosticJson beside the legacy top-level * fields, and re-validated by executePendingDiagnosis / retry paths. * * INVARIANT EQUIVALENCE (review blocker 2): re-entry validation is NOT * weaker than write-time validation. parsePainIngressV1Payload shares the * field parsers and the invariant with evaluatePainIngress * (pain-ingress.ts), so every state illegal at write time — sentinel * session ids, empty "available" evidence, impossible origin/correlation * combinations — is also rejected on re-entry. * * Legacy compatibility (SPEC §9): payloads WITHOUT a painIngress.v1 block * keep the tolerant legacy normalization branch in pain-signal-bridge.ts; * this strictness applies only to payloads CLAIMING the v1 namespace. * * Pure module — no I/O. @principles/host-runtime and other adapters depend * on core; core depends on no adapter. */ import type { PainProvenance } from './admission-gate.js'; export declare const PAIN_INGRESS_PAYLOAD_VERSION = "v1"; export type PainIngressOriginV1 = { kind: 'owner_manual'; channel: 'openclaw_command' | 'cli_explicit_session' | 'external_cli_unbound'; } | { kind: 'automatic_hook'; source: string; }; export type PainIngressCorrelationV1 = { status: 'bound'; hostKind: 'openclaw'; sessionId: string; traceId?: string; } | { status: 'bound'; hostKind: 'codex'; rootSessionId: string; rolloutIdentity: string; logicalObservationKey: string; hostTurnId: string; traceId?: string; } | { status: 'unbound'; reason: 'external_cli' | 'missing_host_session'; }; export type PainIngressEvidenceClassV1 = { status: 'available'; entryCount: number; } | { status: 'unavailable'; reason: 'trajectory_unavailable' | 'session_not_found' | 'empty_trajectory' | 'evidence_read_failed' | 'evidence_invalid' /** Unbound Owner reports never consult a trajectory — no evidence is sought. */ | 'not_applicable_unbound'; }; export type PainEvidenceUnavailableReason = 'trajectory_unavailable' | 'session_not_found' | 'empty_trajectory' | 'evidence_read_failed' | 'evidence_invalid' /** Unbound Owner reports never consult a trajectory — no evidence is sought. */ | 'not_applicable_unbound'; export interface IngressEvidenceEntry { kind: 'behavior_trace' | 'system_event'; sourceRef: string; note: string; } export type PainEvidenceBundle = { status: 'available'; entries: readonly [IngressEvidenceEntry, ...IngressEvidenceEntry[]]; } | { status: 'unavailable'; reason: PainEvidenceUnavailableReason; }; export type PainOrigin = PainIngressOriginV1; export type PainCorrelation = PainIngressCorrelationV1; export interface PainIngressReport { identity: { kind: 'manual_pain_id'; painId: string; } | { kind: 'host_observation'; observationId: string; }; painType: 'tool_failure' | 'subagent_error' | 'user_frustration'; source: string; reason: string; score?: number; origin: PainOrigin; correlation: PainCorrelation; evidence: PainEvidenceBundle; } export declare function isRecord(value: unknown): value is Record; /** * Sentinel session ids are never real correlations (frozen decision #4). * Import this constant instead of re-declaring the set in another module. */ export declare const SENTINEL_SESSION_IDS: ReadonlySet; export declare function isSentinelSessionId(value: string): boolean; export declare function parseOrigin(value: unknown): PainOrigin | null; /** * THE correlation parser — used by report validation (write time) and the * persisted-payload validator (re-entry) so sentinel rejection and lineage * completeness cannot drift between the two. */ export declare function parseCorrelation(value: unknown): { value: PainCorrelation; error?: undefined; } | { value?: undefined; error: string; }; export declare function parseEvidence(value: unknown): { value: PainEvidenceBundle; error?: undefined; } | { value?: undefined; error: string; }; /** rc-3/rc-9: a present-but-invalid score is rejected, not silently dropped. */ export declare function parseScore(value: unknown): { score: number | undefined; scoreInvalid?: undefined; } | { score: undefined; scoreInvalid: true; }; export type PainIngressParseResult = { ok: true; report: PainIngressReport; } | { ok: false; reasonCode: string; message: string; }; /** * Validate an untrusted report (parsed JSON / host payload) into a typed * PainIngressReport. Fails loud with a structured reasonCode (rc-3); uses * the SAME field parsers and invariant as the persisted-payload validator * (rc-1/rc-2/rc-3/rc-4). */ export declare function parsePainIngressReport(input: unknown): PainIngressParseResult; /** * Origin/correlation combinations the matrix (SPEC §8.2) declares invalid. * Shared by evaluatePainIngress (write time) and parsePainIngressV1Payload * (re-entry) so an illegal state can never become legal across a * persistence round trip. Returns a reasonCode string when invalid, null * when the combination is valid. */ export declare function validateOriginCorrelationInvariant(origin: PainOrigin, correlation: PainCorrelation): string | null; /** * The persisted v1 block. Evidence ENTRIES are intentionally not duplicated * here — they remain in the legacy top-level `evidence` field; this block * records their validated classification only (SPEC §9: one versioned * namespace for the rev-2 facts, legacy fields stay authoritative for * their own consumers). */ export interface PainIngressV1Payload { version: typeof PAIN_INGRESS_PAYLOAD_VERSION; origin: PainIngressOriginV1; correlation: PainIngressCorrelationV1; evidenceClass: PainIngressEvidenceClassV1; } export type PainIngressV1ParseResult = { ok: true; payload: PainIngressV1Payload; } | { ok: false; reasonCode: string; }; /** * Runtime validation of an untrusted persisted painIngress block * (rc-1/rc-2/rc-3). Shares parseOrigin/parseCorrelation and the * origin/correlation invariant with the write-time evaluator, so sentinel * sessions, incomplete Codex lineage and impossible combinations are * rejected identically at write time and on re-entry. */ export declare function parsePainIngressV1Payload(value: unknown): PainIngressV1ParseResult; /** * Derive the legacy provenance from validated rev-2 facts (SPEC §8.3). * Adapters do not supply provenance independently; this is the one * derivation shared by writers and re-entry validation. */ export declare function deriveProvenanceFromIngressFacts(origin: PainIngressOriginV1, correlation: PainIngressCorrelationV1): PainProvenance; /** * Re-entry consistency check between the nested v1 block and the legacy * top-level fields produced by the same builder (SPEC §9, §12.2.4). * Returns null when consistent; a reasonCode string otherwise. * * Coverage (review blocker 2 closure): every dimension of the * nested/top-level contract is asserted — provenance, host session id, * evidence count, evidence availability class, and host binding * state. A v1 block with unavailable evidence cannot coexist with * legacy top-level entries; an unbound v1 block cannot coexist with * a legacy sessionIdHint. */ export declare function checkIngressTopLevelConsistency(input: { payload: PainIngressV1Payload; topLevelProvenance: unknown; topLevelSessionIdHint: unknown; topLevelEvidenceCount: number; }): string | null; //# sourceMappingURL=pain-ingress-payload.d.ts.map