@zq-silk/yui 0.15.12 → 0.16.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.
Files changed (236) hide show
  1. package/ARCHITECTURE.md +3 -2
  2. package/ARCHITECTURE.zh-CN.md +3 -2
  3. package/dist/artifacts/artifactCommitLock.js +16 -55
  4. package/dist/artifacts/taskArtifactRepository.js +3 -3
  5. package/dist/cli/agentConfigurationPicker.js +1 -1
  6. package/dist/cli/commandCatalog.js +57 -41
  7. package/dist/cli/completion.js +18 -18
  8. package/dist/cli/completionWizard.js +1 -1
  9. package/dist/cli/dynamicCompletion.js +1 -1
  10. package/dist/cli/interactionPolicy.js +7 -3
  11. package/dist/cli/invocationAuthority.js +64 -0
  12. package/dist/cli/managedDiagnostics.js +1 -1
  13. package/dist/cli/parseRepeatable.js +35 -0
  14. package/dist/cli/updatePorts.js +26 -5
  15. package/dist/cli.js +1280 -1243
  16. package/dist/commands/agentCommands.js +4 -4
  17. package/dist/commands/capabilityCommands.js +1 -1
  18. package/dist/commands/configCommands.js +6 -57
  19. package/dist/commands/configOverview.js +2 -2
  20. package/dist/commands/durableJobCommands.js +12 -6
  21. package/dist/commands/globalRoleCommands.js +53 -29
  22. package/dist/commands/grantCommands.js +0 -3
  23. package/dist/commands/jobCommands.js +1 -4
  24. package/dist/commands/operatorCommands.js +3 -3
  25. package/dist/commands/projectCommands.js +1 -10
  26. package/dist/commands/resourcesCommands.js +10 -40
  27. package/dist/commands/roleConfiguration.js +2 -6
  28. package/dist/commands/taskActivationCommands.js +4 -8
  29. package/dist/commands/taskAgentCapabilities.js +9 -0
  30. package/dist/commands/taskCommandSupport.js +155 -0
  31. package/dist/commands/taskCommandTypes.js +1 -0
  32. package/dist/commands/taskCommands.js +271 -710
  33. package/dist/commands/taskCompletionGate.js +1 -1
  34. package/dist/commands/taskExecutionCommands.js +1 -1
  35. package/dist/commands/taskFactCommands.js +325 -0
  36. package/dist/commands/taskInputCommands.js +12 -42
  37. package/dist/commands/taskIntegrationCommands.js +17 -57
  38. package/dist/commands/taskNextActionCommand.js +7 -6
  39. package/dist/commands/taskPublicationAdoptCommand.js +3 -3
  40. package/dist/commands/taskPublicationCommands.js +1 -1
  41. package/dist/commands/taskPublicationVerifyCommand.js +1 -1
  42. package/dist/commands/taskRemoteDeliveryCommand.js +2 -94
  43. package/dist/commands/taskUpstreamCommands.js +19 -8
  44. package/dist/commands/workflowCommands.js +6 -1
  45. package/dist/completion/completionInstaller.js +26 -26
  46. package/dist/completion/completionState.js +26 -26
  47. package/dist/completion/fileCompletionManager.js +6 -11
  48. package/dist/config/configCatalog.js +0 -2
  49. package/dist/config/timeZone.js +14 -0
  50. package/dist/config/yuiConfig.js +0 -29
  51. package/dist/context/runContextPack.js +2 -3
  52. package/dist/context/taskCatalog.js +3 -5
  53. package/dist/context/taskContext.js +139 -151
  54. package/dist/context/wakeRunReferences.js +11 -0
  55. package/dist/controller/agentCapabilities.js +58 -0
  56. package/dist/controller/agentRuntimeObserver.js +6 -11
  57. package/dist/controller/clientRuntime.js +22 -14
  58. package/dist/controller/controller.js +9 -22
  59. package/dist/controller/fileSchedulerStoreAdapter.js +117 -212
  60. package/dist/controller/jobClient.js +4 -4
  61. package/dist/controller/jobControl.js +60 -25
  62. package/dist/controller/jobSupervisor.js +4 -4
  63. package/dist/controller/providerRetryDelivery.js +7 -7
  64. package/dist/controller/runtime.js +51 -44
  65. package/dist/controller/runtimeEventProcessor.js +0 -5
  66. package/dist/controller/sessionOwnerReconciliation.js +5 -0
  67. package/dist/controller/taskAgentError.js +35 -0
  68. package/dist/coordination/workMailboxQueue.js +2 -24
  69. package/dist/core/controllerClient.js +2 -2
  70. package/dist/core/fileLockOwner.js +74 -0
  71. package/dist/decision/decision.js +17 -0
  72. package/dist/doctor/doctor.js +18 -9
  73. package/dist/event/taskEvent.js +12 -0
  74. package/dist/execution/workItemExecutionProjection.js +2 -2
  75. package/dist/executor/agentCapabilityConfig.js +43 -0
  76. package/dist/executor/agentConfigurationCatalog.js +49 -11
  77. package/dist/executor/agentExecutor.js +5 -8
  78. package/dist/executor/fileRoleLaunchPlanner.js +11 -53
  79. package/dist/executor/taskAgentCapabilities.js +64 -0
  80. package/dist/integration/deliveryObligation.js +1 -69
  81. package/dist/integration/gitIntegrationService.js +60 -84
  82. package/dist/integration/integrationAttempt.js +3 -14
  83. package/dist/integration/integrationSourceApplication.js +4 -12
  84. package/dist/integration/manifestTags.js +2 -2
  85. package/dist/job/durableJob.js +4 -25
  86. package/dist/job/jobAssignmentScope.js +22 -0
  87. package/dist/job/jobOperation.js +16 -0
  88. package/dist/job/jobRunner.js +2 -1
  89. package/dist/job/stepDirectory.js +15 -0
  90. package/dist/kernel/builtinCapabilities.js +15 -11
  91. package/dist/kernel/kernelPorts.js +1 -17
  92. package/dist/lifecycle/exactRunTerminalization.js +1 -9
  93. package/dist/message/inputControlResolution.js +3 -4
  94. package/dist/message/message.js +17 -20
  95. package/dist/message/messageContinuation.js +1 -1
  96. package/dist/milestone/milestone.js +11 -0
  97. package/dist/observability/orchestrationMetrics.js +24 -1
  98. package/dist/observability/runtimeIdentity.js +1 -17
  99. package/dist/output/agentConfigurationPresentation.js +2 -0
  100. package/dist/output/timePresentation.js +1 -14
  101. package/dist/plugins/pluginService.js +12 -4
  102. package/dist/release/releaseWorkflowPorts.js +45 -96
  103. package/dist/release/runtimeRelease.js +9 -15
  104. package/dist/repository/projectMaintenanceLock.js +23 -64
  105. package/dist/repository/taskBaseFreshness.js +1 -1
  106. package/dist/repository/taskWorkspaceCoordinator.js +13 -22
  107. package/dist/repository/taskWorkspacePreparer.js +29 -25
  108. package/dist/repository/workspaceCleanupInspection.js +5 -5
  109. package/dist/resources/autoResourceGc.js +4 -77
  110. package/dist/resources/liveReferences.js +7 -54
  111. package/dist/resources/resourceDiscovery.js +5 -118
  112. package/dist/resources/resourceGc.js +233 -181
  113. package/dist/resources/resourceRegistrar.js +5 -4
  114. package/dist/resources/resourceRegistry.js +3 -37
  115. package/dist/resources/resourceRegistryStore.js +13 -36
  116. package/dist/resources/sqliteResourceRegistry.js +43 -26
  117. package/dist/review/deltaRecheck.js +1 -1
  118. package/dist/review/reviewAcceptance.js +1 -1
  119. package/dist/review/reviewDecision.js +1 -1
  120. package/dist/review/reviewRound.js +8 -7
  121. package/dist/review/taskFinalReviewContractResolution.js +1 -1
  122. package/dist/runtime/acpProtocol.js +4 -51
  123. package/dist/runtime/acpSession.js +10 -75
  124. package/dist/runtime/acpSessionConfiguration.js +1 -8
  125. package/dist/runtime/agentEndpoint.js +3 -2
  126. package/dist/runtime/agentFailureContext.js +43 -0
  127. package/dist/runtime/agentHost.js +15 -15
  128. package/dist/runtime/codexInteractiveHost.js +2 -2
  129. package/dist/runtime/index.js +0 -1
  130. package/dist/runtime/managedCaller.js +21 -1
  131. package/dist/runtime/providerErrors.js +38 -0
  132. package/dist/runtime/runtimeObservation.js +0 -5
  133. package/dist/runtime/runtimeSessionCandidate.js +2 -3
  134. package/dist/runtime/sessionOwnerIdentity.js +1 -1
  135. package/dist/runtime/structuredProviderHost.js +4 -41
  136. package/dist/runtime/tmuxAdapters.js +6 -8
  137. package/dist/scheduler/activeRoleRunDelivery.js +7 -7
  138. package/dist/scheduler/leaderWakeupProcessor.js +19 -6
  139. package/dist/scheduler/operatorEvent.js +12 -20
  140. package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
  141. package/dist/scheduler/ports.js +5 -8
  142. package/dist/scheduler/roleRunLiveness.js +4 -4
  143. package/dist/scheduler/roleRunStall.js +16 -23
  144. package/dist/scheduler/taskExecutionProjection.js +25 -33
  145. package/dist/scheduler/taskObservabilityProjection.js +0 -1
  146. package/dist/scheduler/taskWake.js +5 -10
  147. package/dist/scheduler/wakeReason.js +2 -0
  148. package/dist/scheduler/wakeupQueue.js +2 -4
  149. package/dist/setup/setupCommand.js +1 -1
  150. package/dist/storage/contextRecords.js +106 -0
  151. package/dist/storage/currentTaskStore.js +1 -1
  152. package/dist/storage/homeLayout.js +13 -17
  153. package/dist/storage/migrations/agentFailureContext.js +22 -0
  154. package/dist/storage/migrations/currentInputContract.js +86 -0
  155. package/dist/storage/migrations/currentRuntimeContract.js +228 -0
  156. package/dist/storage/migrations/historicalVerificationPlan.js +35 -0
  157. package/dist/storage/migrations/integrationContinuation.js +5 -4
  158. package/dist/storage/migrations/narrowAgentFailureContext.js +65 -0
  159. package/dist/storage/migrations/notificationOnlyWakes.js +74 -0
  160. package/dist/storage/migrations/verificationPlanV1.js +162 -0
  161. package/dist/storage/migrations/verificationPolicy.js +74 -0
  162. package/dist/storage/migrations/workItemHistory.js +46 -0
  163. package/dist/storage/persistenceWorker.js +12 -4
  164. package/dist/storage/recordValidation.js +87 -0
  165. package/dist/storage/sqliteSchema.js +142 -1
  166. package/dist/storage/sqliteStore.js +156 -162
  167. package/dist/storage/storageSchema.js +2 -14
  168. package/dist/storage/storageVersions.js +1 -1
  169. package/dist/storage/storeRpc.js +15 -7
  170. package/dist/storage/taskStore.js +3 -7
  171. package/dist/storage/upgrade/upgradeOrchestrator.js +25 -37
  172. package/dist/task/completionReadiness.js +5 -24
  173. package/dist/task/draftPlan.js +0 -53
  174. package/dist/task/nextAction.js +7 -13
  175. package/dist/task/remoteDeliveryService.js +89 -0
  176. package/dist/task/taskActivation.js +2 -25
  177. package/dist/task/taskActivationService.js +0 -2
  178. package/dist/task/taskRecordReference.js +0 -1
  179. package/dist/task/taskSubmission.js +3 -14
  180. package/dist/telemetry/sqliteTelemetryBatch.js +29 -0
  181. package/dist/telemetry/sqliteTelemetryStore.js +49 -49
  182. package/dist/telemetry/telemetryWiring.js +4 -3
  183. package/dist/tmux/tmuxManager.js +29 -20
  184. package/dist/verification/gateArtifact.js +15 -24
  185. package/dist/verification/gateArtifactStore.js +14 -7
  186. package/dist/verification/verificationGateService.js +29 -88
  187. package/dist/verification/verificationPlan.js +27 -72
  188. package/dist/web/assets/client/app.js +1 -1
  189. package/dist/web/assets/client/taskSurface.js +4 -4
  190. package/dist/web/webServer.js +4 -6
  191. package/dist/web/webSnapshot.js +3 -83
  192. package/dist/web/webTaskSurface.js +17 -46
  193. package/dist/workItem/workItem.js +1 -12
  194. package/docs/architecture/capabilities-and-resources.md +14 -2
  195. package/docs/architecture/capabilities-and-resources.zh-CN.md +11 -1
  196. package/docs/managed-turn-and-session-runtime.md +83 -10
  197. package/docs/managed-turn-and-session-runtime.zh-CN.md +65 -11
  198. package/docs/plugin-sdk.md +4 -2
  199. package/docs/provider-runtime.md +3 -1
  200. package/docs/provider-runtime.zh-CN.md +4 -2
  201. package/docs/release-workflow.md +190 -7
  202. package/docs/release-workflow.zh-CN.md +139 -5
  203. package/docs/roles-and-configuration.md +29 -0
  204. package/docs/roles-and-configuration.zh-CN.md +21 -0
  205. package/docs/sqlite-control-plane-design.md +16 -3
  206. package/docs/sqlite-control-plane-design.zh-CN.md +13 -2
  207. package/docs/task-dag-semantics.md +3 -3
  208. package/docs/task-dag-semantics.zh-CN.md +2 -2
  209. package/docs/task-delivery.md +91 -5
  210. package/docs/task-delivery.zh-CN.md +72 -4
  211. package/docs/task-discovery.md +10 -11
  212. package/docs/task-discovery.zh-CN.md +8 -9
  213. package/docs/testing/verification-levels.md +145 -9
  214. package/docs/testing/verification-levels.zh-CN.md +103 -8
  215. package/package.json +1 -1
  216. package/skills/yui-leader/SKILL.md +5 -0
  217. package/skills/yui-leader/references/integration.md +9 -5
  218. package/skills/yui-leader/references/planning.md +9 -3
  219. package/skills/yui-operator/SKILL.md +15 -10
  220. package/skills/yui-runtime/SKILL.md +6 -6
  221. package/skills/yui-runtime/references/recovery.md +47 -0
  222. package/dist/agent/agentRegistry.js +0 -10
  223. package/dist/agentRun/runIdentity.js +0 -22
  224. package/dist/commands/deliveryGuardPreflight.js +0 -30
  225. package/dist/commands/taskIntegrationQueueCommands.js +0 -226
  226. package/dist/commands/taskOverviewCommand.js +0 -377
  227. package/dist/completion/completionWizard.js +0 -125
  228. package/dist/context/dispatchContext.js +0 -110
  229. package/dist/context/wakeNotification.js +0 -144
  230. package/dist/integration/integrationCheckEvidenceReuse.js +0 -53
  231. package/dist/integration/integrationQueueEntry.js +0 -191
  232. package/dist/integration/integrationQueueService.js +0 -810
  233. package/dist/release/fakeReleasePorts.js +0 -55
  234. package/dist/runtime/sessionOwnerRegistry.js +0 -137
  235. package/dist/task/deliveryGuard.js +0 -226
  236. /package/dist/{commands/taskActor.js → task/taskAuthority.js} +0 -0
@@ -2,8 +2,8 @@ import { usageError } from "../errors/cliError.js";
2
2
  import { GitIntegrationService } from "../integration/gitIntegrationService.js";
3
3
  import { createIntegrationAttempt } from "../integration/integrationAttempt.js";
4
4
  import { NodeGitWorkspace } from "../repository/gitWorkspace.js";
5
- import { publicationExternalKey } from "../task/publicationReference.js";
6
5
  import { isCompletedTaskReviewEvidence } from "../review/reviewAcceptance.js";
6
+ import { publicationExternalKey } from "../task/publicationReference.js";
7
7
  import { workspaceProjectEntry } from "../worktree/managedWorkspace.js";
8
8
  /**
9
9
  * Verify the one supported ancestry waiver before `task complete` mutates any
@@ -6,7 +6,7 @@ import { finishReviewRound, updateReviewExecutionGroup } from "../review/reviewR
6
6
  import { failRun } from "../agentRun/agentRun.js";
7
7
  import { queueLeaderWakeup } from "../scheduler/wakeupQueue.js";
8
8
  import { startTaskExecution, stopTaskExecution } from "../task/task.js";
9
- import { taskActor } from "./taskActor.js";
9
+ import { taskActor } from "../task/taskAuthority.js";
10
10
  export function parseTaskExecutionStopRequest(args) {
11
11
  const taskId = args[0];
12
12
  if (taskId === undefined || taskId.startsWith("--")) {
@@ -0,0 +1,325 @@
1
+ import { createTaskBrief, updateTaskBrief } from "../brief/taskBrief.js";
2
+ import { assertContextRecordReadable, contextRecordReader } from "../context/taskContext.js";
3
+ import { enqueueWork } from "../coordination/workMailboxQueue.js";
4
+ import { createDecision, supersedeDecision } from "../decision/decision.js";
5
+ import { dataError, usageError } from "../errors/cliError.js";
6
+ import { createMilestone } from "../milestone/milestone.js";
7
+ import { defaultTableWidth, renderTable } from "../output/table.js";
8
+ import { assertTaskOpen, clock, exactPositionals, leaderActionEventPayload, leaderMailbox, notifyMailbox, optionalNonEmptyOption, output, parseMultiValueTail, parseTail, presentTime, recordTaskEvent, requiredOption, requiredText, requireTask, taskActor, taskMailbox, taskRef } from "./taskCommandSupport.js";
9
+ import { failureCapabilitiesCommand } from "../runtime/agentFailureContext.js";
10
+ export function taskBriefCommand(args, store, options) {
11
+ const [command, ...rest] = args;
12
+ if (command === "show") {
13
+ exactPositionals(rest, 1, "Task brief show usage: yui task brief show <task>.");
14
+ const task = requireTask(store, rest[0]);
15
+ assertContextRecordReadable(store, task.id, "task-brief", task.id, options.environment);
16
+ const brief = store.getTaskBrief(task.id);
17
+ if (brief === null) {
18
+ return output(`Task ${task.id} has no brief.\n`, { taskId: task.id, brief: null });
19
+ }
20
+ const timeZone = store.getConfig().timeZone;
21
+ return output([
22
+ `Task: ${task.id}`,
23
+ `Objective: ${brief.objective}`,
24
+ `Boundaries:`,
25
+ ...(brief.boundaries.length === 0 ? [" (none)"] : brief.boundaries.map((b) => ` - ${b}`)),
26
+ `Technical approach: ${brief.technicalApproach || "(not defined)"}`,
27
+ `Current focus: ${brief.currentFocus}`,
28
+ `Leader summary: ${brief.leaderSummary}`,
29
+ `Updated by: ${brief.updatedBy}`,
30
+ `Updated at: ${presentTime(brief.updatedAt, timeZone)}`
31
+ ].join("\n").concat("\n"), { taskId: task.id, brief });
32
+ }
33
+ if (command === "update") {
34
+ const usage = "Task brief update usage: yui task brief update <task> [--objective <text>] [--boundary <text> ...] [--approach <text>] [--focus <text>] [--leader-summary <text>].";
35
+ const parsed = parseMultiValueTail(rest, new Set(["--objective", "--approach", "--focus", "--leader-summary"]), new Set(["--boundary"]), usage);
36
+ exactPositionals(parsed.positionals, 1, usage);
37
+ const hasObjective = parsed.options.has("--objective");
38
+ const hasApproach = parsed.options.has("--approach");
39
+ const hasFocus = parsed.options.has("--focus");
40
+ const hasSummary = parsed.options.has("--leader-summary");
41
+ const boundaries = parsed.multiOptions.get("--boundary") ?? [];
42
+ if (!hasObjective && !hasApproach && !hasFocus && !hasSummary && boundaries.length === 0) {
43
+ throw usageError("At least one brief field is required.", usage);
44
+ }
45
+ const now = clock(options);
46
+ const result = store.transaction((tx) => {
47
+ const task = requireTask(tx, parsed.positionals[0]);
48
+ assertTaskOpen(task);
49
+ const existing = tx.getTaskBrief(task.id);
50
+ const updatedBy = taskActor(tx, options, task.id);
51
+ const brief = existing === null
52
+ ? createTaskBrief({
53
+ objective: requiredText(parsed.options.get("--objective"), "--objective"),
54
+ boundaries,
55
+ ...(hasApproach
56
+ ? { technicalApproach: requiredText(parsed.options.get("--approach"), "--approach") }
57
+ : {}),
58
+ currentFocus: requiredText(parsed.options.get("--focus"), "--focus"),
59
+ leaderSummary: requiredText(parsed.options.get("--leader-summary"), "--leader-summary"),
60
+ updatedBy
61
+ }, now)
62
+ : updateTaskBrief(existing, {
63
+ ...(hasObjective ? { objective: parsed.options.get("--objective") } : {}),
64
+ ...(boundaries.length > 0 ? { boundaries } : {}),
65
+ ...(hasApproach
66
+ ? { technicalApproach: parsed.options.get("--approach") }
67
+ : {}),
68
+ ...(hasFocus ? { currentFocus: parsed.options.get("--focus") } : {}),
69
+ ...(hasSummary ? { leaderSummary: parsed.options.get("--leader-summary") } : {})
70
+ }, updatedBy, now);
71
+ tx.saveTaskBrief(task.id, brief);
72
+ recordTaskEvent(tx, task.id, "brief.updated", {
73
+ updatedBy,
74
+ previous: JSON.stringify(existing), current: JSON.stringify(brief)
75
+ }, now);
76
+ enqueueWork(tx, taskMailbox(task.id), "brief-updated", now, [taskRef(task.id)]);
77
+ if (task.status === "active" && updatedBy !== "leader") {
78
+ enqueueWork(tx, leaderMailbox(task.id), "brief-updated", now, [taskRef(task.id)]);
79
+ }
80
+ return { task, brief };
81
+ });
82
+ notifyMailbox(options.runtime, taskMailbox(result.task.id));
83
+ if (result.task.status === "active") {
84
+ notifyMailbox(options.runtime, leaderMailbox(result.task.id));
85
+ }
86
+ return output(`Updated brief for ${result.task.id}\n`, { taskId: result.task.id, brief: result.brief });
87
+ }
88
+ throw usageError(command === undefined
89
+ ? "Task brief command is required."
90
+ : `Unknown command: task brief ${command}`);
91
+ }
92
+ export function taskDecisionCommand(args, store, options) {
93
+ const [command, ...rest] = args;
94
+ if (command === "record") {
95
+ const usage = "Task decision record usage: yui task decision record <task> --title <text> --rationale <text>.";
96
+ const parsed = parseTail(rest, new Set(["--title", "--rationale"]), usage);
97
+ exactPositionals(parsed.positionals, 1, usage);
98
+ const title = requiredOption(parsed.options, "--title");
99
+ const rationale = requiredOption(parsed.options, "--rationale");
100
+ const now = clock(options);
101
+ const result = store.transaction((tx) => {
102
+ const task = requireTask(tx, parsed.positionals[0]);
103
+ assertTaskOpen(task);
104
+ const actor = taskActor(tx, options, task.id);
105
+ const decision = createDecision(tx.nextDecisionId(task.id), task.id, title, rationale, now);
106
+ tx.saveDecision(task.id, decision);
107
+ recordTaskEvent(tx, task.id, "decision.recorded", {
108
+ decisionId: decision.id,
109
+ title,
110
+ ...leaderActionEventPayload(tx, task.id, options)
111
+ }, now);
112
+ enqueueWork(tx, taskMailbox(task.id), "decision-recorded", now, [taskRef(task.id)]);
113
+ if (task.status === "active" && actor !== "leader") {
114
+ enqueueWork(tx, leaderMailbox(task.id), "decision-recorded", now, [taskRef(task.id)]);
115
+ }
116
+ return { task, decision };
117
+ });
118
+ notifyMailbox(options.runtime, taskMailbox(result.task.id));
119
+ if (result.task.status === "active")
120
+ notifyMailbox(options.runtime, leaderMailbox(result.task.id));
121
+ return output(`Recorded decision ${result.decision.id} for ${result.task.id}\n`);
122
+ }
123
+ if (command === "list") {
124
+ const usage = "Task decision list usage: yui task decision list <task> [--status active|superseded].";
125
+ const parsed = parseTail(rest, new Set(["--status"]), usage);
126
+ exactPositionals(parsed.positionals, 1, usage);
127
+ const task = requireTask(store, parsed.positionals[0]);
128
+ const readable = contextRecordReader(store, task.id, options.environment);
129
+ let decisions = store.listDecisions(task.id).filter(record => readable("task-decision", record.id));
130
+ const status = parsed.options.get("--status");
131
+ if (status !== undefined) {
132
+ if (status !== "active" && status !== "superseded") {
133
+ throw usageError("--status must be active or superseded.", usage);
134
+ }
135
+ decisions = decisions.filter((d) => d.status === status);
136
+ }
137
+ if (decisions.length === 0) {
138
+ return output(`No decisions found for ${task.id}.\n`, { taskId: task.id, decisions: [] });
139
+ }
140
+ const timeZone = store.getConfig().timeZone;
141
+ return output(`${renderTable(`Decisions: ${task.id}`, [
142
+ { header: "Decision", minWidth: 8, maxWidth: 18 },
143
+ { header: "Status", minWidth: 6, maxWidth: 12 },
144
+ { header: "Title", minWidth: 8, maxWidth: 64 },
145
+ { header: "Created", minWidth: 10, maxWidth: 28 }
146
+ ], decisions.map((d) => [d.id, d.status, d.title, presentTime(d.createdAt, timeZone)]), defaultTableWidth())}\n`, { taskId: task.id, decisions });
147
+ }
148
+ if (command === "show") {
149
+ exactPositionals(rest, 2, "Task decision show usage: yui task decision show <task> <decision>.");
150
+ const task = requireTask(store, rest[0]);
151
+ assertContextRecordReadable(store, task.id, "task-decision", rest[1], options.environment);
152
+ const decision = store.getDecision(task.id, rest[1]);
153
+ if (decision === null)
154
+ throw dataError(`Decision not found: ${rest[1]}.`);
155
+ const timeZone = store.getConfig().timeZone;
156
+ return output([
157
+ `Decision: ${decision.id}`,
158
+ `Task: ${task.id}`,
159
+ `Title: ${decision.title}`,
160
+ `Rationale: ${decision.rationale}`,
161
+ `Status: ${decision.status}`,
162
+ ...(decision.supersededReason === undefined ? [] : [`Superseded reason: ${decision.supersededReason}`]),
163
+ ...(decision.supersededAt === undefined ? [] : [`Superseded at: ${presentTime(decision.supersededAt, timeZone)}`]),
164
+ `Created: ${presentTime(decision.createdAt, timeZone)}`,
165
+ `Updated: ${presentTime(decision.updatedAt, timeZone)}`
166
+ ].join("\n").concat("\n"), { taskId: task.id, decision });
167
+ }
168
+ if (command === "supersede") {
169
+ const usage = "Task decision supersede usage: yui task decision supersede <task> <decision> --reason <text>.";
170
+ const parsed = parseTail(rest, new Set(["--reason"]), usage);
171
+ exactPositionals(parsed.positionals, 2, usage);
172
+ const reason = requiredOption(parsed.options, "--reason");
173
+ const now = clock(options);
174
+ const result = store.transaction((tx) => {
175
+ const task = requireTask(tx, parsed.positionals[0]);
176
+ assertTaskOpen(task);
177
+ const actor = taskActor(tx, options, task.id);
178
+ const existing = tx.getDecision(task.id, parsed.positionals[1]);
179
+ if (existing === null)
180
+ throw dataError(`Decision not found: ${parsed.positionals[1]}.`);
181
+ const decision = supersedeDecision(existing, reason, now);
182
+ tx.saveDecision(task.id, decision);
183
+ recordTaskEvent(tx, task.id, "decision.superseded", {
184
+ decisionId: decision.id,
185
+ reason,
186
+ ...leaderActionEventPayload(tx, task.id, options)
187
+ }, now);
188
+ enqueueWork(tx, taskMailbox(task.id), "decision-superseded", now, [taskRef(task.id)]);
189
+ if (task.status === "active" && actor !== "leader") {
190
+ enqueueWork(tx, leaderMailbox(task.id), "decision-superseded", now, [taskRef(task.id)]);
191
+ }
192
+ return { task, decision };
193
+ });
194
+ notifyMailbox(options.runtime, taskMailbox(result.task.id));
195
+ if (result.task.status === "active")
196
+ notifyMailbox(options.runtime, leaderMailbox(result.task.id));
197
+ return output(`Superseded decision ${result.decision.id} for ${result.task.id}\n`);
198
+ }
199
+ throw usageError(command === undefined
200
+ ? "Task decision command is required."
201
+ : `Unknown command: task decision ${command}`);
202
+ }
203
+ export function taskMilestoneCommand(args, store, options) {
204
+ const [command, ...rest] = args;
205
+ if (command === "add") {
206
+ const usage = "Task milestone add usage: yui task milestone add <task> --title <text> --summary <text>.";
207
+ const parsed = parseTail(rest, new Set(["--title", "--summary"]), usage);
208
+ exactPositionals(parsed.positionals, 1, usage);
209
+ const title = requiredOption(parsed.options, "--title");
210
+ const summary = requiredOption(parsed.options, "--summary");
211
+ const now = clock(options);
212
+ const result = store.transaction((tx) => {
213
+ const task = requireTask(tx, parsed.positionals[0]);
214
+ assertTaskOpen(task);
215
+ const actor = taskActor(tx, options, task.id);
216
+ const milestone = createMilestone(tx.nextMilestoneId(task.id), task.id, title, summary, actor, now);
217
+ tx.saveMilestone(task.id, milestone);
218
+ recordTaskEvent(tx, task.id, "milestone.added", {
219
+ milestoneId: milestone.id,
220
+ title,
221
+ ...leaderActionEventPayload(tx, task.id, options)
222
+ }, now);
223
+ enqueueWork(tx, taskMailbox(task.id), "milestone-added", now, [taskRef(task.id)]);
224
+ return { task, milestone };
225
+ });
226
+ notifyMailbox(options.runtime, taskMailbox(result.task.id));
227
+ return output(`Added milestone ${result.milestone.id} for ${result.task.id}\n`);
228
+ }
229
+ if (command === "list") {
230
+ exactPositionals(rest, 1, "Task milestone list usage: yui task milestone list <task>.");
231
+ const task = requireTask(store, rest[0]);
232
+ const readable = contextRecordReader(store, task.id, options.environment);
233
+ const milestones = store.listMilestones(task.id).filter(record => readable("task-milestone", record.id));
234
+ if (milestones.length === 0) {
235
+ return output(`No milestones found for ${task.id}.\n`, { taskId: task.id, milestones: [] });
236
+ }
237
+ const timeZone = store.getConfig().timeZone;
238
+ return output(`${renderTable(`Milestones: ${task.id}`, [
239
+ { header: "Milestone", minWidth: 9, maxWidth: 18 },
240
+ { header: "Title", minWidth: 8, maxWidth: 64 },
241
+ { header: "Created", minWidth: 10, maxWidth: 28 }
242
+ ], milestones.map((m) => [m.id, m.title, presentTime(m.createdAt, timeZone)]), defaultTableWidth())}\n`, { taskId: task.id, milestones });
243
+ }
244
+ if (command === "show") {
245
+ exactPositionals(rest, 2, "Task milestone show usage: yui task milestone show <task> <milestone>.");
246
+ const task = requireTask(store, rest[0]);
247
+ assertContextRecordReadable(store, task.id, "task-milestone", rest[1], options.environment);
248
+ const milestone = store.getMilestone(task.id, rest[1]);
249
+ if (milestone === null)
250
+ throw dataError(`Milestone not found: ${rest[1]}.`);
251
+ const timeZone = store.getConfig().timeZone;
252
+ return output([
253
+ `Milestone: ${milestone.id}`,
254
+ `Task: ${task.id}`,
255
+ `Title: ${milestone.title}`,
256
+ `Summary: ${milestone.summary}`,
257
+ `Created by: ${milestone.createdBy}`,
258
+ `Created: ${presentTime(milestone.createdAt, timeZone)}`
259
+ ].join("\n").concat("\n"), { taskId: task.id, milestone });
260
+ }
261
+ throw usageError(command === undefined
262
+ ? "Task milestone command is required."
263
+ : `Unknown command: task milestone ${command}`);
264
+ }
265
+ export function taskEventCommand(args, store, options = {}) {
266
+ const [command, ...rest] = args;
267
+ if (command === "list") {
268
+ const eventListUsage = "Task event list usage: yui task event list <task> [--after <timestamp>] [--limit <n>].";
269
+ const parsed = parseTail(rest, new Set(["--after", "--limit"]), eventListUsage);
270
+ exactPositionals(parsed.positionals, 1, eventListUsage);
271
+ const task = requireTask(store, parsed.positionals[0]);
272
+ const readable = contextRecordReader(store, task.id, options.environment);
273
+ let events = store.listEvents(task.id).filter(record => readable("task-event", record.id));
274
+ const after = optionalNonEmptyOption(parsed.options, "--after");
275
+ if (after !== undefined) {
276
+ const afterMs = Date.parse(after);
277
+ if (!Number.isFinite(afterMs))
278
+ throw usageError("--after must be a valid timestamp.", eventListUsage);
279
+ events = events.filter((e) => Date.parse(e.createdAt) > afterMs);
280
+ }
281
+ const limit = optionalNonEmptyOption(parsed.options, "--limit");
282
+ if (limit !== undefined) {
283
+ const n = Number(limit);
284
+ if (!Number.isSafeInteger(n) || n <= 0)
285
+ throw usageError("--limit must be a positive integer.", eventListUsage);
286
+ events = events.slice(-n);
287
+ }
288
+ if (events.length === 0) {
289
+ return output(`No events found for ${task.id}.\n`, { taskId: task.id, events: [] });
290
+ }
291
+ const timeZone = store.getConfig().timeZone;
292
+ return output(`${renderTable(`Events: ${task.id}`, [
293
+ { header: "Event", minWidth: 8, maxWidth: 18 },
294
+ { header: "Type", minWidth: 8, maxWidth: 28 },
295
+ { header: "Created", minWidth: 10, maxWidth: 28 }
296
+ ], events.map((e) => [e.id, e.type, presentTime(e.createdAt, timeZone)]), defaultTableWidth())}\n`, { taskId: task.id, events });
297
+ }
298
+ if (command === "show") {
299
+ exactPositionals(rest, 2, "Task event show usage: yui task event show <task> <event>.");
300
+ const task = requireTask(store, rest[0]);
301
+ assertContextRecordReadable(store, task.id, "task-event", rest[1], options.environment);
302
+ const events = store.listEvents(task.id);
303
+ const event = events.find((e) => e.id === rest[1]) ?? null;
304
+ if (event === null)
305
+ throw dataError(`Event not found: ${rest[1]}.`);
306
+ const timeZone = store.getConfig().timeZone;
307
+ const capabilitiesCommand = event.type === "runtime.agent-error" && event.payload.roleName
308
+ ? failureCapabilitiesCommand(task.id, event.payload.roleName, event.id) : undefined;
309
+ return output([
310
+ `Event: ${event.id}`,
311
+ `Task: ${task.id}`,
312
+ `Type: ${event.type}`,
313
+ `Created: ${presentTime(event.createdAt, timeZone)}`,
314
+ ...(capabilitiesCommand === undefined ? [] : [`Inspect capabilities: ${capabilitiesCommand}`]),
315
+ `Payload:`,
316
+ ...(Object.keys(event.payload).length === 0
317
+ ? [" (none)"]
318
+ : Object.entries(event.payload).map(([k, v]) => ` ${k}: ${v}`))
319
+ ].join("\n").concat("\n"), { taskId: task.id, event,
320
+ ...(capabilitiesCommand === undefined ? {} : { diagnostics: { capabilitiesCommand } }) });
321
+ }
322
+ throw usageError(command === undefined
323
+ ? "Task event command is required."
324
+ : `Unknown command: task event ${command}`);
325
+ }
@@ -1,12 +1,11 @@
1
+ import { enqueueWork } from "../coordination/workMailboxQueue.js";
1
2
  import { dataError, roleNotFound, taskNotFound, usageError } from "../errors/cliError.js";
2
3
  import { createTaskEvent } from "../event/taskEvent.js";
3
- import { activeLiveRoleAgentSession } from "../executor/agentExecutor.js";
4
4
  import { answerInputRequest, cancelInputRequest, createInputRequest } from "../input/inputRequest.js";
5
5
  import { defaultTableWidth, renderTable } from "../output/table.js";
6
6
  import { formatTimestamp } from "../output/timePresentation.js";
7
- import { enqueueWork } from "../coordination/workMailboxQueue.js";
7
+ import { requireManagedTaskCaller, requireManagedGlobalCaller } from "../runtime/managedCaller.js";
8
8
  import { isRoleRunStalled, RUN_RECOVERED_EVENT } from "../scheduler/roleRunStall.js";
9
- import { requireManagedTaskCaller } from "../runtime/managedCaller.js";
10
9
  import { resolveTaskRecordReference } from "../task/taskRecordReference.js";
11
10
  const LEADER_ROLE = "leader";
12
11
  export function runTaskInputCommand(args, store, options) {
@@ -84,7 +83,7 @@ function createRequest(args, store, options) {
84
83
  }, now);
85
84
  return created;
86
85
  });
87
- notifyMailbox(options, { kind: "operator" }, request.taskId);
86
+ notifyMailbox(options, { kind: "operator" });
88
87
  return output(`Created input request ${request.id} for ${request.taskId}\n`, { request });
89
88
  }
90
89
  function listRequests(args, store) {
@@ -165,7 +164,7 @@ function answerRequest(args, store, options) {
165
164
  enqueueWork(tx, { kind: "role", taskId: task.id, roleName: LEADER_ROLE }, `input-answered:${answered.id}`, now, [{ type: "input", taskId: task.id, id: answered.id }]);
166
165
  return answered;
167
166
  });
168
- notifyMailbox(options, { kind: "role", taskId: request.taskId, roleName: LEADER_ROLE }, request.taskId);
167
+ notifyMailbox(options, { kind: "role", taskId: request.taskId, roleName: LEADER_ROLE });
169
168
  return output(`Answered input request ${request.id} for ${request.taskId}\n`, { request });
170
169
  }
171
170
  function cancelRequest(args, store, options) {
@@ -195,7 +194,7 @@ function cancelRequest(args, store, options) {
195
194
  enqueueWork(tx, { kind: "role", taskId: task.id, roleName: LEADER_ROLE }, `input-cancelled:${cancelled.id}`, now, [{ type: "input", taskId: task.id, id: cancelled.id }]);
196
195
  return cancelled;
197
196
  });
198
- notifyMailbox(options, { kind: "role", taskId: request.taskId, roleName: LEADER_ROLE }, request.taskId);
197
+ notifyMailbox(options, { kind: "role", taskId: request.taskId, roleName: LEADER_ROLE });
199
198
  return output(`Cancelled input request ${request.id} for ${request.taskId}\n`, { request });
200
199
  }
201
200
  function requireLeaderInputOrigin(store, taskId, environment) {
@@ -237,27 +236,13 @@ export function isCurrentGlobalOperator(store, environment) {
237
236
  || (environment.YUI_ROLE !== undefined && environment.YUI_ROLE !== "operator")
238
237
  || environment.YUI_TASK_ID !== undefined)
239
238
  return false;
240
- const role = store.getGlobalRole("operator");
241
- if (role === null)
242
- return false;
243
- const binding = role.agentBindings[role.activeAgentId];
244
- if (binding === undefined)
245
- return false;
246
- const sessions = store.getGlobalRoleSessionSet(role.name);
247
- const session = activeLiveRoleAgentSession(sessions);
248
- if (sessions === null || session === null || sessions.activeAgentId !== role.activeAgentId) {
239
+ try {
240
+ requireManagedGlobalCaller(store, { ...environment, YUI_SESSION_SCOPE: "global", YUI_ROLE: "operator" });
241
+ return true;
242
+ }
243
+ catch {
249
244
  return false;
250
245
  }
251
- // The provider's conversation survives Host restarts and entry-point changes.
252
- // Prefer its command-time identity over a launch-time environment snapshot.
253
- // A launch reservation alone is not a registered Operator conversation.
254
- const nativeSessionId = exactIdentity(binding.adapterId === "codex"
255
- ? environment.CODEX_THREAD_ID ?? environment.YUI_NATIVE_SESSION_ID
256
- : environment.YUI_NATIVE_SESSION_ID);
257
- return binding.agentId === session.agentId
258
- && binding.adapterId === session.adapterId
259
- && nativeSessionId !== undefined
260
- && nativeSessionId === session.nativeSessionId;
261
246
  }
262
247
  function inputAnswerer(environment) {
263
248
  const env = environment ?? {};
@@ -400,16 +385,6 @@ function requiredText(value, label) {
400
385
  throw usageError(`${label} is required.`);
401
386
  return normalized;
402
387
  }
403
- function trimmed(value) {
404
- const normalized = value?.trim();
405
- return normalized === undefined || normalized.length === 0 ? undefined : normalized;
406
- }
407
- function exactIdentity(value) {
408
- if (value === undefined || value.includes("\0"))
409
- return undefined;
410
- const normalized = value.trim();
411
- return normalized.length === 0 || normalized !== value ? undefined : normalized;
412
- }
413
388
  function timeoutAfter(now, value) {
414
389
  if (!/^[1-9][0-9]*$/.test(value)) {
415
390
  throw usageError("--timeout-seconds must be a positive integer.");
@@ -487,11 +462,6 @@ function parseMultiValueTail(args, valueOptions, repeatOptions, usage) {
487
462
  }
488
463
  return { positionals, options, multiOptions };
489
464
  }
490
- function notifyMailbox(options, target, compatibilityTaskId) {
491
- if (options.runtime?.notifyMailboxChanged !== undefined) {
492
- options.runtime.notifyMailboxChanged(target);
493
- }
494
- else {
495
- options.runtime?.notifyStateChanged(compatibilityTaskId);
496
- }
465
+ function notifyMailbox(options, target) {
466
+ options.runtime?.notifyMailboxChanged(target);
497
467
  }
@@ -1,15 +1,15 @@
1
- import { NodeGitWorkspace } from "../repository/gitWorkspace.js";
2
- import { assertProjectActive, resolveProject } from "../repository/project.js";
3
- import { workspaceProjectEntry } from "../worktree/managedWorkspace.js";
1
+ import { parseRepeatable } from "../cli/parseRepeatable.js";
4
2
  import { usageError } from "../errors/cliError.js";
5
- import { defaultTableWidth, renderTable } from "../output/table.js";
6
- import { createIntegrationAttempt, recordResolutionDecision, supersedeIntegration } from "../integration/integrationAttempt.js";
7
3
  import { GitIntegrationService } from "../integration/gitIntegrationService.js";
4
+ import { createIntegrationAttempt, recordResolutionDecision, supersedeIntegration } from "../integration/integrationAttempt.js";
5
+ import { defaultTableWidth, renderTable } from "../output/table.js";
6
+ import { NodeGitWorkspace } from "../repository/gitWorkspace.js";
7
+ import { assertProjectActive, resolveProject } from "../repository/project.js";
8
8
  import { FileTaskWorkspacePreparer } from "../repository/taskWorkspacePreparer.js";
9
- import { runTaskIntegrationQueueCommand } from "./taskIntegrationQueueCommands.js";
10
- import { assertTaskDeliveryAuthority as taskLocalActor } from "./taskActor.js";
9
+ import { assertTaskDeliveryAuthority as taskLocalActor } from "../task/taskAuthority.js";
11
10
  import { resolveTaskRecordReference } from "../task/taskRecordReference.js";
12
11
  import { governingWorkItemCandidate } from "../workItem/workItem.js";
12
+ import { workspaceProjectEntry } from "../worktree/managedWorkspace.js";
13
13
  export async function runTaskIntegrationCommand(args, store, home, options = {}) {
14
14
  const now = options.now ?? (() => new Date());
15
15
  const [command, ...rest] = args;
@@ -33,9 +33,6 @@ export async function runTaskIntegrationCommand(args, store, home, options = {})
33
33
  return list(rest, store);
34
34
  if (command === "show")
35
35
  return show(rest, store, options.environment);
36
- if (command === "queue") {
37
- return runTaskIntegrationQueueCommand(rest, store, home, options);
38
- }
39
36
  throw usageError(command === undefined
40
37
  ? "Task Integration command is required."
41
38
  : `Unknown command: task integration ${command}`);
@@ -78,8 +75,8 @@ async function cleanupIntegration(args, store, home, environment) {
78
75
  };
79
76
  }
80
77
  async function start(args, store, home, now, options) {
81
- const usage = "Task Integration start usage: yui task integration start <task> --work-item <id> --strategy <ff|cherry-pick|merge|manual> [--project <project>] [--target <ref>] [--check <command> ...].";
82
- const parsed = parseRepeatable(args, new Set(["--check"]), new Set(["--work-item", "--strategy", "--project", "--target"]), usage);
78
+ const usage = "Task Integration start usage: yui task integration start <task> --work-item <id> --strategy <ff|cherry-pick|merge|manual> [--project <project>] [--target <ref>] [--check <command> ...] [--rerun-checks].";
79
+ const parsed = parseRepeatable(args, new Set(["--check"]), new Set(["--work-item", "--strategy", "--project", "--target"]), usage, new Set(["--rerun-checks"]));
83
80
  if (parsed.positionals.length !== 1)
84
81
  throw usageError(usage);
85
82
  let task = store.getTask(parsed.positionals[0]);
@@ -156,7 +153,8 @@ async function start(args, store, home, now, options) {
156
153
  resultCommit,
157
154
  strategy
158
155
  },
159
- checkCommands: parsed.many.get("--check") ?? []
156
+ checkCommands: parsed.many.get("--check") ?? [],
157
+ rerunChecks: parsed.one.has("--rerun-checks")
160
158
  }, now());
161
159
  tx.saveIntegrationAttempt(task.id, created);
162
160
  return created;
@@ -193,7 +191,7 @@ async function runIntegration(store, home, integration, now, options) {
193
191
  : `Integration ${result.attempt.id} failed; inspect its target and preserved evidence\n`;
194
192
  return { output, data: result };
195
193
  }
196
- function resolveDecision(args, store, now, environment, home) {
194
+ function resolveDecision(args, store, now, environment, _home) {
197
195
  const usage = "Task Integration resolve usage: yui task integration resolve <task>/<integration> --option <manual-resolution|reject> --rationale <text>.";
198
196
  const parsed = parseRepeatable(args, new Set(), new Set(["--option", "--rationale"]), usage);
199
197
  if (parsed.positionals.length !== 1)
@@ -239,7 +237,7 @@ async function abortIntegration(args, store, now, options, home) {
239
237
  data: { integration: settled }
240
238
  };
241
239
  }
242
- function supersedeIntegrationCommand(args, store, now, environment, home) {
240
+ function supersedeIntegrationCommand(args, store, now, environment, _home) {
243
241
  const usage = "Task Integration supersede usage: yui task integration supersede <task>/<integration> --reason <text>.";
244
242
  const parsed = parseRepeatable(args, new Set(), new Set(["--reason"]), usage);
245
243
  if (parsed.positionals.length !== 1)
@@ -255,17 +253,6 @@ function supersedeIntegrationCommand(args, store, now, environment, home) {
255
253
  // Superseding a committed Integration rewrites delivery-baseline evidence
256
254
  // and audit history, so it remains an explicit Task-control decision.
257
255
  taskLocalActor(store, environment, integration.taskId);
258
- // A queue-backed committed Attempt cannot be superseded: the queue entry
259
- // would remain in its current status while its Attempt becomes "superseded",
260
- // leaving contradictory terminal records that never converge. This covers
261
- // the crash window where the entry is still "running" or "conflicted" after
262
- // the Attempt committed.
263
- const queueBacked = store.listIntegrationQueueEntries(integration.taskId)
264
- .some((entry) => entry.integrationAttemptId === integration.id);
265
- if (queueBacked) {
266
- throw usageError(`Integration ${integration.id} is backed by a queue entry; `
267
- + "reconcile the queue entry instead of superseding its Attempt.");
268
- }
269
256
  return store.transaction((tx) => {
270
257
  const current = tx.getIntegrationAttempt(integration.taskId, integration.id);
271
258
  if (current === null) {
@@ -330,6 +317,9 @@ function show(args, store, environment) {
330
317
  `Candidate: ${integration.candidateCommit ?? "-"}`,
331
318
  `After commit: ${integration.afterCommit ?? "-"}`,
332
319
  `Job: ${integration.jobId ?? "-"}`,
320
+ `Check execution: ${integration.rerunChecks ? "explicit rerun"
321
+ : integration.checkCommands.length > 0 ? "explicit check commands"
322
+ : "reuse exact successful evidence when available"}`,
333
323
  `Source: ${integrationSourceLabel(integration)}`,
334
324
  `Status: ${integration.status}`,
335
325
  `Summary: ${integration.summary ?? "-"}`,
@@ -353,9 +343,7 @@ function integrationSourceLabel(integration) {
353
343
  if (integration.source.kind === "work-item") {
354
344
  return `work-item:${integration.source.workItemId}@${integration.source.resultCommit.slice(0, 12)}`;
355
345
  }
356
- return integration.source.kind === "upstream"
357
- ? `upstream:${integration.source.branch}@${integration.source.remoteCommit.slice(0, 12)}`
358
- : `historical:${integration.source.changeSetIds.join(",")}`;
346
+ return `upstream:${integration.source.branch}@${integration.source.remoteCommit.slice(0, 12)}`;
359
347
  }
360
348
  function requireIntegration(store, value, environment) {
361
349
  let reference;
@@ -375,31 +363,3 @@ function requireIntegration(store, value, environment) {
375
363
  }
376
364
  return attempt;
377
365
  }
378
- export function parseRepeatable(args, repeatable, singular, usage) {
379
- const positionals = [];
380
- const many = new Map();
381
- const one = new Map();
382
- for (let index = 0; index < args.length; index += 1) {
383
- const value = args[index];
384
- if (!value.startsWith("--")) {
385
- positionals.push(value);
386
- continue;
387
- }
388
- if (!repeatable.has(value) && !singular.has(value)) {
389
- throw usageError(`Unsupported option: ${value}.`, usage);
390
- }
391
- if (singular.has(value) && one.has(value)) {
392
- throw usageError(`Option may only be specified once: ${value}.`, usage);
393
- }
394
- const optionValue = args[index + 1];
395
- if (optionValue === undefined || optionValue.startsWith("--")) {
396
- throw usageError(`${value} is required.`, usage);
397
- }
398
- if (repeatable.has(value))
399
- many.set(value, [...(many.get(value) ?? []), optionValue]);
400
- else
401
- one.set(value, optionValue);
402
- index += 1;
403
- }
404
- return { positionals, many, one };
405
- }
@@ -1,11 +1,12 @@
1
1
  import { taskNotFound, usageError } from "../errors/cliError.js";
2
- import { projectNextAction } from "../task/nextAction.js";
3
- import { projectCompletionReadiness } from "../task/completionReadiness.js";
4
2
  import { projectTaskOrchestration } from "../observability/orchestrationMetrics.js";
5
- import { operationalTaskRecords } from "../task/taskRecordRetirement.js";
6
- import { buildTaskExecutionProjection } from "../scheduler/taskExecutionProjection.js";
7
3
  import { projectReviewDecision } from "../review/reviewDecision.js";
8
- import { projectTaskRemoteDeliveryFromStore, renderTaskRemoteDelivery } from "./taskRemoteDeliveryCommand.js";
4
+ import { buildTaskExecutionProjection } from "../scheduler/taskExecutionProjection.js";
5
+ import { projectCompletionReadiness } from "../task/completionReadiness.js";
6
+ import { projectNextAction } from "../task/nextAction.js";
7
+ import { projectTaskRemoteDeliveryFromStore } from "../task/remoteDeliveryService.js";
8
+ import { operationalTaskRecords } from "../task/taskRecordRetirement.js";
9
+ import { renderTaskRemoteDelivery } from "./taskRemoteDeliveryCommand.js";
9
10
  /**
10
11
  * Issue 07 (Leader convergence): read-only `yui task next-action <task>`.
11
12
  * Folds the existing durable records into exactly one protocol-level next
@@ -34,7 +35,7 @@ export function runTaskNextActionCommand(args, store, currentTaskReviewCandidate
34
35
  const facts = reader.readNextActionFacts(taskId);
35
36
  if (facts === null)
36
37
  throw taskNotFound(taskId);
37
- const execution = buildTaskExecutionProjection(reader, taskId, undefined, now);
38
+ const execution = buildTaskExecutionProjection(reader, taskId, now);
38
39
  if (execution === null)
39
40
  throw taskNotFound(taskId);
40
41
  const actionFacts = {
@@ -1,15 +1,15 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { isDeepStrictEqual } from "node:util";
3
+ import { parseRepeatable } from "../cli/parseRepeatable.js";
3
4
  import { contextContentDigest } from "../context/contextSnapshot.js";
4
- import { createTaskEvent } from "../event/taskEvent.js";
5
5
  import { usageError } from "../errors/cliError.js";
6
+ import { createTaskEvent } from "../event/taskEvent.js";
6
7
  import { NodeGitWorkspace } from "../repository/gitWorkspace.js";
7
8
  import { PUBLICATION_ADOPTED_EVENT, publicationAdoption } from "../task/publicationAdoption.js";
8
9
  import { publicationExternalKey } from "../task/publicationReference.js";
9
10
  import { commitMap, completionEvent } from "../task/remoteDelivery.js";
10
11
  import { resolveTaskRecordReference } from "../task/taskRecordReference.js";
11
- import { assertTaskDeliveryAuthority, taskActor } from "./taskActor.js";
12
- import { parseRepeatable } from "./taskIntegrationCommands.js";
12
+ import { assertTaskDeliveryAuthority, taskActor } from "../task/taskAuthority.js";
13
13
  /** Local read -> explicit semantic adoption of those exact bytes. No Git writes,
14
14
  * provider reads, completion rewrite or implicit acceptance of Integration. */
15
15
  export async function runTaskPublicationAdoptCommand(args, store, options = {}) {