@zq-silk/yui 0.14.0 → 0.14.2

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 (179) hide show
  1. package/ARCHITECTURE.md +64 -35
  2. package/README.md +258 -125
  3. package/dist/agent/argumentPolicy.js +2 -1
  4. package/dist/agent/managedRuntimeEnvironment.js +5 -8
  5. package/dist/cli/commandCatalog.js +74 -109
  6. package/dist/cli/helpRenderer.js +1 -2
  7. package/dist/cli/interactionCandidates.js +10 -0
  8. package/dist/cli/interactionPolicy.js +76 -37
  9. package/dist/cli/roleWizard.js +5 -6
  10. package/dist/cli/updateOrchestrator.js +10 -8
  11. package/dist/cli/updatePorts.js +17 -13
  12. package/dist/cli/upgradeCommand.js +2 -5
  13. package/dist/cli.js +268 -231
  14. package/dist/commands/agentCommands.js +13 -0
  15. package/dist/commands/configCommands.js +7 -19
  16. package/dist/commands/configOverview.js +3 -2
  17. package/dist/commands/durableJobCommands.js +1 -1
  18. package/dist/commands/executionAuditCommands.js +4 -9
  19. package/dist/commands/globalRoleCommands.js +17 -0
  20. package/dist/commands/profileCommands.js +200 -30
  21. package/dist/commands/sessionCommands.js +1 -1
  22. package/dist/commands/taskActor.js +29 -127
  23. package/dist/commands/taskChangeSetCommands.js +1 -3
  24. package/dist/commands/taskCommands.js +1771 -2107
  25. package/dist/commands/taskCompletionGate.js +27 -103
  26. package/dist/commands/taskContextCommand.js +47 -80
  27. package/dist/commands/taskExecutionCommands.js +9 -27
  28. package/dist/commands/taskInputCommands.js +10 -10
  29. package/dist/commands/taskIntegrationCommands.js +69 -61
  30. package/dist/commands/taskIntegrationQueueCommands.js +1 -1
  31. package/dist/commands/taskNextActionCommand.js +8 -29
  32. package/dist/commands/taskOverviewCommand.js +25 -3
  33. package/dist/commands/taskPublicationCommands.js +151 -85
  34. package/dist/commands/taskPublicationVerifyCommand.js +181 -0
  35. package/dist/commands/taskRemoteDeliveryCommand.js +134 -0
  36. package/dist/commands/taskRoleRuntimeStatus.js +5 -6
  37. package/dist/commands/taskUpstreamCommands.js +79 -95
  38. package/dist/config/configCatalog.js +1 -1
  39. package/dist/context/contextSnapshot.js +17 -2
  40. package/dist/context/sessionBootstrapManifest.js +18 -1
  41. package/dist/context/sourceTurnContext.js +30 -0
  42. package/dist/context/turnContextPack.js +107 -48
  43. package/dist/context/turnInputContract.js +11 -4
  44. package/dist/context/wakeNotification.js +84 -22
  45. package/dist/controller/agentRuntimeObserver.js +9 -12
  46. package/dist/controller/clientRuntime.js +13 -13
  47. package/dist/controller/controller.js +11 -11
  48. package/dist/controller/fileSchedulerStoreAdapter.js +262 -219
  49. package/dist/controller/jobControl.js +11 -10
  50. package/dist/controller/resourceInventory.js +6 -6
  51. package/dist/controller/resourceInventoryLinux.js +2 -2
  52. package/dist/controller/runtime.js +18 -61
  53. package/dist/controller/runtimeEventInbox.js +57 -34
  54. package/dist/controller/runtimeEventProcessor.js +36 -96
  55. package/dist/controller/runtimeHookTurnFence.js +33 -57
  56. package/dist/controller/runtimeLaunchCoordinator.js +44 -40
  57. package/dist/controller/runtimeObservationHook.js +18 -12
  58. package/dist/controller/sessionNotify.js +43 -28
  59. package/dist/controller/sessionOwnerReconciliation.js +21 -21
  60. package/dist/controller/structuredProviderObservation.js +13 -16
  61. package/dist/coordination/workMailboxQueue.js +132 -0
  62. package/dist/core/controllerServer.js +1 -0
  63. package/dist/domain/agentResultTransport.js +60 -0
  64. package/dist/execution/executionHealth.js +111 -118
  65. package/dist/execution/reviewMainTurn.js +161 -0
  66. package/dist/execution/workItemExecution.js +454 -0
  67. package/dist/execution/workItemExecutionProjection.js +309 -0
  68. package/dist/execution/workItemMainTurn.js +164 -0
  69. package/dist/executor/agentExecutor.js +19 -19
  70. package/dist/executor/executorRegistry.js +7 -28
  71. package/dist/executor/fileRoleLaunchPlanner.js +51 -83
  72. package/dist/external/githubPublicationVerifier.js +114 -0
  73. package/dist/external/gitlabPublicationVerifier.js +158 -0
  74. package/dist/external/pinnedCommandRunner.js +73 -0
  75. package/dist/integration/changeSetManifest.js +0 -3
  76. package/dist/integration/deliveryObligation.js +43 -6
  77. package/dist/integration/gitIntegrationService.js +284 -101
  78. package/dist/integration/integrationAttempt.js +64 -14
  79. package/dist/integration/integrationQueueEntry.js +5 -35
  80. package/dist/integration/integrationQueueService.js +65 -194
  81. package/dist/lifecycle/canonicalLifecycleEvent.js +16 -15
  82. package/dist/lifecycle/exactTurnTerminalization.js +100 -97
  83. package/dist/message/message.js +16 -0
  84. package/dist/observability/executionAudit.js +4 -25
  85. package/dist/observability/faultClassification.js +58 -70
  86. package/dist/observability/orchestrationMetrics.js +8 -50
  87. package/dist/profile/agentProfile.js +60 -15
  88. package/dist/profile/agentProfileRuntime.js +100 -0
  89. package/dist/release/releaseWorkflowPorts.js +5 -68
  90. package/dist/repository/executionLaneGitSnapshot.js +80 -0
  91. package/dist/repository/gitWorkspace.js +9 -67
  92. package/dist/repository/taskBaseFreshness.js +3 -1
  93. package/dist/repository/taskWorkspaceCoordinator.js +32 -4
  94. package/dist/repository/taskWorkspacePreparer.js +252 -411
  95. package/dist/resources/liveReferences.js +6 -55
  96. package/dist/resources/resourceDiscovery.js +1 -30
  97. package/dist/resources/resourceRegistrar.js +0 -23
  98. package/dist/review/deltaRecheck.js +8 -65
  99. package/dist/review/reviewAcceptance.js +22 -19
  100. package/dist/review/reviewConfig.js +0 -10
  101. package/dist/review/reviewDecision.js +2 -2
  102. package/dist/review/reviewRound.js +147 -273
  103. package/dist/review/reviewerAvailability.js +13 -23
  104. package/dist/role/role.js +3 -0
  105. package/dist/runtime/agentDriverObservation.js +1 -1
  106. package/dist/runtime/agentHost.js +51 -51
  107. package/dist/runtime/builtinAgentDrivers.js +9 -2
  108. package/dist/runtime/codexAppServerRuntime.js +23 -5
  109. package/dist/runtime/exactControlPlane.js +2 -267
  110. package/dist/runtime/firstProgressAdvisory.js +1 -1
  111. package/dist/runtime/index.js +1 -1
  112. package/dist/runtime/launchBroker.js +11 -11
  113. package/dist/runtime/lifecycleReservation.js +2 -2
  114. package/dist/runtime/managedCaller.js +111 -0
  115. package/dist/runtime/ports.js +16 -3
  116. package/dist/runtime/preallocatedNativeSession.js +2 -2
  117. package/dist/runtime/processExitObservation.js +3 -2
  118. package/dist/runtime/providerContinuationReconciliationService.js +1 -1
  119. package/dist/runtime/runtimeBinding.js +1 -1
  120. package/dist/runtime/runtimeObservation.js +33 -16
  121. package/dist/runtime/runtimeSessionCandidate.js +2 -2
  122. package/dist/runtime/runtimeStopReceipt.js +11 -11
  123. package/dist/runtime/sessionLaunchRequest.js +2 -2
  124. package/dist/runtime/sessionOwnerIdentity.js +9 -9
  125. package/dist/runtime/sessionOwnerRegistry.js +13 -13
  126. package/dist/runtime/sessionReconciliation.js +4 -4
  127. package/dist/runtime/sessionTerminationGuard.js +9 -9
  128. package/dist/runtime/sessionTokenMetrics.js +5 -7
  129. package/dist/runtime/structuredProviderHost.js +5 -5
  130. package/dist/runtime/taskRuntimeIsolation.js +24 -24
  131. package/dist/runtime/tmuxAdapters.js +32 -30
  132. package/dist/scheduler/activeRoleTurnDelivery.js +37 -15
  133. package/dist/scheduler/leaderWakeupProcessor.js +78 -55
  134. package/dist/scheduler/roleTurnLiveness.js +6 -6
  135. package/dist/scheduler/roleTurnStall.js +8 -9
  136. package/dist/scheduler/taskExecutionProjection.js +19 -41
  137. package/dist/scheduler/taskObservabilityProjection.js +75 -122
  138. package/dist/storage/sqliteSchema.js +1 -21
  139. package/dist/storage/sqliteStore.js +62 -47
  140. package/dist/storage/storageSchema.js +1 -8
  141. package/dist/storage/storageVersions.js +7 -4
  142. package/dist/storage/taskStore.js +8 -8
  143. package/dist/storage/upgrade/upgradeOrchestrator.js +112 -83
  144. package/dist/task/completionReadiness.js +17 -98
  145. package/dist/task/deliveryGuard.js +9 -11
  146. package/dist/task/draftPlan.js +142 -0
  147. package/dist/task/nextAction.js +179 -477
  148. package/dist/task/publicationVerification.js +1 -0
  149. package/dist/task/remoteDelivery.js +233 -0
  150. package/dist/task/task.js +108 -4
  151. package/dist/task/taskRecordReference.js +0 -1
  152. package/dist/turn/turn.js +109 -16
  153. package/dist/verification/verificationGateService.js +0 -26
  154. package/dist/web/assets/client/components.js +99 -121
  155. package/dist/web/assets/client/i18n.js +136 -58
  156. package/dist/web/assets/client/view.js +44 -0
  157. package/dist/web/assets/styles/cards.js +3 -15
  158. package/dist/web/assets/styles/widgets.js +4 -0
  159. package/dist/web/webSnapshot.js +29 -14
  160. package/dist/workItem/dependencyGate.js +34 -0
  161. package/dist/workItem/workItem.js +99 -266
  162. package/dist/workspace/workItemChangeSetManager.js +15 -59
  163. package/docs/task-local-identity.md +1 -1
  164. package/i18n/README.zh-CN.md +165 -59
  165. package/package.json +1 -1
  166. package/skills/yui-leader/SKILL.md +272 -782
  167. package/skills/yui-operator/SKILL.md +190 -365
  168. package/skills/yui-reviewer/SKILL.md +40 -33
  169. package/skills/yui-runtime/SKILL.md +27 -6
  170. package/skills/yui-worker/SKILL.md +19 -6
  171. package/dist/execution/candidateConvergence.js +0 -623
  172. package/dist/execution/executionGroup.js +0 -854
  173. package/dist/execution/resourceBroker.js +0 -426
  174. package/dist/executor/launchPlan.js +0 -16
  175. package/dist/review/reviewFinding.js +0 -240
  176. package/dist/review/reviewFindingLedger.js +0 -550
  177. package/dist/review/reviewOutcomeClassifier.js +0 -255
  178. package/dist/scheduler/resourceQueueProjection.js +0 -71
  179. package/dist/task/repairWave.js +0 -150
@@ -292,6 +292,50 @@ export function renderTaskDetail(detail, data, t, locale, actions) {
292
292
  // the attention/blocker facts behind it.
293
293
  const band = executionBand(data.execution, t, locale);
294
294
  if (band) summaryBody.append(band);
295
+ if (data.remoteDelivery) {
296
+ const remote = data.remoteDelivery;
297
+ const card = node("article", "record-card");
298
+ const head = node("div", "record-head");
299
+ head.append(
300
+ node("strong", "record-title", t("detail.remoteDelivery")),
301
+ pill(t, "remoteDelivery", remote.status)
302
+ );
303
+ card.append(head);
304
+ card.append(node(
305
+ "p",
306
+ "record-copy",
307
+ t("detail.expectedHeads") + " · " + remote.source
308
+ + (remote.provisional ? " · " + t("detail.provisional") : "")
309
+ ));
310
+ const facts = node("div", "record-meta");
311
+ const unavailable = remote.status === "unavailable";
312
+ facts.append(
313
+ node("span", "", t("detail.allMerged") + " · "
314
+ + (unavailable
315
+ ? t("detail.unavailable")
316
+ : remote.allMerged ? t("common.yes") : t("common.no"))),
317
+ node("span", "", t("detail.allVerified") + " · "
318
+ + (unavailable
319
+ ? t("detail.unavailable")
320
+ : remote.allVerified ? t("common.yes") : t("common.no"))),
321
+ node("span", "", t("detail.integratedCoverage") + " · "
322
+ + (unavailable
323
+ ? t("detail.unavailable")
324
+ : remote.integratedCoverageSatisfied ? t("detail.satisfied") : t("detail.blocked")))
325
+ );
326
+ card.append(facts);
327
+ remote.projects.forEach(function (project) {
328
+ card.append(node(
329
+ "p",
330
+ "muted mono",
331
+ project.directory + " · " + project.coverage + " · "
332
+ + (project.expectedLocalCommit ? project.expectedLocalCommit.slice(0, 12) : "unknown")
333
+ + " → "
334
+ + (project.remoteCommit ? project.remoteCommit.slice(0, 12) : "unknown")
335
+ ));
336
+ });
337
+ summaryBody.append(card);
338
+ }
295
339
  const observability = data.observability || (data.execution && data.execution.observability);
296
340
  const metrics = observabilityMetricCard(observability, t);
297
341
  if (metrics) summaryBody.append(metrics);
@@ -151,7 +151,7 @@ details.work-item-card>summary.record-head~*{margin-top:0}
151
151
  .exec-signal.is-blocker{background:var(--danger-soft);border:1px solid color-mix(in srgb,var(--danger) 30%,transparent)}
152
152
  .exec-signal-kind{flex:none;font-family:var(--font-mono);font-size:9.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}
153
153
  .exec-signal-text{min-width:0;color:var(--text)}
154
- /* Unified execution Group (Lanes + resolution) */
154
+ /* WorkItem execution and recovery */
155
155
  .exec-group{gap:8px}
156
156
  .lane-list{display:grid;gap:4px}
157
157
  .lane-row{display:flex;align-items:center;gap:9px;padding:5px 9px;border-radius:var(--radius);background:var(--bg-3);border:1px solid var(--border);font-size:11.5px}
@@ -159,9 +159,8 @@ details.work-item-card>summary.record-head~*{margin-top:0}
159
159
  .lane-role{font-family:var(--font-body);font-weight:700;font-size:12px;color:var(--text)}
160
160
  .lane-status{font-family:var(--font-mono);font-size:9.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}
161
161
  .lane-summary{flex:1;min-width:0;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
162
- .execution-stage-meta,.execution-resource-meta,.work-item-observability{gap:10px;flex-wrap:wrap}
163
- .work-item-stages{margin-top:6px}
164
- .execution-stage-meta .chip,.execution-resource-meta .chip{margin-left:auto}
162
+ .execution-resource-meta,.work-item-observability{gap:10px;flex-wrap:wrap}
163
+ .execution-resource-meta .chip{margin-left:auto}
165
164
  .observability-metrics{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));gap:8px;margin-top:12px}
166
165
  .observability-metrics .metric{min-height:52px;padding:8px 10px}
167
166
  .observability-metrics .metric-value{font-size:18px}
@@ -193,17 +192,6 @@ details.work-item-card>summary.record-head~*{margin-top:0}
193
192
  .candidate-summary{flex:1;min-width:0;color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
194
193
  .candidate-source{flex:none;font-family:var(--font-mono);font-size:9.5px;letter-spacing:.04em;text-transform:uppercase;color:var(--muted)}
195
194
  .candidate-row time{flex:none;color:var(--muted);font-size:10.5px}
196
- /* Findings */
197
- .finding-list{display:grid;gap:4px}
198
- .finding-row{display:flex;align-items:baseline;gap:8px;padding:5px 9px;border-radius:var(--radius);background:var(--bg-3);border:1px solid var(--border);font-size:11.5px}
199
- .finding-summary{flex:1;min-width:0;color:var(--text)}
200
- .finding-row.is-critical,.finding-row.is-high{border-left:2px solid var(--danger)}
201
- .finding-row.is-medium{border-left:2px solid var(--warning)}
202
- .finding-row.is-low{border-left:2px solid var(--faint)}
203
- .chip.is-critical{color:var(--danger);border-color:var(--danger)}
204
- .chip.is-high{color:var(--danger)}
205
- .chip.is-medium{color:var(--warning)}
206
- .chip.is-low{color:var(--muted)}
207
195
  /* Sidebar derived execution status */
208
196
  .task-exec{grid-row:1;grid-column:3;align-self:center;flex:none;font-family:var(--font-mono);font-size:8.5px;letter-spacing:.05em;text-transform:uppercase;color:var(--faint);white-space:nowrap;max-width:90px;overflow:hidden;text-overflow:ellipsis}
209
197
  .task-exec.is-blocked,.task-exec.is-attention{color:var(--danger)}
@@ -104,6 +104,10 @@ kbd{background:var(--bg-3);border:1px solid var(--border);border-bottom-width:2p
104
104
  .status-dot{width:8px;height:8px;flex:none;border-radius:50%;background:var(--faint);margin-top:4px}
105
105
  .status-dot.active{background:var(--active);box-shadow:0 0 8px var(--active)}
106
106
  .status-dot.completed{background:var(--success);box-shadow:0 0 8px var(--success)}
107
+ .status-dot.running{background:var(--active);box-shadow:0 0 8px var(--active)}
108
+ .status-dot.succeeded{background:var(--success);box-shadow:0 0 8px var(--success)}
109
+ .status-dot.needs-attention{background:var(--warning);box-shadow:0 0 8px var(--warning)}
110
+ .status-dot.failed{background:var(--danger);box-shadow:0 0 8px var(--danger)}
107
111
  .status-dot.draft{background:var(--warning);box-shadow:0 0 8px var(--warning)}
108
112
  .status-dot.retired{background:var(--faint)}
109
113
  .status-dot.archived{background:var(--faint)}
@@ -1,12 +1,13 @@
1
1
  import { isRoleTurnStalled, latestTurnDurableProgressAt } from "../scheduler/roleTurnStall.js";
2
+ import { retiredTaskRecordIds } from "../task/taskRecordRetirement.js";
2
3
  import { buildTaskExecutionProjection } from "../scheduler/taskExecutionProjection.js";
3
- import { summarizeExecutionGroup } from "../execution/executionGroup.js";
4
- import { currentWorkItemExecutionGroup } from "../workItem/workItem.js";
4
+ import { projectWorkItemExecution } from "../execution/workItemExecutionProjection.js";
5
5
  import { classifyRuntimeHealth, projectRuntimeTaskEvents } from "../runtime/runtimeProjection.js";
6
6
  import { builtinDriverIdForAdapter } from "../runtime/builtinAgentDrivers.js";
7
7
  import { formatTurnReceiptId } from "../task/taskRecordReference.js";
8
8
  import { resolveRuntimeHealth } from "../config/yuiConfig.js";
9
9
  import { projectSessionTokenMetrics, resolveSessionTokenIdentity } from "../runtime/sessionTokenMetrics.js";
10
+ import { projectTaskRemoteDelivery } from "../task/remoteDelivery.js";
10
11
  export function buildWebDashboardSnapshot(store, now = new Date()) {
11
12
  return store.transaction((reader) => {
12
13
  const statusCounts = {
@@ -70,6 +71,7 @@ export function buildWebTaskDetail(store, taskId, now = new Date()) {
70
71
  });
71
72
  const turns = reader.listTurns(taskId);
72
73
  const events = reader.listEvents?.(taskId) ?? [];
74
+ const retiredMessageIds = retiredTaskRecordIds(events, "message");
73
75
  const needsAttentionTurns = turns
74
76
  .filter((turn) => turn.status === "active" && isRoleTurnStalled(events, turn.id))
75
77
  .map((turn) => ({
@@ -109,6 +111,9 @@ export function buildWebTaskDetail(store, taskId, now = new Date()) {
109
111
  const execution = buildTaskExecutionProjection(reader, taskId, task, now);
110
112
  if (execution === null)
111
113
  return null;
114
+ const remoteDelivery = webRemoteDelivery(reader, task);
115
+ const workItems = reader.listWorkItems(taskId);
116
+ const roleSessionSets = reader.listRoleSessionSets(taskId);
112
117
  const workItemObservability = new Map(execution.observability.workItems.map((item) => [item.workItemId, item]));
113
118
  return {
114
119
  task: {
@@ -116,27 +121,38 @@ export function buildWebTaskDetail(store, taskId, now = new Date()) {
116
121
  ...(projectNames.length === 0 ? {} : { projectNames })
117
122
  },
118
123
  execution,
124
+ remoteDelivery,
119
125
  observability: execution.observability,
120
126
  brief: reader.getTaskBrief(taskId),
121
127
  roles,
122
- workItems: reader.listWorkItems(taskId).map((item) => {
123
- const group = currentWorkItemExecutionGroup(item);
124
- return {
125
- ...item,
126
- observability: workItemObservability.get(item.id),
127
- ...(group === undefined ? {} : { currentExecution: summarizeExecutionGroup(group) })
128
- };
129
- }),
128
+ workItems: workItems.map((item) => ({
129
+ ...item,
130
+ observability: workItemObservability.get(item.id),
131
+ execution: projectWorkItemExecution(item, turns, roleSessionSets)
132
+ })),
130
133
  turns,
131
134
  runtimeHealth: { needsAttentionTurns, activeTurns: activeTurnHealth },
132
135
  reviewRounds: reader.listReviewRounds(taskId),
133
136
  openInputs: inputs.filter((request) => request.status === "open"),
134
- messages: reader.listMessages(taskId),
137
+ messages: reader.listMessages(taskId).map((message) => ({
138
+ ...message,
139
+ status: retiredMessageIds.has(message.id) ? "retired" : "active"
140
+ })),
135
141
  decisions: reader.listDecisions(taskId),
136
142
  milestones: reader.listMilestones(taskId)
137
143
  };
138
144
  });
139
145
  }
146
+ function webRemoteDelivery(reader, task) {
147
+ return projectTaskRemoteDelivery({
148
+ task,
149
+ events: reader.listEvents?.(task.id) ?? [],
150
+ publications: reader.listPublicationReferences(task.id),
151
+ managedWorkspaces: reader.listManagedWorkspaces(task.id),
152
+ turns: reader.listTurns(task.id),
153
+ currentCandidate: null
154
+ });
155
+ }
140
156
  function latestStallProgress(events, turnId) {
141
157
  return latestStallField(events, turnId, "progressAt");
142
158
  }
@@ -153,7 +169,7 @@ function projectWebTurnRuntimeHealth(reader, taskId, turn, events, now, policy)
153
169
  const sessions = reader.getTaskRoleSessionSet(taskId, turn.roleName);
154
170
  const session = sessions?.sessions[turn.effective.agentId];
155
171
  const stallReason = "the live active Turn has no durable progress in the configured stall window";
156
- if (session?.launchId === undefined) {
172
+ if (session?.runtimeGenerationId === undefined) {
157
173
  return {
158
174
  turnId: turn.id,
159
175
  roleName: turn.roleName,
@@ -184,8 +200,7 @@ function projectWebTurnRuntimeHealth(reader, taskId, turn, events, now, policy)
184
200
  turnId: turn.id,
185
201
  agentId: turn.effective.agentId,
186
202
  driverId,
187
- launchId: session.launchId,
188
- sessionGenerationId: session.launchId,
203
+ runtimeGenerationId: session.runtimeGenerationId,
189
204
  nativeSessionId: session.nativeSessionId,
190
205
  nativeTurnId: providerTurn?.turnId === turn.id
191
206
  ? providerTurn.nativeTurnId ?? turn.id
@@ -0,0 +1,34 @@
1
+ export const WORK_ITEM_DEPENDENCIES_NOT_COMPLETED = "work-item-dependencies-not-completed";
2
+ export class WorkItemDependencyGateError extends Error {
3
+ details;
4
+ constructor(workItemId, unmetDependencies) {
5
+ super(`Work Item dependencies are not completed: ${unmetDependencies
6
+ .map(({ id, status }) => `${id} (${status})`)
7
+ .join(", ")}.`);
8
+ this.name = "WorkItemDependencyGateError";
9
+ this.details = Object.freeze({
10
+ reason: WORK_ITEM_DEPENDENCIES_NOT_COMPLETED,
11
+ workItemId,
12
+ unmetDependencies: Object.freeze([...unmetDependencies])
13
+ });
14
+ }
15
+ }
16
+ /**
17
+ * The direct `dependsOn` list is the only dependency authority. Every direct
18
+ * dependency must exist and be completed; retirement and replacement metadata
19
+ * never satisfy or rewrite the edge.
20
+ */
21
+ export function assertWorkItemDependenciesCompleted(store, item) {
22
+ const unmetDependencies = item.dependsOn.flatMap((dependencyId) => {
23
+ const dependency = store.getWorkItem(item.taskId, dependencyId);
24
+ if (dependency?.status === "completed")
25
+ return [];
26
+ return [{
27
+ id: dependencyId,
28
+ status: dependency?.status ?? "missing"
29
+ }];
30
+ });
31
+ if (unmetDependencies.length > 0) {
32
+ throw new WorkItemDependencyGateError(item.id, unmetDependencies);
33
+ }
34
+ }