feat(ql3): establish 3.0 incubation baseline

This commit is contained in:
whyour
2026-08-12 00:25:26 +08:00
parent 4bf92dcfeb
commit c699c32461
2817 changed files with 779642 additions and 653 deletions
@@ -0,0 +1,80 @@
import type {
ApprovalActionBinding,
ApprovalDecision,
ApprovalRequestRecord,
ApprovedActionDispatchRecord,
ApprovalRisk,
} from '../domain/approvalRequest';
import type {
PolicySubject,
ProjectPolicyFence,
} from '../domain/projectPolicy';
export interface CreateApprovalRequestCommand {
request: ApprovalRequestRecord;
authorizationFence: ProjectPolicyFence;
}
export interface CreateApprovalRequestResult {
status: 'created' | 'existing';
request: Readonly<ApprovalRequestRecord>;
}
export interface DecideApprovalRequestCommand {
requestId: string;
expectedVersion: number;
decisionId: string;
decision: ApprovalDecision;
reasonCode: string;
decidedBy: PolicySubject;
decidedAtMs: number;
authorizationFence: ProjectPolicyFence;
}
export interface DecideApprovalRequestResult {
status: 'decided' | 'existing';
request: Readonly<ApprovalRequestRecord>;
}
export interface ConsumeApprovalRequestCommand {
requestId: string;
expectedVersion: number;
consumptionId: string;
dispatchId: string;
action: ApprovalActionBinding;
requestedBy: PolicySubject;
consumedBy: PolicySubject;
consumedAtMs: number;
authorizationFence: ProjectPolicyFence;
}
export interface ConsumeApprovalRequestResult {
status: 'consumed' | 'existing';
request: Readonly<ApprovalRequestRecord>;
dispatch: Readonly<ApprovedActionDispatchRecord>;
}
export interface ListPendingApprovalRequestsQuery {
projectId: string;
nowMs: number;
limit: number;
afterExpiresAtMs?: number;
afterId?: string;
risks?: readonly ApprovalRisk[];
}
export interface ApprovalRequestRepository {
findById(id: string): Promise<Readonly<ApprovalRequestRecord> | null>;
create(
command: CreateApprovalRequestCommand,
): Promise<CreateApprovalRequestResult>;
decide(
command: DecideApprovalRequestCommand,
): Promise<DecideApprovalRequestResult>;
consume(
command: ConsumeApprovalRequestCommand,
): Promise<ConsumeApprovalRequestResult>;
}
@@ -0,0 +1,113 @@
import type {
ApprovedActionDispatchCursor,
ApprovedActionDispatchExecutionSnapshot,
} from '../domain/approvedActionDispatchExecution';
export interface ListDueApprovedActionDispatchesQuery {
nowMs: number;
limit: number;
cursor?: ApprovedActionDispatchCursor;
}
export interface ListDueApprovedActionDispatchesResult {
dispatches: readonly ApprovedActionDispatchExecutionSnapshot[];
truncated: boolean;
nextCursor?: Readonly<ApprovedActionDispatchCursor>;
}
export interface ClaimApprovedActionDispatchCommand {
dispatchId: string;
owner: string;
leaseToken: string;
nowMs: number;
leaseDurationMs: number;
}
export type ClaimApprovedActionDispatchResult =
| {
status: 'claimed';
snapshot: Readonly<ApprovedActionDispatchExecutionSnapshot>;
}
| { status: 'not_found' }
| {
status:
| 'not_due'
| 'leased'
| 'executing'
| 'recovery_required'
| 'succeeded'
| 'failed'
| 'blocked';
snapshot: Readonly<ApprovedActionDispatchExecutionSnapshot>;
};
export interface StartApprovedActionDispatchCommand {
dispatchId: string;
approvalRequestId: string;
actionDigest: string;
owner: string;
leaseToken: string;
expectedVersion: number;
startedAtMs: number;
}
export interface RenewApprovedActionDispatchLeaseCommand {
dispatchId: string;
owner: string;
leaseToken: string;
expectedVersion: number;
nowMs: number;
leaseDurationMs: number;
}
export interface ReleaseApprovedActionDispatchBeforeStartCommand {
dispatchId: string;
owner: string;
leaseToken: string;
expectedVersion: number;
resultMutationId: string;
resultCode: string;
atMs: number;
retryAtMs?: number;
}
export interface CompleteApprovedActionDispatchCommand {
dispatchId: string;
owner: string;
leaseToken: string;
expectedVersion: number;
resultMutationId: string;
outcome: 'succeeded' | 'failed' | 'indeterminate';
resultCode: string;
completedAtMs: number;
}
export interface ApprovedActionDispatchRepository {
findById(
dispatchId: string,
): Promise<Readonly<ApprovedActionDispatchExecutionSnapshot> | null>;
listDue(
query: ListDueApprovedActionDispatchesQuery,
): Promise<ListDueApprovedActionDispatchesResult>;
claim(
command: ClaimApprovedActionDispatchCommand,
): Promise<ClaimApprovedActionDispatchResult>;
start(
command: StartApprovedActionDispatchCommand,
): Promise<Readonly<ApprovedActionDispatchExecutionSnapshot>>;
renew(
command: RenewApprovedActionDispatchLeaseCommand,
): Promise<Readonly<ApprovedActionDispatchExecutionSnapshot>>;
releaseBeforeStart(
command: ReleaseApprovedActionDispatchBeforeStartCommand,
): Promise<Readonly<ApprovedActionDispatchExecutionSnapshot>>;
complete(
command: CompleteApprovedActionDispatchCommand,
): Promise<Readonly<ApprovedActionDispatchExecutionSnapshot>>;
}
@@ -0,0 +1,36 @@
import type { ApprovedActionDispatchExecutionSnapshot } from '../domain/approvedActionDispatchExecution';
import type { ApprovedActionDispatchRecord } from '../domain/approvalRequest';
export type ApprovedActionInspectionResult =
| { status: 'ready'; actionDigest: string }
| { status: 'retry'; resultCode: string }
| { status: 'blocked'; resultCode: string };
export interface ApprovedActionExecutionContext {
dispatch: Readonly<ApprovedActionDispatchRecord>;
execution: Readonly<ApprovedActionDispatchExecutionSnapshot['execution']>;
idempotencyKey: string;
fence: {
owner: string;
leaseToken: string;
version: number;
};
}
export interface ApprovedActionExecutionResult {
outcome: 'succeeded' | 'failed' | 'indeterminate';
resultCode: string;
}
export interface ApprovedActionHandler {
readonly actionType: string;
/** Must not perform the approved external side effect. */
inspect(
dispatch: Readonly<ApprovedActionDispatchRecord>,
): Promise<ApprovedActionInspectionResult>;
execute(
context: Readonly<ApprovedActionExecutionContext>,
): Promise<ApprovedActionExecutionResult>;
}
@@ -0,0 +1,37 @@
import type {
ApprovedActionRecoveryFinding,
ApprovedActionRecoverySnapshot,
} from '../domain/approvedActionRecovery';
export type ApprovedActionRecoveryCapability = 'automatic' | 'manual_only';
export type ApprovedActionRecoveryEvidence =
| {
finding: 'verified_succeeded' | 'verified_failed';
resultCode: string;
evidenceDigest: string;
}
| {
finding: Exclude<
ApprovedActionRecoveryFinding,
'verified_succeeded' | 'verified_failed'
>;
resultCode: string;
evidenceDigest?: string;
};
export interface ApprovedActionRecoveryEvidenceContext {
snapshot: Readonly<ApprovedActionRecoverySnapshot>;
idempotencyKey: string;
observedAtMs: number;
}
export interface ApprovedActionRecoveryEvidenceProvider {
readonly actionType: string;
readonly capability: ApprovedActionRecoveryCapability;
/** Must only observe evidence; it must not repeat the approved side effect. */
inspect(
context: Readonly<ApprovedActionRecoveryEvidenceContext>,
): Promise<ApprovedActionRecoveryEvidence>;
}
@@ -0,0 +1,126 @@
import type {
ApprovedActionRecoveryCursor,
ApprovedActionRecoveryDecision,
ApprovedActionRecoveryFinding,
ApprovedActionRecoverySnapshot,
} from '../domain/approvedActionRecovery';
import type { PolicySubject } from '../domain/projectPolicy';
import type { ApprovedActionRecoveryAuthorizationFact } from '../domain/approvedActionRecoveryAuthorization';
export interface ListDueApprovedActionRecoveriesQuery {
nowMs: number;
limit: number;
cursor?: ApprovedActionRecoveryCursor;
}
export interface ListDueApprovedActionRecoveriesResult {
recoveries: readonly ApprovedActionRecoverySnapshot[];
truncated: boolean;
nextCursor?: Readonly<ApprovedActionRecoveryCursor>;
}
export interface ClaimApprovedActionRecoveryCommand {
dispatchId: string;
owner: string;
leaseToken: string;
nowMs: number;
leaseDurationMs: number;
}
export type ClaimApprovedActionRecoveryResult =
| {
status: 'claimed';
snapshot: Readonly<ApprovedActionRecoverySnapshot>;
}
| { status: 'not_found' }
| {
status:
| 'not_due'
| 'leased'
| 'execution_active'
| 'manual_required'
| 'resolved';
snapshot: Readonly<ApprovedActionRecoverySnapshot>;
};
export interface RecordApprovedActionRecoveryFindingCommand {
dispatchId: string;
expectedExecutionVersion: number;
expectedRecoveryVersion: number;
owner: string;
leaseToken: string;
findingMutationId: string;
finding: Exclude<
ApprovedActionRecoveryFinding,
'verified_succeeded' | 'verified_failed'
>;
resultCode: string;
evidenceDigest?: string;
observedAtMs: number;
retryAtMs?: number;
}
export interface ResolveApprovedActionRecoveryAutomaticallyCommand {
dispatchId: string;
expectedExecutionVersion: number;
expectedRecoveryVersion: number;
owner: string;
leaseToken: string;
mutationId: string;
source: 'automatic_evidence';
decision: Exclude<ApprovedActionRecoveryDecision, 'abandon_unknown'>;
evidenceDigest: string;
reasonCode: string;
resolvedAtMs: number;
}
export interface ResolveApprovedActionRecoveryManuallyCommand {
dispatchId: string;
expectedExecutionVersion: number;
expectedRecoveryVersion: number;
mutationId: string;
source: 'human';
decision: ApprovedActionRecoveryDecision;
evidenceDigest?: string;
reasonCode: string;
resolvedBy: PolicySubject;
resolvedAtMs: number;
authorizationFact: ApprovedActionRecoveryAuthorizationFact;
}
export type ResolveApprovedActionRecoveryCommand =
| ResolveApprovedActionRecoveryAutomaticallyCommand
| ResolveApprovedActionRecoveryManuallyCommand;
export type ResolveApprovedActionRecoveryResult =
| {
status: 'resolved';
snapshot: Readonly<ApprovedActionRecoverySnapshot>;
}
| {
status: 'already_terminal';
snapshot: Readonly<ApprovedActionRecoverySnapshot>;
}
| { status: 'not_found' };
export interface ApprovedActionRecoveryRepository {
findById(
dispatchId: string,
): Promise<Readonly<ApprovedActionRecoverySnapshot> | null>;
listDue(
query: ListDueApprovedActionRecoveriesQuery,
): Promise<ListDueApprovedActionRecoveriesResult>;
claim(
command: ClaimApprovedActionRecoveryCommand,
): Promise<ClaimApprovedActionRecoveryResult>;
recordFinding(
command: RecordApprovedActionRecoveryFindingCommand,
): Promise<Readonly<ApprovedActionRecoverySnapshot>>;
resolve(
command: ResolveApprovedActionRecoveryCommand,
): Promise<ResolveApprovedActionRecoveryResult>;
}
@@ -0,0 +1,6 @@
import type { ApprovedRunCreationPlan } from '../domain/approvedRunAction';
export interface ApprovedRunActionPlanResolver {
/** Resolves an immutable, versioned plan without performing its side effect. */
resolve(actionRef: string): Promise<Readonly<ApprovedRunCreationPlan> | null>;
}
@@ -0,0 +1,20 @@
import type { ApprovedRunCreationPlan } from '../domain/approvedRunAction';
import type { ApprovedActionDispatchExecutionSnapshot } from '../domain/approvedActionDispatchExecution';
import type { RunAttemptRecord, RunRecord } from '../domain/run';
export interface ApprovedRunReference {
run: Readonly<RunRecord>;
attempt: Readonly<RunAttemptRecord>;
}
export interface CreateApprovedRunCommand {
snapshot: Readonly<ApprovedActionDispatchExecutionSnapshot>;
plan: Readonly<ApprovedRunCreationPlan>;
}
export interface ApprovedRunActionRepository {
/** Creates the Run aggregate and its bound success receipt atomically. */
create(
command: Readonly<CreateApprovedRunCommand>,
): Promise<Readonly<ApprovedRunReference>>;
}
@@ -0,0 +1,20 @@
import type { ArtifactReadSubject } from '../domain/artifactRead';
export type ArtifactReadAuthorizationEffect =
| 'allow'
| 'deny'
| 'require_approval';
export interface ArtifactReadAuthorizationRequest {
action: 'artifact.read';
subject: Readonly<ArtifactReadSubject>;
projectId: string;
runId: string;
logArtifactId: string;
}
export interface ArtifactReadAuthorizer {
authorize(
request: Readonly<ArtifactReadAuthorizationRequest>,
): Promise<ArtifactReadAuthorizationEffect>;
}
@@ -0,0 +1,50 @@
import type {
CancellationDispatchRecord,
CancellationDispatchResult,
} from '../domain/cancellationDispatch';
import type { RunEventRecord } from '../domain/run';
export interface ClaimCancellationDispatchCommand {
runId: string;
attemptId: string;
requestedAtMs: number;
owner: string;
leaseToken: string;
nowMs: number;
leaseDurationMs: number;
}
export type ClaimCancellationDispatchResult =
| { status: 'claimed'; dispatch: CancellationDispatchRecord }
| { status: 'not_eligible' }
| {
status: 'not_due' | 'leased' | 'dispatched' | 'blocked';
dispatch: CancellationDispatchRecord;
};
export interface RecordCancellationDispatchResultCommand {
runId: string;
attemptId: string;
owner: string;
leaseToken: string;
expectedVersion: number;
result: CancellationDispatchResult;
atMs: number;
nextAttemptAtMs?: number;
eventId: string;
}
export interface RecordCancellationDispatchResult {
dispatch: CancellationDispatchRecord;
event: RunEventRecord;
}
export interface CancellationDispatchRepository {
findByRunId(runId: string): Promise<CancellationDispatchRecord | null>;
claim(
command: ClaimCancellationDispatchCommand,
): Promise<ClaimCancellationDispatchResult>;
recordResult(
command: RecordCancellationDispatchResultCommand,
): Promise<RecordCancellationDispatchResult>;
}
@@ -0,0 +1,30 @@
import type {
CompletionReceiptJournalCursor,
CompletionReceiptJournalPage,
} from '../domain/completionReceiptJournal';
export const MAX_COMPLETION_RECEIPT_JOURNAL_BATCH_SIZE = 64;
export interface RegisterCompletionReceiptCommand {
attemptId: string;
runId: string;
registeredAtMs: number;
}
export interface QuarantineCompletionReceiptCommand {
attemptId: string;
quarantineRef: string;
purgeAfterMs: number;
updatedAtMs: number;
}
export interface CompletionReceiptJournal {
register(command: RegisterCompletionReceiptCommand): Promise<void>;
markQuarantined(command: QuarantineCompletionReceiptCommand): Promise<void>;
resolve(attemptId: string): Promise<boolean>;
listCandidates(options: {
observedAtMs: number;
cursor?: CompletionReceiptJournalCursor;
limit?: number;
}): Promise<CompletionReceiptJournalPage>;
}
@@ -0,0 +1,49 @@
import type { RunAttemptStatus } from '../domain/run';
export type CompletionReceiptDirectoryEntryKind =
| 'receipt'
| 'temporary'
| 'unknown'
| 'unsafe';
export interface CompletionReceiptDirectoryEntry {
shard: string;
name: string;
kind: CompletionReceiptDirectoryEntryKind;
attemptId?: string;
modifiedAtMs: number;
sizeBytes: number;
filesystemIdentity: string;
}
export interface CompletionReceiptShardSnapshot {
shard: string;
entries: readonly CompletionReceiptDirectoryEntry[];
overflow: boolean;
}
export type CompletionReceiptOrphanQuarantineResult =
| { status: 'quarantined'; reference: string }
| { status: 'changed' };
export interface CompletionReceiptOrphanDirectory {
inspectShard(
shard: string,
maxEntries: number,
): Promise<CompletionReceiptShardSnapshot>;
quarantine(
entry: CompletionReceiptDirectoryEntry,
): Promise<CompletionReceiptOrphanQuarantineResult>;
}
export interface CompletionReceiptOwnership {
attemptId: string;
attemptStatus?: RunAttemptStatus;
journalState?: 'pending' | 'quarantined';
}
export interface CompletionReceiptOwnershipSource {
lookup(
attemptIds: readonly string[],
): Promise<ReadonlyMap<string, CompletionReceiptOwnership>>;
}
@@ -0,0 +1,11 @@
import type { CompletionReceipt } from '../domain/completionReceipt';
export interface CompletionReceiptStore {
publish(receipt: CompletionReceipt): Promise<void>;
read(attemptId: string): Promise<CompletionReceipt | undefined>;
remove(attemptId: string): Promise<boolean>;
quarantineReference(attemptId: string): string;
/** Moves an untrusted receipt out of the replay path for later inspection. */
quarantine(attemptId: string): Promise<string | undefined>;
purgeQuarantine(attemptId: string): Promise<boolean>;
}
+24
View File
@@ -0,0 +1,24 @@
import type {
ExecutionContext,
ExecutionHandle,
ExecutionInspection,
ExecutionSpec,
ExecutionStopReason,
ExecutionStopResult,
ExecutorCapabilities,
ExecutorType,
} from '../domain/execution';
export interface Executor {
readonly type: ExecutorType;
capabilities(): ExecutorCapabilities;
start(
spec: ExecutionSpec,
context: ExecutionContext,
): Promise<ExecutionHandle>;
stop(
handle: ExecutionHandle,
reason: ExecutionStopReason,
): Promise<ExecutionStopResult>;
inspect(handle: ExecutionHandle): Promise<ExecutionInspection>;
}
@@ -0,0 +1,8 @@
import type { PolicySubject } from '../domain/projectPolicy';
export interface IdentityDirectoryRepository {
resolveAuthenticationSubject(
provider: string,
providerSubject: string,
): Promise<Readonly<PolicySubject> | null>;
}
@@ -0,0 +1,18 @@
export interface LegacyExecutionSelector {
legacyCronId: number;
pid?: number;
logArtifactId?: string;
}
export interface LegacyExecutionCancellationFact
extends LegacyExecutionSelector {
atMs: number;
scope: 'all' | 'one';
reason: 'user' | 'policy' | 'shutdown' | 'reconcile';
}
export interface LegacyExecutionCallbackFact extends LegacyExecutionSelector {
atMs: number;
phase: 'running' | 'finished';
exitCode?: number;
}
@@ -0,0 +1,54 @@
import type { ExecutionOrigin } from '../domain/run';
export interface LegacyExecutionAcceptedFact {
origin: ExecutionOrigin;
projectId: string;
taskId: string;
taskRevision: string;
taskName?: string;
legacyCronId?: number;
triggerType: string;
triggeredBy?: string;
requestId?: string;
scheduledForMs?: number;
acceptedAtMs: number;
}
export interface LegacyExecutionSpawnedFact {
atMs: number;
pid?: number;
executorHandle?: string;
logArtifactId?: string;
}
export interface LegacyExecutionRunningFact {
atMs: number;
}
export interface LegacyExecutionStartFailedFact {
atMs: number;
errorCode: string;
}
export interface LegacyExecutionExitedFact {
atMs: number;
exitCode: number | null;
signal?: NodeJS.Signals;
}
export interface LegacyExecutionCancelledFact {
atMs: number;
reason: 'user' | 'policy' | 'shutdown' | 'reconcile';
}
export interface LegacyExecutionObservation {
spawned(fact: LegacyExecutionSpawnedFact): void;
running(fact: LegacyExecutionRunningFact): void;
startFailed(fact: LegacyExecutionStartFailedFact): void;
exited(fact: LegacyExecutionExitedFact): void;
cancelled(fact: LegacyExecutionCancelledFact): void;
}
export interface LegacyExecutionObserver {
begin(fact: LegacyExecutionAcceptedFact): LegacyExecutionObservation;
}
@@ -0,0 +1,22 @@
export type LegacyPanelPlatform = 'desktop' | 'mobile';
export interface LegacyPanelSessionSource {
isActive(token: string, platform: LegacyPanelPlatform): Promise<boolean>;
}
export interface LegacyPanelTokenSnapshot {
value: string;
}
export interface LegacyPanelAuthSnapshot {
token?: string;
tokens?: Readonly<
Record<
string,
string | readonly LegacyPanelTokenSnapshot[] | null | undefined
>
>;
}
export type LegacyPanelAuthSnapshotReader =
() => Promise<Readonly<LegacyPanelAuthSnapshot> | null>;
@@ -0,0 +1,35 @@
import type {
ExecutionOrigin,
RunAttemptStatus,
RunStatus,
} from '../domain/run';
export const MAX_LEGACY_SHADOW_LOOKUP_CANDIDATES = 64;
export interface ActiveLegacyShadowRun {
runId: string;
attemptId: string;
origin: ExecutionOrigin;
runStatus: RunStatus;
attemptStatus: RunAttemptStatus;
pid?: number;
logArtifactId?: string;
createdAtMs: number;
}
export interface ActiveLegacyShadowRunQuery {
legacyCronId: number;
origins: readonly ExecutionOrigin[];
limit?: number;
}
export interface ActiveLegacyShadowRunResult {
candidates: readonly ActiveLegacyShadowRun[];
truncated: boolean;
}
export interface LegacyShadowRunLocator {
listActiveByLegacyCron(
query: ActiveLegacyShadowRunQuery,
): Promise<ActiveLegacyShadowRunResult>;
}
@@ -0,0 +1,21 @@
import type { LocalArtifactReadRange } from '../domain/artifactRead';
export interface AvailableLocalArtifactByteRange {
status: 'available';
content: Buffer;
start: number;
endExclusive: number;
totalBytes: number;
nextOffset?: number;
}
export type LocalArtifactByteRangeReadResult =
| AvailableLocalArtifactByteRange
| { status: 'missing' };
export interface LocalArtifactByteRangeReader {
read(
logArtifactId: string,
range: Readonly<LocalArtifactReadRange>,
): Promise<LocalArtifactByteRangeReadResult>;
}
@@ -0,0 +1,12 @@
export interface LocalArtifactCapacitySnapshot {
availableBytes: bigint;
totalBytes: bigint;
}
export interface LocalArtifactCapacityProbe {
inspect(root: string): Promise<LocalArtifactCapacitySnapshot>;
}
export interface LocalArtifactCapacitySource {
inspect(): Promise<LocalArtifactCapacitySnapshot>;
}
@@ -0,0 +1,8 @@
export interface LocalArtifactFileRetirementResult {
disposition: 'deleted' | 'already_absent';
bytesReclaimed: number;
}
export interface LocalArtifactFileRetirementStore {
retire(logArtifactId: string): Promise<LocalArtifactFileRetirementResult>;
}
@@ -0,0 +1,9 @@
import type { LocalArtifactReadMetadata } from '../domain/artifactRead';
export interface LocalArtifactReadMetadataRepository {
find(input: {
projectId: string;
runId: string;
logArtifactId: string;
}): Promise<Readonly<LocalArtifactReadMetadata> | null>;
}
@@ -0,0 +1,11 @@
import type { LocalArtifactRetentionCursor } from '../domain/localArtifactRetention';
import type { LocalArtifactRetentionCheckpoint } from '../domain/localArtifactRetentionCheckpoint';
export interface LocalArtifactRetentionCheckpointStore {
load(): Promise<Readonly<LocalArtifactRetentionCheckpoint>>;
compareAndSet(value: {
expectedVersion: number;
cursor?: LocalArtifactRetentionCursor;
updatedAtMs: number;
}): Promise<boolean>;
}
@@ -0,0 +1,24 @@
import type {
LocalArtifactRetentionCandidate,
LocalArtifactRetentionCursor,
LocalArtifactRetentionRecord,
} from '../domain/localArtifactRetention';
export const MAX_LOCAL_ARTIFACT_RETENTION_PAGE_SIZE = 64;
export interface LocalArtifactRetentionPage {
candidates: readonly LocalArtifactRetentionCandidate[];
truncated: boolean;
nextCursor?: LocalArtifactRetentionCursor;
}
export interface LocalArtifactRetentionRepository {
list(options: {
cutoffMs: number;
cursor?: LocalArtifactRetentionCursor;
limit: number;
}): Promise<LocalArtifactRetentionPage>;
record(
record: LocalArtifactRetentionRecord,
): Promise<'inserted' | 'existing'>;
}
@@ -0,0 +1,7 @@
import type { LocalArtifactTruncationFact } from '../domain/localArtifactTruncation';
export interface LocalArtifactTruncationFactStore {
read(
logArtifactId: string,
): Promise<Readonly<LocalArtifactTruncationFact> | null>;
}
@@ -0,0 +1,14 @@
import type { ExecutionOutputSink } from '../domain/execution';
import type { RunDispatchCandidate } from '../domain/runDispatchCandidate';
export interface PreparedLocalExecutionArtifact {
logArtifactId: string;
output: ExecutionOutputSink;
dispose(): void | Promise<void>;
}
export interface LocalExecutionArtifactAllocator {
prepare(
candidate: Readonly<RunDispatchCandidate>,
): Promise<PreparedLocalExecutionArtifact>;
}
@@ -0,0 +1,21 @@
import type { ExecutionContext } from '../domain/execution';
import type { RunDispatchCandidate } from '../domain/runDispatchCandidate';
export interface LocalExecutionContextRequest {
candidate: Readonly<RunDispatchCandidate>;
contextRef: string;
}
export interface MaterializedLocalExecutionContext {
context: ExecutionContext;
/** Opaque bounded reference persisted on the Attempt before spawn. */
logArtifactId?: string;
/** Optional non-blocking cleanup; plaintext capability values must not leak. */
dispose?: () => void | Promise<void>;
}
export interface LocalExecutionContextMaterializer {
prepare(
request: Readonly<LocalExecutionContextRequest>,
): Promise<MaterializedLocalExecutionContext | null>;
}
@@ -0,0 +1,12 @@
import type { LocalExecutionContextRecipe } from '../domain/localExecutionContextRecipe';
import type { LocalExecutionContextRecipeSource } from './localExecutionContextRecipeSource';
export type InsertLocalExecutionContextRecipeResult = 'inserted' | 'idempotent';
export interface LocalExecutionContextRecipeRepository
extends LocalExecutionContextRecipeSource {
insert(
recipe: LocalExecutionContextRecipe,
createdAtMs: number,
): Promise<InsertLocalExecutionContextRecipeResult>;
}
@@ -0,0 +1,6 @@
import type { LocalExecutionContextRecipe } from '../domain/localExecutionContextRecipe';
export interface LocalExecutionContextRecipeSource {
/** Resolves one exact opaque reference and never falls back to latest. */
resolve(contextRef: string): Promise<LocalExecutionContextRecipe | null>;
}
@@ -0,0 +1,20 @@
import type { ExecutionContext, ExecutionSpec } from '../domain/execution';
import type { RunDispatchCandidate } from '../domain/runDispatchCandidate';
export interface LocalRunDispatchPlan {
executionSpec: ExecutionSpec;
context: ExecutionContext;
logArtifactId?: string;
/** Optional non-blocking cleanup for attempt-scoped local resources. */
dispose?: () => void | Promise<void>;
}
/**
* Trusted local materializer. Implementations must resolve the pinned Task
* revision and create fresh Attempt-scoped output and Secret capabilities.
*/
export interface LocalRunDispatchPlanSource {
prepare(
candidate: Readonly<RunDispatchCandidate>,
): Promise<LocalRunDispatchPlan | null>;
}
@@ -0,0 +1,27 @@
import type {
LocalSecretEnvelope,
LocalSecretReference,
} from '../domain/localSecret';
export interface AppendLocalSecretEnvelopeCommand {
envelope: LocalSecretEnvelope;
expectedCurrentVersion: number;
}
export type AppendLocalSecretEnvelopeResult =
| { status: 'inserted'; envelope: LocalSecretEnvelope }
| { status: 'existing'; envelope: LocalSecretEnvelope };
export interface LocalSecretEnvelopeRepository {
append(
command: AppendLocalSecretEnvelopeCommand,
): Promise<AppendLocalSecretEnvelopeResult>;
findByMutation(
projectId: string,
name: string,
mutationId: string,
): Promise<LocalSecretEnvelope | null>;
resolveMany(
references: readonly LocalSecretReference[],
): Promise<readonly (LocalSecretEnvelope | null)[]>;
}
@@ -0,0 +1,13 @@
import type { RunDispatchCandidate } from '../domain/runDispatchCandidate';
export interface LocalSecretEnvironmentRequest {
candidate: Readonly<RunDispatchCandidate>;
secretRefs: readonly string[];
}
/** Returns plaintext only in memory, positionally aligned to secretRefs. */
export interface LocalSecretEnvironmentProvider {
resolve(
request: Readonly<LocalSecretEnvironmentRequest>,
): Promise<readonly string[] | null>;
}
@@ -0,0 +1,10 @@
export interface LocalSecretKeyMaterial {
keyId: string;
/** Exactly 32 bytes; the consumer owns and must wipe this copy. */
key: Uint8Array;
}
export interface LocalSecretKeyProvider {
active(): Promise<LocalSecretKeyMaterial>;
resolve(keyId: string): Promise<LocalSecretKeyMaterial | null>;
}
@@ -0,0 +1,24 @@
import type { ExecutionStopReason, ExecutorType } from '../domain/execution';
export type PersistedExecutionStopStatus =
| 'termination_requested'
| 'already_exited'
| 'identity_mismatch'
| 'pid_mismatch'
| 'unsupported'
| 'invalid';
export interface PersistedExecutionStopResult {
status: PersistedExecutionStopStatus;
termSignalSent: boolean;
killSignalSent: boolean;
}
export interface PersistedExecutionController {
readonly executorType: ExecutorType;
stop(input: {
durableHandle: string;
expectedPid?: number;
reason: ExecutionStopReason;
}): Promise<PersistedExecutionStopResult>;
}
@@ -0,0 +1,18 @@
import type { ExecutorType } from '../domain/execution';
export type PersistedExecutionInspectionStatus =
| 'running'
| 'exited'
| 'identity_mismatch'
| 'unsupported'
| 'invalid';
export interface PersistedExecutionInspection {
status: PersistedExecutionInspectionStatus;
identityPid?: number;
}
export interface PersistedExecutionInspector {
readonly executorType: ExecutorType;
inspect(durableHandle: string): Promise<PersistedExecutionInspection>;
}
@@ -0,0 +1,36 @@
import type { ExecutionStopKind, ExecutorType } from '../domain/execution';
export const MAX_PRIMARY_CANCELLATION_BATCH_SIZE = 64;
export interface PrimaryCancellationCursor {
requestedAtMs: number;
runId: string;
}
export interface PrimaryCancellationAttemptReference {
attemptId: string;
executorType: ExecutorType;
executorHandle?: string;
pid?: number;
}
export interface PrimaryCancellationCandidate {
runId: string;
requestedAtMs: number;
reason: ExecutionStopKind;
attempts: readonly PrimaryCancellationAttemptReference[];
}
export interface PrimaryCancellationPage {
candidates: readonly PrimaryCancellationCandidate[];
truncated: boolean;
unsafeAttemptOverflow: boolean;
nextCursor?: PrimaryCancellationCursor;
}
export interface PrimaryCancellationSource {
listCandidates(options?: {
cursor?: PrimaryCancellationCursor;
limit?: number;
}): Promise<PrimaryCancellationPage>;
}
@@ -0,0 +1,3 @@
export interface PrimaryRunIdempotencyLookup {
findRunId(projectId: string, idempotencyKey: string): Promise<string | null>;
}
@@ -0,0 +1,32 @@
import type { ExecutorType } from '../domain/execution';
export const MAX_PRIMARY_RECOVERY_BATCH_SIZE = 64;
export interface PrimaryRunRecoveryCursor {
createdAtMs: number;
runId: string;
}
export interface PrimaryRunRecoveryAttemptReference {
attemptId: string;
executorType: ExecutorType;
}
export interface PrimaryRunRecoveryCandidate {
runId: string;
attempts: readonly PrimaryRunRecoveryAttemptReference[];
}
export interface PrimaryRunRecoveryPage {
candidates: readonly PrimaryRunRecoveryCandidate[];
truncated: boolean;
unsafeAttemptOverflow: boolean;
nextCursor?: PrimaryRunRecoveryCursor;
}
export interface PrimaryRunRecoverySource {
listCandidates(options?: {
cursor?: PrimaryRunRecoveryCursor;
limit?: number;
}): Promise<PrimaryRunRecoveryPage>;
}
@@ -0,0 +1,26 @@
export const MAX_PRIMARY_TIMEOUT_BATCH_SIZE = 64;
export interface PrimaryTimeoutCursor {
deadlineAtMs: number;
attemptId: string;
}
export interface PrimaryTimeoutCandidate {
runId: string;
attemptId: string;
deadlineAtMs: number;
}
export interface PrimaryTimeoutPage {
candidates: readonly PrimaryTimeoutCandidate[];
truncated: boolean;
nextCursor?: PrimaryTimeoutCursor;
}
export interface PrimaryTimeoutSource {
listOverdue(options: {
nowMs: number;
cursor?: PrimaryTimeoutCursor;
limit?: number;
}): Promise<PrimaryTimeoutPage>;
}
@@ -0,0 +1,36 @@
import type {
PolicySubject,
ProjectRoleBindingRecord,
} from '../domain/projectPolicy';
import type { ProjectOwnerBootstrapChallengeRecord } from '../domain/projectOwnerBootstrap';
export interface IssueProjectOwnerBootstrapChallengeCommand {
projectId: string;
challengeId: string;
tokenDigest: string;
issuedAtMs: number;
expiresAtMs: number;
}
export interface ClaimProjectOwnerBootstrapChallengeCommand {
projectId: string;
challengeId: string;
tokenDigest: string;
subject: PolicySubject;
claimedAtMs: number;
}
export interface ClaimProjectOwnerBootstrapChallengeResult {
status: 'claimed' | 'existing';
binding: Readonly<ProjectRoleBindingRecord>;
}
export interface ProjectOwnerBootstrapRepository {
issue(
command: IssueProjectOwnerBootstrapChallengeCommand,
): Promise<Readonly<ProjectOwnerBootstrapChallengeRecord>>;
claim(
command: ClaimProjectOwnerBootstrapChallengeCommand,
): Promise<ClaimProjectOwnerBootstrapChallengeResult>;
}
@@ -0,0 +1,26 @@
import type {
PolicySubject,
ProjectPolicySnapshot,
ProjectRoleBindingRecord,
} from '../domain/projectPolicy';
export interface AppendProjectRoleBindingCommand {
expectedCurrentVersion: number;
binding: ProjectRoleBindingRecord;
}
export interface AppendProjectRoleBindingResult {
status: 'inserted' | 'existing';
binding: Readonly<ProjectRoleBindingRecord>;
}
export interface ProjectPolicyRepository {
resolve(
projectId: string,
subject: Readonly<PolicySubject>,
): Promise<Readonly<ProjectPolicySnapshot> | null>;
append(
command: AppendProjectRoleBindingCommand,
): Promise<AppendProjectRoleBindingResult>;
}
@@ -0,0 +1,16 @@
import type {
RunDispatchCandidate,
RunDispatchCandidateCursor,
} from '../domain/runDispatchCandidate';
export interface ListRunDispatchCandidatesOptions {
observedAtMs: number;
after?: RunDispatchCandidateCursor;
limit?: number;
}
export interface RunDispatchCandidateSource {
listCandidates(
options: ListRunDispatchCandidatesOptions,
): Promise<RunDispatchCandidate[]>;
}
@@ -0,0 +1,23 @@
export const MAX_RUN_DISPATCH_LEASE_EXPIRY_PAGE_SIZE = 64;
export interface RunDispatchLeaseExpiryCursor {
expiresAtMs: number;
attemptId: string;
}
export interface ExpiredRunDispatchLeaseCandidate
extends RunDispatchLeaseExpiryCursor {
runId: string;
}
export interface ListExpiredRunDispatchLeasesOptions {
observedAtMs: number;
after?: RunDispatchLeaseExpiryCursor;
limit?: number;
}
export interface RunDispatchLeaseExpirySource {
listExpired(
options: ListExpiredRunDispatchLeasesOptions,
): Promise<readonly ExpiredRunDispatchLeaseCandidate[]>;
}
@@ -0,0 +1,137 @@
import type { RunDispatchLeaseRecord } from '../domain/runDispatchLease';
import type { RunEventRecord } from '../domain/run';
import type { RunRepositoryTransaction } from './runRepository';
export interface ClaimRunDispatchLeaseCommand {
runId: string;
attemptId: string;
workerId: string;
workerSessionId: string;
workerGeneration: number;
leaseToken: string;
nowMs: number;
leaseDurationMs: number;
eventId: string;
}
export type ClaimRunDispatchLeaseResult =
| {
status: 'claimed';
lease: RunDispatchLeaseRecord;
event: RunEventRecord;
}
| { status: 'idempotent' | 'leased'; lease: RunDispatchLeaseRecord }
| {
status: 'not_eligible' | 'worker_unavailable' | 'capacity_exhausted';
};
export interface RenewRunDispatchLeaseCommand {
attemptId: string;
workerId: string;
workerSessionId: string;
workerGeneration: number;
leaseGeneration: number;
leaseToken: string;
expectedVersion: number;
nowMs: number;
leaseDurationMs: number;
}
export interface ReleaseRunDispatchLeaseCommand {
runId: string;
attemptId: string;
workerId: string;
workerSessionId: string;
workerGeneration: number;
leaseGeneration: number;
leaseToken: string;
expectedVersion: number;
reason: 'declined' | 'shutdown' | 'start_failed' | 'capacity_changed';
nowMs: number;
eventId: string;
}
export interface ReleaseRunDispatchLeaseResult {
lease: RunDispatchLeaseRecord;
event?: RunEventRecord;
}
export interface CompleteWithRunDispatchLeaseCommand {
runId: string;
attemptId: string;
workerId: string;
workerSessionId: string;
workerGeneration: number;
leaseGeneration: number;
leaseToken: string;
expectedVersion: number;
completedAtMs: number;
}
export interface ExpireRunDispatchLeaseCommand {
runId: string;
attemptId: string;
observedAtMs: number;
}
export interface UseRunDispatchLeaseCommand {
runId: string;
attemptId: string;
workerId: string;
workerSessionId: string;
workerGeneration: number;
leaseGeneration: number;
leaseToken: string;
expectedVersion: number;
observedAtMs: number;
}
export interface UseRunDispatchLeaseResult<T> {
value: T;
lease: RunDispatchLeaseRecord;
}
export interface CompleteWithRunDispatchLeaseResult<T> {
value: T;
lease: RunDispatchLeaseRecord;
}
export type ExpireRunDispatchLeaseResult<T> =
| { status: 'expired'; value: T; lease: RunDispatchLeaseRecord }
| { status: 'already_expired'; lease: RunDispatchLeaseRecord }
| {
status: 'not_found' | 'not_due' | 'not_eligible';
lease?: RunDispatchLeaseRecord;
};
export interface RunDispatchLeaseRepository {
findByAttemptId(attemptId: string): Promise<RunDispatchLeaseRecord | null>;
claim(
command: ClaimRunDispatchLeaseCommand,
): Promise<ClaimRunDispatchLeaseResult>;
renew(command: RenewRunDispatchLeaseCommand): Promise<RunDispatchLeaseRecord>;
release(
command: ReleaseRunDispatchLeaseCommand,
): Promise<ReleaseRunDispatchLeaseResult>;
withLease<T>(
command: UseRunDispatchLeaseCommand,
work: (
transaction: RunRepositoryTransaction,
lease: RunDispatchLeaseRecord,
) => Promise<T>,
): Promise<UseRunDispatchLeaseResult<T>>;
completeWithLease<T>(
command: CompleteWithRunDispatchLeaseCommand,
work: (
transaction: RunRepositoryTransaction,
lease: RunDispatchLeaseRecord,
) => Promise<T>,
): Promise<CompleteWithRunDispatchLeaseResult<T>>;
expireWithLease<T>(
command: ExpireRunDispatchLeaseCommand,
work: (
transaction: RunRepositoryTransaction,
lease: RunDispatchLeaseRecord,
) => Promise<T>,
): Promise<ExpireRunDispatchLeaseResult<T>>;
}
@@ -0,0 +1,10 @@
import type { RunDispatchCandidate } from '../domain/runDispatchCandidate';
import type { RunDispatchPlan } from '../domain/runDispatchOffer';
/**
* Prepares one bounded plan in trusted control-plane memory. The Dispatcher must
* not expose it as an execution offer until the corresponding lease is claimed.
*/
export interface RunDispatchPlanSource {
prepare(candidate: RunDispatchCandidate): Promise<RunDispatchPlan | null>;
}
@@ -0,0 +1,16 @@
import type {
RecoverableRunDispatch,
RunDispatchRecoveryCursor,
} from '../domain/runDispatchRecovery';
export interface ListRecoverableRunDispatchesOptions {
observedAtMs: number;
after?: RunDispatchRecoveryCursor;
limit?: number;
}
export interface RunDispatchRecoverySource {
listRecoverable(
options: ListRecoverableRunDispatchesOptions,
): Promise<RecoverableRunDispatch[]>;
}
+18
View File
@@ -0,0 +1,18 @@
export const MAX_RUN_LOST_RETRY_PAGE_SIZE = 64;
export interface RunLostRetryCandidate {
runId: string;
phase: 'lost' | 'retry_wait';
availableAtMs: number;
}
export interface ListRunLostRetryCandidatesOptions {
observedAtMs: number;
limit?: number;
}
export interface RunLostRetrySource {
listCandidates(
options: ListRunLostRetryCandidatesOptions,
): Promise<readonly RunLostRetryCandidate[]>;
}
+59
View File
@@ -0,0 +1,59 @@
import type {
RunAttemptRecord,
RunAttemptStatus,
RunEventRecord,
RunRecord,
} from '../domain/run';
import type { RunRetryPolicyRecord } from '../domain/runRetryPolicy';
export const MAX_RUN_EVENT_PAYLOAD_BYTES = 16 * 1024;
export const MAX_RUN_EVENT_PAGE_SIZE = 500;
export const MAX_CANCELLATION_RECOVERY_PAGE_SIZE = 500;
export interface RunRepositoryReader {
findRunById(runId: string): Promise<RunRecord | null>;
findAttemptById(attemptId: string): Promise<RunAttemptRecord | null>;
findLatestAttemptByRunId(runId: string): Promise<RunAttemptRecord | null>;
findRetryPolicyByRunId(runId: string): Promise<RunRetryPolicyRecord | null>;
listEvents(
runId: string,
options?: { afterSequence?: number; limit?: number },
): Promise<RunEventRecord[]>;
listCancellationRequested(options?: {
beforeMs?: number;
limit?: number;
}): Promise<RunRecord[]>;
}
export interface RunRepositoryTransaction extends RunRepositoryReader {
insertRun(run: RunRecord): Promise<void>;
insertAttempt(attempt: RunAttemptRecord): Promise<void>;
insertRetryPolicy(policy: RunRetryPolicyRecord): Promise<void>;
/**
* Replaces a Run only when its persisted version still equals
* `expectedVersion`. The supplied Run must carry `expectedVersion + 1`.
*/
compareAndSetRun(run: RunRecord, expectedVersion: number): Promise<boolean>;
/**
* Replaces an Attempt only when both state and callback sequence still match.
* The Run aggregate version remains the primary serialization boundary.
*/
compareAndSetAttempt(
attempt: RunAttemptRecord,
expected: {
status: RunAttemptStatus;
callbackSequence: number;
},
): Promise<boolean>;
compareAndSetRetryPolicy(
policy: RunRetryPolicyRecord,
expectedVersion: number,
): Promise<boolean>;
appendEvent(event: RunEventRecord): Promise<void>;
}
export interface RunRepository extends RunRepositoryReader {
transaction<T>(
work: (transaction: RunRepositoryTransaction) => Promise<T>,
): Promise<T>;
}
@@ -0,0 +1,23 @@
import type { ExecutionOrigin } from '../domain/run';
import type { RunRetryPolicyDefinition } from '../domain/runRetryPolicy';
export interface RunRetryPolicyAdmissionRequest {
projectId: string;
taskId: string;
taskRevision: string;
triggerType: string;
executionOrigin: ExecutionOrigin;
}
/**
* Trusted server-side admission. Implementations must resolve policy from the
* pinned Task revision or another administrator-controlled immutable source;
* an individual Run request is never allowed to self-assert retry safety.
* `deduplicated` is valid only when that revision binds an enforced business
* deduplication contract, not merely a descriptive user flag.
*/
export interface RunRetryPolicyAdmission {
resolve(
request: Readonly<RunRetryPolicyAdmissionRequest>,
): Promise<RunRetryPolicyDefinition | undefined>;
}
@@ -0,0 +1,34 @@
import type { RuntimeRolloutPolicy } from '../domain/runtimeRollout';
import type { RuntimeRolloutManifest } from '../domain/runtimeRolloutManifest';
export type RuntimeRolloutLoadStatus =
| 'missing'
| 'disabled'
| 'accepted'
| 'rejected';
export interface RuntimeRolloutLoadAudit {
event: 'runtime.rollout_config_evaluated';
evaluatedAtMs: number;
sourcePath: string;
status: RuntimeRolloutLoadStatus;
sourceSha256?: string;
revision?: string;
reasonCode?:
| 'FILE_MISSING'
| 'FILE_READ_FAILED'
| 'FILE_TOO_LARGE'
| 'INVALID_JSON'
| 'INVALID_MANIFEST';
}
export interface RuntimeRolloutLoadResult {
status: RuntimeRolloutLoadStatus;
policy: RuntimeRolloutPolicy;
audit: RuntimeRolloutLoadAudit;
manifest?: RuntimeRolloutManifest;
}
export interface RuntimeRolloutLoader {
load(): Promise<RuntimeRolloutLoadResult>;
}
@@ -0,0 +1,13 @@
import type { PinnedTaskExecutionRevision } from '../domain/taskExecutionRevision';
import type { TaskExecutionRevisionSource } from './taskExecutionRevisionSource';
export type InsertTaskExecutionRevisionResult = 'inserted' | 'idempotent';
/** Append-only revision store. Existing identities can never be overwritten. */
export interface TaskExecutionRevisionRepository
extends TaskExecutionRevisionSource {
insert(
revision: PinnedTaskExecutionRevision,
createdAtMs: number,
): Promise<InsertTaskExecutionRevisionResult>;
}
@@ -0,0 +1,14 @@
import type { PinnedTaskExecutionRevision } from '../domain/taskExecutionRevision';
export interface TaskExecutionRevisionRequest {
projectId: string;
taskId: string;
taskRevision: string;
}
/** Reads an immutable revision; implementations must not fall back to latest. */
export interface TaskExecutionRevisionSource {
resolve(
request: Readonly<TaskExecutionRevisionRequest>,
): Promise<PinnedTaskExecutionRevision | null>;
}
@@ -0,0 +1,31 @@
import type { WorkerCapabilities, WorkerRecord } from '../domain/worker';
export interface RegisterWorkerRequest {
workerId: string;
sessionId: string;
capabilities: WorkerCapabilities;
maxConcurrentRuns: number;
availableSlots: number;
}
export interface HeartbeatWorkerRequest {
workerId: string;
sessionId: string;
generation: number;
expectedVersion: number;
availableSlots: number;
}
export interface TransitionWorkerRequest {
workerId: string;
sessionId: string;
generation: number;
expectedVersion: number;
}
export interface WorkerControlPlaneClient {
register(request: RegisterWorkerRequest): Promise<WorkerRecord>;
heartbeat(request: HeartbeatWorkerRequest): Promise<WorkerRecord>;
drain(request: TransitionWorkerRequest): Promise<WorkerRecord>;
disconnect(request: TransitionWorkerRequest): Promise<WorkerRecord>;
}
@@ -0,0 +1,14 @@
import type { CompletionReceipt } from '../domain/completionReceipt';
import type { WorkerExecutionOfferJournalRecord } from '../domain/workerExecutionOffer';
/**
* Verifies the Worker-local completion capability without exposing it in a
* recovery result. Implementations may compare a persisted digest, consult a
* secure local store, or validate a rotated capability.
*/
export interface WorkerExecutionCompletionReceiptAuthenticator {
authenticate(
receipt: CompletionReceipt,
record: WorkerExecutionOfferJournalRecord,
): boolean | Promise<boolean>;
}
@@ -0,0 +1,13 @@
import type { ExecutionContext } from '../domain/execution';
import type { ClaimedExecutionOffer } from '../domain/runDispatchOffer';
export interface PreparedWorkerExecutionContext {
context: ExecutionContext;
logArtifactId?: string;
}
export interface WorkerExecutionContextFactory {
prepare(
offer: ClaimedExecutionOffer,
): Promise<PreparedWorkerExecutionContext>;
}
@@ -0,0 +1,5 @@
export type WorkerExecutionDrainResult = 'drained' | 'timed_out';
export interface WorkerExecutionDrainer {
drain(): Promise<WorkerExecutionDrainResult>;
}
@@ -0,0 +1,24 @@
import type { WorkerExecutionOfferJournalRecord } from '../domain/workerExecutionOffer';
export type WorkerExecutionOfferJournalCreateResult = 'created' | 'exists';
export interface WorkerExecutionOfferJournalPage {
records: readonly WorkerExecutionOfferJournalRecord[];
nextAfterOfferId?: string;
}
export interface WorkerExecutionOfferJournal {
create(
record: WorkerExecutionOfferJournalRecord,
): Promise<WorkerExecutionOfferJournalCreateResult>;
read(offerId: string): Promise<WorkerExecutionOfferJournalRecord | undefined>;
replace(
record: WorkerExecutionOfferJournalRecord,
expectedRevision: number,
): Promise<void>;
remove(offerId: string, expectedRevision?: number): Promise<boolean>;
list(options?: {
afterOfferId?: string;
limit?: number;
}): Promise<WorkerExecutionOfferJournalPage>;
}
@@ -0,0 +1,11 @@
export type WorkerExecutionOfferJournalOwnershipState =
| 'unowned'
| 'owned'
| 'releasing'
| 'compromised';
export interface WorkerExecutionOfferJournalOwnership {
ownershipState(): WorkerExecutionOfferJournalOwnershipState;
acquireOwnership(): Promise<'acquired' | 'already_owned'>;
releaseOwnership(): Promise<'released' | 'not_owned' | 'compromised'>;
}
@@ -0,0 +1,58 @@
import type { WorkerRecord, WorkerStatus } from '../domain/worker';
export const MAX_AVAILABLE_WORKER_PAGE_SIZE = 64;
export interface RegisterWorkerSessionCommand {
workerId: string;
sessionId: string;
capabilitiesJson: string;
capabilitiesHash: string;
maxConcurrentRuns: number;
availableSlots: number;
registeredAtMs: number;
leaseExpiresAtMs: number;
}
export interface RegisterWorkerSessionResult {
worker: WorkerRecord;
replacedSession: boolean;
}
export interface HeartbeatWorkerSessionCommand {
workerId: string;
sessionId: string;
generation: number;
expectedVersion: number;
availableSlots: number;
heartbeatAtMs: number;
leaseExpiresAtMs: number;
}
export interface TransitionWorkerSessionCommand {
workerId: string;
sessionId: string;
generation: number;
expectedVersion: number;
status: Extract<WorkerStatus, 'draining' | 'offline'>;
transitionedAtMs: number;
}
export interface AvailableWorkerPage {
workers: readonly WorkerRecord[];
truncated: boolean;
nextCursor?: string;
}
export interface WorkerRegistryRepository {
findById(workerId: string): Promise<WorkerRecord | null>;
register(
command: RegisterWorkerSessionCommand,
): Promise<RegisterWorkerSessionResult>;
heartbeat(command: HeartbeatWorkerSessionCommand): Promise<WorkerRecord>;
transition(command: TransitionWorkerSessionCommand): Promise<WorkerRecord>;
listAvailable(options: {
observedAtMs: number;
afterWorkerId?: string;
limit?: number;
}): Promise<AvailableWorkerPage>;
}
@@ -0,0 +1,18 @@
import type {
AcknowledgeRemoteRunRunningCommand,
AcknowledgeRemoteRunStartingCommand,
FailRemoteRunStartCommand,
RemoteRunActivationResult,
} from '../application/remoteRunActivationService';
export interface WorkerRemoteRunActivationClient {
acknowledgeStarting(
command: AcknowledgeRemoteRunStartingCommand,
): Promise<RemoteRunActivationResult>;
acknowledgeRunning(
command: AcknowledgeRemoteRunRunningCommand,
): Promise<RemoteRunActivationResult>;
failStart(
command: FailRemoteRunStartCommand,
): Promise<RemoteRunActivationResult>;
}
@@ -0,0 +1,8 @@
import type { RemoteRunCompletionCommand } from '../application/remoteRunCompletionService';
import type { PrimaryRunCompletionResult } from '../application/primaryRunCompletionService';
export interface WorkerRemoteRunCompletionClient {
complete(
command: RemoteRunCompletionCommand,
): Promise<PrimaryRunCompletionResult>;
}
@@ -0,0 +1,15 @@
import type { RunDispatchLeaseRecord } from '../domain/runDispatchLease';
import type {
FencedRunDispatchLeaseRequest,
ReleaseRunDispatchLeaseRequest,
} from '../application/runDispatchLeaseService';
import type { ReleaseRunDispatchLeaseResult } from './runDispatchLeaseRepository';
export interface WorkerRunLeaseClient {
renew(
request: FencedRunDispatchLeaseRequest,
): Promise<RunDispatchLeaseRecord>;
release(
request: ReleaseRunDispatchLeaseRequest,
): Promise<ReleaseRunDispatchLeaseResult>;
}
@@ -0,0 +1,8 @@
import type { RunDispatchLeaseRecord } from '../domain/runDispatchLease';
/** In-process lease renewal view used by the offer receiver. */
export interface WorkerRunLeaseTracker {
track(lease: RunDispatchLeaseRecord): void;
untrack(attemptId: string): RunDispatchLeaseRecord | undefined;
leases(): RunDispatchLeaseRecord[];
}