/** * Mosaic Native Kanban — frozen Mechanical Coordinator contracts v1. * * The pure decision engine and persistence/orchestration service are separate. * Neither surface can create scope, edit acceptance, waive gates, certify, * merge, release a deployment, or close a provider issue. */ import type { DeliberateWriteDenialV1, InternalKanbanMutationContextV1, KanbanEvaluationContextV1, KanbanMutationFailureV1, RetryableTransportErrorV1, VersionConflictV1, } from './health-state.v1.js'; export const COORDINATOR_CONTRACT_VERSION = '1.0.0' as const; export type Uuid = string; export type IsoTimestamp = string; /** PostgreSQL bigint-safe decimal string; never a JavaScript number. */ export type FencingTokenV1 = string; export const specialistRoles = [ 'planning', 'enhance', 'coder', 'review', 'security-review', 'pr-monitor', 'certifier', ] as const; export type SpecialistRole = (typeof specialistRoles)[number]; /** One vocabulary shared with task_assignment_state_v1 in the Drizzle schema. */ export const assignmentStates = [ 'awaiting_approval', 'policy_pre_authorized', 'approved', 'rejected', 'leased', 'released', 'expired', 'superseded', ] as const; export type AssignmentStateV1 = (typeof assignmentStates)[number]; export const readinessStates = [ 'dependency-gated', 'schedule-gated', 'policy-gated', 'lease-available', 'leased', 'retry-delayed', 'exhausted', 'quarantined', ] as const; export type ReadinessState = (typeof readinessStates)[number]; export interface RetryStateSnapshotV1 { disposition: 'available' | 'retry_delayed' | 'quarantined' | 'exhausted'; attemptCount: number; maxAttempts: number; nextEligibleAt: IsoTimestamp | null; idempotent: boolean; terminalReason: string | null; version: number; } export interface TaskEligibilitySnapshotV1 { workspaceId: Uuid; taskId: Uuid; taskVersion: number; projectId: Uuid; projectActive: boolean; missionId: Uuid | null; missionActive: boolean; status: 'ready'; priority: 'critical' | 'high' | 'medium' | 'low'; boardRank: string; dueAt: IsoTimestamp | null; notBeforeAt: IsoTimestamp | null; createdAt: IsoTimestamp; requiredRole: SpecialistRole; requiredCapabilities: readonly string[]; blockingDependencies: readonly { taskId: Uuid; done: boolean; completionConditionSatisfied: boolean; }[]; releaseApproval: { decisionId: Uuid; approved: boolean; policyRevision: string; } | null; activeLeaseId: Uuid | null; retry: RetryStateSnapshotV1; } export interface AgentSessionSnapshotV1 { workspaceId: Uuid; agentId: Uuid; sessionId: Uuid; state: 'available' | 'busy'; roles: readonly SpecialistRole[]; capabilities: readonly string[]; capacity: number; activeLeaseCount: number; heartbeatAt: IsoTimestamp; } export interface EligibilityExplanationV1 { taskId: Uuid; eligible: boolean; readiness: ReadinessState; reasons: readonly { gate: | 'status' | 'project' | 'mission' | 'dependency' | 'schedule' | 'retry' | 'approval' | 'lease' | 'capability' | 'capacity' | 'health'; satisfied: boolean; code: string; detail: string; }[]; policyRevision: string; evaluatedAt: IsoTimestamp; } export interface AssignmentProposalDecisionV1 { workspaceId: Uuid; taskId: Uuid; taskVersion: number; targetAgentId: Uuid; targetSessionId: Uuid; specialistRole: SpecialistRole; initialState: 'awaiting_approval' | 'policy_pre_authorized'; policyRevision: string; explanation: EligibilityExplanationV1; expiresAt: IsoTimestamp; } export interface AssignmentCycleSnapshotV1 { context: KanbanEvaluationContextV1; tasks: readonly TaskEligibilitySnapshotV1[]; sessions: readonly AgentSessionSnapshotV1[]; workspaceFairness: Readonly>; limit: number; } export interface AssignmentCycleDecisionV1 { evaluatedTaskCount: number; proposals: readonly AssignmentProposalDecisionV1[]; explanations: readonly EligibilityExplanationV1[]; } export interface LeaseExpirySnapshotV1 { workspaceId: Uuid; taskId: Uuid; taskVersion: number; leaseId: Uuid; assignmentId: Uuid; sessionId: Uuid; fencingToken: FencingTokenV1; state: 'pending_ack' | 'active'; acknowledgeBy: IsoTimestamp; expiresAt: IsoTimestamp; lastHeartbeatAt: IsoTimestamp | null; retry: RetryStateSnapshotV1; } export interface LeaseExpiryDecisionV1 { leaseId: Uuid; action: 'retain' | 'release' | 'retry' | 'quarantine' | 'exhaust'; reason: string; nextEligibleAt: IsoTimestamp | null; } /** Pure package owned by KBN-200. It receives complete immutable snapshots. */ export interface MechanicalCoordinatorDecisionEngineV1 { evaluateAssignmentCycle(snapshot: AssignmentCycleSnapshotV1): AssignmentCycleDecisionV1; explainEligibility( context: KanbanEvaluationContextV1, task: TaskEligibilitySnapshotV1, sessions: readonly AgentSessionSnapshotV1[], ): EligibilityExplanationV1; decideLeaseExpiry( context: KanbanEvaluationContextV1, lease: LeaseExpirySnapshotV1, ): LeaseExpiryDecisionV1; } export interface PersistedAssignmentV1 { assignmentId: Uuid; workspaceId: Uuid; taskId: Uuid; taskVersion: number; targetAgentId: Uuid; targetSessionId: Uuid; specialistRole: SpecialistRole; state: AssignmentStateV1; policyRevision: string; proposedBy: { kind: 'user' | 'agent'; id: Uuid }; reason: string; createdAt: IsoTimestamp; expiresAt: IsoTimestamp; } export interface TaskLeaseV1 { leaseId: Uuid; workspaceId: Uuid; taskId: Uuid; taskVersion: number; assignmentId: Uuid; agentId: Uuid; sessionId: Uuid; state: 'pending_ack' | 'active'; fencingToken: FencingTokenV1; attempt: number; acquiredAt: IsoTimestamp; acknowledgeBy: IsoTimestamp; lastHeartbeatAt: IsoTimestamp | null; expiresAt: IsoTimestamp; } interface ServiceCommandBaseV1 { context: InternalKanbanMutationContextV1; taskId: Uuid; expectedTaskVersion: number; } export interface AcquireApprovedLeaseCommandV1 extends ServiceCommandBaseV1 { assignmentId: Uuid; approvalDecisionId: Uuid; targetSessionId: Uuid; leaseTtlSeconds: number; } export interface LeaseCommandV1 extends ServiceCommandBaseV1 { leaseId: Uuid; sessionId: Uuid; fencingToken: FencingTokenV1; } export interface HeartbeatLeaseCommandV1 extends LeaseCommandV1 { extendSeconds: number; } export interface CheckpointCommandV1 extends LeaseCommandV1 { sequence: number; resumableSummary: string; artifactIds: readonly Uuid[]; contextUsagePercent: number; } export interface SubmitForReviewCommandV1 extends LeaseCommandV1 { artifactIds: readonly Uuid[]; summary: string; } export interface ReleaseLeaseCommandV1 extends LeaseCommandV1 { reason: | 'worker_requested' | 'ack_timeout' | 'heartbeat_timeout' | 'task_submitted' | 'policy_revoked' | 'shutdown'; } export interface AssignmentCycleCommandV1 { context: InternalKanbanMutationContextV1; limit: number; } export interface ExpirySweepCommandV1 { context: InternalKanbanMutationContextV1; limit: number; } export interface RecoverCoordinatorCommandV1 { context: InternalKanbanMutationContextV1; } interface CoordinatorRejectionBaseV1 { kind: 'coordinator_rejection'; retryable: false; requestOutcome: 'not_applied'; correlationId: Uuid; idempotencyKey: string; message: string; } export type CoordinatorPolicyRejectionV1 = | (CoordinatorRejectionBaseV1 & { code: 'WORKSPACE_MISMATCH' }) | (CoordinatorRejectionBaseV1 & { code: 'TASK_NOT_ELIGIBLE'; explanation: EligibilityExplanationV1; }) | (CoordinatorRejectionBaseV1 & { code: 'APPROVAL_REQUIRED' }) | (CoordinatorRejectionBaseV1 & { code: 'APPROVAL_STALE' }) | (CoordinatorRejectionBaseV1 & { code: 'ASSIGNMENT_STALE' }) | (CoordinatorRejectionBaseV1 & { code: 'ASSIGNMENT_TARGET_MISMATCH' }) | (CoordinatorRejectionBaseV1 & { code: 'POLICY_REVISION_MISMATCH' }) | (CoordinatorRejectionBaseV1 & { code: 'ARTIFACT_WORKSPACE_MISMATCH' }) | (CoordinatorRejectionBaseV1 & { code: 'LEASE_ALREADY_ACTIVE' }) | (CoordinatorRejectionBaseV1 & { code: 'LEASE_NOT_FOUND' }) | (CoordinatorRejectionBaseV1 & { code: 'LEASE_NOT_ACTIVE' }) | (CoordinatorRejectionBaseV1 & { code: 'ACK_DEADLINE_EXPIRED'; expiredAt: IsoTimestamp; }) | (CoordinatorRejectionBaseV1 & { code: 'FENCING_TOKEN_STALE'; currentFencingToken: FencingTokenV1; }) | (CoordinatorRejectionBaseV1 & { code: 'SESSION_MISMATCH' }) | (CoordinatorRejectionBaseV1 & { code: 'HEARTBEAT_EXPIRED'; expiredAt: IsoTimestamp; }) | (CoordinatorRejectionBaseV1 & { code: 'CHECKPOINT_SEQUENCE_CONFLICT'; currentSequence: number; }) | (CoordinatorRejectionBaseV1 & { code: 'RETRY_EXHAUSTED' }) | (CoordinatorRejectionBaseV1 & { code: 'NON_IDEMPOTENT_RETRY_REQUIRES_ORCHESTRATOR'; }); /** Explicit mapping to the Gateway mutation failure union; no arbitrary booleans. */ export type CoordinatorFailureV1 = | DeliberateWriteDenialV1 | VersionConflictV1 | RetryableTransportErrorV1 | CoordinatorPolicyRejectionV1; export interface CoordinatorSuccessV1 { ok: true; value: T; correlationId: Uuid; } export interface CoordinatorFailureResultV1 { ok: false; failure: CoordinatorFailureV1; } export type CoordinatorResultV1 = CoordinatorSuccessV1 | CoordinatorFailureResultV1; export interface ExpirySweepResultV1 { examined: number; released: readonly Uuid[]; retryScheduled: readonly Uuid[]; quarantined: readonly Uuid[]; exhausted: readonly Uuid[]; } export interface RestartRecoveryResultV1 { activeLeaseIds: readonly Uuid[]; expiredLeaseIds: readonly Uuid[]; pendingAssignmentIds: readonly Uuid[]; pendingOutboxEventIds: readonly Uuid[]; } /** Persistence/Gateway adapter owned by KBN-210. */ export interface MechanicalCoordinatorServicePortV1 { /** Loads immutable snapshots, invokes pure engine, and persists proposals atomically. */ runAssignmentCycle( command: AssignmentCycleCommandV1, ): Promise>; /** Query path loads by ID; public health observation cannot authorize mutation. */ getEligibilityExplanation( context: KanbanEvaluationContextV1, taskId: Uuid, ): Promise>; /** * Accepts IDs only. Implementation reloads and locks assignment + approval + * task + target session in PostgreSQL, then verifies workspace, task version, * target agent/session, state, expiry, policy revision, and current approval. */ acquireApprovedLease( command: AcquireApprovedLeaseCommandV1, ): Promise>; acknowledgeLease(command: LeaseCommandV1): Promise>; heartbeatLease(command: HeartbeatLeaseCommandV1): Promise>; appendCheckpoint( command: CheckpointCommandV1, ): Promise>; submitForReview( command: SubmitForReviewCommandV1, ): Promise>; releaseLease(command: ReleaseLeaseCommandV1): Promise>; expireAndRecover( command: ExpirySweepCommandV1, ): Promise>; recoverFromPostgres( command: RecoverCoordinatorCommandV1, ): Promise>; } /** Compile-time mapping guarantee: Coordinator Gateway failures are Kanban failures or exact policy rejections. */ export function isKanbanMutationFailureV1( failure: CoordinatorFailureV1, ): failure is KanbanMutationFailureV1 { return ( failure.kind === 'deliberate_fail_closed_denial' || failure.kind === 'retryable_transport_error' || failure.kind === 'version_conflict' ); }