@fabricorg/platform-host 8.8.0 → 8.10.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/index.d.cts CHANGED
@@ -1,5 +1,79 @@
1
1
  import { ActionDefinition, ActionId, ActionStatus, ActorType, AdapterCircuitBreakerProvider, AdapterImplementation, AgentActionPolicyContext, AgentActionPolicyDecision, AgentActionRiskTier, AgentActionRoute, AssetEventEnvelope, EvidencePacketReference, ExecutionAttestation, ExecutionPrincipal, ExternalReconciliation, FabricRuntimeServices, GovernanceRuntimeEvidence, ModuleRegistry, MutationFootprint, MutationGovernanceResolver, PolicyObligation, PolicyObligationStatus, PolicyOutcome, RuntimePolicyDefinition, StagedInvocationRequest, canonicalJson } from "@fabricorg/platform";
2
2
  import { AssemblyLockfile } from "@fabricorg/assembly";
3
+ //#region src/erasure-types.d.ts
4
+ type StoredRecordErasureTarget = {
5
+ readonly kind: "invocation";
6
+ readonly invocationId: string;
7
+ } | {
8
+ readonly kind: "event";
9
+ readonly eventId: string;
10
+ };
11
+ interface ErasurePayloadFields {
12
+ invocation: "parameters" | "result" | "error" | "provenance" | "authorizationBinding" | "authorizationReconciliation" | "adapterReconciliation" | "hitlReason" | "approvalDecision" | "mutationFootprint" | "executionPrincipal" | "runtimeEvidence" | "pendingCompletion" | "externalCompletion" | "pendingStateTransition" | "finalizationCheckpoint" | "approvalExpiry" | "agentProposal" | "submissionRefusal";
13
+ adapter: "input" | "output" | "error" | "reconciliation";
14
+ staged: "parameters" | "lastError";
15
+ policy_evaluation: "outcome";
16
+ policy_obligation: "description" | "evidenceReferences" | "metadata";
17
+ execution_attestation: "attestation";
18
+ external_reconciliation: "reconciliation";
19
+ event: "payload" | "provenance";
20
+ outbox: "lastError" | "traceContext" | "payload" | "provenance";
21
+ outbox_requeue: "reason" | "priorLastError";
22
+ }
23
+ type ErasureRecordKind = keyof ErasurePayloadFields;
24
+ /** Protected nested keys are omitted from the callback; stores restore structure and overwrite payload digests. */
25
+ type StoredRecordErasureView = { [K in ErasureRecordKind]: {
26
+ readonly kind: K;
27
+ readonly id: string;
28
+ readonly tenantId: string;
29
+ readonly spaceId: string;
30
+ readonly invocationId?: string;
31
+ readonly eventId?: string;
32
+ readonly eventType?: string;
33
+ fields: Partial<Record<ErasurePayloadFields[K], unknown>>;
34
+ }; }[ErasureRecordKind];
35
+ /** Validated identifying facts sent to the application authorizer; no stored payload or callback. */
36
+ interface StoredRecordErasureAuthorizationInput {
37
+ /** Trusted operator identity supplied by the authenticated application session. */
38
+ readonly actorId: string;
39
+ readonly actorType: ActorType;
40
+ readonly tenantId: string;
41
+ readonly spaceId: string;
42
+ readonly requestId: string;
43
+ readonly targets: readonly StoredRecordErasureTarget[];
44
+ }
45
+ interface EraseStoredRecordsInput extends StoredRecordErasureAuthorizationInput {
46
+ /** Target-lock acquisition budget in milliseconds, 1–60000; default 250. */
47
+ readonly lockTimeoutMs?: number;
48
+ /** Rewrite work budget in milliseconds, 1–60000; default 5000. */
49
+ readonly workTimeoutMs?: number;
50
+ /** Synchronous JSON-data transformation. A thrown error or invalid result rolls back the batch. */
51
+ readonly redact: (record: StoredRecordErasureView) => StoredRecordErasureView;
52
+ }
53
+ type StoredRecordErasureOutcome = "erased" | "already_erased" | "active" | "not_found";
54
+ interface EraseStoredRecordsResult {
55
+ targets: Array<StoredRecordErasureTarget & {
56
+ outcome: StoredRecordErasureOutcome;
57
+ }>;
58
+ /** Records processed, including records processed again by repeat erasure. */
59
+ counts: Partial<Record<ErasureRecordKind, number>>;
60
+ }
61
+ interface ErasablePlatformHostStore<TDb> extends PlatformHostStore<TDb> {
62
+ /**
63
+ * Unauthenticated storage port, not an authorization boundary; callers must enforce authorization.
64
+ * Atomic rewrite/marker/audit operation. The audit retains actorId/actorType as identifying metadata.
65
+ * Invocation reads expose storedRecordsErased after commit.
66
+ */
67
+ eraseStoredRecords(input: EraseStoredRecordsInput): Promise<EraseStoredRecordsResult>;
68
+ }
69
+ interface StoredRecordErasureHost {
70
+ /** Throws StoredRecordErasureAuthorizationError (not_authorized) before store access on authorization refusal. */
71
+ eraseStoredRecords(input: EraseStoredRecordsInput): Promise<EraseStoredRecordsResult>;
72
+ }
73
+ export declare const HOST_RECORDS_ERASED_EVENT: "HostRecordsErased";
74
+ /** Sentinel replacing required payload digests after stored-record erasure. */
75
+ export declare const ERASED_DIGEST: "erased";
76
+ //#endregion
3
77
  //#region src/observability.d.ts
4
78
  /** Version of the vendor-neutral health/readiness contract. */
5
79
  export declare const PLATFORM_HOST_HEALTH_CONTRACT_VERSION: 1;
@@ -203,30 +277,60 @@ export declare class IdempotencyConflictError extends Error {
203
277
  export type ActionAdmissionRefusal = "refused" | "failed" | "timed_out" | "invalid_decision" | "invalid_parameters";
204
278
  /**
205
279
  * Thrown by `submitAction` when a declared admission check did not admit the
206
- * submission. Host wrote nothing for it: no invocation, idempotency key,
207
- * event, policy record or error. The message holds the action id, the
208
- * refusal and, for `refused`, the check's code; never a submitted value or
209
- * anything the check threw.
280
+ * submission. By default Host writes nothing for it. With recordRefusals
281
+ * enabled and an idempotency key, a definitive refusal can be recorded;
282
+ * `recorded` confirms persistence of the terminal refusal. The message holds
283
+ * the action id, the refusal and, for `refused`, the check's fixed code. It omits parameter
284
+ * payloads and caught callback error text.
210
285
  */
211
286
  export declare class ActionAdmissionError extends Error {
212
287
  readonly actionId: string;
213
288
  readonly refusal: ActionAdmissionRefusal;
214
289
  /** The check's own code, present only for `refused`. */
215
290
  readonly refusalCode?: string | undefined;
291
+ readonly recorded: boolean;
216
292
  readonly code: "ACTION_ADMISSION_REFUSED";
217
293
  constructor(actionId: string, refusal: ActionAdmissionRefusal,
218
294
  /** The check's own code, present only for `refused`. */
219
- refusalCode?: string | undefined);
295
+ refusalCode?: string | undefined, recorded?: boolean);
220
296
  }
221
297
  export type SubmissionRefusal = "entitlement" | "authorization";
222
298
  /** A definitive authorization-boundary refusal before an invocation is created. */
223
299
  export declare class SubmissionRefusalError extends Error {
224
300
  readonly actionId: string;
225
301
  readonly refusal: SubmissionRefusal;
302
+ readonly recorded: boolean;
226
303
  readonly code: "ACTION_SUBMISSION_REFUSED";
227
- constructor(actionId: string, refusal: SubmissionRefusal, message: string);
304
+ constructor(actionId: string, refusal: SubmissionRefusal, message: string, recorded?: boolean);
305
+ }
306
+ /** Fixed refusal evidence; no submitted values belong in a descriptor. */
307
+ export type SubmissionRefusalDescriptor = {
308
+ class: "entitlement" | "authorization";
309
+ } | {
310
+ class: "admission";
311
+ refusal: "refused" | "invalid_parameters";
312
+ refusalCode?: string;
313
+ };
314
+ /** Minimal identity and evidence for an insert-only, terminal refusal. */
315
+ export interface RecordSubmissionRefusalInput {
316
+ id: string;
317
+ tenantId: string;
318
+ spaceId: string;
319
+ actionId: ActionId;
320
+ actionVersion: number;
321
+ actorId: string;
322
+ actorType: ActorType;
323
+ authorizationBindingId?: string;
324
+ idempotencyKey: string;
325
+ submissionRefusal: SubmissionRefusalDescriptor;
326
+ parameterDigest?: string;
327
+ parameterDigestAlgorithm?: "fabric-canonical-json-sha256-v1";
328
+ correlationId: string;
329
+ causationId?: string;
330
+ createdAt: Date;
331
+ updatedAt: Date;
228
332
  }
229
- export type ExternalCompletionRefusal = "still_executing" | "not_found" | "not_awaiting" | "immediate_contract" | "reference_mismatch" | "provider_mismatch" | "invalid_completion" | "result_invalid" | "redaction_failed";
333
+ export type ExternalCompletionRefusal = "erased" | "still_executing" | "not_found" | "not_awaiting" | "immediate_contract" | "reference_mismatch" | "provider_mismatch" | "invalid_completion" | "result_invalid" | "redaction_failed";
230
334
  /**
231
335
  * Why a completion was not applied, typed so a webhook handler can answer
232
336
  * the external system correctly: `retryable` means the invocation is still
@@ -292,6 +396,9 @@ export interface HostHitlEvaluatorContext extends AgentActionPolicyContext {
292
396
  invocation: Readonly<ActionInvocationRecord>;
293
397
  }
294
398
  export interface ActionInvocationRecord {
399
+ /** Store-owned erasure marker; callers cannot patch it through Host write types. */
400
+ readonly storedRecordsErased?: true;
401
+ submissionRefusal?: SubmissionRefusalDescriptor;
295
402
  readonly agentProposal?: AgentProposal;
296
403
  id: string;
297
404
  tenantId: string;
@@ -342,7 +449,7 @@ export interface ActionInvocationRecord {
342
449
  externalCompletion?: RecordedExternalCompletion;
343
450
  /** Opaque, non-secret reference used to revalidate delegated execution authority. */
344
451
  authorizationBindingId?: string;
345
- /** Worker claims including the first; approval acceptance sets a minimum of 1. */
452
+ /** Worker claims including the first; inline approval acceptance sets a minimum of 1. Recording preserves the count. */
346
453
  attemptCount: number;
347
454
  /** Latest claim durably deferred in pre-effect governance; permits only the immediately next claim. */
348
455
  deferredBeforeEffectAttempt?: number;
@@ -405,6 +512,7 @@ export declare const MAX_APPROVAL_EVIDENCE_BYTES: number;
405
512
  export declare const APPROVAL_REQUESTED_EVENT: "ApprovalRequested";
406
513
  export declare const APPROVAL_DECIDED_EVENT: "ApprovalDecided";
407
514
  export declare const APPROVAL_EXPIRED_EVENT: "ApprovalExpired";
515
+ export declare const APPROVAL_DECISION_REFUSED_EVENT: "ApprovalDecisionRefused";
408
516
  export interface ApprovalRequestedPayload {
409
517
  route: AgentActionRoute;
410
518
  riskTier: AgentActionRiskTier;
@@ -435,6 +543,10 @@ export interface ActionApprovalDecision {
435
543
  }
436
544
  export interface PersistedActionApprovalDecision extends Omit<ActionApprovalDecision, "decidedAt"> {
437
545
  decidedAt: Date;
546
+ /** Caller-stable identity for the separate recording API; absent on inline and older decisions. */
547
+ requestId?: string;
548
+ /** Claim count when recorded; the next claim is the approved first execution. */
549
+ recordedAtAttempt?: number;
438
550
  }
439
551
  export interface PolicyEvaluationRecord {
440
552
  id: string;
@@ -554,7 +666,7 @@ export interface AdapterInvocationRecord {
554
666
  createdAt: Date;
555
667
  updatedAt: Date;
556
668
  }
557
- export interface CreateActionInvocationInput extends Omit<ActionInvocationRecord, "createdAt" | "updatedAt" | "attemptCount" | "leaseOwner" | "leaseExpiresAt" | "leaseToken" | "stagedOrigin"> {}
669
+ export interface CreateActionInvocationInput extends Omit<ActionInvocationRecord, "createdAt" | "updatedAt" | "attemptCount" | "leaseOwner" | "leaseExpiresAt" | "leaseToken" | "stagedOrigin" | "storedRecordsErased"> {}
558
670
  /** Scope of an adapter checkpoint within its owning action invocation. */
559
671
  export interface AdapterInvocationScope {
560
672
  tenantId: string;
@@ -603,6 +715,7 @@ export interface AdapterInvocationWriteInput {
603
715
  };
604
716
  }
605
717
  export interface PlatformHostStore<TDb> {
718
+ getSubmissionOutcomeByKey?(key: Readonly<SubmissionOutcomeKey>): Promise<StoredSubmissionOutcomeRecord | undefined>;
606
719
  readonly db: TDb;
607
720
  /**
608
721
  * Run against the store's domain handle. This base seam does not promise a
@@ -610,7 +723,17 @@ export interface PlatformHostStore<TDb> {
610
723
  * canonical events, and invocation state must commit atomically.
611
724
  */
612
725
  transaction<TResult>(run: (db: TDb) => Promise<TResult>): Promise<TResult>;
726
+ /**
727
+ * First row wins for a supplied idempotency key within tenant, space and action id,
728
+ * including the empty-string key. Replays return the existing row unchanged;
729
+ * the store does not classify submission conflicts (Host does). Unkeyed creates
730
+ * with distinct ids insert independently. `invocationIdempotencyChecks` also
731
+ * covers concurrent key arbitration and preservation of `admittedAt` through
732
+ * an update of another field, plus refusal/admitted arbitration when supported.
733
+ */
613
734
  createActionInvocation(input: CreateActionInvocationInput): Promise<ActionInvocationRecord>;
735
+ /** Insert a refusal or return the key's existing winner unchanged. */
736
+ recordSubmissionRefusal?(input: RecordSubmissionRefusalInput): Promise<ActionInvocationRecord>;
614
737
  getActionInvocation(id: string, tenantId: string, spaceId: string): Promise<ActionInvocationRecord | undefined>;
615
738
  /** Resolve Host-owned staged identity within one tenant and space. */
616
739
  getActionInvocationByStagedOrigin?(tenantId: string, spaceId: string, origin: StagedInvocationOrigin): Promise<ActionInvocationRecord | undefined>;
@@ -684,8 +807,10 @@ export interface PlatformHostStore<TDb> {
684
807
  * back when `run` throws.
685
808
  */
686
809
  export interface PlatformHostMutationTransaction<TDb> {
810
+ /** Lock due approval candidates in this transaction, skipping locked invocation rows. */
811
+ lockDueApprovals?(input: ExpireDueApprovalsStoreInput): Promise<ActionInvocationRecord[]>;
687
812
  /** Conditional SQL approval write on the transaction connection; lifecycle events are appended by the caller in this unit of work. */
688
- beginApprovalDecision?(input: Omit<BeginApprovalDecisionInput, "lifecycleEvent">): Promise<ApprovalDecisionTransitionResult>;
813
+ beginApprovalDecision?(input: Omit<BeginApprovalDecisionInput<TDb>, "lifecycleEvent">): Promise<ApprovalDecisionTransitionResult>;
689
814
  readonly db: TDb;
690
815
  /**
691
816
  * Read and lock an invocation until this transaction completes.
@@ -694,9 +819,12 @@ export interface PlatformHostMutationTransaction<TDb> {
694
819
  getActionInvocationForUpdate?(id: string, tenantId: string, spaceId: string): Promise<ActionInvocationRecord | undefined>;
695
820
  /**
696
821
  * Read one event by its stable identity inside the transaction.
822
+ * Erasure-capable stores expose storedRecordsErased on rewritten events.
697
823
  * Required by serialized saga parent lifecycle transitions.
698
824
  */
699
- getEvent?(id: string, tenantId: string, spaceId: string): Promise<AssetEventEnvelope | undefined>;
825
+ getEvent?(id: string, tenantId: string, spaceId: string): Promise<(AssetEventEnvelope & {
826
+ readonly storedRecordsErased?: true;
827
+ }) | undefined>;
700
828
  /**
701
829
  * First-write-wins: an event ID the store already holds, in any tenant or space, is a no-op
702
830
  * that keeps the first event. Do not throw, replace or merge it, regardless of
@@ -747,7 +875,7 @@ export interface PlatformHostMutationTransaction<TDb> {
747
875
  * governance evidence cannot share the event/verdict transaction.
748
876
  */
749
877
  appendExternalReconciliation?: GovernancePlatformHostStore<TDb>["appendExternalReconciliation"];
750
- /** Atomically retain an immutable staged batch with the parent mutation. */
878
+ /** Atomically retain a staged batch with the parent mutation; erasure may later rewrite its payloads. */
751
879
  stageInvocations?(records: readonly StagedInvocationRecord[]): Promise<void>;
752
880
  }
753
881
  export type StagedInvocationStatus = "pending" | "dispatched" | "rejected" | "dead_lettered" | "cancelled";
@@ -970,8 +1098,31 @@ export interface OutboxPlatformHostStore<TDb> extends PlatformHostStore<TDb> {
970
1098
  * sequences and timestamps and potentially different payloads.
971
1099
  */
972
1100
  appendEventWithOutbox(event: AssetEventEnvelope, metadata: OutboxEventMetadata): Promise<void>;
1101
+ /**
1102
+ * `outboxRelayChecks`: claim pending rows available at or before `now` with no
1103
+ * unexpired lease; equality at lease expiry is claimable. Concurrent workers
1104
+ * claim a row once between them. A claim sets owner and expiry to the worker
1105
+ * and `now + leaseDurationMs`, increments attempts by one and increases the
1106
+ * row's token. `limit: 1` returns one row when eligible rows exist.
1107
+ * Published and dead-lettered rows are excluded; returned ordering is unspecified.
1108
+ * `outboxRelayChecks` requires numeric lease tokens that increase on each claim,
1109
+ * including reclaim, despite the optional token fields in the general interfaces.
1110
+ */
973
1111
  claimOutbox(input: ClaimOutboxInput): Promise<OutboxRecord[]>;
1112
+ /**
1113
+ * `outboxRelayChecks`: reject a stale token after reclaim or a wrong worker
1114
+ * without changing the row. With the current owner/token, set `published`
1115
+ * and `publishedAt` and clear lease owner/expiry. A previously settled row
1116
+ * rejects another settlement under its former owner/token.
1117
+ */
974
1118
  markOutboxPublished(id: string, workerId: string, publishedAt: Date, leaseToken?: number): Promise<void>;
1119
+ /**
1120
+ * `outboxRelayChecks`: reject a stale token after reclaim or a wrong worker
1121
+ * without changing the row. With the current owner/token, set `pending` and
1122
+ * the supplied `availableAt`, clear lease owner/expiry and retain attempts;
1123
+ * `deadLetter: true` sets `dead_letter`. A previously settled row rejects
1124
+ * another settlement under its former owner/token.
1125
+ */
975
1126
  markOutboxFailed(input: {
976
1127
  id: string;
977
1128
  workerId: string;
@@ -980,6 +1131,13 @@ export interface OutboxPlatformHostStore<TDb> extends PlatformHostStore<TDb> {
980
1131
  availableAt: Date;
981
1132
  deadLetter: boolean;
982
1133
  }): Promise<void>;
1134
+ /**
1135
+ * `outboxRelayChecks`: return false for a wrong worker/token or a lease expired
1136
+ * at or before `now`, leaving expiry unchanged. For the current unexpired
1137
+ * owner/token, return true and set expiry to `now + leaseDurationMs`.
1138
+ * When `renewOutboxLeaseAndRead` is present, the same probes expect undefined
1139
+ * on refusal and the renewed record on success.
1140
+ */
983
1141
  renewOutboxLease(input: {
984
1142
  id: string;
985
1143
  workerId: string;
@@ -987,6 +1145,14 @@ export interface OutboxPlatformHostStore<TDb> extends PlatformHostStore<TDb> {
987
1145
  leaseDurationMs: number;
988
1146
  now?: Date;
989
1147
  }): Promise<boolean>;
1148
+ /** Renew and hydrate the stored envelope atomically for erasure-safe publication. */
1149
+ renewOutboxLeaseAndRead?(input: {
1150
+ id: string;
1151
+ workerId: string;
1152
+ leaseToken: number;
1153
+ leaseDurationMs: number;
1154
+ now?: Date;
1155
+ }): Promise<OutboxRecord | undefined>;
990
1156
  listOutbox(input?: {
991
1157
  tenantId?: string;
992
1158
  spaceId?: string;
@@ -1120,6 +1286,15 @@ export interface OutboxRequeueAuthorizationInput {
1120
1286
  * but cannot claim atomic domain-write/event persistence.
1121
1287
  */
1122
1288
  export interface AtomicMutationPlatformHostStore<TDb> extends PlatformHostStore<TDb> {
1289
+ /**
1290
+ * Optional retry evidence: reject with ConfirmedTransactionRollback(callbackError)
1291
+ * after rollback succeeds. Its cause must be the identical callback error. A provider
1292
+ * that does not supply this evidence retains the existing failure behavior.
1293
+ * A commit error is not evidence that the callback rolled back. Built-in stores
1294
+ * expose evidence for Host's internal handler callback request; ordinary public
1295
+ * callbacks and store-internal transactions retain their original error objects.
1296
+ * Host normalizes confirmation from custom stores at non-handler transaction sites.
1297
+ */
1123
1298
  transactionWithEvents<TResult>(run: (transaction: PlatformHostMutationTransaction<TDb>) => Promise<TResult>): Promise<TResult>;
1124
1299
  }
1125
1300
  export interface ApprovalLifecycleEvent {
@@ -1128,6 +1303,50 @@ export interface ApprovalLifecycleEvent {
1128
1303
  }
1129
1304
  /** Called within the transition transaction. Must be pure and return audit-safe data. Sequence is assigned by the store. */
1130
1305
  export type ApprovalLifecycleEventFactory = (invocation: ActionInvocationRecord, kind: "requested" | "decided" | "expired") => ApprovalLifecycleEvent;
1306
+ export interface RecordApprovalDecisionRequest extends ActionApprovalDecision {
1307
+ /** Non-blank caller-stable identifier, at most 256 characters; not trimmed or regenerated by Host. */
1308
+ requestId: string;
1309
+ }
1310
+ export type RecordApprovalDecisionOutcome = "approved" | "denied" | "unauthorized" | "expired" | "already_decided" | "late_terminal" | "not_waiting" | "unsupported";
1311
+ export interface RecordApprovalDecisionResult {
1312
+ outcome: RecordApprovalDecisionOutcome;
1313
+ actionInvocationId: string;
1314
+ status: ActionInvocationRecord["status"];
1315
+ replayed: boolean;
1316
+ /** Included for approved/denied outcomes, including replays; omitted for non-applied outcomes. */
1317
+ decision?: PersistedActionApprovalDecision;
1318
+ }
1319
+ export interface TransactionalApprovalAuthorization<TDb = unknown> {
1320
+ (db: TDb, invocation: Readonly<ActionInvocationRecord>): Promise<boolean>;
1321
+ }
1322
+ export interface RecordApprovalDecisionStoreInput<TDb = unknown> {
1323
+ actionInvocationId: string;
1324
+ tenantId: string;
1325
+ spaceId: string;
1326
+ now: Date;
1327
+ /** Carries no `recordedAtAttempt`: the store assigns it from the locked row. */
1328
+ decision: Omit<PersistedActionApprovalDecision, "recordedAtAttempt"> & {
1329
+ requestId: string;
1330
+ };
1331
+ /** Result of the compatibility authorization boundary evaluated before the transaction. */
1332
+ authorized: boolean;
1333
+ /**
1334
+ * Optional authority check on this transaction's `db`, after the invocation row is locked
1335
+ * and classified as writable, immediately before the decision write.
1336
+ */
1337
+ authorizeInTransaction?: TransactionalApprovalAuthorization<TDb>;
1338
+ actionKind: "atomic" | "saga" | "unknown";
1339
+ lifecycleEvent?: ApprovalLifecycleEventFactory;
1340
+ /** Called under the invocation lock after any update, with the pre-patch observed row. */
1341
+ attemptEvent?: (invocation: ActionInvocationRecord, outcome: RecordApprovalDecisionOutcome) => ApprovalLifecycleEvent;
1342
+ }
1343
+ export interface RecordApprovalDecisionStoreResult {
1344
+ /** Whether this transaction changed the invocation; false for replays and previously expired rows. */
1345
+ applied: boolean;
1346
+ outcome: RecordApprovalDecisionOutcome;
1347
+ invocation: ActionInvocationRecord;
1348
+ replayed: boolean;
1349
+ }
1131
1350
  export interface ExpireDueApprovalsInput {
1132
1351
  tenantId?: string;
1133
1352
  spaceId?: string;
@@ -1148,7 +1367,7 @@ export interface ParkApprovalInput {
1148
1367
  leaseToken?: number;
1149
1368
  lifecycleEvent?: ApprovalLifecycleEventFactory;
1150
1369
  }
1151
- export interface BeginApprovalDecisionInput {
1370
+ export interface BeginApprovalDecisionInput<TDb = unknown> {
1152
1371
  lifecycleEvent?: ApprovalLifecycleEventFactory;
1153
1372
  actionInvocationId: string;
1154
1373
  tenantId: string;
@@ -1157,15 +1376,26 @@ export interface BeginApprovalDecisionInput {
1157
1376
  leaseOwner: string;
1158
1377
  leaseDurationMs: number;
1159
1378
  now: Date;
1379
+ /**
1380
+ * Optional authority check on this transaction's `db`, after the invocation row is locked
1381
+ * and classified as writable, immediately before the decision write.
1382
+ */
1383
+ authorizeInTransaction?: TransactionalApprovalAuthorization<TDb>;
1160
1384
  }
1161
1385
  export interface ApprovalDecisionTransitionResult {
1162
1386
  applied: boolean;
1163
1387
  invocation?: ActionInvocationRecord;
1388
+ /** True when a configured in-transaction authority check refused the decision. */
1389
+ authorizationDenied?: boolean;
1164
1390
  }
1165
- /** Optional store capability required only when agent HITL or approval resume is used. */
1391
+ /** Approval persistence capabilities used by Host HITL, inline resume and decision recording. */
1166
1392
  export interface ApprovalPlatformHostStore<TDb> extends PlatformHostStore<TDb> {
1393
+ /** Serialize classification, optional events/outbox and the queued/terminal transition in one transaction. */
1394
+ recordApprovalDecision?(input: RecordApprovalDecisionStoreInput<TDb>): Promise<RecordApprovalDecisionStoreResult>;
1167
1395
  /** True only when approval transitions and lifecycle outbox events share a transaction. */
1168
1396
  readonly transactionalApprovalLifecycle?: boolean;
1397
+ /** True only when approval authority callbacks run under the locked row in the decision transaction. */
1398
+ readonly transactionalApprovalAuthorization?: boolean;
1169
1399
  /**
1170
1400
  * Conditionally park an executable invocation only with its matching lease owner/generation
1171
1401
  * (when leased) and no prior approval decision. Persist the deadline supplied in input.
@@ -1195,7 +1425,7 @@ export interface ApprovalPlatformHostStore<TDb> extends PlatformHostStore<TDb> {
1195
1425
  * or expired event/outbox writes and state change must commit in the same atomic unit
1196
1426
  * of work, or roll back together. Required when implementing parkApproval/expireDueApprovals.
1197
1427
  */
1198
- beginApprovalDecision(input: BeginApprovalDecisionInput): Promise<ApprovalDecisionTransitionResult>;
1428
+ beginApprovalDecision(input: BeginApprovalDecisionInput<TDb>): Promise<ApprovalDecisionTransitionResult>;
1199
1429
  }
1200
1430
  /** Optional durable capabilities used when external-governance enrichment is configured. */
1201
1431
  export interface GovernancePlatformHostStore<TDb> extends PlatformHostStore<TDb> {
@@ -1294,6 +1524,10 @@ export interface ActionExecutionAuthorizationInput extends ActionAuthorizationIn
1294
1524
  invocation: Readonly<ActionInvocationRecord>;
1295
1525
  executionReason: ActionExecutionReason;
1296
1526
  }
1527
+ /** Execution authorization input evaluated on the handler transaction's domain handle. */
1528
+ export interface ActionExecutionAuthorizationInTransactionInput<TDb> extends ActionExecutionAuthorizationInput {
1529
+ db: TDb;
1530
+ }
1297
1531
  /** Trusted authorization input for deciding a parked approval. */
1298
1532
  export interface ActionApprovalAuthorizationInput extends ActionAuthorizationInput {
1299
1533
  decision: ActionApprovalDecision;
@@ -1302,6 +1536,10 @@ export interface ActionApprovalAuthorizationInput extends ActionAuthorizationInp
1302
1536
  /** The durable (post-redaction) invocation record as read before the decision transition. */
1303
1537
  invocation: Readonly<ActionInvocationRecord>;
1304
1538
  }
1539
+ /** Approval authorization input evaluated while the invocation row is locked for its decision write. */
1540
+ export interface ActionApprovalAuthorizationInTransactionInput<TDb> extends ActionApprovalAuthorizationInput {
1541
+ db: TDb;
1542
+ }
1305
1543
  /**
1306
1544
  * Input to `resolvePolicies`. Carries the invocation identity and the
1307
1545
  * schema-parsed durable (post-redaction) parameters so a resolver can choose
@@ -1312,7 +1550,7 @@ export interface ActionApprovalAuthorizationInput extends ActionAuthorizationInp
1312
1550
  export interface ResolvePoliciesInput extends ActionExecutionAuthorizationInput {
1313
1551
  declaredPolicyIds: readonly string[];
1314
1552
  }
1315
- export interface PlatformHostAuthorization {
1553
+ export interface PlatformHostAuthorization<TDb = unknown> {
1316
1554
  checkEntitlement(input: ActionAuthorizationInput): Promise<boolean>;
1317
1555
  authorize(input: ActionAuthorizationInput): Promise<boolean>;
1318
1556
  /**
@@ -1320,14 +1558,25 @@ export interface PlatformHostAuthorization {
1320
1558
  * execution. When absent, the Host reuses `authorize` at this boundary.
1321
1559
  */
1322
1560
  authorizeExecution?(input: ActionExecutionAuthorizationInput): Promise<boolean>;
1561
+ /** Optional final execution authority check on the handler transaction, immediately before the effect. */
1562
+ authorizeExecutionInTransaction?(input: ActionExecutionAuthorizationInTransactionInput<TDb>): Promise<boolean>;
1323
1563
  /** Optional distinct authorization boundary for human/system approval decisions. */
1324
1564
  authorizeApproval?(input: ActionApprovalAuthorizationInput): Promise<boolean>;
1565
+ /** Optional final approval authority check under the locked row in the decision transaction. */
1566
+ authorizeApprovalInTransaction?(input: ActionApprovalAuthorizationInTransactionInput<TDb>): Promise<boolean>;
1325
1567
  /**
1326
1568
  * Authorization for `requeueOutboxDeadLetter`. There is no action to fall
1327
1569
  * back to, so when this is absent every requeue is refused as
1328
1570
  * `not_authorized`. A throw propagates and nothing is requeued.
1329
1571
  */
1330
1572
  authorizeOutboxRequeue?(input: OutboxRequeueAuthorizationInput): Promise<boolean>;
1573
+ /**
1574
+ * Erasure operator authorization after validation and before store access. No fallback:
1575
+ * absence or a result other than true raises StoredRecordErasureAuthorizationError.
1576
+ * A thrown hook error propagates unchanged; neither outcome reaches the store.
1577
+ * Receives identifying facts and detached validated targets, without redact or stored payloads.
1578
+ */
1579
+ authorizeStoredRecordErasure?(input: StoredRecordErasureAuthorizationInput): Promise<boolean>;
1331
1580
  }
1332
1581
  export interface DispatchActionInput {
1333
1582
  actionInvocationId: string;
@@ -1497,7 +1746,7 @@ export interface ErrorTextRedactionContext {
1497
1746
  }
1498
1747
  export interface PlatformHostOptions<TDb> {
1499
1748
  store: PlatformHostStore<TDb>;
1500
- authorization: PlatformHostAuthorization;
1749
+ authorization: PlatformHostAuthorization<TDb>;
1501
1750
  /** Preferred explicit catalog assembled from this runtime's Fabric modules. */
1502
1751
  registry?: Pick<ModuleRegistry<TDb>, "resolveAction" | "resolvePolicy" | "resolveStateMachine"> & Partial<Pick<ModuleRegistry<TDb>, "orderedModules">>;
1503
1752
  /** Resolve vertical- or runtime-local actions without mutating the process-wide registry. */
@@ -1532,9 +1781,29 @@ export interface PlatformHostOptions<TDb> {
1532
1781
  * approval resume, exhausted attempts, and marker-write failures are not retried.
1533
1782
  * Policy persistence, obligation listing/resolution/persistence, state-machine checks,
1534
1783
  * the handler transaction, handlers, adapters, and finalization are excluded;
1535
- * their failure/reconciliation/recovery semantics apply, subject to recoveryMaxAttempts.
1784
+ * their separate failure/reconciliation/recovery semantics apply, including handlerRetry
1785
+ * for confirmed handler rollback, subject to recoveryMaxAttempts.
1536
1786
  */
1537
1787
  retryableExecutionMaxAttempts?: number;
1788
+ /** Bounded retries within one execution attempt after confirmed handler rollback. */
1789
+ handlerRetry?: {
1790
+ /** Total transaction tries, including the first; integer 1–10, default 3. One disables retry. */
1791
+ maxAttempts?: number;
1792
+ /** Assert the whole retried unit kept effects in the transaction. Literal true opts in; throws refuse retry. */
1793
+ classify?: (error: unknown, context: {
1794
+ actionId: string;
1795
+ }) => boolean;
1796
+ /** Retry number starts at 1. Default full jitter on 25 ms doubling; clamped to 0–1000 ms. Throws/non-finite stop retry. */
1797
+ delayMs?: number | ((retry: number) => number);
1798
+ /** Called before each delay; attempt is the upcoming try (2..maxAttempts). Sync throws and async rejections are ignored without awaiting. */
1799
+ onRetry?: (info: {
1800
+ actionInvocationId: string;
1801
+ actionId: string;
1802
+ attempt: number;
1803
+ maxAttempts: number;
1804
+ error: unknown;
1805
+ }) => void;
1806
+ };
1538
1807
  /**
1539
1808
  * Positive integer cap on worker claims for resumed leased running invocations
1540
1809
  * including the first (default max(5, retryableExecutionMaxAttempts); Infinity is rejected).
@@ -1550,6 +1819,8 @@ export interface PlatformHostOptions<TDb> {
1550
1819
  services?: FabricRuntimeServices;
1551
1820
  idempotency?: {
1552
1821
  conflictMode?: "audit-only" | "enforce";
1822
+ /** Reserve keyed definitive submission refusals. Requires enforcing identity and store support. */
1823
+ recordRefusals?: boolean;
1553
1824
  onConflict?(conflict: IdempotencyConflict): void;
1554
1825
  onLegacyRecord?(invocation: ActionInvocationRecord): void;
1555
1826
  };
@@ -1581,6 +1852,8 @@ export interface PlatformHostOptions<TDb> {
1581
1852
  approvalDefaultTtlMs?: number;
1582
1853
  /** Atomic approval lifecycle events and outbox writes; defaults to false. */
1583
1854
  approvalLifecycleEvents?: boolean;
1855
+ /** Atomic audit events for refused recording attempts; defaults to false. */
1856
+ approvalDecisionAttemptEvents?: boolean;
1584
1857
  /** Durable evaluator/ruleset version recorded with every HITL decision. */
1585
1858
  hitlPolicyVersion?: string;
1586
1859
  /** Optional portable resolver; provider-specific implementations belong in integration packages. */
@@ -1696,6 +1969,10 @@ export interface PlatformHostOptions<TDb> {
1696
1969
  */
1697
1970
  adapterDeadlineMs?: number;
1698
1971
  }
1972
+ /** Additive capability; handwritten GovernedActionHost implementations retain their existing shape. */
1973
+ export interface ApprovalDecisionRecordingHost {
1974
+ recordApprovalDecision(id: string, tenantId: string, spaceId: string, request: RecordApprovalDecisionRequest): Promise<RecordApprovalDecisionResult>;
1975
+ }
1699
1976
  export interface ApprovalExpiryHost {
1700
1977
  expireDueApprovals(input?: ExpireDueApprovalsInput): Promise<{
1701
1978
  expired: string[];
@@ -1747,20 +2024,59 @@ export interface OutboxRequeueHost {
1747
2024
  */
1748
2025
  requeueOutboxDeadLetter(input: RequeueOutboxDeadLetterInput): Promise<RequeueOutboxDeadLetterResult>;
1749
2026
  }
2027
+ /** Exact durable key scope; an empty idempotency key is valid. */
2028
+ export interface SubmissionOutcomeKey {
2029
+ tenantId: string;
2030
+ spaceId: string;
2031
+ actionId: ActionId;
2032
+ idempotencyKey: string;
2033
+ }
2034
+ export type StoredSubmissionOutcomeRecord = Readonly<SubmissionOutcomeKey & Pick<ActionInvocationRecord, "id" | "actionVersion" | "actorId" | "actorType" | "authorizationBindingId" | "idempotencyActorId" | "idempotencyAuthorizationBindingId" | "status" | "submissionRefusal" | "storedRecordsErased">>;
2035
+ export interface ReadSubmissionOutcomeInput extends SubmissionOutcomeKey {
2036
+ actorId: string;
2037
+ actorType: ActorType;
2038
+ authorizationBindingId?: string;
2039
+ }
2040
+ type ReceiptIdentity = {
2041
+ actionInvocationId: string;
2042
+ actionVersion: number;
2043
+ };
2044
+ export type ReadSubmissionOutcomeResult = {
2045
+ kind: "no_record";
2046
+ } | (ReceiptIdentity & {
2047
+ kind: "submission_refused";
2048
+ submissionRefusal: SubmissionRefusalDescriptor;
2049
+ }) | (ReceiptIdentity & {
2050
+ kind: "invocation";
2051
+ status: Exclude<ActionStatus, "submission_refused">;
2052
+ }) | (ReceiptIdentity & {
2053
+ kind: "records_erased";
2054
+ recordedStatus: ActionStatus;
2055
+ });
2056
+ export interface SubmissionOutcomeReader {
2057
+ readSubmissionOutcome(input: Readonly<ReadSubmissionOutcomeInput>): Promise<ReadSubmissionOutcomeResult>;
2058
+ }
1750
2059
  //#endregion
1751
2060
  //#region src/host.d.ts
1752
- export declare function createGovernedActionHost<TDb>(options: PlatformHostOptions<TDb>): GovernedActionHost & OutboxRequeueHost & ApprovalExpiryHost;
2061
+ export declare function createGovernedActionHost<TDb>(options: PlatformHostOptions<TDb>): SubmissionOutcomeReader & GovernedActionHost & OutboxRequeueHost & ApprovalExpiryHost & ApprovalDecisionRecordingHost & StoredRecordErasureHost;
1753
2062
  //#endregion
1754
2063
  //#region src/memory-store.d.ts
2064
+ /**
2065
+ * Optional domain snapshot/restore binder. The Host handler callback can request
2066
+ * rollback confirmation after a failed callback and successful undo/restore.
2067
+ * Commit failures and providerless internal transactions supply no confirmation.
2068
+ * Ordinary public transaction callbacks retain their original error objects.
2069
+ */
1755
2070
  export interface MemoryPlatformHostTransactionProvider<TDb> {
1756
2071
  snapshot(db: TDb): TDb;
1757
2072
  restore(db: TDb, snapshot: TDb): void;
1758
2073
  }
1759
- export declare class MemoryPlatformHostStore<TDb> implements RecoverablePlatformHostStore<TDb>, ApprovalPlatformHostStore<TDb>, GovernancePlatformHostStore<TDb>, OutboxPlatformHostStore<TDb>, OutboxRequeuePlatformHostStore<TDb>, StagedInvocationPlatformHostStore<TDb> {
2074
+ export declare class MemoryPlatformHostStore<TDb> implements ErasablePlatformHostStore<TDb>, RecoverablePlatformHostStore<TDb>, ApprovalPlatformHostStore<TDb>, GovernancePlatformHostStore<TDb>, OutboxPlatformHostStore<TDb>, OutboxRequeuePlatformHostStore<TDb>, StagedInvocationPlatformHostStore<TDb> {
1760
2075
  readonly db: TDb;
2076
+ getSubmissionOutcomeByKey(key: Readonly<SubmissionOutcomeKey>): Promise<StoredSubmissionOutcomeRecord | undefined>;
1761
2077
  readonly transactionalOutbox: boolean;
1762
2078
  readonly transactionalApprovalLifecycle: boolean;
1763
- private readonly approvalTransactionProvider;
2079
+ readonly transactionalApprovalAuthorization = true;
1764
2080
  readonly transactionalStagedInvocations: boolean;
1765
2081
  transactionWithEvents?: <TResult>(run: (transaction: PlatformHostMutationTransaction<TDb>) => Promise<TResult>) => Promise<TResult>;
1766
2082
  private adapterCommit;
@@ -1781,10 +2097,16 @@ export declare class MemoryPlatformHostStore<TDb> implements RecoverablePlatform
1781
2097
  * `private`, as for the store's other internals, is a compile-time guard.
1782
2098
  */
1783
2099
  private readonly outboxRequeues;
2100
+ private readonly erasedStages;
2101
+ private readonly erasureMarkers;
2102
+ private isErased;
2103
+ private assertNotErased;
2104
+ eraseStoredRecords(request: EraseStoredRecordsInput): Promise<EraseStoredRecordsResult>;
1784
2105
  constructor(db: TDb, transactionProvider?: MemoryPlatformHostTransactionProvider<TDb>);
1785
2106
  transaction<TResult>(run: (db: TDb) => Promise<TResult>): Promise<TResult>;
1786
2107
  private runTransactionWithEvents;
1787
2108
  createActionInvocation(input: CreateActionInvocationInput): Promise<ActionInvocationRecord>;
2109
+ recordSubmissionRefusal(input: RecordSubmissionRefusalInput): Promise<ActionInvocationRecord>;
1788
2110
  createActionInvocationForStagedLease(input: CreateStagedActionInvocationInput): Promise<CreateStagedActionInvocationResult>;
1789
2111
  private applyStagedBatch;
1790
2112
  getActionInvocation(id: string, tenantId: string, spaceId: string): Promise<ActionInvocationRecord | undefined>;
@@ -1793,7 +2115,8 @@ export declare class MemoryPlatformHostStore<TDb> implements RecoverablePlatform
1793
2115
  recordHitlDecision(actionInvocationId: string, tenantId: string, spaceId: string, decision: HitlDecisionEvidence): Promise<ActionInvocationRecord>;
1794
2116
  private runApprovalTransaction;
1795
2117
  parkApproval(input: ParkApprovalInput): Promise<ApprovalDecisionTransitionResult>;
1796
- beginApprovalDecision(input: BeginApprovalDecisionInput): Promise<ApprovalDecisionTransitionResult>;
2118
+ recordApprovalDecision(input: RecordApprovalDecisionStoreInput<TDb>): Promise<RecordApprovalDecisionStoreResult>;
2119
+ beginApprovalDecision(input: BeginApprovalDecisionInput<TDb>): Promise<ApprovalDecisionTransitionResult>;
1797
2120
  expireDueApprovals(input: ExpireDueApprovalsStoreInput): Promise<ActionInvocationRecord[]>;
1798
2121
  appendPolicyEvaluation(record: PolicyEvaluationRecord): Promise<void>;
1799
2122
  recordMutationGovernance(actionInvocationId: string, tenantId: string, spaceId: string, context: Pick<ActionInvocationRecord, "mutationFootprint" | "executionPrincipal"> & {
@@ -1824,13 +2147,14 @@ export declare class MemoryPlatformHostStore<TDb> implements RecoverablePlatform
1824
2147
  availableAt: Date;
1825
2148
  deadLetter: boolean;
1826
2149
  }): Promise<void>;
1827
- renewOutboxLease(input: {
2150
+ renewOutboxLease(input: Parameters<OutboxPlatformHostStore<TDb>["renewOutboxLease"]>[0]): Promise<boolean>;
2151
+ renewOutboxLeaseAndRead(input: {
1828
2152
  id: string;
1829
2153
  workerId: string;
1830
2154
  leaseToken: number;
1831
2155
  leaseDurationMs: number;
1832
2156
  now?: Date;
1833
- }): Promise<boolean>;
2157
+ }): Promise<OutboxRecord | undefined>;
1834
2158
  listOutbox(input?: {
1835
2159
  tenantId?: string;
1836
2160
  spaceId?: string;
@@ -1972,21 +2296,38 @@ export interface PostgresPlatformHostTransactionContext<TDb> {
1972
2296
  * `pg.PoolClient`. Production applications provide this adapter so domain
1973
2297
  * methods and Host ledger writes share one PostgreSQL transaction.
1974
2298
  */
2299
+ /**
2300
+ * May reject with ConfirmedTransactionRollback(callbackError) after successful rollback
2301
+ * of a failed callback. Providers omitting that evidence get no in-attempt handler retry.
2302
+ * Commit errors must not be represented as confirmed callback rollback. The store exposes
2303
+ * evidence at the Host handler callback request and normalizes it for other consumers.
2304
+ */
1975
2305
  export interface PostgresPlatformHostTransactionProvider<TDb> {
1976
2306
  run<TResult>(run: (context: PostgresPlatformHostTransactionContext<TDb>) => Promise<TResult>): Promise<TResult>;
1977
2307
  }
1978
2308
  /** Durable mutation-pipeline ledger for Databricks Lakebase or standard Postgres. */
1979
- export declare class PostgresPlatformHostStore<TDb> implements RecoverablePlatformHostStore<TDb>, ApprovalPlatformHostStore<TDb>, GovernancePlatformHostStore<TDb>, OutboxPlatformHostStore<TDb>, OutboxRequeuePlatformHostStore<TDb>, StagedInvocationPlatformHostStore<TDb> {
2309
+ export declare class PostgresPlatformHostStore<TDb> implements ErasablePlatformHostStore<TDb>, RecoverablePlatformHostStore<TDb>, ApprovalPlatformHostStore<TDb>, GovernancePlatformHostStore<TDb>, OutboxPlatformHostStore<TDb>, OutboxRequeuePlatformHostStore<TDb>, StagedInvocationPlatformHostStore<TDb> {
1980
2310
  readonly db: TDb;
1981
2311
  private readonly sql;
2312
+ private readonly transactionProvider?;
1982
2313
  readonly transactionWithEvents?: AtomicMutationPlatformHostStore<TDb>["transactionWithEvents"];
2314
+ private ownerLocksPersist;
2315
+ private readonly lockedOwners;
1983
2316
  readonly transactionalOutbox: boolean;
1984
2317
  readonly transactionalApprovalLifecycle: boolean;
2318
+ readonly transactionalApprovalAuthorization: boolean;
1985
2319
  readonly transactionalStagedInvocations: boolean;
1986
- constructor(db: TDb, sql: PlatformHostSqlClient, transactionProvider?: PostgresPlatformHostTransactionProvider<TDb>);
2320
+ constructor(db: TDb, sql: PlatformHostSqlClient, transactionProvider?: PostgresPlatformHostTransactionProvider<TDb> | undefined);
2321
+ /** Internal provider entry: non-handler consumers retain their original error objects. */
2322
+ private runTransaction;
2323
+ private refuseErased;
2324
+ private ownerFence;
1987
2325
  ensureSchema(): Promise<void>;
2326
+ eraseStoredRecords(request: EraseStoredRecordsInput): Promise<EraseStoredRecordsResult>;
1988
2327
  transaction<TResult>(run: (db: TDb) => Promise<TResult>): Promise<TResult>;
2328
+ getSubmissionOutcomeByKey(key: Readonly<SubmissionOutcomeKey>): Promise<StoredSubmissionOutcomeRecord | undefined>;
1989
2329
  createActionInvocation(input: CreateActionInvocationInput): Promise<ActionInvocationRecord>;
2330
+ recordSubmissionRefusal(input: RecordSubmissionRefusalInput): Promise<ActionInvocationRecord>;
1990
2331
  createActionInvocationForStagedLease(input: CreateStagedActionInvocationInput): Promise<CreateStagedActionInvocationResult>;
1991
2332
  stageInvocations(records: readonly StagedInvocationRecord[]): Promise<void>;
1992
2333
  getActionInvocation(id: string, tenantId: string, spaceId: string): Promise<ActionInvocationRecord | undefined>;
@@ -1995,7 +2336,9 @@ export declare class PostgresPlatformHostStore<TDb> implements RecoverablePlatfo
1995
2336
  recordHitlDecision(actionInvocationId: string, tenantId: string, spaceId: string, decision: HitlDecisionEvidence): Promise<ActionInvocationRecord>;
1996
2337
  parkApproval(input: ParkApprovalInput): Promise<ApprovalDecisionTransitionResult>;
1997
2338
  expireDueApprovals(input: ExpireDueApprovalsStoreInput): Promise<ActionInvocationRecord[]>;
1998
- beginApprovalDecision(input: BeginApprovalDecisionInput): Promise<ApprovalDecisionTransitionResult>;
2339
+ private lockDueApprovals;
2340
+ recordApprovalDecision(input: RecordApprovalDecisionStoreInput<TDb>): Promise<RecordApprovalDecisionStoreResult>;
2341
+ beginApprovalDecision(input: BeginApprovalDecisionInput<TDb>): Promise<ApprovalDecisionTransitionResult>;
1999
2342
  appendPolicyEvaluation(record: PolicyEvaluationRecord): Promise<void>;
2000
2343
  recordMutationGovernance(actionInvocationId: string, tenantId: string, spaceId: string, context: {
2001
2344
  footprint: NonNullable<ActionInvocationRecord["mutationFootprint"]>;
@@ -2024,13 +2367,14 @@ export declare class PostgresPlatformHostStore<TDb> implements RecoverablePlatfo
2024
2367
  availableAt: Date;
2025
2368
  deadLetter: boolean;
2026
2369
  }): Promise<void>;
2027
- renewOutboxLease(input: {
2370
+ renewOutboxLease(input: Parameters<OutboxPlatformHostStore<TDb>["renewOutboxLease"]>[0]): Promise<boolean>;
2371
+ renewOutboxLeaseAndRead(input: {
2028
2372
  id: string;
2029
2373
  workerId: string;
2030
2374
  leaseToken: number;
2031
2375
  leaseDurationMs: number;
2032
2376
  now?: Date;
2033
- }): Promise<boolean>;
2377
+ }): Promise<OutboxRecord | undefined>;
2034
2378
  listOutbox(input?: {
2035
2379
  tenantId?: string;
2036
2380
  spaceId?: string;
@@ -2115,7 +2459,9 @@ export declare class PostgresPlatformHostStore<TDb> implements RecoverablePlatfo
2115
2459
  updateLeasedActionInvocation(input: UpdateLeasedActionInvocationInput): Promise<boolean>;
2116
2460
  renewActionInvocationLease(input: RenewActionInvocationLeaseInput): Promise<boolean>;
2117
2461
  listEvents(tenantId: string, spaceId: string): Promise<AssetEventEnvelope[]>;
2118
- getEvent(id: string, tenantId: string, spaceId: string): Promise<AssetEventEnvelope | undefined>;
2462
+ getEvent(id: string, tenantId: string, spaceId: string): Promise<(AssetEventEnvelope & {
2463
+ readonly storedRecordsErased?: true;
2464
+ }) | undefined>;
2119
2465
  }
2120
2466
  //#endregion
2121
2467
  //#region src/postgres-migrations.d.ts
@@ -2259,6 +2605,11 @@ export declare function prepareStagedInvocationBatch<TDb>(input: PrepareStagedIn
2259
2605
  export declare function stagedInvocationRequestIdentity(record: StagedInvocationRecord): string;
2260
2606
  export declare function cloneStagedInvocationRecord(record: StagedInvocationRecord): StagedInvocationRecord;
2261
2607
  export declare function asStagedInvocationStore<TDb>(store: PlatformHostStore<TDb>): StagedInvocationPlatformHostStore<TDb> | undefined;
2608
+ /** Placeholder for published payload-covering batch digests replaced by migration 16. */
2609
+ export declare const LEGACY_BATCH_DIGEST = "legacy-batch";
2610
+ /** Replay comparison used by both built-in stores. Compares the full request identity,
2611
+ * skipping only the batch-digest comparison when the stored row carries LEGACY_BATCH_DIGEST. */
2612
+ export declare function stagedInvocationRequestMatches(stored: StagedInvocationRecord, incoming: StagedInvocationRecord): boolean;
2262
2613
  //#endregion
2263
2614
  //#region src/tenant-isolation.d.ts
2264
2615
  export interface TenantIsolationScope {
@@ -2291,13 +2642,13 @@ export interface TenantIsolationSubject<TDb = unknown> {
2291
2642
  }>;
2292
2643
  /**
2293
2644
  * Optional: put the host and store back to a known state before a check.
2294
- * The checks tolerate accumulated state, and each retires any row it
2295
- * creates directly, so this exists for subjects whose store is shared.
2645
+ * The checks tolerate accumulated state, and direct probes use terminal rows
2646
+ * or retire the active rows they create, so this exists for subjects whose store is shared.
2296
2647
  */
2297
2648
  reset?(): Promise<void> | void;
2298
2649
  }
2299
2650
  export interface TenantIsolationCheck {
2300
- id: "fabric.tenancy.invocation-scoped-read.v1" | "fabric.tenancy.idempotency-key-scoped.v1" | "fabric.tenancy.events-scoped.v1" | "fabric.tenancy.worker-claim-scoped.v1" | "fabric.tenancy.staged-invocation-scoped.v1" | "fabric.tenancy.completion-scoped.v1" | "fabric.tenancy.projection-scoped.v1" | "fabric.tenancy.adapter-invocation-read-scoped.v1" | "fabric.tenancy.adapter-invocation-write-scoped.v1";
2651
+ id: "fabric.tenancy.invocation-scoped-read.v1" | "fabric.tenancy.idempotency-key-scoped.v1" | "fabric.tenancy.submission-refusal-scoped.v1" | "fabric.tenancy.events-scoped.v1" | "fabric.tenancy.worker-claim-scoped.v1" | "fabric.tenancy.staged-invocation-scoped.v1" | "fabric.tenancy.completion-scoped.v1" | "fabric.tenancy.projection-scoped.v1" | "fabric.tenancy.adapter-invocation-read-scoped.v1" | "fabric.tenancy.adapter-invocation-write-scoped.v1";
2301
2652
  run(subject: TenantIsolationSubject): Promise<string[]>;
2302
2653
  }
2303
2654
  export interface TenantIsolationResult {
@@ -2312,9 +2663,10 @@ export interface TenantIsolationResult {
2312
2663
  /**
2313
2664
  * The suite a deployment of the host must pass before it claims isolation.
2314
2665
  *
2315
- * Nine checks: an invocation cannot be read, executed, or completed under
2666
+ * Ten checks: an invocation cannot be read, executed, or completed under
2316
2667
  * another tenant's scope; an idempotency key is a tenant's own, so the same
2317
- * key in two tenants is two invocations; a tenant's event stream carries no
2668
+ * key in two tenants is two invocations; optional recorded refusals retain
2669
+ * tenant and space scope, including when ordinary creation encounters a refusal; a tenant's event stream carries no
2318
2670
  * other tenant's events; a worker scoped to one tenant does not claim another's
2319
2671
  * work; staged listing and claiming retain tenant and space scope; an adapter
2320
2672
  * checkpoint cannot be read or written under another tenant, space, or owning
@@ -2401,4 +2753,224 @@ export declare function eventAppendChecks(): readonly EventAppendCheck[];
2401
2753
  */
2402
2754
  export declare function runEventAppendChecks<TDb>(subject: EventAppendSubject<TDb>): Promise<EventAppendResult>;
2403
2755
  //#endregion
2404
- export { canonicalJson };
2756
+ //#region src/erasure.d.ts
2757
+ export declare function createStoredRecordErasureHost<TDb>(store: PlatformHostStore<TDb>, authorization: Pick<PlatformHostAuthorization<TDb>, "authorizeStoredRecordErasure">): StoredRecordErasureHost;
2758
+ //#endregion
2759
+ //#region src/erasure-conformance.d.ts
2760
+ export interface ErasureProbeScope {
2761
+ tenantId: string;
2762
+ spaceId: string;
2763
+ invocationId: string;
2764
+ }
2765
+ export interface ErasureSubject<TDb = unknown> {
2766
+ store: PlatformHostStore<TDb> & Partial<ErasablePlatformHostStore<TDb>> & {
2767
+ listEvents?(tenantId: string, spaceId: string): Promise<AssetEventEnvelope[]>;
2768
+ };
2769
+ tenantId: string;
2770
+ /** Test-store fixture access for record kinds not readable through the base store port. */
2771
+ payloadRecords?: {
2772
+ seed(scope: ErasureProbeScope, sentinel: string): Promise<void>;
2773
+ read(scope: ErasureProbeScope): Promise<readonly {
2774
+ kind: ErasureRecordKind;
2775
+ value: unknown;
2776
+ }[]>;
2777
+ };
2778
+ }
2779
+ export interface ErasureCheck {
2780
+ id: string;
2781
+ run(subject: ErasureSubject): Promise<string[]>;
2782
+ }
2783
+ export interface ErasureResult {
2784
+ passed: boolean;
2785
+ checks: Array<{
2786
+ id: string;
2787
+ status: "passed" | "failed" | "skipped";
2788
+ evidence: string[];
2789
+ error?: string;
2790
+ }>;
2791
+ }
2792
+ /** Destructive probes for isolated test stores; leave evidence in fresh probe spaces. */
2793
+ export declare function erasureChecks(): readonly ErasureCheck[];
2794
+ export declare function runErasureChecks<TDb>(subject: ErasureSubject<TDb>): Promise<ErasureResult>;
2795
+ //#endregion
2796
+ //#region src/erasure-errors.d.ts
2797
+ /** Host authorization refusal; contains no target existence or authorizer failure details. */
2798
+ export declare class StoredRecordErasureAuthorizationError extends Error {
2799
+ readonly refusal = "not_authorized";
2800
+ constructor(message: string);
2801
+ }
2802
+ /** A later mutation of an erased record was refused. */
2803
+ export declare class StoredRecordsErasedError extends Error {
2804
+ readonly code = "HOST_RECORDS_ERASED";
2805
+ constructor();
2806
+ }
2807
+ /** Erasure could not lock its targets or finish within the configured budget. */
2808
+ export declare class ErasureBusyError extends Error {
2809
+ readonly reason: "lock_timeout" | "work_timeout";
2810
+ readonly code = "HOST_ERASURE_BUSY";
2811
+ constructor(reason: "lock_timeout" | "work_timeout", options?: ErrorOptions);
2812
+ }
2813
+ //#endregion
2814
+ //#region src/staged-invocation-conformance.d.ts
2815
+ export type StagedContractStore = StagedInvocationPlatformHostStore<unknown> & AtomicMutationPlatformHostStore<unknown>;
2816
+ export interface StagedCreateRotateRaceInput {
2817
+ record: StagedInvocationRecord;
2818
+ workerId: string;
2819
+ leaseToken: number;
2820
+ now: Date;
2821
+ invocation: CreateActionInvocationInput & {
2822
+ stagedOrigin: {
2823
+ parentInvocationId: string;
2824
+ stageKey: string;
2825
+ principal?: string;
2826
+ };
2827
+ };
2828
+ }
2829
+ export interface StagedCreateRotateRaceOutcome {
2830
+ creation: CreateStagedActionInvocationResult;
2831
+ rotatedToken: number | undefined;
2832
+ }
2833
+ /**
2834
+ * A store whose fence can interleave with rotation (PostgreSQL) supplies the
2835
+ * two races with the first operation held open in a transaction, proving the
2836
+ * second waits for it. Without them the contract runs both operations
2837
+ * concurrently, which serializes in the memory store.
2838
+ */
2839
+ export interface StagedContractHarness {
2840
+ store: StagedContractStore;
2841
+ /** Fenced create held uncommitted; a rotation under the same token must wait for it. */
2842
+ raceCreateThenRotate?(input: StagedCreateRotateRaceInput): Promise<StagedCreateRotateRaceOutcome>;
2843
+ /** Rotation held uncommitted; a fenced create under the old token must wait, then create nothing. */
2844
+ raceRotateThenCreate?(input: StagedCreateRotateRaceInput): Promise<StagedCreateRotateRaceOutcome>;
2845
+ }
2846
+ export interface StagedInvocationSubject {
2847
+ makeHarness(): Promise<StagedContractHarness>;
2848
+ }
2849
+ export interface StagedInvocationCheck {
2850
+ id: string;
2851
+ run(subject: StagedInvocationSubject): Promise<string[]>;
2852
+ }
2853
+ export interface StagedInvocationResult {
2854
+ passed: boolean;
2855
+ checks: Array<{
2856
+ id: string;
2857
+ status: "passed" | "failed" | "skipped";
2858
+ evidence: string[];
2859
+ error?: string;
2860
+ }>;
2861
+ }
2862
+ /** Destructive probes for isolated test stores; leave records in fresh probe spaces. */
2863
+ export declare function stagedInvocationChecks(): readonly StagedInvocationCheck[];
2864
+ export declare function runStagedInvocationChecks(subject: StagedInvocationSubject): Promise<StagedInvocationResult>;
2865
+ //#endregion
2866
+ //#region src/outbox-requeue-conformance.d.ts
2867
+ export interface OutboxRequeueSubject {
2868
+ makeStore(): Promise<OutboxRequeuePlatformHostStore<unknown>>;
2869
+ }
2870
+ export interface OutboxRequeueCheck {
2871
+ id: string;
2872
+ run(subject: OutboxRequeueSubject): Promise<string[]>;
2873
+ }
2874
+ export interface OutboxRequeueResult {
2875
+ passed: boolean;
2876
+ checks: Array<{
2877
+ id: string;
2878
+ status: "passed" | "failed" | "skipped";
2879
+ evidence: string[];
2880
+ error?: string;
2881
+ }>;
2882
+ }
2883
+ /** Destructive probes for isolated test stores; leave outbox and audit records under fresh identities. */
2884
+ export declare function outboxRequeueChecks(): readonly OutboxRequeueCheck[];
2885
+ export declare function runOutboxRequeueChecks(subject: OutboxRequeueSubject): Promise<OutboxRequeueResult>;
2886
+ //#endregion
2887
+ //#region src/approval-conformance.d.ts
2888
+ export type ApprovalCheckStore = ApprovalPlatformHostStore<{}> & Partial<AtomicMutationPlatformHostStore<{}>> & RecoverablePlatformHostStore<{}> & OutboxPlatformHostStore<{}>;
2889
+ export interface ApprovalSubject {
2890
+ makeStore(): Promise<ApprovalCheckStore>;
2891
+ }
2892
+ export interface ApprovalCheck {
2893
+ id: string;
2894
+ run(subject: ApprovalSubject): Promise<string[]>;
2895
+ }
2896
+ export interface ApprovalResult {
2897
+ passed: boolean;
2898
+ checks: Array<{
2899
+ id: string;
2900
+ status: "passed" | "failed" | "skipped";
2901
+ evidence: string[];
2902
+ error?: string;
2903
+ }>;
2904
+ }
2905
+ /** Destructive probes for isolated test stores; leave invocation, event and outbox evidence under fresh identities. */
2906
+ export declare function approvalChecks(): readonly ApprovalCheck[];
2907
+ export declare function runApprovalChecks(subject: ApprovalSubject): Promise<ApprovalResult>;
2908
+ //#endregion
2909
+ //#region src/outbox-relay-conformance.d.ts
2910
+ export interface OutboxRelaySubject {
2911
+ makeStore(): Promise<OutboxPlatformHostStore<unknown>>;
2912
+ }
2913
+ export interface OutboxRelayCheck {
2914
+ id: string;
2915
+ run(subject: OutboxRelaySubject): Promise<string[]>;
2916
+ }
2917
+ export interface OutboxRelayConformanceResult {
2918
+ passed: boolean;
2919
+ checks: Array<{
2920
+ id: string;
2921
+ status: "passed" | "failed" | "skipped";
2922
+ evidence: string[];
2923
+ error?: string;
2924
+ }>;
2925
+ }
2926
+ /** Destructive probes for isolated test stores; leave outbox records under fresh identities and spaces. */
2927
+ export declare function outboxRelayChecks(): readonly OutboxRelayCheck[];
2928
+ export declare function runOutboxRelayChecks(subject: OutboxRelaySubject): Promise<OutboxRelayConformanceResult>;
2929
+ //#endregion
2930
+ //#region src/invocation-idempotency-conformance.d.ts
2931
+ export interface InvocationIdempotencySubject {
2932
+ makeStore(): Promise<PlatformHostStore<unknown>>;
2933
+ }
2934
+ export interface InvocationIdempotencyCheck {
2935
+ id: string;
2936
+ run(subject: InvocationIdempotencySubject): Promise<string[]>;
2937
+ }
2938
+ export interface InvocationIdempotencyResult {
2939
+ passed: boolean;
2940
+ checks: Array<{
2941
+ id: string;
2942
+ status: "passed" | "failed" | "skipped";
2943
+ evidence: string[];
2944
+ error?: string;
2945
+ }>;
2946
+ }
2947
+ /** Destructive probes for isolated test stores; leave invocation records under fresh identities and spaces. */
2948
+ export declare function invocationIdempotencyChecks(): readonly InvocationIdempotencyCheck[];
2949
+ export declare function runInvocationIdempotencyChecks(subject: InvocationIdempotencySubject): Promise<InvocationIdempotencyResult>;
2950
+ //#endregion
2951
+ //#region src/transaction-rollback.d.ts
2952
+ /** Evidence from a transaction provider that rollback completed successfully. */
2953
+ export declare class ConfirmedTransactionRollback extends Error {
2954
+ readonly name = "ConfirmedTransactionRollback";
2955
+ readonly code: "TRANSACTION_ROLLBACK_CONFIRMED";
2956
+ constructor(cause: unknown);
2957
+ }
2958
+ /** Recognise rollback evidence across ESM/CommonJS or other package copies. */
2959
+ export declare function isConfirmedTransactionRollback(error: unknown): error is ConfirmedTransactionRollback;
2960
+ /** Inspect up to 16 errors, failing closed on cycles or unreadable properties. Intended for handlerRetry.classify. */
2961
+ export declare function isTransientPostgresTransactionError(error: unknown): boolean;
2962
+ //#endregion
2963
+ //#region src/submission-outcome.d.ts
2964
+ export declare class SubmissionOutcomeUnsupportedError extends Error {
2965
+ constructor();
2966
+ }
2967
+ //#endregion
2968
+ //#region src/submission-outcome-conformance.d.ts
2969
+ /**
2970
+ * Isolated destructive probe with fresh identities and timestamps from new Date().
2971
+ * Refusal and erasure sections run only with their respective store capabilities;
2972
+ * returned evidence identifies each unexercised optional section with `skipped:`.
2973
+ */
2974
+ export declare function checkSubmissionOutcomeStore(store: PlatformHostStore<unknown>): Promise<string[]>;
2975
+ //#endregion
2976
+ export { type ErasablePlatformHostStore, type EraseStoredRecordsInput, type EraseStoredRecordsResult, type ErasurePayloadFields, type ErasureRecordKind, type StoredRecordErasureAuthorizationInput, type StoredRecordErasureHost, type StoredRecordErasureOutcome, type StoredRecordErasureTarget, type StoredRecordErasureView, canonicalJson };