{"version":3,"file":"scheduler-store.d.ts","sourceRoot":"","sources":["../../../src/core/scheduler/scheduler-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAE7E,OAAO,EAGN,KAAK,sBAAsB,EAE3B,MAAM,sBAAsB,CAAC;AAE9B,eAAO,MAAM,wBAAwB,GAAa,CAAC;AAMnD,MAAM,MAAM,4BAA4B,GAAG;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzG,MAAM,MAAM,0BAA0B,GACnC;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,sBAAsB,CAAA;CAAE,GAChD;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,GACrB;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/D,uEAAuE;AACvE,MAAM,MAAM,wBAAwB,CAAC,CAAC,IACnC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,sBAAsB,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GACzD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,CAAC;AAE9B,MAAM,MAAM,4BAA4B,CAAC,CAAC,IACvC;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAC1B;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,GACrB;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/D,MAAM,WAAW,iCAAiC;IACjD,OAAO,EAAE,sBAAsB,EAAE,CAAC;IAClC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACpD;AAED;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,8EAA4E;IAC5E,MAAM,CAAC,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAAC,4BAA4B,CAAC,CAAC;IAC9E,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAAC;IAC5D,WAAW,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACjC,WAAW,IAAI,OAAO,CAAC,iCAAiC,CAAC,CAAC;IAC1D,MAAM,CAAC,CAAC,EACP,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,CAAC,OAAO,EAAE,sBAAsB,KAAK,wBAAwB,CAAC,CAAC,CAAC,GACxE,OAAO,CAAC,4BAA4B,CAAC,CAAC,CAAC,CAAC,CAAC;CAC5C;AAMD,MAAM,MAAM,2BAA2B,GACpC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,sBAAsB,CAAA;CAAE,GAC5C;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAUrC;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,2BAA2B,CAiEvF;AAMD,MAAM,WAAW,iCAAiC;IACjD,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,mBAAmB,CAAC;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,8CAA8C;AAC9C,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,iCAAiC,GAAG,sBAAsB,CAgB7G","sourcesContent":["/**\n * Scheduler Foundation — domain-facing persistence port (2.12.0).\n *\n * Pure types + validation only. No filesystem, process, provider, worker-daemon,\n * or remote-RPC machinery. The concrete file store writes one small record per\n * scheduling intent and keys it deterministically by mission (`intent_<missionId>`),\n * so there is at most one durable queue entry per mission.\n *\n * Authority model:\n *   - This store records pending scheduling intent and the deterministic\n *     decision outcome (ASSIGNED / UNSCHEDULABLE). It never fabricates a\n *     mission result, an executor runtime, or an assignment.\n */\n\nimport { validateMissionRequirements } from \"../assignment/assignment-store.js\";\nimport type { MissionRequirements } from \"../assignment/assignment-types.js\";\nimport { isSafeMissionId } from \"../mission-domain/durable-store.js\";\nimport {\n\tisSafeIntentId,\n\tisSchedulingIntentState,\n\ttype SchedulingIntentRecord,\n\ttype SchedulingIntentState,\n} from \"./scheduler-types.js\";\n\nexport const SCHEDULER_SCHEMA_VERSION = 1 as const;\n\n// =============================================================================\n// Store port result types\n// =============================================================================\n\nexport type SchedulingIntentCreateResult = { status: \"created\" } | { status: \"conflict\"; error: string };\n\nexport type SchedulingIntentLoadResult =\n\t| { status: \"ok\"; record: SchedulingIntentRecord }\n\t| { status: \"missing\" }\n\t| { status: \"corrupt\"; intentId: string; diagnostic: string };\n\n/** A store-level atomic read-modify-write mutation (sync callback). */\nexport type SchedulingIntentMutation<T> =\n\t| { kind: \"write\"; next: SchedulingIntentRecord; value: T }\n\t| { kind: \"noop\"; value: T };\n\nexport type SchedulingIntentMutateResult<T> =\n\t| { status: \"ok\"; value: T }\n\t| { status: \"missing\" }\n\t| { status: \"corrupt\"; intentId: string; diagnostic: string };\n\nexport interface SchedulingIntentListRecordsResult {\n\trecords: SchedulingIntentRecord[];\n\tcorrupt: { intentId: string; diagnostic: string }[];\n}\n\n/**\n * Canonical scheduling-intent persistence port. Implementations must be\n * crash-conscious (atomic record replacement, schema validation on load,\n * deterministic corrupt handling) and must never fabricate a scheduled intent.\n */\nexport interface SchedulingIntentStore {\n\treadonly storeId: string;\n\n\t/** Create a record at a previously-absent intentId. Existing → conflict. */\n\tcreate(record: SchedulingIntentRecord): Promise<SchedulingIntentCreateResult>;\n\tload(intentId: string): Promise<SchedulingIntentLoadResult>;\n\tlistIntents(): Promise<string[]>;\n\tlistRecords(): Promise<SchedulingIntentListRecordsResult>;\n\tmutate<T>(\n\t\tintentId: string,\n\t\tmutation: (current: SchedulingIntentRecord) => SchedulingIntentMutation<T>,\n\t): Promise<SchedulingIntentMutateResult<T>>;\n}\n\n// =============================================================================\n// Validation\n// =============================================================================\n\nexport type SchedulingIntentParseResult =\n\t| { ok: true; record: SchedulingIntentRecord }\n\t| { ok: false; diagnostic: string };\n\nfunction invalid(why: string): SchedulingIntentParseResult {\n\treturn { ok: false, diagnostic: why };\n}\n\nfunction isSafeInteger(value: unknown): value is number {\n\treturn typeof value === \"number\" && Number.isSafeInteger(value);\n}\n\n/**\n * Validate an untrusted persisted value into a SchedulingIntentRecord.\n * Corruption is surfaced structurally (`ok: false`), never silently dropped or\n * fabricated.\n */\nexport function parseSchedulingIntentRecord(value: unknown): SchedulingIntentParseResult {\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) return invalid(\"record must be an object\");\n\tconst doc = value as Record<string, unknown>;\n\n\tif (doc.schemaVersion !== SCHEDULER_SCHEMA_VERSION) {\n\t\treturn invalid(`unsupported schemaVersion: ${String(doc.schemaVersion)}`);\n\t}\n\tif (typeof doc.intentId !== \"string\" || !isSafeIntentId(doc.intentId)) {\n\t\treturn invalid(\"intentId is missing or unsafe\");\n\t}\n\tconst intentId = doc.intentId;\n\tif (typeof doc.missionId !== \"string\" || !isSafeMissionId(doc.missionId)) {\n\t\treturn invalid(\"missionId is missing or unsafe\");\n\t}\n\tif (!isSchedulingIntentState(doc.state)) return invalid(`invalid intent state: ${String(doc.state)}`);\n\tconst state = doc.state as SchedulingIntentState;\n\n\tif (!isSafeInteger(doc.priority)) return invalid(\"priority must be a safe integer\");\n\tfor (const key of [\"createdAtMs\", \"updatedAtMs\", \"enqueuedAtMs\"] as const) {\n\t\tif (!isSafeInteger(doc[key])) return invalid(`${key} must be a safe integer`);\n\t}\n\tif (!isSafeInteger(doc.revision) || (doc.revision as number) < 0)\n\t\treturn invalid(\"revision must be a non-negative integer\");\n\n\tif (doc.requirements !== undefined) {\n\t\tconst err = validateMissionRequirements(doc.requirements);\n\t\tif (err) return invalid(`requirements: ${err}`);\n\t}\n\n\t// Assignment/state consistency. ASSIGNED is the only state that carries an\n\t// assignmentId; UNSCHEDULABLE must carry an explainable reason.\n\tif (state === \"ASSIGNED\") {\n\t\tif (typeof doc.assignmentId !== \"string\" || !isSafeIntentId(doc.assignmentId)) {\n\t\t\treturn invalid(\"an ASSIGNED intent must carry a safe assignmentId\");\n\t\t}\n\t\tif (doc.unschedulableReason !== undefined) {\n\t\t\treturn invalid(\"an ASSIGNED intent must not carry an unschedulableReason\");\n\t\t}\n\t} else {\n\t\tif (doc.assignmentId !== undefined) return invalid(`a ${state} intent must not carry an assignmentId`);\n\t\tif (state === \"UNSCHEDULABLE\" && typeof doc.unschedulableReason !== \"string\") {\n\t\t\treturn invalid(\"an UNSCHEDULABLE intent must carry an unschedulableReason\");\n\t\t}\n\t}\n\tif (doc.unschedulableReason !== undefined && typeof doc.unschedulableReason !== \"string\") {\n\t\treturn invalid(\"unschedulableReason must be a string\");\n\t}\n\n\treturn {\n\t\tok: true,\n\t\trecord: {\n\t\t\tschemaVersion: SCHEDULER_SCHEMA_VERSION,\n\t\t\tintentId,\n\t\t\tmissionId: doc.missionId as string,\n\t\t\trequirements: doc.requirements as MissionRequirements | undefined,\n\t\t\tpriority: doc.priority as number,\n\t\t\tcreatedAtMs: doc.createdAtMs as number,\n\t\t\tupdatedAtMs: doc.updatedAtMs as number,\n\t\t\tenqueuedAtMs: doc.enqueuedAtMs as number,\n\t\t\tstate,\n\t\t\tassignmentId: doc.assignmentId as string | undefined,\n\t\t\tunschedulableReason: doc.unschedulableReason as string | undefined,\n\t\t\trevision: doc.revision as number,\n\t\t},\n\t};\n}\n\n// =============================================================================\n// Record construction helper\n// =============================================================================\n\nexport interface CreateSchedulingIntentRecordInput {\n\tintentId: string;\n\tmissionId: string;\n\trequirements?: MissionRequirements;\n\tpriority?: number;\n\tnow?: number;\n}\n\n/** Build an initial PENDING intent record. */\nexport function createSchedulingIntentRecord(input: CreateSchedulingIntentRecordInput): SchedulingIntentRecord {\n\tif (!isSafeIntentId(input.intentId)) throw new Error(`Unsafe intent id: ${input.intentId}`);\n\tif (!isSafeMissionId(input.missionId)) throw new Error(`Unsafe mission id: ${input.missionId}`);\n\tconst now = input.now ?? Date.now();\n\treturn {\n\t\tschemaVersion: SCHEDULER_SCHEMA_VERSION,\n\t\tintentId: input.intentId,\n\t\tmissionId: input.missionId,\n\t\trequirements: input.requirements,\n\t\tpriority: input.priority ?? 0,\n\t\tcreatedAtMs: now,\n\t\tupdatedAtMs: now,\n\t\tenqueuedAtMs: now,\n\t\tstate: \"PENDING\",\n\t\trevision: 1,\n\t};\n}\n"]}