@zq-silk/yui 0.11.3 → 0.12.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/ARCHITECTURE.md +23 -4
- package/README.md +36 -13
- package/dist/cli/commandCatalog.js +2 -4
- package/dist/cli.js +105 -43
- package/dist/commands/configCommands.js +12 -45
- package/dist/commands/executionAuditCommands.js +1 -1
- package/dist/commands/jobCommands.js +4 -10
- package/dist/commands/taskCommands.js +154 -171
- package/dist/commands/taskCompletionGate.js +29 -54
- package/dist/commands/taskContextCommand.js +55 -11
- package/dist/commands/taskInputCommands.js +8 -5
- package/dist/commands/taskNextActionCommand.js +56 -6
- package/dist/commands/taskOverviewCommand.js +17 -31
- package/dist/commands/taskRoleRuntimeStatus.js +24 -0
- package/dist/commands/taskWorkspaceCommands.js +5 -2
- package/dist/config/configCatalog.js +2 -2
- package/dist/config/yuiConfig.js +3 -7
- package/dist/context/wakeNotification.js +20 -5
- package/dist/controller/agentRuntimeObserver.js +247 -51
- package/dist/controller/clientRuntime.js +1 -1
- package/dist/controller/controller.js +8 -12
- package/dist/controller/fileSchedulerStoreAdapter.js +146 -198
- package/dist/controller/runtime.js +3 -3
- package/dist/controller/runtimeEventInbox.js +17 -7
- package/dist/controller/runtimeEventProcessor.js +12 -19
- package/dist/controller/runtimeObservationHook.js +45 -0
- package/dist/execution/resourceBroker.js +5 -4
- package/dist/executor/agentExecutor.js +4 -4
- package/dist/executor/effectiveLaunch.js +8 -48
- package/dist/executor/fileRoleLaunchPlanner.js +36 -16
- package/dist/executor/workspacePreflightClassification.js +23 -2
- package/dist/interaction/operatorPresentation.js +33 -89
- package/dist/lifecycle/exactRunTerminalization.js +1 -2
- package/dist/lifecycle/taskRoleSessionReset.js +5 -13
- package/dist/observability/orchestrationMetrics.js +8 -3
- package/dist/profile/agentProfile.js +1 -1
- package/dist/release/cliHomeReleaseFence.js +123 -0
- package/dist/repository/taskWorkspacePreparer.js +176 -60
- package/dist/resources/sqliteResourceRegistry.js +1 -1
- package/dist/review/deltaRecheck.js +12 -51
- package/dist/review/reviewAcceptance.js +26 -0
- package/dist/review/reviewConfig.js +0 -31
- package/dist/review/reviewDecision.js +113 -0
- package/dist/review/reviewOutcomeClassifier.js +1 -1
- package/dist/review/reviewRound.js +1 -1
- package/dist/review/reviewerAvailability.js +69 -0
- package/dist/runtime/agentDriverObservation.js +24 -10
- package/dist/runtime/builtinAgentDrivers.js +4 -3
- package/dist/runtime/builtinTranscriptObserver.js +301 -64
- package/dist/runtime/builtinTranscriptUsage.js +9 -7
- package/dist/runtime/index.js +1 -0
- package/dist/runtime/runtimeObservation.js +13 -0
- package/dist/runtime/runtimeProjection.js +16 -25
- package/dist/runtime/sessionTokenMetrics.js +181 -0
- package/dist/runtime/structuredProviderHost.js +7 -1
- package/dist/scheduler/activeRoleRunDelivery.js +4 -4
- package/dist/scheduler/activeTaskProgress.js +7 -6
- package/dist/scheduler/leaderWakeupProcessor.js +3 -5
- package/dist/scheduler/operatorEvent.js +34 -0
- package/dist/scheduler/operatorInputNotificationProcessor.js +54 -94
- package/dist/scheduler/roleRunStall.js +0 -14
- package/dist/scheduler/taskExecutionProjection.js +97 -76
- package/dist/scheduler/taskObservabilityProjection.js +10 -11
- package/dist/storage/migration/productionRegistry.js +379 -0
- package/dist/storage/sqliteSchema.js +18 -8
- package/dist/storage/sqliteStore.js +24 -20
- package/dist/storage/storeRpc.js +0 -1
- package/dist/storage/taskStore.js +15 -30
- package/dist/storage/upgrade/recordVersions.js +3 -2
- package/dist/storage/upgrade/sqliteMigrationTarget.js +30 -8
- package/dist/storage/upgrade/sqliteRecordMigrationTarget.js +23 -11
- package/dist/storage/upgrade/sqliteStateMigration.js +66 -8
- package/dist/task/completionReadiness.js +10 -6
- package/dist/task/nextAction.js +82 -24
- package/dist/telemetry/sqliteTelemetryStore.js +1 -1
- package/dist/web/assets/client/components.js +13 -3
- package/dist/web/assets/client/i18n.js +6 -2
- package/dist/web/webSnapshot.js +4 -0
- package/i18n/README.zh-CN.md +2 -2
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +53 -16
- package/skills/yui-operator/SKILL.md +9 -0
- package/skills/yui-reviewer/SKILL.md +28 -7
- package/dist/context/sessionContextBudget.js +0 -71
- package/dist/lifecycle/contextBudgetRollover.js +0 -81
- package/dist/scheduler/operatorNotification.js +0 -59
|
@@ -79,6 +79,8 @@ const STORED_TASK_JOB_CALLER_KEY_HASHES_FROM_VERSION = 15;
|
|
|
79
79
|
const STORED_TASK_JOB_CALLER_KEY_HASHES_TO_VERSION = 16;
|
|
80
80
|
const STORED_TASK_WAKES_FROM_VERSION = 16;
|
|
81
81
|
const STORED_TASK_WAKES_TO_VERSION = 17;
|
|
82
|
+
const STORED_TASK_EVENT_NOTICES_FROM_VERSION = 17;
|
|
83
|
+
const STORED_TASK_EVENT_NOTICES_TO_VERSION = 18;
|
|
82
84
|
const TASK_WAKE_FROM_VERSION = 0;
|
|
83
85
|
const TASK_WAKE_TO_VERSION = 1;
|
|
84
86
|
const STORED_TASK_PUBLICATION_REFERENCES_FROM_VERSION = 16;
|
|
@@ -97,6 +99,8 @@ const PUBLICATION_REFERENCE_FROM_VERSION = 0;
|
|
|
97
99
|
const PUBLICATION_REFERENCE_TO_VERSION = 1;
|
|
98
100
|
const CONFIG_FROM_VERSION = 1;
|
|
99
101
|
const CONFIG_TO_VERSION = 2;
|
|
102
|
+
const CONFIG_REVIEW_CONTROLS_FROM_VERSION = 2;
|
|
103
|
+
const CONFIG_REVIEW_CONTROLS_TO_VERSION = 3;
|
|
100
104
|
/**
|
|
101
105
|
* Build the authoritative production graph. Transition intent and executable
|
|
102
106
|
* transforms are registered together here; compatible loading and offline
|
|
@@ -146,6 +150,7 @@ export function createProductionStorageRegistry() {
|
|
|
146
150
|
})
|
|
147
151
|
.registerOfflineMigration(projectOwnershipStep())
|
|
148
152
|
.registerOfflineMigration(configV2Step())
|
|
153
|
+
.registerOfflineMigration(configReviewControlsRemovalStep())
|
|
149
154
|
.registerCompatible(projectKnowledgeProposalsStep())
|
|
150
155
|
.registerCompatible(projectLifecycleStep())
|
|
151
156
|
.registerOfflineMigration(taskWorkspaceIdentityStep())
|
|
@@ -180,6 +185,7 @@ export function createProductionStorageRegistry() {
|
|
|
180
185
|
.registerCompatible(storedTaskDurableJobsStep())
|
|
181
186
|
.registerCompatible(storedTaskJobCallerKeyHashesStep())
|
|
182
187
|
.registerCompatible(storedTaskWakesAndPublicationReferencesStep())
|
|
188
|
+
.registerOfflineMigration(storedTaskEventNoticesStep())
|
|
183
189
|
.registerCompatible(taskWakeIntroductionStep())
|
|
184
190
|
.registerCompatible(durableJobIntroductionStep())
|
|
185
191
|
.registerOfflineMigration(capabilityGrantIntroductionStep())
|
|
@@ -246,6 +252,61 @@ function migrateConfigV1ToV2(snapshot) {
|
|
|
246
252
|
}
|
|
247
253
|
};
|
|
248
254
|
}
|
|
255
|
+
/** Removes retired Core controls for the Leader-owned Delta decision. */
|
|
256
|
+
function configReviewControlsRemovalStep() {
|
|
257
|
+
return {
|
|
258
|
+
axis: "record",
|
|
259
|
+
recordKind: "config",
|
|
260
|
+
fromVersion: CONFIG_REVIEW_CONTROLS_FROM_VERSION,
|
|
261
|
+
toVersion: CONFIG_REVIEW_CONTROLS_TO_VERSION,
|
|
262
|
+
preconditions: requireConfigV2,
|
|
263
|
+
transform: migrateConfigV2ToV3,
|
|
264
|
+
declaredEffects: []
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
function requireConfigV2(snapshot) {
|
|
268
|
+
const versions = asObject(snapshot.schemaManifest.recordVersions, "schema manifest recordVersions");
|
|
269
|
+
if (versions.config !== CONFIG_REVIEW_CONTROLS_FROM_VERSION) {
|
|
270
|
+
throw new Error(`Record config migration requires manifest version ${CONFIG_REVIEW_CONTROLS_FROM_VERSION}.`);
|
|
271
|
+
}
|
|
272
|
+
if (snapshot.state === null)
|
|
273
|
+
return;
|
|
274
|
+
const config = asObject(snapshot.state.config, "Yui config");
|
|
275
|
+
if (config.schemaVersion !== CONFIG_REVIEW_CONTROLS_FROM_VERSION) {
|
|
276
|
+
throw new Error(`Yui config must use schemaVersion ${CONFIG_REVIEW_CONTROLS_FROM_VERSION} before migration.`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
function migrateConfigV2ToV3(snapshot) {
|
|
280
|
+
requireConfigV2(snapshot);
|
|
281
|
+
const versions = asObject(snapshot.schemaManifest.recordVersions, "schema manifest recordVersions");
|
|
282
|
+
const schemaManifest = {
|
|
283
|
+
...snapshot.schemaManifest,
|
|
284
|
+
recordVersions: { ...versions, config: CONFIG_REVIEW_CONTROLS_TO_VERSION }
|
|
285
|
+
};
|
|
286
|
+
if (snapshot.state === null)
|
|
287
|
+
return { schemaManifest, state: null };
|
|
288
|
+
const config = asObject(snapshot.state.config, "Yui config");
|
|
289
|
+
const review = config.review === undefined
|
|
290
|
+
? undefined
|
|
291
|
+
: asObject(config.review, "Yui review config");
|
|
292
|
+
const migratedReview = review === undefined
|
|
293
|
+
? undefined
|
|
294
|
+
: (() => {
|
|
295
|
+
const { deltaRecheck: _retiredSwitch, deltaRecheckMaxChangedLines: _retiredLineThreshold, deltaRecheckMaxChangedFiles: _retiredFileThreshold, ...retained } = review;
|
|
296
|
+
return retained;
|
|
297
|
+
})();
|
|
298
|
+
return {
|
|
299
|
+
schemaManifest,
|
|
300
|
+
state: {
|
|
301
|
+
...snapshot.state,
|
|
302
|
+
config: {
|
|
303
|
+
...config,
|
|
304
|
+
schemaVersion: CONFIG_REVIEW_CONTROLS_TO_VERSION,
|
|
305
|
+
...(migratedReview === undefined ? {} : { review: migratedReview })
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
}
|
|
249
310
|
function workItemExecutionGroupHistoryStep() {
|
|
250
311
|
return {
|
|
251
312
|
axis: "record",
|
|
@@ -1241,6 +1302,324 @@ function normalizeStoredTaskV16ToV17(snapshot) {
|
|
|
1241
1302
|
state: { ...snapshot.state, tasks: nextTasks }
|
|
1242
1303
|
};
|
|
1243
1304
|
}
|
|
1305
|
+
/** StoredTask v18 retires the mutable OperatorNotification projection. */
|
|
1306
|
+
function storedTaskEventNoticesStep() {
|
|
1307
|
+
return {
|
|
1308
|
+
axis: "record",
|
|
1309
|
+
recordKind: "storedTask",
|
|
1310
|
+
fromVersion: STORED_TASK_EVENT_NOTICES_FROM_VERSION,
|
|
1311
|
+
toVersion: STORED_TASK_EVENT_NOTICES_TO_VERSION,
|
|
1312
|
+
preconditions: requireStoredTaskV17OperatorNoticeShape,
|
|
1313
|
+
transform: normalizeStoredTaskV17ToV18,
|
|
1314
|
+
declaredEffects: ["operator-mailbox-event-references"]
|
|
1315
|
+
};
|
|
1316
|
+
}
|
|
1317
|
+
const STORED_TASK_V17_FIELDS = [
|
|
1318
|
+
"schemaVersion",
|
|
1319
|
+
"task",
|
|
1320
|
+
"idHighWaterMarks",
|
|
1321
|
+
"brief",
|
|
1322
|
+
"changeSets",
|
|
1323
|
+
"integrationAttempts",
|
|
1324
|
+
"integrationQueue",
|
|
1325
|
+
"durableJobs",
|
|
1326
|
+
"roles",
|
|
1327
|
+
"managedWorkspaces",
|
|
1328
|
+
"roleSessionSets",
|
|
1329
|
+
"jobCallerKeyHashes",
|
|
1330
|
+
"workItems",
|
|
1331
|
+
"contextSnapshots",
|
|
1332
|
+
"agentRuns",
|
|
1333
|
+
"reviewRounds",
|
|
1334
|
+
"activeRuns",
|
|
1335
|
+
"messages",
|
|
1336
|
+
"inputRequests",
|
|
1337
|
+
"decisions",
|
|
1338
|
+
"milestones",
|
|
1339
|
+
"events",
|
|
1340
|
+
"wakes",
|
|
1341
|
+
"capabilityGrants",
|
|
1342
|
+
"releaseWorkflows",
|
|
1343
|
+
"publicationReferences",
|
|
1344
|
+
"leaderFailure",
|
|
1345
|
+
"operatorNotification"
|
|
1346
|
+
];
|
|
1347
|
+
function requireStoredTaskV17OperatorNoticeShape(snapshot) {
|
|
1348
|
+
const versions = asObject(snapshot.schemaManifest.recordVersions, "schema manifest recordVersions");
|
|
1349
|
+
if (versions.storedTask !== STORED_TASK_EVENT_NOTICES_FROM_VERSION) {
|
|
1350
|
+
throw new Error(`Record storedTask compatible step requires manifest version ${STORED_TASK_EVENT_NOTICES_FROM_VERSION}.`);
|
|
1351
|
+
}
|
|
1352
|
+
if (snapshot.state === null)
|
|
1353
|
+
return;
|
|
1354
|
+
const tasks = asObject(snapshot.state.tasks, "state tasks");
|
|
1355
|
+
for (const [taskId, rawTask] of Object.entries(tasks)) {
|
|
1356
|
+
const task = asObject(rawTask, `Task aggregate ${taskId}`);
|
|
1357
|
+
if (task.schemaVersion !== STORED_TASK_EVENT_NOTICES_FROM_VERSION) {
|
|
1358
|
+
throw new Error(`Task aggregate ${taskId} must use schemaVersion ${STORED_TASK_EVENT_NOTICES_FROM_VERSION}.`);
|
|
1359
|
+
}
|
|
1360
|
+
const allowed = new Set(STORED_TASK_V17_FIELDS);
|
|
1361
|
+
const unknown = Object.keys(task).find((key) => !allowed.has(key));
|
|
1362
|
+
if (unknown !== undefined) {
|
|
1363
|
+
throw new Error(`Task aggregate ${taskId} has an unknown v17 field: ${unknown}.`);
|
|
1364
|
+
}
|
|
1365
|
+
const missing = STORED_TASK_V17_FIELDS.find((key) => !Object.hasOwn(task, key));
|
|
1366
|
+
if (missing !== undefined) {
|
|
1367
|
+
throw new Error(`Task aggregate ${taskId} is missing v17 field: ${missing}.`);
|
|
1368
|
+
}
|
|
1369
|
+
if (task.operatorNotification !== null) {
|
|
1370
|
+
requireLegacyOperatorNotice(task.operatorNotification, taskId);
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
function requireLegacyOperatorNotice(value, taskId) {
|
|
1375
|
+
const notice = asObject(value, `Operator notification ${taskId}`);
|
|
1376
|
+
if (notice.schemaVersion !== 1 || notice.taskId !== taskId) {
|
|
1377
|
+
throw new Error(`Operator notification identity is invalid: ${taskId}.`);
|
|
1378
|
+
}
|
|
1379
|
+
if (notice.type === "leader-recovery-failed") {
|
|
1380
|
+
requireLegacyNoticeText(notice.message, `Operator notification message ${taskId}`);
|
|
1381
|
+
}
|
|
1382
|
+
else if (notice.type === "leader-stalled") {
|
|
1383
|
+
requireLegacyNoticeText(notice.message, `Operator notification message ${taskId}`);
|
|
1384
|
+
requireLegacyNoticeText(notice.runId, `Operator notification Run ${taskId}`);
|
|
1385
|
+
requireLegacyNoticeTimestamp(notice.progressAt, `Operator notification progress ${taskId}`);
|
|
1386
|
+
requireLegacyNoticeText(notice.evidenceKey, `Operator notification evidence ${taskId}`);
|
|
1387
|
+
if (notice.classification !== "truly-stalled") {
|
|
1388
|
+
throw new Error(`Operator notification classification is invalid: ${taskId}.`);
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
else if (notice.type === "task-terminal") {
|
|
1392
|
+
if (notice.status !== "completed" && notice.status !== "retired") {
|
|
1393
|
+
throw new Error(`Operator notification Task status is invalid: ${taskId}.`);
|
|
1394
|
+
}
|
|
1395
|
+
if (!["user", "operator", "leader"].includes(String(notice.by))) {
|
|
1396
|
+
throw new Error(`Operator notification Task actor is invalid: ${taskId}.`);
|
|
1397
|
+
}
|
|
1398
|
+
requireLegacyNoticeText(notice.summary, `Operator notification summary ${taskId}`);
|
|
1399
|
+
}
|
|
1400
|
+
else {
|
|
1401
|
+
throw new Error(`Operator notification type is invalid: ${taskId}.`);
|
|
1402
|
+
}
|
|
1403
|
+
requireLegacyNoticeTimestamp(notice.createdAt, `Operator notification createdAt ${taskId}`);
|
|
1404
|
+
requireLegacyNoticeTimestamp(notice.updatedAt, `Operator notification updatedAt ${taskId}`);
|
|
1405
|
+
}
|
|
1406
|
+
function requireLegacyNoticeText(value, label) {
|
|
1407
|
+
if (typeof value !== "string" || value.trim().length === 0 || value.includes("\0")) {
|
|
1408
|
+
throw new Error(`${label} is invalid.`);
|
|
1409
|
+
}
|
|
1410
|
+
return value;
|
|
1411
|
+
}
|
|
1412
|
+
function requireLegacyNoticeTimestamp(value, label) {
|
|
1413
|
+
const timestamp = requireLegacyNoticeText(value, label);
|
|
1414
|
+
if (!Number.isFinite(Date.parse(timestamp)))
|
|
1415
|
+
throw new Error(`${label} is invalid.`);
|
|
1416
|
+
return timestamp;
|
|
1417
|
+
}
|
|
1418
|
+
function normalizeStoredTaskV17ToV18(snapshot) {
|
|
1419
|
+
requireStoredTaskV17OperatorNoticeShape(snapshot);
|
|
1420
|
+
const versions = asObject(snapshot.schemaManifest.recordVersions, "schema manifest recordVersions");
|
|
1421
|
+
const schemaManifest = {
|
|
1422
|
+
...snapshot.schemaManifest,
|
|
1423
|
+
recordVersions: {
|
|
1424
|
+
...versions,
|
|
1425
|
+
storedTask: STORED_TASK_EVENT_NOTICES_TO_VERSION
|
|
1426
|
+
}
|
|
1427
|
+
};
|
|
1428
|
+
if (snapshot.state === null)
|
|
1429
|
+
return { schemaManifest, state: null };
|
|
1430
|
+
const tasks = asObject(snapshot.state.tasks, "state tasks");
|
|
1431
|
+
const nextTasks = {};
|
|
1432
|
+
const legacyNotices = new Map();
|
|
1433
|
+
for (const [taskId, rawTask] of Object.entries(tasks)) {
|
|
1434
|
+
const task = asObject(rawTask, `Task aggregate ${taskId}`);
|
|
1435
|
+
legacyNotices.set(taskId, task.operatorNotification === null
|
|
1436
|
+
? null
|
|
1437
|
+
: asObject(task.operatorNotification, `Operator notification ${taskId}`));
|
|
1438
|
+
const nextTask = {
|
|
1439
|
+
...task,
|
|
1440
|
+
schemaVersion: STORED_TASK_EVENT_NOTICES_TO_VERSION,
|
|
1441
|
+
events: { ...asObject(task.events, `Task events ${taskId}`) },
|
|
1442
|
+
idHighWaterMarks: {
|
|
1443
|
+
...asObject(task.idHighWaterMarks, `Task id high-water marks ${taskId}`)
|
|
1444
|
+
}
|
|
1445
|
+
};
|
|
1446
|
+
delete nextTask.operatorNotification;
|
|
1447
|
+
nextTasks[taskId] = nextTask;
|
|
1448
|
+
}
|
|
1449
|
+
const eventByTask = new Map();
|
|
1450
|
+
const eventRefForTask = (taskId) => {
|
|
1451
|
+
const existing = eventByTask.get(taskId);
|
|
1452
|
+
if (existing !== undefined)
|
|
1453
|
+
return { type: "event", taskId, id: existing };
|
|
1454
|
+
const task = asObject(nextTasks[taskId], `Task aggregate ${taskId}`);
|
|
1455
|
+
const events = asObject(task.events, `Task events ${taskId}`);
|
|
1456
|
+
const marks = asObject(task.idHighWaterMarks, `Task id high-water marks ${taskId}`);
|
|
1457
|
+
let sequence = Number(marks.event ?? 0) + 1;
|
|
1458
|
+
if (!Number.isSafeInteger(sequence) || sequence < 1) {
|
|
1459
|
+
throw new Error(`Task event high-water mark is invalid: ${taskId}.`);
|
|
1460
|
+
}
|
|
1461
|
+
while (events[`event-${sequence}`] !== undefined)
|
|
1462
|
+
sequence += 1;
|
|
1463
|
+
const eventId = `event-${sequence}`;
|
|
1464
|
+
const notice = legacyNotices.get(taskId) ?? null;
|
|
1465
|
+
const migrated = migratedOperatorNoticeEvent(taskId, eventId, notice, task);
|
|
1466
|
+
events[eventId] = migrated;
|
|
1467
|
+
marks.event = sequence;
|
|
1468
|
+
eventByTask.set(taskId, eventId);
|
|
1469
|
+
return { type: "event", taskId, id: eventId };
|
|
1470
|
+
};
|
|
1471
|
+
const mailboxes = asObject(snapshot.state.mailboxes, "state mailboxes");
|
|
1472
|
+
const nextMailboxes = {};
|
|
1473
|
+
for (const [key, rawMailbox] of Object.entries(mailboxes)) {
|
|
1474
|
+
const mailbox = asObject(rawMailbox, `WorkMailbox ${key}`);
|
|
1475
|
+
const target = asObject(mailbox.target, `WorkMailbox target ${key}`);
|
|
1476
|
+
nextMailboxes[key] = target.kind === "operator"
|
|
1477
|
+
? rewriteLegacyOperatorMailbox(mailbox, key, eventRefForTask)
|
|
1478
|
+
: { ...mailbox };
|
|
1479
|
+
}
|
|
1480
|
+
for (const [taskId, notice] of legacyNotices) {
|
|
1481
|
+
if (notice !== null)
|
|
1482
|
+
eventRefForTask(taskId);
|
|
1483
|
+
}
|
|
1484
|
+
return {
|
|
1485
|
+
schemaManifest,
|
|
1486
|
+
state: { ...snapshot.state, tasks: nextTasks, mailboxes: nextMailboxes }
|
|
1487
|
+
};
|
|
1488
|
+
}
|
|
1489
|
+
function migratedOperatorNoticeEvent(taskId, eventId, notice, taskAggregate) {
|
|
1490
|
+
const task = asObject(taskAggregate.task, `Task ${taskId}`);
|
|
1491
|
+
const timestamp = String(notice?.updatedAt ?? task.updatedAt ?? task.createdAt ?? "");
|
|
1492
|
+
if (!Number.isFinite(Date.parse(timestamp))) {
|
|
1493
|
+
throw new Error(`Legacy Operator notice timestamp is invalid: ${taskId}.`);
|
|
1494
|
+
}
|
|
1495
|
+
const type = notice?.type;
|
|
1496
|
+
if (notice !== null && type === "task-terminal") {
|
|
1497
|
+
const status = String(notice.status ?? "");
|
|
1498
|
+
if (status !== "completed" && status !== "retired") {
|
|
1499
|
+
throw new Error(`Legacy terminal Operator notice status is invalid: ${taskId}.`);
|
|
1500
|
+
}
|
|
1501
|
+
return {
|
|
1502
|
+
schemaVersion: 2,
|
|
1503
|
+
id: eventId,
|
|
1504
|
+
taskId,
|
|
1505
|
+
type: `task.${status}`,
|
|
1506
|
+
payload: {
|
|
1507
|
+
status,
|
|
1508
|
+
by: String(notice.by ?? "operator"),
|
|
1509
|
+
summary: String(notice.summary ?? "Task reached a terminal state."),
|
|
1510
|
+
migratedFrom: "operator-notification"
|
|
1511
|
+
},
|
|
1512
|
+
createdAt: timestamp
|
|
1513
|
+
};
|
|
1514
|
+
}
|
|
1515
|
+
if (notice !== null && type === "leader-stalled") {
|
|
1516
|
+
return {
|
|
1517
|
+
schemaVersion: 2,
|
|
1518
|
+
id: eventId,
|
|
1519
|
+
taskId,
|
|
1520
|
+
type: "run.stalled",
|
|
1521
|
+
payload: {
|
|
1522
|
+
runId: String(notice.runId ?? "unknown"),
|
|
1523
|
+
roleName: "leader",
|
|
1524
|
+
progressAt: String(notice.progressAt ?? timestamp),
|
|
1525
|
+
classification: "truly-stalled",
|
|
1526
|
+
evidenceKey: String(notice.evidenceKey ?? "legacy-operator-notification"),
|
|
1527
|
+
migratedFrom: "operator-notification"
|
|
1528
|
+
},
|
|
1529
|
+
createdAt: timestamp
|
|
1530
|
+
};
|
|
1531
|
+
}
|
|
1532
|
+
return {
|
|
1533
|
+
schemaVersion: 2,
|
|
1534
|
+
id: eventId,
|
|
1535
|
+
taskId,
|
|
1536
|
+
type: "leader.attention-required",
|
|
1537
|
+
payload: {
|
|
1538
|
+
reason: type === "leader-recovery-failed"
|
|
1539
|
+
? "leader-recovery-failed"
|
|
1540
|
+
: "legacy-operator-mailbox",
|
|
1541
|
+
message: String(notice?.message ?? "Inspect the migrated Operator notice."),
|
|
1542
|
+
migratedFrom: notice === null ? "operator-mailbox" : "operator-notification"
|
|
1543
|
+
},
|
|
1544
|
+
createdAt: timestamp
|
|
1545
|
+
};
|
|
1546
|
+
}
|
|
1547
|
+
function rewriteLegacyOperatorMailbox(mailbox, key, eventRefForTask) {
|
|
1548
|
+
const rewriteRef = (rawRef) => {
|
|
1549
|
+
const ref = asObject(rawRef, `WorkMailbox ${key} entity ref`);
|
|
1550
|
+
if (ref.type === "input" || ref.type === "event")
|
|
1551
|
+
return { ...ref };
|
|
1552
|
+
const taskId = ref.type === "task" ? ref.id : ref.taskId;
|
|
1553
|
+
if (typeof taskId !== "string" || taskId.trim().length === 0) {
|
|
1554
|
+
throw new Error(`Legacy Operator mailbox ref has no Task identity: ${key}.`);
|
|
1555
|
+
}
|
|
1556
|
+
return eventRefForTask(taskId);
|
|
1557
|
+
};
|
|
1558
|
+
const rewriteBatch = (rawBatch, label) => {
|
|
1559
|
+
const batch = asObject(rawBatch, label);
|
|
1560
|
+
if (!Array.isArray(batch.refs))
|
|
1561
|
+
throw new Error(`${label} refs must be an array.`);
|
|
1562
|
+
const sourceRefs = batch.refs.map((rawRef) => asObject(rawRef, `${label} entity ref`));
|
|
1563
|
+
const semanticTaskIds = new Set(sourceRefs.flatMap((ref) => ((ref.type === "input" || ref.type === "event") && typeof ref.taskId === "string"
|
|
1564
|
+
? [ref.taskId]
|
|
1565
|
+
: [])));
|
|
1566
|
+
const refs = [];
|
|
1567
|
+
const seen = new Set();
|
|
1568
|
+
for (const sourceRef of sourceRefs) {
|
|
1569
|
+
const referencedTaskId = sourceRef.type === "task" ? sourceRef.id : sourceRef.taskId;
|
|
1570
|
+
if (sourceRef.type !== "input"
|
|
1571
|
+
&& sourceRef.type !== "event"
|
|
1572
|
+
&& typeof referencedTaskId === "string"
|
|
1573
|
+
&& semanticTaskIds.has(referencedTaskId)) {
|
|
1574
|
+
continue;
|
|
1575
|
+
}
|
|
1576
|
+
const ref = rewriteRef(sourceRef);
|
|
1577
|
+
const identity = `${String(ref.type)}\0${String(ref.taskId ?? "")}\0${String(ref.id)}`;
|
|
1578
|
+
if (seen.has(identity))
|
|
1579
|
+
continue;
|
|
1580
|
+
seen.add(identity);
|
|
1581
|
+
refs.push(ref);
|
|
1582
|
+
}
|
|
1583
|
+
return { ...batch, refs };
|
|
1584
|
+
};
|
|
1585
|
+
const rewriteProcessing = (raw) => {
|
|
1586
|
+
if (raw === null)
|
|
1587
|
+
return null;
|
|
1588
|
+
const processing = asObject(raw, `WorkMailbox ${key} processing`);
|
|
1589
|
+
return {
|
|
1590
|
+
...processing,
|
|
1591
|
+
batch: rewriteBatch(processing.batch, `WorkMailbox ${key} processing batch`),
|
|
1592
|
+
...(processing.executionRef === undefined
|
|
1593
|
+
? {}
|
|
1594
|
+
: { executionRef: rewriteRef(processing.executionRef) })
|
|
1595
|
+
};
|
|
1596
|
+
};
|
|
1597
|
+
const rewriteDelivery = (raw) => {
|
|
1598
|
+
if (raw === null)
|
|
1599
|
+
return null;
|
|
1600
|
+
const delivery = asObject(raw, `WorkMailbox ${key} input delivery`);
|
|
1601
|
+
return {
|
|
1602
|
+
...delivery,
|
|
1603
|
+
batch: rewriteBatch(delivery.batch, `WorkMailbox ${key} input delivery batch`),
|
|
1604
|
+
executionRef: rewriteRef(delivery.executionRef)
|
|
1605
|
+
};
|
|
1606
|
+
};
|
|
1607
|
+
const pending = asObject(mailbox.pending, `WorkMailbox ${key} pending lanes`);
|
|
1608
|
+
return {
|
|
1609
|
+
...mailbox,
|
|
1610
|
+
processing: rewriteProcessing(mailbox.processing),
|
|
1611
|
+
pending: {
|
|
1612
|
+
...pending,
|
|
1613
|
+
normal: pending.normal === null
|
|
1614
|
+
? null
|
|
1615
|
+
: rewriteBatch(pending.normal, `WorkMailbox ${key} normal batch`),
|
|
1616
|
+
userCorrection: pending.userCorrection === null
|
|
1617
|
+
? null
|
|
1618
|
+
: rewriteBatch(pending.userCorrection, `WorkMailbox ${key} correction batch`)
|
|
1619
|
+
},
|
|
1620
|
+
inputDelivery: rewriteDelivery(mailbox.inputDelivery)
|
|
1621
|
+
};
|
|
1622
|
+
}
|
|
1244
1623
|
/**
|
|
1245
1624
|
* The taskWake record family is introduced alongside StoredTask v17. The
|
|
1246
1625
|
* compatible step supplies the empty map and high-water mark; this step only
|
|
@@ -327,7 +327,8 @@ CREATE TABLE IF NOT EXISTS events (
|
|
|
327
327
|
);
|
|
328
328
|
CREATE INDEX IF NOT EXISTS idx_events_type_time ON events(task_id, type, occurred_at);
|
|
329
329
|
|
|
330
|
-
-- Per-task scheduler projections
|
|
330
|
+
-- Per-task scheduler projections. operator-notification is a retired legacy
|
|
331
|
+
-- kind retained only so old layout-7 Homes can be read by the upgrade path.
|
|
331
332
|
CREATE TABLE IF NOT EXISTS task_projections (
|
|
332
333
|
task_id TEXT NOT NULL,
|
|
333
334
|
kind TEXT NOT NULL CHECK (kind IN ('leader-failure','operator-notification')),
|
|
@@ -986,10 +987,13 @@ const SCHEMA_MIGRATIONS_SQL = `
|
|
|
986
987
|
* layout migrations. A database with any sqlite_master object is therefore
|
|
987
988
|
* diagnosed as corrupt/partially initialized and left untouched.
|
|
988
989
|
*/
|
|
989
|
-
function ensureMigrationLedger(db) {
|
|
990
|
+
function ensureMigrationLedger(db, mode) {
|
|
990
991
|
const objects = db.prepare("SELECT type, name FROM sqlite_master WHERE name IS NOT NULL").all();
|
|
991
992
|
const ledger = objects.find(({ name }) => name === "schema_migrations");
|
|
992
993
|
if (ledger === undefined) {
|
|
994
|
+
if (mode === "validate") {
|
|
995
|
+
throw new SqliteSchemaMigrationError("schema_migrations ledger is missing from an existing database");
|
|
996
|
+
}
|
|
993
997
|
if (objects.length !== 0) {
|
|
994
998
|
throw new SqliteSchemaMigrationError("schema_migrations ledger is missing from a non-empty database");
|
|
995
999
|
}
|
|
@@ -1134,19 +1138,25 @@ function validatePendingProjectionBackfillSources(db) {
|
|
|
1134
1138
|
}
|
|
1135
1139
|
}
|
|
1136
1140
|
/**
|
|
1137
|
-
* Apply
|
|
1141
|
+
* Apply or validate schema migrations without letting an ordinary open mutate
|
|
1142
|
+
* an existing authoritative database.
|
|
1138
1143
|
*
|
|
1139
|
-
*
|
|
1144
|
+
* In `apply` mode each migration runs in its own transaction: the DDL and the
|
|
1140
1145
|
* `schema_migrations` bookkeeping commit atomically, so a crash mid-migration
|
|
1141
|
-
* rolls back and the
|
|
1142
|
-
*
|
|
1146
|
+
* rolls back and the disposable staged database can be rebuilt cleanly.
|
|
1147
|
+
* `validate` mode rejects a pending version before executing any migration.
|
|
1143
1148
|
*/
|
|
1144
|
-
export function migrateSqliteSchema(db) {
|
|
1145
|
-
const ledgerWasCreated = ensureMigrationLedger(db);
|
|
1149
|
+
export function migrateSqliteSchema(db, options) {
|
|
1150
|
+
const ledgerWasCreated = ensureMigrationLedger(db, options.mode);
|
|
1146
1151
|
// Validate the complete ledger before touching any pending migration. This
|
|
1147
1152
|
// prevents a manually altered or partially recorded ledger from silently
|
|
1148
1153
|
// skipping the partial-index/projection migrations added after a valid Home.
|
|
1149
1154
|
const applied = validateAppliedMigrations(db, ledgerWasCreated);
|
|
1155
|
+
const pending = MIGRATIONS.filter((migration) => !applied.has(migration.version));
|
|
1156
|
+
if (options.mode === "validate" && pending.length > 0) {
|
|
1157
|
+
throw new SqliteSchemaMigrationError(`pending SQLite schema migration ${pending[0].version}; `
|
|
1158
|
+
+ "run the offline staged upgrade before opening this database", "admission");
|
|
1159
|
+
}
|
|
1150
1160
|
const newlyApplied = [];
|
|
1151
1161
|
for (const migration of MIGRATIONS) {
|
|
1152
1162
|
if (applied.has(migration.version))
|
|
@@ -55,7 +55,7 @@ import { assertHomeWritable } from "./upgradeFence.js";
|
|
|
55
55
|
import { CURRENT_CONFIG_SCHEMA_VERSION, CURRENT_PENDING_WAKEUP_SCHEMA_VERSION, CURRENT_WORK_MAILBOX_SCHEMA_VERSION, executionLaneActiveRunKey, executionLaneActiveRunKeyParts, StorageConflictError, StorageCancelledError, StorageRecordError, FileTaskStore, storedCapabilityGrant, storedPublicationReference, storedReleaseWorkflow, isValidCapabilityGrantTransition, isValidReleaseWorkflowTransition, validateYuiConfig } from "./taskStore.js";
|
|
56
56
|
import { publicationExternalKey } from "../task/publicationReference.js";
|
|
57
57
|
import { gateArtifactKey, validateGateArtifact } from "../verification/gateArtifact.js";
|
|
58
|
-
import { migrateSqliteSchema, SQLITE_AGGREGATE_VERSION, SQLITE_LAYOUT_VERSION, TELEMETRY_KEEP_PER_GENERATION, TELEMETRY_RUN_CAP } from "./sqliteSchema.js";
|
|
58
|
+
import { migrateSqliteSchema, SqliteSchemaMigrationError, SQLITE_AGGREGATE_VERSION, SQLITE_LAYOUT_VERSION, TELEMETRY_KEEP_PER_GENERATION, TELEMETRY_RUN_CAP } from "./sqliteSchema.js";
|
|
59
59
|
import { inspectStorageSchema } from "./storageSchema.js";
|
|
60
60
|
/** Read the immutable Home identity without opening a writable Store connection. */
|
|
61
61
|
export function readSqliteHomeIdentity(rootDir, databaseFilename = "yui.db") {
|
|
@@ -143,16 +143,29 @@ export class SqliteTaskStore {
|
|
|
143
143
|
this.#migration = _options.migration ?? false;
|
|
144
144
|
mkdirSync(rootDir, { recursive: true, mode: 0o700 });
|
|
145
145
|
const filename = _options.databaseFilename ?? "yui.db";
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
this.#
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
this.#db
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
146
|
+
const databasePath = join(rootDir, filename);
|
|
147
|
+
const databaseExisted = existsSync(databasePath);
|
|
148
|
+
if (this.#migration && filename === "yui.db") {
|
|
149
|
+
throw new SqliteSchemaMigrationError("offline migration mode cannot open the authoritative yui.db", "admission");
|
|
150
|
+
}
|
|
151
|
+
this.#db = new Database(databasePath);
|
|
152
|
+
try {
|
|
153
|
+
// §4.1 / §9: WAL, no fsync weakening, FKs on, busy timeout for CLI contention.
|
|
154
|
+
this.#db.pragma("journal_mode = WAL");
|
|
155
|
+
this.#db.pragma("synchronous = FULL");
|
|
156
|
+
this.#db.pragma("foreign_keys = ON");
|
|
157
|
+
this.#db.pragma("busy_timeout = 5000");
|
|
158
|
+
this.#db.pragma("wal_autocheckpoint = 1000");
|
|
159
|
+
migrateSqliteSchema(this.#db, {
|
|
160
|
+
mode: this.#migration || !databaseExisted ? "apply" : "validate"
|
|
161
|
+
});
|
|
162
|
+
this.#seedHomeMeta();
|
|
163
|
+
this.#seedConfig();
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
this.#db.close();
|
|
167
|
+
throw error;
|
|
168
|
+
}
|
|
156
169
|
}
|
|
157
170
|
rootDirectory() { return this.#rootDir; }
|
|
158
171
|
/** Close the underlying database connection. */
|
|
@@ -2048,15 +2061,6 @@ export class SqliteTaskStore {
|
|
|
2048
2061
|
clearLeaderFailure(taskId) {
|
|
2049
2062
|
this.#clearProjection(taskId, "leader-failure");
|
|
2050
2063
|
}
|
|
2051
|
-
getOperatorNotification(taskId) {
|
|
2052
|
-
return this.#getProjection(taskId, "operator-notification");
|
|
2053
|
-
}
|
|
2054
|
-
saveOperatorNotification(notification) {
|
|
2055
|
-
this.#saveProjection(notification.taskId, "operator-notification", notification);
|
|
2056
|
-
}
|
|
2057
|
-
clearOperatorNotification(taskId) {
|
|
2058
|
-
this.#clearProjection(taskId, "operator-notification");
|
|
2059
|
-
}
|
|
2060
2064
|
// -- pending wakeups (leader-role work-mailbox projection, mirrors taskStore.ts) --
|
|
2061
2065
|
getPendingWakeup(taskId) {
|
|
2062
2066
|
return pendingWakeupProjection(this.getWorkMailbox({ kind: "role", taskId, roleName: "leader" }));
|
package/dist/storage/storeRpc.js
CHANGED
|
@@ -31,7 +31,6 @@ import { validateIntegrationQueueEntry } from "../integration/integrationQueueEn
|
|
|
31
31
|
import { CURRENT_DURABLE_JOB_SCHEMA_VERSION, validDurableJobTransition, validateDurableJob } from "../job/durableJob.js";
|
|
32
32
|
import { validateGlobalRole, validateTaskRole } from "../role/role.js";
|
|
33
33
|
import { CURRENT_LEADER_FAILURE_SCHEMA_VERSION } from "../scheduler/leaderFailure.js";
|
|
34
|
-
import { CURRENT_OPERATOR_NOTIFICATION_SCHEMA_VERSION } from "../scheduler/operatorNotification.js";
|
|
35
34
|
import { CURRENT_TASK_WAKE_SCHEMA_VERSION, validateTaskWake } from "../scheduler/taskWake.js";
|
|
36
35
|
import { validateTask } from "../task/task.js";
|
|
37
36
|
import { operationalTaskRecords } from "../task/taskRecordRetirement.js";
|
|
@@ -46,7 +45,7 @@ import { CURRENT_AGGREGATE_SCHEMA_VERSION, requireCompatibleStorageSchema, requi
|
|
|
46
45
|
export const STORAGE_STATE_FILE = "state.json";
|
|
47
46
|
/** The root StorageState schema is the persisted aggregate document version. */
|
|
48
47
|
export const CURRENT_STORAGE_STATE_SCHEMA_VERSION = CURRENT_AGGREGATE_SCHEMA_VERSION;
|
|
49
|
-
export const CURRENT_CONFIG_SCHEMA_VERSION =
|
|
48
|
+
export const CURRENT_CONFIG_SCHEMA_VERSION = 3;
|
|
50
49
|
export const CURRENT_HOME_IDENTITY_SCHEMA_VERSION = 1;
|
|
51
50
|
export const CURRENT_ACTIVE_RUN_POINTER_SCHEMA_VERSION = 3;
|
|
52
51
|
/**
|
|
@@ -113,7 +112,7 @@ export function executionLaneActiveRunKeyParts(key) {
|
|
|
113
112
|
}
|
|
114
113
|
}
|
|
115
114
|
/** The schema version of each persisted `state.json#/tasks/*` aggregate. */
|
|
116
|
-
export const CURRENT_STORED_TASK_SCHEMA_VERSION =
|
|
115
|
+
export const CURRENT_STORED_TASK_SCHEMA_VERSION = 18;
|
|
117
116
|
/**
|
|
118
117
|
* Persisted nested-record versions consumed by this store's strict parser.
|
|
119
118
|
* Keep these named at the storage boundary so the upgrade record-axis map can
|
|
@@ -1730,22 +1729,16 @@ export class FileTaskStore {
|
|
|
1730
1729
|
this.saveWorkMailbox(consumePendingBatch(mailbox, "normal"));
|
|
1731
1730
|
}
|
|
1732
1731
|
getLeaderFailure(taskId) { return optional(this.#state().tasks[taskId]?.leaderFailure ?? undefined); }
|
|
1733
|
-
saveLeaderFailure(value) {
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
clearOperatorNotification(taskId) { this.#clearSingleton(taskId, "operatorNotification"); }
|
|
1738
|
-
#saveSingleton(taskId, key, value, label) {
|
|
1739
|
-
const schemaVersion = key === "leaderFailure"
|
|
1740
|
-
? CURRENT_LEADER_FAILURE_SCHEMA_VERSION
|
|
1741
|
-
: CURRENT_OPERATOR_NOTIFICATION_SCHEMA_VERSION;
|
|
1742
|
-
const stored = identified(value, schemaVersion, "taskId", taskId, label);
|
|
1743
|
-
this.#requireTaskForWrite(taskId);
|
|
1744
|
-
this.#mutate((state) => { state.tasks[taskId][key] = stored; });
|
|
1732
|
+
saveLeaderFailure(value) {
|
|
1733
|
+
const stored = identified(value, CURRENT_LEADER_FAILURE_SCHEMA_VERSION, "taskId", value.taskId, "Leader failure");
|
|
1734
|
+
this.#requireTaskForWrite(value.taskId);
|
|
1735
|
+
this.#mutate((state) => { state.tasks[value.taskId].leaderFailure = stored; });
|
|
1745
1736
|
}
|
|
1746
|
-
|
|
1747
|
-
this.#mutate((state) => {
|
|
1748
|
-
state.tasks[
|
|
1737
|
+
clearLeaderFailure(taskId) {
|
|
1738
|
+
this.#mutate((state) => {
|
|
1739
|
+
if (state.tasks[taskId] !== undefined)
|
|
1740
|
+
state.tasks[taskId].leaderFailure = null;
|
|
1741
|
+
});
|
|
1749
1742
|
}
|
|
1750
1743
|
#saveTaskRecord(taskId, key, value, label) {
|
|
1751
1744
|
const record = versioned(value, CURRENT_MESSAGE_SCHEMA_VERSION, label);
|
|
@@ -2014,8 +2007,7 @@ function emptyStoredTask(task) {
|
|
|
2014
2007
|
capabilityGrants: {},
|
|
2015
2008
|
releaseWorkflows: {},
|
|
2016
2009
|
publicationReferences: {},
|
|
2017
|
-
leaderFailure: null
|
|
2018
|
-
operatorNotification: null
|
|
2010
|
+
leaderFailure: null
|
|
2019
2011
|
};
|
|
2020
2012
|
}
|
|
2021
2013
|
function emptyTaskIdHighWaterMarks() {
|
|
@@ -2233,8 +2225,7 @@ function parseStoredTask(value, taskId) {
|
|
|
2233
2225
|
"capabilityGrants",
|
|
2234
2226
|
"releaseWorkflows",
|
|
2235
2227
|
"publicationReferences",
|
|
2236
|
-
"leaderFailure"
|
|
2237
|
-
"operatorNotification"
|
|
2228
|
+
"leaderFailure"
|
|
2238
2229
|
], `Task aggregate ${taskId}`);
|
|
2239
2230
|
parseMap(aggregate.changeSets, (record, key) => {
|
|
2240
2231
|
const changeSet = identifiedChangeSet(record, key);
|
|
@@ -2434,14 +2425,8 @@ function parseStoredTask(value, taskId) {
|
|
|
2434
2425
|
}
|
|
2435
2426
|
return reference;
|
|
2436
2427
|
}, "publicationReferences");
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
if (record !== null) {
|
|
2440
|
-
const schemaVersion = key === "leaderFailure"
|
|
2441
|
-
? CURRENT_LEADER_FAILURE_SCHEMA_VERSION
|
|
2442
|
-
: CURRENT_OPERATOR_NOTIFICATION_SCHEMA_VERSION;
|
|
2443
|
-
identified(record, schemaVersion, "taskId", taskId, label);
|
|
2444
|
-
}
|
|
2428
|
+
if (aggregate.leaderFailure !== null) {
|
|
2429
|
+
identified(aggregate.leaderFailure, CURRENT_LEADER_FAILURE_SCHEMA_VERSION, "taskId", taskId, "Leader failure");
|
|
2445
2430
|
}
|
|
2446
2431
|
validateTaskIdHighWaterCoverage(aggregate, taskId);
|
|
2447
2432
|
return aggregate;
|
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
import { CURRENT_AGGREGATE_SCHEMA_VERSION, CURRENT_STORAGE_LAYOUT_VERSION } from "../storageVersions.js";
|
|
19
19
|
import { CURRENT_AGENT_RUN_SCHEMA_VERSION, CURRENT_ACTIVE_RUN_POINTER_SCHEMA_VERSION, CURRENT_AGENT_PROFILE_SCHEMA_VERSION, CURRENT_CAPABILITY_GRANT_SCHEMA_VERSION, CURRENT_CONFIG_SCHEMA_VERSION, CURRENT_CONFIGURED_AGENT_SCHEMA_VERSION, CURRENT_CHANGE_SET_SCHEMA_VERSION, CURRENT_CONTEXT_SNAPSHOT_SCHEMA_VERSION, CURRENT_DECISION_SCHEMA_VERSION, CURRENT_EVENT_SCHEMA_VERSION, CURRENT_GLOBAL_ROLE_SCHEMA_VERSION, CURRENT_GLOBAL_ROLE_SESSION_SET_SCHEMA_VERSION, CURRENT_INPUT_REQUEST_SCHEMA_VERSION, CURRENT_INTEGRATION_ATTEMPT_SCHEMA_VERSION, CURRENT_INTEGRATION_QUEUE_SCHEMA_VERSION, CURRENT_MANAGED_WORKSPACE_SCHEMA_VERSION, CURRENT_MESSAGE_SCHEMA_VERSION, CURRENT_MILESTONE_SCHEMA_VERSION, CURRENT_PUBLICATION_REFERENCE_SCHEMA_VERSION, CURRENT_PROJECT_SCHEMA_VERSION, CURRENT_RELEASE_WORKFLOW_SCHEMA_VERSION, CURRENT_REVIEW_ROUND_SCHEMA_VERSION, CURRENT_STORED_TASK_SCHEMA_VERSION, CURRENT_TASK_BRIEF_SCHEMA_VERSION, CURRENT_TASK_ROLE_SCHEMA_VERSION, CURRENT_TASK_ROLE_SESSION_SET_SCHEMA_VERSION, CURRENT_TASK_SCHEMA_VERSION, CURRENT_WORK_ITEM_SCHEMA_VERSION, CURRENT_WORK_MAILBOX_SCHEMA_VERSION } from "../taskStore.js";
|
|
20
20
|
import { CURRENT_LEADER_FAILURE_SCHEMA_VERSION } from "../../scheduler/leaderFailure.js";
|
|
21
|
-
import { CURRENT_OPERATOR_NOTIFICATION_SCHEMA_VERSION } from "../../scheduler/operatorNotification.js";
|
|
22
21
|
import { CURRENT_TASK_WAKE_SCHEMA_VERSION } from "../../scheduler/taskWake.js";
|
|
23
22
|
import { CURRENT_DURABLE_JOB_SCHEMA_VERSION } from "../../job/durableJob.js";
|
|
24
23
|
function descriptor(version, path) {
|
|
@@ -118,7 +117,9 @@ function getCurrentRecordDescriptors() {
|
|
|
118
117
|
releaseWorkflow: descriptor(CURRENT_RELEASE_WORKFLOW_SCHEMA_VERSION, "state.json#/tasks/*/releaseWorkflows"),
|
|
119
118
|
publicationReference: descriptor(CURRENT_PUBLICATION_REFERENCE_SCHEMA_VERSION, "state.json#/tasks/*/publicationReferences"),
|
|
120
119
|
leaderFailure: descriptor(CURRENT_LEADER_FAILURE_SCHEMA_VERSION, "state.json#/tasks/*/leaderFailure"),
|
|
121
|
-
|
|
120
|
+
// Historical family retained in the version graph so StoredTask v17
|
|
121
|
+
// Homes can migrate; current v18 aggregates never store this field.
|
|
122
|
+
operatorNotification: descriptor(1, "state.json#/tasks/*/operatorNotification"),
|
|
122
123
|
workMailbox: descriptor(CURRENT_WORK_MAILBOX_SCHEMA_VERSION, "state.json#/mailboxes")
|
|
123
124
|
});
|
|
124
125
|
}
|