@zq-silk/yui 0.8.3 → 0.8.7

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.
Files changed (73) hide show
  1. package/ARCHITECTURE.md +40 -22
  2. package/README.md +66 -19
  3. package/dist/cli/commandCatalog.js +43 -14
  4. package/dist/cli/operatorWizard.js +10 -20
  5. package/dist/cli/updatePorts.js +6 -0
  6. package/dist/cli.js +252 -37
  7. package/dist/commands/executionAuditCommands.js +30 -0
  8. package/dist/commands/globalRoleCommands.js +8 -4
  9. package/dist/commands/operatorCommands.js +42 -1
  10. package/dist/commands/taskCommands.js +527 -147
  11. package/dist/commands/taskCompletionGate.js +36 -24
  12. package/dist/commands/taskContextCommand.js +11 -4
  13. package/dist/commands/taskInputCommands.js +48 -10
  14. package/dist/commands/taskNextActionCommand.js +38 -3
  15. package/dist/commands/taskOverviewCommand.js +2 -1
  16. package/dist/commands/taskRoleRuntimeStatus.js +2 -1
  17. package/dist/context/runContextPack.js +9 -5
  18. package/dist/context/sessionBootstrapManifest.js +158 -11
  19. package/dist/context/wakeNotification.js +5 -3
  20. package/dist/controller/clientRuntime.js +15 -15
  21. package/dist/controller/controller.js +16 -8
  22. package/dist/controller/fileSchedulerStoreAdapter.js +67 -7
  23. package/dist/controller/handoverCandidate.js +10 -3
  24. package/dist/controller/sessionNotify.js +4 -22
  25. package/dist/executor/agentAdapter.js +2 -2
  26. package/dist/executor/agentExecutor.js +29 -9
  27. package/dist/executor/fileRoleLaunchPlanner.js +37 -45
  28. package/dist/integration/gitIntegrationService.js +50 -2
  29. package/dist/integration/integrationCheckEvidenceReuse.js +53 -0
  30. package/dist/observability/executionAudit.js +47 -1
  31. package/dist/observability/faultClassification.js +6 -4
  32. package/dist/observability/orchestrationMetrics.js +196 -0
  33. package/dist/operator/operatorSessionHistory.js +36 -0
  34. package/dist/release/releaseHandover.js +7 -5
  35. package/dist/release/runtimeRelease.js +15 -0
  36. package/dist/repository/taskWorkspaceCoordinator.js +13 -10
  37. package/dist/review/deltaRecheck.js +3 -2
  38. package/dist/review/reviewFindingLedger.js +5 -4
  39. package/dist/review/reviewOutcomeClassifier.js +263 -54
  40. package/dist/review/taskFinalReviewContractEvent.js +1 -0
  41. package/dist/review/taskFinalReviewContractRebind.js +367 -0
  42. package/dist/run/runIdentity.js +10 -70
  43. package/dist/runtime/agentHost.js +3 -4
  44. package/dist/runtime/codexAppServerRuntime.js +6 -0
  45. package/dist/runtime/exactControlPlane.js +47 -37
  46. package/dist/runtime/firstProgressStopLoss.js +54 -0
  47. package/dist/runtime/launchBroker.js +10 -2
  48. package/dist/runtime/runtimeDeadlines.js +14 -0
  49. package/dist/runtime/sessionTitle.js +24 -12
  50. package/dist/runtime/structuredProviderHost.js +7 -1
  51. package/dist/runtime/tmuxAdapters.js +10 -3
  52. package/dist/scheduler/actionability.js +4 -2
  53. package/dist/scheduler/activeRoleRunDelivery.js +20 -18
  54. package/dist/scheduler/activeTaskProgress.js +2 -1
  55. package/dist/scheduler/leaderWakeupProcessor.js +33 -2
  56. package/dist/scheduler/taskExecutionProjection.js +13 -4
  57. package/dist/scheduler/wakeReason.js +1 -0
  58. package/dist/storage/sqliteStore.js +18 -3
  59. package/dist/storage/taskStore.js +14 -3
  60. package/dist/task/completionReadiness.js +48 -19
  61. package/dist/task/deliveryGuard.js +3 -1
  62. package/dist/task/nextAction.js +153 -55
  63. package/dist/task/repairWave.js +14 -1
  64. package/dist/task/task.js +10 -0
  65. package/dist/task/taskRecordRetirement.js +72 -0
  66. package/dist/web/webSnapshot.js +7 -1
  67. package/dist/workItem/workItem.js +6 -4
  68. package/i18n/README.zh-CN.md +48 -9
  69. package/package.json +1 -1
  70. package/skills/yui-leader/SKILL.md +73 -31
  71. package/skills/yui-operator/SKILL.md +58 -10
  72. package/skills/yui-reviewer/SKILL.md +23 -0
  73. package/skills/yui-runtime/SKILL.md +6 -6
package/dist/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { readFileSync } from "node:fs";
3
- import { resolve } from "node:path";
3
+ import { join, resolve } from "node:path";
4
4
  import { createInterface } from "node:readline/promises";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { isDeepStrictEqual } from "node:util";
@@ -36,8 +36,9 @@ import { runResourcesCommand } from "./commands/resourcesCommands.js";
36
36
  import { applyOperatorSessionControl, runOperatorCommand } from "./commands/operatorCommands.js";
37
37
  import { runProjectCommand } from "./commands/projectCommands.js";
38
38
  import { runProfileCommand } from "./commands/profileCommands.js";
39
- import { dispatchPreparedReviewRound, failPendingReviewRound, RESUMED_PENDING_FINAL_REVIEW, TERMINALIZED_LEADER_BEFORE_FINAL_REVIEW, TaskFinalReviewDispatchDriftError, preserveReviewRoundWorkspace, parseTaskCompletionRequest, preflightTaskCompletion, runTaskCommand, normalizedExecutionLanePlan, validateTaskArchiveRequest } from "./commands/taskCommands.js";
39
+ import { dispatchPreparedReviewRound, failPendingReviewRound, RESUMED_PENDING_FINAL_REVIEW, TERMINALIZED_LEADER_BEFORE_FINAL_REVIEW, TaskFinalReviewDispatchDriftError, preserveReviewRoundWorkspace, parseTaskCompletionRequest, parseTaskFinalReviewContractRebindRequest, preflightTaskCompletion, runTaskCommand, normalizedExecutionLanePlan, validateTaskArchiveRequest } from "./commands/taskCommands.js";
40
40
  import { taskActor } from "./commands/taskActor.js";
41
+ import { isCurrentGlobalOperator } from "./commands/taskInputCommands.js";
41
42
  import { runTaskIntegrationCommand } from "./commands/taskIntegrationCommands.js";
42
43
  import { runTaskChangeSetCommand } from "./commands/taskChangeSetCommands.js";
43
44
  import { runTaskOverlapCommand } from "./commands/taskOverlapCommands.js";
@@ -46,7 +47,7 @@ import { runTaskWorkspaceCommand } from "./commands/taskWorkspaceCommands.js";
46
47
  import { runWorkflowCommandAsync } from "./commands/workflowCommands.js";
47
48
  import { createUpdatePorts } from "./cli/updatePorts.js";
48
49
  import { createReleaseWorkflowPorts } from "./release/releaseWorkflowPorts.js";
49
- import { readRuntimeIdentity } from "./release/runtimeRelease.js";
50
+ import { acquireHandoverLock, readRuntimeIdentity } from "./release/runtimeRelease.js";
50
51
  import { renderReleaseActivateResult, renderReleaseInstallResult, renderReleaseList, runReleaseActivate, runReleaseInstall, runReleaseList } from "./commands/releaseCommands.js";
51
52
  import { reconcileTaskRemoteBaselines, verifyTaskCompletionPublishedTree } from "./commands/taskCompletionGate.js";
52
53
  import { runTaskBaseStatusCommand } from "./commands/taskBaseCommands.js";
@@ -80,9 +81,11 @@ import { AgentConfigurationCatalogService } from "./executor/agentConfigurationC
80
81
  import { TmuxWebTerminalService } from "./web/tmuxWebTerminal.js";
81
82
  import { listOperatorSessions, operatorSessionRef } from "./operator/operatorSessionHistory.js";
82
83
  import { YUI_VERSION, yuiVersionIdentity } from "./version.js";
83
- import { YUI_CONTROL_PLANE_DESCRIPTOR, YUI_TASK_RUNTIME_DESCRIPTOR, assertExactControlPlanePreflight, assertExactTaskRuntimeEnvironment, assertExactTaskRuntimeState, exactControlPlaneDigest, extractExactControlArgument, parseExactControlPlaneDescriptor } from "./runtime/exactControlPlane.js";
84
+ import { YUI_CONTROL_PLANE_DESCRIPTOR, YUI_TASK_RUNTIME_DESCRIPTOR, assertCompatibleControlPlanePreflight, assertExactControlPlanePreflight, assertExactTaskRuntimeEnvironment, assertExactTaskRuntimeState, exactControlPlaneDigest, extractExactControlArgument, createExactControlPlaneDescriptor, parseExactControlPlaneDescriptor } from "./runtime/exactControlPlane.js";
85
+ import { readSessionBootstrapManifest, refreshManagedSessionCliWrappers } from "./context/sessionBootstrapManifest.js";
84
86
  import { builtinAgentDriverRegistry } from "./runtime/builtinAgentDrivers.js";
85
87
  import { createTaskFinalReviewContract, extractTaskFinalReviewRequest } from "./review/taskFinalReviewContract.js";
88
+ import { prepareTaskFinalReviewContractRebindProof, resolveRecordedTaskFinalReviewContract } from "./review/taskFinalReviewContractRebind.js";
86
89
  import { assessDeltaRecheck } from "./review/deltaRecheck.js";
87
90
  import { deltaRecheckEnabled } from "./review/reviewConfig.js";
88
91
  import { NodeGitWorkspace } from "./repository/gitWorkspace.js";
@@ -109,7 +112,7 @@ void main().catch((error) => {
109
112
  process.exitCode = 5;
110
113
  });
111
114
  export async function main() {
112
- const { contract: taskFinalReviewContract, verifiedStore } = await preflightManagedTaskControlPlane();
115
+ const { contract: taskFinalReviewContract, verifiedStore, controlPlane: exactCurrentControlPlane } = await preflightManagedTaskControlPlane();
113
116
  if (args.length === 0) {
114
117
  emit(renderCommandHelp((await import("./cli/commandCatalog.js")).ROOT_COMMAND, VERSION));
115
118
  return;
@@ -261,6 +264,16 @@ export async function main() {
261
264
  return;
262
265
  }
263
266
  if (args[0] === "internal") {
267
+ if (args[1] === "session-cli-refresh" && args.length === 2) {
268
+ if (process.env.YUI_SESSION_SCOPE === "task"
269
+ || (process.env.YUI_SESSION_SCOPE === "global" && process.env.YUI_ROLE !== "operator")) {
270
+ throw usageError("Managed Session CLI refresh may be run only by the user or global Operator.");
271
+ }
272
+ const result = refreshManagedSessionCliWrappers(home);
273
+ emit(`Refreshed ${result.refreshed} legacy Session CLI wrapper(s); `
274
+ + `${result.current} already current, ${result.skipped} skipped.`, false, result);
275
+ return;
276
+ }
264
277
  if (args[1] === "agent-host" && args.length === 4) {
265
278
  process.exitCode = await runAgentHost({
266
279
  home,
@@ -988,6 +1001,59 @@ export async function main() {
988
1001
  }
989
1002
  }
990
1003
  }
1004
+ let taskFinalReviewRebindProof;
1005
+ let taskFinalReviewRebindRequest;
1006
+ let releaseTaskFinalReviewHandoverLock;
1007
+ if (resolved[1] === "review" && resolved[2] === "rebind") {
1008
+ if (!isCurrentGlobalOperator(store, process.env)) {
1009
+ throw usageError("Task-final Review contract rebind requires the authenticated global Operator session.");
1010
+ }
1011
+ if (exactCurrentControlPlane === undefined) {
1012
+ throw usageError("Task-final Review contract rebind requires the exact current global Session CLI.");
1013
+ }
1014
+ taskFinalReviewRebindRequest = parseTaskFinalReviewContractRebindRequest(resolved.slice(3));
1015
+ }
1016
+ if ((resolved[1] === "review"
1017
+ && ["request", "force-fresh", "retry", "rebind"].includes(resolved[2] ?? ""))
1018
+ || resolved[1] === "complete") {
1019
+ const handoverLock = acquireHandoverLock(home);
1020
+ releaseTaskFinalReviewHandoverLock = handoverLock.release;
1021
+ }
1022
+ if (taskFinalReviewRebindRequest !== undefined) {
1023
+ const request = taskFinalReviewRebindRequest;
1024
+ try {
1025
+ taskFinalReviewRebindProof = prepareTaskFinalReviewContractRebindProof({
1026
+ home,
1027
+ taskId: request.taskId,
1028
+ fromControlPlaneDigest: request.fromControlPlaneDigest,
1029
+ toControlPlaneDigest: request.toControlPlaneDigest,
1030
+ fromReleaseId: request.fromReleaseId,
1031
+ toReleaseId: request.toReleaseId,
1032
+ currentControlPlane: exactCurrentControlPlane.descriptor
1033
+ });
1034
+ const workItems = store.listWorkItems(request.taskId);
1035
+ const reviewRounds = store.listReviewRounds(request.taskId);
1036
+ const resolution = resolveRecordedTaskFinalReviewContract(request.taskId, workItems, reviewRounds, store.listEvents(request.taskId));
1037
+ const activeRound = reviewRounds.find((round) => ((round.scope ?? "work-item") === "task"
1038
+ && (round.status === "pending" || round.status === "running")));
1039
+ // An exact replay is a true no-op, even when the target Leader is live.
1040
+ // Invalid/stale source tuples also reach the command unchanged so its
1041
+ // transactional diagnosis cannot stop a healthy target runtime.
1042
+ if (resolution?.effective.controlPlaneDigest === request.fromControlPlaneDigest
1043
+ && activeRound === undefined) {
1044
+ // The contract event and wake must never target a Provider
1045
+ // Conversation launched by the source release. Active Runs fail
1046
+ // closed; otherwise stop and retire that runtime under the handover
1047
+ // lock before the transactional event+wake commit.
1048
+ await workspaceCoordinator.cleanupTaskRoleRuntime(request.taskId, "leader");
1049
+ }
1050
+ }
1051
+ catch (error) {
1052
+ releaseTaskFinalReviewHandoverLock();
1053
+ releaseTaskFinalReviewHandoverLock = undefined;
1054
+ throw error;
1055
+ }
1056
+ }
991
1057
  let candidateMaterialization;
992
1058
  let candidateMaterializationCommitted = false;
993
1059
  try {
@@ -1002,7 +1068,7 @@ export async function main() {
1002
1068
  ? await candidateSnapshotForTaskCommand(resolved, store, workspacePreparer, process.env, taskFinalReviewContract)
1003
1069
  : candidateMaterialization.snapshot;
1004
1070
  const directTaskMainSnapshot = await directTaskMainSnapshotForTaskCommand(resolved, store, workspacePreparer, process.env, taskFinalReviewContract);
1005
- const actualTaskReviewCandidate = await actualTaskReviewCandidateForTaskCommand(resolved, store, workspacePreparer, process.env, taskFinalReviewContract);
1071
+ const actualTaskReviewCandidate = await actualTaskReviewCandidateForTaskCommand(resolved, store, workspacePreparer, process.env);
1006
1072
  const deltaRecheckPreflight = await deltaRecheckPreflightForTaskCommand(resolved.slice(1), store, actualTaskReviewCandidate);
1007
1073
  const reviewWorkspaceResult = await reviewWorkspaceResultForTaskCommand(resolved, store, workspacePreparer, process.env);
1008
1074
  const executionLaneGitSnapshot = await executionLaneGitSnapshotForTaskCommand(resolved, store, workspacePreparer, process.env);
@@ -1016,6 +1082,9 @@ export async function main() {
1016
1082
  ...(taskFinalReviewContract === undefined
1017
1083
  ? {}
1018
1084
  : { taskFinalReviewContract }),
1085
+ ...(taskFinalReviewRebindProof === undefined
1086
+ ? {}
1087
+ : { taskFinalReviewRebindProof }),
1019
1088
  ...(completionSummary === undefined ? {} : { completionSummary }),
1020
1089
  ...(completionPublishedTreeProof === undefined
1021
1090
  ? {}
@@ -1260,6 +1329,12 @@ export async function main() {
1260
1329
  }
1261
1330
  throw error;
1262
1331
  }
1332
+ finally {
1333
+ if (releaseTaskFinalReviewHandoverLock !== undefined) {
1334
+ releaseTaskFinalReviewHandoverLock();
1335
+ releaseTaskFinalReviewHandoverLock = undefined;
1336
+ }
1337
+ }
1263
1338
  }
1264
1339
  if (resolved[0] === "jobs") {
1265
1340
  emit(runJobCommand(resolved.slice(1), store, { runtime }));
@@ -1299,13 +1374,14 @@ async function preflightManagedTaskControlPlane() {
1299
1374
  throw new Error("Exact control-plane invocation requires both frozen descriptors in a managed Task runtime.");
1300
1375
  }
1301
1376
  if (!exactRuntime) {
1302
- if (exactControlInvocation.digest !== undefined) {
1303
- throw new Error("Exact Task control-plane invocation requires its frozen runtime descriptors.");
1377
+ if (exactControlInvocation.digest !== undefined
1378
+ || process.env.YUI_SESSION_SCOPE === "global") {
1379
+ return await preflightManagedGlobalControlPlane(exactControlInvocation.digest);
1304
1380
  }
1305
1381
  if (taskFinalReviewInvocation.request !== undefined) {
1306
1382
  throw new Error("Task final-review contract requires a verified exact Task control-plane invocation.");
1307
1383
  }
1308
- return { contract: undefined, verifiedStore: undefined };
1384
+ return { contract: undefined, controlPlane: undefined, verifiedStore: undefined };
1309
1385
  }
1310
1386
  if (process.env.YUI_SESSION_SCOPE !== "task") {
1311
1387
  throw new Error("Exact Task control-plane invocation requires a managed Task runtime.");
@@ -1314,24 +1390,42 @@ async function preflightManagedTaskControlPlane() {
1314
1390
  throw new Error("Exact control-plane invocation is required for this managed Task runtime.");
1315
1391
  }
1316
1392
  const control = parseExactControlPlaneDescriptor(serializedControl);
1317
- const internalCallback = args[0] === "internal";
1318
- if (!internalCallback && exactControlInvocation.digest === undefined) {
1319
- throw new Error("Exact control-plane invocation is required; bare `yui` and PATH launchers are not valid in a managed Task runtime.");
1320
- }
1321
- const digest = exactControlInvocation.digest ?? exactControlPlaneDigest(control);
1322
- await assertExactControlPlanePreflight({
1323
- serializedDescriptor: serializedControl,
1324
- digest,
1325
- actualExecutable: process.execPath,
1326
- actualCliEntry: fileURLToPath(import.meta.url),
1327
- actualHome: resolveYuiHome(process.env)
1328
- }, {
1329
- // Provider callbacks must remain able to append their immutable inbox fact
1330
- // while the Controller is offline. They still validate executable, CLI,
1331
- // Home, build, schema, and the exact Task runtime envelope first.
1332
- checkController: !internalCallback
1333
- });
1334
- const runtime = assertExactTaskRuntimeEnvironment(serializedRuntime, process.env, digest, control.yuiHome);
1393
+ const internalCallback = args[0] === "internal"
1394
+ && ["agent-host", "session-notify", "runtime-hook"].includes(args[1] ?? "");
1395
+ const home = resolveYuiHome(process.env);
1396
+ assertManagedSessionManifest(home, "task");
1397
+ if (resolve(control.yuiHome) !== resolve(home)) {
1398
+ throw new Error("Managed Task control-plane descriptor belongs to another YUI_HOME.");
1399
+ }
1400
+ const frozenDigest = exactControlPlaneDigest(control);
1401
+ const exactCommand = internalCallback || exactControlInvocation.digest !== undefined;
1402
+ let commandControl;
1403
+ let digest;
1404
+ if (exactCommand) {
1405
+ digest = exactControlInvocation.digest ?? frozenDigest;
1406
+ await assertExactControlPlanePreflight({
1407
+ serializedDescriptor: serializedControl,
1408
+ digest,
1409
+ actualExecutable: process.execPath,
1410
+ actualCliEntry: fileURLToPath(import.meta.url),
1411
+ actualHome: home
1412
+ }, {
1413
+ // Provider callbacks must remain able to append their immutable inbox fact
1414
+ // while the Controller is offline. They still validate executable, CLI,
1415
+ // Home, schema, and the exact Task runtime envelope first.
1416
+ checkController: !internalCallback
1417
+ });
1418
+ commandControl = control;
1419
+ }
1420
+ else {
1421
+ await assertCompatibleControlPlanePreflight({ actualHome: home });
1422
+ // Package/build identity is deliberately not part of ordinary Session
1423
+ // continuity. Keep contracts on the Session's frozen digest while the
1424
+ // current CLI is proven protocol/storage compatible.
1425
+ commandControl = control;
1426
+ digest = frozenDigest;
1427
+ }
1428
+ const runtime = assertExactTaskRuntimeEnvironment(serializedRuntime, process.env, frozenDigest, control.yuiHome);
1335
1429
  const runtimeDriverCallback = args[0] === "internal"
1336
1430
  && args[1] === "runtime-hook"
1337
1431
  && process.env.YUI_DRIVER_ID !== undefined
@@ -1349,23 +1443,117 @@ async function preflightManagedTaskControlPlane() {
1349
1443
  }
1350
1444
  : {});
1351
1445
  const request = taskFinalReviewInvocation.request;
1352
- if (request === undefined)
1353
- return { contract: undefined, verifiedStore };
1446
+ if (request === undefined) {
1447
+ return {
1448
+ contract: undefined,
1449
+ controlPlane: { digest, descriptor: commandControl },
1450
+ verifiedStore
1451
+ };
1452
+ }
1354
1453
  if (runtime.roleName !== "leader") {
1355
1454
  throw new Error("Only the exact Task Leader invocation may establish a final-review contract.");
1356
1455
  }
1357
1456
  if (request.taskId !== runtime.taskId) {
1358
1457
  throw new Error(`Task final-review contract Task id mismatch: expected ${runtime.taskId}, found ${request.taskId}.`);
1359
1458
  }
1459
+ const recordedContract = resolveRecordedTaskFinalReviewContract(runtime.taskId, verifiedStore.listWorkItems(runtime.taskId), verifiedStore.listReviewRounds(runtime.taskId), verifiedStore.listEvents(runtime.taskId))?.effective;
1460
+ if (recordedContract !== undefined
1461
+ && recordedContract.reviewerRoleName !== request.reviewerRoleName) {
1462
+ throw new Error(`Task final-review Reviewer mismatch: expected ${recordedContract.reviewerRoleName}, `
1463
+ + `found ${request.reviewerRoleName}.`);
1464
+ }
1360
1465
  return {
1361
1466
  contract: createTaskFinalReviewContract({
1362
1467
  taskId: runtime.taskId,
1363
1468
  reviewerRoleName: request.reviewerRoleName,
1364
- controlPlaneDigest: digest
1469
+ // Once Task evidence establishes a contract, a compatible Session or
1470
+ // CLI replacement presents that same capability. Package/build identity
1471
+ // must not force a release rebind or invalidate delivery evidence.
1472
+ controlPlaneDigest: recordedContract?.controlPlaneDigest ?? digest
1365
1473
  }),
1474
+ controlPlane: { digest, descriptor: commandControl },
1366
1475
  verifiedStore
1367
1476
  };
1368
1477
  }
1478
+ async function preflightManagedGlobalControlPlane(digest) {
1479
+ if (process.env.YUI_SESSION_SCOPE !== "global") {
1480
+ throw new Error("Exact Task control-plane invocation requires its frozen runtime descriptors.");
1481
+ }
1482
+ if (taskFinalReviewInvocation.request !== undefined) {
1483
+ throw new Error("Task final-review contract establishment requires a verified exact Task control-plane invocation.");
1484
+ }
1485
+ const manifestPath = process.env.YUI_SESSION_MANIFEST;
1486
+ if (manifestPath === undefined) {
1487
+ throw new Error("Managed global control-plane invocation requires its Session Manifest.");
1488
+ }
1489
+ const home = resolveYuiHome(process.env);
1490
+ const manifest = assertManagedSessionManifest(home, "global");
1491
+ const expectedRoleKind = process.env.YUI_ROLE === "operator" ? "operator" : "global";
1492
+ if (manifest.owner.scope !== "global"
1493
+ || manifest.roleKind !== expectedRoleKind) {
1494
+ throw new Error("Managed global invocation does not match its Session Manifest.");
1495
+ }
1496
+ const serializedDescriptor = readFileSync(manifest.controlPlane.descriptorPath, "utf8");
1497
+ const descriptor = parseExactControlPlaneDescriptor(serializedDescriptor);
1498
+ if (resolve(descriptor.yuiHome) !== resolve(home)) {
1499
+ throw new Error("Managed global control-plane descriptor belongs to another YUI_HOME.");
1500
+ }
1501
+ const frozenDigest = exactControlPlaneDigest(descriptor);
1502
+ if (frozenDigest !== manifest.controlPlane.digest
1503
+ || resolve(manifest.controlPlane.descriptorPath) !== resolve(join(descriptor.yuiHome, "runtime", "control-plane", `${frozenDigest}.json`))
1504
+ || resolve(manifestPath) !== resolve(join(descriptor.yuiHome, "runtime", "session-manifests", `${manifest.digest}.json`))) {
1505
+ throw new Error("Exact global Session Manifest does not match its control-plane descriptor.");
1506
+ }
1507
+ let commandControl;
1508
+ let commandDigest;
1509
+ if (digest !== undefined) {
1510
+ await assertExactControlPlanePreflight({
1511
+ serializedDescriptor,
1512
+ digest,
1513
+ actualExecutable: process.execPath,
1514
+ actualCliEntry: fileURLToPath(import.meta.url),
1515
+ actualHome: home
1516
+ });
1517
+ commandControl = descriptor;
1518
+ commandDigest = digest;
1519
+ }
1520
+ else {
1521
+ await assertCompatibleControlPlanePreflight({ actualHome: home });
1522
+ commandControl = currentInvocationControlPlane(home);
1523
+ commandDigest = exactControlPlaneDigest(commandControl);
1524
+ }
1525
+ return {
1526
+ contract: undefined,
1527
+ controlPlane: { digest: commandDigest, descriptor: commandControl },
1528
+ verifiedStore: openCompatibleFileTaskStore(home)
1529
+ };
1530
+ }
1531
+ function currentInvocationControlPlane(home) {
1532
+ return createExactControlPlaneDescriptor({
1533
+ executable: process.execPath,
1534
+ cliEntry: fileURLToPath(import.meta.url),
1535
+ yuiHome: home
1536
+ });
1537
+ }
1538
+ function assertManagedSessionManifest(home, scope) {
1539
+ const manifestPath = process.env.YUI_SESSION_MANIFEST;
1540
+ if (manifestPath === undefined) {
1541
+ throw new Error("Managed control-plane invocation requires its Session Manifest.");
1542
+ }
1543
+ const manifest = readSessionBootstrapManifest(manifestPath);
1544
+ if (manifest.owner.scope !== scope) {
1545
+ throw new Error("Managed invocation scope does not match its Session Manifest.");
1546
+ }
1547
+ if (scope === "task" && (manifest.owner.scope !== "task"
1548
+ || manifest.owner.taskId !== process.env.YUI_TASK_ID)) {
1549
+ throw new Error("Managed Task invocation does not match its Session Manifest owner.");
1550
+ }
1551
+ const expectedPath = resolve(home, "runtime", "session-manifests", `${manifest.digest}.json`);
1552
+ if (resolve(manifestPath) !== expectedPath) {
1553
+ throw new Error("Managed Session Manifest path is outside this YUI_HOME.");
1554
+ }
1555
+ return manifest;
1556
+ }
1369
1557
  function cleanupCliError(error, fallbackResource) {
1370
1558
  if (error instanceof WorkspaceCleanupBlockedError) {
1371
1559
  return usageError(error.message, undefined, cleanupBlockedDetails(error.reason, error.resource, error.retryable));
@@ -1630,6 +1818,36 @@ async function prepareReviewLaneWorkspaces(taskId, reviewRoundId, store, prepare
1630
1818
  return map;
1631
1819
  }
1632
1820
  async function directTaskMainSnapshotForTaskCommand(args, store, preparer, environment, taskFinalReviewContract) {
1821
+ const deliveryOption = args.indexOf("--delivery", 3);
1822
+ const deliveryPromotion = args[0] === "task"
1823
+ && args[1] === "update"
1824
+ && args[2] !== undefined
1825
+ && (args.includes("--require-integration", 3)
1826
+ || (deliveryOption >= 0 && args[deliveryOption + 1] === "integrated"));
1827
+ if (deliveryPromotion) {
1828
+ const task = store.getTask(args[2]);
1829
+ if (task === null
1830
+ || (task.status !== "active" && task.status !== "draft")
1831
+ || task.requireIntegration === true
1832
+ || task.projectBindings.length === 0) {
1833
+ return undefined;
1834
+ }
1835
+ const workspace = store.getTaskWorkspace(task.id);
1836
+ if (task.status === "draft" && workspace === null)
1837
+ return undefined;
1838
+ if (workspace === null
1839
+ || workspace.owner.type !== "task"
1840
+ || workspace.owner.taskId !== task.id) {
1841
+ throw usageError(`Task has no authoritative main workspace: ${task.id}.`);
1842
+ }
1843
+ try {
1844
+ return await preparer.snapshotDirectTaskMain(workspace, task.projectBindings.map(({ projectId }) => projectId));
1845
+ }
1846
+ catch (error) {
1847
+ throw usageError(`Delivery promotion Task-main verification failed for ${task.id}: `
1848
+ + `${error instanceof Error ? error.message : String(error)}`);
1849
+ }
1850
+ }
1633
1851
  if (taskFinalReviewContract === undefined
1634
1852
  || args[0] !== "task"
1635
1853
  || args[1] !== "work"
@@ -1660,7 +1878,7 @@ async function directTaskMainSnapshotForTaskCommand(args, store, preparer, envir
1660
1878
  throw usageError(error instanceof Error ? error.message : String(error));
1661
1879
  }
1662
1880
  }
1663
- async function actualTaskReviewCandidateForTaskCommand(args, store, preparer, environment, taskFinalReviewContract) {
1881
+ async function actualTaskReviewCandidateForTaskCommand(args, store, preparer, environment) {
1664
1882
  if (args[0] !== "task")
1665
1883
  return undefined;
1666
1884
  let taskId;
@@ -1669,12 +1887,9 @@ async function actualTaskReviewCandidateForTaskCommand(args, store, preparer, en
1669
1887
  if (task === null || task.status !== "active" || task.projectBindings.length === 0) {
1670
1888
  return undefined;
1671
1889
  }
1672
- const establishedFinalRound = store.listReviewRounds(task.id).some((round) => ((round.scope ?? "work-item") === "task"));
1673
- if (taskFinalReviewContract === undefined
1674
- && store.getReviewConfig()?.trigger !== "final"
1675
- && !establishedFinalRound) {
1676
- return undefined;
1677
- }
1890
+ // Every Project-backed completion, including direct delivery, must freeze
1891
+ // a clean committed Task-main snapshot. Review policy only decides whether
1892
+ // that head also needs an independent ReviewRound.
1678
1893
  taskId = task.id;
1679
1894
  }
1680
1895
  else if (args[1] === "review"
@@ -213,6 +213,36 @@ export function renderExecutionAudit(report, width = defaultTableWidth()) {
213
213
  else {
214
214
  lines.push("", ...sectionError("workItems", report));
215
215
  }
216
+ if (report.orchestration.status === "ok" && report.orchestration.data !== undefined) {
217
+ const orchestration = report.orchestration.data;
218
+ lines.push("", `Orchestration: ${orchestration.tasks.length} Task(s) · ${orchestration.advisoryCount} advisory finding(s)`);
219
+ if (orchestration.tasks.length > 0) {
220
+ lines.push(renderTable("Task orchestration metrics", [
221
+ { header: "Task", minWidth: 7, maxWidth: 14 },
222
+ { header: "Delivery", minWidth: 8, maxWidth: 10 },
223
+ { header: "Runs", minWidth: 4, maxWidth: 6 },
224
+ { header: "WIs", minWidth: 3, maxWidth: 5 },
225
+ { header: "Review F/D/N", minWidth: 12, maxWidth: 16 },
226
+ { header: "Integration A/F/R", minWidth: 17, maxWidth: 20 },
227
+ { header: "Pre-progress gen", minWidth: 12, maxWidth: 16 },
228
+ { header: "Terminal ws", minWidth: 10, maxWidth: 12 },
229
+ { header: "Advice", minWidth: 6, maxWidth: 8 }
230
+ ], orchestration.tasks.map((task) => [
231
+ task.taskId,
232
+ task.deliveryPath,
233
+ String(task.runs.total),
234
+ String(task.workItems),
235
+ `${task.reviews.full}/${task.reviews.delta}/${task.reviews.nonSemantic}`,
236
+ `${task.integrations.attempts}/${task.integrations.failed}/${task.integrations.repeatedIdentities}`,
237
+ String(task.providerGenerationsBeforeFirstProgress),
238
+ String(task.terminalWorkspaceCount),
239
+ String(task.advisories.length)
240
+ ]), width));
241
+ }
242
+ }
243
+ else {
244
+ lines.push("", ...sectionError("orchestration", report));
245
+ }
216
246
  if (report.storage.status === "ok" && report.storage.data !== undefined) {
217
247
  const storage = report.storage.data;
218
248
  lines.push("", `Storage: backend ${storage.backend} · state.json ${formatBytes(storage.stateJsonBytes)} · yui.db ${formatBytes(storage.databaseBytes)}`
@@ -36,9 +36,8 @@ function roleContext(args, store, options) {
36
36
  if (environment.YUI_SESSION_SCOPE !== "global" || environment.YUI_ROLE !== name) {
37
37
  throw usageError("Managed GlobalRole context is outside the exact Session authority.");
38
38
  }
39
- if (environment.YUI_SESSION_MANIFEST === undefined
40
- || environment.YUI_SESSION_CLI === undefined) {
41
- throw usageError("Managed GlobalRole context requires the exact Session Manifest and CLI.");
39
+ if (environment.YUI_SESSION_MANIFEST === undefined) {
40
+ throw usageError("Managed GlobalRole context requires its Session Manifest.");
42
41
  }
43
42
  }
44
43
  const role = requireRole(name, store);
@@ -81,7 +80,12 @@ function roleContext(args, store, options) {
81
80
  : ["perform-global-role-request"]
82
81
  },
83
82
  sessionManifestPath: environment.YUI_SESSION_MANIFEST,
84
- sessionCliPath: environment.YUI_SESSION_CLI
83
+ cliCommand: "yui",
84
+ // Compatibility hint for already-running Sessions. New instructions use
85
+ // the stable command name and do not bind behavior to this wrapper path.
86
+ ...(environment.YUI_SESSION_CLI === undefined
87
+ ? {}
88
+ : { legacySessionCliPath: environment.YUI_SESSION_CLI })
85
89
  });
86
90
  if (options.jsonOutput === true)
87
91
  return `${JSON.stringify(context)}\n`;
@@ -1,6 +1,6 @@
1
1
  import { usageError } from "../errors/cliError.js";
2
2
  import { createRoleSessionSet } from "../executor/agentExecutor.js";
3
- import { listOperatorSessions, prepareOperatorNewSession, prepareOperatorResumeSession } from "../operator/operatorSessionHistory.js";
3
+ import { listOperatorSessions, projectOperatorStatus, prepareOperatorNewSession, prepareOperatorResumeSession } from "../operator/operatorSessionHistory.js";
4
4
  import { defaultTableWidth, renderTable } from "../output/table.js";
5
5
  import { formatRelativeTimestamp } from "../output/timePresentation.js";
6
6
  import { updateGlobalRole } from "../role/role.js";
@@ -11,6 +11,7 @@ export function runOperatorCommand(args, store, options = {}) {
11
11
  const [command, ...rest] = args;
12
12
  switch (command) {
13
13
  case "list": return listSessions(rest, store, options);
14
+ case "status": return status(rest, store, options);
14
15
  case "new": return newSession(rest, store);
15
16
  case "resume": return resumeSession(rest, store);
16
17
  case "submit": return submit(rest, store, options);
@@ -20,6 +21,46 @@ export function runOperatorCommand(args, store, options = {}) {
20
21
  : `Unknown command: operator ${command}`);
21
22
  }
22
23
  }
24
+ function status(args, store, options) {
25
+ if (args.length !== 0)
26
+ throw usageError("Operator status usage: yui operator status.");
27
+ const role = requireOperator(store);
28
+ const activeBinding = role.agentBindings[role.activeAgentId];
29
+ if (activeBinding === undefined) {
30
+ throw usageError(`Operator active Agent is not bound: ${role.activeAgentId}.`);
31
+ }
32
+ const projection = projectOperatorStatus(store.getGlobalRoleSessionSet(role.name), role.activeAgentId, activeBinding.adapterId);
33
+ const writer = projection.writer;
34
+ const writerLines = [
35
+ "Operator status",
36
+ "Active writer:",
37
+ ` State: ${writer.state}`,
38
+ ` Agent: ${writer.agentId}`,
39
+ ` Adapter: ${adapterLabel(writer.adapterId)}`,
40
+ ` Conversation: ${writer.sessionRef ?? "-"}`,
41
+ ` Native session: ${writer.nativeSessionId ?? "-"}`,
42
+ ` Session state: ${writer.sessionStatus ?? "-"}`
43
+ ];
44
+ const history = projection.historicalConversations;
45
+ const historyOutput = history.length === 0
46
+ ? "Historical conversations: none"
47
+ : renderTable("Historical conversations", [
48
+ { header: "Updated", minWidth: 7, maxWidth: 12 },
49
+ { header: "Agent", minWidth: 6, maxWidth: 16 },
50
+ { header: "Conversation", minWidth: 20, maxWidth: 72 },
51
+ { header: "State", minWidth: 7, maxWidth: 10 }
52
+ ], history.map((session) => [
53
+ formatRelativeTimestamp(session.updatedAt, options.now?.() ?? new Date()),
54
+ adapterLabel(session.adapterId),
55
+ session.displayTitle,
56
+ session.state
57
+ ]), options.width ?? defaultTableWidth());
58
+ return {
59
+ kind: "output",
60
+ output: `${writerLines.join("\n")}\n\n${historyOutput}\n`,
61
+ data: projection
62
+ };
63
+ }
23
64
  function submit(rest, store, options) {
24
65
  const usage = "Operator submit usage: yui operator submit (<body>|--body-file <path|->) [--task <id>].";
25
66
  const positionals = [];